Files
yidaima_tools/quark_uploader.py

167 lines
7.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from __future__ import annotations
import os
import time
from typing import Optional, Callable
from playwright.sync_api import sync_playwright
class QuarkUploader:
"""
夸克网盘上传工具 - 终极稳健版 (双重状态锁)
"""
def __init__(self, chrome_path: str, cookies_dir: str, log_callback: Optional[Callable[[str], None]] = None):
self.chrome_path = chrome_path
self.cookies_dir = cookies_dir
self.log_callback = log_callback
self.url = "https://pan.quark.cn/"
if not os.path.exists(self.cookies_dir):
os.makedirs(self.cookies_dir, exist_ok=True)
def _log(self, message: str):
print(message)
if self.log_callback:
self.log_callback(message)
def upload_file(self, file_path: str, target_folder_name: str, root_path: str = "精品项目整理") -> bool:
"""
上传文件到夸克网盘
"""
if not os.path.exists(file_path):
self._log(f"错误: 本地文件不存在 {file_path}")
return False
filename = os.path.basename(file_path)
try:
with sync_playwright() as p:
self._log(f"正在启动浏览器...")
launch_args = {
"user_data_dir": self.cookies_dir,
"headless": False,
"viewport": {"width": 1280, "height": 800}
}
if self.chrome_path and os.path.exists(self.chrome_path):
launch_args["executable_path"] = self.chrome_path
context = p.chromium.launch_persistent_context(**launch_args)
page = context.new_page()
self._log("正在打开夸克网盘...")
page.goto(self.url, wait_until="networkidle")
# 1. 登录检测
try:
page.wait_for_selector("text=全部文件", timeout=10000)
except:
self._log("请在浏览器中完成登录...")
page.wait_for_selector("text=全部文件", timeout=180000)
# 2. 导航到目录
self._log(f"定位目录: {root_path} > {target_folder_name}")
page.get_by_text("全部文件").first.click()
page.wait_for_timeout(2000)
# 进入根目录
if not page.get_by_text(root_path, exact=True).first.is_visible():
self._create_folder(page, root_path)
page.get_by_text(root_path, exact=True).first.dblclick()
page.wait_for_timeout(1000)
# 进入项目目录
if not page.get_by_text(target_folder_name, exact=True).first.is_visible():
self._create_folder(page, target_folder_name)
page.get_by_text(target_folder_name, exact=True).first.dblclick()
page.wait_for_timeout(2000)
# 3. 查重
if page.get_by_text(filename, exact=True).count() > 0:
self._log(f"云端已存在 '{filename}',跳过上传。")
context.close()
return True
# 4. 执行上传
self._log(f"开始上传: {filename}")
# 清除可能存在的旧“上传成功”提示
page.evaluate("() => { const els = document.querySelectorAll('.ant-notification-notice'); els.forEach(e => e.remove()); }")
page.set_input_files("input[type=file]", file_path)
# 5. 确认上传已启动 (关键一步)
self._log("等待上传任务初始化...")
try:
# 等待出现任何进度指示元素
page.wait_for_selector("text=% , .ant-progress-inner, text=正在上传", timeout=20000)
self._log("检测到上传流已建立。")
except:
self._log("警告:未检测到明显的进度条,可能由于文件较小或 UI 延迟,继续监测列表状态。")
# 6. 深度监测循环
self._log("进入深度监测模式,请保持浏览器前台运行...")
start_time = time.time()
timeout = 1800 # 30分钟
# 连续稳定次数计数
stable_count = 0
while time.time() - start_time < timeout:
# A. 检查全局进度标志
is_uploading = page.get_by_text("%").is_visible() or \
page.get_by_text("正在上传").is_visible() or \
page.locator(".ant-progress-inner").is_visible()
# B. 检查列表中该行的具体状态 (精准匹配)
# 找到包含文件名的那一行 tr看里面是否有“正在上传”字样
row_status_text = ""
try:
row = page.locator(f"tr:has-text('{filename}')").first
if row.is_visible():
row_status_text = row.inner_text()
except:
pass
is_item_active = "正在上传" in row_status_text or "%" in row_status_text or "等待上传" in row_status_text
# C. 确认列表里确实有这个文件
in_list = page.get_by_text(filename, exact=True).count() > 0
if not is_uploading and not is_item_active and in_list:
stable_count += 1
if stable_count >= 5: # 连续 5 次检测(约 15 秒)都处于稳定态
self._log("检测到上传任务已从任务列表中消失,且列表文件状态正常。")
self._log("为了绝对安全,最后等待 15 秒进行数据落盘同步...")
page.wait_for_timeout(15000)
self._log("上传确认圆满成功!")
context.close()
return True
else:
self._log(f"上传疑似完成,正在进行稳定性校验 ({stable_count}/5)...")
else:
if is_uploading or is_item_active:
self._log("监测到活跃传输流...")
stable_count = 0 # 只要发现还在传,重置计数
time.sleep(3)
self._log("传输任务监测超时,请确认网速或手动核实。")
context.close()
return False
except Exception as e:
self._log(f"代码执行异常: {str(e)}")
return False
def _create_folder(self, page, folder_name: str):
"""创建目录"""
try:
page.get_by_text("新建").first.click()
page.wait_for_timeout(500)
page.get_by_text("新建文件夹").first.click()
page.wait_for_timeout(500)
page.keyboard.type(folder_name)
page.keyboard.press("Enter")
self._log(f"已创建目录: {folder_name}")
page.wait_for_timeout(1500)
except:
pass