feat(chv): 完整 Chromium 改装数据盘
根盘只剩 ~600MB,playwright install chromium 装根盘 109 秒后 ENOSPC 失败(2026-11-08 构建 #8 驱动实证)。完整版改装数据盘 /home/chv/workspace/.chv-browsers(9.3G 空闲),仍不 bake 进 base、按需;full 引擎启动时直接传 executable_path(channel 与 executable_path 互斥,且驱动进程启动时才读 env,运行中改 env 无效);status 增 full_browsers_dir,mount 失败报错给数据盘 安装命令。驱动的安装步同步改数据盘并 chown 给 chv-browser。
This commit is contained in:
1 parent
435664925e
commit
9d986becaf
2 files changed
+135
-27
No files matched your search
+69
-18
@@ -19,6 +19,9 @@
|
||||
Chromium 按需挂载(op `mount_full`:切到 full 引擎)、用完
|
||||
卸载(op `unmount_full`:切回 shell)。完整版不 bake 进 base;
|
||||
未安装时 mount_full 明确报错并回落 shell,不硬凑通过。
|
||||
完整版装在数据盘 /home/chv/workspace/.chv-browsers(根盘只剩
|
||||
约 600MB,装根盘会 ENOSPC;full 引擎启动时直接传
|
||||
executable_path,不走 channel/env)。
|
||||
* 截图:JPEG quality 70,viewport 宽封顶 1280(上下文创建时
|
||||
定死 1280x800);diff_only 时与上一帧比对,字节相同即不落盘、
|
||||
返回 changed=false(整帧相同跳过;变化区裁剪是阶段 4 的事,
|
||||
@@ -77,6 +80,11 @@ PLAYWRIGHT_BROWSERS_PATH = "/usr/local/share/ms-playwright"
|
||||
WORKSPACE_MOUNT = "/home/chv/workspace"
|
||||
PROFILE_DATA_DIR = "/home/chv/workspace/.chv-browser-profile"
|
||||
PROFILE_FALLBACK_DIR = "/var/lib/chv-browser/profile"
|
||||
# 完整 Chromium 不 bake 进 base、不装根盘:根盘只剩 ~600MB,
|
||||
# playwright install chromium 会 ENOSPC(2026-11-08 构建 #8 驱动
|
||||
# 实证:109 秒后写满失败)。按需装到数据盘(9.3G 空闲),用完可删。
|
||||
FULL_BROWSERS_DATA_DIR = "/home/chv/workspace/.chv-browsers"
|
||||
FULL_BROWSERS_FALLBACK_DIR = "/var/lib/chv-browser/browsers"
|
||||
CDP_PORT_DEFAULT = 9222
|
||||
# 截图规格(方案阶段 4 口径,阶段 3 先落地编码与尺寸两项):
|
||||
# JPEG quality 70,宽封顶 1280(上下文 viewport 定死 1280x800)。
|
||||
@@ -128,19 +136,39 @@ def resolve_profile_dir(env=None, ismount=None):
|
||||
return PROFILE_FALLBACK_DIR, False
|
||||
|
||||
|
||||
def full_chromium_present(browsers_path=None):
|
||||
"""完整 Chromium 是否已装:ms-playwright 下有 chromium-*
|
||||
目录且含 chrome-linux/chrome(headless-shell 目录不算)。"""
|
||||
def full_chromium_executable(browsers_path=None):
|
||||
"""完整 Chromium 可执行文件路径(数据盘安装目录下),未装返回 None。
|
||||
headless-shell 目录不算。"""
|
||||
base = browsers_path or os.environ.get(
|
||||
"PLAYWRIGHT_BROWSERS_PATH", PLAYWRIGHT_BROWSERS_PATH)
|
||||
for cand in glob.glob(os.path.join(base, "chromium-*")):
|
||||
if os.path.basename(cand).startswith(
|
||||
"chromium_headless_shell"):
|
||||
continue
|
||||
if os.path.isfile(os.path.join(
|
||||
cand, "chrome-linux", "chrome")):
|
||||
return True
|
||||
return False
|
||||
exe = os.path.join(cand, "chrome-linux", "chrome")
|
||||
if os.path.isfile(exe):
|
||||
return exe
|
||||
return None
|
||||
|
||||
|
||||
def full_chromium_present(browsers_path=None):
|
||||
"""完整 Chromium 是否已装:ms-playwright 下有 chromium-*
|
||||
目录且含 chrome-linux/chrome(headless-shell 目录不算)。"""
|
||||
return full_chromium_executable(browsers_path) is not None
|
||||
|
||||
|
||||
def resolve_full_browsers_dir(profile_on_data_disk, env=None):
|
||||
"""完整 Chromium 的安装目录:profile 在数据盘上时跟 profile 走
|
||||
数据盘(/home/chv/workspace/.chv-browsers),否则回落本机盘。
|
||||
env 显式指定(CHV_BROWSER_FULL_BROWSERS_DIR)时优先。"""
|
||||
if env is None:
|
||||
env = os.environ
|
||||
explicit = (env.get("CHV_BROWSER_FULL_BROWSERS_DIR") or "").strip()
|
||||
if explicit:
|
||||
return explicit
|
||||
if profile_on_data_disk:
|
||||
return FULL_BROWSERS_DATA_DIR
|
||||
return FULL_BROWSERS_FALLBACK_DIR
|
||||
|
||||
|
||||
def truncate_a11y(node, max_nodes=MAX_A11Y_NODES,
|
||||
@@ -218,7 +246,7 @@ class BrowserAgent:
|
||||
_load_playwright()。"""
|
||||
|
||||
def __init__(self, playwright_factory=None, shots_dir=SHOTS_DIR,
|
||||
profile_dir=None, cdp_port=None):
|
||||
profile_dir=None, cdp_port=None, full_browsers_dir=None):
|
||||
self._factory = playwright_factory or _load_playwright
|
||||
self._shots_dir = shots_dir
|
||||
if profile_dir:
|
||||
@@ -228,6 +256,9 @@ class BrowserAgent:
|
||||
else:
|
||||
self._profile_dir, self._profile_on_data_disk = \
|
||||
resolve_profile_dir()
|
||||
self._full_browsers_dir = (
|
||||
full_browsers_dir or resolve_full_browsers_dir(
|
||||
self._profile_on_data_disk))
|
||||
try:
|
||||
self._cdp_port = int(cdp_port or os.environ.get(
|
||||
"CHV_BROWSER_CDP_PORT") or CDP_PORT_DEFAULT)
|
||||
@@ -279,7 +310,18 @@ class BrowserAgent:
|
||||
"--remote-debugging-address=0.0.0.0"],
|
||||
}
|
||||
if engine == "full":
|
||||
kwargs["channel"] = "chromium"
|
||||
# 完整浏览器在数据盘安装目录里:直接给可执行文件路径,
|
||||
# 不用 channel(channel 与 executable_path 互斥,且
|
||||
# channel 要走 PLAYWRIGHT_BROWSERS_PATH——驱动进程
|
||||
# 启动时才读 env,运行中改 env 对已起的驱动无效)。
|
||||
exe = full_chromium_executable(
|
||||
self._full_browsers_dir)
|
||||
if exe is None:
|
||||
self._context = None
|
||||
return False, ("完整 Chromium 未安装(%s 下未见 "
|
||||
"chromium-*/chrome-linux/chrome)"
|
||||
% self._full_browsers_dir)
|
||||
kwargs["executable_path"] = exe
|
||||
launch = getattr(self._entered.chromium,
|
||||
"launch_persistent_context", None)
|
||||
if launch is None:
|
||||
@@ -439,7 +481,9 @@ class BrowserAgent:
|
||||
"sessions": [s["session"] for s in
|
||||
self.sessions()["sessions"]],
|
||||
"mounted_full": self._mounted_full,
|
||||
"full_present": full_chromium_present()}
|
||||
"full_present": full_chromium_present(
|
||||
self._full_browsers_dir),
|
||||
"full_browsers_dir": self._full_browsers_dir}
|
||||
|
||||
def cdp(self):
|
||||
ok, err = self._ensure_browser()
|
||||
@@ -457,12 +501,16 @@ class BrowserAgent:
|
||||
return {"ok": False,
|
||||
"error": "未知引擎:%r(只允许 shell/full)"
|
||||
% (engine,)}
|
||||
if engine == "full" and not full_chromium_present():
|
||||
if engine == "full" and not full_chromium_present(
|
||||
self._full_browsers_dir):
|
||||
return {"ok": False,
|
||||
"error": "完整 Chromium 未安装(未见 chromium-* "
|
||||
"chrome-linux/chrome),回落 headless-shell;"
|
||||
"先跑 python3 -m playwright install "
|
||||
"chromium 安装",
|
||||
"error": "完整 Chromium 未安装(%s 下未见 "
|
||||
"chromium-*/chrome-linux/chrome),回落 "
|
||||
"headless-shell;先跑 PLAYWRIGHT_BROWSERS_"
|
||||
"PATH=%s python3 -m playwright install "
|
||||
"chromium 安装(装数据盘,根盘空间不够)"
|
||||
% (self._full_browsers_dir,
|
||||
self._full_browsers_dir),
|
||||
"engine": self._engine, "fallback": True,
|
||||
"mounted_full": False}
|
||||
ok, err = self._ensure_browser(engine)
|
||||
@@ -482,11 +530,14 @@ class BrowserAgent:
|
||||
"profile": self._profile_dir}
|
||||
|
||||
def mount_full(self):
|
||||
if not full_chromium_present():
|
||||
if not full_chromium_present(self._full_browsers_dir):
|
||||
return {"ok": False,
|
||||
"error": "完整 Chromium 未安装,无法挂载(回落 "
|
||||
"headless-shell 继续工作);先跑 python3 "
|
||||
"-m playwright install chromium 安装",
|
||||
"headless-shell 继续工作);先跑 "
|
||||
"PLAYWRIGHT_BROWSERS_PATH=%s python3 -m "
|
||||
"playwright install chromium 安装(装数据"
|
||||
"盘,根盘空间不够)"
|
||||
% self._full_browsers_dir,
|
||||
"engine": self._engine, "fallback": True,
|
||||
"mounted_full": False}
|
||||
return self.switch_engine("full")
|
||||
|
||||
Reference in new issue
Block a user