From 9e7d5debdc408ed15bf35832103d3f8801e14567 Mon Sep 17 00:00:00 2001 From: Aidan Epstein Date: Tue, 15 Jul 2025 09:11:28 -0700 Subject: [PATCH 1/3] Allow selecting either tags or genres in the includes, defaulting to genres MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Genres is a filtered list based on what musicbrainz considers a genre, tags are all the user-submitted tags. [1] 1. https://musicbrainz.org/doc/MusicBrainz_API#:~:text=Since%20genres%20are,!). Also apply suggestions from code review Co-authored-by: Šarūnas Nejus --- beetsplug/musicbrainz.py | 10 ++++++++-- docs/changelog.rst | 2 ++ docs/plugins/musicbrainz.rst | 7 +++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/beetsplug/musicbrainz.py b/beetsplug/musicbrainz.py index 3b49107ad..57656b956 100644 --- a/beetsplug/musicbrainz.py +++ b/beetsplug/musicbrainz.py @@ -90,6 +90,7 @@ RELEASE_INCLUDES = list( "isrcs", "url-rels", "release-rels", + "genres", "tags", } & set(musicbrainzngs.VALID_INCLUDES["release"]) @@ -370,6 +371,10 @@ def _merge_pseudo_and_actual_album( class MusicBrainzPlugin(MetadataSourcePlugin): + @cached_property + def genres_field(self) -> str: + return f"{config['musicbrainz']['genres_tag'].get()}-list" + def __init__(self): """Set up the python-musicbrainz-ngs module according to settings from the beets configuration. This should be called at startup. @@ -382,6 +387,7 @@ class MusicBrainzPlugin(MetadataSourcePlugin): "ratelimit": 1, "ratelimit_interval": 1, "genres": False, + "genres_tag": "genre", "external_ids": { "discogs": False, "bandcamp": False, @@ -723,8 +729,8 @@ class MusicBrainzPlugin(MetadataSourcePlugin): if self.config["genres"]: sources = [ - release["release-group"].get("tag-list", []), - release.get("tag-list", []), + release["release-group"].get(self.genres_field, []), + release.get(self.genres_field, []), ] genres: Counter[str] = Counter() for source in sources: diff --git a/docs/changelog.rst b/docs/changelog.rst index e6821327e..1225f4f9f 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -13,6 +13,8 @@ been dropped. New features: - :doc:`plugins/ftintitle`: Added argument for custom feat. words in ftintitle. +- :doc:`plugins/musicbrainz`: Allow selecting tags or genres to populate the + genres tag. - :doc:`plugins/ftintitle`: Added argument to skip the processing of artist and album artist are the same in ftintitle. - :doc:`plugins/play`: Added `$playlist` marker to precisely edit the playlist diff --git a/docs/plugins/musicbrainz.rst b/docs/plugins/musicbrainz.rst index 00c553d8b..ac6d7a7d6 100644 --- a/docs/plugins/musicbrainz.rst +++ b/docs/plugins/musicbrainz.rst @@ -32,6 +32,7 @@ Default ratelimit_interval: 1.0 extra_tags: [] genres: no + genres_tag: genre external_ids: discogs: no bandcamp: no @@ -136,6 +137,12 @@ Default ``beatport_album_id``, ``deezer_album_id``, ``tidal_album_id``). On re-imports existing data will be overwritten. +.. conf:: _genres_tag + :default: genres + + Either ``genres`` or ``tags``. Specify ``genres`` to use just musicbrainz genres and + ``tags`` to use all user-supplied musicbrainz tags. + .. include:: ./shared_metadata_source_config.rst .. _building search indexes: https://musicbrainz.org/doc/Development/Search_server_setup From d7636fb0c3b34ea90e4296039208bd09203c9992 Mon Sep 17 00:00:00 2001 From: Aidan Epstein Date: Tue, 11 Nov 2025 13:18:51 -0800 Subject: [PATCH 2/3] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Šarūnas Nejus --- beetsplug/musicbrainz.py | 2 +- docs/plugins/musicbrainz.rst | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/beetsplug/musicbrainz.py b/beetsplug/musicbrainz.py index 57656b956..2b9d5e9c2 100644 --- a/beetsplug/musicbrainz.py +++ b/beetsplug/musicbrainz.py @@ -373,7 +373,7 @@ def _merge_pseudo_and_actual_album( class MusicBrainzPlugin(MetadataSourcePlugin): @cached_property def genres_field(self) -> str: - return f"{config['musicbrainz']['genres_tag'].get()}-list" + return f"{self.config['genres_tag'].as_choice(['genre', 'tag'])}-list" def __init__(self): """Set up the python-musicbrainz-ngs module according to settings diff --git a/docs/plugins/musicbrainz.rst b/docs/plugins/musicbrainz.rst index ac6d7a7d6..7fe436c2c 100644 --- a/docs/plugins/musicbrainz.rst +++ b/docs/plugins/musicbrainz.rst @@ -137,11 +137,11 @@ Default ``beatport_album_id``, ``deezer_album_id``, ``tidal_album_id``). On re-imports existing data will be overwritten. -.. conf:: _genres_tag - :default: genres +.. conf:: genres_tag + :default: genre - Either ``genres`` or ``tags``. Specify ``genres`` to use just musicbrainz genres and - ``tags`` to use all user-supplied musicbrainz tags. + Either ``genre`` or ``tag``. Specify ``genre`` to use just musicbrainz genre and + ``tag`` to use all user-supplied musicbrainz tags. .. include:: ./shared_metadata_source_config.rst From 672bf0bf41304e20bb8f9b1b2da78937e8087474 Mon Sep 17 00:00:00 2001 From: Aidan Epstein Date: Tue, 11 Nov 2025 16:24:32 -0800 Subject: [PATCH 3/3] Add tests. --- test/plugins/test_musicbrainz.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/plugins/test_musicbrainz.py b/test/plugins/test_musicbrainz.py index 844b2ad4e..9e271a481 100644 --- a/test/plugins/test_musicbrainz.py +++ b/test/plugins/test_musicbrainz.py @@ -65,6 +65,8 @@ class MBAlbumInfoTest(MusicBrainzTestCase): ], "date": "3001", "medium-list": [], + "genre-list": [{"count": 1, "name": "GENRE"}], + "tag-list": [{"count": 1, "name": "TAG"}], "label-info-list": [ { "catalog-number": "CATALOG NUMBER", @@ -515,6 +517,26 @@ class MBAlbumInfoTest(MusicBrainzTestCase): d = self.mb.album_info(release) assert d.data_source == "MusicBrainz" + def test_genres(self): + config["musicbrainz"]["genres"] = True + config["musicbrainz"]["genres_tag"] = "genre" + release = self._make_release() + d = self.mb.album_info(release) + assert d.genre == "GENRE" + + def test_tags(self): + config["musicbrainz"]["genres"] = True + config["musicbrainz"]["genres_tag"] = "tag" + release = self._make_release() + d = self.mb.album_info(release) + assert d.genre == "TAG" + + def test_no_genres(self): + config["musicbrainz"]["genres"] = False + release = self._make_release() + d = self.mb.album_info(release) + assert d.genre is None + def test_ignored_media(self): config["match"]["ignored_media"] = ["IGNORED1", "IGNORED2"] tracks = [