From 1855ba81eccc32fd0be9b9273ba0dc29ff007138 Mon Sep 17 00:00:00 2001 From: freeman Date: Mon, 13 Jul 2026 16:50:00 +0800 Subject: [PATCH] =?UTF-8?q?fix(meta):=20=E5=89=AF=E6=A0=87=E9=A2=98?= =?UTF-8?q?=E8=AF=86=E5=88=AB=2001-26Fin=20=E7=AD=89=E6=95=B0=E5=AD=97?= =?UTF-8?q?=E8=8C=83=E5=9B=B4=E5=AE=8C=E7=BB=93=E6=A0=87=E8=AE=B0=E9=9B=86?= =?UTF-8?q?=E6=95=B0=20(#6105)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/core/meta/metabase.py | 37 +++++++++++++++++++++++++ tests/test_metainfo.py | 58 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) diff --git a/app/core/meta/metabase.py b/app/core/meta/metabase.py index 12b1160d7..6ad44d29a 100644 --- a/app/core/meta/metabase.py +++ b/app/core/meta/metabase.py @@ -24,6 +24,13 @@ SUBTITLE_EPISODE_ALL_RE = re.compile( r"([0-9一二三四五六七八九十百零]+)\s*集\s*全|[全共]\s*([0-9一二三四五六七八九十百零]+)\s*[集话話期幕]", re.IGNORECASE, ) +# 01-26Fin / [01-38 END] / 01-24完结 等"数字范围+完结标记"格式,完结标记必须 +# 存在以避免年份范围(如 2019-2020)误识别为集数;Fin/End 后不能紧跟字母数字, +# 避免 Final/Ending 等单词前缀误匹配 +SUBTITLE_EPISODE_RANGE_FIN_RE = re.compile( + r"(?8|10|12|16)[\s._-]*bits?(?![A-Za-z0-9])", re.IGNORECASE, @@ -292,6 +299,36 @@ class MetaBase(object): self.type = MediaType.TV self._subtitle_flag = True return + # 01-26Fin 等数字范围+完结标记 + self.__init_episode_range_fin(title_text) + else: + # 副标题无中文季集标记时,仍识别 01-26Fin 等数字范围+完结标记 + self.__init_episode_range_fin(title_text) + + def __init_episode_range_fin(self, title_text: str): + """ + 识别 01-26Fin / [01-38 END] 等"数字范围+完结标记"格式的集数信息 + """ + episode_range_str = SUBTITLE_EPISODE_RANGE_FIN_RE.search(title_text) + if not episode_range_str: + return + try: + begin_episode = int(episode_range_str.group(1)) + end_episode = int(episode_range_str.group(2)) + except Exception as err: + logger.debug(f'识别集失败:{str(err)} - {traceback.format_exc()}') + return + if begin_episode < 1 or begin_episode > end_episode or end_episode >= 10000: + return + # 两个数字都落在常见年份区间时视为年份范围而非集数(如 2019-2020完结) + if begin_episode >= 1900 and end_episode <= 2155: + return + if self.begin_episode is None: + self.begin_episode = begin_episode + self.end_episode = end_episode + self.total_episode = end_episode + self.type = MediaType.TV + self._subtitle_flag = True @property def season(self) -> str: diff --git a/tests/test_metainfo.py b/tests/test_metainfo.py index bf2d6857b..8655b1070 100644 --- a/tests/test_metainfo.py +++ b/tests/test_metainfo.py @@ -169,6 +169,64 @@ def test_python_metainfo_fallback_preserves_xxx_movie_title(): assert meta.audio_encode == "DDP 5.1" +def test_python_subtitle_episode_range_fin_with_chinese_season(): + """Python 兜底解析应识别副标题中 [01-26Fin] 格式的集数范围(#6103)。""" + with patch("app.core.metainfo.rust_accel.parse_metainfo", return_value=None): + meta = MetaInfo( + title="JoJos Bizarre Adventure S01 2012 1080i BluRay x264 FLAC 2.0-AnimeF@ADE", + subtitle="JOJO的奇妙冒险 第一季 / JoJo's Bizarre Adventure [01-26Fin] [简繁字幕]", + ) + + assert meta.type == MediaType.TV + assert meta.begin_season == 1 + assert meta.begin_episode == 1 + assert meta.end_episode == 26 + assert meta.total_episode == 26 + + +def test_python_subtitle_episode_range_fin_without_chinese_marker(): + """副标题无中文季集标记时也应识别 [01-38Fin] 集数范围。""" + with patch("app.core.metainfo.rust_accel.parse_metainfo", return_value=None): + meta = MetaInfo( + title="Some Show S01 2022 1080p WEB-DL H264-GRP", + subtitle="Some Show [01-38Fin]", + ) + + assert meta.begin_episode == 1 + assert meta.end_episode == 38 + assert meta.total_episode == 38 + + +def test_python_subtitle_episode_range_end_variant(): + """END/完结 等完结标记变体同样应识别为集数范围。""" + with patch("app.core.metainfo.rust_accel.parse_metainfo", return_value=None): + meta_end = MetaInfo( + title="Some Show S01 2022 1080p WEB-DL H264-GRP", + subtitle="Some Show 01-24 END", + ) + meta_cn = MetaInfo( + title="Some Show S01 2022 1080p WEB-DL H264-GRP", + subtitle="某剧 01-12完结", + ) + + assert meta_end.begin_episode == 1 + assert meta_end.end_episode == 24 + assert meta_cn.begin_episode == 1 + assert meta_cn.end_episode == 12 + + +def test_python_subtitle_year_range_not_treated_as_episodes(): + """年份范围(如 2019-2020)不得误识别为集数。""" + with patch("app.core.metainfo.rust_accel.parse_metainfo", return_value=None): + meta = MetaInfo( + title="Some Collection 2020 1080p WEB-DL H264-GRP", + subtitle="A Collection [2019-2020Fin]", + ) + + assert meta.begin_episode is None + assert meta.total_episode == 0 + + def test_custom_words_replace_then_episode_offset(): """测试复杂识别词仍按先替换、后集数偏移的顺序处理。""" custom_words = ["旧名 => 新名 && 第 <> 集 >> EP+1"]