- 新增依赖检测模块:启动时自动检测wkhtmltoimage和Playwright Chromium - 新增依赖安装对话框:缺失时提示用户一键下载安装 - 修复选项记忆功能:浏览类型、自动截图、自动上传选项现在会保存 - 优化KDocs登录检测:未登录时自动切换到金山文档页面并显示二维码 - 简化日志输出:移除debug信息,保留用户友好的状态提示 - 新增账号变化信号:账号管理页面的修改会自动同步到浏览任务页面 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
229 lines
6.1 KiB
Python
229 lines
6.1 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
依赖检测和安装模块
|
||
检测 wkhtmltoimage 和 Playwright Chromium 是否已安装
|
||
如果缺失则从直链下载安装
|
||
"""
|
||
|
||
import os
|
||
import shutil
|
||
import subprocess
|
||
import tempfile
|
||
import zipfile
|
||
from pathlib import Path
|
||
from typing import Optional, Callable, Dict, Tuple
|
||
|
||
# 直链下载地址
|
||
DOWNLOAD_URLS = {
|
||
"wkhtmltopdf": "http://by.haory.cn/3/1223/wkhtmltox.exe",
|
||
"chromium": "http://by.haory.cn/3/1223/chromium-win64.zip",
|
||
}
|
||
|
||
# Playwright chromium 版本目录名(与 playwright 1.57.0 对应)
|
||
CHROMIUM_VERSION_DIR = "chromium-1200"
|
||
|
||
|
||
def check_wkhtmltoimage() -> bool:
|
||
"""检测 wkhtmltoimage 是否已安装"""
|
||
# 先检查 PATH
|
||
if shutil.which("wkhtmltoimage"):
|
||
return True
|
||
|
||
# 检查默认安装路径
|
||
default_paths = [
|
||
r"C:\Program Files\wkhtmltopdf\bin\wkhtmltoimage.exe",
|
||
r"C:\Program Files (x86)\wkhtmltopdf\bin\wkhtmltoimage.exe",
|
||
]
|
||
for p in default_paths:
|
||
if os.path.exists(p):
|
||
return True
|
||
|
||
return False
|
||
|
||
|
||
def check_playwright_chromium() -> bool:
|
||
"""检测 Playwright Chromium 浏览器是否已安装"""
|
||
local_appdata = os.environ.get("LOCALAPPDATA", "")
|
||
if not local_appdata:
|
||
return False
|
||
|
||
chromium_path = Path(local_appdata) / "ms-playwright" / CHROMIUM_VERSION_DIR
|
||
|
||
# 检查目录是否存在且包含chrome.exe
|
||
if chromium_path.exists():
|
||
chrome_exe = chromium_path / "chrome-win" / "chrome.exe"
|
||
if chrome_exe.exists():
|
||
return True
|
||
|
||
return False
|
||
|
||
|
||
def get_missing_dependencies() -> Dict[str, bool]:
|
||
"""获取缺失的依赖列表"""
|
||
return {
|
||
"wkhtmltoimage": not check_wkhtmltoimage(),
|
||
"chromium": not check_playwright_chromium(),
|
||
}
|
||
|
||
|
||
def download_file(url: str, dest_path: str, progress_callback: Optional[Callable] = None) -> bool:
|
||
"""
|
||
下载文件
|
||
|
||
Args:
|
||
url: 下载地址
|
||
dest_path: 保存路径
|
||
progress_callback: 进度回调 (downloaded_bytes, total_bytes)
|
||
|
||
Returns:
|
||
是否成功
|
||
"""
|
||
import urllib.request
|
||
|
||
try:
|
||
def report_progress(block_num, block_size, total_size):
|
||
if progress_callback:
|
||
downloaded = block_num * block_size
|
||
progress_callback(downloaded, total_size)
|
||
|
||
urllib.request.urlretrieve(url, dest_path, reporthook=report_progress)
|
||
return True
|
||
except Exception as e:
|
||
print(f"下载失败: {e}")
|
||
return False
|
||
|
||
|
||
def install_wkhtmltopdf(installer_path: str, log_callback: Optional[Callable] = None) -> bool:
|
||
"""
|
||
安装 wkhtmltopdf
|
||
|
||
Args:
|
||
installer_path: 安装包路径
|
||
log_callback: 日志回调
|
||
|
||
Returns:
|
||
是否成功
|
||
"""
|
||
def log(msg):
|
||
if log_callback:
|
||
log_callback(msg)
|
||
|
||
try:
|
||
log("正在启动 wkhtmltopdf 安装程序...")
|
||
# 启动安装程序(需要用户手动完成安装向导)
|
||
subprocess.Popen([installer_path], shell=True)
|
||
log("请在安装向导中完成安装(建议保持默认路径)")
|
||
return True
|
||
except Exception as e:
|
||
log(f"启动安装程序失败: {e}")
|
||
return False
|
||
|
||
|
||
def install_playwright_chromium(zip_path: str, log_callback: Optional[Callable] = None) -> bool:
|
||
"""
|
||
安装 Playwright Chromium 浏览器
|
||
|
||
Args:
|
||
zip_path: zip包路径
|
||
log_callback: 日志回调
|
||
|
||
Returns:
|
||
是否成功
|
||
"""
|
||
def log(msg):
|
||
if log_callback:
|
||
log_callback(msg)
|
||
|
||
try:
|
||
local_appdata = os.environ.get("LOCALAPPDATA", "")
|
||
if not local_appdata:
|
||
log("无法获取 LOCALAPPDATA 路径")
|
||
return False
|
||
|
||
# 目标目录
|
||
playwright_dir = Path(local_appdata) / "ms-playwright"
|
||
chromium_dir = playwright_dir / CHROMIUM_VERSION_DIR
|
||
|
||
# 创建目录
|
||
playwright_dir.mkdir(parents=True, exist_ok=True)
|
||
|
||
log("正在解压 Chromium 浏览器...")
|
||
|
||
# 解压
|
||
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
|
||
zip_ref.extractall(chromium_dir)
|
||
|
||
# 创建标记文件(Playwright需要这些文件来确认安装完成)
|
||
(chromium_dir / "INSTALLATION_COMPLETE").touch()
|
||
(chromium_dir / "DEPENDENCIES_VALIDATED").touch()
|
||
|
||
log("Chromium 浏览器安装成功")
|
||
return True
|
||
except Exception as e:
|
||
log(f"安装 Chromium 失败: {e}")
|
||
return False
|
||
|
||
|
||
def download_and_install_dependency(
|
||
dep_name: str,
|
||
progress_callback: Optional[Callable] = None,
|
||
log_callback: Optional[Callable] = None
|
||
) -> Tuple[bool, str]:
|
||
"""
|
||
下载并安装指定依赖
|
||
|
||
Args:
|
||
dep_name: 依赖名称 ("wkhtmltoimage" 或 "chromium")
|
||
progress_callback: 下载进度回调
|
||
log_callback: 日志回调
|
||
|
||
Returns:
|
||
(是否成功, 错误信息)
|
||
"""
|
||
def log(msg):
|
||
if log_callback:
|
||
log_callback(msg)
|
||
|
||
# 获取下载地址
|
||
if dep_name == "wkhtmltoimage":
|
||
url = DOWNLOAD_URLS["wkhtmltopdf"]
|
||
filename = "wkhtmltox_installer.exe"
|
||
elif dep_name == "chromium":
|
||
url = DOWNLOAD_URLS["chromium"]
|
||
filename = "chromium-win64.zip"
|
||
else:
|
||
return False, f"未知依赖: {dep_name}"
|
||
|
||
# 下载到临时目录
|
||
temp_dir = tempfile.gettempdir()
|
||
download_path = os.path.join(temp_dir, filename)
|
||
|
||
log(f"正在下载 {dep_name}...")
|
||
|
||
if not download_file(url, download_path, progress_callback):
|
||
return False, "下载失败"
|
||
|
||
log("下载完成,正在安装...")
|
||
|
||
# 安装
|
||
if dep_name == "wkhtmltoimage":
|
||
success = install_wkhtmltopdf(download_path, log_callback)
|
||
if success:
|
||
return True, "安装程序已启动,请完成安装向导"
|
||
else:
|
||
return False, "启动安装程序失败"
|
||
elif dep_name == "chromium":
|
||
success = install_playwright_chromium(download_path, log_callback)
|
||
# 清理下载的zip文件
|
||
try:
|
||
os.remove(download_path)
|
||
except:
|
||
pass
|
||
if success:
|
||
return True, ""
|
||
else:
|
||
return False, "安装失败"
|
||
|
||
return False, "未知错误"
|