Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
5fdc10b
Fetching artists flexibly, cleaning up on singletons
dosoe May 14, 2020
149c144
Get artist-rels on singletons
dosoe May 14, 2020
bfd7021
Flake-8
dosoe May 14, 2020
25712df
implement album changes, support for 500+ releases, no whitespaces
dosoe Jun 7, 2020
8cb0cca
debugging
dosoe Jun 7, 2020
ab36272
debugging
dosoe Jun 7, 2020
4178eb8
add "more_info" as an option
dosoe Jun 7, 2020
2d4083b
typo
dosoe Jun 7, 2020
aa683e6
typo
dosoe Jun 7, 2020
8d37234
debugging, mbsync shows changes also for singletons
dosoe Jun 7, 2020
fdc0fc8
flake8
dosoe Jun 7, 2020
19c4f59
forgot some more_info
dosoe Jun 7, 2020
259d720
convert flexible fields to unicode
dosoe Jun 7, 2020
2a2d5d3
adapt roles
dosoe Jun 7, 2020
e9b9b4b
decoding roles doesn't work
dosoe Jun 7, 2020
263a821
arranging orchestra role
dosoe Jun 7, 2020
5464a75
moving fetching of performers to plugin
dosoe Aug 10, 2020
48f43ed
getting the events and return values correctly
dosoe Dec 24, 2020
83cfd6b
minor cleaning
dosoe Dec 24, 2020
aeaca42
Merge branch 'master' into beet_fetch_more_data
dosoe Dec 28, 2020
51cc2be
test
dosoe Dec 28, 2020
90305ef
add album artists
dosoe Dec 28, 2020
98f1c87
clean up obsolete albumartists
dosoe Dec 28, 2020
305cddb
minor cleaning
dosoe Dec 28, 2020
1876caa
style
dosoe Dec 28, 2020
daab62f
album orchestra issue
dosoe Dec 28, 2020
d3a9b0f
style
dosoe Dec 28, 2020
98d1844
add performer_info as config option
dosoe Dec 28, 2020
97005d2
obsolete todo
dosoe Dec 28, 2020
97c648f
add permanent option, checking only if true
dosoe Dec 28, 2020
cd6e8a0
style
dosoe Dec 28, 2020
36a776b
docs and changelog
dosoe Dec 28, 2020
aac6dc4
docs on new events
dosoe Dec 28, 2020
46a8219
typo
dosoe Dec 28, 2020
0605848
changelog improvements
dosoe Dec 28, 2020
63d1ea4
Merge branch 'master' into beet_fetch_more_data
dosoe Jan 8, 2021
1aeb4ed
Merge branch 'master' into beet_fetch_more_data
dosoe May 12, 2021
89098ee
update documentation
dosoe May 12, 2021
d47f56b
Update changelog.rst
dosoe May 12, 2021
5cb1f0d
update event names
dosoe May 12, 2021
59e393e
trying to understand how links are embedded
dosoe May 12, 2021
28b9b3d
Merge branch 'master' into beet_fetch_more_data
dosoe May 20, 2021
6aa7bff
remove whitespaces
dosoe Jun 29, 2021
6419071
Docstrings for track_performers and album_performers
dosoe Jun 29, 2021
a24a375
spelling
dosoe Jun 29, 2021
e2ab780
fix trailing whitespaces
dosoe Jun 29, 2021
0422306
accidentally deleted line
dosoe Jun 29, 2021
429837f
correct changelog
dosoe Jun 29, 2021
b6ae895
spelling
dosoe Jun 29, 2021
7780bce
testing tags
dosoe Jun 29, 2021
50429b1
multiline test
dosoe Jun 29, 2021
cc11fe8
multiline tests
dosoe Jun 29, 2021
a0e1869
plugins.send() was using 'data' as parameter, instead of 'info' as sa…
dosoe Jun 30, 2021
6cd4a33
data is already plural
dosoe Jun 30, 2021
cb28d19
minor nitpick 2
dosoe Jun 30, 2021
bcd3ddf
shouldn't have changed parameter in event calls
dosoe Jul 4, 2021
e716ab1
update argument from album_performers and track_performers
dosoe Jul 4, 2021
e7aa7cf
Merge branch 'master' into beet_fetch_more_data
dosoe Jul 5, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions beets/autotag/mb.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def get_message(self):
BROWSE_INCLUDES.append("work-level-rels")
BROWSE_CHUNKSIZE = 100
BROWSE_MAXTRACKS = 500
TRACK_INCLUDES = ['artists', 'aliases', 'isrcs']
TRACK_INCLUDES = ['artists', 'aliases', 'isrcs', 'artist-rels']
if 'work-level-rels' in musicbrainzngs.VALID_INCLUDES['recording']:
TRACK_INCLUDES += ['work-level-rels', 'artist-rels']
if 'genres' in musicbrainzngs.VALID_INCLUDES['recording']:
Expand Down Expand Up @@ -270,8 +270,8 @@ 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)
for extra_trackdata in extra_trackdatas:
extra_trackdatasets = plugins.send('mb_track_extract', data=recording)
for extra_trackdata in extra_trackdatasets:
info.update(extra_trackdata)

info.decode()
Expand Down Expand Up @@ -462,8 +462,8 @@ 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)
for extra_albumdata in extra_albumdatas:
extra_albumdatasets = plugins.send('mb_album_extract', data=release)
for extra_albumdata in extra_albumdatasets:
info.update(extra_albumdata)

info.decode()
Expand Down
122 changes: 117 additions & 5 deletions beetsplug/mbsync.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand All @@ -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]
Expand All @@ -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.
"""
Expand All @@ -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]

Copy link
Copy Markdown
Member

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.)

# Apply.
with lib.transaction():
autotag.apply_item_metadata(item, track_info)
ui.show_model_changes(item)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The 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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The 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.
"""
Expand Down Expand Up @@ -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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The 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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The 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]
Expand Down
7 changes: 6 additions & 1 deletion docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -211,10 +211,15 @@ 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`.
* :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/lyrics`: Added Tekstowo.pl lyrics provider. Thanks to various
people for the implementation and for reporting issues with the initial version.
:bug:`3344` :bug:`3904` :bug:`3905` :bug:`3994`
* :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/

Expand Down
5 changes: 5 additions & 0 deletions docs/plugins/mbsync.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 automatically
fetch performer info, enable the ``performer_info`` option in the
configuration. Default: ``no``.