diff --git a/backend/src/module/conf/search_provider.py b/backend/src/module/conf/search_provider.py index 04e0c860..7a63eda4 100644 --- a/backend/src/module/conf/search_provider.py +++ b/backend/src/module/conf/search_provider.py @@ -18,6 +18,7 @@ def _default_parser(site: str) -> str: DEFAULT_PROVIDER: dict[str, ProviderConfig] = { "mikan": {"url": "https://mikanani.me/RSS/Search?searchstr=%s", "parser": "mikan"}, + "anibt": {"url": "https://anibt.net/rss/magnets.xml?q=%s", "parser": "tmdb"}, "nyaa": {"url": "https://nyaa.si/?page=rss&q=%s&c=0_0&f=0", "parser": "tmdb"}, "dmhy": {"url": "http://dmhy.org/topics/rss/rss.xml?keyword=%s", "parser": "tmdb"}, } @@ -41,7 +42,12 @@ def _normalize(raw: dict) -> dict[str, ProviderConfig]: def load_provider() -> dict[str, ProviderConfig]: if PROVIDER_PATH.exists(): - return _normalize(json_config.load(PROVIDER_PATH)) + providers = _normalize(json_config.load(PROVIDER_PATH)) + # 旧配置文件可能缺少后来新增的内置搜索源(如 anibt):补齐缺失项, + # 用户对已有站点的自定义 URL / 解析器保持不变。 + for site, config in DEFAULT_PROVIDER.items(): + providers.setdefault(site, config) + return providers else: PROVIDER_PATH.parent.mkdir(parents=True, exist_ok=True) json_config.save(PROVIDER_PATH, DEFAULT_PROVIDER) diff --git a/backend/src/test/test_search_provider.py b/backend/src/test/test_search_provider.py index 14c8df5e..3fe59661 100644 --- a/backend/src/test/test_search_provider.py +++ b/backend/src/test/test_search_provider.py @@ -49,6 +49,14 @@ class TestNormalize: assert result["custom"]["parser"] == "tmdb" +class TestDefaultProvider: + def test_default_provider_includes_anibt_with_tmdb_parser(self): + """anibt is a built-in default provider searched via its RSS feed.""" + anibt = search_provider_module.DEFAULT_PROVIDER["anibt"] + assert anibt["url"] == "https://anibt.net/rss/magnets.xml?q=%s" + assert anibt["parser"] == "tmdb" + + class TestLoadProvider: def test_load_provider_backward_compatible_with_legacy_file( self, tmp_path, monkeypatch @@ -74,6 +82,22 @@ class TestLoadProvider: assert result == search_provider_module.DEFAULT_PROVIDER assert search_provider_module.PROVIDER_PATH.exists() + def test_load_provider_merges_new_default_sites_into_saved_config(self): + """A saved config that predates a newly added built-in provider gains + that provider on load, while user-customized entries are kept as-is.""" + from module.utils import json_config + + json_config.save( + search_provider_module.PROVIDER_PATH, + {"mikan": {"url": "https://my.mirror/RSS/Search?searchstr=%s"}}, + ) + + result = load_provider() + + assert result["mikan"]["url"] == "https://my.mirror/RSS/Search?searchstr=%s" + assert result["anibt"] == search_provider_module.DEFAULT_PROVIDER["anibt"] + assert result["nyaa"] == search_provider_module.DEFAULT_PROVIDER["nyaa"] + class TestSaveProvider: def test_save_provider_stores_url_and_parser(self): diff --git a/webui/src/components/setting/config-search-provider.vue b/webui/src/components/setting/config-search-provider.vue index b4fd0986..f21a330c 100644 --- a/webui/src/components/setting/config-search-provider.vue +++ b/webui/src/components/setting/config-search-provider.vue @@ -85,7 +85,7 @@ const canAdd = computed( ); // Default providers that cannot be deleted -const defaultProviderNames = ['mikan', 'nyaa', 'dmhy']; +const defaultProviderNames = ['mikan', 'anibt', 'nyaa', 'dmhy']; onMounted(() => { loadProviders(); diff --git a/webui/src/store/search.ts b/webui/src/store/search.ts index 7017047f..5d4b5cab 100644 --- a/webui/src/store/search.ts +++ b/webui/src/store/search.ts @@ -25,7 +25,7 @@ export interface GroupedBangumi { } export const useSearchStore = defineStore('search', () => { - const providers = ref(['mikan', 'dmhy', 'nyaa']); + const providers = ref(['mikan', 'anibt', 'dmhy', 'nyaa']); const { keyword,