fix(music): accept multi-file album release names

This commit is contained in:
jxxghp
2026-11-09 18:41:43 +08:00
parent 252d05767f
commit db0730f65f
3 changed files with 49 additions and 2 deletions

View File

@@ -321,9 +321,15 @@ _MUSIC_RIP_METHOD_RE = re.compile(r"(?:分轨|分軌|整轨|整軌|原抓|自抓
_MUSIC_AUDIO_RELEASE_TAIL_RE = re.compile(
rf"(?<!\d)(?P<year>(?:19|20)\d{{2}})\s*[-–—−-]\s*"
rf"(?:{_MUSIC_FORMAT_TOKEN_ALT})(?:\s*(?:分[轨軌]|整[轨軌]|原抓|自抓|自扫|自掃))*"
r"(?:\s+multi[\s_-]*file)?"
r"(?:\s*[-–—−-]\s*[^\s\-–—−-]+){0,4}\s*$",
re.IGNORECASE,
)
# OpenCD 等站点用 ``格式 Multi File`` 表示分轨资源;保留格式词,去掉文件形态尾标。
_MUSIC_MULTI_FILE_SUFFIX_RE = re.compile(
rf"\s+(?P<format>{_MUSIC_FORMAT_TOKEN_ALT})\s+multi[\s_-]*file"
r"(?:\s*[-–—−-]\s*[A-Za-z0-9][A-Za-z0-9@._-]{1,20})?\s*$", re.IGNORECASE,
)
# 圆括号中的联合位深声明(24/48bit)及规格剔除后留下的空括号。
_MUSIC_PAREN_SPEC_RE = re.compile(
r"[\((]\s*\d{1,3}\s*/\s*\d{1,3}\s*-?\s*bits?\s*[\))]",
@@ -1499,7 +1505,8 @@ class MetaMusic(MetaBase):
为两种解析引擎统一清理有格式证据的发行尾标与音频文件曲序。
点分发行类型只在音频规格前生效;版本括号保留语义,介质和目录编号
不进入检索。斜杠规格尾链整段剥离并保留年份,正文中的斜杠仍保留。
不进入检索。斜杠规格尾链和 ``FLAC Multi File`` 分轨尾标整段剥离并保留年份,
正文中的斜杠仍保留。
文件曲序先于艺人拆分,避免 ``01. Artist`` 成为艺人名。
:param value: 原始音乐资源标题或文件名
@@ -1507,6 +1514,8 @@ class MetaMusic(MetaBase):
"""
text, file_title = self._prepare_filename_input(value)
text = _MUSIC_SLASH_SPEC_TAIL_RE.sub(r" \g<year>", text)
text = _MUSIC_AUDIO_RELEASE_TAIL_RE.sub(r" \g<year>", text)
text = _MUSIC_MULTI_FILE_SUFFIX_RE.sub(r" \g<format>", text)
text = _MUSIC_EDITION_BRACKET_RE.sub(r"(\1)", text)
text = _MUSIC_CATALOG_SPEC_RE.sub("", text)
if re.search(r"(?:19|20)\d{2}", text) and len(_SCENE_DOT_RE.findall(value)) >= 3:

View File

@@ -915,12 +915,30 @@ def _music_title_key(value: Optional[str], *, preserve_editions: bool = False) -
return re.sub(r"\s+", "", base).casefold()
def _music_album_main_title(value: Optional[str], *, preserve_editions: bool = False) -> str:
"""
提取带冒号说明的专辑主标题,兼容资源名省略说明性副标题的常见命名。
"""
text = normalize("NFKC", str(value or "")).strip()
if not re.search(r"\s*[::]\s*", text):
return ""
main = re.split(r"\s*[::]\s*", text, maxsplit=1)[0]
key = _music_title_key(main, preserve_editions=preserve_editions)
return key if key != _music_title_key(text, preserve_editions=preserve_editions) else ""
def music_title_matches(music: MusicInfo, title: Optional[str], *, preserve_editions: bool = False) -> bool:
"""
比较完整名称及可信双语别名,保留纯符号名称、未知括号和发行版的身份约束。
比较完整名称、可信别名及专辑主标题,保留纯符号名称和发行版的身份约束。
"""
key = partial(_music_title_key, preserve_editions=preserve_editions)
names = {key(name) for name in music_titles(music)}
if music.music_type == MUSIC_ENTITY_ALBUM:
names.update(
main
for name in music_titles(music, album=True)
if (main := _music_album_main_title(name, preserve_editions=preserve_editions))
)
expected = _music_name_key(title, names, key)
return bool(expected and expected in names)

View File

@@ -128,6 +128,26 @@ def test_release_names_without_group_or_cjk_credit(title, artists):
assert (meta.title, meta.artists, meta.year, meta.audio_format) == ("Album", artists, 2022, "FLAC")
@pytest.mark.usefixtures("music_engine")
def test_multi_file_release_suffix_matches_album_main_title():
"""
分轨资源常用的 ``FLAC Multi File`` 尾标不应进入专辑标题,专辑副标题省略时仍应命中主标题。
"""
resource = "Various Artists - The Secret Life Of Walter Mitty 2013 - FLAC Multi File"
meta = MetaMusic.parse_resource(resource)
target = MusicInfo(
music_type="album",
title="The Secret Life of Walter Mitty: Music From and Inspired by the Motion Picture",
artists=["Various Artists"],
year=2013,
)
assert (meta.title, meta.year, meta.artists) == (
"The Secret Life Of Walter Mitty", 2013, ["Various Artists"],
)
assert match_music_resource(target, resource).status == "exact"
@pytest.mark.usefixtures("music_engine")
@pytest.mark.parametrize("title,expected,year", [
(