Add discogs.extra_tags and updated documentation

This commit is contained in:
jdoe29103 2026-03-09 16:59:20 -04:00 committed by Šarūnas Nejus
parent 6af84bcfb6
commit 0d8d3bfadf
3 changed files with 113 additions and 3 deletions

View file

@ -25,7 +25,7 @@ import re
import socket
import time
import traceback
from functools import cache
from functools import cache, cached_property
from string import ascii_lowercase
from typing import TYPE_CHECKING
@ -36,7 +36,7 @@ from requests.exceptions import ConnectionError
import beets
import beets.ui
from beets import config
from beets import config, util
from beets.autotag.distance import string_dist
from beets.autotag.hooks import AlbumInfo, TrackInfo
from beets.metadata_plugins import IDResponse, SearchApiMetadataSourcePlugin
@ -80,6 +80,15 @@ TRACK_INDEX_RE = re.compile(
re.VERBOSE,
)
FIELDS_TO_DISCOGS_KEYS = {
"barcode": "barcode",
"catalognum": "catno",
"country": "country",
"label": "label",
"media": "format",
"year": "year",
}
class DiscogsPlugin(SearchApiMetadataSourcePlugin[IDResponse]):
def __init__(self):
@ -95,6 +104,7 @@ class DiscogsPlugin(SearchApiMetadataSourcePlugin[IDResponse]):
"append_style_genre": False,
"strip_disambiguation": True,
"featured_string": "Feat.",
"extra_tags": [],
"anv": {
"artist_credit": True,
"artist": False,
@ -107,6 +117,25 @@ class DiscogsPlugin(SearchApiMetadataSourcePlugin[IDResponse]):
self.config["user_token"].redact = True
self.setup()
@cached_property
def extra_discogs_field_by_tag(self) -> dict[str, str]:
"""Map configured extra tags to Discogs API search parameters.
Process user configuration to determine which additional Discogs
fields should be included in search queries.
"""
field_by_tag = {
tag: FIELDS_TO_DISCOGS_KEYS[tag]
for tag in self.config["extra_tags"].as_str_seq()
if tag in FIELDS_TO_DISCOGS_KEYS
}
if field_by_tag:
self._log.debug(
"Discogs additional search filters from tags: {}", field_by_tag
)
return field_by_tag
def setup(self, session=None) -> None:
"""Create the `discogs_client` field. Authenticate if necessary."""
c_key = self.config["apikey"].as_str()
@ -256,7 +285,26 @@ class DiscogsPlugin(SearchApiMetadataSourcePlugin[IDResponse]):
# can also negate an otherwise positive result.
query = re.sub(r"(?i)\b(CD|disc|vinyl)\s*\d+", "", query)
return query, {"type": "release"}
filters: dict[str, str] = {"type": "release"}
for tag, api_field in self.extra_discogs_field_by_tag.items():
# The Discogs search API does not provide direct equivalents for
# MusicBrainz "alias" or "tracks" search fields, so we ignore
# those tags if configured.
if tag in {"alias", "tracks"}:
continue
most_common, _count = util.plurality(item.get(tag) for item in items)
if most_common is None:
continue
value = str(most_common)
if tag == "catalognum":
value = value.replace(" ", "")
filters[api_field] = value
return query, filters
def get_search_response(self, params: SearchParams) -> Sequence[IDResponse]:
"""Search Discogs releases and return raw result mappings with IDs."""

View file

@ -80,6 +80,7 @@ Default
separator: ', '
strip_disambiguation: yes
featured_string: Feat.
extra_tags: []
anv:
artist_credit: yes
artist: no
@ -144,6 +145,31 @@ Default
Configure the string used for noting featured artists. Useful if you prefer ``Featuring`` or ``ft.``.
.. conf:: extra_tags
:default: []
By default, beets will only use the artist and album information to query Discogs. Additional tags to be queried can be supplied with the
``extra_tags`` setting.
This setting should improve the autotagger results if the metadata with the
given tags match the metadata returned by Discogs.
Tags supported by this setting:
* ``barcode``
* ``catalognum``
* ``country``
* ``label``
* ``media``
* ``year``
Example:
.. code-block:: yaml
discogs:
extra_tags: [barcode, catalognum, country, label, media, year]
.. conf:: anv
This configuration option is dedicated to handling Artist Name

View file

@ -19,6 +19,7 @@ from unittest.mock import Mock, patch
import pytest
from beets import config
from beets.library import Item
from beets.test._common import Bag
from beets.test.helper import BeetsTestCase, capture_log
from beetsplug.discogs import ArtistState, DiscogsPlugin
@ -357,6 +358,41 @@ class DGAlbumInfoTest(BeetsTestCase):
assert d is None
assert "Release does not contain the required fields" in logs[0]
@patch("beetsplug.discogs.DiscogsPlugin.setup", Mock())
class DGSearchQueryTest(BeetsTestCase):
def test_default_search_filters_without_extra_tags(self):
"""Discogs search uses only the type filter when no extra_tags are set."""
plugin = DiscogsPlugin()
items = [Item()]
query, filters = plugin.get_search_query_with_filters(
"album", items, "Artist", "Album", False
)
assert "Album" in query
assert filters == {"type": "release"}
def test_extra_tags_populate_discogs_filters(self):
"""Configured extra_tags should populate Discogs search filters."""
config["discogs"]["extra_tags"] = ["label", "catalognum"]
plugin = DiscogsPlugin()
items = [
Item(catalognum="ABC 123", label="abc"),
Item(catalognum="ABC 123", label="abc"),
Item(catalognum="ABC 123", label="def"),
]
_query, filters = plugin.get_search_query_with_filters(
"album", items, "Artist", "Album", False
)
assert filters["type"] == "release"
assert filters["label"] == "abc"
# Catalog number should have whitespace removed.
assert filters["catno"] == "ABC123"
def test_default_genre_style_settings(self):
"""Test genre default settings, genres to genre, styles to style"""
release = self._make_release_from_positions(["1", "2"])