From 5fdc10bbd45bc614cf91113646e26dea2c7c1886 Mon Sep 17 00:00:00 2001 From: soergeld Date: Thu, 14 May 2020 12:17:49 +0200 Subject: [PATCH 01/53] Fetching artists flexibly, cleaning up on singletons --- beets/autotag/mb.py | 30 ++++++++++++++++++++++++++++++ beetsplug/mbsync.py | 6 ++++++ 2 files changed, 36 insertions(+) diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py index ea8ef24da7..d15c391ae5 100644 --- a/beets/autotag/mb.py +++ b/beets/autotag/mb.py @@ -250,6 +250,36 @@ def track_info(recording, index=None, medium=None, medium_index=None, arranger.append(artist_relation['artist']['name']) if arranger: info.arranger = u', '.join(arranger) + + artists = {} + for artist_relation in recording.get('artist-relation-list', ()): + if 'type' in artist_relation: + role = 'mb ' + role += artist_relation['type'] + if role in ['balance', 'recording', 'sound']: + role += ' engineer' + if role == 'performing orchestra': + role = 'orchestra' + role_sort = role + ' sort' + if 'attribute-list' in artist_relation: + role += ' - ' + role_sort += ' - ' + role += ', '.join(artist_relation['attribute-list']) + role_sort += ', '.join(artist_relation['attribute-list']) + if 'attributes' in artist_relation: + for attribute in artist_relation['attributes']: + if 'credited-as' in attribute: + role += ' ('+attribute['credited-as']+')' + role_sort += ' ('+attribute['credited-as']+')' + if role in artists: + artists[role].append(artist_relation['artist']['name']) + artists[role_sort].append( + artist_relation['artist']['sort-name']) + else: + artists[role] = [artist_relation['artist']['name']] + artists[role_sort] = [artist_relation['artist']['sort-name']] + for key in artists: + info[key] = u'; '.join(artists[key]) info.decode() return info diff --git a/beetsplug/mbsync.py b/beetsplug/mbsync.py index a2b3bc4aa4..aacd84289c 100644 --- a/beetsplug/mbsync.py +++ b/beetsplug/mbsync.py @@ -87,6 +87,10 @@ def singletons(self, lib, query, move, pretend, write): item_formatted) continue + # Clean up obsolete flexible fields + for tag in item: + if tag[:3] == 'mb ' and tag not in track_info: + del item[tag] # Apply. with lib.transaction(): autotag.apply_item_metadata(item, track_info) @@ -153,6 +157,8 @@ def albums(self, lib, query, move, pretend, write): # Apply. self._log.debug(u'applying changes to {}', album_formatted) with lib.transaction(): + # TODO: For all items, delete all tags of the form 'mb ...' + # that are in the item but not in the corresponding track_info. autotag.apply_metadata(album_info, mapping) changed = False # Find any changed item to apply MusicBrainz changes to album. From 149c1443d0ed13de4ce8375ae9c6b279b2e0b1c1 Mon Sep 17 00:00:00 2001 From: soergeld Date: Thu, 14 May 2020 12:35:07 +0200 Subject: [PATCH 02/53] Get artist-rels on singletons --- beets/autotag/mb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py index d15c391ae5..69157a5f0c 100644 --- a/beets/autotag/mb.py +++ b/beets/autotag/mb.py @@ -71,7 +71,7 @@ def get_message(self): 'labels', 'artist-credits', 'aliases', 'recording-level-rels', 'work-rels', 'work-level-rels', 'artist-rels'] -TRACK_INCLUDES = ['artists', 'aliases'] +TRACK_INCLUDES = ['artists', 'aliases', 'artist_rels'] if 'work-level-rels' in musicbrainzngs.VALID_INCLUDES['recording']: TRACK_INCLUDES += ['work-level-rels', 'artist-rels'] From bfd70213cecf27593d0b66834ce7a2da8024d242 Mon Sep 17 00:00:00 2001 From: soergeld Date: Thu, 14 May 2020 12:45:44 +0200 Subject: [PATCH 03/53] Flake-8 --- beets/autotag/mb.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py index 69157a5f0c..f31d17106f 100644 --- a/beets/autotag/mb.py +++ b/beets/autotag/mb.py @@ -250,7 +250,7 @@ def track_info(recording, index=None, medium=None, medium_index=None, arranger.append(artist_relation['artist']['name']) if arranger: info.arranger = u', '.join(arranger) - + artists = {} for artist_relation in recording.get('artist-relation-list', ()): if 'type' in artist_relation: @@ -269,8 +269,8 @@ def track_info(recording, index=None, medium=None, medium_index=None, if 'attributes' in artist_relation: for attribute in artist_relation['attributes']: if 'credited-as' in attribute: - role += ' ('+attribute['credited-as']+')' - role_sort += ' ('+attribute['credited-as']+')' + role += ' (' + attribute['credited-as'] + ')' + role_sort += ' (' + attribute['credited-as'] + ')' if role in artists: artists[role].append(artist_relation['artist']['name']) artists[role_sort].append( From 25712df80b3fb663eae3469c9472aab178bece9f Mon Sep 17 00:00:00 2001 From: soergeld Date: Sun, 7 Jun 2020 20:28:51 +0200 Subject: [PATCH 04/53] implement album changes, support for 500+ releases, no whitespaces --- beets/autotag/hooks.py | 4 +- beets/autotag/mb.py | 88 ++++++++++++++++++++++++++---------------- beetsplug/mbsync.py | 7 +++- 3 files changed, 62 insertions(+), 37 deletions(-) diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py index 9bdf6b0018..5a21584dab 100644 --- a/beets/autotag/hooks.py +++ b/beets/autotag/hooks.py @@ -550,12 +550,12 @@ def add_string(self, key, str1, str2): # Aggregation of sources. -def album_for_mbid(release_id): +def album_for_mbid(release_id, more_info=False): """Get an AlbumInfo object for a MusicBrainz release ID. Return None if the ID is not found. """ try: - album = mb.album_for_id(release_id) + album = mb.album_for_id(release_id, more_info=more_info) if album: plugins.send(u'albuminfo_received', info=album) return album diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py index f31d17106f..e4e16c5f39 100644 --- a/beets/autotag/mb.py +++ b/beets/autotag/mb.py @@ -184,7 +184,7 @@ def _flatten_artist_credit(credit): def track_info(recording, index=None, medium=None, medium_index=None, - medium_total=None): + medium_total=None, more_data=False, by_rec=False): """Translates a MusicBrainz recording result dictionary into a beets ``TrackInfo`` object. Three parameters are optional and are used only for tracks that appear on releases (non-singletons): ``index``, @@ -218,6 +218,17 @@ def track_info(recording, index=None, medium=None, medium_index=None, lyricist = [] composer = [] composer_sort = [] + + if by_rec: + recording_info = musicbrainzngs.get_recording_by_id( + recording['id'], TRACK_INCLUDES)['recording'] + if 'work-relation-list' not in recording_info.keys(): + recording_info = [] + else: + recording_info = recording_info['work-relation-list'] + else: + recording_info = recording.get('work-relation-list', ()) + for work_relation in recording.get('work-relation-list', ()): if work_relation['type'] != 'performance': continue @@ -251,35 +262,38 @@ def track_info(recording, index=None, medium=None, medium_index=None, if arranger: info.arranger = u', '.join(arranger) - artists = {} - for artist_relation in recording.get('artist-relation-list', ()): - if 'type' in artist_relation: - role = 'mb ' - role += artist_relation['type'] - if role in ['balance', 'recording', 'sound']: - role += ' engineer' - if role == 'performing orchestra': - role = 'orchestra' - role_sort = role + ' sort' - if 'attribute-list' in artist_relation: - role += ' - ' - role_sort += ' - ' - role += ', '.join(artist_relation['attribute-list']) - role_sort += ', '.join(artist_relation['attribute-list']) - if 'attributes' in artist_relation: - for attribute in artist_relation['attributes']: - if 'credited-as' in attribute: - role += ' (' + attribute['credited-as'] + ')' - role_sort += ' (' + attribute['credited-as'] + ')' - if role in artists: - artists[role].append(artist_relation['artist']['name']) - artists[role_sort].append( - artist_relation['artist']['sort-name']) - else: - artists[role] = [artist_relation['artist']['name']] - artists[role_sort] = [artist_relation['artist']['sort-name']] - for key in artists: - info[key] = u'; '.join(artists[key]) + if more_data: + artists = {} + for artist_relation in recording.get('artist-relation-list', ()): + if 'type' in artist_relation: + role = 'mbsync ' + role += artist_relation['type'] + if role in ['balance', 'recording', 'sound']: + role += ' engineer' + if role == 'performing orchestra': + role = 'orchestra' + role_sort = role + ' sort' + if 'attribute-list' in artist_relation: + role += ' - ' + role_sort += ' - ' + role += ', '.join(artist_relation['attribute-list']) + role_sort += ', '.join(artist_relation['attribute-list']) + if 'attributes' in artist_relation: + for attribute in artist_relation['attributes']: + if 'credited-as' in attribute: + role += ' (' + attribute['credited-as'] + ')' + role_sort += ' (' + attribute['credited-as'] + ')' + role.replace(" ", "_") + if role in artists: + artists[role].append(artist_relation['artist']['name']) + artists[role_sort].append( + artist_relation['artist']['sort-name']) + else: + artists[role] = [artist_relation['artist']['name']] + artists[role_sort] = [artist_relation[ + 'artist']['sort-name']] + for key in artists: + info[key] = u'; '.join(artists[key]) info.decode() return info @@ -305,7 +319,7 @@ def _set_date_str(info, date_str, original=False): setattr(info, key, date_num) -def album_info(release): +def album_info(release, more_info=False): """Takes a MusicBrainz release result dictionary and returns a beets AlbumInfo object containing the interesting data about that release. """ @@ -313,6 +327,10 @@ def album_info(release): artist_name, artist_sort_name, artist_credit_name = \ _flatten_artist_credit(release['artist-credit']) + ntracks = 0 + for medium in release['medium-list']: + ntracks += len(medium['track-list']) + # Basic info. track_infos = [] index = 0 @@ -344,6 +362,8 @@ def album_info(release): continue # Basic information from the recording. + by_rec = more_info and ntracks > 500 + index += 1 ti = track_info( track['recording'], @@ -351,6 +371,8 @@ def album_info(release): int(medium['position']), int(track['position']), track_count, + more_info=more_info, + by_rec=by_rec ) ti.release_track_id = track['id'] ti.disctitle = disctitle @@ -528,7 +550,7 @@ def _parse_id(s): return match.group() -def album_for_id(releaseid): +def album_for_id(releaseid, more_info=False): """Fetches an album by its MusicBrainz ID and returns an AlbumInfo object or None if the album is not found. May raise a MusicBrainzAPIError. @@ -547,7 +569,7 @@ def album_for_id(releaseid): except musicbrainzngs.MusicBrainzError as exc: raise MusicBrainzAPIError(exc, u'get release by ID', albumid, traceback.format_exc()) - return album_info(res['release']) + return album_info(res['release'], more_info=more_info) def track_for_id(releaseid): diff --git a/beetsplug/mbsync.py b/beetsplug/mbsync.py index aacd84289c..38b2957d19 100644 --- a/beetsplug/mbsync.py +++ b/beetsplug/mbsync.py @@ -89,7 +89,7 @@ def singletons(self, lib, query, move, pretend, write): # Clean up obsolete flexible fields for tag in item: - if tag[:3] == 'mb ' and tag not in track_info: + if tag[:6] == 'mbsync' and tag not in track_info: del item[tag] # Apply. with lib.transaction(): @@ -117,7 +117,7 @@ def albums(self, lib, query, move, pretend, write): continue # Get the MusicBrainz album information. - album_info = hooks.album_for_mbid(a.mb_albumid) + album_info = hooks.album_for_mbid(a.mb_albumid, more_info=True) if not album_info: self._log.info(u'Release ID {0} not found for album {1}', a.mb_albumid, @@ -138,6 +138,9 @@ def albums(self, lib, query, move, pretend, write): # work for albums that have missing or extra tracks. mapping = {} for item in items: + for tag in item: + if tag[:6] == 'mbsync' and tag not in track_info: + del item[tag] if item.mb_releasetrackid and \ item.mb_releasetrackid in releasetrack_index: mapping[item] = releasetrack_index[item.mb_releasetrackid] From 8cb0ccadfadc61aadae63a53eb764b180512a0c2 Mon Sep 17 00:00:00 2001 From: soergeld Date: Sun, 7 Jun 2020 20:32:33 +0200 Subject: [PATCH 05/53] debugging --- beets/autotag/hooks.py | 4 ++-- beets/autotag/mb.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py index 5a21584dab..50cc1a0634 100644 --- a/beets/autotag/hooks.py +++ b/beets/autotag/hooks.py @@ -563,12 +563,12 @@ def album_for_mbid(release_id, more_info=False): exc.log(log) -def track_for_mbid(recording_id): +def track_for_mbid(recording_id, more_info=False): """Get a TrackInfo object for a MusicBrainz recording ID. Return None if the ID is not found. """ try: - track = mb.track_for_id(recording_id) + track = mb.track_for_id(recording_id, more_info=more_info) if track: plugins.send(u'trackinfo_received', info=track) return track diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py index e4e16c5f39..d719d8b444 100644 --- a/beets/autotag/mb.py +++ b/beets/autotag/mb.py @@ -184,7 +184,7 @@ def _flatten_artist_credit(credit): def track_info(recording, index=None, medium=None, medium_index=None, - medium_total=None, more_data=False, by_rec=False): + medium_total=None, more_info=False, by_rec=False): """Translates a MusicBrainz recording result dictionary into a beets ``TrackInfo`` object. Three parameters are optional and are used only for tracks that appear on releases (non-singletons): ``index``, @@ -572,7 +572,7 @@ def album_for_id(releaseid, more_info=False): return album_info(res['release'], more_info=more_info) -def track_for_id(releaseid): +def track_for_id(releaseid, more_info=False): """Fetches a track by its MusicBrainz ID. Returns a TrackInfo object or None if no track is found. May raise a MusicBrainzAPIError. """ @@ -588,4 +588,4 @@ def track_for_id(releaseid): except musicbrainzngs.MusicBrainzError as exc: raise MusicBrainzAPIError(exc, u'get recording by ID', trackid, traceback.format_exc()) - return track_info(res['recording']) + return track_info(res['recording'], more_info=more_info) From ab36272210d6064fda7959e7f2375bd35062f3a0 Mon Sep 17 00:00:00 2001 From: soergeld Date: Sun, 7 Jun 2020 20:32:40 +0200 Subject: [PATCH 06/53] debugging --- beetsplug/mbsync.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/beetsplug/mbsync.py b/beetsplug/mbsync.py index 38b2957d19..32d87f3fab 100644 --- a/beetsplug/mbsync.py +++ b/beetsplug/mbsync.py @@ -47,6 +47,10 @@ def commands(self): u'-W', u'--nowrite', action='store_false', default=None, dest='write', help=u"don't write updated metadata to files") + cmd.parser.add_option( + u'-W', u'--nowrite', action='store_false', + default=None, dest='write', + help=u"don't write updated metadata to files") cmd.parser.add_format_option() cmd.func = self.func return [cmd] @@ -80,7 +84,7 @@ def singletons(self, lib, query, move, pretend, write): continue # Get the MusicBrainz recording info. - track_info = hooks.track_for_mbid(item.mb_trackid) + track_info = hooks.track_for_mbid(item.mb_trackid, more_info=True) if not track_info: self._log.info(u'Recording ID not found: {0} for track {0}', item.mb_trackid, From 4178eb89f1f63300134ac5a9b308f8e5ea3fc129 Mon Sep 17 00:00:00 2001 From: soergeld Date: Sun, 7 Jun 2020 20:37:58 +0200 Subject: [PATCH 07/53] add "more_info" as an option --- beetsplug/mbsync.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/beetsplug/mbsync.py b/beetsplug/mbsync.py index 32d87f3fab..8190c1fca4 100644 --- a/beetsplug/mbsync.py +++ b/beetsplug/mbsync.py @@ -48,9 +48,8 @@ def commands(self): default=None, dest='write', help=u"don't write updated metadata to files") cmd.parser.add_option( - u'-W', u'--nowrite', action='store_false', - default=None, dest='write', - help=u"don't write updated metadata to files") + u'-I', u'--more_info', action='store_true', default=None, + help=u"Fetch more data") cmd.parser.add_format_option() cmd.func = self.func return [cmd] @@ -61,12 +60,13 @@ def func(self, lib, opts, args): move = ui.should_move(opts.move) pretend = opts.pretend write = ui.should_write(opts.write) + more_info = opts.more_info query = ui.decargs(args) - self.singletons(lib, query, move, pretend, write) - self.albums(lib, query, move, pretend, write) + self.singletons(lib, query, move, pretend, write, more_info) + self.albums(lib, query, move, pretend, write, more_info) - def singletons(self, lib, query, move, pretend, write): + def singletons(self, lib, query, move, pretend, write, more_info): """Retrieve and apply info from the autotagger for items matched by query. """ @@ -84,7 +84,8 @@ def singletons(self, lib, query, move, pretend, write): continue # Get the MusicBrainz recording info. - track_info = hooks.track_for_mbid(item.mb_trackid, more_info=True) + track_info = hooks.track_for_mbid(item.mb_trackid, + more_info=more_info) if not track_info: self._log.info(u'Recording ID not found: {0} for track {0}', item.mb_trackid, @@ -100,7 +101,7 @@ def singletons(self, lib, query, move, pretend, write): autotag.apply_item_metadata(item, track_info) apply_item_changes(lib, item, move, pretend, write) - def albums(self, lib, query, move, pretend, write): + def albums(self, lib, query, move, pretend, write, more_info): """Retrieve and apply info from the autotagger for albums matched by query and their items. """ @@ -121,7 +122,8 @@ def albums(self, lib, query, move, pretend, write): continue # Get the MusicBrainz album information. - album_info = hooks.album_for_mbid(a.mb_albumid, more_info=True) + album_info = hooks.album_for_mbid(a.mb_albumid, + more_info=more_info) if not album_info: self._log.info(u'Release ID {0} not found for album {1}', a.mb_albumid, From 2d4083b85cfb47f6960998793ca52bc4a9fec6f4 Mon Sep 17 00:00:00 2001 From: soergeld Date: Sun, 7 Jun 2020 20:39:14 +0200 Subject: [PATCH 08/53] typo --- beets/autotag/mb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py index d719d8b444..b6af4f1218 100644 --- a/beets/autotag/mb.py +++ b/beets/autotag/mb.py @@ -71,7 +71,7 @@ def get_message(self): 'labels', 'artist-credits', 'aliases', 'recording-level-rels', 'work-rels', 'work-level-rels', 'artist-rels'] -TRACK_INCLUDES = ['artists', 'aliases', 'artist_rels'] +TRACK_INCLUDES = ['artists', 'aliases', 'artist-rels'] if 'work-level-rels' in musicbrainzngs.VALID_INCLUDES['recording']: TRACK_INCLUDES += ['work-level-rels', 'artist-rels'] From aa683e6af505eed2d101d2115934594469cdb133 Mon Sep 17 00:00:00 2001 From: soergeld Date: Sun, 7 Jun 2020 20:40:01 +0200 Subject: [PATCH 09/53] typo --- beets/autotag/mb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py index b6af4f1218..4b2ae4ea53 100644 --- a/beets/autotag/mb.py +++ b/beets/autotag/mb.py @@ -262,7 +262,7 @@ def track_info(recording, index=None, medium=None, medium_index=None, if arranger: info.arranger = u', '.join(arranger) - if more_data: + if more_info: artists = {} for artist_relation in recording.get('artist-relation-list', ()): if 'type' in artist_relation: From 8d37234af46678acb0eae7d5db032b88bf5cadde Mon Sep 17 00:00:00 2001 From: soergeld Date: Sun, 7 Jun 2020 20:49:09 +0200 Subject: [PATCH 10/53] debugging, mbsync shows changes also for singletons --- beets/autotag/mb.py | 3 ++- beetsplug/mbsync.py | 15 +++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py index 4b2ae4ea53..1b99cdac63 100644 --- a/beets/autotag/mb.py +++ b/beets/autotag/mb.py @@ -283,7 +283,8 @@ def track_info(recording, index=None, medium=None, medium_index=None, if 'credited-as' in attribute: role += ' (' + attribute['credited-as'] + ')' role_sort += ' (' + attribute['credited-as'] + ')' - role.replace(" ", "_") + role = role.replace(" ", "_") + role_sort = role_sort.replace(' ','_') if role in artists: artists[role].append(artist_relation['artist']['name']) artists[role_sort].append( diff --git a/beetsplug/mbsync.py b/beetsplug/mbsync.py index 8190c1fca4..ee87b6ce60 100644 --- a/beetsplug/mbsync.py +++ b/beetsplug/mbsync.py @@ -93,12 +93,14 @@ def singletons(self, lib, query, move, pretend, write, more_info): continue # Clean up obsolete flexible fields - for tag in item: - if tag[:6] == 'mbsync' and tag not in track_info: - del item[tag] + if more_info: + for tag in item: + if tag[:6] == 'mbsync' and tag not in track_info: + del item[tag] # Apply. with lib.transaction(): autotag.apply_item_metadata(item, track_info) + ui.show_model_changes(item) apply_item_changes(lib, item, move, pretend, write) def albums(self, lib, query, move, pretend, write, more_info): @@ -144,9 +146,10 @@ def albums(self, lib, query, move, pretend, write, more_info): # work for albums that have missing or extra tracks. mapping = {} for item in items: - for tag in item: - if tag[:6] == 'mbsync' and tag not in track_info: - del item[tag] + if more_info: + for tag in item: + if tag[:6] == 'mbsync' and tag not in track_info: + del item[tag] if item.mb_releasetrackid and \ item.mb_releasetrackid in releasetrack_index: mapping[item] = releasetrack_index[item.mb_releasetrackid] From fdc0fc861aec400fead1ca8015281fc2b32ca848 Mon Sep 17 00:00:00 2001 From: soergeld Date: Sun, 7 Jun 2020 20:50:27 +0200 Subject: [PATCH 11/53] flake8 --- beets/autotag/mb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py index 1b99cdac63..d8a5d5f227 100644 --- a/beets/autotag/mb.py +++ b/beets/autotag/mb.py @@ -284,7 +284,7 @@ def track_info(recording, index=None, medium=None, medium_index=None, role += ' (' + attribute['credited-as'] + ')' role_sort += ' (' + attribute['credited-as'] + ')' role = role.replace(" ", "_") - role_sort = role_sort.replace(' ','_') + role_sort = role_sort.replace(' ', '_') if role in artists: artists[role].append(artist_relation['artist']['name']) artists[role_sort].append( From 19c4f59afb5f69a3cfa42a80f8c1d9e106e2f01e Mon Sep 17 00:00:00 2001 From: soergeld Date: Sun, 7 Jun 2020 21:02:17 +0200 Subject: [PATCH 12/53] forgot some more_info --- beets/autotag/hooks.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py index 50cc1a0634..e85d260c2d 100644 --- a/beets/autotag/hooks.py +++ b/beets/autotag/hooks.py @@ -576,9 +576,9 @@ def track_for_mbid(recording_id, more_info=False): exc.log(log) -def albums_for_id(album_id): +def albums_for_id(album_id, more_info=False): """Get a list of albums for an ID.""" - a = album_for_mbid(album_id) + a = album_for_mbid(album_id, more_info=more_info) if a: yield a for a in plugins.album_for_id(album_id): @@ -587,9 +587,9 @@ def albums_for_id(album_id): yield a -def tracks_for_id(track_id): +def tracks_for_id(track_id, more_info=False): """Get a list of tracks for an ID.""" - t = track_for_mbid(track_id) + t = track_for_mbid(track_id, more_info=more_info) if t: yield t for t in plugins.track_for_id(track_id): From 259d72096591e87cb24eac58ba77f7217aaeefdd Mon Sep 17 00:00:00 2001 From: soergeld Date: Sun, 7 Jun 2020 22:02:32 +0200 Subject: [PATCH 13/53] convert flexible fields to unicode --- beets/autotag/mb.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py index d8a5d5f227..37800f0d2d 100644 --- a/beets/autotag/mb.py +++ b/beets/autotag/mb.py @@ -283,8 +283,9 @@ def track_info(recording, index=None, medium=None, medium_index=None, if 'credited-as' in attribute: role += ' (' + attribute['credited-as'] + ')' role_sort += ' (' + attribute['credited-as'] + ')' - role = role.replace(" ", "_") - role_sort = role_sort.replace(' ', '_') + role = role.replace(" ", "_").decode('utf-8', 'ignore') + role_sort = role_sort.replace(' ', '_').decode('utf-8', + 'ignore') if role in artists: artists[role].append(artist_relation['artist']['name']) artists[role_sort].append( From 2a2d5d3e2458470a2ff7bcaeb04486ecdd0e03da Mon Sep 17 00:00:00 2001 From: soergeld Date: Sun, 7 Jun 2020 22:14:13 +0200 Subject: [PATCH 14/53] adapt roles --- beets/autotag/mb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py index 37800f0d2d..203e3b3923 100644 --- a/beets/autotag/mb.py +++ b/beets/autotag/mb.py @@ -268,7 +268,7 @@ def track_info(recording, index=None, medium=None, medium_index=None, if 'type' in artist_relation: role = 'mbsync ' role += artist_relation['type'] - if role in ['balance', 'recording', 'sound']: + if 'balance' in role or 'recording' in role or 'sound' in role: role += ' engineer' if role == 'performing orchestra': role = 'orchestra' From e9b9b4b7621f0b08de7bc192e466c68c77fae67d Mon Sep 17 00:00:00 2001 From: soergeld Date: Sun, 7 Jun 2020 23:42:35 +0200 Subject: [PATCH 15/53] decoding roles doesn't work --- beets/autotag/mb.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py index 203e3b3923..8a8c8aa96c 100644 --- a/beets/autotag/mb.py +++ b/beets/autotag/mb.py @@ -283,9 +283,8 @@ def track_info(recording, index=None, medium=None, medium_index=None, if 'credited-as' in attribute: role += ' (' + attribute['credited-as'] + ')' role_sort += ' (' + attribute['credited-as'] + ')' - role = role.replace(" ", "_").decode('utf-8', 'ignore') - role_sort = role_sort.replace(' ', '_').decode('utf-8', - 'ignore') + role = role.replace(" ", "_") + role_sort = role_sort.replace(' ', '_') if role in artists: artists[role].append(artist_relation['artist']['name']) artists[role_sort].append( From 263a821a419337371ae925557a9b749ce3269078 Mon Sep 17 00:00:00 2001 From: soergeld Date: Sun, 7 Jun 2020 23:49:47 +0200 Subject: [PATCH 16/53] arranging orchestra role --- beets/autotag/mb.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py index 8a8c8aa96c..1abe0d879c 100644 --- a/beets/autotag/mb.py +++ b/beets/autotag/mb.py @@ -270,8 +270,8 @@ def track_info(recording, index=None, medium=None, medium_index=None, role += artist_relation['type'] if 'balance' in role or 'recording' in role or 'sound' in role: role += ' engineer' - if role == 'performing orchestra': - role = 'orchestra' + if 'performing orchestra' in role: + role = 'mbsync orchestra' role_sort = role + ' sort' if 'attribute-list' in artist_relation: role += ' - ' From 5464a75d2df9650f260648bb615044795c85bf5e Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 10 Aug 2020 11:04:20 +0200 Subject: [PATCH 17/53] moving fetching of performers to plugin --- beets/autotag/hooks.py | 16 +++--- beets/autotag/mb.py | 67 ++++--------------------- beetsplug/mbsync.py | 108 +++++++++++++++++++++++++++++++++++++++-- 3 files changed, 120 insertions(+), 71 deletions(-) diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py index e85d260c2d..9bdf6b0018 100644 --- a/beets/autotag/hooks.py +++ b/beets/autotag/hooks.py @@ -550,12 +550,12 @@ def add_string(self, key, str1, str2): # Aggregation of sources. -def album_for_mbid(release_id, more_info=False): +def album_for_mbid(release_id): """Get an AlbumInfo object for a MusicBrainz release ID. Return None if the ID is not found. """ try: - album = mb.album_for_id(release_id, more_info=more_info) + album = mb.album_for_id(release_id) if album: plugins.send(u'albuminfo_received', info=album) return album @@ -563,12 +563,12 @@ def album_for_mbid(release_id, more_info=False): exc.log(log) -def track_for_mbid(recording_id, more_info=False): +def track_for_mbid(recording_id): """Get a TrackInfo object for a MusicBrainz recording ID. Return None if the ID is not found. """ try: - track = mb.track_for_id(recording_id, more_info=more_info) + track = mb.track_for_id(recording_id) if track: plugins.send(u'trackinfo_received', info=track) return track @@ -576,9 +576,9 @@ def track_for_mbid(recording_id, more_info=False): exc.log(log) -def albums_for_id(album_id, more_info=False): +def albums_for_id(album_id): """Get a list of albums for an ID.""" - a = album_for_mbid(album_id, more_info=more_info) + a = album_for_mbid(album_id) if a: yield a for a in plugins.album_for_id(album_id): @@ -587,9 +587,9 @@ def albums_for_id(album_id, more_info=False): yield a -def tracks_for_id(track_id, more_info=False): +def tracks_for_id(track_id): """Get a list of tracks for an ID.""" - t = track_for_mbid(track_id, more_info=more_info) + t = track_for_mbid(track_id) if t: yield t for t in plugins.track_for_id(track_id): diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py index 1abe0d879c..d3a1435f7d 100644 --- a/beets/autotag/mb.py +++ b/beets/autotag/mb.py @@ -23,6 +23,7 @@ from six.moves.urllib.parse import urljoin from beets import logging +from beets import plugins import beets.autotag.hooks import beets from beets import util @@ -184,7 +185,7 @@ def _flatten_artist_credit(credit): def track_info(recording, index=None, medium=None, medium_index=None, - medium_total=None, more_info=False, by_rec=False): + medium_total=None): """Translates a MusicBrainz recording result dictionary into a beets ``TrackInfo`` object. Three parameters are optional and are used only for tracks that appear on releases (non-singletons): ``index``, @@ -219,16 +220,6 @@ def track_info(recording, index=None, medium=None, medium_index=None, composer = [] composer_sort = [] - if by_rec: - recording_info = musicbrainzngs.get_recording_by_id( - recording['id'], TRACK_INCLUDES)['recording'] - if 'work-relation-list' not in recording_info.keys(): - recording_info = [] - else: - recording_info = recording_info['work-relation-list'] - else: - recording_info = recording.get('work-relation-list', ()) - for work_relation in recording.get('work-relation-list', ()): if work_relation['type'] != 'performance': continue @@ -262,40 +253,6 @@ def track_info(recording, index=None, medium=None, medium_index=None, if arranger: info.arranger = u', '.join(arranger) - if more_info: - artists = {} - for artist_relation in recording.get('artist-relation-list', ()): - if 'type' in artist_relation: - role = 'mbsync ' - role += artist_relation['type'] - if 'balance' in role or 'recording' in role or 'sound' in role: - role += ' engineer' - if 'performing orchestra' in role: - role = 'mbsync orchestra' - role_sort = role + ' sort' - if 'attribute-list' in artist_relation: - role += ' - ' - role_sort += ' - ' - role += ', '.join(artist_relation['attribute-list']) - role_sort += ', '.join(artist_relation['attribute-list']) - if 'attributes' in artist_relation: - for attribute in artist_relation['attributes']: - if 'credited-as' in attribute: - role += ' (' + attribute['credited-as'] + ')' - role_sort += ' (' + attribute['credited-as'] + ')' - role = role.replace(" ", "_") - role_sort = role_sort.replace(' ', '_') - if role in artists: - artists[role].append(artist_relation['artist']['name']) - artists[role_sort].append( - artist_relation['artist']['sort-name']) - else: - artists[role] = [artist_relation['artist']['name']] - artists[role_sort] = [artist_relation[ - 'artist']['sort-name']] - for key in artists: - info[key] = u'; '.join(artists[key]) - info.decode() return info @@ -320,7 +277,7 @@ def _set_date_str(info, date_str, original=False): setattr(info, key, date_num) -def album_info(release, more_info=False): +def album_info(release): """Takes a MusicBrainz release result dictionary and returns a beets AlbumInfo object containing the interesting data about that release. """ @@ -328,10 +285,6 @@ def album_info(release, more_info=False): artist_name, artist_sort_name, artist_credit_name = \ _flatten_artist_credit(release['artist-credit']) - ntracks = 0 - for medium in release['medium-list']: - ntracks += len(medium['track-list']) - # Basic info. track_infos = [] index = 0 @@ -363,8 +316,6 @@ def album_info(release, more_info=False): continue # Basic information from the recording. - by_rec = more_info and ntracks > 500 - index += 1 ti = track_info( track['recording'], @@ -372,8 +323,6 @@ def album_info(release, more_info=False): int(medium['position']), int(track['position']), track_count, - more_info=more_info, - by_rec=by_rec ) ti.release_track_id = track['id'] ti.disctitle = disctitle @@ -551,7 +500,7 @@ def _parse_id(s): return match.group() -def album_for_id(releaseid, more_info=False): +def album_for_id(releaseid): """Fetches an album by its MusicBrainz ID and returns an AlbumInfo object or None if the album is not found. May raise a MusicBrainzAPIError. @@ -564,16 +513,17 @@ def album_for_id(releaseid, more_info=False): try: res = musicbrainzngs.get_release_by_id(albumid, RELEASE_INCLUDES) + beets.plugins.send(u'albumdata_recieved', info=res['release']) except musicbrainzngs.ResponseError: log.debug(u'Album ID match failed.') return None except musicbrainzngs.MusicBrainzError as exc: raise MusicBrainzAPIError(exc, u'get release by ID', albumid, traceback.format_exc()) - return album_info(res['release'], more_info=more_info) + return album_info(res['release']) -def track_for_id(releaseid, more_info=False): +def track_for_id(releaseid): """Fetches a track by its MusicBrainz ID. Returns a TrackInfo object or None if no track is found. May raise a MusicBrainzAPIError. """ @@ -583,10 +533,11 @@ def track_for_id(releaseid, more_info=False): return try: res = musicbrainzngs.get_recording_by_id(trackid, TRACK_INCLUDES) + beets.plugins.send(u'trackdata_recieved', info=res['recording']) except musicbrainzngs.ResponseError: log.debug(u'Track ID match failed.') return None except musicbrainzngs.MusicBrainzError as exc: raise MusicBrainzAPIError(exc, u'get recording by ID', trackid, traceback.format_exc()) - return track_info(res['recording'], more_info=more_info) + return track_info(res['recording']) diff --git a/beetsplug/mbsync.py b/beetsplug/mbsync.py index ee87b6ce60..2ebbfd5fae 100644 --- a/beetsplug/mbsync.py +++ b/beetsplug/mbsync.py @@ -12,7 +12,6 @@ # # 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. """ from __future__ import division, absolute_import, print_function @@ -21,12 +20,95 @@ from beets import autotag, library, ui, util from beets.autotag import hooks from collections import defaultdict +from beets import config +import musicbrainzngs import re +TRACK_INCLUDES = ['artist-rels'] +SKIPPED_TRACKS = ['[data track]'] + MBID_REGEX = r"(\d|\w){8}-(\d|\w){4}-(\d|\w){4}-(\d|\w){4}-(\d|\w){12}" +def track_performers(info): + artists = {} + for artist_relation in info.get('artist-relation-list', ()): + if 'type' in artist_relation: + role = 'mbsync ' + role += artist_relation['type'] + if 'balance' in role or 'recording' in role or 'sound' in role: + role += ' engineer' + if 'performing orchestra' in role: + role = 'mbsync orchestra' + role_sort = role + ' sort' + if 'attribute-list' in artist_relation: + role += ' - ' + role_sort += ' - ' + role += ', '.join(artist_relation['attribute-list']) + role_sort += ', '.join(artist_relation['attribute-list']) + if 'attributes' in artist_relation: + for attribute in artist_relation['attributes']: + if 'credited-as' in attribute: + role += ' (' + attribute['credited-as'] + ')' + role_sort += ' (' + attribute['credited-as'] + ')' + role = role.replace(" ", "_") + role_sort = role_sort.replace(' ', '_') + if role in artists: + artists[role].append(artist_relation['artist']['name']) + artists[role_sort].append( + artist_relation['artist']['sort-name']) + else: + artists[role] = [artist_relation['artist']['name']] + artists[role_sort] = [artist_relation[ + 'artist']['sort-name']] + return artists + + +def album_performers(info): + + track_infos = [] + + ntracks = 0 + for medium in info['medium-list']: + ntracks += len(medium['track-list']) + + for medium in info['medium-list']: + format = medium.get('format') + + if format in config['match']['ignored_media'].as_str_seq(): + continue + + all_tracks = medium['track-list'] + if ('data-track-list' in medium + and not config['match']['ignore_data_tracks']): + all_tracks += medium['data-track-list'] + + if 'pregap' in medium: + all_tracks.insert(0, medium['pregap']) + + for track in all_tracks: + + if ('title' in track['recording'] and + track['recording']['title'] in SKIPPED_TRACKS): + continue + + if ('video' in track['recording'] and + track['recording']['video'] == 'true' and + config['match']['ignore_video_tracks']): + continue + + # Basic information from the recording. + if ntracks < 500: + track_infos.append(track_performers(track['recording'])) + else: + res = musicbrainzngs.get_recording_by_id( + track['recording']['id'], TRACK_INCLUDES)['recording'] + track_infos.append(track_performers(res)) + + return track_infos + + class MBSyncPlugin(BeetsPlugin): def __init__(self): super(MBSyncPlugin, self).__init__() @@ -84,19 +166,26 @@ def singletons(self, lib, query, move, pretend, write, more_info): continue # Get the MusicBrainz recording info. - track_info = hooks.track_for_mbid(item.mb_trackid, - more_info=more_info) + if more_info: + performer_info = self.register_listener('trackdata_recieved', track_performers) + track_info = hooks.track_for_mbid(item.mb_trackid) if not track_info: self._log.info(u'Recording ID not found: {0} for track {0}', item.mb_trackid, item_formatted) continue - + print(track_info['title']) + print(track_info['artist']) + print(performer_info) # Clean up obsolete flexible fields if more_info: for tag in item: if tag[:6] == 'mbsync' and tag not in track_info: del item[tag] + + for key in performer_info: + track_info[key] = u'; '.join(performer_info[key]) + track_info.decode() # Apply. with lib.transaction(): autotag.apply_item_metadata(item, track_info) @@ -124,6 +213,8 @@ def albums(self, lib, query, move, pretend, write, more_info): continue # Get the MusicBrainz album information. + if more_info: + performer_info = self.register_listener('albumdata_recieved', album_performers) album_info = hooks.album_for_mbid(a.mb_albumid, more_info=more_info) if not album_info: @@ -137,9 +228,16 @@ def albums(self, lib, query, move, pretend, write, more_info): # maps to a list of TrackInfo objects. releasetrack_index = dict() track_index = defaultdict(list) - for track_info in album_info.tracks: + for i in range(len(album_info.tracks)): + track_info = album_info.tracks[i] releasetrack_index[track_info.release_track_id] = track_info track_index[track_info.track_id].append(track_info) + if more_info: + track_performer_info = performer_info[i] + for key in track_performer_info: + track_info[key] = track_performer_info[key] + if more_info: + album_info.decode() # Construct a track mapping according to MBIDs (release track MBIDs # first, if available, and recording MBIDs otherwise). This should From 48f43ed1a1891bac454b1e047d905e02e24dd258 Mon Sep 17 00:00:00 2001 From: soergeld Date: Thu, 24 Dec 2020 11:39:58 +0100 Subject: [PATCH 18/53] getting the events and return values correctly --- beets/autotag/mb.py | 17 ++++++++++- beetsplug/mbsync.py | 71 +++++---------------------------------------- 2 files changed, 24 insertions(+), 64 deletions(-) diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py index d3a1435f7d..1093a0482c 100644 --- a/beets/autotag/mb.py +++ b/beets/autotag/mb.py @@ -21,6 +21,7 @@ import re import traceback from six.moves.urllib.parse import urljoin +from itertools import chain from beets import logging from beets import plugins @@ -253,6 +254,12 @@ def track_info(recording, index=None, medium=None, medium_index=None, if arranger: info.arranger = u', '.join(arranger) + # supplementary tags provided by plugins + extra_trackdatas = plugins.send('extracting_trackdata', info=recording) + for extra_trackdata in extra_trackdatas: + for key in extra_trackdata: + info[key] = extra_trackdata[key] + info.decode() return info @@ -417,6 +424,15 @@ def album_info(release): first_medium = release['medium-list'][0] info.media = first_medium.get('format') + # supplementary tags provided by plugins + extra_albumdatas = list(chain(*plugins.send('extracting_albumdata', + info=release))) + print(extra_albumdatas) + for extra_albumdata in extra_albumdatas: + print(extra_albumdatas) + for key in extra_albumdata: + print(key) + info[key] = extra_albumdata[key] info.decode() return info @@ -533,7 +549,6 @@ def track_for_id(releaseid): return try: res = musicbrainzngs.get_recording_by_id(trackid, TRACK_INCLUDES) - beets.plugins.send(u'trackdata_recieved', info=res['recording']) except musicbrainzngs.ResponseError: log.debug(u'Track ID match failed.') return None diff --git a/beetsplug/mbsync.py b/beetsplug/mbsync.py index 2ebbfd5fae..22e1bedb02 100644 --- a/beetsplug/mbsync.py +++ b/beetsplug/mbsync.py @@ -20,14 +20,9 @@ from beets import autotag, library, ui, util from beets.autotag import hooks from collections import defaultdict -from beets import config -import musicbrainzngs import re -TRACK_INCLUDES = ['artist-rels'] -SKIPPED_TRACKS = ['[data track]'] - MBID_REGEX = r"(\d|\w){8}-(\d|\w){4}-(\d|\w){4}-(\d|\w){4}-(\d|\w){12}" @@ -62,49 +57,15 @@ def track_performers(info): artists[role] = [artist_relation['artist']['name']] artists[role_sort] = [artist_relation[ 'artist']['sort-name']] + for key in artists: + artists[key] = u', '.join(artists[key]) return artists def album_performers(info): - - track_infos = [] - - ntracks = 0 - for medium in info['medium-list']: - ntracks += len(medium['track-list']) - - for medium in info['medium-list']: - format = medium.get('format') - - if format in config['match']['ignored_media'].as_str_seq(): - continue - - all_tracks = medium['track-list'] - if ('data-track-list' in medium - and not config['match']['ignore_data_tracks']): - all_tracks += medium['data-track-list'] - - if 'pregap' in medium: - all_tracks.insert(0, medium['pregap']) - - for track in all_tracks: - - if ('title' in track['recording'] and - track['recording']['title'] in SKIPPED_TRACKS): - continue - - if ('video' in track['recording'] and - track['recording']['video'] == 'true' and - config['match']['ignore_video_tracks']): - continue - - # Basic information from the recording. - if ntracks < 500: - track_infos.append(track_performers(track['recording'])) - else: - res = musicbrainzngs.get_recording_by_id( - track['recording']['id'], TRACK_INCLUDES)['recording'] - track_infos.append(track_performers(res)) + """ placeholder for more album-related performers + """ + track_infos = {} return track_infos @@ -112,6 +73,7 @@ def album_performers(info): class MBSyncPlugin(BeetsPlugin): def __init__(self): super(MBSyncPlugin, self).__init__() + self.register_listener('extracting_trackdata', track_performers) def commands(self): cmd = ui.Subcommand('mbsync', @@ -166,26 +128,17 @@ def singletons(self, lib, query, move, pretend, write, more_info): continue # Get the MusicBrainz recording info. - if more_info: - performer_info = self.register_listener('trackdata_recieved', track_performers) track_info = hooks.track_for_mbid(item.mb_trackid) if not track_info: self._log.info(u'Recording ID not found: {0} for track {0}', item.mb_trackid, item_formatted) continue - print(track_info['title']) - print(track_info['artist']) - print(performer_info) # Clean up obsolete flexible fields if more_info: for tag in item: if tag[:6] == 'mbsync' and tag not in track_info: del item[tag] - - for key in performer_info: - track_info[key] = u'; '.join(performer_info[key]) - track_info.decode() # Apply. with lib.transaction(): autotag.apply_item_metadata(item, track_info) @@ -213,10 +166,7 @@ def albums(self, lib, query, move, pretend, write, more_info): continue # Get the MusicBrainz album information. - if more_info: - performer_info = self.register_listener('albumdata_recieved', album_performers) - album_info = hooks.album_for_mbid(a.mb_albumid, - more_info=more_info) + album_info = hooks.album_for_mbid(a.mb_albumid) if not album_info: self._log.info(u'Release ID {0} not found for album {1}', a.mb_albumid, @@ -232,18 +182,13 @@ def albums(self, lib, query, move, pretend, write, more_info): track_info = album_info.tracks[i] releasetrack_index[track_info.release_track_id] = track_info track_index[track_info.track_id].append(track_info) - if more_info: - track_performer_info = performer_info[i] - for key in track_performer_info: - track_info[key] = track_performer_info[key] - if more_info: - album_info.decode() # Construct a track mapping according to MBIDs (release track MBIDs # first, if available, and recording MBIDs otherwise). This should # work for albums that have missing or extra tracks. mapping = {} for item in items: + # Clean up obsolete flexible fields if more_info: for tag in item: if tag[:6] == 'mbsync' and tag not in track_info: From 83cfd6b2868e376c429585272bd1e192e631e18a Mon Sep 17 00:00:00 2001 From: soergeld Date: Thu, 24 Dec 2020 11:51:10 +0100 Subject: [PATCH 19/53] minor cleaning --- beets/autotag/mb.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py index 1093a0482c..07ae9c3f35 100644 --- a/beets/autotag/mb.py +++ b/beets/autotag/mb.py @@ -21,7 +21,6 @@ import re import traceback from six.moves.urllib.parse import urljoin -from itertools import chain from beets import logging from beets import plugins @@ -67,6 +66,7 @@ def get_message(self): self._reasonstr(), self.verb, repr(self.query) ) + log = logging.getLogger('beets') RELEASE_INCLUDES = ['artists', 'media', 'recordings', 'release-groups', @@ -425,13 +425,9 @@ def album_info(release): info.media = first_medium.get('format') # supplementary tags provided by plugins - extra_albumdatas = list(chain(*plugins.send('extracting_albumdata', - info=release))) - print(extra_albumdatas) + extra_albumdatas = plugins.send('extracting_albumdata', info=release) for extra_albumdata in extra_albumdatas: - print(extra_albumdatas) for key in extra_albumdata: - print(key) info[key] = extra_albumdata[key] info.decode() return info @@ -529,7 +525,6 @@ def album_for_id(releaseid): try: res = musicbrainzngs.get_release_by_id(albumid, RELEASE_INCLUDES) - beets.plugins.send(u'albumdata_recieved', info=res['release']) except musicbrainzngs.ResponseError: log.debug(u'Album ID match failed.') return None From 51cc2bef26bbd3fbaf646e7b7cd884e111018676 Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 28 Dec 2020 11:02:35 +0100 Subject: [PATCH 20/53] test --- beetsplug/mbsync.py | 1 + 1 file changed, 1 insertion(+) diff --git a/beetsplug/mbsync.py b/beetsplug/mbsync.py index 22e1bedb02..2dfc47eb56 100644 --- a/beetsplug/mbsync.py +++ b/beetsplug/mbsync.py @@ -74,6 +74,7 @@ class MBSyncPlugin(BeetsPlugin): def __init__(self): super(MBSyncPlugin, self).__init__() self.register_listener('extracting_trackdata', track_performers) + self.register_listener('extracting_albumdata', album_performers) def commands(self): cmd = ui.Subcommand('mbsync', From 90305ef87c1087d932537ac3bb687adb0c3308fe Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 28 Dec 2020 11:21:41 +0100 Subject: [PATCH 21/53] add album artists --- beetsplug/mbsync.py | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/beetsplug/mbsync.py b/beetsplug/mbsync.py index 2dfc47eb56..a281c55976 100644 --- a/beetsplug/mbsync.py +++ b/beetsplug/mbsync.py @@ -65,9 +65,41 @@ def track_performers(info): def album_performers(info): """ placeholder for more album-related performers """ - track_infos = {} - return track_infos + artists = {} + for artist_relation in info.get('artist-relation-list', ()): + if 'type' in artist_relation: + role = 'mbsync album ' + role += artist_relation['type'] + if 'balance' in role or 'recording' in role or 'sound' in role: + role += ' engineer' + if 'performing orchestra' in role: + role += ' orchestra' + role_sort = role + ' sort' + if 'attribute-list' in artist_relation: + role += ' - ' + role_sort += ' - ' + role += ', '.join(artist_relation['attribute-list']) + role_sort += ', '.join(artist_relation['attribute-list']) + if 'attributes' in artist_relation: + for attribute in artist_relation['attributes']: + if 'credited-as' in attribute: + role += ' (' + attribute['credited-as'] + ')' + role_sort += ' (' + attribute['credited-as'] + ')' + role = role.replace(" ", "_") + role_sort = role_sort.replace(' ', '_') + if role in artists: + artists[role].append(artist_relation['artist']['name']) + artists[role_sort].append( + artist_relation['artist']['sort-name']) + else: + artists[role] = [artist_relation['artist']['name']] + artists[role_sort] = [artist_relation[ + 'artist']['sort-name']] + for key in artists: + artists[key] = u', '.join(artists[key]) + + return artists class MBSyncPlugin(BeetsPlugin): From 98f1c87cc69544026618ea4edf93302c100a9f33 Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 28 Dec 2020 11:27:32 +0100 Subject: [PATCH 22/53] clean up obsolete albumartists --- beetsplug/mbsync.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/beetsplug/mbsync.py b/beetsplug/mbsync.py index a281c55976..998746ab1d 100644 --- a/beetsplug/mbsync.py +++ b/beetsplug/mbsync.py @@ -224,7 +224,8 @@ def albums(self, lib, query, move, pretend, write, more_info): # Clean up obsolete flexible fields if more_info: for tag in item: - if tag[:6] == 'mbsync' and tag not in track_info: + if tag[:6] == 'mbsync' and (tag not in track_info or + tag not in album_info): del item[tag] if item.mb_releasetrackid and \ item.mb_releasetrackid in releasetrack_index: From 305cddbd4e7cb0fd8dbb38dafae859bc6af9e076 Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 28 Dec 2020 11:43:37 +0100 Subject: [PATCH 23/53] minor cleaning --- beetsplug/mbsync.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/beetsplug/mbsync.py b/beetsplug/mbsync.py index 998746ab1d..19c2f6439d 100644 --- a/beetsplug/mbsync.py +++ b/beetsplug/mbsync.py @@ -58,7 +58,7 @@ def track_performers(info): artists[role_sort] = [artist_relation[ 'artist']['sort-name']] for key in artists: - artists[key] = u', '.join(artists[key]) + artists[key] = u'; '.join(artists[key]) return artists @@ -97,7 +97,7 @@ def album_performers(info): artists[role_sort] = [artist_relation[ 'artist']['sort-name']] for key in artists: - artists[key] = u', '.join(artists[key]) + artists[key] = u'; '.join(artists[key]) return artists From 1876caab14b752053793d701c849ad0017a62182 Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 28 Dec 2020 11:48:57 +0100 Subject: [PATCH 24/53] style --- beetsplug/mbsync.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/beetsplug/mbsync.py b/beetsplug/mbsync.py index 19c2f6439d..b443eefe29 100644 --- a/beetsplug/mbsync.py +++ b/beetsplug/mbsync.py @@ -125,8 +125,8 @@ def commands(self): default=None, dest='write', help=u"don't write updated metadata to files") cmd.parser.add_option( - u'-I', u'--more_info', action='store_true', default=None, - help=u"Fetch more data") + u'-I', u'--performer_info', action='store_true', default=None, + help=u"Fetch performer info") cmd.parser.add_format_option() cmd.func = self.func return [cmd] @@ -137,13 +137,13 @@ def func(self, lib, opts, args): move = ui.should_move(opts.move) pretend = opts.pretend write = ui.should_write(opts.write) - more_info = opts.more_info + performer_info = opts.performer_info query = ui.decargs(args) - self.singletons(lib, query, move, pretend, write, more_info) - self.albums(lib, query, move, pretend, write, more_info) + self.singletons(lib, query, move, pretend, write, performer_info) + self.albums(lib, query, move, pretend, write, performer_info) - def singletons(self, lib, query, move, pretend, write, more_info): + def singletons(self, lib, query, move, pretend, write, performer_info): """Retrieve and apply info from the autotagger for items matched by query. """ @@ -168,7 +168,7 @@ def singletons(self, lib, query, move, pretend, write, more_info): item_formatted) continue # Clean up obsolete flexible fields - if more_info: + if performer_info: for tag in item: if tag[:6] == 'mbsync' and tag not in track_info: del item[tag] @@ -178,7 +178,7 @@ def singletons(self, lib, query, move, pretend, write, more_info): ui.show_model_changes(item) apply_item_changes(lib, item, move, pretend, write) - def albums(self, lib, query, move, pretend, write, more_info): + def albums(self, lib, query, move, pretend, write, performer_info): """Retrieve and apply info from the autotagger for albums matched by query and their items. """ @@ -222,7 +222,7 @@ def albums(self, lib, query, move, pretend, write, more_info): mapping = {} for item in items: # Clean up obsolete flexible fields - if more_info: + if performer_info: for tag in item: if tag[:6] == 'mbsync' and (tag not in track_info or tag not in album_info): From daab62f4adc1cb85a70a48397ccfabe537c88c77 Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 28 Dec 2020 12:01:24 +0100 Subject: [PATCH 25/53] album orchestra issue --- beetsplug/mbsync.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beetsplug/mbsync.py b/beetsplug/mbsync.py index b443eefe29..eee3f3bb23 100644 --- a/beetsplug/mbsync.py +++ b/beetsplug/mbsync.py @@ -74,7 +74,7 @@ def album_performers(info): if 'balance' in role or 'recording' in role or 'sound' in role: role += ' engineer' if 'performing orchestra' in role: - role += ' orchestra' + role = 'mbsync album orchestra' role_sort = role + ' sort' if 'attribute-list' in artist_relation: role += ' - ' From d3a9b0fc277cd5658619666dfd411f42d45f4cc9 Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 28 Dec 2020 12:06:14 +0100 Subject: [PATCH 26/53] style --- beetsplug/mbsync.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/beetsplug/mbsync.py b/beetsplug/mbsync.py index eee3f3bb23..b9bf121021 100644 --- a/beetsplug/mbsync.py +++ b/beetsplug/mbsync.py @@ -211,8 +211,7 @@ def albums(self, lib, query, move, pretend, write, performer_info): # maps to a list of TrackInfo objects. releasetrack_index = dict() track_index = defaultdict(list) - for i in range(len(album_info.tracks)): - track_info = album_info.tracks[i] + for track_info in album_info.tracks: releasetrack_index[track_info.release_track_id] = track_info track_index[track_info.track_id].append(track_info) From 98d18442de57ec0885edfda6945a3af954f0a2cb Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 28 Dec 2020 12:11:03 +0100 Subject: [PATCH 27/53] add performer_info as config option --- beetsplug/mbsync.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/beetsplug/mbsync.py b/beetsplug/mbsync.py index b9bf121021..4764c3a035 100644 --- a/beetsplug/mbsync.py +++ b/beetsplug/mbsync.py @@ -125,7 +125,8 @@ def commands(self): default=None, dest='write', help=u"don't write updated metadata to files") cmd.parser.add_option( - u'-I', u'--performer_info', action='store_true', default=None, + u'-P', u'--performer_info', action='store_true', + dest='performer_info', default=None, help=u"Fetch performer info") cmd.parser.add_format_option() cmd.func = self.func @@ -221,6 +222,7 @@ def albums(self, lib, query, move, pretend, write, performer_info): mapping = {} for item in items: # Clean up obsolete flexible fields + print(performer_info) if performer_info: for tag in item: if tag[:6] == 'mbsync' and (tag not in track_info or From 97005d209d38d90a2117503366f62fa056ae619c Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 28 Dec 2020 12:13:42 +0100 Subject: [PATCH 28/53] obsolete todo --- beetsplug/mbsync.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/beetsplug/mbsync.py b/beetsplug/mbsync.py index 4764c3a035..9ea084ed72 100644 --- a/beetsplug/mbsync.py +++ b/beetsplug/mbsync.py @@ -247,8 +247,6 @@ def albums(self, lib, query, move, pretend, write, performer_info): # Apply. self._log.debug(u'applying changes to {}', album_formatted) with lib.transaction(): - # TODO: For all items, delete all tags of the form 'mb ...' - # that are in the item but not in the corresponding track_info. autotag.apply_metadata(album_info, mapping) changed = False # Find any changed item to apply MusicBrainz changes to album. From 97c648ff273c9f28d2b1c3c1ce9f0e187621198d Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 28 Dec 2020 13:17:44 +0100 Subject: [PATCH 29/53] add permanent option, checking only if true --- beetsplug/mbsync.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/beetsplug/mbsync.py b/beetsplug/mbsync.py index 9ea084ed72..c568970091 100644 --- a/beetsplug/mbsync.py +++ b/beetsplug/mbsync.py @@ -105,8 +105,15 @@ def album_performers(info): class MBSyncPlugin(BeetsPlugin): def __init__(self): super(MBSyncPlugin, self).__init__() - self.register_listener('extracting_trackdata', track_performers) - self.register_listener('extracting_albumdata', album_performers) + + self.config.add({ + u'bin': u'mbsync', + u'performer_info': False, + }) + + if self.config['performer_info'].get(bool): + self.register_listener('extracting_trackdata', track_performers) + self.register_listener('extracting_albumdata', album_performers) def commands(self): cmd = ui.Subcommand('mbsync', @@ -126,7 +133,8 @@ def commands(self): help=u"don't write updated metadata to files") cmd.parser.add_option( u'-P', u'--performer_info', action='store_true', - dest='performer_info', default=None, + dest='performer_info', + default=self.config['performer_info'].get(bool), help=u"Fetch performer info") cmd.parser.add_format_option() cmd.func = self.func @@ -222,7 +230,7 @@ def albums(self, lib, query, move, pretend, write, performer_info): mapping = {} for item in items: # Clean up obsolete flexible fields - print(performer_info) + if performer_info: for tag in item: if tag[:6] == 'mbsync' and (tag not in track_info or From cd6e8a0a9da4ccfaf3ad06cdb666cd87de3cb9b7 Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 28 Dec 2020 13:20:40 +0100 Subject: [PATCH 30/53] style --- beetsplug/mbsync.py | 1 - 1 file changed, 1 deletion(-) diff --git a/beetsplug/mbsync.py b/beetsplug/mbsync.py index c568970091..992b15f710 100644 --- a/beetsplug/mbsync.py +++ b/beetsplug/mbsync.py @@ -230,7 +230,6 @@ def albums(self, lib, query, move, pretend, write, performer_info): mapping = {} for item in items: # Clean up obsolete flexible fields - if performer_info: for tag in item: if tag[:6] == 'mbsync' and (tag not in track_info or From 36a776bd201a0ee33ffd751e45faa9e312e9428f Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 28 Dec 2020 15:27:22 +0100 Subject: [PATCH 31/53] docs and changelog --- docs/changelog.rst | 2 ++ docs/plugins/mbsync.rst | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index b733207564..f58f90337d 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -173,6 +173,8 @@ New features: * :doc:`/plugins/replaygain` now does its analysis in parallel when using the ``command``, ``ffmpeg`` or ``bs1770gain`` backends. :bug:`3478` +* The ``mbsync`` plugin now also fetches performers, if the ``performer_info`` + option is enabled. Fixes :bug`1547`, thanks to :user:`dosoe`. Fixes: diff --git a/docs/plugins/mbsync.rst b/docs/plugins/mbsync.rst index 1c8663dcae..7df46b62f9 100644 --- a/docs/plugins/mbsync.rst +++ b/docs/plugins/mbsync.rst @@ -36,3 +36,8 @@ The command has a few command-line options: * To customize the output of unrecognized items, use the ``-f`` (``--format``) option. The default output is ``format_item`` or ``format_album`` for items and albums, respectively. +* To also get performer data from MusicBrainz, use the ``-P`` + (``--performer_info``) option. This will add all track and album performers + as additional tags (recognisable by the ``mbsync_`` prefix). To autmmatically + fetch performer info, enable the ``performer_info`` option in the + configuration. Default: ``no``. From aac6dc4e5009a4ceeeea28e74da19219017c4b13 Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 28 Dec 2020 15:40:24 +0100 Subject: [PATCH 32/53] docs on new events --- docs/dev/plugins.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/dev/plugins.rst b/docs/dev/plugins.rst index a6aa3d6d77..847ea52073 100644 --- a/docs/dev/plugins.rst +++ b/docs/dev/plugins.rst @@ -239,6 +239,11 @@ The events currently available are: :ref:`appending choices to the prompt ` by returning a list of ``PromptChoices``. Parameters: ``task`` and ``session``. +* `extracting_trackdata` and `extracting_albumdata`: called after the metadata + is obtained from MusicBrainz. The parameter is a ``dict`` containing the data + retrieved from MusicBrainz for a track or an album. + Allows to add additional tags. + The included ``mpdupdate`` plugin provides an example use case for event listeners. Extend the Autotagger From 46a8219570a1574b37b0c93e166c82036c1b27db Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 28 Dec 2020 16:07:17 +0100 Subject: [PATCH 33/53] typo --- docs/changelog.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index f58f90337d..504a02f186 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -174,7 +174,7 @@ New features: the ``command``, ``ffmpeg`` or ``bs1770gain`` backends. :bug:`3478` * The ``mbsync`` plugin now also fetches performers, if the ``performer_info`` - option is enabled. Fixes :bug`1547`, thanks to :user:`dosoe`. + option is enabled. Fixes :bug:`1547`, thanks to :user:`dosoe`. Fixes: From 0605848010d5366e01e4b79abeda7eb2ec6afbb9 Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 28 Dec 2020 19:34:20 +0100 Subject: [PATCH 34/53] changelog improvements --- docs/changelog.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 504a02f186..0534e0a180 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -94,7 +94,7 @@ New features: Windows. Thanks to :user:`MartyLake`. :bug:`3331` :bug:`3334` -* The 'data_source' field is now also applied as an album-level flexible +* The ``data_source`` field is now also applied as an album-level flexible attribute during imports, allowing for more refined album level searches. :bug:`3350` :bug:`1693` * :doc:`/plugins/deezer`: Added Deezer plugin as an import metadata provider: @@ -1924,7 +1924,7 @@ Major new features and bigger changes: search results you wish to see when looking up releases at MusicBrainz during import. :bug:`1245` * The importer now records the data source for a match in a new - flexible attribute `data_source` on items and albums. :bug:`1311` + flexible attribute ``data_source`` on items and albums. :bug:`1311` * The colors used in the terminal interface are now configurable via the new config option ``colors``, nested under the option ``ui``. (Also, the `color` config option has been moved from top-level to under ``ui``. Beets will From 89098ee21dd67ebef3fd956bca3cb8542378b61c Mon Sep 17 00:00:00 2001 From: Dorian Soergel Date: Wed, 12 May 2021 10:46:29 +0200 Subject: [PATCH 35/53] update documentation The events have been added in a separate PR, no need for them here. --- docs/dev/plugins.rst | 5 ----- 1 file changed, 5 deletions(-) diff --git a/docs/dev/plugins.rst b/docs/dev/plugins.rst index cd3b528bc1..b32955b61c 100644 --- a/docs/dev/plugins.rst +++ b/docs/dev/plugins.rst @@ -258,11 +258,6 @@ The events currently available are: tags set at the track level, if they have the same ``field``. Parameter: ``data`` -* `extracting_trackdata` and `extracting_albumdata`: called after the metadata - is obtained from MusicBrainz. The parameter is a ``dict`` containing the data - retrieved from MusicBrainz for a track or an album. - Allows to add additional tags. - The included ``mpdupdate`` plugin provides an example use case for event listeners. Extend the Autotagger From d47f56bfb939fafea25a4b5341cfc662c1ba33ef Mon Sep 17 00:00:00 2001 From: Dorian Soergel Date: Wed, 12 May 2021 10:47:22 +0200 Subject: [PATCH 36/53] Update changelog.rst documentation typo --- docs/changelog.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index d9cc6dad55..f7f161ac1a 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -220,9 +220,9 @@ Other new things: Thanks to :user:`ssssam`. * Added 7z support via the `py7zr`_ library Thanks to :user:`arogl`. :bug:`3906` + .. _py7zr: https://pypi.org/project/py7zr/ * The ``mbsync`` plugin now also fetches performers, if the ``performer_info`` option is enabled. Fixes :bug:`1547`, thanks to :user:`dosoe`. - .. _py7zr: https://pypi.org/project/py7zr/ Fixes: From 5cb1f0de4823a05cddcf31e27adf519a12a2924c Mon Sep 17 00:00:00 2001 From: Dorian Soergel Date: Wed, 12 May 2021 10:51:02 +0200 Subject: [PATCH 37/53] update event names --- beetsplug/mbsync.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/beetsplug/mbsync.py b/beetsplug/mbsync.py index d4f470d246..27f15969a4 100644 --- a/beetsplug/mbsync.py +++ b/beetsplug/mbsync.py @@ -112,8 +112,8 @@ def __init__(self): }) if self.config['performer_info'].get(bool): - self.register_listener('extracting_trackdata', track_performers) - self.register_listener('extracting_albumdata', album_performers) + self.register_listener('mb_track_extract', track_performers) + self.register_listener('mb_album_extract', album_performers) def commands(self): cmd = ui.Subcommand('mbsync', From 59e393e132628a917db670828c83a499b9829b53 Mon Sep 17 00:00:00 2001 From: Dorian Soergel Date: Wed, 12 May 2021 11:03:10 +0200 Subject: [PATCH 38/53] trying to understand how links are embedded --- docs/changelog.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index f7f161ac1a..07ca7d7fed 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -220,9 +220,10 @@ Other new things: Thanks to :user:`ssssam`. * Added 7z support via the `py7zr`_ library Thanks to :user:`arogl`. :bug:`3906` - .. _py7zr: https://pypi.org/project/py7zr/ * The ``mbsync`` plugin now also fetches performers, if the ``performer_info`` option is enabled. Fixes :bug:`1547`, thanks to :user:`dosoe`. + + .. _py7zr: https://pypi.org/project/py7zr/ Fixes: From 6aa7bff7b442434b16b13af82057db73d181e9ce Mon Sep 17 00:00:00 2001 From: Dorian Soergel Date: Tue, 29 Jun 2021 16:53:13 +0200 Subject: [PATCH 39/53] remove whitespaces --- beets/autotag/mb.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py index 05a96ec3ad..b8aa3a2d22 100644 --- a/beets/autotag/mb.py +++ b/beets/autotag/mb.py @@ -66,7 +66,6 @@ def get_message(self): self._reasonstr(), self.verb, repr(self.query) ) - log = logging.getLogger('beets') RELEASE_INCLUDES = ['artists', 'media', 'recordings', 'release-groups', @@ -237,7 +236,6 @@ def track_info(recording, index=None, medium=None, medium_index=None, lyricist = [] composer = [] composer_sort = [] - for work_relation in recording.get('work-relation-list', ()): if work_relation['type'] != 'performance': continue From 6419071f878f390e9e9902cd3504309f7957c282 Mon Sep 17 00:00:00 2001 From: Dorian Soergel Date: Tue, 29 Jun 2021 16:58:43 +0200 Subject: [PATCH 40/53] Docstrings for track_performers and album_performers --- beetsplug/mbsync.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/beetsplug/mbsync.py b/beetsplug/mbsync.py index 27f15969a4..b6dc522dc2 100644 --- a/beetsplug/mbsync.py +++ b/beetsplug/mbsync.py @@ -27,6 +27,14 @@ def track_performers(info): + """ + Gets the info dict (track info) from MusicBrainz and extracts + performer names and roles, puts them in the artists dict. + Fetches both names and sort names, adapts the roles to fit as + single understandable strings, with the mbsync_ prefix. + input: info (dict from MusicBrainz) + output: artists (dict with roles, names and sort names) + """ artists = {} for artist_relation in info.get('artist-relation-list', ()): if 'type' in artist_relation: @@ -63,7 +71,9 @@ def track_performers(info): def album_performers(info): - """ placeholder for more album-related performers + """ + Similar to track_performers but with album performers, + info is the album_info dict from MusicBrainz. """ artists = {} From a24a375169ec5d0a22c02c799b78157244446206 Mon Sep 17 00:00:00 2001 From: Dorian Soergel Date: Tue, 29 Jun 2021 17:03:58 +0200 Subject: [PATCH 41/53] spelling --- docs/plugins/mbsync.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/mbsync.rst b/docs/plugins/mbsync.rst index 7df46b62f9..560f2468a8 100644 --- a/docs/plugins/mbsync.rst +++ b/docs/plugins/mbsync.rst @@ -38,6 +38,6 @@ The command has a few command-line options: ``format_album`` for items and albums, respectively. * To also get performer data from MusicBrainz, use the ``-P`` (``--performer_info``) option. This will add all track and album performers - as additional tags (recognisable by the ``mbsync_`` prefix). To autmmatically + as additional tags (recognisable by the ``mbsync_`` prefix). To autmatically fetch performer info, enable the ``performer_info`` option in the configuration. Default: ``no``. From e2ab7808423b9c3f46460978f7cae8784c4f0c39 Mon Sep 17 00:00:00 2001 From: Dorian Soergel Date: Tue, 29 Jun 2021 17:09:38 +0200 Subject: [PATCH 42/53] fix trailing whitespaces --- beetsplug/mbsync.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/beetsplug/mbsync.py b/beetsplug/mbsync.py index b6dc522dc2..e2df7b9806 100644 --- a/beetsplug/mbsync.py +++ b/beetsplug/mbsync.py @@ -29,8 +29,8 @@ def track_performers(info): """ Gets the info dict (track info) from MusicBrainz and extracts - performer names and roles, puts them in the artists dict. - Fetches both names and sort names, adapts the roles to fit as + performer names and roles, puts them in the artists dict. + Fetches both names and sort names, adapts the roles to fit as single understandable strings, with the mbsync_ prefix. input: info (dict from MusicBrainz) output: artists (dict with roles, names and sort names) @@ -72,8 +72,8 @@ def track_performers(info): def album_performers(info): """ - Similar to track_performers but with album performers, - info is the album_info dict from MusicBrainz. + Similar to track_performers but with album performers, + info is the album_info dict from MusicBrainz. """ artists = {} From 0422306b7999a1b414e6e0024e64a3975849125c Mon Sep 17 00:00:00 2001 From: Dorian Soergel Date: Tue, 29 Jun 2021 17:11:49 +0200 Subject: [PATCH 43/53] accidentally deleted line --- beetsplug/mbsync.py | 1 + 1 file changed, 1 insertion(+) diff --git a/beetsplug/mbsync.py b/beetsplug/mbsync.py index e2df7b9806..83cd058884 100644 --- a/beetsplug/mbsync.py +++ b/beetsplug/mbsync.py @@ -12,6 +12,7 @@ # # 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. """ from __future__ import division, absolute_import, print_function From 429837f9b426f524612cfa9e23740f5bce018d8e Mon Sep 17 00:00:00 2001 From: Dorian Soergel Date: Tue, 29 Jun 2021 17:13:37 +0200 Subject: [PATCH 44/53] correct changelog --- docs/changelog.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index f262aaab08..62d484da13 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -224,8 +224,12 @@ Other new things: Thanks to :user:`arogl`. :bug:`3906` * Get ISRC identifiers from musicbrainz Thanks to :user:`aereaux`. -* :doc:`/plugins/metasync`: The ``metasync`` plugin now also fetches the ``Date Added`` field from iTunes databases and stores it in the``itunes_dateadded`` field.Thanks to :user:`sandersantema`.* The ``mbsync`` plugin now also fetches performers, if the ``performer_info`` - option is enabled. Fixes :bug:`1547`, thanks to :user:`dosoe`. +* :doc:`/plugins/metasync`: The ``metasync`` plugin now also fetches the + ``Date Added`` field from iTunes databases and stores it in the + ``itunes_dateadded`` field.Thanks to :user:`sandersantema`. +* :doc:`/plugins/mbsync`: The ``mbsync`` plugin now also fetches performers, + if the ``performer_info`` option is enabled. + Fixes :bug:`1547`, thanks to :user:`dosoe`. .. _py7zr: https://pypi.org/project/py7zr/ From b6ae8956e5c5b09004804af3d39edd9fbb1d6d04 Mon Sep 17 00:00:00 2001 From: Dorian Soergel Date: Tue, 29 Jun 2021 17:14:17 +0200 Subject: [PATCH 45/53] spelling --- docs/plugins/mbsync.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/mbsync.rst b/docs/plugins/mbsync.rst index 560f2468a8..0c84050242 100644 --- a/docs/plugins/mbsync.rst +++ b/docs/plugins/mbsync.rst @@ -38,6 +38,6 @@ The command has a few command-line options: ``format_album`` for items and albums, respectively. * To also get performer data from MusicBrainz, use the ``-P`` (``--performer_info``) option. This will add all track and album performers - as additional tags (recognisable by the ``mbsync_`` prefix). To autmatically + as additional tags (recognisable by the ``mbsync_`` prefix). To automatically fetch performer info, enable the ``performer_info`` option in the configuration. Default: ``no``. From 7780bcec65c761ec14703cdbf06e3d85c4de96ab Mon Sep 17 00:00:00 2001 From: Dorian Soergel Date: Tue, 29 Jun 2021 17:21:49 +0200 Subject: [PATCH 46/53] testing tags --- beetsplug/mbsync.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/beetsplug/mbsync.py b/beetsplug/mbsync.py index 83cd058884..fbec5f710d 100644 --- a/beetsplug/mbsync.py +++ b/beetsplug/mbsync.py @@ -190,7 +190,7 @@ def singletons(self, lib, query, move, pretend, write, performer_info): # Clean up obsolete flexible fields if performer_info: for tag in item: - if tag[:6] == 'mbsync' and tag not in track_info: + if tag.startswith('mbsync_') and tag not in track_info: del item[tag] # Apply. with lib.transaction(): @@ -243,8 +243,8 @@ def albums(self, lib, query, move, pretend, write, performer_info): # Clean up obsolete flexible fields if performer_info: for tag in item: - if tag[:6] == 'mbsync' and (tag not in track_info or - tag not in album_info): + if tag.startswith('mbsync_') and + (tag not in track_info or tag not in album_info): del item[tag] if item.mb_releasetrackid and \ item.mb_releasetrackid in releasetrack_index: From 50429b11619069de84c480ee0513213d744c4a50 Mon Sep 17 00:00:00 2001 From: Dorian Soergel Date: Tue, 29 Jun 2021 17:24:30 +0200 Subject: [PATCH 47/53] multiline test --- beetsplug/mbsync.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/beetsplug/mbsync.py b/beetsplug/mbsync.py index fbec5f710d..b7460a31f7 100644 --- a/beetsplug/mbsync.py +++ b/beetsplug/mbsync.py @@ -243,8 +243,8 @@ def albums(self, lib, query, move, pretend, write, performer_info): # Clean up obsolete flexible fields if performer_info: for tag in item: - if tag.startswith('mbsync_') and - (tag not in track_info or tag not in album_info): + if (tag.startswith('mbsync_') and + (tag not in track_info or tag not in album_info)): del item[tag] if item.mb_releasetrackid and \ item.mb_releasetrackid in releasetrack_index: From cc11fe82a636a5ed5044d3582f0216cf44314c76 Mon Sep 17 00:00:00 2001 From: Dorian Soergel Date: Tue, 29 Jun 2021 17:27:04 +0200 Subject: [PATCH 48/53] multiline tests --- beetsplug/mbsync.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/beetsplug/mbsync.py b/beetsplug/mbsync.py index b7460a31f7..7f768fb624 100644 --- a/beetsplug/mbsync.py +++ b/beetsplug/mbsync.py @@ -243,8 +243,8 @@ def albums(self, lib, query, move, pretend, write, performer_info): # Clean up obsolete flexible fields if performer_info: for tag in item: - if (tag.startswith('mbsync_') and - (tag not in track_info or tag not in album_info)): + if (tag.startswith('mbsync_') and + (tag not in track_info or tag not in album_info)): del item[tag] if item.mb_releasetrackid and \ item.mb_releasetrackid in releasetrack_index: From a0e186970c7689b1de3e8ee730acddb222e4fefa Mon Sep 17 00:00:00 2001 From: Dorian Soergel Date: Wed, 30 Jun 2021 14:26:42 +0200 Subject: [PATCH 49/53] plugins.send() was using 'data' as parameter, instead of 'info' as said in the docs --- beets/autotag/mb.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py index b8aa3a2d22..3c09645a90 100644 --- a/beets/autotag/mb.py +++ b/beets/autotag/mb.py @@ -270,7 +270,7 @@ def track_info(recording, index=None, medium=None, medium_index=None, info.arranger = u', '.join(arranger) # Supplementary fields provided by plugins - extra_trackdatas = plugins.send('mb_track_extract', data=recording) + extra_trackdatas = plugins.send('mb_track_extract', info=recording) for extra_trackdata in extra_trackdatas: info.update(extra_trackdata) @@ -462,7 +462,7 @@ def album_info(release): if config['musicbrainz']['genres'] and genres: info.genre = ';'.join(g['name'] for g in genres) - extra_albumdatas = plugins.send('mb_album_extract', data=release) + extra_albumdatas = plugins.send('mb_album_extract', info=release) for extra_albumdata in extra_albumdatas: info.update(extra_albumdata) From 6cd4a336f10a23350c47daf9233c622e65d192e8 Mon Sep 17 00:00:00 2001 From: Dorian Soergel Date: Wed, 30 Jun 2021 14:55:06 +0200 Subject: [PATCH 50/53] data is already plural --- beets/autotag/mb.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py index 3c09645a90..d906e0516f 100644 --- a/beets/autotag/mb.py +++ b/beets/autotag/mb.py @@ -270,7 +270,7 @@ def track_info(recording, index=None, medium=None, medium_index=None, info.arranger = u', '.join(arranger) # Supplementary fields provided by plugins - extra_trackdatas = plugins.send('mb_track_extract', info=recording) + extra_trackdatasets = plugins.send('mb_track_extract', info=recording) for extra_trackdata in extra_trackdatas: info.update(extra_trackdata) @@ -462,7 +462,7 @@ def album_info(release): if config['musicbrainz']['genres'] and genres: info.genre = ';'.join(g['name'] for g in genres) - extra_albumdatas = plugins.send('mb_album_extract', info=release) + extra_albumdatasets = plugins.send('mb_album_extract', info=release) for extra_albumdata in extra_albumdatas: info.update(extra_albumdata) From cb28d1969a3506e2fd7a397681d64f128b3ce172 Mon Sep 17 00:00:00 2001 From: Dorian Soergel Date: Wed, 30 Jun 2021 14:57:36 +0200 Subject: [PATCH 51/53] minor nitpick 2 --- beets/autotag/mb.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py index d906e0516f..91c89ca0dc 100644 --- a/beets/autotag/mb.py +++ b/beets/autotag/mb.py @@ -271,7 +271,7 @@ def track_info(recording, index=None, medium=None, medium_index=None, # Supplementary fields provided by plugins extra_trackdatasets = plugins.send('mb_track_extract', info=recording) - for extra_trackdata in extra_trackdatas: + for extra_trackdata in extra_trackdatasets: info.update(extra_trackdata) info.decode() @@ -463,7 +463,7 @@ def album_info(release): info.genre = ';'.join(g['name'] for g in genres) extra_albumdatasets = plugins.send('mb_album_extract', info=release) - for extra_albumdata in extra_albumdatas: + for extra_albumdata in extra_albumdatasets: info.update(extra_albumdata) info.decode() From bcd3ddf28e49eb59a1a779a077976d57371fbc41 Mon Sep 17 00:00:00 2001 From: Dorian Soergel Date: Sun, 4 Jul 2021 11:39:45 +0200 Subject: [PATCH 52/53] shouldn't have changed parameter in event calls --- beets/autotag/mb.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py index 91c89ca0dc..12dd6a0a0d 100644 --- a/beets/autotag/mb.py +++ b/beets/autotag/mb.py @@ -270,7 +270,7 @@ def track_info(recording, index=None, medium=None, medium_index=None, info.arranger = u', '.join(arranger) # Supplementary fields provided by plugins - extra_trackdatasets = plugins.send('mb_track_extract', info=recording) + extra_trackdatasets = plugins.send('mb_track_extract', data=recording) for extra_trackdata in extra_trackdatasets: info.update(extra_trackdata) @@ -462,7 +462,7 @@ def album_info(release): if config['musicbrainz']['genres'] and genres: info.genre = ';'.join(g['name'] for g in genres) - extra_albumdatasets = plugins.send('mb_album_extract', info=release) + extra_albumdatasets = plugins.send('mb_album_extract', data=release) for extra_albumdata in extra_albumdatasets: info.update(extra_albumdata) From e716ab1d04703f164dbbbf77c48fb47e43129e71 Mon Sep 17 00:00:00 2001 From: Dorian Soergel Date: Sun, 4 Jul 2021 11:41:08 +0200 Subject: [PATCH 53/53] update argument from album_performers and track_performers --- beetsplug/mbsync.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/beetsplug/mbsync.py b/beetsplug/mbsync.py index 7f768fb624..aa6d5b19c1 100644 --- a/beetsplug/mbsync.py +++ b/beetsplug/mbsync.py @@ -27,17 +27,17 @@ MBID_REGEX = r"(\d|\w){8}-(\d|\w){4}-(\d|\w){4}-(\d|\w){4}-(\d|\w){12}" -def track_performers(info): +def track_performers(data): """ - Gets the info dict (track info) from MusicBrainz and extracts + Gets the data dict (track info) from MusicBrainz and extracts performer names and roles, puts them in the artists dict. Fetches both names and sort names, adapts the roles to fit as single understandable strings, with the mbsync_ prefix. - input: info (dict from MusicBrainz) + input: data (dict from MusicBrainz) output: artists (dict with roles, names and sort names) """ artists = {} - for artist_relation in info.get('artist-relation-list', ()): + for artist_relation in data.get('artist-relation-list', ()): if 'type' in artist_relation: role = 'mbsync ' role += artist_relation['type'] @@ -71,14 +71,14 @@ def track_performers(info): return artists -def album_performers(info): +def album_performers(data): """ Similar to track_performers but with album performers, - info is the album_info dict from MusicBrainz. + data is the album_info dict from MusicBrainz. """ artists = {} - for artist_relation in info.get('artist-relation-list', ()): + for artist_relation in data.get('artist-relation-list', ()): if 'type' in artist_relation: role = 'mbsync album ' role += artist_relation['type']