From d5f998b3120c8027986b55b9288b530fbf165fcc Mon Sep 17 00:00:00 2001 From: wisp3rwind <17089248+wisp3rwind@users.noreply.github.com> Date: Sun, 18 Dec 2022 08:41:42 +0100 Subject: [PATCH 01/34] tests: fix assertions for empty MediaFile.images used to work due to inconsistent mediafile implementation, but with https://github.com/beetbox/mediafile/pull/64 (in mediafile >= 0.11.0) list fields are None if non-existent, not the empty list --- test/test_embedart.py | 4 ++-- test/test_zero.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test_embedart.py b/test/test_embedart.py index f41180ec1..6cf5bfa56 100644 --- a/test/test_embedart.py +++ b/test/test_embedart.py @@ -82,7 +82,7 @@ class EmbedartCliTest(_common.TestCase, TestHelper): self.run_command('embedart', '-f', self.small_artpath) mediafile = MediaFile(syspath(item.path)) # make sure that images array is empty (nothing embedded) - self.assertEqual(len(mediafile.images), 0) + self.assertFalse(mediafile.images) def test_embed_art_from_file(self): self._setup_data() @@ -203,7 +203,7 @@ class EmbedartCliTest(_common.TestCase, TestHelper): self.io.addinput('y') self.run_command('clearart') mediafile = MediaFile(syspath(item.path)) - self.assertEqual(len(mediafile.images), 0) + self.assertFalse(mediafile.images) def test_clear_art_with_no_input(self): self._setup_data() diff --git a/test/test_zero.py b/test/test_zero.py index c4c176960..b48367b41 100644 --- a/test/test_zero.py +++ b/test/test_zero.py @@ -103,7 +103,7 @@ class ZeroPluginTest(unittest.TestCase, TestHelper): item.write() mf = MediaFile(syspath(path)) - self.assertEqual(0, len(mf.images)) + self.assertFalse(mf.images) def test_auto_false(self): self.config['zero']['fields'] = ['year'] From e57783863113d35e6398984e74de0060e4da9362 Mon Sep 17 00:00:00 2001 From: wisp3rwind <17089248+wisp3rwind@users.noreply.github.com> Date: Sat, 17 Dec 2022 22:20:33 +0100 Subject: [PATCH 02/34] add mypy tox environment and dependencies --- setup.py | 9 +++++++++ tox.ini | 10 +++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index d49ed65b2..a6984ffd2 100755 --- a/setup.py +++ b/setup.py @@ -119,6 +119,15 @@ setup( 'flake8-docstrings', 'pep8-naming', ], + 'mypy': [ + 'mypy', + 'types-Pillow', + 'types-urllib3', + 'types-beautifulsoup4', + 'types-PyYAML', + 'types-requests', + 'types-Flask-Cors', + ], # Plugin (optional) dependencies: 'absubmit': ['requests'], diff --git a/tox.ini b/tox.ini index 1c0a984ed..861101e0f 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ # and then run "tox" from this directory. [tox] -envlist = py38-{cov,lint}, docs +envlist = py38-{cov,lint,mypy}, docs [_test] deps = .[test] @@ -13,15 +13,23 @@ deps = .[test] deps = .[lint] files = beets beetsplug beet test setup.py docs +[_mypy] +deps = + .[mypy] + .[test] + [testenv] deps = {test,cov}: {[_test]deps} lint: {[_lint]deps} + mypy: {[_mypy]deps} passenv = INTEGRATION_TEST commands = test: python -bb -m pytest -rs {posargs} cov: coverage run -m pytest -rs {posargs} lint: python -m flake8 {posargs} {[_lint]files} + mypy: mypy -p beets -p beetsplug + mypy: mypy test [testenv:docs] basepython = python3.10 From d604c7e175ed117162681d09770f81ae8677a9f4 Mon Sep 17 00:00:00 2001 From: wisp3rwind <17089248+wisp3rwind@users.noreply.github.com> Date: Sat, 17 Dec 2022 22:23:06 +0100 Subject: [PATCH 03/34] add mypy to actions, but allow failure --- .github/workflows/ci.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index b3472e412..96b230c59 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -54,6 +54,15 @@ jobs: run: | tox -vv -e py-cov + - name: Test latest Python version with tox and mypy + if: matrix.python-version == '3.10' + # continue-on-error is not ideal since it doesn't give a visible + # warning, but there doesn't seem to be anything better: + # https://github.com/actions/toolkit/issues/399 + continue-on-error: true + run: | + tox -vv -e py-mypy + - name: Test nightly Python version with tox if: matrix.python-version == '3.11-dev' # continue-on-error is not ideal since it doesn't give a visible From d15c5c4ba246d5866c945342cc09b29d2e849c5c Mon Sep 17 00:00:00 2001 From: wisp3rwind <17089248+wisp3rwind@users.noreply.github.com> Date: Sat, 17 Dec 2022 22:32:30 +0100 Subject: [PATCH 04/34] mypy: ignore_missing_imports = True --- .mypy.ini | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .mypy.ini diff --git a/.mypy.ini b/.mypy.ini new file mode 100644 index 000000000..b47e5dff3 --- /dev/null +++ b/.mypy.ini @@ -0,0 +1,5 @@ +[mypy] +# FIXME: Would be better to actually type the libraries (if under our control), +# or write our own stubs. For now, silence errors +ignore_missing_imports = True + From 1c7889b0baf8564d87f090eef32c97c2747dfdbe Mon Sep 17 00:00:00 2001 From: wisp3rwind <17089248+wisp3rwind@users.noreply.github.com> Date: Thu, 15 Dec 2022 21:53:02 +0100 Subject: [PATCH 05/34] tests: add a (failing) test for issue #4528 --- test/test_ui.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/test_ui.py b/test/test_ui.py index ad4387013..dd393035b 100644 --- a/test/test_ui.py +++ b/test/test_ui.py @@ -701,6 +701,28 @@ class UpdateTest(_common.TestCase): item = self.lib.items().get() self.assertEqual(item.title, 'full') + @unittest.expectedFailure + def test_multivalued_albumtype_roundtrip(self): + # https://github.com/beetbox/beets/issues/4528 + + # albumtypes is empty for our test fixtures, so populate it first + album = self.album + # setting albumtypes does not set albumtype currently... + # FIXME: When actually fixing the issue 4528, consider whether this + # should be set to "album" or ["album"] + album.albumtype = "album" + album.albumtypes = "album" + album.try_sync(write=True, move=False) + + album.load() + albumtype_before = album.albumtype + self.assertEqual(albumtype_before, "album") + + self._update() + + album.load() + self.assertEqual(albumtype_before, album.albumtype) + class PrintTest(_common.TestCase): def setUp(self): From 2106f471affd1dab35b4b26187b9c74d034528c5 Mon Sep 17 00:00:00 2001 From: Jack Wilsdon Date: Sat, 31 Dec 2022 14:23:34 +0000 Subject: [PATCH 06/34] Add missing placeholders to extlinks captions Sphinx 6.0.0 changed extlinks to always require placeholders in link captions. See https://github.com/sphinx-doc/sphinx/commit/93cf1a57d916a1ff96c8e8a0356d0256e40489ac --- docs/conf.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index f8ed63f9d..cb7596c55 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -18,10 +18,10 @@ pygments_style = 'sphinx' # External links to the bug tracker and other sites. extlinks = { - 'bug': ('https://github.com/beetbox/beets/issues/%s', '#'), - 'user': ('https://github.com/%s', ''), - 'pypi': ('https://pypi.org/project/%s/', ''), - 'stdlib': ('https://docs.python.org/3/library/%s.html', ''), + 'bug': ('https://github.com/beetbox/beets/issues/%s', '#%s'), + 'user': ('https://github.com/%s', '%s'), + 'pypi': ('https://pypi.org/project/%s/', '%s'), + 'stdlib': ('https://docs.python.org/3/library/%s.html', '%s'), } linkcheck_ignore = [ From dad9d2393dc9f984b51a3c7b3b6172b73c17b8d6 Mon Sep 17 00:00:00 2001 From: Jack Wilsdon Date: Sat, 31 Dec 2022 14:04:25 +0000 Subject: [PATCH 07/34] Remove Musixmatch from default enabled sources Musixmatch are currently blocking all requests with the beets user agent. See #4585. --- beetsplug/lyrics.py | 4 +++- docs/changelog.rst | 5 ++++- docs/plugins/lyrics.rst | 14 ++++++-------- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/beetsplug/lyrics.py b/beetsplug/lyrics.py index 15e02cf4f..758f092de 100644 --- a/beetsplug/lyrics.py +++ b/beetsplug/lyrics.py @@ -743,7 +743,9 @@ class LyricsPlugin(plugins.BeetsPlugin): 'fallback': None, 'force': False, 'local': False, - 'sources': self.SOURCES, + # Musixmatch is disabled by default as they are currently blocking + # requests with the beets user agent. + 'sources': [s for s in self.SOURCES if s != "musixmatch"], 'dist_thresh': 0.1, }) self.config['bing_client_secret'].redact = True diff --git a/docs/changelog.rst b/docs/changelog.rst index c7f3eb614..a73bd3373 100755 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -129,11 +129,14 @@ For packagers: :bug:`4167` * The minimum required version of :pypi:`mediafile` is now 0.9.0. -Other new things: +Other changes: * :doc:`/plugins/limit`: Limit query results to head or tail (``lslimit`` command only) * :doc:`/plugins/fish`: Add ``--output`` option. +* :doc:`/plugins/lyrics`: Remove Musixmatch from default enabled sources as + they are currently blocking requests from the beets user agent. + :bug:`4585` 1.6.0 (November 27, 2021) ------------------------- diff --git a/docs/plugins/lyrics.rst b/docs/plugins/lyrics.rst index 7c5a3773c..90c455e15 100644 --- a/docs/plugins/lyrics.rst +++ b/docs/plugins/lyrics.rst @@ -2,10 +2,9 @@ Lyrics Plugin ============= The ``lyrics`` plugin fetches and stores song lyrics from databases on the Web. -Namely, the current version of the plugin uses `Musixmatch`_, `Genius.com`_, -`Tekstowo.pl`_, and, optionally, the Google custom search API. +Namely, the current version of the plugin uses `Genius.com`_, `Tekstowo.pl`_, +and, optionally, the Google custom search API. -.. _Musixmatch: https://www.musixmatch.com/ .. _Genius.com: https://genius.com/ .. _Tekstowo.pl: https://www.tekstowo.pl/ @@ -59,9 +58,9 @@ configuration file. The available options are: sources known to be scrapeable. - **sources**: List of sources to search for lyrics. An asterisk ``*`` expands to all available sources. - Default: ``google musixmatch genius tekstowo``, i.e., all the - available sources. The ``google`` source will be automatically - deactivated if no ``google_API_key`` is setup. + Default: ``google genius tekstowo``, i.e., all the available sources. The + ``google`` source will be automatically deactivated if no ``google_API_key`` + is setup. The ``google``, ``genius``, and ``tekstowo`` sources will only be enabled if BeautifulSoup is installed. @@ -139,8 +138,7 @@ configuration option to your key. Then add ``google`` to the list of sources in your configuration (or use default list, which includes it as long as you have an API key). If you use default ``google_engine_ID``, we recommend limiting the sources to -``musixmatch google`` as the other sources are already included in the Google -results. +``google`` as the other sources are already included in the Google results. .. _register for a Google API key: https://console.developers.google.com/ From 2839302d53fde011cd2cff90f265ab71eddbf0f7 Mon Sep 17 00:00:00 2001 From: J0J0 Todos Date: Thu, 22 Dec 2022 17:45:34 +0100 Subject: [PATCH 08/34] fromfilename: Swap regex lines as suggested in #4561 --- beetsplug/fromfilename.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beetsplug/fromfilename.py b/beetsplug/fromfilename.py index 55684a274..c216a654e 100644 --- a/beetsplug/fromfilename.py +++ b/beetsplug/fromfilename.py @@ -29,8 +29,8 @@ PATTERNS = [ r'^(?P\d+)[\s.\-_]+(?P.+)[\-_](?P.+)[\-_](?P<tag>.*)$', r'^(?P<artist>.+)[\-_](?P<title>.+)$', r'^(?P<track>\d+)[\s.\-_]+(?P<artist>.+)[\-_](?P<title>.+)$', - r'^(?P<title>.+)$', r'^(?P<track>\d+)[\s.\-_]+(?P<title>.+)$', + r'^(?P<title>.+)$', r'^(?P<track>\d+)\s+(?P<title>.+)$', r'^(?P<title>.+) by (?P<artist>.+)$', r'^(?P<track>\d+).*$', From 5461ddf9f21c0301d7d65ee149b778c4ab76304b Mon Sep 17 00:00:00 2001 From: J0J0 Todos <jojo@peek-a-boo.at> Date: Wed, 28 Dec 2022 09:30:21 +0100 Subject: [PATCH 09/34] fromfilename: Move <title> regex to the very end since it's the least significant as discussed in the PR's thread. --- beetsplug/fromfilename.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beetsplug/fromfilename.py b/beetsplug/fromfilename.py index c216a654e..74004a502 100644 --- a/beetsplug/fromfilename.py +++ b/beetsplug/fromfilename.py @@ -30,10 +30,10 @@ PATTERNS = [ r'^(?P<artist>.+)[\-_](?P<title>.+)$', r'^(?P<track>\d+)[\s.\-_]+(?P<artist>.+)[\-_](?P<title>.+)$', r'^(?P<track>\d+)[\s.\-_]+(?P<title>.+)$', - r'^(?P<title>.+)$', r'^(?P<track>\d+)\s+(?P<title>.+)$', r'^(?P<title>.+) by (?P<artist>.+)$', r'^(?P<track>\d+).*$', + r'^(?P<title>.+)$', ] # Titles considered "empty" and in need of replacement. From c1abcf33103dbc04f5490525ca24d1da4c14851b Mon Sep 17 00:00:00 2001 From: J0J0 T <jojo@peek-a-boo.at> Date: Wed, 23 Mar 2022 21:23:19 +0100 Subject: [PATCH 10/34] fromfilename: Add debug log messages that inform when the plugin replaced bad artist, title or tracknumber metadata. --- beetsplug/fromfilename.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/beetsplug/fromfilename.py b/beetsplug/fromfilename.py index 74004a502..1697b7081 100644 --- a/beetsplug/fromfilename.py +++ b/beetsplug/fromfilename.py @@ -18,9 +18,11 @@ filename. from beets import plugins from beets.util import displayable_path +from beets import logging import os import re +log = logging.getLogger('beets') # Filename field extraction patterns. PATTERNS = [ @@ -113,6 +115,9 @@ def apply_matches(d): for item in d: if not item.artist: item.artist = artist + log.debug( + 'fromfilename: Artist replaced with: {}' + .format(item.artist)) # No artist field: remaining field is the title. else: @@ -122,8 +127,14 @@ def apply_matches(d): for item in d: if bad_title(item.title): item.title = str(d[item][title_field]) + log.debug( + 'fromfilename: Title replaced with: {}' + .format(item.title)) if 'track' in d[item] and item.track == 0: item.track = int(d[item]['track']) + log.debug( + 'fromfilename: Track replaced with: {}' + .format(item.track)) # Plugin structure and hook into import process. From 688ad4aad50599b62f47d656e326d1710eb0a9a0 Mon Sep 17 00:00:00 2001 From: J0J0 Todos <jojo@peek-a-boo.at> Date: Wed, 28 Dec 2022 11:37:55 +0100 Subject: [PATCH 11/34] fromfilename: Changelog for #4600 --- docs/changelog.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index c7f3eb614..6acfe9274 100755 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -49,6 +49,9 @@ New features: :bug:`4438` * Add a new ``import.ignored_alias_types`` config option to allow for specific alias types to be skipped over when importing items/albums. +* :doc:`/plugins/fromfilename`: Add debug log messages that inform when the + plugin replaced bad (missing) artist, title or tracknumber metadata. + :bug:`4561` :bug:`4600` Bug fixes: @@ -122,6 +125,9 @@ Bug fixes: * :doc:`plugins/lyrics`: Fixed issue with Tekstowo backend not actually checking if the found song matches. :bug:`4406` +* :doc:`/plugins/fromfilename`: Fix failed detection of <track> <title> + filename patterns. + :bug:`4561` :bug:`4600` For packagers: From 6fb6f59c404593fc713510d3bf347326941ad37b Mon Sep 17 00:00:00 2001 From: J0J0 Todos <jojo@peek-a-boo.at> Date: Sat, 31 Dec 2022 08:46:31 +0100 Subject: [PATCH 12/34] fromfilename: Use logging magic from inherited BeetsPlugin and change log level to INFO. --- beetsplug/fromfilename.py | 67 +++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 38 deletions(-) diff --git a/beetsplug/fromfilename.py b/beetsplug/fromfilename.py index 1697b7081..6d7e3d009 100644 --- a/beetsplug/fromfilename.py +++ b/beetsplug/fromfilename.py @@ -18,12 +18,9 @@ filename. from beets import plugins from beets.util import displayable_path -from beets import logging import os import re -log = logging.getLogger('beets') - # Filename field extraction patterns. PATTERNS = [ # Useful patterns. @@ -87,7 +84,7 @@ def bad_title(title): return False -def apply_matches(d): +def apply_matches(d, log): """Given a mapping from items to field dicts, apply the fields to the objects. """ @@ -115,9 +112,7 @@ def apply_matches(d): for item in d: if not item.artist: item.artist = artist - log.debug( - 'fromfilename: Artist replaced with: {}' - .format(item.artist)) + log.info('Artist replaced with: {}'.format(item.artist)) # No artist field: remaining field is the title. else: @@ -127,14 +122,11 @@ def apply_matches(d): for item in d: if bad_title(item.title): item.title = str(d[item][title_field]) - log.debug( - 'fromfilename: Title replaced with: {}' - .format(item.title)) + log.info('Title replaced with: {}'.format(item.title)) + if 'track' in d[item] and item.track == 0: item.track = int(d[item]['track']) - log.debug( - 'fromfilename: Track replaced with: {}' - .format(item.track)) + log.info('Track replaced with: {}'.format(item.track)) # Plugin structure and hook into import process. @@ -142,32 +134,31 @@ def apply_matches(d): class FromFilenamePlugin(plugins.BeetsPlugin): def __init__(self): super().__init__() - self.register_listener('import_task_start', filename_task) + self.register_listener('import_task_start', self.filename_task) + def filename_task(self, task, session): + """Examine each item in the task to see if we can extract a title + from the filename. Try to match all filenames to a number of + regexps, starting with the most complex patterns and successively + trying less complex patterns. As soon as all filenames match the + same regex we can make an educated guess of which part of the + regex that contains the title. + """ + items = task.items if task.is_album else [task.item] -def filename_task(task, session): - """Examine each item in the task to see if we can extract a title - from the filename. Try to match all filenames to a number of - regexps, starting with the most complex patterns and successively - trying less complex patterns. As soon as all filenames match the - same regex we can make an educated guess of which part of the - regex that contains the title. - """ - items = task.items if task.is_album else [task.item] + # Look for suspicious (empty or meaningless) titles. + missing_titles = sum(bad_title(i.title) for i in items) - # Look for suspicious (empty or meaningless) titles. - missing_titles = sum(bad_title(i.title) for i in items) + if missing_titles: + # Get the base filenames (no path or extension). + names = {} + for item in items: + path = displayable_path(item.path) + name, _ = os.path.splitext(os.path.basename(path)) + names[item] = name - if missing_titles: - # Get the base filenames (no path or extension). - names = {} - for item in items: - path = displayable_path(item.path) - name, _ = os.path.splitext(os.path.basename(path)) - names[item] = name - - # Look for useful information in the filenames. - for pattern in PATTERNS: - d = all_matches(names, pattern) - if d: - apply_matches(d) + # Look for useful information in the filenames. + for pattern in PATTERNS: + d = all_matches(names, pattern) + if d: + apply_matches(d, self._log) From fddeecb29fadb0e1758be4126daca0fcc5161208 Mon Sep 17 00:00:00 2001 From: J0J0 Todos <jojo@peek-a-boo.at> Date: Mon, 10 Oct 2022 08:16:08 +0200 Subject: [PATCH 13/34] Add join_key arg to source plugin get_artist method Add an optional argument to MetadataSourcePlugin.get_artist method that enables making use of a field containing a keyword supposed to be used to combine artists together into a single string like "Feat.", "And", "Vs." and so on. --- beets/plugins.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/beets/plugins.py b/beets/plugins.py index ed1f82d8f..aa62d7003 100644 --- a/beets/plugins.py +++ b/beets/plugins.py @@ -655,7 +655,7 @@ class MetadataSourcePlugin(metaclass=abc.ABCMeta): raise NotImplementedError @staticmethod - def get_artist(artists, id_key='id', name_key='name'): + def get_artist(artists, id_key='id', name_key='name', join_key=None): """Returns an artist string (all artists) and an artist_id (the main artist) for a list of artist object dicts. @@ -663,6 +663,8 @@ class MetadataSourcePlugin(metaclass=abc.ABCMeta): and 'the') to the front and strips trailing disambiguation numbers. It returns a tuple containing the comma-separated string of all normalized artists and the ``id`` of the main/first artist. + Alternatively a keyword can be used to combine artists together into a + single string by passing the join_key argument. :param artists: Iterable of artist dicts or lists returned by API. :type artists: list[dict] or list[list] @@ -673,11 +675,17 @@ class MetadataSourcePlugin(metaclass=abc.ABCMeta): to concatenate for the artist string (containing all artists). Defaults to 'name'. :type name_key: str or int + :param join_key: Key or index corresponding to a field containing a + keyword to use for combining artists into a single string, for + example "Feat.", "Vs.", "And" or similar. The default is None + which keeps the default behaviour (comma-separated). + :type join_key: str or int :return: Normalized artist string. :rtype: str """ artist_id = None artist_names = [] + joined = False for artist in artists: if not artist_id: artist_id = artist[id_key] @@ -686,8 +694,17 @@ class MetadataSourcePlugin(metaclass=abc.ABCMeta): name = re.sub(r' \(\d+\)$', '', name) # Move articles to the front. name = re.sub(r'^(.*?), (a|an|the)$', r'\2 \1', name, flags=re.I) + # Use a join keyword if requested and available. + if join_key and artist.get(join_key, None): + name += " " + artist[join_key] + joined = True artist_names.append(name) - artist = ', '.join(artist_names).replace(' ,', ',') or None + # Concat using spaces only if join_key was passed and the join_key was + # ever found. Otherwise join comma-separated. + if join_key and joined is True: + artist = ' '.join(artist_names) or None + else: + artist = ', '.join(artist_names).replace(' ,', ',') or None return artist, artist_id def _get_id(self, url_type, id_): From 29ed4abb52f9ef0e5b78d12bcb84fba609048f06 Mon Sep 17 00:00:00 2001 From: J0J0 Todos <jojo@peek-a-boo.at> Date: Mon, 10 Oct 2022 08:23:26 +0200 Subject: [PATCH 14/34] Use join_key arg in Discogs source plugin Fixes #4401 --- beetsplug/discogs.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/beetsplug/discogs.py b/beetsplug/discogs.py index 820a0acbd..a474871ac 100644 --- a/beetsplug/discogs.py +++ b/beetsplug/discogs.py @@ -302,7 +302,8 @@ class DiscogsPlugin(BeetsPlugin): return None artist, artist_id = MetadataSourcePlugin.get_artist( - [a.data for a in result.artists] + [a.data for a in result.artists], + join_key='join' ) album = re.sub(r' +', ' ', result.title) album_id = result.data['id'] @@ -566,7 +567,8 @@ class DiscogsPlugin(BeetsPlugin): track_id = None medium, medium_index, _ = self.get_track_index(track['position']) artist, artist_id = MetadataSourcePlugin.get_artist( - track.get('artists', []) + track.get('artists', []), + join_key='join' ) length = self.get_track_length(track['duration']) return TrackInfo(title=title, track_id=track_id, artist=artist, From e1d6110249238f822aa482a25e4d84460b9442c1 Mon Sep 17 00:00:00 2001 From: J0J0 Todos <jojo@peek-a-boo.at> Date: Mon, 10 Oct 2022 10:01:17 +0200 Subject: [PATCH 15/34] Changelog for #4515 --- docs/changelog.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index c7f3eb614..c59944b1f 100755 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -52,6 +52,11 @@ New features: Bug fixes: +* :doc:`/plugins/discogs`: Fix "Discogs plugin replacing Feat. or Ft. with + a comma" by fixing an oversight that removed a functionality from the code + base when the MetadataSourcePlugin abstract class was introduced in PR's + #3335 and #3371. + :bug:`4401` * :doc:`/plugins/convert`: Set default ``max_bitrate`` value to ``None`` to avoid transcoding when this parameter is not set. :bug:`4472` * :doc:`/plugins/replaygain`: Avoid a crash when errors occur in the analysis From 1b0f49fc3bcc3a528374f4bf55e2005959824289 Mon Sep 17 00:00:00 2001 From: J0J0 Todos <jojo@peek-a-boo.at> Date: Thu, 29 Dec 2022 14:26:02 +0100 Subject: [PATCH 16/34] Simplify MetadataSourcePlugin.get_artist method - Originally a list of "artists" was generated and joined together to a final string later. - This simplifies by concatinating to a final string within the main loop. - Also this commit gets rid of a mysterious replacement code (` ,' -> ',') --- beets/plugins.py | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/beets/plugins.py b/beets/plugins.py index aa62d7003..fe8bb532a 100644 --- a/beets/plugins.py +++ b/beets/plugins.py @@ -684,9 +684,8 @@ class MetadataSourcePlugin(metaclass=abc.ABCMeta): :rtype: str """ artist_id = None - artist_names = [] - joined = False - for artist in artists: + artist_string = "" + for idx, artist in enumerate(artists): if not artist_id: artist_id = artist[id_key] name = artist[name_key] @@ -695,17 +694,14 @@ class MetadataSourcePlugin(metaclass=abc.ABCMeta): # Move articles to the front. name = re.sub(r'^(.*?), (a|an|the)$', r'\2 \1', name, flags=re.I) # Use a join keyword if requested and available. - if join_key and artist.get(join_key, None): - name += " " + artist[join_key] - joined = True - artist_names.append(name) - # Concat using spaces only if join_key was passed and the join_key was - # ever found. Otherwise join comma-separated. - if join_key and joined is True: - artist = ' '.join(artist_names) or None - else: - artist = ', '.join(artist_names).replace(' ,', ',') or None - return artist, artist_id + if idx < (len(artists) - 1): # Skip joining on last. + if join_key and artist.get(join_key, None): + name += f" {artist[join_key]} " + else: + name += ', ' + artist_string += name + + return artist_string, artist_id def _get_id(self, url_type, id_): """Parse an ID from its URL if necessary. From f1794e87098c435b07006158504ad7b5cbed1bad Mon Sep 17 00:00:00 2001 From: J0J0 Todos <jojo@peek-a-boo.at> Date: Thu, 29 Dec 2022 18:04:57 +0100 Subject: [PATCH 17/34] Support generators in MetadataSourcePlugin.get_artist method --- beets/plugins.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/beets/plugins.py b/beets/plugins.py index fe8bb532a..c14d7b423 100644 --- a/beets/plugins.py +++ b/beets/plugins.py @@ -685,6 +685,8 @@ class MetadataSourcePlugin(metaclass=abc.ABCMeta): """ artist_id = None artist_string = "" + artists = list(artists) # In case a generator was passed. + total = len(artists) for idx, artist in enumerate(artists): if not artist_id: artist_id = artist[id_key] @@ -694,7 +696,7 @@ class MetadataSourcePlugin(metaclass=abc.ABCMeta): # Move articles to the front. name = re.sub(r'^(.*?), (a|an|the)$', r'\2 \1', name, flags=re.I) # Use a join keyword if requested and available. - if idx < (len(artists) - 1): # Skip joining on last. + if idx < (total - 1): # Skip joining on last. if join_key and artist.get(join_key, None): name += f" {artist[join_key]} " else: From 8938319075d7e8974baedf8d3c53a8934bb8c5a1 Mon Sep 17 00:00:00 2001 From: ghbrown <gabriel.h.brown@gmail.com> Date: Tue, 10 Jan 2023 12:02:35 -0600 Subject: [PATCH 18/34] Add Discogs singleton limitation to docs --- docs/changelog.rst | 1 + docs/plugins/discogs.rst | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 66802d203..959ea9516 100755 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -37,6 +37,7 @@ New features: ``=~``. :bug:`4251` * :doc:`/plugins/discogs`: Permit appending style to genre +* :doc:`/plugins/discogs`: State limitation when importing singletons * :doc:`/plugins/convert`: Add a new `auto_keep` option that automatically converts files but keeps the *originals* in the library. :bug:`1840` :bug:`4302` diff --git a/docs/plugins/discogs.rst b/docs/plugins/discogs.rst index 53c6c2ac0..7b3ffe914 100644 --- a/docs/plugins/discogs.rst +++ b/docs/plugins/discogs.rst @@ -2,7 +2,9 @@ Discogs Plugin ============== The ``discogs`` plugin extends the autotagger's search capabilities to -include matches from the `Discogs`_ database. +include matches from the `Discogs`_ database when importing albums. +Note that matching of singleton tracks using the ``discogs`` plugin +is not currently supported. .. _Discogs: https://discogs.com @@ -99,4 +101,9 @@ Here are two things you can try: * Make sure that your system clock is accurate. The Discogs servers can reject your request if your clock is too out of sync. +Furthermore, the plugin itself is limited to matching albums, so no +Discogs matches will be reported when importing singletons using +``-s``. One possible workaround is to use the ``--group-albums`` +option. + .. _python3-discogs-client: https://github.com/joalla/discogs_client From a6dfd1b40cecc38235d202009c4f2b16b18e4d87 Mon Sep 17 00:00:00 2001 From: ghbrown <gabriel.h.brown@gmail.com> Date: Wed, 11 Jan 2023 11:40:25 -0600 Subject: [PATCH 19/34] Writing improvements --- docs/changelog.rst | 1 - docs/plugins/discogs.rst | 10 ++++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 959ea9516..66802d203 100755 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -37,7 +37,6 @@ New features: ``=~``. :bug:`4251` * :doc:`/plugins/discogs`: Permit appending style to genre -* :doc:`/plugins/discogs`: State limitation when importing singletons * :doc:`/plugins/convert`: Add a new `auto_keep` option that automatically converts files but keeps the *originals* in the library. :bug:`1840` :bug:`4302` diff --git a/docs/plugins/discogs.rst b/docs/plugins/discogs.rst index 7b3ffe914..a9125e737 100644 --- a/docs/plugins/discogs.rst +++ b/docs/plugins/discogs.rst @@ -3,8 +3,7 @@ Discogs Plugin The ``discogs`` plugin extends the autotagger's search capabilities to include matches from the `Discogs`_ database when importing albums. -Note that matching of singleton tracks using the ``discogs`` plugin -is not currently supported. +(The plugin does not yet support matching singleton tracks.) .. _Discogs: https://discogs.com @@ -101,9 +100,8 @@ Here are two things you can try: * Make sure that your system clock is accurate. The Discogs servers can reject your request if your clock is too out of sync. -Furthermore, the plugin itself is limited to matching albums, so no -Discogs matches will be reported when importing singletons using -``-s``. One possible workaround is to use the ``--group-albums`` -option. +The plugin can only match albums, so no Discogs matches will be +reported when importing singletons using ``-s``. One possible +workaround is to use the ``--group-albums`` option. .. _python3-discogs-client: https://github.com/joalla/discogs_client From 2db0796fadb248f6786e923d5dbdf0daf67387b2 Mon Sep 17 00:00:00 2001 From: ghbrown <gabriel.h.brown@gmail.com> Date: Wed, 11 Jan 2023 13:20:54 -0600 Subject: [PATCH 20/34] Implement item_candidates for Discogs --- beetsplug/discogs.py | 49 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/beetsplug/discogs.py b/beetsplug/discogs.py index a474871ac..de6cf45f2 100644 --- a/beetsplug/discogs.py +++ b/beetsplug/discogs.py @@ -180,6 +180,55 @@ class DiscogsPlugin(BeetsPlugin): self._log.debug('Connection error in album search', exc_info=True) return [] + def item_candidates(self, item, artist, title): + """Returns a list of TrackInfo objects for Search API results + matching ``title`` and ``artist``. + :param item: Singleton item to be matched. + :type item: beets.library.Item + :param artist: The artist of the track to be matched. + :type artist: str + :param title: The title of the track to be matched. + :type title: str + :return: Candidate TrackInfo objects. + :rtype: list[beets.autotag.hooks.TrackInfo] + """ + if not self.discogs_client: + return + + query = f'{artist} {title}' + try: + albums = self.get_albums(query) + except DiscogsAPIError as e: + self._log.debug('API Error: {0} (query: {1})', e, query) + if e.status_code == 401: + self.reset_auth() + return self.item_candidates(item, artist, title) + else: + return [] + except CONNECTION_ERRORS: + self._log.debug('Connection error in track search', exc_info=True) + candidates = [] + for album_cur in albums: + self._log.debug(u'searching within album {0}', album_cur.album) + track_list = self.get_tracks_from_album(album_cur) + candidates += track_list + return candidates + + def get_tracks_from_album(self, album_info): + """Return a list of tracks in the release + """ + if not album_info: + return [] + + result = [] + for track_info in album_info.tracks: + # 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 + result.append(track_info) + return result + @staticmethod def extract_release_id_regex(album_id): """Returns the Discogs_id or None.""" From 2e916404f93e739750a1d6c5cfadfa8e49c9bcd0 Mon Sep 17 00:00:00 2001 From: ghbrown <gabriel.h.brown@gmail.com> Date: Thu, 12 Jan 2023 19:41:04 -0600 Subject: [PATCH 21/34] early exit; add data_source --- beetsplug/discogs.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/beetsplug/discogs.py b/beetsplug/discogs.py index de6cf45f2..435d67b40 100644 --- a/beetsplug/discogs.py +++ b/beetsplug/discogs.py @@ -195,6 +195,10 @@ class DiscogsPlugin(BeetsPlugin): if not self.discogs_client: return + if not artist and not title: + self._log.debug('Skipping Discogs query. File missing artist and ' + 'title tags.') + query = f'{artist} {title}' try: albums = self.get_albums(query) @@ -212,12 +216,14 @@ class DiscogsPlugin(BeetsPlugin): self._log.debug(u'searching within album {0}', album_cur.album) track_list = self.get_tracks_from_album(album_cur) candidates += track_list + for candidate in candidates: + candidate.data_source = 'Discogs' return candidates def get_tracks_from_album(self, album_info): """Return a list of tracks in the release """ - if not album_info: + if not album_info: return [] result = [] From 2df41b9e166bd3c5e449c9c55d026519f7d6258e Mon Sep 17 00:00:00 2001 From: ghbrown <gabriel.h.brown@gmail.com> Date: Mon, 16 Jan 2023 18:43:26 -0600 Subject: [PATCH 22/34] Limit number of returned track candidates --- beetsplug/discogs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/beetsplug/discogs.py b/beetsplug/discogs.py index 435d67b40..990c1d786 100644 --- a/beetsplug/discogs.py +++ b/beetsplug/discogs.py @@ -218,7 +218,8 @@ class DiscogsPlugin(BeetsPlugin): candidates += track_list for candidate in candidates: candidate.data_source = 'Discogs' - return candidates + # first 10 results, don't overwhelm with options + return candidates[:10] def get_tracks_from_album(self, album_info): """Return a list of tracks in the release From 21bf37befab32e4a24900a0516a3807e7b899afa Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi <me@steffo.eu> Date: Fri, 20 Jan 2023 12:36:44 +0100 Subject: [PATCH 23/34] Use definition lists with the plugins' names So that they are easier to find when knowing the name of an enabled plugin --- docs/plugins/index.rst | 390 ++++++++++++++++++++++++++++------------- 1 file changed, 271 insertions(+), 119 deletions(-) diff --git a/docs/plugins/index.rst b/docs/plugins/index.rst index 4de531099..39925cf56 100644 --- a/docs/plugins/index.rst +++ b/docs/plugins/index.rst @@ -132,13 +132,21 @@ following to your configuration:: Autotagger Extensions --------------------- -* :doc:`chroma`: Use acoustic fingerprinting to identify audio files with - missing or incorrect metadata. -* :doc:`discogs`: Search for releases in the `Discogs`_ database. -* :doc:`spotify`: Search for releases in the `Spotify`_ database. -* :doc:`deezer`: Search for releases in the `Deezer`_ database. -* :doc:`fromfilename`: Guess metadata for untagged tracks from their - filenames. +:doc:`chroma <chroma>` + Use acoustic fingerprinting to identify audio files with + missing or incorrect metadata. + +:doc:`discogs <discogs>` + Search for releases in the `Discogs`_ database. + +:doc:`spotify <spotify>` + Search for releases in the `Spotify`_ database. + +:doc:`deezer <deezer>` + Search for releases in the `Deezer`_ database. + +:doc:`fromfilename <fromfilename>` + Guess metadata for untagged tracks from their filenames. .. _Discogs: https://www.discogs.com/ .. _Spotify: https://www.spotify.com @@ -147,30 +155,69 @@ Autotagger Extensions Metadata -------- -* :doc:`absubmit`: Analyse audio with the `streaming_extractor_music`_ program and submit the metadata to the AcousticBrainz server -* :doc:`acousticbrainz`: Fetch various AcousticBrainz metadata -* :doc:`bpm`: Measure tempo using keystrokes. -* :doc:`bpsync`: Fetch updated metadata from Beatport. -* :doc:`edit`: Edit metadata from a text editor. -* :doc:`embedart`: Embed album art images into files' metadata. -* :doc:`fetchart`: Fetch album cover art from various sources. -* :doc:`ftintitle`: Move "featured" artists from the artist field to the title - field. -* :doc:`keyfinder`: Use the `KeyFinder`_ program to detect the musical +:doc:`absubmit <absubmit>` + Analyse audio with the `streaming_extractor_music`_ program and submit the metadata to the AcousticBrainz server + +:doc:`acousticbrainz <acousticbrainz>` + Fetch various AcousticBrainz metadata + +:doc:`bpm <bpm>` + Measure tempo using keystrokes. + +:doc:`bpsync <bpsync>` + Fetch updated metadata from Beatport. + +:doc:`edit <edit>` + Edit metadata from a text editor. + +:doc:`embedart <embedart>` + Embed album art images into files' metadata. + +:doc:`fetchart <fetchart>` + Fetch album cover art from various sources. + +:doc:`ftintitle <ftintitle>` + Move "featured" artists from the artist field to the title + field. + +:doc:`keyfinder <keyfinder>` + Use the `KeyFinder`_ program to detect the musical key from the audio. -* :doc:`importadded`: Use file modification times for guessing the value for - the `added` field in the database. -* :doc:`lastgenre`: Fetch genres based on Last.fm tags. -* :doc:`lastimport`: Collect play counts from Last.fm. -* :doc:`lyrics`: Automatically fetch song lyrics. -* :doc:`mbsync`: Fetch updated metadata from MusicBrainz. -* :doc:`metasync`: Fetch metadata from local or remote sources -* :doc:`mpdstats`: Connect to `MPD`_ and update the beets library with play - statistics (last_played, play_count, skip_count, rating). -* :doc:`parentwork`: Fetch work titles and works they are part of. -* :doc:`replaygain`: Calculate volume normalization for players that support it. -* :doc:`scrub`: Clean extraneous metadata from music files. -* :doc:`zero`: Nullify fields by pattern or unconditionally. + +:doc:`importadded <importadded>` + Use file modification times for guessing the value for + the `added` field in the database. + +:doc:`lastgenre <lastgenre>` + Fetch genres based on Last.fm tags. + +:doc:`lastimport <lastimport>` + Collect play counts from Last.fm. + +:doc:`lyrics <lyrics>` + Automatically fetch song lyrics. + +:doc:`mbsync <mbsync>` + Fetch updated metadata from MusicBrainz. + +:doc:`metasync <metasync>` + Fetch metadata from local or remote sources + +:doc:`mpdstats <mpdstats>` + Connect to `MPD`_ and update the beets library with play + statistics (last_played, play_count, skip_count, rating). + +:doc:`parentwork <parentwork>` + Fetch work titles and works they are part of. + +:doc:`replaygain <replaygain>` + Calculate volume normalization for players that support it. + +:doc:`scrub <scrub>` + Clean extraneous metadata from music files. + +:doc:`zero <zero>` + Nullify fields by pattern or unconditionally. .. _KeyFinder: http://www.ibrahimshaath.co.uk/keyfinder/ .. _streaming_extractor_music: https://acousticbrainz.org/download @@ -178,37 +225,75 @@ Metadata Path Formats ------------ -* :doc:`albumtypes`: Format album type in path formats. -* :doc:`bucket`: Group your files into bucket directories that cover different - field values ranges. -* :doc:`inline`: Use Python snippets to customize path format strings. -* :doc:`rewrite`: Substitute values in path formats. -* :doc:`the`: Move patterns in path formats (i.e., move "a" and "the" to the - end). +:doc:`albumtypes <albumtypes>` + Format album type in path formats. + +:doc:`bucket <bucket>` + Group your files into bucket directories that cover different + field values ranges. + +:doc:`inline <inline>` + Use Python snippets to customize path format strings. + +:doc:`rewrite <rewrite>` + Substitute values in path formats. + +:doc:`the <the>` + Move patterns in path formats (i.e., move "a" and "the" to the + end). Interoperability ---------------- -* :doc:`aura`: A server implementation of the `AURA`_ specification. -* :doc:`badfiles`: Check audio file integrity. -* :doc:`embyupdate`: Automatically notifies `Emby`_ whenever the beets library changes. -* :doc:`fish`: Adds `Fish shell`_ tab autocompletion to ``beet`` commands. -* :doc:`importfeeds`: Keep track of imported files via ``.m3u`` playlist file(s) or symlinks. -* :doc:`ipfs`: Import libraries from friends and get albums from them via ipfs. -* :doc:`kodiupdate`: Automatically notifies `Kodi`_ whenever the beets library - changes. -* :doc:`mpdupdate`: Automatically notifies `MPD`_ whenever the beets library - changes. -* :doc:`play`: Play beets queries in your music player. -* :doc:`playlist`: Use M3U playlists to query the beets library. -* :doc:`plexupdate`: Automatically notifies `Plex`_ whenever the beets library - changes. -* :doc:`smartplaylist`: Generate smart playlists based on beets queries. -* :doc:`sonosupdate`: Automatically notifies `Sonos`_ whenever the beets library - changes. -* :doc:`thumbnails`: Get thumbnails with the cover art on your album folders. -* :doc:`subsonicupdate`: Automatically notifies `Subsonic`_ whenever the beets - library changes. +:doc:`aura <aura>` + A server implementation of the `AURA`_ specification. + +:doc:`badfiles <badfiles>` + Check audio file integrity. + +:doc:`embyupdate <embyupdate>` + Automatically notifies `Emby`_ whenever the beets library changes. + +:doc:`fish <fish>` + Adds `Fish shell`_ tab autocompletion to ``beet`` commands. + +:doc:`importfeeds <importfeeds>` + Keep track of imported files via ``.m3u`` playlist file(s) or symlinks. + +:doc:`ipfs <ipfs>` + Import libraries from friends and get albums from them via ipfs. + +:doc:`kodiupdate <kodiupdate>` + Automatically notifies `Kodi`_ whenever the beets library + changes. + +:doc:`mpdupdate <mpdupdate>` + Automatically notifies `MPD`_ whenever the beets library + changes. + +:doc:`play <play>` + Play beets queries in your music player. + +:doc:`playlist <playlist>` + Use M3U playlists to query the beets library. + +:doc:`plexupdate <plexupdate>` + Automatically notifies `Plex`_ whenever the beets library + changes. + +:doc:`smartplaylist <smartplaylist>` + Generate smart playlists based on beets queries. + +:doc:`sonosupdate <sonosupdate>` + Automatically notifies `Sonos`_ whenever the beets library + changes. + +:doc:`thumbnails <thumbnails>` + Get thumbnails with the cover art on your album folders. + +:doc:`subsonicupdate <subsonicupdate>` + Automatically notifies `Subsonic`_ whenever the beets + library changes. .. _AURA: https://auraspec.readthedocs.io @@ -222,28 +307,65 @@ Interoperability Miscellaneous ------------- -* :doc:`bareasc`: Search albums and tracks with bare ASCII string matching. -* :doc:`bpd`: A music player for your beets library that emulates `MPD`_ and is - compatible with `MPD clients`_. -* :doc:`convert`: Transcode music and embed album art while exporting to - a different directory. -* :doc:`duplicates`: List duplicate tracks or albums. -* :doc:`export`: Export data from queries to a format. -* :doc:`filefilter`: Automatically skip files during the import process based - on regular expressions. -* :doc:`fuzzy`: Search albums and tracks with fuzzy string matching. -* :doc:`hook`: Run a command when an event is emitted by beets. -* :doc:`ihate`: Automatically skip albums and tracks during the import process. -* :doc:`info`: Print music files' tags to the console. -* :doc:`loadext`: Load SQLite extensions. -* :doc:`mbcollection`: Maintain your MusicBrainz collection list. -* :doc:`mbsubmit`: Print an album's tracks in a MusicBrainz-friendly format. -* :doc:`missing`: List missing tracks. -* `mstream`_: A music streaming server + webapp that can be used alongside beets. -* :doc:`random`: Randomly choose albums and tracks from your library. -* :doc:`spotify`: Create Spotify playlists from the Beets library. -* :doc:`types`: Declare types for flexible attributes. -* :doc:`web`: An experimental Web-based GUI for beets. +:doc:`bareasc <bareasc>` + Search albums and tracks with bare ASCII string matching. + +:doc:`bpd <bpd>` + A music player for your beets library that emulates `MPD`_ and is + compatible with `MPD clients`_. + +:doc:`convert <convert>` + Transcode music and embed album art while exporting to + a different directory. + +:doc:`duplicates <duplicates>` + List duplicate tracks or albums. + +:doc:`export <export>` + Export data from queries to a format. + +:doc:`filefilter <filefilter>` + Automatically skip files during the import process based + on regular expressions. + +:doc:`fuzzy <fuzzy>` + Search albums and tracks with fuzzy string matching. + +:doc:`hook <hook>` + Run a command when an event is emitted by beets. + +:doc:`ihate <ihate>` + Automatically skip albums and tracks during the import process. + +:doc:`info <info>` + Print music files' tags to the console. + +:doc:`loadext <loadext>` + Load SQLite extensions. + +:doc:`mbcollection <mbcollection>` + Maintain your MusicBrainz collection list. + +:doc:`mbsubmit <mbsubmit>` + Print an album's tracks in a MusicBrainz-friendly format. + +:doc:`missing <missing>` + List missing tracks. + +`mstream`_ + A music streaming server + webapp that can be used alongside beets. + +:doc:`random <random>` + Randomly choose albums and tracks from your library. + +:doc:`spotify <spotify>` + Create Spotify playlists from the Beets library. + +:doc:`types <types>` + Declare types for flexible attributes. + +:doc:`web <web>` + An experimental Web-based GUI for beets. .. _MPD: https://www.musicpd.org/ .. _MPD clients: https://mpd.wikia.com/wiki/Clients @@ -270,76 +392,106 @@ line in your config file. Here are a few of the plugins written by the beets community: -* `beets-alternatives`_ manages external files. +`beets-alternatives`_ + Manages external files. -* `beet-amazon`_ adds Amazon.com as a tagger data source. +`beet-amazon`_ + Adds Amazon.com as a tagger data source. -* `beets-artistcountry`_ fetches the artist's country of origin from - MusicBrainz. +`beets-artistcountry`_ + Fetches the artist's country of origin from MusicBrainz. -* `beets-autofix`_ automates repetitive tasks to keep your library in order. +`beets-autofix`_ + Automates repetitive tasks to keep your library in order. -* `beets-audible`_ adds Audible as a tagger data source and provides - other features for managing audiobook collections. +`beets-audible`_ + Adds Audible as a tagger data source and provides + other features for managing audiobook collections. -* `beets-barcode`_ lets you scan or enter barcodes for physical media to - search for their metadata. +`beets-barcode`_ + Lets you scan or enter barcodes for physical media to + search for their metadata. -* `beetcamp`_ enables **bandcamp.com** autotagger with a fairly extensive amount of metadata. +`beetcamp`_ + Enables **bandcamp.com** autotagger with a fairly extensive amount of metadata. -* `beetstream`_ is server implementation of the `SubSonic API`_ specification, allowing you to stream your music on a multitude of clients. +`beetstream`_ + Is server implementation of the `SubSonic API`_ specification, allowing you to stream your music on a multitude of clients. -* `beets-bpmanalyser`_ analyses songs and calculates their tempo (BPM). +`beets-bpmanalyser`_ + Analyses songs and calculates their tempo (BPM). -* `beets-check`_ automatically checksums your files to detect corruption. +`beets-check`_ + Automatically checksums your files to detect corruption. -* `A cmus plugin`_ integrates with the `cmus`_ console music player. +`A cmus plugin`_ + Integrates with the `cmus`_ console music player. -* `beets-copyartifacts`_ helps bring non-music files along during import. +`beets-copyartifacts`_ + Helps bring non-music files along during import. -* `beets-describe`_ gives you the full picture of a single attribute of your library items. +`beets-describe`_ + Gives you the full picture of a single attribute of your library items. -* `drop2beets`_ automatically imports singles as soon as they are dropped in a - folder (using Linux's ``inotify``). You can also set a sub-folders - hierarchy to set flexible attributes by the way. +`drop2beets`_ + Automatically imports singles as soon as they are dropped in a + folder (using Linux's ``inotify``). You can also set a sub-folders + hierarchy to set flexible attributes by the way. -* `dsedivec`_ has two plugins: ``edit`` and ``moveall``. +`dsedivec`_ + Has two plugins: ``edit`` and ``moveall``. -* `beets-follow`_ lets you check for new albums from artists you like. +`beets-follow`_ + Lets you check for new albums from artists you like. -* `beetFs`_ is a FUSE filesystem for browsing the music in your beets library. - (Might be out of date.) +`beetFs`_ + Is a FUSE filesystem for browsing the music in your beets library. + (Might be out of date.) -* `beets-goingrunning`_ generates playlists to go with your running sessions. +`beets-goingrunning`_ + Generates playlists to go with your running sessions. -* `beets-ibroadcast`_ uploads tracks to the `iBroadcast`_ cloud service. +`beets-ibroadcast`_ + Uploads tracks to the `iBroadcast`_ cloud service. -* `beets-importreplace`_ lets you perform regex replacements on incoming - metadata. +`beets-importreplace`_ + Lets you perform regex replacements on incoming + metadata. -* `beets-mosaic`_ generates a montage of a mosaic from cover art. +`beets-mosaic`_ + Generates a montage of a mosaic from cover art. -* `beets-noimport`_ adds and removes directories from the incremental import skip list. +`beets-noimport`_ + Adds and removes directories from the incremental import skip list. -* `beets-originquery`_ augments MusicBrainz queries with locally-sourced data - to improve autotagger results. +`beets-originquery`_ + Augments MusicBrainz queries with locally-sourced data + to improve autotagger results. -* `beets-popularity`_ fetches popularity values from Deezer. +`beets-popularity`_ + Fetches popularity values from Deezer. -* `beets-setlister`_ generate playlists from the setlists of a given artist. +`beets-setlister`_ + Generate playlists from the setlists of a given artist. -* `beet-summarize`_ can compute lots of counts and statistics about your music - library. +`beet-summarize`_ + Can compute lots of counts and statistics about your music + library. -* `beets-usertag`_ lets you use keywords to tag and organize your music. +`beets-usertag`_ + Lets you use keywords to tag and organize your music. -* `whatlastgenre`_ fetches genres from various music sites. +`whatlastgenre`_ + Fetches genres from various music sites. -* `beets-xtractor`_ extracts low- and high-level musical information from your songs. +`beets-xtractor`_ + Extracts low- and high-level musical information from your songs. -* `beets-ydl`_ downloads audio from youtube-dl sources and import into beets. +`beets-ydl`_ + Downloads audio from youtube-dl sources and import into beets. -* `beets-yearfixer`_ attempts to fix all missing ``original_year`` and ``year`` fields. +`beets-yearfixer`_ + Attempts to fix all missing ``original_year`` and ``year`` fields. .. _beets-barcode: https://github.com/8h2a/beets-barcode .. _beetcamp: https://github.com/snejus/beetcamp From bc379850b21388d284523fc7ea298e9365080c84 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi <me@steffo.eu> Date: Fri, 20 Jan 2023 12:47:43 +0100 Subject: [PATCH 24/34] Improve formatting in the `plugins/discogs` page --- docs/plugins/discogs.rst | 49 ++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/docs/plugins/discogs.rst b/docs/plugins/discogs.rst index a9125e737..e7346c960 100644 --- a/docs/plugins/discogs.rst +++ b/docs/plugins/discogs.rst @@ -39,11 +39,19 @@ Authentication via Personal Access Token As an alternative to OAuth, you can get a token from Discogs and add it to your configuration. To get a personal access token (called a "user token" in the `python3-discogs-client`_ -documentation), login to `Discogs`_, and visit the -`Developer settings page -<https://www.discogs.com/settings/developers>`_. Press the ``Generate new -token`` button, and place the generated token in your configuration, as the -``user_token`` config option in the ``discogs`` section. +documentation): + +#. login to `Discogs`_; +#. visit the `Developer settings page <https://www.discogs.com/settings/developers>`_; +#. press the *Generate new token* button; +#. copy the generated token; +#. place it in your configuration in the ``discogs`` section as the ``user_token`` option: + + .. code-block:: yaml + + discogs: + user_token: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + Configuration ------------- @@ -54,22 +62,30 @@ There is one additional option in the ``discogs:`` section, ``index_tracks``. Index tracks (see the `Discogs guidelines <https://support.discogs.com/hc/en-us/articles/360005055373-Database-Guidelines-12-Tracklisting#Index_Tracks_And_Headings>`_), along with headers, mark divisions between distinct works on the same release -or within works. When ``index_tracks`` is enabled:: +or within works. When ``index_tracks`` is enabled: + +.. code-block:: yaml discogs: index_tracks: yes beets will incorporate the names of the divisions containing each track into -the imported track's title. For example, importing +the imported track's title. + +For example, importing `this album <https://www.discogs.com/Handel-Sutherland-Kirkby-Kwella-Nelson-Watkinson-Bowman-Rolfe-Johnson-Elliott-Partridge-Thomas-The-A/release/2026070>`_ -would result in track names like:: +would result in track names like: + +.. code-block:: text Messiah, Part I: No.1: Sinfony Messiah, Part II: No.22: Chorus- Behold The Lamb Of God Athalia, Act I, Scene I: Sinfonia -whereas with ``index_tracks`` disabled you'd get:: +whereas with ``index_tracks`` disabled you'd get: + +.. code-block:: text No.1: Sinfony No.22: Chorus- Behold The Lamb Of God @@ -79,11 +95,16 @@ This option is useful when importing classical music. Other configurations available under ``discogs:`` are: -- **append_style_genre**: Appends the Discogs style (if found) to the genre tag. This can be useful if you want more granular genres to categorize your music. - For example, a release in Discogs might have a genre of "Electronic" and a style of "Techno": enabling this setting would set the genre to be "Electronic, Techno" (assuming default separator of ``", "``) instead of just "Electronic". - Default: ``false`` -- **separator**: How to join multiple genre and style values from Discogs into a string. - Default: ``", "`` +``append_style_genre`` + Appends the Discogs style (if found) to the genre tag. This can be useful if you want more granular genres to categorize your music. + For example, a release in Discogs might have a genre of "Electronic" and a style of "Techno": enabling this setting would set the genre to be "Electronic, Techno" (assuming default separator of ``", "``) instead of just "Electronic". + + Default: ``false`` + +``separator`` + How to join multiple genre and style values from Discogs into a string. + + Default: ``", "`` Troubleshooting From 54aa0958822b932c8a508548e50a4ec231f3f001 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi <me@steffo.eu> Date: Fri, 20 Jan 2023 13:01:23 +0100 Subject: [PATCH 25/34] Specify the language of a code-block --- docs/plugins/discogs.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/plugins/discogs.rst b/docs/plugins/discogs.rst index e7346c960..febdf4ab0 100644 --- a/docs/plugins/discogs.rst +++ b/docs/plugins/discogs.rst @@ -11,9 +11,11 @@ Installation ------------ To use the ``discogs`` plugin, first enable it in your configuration (see -:ref:`using-plugins`). Then, install the `python3-discogs-client`_ library by typing:: +:ref:`using-plugins`). Then, install the `python3-discogs-client`_ library by typing: - pip install python3-discogs-client +.. code-block:: console + + $ pip install python3-discogs-client You will also need to register for a `Discogs`_ account, and provide authentication credentials via a personal access token or an OAuth2 From ca320527c8903e7740515f52eb09a5c6ad69897b Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi <me@steffo.eu> Date: Fri, 20 Jan 2023 13:10:15 +0100 Subject: [PATCH 26/34] Improve formatting in the `plugins/plexupdate` page --- docs/plugins/plexupdate.rst | 59 ++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/docs/plugins/plexupdate.rst b/docs/plugins/plexupdate.rst index b6a2bf920..9fbb8e7df 100644 --- a/docs/plugins/plexupdate.rst +++ b/docs/plugins/plexupdate.rst @@ -8,18 +8,22 @@ To use ``plexupdate`` plugin, enable it in your configuration (see :ref:`using-plugins`). Then, you'll probably want to configure the specifics of your Plex server. You can do that using an ``plex:`` section in your ``config.yaml``, -which looks like this:: +which looks like this: - plex: - host: localhost - port: 32400 - token: token +.. code-block:: yaml + + plex: + host: "localhost" + port: 32400 + token: "TOKEN" The ``token`` key is optional: you'll need to use it when in a Plex Home (see Plex's own `documentation about tokens`_). To use the ``plexupdate`` plugin you need to install the `requests`_ library with: - pip install requests +.. code-block:: console + + $ pip install beets[plexupdate] With that all in place, you'll see beets send the "update" command to your Plex server every time you change your beets library. @@ -33,15 +37,34 @@ Configuration The available options under the ``plex:`` section are: -- **host**: The Plex server name. - Default: ``localhost``. -- **port**: The Plex server port. - Default: 32400. -- **token**: The Plex Home token. - Default: Empty. -- **library_name**: The name of the Plex library to update. - Default: ``Music`` -- **secure**: Use secure connections to the Plex server. - Default: ``False`` -- **ignore_cert_errors**: Ignore TLS certificate errors when using secure connections. - Default: ``False`` +``host`` + The Plex server name. + + Default: ``"localhost"``. + +``port`` + + The Plex server port. + + Default: ``32400``. + +``token`` + The Plex Home token. + + Default: ``""``. + +``library_name`` + The name of the Plex library to update. + + Default: ``"Music"`` + +``secure`` + Use secure connections to the Plex server. + + Default: ``False`` + +``ignore_cert_errors`` + + Ignore TLS certificate errors when using secure connections. + + Default: ``False`` From cbb1b214089fed6ac4c1e3a25336ef5c229cace9 Mon Sep 17 00:00:00 2001 From: ghbrown <gabriel.h.brown@gmail.com> Date: Fri, 20 Jan 2023 21:07:15 -0600 Subject: [PATCH 27/34] Use tracks field in item_candidates; add more info to tracks of AlbumInfo --- beetsplug/discogs.py | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/beetsplug/discogs.py b/beetsplug/discogs.py index 990c1d786..44601e7e9 100644 --- a/beetsplug/discogs.py +++ b/beetsplug/discogs.py @@ -214,27 +214,11 @@ class DiscogsPlugin(BeetsPlugin): candidates = [] for album_cur in albums: self._log.debug(u'searching within album {0}', album_cur.album) - track_list = self.get_tracks_from_album(album_cur) - candidates += track_list + candidates += album_cur.tracks for candidate in candidates: candidate.data_source = 'Discogs' # first 10 results, don't overwhelm with options - return candidates[:10] - - def get_tracks_from_album(self, album_info): - """Return a list of tracks in the release - """ - if not album_info: - return [] - - result = [] - for track_info in album_info.tracks: - # 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 - result.append(track_info) - return result + return candidates[:10] @staticmethod def extract_release_id_regex(album_id): @@ -407,9 +391,13 @@ class DiscogsPlugin(BeetsPlugin): for track in tracks: track.media = media track.medium_total = mediums.count(track.medium) + # artist info will be identical for all tracks until #3353 fixed + track.artist = artist + track.artist_id = artist_id # Discogs does not have track IDs. Invent our own IDs as proposed # in #2336. track.track_id = str(album_id) + "-" + track.track_alt + track.data_url = data_url # Retrieve master release id (returns None if there isn't one). master_id = result.data.get('master_id') From a99eb773373dd6816e566c03fe409012d1cb436a Mon Sep 17 00:00:00 2001 From: ghbrown <gabriel.h.brown@gmail.com> Date: Fri, 20 Jan 2023 22:15:50 -0600 Subject: [PATCH 28/34] Improve where an how data added to tracks of album --- beetsplug/discogs.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/beetsplug/discogs.py b/beetsplug/discogs.py index 44601e7e9..103aa1107 100644 --- a/beetsplug/discogs.py +++ b/beetsplug/discogs.py @@ -215,8 +215,6 @@ class DiscogsPlugin(BeetsPlugin): for album_cur in albums: self._log.debug(u'searching within album {0}', album_cur.album) candidates += album_cur.tracks - for candidate in candidates: - candidate.data_source = 'Discogs' # first 10 results, don't overwhelm with options return candidates[:10] @@ -391,13 +389,15 @@ class DiscogsPlugin(BeetsPlugin): for track in tracks: track.media = media track.medium_total = mediums.count(track.medium) - # artist info will be identical for all tracks until #3353 fixed - track.artist = artist - track.artist_id = artist_id + if not track.artist: # get_track_info often fails to find artist + track.artist = artist + if not track.artist_id: + track.artist_id = artist_id # Discogs does not have track IDs. Invent our own IDs as proposed # in #2336. track.track_id = str(album_id) + "-" + track.track_alt track.data_url = data_url + track.data_source = 'Discogs' # Retrieve master release id (returns None if there isn't one). master_id = result.data.get('master_id') From 47fe387de1163a78dbb21e88acd83649f1af9402 Mon Sep 17 00:00:00 2001 From: ghbrown <gabriel.h.brown@gmail.com> Date: Sat, 21 Jan 2023 20:56:44 -0600 Subject: [PATCH 29/34] Docs and changelog --- docs/changelog.rst | 1 + docs/plugins/discogs.rst | 8 +++----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 66802d203..8311bffa9 100755 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -37,6 +37,7 @@ New features: ``=~``. :bug:`4251` * :doc:`/plugins/discogs`: Permit appending style to genre +* :doc:`plugins/discogs`: Implement item_candidates for matching singletons * :doc:`/plugins/convert`: Add a new `auto_keep` option that automatically converts files but keeps the *originals* in the library. :bug:`1840` :bug:`4302` diff --git a/docs/plugins/discogs.rst b/docs/plugins/discogs.rst index a9125e737..e424a4b04 100644 --- a/docs/plugins/discogs.rst +++ b/docs/plugins/discogs.rst @@ -2,8 +2,7 @@ Discogs Plugin ============== The ``discogs`` plugin extends the autotagger's search capabilities to -include matches from the `Discogs`_ database when importing albums. -(The plugin does not yet support matching singleton tracks.) +include matches from the `Discogs`_ database. .. _Discogs: https://discogs.com @@ -100,8 +99,7 @@ Here are two things you can try: * Make sure that your system clock is accurate. The Discogs servers can reject your request if your clock is too out of sync. -The plugin can only match albums, so no Discogs matches will be -reported when importing singletons using ``-s``. One possible -workaround is to use the ``--group-albums`` option. +Support for matching singleton tracks using ``-s`` is in progress. +If this is not working well, try the ``--group-albums`` option in album import mode. .. _python3-discogs-client: https://github.com/joalla/discogs_client From 7f3f522973364c176000155ec8621efa34a0ba68 Mon Sep 17 00:00:00 2001 From: Jordyn <onlinecloud1@gmail.com> Date: Sat, 21 Jan 2023 22:08:03 -0600 Subject: [PATCH 30/34] config.rst: Remove extraneous for --- docs/reference/config.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/config.rst b/docs/reference/config.rst index e59937dc3..b6fa8fea6 100644 --- a/docs/reference/config.rst +++ b/docs/reference/config.rst @@ -135,7 +135,7 @@ unexpected behavior on all popular platforms:: These substitutions remove forward and back slashes, leading dots, and control characters—all of which is a good idea on any OS. The fourth line -removes the Windows "reserved characters" (useful even on Unix for for +removes the Windows "reserved characters" (useful even on Unix for compatibility with Windows-influenced network filesystems like Samba). Trailing dots and trailing whitespace, which can cause problems on Windows clients, are also removed. From 566579b5f8e6b75aace72b1fe1af02a81e410f96 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi <me@steffo.eu> Date: Sun, 22 Jan 2023 14:37:39 +0100 Subject: [PATCH 31/34] Revert the configuration section back to how it was before --- docs/plugins/plexupdate.rst | 43 +++++++++++-------------------------- 1 file changed, 12 insertions(+), 31 deletions(-) diff --git a/docs/plugins/plexupdate.rst b/docs/plugins/plexupdate.rst index 9fbb8e7df..aaeb28e5b 100644 --- a/docs/plugins/plexupdate.rst +++ b/docs/plugins/plexupdate.rst @@ -37,34 +37,15 @@ Configuration The available options under the ``plex:`` section are: -``host`` - The Plex server name. - - Default: ``"localhost"``. - -``port`` - - The Plex server port. - - Default: ``32400``. - -``token`` - The Plex Home token. - - Default: ``""``. - -``library_name`` - The name of the Plex library to update. - - Default: ``"Music"`` - -``secure`` - Use secure connections to the Plex server. - - Default: ``False`` - -``ignore_cert_errors`` - - Ignore TLS certificate errors when using secure connections. - - Default: ``False`` +- **host**: The Plex server name. + Default: ``localhost``. +- **port**: The Plex server port. + Default: 32400. +- **token**: The Plex Home token. + Default: Empty. +- **library_name**: The name of the Plex library to update. + Default: ``Music`` +- **secure**: Use secure connections to the Plex server. + Default: ``False`` +- **ignore_cert_errors**: Ignore TLS certificate errors when using secure connections. + Default: ``False`` \ No newline at end of file From 2b600fa15186e1ac934389b23cdcbe62f22d6fa2 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi <me@steffo.eu> Date: Sun, 22 Jan 2023 14:56:23 +0100 Subject: [PATCH 32/34] Revert the configuration section back to how it was before --- docs/plugins/discogs.rst | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/docs/plugins/discogs.rst b/docs/plugins/discogs.rst index febdf4ab0..17a1645bb 100644 --- a/docs/plugins/discogs.rst +++ b/docs/plugins/discogs.rst @@ -97,16 +97,11 @@ This option is useful when importing classical music. Other configurations available under ``discogs:`` are: -``append_style_genre`` - Appends the Discogs style (if found) to the genre tag. This can be useful if you want more granular genres to categorize your music. - For example, a release in Discogs might have a genre of "Electronic" and a style of "Techno": enabling this setting would set the genre to be "Electronic, Techno" (assuming default separator of ``", "``) instead of just "Electronic". - - Default: ``false`` - -``separator`` - How to join multiple genre and style values from Discogs into a string. - - Default: ``", "`` +- **append_style_genre**: Appends the Discogs style (if found) to the genre tag. This can be useful if you want more granular genres to categorize your music. + For example, a release in Discogs might have a genre of "Electronic" and a style of "Techno": enabling this setting would set the genre to be "Electronic, Techno" (assuming default separator of ``", "``) instead of just "Electronic". + Default: ``False`` +- **separator**: How to join multiple genre and style values from Discogs into a string. + Default: ``", "`` Troubleshooting From e76fe012128e9ab389aad5cdfd70c3e7240b88ff Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi <me@steffo.eu> Date: Sun, 22 Jan 2023 15:02:18 +0100 Subject: [PATCH 33/34] Add missing space --- docs/plugins/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/index.rst b/docs/plugins/index.rst index 39925cf56..8404ce716 100644 --- a/docs/plugins/index.rst +++ b/docs/plugins/index.rst @@ -182,7 +182,7 @@ Metadata :doc:`keyfinder <keyfinder>` Use the `KeyFinder`_ program to detect the musical - key from the audio. + key from the audio. :doc:`importadded <importadded>` Use file modification times for guessing the value for From 429dfb3e7ad2793663e6a6a56dacc401698cc833 Mon Sep 17 00:00:00 2001 From: ghbrown <gabriel.h.brown@gmail.com> Date: Sat, 28 Jan 2023 18:11:22 -0600 Subject: [PATCH 34/34] Fix docs phrasing; fix changelog formatting --- docs/changelog.rst | 4 ++-- docs/plugins/discogs.rst | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 8311bffa9..9587788fa 100755 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -36,8 +36,8 @@ New features: * Add :ref:`exact match <exact-match>` queries, using the prefixes ``=`` and ``=~``. :bug:`4251` -* :doc:`/plugins/discogs`: Permit appending style to genre -* :doc:`plugins/discogs`: Implement item_candidates for matching singletons +* :doc:`/plugins/discogs`: Permit appending style to genre. +* :doc:`plugins/discogs`: Implement item_candidates for matching singletons. * :doc:`/plugins/convert`: Add a new `auto_keep` option that automatically converts files but keeps the *originals* in the library. :bug:`1840` :bug:`4302` diff --git a/docs/plugins/discogs.rst b/docs/plugins/discogs.rst index e424a4b04..1f0628072 100644 --- a/docs/plugins/discogs.rst +++ b/docs/plugins/discogs.rst @@ -99,7 +99,7 @@ Here are two things you can try: * Make sure that your system clock is accurate. The Discogs servers can reject your request if your clock is too out of sync. -Support for matching singleton tracks using ``-s`` is in progress. -If this is not working well, try the ``--group-albums`` option in album import mode. +Matching tracks by Discogs ID is not yet supported. The ``--group-albums`` +option in album import mode provides an alternative to singleton mode for autotagging tracks that are not in album-related folders. .. _python3-discogs-client: https://github.com/joalla/discogs_client