113 lines
3.3 KiB
Python
113 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
from __future__ import annotations
|
||
|
||
import threading
|
||
import time
|
||
from typing import Optional
|
||
|
||
from app_logger import get_logger
|
||
from browser_installer import check_and_install_browser
|
||
from playwright_automation import PlaywrightBrowserManager
|
||
|
||
logger = get_logger("browser_manager")
|
||
|
||
_browser_manager: Optional[PlaywrightBrowserManager] = None
|
||
_lock = threading.Lock()
|
||
_cond = threading.Condition(_lock)
|
||
_init_in_progress = False
|
||
_init_error: Optional[str] = None
|
||
_init_thread: Optional[threading.Thread] = None
|
||
|
||
|
||
def get_browser_manager() -> Optional[PlaywrightBrowserManager]:
|
||
return _browser_manager
|
||
|
||
|
||
def is_browser_manager_ready() -> bool:
|
||
return _browser_manager is not None
|
||
|
||
|
||
def get_browser_manager_init_error() -> Optional[str]:
|
||
return _init_error
|
||
|
||
|
||
def init_browser_manager(*, block: bool = True, timeout: Optional[float] = None) -> bool:
|
||
global _browser_manager
|
||
global _init_in_progress, _init_error
|
||
|
||
deadline = time.monotonic() + float(timeout) if timeout is not None else None
|
||
|
||
with _cond:
|
||
if _browser_manager is not None:
|
||
return True
|
||
|
||
if _init_in_progress:
|
||
if not block:
|
||
return False
|
||
while _init_in_progress:
|
||
if deadline is None:
|
||
_cond.wait(timeout=0.5)
|
||
continue
|
||
remaining = deadline - time.monotonic()
|
||
if remaining <= 0:
|
||
break
|
||
_cond.wait(timeout=min(0.5, remaining))
|
||
return _browser_manager is not None
|
||
|
||
_init_in_progress = True
|
||
_init_error = None
|
||
|
||
ok = False
|
||
error: Optional[str] = None
|
||
manager: Optional[PlaywrightBrowserManager] = None
|
||
|
||
try:
|
||
logger.info("正在初始化Playwright浏览器管理器...")
|
||
if not check_and_install_browser(log_callback=lambda msg, account_id=None: logger.info(str(msg))):
|
||
error = "浏览器环境检查失败"
|
||
logger.error("浏览器环境检查失败!")
|
||
ok = False
|
||
else:
|
||
manager = PlaywrightBrowserManager(
|
||
headless=True,
|
||
log_callback=lambda msg, account_id=None: logger.info(str(msg)),
|
||
)
|
||
ok = True
|
||
logger.info("Playwright浏览器管理器创建成功!")
|
||
except Exception as exc:
|
||
error = f"{type(exc).__name__}: {exc}"
|
||
logger.exception("初始化Playwright浏览器管理器时发生异常")
|
||
ok = False
|
||
|
||
with _cond:
|
||
if ok and manager is not None:
|
||
_browser_manager = manager
|
||
else:
|
||
_init_error = error or "初始化失败"
|
||
_init_in_progress = False
|
||
_cond.notify_all()
|
||
|
||
return ok
|
||
|
||
|
||
def init_browser_manager_async() -> None:
|
||
"""异步初始化浏览器环境,避免阻塞 Web 请求/服务启动。"""
|
||
global _init_thread
|
||
|
||
def _worker():
|
||
try:
|
||
init_browser_manager(block=True)
|
||
except Exception:
|
||
logger.exception("异步初始化浏览器管理器失败")
|
||
|
||
with _cond:
|
||
if _browser_manager is not None:
|
||
return
|
||
if _init_thread and _init_thread.is_alive():
|
||
return
|
||
if _init_in_progress:
|
||
return
|
||
_init_thread = threading.Thread(target=_worker, daemon=True, name="browser-manager-init")
|
||
_init_thread.start()
|