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)"}