Skip to content

Commit cc92e1c

Browse files
committed
fix(captcha): 修复内置打码并发分发与清理配置
1 parent d6d4c61 commit cc92e1c

8 files changed

Lines changed: 193 additions & 77 deletions

File tree

config/setting_example.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ browser_recaptcha_settle_seconds = 3.0 # reload/clr 就绪后的额外稳态等
5959
browser_count = 1 # browser 模式的有头浏览器实例数量
6060
personal_project_pool_size = 4 # personal 模式下单个 Token 默认维护的项目池数量(仅影响项目轮换,不决定打码标签页数量)
6161
personal_max_resident_tabs = 5 # personal 模式共享打码标签页上限,所有 Token/project 共用这组 tab
62+
browser_personal_fresh_restart_every_n_solves = 10 # personal 模式成功获取多少个码后清理并重启浏览器,0 表示禁用
6263
personal_idle_tab_ttl_seconds = 600 # personal 模式标签页空闲回收时间(秒)
6364
yescaptcha_api_key = "" # YesCaptcha API密钥
6465
yescaptcha_base_url = "https://api.yescaptcha.com"

src/api/admin.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1596,6 +1596,10 @@ async def update_captcha_config(
15961596
browser_count = request.get("browser_count", 1)
15971597
personal_project_pool_size = request.get("personal_project_pool_size")
15981598
personal_max_resident_tabs = request.get("personal_max_resident_tabs")
1599+
browser_personal_fresh_restart_every_n_solves = request.get(
1600+
"browser_personal_fresh_restart_every_n_solves",
1601+
10,
1602+
)
15991603
personal_idle_tab_ttl_seconds = request.get("personal_idle_tab_ttl_seconds")
16001604

16011605
# 验证浏览器代理URL格式
@@ -1614,6 +1618,17 @@ async def update_captcha_config(
16141618
remote_browser_timeout = max(5, int(remote_browser_timeout or 60))
16151619
except Exception:
16161620
return {"success": False, "message": "远程打码超时时间必须是整数秒"}
1621+
try:
1622+
browser_count = max(1, min(20, int(browser_count or 1)))
1623+
except Exception:
1624+
return {"success": False, "message": "浏览器实例数量必须是整数"}
1625+
try:
1626+
browser_personal_fresh_restart_every_n_solves = max(
1627+
0,
1628+
int(browser_personal_fresh_restart_every_n_solves if browser_personal_fresh_restart_every_n_solves is not None else 10),
1629+
)
1630+
except Exception:
1631+
return {"success": False, "message": "重置码数必须是整数,0 表示禁用"}
16171632

16181633
if captcha_method == "remote_browser":
16191634
if not (remote_browser_base_url or "").strip():
@@ -1637,9 +1652,10 @@ async def update_captcha_config(
16371652
remote_browser_timeout=remote_browser_timeout,
16381653
browser_proxy_enabled=browser_proxy_enabled,
16391654
browser_proxy_url=browser_proxy_url if browser_proxy_enabled else None,
1640-
browser_count=max(1, int(browser_count)) if browser_count else 1,
1655+
browser_count=browser_count,
16411656
personal_project_pool_size=personal_project_pool_size,
16421657
personal_max_resident_tabs=personal_max_resident_tabs,
1658+
browser_personal_fresh_restart_every_n_solves=browser_personal_fresh_restart_every_n_solves,
16431659
personal_idle_tab_ttl_seconds=personal_idle_tab_ttl_seconds
16441660
)
16451661

@@ -1690,6 +1706,7 @@ async def get_captcha_config(token: str = Depends(verify_admin_token)):
16901706
"browser_count": captcha_config.browser_count,
16911707
"personal_project_pool_size": captcha_config.personal_project_pool_size,
16921708
"personal_max_resident_tabs": captcha_config.personal_max_resident_tabs,
1709+
"browser_personal_fresh_restart_every_n_solves": captcha_config.browser_personal_fresh_restart_every_n_solves,
16931710
"personal_idle_tab_ttl_seconds": captcha_config.personal_idle_tab_ttl_seconds
16941711
}
16951712

src/core/config.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,21 @@ def set_browser_launch_background(self, enabled: bool):
405405
self._config["captcha"] = {}
406406
self._config["captcha"]["browser_launch_background"] = bool(enabled)
407407

408+
@property
409+
def browser_count(self) -> int:
410+
"""浏览器打码实例数量,browser/personal 模式共用。"""
411+
value = self._config.get("captcha", {}).get("browser_count", 1)
412+
try:
413+
return max(1, min(20, int(value)))
414+
except Exception:
415+
return 1
416+
417+
def set_browser_count(self, value: int):
418+
"""设置浏览器打码实例数量。"""
419+
if "captcha" not in self._config:
420+
self._config["captcha"] = {}
421+
self._config["captcha"]["browser_count"] = max(1, min(20, int(value)))
422+
408423
@property
409424
def browser_recaptcha_settle_seconds(self) -> float:
410425
"""有头打码在 reload/clr 就绪后的额外等待秒数。"""
@@ -467,6 +482,21 @@ def set_personal_idle_tab_ttl_seconds(self, value: int):
467482
self._config["captcha"] = {}
468483
self._config["captcha"]["personal_idle_tab_ttl_seconds"] = max(60, int(value))
469484

485+
@property
486+
def browser_personal_fresh_restart_every_n_solves(self) -> int:
487+
"""内置浏览器成功打码多少次后使用全新 profile 重启,0 表示禁用。"""
488+
value = self._config.get("captcha", {}).get("browser_personal_fresh_restart_every_n_solves", 10)
489+
try:
490+
return max(0, int(value))
491+
except Exception:
492+
return 10
493+
494+
def set_browser_personal_fresh_restart_every_n_solves(self, value: int):
495+
"""设置内置浏览器 fresh profile 轮换阈值。"""
496+
if "captcha" not in self._config:
497+
self._config["captcha"] = {}
498+
self._config["captcha"]["browser_personal_fresh_restart_every_n_solves"] = max(0, int(value))
499+
470500
@property
471501
def yescaptcha_api_key(self) -> str:
472502
"""Get YesCaptcha API key"""

src/core/database.py

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ async def _ensure_config_rows(self, db, config_dict: dict = None):
231231
browser_count = 1
232232
personal_project_pool_size = 4
233233
personal_max_resident_tabs = 5
234+
browser_personal_fresh_restart_every_n_solves = 10
234235
personal_idle_tab_ttl_seconds = 600
235236

236237
if config_dict:
@@ -245,6 +246,7 @@ async def _ensure_config_rows(self, db, config_dict: dict = None):
245246
browser_count = captcha_config.get("browser_count", 1)
246247
personal_project_pool_size = captcha_config.get("personal_project_pool_size", 4)
247248
personal_max_resident_tabs = captcha_config.get("personal_max_resident_tabs", 5)
249+
browser_personal_fresh_restart_every_n_solves = captcha_config.get("browser_personal_fresh_restart_every_n_solves", 10)
248250
personal_idle_tab_ttl_seconds = captcha_config.get("personal_idle_tab_ttl_seconds", 600)
249251
try:
250252
remote_browser_timeout = max(5, int(remote_browser_timeout))
@@ -262,6 +264,10 @@ async def _ensure_config_rows(self, db, config_dict: dict = None):
262264
personal_max_resident_tabs = max(1, min(50, int(personal_max_resident_tabs)))
263265
except Exception:
264266
personal_max_resident_tabs = 5
267+
try:
268+
browser_personal_fresh_restart_every_n_solves = max(0, int(browser_personal_fresh_restart_every_n_solves))
269+
except Exception:
270+
browser_personal_fresh_restart_every_n_solves = 10
265271
try:
266272
personal_idle_tab_ttl_seconds = max(60, int(personal_idle_tab_ttl_seconds))
267273
except Exception:
@@ -273,9 +279,10 @@ async def _ensure_config_rows(self, db, config_dict: dict = None):
273279
yescaptcha_task_type,
274280
remote_browser_base_url, remote_browser_api_key, remote_browser_timeout,
275281
browser_count, personal_project_pool_size,
276-
personal_max_resident_tabs, personal_idle_tab_ttl_seconds
282+
personal_max_resident_tabs, browser_personal_fresh_restart_every_n_solves,
283+
personal_idle_tab_ttl_seconds
277284
)
278-
VALUES (1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
285+
VALUES (1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
279286
""", (
280287
captcha_method,
281288
yescaptcha_api_key,
@@ -287,6 +294,7 @@ async def _ensure_config_rows(self, db, config_dict: dict = None):
287294
browser_count,
288295
personal_project_pool_size,
289296
personal_max_resident_tabs,
297+
browser_personal_fresh_restart_every_n_solves,
290298
personal_idle_tab_ttl_seconds,
291299
))
292300

@@ -381,6 +389,11 @@ async def check_and_migrate_db(self, config_dict: dict = None):
381389
page_action TEXT DEFAULT 'IMAGE_GENERATION',
382390
browser_proxy_enabled BOOLEAN DEFAULT 0,
383391
browser_proxy_url TEXT,
392+
browser_count INTEGER DEFAULT 1,
393+
personal_project_pool_size INTEGER DEFAULT 4,
394+
personal_max_resident_tabs INTEGER DEFAULT 5,
395+
browser_personal_fresh_restart_every_n_solves INTEGER DEFAULT 10,
396+
personal_idle_tab_ttl_seconds INTEGER DEFAULT 600,
384397
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
385398
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
386399
)
@@ -528,6 +541,7 @@ async def check_and_migrate_db(self, config_dict: dict = None):
528541
captcha_columns_to_add = [
529542
("personal_project_pool_size", "INTEGER DEFAULT 4"),
530543
("personal_max_resident_tabs", "INTEGER DEFAULT 5"),
544+
("browser_personal_fresh_restart_every_n_solves", "INTEGER DEFAULT 10"),
531545
("personal_idle_tab_ttl_seconds", "INTEGER DEFAULT 600"),
532546
]
533547

@@ -747,6 +761,7 @@ async def init_db(self):
747761
browser_count INTEGER DEFAULT 1,
748762
personal_project_pool_size INTEGER DEFAULT 4,
749763
personal_max_resident_tabs INTEGER DEFAULT 5,
764+
browser_personal_fresh_restart_every_n_solves INTEGER DEFAULT 10,
750765
personal_idle_tab_ttl_seconds INTEGER DEFAULT 600,
751766
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
752767
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
@@ -1642,8 +1657,12 @@ async def reload_config_to_memory(self):
16421657
config.set_remote_browser_base_url(captcha_config.remote_browser_base_url)
16431658
config.set_remote_browser_api_key(captcha_config.remote_browser_api_key)
16441659
config.set_remote_browser_timeout(captcha_config.remote_browser_timeout)
1660+
config.set_browser_count(captcha_config.browser_count)
16451661
config.set_personal_project_pool_size(captcha_config.personal_project_pool_size)
16461662
config.set_personal_max_resident_tabs(captcha_config.personal_max_resident_tabs)
1663+
config.set_browser_personal_fresh_restart_every_n_solves(
1664+
captcha_config.browser_personal_fresh_restart_every_n_solves
1665+
)
16471666
config.set_personal_idle_tab_ttl_seconds(captcha_config.personal_idle_tab_ttl_seconds)
16481667

16491668
# Cache config operations
@@ -1780,6 +1799,7 @@ async def update_captcha_config(
17801799
browser_count: int = None,
17811800
personal_project_pool_size: int = None,
17821801
personal_max_resident_tabs: int = None,
1802+
browser_personal_fresh_restart_every_n_solves: int = None,
17831803
personal_idle_tab_ttl_seconds: int = None
17841804
):
17851805
"""Update captcha configuration"""
@@ -1810,10 +1830,17 @@ async def update_captcha_config(
18101830
new_browser_count = browser_count if browser_count is not None else current.get("browser_count", 1)
18111831
new_personal_project_pool_size = personal_project_pool_size if personal_project_pool_size is not None else current.get("personal_project_pool_size", 4)
18121832
new_personal_max_tabs = personal_max_resident_tabs if personal_max_resident_tabs is not None else current.get("personal_max_resident_tabs", 5)
1833+
new_personal_fresh_restart_every = (
1834+
browser_personal_fresh_restart_every_n_solves
1835+
if browser_personal_fresh_restart_every_n_solves is not None
1836+
else current.get("browser_personal_fresh_restart_every_n_solves", 10)
1837+
)
18131838
new_personal_idle_ttl = personal_idle_tab_ttl_seconds if personal_idle_tab_ttl_seconds is not None else current.get("personal_idle_tab_ttl_seconds", 600)
18141839
new_remote_timeout = max(5, int(new_remote_timeout)) if new_remote_timeout is not None else 60
1840+
new_browser_count = max(1, min(20, int(new_browser_count)))
18151841
new_personal_project_pool_size = max(1, min(50, int(new_personal_project_pool_size)))
18161842
new_personal_max_tabs = max(1, min(50, int(new_personal_max_tabs))) # 限制1-50
1843+
new_personal_fresh_restart_every = max(0, int(new_personal_fresh_restart_every))
18171844
new_personal_idle_ttl = max(60, int(new_personal_idle_ttl)) # 最少60秒
18181845

18191846
await db.execute("""
@@ -1826,15 +1853,17 @@ async def update_captcha_config(
18261853
remote_browser_base_url = ?, remote_browser_api_key = ?, remote_browser_timeout = ?,
18271854
browser_proxy_enabled = ?, browser_proxy_url = ?, browser_count = ?,
18281855
personal_project_pool_size = ?,
1829-
personal_max_resident_tabs = ?, personal_idle_tab_ttl_seconds = ?,
1856+
personal_max_resident_tabs = ?,
1857+
browser_personal_fresh_restart_every_n_solves = ?,
1858+
personal_idle_tab_ttl_seconds = ?,
18301859
updated_at = CURRENT_TIMESTAMP
18311860
WHERE id = 1
18321861
""", (new_method, new_yes_key, new_yes_url, new_yes_task_type,
18331862
new_cap_key, new_cap_url,
18341863
new_ez_key, new_ez_url, new_cs_key, new_cs_url,
18351864
(new_remote_base_url or "").strip(), (new_remote_api_key or "").strip(), new_remote_timeout,
18361865
new_proxy_enabled, new_proxy_url, new_browser_count, new_personal_project_pool_size,
1837-
new_personal_max_tabs, new_personal_idle_ttl))
1866+
new_personal_max_tabs, new_personal_fresh_restart_every, new_personal_idle_ttl))
18381867
else:
18391868
new_method = captcha_method if captcha_method is not None else "yescaptcha"
18401869
new_yes_key = yescaptcha_api_key if yescaptcha_api_key is not None else ""
@@ -1854,10 +1883,17 @@ async def update_captcha_config(
18541883
new_browser_count = browser_count if browser_count is not None else 1
18551884
new_personal_project_pool_size = personal_project_pool_size if personal_project_pool_size is not None else 4
18561885
new_personal_max_tabs = personal_max_resident_tabs if personal_max_resident_tabs is not None else 5
1886+
new_personal_fresh_restart_every = (
1887+
browser_personal_fresh_restart_every_n_solves
1888+
if browser_personal_fresh_restart_every_n_solves is not None
1889+
else 10
1890+
)
18571891
new_personal_idle_ttl = personal_idle_tab_ttl_seconds if personal_idle_tab_ttl_seconds is not None else 600
18581892
new_remote_timeout = max(5, int(new_remote_timeout))
1893+
new_browser_count = max(1, min(20, int(new_browser_count)))
18591894
new_personal_project_pool_size = max(1, min(50, int(new_personal_project_pool_size)))
18601895
new_personal_max_tabs = max(1, min(50, int(new_personal_max_tabs)))
1896+
new_personal_fresh_restart_every = max(0, int(new_personal_fresh_restart_every))
18611897
new_personal_idle_ttl = max(60, int(new_personal_idle_ttl))
18621898

18631899
await db.execute("""
@@ -1868,14 +1904,15 @@ async def update_captcha_config(
18681904
remote_browser_base_url, remote_browser_api_key, remote_browser_timeout,
18691905
browser_proxy_enabled, browser_proxy_url, browser_count,
18701906
personal_project_pool_size,
1871-
personal_max_resident_tabs, personal_idle_tab_ttl_seconds)
1872-
VALUES (1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1907+
personal_max_resident_tabs, browser_personal_fresh_restart_every_n_solves,
1908+
personal_idle_tab_ttl_seconds)
1909+
VALUES (1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
18731910
""", (new_method, new_yes_key, new_yes_url, new_yes_task_type,
18741911
new_cap_key, new_cap_url,
18751912
new_ez_key, new_ez_url, new_cs_key, new_cs_url,
18761913
(new_remote_base_url or "").strip(), (new_remote_api_key or "").strip(), new_remote_timeout,
18771914
new_proxy_enabled, new_proxy_url, new_browser_count, new_personal_project_pool_size,
1878-
new_personal_max_tabs, new_personal_idle_ttl))
1915+
new_personal_max_tabs, new_personal_fresh_restart_every, new_personal_idle_ttl))
18791916

18801917
await db.commit()
18811918

src/core/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ class CaptchaConfig(BaseModel):
198198
browser_count: int = 1 # 浏览器打码实例数量
199199
personal_project_pool_size: int = 4 # 单个 Token 默认维护的项目池数量(仅影响项目轮换)
200200
personal_max_resident_tabs: int = 5 # 内置浏览器共享打码标签页数量上限
201+
browser_personal_fresh_restart_every_n_solves: int = 10 # 成功打码多少次后清理并重启浏览器,0表示禁用
201202
personal_idle_tab_ttl_seconds: int = 600 # 内置浏览器标签页空闲超时(秒)
202203
created_at: Optional[datetime] = None
203204
updated_at: Optional[datetime] = None

0 commit comments

Comments
 (0)