@@ -52,6 +52,20 @@ def _is_truthy_env(name: str) -> bool:
5252 return value .strip ().lower () in {"1" , "true" , "yes" , "on" }
5353
5454
55+ def _get_optional_bool_env (name : str ) -> Optional [bool ]:
56+ """读取可选布尔环境变量,未设置或无法识别时返回 None。"""
57+ value = os .environ .get (name )
58+ if value is None :
59+ return None
60+
61+ normalized = value .strip ().lower ()
62+ if normalized in {"1" , "true" , "yes" , "on" }:
63+ return True
64+ if normalized in {"0" , "false" , "no" , "off" }:
65+ return False
66+ return None
67+
68+
5569ALLOW_DOCKER_HEADED = (
5670 _is_truthy_env ("ALLOW_DOCKER_HEADED_CAPTCHA" )
5771 or _is_truthy_env ("ALLOW_DOCKER_BROWSER_CAPTCHA" )
@@ -555,7 +569,7 @@ class BrowserCaptchaService:
555569
556570 def __init__ (self , db = None ):
557571 """初始化服务"""
558- self .headless = True # 无头模式
572+ self .headless = self . _resolve_headless_mode () # 默认改为有头,可用环境变量回退到无头
559573 self .browser = None
560574 self ._initialized = False
561575 self .website_key = "6LdsFiUsAAAAAIjVDZcuLhaHiDn5nnHVXVRQGeMV"
@@ -640,6 +654,18 @@ async def reload_config(self):
640654 f"fingerprint_ttl { old_fingerprint_ttl } s->{ self ._fingerprint_cache_ttl_seconds } s"
641655 )
642656
657+ def _resolve_headless_mode (self ) -> bool :
658+ """personal 模式默认改为有头,仅在显式环境变量要求时回退到无头。"""
659+ for env_name in ("PERSONAL_BROWSER_HEADLESS" , "FLOW2API_PERSONAL_HEADLESS" ):
660+ override = _get_optional_bool_env (env_name )
661+ if override is not None :
662+ debug_logger .log_info (
663+ f"[BrowserCaptcha] Personal headless 模式由环境变量 { env_name } 控制: { override } "
664+ )
665+ return override
666+
667+ return False
668+
643669 def _refresh_runtime_tunables (self ):
644670 """刷新运行时调优参数,缺省时使用保守的低开销默认值。"""
645671 try :
@@ -1454,6 +1480,7 @@ async def initialize(self):
14541480 self ._proxy_url = f"{ protocol } ://{ host } :{ port } "
14551481 debug_logger .log_info (f"[BrowserCaptcha] Personal 浏览器代理: { self ._proxy_url } " )
14561482
1483+ launch_in_background = bool (getattr (config , "browser_launch_background" , True ))
14571484 browser_args = [
14581485 '--disable-quic' ,
14591486 '--disable-features=UseDnsHttpsSvcb' ,
@@ -1463,7 +1490,6 @@ async def initialize(self):
14631490 '--disable-infobars' ,
14641491 '--hide-scrollbars' ,
14651492 '--window-size=1280,720' ,
1466- '--window-position=3000,3000' ,
14671493 '--profile-directory=Default' ,
14681494 '--disable-background-networking' ,
14691495 '--disable-sync' ,
@@ -1473,6 +1499,20 @@ async def initialize(self):
14731499 '--no-default-browser-check' ,
14741500 '--no-zygote' ,
14751501 ]
1502+ if launch_in_background and not self .headless :
1503+ browser_args .extend ([
1504+ '--start-minimized' ,
1505+ '--disable-background-timer-throttling' ,
1506+ '--disable-renderer-backgrounding' ,
1507+ '--disable-backgrounding-occluded-windows' ,
1508+ ])
1509+ if sys .platform .startswith ("win" ):
1510+ browser_args .append ('--window-position=-32000,-32000' )
1511+ else :
1512+ browser_args .append ('--window-position=3000,3000' )
1513+ debug_logger .log_info ("[BrowserCaptcha] Personal 有头浏览器将以后台模式启动" )
1514+ elif not self .headless :
1515+ debug_logger .log_info ("[BrowserCaptcha] Personal 有头浏览器将以可见窗口模式启动" )
14761516 if proxy_server_arg :
14771517 browser_args .append (proxy_server_arg )
14781518 if self ._proxy_ext_dir :
@@ -1503,7 +1543,7 @@ async def initialize(self):
15031543 debug_logger .log_info (
15041544 "[BrowserCaptcha] nodriver 启动上下文: "
15051545 f"docker={ IS_DOCKER } , display={ display_value or '<empty>' } , "
1506- f"uid={ effective_uid } , headless={ self .headless } , sandbox=False, "
1546+ f"uid={ effective_uid } , headless={ self .headless } , background= { launch_in_background } , sandbox=False, "
15071547 f"executable={ browser_executable_path or '<auto>' } , "
15081548 f"args={ ' ' .join (effective_launch_args )} "
15091549 )
0 commit comments