From 15c73240ac3ff3714c566292c828ed8d38db7f26 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Sun, 9 Aug 2026 16:32:12 +0800 Subject: [PATCH] fix(download): apply custom words to torrent episodes (#6234) --- app/chain/download.py | 11 +++++++-- app/helper/torrent.py | 8 +++++-- tests/test_download_chain.py | 45 +++++++++++++++++++++++++++++++++++- tests/test_metainfo.py | 15 ++++++++++++ 4 files changed, 74 insertions(+), 5 deletions(-) diff --git a/app/chain/download.py b/app/chain/download.py index 14455ed5b..b551037ac 100644 --- a/app/chain/download.py +++ b/app/chain/download.py @@ -1097,6 +1097,7 @@ class DownloadChain(ChainBase): """ # 已下载的项目 downloaded_list: List[Context] = [] + custom_word_list = custom_words.splitlines() if custom_words else None def __update_seasons(_mid: Union[int, str], _need: list, _current: list) -> list: """ @@ -1357,7 +1358,10 @@ class DownloadChain(ChainBase): if isinstance(content, str): logger.warn(f"{meta.org_string} 下载地址是磁力链,无法确定种子文件集数") continue - torrent_episodes = TorrentHelper().get_torrent_episodes(torrent_files) + torrent_episodes = TorrentHelper().get_torrent_episodes( + torrent_files, + custom_words=custom_word_list, + ) logger.info(f"{meta.org_string} 解析种子文件集数为 {torrent_episodes}") if not torrent_episodes: continue @@ -1586,7 +1590,10 @@ class DownloadChain(ChainBase): logger.warn(f"{meta.org_string} 下载地址是磁力链,无法解析种子文件集数") continue # 种子全部集 - torrent_episodes = TorrentHelper().get_torrent_episodes(torrent_files) + torrent_episodes = TorrentHelper().get_torrent_episodes( + torrent_files, + custom_words=custom_word_list, + ) logger.info(f"{torrent.site_name} - {meta.org_string} 解析种子文件集数:{torrent_episodes}") # 选中的集 selected_episodes = set(torrent_episodes).intersection(effective_need) diff --git a/app/helper/torrent.py b/app/helper/torrent.py index ad993d1ae..11b895dc2 100644 --- a/app/helper/torrent.py +++ b/app/helper/torrent.py @@ -366,9 +366,13 @@ class TorrentHelper: return result @staticmethod - def get_torrent_episodes(files: list) -> list: + def get_torrent_episodes(files: list, custom_words: Optional[List[str]] = None) -> list: """ 从种子的文件清单中获取所有集数 + + :param files: 种子文件清单 + :param custom_words: 当前下载来源的临时自定义识别词 + :return: 识别到的全部集数 """ episodes = [] for file in files: @@ -378,7 +382,7 @@ class TorrentHelper: if not file_path.suffix or file_path.suffix.lower() not in settings.RMT_MEDIAEXT: continue # 只使用文件名识别 - meta = MetaInfo(file_path.name) + meta = MetaInfo(file_path.name, custom_words=custom_words) if not meta.begin_episode: continue episodes = list(set(episodes).union(set(meta.episode_list))) diff --git a/tests/test_download_chain.py b/tests/test_download_chain.py index 817c5863d..6e28509fa 100644 --- a/tests/test_download_chain.py +++ b/tests/test_download_chain.py @@ -426,6 +426,7 @@ class _FakeBatchTorrentHelper: """ episodes = [] + received_custom_words = None def sort_torrents(self, contexts): """ @@ -452,7 +453,8 @@ class _FakeBatchTorrentHelper: results.append(context) return results - def get_torrent_episodes(self, _files): + def get_torrent_episodes(self, _files, custom_words=None): + type(self).received_custom_words = custom_words return list(self.episodes) @@ -675,6 +677,47 @@ def test_batch_download_threads_custom_words_to_download_single(monkeypatch): assert chain.download_single.call_args.kwargs["custom_words"] == custom_words +def test_batch_download_applies_custom_words_to_torrent_file_episodes(monkeypatch): + """订阅识别词须用于种子文件集数解析,确保跨季映射后能选中缺失集。""" + _FakeBatchTorrentHelper.episodes = [170] + _FakeBatchTorrentHelper.received_custom_words = None + monkeypatch.setattr(download_module, "TorrentHelper", _FakeBatchTorrentHelper) + monkeypatch.setattr(download_module.eventmanager, "send_event", lambda *args, **kwargs: None) + + chain = DownloadChain.__new__(DownloadChain) + chain.download_torrent = MagicMock( + return_value=(b"torrent-content", "", ["A.Will.Eternal.S04E05.mkv"]), + ) + chain.download_single = MagicMock(return_value="hash") + + context = _build_tv_context() + no_exists = { + 1: { + 1: NotExistMediaInfo( + season=1, + episodes=[170], + total_episode=200, + start_episode=155, + ) + } + } + custom_words = ( + "A.Will.Eternal.S04 => 一念永恒{[tmdbid=107371;type=tv]}S01 " + "&& S01 <> 2160p >> EP+165" + ) + + downloads, lefts = chain.batch_download( + contexts=[context], + no_exists=no_exists, + custom_words=custom_words, + ) + + assert downloads == [context] + assert lefts == {} + assert _FakeBatchTorrentHelper.received_custom_words == [custom_words] + assert chain.download_single.call_args.kwargs["episodes"] == {170} + + def test_download_single_records_failure_cooldown_when_downloader_rejects(monkeypatch): """ 下载器拒绝种子且没有返回 hash 时,应记录资源级失败冷却。 diff --git a/tests/test_metainfo.py b/tests/test_metainfo.py index 30af767da..b4ee83335 100644 --- a/tests/test_metainfo.py +++ b/tests/test_metainfo.py @@ -314,6 +314,21 @@ def test_custom_words_replace_then_episode_offset(): assert meta.apply_words == custom_words +def test_get_torrent_episodes_applies_custom_words(): + """种子文件集数解析应使用订阅识别词完成跨季集数映射。""" + custom_words = [ + "A.Will.Eternal.S04 => 一念永恒{[tmdbid=107371;type=tv]}S01 " + "&& S01 <> 2160p >> EP+165" + ] + + episodes = TorrentHelper.get_torrent_episodes( + ["A.Will.Eternal.S04E05.2026.2160p.WEB-DL.mkv"], + custom_words=custom_words, + ) + + assert episodes == [170] + + def test_custom_words_episode_offset_supports_multiplication_expression(): """测试集数偏移表达式支持乘法和连续运算。""" custom_words = [