mirror of
https://github.com/zhayujie/chatgpt-on-wechat.git
synced 2026-06-02 00:57:41 +08:00
185 lines
6.2 KiB
Python
185 lines
6.2 KiB
Python
"""cow install-browser - Install Playwright + Chromium for the browser tool."""
|
|
|
|
import os
|
|
import sys
|
|
import subprocess
|
|
|
|
import click
|
|
|
|
PLAYWRIGHT_VERSION = "1.52.0"
|
|
PLAYWRIGHT_LEGACY_VERSION = "1.28.0"
|
|
GLIBC_THRESHOLD = (2, 28)
|
|
CHINA_MIRROR = "https://registry.npmmirror.com/-/binary/playwright"
|
|
|
|
|
|
def _has_display() -> bool:
|
|
"""Check if a graphical display is available (Linux only)."""
|
|
return bool(os.environ.get("DISPLAY") or os.environ.get("WAYLAND_DISPLAY"))
|
|
|
|
|
|
def _is_headless_linux() -> bool:
|
|
return sys.platform == "linux" and not _has_display()
|
|
|
|
|
|
def _get_installed_version() -> str:
|
|
try:
|
|
out = subprocess.check_output(
|
|
[sys.executable, "-c", "import playwright; print(playwright.__version__)"],
|
|
stderr=subprocess.DEVNULL,
|
|
)
|
|
return out.decode().strip()
|
|
except Exception:
|
|
return ""
|
|
|
|
|
|
def _version_tuple(v: str):
|
|
try:
|
|
return tuple(int(x) for x in v.split(".")[:3])
|
|
except (ValueError, AttributeError):
|
|
return (0, 0, 0)
|
|
|
|
|
|
def _get_glibc_version():
|
|
if sys.platform != "linux":
|
|
return None
|
|
try:
|
|
import ctypes
|
|
libc = ctypes.CDLL("libc.so.6")
|
|
gnu_get_libc_version = libc.gnu_get_libc_version
|
|
gnu_get_libc_version.restype = ctypes.c_char_p
|
|
ver = gnu_get_libc_version().decode()
|
|
parts = ver.split(".")
|
|
return (int(parts[0]), int(parts[1]))
|
|
except Exception:
|
|
return None
|
|
|
|
|
|
def _is_china_network() -> bool:
|
|
try:
|
|
out = subprocess.check_output(
|
|
[sys.executable, "-m", "pip", "config", "get", "global.index-url"],
|
|
stderr=subprocess.DEVNULL,
|
|
)
|
|
url = out.decode().strip().lower()
|
|
return any(kw in url for kw in ("tsinghua", "aliyun", "npmmirror", "douban", "ustc", "huawei", "tencentyun"))
|
|
except Exception:
|
|
return False
|
|
|
|
|
|
def _pip_install(package_spec: str) -> int:
|
|
"""Install a package, retrying with --user on permission failure."""
|
|
python = sys.executable
|
|
ret = subprocess.call([python, "-m", "pip", "install", package_spec])
|
|
if ret != 0:
|
|
click.echo(" Retrying with --user flag...")
|
|
ret = subprocess.call([python, "-m", "pip", "install", "--user", package_spec])
|
|
return ret
|
|
|
|
|
|
@click.command("install-browser")
|
|
def install_browser():
|
|
"""Install browser tool dependencies (Playwright + Chromium)."""
|
|
python = sys.executable
|
|
legacy_mode = False
|
|
|
|
# Determine playwright version based on glibc
|
|
glibc = _get_glibc_version()
|
|
if glibc and glibc < GLIBC_THRESHOLD:
|
|
legacy_mode = True
|
|
glibc_str = f"{glibc[0]}.{glibc[1]}"
|
|
click.echo(click.style(
|
|
f"glibc {glibc_str} detected (< 2.28). "
|
|
f"Will install playwright {PLAYWRIGHT_LEGACY_VERSION} for compatibility.",
|
|
fg="yellow",
|
|
))
|
|
click.echo(click.style(
|
|
" Note: upgrade your OS for full browser tool support.",
|
|
fg="yellow",
|
|
))
|
|
click.echo()
|
|
|
|
target_version = PLAYWRIGHT_LEGACY_VERSION if legacy_mode else PLAYWRIGHT_VERSION
|
|
|
|
# Step 1: Install playwright package
|
|
click.echo(click.style("[1/3] Installing playwright Python package...", fg="yellow"))
|
|
ret = _pip_install(f"playwright=={target_version}")
|
|
if ret != 0:
|
|
click.echo(click.style("Failed to install playwright package.", fg="red"))
|
|
raise SystemExit(1)
|
|
|
|
installed = _get_installed_version()
|
|
if installed:
|
|
click.echo(click.style(f" playwright {installed} installed.", fg="green"))
|
|
click.echo()
|
|
|
|
# Step 2: System dependencies (Linux only)
|
|
if sys.platform == "linux":
|
|
click.echo(click.style("[2/3] Installing system dependencies (Linux)...", fg="yellow"))
|
|
ret = subprocess.call([python, "-m", "playwright", "install-deps", "chromium"])
|
|
if ret != 0:
|
|
click.echo(click.style(
|
|
" Could not auto-install system deps (may need sudo).\n"
|
|
f" Run manually: sudo {python} -m playwright install-deps chromium",
|
|
fg="yellow",
|
|
))
|
|
else:
|
|
click.echo(click.style(f"[2/3] Skipping system deps (not needed on {sys.platform}).", fg="yellow"))
|
|
click.echo()
|
|
|
|
# Step 3: Install Chromium
|
|
click.echo(click.style("[3/3] Installing Chromium browser...", fg="yellow"))
|
|
cmd = [python, "-m", "playwright", "install", "chromium"]
|
|
|
|
# --only-shell requires playwright >= 1.57
|
|
if _is_headless_linux() and not legacy_mode:
|
|
ver = _version_tuple(installed or "")
|
|
if ver >= (1, 57, 0):
|
|
cmd.append("--only-shell")
|
|
click.echo(" (headless shell for Linux server)")
|
|
else:
|
|
click.echo(" (full Chromium)")
|
|
elif sys.platform == "linux" and _has_display():
|
|
click.echo(" (full browser for Linux desktop)")
|
|
|
|
# Use China mirror if pip is configured with a domestic index
|
|
env = os.environ.copy()
|
|
use_mirror = _is_china_network()
|
|
if use_mirror:
|
|
env["PLAYWRIGHT_DOWNLOAD_HOST"] = CHINA_MIRROR
|
|
click.echo(f" (using China mirror: {CHINA_MIRROR})")
|
|
|
|
ret = subprocess.call(cmd, env=env)
|
|
|
|
# Fallback: if mirror download failed, retry with official CDN
|
|
if ret != 0 and use_mirror:
|
|
click.echo(click.style(
|
|
" Mirror download failed, retrying with official CDN...",
|
|
fg="yellow",
|
|
))
|
|
env_no_mirror = os.environ.copy()
|
|
env_no_mirror.pop("PLAYWRIGHT_DOWNLOAD_HOST", None)
|
|
ret = subprocess.call(cmd, env=env_no_mirror)
|
|
|
|
if ret != 0:
|
|
click.echo(click.style("Failed to install Chromium.", fg="red"))
|
|
raise SystemExit(1)
|
|
|
|
# Quick smoke test
|
|
click.echo()
|
|
click.echo("Verifying browser installation...")
|
|
ret = subprocess.call(
|
|
[python, "-c", "from playwright.sync_api import sync_playwright; print('OK')"],
|
|
stderr=subprocess.DEVNULL,
|
|
)
|
|
if ret != 0:
|
|
click.echo(click.style(
|
|
" Warning: playwright import failed. Browser tool may not work on this system.\n"
|
|
" Consider upgrading your OS or using Docker.",
|
|
fg="yellow",
|
|
))
|
|
else:
|
|
click.echo(click.style(" Verification passed.", fg="green"))
|
|
|
|
click.echo()
|
|
click.echo(click.style("Browser tool ready! Restart CowAgent to enable it.", fg="green"))
|