mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-14 02:05:13 +08:00
235 lines
7.4 KiB
Python
235 lines
7.4 KiB
Python
from pathlib import Path
|
||
from types import SimpleNamespace
|
||
|
||
from jinja2 import Template
|
||
|
||
from app.chain.music import MusicChain
|
||
from app.chain.transfer import JobManager, TransferChain
|
||
from app.core.config import settings
|
||
from app.core.meta import MetaMusic
|
||
from app.core.context import MusicInfo
|
||
from app.helper.message import TemplateHelper
|
||
from app.schemas.file import FileItem
|
||
from app.schemas.transfer import TransferTask
|
||
from app.schemas.types import MediaType
|
||
|
||
|
||
def _music_context() -> tuple[MetaMusic, MusicInfo]:
|
||
"""构造整理测试使用的音乐元数据和媒体信息。"""
|
||
info = MusicInfo(
|
||
source="musicbrainz",
|
||
media_id="recording-1",
|
||
title="Get Lucky",
|
||
artists=["Daft Punk", "Pharrell Williams"],
|
||
album="Random Access Memories",
|
||
album_artist="Daft Punk",
|
||
year=2013,
|
||
track_number=8,
|
||
total_tracks=13,
|
||
category="Album",
|
||
)
|
||
return MusicChain.to_meta(info), info
|
||
|
||
|
||
def test_music_rename_context_contains_audio_fields():
|
||
"""重命名模板上下文应提供艺术家、专辑、盘号和曲序字段。"""
|
||
meta, info = _music_context()
|
||
|
||
context = TemplateHelper().builder.build(
|
||
meta=meta,
|
||
mediainfo=info,
|
||
file_extension=".flac",
|
||
include_raw_objects=False,
|
||
)
|
||
|
||
assert context["artist"] == "Daft Punk / Pharrell Williams"
|
||
assert context["album"] == "Random Access Memories"
|
||
assert context["track"] == "08"
|
||
assert context["fileExt"] == ".flac"
|
||
|
||
|
||
def test_music_rename_prefers_track_meta_over_album_media():
|
||
"""专辑整理时应使用每个文件的曲名和曲序,不能把专辑名写成所有目标文件名。"""
|
||
meta = MetaMusic(
|
||
org_string="10. 明天晴天.m4a",
|
||
title="明天晴天",
|
||
artists=["孙燕姿"],
|
||
album="完美的一天",
|
||
album_artist="孙燕姿",
|
||
year=2005,
|
||
track_number=10,
|
||
total_tracks=11,
|
||
)
|
||
album = MusicInfo(
|
||
source="musicbrainz",
|
||
media_id="album-1",
|
||
music_type="album",
|
||
title="完美的一天",
|
||
artists=["孙燕姿"],
|
||
album="完美的一天",
|
||
album_artist="孙燕姿",
|
||
year=2005,
|
||
total_tracks=11,
|
||
)
|
||
|
||
context = TemplateHelper().builder.build(
|
||
meta=meta,
|
||
mediainfo=album,
|
||
file_extension=".m4a",
|
||
include_raw_objects=False,
|
||
)
|
||
rendered = Template(settings.MUSIC_RENAME_FORMAT).render(context)
|
||
|
||
assert context["title"] == "明天晴天"
|
||
assert context["track"] == "10"
|
||
assert rendered == "孙燕姿/完美的一天 (2005)/10 - 明天晴天.m4a"
|
||
|
||
|
||
def test_music_rename_format_is_independent_from_movie_format():
|
||
"""音乐应使用独立重命名模板且保持影视模板不变。"""
|
||
assert settings.RENAME_FORMAT(MediaType.MUSIC) == settings.MUSIC_RENAME_FORMAT
|
||
assert settings.RENAME_FORMAT(MediaType.MOVIE) == settings.MOVIE_RENAME_FORMAT
|
||
|
||
|
||
def test_audio_is_primary_only_in_music_context():
|
||
"""音频文件只在音乐上下文中作为主要媒体文件。"""
|
||
chain = TransferChain()
|
||
audio = FileItem(
|
||
storage="local",
|
||
path="/music/track.flac",
|
||
name="track.flac",
|
||
basename="track",
|
||
type="file",
|
||
extension="flac",
|
||
)
|
||
|
||
assert chain._is_primary_media_file(audio, MusicInfo(title="Track")) is True
|
||
assert chain._is_primary_media_file(audio, None) is False
|
||
|
||
|
||
def test_restore_music_context_from_download_history():
|
||
"""自动整理应从下载历史备注恢复标准音乐身份。"""
|
||
meta, info = _music_context()
|
||
history = SimpleNamespace(
|
||
note={
|
||
"music": {
|
||
"version": 1,
|
||
"meta": meta.to_dict(),
|
||
"media": info.to_dict(),
|
||
}
|
||
}
|
||
)
|
||
|
||
restored_meta, restored_info = TransferChain._restore_music_download_context(
|
||
history,
|
||
Path("/remote/08 - Get Lucky.flac"),
|
||
)
|
||
|
||
assert restored_meta is not None
|
||
assert restored_info is not None
|
||
assert restored_meta.org_string == "08 - Get Lucky.flac"
|
||
assert restored_info.source == "musicbrainz"
|
||
assert restored_info.media_id == "recording-1"
|
||
assert restored_info.album == "Random Access Memories"
|
||
|
||
|
||
def test_restore_music_context_uses_file_title_over_subscription_title(tmp_path, monkeypatch):
|
||
"""曲目标题应优先取当前文件自身的标签/文件名,而非沿用订阅时的单曲标题。"""
|
||
meta, info = _music_context()
|
||
history = SimpleNamespace(
|
||
note={
|
||
"music": {
|
||
"version": 1,
|
||
"meta": meta.to_dict(),
|
||
"media": info.to_dict(),
|
||
}
|
||
}
|
||
)
|
||
# 订阅标题被识别为单曲"Get Lucky",而实际音轨标签曲目名 不同且无身份字段
|
||
audio_file = tmp_path / "07.幸福.flac"
|
||
audio_file.write_bytes(b"fake-flac")
|
||
|
||
from app.helper.audio import AudioMetadataHelper
|
||
|
||
monkeypatch.setattr(
|
||
AudioMetadataHelper,
|
||
"read",
|
||
lambda path: MetaMusic(org_string=path.name, title="流浪地图"),
|
||
)
|
||
|
||
restored_meta, _ = TransferChain._restore_music_download_context(history, audio_file)
|
||
|
||
assert restored_meta is not None
|
||
assert restored_meta.title == "流浪地图"
|
||
|
||
|
||
def test_restore_music_context_uses_filename_when_source_is_not_locally_accessible():
|
||
"""远端音频无法直接读取标签时也应按文件名区分曲目,避免整张专辑重名。"""
|
||
meta, info = _music_context()
|
||
meta.artists = ["孙燕姿"]
|
||
meta.title = "完美的一天"
|
||
meta.album = "完美的一天"
|
||
meta.album_artist = "孙燕姿"
|
||
meta.year = 2005
|
||
meta.track_number = None
|
||
meta.total_tracks = None
|
||
info.music_type = "album"
|
||
info.artists = ["孙燕姿"]
|
||
info.title = "完美的一天"
|
||
info.album = "完美的一天"
|
||
info.album_artist = "孙燕姿"
|
||
info.year = 2005
|
||
info.track_number = None
|
||
info.total_tracks = None
|
||
history = SimpleNamespace(
|
||
note={
|
||
"music": {
|
||
"version": 1,
|
||
"meta": meta.to_dict(),
|
||
"media": info.to_dict(),
|
||
}
|
||
}
|
||
)
|
||
|
||
restored_meta, restored_info = TransferChain._restore_music_download_context(
|
||
history,
|
||
Path("/remote/10. 明天晴天.m4a"),
|
||
)
|
||
|
||
assert restored_meta is not None
|
||
assert restored_info is not None
|
||
assert restored_meta.title == "10. 明天晴天"
|
||
assert restored_info.title == "10. 明天晴天"
|
||
context = TemplateHelper().builder.build(
|
||
meta=restored_meta,
|
||
mediainfo=restored_info,
|
||
file_extension=".m4a",
|
||
include_raw_objects=False,
|
||
)
|
||
rendered = Template(settings.MUSIC_RENAME_FORMAT).render(context)
|
||
assert rendered == "孙燕姿/完美的一天 (2005)/10. 明天晴天.m4a"
|
||
|
||
|
||
def test_job_manager_serializes_music_queue_models():
|
||
"""整理队列应使用音乐专属 Schema 序列化任务。"""
|
||
meta, info = _music_context()
|
||
task = TransferTask(
|
||
fileitem=FileItem(
|
||
storage="local",
|
||
path="/music/track.flac",
|
||
name="track.flac",
|
||
basename="track",
|
||
type="file",
|
||
extension="flac",
|
||
),
|
||
meta=meta,
|
||
mediainfo=info,
|
||
)
|
||
manager = JobManager()
|
||
|
||
assert manager.add_task(task) is True
|
||
job = manager.list_jobs()[0]
|
||
assert job.media.type == "音乐"
|
||
assert job.media.album == "Random Access Memories"
|
||
assert job.tasks[0].meta.type == "音乐"
|