mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-07 00:16:57 +08:00
fix(search): disambiguate yearless same-title resources
This commit is contained in:
@@ -5,6 +5,7 @@ from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from app.domain.context import MediaInfo
|
||||
from app.domain.metainfo import MetaInfo, MetaInfoPath, find_metainfo
|
||||
from app.domain.meta.metabase import MetaBase
|
||||
from app.domain.meta.metamusic import MetaMusic
|
||||
@@ -160,6 +161,29 @@ def test_torrent_title_match_ignores_question_mark_variants():
|
||||
)
|
||||
|
||||
|
||||
def test_torrent_title_match_rejects_season_absent_from_target_series():
|
||||
"""无年份同名剧的资源季超出目标剧季范围时应拒绝。"""
|
||||
mediainfo = MediaInfo(
|
||||
title="家族计划",
|
||||
original_title="가족계획",
|
||||
names=["Family Matters"],
|
||||
type=MediaType.TV,
|
||||
year="2024",
|
||||
number_of_seasons=1,
|
||||
seasons={1: list(range(1, 7))},
|
||||
season_years={1: "2024"},
|
||||
)
|
||||
torrent_meta = MetaInfo("Family Matters S02 1080p WEBRip DD2.0 x264-TrollHD")
|
||||
torrent = SimpleNamespace(
|
||||
site_name="测试站点",
|
||||
title=torrent_meta.org_string,
|
||||
category=MediaType.TV.value,
|
||||
description=None,
|
||||
)
|
||||
|
||||
assert not TorrentHelper.match_torrent(mediainfo, torrent_meta, torrent)
|
||||
|
||||
|
||||
def test_python_metainfo_fallback_preserves_xxx_movie_title():
|
||||
"""Python 兜底解析不应删除合法 xXx 片名。"""
|
||||
with patch("app.adapters.system.rust.parse_metainfo", return_value=None):
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
from unittest.mock import Mock
|
||||
|
||||
from app.chain import search as search_module
|
||||
from app.chain.search import SearchChain
|
||||
from app.domain.context import MediaInfo, TorrentInfo
|
||||
from app.schemas.types import MediaSource, MediaType
|
||||
|
||||
|
||||
def test_exact_search_rejects_no_year_alias_recognized_as_different_work(monkeypatch):
|
||||
"""精确搜索中的无年份别名候选识别为不同作品时应拒绝。"""
|
||||
target = MediaInfo(
|
||||
media_source=MediaSource.TMDB,
|
||||
media_id="236356",
|
||||
tmdb_id=236356,
|
||||
title="家族计划",
|
||||
original_title="가족계획",
|
||||
names=["Family Matters"],
|
||||
type=MediaType.TV,
|
||||
year="2024",
|
||||
original_language="ko",
|
||||
season_years={1: "2024"},
|
||||
)
|
||||
candidate = MediaInfo(
|
||||
media_source=MediaSource.TMDB,
|
||||
media_id="30161",
|
||||
tmdb_id=30161,
|
||||
title="Family Matters",
|
||||
original_title="Family Matters",
|
||||
type=MediaType.TV,
|
||||
year="2008",
|
||||
original_language="en",
|
||||
)
|
||||
torrent = TorrentInfo(
|
||||
title="Family Matters S01 1080p WEBRip DD2.0 x264-TrollHD",
|
||||
site_name="测试站点",
|
||||
category=MediaType.TV.value,
|
||||
)
|
||||
media_chain = Mock()
|
||||
media_chain.recognize_by_meta.return_value = candidate
|
||||
monkeypatch.setattr(search_module, "MediaChain", Mock(return_value=media_chain))
|
||||
monkeypatch.setattr(
|
||||
search_module.TorrentHelper,
|
||||
"sort_torrents",
|
||||
staticmethod(lambda contexts: contexts),
|
||||
)
|
||||
chain = object.__new__(SearchChain)
|
||||
|
||||
contexts = chain._SearchChain__parse_result(
|
||||
torrents=[torrent],
|
||||
mediainfo=target,
|
||||
rule_groups=[],
|
||||
)
|
||||
|
||||
assert contexts == []
|
||||
media_chain.recognize_by_meta.assert_called_once()
|
||||
|
||||
|
||||
def test_exact_search_reuses_disambiguation_for_same_parsed_title(monkeypatch):
|
||||
"""同一解析标题的多条资源应复用一次候选识别,避免重复外部查询。"""
|
||||
target = MediaInfo(
|
||||
media_source=MediaSource.TMDB,
|
||||
media_id="236356",
|
||||
tmdb_id=236356,
|
||||
title="家族计划",
|
||||
original_title="가족계획",
|
||||
names=["Family Matters"],
|
||||
type=MediaType.TV,
|
||||
year="2024",
|
||||
season_years={1: "2024"},
|
||||
)
|
||||
candidate = MediaInfo(
|
||||
media_source=MediaSource.TMDB,
|
||||
media_id="236356",
|
||||
tmdb_id=236356,
|
||||
title="家族计划",
|
||||
type=MediaType.TV,
|
||||
year="2024",
|
||||
)
|
||||
torrents = [
|
||||
TorrentInfo(
|
||||
title=f"Family Matters S01 1080p WEB-DL {group}",
|
||||
site_name=f"测试站点{index}",
|
||||
category=MediaType.TV.value,
|
||||
)
|
||||
for index, group in enumerate(("GROUP-A", "GROUP-B"), start=1)
|
||||
]
|
||||
media_chain = Mock()
|
||||
media_chain.recognize_by_meta.return_value = candidate
|
||||
monkeypatch.setattr(search_module, "MediaChain", Mock(return_value=media_chain))
|
||||
monkeypatch.setattr(
|
||||
search_module.TorrentHelper,
|
||||
"sort_torrents",
|
||||
staticmethod(lambda contexts: contexts),
|
||||
)
|
||||
chain = object.__new__(SearchChain)
|
||||
|
||||
contexts = chain._SearchChain__parse_result(
|
||||
torrents=torrents,
|
||||
mediainfo=target,
|
||||
rule_groups=[],
|
||||
)
|
||||
|
||||
assert len(contexts) == 2
|
||||
media_chain.recognize_by_meta.assert_called_once()
|
||||
@@ -5,36 +5,44 @@ from app.domain.context import Context, MediaInfo, TorrentInfo
|
||||
from app.schemas.types import MediaSource, MediaType
|
||||
|
||||
|
||||
def _target_media(tmdb_id=106449, douban_id=None) -> MediaInfo:
|
||||
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,
|
||||
title="凡人修仙传",
|
||||
original_title="凡人修仙传",
|
||||
names=["A Record Of A Mortals Journey To Immortality"],
|
||||
type=MediaType.TV,
|
||||
year="2020",
|
||||
season_years={1: "2020"},
|
||||
tmdb_id=tmdb_id,
|
||||
douban_id=douban_id,
|
||||
**defaults,
|
||||
)
|
||||
|
||||
|
||||
def _candidate_media(tmdb_id=285479, douban_id=None) -> MediaInfo:
|
||||
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,
|
||||
title="凡人修仙传",
|
||||
type=MediaType.TV,
|
||||
year="2020",
|
||||
tmdb_id=tmdb_id,
|
||||
douban_id=douban_id,
|
||||
**defaults,
|
||||
)
|
||||
|
||||
|
||||
@@ -96,6 +104,69 @@ def test_inferred_tmdb_conflict_falls_back_to_strict_alias_match():
|
||||
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()
|
||||
|
||||
Reference in New Issue
Block a user