[fix] 兼容 linux 的 pdf 生成情况

This commit is contained in:
SXP-Simon
2025-10-09 20:31:30 +08:00
parent 9fc9967306
commit af27768e50
3 changed files with 151 additions and 36 deletions
+5 -1
View File
@@ -278,9 +278,13 @@ class QQGroupDailyAnalysis(Star):
yield event.plain_result("🔄 开始安装 PDF 功能依赖,请稍候...") yield event.plain_result("🔄 开始安装 PDF 功能依赖,请稍候...")
try: try:
# 使用模块化的PDF安装器 # 安装 pyppeteer
result = await PDFInstaller.install_pyppeteer(config_manager) result = await PDFInstaller.install_pyppeteer(config_manager)
yield event.plain_result(result) yield event.plain_result(result)
# 提供系统依赖安装指导
system_deps_result = await PDFInstaller.install_system_deps()
yield event.plain_result(system_deps_result)
except Exception as e: except Exception as e:
logger.error(f"安装 PDF 依赖失败: {e}", exc_info=True) logger.error(f"安装 PDF 依赖失败: {e}", exc_info=True)
+107 -35
View File
@@ -11,6 +11,7 @@ from pathlib import Path
from astrbot.api import logger from astrbot.api import logger
from .templates import HTMLTemplates from .templates import HTMLTemplates
from ..visualization.activity_charts import ActivityVisualizer from ..visualization.activity_charts import ActivityVisualizer
import asyncio
class ReportGenerator: class ReportGenerator:
@@ -297,7 +298,7 @@ class ReportGenerator:
# 尝试启动浏览器,如果 Chromium 不存在会自动下载 # 尝试启动浏览器,如果 Chromium 不存在会自动下载
logger.info("启动浏览器进行 PDF 转换") logger.info("启动浏览器进行 PDF 转换")
# 配置浏览器启动参数,避免 Chromium 下载问题 # 配置浏览器启动参数,提高稳定性,避免意外关闭
launch_options = { launch_options = {
'headless': True, 'headless': True,
'args': [ 'args': [
@@ -307,61 +308,132 @@ class ReportGenerator:
'--disable-gpu', '--disable-gpu',
'--no-first-run', '--no-first-run',
'--disable-extensions', '--disable-extensions',
'--disable-default-apps' '--disable-default-apps',
'--disable-background-timer-throttling',
'--disable-backgrounding-occluded-windows',
'--disable-renderer-backgrounding',
'--disable-features=TranslateUI',
'--disable-ipc-flooding-protection',
'--disable-background-networking',
'--enable-features=NetworkService,NetworkServiceInProcess',
'--force-color-profile=srgb',
'--metrics-recording-only',
'--disable-breakpad',
'--disable-component-extensions-with-background-pages',
'--disable-features=Translate,BackForwardCache,AcceptCHFrame,AvoidUnnecessaryBeforeUnloadCheckSync',
'--enable-automation',
'--password-store=basic',
'--use-mock-keychain',
'--export-tagged-pdf'
] ]
} }
# 如果是 Windows 系统,尝试使用系统 Chrome # 检测系统 Chrome/Chromium 路径
chrome_paths = []
if sys.platform.startswith('win'): if sys.platform.startswith('win'):
# 常见的 Chrome 安装路径 # Windows 系统 Chrome 安装路径
chrome_paths = [ chrome_paths = [
r"C:\Program Files\Google\Chrome\Application\chrome.exe", r"C:\Program Files\Google\Chrome\Application\chrome.exe",
r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe", r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe",
r"C:\Users\{}\AppData\Local\Google\Chrome\Application\chrome.exe".format(os.environ.get('USERNAME', '')), r"C:\Users\{}\AppData\Local\Google\Chrome\Application\chrome.exe".format(os.environ.get('USERNAME', '')),
] ]
elif sys.platform.startswith('linux'):
# Linux 系统 Chrome/Chromium 路径
chrome_paths = [
'/usr/bin/google-chrome',
'/usr/bin/google-chrome-stable',
'/usr/bin/chromium',
'/usr/bin/chromium-browser',
'/snap/bin/chromium',
]
elif sys.platform.startswith('darwin'):
# macOS 系统 Chrome 路径
chrome_paths = [
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
'/Applications/Chromium.app/Contents/MacOS/Chromium',
]
for chrome_path in chrome_paths: # 查找可用的浏览器
if Path(chrome_path).exists(): for chrome_path in chrome_paths:
launch_options['executablePath'] = chrome_path if Path(chrome_path).exists():
logger.info(f"使用系统 Chrome: {chrome_path}") launch_options['executablePath'] = chrome_path
break logger.info(f"使用系统浏览器: {chrome_path}")
break
browser = await launch(**launch_options) # 尝试启动浏览器,最多重试3次
page = await browser.newPage() max_retries = 3
for attempt in range(max_retries):
try:
logger.info(f"尝试启动浏览器 (第 {attempt + 1} 次)")
browser = await launch(**launch_options)
break
except Exception as e:
logger.warning(f"{attempt + 1} 次启动浏览器失败: {e}")
if attempt < max_retries - 1:
await asyncio.sleep(2) # 等待2秒后重试
else:
raise e
# 设置页面内容 (pyppeteer 1.0.2 版本的 API)
await page.setContent(html_content)
# 等待页面加载完成
try: try:
await page.waitForSelector('body', {'timeout': 10000}) page = await browser.newPage()
except Exception:
# 如果等待失败,继续执行(可能页面已经加载完成)
pass
# 导出 PDF # 设置页面内容 (pyppeteer 1.0.2 版本的 API)
await page.pdf({ await page.setContent(html_content)
'path': output_path,
'format': 'A4', # 等待页面加载完成
'printBackground': True, try:
'margin': { await page.waitForSelector('body', {'timeout': 15000})
'top': '10mm', except Exception:
'right': '10mm', # 如果等待失败,继续执行(可能页面已经加载完成)
'bottom': '10mm', logger.warning("等待页面加载超时,继续执行")
'left': '10mm' pass
},
'scale': 0.8
})
await browser.close() # 等待额外时间确保页面完全渲染
logger.info(f"PDF 生成成功: {output_path}") await asyncio.sleep(2)
return True
# 导出 PDF
await page.pdf({
'path': output_path,
'format': 'A4',
'printBackground': True,
'margin': {
'top': '10mm',
'right': '10mm',
'bottom': '10mm',
'left': '10mm'
},
'scale': 0.8,
'displayHeaderFooter': False,
'preferCSSPageSize': True
})
logger.info(f"PDF 生成成功: {output_path}")
return True
finally:
# 确保浏览器被关闭
try:
await browser.close()
except Exception as e:
logger.warning(f"关闭浏览器时出错: {e}")
except Exception as e: except Exception as e:
error_msg = str(e) error_msg = str(e)
if "Chromium downloadable not found" in error_msg: if "Chromium downloadable not found" in error_msg:
logger.error("Chromium 下载失败,建议安装 pyppeteer2 或使用系统 Chrome") logger.error("Chromium 下载失败,建议安装系统 Chrome/Chromium")
logger.info("💡 Linux 系统建议: sudo apt-get install chromium-browser 或 sudo yum install chromium")
elif "No usable sandbox" in error_msg: elif "No usable sandbox" in error_msg:
logger.error("沙盒权限问题,已尝试禁用沙盒") logger.error("沙盒权限问题,已尝试禁用沙盒")
elif "Connection refused" in error_msg or "connect" in error_msg.lower():
logger.error("浏览器连接失败,请检查系统资源或尝试重启")
elif "executablePath" in error_msg and "not found" in error_msg:
logger.error("未找到系统浏览器,请安装 Chrome 或 Chromium")
logger.info("💡 安装建议: sudo apt-get install chromium-browser (Ubuntu/Debian) 或 sudo yum install chromium (CentOS/RHEL)")
elif "Browser closed unexpectedly" in error_msg:
logger.error("浏览器意外关闭,可能是由于内存不足或系统资源限制")
logger.info("💡 建议: 检查系统内存,或重启 AstrBot 后重试")
else: else:
logger.error(f"HTML 转 PDF 失败: {e}") logger.error(f"HTML 转 PDF 失败: {e}")
logger.info("💡 可以尝试使用 /安装PDF 命令重新安装依赖,或检查系统日志获取更多信息")
return False return False
+39
View File
@@ -47,6 +47,45 @@ class PDFInstaller:
logger.error(f"安装 pyppeteer 时出错: {e}") logger.error(f"安装 pyppeteer 时出错: {e}")
return f"❌ 安装过程中出错: {str(e)}" return f"❌ 安装过程中出错: {str(e)}"
@staticmethod
async def install_system_deps():
"""安装系统依赖(仅提供指导)"""
try:
logger.info("提供系统依赖安装指导...")
if sys.platform.startswith('linux'):
return """💡 Linux 系统依赖安装指导:
1. 安装 Chrome/Chromium 浏览器:
Ubuntu/Debian: sudo apt-get install chromium-browser
CentOS/RHEL: sudo yum install chromium
Arch Linux: sudo pacman -S chromium
2. 安装完成后,重启 AstrBot
3. 如果仍然有问题,请检查系统日志"""
elif sys.platform.startswith('win'):
return """💡 Windows 系统依赖安装指导:
1. 安装 Google Chrome 浏览器
2. 重启 AstrBot
3. 如果仍然有问题,请检查系统日志"""
elif sys.platform.startswith('darwin'):
return """💡 macOS 系统依赖安装指导:
1. 安装 Google Chrome 浏览器
2. 重启 AstrBot
3. 如果仍然有问题,请检查系统日志"""
else:
return "💡 请安装 Chrome 或 Chromium 浏览器,然后重启 AstrBot"
except Exception as e:
logger.error(f"提供系统依赖指导时出错: {e}")
return f"❌ 提供指导时出错: {str(e)}"
@staticmethod @staticmethod
def get_pdf_status(config_manager) -> str: def get_pdf_status(config_manager) -> str:
"""获取PDF功能状态""" """获取PDF功能状态"""