-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Fetch artists, performers, engineers etc flexibly #3589
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5fdc10b
149c144
bfd7021
25712df
8cb0cca
ab36272
4178eb8
2d4083b
aa683e6
8d37234
fdc0fc8
19c4f59
259d720
2a2d5d3
e9b9b4b
263a821
5464a75
48f43ed
83cfd6b
aeaca42
51cc2be
90305ef
98f1c87
305cddb
1876caa
daab62f
d3a9b0f
98d1844
97005d2
97c648f
cd6e8a0
36a776b
aac6dc4
46a8219
0605848
63d1ea4
1aeb4ed
89098ee
d47f56b
5cb1f0d
59e393e
28b9b3d
6aa7bff
6419071
a24a375
e2ab780
0422306
429837f
b6ae895
7780bce
50429b1
cc11fe8
a0e1869
6cd4a33
cb28d19
bcd3ddf
e716ab1
e7aa7cf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,10 +27,105 @@ | |
| MBID_REGEX = r"(\d|\w){8}-(\d|\w){4}-(\d|\w){4}-(\d|\w){4}-(\d|\w){12}" | ||
|
|
||
|
|
||
| def track_performers(data): | ||
| """ | ||
| 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: data (dict from MusicBrainz) | ||
| output: artists (dict with roles, names and sort names) | ||
| """ | ||
| artists = {} | ||
| for artist_relation in data.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: | ||
| artists[key] = u'; '.join(artists[key]) | ||
| return artists | ||
|
|
||
|
|
||
| def album_performers(data): | ||
| """ | ||
| Similar to track_performers but with album performers, | ||
| data is the album_info dict from MusicBrainz. | ||
| """ | ||
|
|
||
| artists = {} | ||
| for artist_relation in data.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 = 'mbsync album 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): | ||
| def __init__(self): | ||
| super(MBSyncPlugin, self).__init__() | ||
|
|
||
| self.config.add({ | ||
| u'bin': u'mbsync', | ||
| u'performer_info': False, | ||
| }) | ||
|
|
||
| if self.config['performer_info'].get(bool): | ||
| self.register_listener('mb_track_extract', track_performers) | ||
| self.register_listener('mb_album_extract', album_performers) | ||
|
|
||
| def commands(self): | ||
| cmd = ui.Subcommand('mbsync', | ||
| help=u'update metadata from musicbrainz') | ||
|
|
@@ -47,6 +142,11 @@ 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'-P', u'--performer_info', action='store_true', | ||
| dest='performer_info', | ||
| default=self.config['performer_info'].get(bool), | ||
| help=u"Fetch performer info") | ||
| cmd.parser.add_format_option() | ||
| cmd.func = self.func | ||
| return [cmd] | ||
|
|
@@ -57,12 +157,13 @@ def func(self, lib, opts, args): | |
| move = ui.should_move(opts.move) | ||
| pretend = opts.pretend | ||
| write = ui.should_write(opts.write) | ||
| performer_info = opts.performer_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, performer_info) | ||
| self.albums(lib, query, move, pretend, write, performer_info) | ||
|
|
||
| def singletons(self, lib, query, move, pretend, write): | ||
| def singletons(self, lib, query, move, pretend, write, performer_info): | ||
| """Retrieve and apply info from the autotagger for items matched by | ||
| query. | ||
| """ | ||
|
|
@@ -86,13 +187,18 @@ def singletons(self, lib, query, move, pretend, write): | |
| item.mb_trackid, | ||
| item_formatted) | ||
| continue | ||
|
|
||
| # Clean up obsolete flexible fields | ||
| if performer_info: | ||
| for tag in item: | ||
| if tag.startswith('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) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems unrelated to the logic change at hand—any chance this was left over from debugging?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is not left from debugging, it is just an unrelated issue: I noticed that mbsync shows the changes for albums but not for singletons, this corrects it. |
||
| apply_item_changes(lib, item, move, pretend, write) | ||
|
|
||
| def albums(self, lib, query, move, pretend, write): | ||
| 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. | ||
| """ | ||
|
|
@@ -134,6 +240,12 @@ def albums(self, lib, query, move, pretend, write): | |
| # work for albums that have missing or extra tracks. | ||
| mapping = {} | ||
| for item in items: | ||
| # Clean up obsolete flexible fields | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I admit I don't quite understand why this is necessary… any chance it could have a slightly more detailed explanation? Why do these tags need to be removed?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is because the tags are attributed 'on the fly'. If a performer is tagged as 'Percussions' for example, he will be tagged as 'mbsync_Percussions'. If then someone on MB corrects it to 'Cymbals' mbsync will add a tag 'mbsync_Cymbals' but will not remove the now obsolete 'mbsync_Percussions' tag, unless I remove all tags that start with 'mbsync_' and are not in the new tags. That's why I need this cleanup step. |
||
| if performer_info: | ||
| for tag in item: | ||
| 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: | ||
| mapping[item] = releasetrack_index[item.mb_releasetrackid] | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Same questions as a similar stanza below.)