import json import pathlib from copy import deepcopy import pytest 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 ConfigMixin, PluginMixin from beetsplug._typing import JSONDict from beetsplug.mbpseudo import ( _STATUS_PSEUDO, MusicBrainzPseudoReleasePlugin, PseudoAlbumInfo, ) @pytest.fixture(scope="module") def rsrc_dir(pytestconfig: pytest.Config): return pytestconfig.rootpath / "test" / "rsrc" / "mbpseudo" @pytest.fixture def official_release(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(rsrc_dir: pathlib.Path) -> JSONDict: info_json = (rsrc_dir / "pseudo_release.json").read_text(encoding="utf-8") return json.loads(info_json) @pytest.fixture def official_release_info() -> AlbumInfo: return AlbumInfo( tracks=[TrackInfo(title="百花繚乱")], album_id="official", album="百花繚乱", ) @pytest.fixture def pseudo_release_info() -> AlbumInfo: return AlbumInfo( tracks=[TrackInfo(title="In Bloom")], album_id="pseudo", album="In Bloom", ) @pytest.fixture(scope="module", autouse=True) def config(): config = ConfigMixin().config with pytest.MonkeyPatch.context() as m: m.setattr("beetsplug.mbpseudo.config", config) yield config 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) 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(title="百花繚乱") assert info.determine_best_ref([item]) == "official" info.use_pseudo_as_ref() assert info.data_source == "test" class TestMBPseudoMixin(PluginMixin): plugin = "mbpseudo" @pytest.fixture(autouse=True) def patch_get_release(self, monkeypatch, pseudo_release: JSONDict): monkeypatch.setattr( "beetsplug.musicbrainz.MusicBrainzAPI.get_release", lambda _, album_id: deepcopy( {pseudo_release["id"]: pseudo_release}[album_id] ), ) @pytest.fixture(scope="class") def plugin_config(self): return {"scripts": ["Latn", "Dummy"]} @pytest.fixture def mbpseudo_plugin(self, plugin_config) -> MusicBrainzPseudoReleasePlugin: self.config[self.plugin].set(plugin_config) return MusicBrainzPseudoReleasePlugin() class TestMBPseudoPlugin(TestMBPseudoMixin): 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_album_info_for_pseudo_release( self, mbpseudo_plugin: MusicBrainzPseudoReleasePlugin, pseudo_release: JSONDict, ): album_info = mbpseudo_plugin.album_info(pseudo_release) assert not 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, ): del official_release["release-relations"][0][json_key] album_info = mbpseudo_plugin.album_info(official_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, ): official_release["release-relations"][0]["release"][ "text-representation" ]["script"] = "Null" album_info = mbpseudo_plugin.album_info(official_release) assert not isinstance(album_info, PseudoAlbumInfo) assert album_info.data_source == "MusicBrainzPseudoRelease" def test_interception( self, mbpseudo_plugin: MusicBrainzPseudoReleasePlugin, official_release: JSONDict, ): album_info = mbpseudo_plugin.album_info(official_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 TestMBPseudoPluginCustomTagsOnly(TestMBPseudoMixin): @pytest.fixture(scope="class") def plugin_config(self): return {"scripts": ["Latn", "Dummy"], "custom_tags_only": True} def test_custom_tags( self, config, mbpseudo_plugin: MusicBrainzPseudoReleasePlugin, official_release: JSONDict, ): config["import"]["languages"] = ["en", "jp"] album_info = mbpseudo_plugin.album_info(official_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, config, mbpseudo_plugin: MusicBrainzPseudoReleasePlugin, official_release: JSONDict, ): config["import"]["languages"] = [] album_info = mbpseudo_plugin.album_info(official_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"