From 3debf4edca3bdba6b0adcae46d7eef6d8ece57ae Mon Sep 17 00:00:00 2001 From: J0J0 Todos Date: Sat, 18 Mar 2023 09:41:55 +0100 Subject: [PATCH 01/10] Fix early exit in Discogs item_candidates method --- beetsplug/discogs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/beetsplug/discogs.py b/beetsplug/discogs.py index 3e12b47c25..1d691406f2 100644 --- a/beetsplug/discogs.py +++ b/beetsplug/discogs.py @@ -214,6 +214,7 @@ def item_candidates(self, item, artist, title): if not artist and not title: self._log.debug('Skipping Discogs query. File missing artist and ' 'title tags.') + return query = f'{artist} {title}' try: From 0630f4a2637e61a53b857cb93299a3a4a2bdd1ef Mon Sep 17 00:00:00 2001 From: J0J0 Todos Date: Fri, 17 Mar 2023 20:10:07 +0100 Subject: [PATCH 02/10] Strip word "vinyl" when searching Discogs In DiscogsPlugin.get_albums() we already strip away the words "CD" and "disk". It makes sense to also remove "vinyl" --- beetsplug/discogs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beetsplug/discogs.py b/beetsplug/discogs.py index 1d691406f2..d86885f3c0 100644 --- a/beetsplug/discogs.py +++ b/beetsplug/discogs.py @@ -277,7 +277,7 @@ def get_albums(self, query): query = re.sub(r'(?u)\W+', ' ', query) # Strip medium information from query, Things like "CD1" and "disk 1" # can also negate an otherwise positive result. - query = re.sub(r'(?i)\b(CD|disc)\s*\d+', '', query) + query = re.sub(r'(?i)\b(CD|disc|vinyl)\s*\d+', '', query) try: releases = self.discogs_client.search(query, From 106a1638cc72d6822cfb18644c2fa80fd405344d Mon Sep 17 00:00:00 2001 From: J0J0 Todos Date: Sat, 18 Mar 2023 19:25:30 +0100 Subject: [PATCH 03/10] Improve singleton disambiguation string When available, display e.g: - Track Index as "Index 2" - Alternative Track name as "Track A2" --- beets/ui/commands.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/beets/ui/commands.py b/beets/ui/commands.py index 1377ad0c5a..79297ee355 100755 --- a/beets/ui/commands.py +++ b/beets/ui/commands.py @@ -215,6 +215,12 @@ def disambig_string(info): if info.albumstatus == 'Pseudo-Release': disambig.append(info.albumstatus) + if isinstance(info, hooks.TrackInfo): + if info.index: + disambig.append("Index {}".format(str(info.index))) + if info.track_alt: + disambig.append("Track {}".format(info.track_alt)) + if disambig: return ', '.join(disambig) From 090cfc6f076553bb9c6acd056f05b655c93fa6ba Mon Sep 17 00:00:00 2001 From: J0J0 Todos Date: Mon, 17 Jul 2023 20:29:43 +0200 Subject: [PATCH 04/10] Add optional album field to candidate TrackInfo --- beets/autotag/hooks.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py index 5aa23d8a73..787b819916 100644 --- a/beets/autotag/hooks.py +++ b/beets/autotag/hooks.py @@ -212,6 +212,7 @@ def __init__( bpm: Optional[str] = None, initial_key: Optional[str] = None, genre: Optional[str] = None, + album: Optional[str] = None, **kwargs, ): self.title = title @@ -241,6 +242,7 @@ def __init__( self.bpm = bpm self.initial_key = initial_key self.genre = genre + self.album = album self.update(kwargs) # As above, work around a bug in python-musicbrainz-ngs. From f84b9ea7cf4c56e66bb0ac97a6b5198a1be70c4e Mon Sep 17 00:00:00 2001 From: J0J0 Todos Date: Tue, 18 Jul 2023 07:23:28 +0200 Subject: [PATCH 05/10] DiscogsPlugin.item_candidates attaches album info to the TrackInfo objects it returns. Additionally a new feature is introduced that uses string_dist to find the correct track on the Discogs album. --- beetsplug/discogs.py | 42 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/beetsplug/discogs.py b/beetsplug/discogs.py index d86885f3c0..d804a3e15a 100644 --- a/beetsplug/discogs.py +++ b/beetsplug/discogs.py @@ -19,7 +19,7 @@ import beets.ui from beets import config from beets.util.id_extractors import extract_discogs_id_regex -from beets.autotag.hooks import AlbumInfo, TrackInfo +from beets.autotag.hooks import AlbumInfo, TrackInfo, string_dist from beets.plugins import MetadataSourcePlugin, BeetsPlugin, get_distance import confuse from discogs_client import __version__ as dc_string @@ -196,6 +196,40 @@ def candidates(self, items, artist, album, va_likely, extra_tags=None): self._log.debug('Connection error in album search', exc_info=True) return [] + def get_track_from_album_by_title(self, album_info, title, + dist_threshold=0.3): + def compare_func(track_info): + track_title = getattr(track_info, "title", None) + dist = string_dist(track_title, title) + return (track_title and dist < dist_threshold) + return self.get_track_from_album(album_info, compare_func) + + def get_track_from_album(self, album_info, compare_func): + """Return the first track of the release where `compare_func` returns + true. + + :return: TrackInfo object. + :rtype: beets.autotag.hooks.TrackInfo + """ + if not album_info: + return None + + for track_info in album_info.tracks: + # check for matching position + if not compare_func(track_info): + continue + + # attach artist info if not provided + if not track_info['artist']: + track_info['artist'] = album_info.artist + track_info['artist_id'] = album_info.artist_id + # attach album info + track_info['album'] = album_info.album + + return track_info + + return None + def item_candidates(self, item, artist, title): """Returns a list of TrackInfo objects for Search API results matching ``title`` and ``artist``. @@ -231,7 +265,11 @@ def item_candidates(self, item, artist, title): candidates = [] for album_cur in albums: self._log.debug(u'searching within album {0}', album_cur.album) - candidates += album_cur.tracks + track_result = self.get_track_from_album_by_title( + album_cur, item['title'] + ) + if track_result: + candidates.append(track_result) # first 10 results, don't overwhelm with options return candidates[:10] From 4272cbec6a342cba864089438711c0b7bd58bef6 Mon Sep 17 00:00:00 2001 From: J0J0 Todos Date: Mon, 20 Mar 2023 08:20:01 +0100 Subject: [PATCH 06/10] Show album information during singleton imports - If the file being imported has an album tag already, display it. - If the metadata plugin provides a new album value, preview the change. --- beets/ui/commands.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/beets/ui/commands.py b/beets/ui/commands.py index 79297ee355..2a92ac2dd5 100755 --- a/beets/ui/commands.py +++ b/beets/ui/commands.py @@ -450,18 +450,25 @@ def show_item_change(item, match): """ cur_artist, new_artist = item.artist, match.info.artist cur_title, new_title = item.title, match.info.title + cur_album = item.album if item.album else "" + new_album = match.info.album if match.info.album else "" - if cur_artist != new_artist or cur_title != new_title: + if (cur_artist != new_artist or cur_title != new_title + or cur_album != new_album): cur_artist, new_artist = ui.colordiff(cur_artist, new_artist) cur_title, new_title = ui.colordiff(cur_title, new_title) + cur_album, new_album = ui.colordiff(cur_album, new_album) print_("Correcting track tags from:") print_(f" {cur_artist} - {cur_title}") + print_(f" Album: {cur_album}") if cur_album else None print_("To:") print_(f" {new_artist} - {new_title}") + print_(f" Album: {new_album}") if new_album else None else: print_(f"Tagging track: {cur_artist} - {cur_title}") + print_(f" Album: {new_album}") if cur_album else None # Data URL. if match.info.data_url: From d61b7c9a4225bd8afdcad94912ff8b7c02fe1c1c Mon Sep 17 00:00:00 2001 From: J0J0 Todos Date: Tue, 18 Jul 2023 12:58:28 +0200 Subject: [PATCH 07/10] Add changelog for #4854 --- docs/changelog.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index ab24133ee3..c2954c265c 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -94,6 +94,9 @@ New features: :bug:`4373` * Fetch the ``release_group_title`` field from MusicBrainz. :bug: `4809` +* :doc:`plugins/discogs`: Add support for applying album information on + singleton imports. + :bug: `4716` Bug fixes: From 1e050e2703991cecaeb03d1a3c799c4790966504 Mon Sep 17 00:00:00 2001 From: J0J0 Todos Date: Tue, 18 Jul 2023 13:24:26 +0200 Subject: [PATCH 08/10] Add singleton matching info to Discogs plugin docs PR #4854 --- docs/plugins/discogs.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/plugins/discogs.rst b/docs/plugins/discogs.rst index 1203a9ca31..bc4794724d 100644 --- a/docs/plugins/discogs.rst +++ b/docs/plugins/discogs.rst @@ -4,6 +4,11 @@ Discogs Plugin The ``discogs`` plugin extends the autotagger's search capabilities to include matches from the `Discogs`_ database. +Files can be imported as albums or as singletons. Since `Discogs`_ matches are +always based on `Discogs`_ releases, the album tag is written even to +singletons. This enhances the importers results when reimporting as (full or +partial) albums later on. + .. _Discogs: https://discogs.com Installation From 4087e8c7906aad0f3e15bbba7ed71cfe28ac0902 Mon Sep 17 00:00:00 2001 From: J0J0 Todos <2733783+JOJ0@users.noreply.github.com> Date: Wed, 19 Jul 2023 08:02:57 +0200 Subject: [PATCH 09/10] Simplify singleton importer "Album: " print conditions. Co-authored-by: Benedikt --- beets/ui/commands.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/beets/ui/commands.py b/beets/ui/commands.py index 2a92ac2dd5..e43726afbd 100755 --- a/beets/ui/commands.py +++ b/beets/ui/commands.py @@ -461,14 +461,17 @@ def show_item_change(item, match): print_("Correcting track tags from:") print_(f" {cur_artist} - {cur_title}") - print_(f" Album: {cur_album}") if cur_album else None + if cur_album: + print_(f" Album: {cur_album}") print_("To:") print_(f" {new_artist} - {new_title}") - print_(f" Album: {new_album}") if new_album else None + if new_album: + print_(f" Album: {new_album}") else: print_(f"Tagging track: {cur_artist} - {cur_title}") - print_(f" Album: {new_album}") if cur_album else None + if cur_album: + print_(f" Album: {new_album}") # Data URL. if match.info.data_url: From a3749779534d579b0f032db83ff530724f6b52d1 Mon Sep 17 00:00:00 2001 From: J0J0 Todos Date: Thu, 20 Jul 2023 09:14:07 +0200 Subject: [PATCH 10/10] Append album to singleton disambiguation string - New config option for the importer 'singleton_album_disambig' lets users choose whether they want to display [album names] in the list of candidates. Enabled by default but ony applicable if the candidate provides an album attribute. - Add docs describing the new option and which source plugins currently support it. --- beets/config_default.yaml | 1 + beets/ui/commands.py | 3 +++ docs/reference/config.rst | 13 +++++++++++++ 3 files changed, 17 insertions(+) diff --git a/beets/config_default.yaml b/beets/config_default.yaml index 97c6c8c572..dcb244bbd3 100644 --- a/beets/config_default.yaml +++ b/beets/config_default.yaml @@ -34,6 +34,7 @@ import: bell: no set_fields: {} ignored_alias_types: [] + singleton_album_disambig: yes clutter: ["Thumbs.DB", ".DS_Store"] ignore: [".*", "*~", "System Volume Information", "lost+found"] diff --git a/beets/ui/commands.py b/beets/ui/commands.py index e43726afbd..1f0375b633 100755 --- a/beets/ui/commands.py +++ b/beets/ui/commands.py @@ -220,6 +220,9 @@ def disambig_string(info): disambig.append("Index {}".format(str(info.index))) if info.track_alt: disambig.append("Track {}".format(info.track_alt)) + if (config['import']['singleton_album_disambig'].get() + and info.get('album')): + disambig.append("[{}]".format(info.album)) if disambig: return ', '.join(disambig) diff --git a/docs/reference/config.rst b/docs/reference/config.rst index beefcbbdfc..9e3834720f 100644 --- a/docs/reference/config.rst +++ b/docs/reference/config.rst @@ -760,6 +760,19 @@ Fields are persisted to the media files of each track. Default: ``{}`` (empty). +.. _singleton_album_disambig: + +singleton_album_disambig +~~~~~~~~~~~~~~~~~~~~~~~~ + +During singleton imports and if the metadata source provides it, album names +are appended to the disambiguation string of matching track candidates. For +example: ``The Artist - The Title (Discogs, Index 3, Track B1, [The Album]``. +This feature is currently supported by the :doc:`/plugins/discogs` and the +:doc:`/plugins/spotify`. + +Default: ``yes``. + .. _musicbrainz-config: MusicBrainz Options