mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-02 05:56:47 +08:00
260 lines
8.7 KiB
Python
260 lines
8.7 KiB
Python
from types import SimpleNamespace
|
|
|
|
from app.chain.subscribe import SubscribeChain
|
|
from app.domain.context import Context, MediaInfo, TorrentInfo
|
|
from app.schemas.types import MediaSource, MediaType
|
|
|
|
|
|
def _target_media(tmdb_id=106449, douban_id=None, **kwargs) -> MediaInfo:
|
|
"""构造订阅目标媒体。"""
|
|
media_source = MediaSource.TMDB if tmdb_id is not None else MediaSource.Douban
|
|
media_id = str(tmdb_id) if tmdb_id is not None else douban_id
|
|
defaults = {
|
|
"title": "凡人修仙传",
|
|
"original_title": "凡人修仙传",
|
|
"names": ["A Record Of A Mortals Journey To Immortality"],
|
|
"type": MediaType.TV,
|
|
"year": "2020",
|
|
"season_years": {1: "2020"},
|
|
}
|
|
defaults.update(kwargs)
|
|
return MediaInfo(
|
|
media_source=media_source if media_id is not None else None,
|
|
media_id=media_id,
|
|
tmdb_id=tmdb_id,
|
|
douban_id=douban_id,
|
|
**defaults,
|
|
)
|
|
|
|
|
|
def _candidate_media(tmdb_id=285479, douban_id=None, **kwargs) -> MediaInfo:
|
|
"""构造由 RSS 标题推断出的候选媒体。"""
|
|
media_source = MediaSource.TMDB if tmdb_id is not None else MediaSource.Douban
|
|
media_id = str(tmdb_id) if tmdb_id is not None else douban_id
|
|
defaults = {
|
|
"title": "凡人修仙传",
|
|
"type": MediaType.TV,
|
|
"year": "2020",
|
|
}
|
|
defaults.update(kwargs)
|
|
return MediaInfo(
|
|
media_source=media_source if media_id is not None else None,
|
|
media_id=media_id,
|
|
tmdb_id=tmdb_id,
|
|
douban_id=douban_id,
|
|
**defaults,
|
|
)
|
|
|
|
|
|
def _torrent_meta(*, en_name="A Record Of A Mortals Journey To Immortality", tmdbid=None):
|
|
"""构造不触发外部识别的标题解析结果。"""
|
|
return SimpleNamespace(
|
|
media_source=MediaSource.TMDB if tmdbid else None,
|
|
media_id=str(tmdbid) if tmdbid else None,
|
|
cn_name="",
|
|
en_name=en_name,
|
|
type=MediaType.TV,
|
|
year=None,
|
|
org_string=f"{en_name} S01E186 2160p WEB-DL",
|
|
)
|
|
|
|
|
|
def _context(meta, candidate, torrent) -> Context:
|
|
"""构造 RSS 候选上下文。"""
|
|
return Context(
|
|
meta_info=meta,
|
|
media_info=candidate,
|
|
torrent_info=torrent,
|
|
resource_source="rss",
|
|
match_source="tmdbid",
|
|
candidate_recognized=True,
|
|
media_info_is_target=False,
|
|
)
|
|
|
|
|
|
def _reconcile(target, candidate, meta, torrent, context):
|
|
"""调用订阅链的候选媒体冲突复核。"""
|
|
return SubscribeChain._SubscribeChain__reconcile_candidate_media(
|
|
target_mediainfo=target,
|
|
candidate_mediainfo=candidate,
|
|
torrent_meta=meta,
|
|
torrent_info=torrent,
|
|
context=context,
|
|
)
|
|
|
|
|
|
def test_inferred_tmdb_conflict_falls_back_to_strict_alias_match():
|
|
"""标题推断到重复 TMDB 条目时应允许订阅目标别名复核。"""
|
|
target = _target_media()
|
|
candidate = _candidate_media()
|
|
meta = _torrent_meta()
|
|
torrent = TorrentInfo(
|
|
title="A Record Of A Mortals Journey To Immortality S01E186 2160p WEB-DL",
|
|
site_name="测试站点",
|
|
category=MediaType.TV.value,
|
|
)
|
|
context = _context(meta, candidate, torrent)
|
|
|
|
result = _reconcile(target, candidate, meta, torrent, context)
|
|
|
|
assert result is target
|
|
assert context.media_info is target
|
|
assert context.match_source == "title"
|
|
assert context.candidate_recognized is False
|
|
assert context.media_info_is_target is True
|
|
|
|
|
|
def test_no_year_alias_rejects_candidate_with_different_first_air_year():
|
|
"""无年份别名命中另一个首播年份的同名作品时应拒绝。"""
|
|
target = _target_media(
|
|
tmdb_id=236356,
|
|
title="家族计划",
|
|
original_title="가족계획",
|
|
names=["Family Matters"],
|
|
year="2024",
|
|
original_language="ko",
|
|
season_years={1: "2024"},
|
|
)
|
|
candidate = _candidate_media(
|
|
tmdb_id=30161,
|
|
title="Family Matters",
|
|
original_title="Family Matters",
|
|
year="2008",
|
|
original_language="en",
|
|
)
|
|
meta = _torrent_meta(en_name="Family Matters")
|
|
torrent = TorrentInfo(
|
|
title="Family Matters S01 1080p WEBRip DD2.0 x264-TrollHD",
|
|
site_name="测试站点",
|
|
category=MediaType.TV.value,
|
|
)
|
|
context = _context(meta, candidate, torrent)
|
|
|
|
assert _reconcile(target, candidate, meta, torrent, context) is None
|
|
assert context.media_info is candidate
|
|
assert context.match_source == "tmdbid"
|
|
|
|
|
|
def test_explicit_target_year_can_override_wrong_same_name_candidate():
|
|
"""资源明确携带目标年份时应允许纠正同名候选的错误识别。"""
|
|
target = _target_media(
|
|
tmdb_id=236356,
|
|
title="家族计划",
|
|
original_title="가족계획",
|
|
names=["Family Matters"],
|
|
year="2024",
|
|
original_language="ko",
|
|
season_years={1: "2024"},
|
|
)
|
|
candidate = _candidate_media(
|
|
tmdb_id=30161,
|
|
title="Family Matters",
|
|
original_title="Family Matters",
|
|
year="2008",
|
|
original_language="en",
|
|
)
|
|
meta = _torrent_meta(en_name="Family Matters")
|
|
meta.year = "2024"
|
|
torrent = TorrentInfo(
|
|
title="Family Matters 2024 S01 1080p WEB-DL",
|
|
site_name="测试站点",
|
|
category=MediaType.TV.value,
|
|
)
|
|
context = _context(meta, candidate, torrent)
|
|
|
|
assert _reconcile(target, candidate, meta, torrent, context) is target
|
|
assert context.media_info is target
|
|
assert context.match_source == "title"
|
|
|
|
|
|
def test_inferred_tmdb_conflict_rejects_nonmatching_title():
|
|
"""ID 冲突且标题和别名不匹配时应继续拒绝候选。"""
|
|
target = _target_media()
|
|
candidate = _candidate_media()
|
|
meta = _torrent_meta(en_name="Different Series")
|
|
torrent = TorrentInfo(
|
|
title="Different Series S01E186 2160p WEB-DL",
|
|
site_name="测试站点",
|
|
category=MediaType.TV.value,
|
|
)
|
|
context = _context(meta, candidate, torrent)
|
|
|
|
assert _reconcile(target, candidate, meta, torrent, context) is None
|
|
assert context.media_info is candidate
|
|
assert context.match_source == "tmdbid"
|
|
assert context.candidate_recognized is True
|
|
assert context.media_info_is_target is False
|
|
|
|
|
|
def test_inferred_douban_conflict_falls_back_to_strict_alias_match():
|
|
"""豆瓣 ID 推断冲突时应使用同一套严格标题复核。"""
|
|
target = _target_media(tmdb_id=None, douban_id="30170816")
|
|
candidate = _candidate_media(tmdb_id=None, douban_id="36612345")
|
|
meta = _torrent_meta()
|
|
torrent = TorrentInfo(
|
|
title="A Record Of A Mortals Journey To Immortality S01E186 2160p WEB-DL",
|
|
site_name="测试站点",
|
|
category=MediaType.TV.value,
|
|
)
|
|
context = _context(meta, candidate, torrent)
|
|
|
|
result = _reconcile(target, candidate, meta, torrent, context)
|
|
|
|
assert result is target
|
|
assert context.media_info is target
|
|
assert context.match_source == "title"
|
|
|
|
|
|
def test_explicit_tmdb_identity_keeps_strict_conflict_rejection():
|
|
"""标题显式携带媒体 ID 时不得被标题别名覆盖。"""
|
|
target = _target_media()
|
|
candidate = _candidate_media()
|
|
meta = _torrent_meta(tmdbid=285479)
|
|
torrent = TorrentInfo(
|
|
title="A Record Of A Mortals Journey To Immortality S01E186 {tmdbid=285479}",
|
|
site_name="测试站点",
|
|
category=MediaType.TV.value,
|
|
)
|
|
context = _context(meta, candidate, torrent)
|
|
|
|
assert _reconcile(target, candidate, meta, torrent, context) is None
|
|
assert context.media_info is candidate
|
|
assert context.match_source == "tmdbid"
|
|
|
|
|
|
def test_conflict_rejects_target_without_same_source_identity():
|
|
"""订阅目标缺少候选同来源 ID 时不得用标题跨来源放行。"""
|
|
target = _target_media(tmdb_id=None)
|
|
candidate = _candidate_media()
|
|
meta = _torrent_meta()
|
|
torrent = TorrentInfo(
|
|
title="A Record Of A Mortals Journey To Immortality S01E186",
|
|
site_name="测试站点",
|
|
category=MediaType.TV.value,
|
|
)
|
|
context = _context(meta, candidate, torrent)
|
|
|
|
assert _reconcile(target, candidate, meta, torrent, context) is None
|
|
assert context.media_info is candidate
|
|
|
|
|
|
def test_matching_media_id_preserves_candidate_identity():
|
|
"""媒体 ID 原本一致时应保持候选识别上下文。"""
|
|
target = _target_media()
|
|
candidate = _candidate_media(tmdb_id=106449)
|
|
meta = _torrent_meta()
|
|
torrent = TorrentInfo(
|
|
title="A Record Of A Mortals Journey To Immortality S01E186",
|
|
site_name="测试站点",
|
|
category=MediaType.TV.value,
|
|
)
|
|
context = _context(meta, candidate, torrent)
|
|
|
|
result = _reconcile(target, candidate, meta, torrent, context)
|
|
|
|
assert result is candidate
|
|
assert context.media_info is candidate
|
|
assert context.match_source == "tmdbid"
|
|
assert context.candidate_recognized is True
|
|
assert context.media_info_is_target is False
|