fix: map Bangumi movie subjects correctly (#6155)

This commit is contained in:
jxxghp
2026-07-20 16:16:25 +08:00
parent ed92be08af
commit 44fa202778
3 changed files with 165 additions and 6 deletions

View File

@@ -845,7 +845,7 @@ class MediaChain(ChainBase, ConfigReloadMixin, metaclass=Singleton):
tmdbinfo = self._match_tmdb_with_names(
meta_names=meta_names,
year=year,
mtype=MediaType.TV,
mtype=MediaInfo.get_bangumi_media_type(bangumiinfo),
season=meta.begin_season,
)
return tmdbinfo
@@ -885,7 +885,10 @@ class MediaChain(ChainBase, ConfigReloadMixin, metaclass=Singleton):
year = self._extract_year_from_bangumi(bangumiinfo)
# 使用名称识别豆瓣媒体信息
return self.match_doubaninfo(
name=meta.name, year=year, mtype=MediaType.TV, season=meta.begin_season
name=meta.name,
year=year,
mtype=MediaInfo.get_bangumi_media_type(bangumiinfo),
season=meta.begin_season,
)
return None
@@ -1861,7 +1864,7 @@ class MediaChain(ChainBase, ConfigReloadMixin, metaclass=Singleton):
tmdbinfo = await self._async_match_tmdb_with_names(
meta_names=meta_names,
year=year,
mtype=MediaType.TV,
mtype=MediaInfo.get_bangumi_media_type(bangumiinfo),
season=meta.begin_season,
)
return tmdbinfo
@@ -1901,6 +1904,9 @@ class MediaChain(ChainBase, ConfigReloadMixin, metaclass=Singleton):
year = self._extract_year_from_bangumi(bangumiinfo)
# 使用名称识别豆瓣媒体信息
return await self.async_match_doubaninfo(
name=meta.name, year=year, mtype=MediaType.TV, season=meta.begin_season
name=meta.name,
year=year,
mtype=MediaInfo.get_bangumi_media_type(bangumiinfo),
season=meta.begin_season,
)
return None

View File

@@ -9,6 +9,8 @@ from app.core.metainfo import MetaInfo
from app.schemas.types import MediaType
from app.utils.string import StringUtils
BANGUMI_MOVIE_PLATFORMS = frozenset({"movie", "电影", "剧场版"})
@dataclass
class TorrentInfo:
@@ -243,6 +245,10 @@ class SubtitleInfo:
@dataclass
class MediaInfo:
"""
统一媒体信息,负责聚合各元数据源的标准字段
"""
# 内部标记:是否命中本地识别缓存,不参与序列化
recognize_cache_hit = False
# 来源themoviedb、douban、bangumi
@@ -721,7 +727,20 @@ class MediaInfo:
elif type(current_value) is type(value):
setattr(self, key, value)
def set_bangumi_info(self, info: dict):
@staticmethod
def get_bangumi_media_type(info: dict) -> MediaType:
"""
根据Bangumi媒介平台获取标准媒体类型未知平台兼容回退为电视剧
:param info: Bangumi条目信息
:return: 标准媒体类型
"""
platform = str(info.get("platform") or "").strip().casefold()
if platform in BANGUMI_MOVIE_PLATFORMS:
return MediaType.MOVIE
return MediaType.TV
def set_bangumi_info(self, info: dict) -> None:
"""
初始化Bangumi信息
"""
@@ -735,7 +754,7 @@ class MediaInfo:
self.bangumi_id = info.get("id")
# 类型
if not self.type:
self.type = MediaType.TV
self.type = self.get_bangumi_media_type(info)
# 标题
if not self.title:
self.title = info.get("name_cn") or info.get("name")

View File

@@ -0,0 +1,134 @@
import asyncio
from typing import Optional
import pytest
from app.chain.media import MediaChain
from app.core.context import MediaInfo
from app.schemas.types import MediaType
@pytest.mark.parametrize(
("platform", "expected_type"),
[
("剧场版", MediaType.MOVIE),
("Movie", MediaType.MOVIE),
("电影", MediaType.MOVIE),
("TV", MediaType.TV),
("WEB", MediaType.TV),
("OVA", MediaType.TV),
("未知", MediaType.TV),
(None, MediaType.TV),
],
)
def test_bangumi_platform_maps_to_media_type(
platform: Optional[str], expected_type: MediaType
) -> None:
"""Bangumi媒介平台应映射为正确的标准媒体类型。"""
media_info = MediaInfo(bangumi_info={"id": 1, "name": "测试条目", "platform": platform})
assert media_info.type == expected_type
def test_bangumi_media_type_does_not_override_explicit_type() -> None:
"""显式媒体类型应优先于Bangumi媒介平台。"""
media_info = MediaInfo(
type=MediaType.TV,
bangumi_info={"id": 1, "name": "测试条目", "platform": "剧场版"},
)
assert media_info.type == MediaType.TV
class _SyncBangumiMediaChain:
"""同步Bangumi跨数据源转换测试桩。"""
def __init__(self):
"""初始化调用参数记录。"""
self.tmdb_mtype = None
self.douban_mtype = None
def bangumi_info(self, bangumiid: int) -> dict:
"""返回剧场版Bangumi条目信息。"""
return {
"id": bangumiid,
"name": "Movie Test",
"name_cn": "电影测试",
"date": "2026-01-01",
"platform": "剧场版",
}
@staticmethod
def _extract_year_from_bangumi(bangumi_info: dict) -> str:
"""返回测试条目的年份。"""
return bangumi_info["date"][:4]
def _match_tmdb_with_names(self, **kwargs) -> dict:
"""记录TMDB匹配使用的媒体类型。"""
self.tmdb_mtype = kwargs["mtype"]
return {"id": 100}
def match_doubaninfo(self, **kwargs) -> dict:
"""记录豆瓣匹配使用的媒体类型。"""
self.douban_mtype = kwargs["mtype"]
return {"id": "200"}
def test_bangumi_movie_conversion_uses_movie_type() -> None:
"""Bangumi剧场版转TMDB和豆瓣时均应按电影匹配。"""
chain = _SyncBangumiMediaChain()
tmdb_info = MediaChain.get_tmdbinfo_by_bangumiid(chain, 1)
douban_info = MediaChain.get_doubaninfo_by_bangumiid(chain, 1)
assert tmdb_info == {"id": 100}
assert douban_info == {"id": "200"}
assert chain.tmdb_mtype == MediaType.MOVIE
assert chain.douban_mtype == MediaType.MOVIE
class _AsyncBangumiMediaChain:
"""异步Bangumi跨数据源转换测试桩。"""
def __init__(self):
"""初始化调用参数记录。"""
self.tmdb_mtype = None
self.douban_mtype = None
async def async_bangumi_info(self, bangumiid: int) -> dict:
"""返回剧场版Bangumi条目信息。"""
return {
"id": bangumiid,
"name": "Movie Test",
"name_cn": "电影测试",
"date": "2026-01-01",
"platform": "Movie",
}
@staticmethod
def _extract_year_from_bangumi(bangumi_info: dict) -> str:
"""返回测试条目的年份。"""
return bangumi_info["date"][:4]
async def _async_match_tmdb_with_names(self, **kwargs) -> dict:
"""记录异步TMDB匹配使用的媒体类型。"""
self.tmdb_mtype = kwargs["mtype"]
return {"id": 100}
async def async_match_doubaninfo(self, **kwargs) -> dict:
"""记录异步豆瓣匹配使用的媒体类型。"""
self.douban_mtype = kwargs["mtype"]
return {"id": "200"}
def test_async_bangumi_movie_conversion_uses_movie_type() -> None:
"""异步Bangumi电影转换应向TMDB和豆瓣传递电影类型。"""
chain = _AsyncBangumiMediaChain()
tmdb_info = asyncio.run(MediaChain.async_get_tmdbinfo_by_bangumiid(chain, 1))
douban_info = asyncio.run(MediaChain.async_get_doubaninfo_by_bangumiid(chain, 1))
assert tmdb_info == {"id": 100}
assert douban_info == {"id": "200"}
assert chain.tmdb_mtype == MediaType.MOVIE
assert chain.douban_mtype == MediaType.MOVIE