Merge pull request #7119 from JontyLee/fix/music-suffix-disc-directory

This commit is contained in:
jxxghp
2026-10-21 10:59:39 +08:00
committed by GitHub
3 changed files with 82 additions and 0 deletions

View File

@@ -468,6 +468,11 @@ _MUSIC_DISC_DIR_RE = re.compile(
r"^\s*(?:cd|disc|disk)\s*(?P<num>\d{1,2})\s*$",
re.IGNORECASE,
)
# 碟号后缀在目录名末尾的碟片目录:陈奕迅 - 十年经典 CD1、专辑名 Disc 2
_MUSIC_DISC_DIR_SUFFIX_RE = re.compile(
r"^(?:.*[\s\-–—_.,、/\\])?(?:cd|disc|disk)\s*(?P<num>\d{1,2})\s*$",
re.IGNORECASE,
)
# 目录名中的年份:(2004)、[2004]
_MUSIC_DIR_YEAR_RE = re.compile(r"[(\[]\s*(?P<year>(?:19|20)\d{2})\s*[)\]]")
_MUSIC_DIR_PREFIX_YEAR_RE = re.compile(
@@ -2174,6 +2179,14 @@ class MetaMusic(MetaBase):
match = _MUSIC_DISC_DIR_RE.match(str(name or "").strip())
return int(match.group("num")) if match and int(match.group("num")) > 0 else None
@classmethod
def parse_disc_dir_suffix(cls, name: str) -> Optional[int]:
"""
识别把碟号后缀在目录名末尾的碟片目录并返回碟号,如 `陈奕迅 - 十年经典 CD1`。
"""
match = _MUSIC_DISC_DIR_SUFFIX_RE.match(str(name or "").strip())
return int(match.group("num")) if match and int(match.group("num")) > 0 else None
@staticmethod
def _normalize_album_year(value: str) -> str:
"""
@@ -2292,6 +2305,10 @@ class MetaMusic(MetaBase):
self.disc_number = parent_disc
self.field_sources["disc_number"] = "directory"
album_dir = parent.parent
elif self.disc_number is None and (suffix_disc := self.parse_disc_dir_suffix(parent.name)) is not None:
# 碟号写进专辑目录名末尾时只补碟号,该目录仍是专辑目录,不能上移专辑根。
self.disc_number = suffix_disc
self.field_sources["disc_number"] = "directory"
dir_info = self.parse_album_dir(album_dir.name)
if dir_info:
# 文件已明确歌手时,纯专辑名目录也可补足检索线索;通用监控根目录仍须排除。

View File

@@ -605,6 +605,41 @@ def test_parse_disc_dir():
assert MetaMusic.parse_disc_dir("无损音乐") is None
def test_parse_disc_dir_suffix():
"""
碟号后缀在专辑目录名末尾时识别碟号,且不得误读非碟片后缀的目录名。
"""
assert MetaMusic.parse_disc_dir_suffix("陈奕迅 - 十年经典 CD1") == 1
assert MetaMusic.parse_disc_dir_suffix("周杰伦 哎哟不错哦 Disc 2") == 2
assert MetaMusic.parse_disc_dir_suffix("陈奕迅 DUO 演唱会 2CD") is None
assert MetaMusic.parse_disc_dir_suffix("2CD1") is None
assert MetaMusic.parse_disc_dir_suffix("某某专辑 CD100") is None
assert MetaMusic.parse_disc_dir_suffix("Discipline 1") is None
assert MetaMusic.parse_disc_dir_suffix("The Best disco") is None
def test_apply_path_context_reads_suffix_disc_without_moving_album_root():
"""
碟号后缀目录只补碟号,专辑线索仍来自该目录本身,多碟同曲序不会互相覆盖。
"""
disc_two = MetaMusic(
org_string="01 你的背包.flac",
title="01 你的背包",
).apply_path_context(Path("陈奕迅 - 十年经典 CD2/01 你的背包.flac"))
album_root = MetaMusic(
org_string="01 你的背包.flac",
title="01 你的背包",
).apply_path_context(Path("陈奕迅 - 十年经典/CD2/01 你的背包.flac"))
assert disc_two.disc_number == 2
assert disc_two.track_number == 1
assert disc_two.field_sources["disc_number"] == "directory"
assert disc_two.album == "十年经典 CD2"
# 独立碟片子目录仍把专辑根上移一级,两种布局的碟号证据都必须可用。
assert album_root.disc_number == 2
assert album_root.album == "十年经典"
def test_parse_album_dir_extracts_artist_album_year():
"""
专辑目录名应提取歌手、专辑、年份和音质描述。

View File

@@ -139,6 +139,36 @@ def test_album_resource_dedupes_same_track_in_different_formats():
assert context.confirmed_full_coverage is False
def test_album_resource_keeps_suffix_disc_directory_tracks_separate():
"""
碟号后缀在顶层目录名中的多碟专辑,两盘同曲序不能压成同一音轨键。
"""
context = Context(media_info=_album_info(total_tracks=6))
DownloadChain._record_music_album_track_keys(
context,
[
"陈奕迅 - 十年经典 CD1/01 K歌之王.flac",
"陈奕迅 - 十年经典 CD1/02 富士山下.flac",
"陈奕迅 - 十年经典 CD1/03 你的背包.flac",
"陈奕迅 - 十年经典 CD2/01 你的背包.flac",
"陈奕迅 - 十年经典 CD2/02 Shall We Talk.flac",
"陈奕迅 - 十年经典 CD2/03 不如不见.flac",
],
)
assert context.music_track_keys == ["[1,1]", "[1,2]", "[1,3]", "[2,1]", "[2,2]", "[2,3]"]
# 独立碟片子目录的既有布局必须保持同样的键集,修复不能改变已正确的解析。
nested = Context(media_info=_album_info(total_tracks=6))
DownloadChain._record_music_album_track_keys(
nested,
[f"十年经典/CD{disc}/{track:02d} 曲目.flac" for disc in (1, 2) for track in range(1, 4)],
)
assert nested.music_track_keys == context.music_track_keys
assert context.confirmed_full_coverage is False
def test_download_single_prepares_partial_album_for_client_submission():
"""
下载入口允许不完整专辑进入下载器,并把本轮音轨事实附着到上下文。