diff --git a/utils/markdown_to_image.py b/utils/markdown_to_image.py index e45b9f8..1737ae8 100644 --- a/utils/markdown_to_image.py +++ b/utils/markdown_to_image.py @@ -2,42 +2,54 @@ import subprocess import time import markdown from pathlib import Path + +import psutil from playwright.async_api import async_playwright import os import asyncio from loguru import logger -async def safe_close_browser(browser, timeout: float = 5.0) -> None: +async def safe_close_browser(browser, timeout: float = 4.0) -> None: if not browser: return - # Close contexts first to reduce hanging risk. - try: - for ctx in browser.contexts: + # 1. 先尝试关闭所有 page(最重要) + for context in browser.contexts[:]: # 复制列表防止修改中迭代 + for page in context.pages[:]: try: - await asyncio.wait_for(ctx.close(), timeout=timeout) - except Exception: + await asyncio.wait_for(page.close(), timeout=1.5) + except: pass - except Exception: - pass + try: + await asyncio.wait_for(context.close(), timeout=timeout) + except: + pass - # Then close browser with timeout. + # 2. 尝试优雅关闭 browser try: await asyncio.wait_for(browser.close(), timeout=timeout) + logger.debug("browser closed gracefully") return - except asyncio.TimeoutError: - logger.warning("Browser close timed out, killing process") - except Exception as e: - logger.warning(f"Browser close failed, killing process: {e}") + except (asyncio.TimeoutError, Exception) as e: + logger.warning(f"browser.close failed: {e}") - # Final fallback: kill underlying process. - try: - proc = browser.process - if proc: - proc.kill() - except Exception: - pass + # 3. 强制杀进程树(最关键一步) + if browser.process and browser.process.pid: + try: + parent = psutil.Process(browser.process.pid) + children = parent.children(recursive=True) + for child in children: + try: + child.kill() + except psutil.NoSuchProcess: + pass + parent.kill() + # 可选:等待一小会儿确认退出 + psutil.wait_procs([parent] + children, timeout=3) + logger.debug("process tree killed") + except (psutil.NoSuchProcess, Exception) as e: + logger.warning(f"force kill failed: {e}") # ================= 样式与 HTML 处理 =================