From 36e68b133ce6de17e9de519ac6db1874dbedd57a Mon Sep 17 00:00:00 2001 From: LIN student <86354789+MiCat-S@users.noreply.github.com> Date: Mon, 5 Oct 2026 16:52:41 +0800 Subject: [PATCH] =?UTF-8?q?fix(nettest):=20TMDB=20=E7=BD=91=E7=AB=99?= =?UTF-8?q?=E8=BF=9E=E9=80=9A=E6=80=A7=E6=B5=8B=E8=AF=95=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E8=87=AA=E8=BA=AB=20UA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 网速连通性测试对所有目标统一发送浏览器 UA(NORMAL_USER_AGENT)。 www.themoviedb.org 对自称 Chrome、但 TLS 握手特征来自 Python 客户端的 请求返回 403 人机校验页(Just a moment...),因此网络正常时该项也固定 显示「错误码:403」。同一请求换成程序自身 UA(USER_AGENT)即返回 200; TMDB API 两个目标不受影响。 tmdb_web 规则改为携带 USER_AGENT 请求头;未配置时沿用原有浏览器 UA。 Co-Authored-By: Claude Opus 5.5 --- app/application/nettest/catalogue.py | 3 +++ tests/test_nettest_probe_contract.py | 29 ++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/app/application/nettest/catalogue.py b/app/application/nettest/catalogue.py index f87b0102c..768c1b364 100644 --- a/app/application/nettest/catalogue.py +++ b/app/application/nettest/catalogue.py @@ -131,6 +131,9 @@ def _build_metadata_module_rules(settings: SettingsReader) -> list[NetworkTestRu proxy=True, allowed_redirect_prefixes=("https://www.themoviedb.org/",), module_ids=("TheMovieDbModule",), + # 站点对自称浏览器、但 TLS 握手特征不符的请求返回 403 人机校验页, + # 使用程序自身 UA 才能反映真实连通性 + headers=_freeze_headers({"User-Agent": settings("USER_AGENT", None)}), ), NetworkTestRule( id="tvdb_api", diff --git a/tests/test_nettest_probe_contract.py b/tests/test_nettest_probe_contract.py index 533d8b4eb..c0761d3ad 100644 --- a/tests/test_nettest_probe_contract.py +++ b/tests/test_nettest_probe_contract.py @@ -119,3 +119,32 @@ def test_network_proxy_probe_uses_configured_github_identity(github_headers) -> assert captured["headers"] == github_headers assert public_target.address == "http://proxy.example:7890" assert "github-token" not in public_target.address + + +def test_tmdb_web_probe_identifies_as_moviepilot() -> None: + """TMDB 网站探针应使用程序自身 UA,浏览器 UA 会被站点人机校验页拦成 403。""" + captured = {} + + async def respond(method, url, **options): + """按请求头里的 UA 模拟 TMDB 网站对非真实浏览器的响应。""" + captured.update(method=method, url=url, **options) + identifies_as_app = (options["headers"] or {}).get("User-Agent") == "MoviePilot/3.1.1 (Linux; x86_64)" + return SimpleNamespace( + status_code=200 if identifies_as_app else 403, + headers={}, + text="", + aclose=AsyncMock(), + ) + + transport = SimpleNamespace(request=AsyncMock(side_effect=respond)) + service = _network_test_service( + transport, + USER_AGENT="MoviePilot/3.1.1 (Linux; x86_64)", + ) + + result = asyncio.run(service.execute(target_id="tmdb_web")) + + assert result.success + assert captured["method"] == "GET" + assert captured["url"] == "https://www.themoviedb.org" + assert captured["headers"] == {"User-Agent": "MoviePilot/3.1.1 (Linux; x86_64)"}