From bfda2ef79704fcd0c1d949f370f02abb18c6f81d Mon Sep 17 00:00:00 2001 From: jxxghp Date: Wed, 9 Sep 2026 07:23:26 +0800 Subject: [PATCH] fix(indexer): limit userdata schema fallbacks --- app/modules/indexer/__init__.py | 16 +++++- tests/test_indexer_userdata_fallback.py | 67 +++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 tests/test_indexer_userdata_fallback.py diff --git a/app/modules/indexer/__init__.py b/app/modules/indexer/__init__.py index f64d1953b..3c789a661 100644 --- a/app/modules/indexer/__init__.py +++ b/app/modules/indexer/__init__.py @@ -12,7 +12,7 @@ from app.domain.context import Context, SubtitleInfo, TorrentInfo from app.foundation import text as text_tools from app.foundation.reflection import ModuleHelper from app.modules import _ModuleBase -from app.modules.indexer.parser import SiteParserBase +from app.modules.indexer.parser import SiteParserBase, SiteSchema from app.modules.indexer.spider import SiteSpider from app.modules.indexer.spider.haidan import HaiDanSpider from app.modules.indexer.spider.hddolby import HddolbySpider @@ -51,6 +51,14 @@ _SPECIALIZED_SEARCH_ARGUMENTS = { _UNKNOWN_SEARCH_FAILURE_MESSAGE = "站点请求或页面解析失败" +# NexusPHP、Gazelle 和 Unit3d 覆盖大多数站点,其余模型多为专站适配, +# 不参与自动探测,避免对同一站点重复请求专用接口。 +_COMMON_SITE_USERDATA_FALLBACK_SCHEMAS = frozenset({ + SiteSchema.Gazelle.value, + SiteSchema.NexusPhp.value, + SiteSchema.Unit3d.value, +}) + @dataclass(frozen=True) class _IndexerSearchRequest: @@ -758,7 +766,11 @@ class IndexerModule(_ModuleBase): if not site_obj.userid and not site.get("public"): tried = {site.get("schema")} for site_schema in self._site_schemas: - if not site_schema.schema or site_schema.schema.value in tried: + if ( + not site_schema.schema + or site_schema.schema.value in tried + or site_schema.schema.value not in _COMMON_SITE_USERDATA_FALLBACK_SCHEMAS + ): continue tried.add(site_schema.schema.value) logger.info(f"站点 {site.get('name')} schema {site.get('schema')} 解析失败, " diff --git a/tests/test_indexer_userdata_fallback.py b/tests/test_indexer_userdata_fallback.py new file mode 100644 index 000000000..45136b7e3 --- /dev/null +++ b/tests/test_indexer_userdata_fallback.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +from types import SimpleNamespace +from unittest.mock import Mock + +from app.modules.indexer import IndexerModule +from app.modules.indexer.parser import SiteSchema + + +def _build_parser_constructor(schema_value: str, calls: list[str]) -> Mock: + """构造记录解析调用的站点解析器桩。""" + constructor = Mock(name=f"{schema_value}Parser") + constructor.schema = SimpleNamespace(value=schema_value) + + def build_parser(**_kwargs): + """构造一个解析失败且不发起真实请求的解析器实例。""" + parser = Mock() + parser.userid = None + parser.username = None + parser.user_level = None + parser.join_at = None + parser.upload = 0 + parser.download = 0 + parser.ratio = 0 + parser.bonus = 0 + parser.seeding = 0 + parser.seeding_size = 0 + parser.seeding_info = [] + parser.leeching = 0 + parser.leeching_size = 0 + parser.message_unread = 0 + parser.message_unread_contents = [] + parser.err_msg = None + parser.parse.side_effect = lambda: calls.append(schema_value) + return parser + + constructor.side_effect = build_parser + return constructor + + +def test_refresh_userdata_fallback_only_uses_common_schemas(monkeypatch): + """声明模型失败时只尝试常用模型,不触发专用模型兜底请求。""" + calls = [] + parser_constructors = [ + _build_parser_constructor(SiteSchema.NexusPhp.value, calls), + _build_parser_constructor(SiteSchema.Gazelle.value, calls), + _build_parser_constructor(SiteSchema.Unit3d.value, calls), + _build_parser_constructor(SiteSchema.TNode.value, calls), + _build_parser_constructor(SiteSchema.HDDolby.value, calls), + ] + monkeypatch.setattr(IndexerModule, "_site_schemas", parser_constructors) + + module = object.__new__(IndexerModule) + result = module.refresh_userdata({ + "name": "音乐乌托邦", + "url": "https://www.musopia.vip/", + "schema": SiteSchema.NexusPhp.value, + "public": False, + }) + + assert result.userid is None + assert calls == [ + SiteSchema.NexusPhp.value, + SiteSchema.Gazelle.value, + SiteSchema.Unit3d.value, + ] + assert not parser_constructors[3].called + assert not parser_constructors[4].called