mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-11-03 22:04:57 +08:00
fix(subscribe): support cross-source video status lookup
This commit is contained in:
@@ -408,17 +408,37 @@ async def subscribe_media_identity(
|
||||
media_source: MediaSource,
|
||||
season: Optional[int] = None,
|
||||
title: Optional[str] = None,
|
||||
year: Optional[str] = None,
|
||||
mtype: Optional[MediaType] = None,
|
||||
music_type: Optional[str] = None,
|
||||
query: SubscriptionQueryService = Depends(get_subscription_query_service),
|
||||
current_user: ApiPrincipal = Depends(get_current_active_user_async),
|
||||
) -> Any:
|
||||
"""
|
||||
根据媒体来源和原生 ID 查询订阅。
|
||||
根据媒体身份查询订阅,视频身份未命中时按类型、标题和年份回退。
|
||||
"""
|
||||
metadata = MetaInfo(title) if title else None
|
||||
normalized_title = metadata.name if metadata else None
|
||||
if season is None and metadata:
|
||||
season = metadata.begin_season
|
||||
subscribes = await query.list_by_media_identity(media_source, media_id, music_type)
|
||||
if season is not None:
|
||||
subscribes = [subscribe for subscribe in subscribes if subscribe.season == season]
|
||||
result = select_accessible_subscribe(subscribes, current_user)
|
||||
if (
|
||||
not result
|
||||
and media_source != MediaSource.TMDB
|
||||
and mtype in (MediaType.MOVIE, MediaType.TV)
|
||||
and normalized_title
|
||||
and year
|
||||
):
|
||||
subscribes = await query.list_by_video_metadata(
|
||||
title=normalized_title,
|
||||
year=year,
|
||||
media_type=mtype,
|
||||
season=season,
|
||||
)
|
||||
result = select_accessible_subscribe(subscribes, current_user)
|
||||
return result if result else _SchemaSubscribe()
|
||||
|
||||
|
||||
|
||||
@@ -123,6 +123,31 @@ class SubscriptionQueryService:
|
||||
if self._matches_music_type(record, music_type)
|
||||
]
|
||||
|
||||
async def list_by_video_metadata(
|
||||
self,
|
||||
*,
|
||||
title: str,
|
||||
year: str,
|
||||
media_type: MediaType,
|
||||
season: Optional[int] = None,
|
||||
) -> list[SubscribeView]:
|
||||
"""按影视类型、规范标题、年份和可选季号读取跨来源订阅。"""
|
||||
if self._async_repository is None:
|
||||
raise RuntimeError("异步订阅查询端口未注册")
|
||||
if media_type not in (MediaType.MOVIE, MediaType.TV):
|
||||
return []
|
||||
records = await self._async_repository.async_list_by_title(
|
||||
title=title,
|
||||
season=season,
|
||||
)
|
||||
expected_year = str(year).strip()
|
||||
return [
|
||||
self._to_public_view(record)
|
||||
for record in records
|
||||
if record.type == media_type.value
|
||||
and str(record.year or "").strip() == expected_year
|
||||
]
|
||||
|
||||
async def list_history(
|
||||
self,
|
||||
mtype: str,
|
||||
|
||||
@@ -598,6 +598,88 @@ class TestSubscribeEndpoint:
|
||||
assert result.id is None
|
||||
repository.async_list_by_title.assert_not_awaited()
|
||||
|
||||
def test_subscribe_media_identity_falls_back_to_video_metadata(self):
|
||||
"""豆瓣身份未命中时,电影应按类型、标题和年份匹配已有订阅。"""
|
||||
from app.api.endpoints.subscribe import subscribe_media_identity
|
||||
|
||||
other_year = _EndpointSubscribe(
|
||||
id=31,
|
||||
username="alice",
|
||||
name="跨来源电影",
|
||||
year="2024",
|
||||
type=MediaType.MOVIE.value,
|
||||
media_source=MediaSource.TMDB,
|
||||
media_id="tmdb-2024",
|
||||
)
|
||||
matched = _EndpointSubscribe(
|
||||
id=32,
|
||||
username="alice",
|
||||
name="跨来源电影",
|
||||
year="2025",
|
||||
type=MediaType.MOVIE.value,
|
||||
media_source=MediaSource.TMDB,
|
||||
media_id="tmdb-2025",
|
||||
)
|
||||
inaccessible_exact = _EndpointSubscribe(
|
||||
id=30,
|
||||
username="bob",
|
||||
name="跨来源电影",
|
||||
year="2025",
|
||||
type=MediaType.MOVIE.value,
|
||||
media_source=MediaSource.Douban,
|
||||
media_id="douban-2025",
|
||||
)
|
||||
repository = _SubscriptionRepositoryFake(inaccessible_exact, other_year, matched)
|
||||
|
||||
result = asyncio.run(
|
||||
subscribe_media_identity(
|
||||
media_id="douban-2025",
|
||||
media_source=MediaSource.Douban,
|
||||
title="跨来源电影",
|
||||
year="2025",
|
||||
mtype=MediaType.MOVIE,
|
||||
query=_subscription_query(repository),
|
||||
current_user=_EndpointUser(name="alice", is_superuser=False),
|
||||
)
|
||||
)
|
||||
|
||||
assert result.id == 32
|
||||
repository.async_list_by_title.assert_awaited_once_with(
|
||||
title="跨来源电影",
|
||||
season=None,
|
||||
)
|
||||
|
||||
def test_subscribe_media_identity_keeps_tmdb_lookup_strict(self):
|
||||
"""TMDB 身份可用时不得退化为同名年份查询。"""
|
||||
from app.api.endpoints.subscribe import subscribe_media_identity
|
||||
|
||||
repository = _SubscriptionRepositoryFake(
|
||||
_EndpointSubscribe(
|
||||
id=33,
|
||||
username="alice",
|
||||
name="同名电影",
|
||||
year="2025",
|
||||
type=MediaType.MOVIE.value,
|
||||
media_source=MediaSource.Douban,
|
||||
media_id="douban-same-title",
|
||||
)
|
||||
)
|
||||
|
||||
result = asyncio.run(
|
||||
subscribe_media_identity(
|
||||
media_id="missing-tmdb",
|
||||
media_source=MediaSource.TMDB,
|
||||
title="同名电影",
|
||||
year="2025",
|
||||
mtype=MediaType.MOVIE,
|
||||
query=_subscription_query(repository),
|
||||
current_user=_EndpointUser(name="alice", is_superuser=False),
|
||||
)
|
||||
)
|
||||
|
||||
assert result.id is None
|
||||
repository.async_list_by_title.assert_not_awaited()
|
||||
|
||||
def test_delete_subscribe_by_media_identity_deletes_owner_candidate(self):
|
||||
"""
|
||||
按媒体删除端点应把媒体身份和当前用户交给应用命令。
|
||||
|
||||
Reference in New Issue
Block a user