mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-14 02:05:13 +08:00
fix(music): 艺术家-年份-专辑三明治结构拆分提取发行年份
This commit is contained in:
@@ -580,9 +580,18 @@ class MetaMusic(MetaBase):
|
||||
self.artists = artists
|
||||
self._finalize_title(self._clean_tail(title))
|
||||
else:
|
||||
title = self._clean_tail(cleaned)
|
||||
# 无艺术家线索时剥离 CJK 标题尾部的「曲名-歌手」署名,候选比对阶段负责验证身份
|
||||
self._finalize_title(self._strip_cjk_artist_suffix(title))
|
||||
# 「艺术家 年份 专辑」三明治结构(Leehom Wang 2010 The 18 Martial Arts),
|
||||
# 其他拆分均无艺术家线索时按中部年份拆分,年份提取为发行线索
|
||||
artists, title, inner_year = self._split_year_sandwich(cleaned)
|
||||
if artists:
|
||||
self.artists = artists
|
||||
if inner_year and not self.year:
|
||||
self.year = inner_year
|
||||
self._finalize_title(self._clean_tail(title))
|
||||
else:
|
||||
title = self._clean_tail(cleaned)
|
||||
# 无艺术家线索时剥离 CJK 标题尾部的「曲名-歌手」署名,候选比对阶段负责验证身份
|
||||
self._finalize_title(self._strip_cjk_artist_suffix(title))
|
||||
else:
|
||||
self.title = None
|
||||
# 括号注释属于曲名的版本/出处说明,剥离的规格文本之后拼回展示
|
||||
@@ -807,6 +816,37 @@ class MetaMusic(MetaBase):
|
||||
return cls._split_artists(head), tail
|
||||
return None, text
|
||||
|
||||
# 「艺术家 年份 专辑」三明治结构:艺术家段为不超过 4 个词的拉丁词组;
|
||||
# 贪婪匹配艺术家段,避免懒惰量词把首个词当艺术家、剩余词混入专辑名
|
||||
_YEAR_SANDWICH_RE = re.compile(
|
||||
r"^(?P<artist>[A-Za-z][A-Za-z0-9&+.'’\- ]*)\s+(?P<year>(?:19|20)\d{2})\s+(?P<rest>\S.*)$"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _split_year_sandwich(cls, value: str) -> tuple[Optional[list[str]], str, Optional[int]]:
|
||||
"""按中部独立年份拆分「艺术家 年份 专辑」(Jacky Cheung 1987 Jacky)。
|
||||
|
||||
:return: (艺术家列表, 专辑名, 年份),不适用时 (None, 原文, None)
|
||||
"""
|
||||
match = cls._YEAR_SANDWICH_RE.match(str(value or "").strip())
|
||||
if not match:
|
||||
return None, str(value or ""), None
|
||||
artist_text = match.group("artist").strip()
|
||||
raw_rest = match.group("rest").strip()
|
||||
rest = raw_rest.strip(" \t-–—−-")
|
||||
# 艺术家段限 4 个词以内;年份后紧跟另一个年份时是年份区间不是三明治结构;
|
||||
# 剩余段原文需以字母开头:「… 2014 2.0 -MINIBEL」数字开头、
|
||||
# 「… 2013 -PTer」发布组标签连字符开头都不是专辑名,拒绝拆分
|
||||
if (
|
||||
len(artist_text.split()) <= 4
|
||||
and rest
|
||||
and not re.match(r"(?:19|20)\d{2}\b", raw_rest)
|
||||
and raw_rest[0].isalpha()
|
||||
and any(char.isalpha() for char in rest)
|
||||
):
|
||||
return cls._split_artists(artist_text), rest, int(match.group("year"))
|
||||
return None, str(value or ""), None
|
||||
|
||||
@staticmethod
|
||||
def _compact_text(value: Any) -> str:
|
||||
"""移除大小写、空白与标点,生成比对使用的紧凑文本。"""
|
||||
|
||||
@@ -190,6 +190,31 @@ def test_apply_title_splits_various_artists_prefix():
|
||||
assert meta.title == "Reply 1988 OST"
|
||||
|
||||
|
||||
def test_apply_title_splits_year_sandwich():
|
||||
"""「艺术家 年份 专辑」三明治结构按中部年份拆分并提取年份。"""
|
||||
meta = parse_title("Leehom Wang 2010 The 18 Martial Arts")
|
||||
|
||||
assert meta.artists == ["Leehom Wang"]
|
||||
assert meta.title == "The 18 Martial Arts"
|
||||
assert meta.year == 2010
|
||||
|
||||
# 多艺术家分隔符与单词专辑名同样适用
|
||||
meta = parse_title("ASKA&SENS 1993 YAH YAH YAH")
|
||||
assert meta.artists == ["ASKA", "SENS"]
|
||||
assert meta.title == "YAH YAH YAH"
|
||||
assert meta.year == 1993
|
||||
|
||||
|
||||
def test_apply_title_year_sandwich_guards():
|
||||
"""三明治拆分的护栏:规格残留与长艺术家段不误拆。"""
|
||||
# 剩余段数字开头是规格(2.0)不是专辑名
|
||||
assert parse_title("K3 Kan Het S02 2014 2.0 -MINIBEL").artists == []
|
||||
# 剩余段连字符开头是发布组标签(-PTer)不是专辑名
|
||||
assert parse_title("Ashton Celebration 2013 -PTer").artists == []
|
||||
# 艺术家段超过 4 个词时拒绝拆分(含曲名与年份的完整标题)
|
||||
assert parse_title("Bee Gees One Night Only 1997 -ProfessorP").artists == []
|
||||
|
||||
|
||||
def test_apply_title_restores_letter_abbrev():
|
||||
"""单字母点号缩写还原不误伤合法缩写与单词。"""
|
||||
meta = parse_title("E.S.Posthumus - Ashielf Alpen FLAC")
|
||||
|
||||
Reference in New Issue
Block a user