diff --git a/beetsplug/mbsync.py b/beetsplug/mbsync.py index 283c40186..5a17473c5 100644 --- a/beetsplug/mbsync.py +++ b/beetsplug/mbsync.py @@ -12,17 +12,14 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. -"""Update library's tags using MusicBrainz.""" +"""Synchronise library metadata with metadata source backends.""" -import re from collections import defaultdict from beets import autotag, library, ui, util from beets.autotag import hooks from beets.plugins import BeetsPlugin, apply_item_changes -MBID_REGEX = r"(\d|\w){8}-(\d|\w){4}-(\d|\w){4}-(\d|\w){4}-(\d|\w){12}" - class MBSyncPlugin(BeetsPlugin): def __init__(self): @@ -84,17 +81,7 @@ class MBSyncPlugin(BeetsPlugin): ) continue - # Do we have a valid MusicBrainz track ID? - if not re.match(MBID_REGEX, item.mb_trackid): - self._log.info( - "Skipping singleton with invalid mb_trackid:" + " {0}", - item_formatted, - ) - continue - - # Get the MusicBrainz recording info. - track_info = hooks.track_for_mbid(item.mb_trackid) - if not track_info: + if not (track_info := hooks.track_for_id(item.mb_trackid)): self._log.info( "Recording ID not found: {0} for track {0}", item.mb_trackid, @@ -121,18 +108,7 @@ class MBSyncPlugin(BeetsPlugin): continue items = list(a.items()) - - # Do we have a valid MusicBrainz album ID? - if not re.match(MBID_REGEX, a.mb_albumid): - self._log.info( - "Skipping album with invalid mb_albumid: {0}", - album_formatted, - ) - continue - - # Get the MusicBrainz album information. - album_info = hooks.album_for_mbid(a.mb_albumid) - if not album_info: + if not (album_info := hooks.album_for_id(a.mb_albumid)): self._log.info( "Release ID {0} not found for album {1}", a.mb_albumid, @@ -179,7 +155,7 @@ class MBSyncPlugin(BeetsPlugin): with lib.transaction(): autotag.apply_metadata(album_info, mapping) changed = False - # Find any changed item to apply MusicBrainz changes to album. + # Find any changed item to apply changes to album. any_changed_item = items[0] for item in items: item_changed = ui.show_model_changes(item) diff --git a/docs/changelog.rst b/docs/changelog.rst index 4dedabf33..32bd91390 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -22,6 +22,7 @@ New features: * :doc:`plugins/lyrics`: Rewrite lyrics translation functionality to use Azure AI Translator API and add relevant instructions to the documentation. * :doc:`plugins/missing`: Add support for all metadata sources. +* :doc:`plugins/mbsync`: Add support for all metadata sorces. Bug fixes: diff --git a/docs/plugins/mbsync.rst b/docs/plugins/mbsync.rst index 1c8663dca..647ff4df8 100644 --- a/docs/plugins/mbsync.rst +++ b/docs/plugins/mbsync.rst @@ -1,14 +1,13 @@ MBSync Plugin ============= -This plugin provides the ``mbsync`` command, which lets you fetch metadata -from MusicBrainz for albums and tracks that already have MusicBrainz IDs. This -is useful for updating tags as they are fixed in the MusicBrainz database, or -when you change your mind about some config options that change how tags are -written to files. If you have a music library that is already nicely tagged by -a program that also uses MusicBrainz like Picard, this can speed up the -initial import if you just import "as-is" and then use ``mbsync`` to get -up-to-date tags that are written to the files according to your beets +This plugin provides the ``mbsync`` command, which lets you synchronize +metadata for albums and tracks that have external data source IDs. + +This is useful for syncing your library with online data or when changing +configuration options that affect tag writing. If your music library already +contains correct tags, you can speed up the initial import by importing files +"as-is" and then using ``mbsync`` to write tags according to your beets configuration. diff --git a/test/plugins/test_mbsync.py b/test/plugins/test_mbsync.py index f65df4256..088165ef5 100644 --- a/test/plugins/test_mbsync.py +++ b/test/plugins/test_mbsync.py @@ -12,7 +12,7 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. -from unittest.mock import patch +from unittest.mock import Mock, patch from beets.autotag.hooks import AlbumInfo, TrackInfo from beets.library import Item @@ -22,37 +22,36 @@ from beets.test.helper import PluginTestCase, capture_log class MbsyncCliTest(PluginTestCase): plugin = "mbsync" - @patch("beets.autotag.mb.album_for_id") - @patch("beets.autotag.mb.track_for_id") - def test_update_library(self, track_for_id, album_for_id): + @patch( + "beets.plugins.album_for_id", + Mock( + side_effect=lambda *_: AlbumInfo( + album_id="album id", + album="new album", + tracks=[TrackInfo(track_id="track id", title="new title")], + ) + ), + ) + @patch( + "beets.plugins.track_for_id", + Mock( + side_effect=lambda *_: TrackInfo( + track_id="singleton id", title="new title" + ) + ), + ) + def test_update_library(self): album_item = Item( album="old album", - mb_albumid="81ae60d4-5b75-38df-903a-db2cfa51c2c6", + mb_albumid="album id", mb_trackid="track id", ) self.lib.add_album([album_item]) - singleton = Item( - title="old title", mb_trackid="b8c2cf90-83f9-3b5f-8ccd-31fb866fcf37" - ) + singleton = Item(title="old title", mb_trackid="singleton id") self.lib.add(singleton) - album_for_id.return_value = AlbumInfo( - album_id="album id", - album="new album", - tracks=[ - TrackInfo(track_id=album_item.mb_trackid, title="new title") - ], - ) - track_for_id.return_value = TrackInfo( - track_id=singleton.mb_trackid, title="new title" - ) - - with capture_log() as logs: - self.run_command("mbsync") - - assert "Sending event: albuminfo_received" in logs - assert "Sending event: trackinfo_received" in logs + self.run_command("mbsync") singleton.load() assert singleton.title == "new title" @@ -81,9 +80,6 @@ class MbsyncCliTest(PluginTestCase): with capture_log("beets.mbsync") as logs: self.run_command("mbsync", "-f", "'%if{$album,$album,$title}'") - assert set(logs) == { - "mbsync: Skipping album with no mb_albumid: 'no id'", - "mbsync: Skipping album with invalid mb_albumid: 'invalid id'", - "mbsync: Skipping singleton with no mb_trackid: 'no id'", - "mbsync: Skipping singleton with invalid mb_trackid: 'invalid id'", - } + + assert "mbsync: Skipping album with no mb_albumid: 'no id'" in logs + assert "mbsync: Skipping singleton with no mb_trackid: 'no id'" in logs