Auto headless + Xvfb for headless servers
Enable Chromium on machines without DISPLAY: Python auto headless with Xvfb hints, and start.sh installs/starts Xvfb (xvfb-run or persistent :99) so registration can run headed under a virtual display.
This commit is contained in:
1 parent
4feac70c91
commit
cd4a2351ae
8 files changed
+536
-21
No files matched your search
+78
-1
@@ -33,9 +33,11 @@ _orig_create_browser_options = reg.create_browser_options
|
||||
|
||||
|
||||
def _patched_create_browser_options():
|
||||
# Prefer original factory (proxy + CHROMIUM_SLIM_FLAGS + extension)
|
||||
# Prefer original factory (proxy + CHROMIUM_SLIM_FLAGS + extension + auto-headless)
|
||||
used_orig = False
|
||||
try:
|
||||
opts = _orig_create_browser_options()
|
||||
used_orig = True
|
||||
except Exception:
|
||||
from DrissionPage import ChromiumOptions
|
||||
|
||||
@@ -107,6 +109,30 @@ def _patched_create_browser_options():
|
||||
opts.add_extension(ext_path)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Fallback path only: original factory already applied headless when used_orig.
|
||||
if not used_orig:
|
||||
try:
|
||||
apply = getattr(reg, "apply_headless_to_options", None)
|
||||
should = getattr(reg, "should_use_headless", None)
|
||||
if callable(apply) and callable(should):
|
||||
use_hl = should(None, log_hint=True)
|
||||
apply(opts, use_hl)
|
||||
print(f"[browser] headless={use_hl} (fallback options)", flush=True)
|
||||
else:
|
||||
# Minimal fallback without helpers
|
||||
has_disp = sys.platform == "win32" or bool(
|
||||
(os.environ.get("DISPLAY") or "").strip()
|
||||
or (os.environ.get("WAYLAND_DISPLAY") or "").strip()
|
||||
)
|
||||
if not has_disp:
|
||||
try:
|
||||
opts.headless(True)
|
||||
except Exception:
|
||||
opts.set_argument("--headless=new")
|
||||
print("[browser] headless=True (no DISPLAY, minimal fallback)", flush=True)
|
||||
except Exception as exc:
|
||||
print(f"[browser] headless apply failed: {exc}", flush=True)
|
||||
return opts
|
||||
|
||||
|
||||
@@ -573,6 +599,16 @@ def main() -> int:
|
||||
parser.add_argument("--browser-recycle-every", type=int, default=25, help="复用 N 次后完整回收")
|
||||
parser.add_argument("--cookie-snapshot", action="store_true", help="注册成功写 cookie 快照(默认关,fast)")
|
||||
parser.add_argument("--inline-mint", action="store_true", help="强制注册线程内联 mint(调试用)")
|
||||
parser.add_argument(
|
||||
"--headless",
|
||||
action="store_true",
|
||||
help="强制无头浏览器(无 DISPLAY 时默认也会自动 headless)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--headed",
|
||||
action="store_true",
|
||||
help="强制有头浏览器(需 DISPLAY;无显示时仍会回退 headless 并提示 Xvfb)",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
reg.load_config()
|
||||
@@ -580,6 +616,19 @@ def main() -> int:
|
||||
threads = max(1, min(args.threads, 10))
|
||||
fast = bool(args.fast) and not bool(args.no_fast)
|
||||
|
||||
# Headless preference: CLI > config/env/auto (no DISPLAY → headless)
|
||||
if args.headless and args.headed:
|
||||
print("[!] --headless 与 --headed 互斥,忽略 --headed", flush=True)
|
||||
args.headed = False
|
||||
if args.headless:
|
||||
cfg0["browser_headless"] = True
|
||||
if isinstance(getattr(reg, "config", None), dict):
|
||||
reg.config["browser_headless"] = True
|
||||
elif args.headed:
|
||||
cfg0["browser_headless"] = False
|
||||
if isinstance(getattr(reg, "config", None), dict):
|
||||
reg.config["browser_headless"] = False
|
||||
|
||||
mint_workers = resolve_mint_workers(
|
||||
cli_value=args.mint_workers,
|
||||
threads=threads,
|
||||
@@ -639,6 +688,28 @@ def main() -> int:
|
||||
print("[*] 所有账号已完成,无需继续(可用 --extra N 再注册)", flush=True)
|
||||
return 0
|
||||
|
||||
# Surface display/headless decision early (before TabPool cold-start)
|
||||
try:
|
||||
will_hl = reg.should_use_headless(None, log_hint=True)
|
||||
has_disp = reg.has_display()
|
||||
print(
|
||||
f"[*] browser mode: {'headless' if will_hl else 'headed'} "
|
||||
f"(DISPLAY={os.environ.get('DISPLAY', '')!r} has_display={has_disp})",
|
||||
flush=True,
|
||||
)
|
||||
# Keep cpa browser fallback aligned when config left cpa_headless unset/false
|
||||
# and we are on a headless host — only auto-enable if user did not force headed.
|
||||
if will_hl and not args.headed:
|
||||
if "cpa_headless" not in cfg0 or cfg0.get("cpa_headless") is False:
|
||||
# Auto-headless host: enable cpa headless unless explicitly headed CLI
|
||||
if not has_disp:
|
||||
cfg0["cpa_headless"] = True
|
||||
if isinstance(getattr(reg, "config", None), dict):
|
||||
reg.config["cpa_headless"] = True
|
||||
print("[*] cpa_headless 自动=true(无 DISPLAY;协议 mint 仍优先纯 HTTP)", flush=True)
|
||||
except Exception as exc:
|
||||
print(f"[!] headless probe failed: {exc}", flush=True)
|
||||
|
||||
log_thread = threading.Thread(target=_log_writer, daemon=True)
|
||||
log_thread.start()
|
||||
|
||||
@@ -646,6 +717,12 @@ def main() -> int:
|
||||
reg.TabPool.init(reg.create_browser_options, log_callback=lambda m: log(0, m))
|
||||
except Exception as exc:
|
||||
print(f"[!] 浏览器初始化失败: {exc}", flush=True)
|
||||
if not reg.has_display():
|
||||
print(
|
||||
"[!] 无 DISPLAY 时若仍失败,请检查 Chrome/Chromium 是否安装,并尝试:\n"
|
||||
" xvfb-run -a uv run python -u register_cli.py --extra 1 --threads 1",
|
||||
flush=True,
|
||||
)
|
||||
return 1
|
||||
|
||||
task_queue: queue.Queue = queue.Queue()
|
||||
|
||||
Reference in new issue
Block a user