Skip to content
Merged
35 changes: 18 additions & 17 deletions beetsplug/musicbrainz.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,20 +206,13 @@ def _preferred_alias(
for locale in languages:
# Find matching primary aliases for this locale that are not
# being ignored
matches = []
for alias in valid_aliases:
if (
alias["locale"] == locale
and alias.get("primary")
and (alias.get("type") or "").lower() not in ignored_alias_types
):
matches.append(alias)

# Skip to the next locale if we have no matches
if not matches:
continue

return matches[0]
return alias

return None

Expand All @@ -238,10 +231,7 @@ def _multi_artist_credit(
alias = _preferred_alias(el["artist"].get("aliases", ()))

# An artist.
if alias:
cur_artist_name = alias["name"]
else:
cur_artist_name = el["artist"]["name"]
cur_artist_name = alias["name"] if alias else el["artist"]["name"]
artist_parts.append(cur_artist_name)

# Artist sort name.
Expand Down Expand Up @@ -473,8 +463,11 @@ def track_info(
``medium_index``, the track's index on its medium; ``medium_total``,
the number of tracks on the medium. Each number is a 1-based index.
"""
alias = _preferred_alias(recording.get("aliases", ()))
title = alias["name"] if alias else recording["title"]

info = beets.autotag.hooks.TrackInfo(
title=recording["title"],
title=title,
track_id=recording["id"],
index=index,
medium=medium,
Expand Down Expand Up @@ -646,8 +639,11 @@ def album_info(self, release: JSONDict) -> beets.autotag.hooks.AlbumInfo:
ti.media = format
ti.track_alt = track["number"]

# Prefer track data, where present, over recording data.
if track.get("title"):
# Prefer track data, where present, over recording data except
# if an alias is available.
if track.get("title") and not _preferred_alias(
track["recording"].get("aliases", ())
):
ti.title = track["title"]
if track.get("artist-credit"):
# Get the artist names.
Expand All @@ -673,8 +669,10 @@ def album_info(self, release: JSONDict) -> beets.autotag.hooks.AlbumInfo:
track_infos.append(ti)

album_artist_ids = _artist_ids(release["artist-credit"])
alias = _preferred_alias(release.get("aliases", ()))
release_title = alias["name"] if alias else release["title"]
info = beets.autotag.hooks.AlbumInfo(
album=release["title"],
album=release_title,
album_id=release["id"],
artist=artist_name,
artist_id=album_artist_ids[0],
Expand All @@ -698,7 +696,10 @@ def album_info(self, release: JSONDict) -> beets.autotag.hooks.AlbumInfo:
info.albumstatus = release.get("status")

if release["release-group"].get("title"):
info.release_group_title = release["release-group"].get("title")
alias = _preferred_alias(release["release-group"].get("aliases", ()))
info.release_group_title = (
alias["name"] if alias else release["release-group"].get("title")
)

# Get the disambiguation strings at the release and release group level.
if release["release-group"].get("disambiguation"):
Expand Down
2 changes: 2 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ New features:
resolve differences in metadata source styles.
- :doc:`plugins/spotify`: Added support for multi-artist albums and tracks,
saving all contributing artists to the respective fields.
- :doc:`plugins/musicbrainz`: Use title alias for releases, release groups and
recordings.

Bug fixes:

Expand Down
3 changes: 3 additions & 0 deletions docs/reference/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,9 @@ MusicBrainz. You can use a space-separated list of language abbreviations, like
``en jp es``, to specify a preference order. Defaults to an empty list, meaning
that no language is preferred.

The alias is used for artist name, track title, album group title and album
title.

.. _ignored_alias_types:

ignored_alias_types
Expand Down
107 changes: 84 additions & 23 deletions test/plugins/test_musicbrainz.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@
from beetsplug import musicbrainz


def make_alias(suffix: str, locale: str, primary: bool = False):
alias = {
"name": f"ALIAS{suffix}",
"locale": locale,
"sort-name": f"ALIASSORT{suffix}",
}
if primary:
alias["primary"] = True

Check failure on line 35 in test/plugins/test_musicbrainz.py

View workflow job for this annotation

GitHub Actions / Check types with mypy

Incompatible types in assignment (expression has type "bool", target has type "str")
return alias


class MusicBrainzTestCase(BeetsTestCase):
def setUp(self):
super().setUp()
Expand Down Expand Up @@ -52,6 +63,7 @@
"first-release-date": date_str,
"id": "RELEASE GROUP ID",
"disambiguation": "RG_DISAMBIGUATION",
"title": "RELEASE GROUP TITLE",
},
"artist-credit": [
{
Expand Down Expand Up @@ -169,6 +181,7 @@
disambiguation=None,
remixer=False,
multi_artist_credit=False,
aliases=None,
):
track = {
"title": title,
Expand Down Expand Up @@ -218,8 +231,26 @@
track["video"] = True
if disambiguation:
track["disambiguation"] = disambiguation
if aliases is not None:
track["aliases"] = aliases
return track

def test_parse_release_title(self):
release = self._make_release(None)
release["aliases"] = [
make_alias(suffix="en", locale="en", primary=True),
]

# test no alias
config["import"]["languages"] = [""]
d = self.mb.album_info(release)
assert d.album == "ALBUM TITLE"

# test en primary
config["import"]["languages"] = ["en"]
d = self.mb.album_info(release)
assert d.album == "ALIASen"

def test_parse_release_with_year(self):
release = self._make_release("1984")
d = self.mb.album_info(release)
Expand All @@ -245,18 +276,45 @@

def test_parse_tracks(self):
tracks = [
self._make_track("TITLE ONE", "ID ONE", 100.0 * 1000.0),
self._make_track("TITLE TWO", "ID TWO", 200.0 * 1000.0),
self._make_track(
"TITLE ONE",
"ID ONE",
100.0 * 1000.0,
aliases=[make_alias(suffix="ONEen", locale="en", primary=True)],
),
self._make_track(
"TITLE TWO",
"ID TWO",
200.0 * 1000.0,
aliases=[make_alias(suffix="TWOen", locale="en", primary=True)],
),
]
release = self._make_release(tracks=tracks)

# Preference over recording data
release["media"][0]["tracks"][1]["title"] = "TRACK TITLE TWO"

# test no alias
config["import"]["languages"] = [""]
d = self.mb.album_info(release)
t = d.tracks
assert len(t) == 2
assert t[0].title == "TITLE ONE"
assert t[0].track_id == "ID ONE"
assert t[0].length == 100.0
assert t[1].title == "TITLE TWO"
assert t[1].title == "TRACK TITLE TWO"
assert t[1].track_id == "ID TWO"
assert t[1].length == 200.0

# test en primary
config["import"]["languages"] = ["en"]
d = self.mb.album_info(release)
t = d.tracks
assert len(t) == 2
assert t[0].title == "ALIASONEen"
assert t[0].track_id == "ID ONE"
assert t[0].length == 100.0
assert t[1].title == "ALIASTWOen"
assert t[1].track_id == "ID TWO"
assert t[1].length == 200.0

Expand Down Expand Up @@ -366,6 +424,22 @@
d = self.mb.album_info(release)
assert d.releasegroup_id == "RELEASE GROUP ID"

def test_parse_release_group_title(self):
release = self._make_release(None)
release["release-group"]["aliases"] = [
make_alias(suffix="en", locale="en", primary=True),
]

# test no alias
config["import"]["languages"] = [""]
d = self.mb.album_info(release)
assert d.release_group_title == "RELEASE GROUP TITLE"

# test en primary
config["import"]["languages"] = ["en"]
d = self.mb.album_info(release)
assert d.release_group_title == "ALIASen"

def test_parse_asin(self):
release = self._make_release(None)
d = self.mb.album_info(release)
Expand Down Expand Up @@ -698,18 +772,6 @@
"name": f"CREDIT{suffix}",
}

def _add_alias(self, credit_dict, suffix="", locale="", primary=False):
alias = {
"name": f"ALIAS{suffix}",
"locale": locale,
"sort-name": f"ALIASSORT{suffix}",
}
if primary:
alias["primary"] = "primary"
if "aliases" not in credit_dict["artist"]:
credit_dict["artist"]["aliases"] = []
credit_dict["artist"]["aliases"].append(alias)

def test_single_artist(self):
credit = [self._credit_dict()]
a, s, c = musicbrainz._flatten_artist_credit(credit)
Expand Down Expand Up @@ -743,14 +805,13 @@

def test_alias(self):
credit_dict = self._credit_dict()
self._add_alias(credit_dict, suffix="en", locale="en", primary=True)
self._add_alias(
credit_dict, suffix="en_GB", locale="en_GB", primary=True
)
self._add_alias(credit_dict, suffix="fr", locale="fr")
self._add_alias(credit_dict, suffix="fr_P", locale="fr", primary=True)
self._add_alias(credit_dict, suffix="pt_BR", locale="pt_BR")

credit_dict["artist"]["aliases"] = [
make_alias(suffix="en", locale="en", primary=True),
make_alias(suffix="en_GB", locale="en_GB", primary=True),
make_alias(suffix="fr", locale="fr"),
make_alias(suffix="fr_P", locale="fr", primary=True),
make_alias(suffix="pt_BR", locale="pt_BR"),
]
# test no alias
config["import"]["languages"] = [""]
flat = musicbrainz._flatten_artist_credit([credit_dict])
Expand Down
Loading