diff --git a/app/domain/meta/metavideo.py b/app/domain/meta/metavideo.py index d66e59131..20f827a91 100644 --- a/app/domain/meta/metavideo.py +++ b/app/domain/meta/metavideo.py @@ -259,10 +259,10 @@ class MetaVideo(MetaBase): media_exts: list, state: _VideoParseState, ) -> None: - """按固定优先级处理单个词元,首个命中的阶段终止后续识别。""" + """按固定优先级处理词元,并标记当前词元是否为输入末尾。""" if self.__init_part(token, tokens, state): return - if self.__init_name(token, media_exts, state): + if self.__init_name(token, media_exts, state, tokens.cur() is None): return if self.__init_year(token, state): return @@ -336,9 +336,10 @@ class MetaVideo(MetaBase): token: Optional[str], media_exts: list, state: _VideoParseState, + is_last_token: bool, ) -> bool: """ - 识别名称 + 识别名称,并只忽略原始文件名末尾的媒体扩展名。 """ if not token: return False @@ -422,7 +423,12 @@ class MetaVideo(MetaBase): return False else: # 后缀名不要 - if ".%s".lower() % token in media_exts: + suffix = f".{token.lower()}" + if ( + is_last_token + and suffix in media_exts + and (self.org_string or "").lower().endswith(suffix) + ): return False # 英文或者英文+数字,拼装起来 if self.en_name: diff --git a/tests/test_metainfo.py b/tests/test_metainfo.py index 302599b9f..3f21b86f6 100644 --- a/tests/test_metainfo.py +++ b/tests/test_metainfo.py @@ -5,6 +5,7 @@ from unittest.mock import patch import pytest +from app.domain import metainfo as metainfo_module from app.domain.context import MediaInfo from app.domain.metainfo import MetaInfo, MetaInfoPath, find_metainfo from app.domain.meta.metabase import MetaBase, MetaInfoSnapshot @@ -275,6 +276,34 @@ def test_python_metainfo_fallback_preserves_xxx_movie_title(): assert meta.audio_encode == "DDP 5.1" +@pytest.mark.parametrize("use_path", [False, True]) +def test_python_metainfo_fallback_keeps_title_token_matching_subtitle_extension( + use_path, monkeypatch +): + """Python 回退解析在标题和路径入口都应保留与字幕扩展名同名的词元。""" + title = ( + "Kick-Ass.2010.PROPER.2160p.BluRay.REMUX.HEVC.DTS-HD.MA." + "TrueHD.7.1.Atmos-FGT.mkv" + ) + monkeypatch.setattr( + metainfo_module, + "get_media_extensions", + lambda: (".mkv", ".ass"), + ) + with ( + patch("app.adapters.system.rust.parse_metainfo", return_value=None), + patch("app.adapters.system.rust.parse_metainfo_path", return_value=None), + ): + meta = ( + MetaInfoPath(Path("/movies") / title) + if use_path + else MetaInfo(title) + ) + + assert meta.en_name == "Kick Ass" + assert meta.year == "2010" + + def test_python_metainfo_fallback_recognizes_eac3_audio_codec(): """Python 兜底解析应识别 EAC3 及其声道信息。""" with patch("app.adapters.system.rust.parse_metainfo", return_value=None): diff --git a/tests/test_rust_accel.py b/tests/test_rust_accel.py index ac2077747..73276cb5c 100644 --- a/tests/test_rust_accel.py +++ b/tests/test_rust_accel.py @@ -238,6 +238,25 @@ def test_rust_metainfo_parser_handles_video_from_entry(): assert result["fps"] == 120 +def test_rust_metainfo_keeps_title_token_matching_subtitle_extension(): + """Rust 标题和路径入口都应保留与字幕扩展名同名的片名词元。""" + title = ( + "Kick-Ass.2010.PROPER.2160p.BluRay.REMUX.HEVC.DTS-HD.MA." + "TrueHD.7.1.Atmos-FGT.mkv" + ) + options = _metainfo_options() + parsed_title = rust_accel.parse_metainfo(title, options=options) + parsed_path = rust_accel.parse_metainfo_path( + f"/movies/{title}", + options=options, + ) + + assert parsed_title["en_name"] == "Kick Ass" + assert parsed_title["year"] == "2010" + assert parsed_path["en_name"] == "Kick Ass" + assert parsed_path["year"] == "2010" + + def test_rust_metainfo_parser_handles_anime_from_entry(): """ Rust MetaInfo 入口应完整识别 Anime 标题。