Files
MoviePilot/tests/test_transfer_music_classification.py
DDSRem 2de976f6b3 Merge pull request #7050 from DDSRem-Dev/codex-comment-migration
docs: 统一全项目中文注释和结构化契约说明
2026-10-17 19:26:29 +08:00

240 lines
7.9 KiB
Python

"""
验证音乐整理计划中的分类决策。
"""
from app.chain.media import MediaChain
from app.chain.transfer.filter import FileFilterMixin
from app.domain.context import MusicInfo
from app.domain.meta.metamusic import MetaMusic
from app.schemas.category import ClassificationResult, ClassificationSelection
from app.schemas.file import FileItem
def test_album_context_preserves_classification_and_remote_music_facts(
tmp_path,
monkeypatch,
) -> None:
"""
目录专辑匹配重建曲目时应保留分类事实,并隔离缓存中的可变结果。
"""
file_path = tmp_path / "album" / "01-track.flac"
classification = ClassificationResult(
recommended=ClassificationSelection(
category_id="live",
category_path=["音乐", "现场"],
rule_id="music-live",
source="automatic",
),
effective=ClassificationSelection(
category_id="favorites",
category_path=["音乐", "收藏"],
rule_id="music-live",
source="subscription",
),
labels=["现场", "高解析"],
policy_revision=12,
state="complete",
)
matched_info = MusicInfo(
media_source="musicbrainz",
media_id="recording-1",
title="Remote title",
artists=["Remote artist"],
artist_ids=["artist-1"],
album="Remote album",
album_artist="Remote album artist",
album_id="release-group-1",
album_type="Album",
secondary_types=["Live", "Compilation"],
year=2024,
release_date="2024-05-01",
release_status="Official",
disc_number=1,
track_number=1,
total_tracks=10,
duration=1,
audio_format="MP3",
audio_lossless=False,
bit_depth=16,
sample_rate=44100,
bitrate=320000,
library_category="音乐/收藏",
metadata_category="Album / Live / Compilation",
classification=classification,
genres=["Rock"],
tags=["concert", "hi-res"],
artist_country="GB",
names=["Remote title", "远端曲名"],
detail_link="https://example.test/recording-1",
listen_count=42,
raw_data={"release": {"secondary_types": ["Live"]}},
)
local_meta = MetaMusic(
title="Remote title",
duration=321,
audio_format="FLAC",
audio_lossless=True,
bit_depth=24,
sample_rate=96000,
bitrate=2304000,
)
file_item = FileItem(
storage="local",
path=str(file_path),
type="file",
name=file_path.name,
basename=file_path.stem,
extension="flac",
)
monkeypatch.setattr(
MediaChain,
"recognize_music_album_directory",
lambda _self, _directory, **_kwargs: {str(file_path.resolve()): matched_info},
)
merged_meta, merged_info = FileFilterMixin._match_music_album_context(
file_item,
file_path,
local_meta,
)
assert merged_info is not None
assert merged_info.classification == classification
assert merged_info.classification is not classification
assert merged_info.classification.recommended is not classification.recommended
assert merged_info.classification.effective is not classification.effective
assert merged_info.classification.recommended.category_path == ["音乐", "现场"]
assert merged_info.classification.effective.category_path == ["音乐", "收藏"]
assert merged_info.classification.labels == ["现场", "高解析"]
assert merged_info.category == merged_info.library_category == "音乐/收藏"
assert merged_info.metadata_category == "Album / Live / Compilation"
assert merged_info.secondary_types == ["Live", "Compilation"]
assert merged_info.tags == ["concert", "hi-res"]
assert merged_info.genres == ["Rock"]
assert merged_info.release_status == "Official"
assert merged_info.artist_country == "GB"
assert merged_info.names == ["Remote title", "远端曲名"]
assert merged_meta.duration == merged_info.duration == 321
assert merged_meta.audio_format == merged_info.audio_format == "FLAC"
assert merged_info.audio_lossless is True
assert merged_info.bit_depth == 24
assert merged_info.sample_rate == 96000
assert merged_info.bitrate == 2304000
merged_info.classification.recommended.category_path.append("追加")
merged_info.classification.labels.append("新标签")
merged_info.secondary_types.append("Soundtrack")
merged_info.tags.append("new-tag")
merged_info.raw_data["release"]["secondary_types"].append("Remix")
assert classification.recommended.category_path == ["音乐", "现场"]
assert classification.labels == ["现场", "高解析"]
assert matched_info.secondary_types == ["Live", "Compilation"]
assert matched_info.tags == ["concert", "hi-res"]
assert matched_info.raw_data == {"release": {"secondary_types": ["Live"]}}
def test_album_context_rejects_candidate_conflicting_with_file_tags(
tmp_path,
monkeypatch,
) -> None:
"""
目录级匹配仍须核验逐文件明确的发行年份,不能用普通年份规则绕过冲突。
"""
file_path = tmp_path / "Speak Now" / "02 - Sparks Fly.flac"
local_meta = MetaMusic(
title="Sparks Fly",
artists=["Taylor Swift"],
album="Speak Now",
year=2010,
release_year=2010,
)
wrong_release = MusicInfo(
media_source="musicbrainz",
media_id="wrong-release-recording",
title="Sparks Fly",
artists=["Taylor Swift"],
album="Speak Now",
year=2025,
release_year=2025,
)
file_item = FileItem(
storage="local",
path=str(file_path),
type="file",
name=file_path.name,
basename=file_path.stem,
extension="flac",
)
monkeypatch.setattr(
MediaChain,
"recognize_music_album_directory",
lambda _self, _directory, **_kwargs: {str(file_path.resolve()): wrong_release},
)
returned_meta, returned_info = FileFilterMixin._match_music_album_context(
file_item,
file_path,
local_meta,
)
assert returned_meta is local_meta
assert returned_info is None
def test_album_context_preserves_local_release_track_position(
tmp_path,
monkeypatch,
) -> None:
"""
Recording 的其它发行版曲序不得覆盖当前文件的本地曲序。
"""
file_path = tmp_path / "Speak Now (Taylor's Version)" / "20 - Castles Crumbling.flac"
local_meta = MetaMusic(
title="Castles Crumbling (Taylor's Version)",
artists=["Taylor Swift", "Hayley Williams"],
album="Speak Now (Taylor's Version)",
year=2023,
disc_number=2,
track_number=20,
total_tracks=22,
)
matched_info = MusicInfo(
media_source="musicbrainz",
media_id="recording-castles-crumbling",
title="Castles Crumbling (Taylor's Version)",
artists=["Taylor Swift", "Hayley Williams"],
album="Speak Now (Taylor's Version)",
album_artist="Taylor Swift",
album_type="Album",
year=2023,
disc_number=1,
track_number=6,
total_tracks=6,
)
file_item = FileItem(
storage="local",
path=str(file_path),
type="file",
name=file_path.name,
basename=file_path.stem,
extension="flac",
)
monkeypatch.setattr(
MediaChain,
"recognize_music_album_directory",
lambda _self, _directory, **_kwargs: {str(file_path.resolve()): matched_info},
)
merged_meta, merged_info = FileFilterMixin._match_music_album_context(
file_item,
file_path,
local_meta,
)
assert merged_info is not None
assert merged_meta.disc_number == merged_info.disc_number == 2
assert merged_meta.track_number == merged_info.track_number == 20
assert merged_meta.total_tracks == merged_info.total_tracks == 22