fix(rss): map search-site name to its configured parser on subscribe (#1053)

Search-subscribe sent the provider site name (nyaa/dmhy) as the RSSItem
parser, but the analyser only understands mikan/tmdb, so those
subscriptions silently skipped TMDB title/poster enrichment. Resolve the
site name through the search-provider config at the subscribe boundary;
values that are already parser types pass through unchanged.

Note: the WebUI RSS tag for search-subscribed nyaa/dmhy feeds now reads
"tmdb" (the parser type) instead of the site name.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JhTduCYBGeHETU7dSWoh37
This commit is contained in:
Estrella Pan
2026-07-07 12:42:35 +02:00
parent 1b928ee326
commit b659795288
2 changed files with 65 additions and 1 deletions

View File

@@ -1,6 +1,7 @@
from fastapi import APIRouter, Depends
from fastapi.responses import JSONResponse
from module.conf.search_provider import get_provider
from module.database import Database, get_db
from module.downloader import DownloadClient
from module.manager import SeasonCollector
@@ -204,5 +205,10 @@ async def download_collection(data: Bangumi):
"/subscribe", response_model=APIResponse, dependencies=[Depends(get_current_user)]
)
async def subscribe(data: Bangumi, rss: RSSItem):
resp = await SeasonCollector.subscribe_season(data, parser=rss.parser)
# 搜索订阅时前端传来的是站点名(nyaa/dmhy/mikan),而分析器只认识解析器
# 类型 mikan/tmdb(见 rss/analyser.py),站点名会静默跳过 TMDB 补全(#1053)。
# 这里按搜索源配置把站点名映射为解析器;已是解析器类型的值原样透传。
providers = get_provider()
parser = providers[rss.parser]["parser"] if rss.parser in providers else rss.parser
resp = await SeasonCollector.subscribe_season(data, parser=parser)
return u_response(resp)

View File

@@ -361,3 +361,61 @@ class TestSubscribe:
)
assert response.status_code == 200
_PROVIDERS = {
"mikan": {
"url": "https://mikanani.me/RSS/Search?searchstr=%s",
"parser": "mikan",
},
"nyaa": {"url": "https://nyaa.si/?page=rss&q=%s", "parser": "tmdb"},
}
def test_subscribe_site_name_maps_to_provider_parser(self, authed_client):
"""搜索订阅传入站点名(如 nyaa)时应映射为该站点配置的解析器(#1053)。"""
resp_model = ResponseModel(
status=True, status_code=200, msg_en="Subscribed.", msg_zh="订阅成功。"
)
with (
patch(
"module.api.rss.SeasonCollector.subscribe_season",
new_callable=AsyncMock,
return_value=resp_model,
) as mock_subscribe,
patch("module.api.rss.get_provider", return_value=self._PROVIDERS),
):
response = authed_client.post(
"/api/v1/rss/subscribe",
json={
"data": make_bangumi(id=1).model_dump(),
"rss": {"url": "https://nyaa.si/?page=rss&q=x", "parser": "nyaa"},
},
)
assert response.status_code == 200
assert mock_subscribe.await_args is not None
assert mock_subscribe.await_args.kwargs["parser"] == "tmdb"
def test_subscribe_parser_value_passes_through_unchanged(self, authed_client):
"""已是解析器类型的值(非站点名)应原样传递。"""
resp_model = ResponseModel(
status=True, status_code=200, msg_en="Subscribed.", msg_zh="订阅成功。"
)
with (
patch(
"module.api.rss.SeasonCollector.subscribe_season",
new_callable=AsyncMock,
return_value=resp_model,
) as mock_subscribe,
patch("module.api.rss.get_provider", return_value=self._PROVIDERS),
):
response = authed_client.post(
"/api/v1/rss/subscribe",
json={
"data": make_bangumi(id=1).model_dump(),
"rss": {"url": "https://example.com/rss", "parser": "tmdb"},
},
)
assert response.status_code == 200
assert mock_subscribe.await_args is not None
assert mock_subscribe.await_args.kwargs["parser"] == "tmdb"