Files
MoviePilot/tests/test_music_download.py

192 lines
6.8 KiB
Python

from unittest.mock import Mock, patch
from app.api.endpoints.download import download
from app.chain.download import DownloadChain
from app.chain.music import MusicChain
from app.core.context import MUSIC_ENTITY_ALBUM, Context, MusicInfo
from app.schemas import ExistMediaInfo
from app.schemas.context import TorrentInfo
from app.schemas.music import MusicInfo as MusicInfoSchema
from app.schemas.types import MediaType
def _music_info() -> MusicInfo:
"""构造下载测试使用的标准音乐信息。"""
return MusicInfo(
source="musicbrainz",
media_id="recording-1",
title="晴天",
artists=["周杰伦"],
album="叶惠美",
year=2003,
cover_url="https://example.com/cover.jpg",
raw_data={"large": "payload"},
)
def _album_info(total_tracks: int | None = 3) -> MusicInfo:
"""构造整张专辑下载校验使用的目标信息。"""
return MusicInfo(
source="musicbrainz",
media_id="release-group-1",
music_type=MUSIC_ENTITY_ALBUM,
title="叶惠美",
album="叶惠美",
artists=["周杰伦"],
total_tracks=total_tracks,
)
def test_music_info_exposes_download_chain_compatibility_fields():
"""音乐信息应安全兼容下载链现有的视频身份字段访问。"""
info = _music_info()
meta = MusicChain.to_meta(info)
assert info.type == MediaType.MUSIC
assert info.tmdb_id is None
assert info.episode_group is None
assert meta.episode_list == []
assert meta.season_episode == ""
def test_download_note_keeps_versioned_music_context():
"""音乐下载历史备注应保存可恢复且不含上游原始大对象的上下文。"""
info = _music_info()
meta = MusicChain.to_meta(info)
note = DownloadChain._build_download_note("Manual", info, meta)
assert note["source"] == "Manual"
assert note["music"]["version"] == 1
assert note["music"]["meta"]["album"] == "叶惠美"
assert note["music"]["media"]["media_id"] == "recording-1"
assert "raw_data" not in note["music"]["media"]
def test_album_resource_requires_all_independent_audio_tracks():
"""整专资源只有在独立音频文件数覆盖专辑曲目数时才可标记完整。"""
context = Context(media_info=_album_info(total_tracks=3))
error = DownloadChain._validate_music_album_resource(
context,
["叶惠美/01.flac", "叶惠美/02.flac", "叶惠美/03.m4a", "叶惠美/cover.jpg"],
)
assert error is None
assert context.confirmed_full_coverage is True
def test_album_resource_rejects_incomplete_or_unverifiable_pack():
"""曲目不足、未知曲目总数或无文件清单时不得把专辑订阅判定为完成。"""
incomplete = Context(media_info=_album_info(total_tracks=3))
unknown = Context(media_info=_album_info(total_tracks=None))
assert "仅包含 1 个独立音频文件" in (
DownloadChain._validate_music_album_resource(incomplete, ["叶惠美/disc.flac"]) or ""
)
assert incomplete.confirmed_full_coverage is False
assert "总曲目数未知" in (
DownloadChain._validate_music_album_resource(unknown, ["叶惠美/01.flac"]) or ""
)
assert "未提供文件清单" in (
DownloadChain._validate_music_album_resource(
Context(media_info=_album_info(total_tracks=3)),
[],
) or ""
)
def test_download_single_stops_before_client_when_album_pack_is_incomplete():
"""下载入口应在添加任务前拒绝不完整专辑,并记录可供后续候选继续尝试的失败原因。"""
context = Context(
media_info=_album_info(total_tracks=3),
meta_info=MusicChain.to_meta(_album_info(total_tracks=3)),
torrent_info=TorrentInfo(
title="周杰伦 - 叶惠美 FLAC",
category=MediaType.MUSIC.value,
),
)
chain = DownloadChain()
chain._record_download_failure = Mock()
media_chain = Mock()
media_chain.supplement_tmdb_info.return_value = context.media_info
torrent_helper = Mock()
torrent_helper.get_fileinfo_from_torrent_content.return_value = (
"叶惠美",
["叶惠美/整轨.flac", "叶惠美/整轨.cue"],
)
with patch("app.chain.download.MediaChain", return_value=media_chain), \
patch("app.chain.download.TorrentHelper", return_value=torrent_helper), \
patch("app.chain.download.eventmanager.send_event", return_value=None):
task_id, error = chain.download_single(
context,
torrent_content=b"torrent",
return_detail=True,
)
assert task_id is None
assert "专辑资源不完整" in error
chain._record_download_failure.assert_called_once()
def test_download_endpoint_builds_music_context():
"""现有添加下载接口应使用 MusicInfo 和 MetaMusic 构造音乐上下文。"""
chain = Mock()
chain.download_single.return_value = "hash-1"
current_user = Mock(name="admin")
with patch("app.api.endpoints.download.DownloadChain", return_value=chain):
response = download(
media_in=MusicInfoSchema(**_music_info().to_dict()),
torrent_in=TorrentInfo(
title="周杰伦 - 叶惠美 FLAC",
enclosure="https://example.com/download?id=1",
category="音乐",
),
downloader="qb",
save_path=None,
current_user=current_user,
)
assert response.success is True
context = chain.download_single.call_args.kwargs["context"]
assert isinstance(context.media_info, MusicInfo)
assert context.media_info.media_id == "recording-1"
assert context.meta_info.type == MediaType.MUSIC
assert context.meta_info.org_string == "周杰伦 - 叶惠美 FLAC"
def test_music_library_exists_uses_atomic_album_lookup():
"""整专存在性检查应按音乐条目判断,不能落入电视剧季集补全分支。"""
album = _album_info(total_tracks=11)
chain = DownloadChain()
chain.media_exists = Mock(
return_value=ExistMediaInfo(
type=MediaType.MUSIC,
server_type="navidrome",
server="music",
itemid="album-item-1",
)
)
mediaserver = Mock()
mediaserver.get_item_id.return_value = "album-item-1"
with patch("app.chain.download.MediaServerOper", return_value=mediaserver):
exists, no_exists = chain.get_no_exists_info(
meta=MusicChain.to_meta(album),
mediainfo=album,
)
assert exists is True
assert no_exists == {}
mediaserver.get_item_id.assert_called_once_with(
mtype=MediaType.MUSIC.value,
title="叶惠美",
year=None,
)
chain.media_exists.assert_called_once_with(
mediainfo=album,
itemid="album-item-1",
)