beets/test/plugins/test_mbpseudo.py

378 lines
13 KiB
Python

import json
import pathlib
from copy import deepcopy
import pytest
from beets import config
from beets.autotag import AlbumMatch
from beets.autotag.distance import Distance
from beets.autotag.hooks import AlbumInfo, TrackInfo
from beets.library import Item
from beets.test.helper import PluginMixin
from beetsplug._typing import JSONDict
from beetsplug.mbpseudo import (
_STATUS_PSEUDO,
MultiPseudoAlbumInfo,
MusicBrainzPseudoReleasePlugin,
PseudoAlbumInfo,
)
@pytest.fixture(scope="module")
def official_release_info() -> AlbumInfo:
return AlbumInfo(
tracks=[TrackInfo(title="百花繚乱")],
album_id="official",
album="百花繚乱",
)
@pytest.fixture(scope="module")
def pseudo_release_info() -> AlbumInfo:
return AlbumInfo(
tracks=[TrackInfo(title="In Bloom")],
album_id="pseudo",
album="In Bloom",
)
class TestPseudoAlbumInfo:
def test_album_id_always_from_pseudo(
self, official_release_info: AlbumInfo, pseudo_release_info: AlbumInfo
):
info = PseudoAlbumInfo(pseudo_release_info, official_release_info)
info.use_official_as_ref()
assert info.album_id == "pseudo"
def test_get_attr_from_pseudo(
self, official_release_info: AlbumInfo, pseudo_release_info: AlbumInfo
):
info = PseudoAlbumInfo(pseudo_release_info, official_release_info)
info.use_pseudo_as_ref()
assert info.album == "In Bloom"
def test_get_attr_from_official(
self, official_release_info: AlbumInfo, pseudo_release_info: AlbumInfo
):
info = PseudoAlbumInfo(pseudo_release_info, official_release_info)
info.use_official_as_ref()
assert info.album == info.get_official_release().album
def test_determine_best_ref(
self, official_release_info: AlbumInfo, pseudo_release_info: AlbumInfo
):
info = PseudoAlbumInfo(
pseudo_release_info, official_release_info, data_source="test"
)
item = Item()
item["title"] = "百花繚乱"
assert info.determine_best_ref([item]) == "official"
info.use_pseudo_as_ref()
assert info.data_source == "test"
@pytest.fixture(scope="module")
def rsrc_dir(pytestconfig: pytest.Config):
return pytestconfig.rootpath / "test" / "rsrc" / "mbpseudo"
class TestMBPseudoPlugin(PluginMixin):
plugin = "mbpseudo"
@pytest.fixture(scope="class")
def plugin_config(self):
return {"scripts": ["Latn", "Dummy"]}
@pytest.fixture(scope="class")
def mbpseudo_plugin(self, plugin_config) -> MusicBrainzPseudoReleasePlugin:
self.config[self.plugin].set(plugin_config)
return MusicBrainzPseudoReleasePlugin()
@pytest.fixture
def official_release(self, rsrc_dir: pathlib.Path) -> JSONDict:
info_json = (rsrc_dir / "official_release.json").read_text(
encoding="utf-8"
)
return json.loads(info_json)
@pytest.fixture
def pseudo_release(self, rsrc_dir: pathlib.Path) -> JSONDict:
info_json = (rsrc_dir / "pseudo_release.json").read_text(
encoding="utf-8"
)
return json.loads(info_json)
def test_scripts_init(
self, mbpseudo_plugin: MusicBrainzPseudoReleasePlugin
):
assert mbpseudo_plugin._scripts == ["Latn", "Dummy"]
@pytest.mark.parametrize(
"album_id",
[
"a5ce1d11-2e32-45a4-b37f-c1589d46b103",
"-5ce1d11-2e32-45a4-b37f-c1589d46b103",
],
)
def test_extract_id_uses_music_brainz_pattern(
self,
mbpseudo_plugin: MusicBrainzPseudoReleasePlugin,
album_id: str,
):
if album_id.startswith("-"):
assert mbpseudo_plugin._extract_id(album_id) is None
else:
assert mbpseudo_plugin._extract_id(album_id) == album_id
def test_reimport_logic(
self,
mbpseudo_plugin: MusicBrainzPseudoReleasePlugin,
official_release_info: AlbumInfo,
pseudo_release_info: AlbumInfo,
):
pseudo_info = PseudoAlbumInfo(
pseudo_release_info, official_release_info
)
item = Item()
item["title"] = "百花繚乱"
# if items don't have mb_*, they are not modified
mbpseudo_plugin.before_album_info_emitted([item], pseudo_info)
assert pseudo_info.album == item.title
pseudo_info.use_pseudo_as_ref()
assert pseudo_info.album == "In Bloom"
item["mb_albumid"] = "mb_aid"
item["mb_trackid"] = "mb_tid"
assert item.get("mb_albumid") == "mb_aid"
assert item.get("mb_trackid") == "mb_tid"
# if items have mb_*, they are deleted
mbpseudo_plugin.before_album_info_emitted([item], pseudo_info)
assert pseudo_info.album == item.title
assert item.get("mb_albumid") == ""
assert item.get("mb_trackid") == ""
def test_album_info_for_pseudo_release(
self,
mbpseudo_plugin: MusicBrainzPseudoReleasePlugin,
pseudo_release: JSONDict,
):
album_info = mbpseudo_plugin.album_info(pseudo_release["release"])
assert isinstance(album_info, PseudoAlbumInfo)
assert album_info.data_source == "MusicBrainzPseudoRelease"
assert album_info.albumstatus == _STATUS_PSEUDO
@pytest.mark.parametrize(
"json_key",
[
"type",
"direction",
"release",
],
)
def test_interception_skip_when_rel_values_dont_match(
self,
mbpseudo_plugin: MusicBrainzPseudoReleasePlugin,
official_release: JSONDict,
json_key: str,
):
for r in official_release["release"]["release-relation-list"]:
del r[json_key]
album_info = mbpseudo_plugin.album_info(official_release["release"])
assert not isinstance(album_info, PseudoAlbumInfo)
assert album_info.data_source == "MusicBrainzPseudoRelease"
def test_interception_skip_when_script_doesnt_match(
self,
mbpseudo_plugin: MusicBrainzPseudoReleasePlugin,
official_release: JSONDict,
):
for r in official_release["release"]["release-relation-list"]:
r["release"]["text-representation"]["script"] = "Null"
album_info = mbpseudo_plugin.album_info(official_release["release"])
assert not isinstance(album_info, PseudoAlbumInfo)
assert album_info.data_source == "MusicBrainzPseudoRelease"
def test_interception(
self,
mbpseudo_plugin: MusicBrainzPseudoReleasePlugin,
official_release: JSONDict,
pseudo_release: JSONDict,
):
mbpseudo_plugin._release_getter = (
lambda album_id, includes: pseudo_release
)
album_info = mbpseudo_plugin.album_info(official_release["release"])
assert isinstance(album_info, PseudoAlbumInfo)
assert album_info.data_source == "MusicBrainzPseudoRelease"
def test_final_adjustment_skip(
self,
mbpseudo_plugin: MusicBrainzPseudoReleasePlugin,
):
match = AlbumMatch(
distance=Distance(),
info=AlbumInfo(tracks=[], data_source="mb"),
mapping={},
extra_items=[],
extra_tracks=[],
)
mbpseudo_plugin._adjust_final_album_match(match)
assert match.info.data_source == "mb"
def test_final_adjustment(
self,
mbpseudo_plugin: MusicBrainzPseudoReleasePlugin,
official_release_info: AlbumInfo,
pseudo_release_info: AlbumInfo,
):
pseudo_album_info = PseudoAlbumInfo(
pseudo_release=pseudo_release_info,
official_release=official_release_info,
data_source=mbpseudo_plugin.data_source,
)
pseudo_album_info.use_official_as_ref()
item = Item()
item["title"] = "百花繚乱"
match = AlbumMatch(
distance=Distance(),
info=pseudo_album_info,
mapping={item: pseudo_album_info.tracks[0]},
extra_items=[],
extra_tracks=[],
)
mbpseudo_plugin._adjust_final_album_match(match)
assert match.info.data_source == "MusicBrainz"
assert match.info.album_id == "pseudo"
assert match.info.album == "In Bloom"
class TestMBPseudoPluginMultipleAllowed(PluginMixin):
plugin = "mbpseudo"
@pytest.fixture(scope="class")
def plugin_config(self):
return {"scripts": ["Latn", "Dummy"], "multiple_allowed": True}
@pytest.fixture(scope="class")
def mbpseudo_plugin(self, plugin_config) -> MusicBrainzPseudoReleasePlugin:
self.config[self.plugin].set(plugin_config)
config["import"]["languages"] = ["jp", "en"]
return MusicBrainzPseudoReleasePlugin()
@pytest.fixture(scope="class")
def official_release(self, rsrc_dir: pathlib.Path) -> JSONDict:
info_json = (rsrc_dir / "official_release.json").read_text(
encoding="utf-8"
)
return json.loads(info_json)
@pytest.fixture(scope="class")
def pseudo_release(self, rsrc_dir: pathlib.Path) -> JSONDict:
info_json = (rsrc_dir / "pseudo_release.json").read_text(
encoding="utf-8"
)
return json.loads(info_json)
def test_multiple_releases(
self,
mbpseudo_plugin: MusicBrainzPseudoReleasePlugin,
official_release: JSONDict,
pseudo_release: JSONDict,
):
def mock_release_getter(album_id: str, _) -> JSONDict:
if album_id == "dc3ee2df-0bc1-49eb-b8c4-34473d279a43":
return pseudo_release
else:
clone = deepcopy(pseudo_release)
clone["release"]["id"] = album_id
clone["release"]["text-representation"]["language"] = "jpn"
return clone
mbpseudo_plugin._release_getter = mock_release_getter
album_info = mbpseudo_plugin.album_info(official_release["release"])
assert isinstance(album_info, MultiPseudoAlbumInfo)
assert album_info.data_source == "MusicBrainzPseudoRelease"
assert len(album_info.unwrap()) == 2
assert (
album_info.unwrap()[0].album_id
== "dc3ee2df-mock-49eb-b8c4-34473d279a43"
)
assert (
album_info.unwrap()[1].album_id
== "dc3ee2df-0bc1-49eb-b8c4-34473d279a43"
)
class TestMBPseudoPluginCustomTagsOnly(PluginMixin):
plugin = "mbpseudo"
@pytest.fixture(scope="class")
def mbpseudo_plugin(self) -> MusicBrainzPseudoReleasePlugin:
self.config[self.plugin]["scripts"] = ["Latn"]
self.config[self.plugin]["custom_tags_only"] = True
return MusicBrainzPseudoReleasePlugin()
@pytest.fixture(scope="class")
def official_release(self, rsrc_dir: pathlib.Path) -> JSONDict:
info_json = (rsrc_dir / "official_release.json").read_text(
encoding="utf-8"
)
return json.loads(info_json)
@pytest.fixture(scope="class")
def pseudo_release(self, rsrc_dir: pathlib.Path) -> JSONDict:
info_json = (rsrc_dir / "pseudo_release.json").read_text(
encoding="utf-8"
)
return json.loads(info_json)
def test_custom_tags(
self,
mbpseudo_plugin: MusicBrainzPseudoReleasePlugin,
official_release: JSONDict,
pseudo_release: JSONDict,
):
config["import"]["languages"] = []
mbpseudo_plugin._release_getter = (
lambda album_id, includes: pseudo_release
)
album_info = mbpseudo_plugin.album_info(official_release["release"])
assert not isinstance(album_info, PseudoAlbumInfo)
assert album_info.data_source == "MusicBrainzPseudoRelease"
assert album_info["album_transl"] == "In Bloom"
assert album_info["album_artist_transl"] == "Lilas Ikuta"
assert album_info.tracks[0]["title_transl"] == "In Bloom"
assert album_info.tracks[0]["artist_transl"] == "Lilas Ikuta"
def test_custom_tags_with_import_languages(
self,
mbpseudo_plugin: MusicBrainzPseudoReleasePlugin,
official_release: JSONDict,
pseudo_release: JSONDict,
):
config["import"]["languages"] = ["en", "jp"]
mbpseudo_plugin._release_getter = (
lambda album_id, includes: pseudo_release
)
album_info = mbpseudo_plugin.album_info(official_release["release"])
assert not isinstance(album_info, PseudoAlbumInfo)
assert album_info.data_source == "MusicBrainzPseudoRelease"
assert album_info["album_transl"] == "In Bloom"
assert album_info["album_artist_transl"] == "Lilas Ikuta"
assert album_info.tracks[0]["title_transl"] == "In Bloom"
assert album_info.tracks[0]["artist_transl"] == "Lilas Ikuta"