From edd6e7f7894440a610c573ff9b93b2c29cf2b9ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Detrey?= Date: Wed, 7 Oct 2015 18:17:53 +0200 Subject: [PATCH 01/15] Take the audio tracks in a data track into account. Depends on commit 6ec04e6 of `python-musicbrainzngs'. --- beets/autotag/mb.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py index 8589a62aa..ff267634f 100644 --- a/beets/autotag/mb.py +++ b/beets/autotag/mb.py @@ -217,6 +217,10 @@ def album_info(release): format = medium.get('format') all_tracks = medium['track-list'] + if 'data-track-list' in medium: + all_tracks += medium['data-track-list'] + total = len(all_tracks) + if 'pregap' in medium: all_tracks.insert(0, medium['pregap']) @@ -228,7 +232,7 @@ def album_info(release): index, int(medium['position']), int(track['position']), - len(medium['track-list']), + total, ) ti.disctitle = disctitle ti.media = format From 6ea1be731475ca5c84f498320d09c3773ba8f2bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Detrey?= Date: Wed, 7 Oct 2015 23:30:52 +0200 Subject: [PATCH 02/15] Add the `data-track-list' feature to changelog. --- docs/changelog.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 1aa89ef1e..f41243e65 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -23,6 +23,11 @@ The new features: * :doc:`/plugins/plexupdate`: A new ``library_name`` option allows you to select which Plex library to update. :bug:`1572` :bug:`1595` * Add new `include` config option to allow including external config files. +* The importer now supports audio files contained in data tracks when they are + listed in MusicBrainz: the corresponding audio tracks are now merged into the + main track list. (This feature depends on a new version of the + ``musicbrainzngs`` library which is not yet released, but will start working + when it is available.) Thanks to :user:`jdetrey`. :bug:`1638` Fixes: From 0f6f0391fd4ae82fe0ff52820cb3fb96023ab2cb Mon Sep 17 00:00:00 2001 From: Rawrmonkeys Date: Sun, 25 Mar 2018 18:58:04 -0700 Subject: [PATCH 03/15] add tag, database entry, data population for musicbrainz release track id --- beets/autotag/__init__.py | 2 ++ beets/autotag/hooks.py | 5 ++++- beets/autotag/mb.py | 1 + beets/library.py | 1 + beets/mediafile.py | 6 ++++++ docs/reference/pathformat.rst | 1 + test/_common.py | 1 + 7 files changed, 16 insertions(+), 1 deletion(-) diff --git a/beets/autotag/__init__.py b/beets/autotag/__init__.py index 4c5f09eb4..24f2ebfc0 100644 --- a/beets/autotag/__init__.py +++ b/beets/autotag/__init__.py @@ -40,6 +40,7 @@ def apply_item_metadata(item, track_info): item.artist_credit = track_info.artist_credit item.title = track_info.title item.mb_trackid = track_info.track_id + item.mb_releasetrackid = track_info.release_track_id if track_info.artist_id: item.mb_artistid = track_info.artist_id if track_info.data_source: @@ -122,6 +123,7 @@ def apply_metadata(album_info, mapping): # MusicBrainz IDs. item.mb_trackid = track_info.track_id + item.mb_releasetrackid = track_info.release_track_id item.mb_albumid = album_info.album_id if track_info.artist_id: item.mb_artistid = track_info.artist_id diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py index 053d050c6..cc5f37003 100644 --- a/beets/autotag/hooks.py +++ b/beets/autotag/hooks.py @@ -129,6 +129,8 @@ class TrackInfo(object): - ``title``: name of the track - ``track_id``: MusicBrainz ID; UUID fragment only + - ``release_track_id``: MusicBrainz ID respective to a track on a + particular release; UUID fragment only - ``artist``: individual track artist name - ``artist_id`` - ``length``: float: duration of the track in seconds @@ -152,7 +154,7 @@ class TrackInfo(object): may be None. The indices ``index``, ``medium``, and ``medium_index`` are all 1-based. """ - def __init__(self, title, track_id, artist=None, artist_id=None, + def __init__(self, title, track_id, release_track_id=None, artist=None, artist_id=None, length=None, index=None, medium=None, medium_index=None, medium_total=None, artist_sort=None, disctitle=None, artist_credit=None, data_source=None, data_url=None, @@ -160,6 +162,7 @@ class TrackInfo(object): arranger=None, track_alt=None): self.title = title self.track_id = track_id + self.release_track_id = release_track_id self.artist = artist self.artist_id = artist_id self.length = length diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py index 9ce449a8b..049b70909 100644 --- a/beets/autotag/mb.py +++ b/beets/autotag/mb.py @@ -304,6 +304,7 @@ def album_info(release): int(track['position']), len(medium['track-list']), ) + ti.release_track_id = track['id'] ti.disctitle = disctitle ti.media = format ti.track_alt = track['number'] diff --git a/beets/library.py b/beets/library.py index 64035e642..ba57407d0 100644 --- a/beets/library.py +++ b/beets/library.py @@ -455,6 +455,7 @@ class Item(LibModel): 'mb_albumid': types.STRING, 'mb_artistid': types.STRING, 'mb_albumartistid': types.STRING, + 'mb_releasetrackid': types.STRING, 'albumtype': types.STRING, 'label': types.STRING, 'acoustid_fingerprint': types.STRING, diff --git a/beets/mediafile.py b/beets/mediafile.py index 34ad49af7..32a32fe1d 100644 --- a/beets/mediafile.py +++ b/beets/mediafile.py @@ -1865,6 +1865,12 @@ class MediaFile(object): StorageStyle('MUSICBRAINZ_TRACKID'), ASFStorageStyle('MusicBrainz/Track Id'), ) + mb_releasetrackid = MediaField( + MP3DescStorageStyle(u'MusicBrainz Release Track Id'), + MP4StorageStyle('----:com.apple.iTunes:MusicBrainz Release Track Id'), + StorageStyle('MUSICBRAINZ_RELEASETRACKID'), + ASFStorageStyle('MusicBrainz/Release Track Id'), + ) mb_albumid = MediaField( MP3DescStorageStyle(u'MusicBrainz Album Id'), MP4StorageStyle('----:com.apple.iTunes:MusicBrainz Album Id'), diff --git a/docs/reference/pathformat.rst b/docs/reference/pathformat.rst index 667be3150..72907b6df 100644 --- a/docs/reference/pathformat.rst +++ b/docs/reference/pathformat.rst @@ -239,6 +239,7 @@ Audio information: MusicBrainz and fingerprint information: * mb_trackid +* mb_releasetrackid * mb_albumid * mb_artistid * mb_albumartistid diff --git a/test/_common.py b/test/_common.py index fc7b650b3..f5e65ca76 100644 --- a/test/_common.py +++ b/test/_common.py @@ -89,6 +89,7 @@ def item(lib=None): mb_albumid='someID-2', mb_artistid='someID-3', mb_albumartistid='someID-4', + mb_releasetrackid='someID-5', album_id=None, mtime=12345, ) From 1a37db732ec0eb7becd0a52d9f1af3bdb588ffae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Detrey?= Date: Sun, 29 Apr 2018 18:00:36 +0200 Subject: [PATCH 04/15] More fitting name for total track count. --- 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 0539c909e..268baf766 100644 --- a/beets/autotag/mb.py +++ b/beets/autotag/mb.py @@ -283,7 +283,7 @@ def album_info(release): all_tracks = medium['track-list'] if 'data-track-list' in medium: all_tracks += medium['data-track-list'] - total = len(all_tracks) + track_count = len(all_tracks) if 'pregap' in medium: all_tracks.insert(0, medium['pregap']) @@ -306,7 +306,7 @@ def album_info(release): index, int(medium['position']), int(track['position']), - total, + track_count, ) ti.disctitle = disctitle ti.media = format From c15f1ba8773dffea94e7a17238e559aa7e015200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Detrey?= Date: Sun, 29 Apr 2018 18:21:40 +0200 Subject: [PATCH 05/15] Add the `data-track-list' feature to changelog. --- docs/changelog.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 8f45aa69c..ec6c1b3a7 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -103,6 +103,9 @@ Fixes: to which a track belongs, not the total number of different mediums present on the release. :bug:`2887` Thanks to :user:`dbogdanov`. +* The importer now supports audio files contained in data tracks when they are + listed in MusicBrainz: the corresponding audio tracks are now merged into the + main track list. Thanks to :user:`jdetrey`. :bug:`1638` For developers: From e8190b5c1adab6b9193fecc3a4cbe8fdb9495555 Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Sun, 29 Apr 2018 13:37:59 -0400 Subject: [PATCH 06/15] Avoid a crash when mediums is none --- beets/ui/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beets/ui/commands.py b/beets/ui/commands.py index 610ae551e..46ae1d936 100644 --- a/beets/ui/commands.py +++ b/beets/ui/commands.py @@ -237,7 +237,7 @@ def show_change(cur_artist, cur_album, match): medium = track_info.disc mediums = track_info.disctotal if config['per_disc_numbering']: - if mediums > 1: + if mediums and mediums > 1: return u'{0}-{1}'.format(medium, medium_index) else: return six.text_type(medium_index or index) From f1d5c2f398c3ef57dcd13579dffabc3730b00a76 Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Sun, 29 Apr 2018 13:45:39 -0400 Subject: [PATCH 07/15] Fix #2537: keyfinder uses imported_items() --- beetsplug/keyfinder.py | 2 +- docs/changelog.rst | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/beetsplug/keyfinder.py b/beetsplug/keyfinder.py index 34a4abca4..a3fbc8211 100644 --- a/beetsplug/keyfinder.py +++ b/beetsplug/keyfinder.py @@ -48,7 +48,7 @@ class KeyFinderPlugin(BeetsPlugin): self.find_key(lib.items(ui.decargs(args)), write=ui.should_write()) def imported(self, session, task): - self.find_key(task.items) + self.find_key(task.imported_items()) def find_key(self, items, write=False): overwrite = self.config['overwrite'].get(bool) diff --git a/docs/changelog.rst b/docs/changelog.rst index 8f45aa69c..12fee62c8 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -103,6 +103,8 @@ Fixes: to which a track belongs, not the total number of different mediums present on the release. :bug:`2887` Thanks to :user:`dbogdanov`. +* :doc:`/plugins/keyfinder`: Avoid a crash when trying to process unmatched + tracks. :bug:`2537` For developers: From e88a7cea90a194bfedac208f423ec4e85c5169b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Detrey?= Date: Sun, 29 Apr 2018 20:05:57 +0200 Subject: [PATCH 08/15] Unit test for import with audio data tracks. --- test/test_mb.py | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/test/test_mb.py b/test/test_mb.py index 644e7f5de..585cc4f1e 100644 --- a/test/test_mb.py +++ b/test/test_mb.py @@ -27,7 +27,8 @@ import mock class MBAlbumInfoTest(_common.TestCase): def _make_release(self, date_str='2009', tracks=None, track_length=None, - track_artist=False, medium_format='FORMAT'): + track_artist=False, data_tracks=None, + medium_format='FORMAT'): release = { 'title': 'ALBUM TITLE', 'id': 'ALBUM ID', @@ -62,8 +63,8 @@ class MBAlbumInfoTest(_common.TestCase): 'country': 'COUNTRY', 'status': 'STATUS', } + track_list = [] if tracks: - track_list = [] for i, recording in enumerate(tracks): track = { 'recording': recording, @@ -87,12 +88,22 @@ class MBAlbumInfoTest(_common.TestCase): } ] track_list.append(track) - release['medium-list'].append({ - 'position': '1', - 'track-list': track_list, - 'format': medium_format, - 'title': 'MEDIUM TITLE', - }) + data_track_list = [] + if data_tracks: + for i, recording in enumerate(data_tracks): + data_track = { + 'recording': recording, + 'position': len(track_list) + i + 1, + 'number': 'A1', + } + data_track_list.append(data_track) + release['medium-list'].append({ + 'position': '1', + 'track-list': track_list, + 'data-track-list': data_track_list, + 'format': medium_format, + 'title': 'MEDIUM TITLE', + }) return release def _make_track(self, title, tr_id, duration, artist=False, video=False): @@ -354,6 +365,18 @@ class MBAlbumInfoTest(_common.TestCase): self.assertEqual(d.tracks[0].title, 'TITLE ONE') self.assertEqual(d.tracks[1].title, 'TITLE TWO') + def test_no_skip_audio_data_tracks(self): + tracks = [self._make_track('TITLE ONE', 'ID ONE', 100.0 * 1000.0), + self._make_track('TITLE TWO', 'ID TWO', 200.0 * 1000.0)] + data_tracks = [self._make_track('TITLE AUDIO DATA', 'ID DATA TRACK', + 100.0 * 1000.0)] + release = self._make_release(tracks=tracks, data_tracks=data_tracks) + d = mb.album_info(release) + self.assertEqual(len(d.tracks), 3) + self.assertEqual(d.tracks[0].title, 'TITLE ONE') + self.assertEqual(d.tracks[1].title, 'TITLE TWO') + self.assertEqual(d.tracks[2].title, 'TITLE AUDIO DATA') + def test_skip_video_tracks_by_default(self): tracks = [self._make_track('TITLE ONE', 'ID ONE', 100.0 * 1000.0), self._make_track('TITLE VIDEO', 'ID VIDEO', 100.0 * 1000.0, From c1d1388729415d212051b7ef4249c26284851fc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Detrey?= Date: Sun, 29 Apr 2018 20:19:11 +0200 Subject: [PATCH 09/15] Unit test for import with video data tracks. --- test/test_mb.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/test_mb.py b/test/test_mb.py index 585cc4f1e..55df22944 100644 --- a/test/test_mb.py +++ b/test/test_mb.py @@ -388,6 +388,17 @@ class MBAlbumInfoTest(_common.TestCase): self.assertEqual(d.tracks[0].title, 'TITLE ONE') self.assertEqual(d.tracks[1].title, 'TITLE TWO') + def test_skip_video_data_tracks_by_default(self): + tracks = [self._make_track('TITLE ONE', 'ID ONE', 100.0 * 1000.0), + self._make_track('TITLE TWO', 'ID TWO', 200.0 * 1000.0)] + data_tracks = [self._make_track('TITLE VIDEO', 'ID VIDEO', + 100.0 * 1000.0, False, True)] + release = self._make_release(tracks=tracks, data_tracks=data_tracks) + d = mb.album_info(release) + self.assertEqual(len(d.tracks), 2) + self.assertEqual(d.tracks[0].title, 'TITLE ONE') + self.assertEqual(d.tracks[1].title, 'TITLE TWO') + def test_no_skip_video_tracks_if_configured(self): config['match']['ignore_video_tracks'] = False tracks = [self._make_track('TITLE ONE', 'ID ONE', 100.0 * 1000.0), @@ -401,6 +412,19 @@ class MBAlbumInfoTest(_common.TestCase): self.assertEqual(d.tracks[1].title, 'TITLE VIDEO') self.assertEqual(d.tracks[2].title, 'TITLE TWO') + def test_no_skip_video_data_tracks_if_configured(self): + config['match']['ignore_video_tracks'] = False + tracks = [self._make_track('TITLE ONE', 'ID ONE', 100.0 * 1000.0), + self._make_track('TITLE TWO', 'ID TWO', 200.0 * 1000.0)] + data_tracks = [self._make_track('TITLE VIDEO', 'ID VIDEO', + 100.0 * 1000.0, False, True)] + release = self._make_release(tracks=tracks, data_tracks=data_tracks) + d = mb.album_info(release) + self.assertEqual(len(d.tracks), 3) + self.assertEqual(d.tracks[0].title, 'TITLE ONE') + self.assertEqual(d.tracks[1].title, 'TITLE TWO') + self.assertEqual(d.tracks[2].title, 'TITLE VIDEO') + class ParseIDTest(_common.TestCase): def test_parse_id_correct(self): From f74899ab9af4427fc87e6535fffeb0c85329ad65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Detrey?= Date: Mon, 30 Apr 2018 18:26:06 +0200 Subject: [PATCH 10/15] Fix errors due to missing track['id'] in unit tests. --- test/test_importer.py | 1 + test/test_mb.py | 15 +++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/test/test_importer.py b/test/test_importer.py index e30f5609c..6721f0dc2 100644 --- a/test/test_importer.py +++ b/test/test_importer.py @@ -1819,6 +1819,7 @@ def mocked_get_release_by_id(id_, includes=[], release_status=[], 'id': id_, 'medium-list': [{ 'track-list': [{ + 'id': 'baz', 'recording': { 'title': 'foo', 'id': 'bar', diff --git a/test/test_mb.py b/test/test_mb.py index 55df22944..b61ebe59a 100644 --- a/test/test_mb.py +++ b/test/test_mb.py @@ -63,12 +63,15 @@ class MBAlbumInfoTest(_common.TestCase): 'country': 'COUNTRY', 'status': 'STATUS', } + i = 0 track_list = [] if tracks: - for i, recording in enumerate(tracks): + for recording in tracks: + i += 1 track = { + 'id': 'RELEASE TRACK ID %d' % i, 'recording': recording, - 'position': i + 1, + 'position': i, 'number': 'A1', } if track_length: @@ -90,10 +93,12 @@ class MBAlbumInfoTest(_common.TestCase): track_list.append(track) data_track_list = [] if data_tracks: - for i, recording in enumerate(data_tracks): + for recording in data_tracks: + i += 1 data_track = { + 'id': 'RELEASE TRACK ID %d' % i, 'recording': recording, - 'position': len(track_list) + i + 1, + 'position': i, 'number': 'A1', } data_track_list.append(data_track) @@ -194,6 +199,7 @@ class MBAlbumInfoTest(_common.TestCase): self._make_track('TITLE TWO', 'ID TWO', 200.0 * 1000.0)] release = self._make_release(tracks=[tracks[0]]) second_track_list = [{ + 'id': 'RELEASE TRACK ID 2', 'recording': tracks[1], 'position': '1', 'number': 'A1', @@ -551,6 +557,7 @@ class MBLibraryTest(unittest.TestCase): 'id': mbid, 'medium-list': [{ 'track-list': [{ + 'id': 'baz', 'recording': { 'title': 'foo', 'id': 'bar', From 9f4c5c8096832fdf1f289e0843250e3758ce05fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Detrey?= Date: Mon, 30 Apr 2018 18:26:46 +0200 Subject: [PATCH 11/15] Do not rely on positional arguments for TrackInfo. --- beetsplug/discogs.py | 7 ++++--- test/test_autotag.py | 14 +++++++------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/beetsplug/discogs.py b/beetsplug/discogs.py index 82c79a023..0e9d7c229 100644 --- a/beetsplug/discogs.py +++ b/beetsplug/discogs.py @@ -474,9 +474,10 @@ class DiscogsPlugin(BeetsPlugin): medium, medium_index, _ = self.get_track_index(track['position']) artist, artist_id = self.get_artist(track.get('artists', [])) length = self.get_track_length(track['duration']) - return TrackInfo(title, track_id, artist, artist_id, length, index, - medium, medium_index, artist_sort=None, - disctitle=None, artist_credit=None) + return TrackInfo(title, track_id, artist=artist, artist_id=artist_id, + length=length, index=index, + medium=medium, medium_index=medium_index, + artist_sort=None, disctitle=None, artist_credit=None) def get_track_index(self, position): """Returns the medium, medium index and subtrack index for a discogs diff --git a/test/test_autotag.py b/test/test_autotag.py index 932616be1..244e48ecc 100644 --- a/test/test_autotag.py +++ b/test/test_autotag.py @@ -103,9 +103,9 @@ def _make_item(title, track, artist=u'some artist'): def _make_trackinfo(): return [ - TrackInfo(u'one', None, u'some artist', length=1, index=1), - TrackInfo(u'two', None, u'some artist', length=1, index=2), - TrackInfo(u'three', None, u'some artist', length=1, index=3), + TrackInfo(u'one', None, artist=u'some artist', length=1, index=1), + TrackInfo(u'two', None, artist=u'some artist', length=1, index=2), + TrackInfo(u'three', None, artist=u'some artist', length=1, index=3), ] @@ -827,15 +827,15 @@ class ApplyCompilationTest(_common.TestCase, ApplyTestUtil): trackinfo.append(TrackInfo( u'oneNew', u'dfa939ec-118c-4d0f-84a0-60f3d1e6522c', - u'artistOneNew', - u'a05686fc-9db2-4c23-b99e-77f5db3e5282', + artist=u'artistOneNew', + artist_id=u'a05686fc-9db2-4c23-b99e-77f5db3e5282', index=1, )) trackinfo.append(TrackInfo( u'twoNew', u'40130ed1-a27c-42fd-a328-1ebefb6caef4', - u'artistTwoNew', - u'80b3cf5e-18fe-4c59-98c7-e5bb87210710', + artist=u'artistTwoNew', + artist_id=u'80b3cf5e-18fe-4c59-98c7-e5bb87210710', index=2, )) self.info = AlbumInfo( From f01799207a11fc71ca1287b20be3a2718c98bcee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Detrey?= Date: Tue, 1 May 2018 08:37:34 +0200 Subject: [PATCH 12/15] Add `mb_releasetrackid` tag to full.* media files. --- test/rsrc/full.aiff | Bin 89296 -> 89296 bytes test/rsrc/full.alac.m4a | Bin 6884 -> 6884 bytes test/rsrc/full.ape | Bin 14005 -> 14076 bytes test/rsrc/full.dsf | Bin 113475 -> 114582 bytes test/rsrc/full.flac | Bin 21890 -> 21890 bytes test/rsrc/full.m4a | Bin 5862 -> 5862 bytes test/rsrc/full.mp3 | Bin 12820 -> 12820 bytes test/rsrc/full.mpc | Bin 2949 -> 3020 bytes test/rsrc/full.ogg | Bin 9073 -> 10176 bytes test/rsrc/full.opus | Bin 7248 -> 8349 bytes test/rsrc/full.wma | Bin 25540 -> 26883 bytes test/rsrc/full.wv | Bin 13593 -> 13664 bytes test/test_mediafile.py | 56 +++++++++++++++++++++------------------- 13 files changed, 29 insertions(+), 27 deletions(-) diff --git a/test/rsrc/full.aiff b/test/rsrc/full.aiff index 5d791b0b7dc4cf1805d83f34929f7c1a05a2f275..e9606f9e74ee1a50d1cca3a4ca4ce7ab51575a58 100644 GIT binary patch delta 245 zcmcbxll8(*)`l&Nev#AfL^4V{2ZTB?Ffed4Fff;7q$=bjCZ*;uggE>A0tNY?g30;0 z1^LCPMGPUHA%;le=|%aa1(|v2(|My9r6faxeL{ee0zgebiUDj|Wl?5w@pRuvM%n51 zQ9!~!no)GRU^JuD^w4NVi|JO;jMCHVq8XK^7eq0-PG1-eB(_E|`noy;4e>24&P;YH zO3ciwQV2@TNlh$HRR}3cOwLyDOkqeivP?5hG&R>vGBLK)H8D#|(M>c;HPW>-Ha9m* VGBh?fOf{IE8Oc~YohgQ~831D3L=FG| delta 161 zcmcbxll8(*)`l&Nev#83L^8@uza7n3G~F+fQF?k^B%{RibwENq#52T@fq{XKfq}Us zBUK^2D8IBIGcSGm??^^D;eb#lpd2?;E+;W5HD~(XC`PyG9?^`x(>7f%n2VXU8?5W^S*0O-9h A1^@s6 diff --git a/test/rsrc/full.alac.m4a b/test/rsrc/full.alac.m4a index 8ec7d377c37176e1b7e98f469eb2801f5e375323..6bfcf317e395c5fe92231ef4decba8088f8f9dd7 100644 GIT binary patch delta 155 zcmaE2`owg@ZYIXroA)r4F;2E+Tfo>b*^gB@LL)CRHLu* z6N^(7LW&ZTvlTp3fYK%@i6x0Znh~Tn*~l`@IMLKxH_61U(%9VG zEXmN=+%R?X1(saK$#>b(CmXRWn(V@^G`Wx6eR3+h1{=%fw4&71%@5hvvTWwyy~7It Dl;$s3 delta 70 zcmV-M0J;Ct<8 diff --git a/test/rsrc/full.ape b/test/rsrc/full.ape index 5bcea98e86f05f90310f46b45d5e2c6931912a8b..e1667505c4665289c78e88f20b84729524dcbd45 100644 GIT binary patch delta 252 zcmdm*`zLpUg$e5hCI$wv$+jlOqArdht_(&728JvQ5D@9=7&N)n#E8emGuYWL)Yr*1 zh{0s?dJ~z+_f2%{-Cg~HTp3C-QWeru^NLbAp_+0MlTvfQ!eAB`RM;^n#4|VqES^|Y zl383b+0aySa{CMOjoX69AJ7p3N;CKjib6eT8S zXQnVD8(F3qCz_h;CYcyp>YA9PrRXLar5fp48k?J&B^er<8>SjK2DpYey1PbPU}sah=?1V#E^?C&{T4AvT5sN4Kv5dFHP+yXPD_KI0m?eIJ&z=TwrHl06Pq#y8&tl0|3f4ER+BM diff --git a/test/rsrc/full.dsf b/test/rsrc/full.dsf index a90e6946fc44cd97a56c9846cbea855201a22808..f13ea694b71e010c1099b0a5afa110aa851c63fa 100644 GIT binary patch delta 412 zcmYk1&rZTX5XNVN_98aMiyl1m1seJX+BDIF6c0oy4J@YDZCS9P6tZm(dLh09eGNUB zC>}icPF{5yAz`zZ-#6cUGxPl;t$!uCGwdiA@c-6d&+?m0?mt#_jdrPlssvD*6iEWq zztNHF$oT_Tn8@QS2Rd+XQ9VS}XqZ5+du_y{ohb>6Id=AkTTipm0@@k4h*hwmX*y81 zf4A2&<*DE+pl#nnwT9n9i|20v{r+fwM#4gl+qwP9E^h}%i}`YqaRGQmwe?P)ibb)E z(`{vemF@I%?CJj8$fELv>8swnE*s Kb*{ZKa_|QOB4BF( delta 330 zcmbRCpY8BBHa?eN7X=vx2your$hVb`F>Gf&b8Lubh!Iefm4Sgdtu!YmB*4`W$mIrd zOEOXw5{pVQi%UWreVl-TTrk0$q|)4wAZKr&7zJ`$9@=FUc^U_0{{XuG=;tI+6xdr*fsYO6xKcFzo z=JeFOqSO#)-vFp_h9Lo=$S%uCOiIlGnglXR5NJheUOEHF8$jc7Q}arSLxX)nfKvQ0 sDNdkVPGwPMa`E(iyBK{Kt*3MEX0+u9iHL{*Dzlp2xQo%26Uc%90O;IFhX4Qo diff --git a/test/rsrc/full.flac b/test/rsrc/full.flac index abc18ac300bdc4d525ef3e983f0033022cd0dbed..e2d4f9bb4622fff0106f87b244b0014d17a3dd8e 100644 GIT binary patch delta 101 zcmZo#&DgY>ae_1Bgo!RHjFKBOHJIG(85kIHON%p;lZp~E^Qz*DQgc!hi&IOA5|gtt zQ*4usEYpk=P0e+aOpGmcP0Z3#bQ6tIjdU%I&CSh{42{hVQwae_0W??e|BMv0A?8cZyW46?U3KV<5co&15xXLEsqkSPHARt)X{ diff --git a/test/rsrc/full.m4a b/test/rsrc/full.m4a index 6105250e6c74f9c4670203f1039b9bd48edf4b33..2ec831e328c8dec15994a9e0a2daa7944958c973 100644 GIT binary patch delta 169 zcmaE+`%HI(FEbMx<7R*6cE-tb*%vT2OkT&P9HEhyn48J~1iq!knaNH?iJ5s-3PGtk zsfop@3L!;_$=M2?DL`qHl*E!mAk7F;n`~s6W}IkhuA5|HY^iHvmX@NMXq0NCYiVq5 zZkA+dY;Ksk*?=vVk#P~oj>&Uac{j7O>M~BY~V~Ym6HqDls8MUxiT{PftZtjvD8f7%b~=nk?0r{ r!oa|wF!>;}D7!*pQAuWT$>cj6UQEpQHV1IVFi$SvY}mYp$BGL8$`2cY diff --git a/test/rsrc/full.mp3 b/test/rsrc/full.mp3 index 9aeca5fb36eaf32acced04005fb8b5417300ccc0..d8c638f9c426b1af8033e80ff286a1d3e6cab80a 100644 GIT binary patch delta 243 zcmbP|G9_g~r>;|gF9QPuGZ33GggE;KKskmCApxOIKn^!hvLqu_Atx~@HD~e%Mk!Cv z5JR9i9}t7Y(~I&;3o`T48A4qBoPlEeK%5y;nwMIvP-d=Yrf0<9?C%S*LI8+U^U^_b z0q%|g3jp$L_`Eo9nc_V-_qjDWT&FU%)Bavpwyhy z#Nt$ikfOxoYz5C0hGZklG~+~5bKN8pV@q8Vv$Pc5M59zAT}xwgbF(BvV{^k)gUPIn U#gl88dNw;TZ)e$T$8k*)09p1vNB{r; delta 131 zcmbP|G9_g~=j88 z%W<+Lw@$hW0|exj7H1|W6(wfoRmB&j=A^%km delta 157 zcmX>j-YULf0SD_vCI$xS$xAs@CST{^n|zW(X!1)A>B*v;f|FT*SdG(SauKHnPfBKS za$ad}Qfd){$>d3#M*Qxnc}1xVB^jv-=^)nRJ)H8BKXHl+=2RACCKrPxAk4{(T#}QG qxmqXx;BuV2lgoax1-Gt(V}NUjqq}Rw1$G7oumc#tpaDWLFaQ8_lP!$^ diff --git a/test/rsrc/full.ogg b/test/rsrc/full.ogg index d2598d0021109bf2e962d82ff6bda9650a5dd9f5..54e1d71eb8474999106eec67423308c4e245f686 100644 GIT binary patch delta 169 zcmez9cEEo^0MCxrdwQb(!Juzqgc+m6#(rTYXL|+)hTPKP%;cn^#LT>^_@dOD)WqV{ zlA^@q?93F~WFyNo<3v+)-6RuZOI;JQv=rS$qf{eZOJj3$vm`@fbHh}F$%3MalMR#| NDZrAO7c-qx007jkII#c# delta 31 ncmX@$|Iuwi0LP|n7uf~>|F@qQW5y`Hak4NIBje^zZ08gJ<6aEx diff --git a/test/rsrc/full.opus b/test/rsrc/full.opus index 9a5534b2215ddf17f1ff9c67b97d5c1ffde630f6..2fb362c2fe05ee4ab33a5b05a2776c22e6554eb3 100644 GIT binary patch delta 1139 zcmca$G1qZ|E6>GR)-~+^!C>!1Ul~TJjah+A_4W)547sJnnaN2-iJ5s-@kOaQsfop@ zB}IwJ*_kP}$wroG#)+opx=AL+mbxZpX(_siMyW=+md57hW=V#|=7y;TKoiIY^b{@sS}Rmnlc*x=+OD9-=@ delta 29 lcmbR1c)?5qoje_rqaH&^McE6GOS`qI+t^lj{ywWH%clpa!%ub@KiQCGv=~0T221V zBFP`bkjjt)#EA^WKvH4yTxN0aWCkM!ONKNCW1y%hgE@mPLlT1tkY@>GnJ}0EWm15w zM4+lvAl3zn8v|9D17(sJ48d$epo{^~HnYisT#~#&46Y15KIp0`(as&{! z0r56tK}jwK2*{}{%1kb1D9K1wfG{^7H#TBSSAk0BmKJ9wClw`T=2gWPrRJn27N?dJ zB_?NQrZ6NMS*95$nwsk-nHXE@nwX`f=q4Ja8tGaZo12>@85)}#rW!Z~xP~~oyGC4K PXJ7!k5n^lu)N}>_^}93Q delta 121 zcmaEmH8X32i4p5XCI$xS$(Ba(lUEuEPM!h8yNoO*vl(kl{$wOFSc z1LAGQf|6Ve5Rh0@l385BP?C|V0AX%EZfwM;;27W<;^^)gae Date: Tue, 1 May 2018 21:58:11 +0100 Subject: [PATCH 13/15] Update _beet to use zsh caching Caching is now done with zsh builtin completion caching functions. --- extra/_beet | 183 +++++++++++++++++++++++++++------------------------- 1 file changed, 95 insertions(+), 88 deletions(-) diff --git a/extra/_beet b/extra/_beet index 23155b8e5..5b715dcef 100644 --- a/extra/_beet +++ b/extra/_beet @@ -2,28 +2,29 @@ # zsh completion for beets music library manager and MusicBrainz tagger: http://beets.radbox.org/ -# NOTE: it will be very slow the first time you try to complete in a zsh shell (especially if you've enable many plugins) -# You can make it faster in future by creating a cached version: -# 1) perform a query completion with this file (_beet), e.g. do: beet list artist:" -# to create the completion function (takes a few seconds) -# 2) save a copy of the completion function: which _beet > _beet_cached -# 3) save a copy of the query completion function: which _beet_query > _beet_query_cached -# 4) copy the contents of _beet_query_cached to the top of _beet_cached -# 5) copy and paste the _beet_field_values function from _beet to the top of _beet_cached -# 6) add the following line to the top of _beet_cached: #compdef beet -# 7) add the following line to the bottom of _beet_cached: _beet "$@" -# 8) save _beet_cached to your completions directory (e.g. /usr/share/zsh/functions/Completion) -# 9) add the following line to your .zshrc file: compdef _beet_cached beet -# You will need to repeat this proceedure each time you enable new plugins if you want them to complete properly. +# Cache will be updated if it is older than the beets database or binary. +# Need to set BEETS_LIBRARY to some preliminary value since it is used by the cache checking function. +typeset -g BEETS_LIBRARY=~/.config/beets/library.db +zstyle ":completion:${curcontext}:" cache-policy _beet_check_cache +_beet_check_cache () { + [[ ! -a "${1}" ]] || [[ ! -a ${~BEETS_LIBRARY} ]] || [[ "${1}" -ot ${~BEETS_LIBRARY} ]] || [[ "${1}" -ot =beet ]] +} +# Try to retrieve the cache, and find out if it needs to be updated +if ! _retrieve_cache beets || _cache_invalid beets; then + local updatecache=1 + # Location of database + typeset -g BEETS_LIBRARY="$(beet config|grep library|cut -f 2 -d ' ')" + # List of all fields + local -a fields + fields=(`beet fields | grep -G '^ ' | sort -u | colrm 1 2`) +fi # useful: argument to _regex_arguments for matching any word local matchany=/$'[^\0]##\0'/ # Deal with completions for querying and modifying fields.. local fieldargs matchquery matchmodify -local -a fields -# get list of all fields -fields=(`beet fields | grep -G '^ ' | sort -u | colrm 1 2`) + # regexps for matching query and modify terms on the command line matchquery=/"(${(j/|/)fields[@]})"$':[^\0]##\0'/ matchmodify=/"(${(j/|/)fields[@]})"$'(=[^\0]##|!)\0'/ @@ -43,14 +44,17 @@ function _join_lines() { function _beet_field_values() { local -a output fieldvals - local library="$(beet config|grep library|cut -f 2 -d ' ')" - output=$(sqlite3 ${~library} "select distinct $1 from items;") + local sqlcmd="select distinct $1 from items;" case $1 in lyrics) fieldvals= ;; *) + if [[ "$(sqlite3 ${~BEETS_LIBRARY} ${sqlcmd} 2>&1)" =~ "no such column" ]]; then + sqlcmd="select distinct value from item_attributes where key=='$1' and value!='';" + fi + output="$(sqlite3 ${~BEETS_LIBRARY} ${sqlcmd} 2>/dev/null | sed -rn '/^-+$/,${{/^[- ]+$/n};p}')" fieldvals=("${(f)output[@]}") ;; esac @@ -68,8 +72,7 @@ queryelem="_values -S : 'query field (add an extra : to match by regexp)' '::' $ # store call to _values function for completing modify terms (no need to complete field values) modifyelem="_values -S = 'modify field (replace = with ! to remove field)' $(echo "'${^fields[@]}:: '")" # Create completion function for queries -_regex_arguments _beet_query "$matchany" \# \( "$matchquery" ":query:query string:$queryelem" \) \ - \( "$matchquery" ":query:query string:$queryelem" \) \# +_regex_arguments _beet_query "$matchany" \# \( "$matchquery" ":query:query string:$queryelem" \) \( "$matchquery" ":query:query string:$queryelem" \) \# # store regexps for completing lists of queries and modifications local -a query modify query=( \( "$matchquery" ":query:query string:{_beet_query}" \) \( "$matchquery" ":query:query string:{_beet_query}" \) \# ) @@ -108,7 +111,7 @@ retagopt='-L:retag items matching a query:${query[@]}' skipopt='-i:skip already-imported directories' noskipopt='-I:do not skip already-imported directories' flatopt='--flat:import an entire tree as a single album' -groupopt='-g:group tracks in a folder into separate albums' +groupopt='-g:group tracks in a folder into separate albums' editopt='-e:edit user configuration with $EDITOR' defaultopt='-d:include the default configuration' copynomoveopt='-c:copy instead of moving' @@ -174,73 +177,76 @@ function _beet_subcmd_options() } # Now build the arguments to _regex_arguments for each subcommand. -local -a options regex_words_subcmds regex_words_help -local subcmd cmddesc -for i in ${${(f)"$(beet help | _join_lines ' ' 3 'Commands:')"[@]}[@]} -do - subcmd="${i[(w)1]}" - # remove first word and parenthesised alias, replace : with -, [ with (, ] with ), and remove single quotes - cmddesc="${${${${${i[(w)2,-1]##\(*\) #}//:/-}//\[/(}//\]/)}//\'/}" - case $subcmd +if [[ -n $updatecache ]]; then + local -a options regex_words_subcmds regex_words_help + local subcmd cmddesc + for i in ${${(f)"$(beet help | _join_lines ' ' 3 'Commands:')"[@]}[@]} + do + subcmd="${i[(w)1]}" + # remove first word and parenthesised alias, replace : with -, [ with (, ] with ), and remove single quotes + cmddesc="${${${${${i[(w)2,-1]##\(*\) #}//:/-}//\[/(}//\]/)}//\'/}" + case $subcmd in - (config) - _regex_words options "config options" "$helpopt" "$pathopt" "$editopt" "$defaultopt" - options=("${reply[@]}") - ;; - (import) - _regex_words options "import options" "$helpopt" "$writeopt" "$nowriteopt" "$copyopt" "$nocopyopt"\ - "$inferopt" "$noinferopt" "$resumeopt" "$noresumeopt" "$nopromptopt" "$logopt" "$individualopt" "$confirmopt"\ - "$retagopt" "$skipopt" "$noskipopt" "$flatopt" "$groupopt" - options=( "${reply[@]}" \# "${files[@]}" \# ) - ;; - (list) - _regex_words options "list options" "$helpopt" "$pathopt" "$albumopt" "$formatopt" - options=( "$reply[@]" \# "${query[@]}" ) - ;; - (modify) - _regex_words options "modify options" "$helpopt" "$dontmoveopt" "$writeopt" "$nowriteopt" "$albumopt" \ - "$noconfirmopt" "$formatopt" - options=( "${reply[@]}" \# "${query[@]}" "${modify[@]}" ) - ;; - (move) - _regex_words options "move options" "$helpopt" "$albumopt" "$destopt" "$copynomoveopt" - options=( "${reply[@]}" \# "${query[@]}") - ;; - (remove) - _regex_words options "remove options" "$helpopt" "$albumopt" "$removeopt" - options=( "${reply[@]}" \# "${query[@]}" ) - ;; - (stats) - _regex_words options "stats options" "$helpopt" "$exactopt" - options=( "${reply[@]}" \# "${query[@]}" ) - ;; - (update) - _regex_words options "update options" "$helpopt" "$albumopt" "$dontmoveopt" "$pretendopt" "$formatopt" - options=( "${reply[@]}" \# "${query[@]}" ) - ;; - (write) - _regex_words options "write options" "$helpopt" "$pretendopt" - options=( "${reply[@]}" \# "${query[@]}" ) - ;; - (fields|migrate|version) - options=() - ;; - (help) - # The help subcommand is treated separately - continue - ;; - (*) # completions for plugin commands are generated using _beet_subcmd_options - _beet_subcmd_options "$subcmd" - options=( \( "${reply[@]}" \# "${query[@]}" \) ) - ;; - esac - # Create variable for holding option for this subcommand, and assign to it (needs to have a unique name). - typeset -a opts_for_$subcmd - set -A opts_for_$subcmd ${options[@]} # Assignment MUST be done using set (other methods fail). - regex_words_subcmds+=("$subcmd:$cmddesc:\${(@)opts_for_$subcmd}") - # Add to regex_words args for help subcommand - regex_words_help+=("$subcmd:$cmddesc") -done + (config) + _regex_words options "config options" "$helpopt" "$pathopt" "$editopt" "$defaultopt" + options=("${reply[@]}") + ;; + (import) + _regex_words options "import options" "$helpopt" "$writeopt" "$nowriteopt" "$copyopt" "$nocopyopt"\ + "$inferopt" "$noinferopt" "$resumeopt" "$noresumeopt" "$nopromptopt" "$logopt" "$individualopt" "$confirmopt"\ + "$retagopt" "$skipopt" "$noskipopt" "$flatopt" "$groupopt" + options=( "${reply[@]}" \# "${files[@]}" \# ) + ;; + (list) + _regex_words options "list options" "$helpopt" "$pathopt" "$albumopt" "$formatopt" + options=( "$reply[@]" \# "${query[@]}" ) + ;; + (modify) + _regex_words options "modify options" "$helpopt" "$dontmoveopt" "$writeopt" "$nowriteopt" "$albumopt" \ + "$noconfirmopt" "$formatopt" + options=( "${reply[@]}" \# "${query[@]}" "${modify[@]}" ) + ;; + (move) + _regex_words options "move options" "$helpopt" "$albumopt" "$destopt" "$copynomoveopt" + options=( "${reply[@]}" \# "${query[@]}") + ;; + (remove) + _regex_words options "remove options" "$helpopt" "$albumopt" "$removeopt" + options=( "${reply[@]}" \# "${query[@]}" ) + ;; + (stats) + _regex_words options "stats options" "$helpopt" "$exactopt" + options=( "${reply[@]}" \# "${query[@]}" ) + ;; + (update) + _regex_words options "update options" "$helpopt" "$albumopt" "$dontmoveopt" "$pretendopt" "$formatopt" + options=( "${reply[@]}" \# "${query[@]}" ) + ;; + (write) + _regex_words options "write options" "$helpopt" "$pretendopt" + options=( "${reply[@]}" \# "${query[@]}" ) + ;; + (fields|migrate|version) + options=() + ;; + (help) + # The help subcommand is treated separately + continue + ;; + (*) # completions for plugin commands are generated using _beet_subcmd_options + _beet_subcmd_options "$subcmd" + options=( \( "${reply[@]}" \# "${query[@]}" \) ) + ;; + esac + # Create variable for holding option for this subcommand, and assign to it (needs to have a unique name). + typeset -a opts_for_$subcmd + set -A opts_for_$subcmd ${options[@]} # Assignment MUST be done using set (other methods fail). + regex_words_subcmds+=("$subcmd:$cmddesc:\${(@)opts_for_$subcmd}") + # Add to regex_words args for help subcommand + regex_words_help+=("$subcmd:$cmddesc") + done + _store_cache beets regex_words_subcmds regex_words_help BEETS_LIBRARY fields +fi local -a opts_for_help _regex_words subcmds "subcommands" "${regex_words_help[@]}" @@ -253,7 +259,7 @@ _regex_words options "global options" "$configopt" "$debugopt" "$libopt" "$helpo globalopts=("${reply[@]}") # Create main completion function -#local -a subcmds +local -a subcmds _regex_words subcmds "subcommands" "${regex_words_subcmds[@]}" subcmds=("${reply[@]}") _regex_arguments _beet "$matchany" \( "${globalopts[@]}" \# \) "${subcmds[@]}" @@ -267,3 +273,4 @@ _beet "$@" # Local Variables: # mode:shell-script # End: + From dd467e41e47d2a375f53ad3e39f4ce50e3267f02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Detrey?= Date: Wed, 2 May 2018 09:21:03 +0200 Subject: [PATCH 14/15] Add support for `musicbrainz_releasetrackid` to changelog. --- docs/changelog.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 8b49b97a1..86d4810d3 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -28,6 +28,9 @@ New features: ``mb_releasegroupid`` as well as simulates track ids using release id and tracklist positions. Track ids are stored in ``mb_trackid``. :bug:`#2336` Thanks to :user:`dbogdanov`. +* As a first step to get :bug:`#406` implemented, beets now imports the + ``musicbrainz_releasetrackid`` field into the library and tags media files + accordingly. Thanks to :user:`Rawrmonkeys`. Fixes: From f838dea08bbdd5024c7b9783eb401e8822f659bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Detrey?= Date: Wed, 2 May 2018 09:27:58 +0200 Subject: [PATCH 15/15] Fixed line too long. --- beets/autotag/hooks.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py index cc5f37003..7df1f62f4 100644 --- a/beets/autotag/hooks.py +++ b/beets/autotag/hooks.py @@ -154,12 +154,12 @@ class TrackInfo(object): may be None. The indices ``index``, ``medium``, and ``medium_index`` are all 1-based. """ - def __init__(self, title, track_id, release_track_id=None, artist=None, artist_id=None, - length=None, index=None, medium=None, medium_index=None, - medium_total=None, artist_sort=None, disctitle=None, - artist_credit=None, data_source=None, data_url=None, - media=None, lyricist=None, composer=None, composer_sort=None, - arranger=None, track_alt=None): + def __init__(self, title, track_id, release_track_id=None, artist=None, + artist_id=None, length=None, index=None, medium=None, + medium_index=None, medium_total=None, artist_sort=None, + disctitle=None, artist_credit=None, data_source=None, + data_url=None, media=None, lyricist=None, composer=None, + composer_sort=None, arranger=None, track_alt=None): self.title = title self.track_id = track_id self.release_track_id = release_track_id