mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-15 11:04:12 +08:00
210 lines
7.0 KiB
Python
210 lines
7.0 KiB
Python
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
from unittest.mock import Mock, patch
|
|
|
|
from app.core.config import settings
|
|
from app.core.context import MusicInfo
|
|
from app.core.meta import MetaMusic
|
|
from app.helper.audio import AudioMetadataHelper
|
|
from app.helper.directory import DirectoryHelper
|
|
from app.modules.filemanager import FileManagerModule
|
|
from app.modules.filemanager.transhandler import TransHandler
|
|
from app.schemas import FileItem, TransferDirectoryConf
|
|
from app.schemas.types import MediaType
|
|
|
|
|
|
def _audio_file(name: str, extension: str = "flac") -> FileItem:
|
|
"""构造标准专辑目录下的音频文件项。"""
|
|
path = Path("/library/Daft Punk/Random Access Memories (2013)") / name
|
|
return FileItem(
|
|
storage="local",
|
|
path=path.as_posix(),
|
|
name=path.name,
|
|
basename=path.stem,
|
|
type="file",
|
|
extension=extension,
|
|
)
|
|
|
|
|
|
def _recording(**overrides) -> MusicInfo:
|
|
"""构造本地媒体库查重使用的单曲身份。"""
|
|
values = {
|
|
"media_source": "musicbrainz",
|
|
"media_id": "recording-1",
|
|
"title": "Get Lucky",
|
|
"artists": ["Daft Punk"],
|
|
"album": "Random Access Memories",
|
|
"album_artist": "Daft Punk",
|
|
"year": 2013,
|
|
"disc_number": 1,
|
|
"track_number": 8,
|
|
}
|
|
values.update(overrides)
|
|
return MusicInfo(**values)
|
|
|
|
|
|
def test_music_media_root_uses_album_directory_with_or_without_disc_folder():
|
|
"""音乐根目录应稳定落在专辑层,不受动态 Disc 子目录是否渲染影响。"""
|
|
album_dir = Path("/library/Daft Punk/Random Access Memories (2013)")
|
|
|
|
assert DirectoryHelper.get_media_root_path(
|
|
settings.MUSIC_RENAME_FORMAT,
|
|
album_dir / "08 - Get Lucky.flac",
|
|
media_type=MediaType.MUSIC,
|
|
) == album_dir
|
|
assert DirectoryHelper.get_media_root_path(
|
|
settings.MUSIC_RENAME_FORMAT,
|
|
album_dir / "Disc 1" / "08 - Get Lucky.flac",
|
|
media_type=MediaType.MUSIC,
|
|
) == album_dir
|
|
|
|
|
|
def test_media_files_uses_music_template_root_and_only_returns_audio():
|
|
"""本地音乐查重应扫描目标专辑目录并排除同目录视频文件。"""
|
|
module = FileManagerModule()
|
|
storage = Mock()
|
|
album_dir = Path("/library/Daft Punk/Random Access Memories (2013)")
|
|
storage.get_item.return_value = FileItem(
|
|
storage="local",
|
|
path=album_dir.as_posix(),
|
|
name=album_dir.name,
|
|
type="dir",
|
|
)
|
|
audio = _audio_file("08 - Get Lucky.flac")
|
|
video = FileItem(
|
|
storage="local",
|
|
path=(album_dir / "Get Lucky.mkv").as_posix(),
|
|
name="Get Lucky.mkv",
|
|
basename="Get Lucky",
|
|
type="file",
|
|
extension="mkv",
|
|
)
|
|
module._FileManagerModule__get_storage_oper = Mock(return_value=storage)
|
|
module.list_files = Mock(return_value=[audio, video])
|
|
directory = TransferDirectoryConf(
|
|
library_path="/library",
|
|
library_storage="local",
|
|
)
|
|
|
|
with patch(
|
|
"app.modules.filemanager.DirectoryHelper.get_library_dirs",
|
|
return_value=[directory],
|
|
), patch(
|
|
"app.modules.filemanager.transhandler.eventmanager.send_event",
|
|
return_value=None,
|
|
):
|
|
files = module.media_files(_recording())
|
|
|
|
storage.get_item.assert_called_once_with(album_dir)
|
|
assert files == [audio]
|
|
|
|
|
|
def test_local_music_recording_requires_matching_track_not_any_album_file():
|
|
"""单曲查重必须命中目标曲名,不能因同专辑存在其它音轨而误判完成。"""
|
|
module = FileManagerModule()
|
|
module.media_files = Mock(
|
|
return_value=[
|
|
_audio_file("01 - Give Life Back to Music.flac"),
|
|
_audio_file("08 - Get Lucky.flac"),
|
|
]
|
|
)
|
|
|
|
with patch("app.modules.filemanager.settings.LOCAL_EXISTS_SEARCH", True):
|
|
exists = module.media_exists(_recording())
|
|
missing = module.media_exists(
|
|
_recording(title="Instant Crush", media_id="recording-2", track_number=5)
|
|
)
|
|
|
|
assert exists and exists.type == MediaType.MUSIC
|
|
assert missing is None
|
|
|
|
|
|
def test_local_music_album_requires_unique_complete_track_coverage():
|
|
"""专辑查重应按去重曲目数判定完整,重复格式不能冒充缺失音轨。"""
|
|
module = FileManagerModule()
|
|
duplicate = _audio_file("01 - Give Life Back to Music.mp3", extension="mp3")
|
|
files = [
|
|
_audio_file("01 - Give Life Back to Music.flac"),
|
|
duplicate,
|
|
_audio_file("02 - The Game of Love.flac"),
|
|
]
|
|
module.media_files = Mock(return_value=files)
|
|
album = _recording(
|
|
music_type="album",
|
|
media_id="release-group-1",
|
|
title="Random Access Memories",
|
|
track_number=None,
|
|
total_tracks=3,
|
|
)
|
|
|
|
with patch("app.modules.filemanager.settings.LOCAL_EXISTS_SEARCH", True):
|
|
assert module.media_exists(album) is None
|
|
files.append(_audio_file("03 - Giorgio by Moroder.flac"))
|
|
exists = module.media_exists(album)
|
|
|
|
assert exists and exists.type == MediaType.MUSIC
|
|
|
|
|
|
def test_local_music_album_with_unknown_total_is_not_assumed_complete():
|
|
"""未知专辑总曲目数时不能仅凭目录中存在音频就判定订阅完成。"""
|
|
assert FileManagerModule._music_album_is_complete(
|
|
[_audio_file("01 - Intro.flac")],
|
|
total_tracks=None,
|
|
) is False
|
|
|
|
|
|
def test_local_music_album_counts_same_track_number_on_different_discs():
|
|
"""多碟专辑中相同曲序应通过盘号区分为不同曲目。"""
|
|
files = [
|
|
_audio_file("Disc 1/01 - Intro.flac"),
|
|
_audio_file("Disc 2/01 - Finale.flac"),
|
|
]
|
|
|
|
assert FileManagerModule._music_album_is_complete(files, total_tracks=2) is True
|
|
|
|
|
|
def test_music_size_overwrite_prefers_actual_audio_quality(tmp_path, monkeypatch):
|
|
"""音乐的 size 覆盖判断应优先使用音频参数,而不是把字节数当作音质。"""
|
|
target_path = tmp_path / "Track.flac"
|
|
target_path.write_bytes(b"target")
|
|
target_item = FileItem(
|
|
storage="local",
|
|
path=target_path.as_posix(),
|
|
name=target_path.name,
|
|
basename=target_path.stem,
|
|
type="file",
|
|
extension="flac",
|
|
)
|
|
music = _recording()
|
|
|
|
monkeypatch.setattr(
|
|
AudioMetadataHelper,
|
|
"read",
|
|
lambda path: MetaMusic(audio_format="MP3", bitrate=320000),
|
|
)
|
|
upgrade = TransHandler._TransHandler__music_quality_overwrite_decision(
|
|
MetaMusic(audio_format="FLAC", bit_depth=24, sample_rate=192000),
|
|
music,
|
|
target_item,
|
|
)
|
|
|
|
monkeypatch.setattr(
|
|
AudioMetadataHelper,
|
|
"read",
|
|
lambda path: MetaMusic(audio_format="FLAC", bit_depth=24, sample_rate=192000),
|
|
)
|
|
downgrade = TransHandler._TransHandler__music_quality_overwrite_decision(
|
|
MetaMusic(audio_format="MP3", bitrate=320000),
|
|
music,
|
|
target_item,
|
|
)
|
|
non_music = TransHandler._TransHandler__music_quality_overwrite_decision(
|
|
MetaMusic(audio_format="FLAC"),
|
|
SimpleNamespace(type=MediaType.MOVIE),
|
|
target_item,
|
|
)
|
|
|
|
assert upgrade is True
|
|
assert downgrade is False
|
|
assert non_music is None
|