diff --git a/beetsplug/mbpseudo.py b/beetsplug/mbpseudo.py index e55847f81..448aef365 100644 --- a/beetsplug/mbpseudo.py +++ b/beetsplug/mbpseudo.py @@ -16,6 +16,7 @@ from __future__ import annotations +import itertools import traceback from copy import deepcopy from typing import TYPE_CHECKING, Any, Iterable, Sequence @@ -24,6 +25,7 @@ import mediafile import musicbrainzngs from typing_extensions import override +from beets import config from beets.autotag.distance import Distance, distance from beets.autotag.hooks import AlbumInfo from beets.autotag.match import assign_items @@ -34,6 +36,7 @@ from beetsplug.musicbrainz import ( MusicBrainzAPIError, MusicBrainzPlugin, _merge_pseudo_and_actual_album, + _preferred_alias, ) if TYPE_CHECKING: @@ -143,12 +146,13 @@ class MusicBrainzPseudoReleasePlugin(MusicBrainzPlugin): try: raw_pseudo_release = self._release_getter( album_id, RELEASE_INCLUDES - ) - pseudo_release = super().album_info( - raw_pseudo_release["release"] - ) + )["release"] + pseudo_release = super().album_info(raw_pseudo_release) if self.config["custom_tags_only"].get(bool): + self._replace_artist_with_alias( + raw_pseudo_release, pseudo_release + ) self._add_custom_tags(official_release, pseudo_release) return official_release else: @@ -212,6 +216,41 @@ class MusicBrainzPseudoReleasePlugin(MusicBrainzPlugin): else: return None + def _replace_artist_with_alias( + self, + raw_pseudo_release: JSONDict, + pseudo_release: AlbumInfo, + ): + """Use the pseudo-release's language to search for artist + alias if the user hasn't configured import languages.""" + + if len(config["import"]["languages"].as_str_seq()) > 0: + return + + lang = raw_pseudo_release.get("text-representation", {}).get("language") + artist_credits = raw_pseudo_release.get("release-group", {}).get( + "artist-credit", [] + ) + aliases = [ + artist_credit.get("artist", {}).get("alias-list", []) + for artist_credit in artist_credits + ] + + if lang and len(lang) >= 2 and len(aliases) > 0: + locale = lang[0:2] + aliases_flattened = list(itertools.chain.from_iterable(aliases)) + self._log.debug( + "Using locale '{0}' to search aliases {1}", + locale, + aliases_flattened, + ) + if alias_dict := _preferred_alias(aliases_flattened, [locale]): + if alias := alias_dict.get("alias"): + self._log.debug("Got alias '{0}'", alias) + pseudo_release.artist = alias + for track in pseudo_release.tracks: + track.artist = alias + def _add_custom_tags( self, official_release: AlbumInfo, diff --git a/beetsplug/musicbrainz.py b/beetsplug/musicbrainz.py index cd53c3156..29bbc26d0 100644 --- a/beetsplug/musicbrainz.py +++ b/beetsplug/musicbrainz.py @@ -118,13 +118,15 @@ BROWSE_CHUNKSIZE = 100 BROWSE_MAXTRACKS = 500 -def _preferred_alias(aliases: list[JSONDict]): - """Given an list of alias structures for an artist credit, select - and return the user's preferred alias alias or None if no matching +def _preferred_alias( + aliases: list[JSONDict], languages: list[str] | None = None +) -> JSONDict | None: + """Given a list of alias structures for an artist credit, select + and return the user's preferred alias or None if no matching alias is found. """ if not aliases: - return + return None # Only consider aliases that have locales set. valid_aliases = [a for a in aliases if "locale" in a] @@ -134,7 +136,10 @@ def _preferred_alias(aliases: list[JSONDict]): ignored_alias_types = [a.lower() for a in ignored_alias_types] # Search configured locales in order. - for locale in config["import"]["languages"].as_str_seq(): + if languages is None: + languages = config["import"]["languages"].as_str_seq() + + for locale in languages: # Find matching primary aliases for this locale that are not # being ignored matches = [] @@ -152,6 +157,8 @@ def _preferred_alias(aliases: list[JSONDict]): return matches[0] + return None + def _multi_artist_credit( credit: list[JSONDict], include_join_phrase: bool diff --git a/test/plugins/test_mbpseudo.py b/test/plugins/test_mbpseudo.py index 8046dd0e6..621e08950 100644 --- a/test/plugins/test_mbpseudo.py +++ b/test/plugins/test_mbpseudo.py @@ -3,6 +3,7 @@ import pathlib import pytest +from beets import config from beets.autotag import AlbumMatch from beets.autotag.distance import Distance from beets.autotag.hooks import AlbumInfo, TrackInfo @@ -230,7 +231,6 @@ class TestMBPseudoPluginCustomTagsOnly(PluginMixin): @pytest.fixture(scope="class") def mbpseudo_plugin(self) -> MusicBrainzPseudoReleasePlugin: - self.config["import"]["languages"] = ["en", "jp"] self.config[self.plugin]["scripts"] = ["Latn"] self.config[self.plugin]["custom_tags_only"] = True return MusicBrainzPseudoReleasePlugin() @@ -255,6 +255,25 @@ class TestMBPseudoPluginCustomTagsOnly(PluginMixin): 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 )