diff --git a/beetsplug/discogs/__init__.py b/beetsplug/discogs/__init__.py index 23e2267df..dc88e0f14 100644 --- a/beetsplug/discogs/__init__.py +++ b/beetsplug/discogs/__init__.py @@ -18,9 +18,11 @@ python3-discogs-client library. from __future__ import annotations +import http.client import json import os import re +import socket import time import traceback from functools import cache @@ -30,6 +32,7 @@ from typing import TYPE_CHECKING import confuse from discogs_client import Client, Master, Release from discogs_client.exceptions import DiscogsAPIError +from requests.exceptions import ConnectionError import beets import beets.ui @@ -38,8 +41,7 @@ from beets.autotag.distance import string_dist from beets.autotag.hooks import AlbumInfo, TrackInfo from beets.metadata_plugins import MetadataSourcePlugin -from .states import ArtistState, TracklistState -from .utils import CONNECTION_ERRORS, DISAMBIGUATION_RE, TRACK_INDEX_RE +from .states import DISAMBIGUATION_RE, ArtistState, TracklistState if TYPE_CHECKING: from collections.abc import Callable, Iterable, Sequence @@ -53,6 +55,31 @@ API_KEY = "rAzVUQYRaoFjeBjyWuWZ" API_SECRET = "plxtUTqoCzwxZpqdPysCwGuBSmZNdZVy" +# Exceptions that discogs_client should really handle but does not. +CONNECTION_ERRORS = ( + ConnectionError, + socket.error, + http.client.HTTPException, + ValueError, # JSON decoding raises a ValueError. + DiscogsAPIError, +) + +TRACK_INDEX_RE = re.compile( + r""" + (.*?) # medium: everything before medium_index. + (\d*?) # medium_index: a number at the end of + # `position`, except if followed by a subtrack index. + # subtrack_index: can only be matched if medium + # or medium_index have been matched, and can be + ( + (?<=\w)\.[\w]+ # a dot followed by a string (A.1, 2.A) + | (?<=\d)[A-Z]+ # a string that follows a number (1A, B2a) + )? + """, + re.VERBOSE, +) + + class DiscogsPlugin(MetadataSourcePlugin): def __init__(self): super().__init__() @@ -304,8 +331,8 @@ class DiscogsPlugin(MetadataSourcePlugin): artist_data = [a.data for a in result.artists] # Information for the album artist - albumartist = ArtistState.from_plugin( - self, artist_data, for_album_artist=True + albumartist = ArtistState.from_config( + self.config, artist_data, for_album_artist=True ) album = re.sub(r" +", " ", result.title) @@ -315,7 +342,8 @@ class DiscogsPlugin(MetadataSourcePlugin): # information and leave us with skeleton `Artist` objects that will # each make an API call just to get the same data back. tracks = self.get_tracks( - result.data["tracklist"], ArtistState.from_plugin(self, artist_data) + result.data["tracklist"], + ArtistState.from_config(self.config, artist_data), ) # Extract information for the optional AlbumInfo fields, if possible. @@ -611,8 +639,8 @@ class DiscogsPlugin(MetadataSourcePlugin): length = self.get_track_length(track["duration"]) # If artists are found on the track, we will use those instead - artistinfo = ArtistState.from_plugin( - self, + artistinfo = ArtistState.from_config( + self.config, [ *(track.get("artists") or albumartistinfo.raw_artists), *track.get("extraartists", []), diff --git a/beetsplug/discogs/states.py b/beetsplug/discogs/states.py index 265c92c4e..4f59404ca 100644 --- a/beetsplug/discogs/states.py +++ b/beetsplug/discogs/states.py @@ -16,6 +16,7 @@ from __future__ import annotations +import re from dataclasses import asdict, dataclass, field from functools import cached_property from typing import TYPE_CHECKING, NamedTuple @@ -23,13 +24,16 @@ from typing import TYPE_CHECKING, NamedTuple from beets import config from .types import Artist, ArtistInfo, Track, TracklistInfo -from .utils import DISAMBIGUATION_RE if TYPE_CHECKING: + from confuse import ConfigView + from beets.autotag.hooks import TrackInfo from . import DiscogsPlugin +DISAMBIGUATION_RE = re.compile(r" \(\d+\)") + @dataclass class ArtistState: @@ -170,20 +174,20 @@ class ArtistState: return artist @classmethod - def from_plugin( + def from_config( cls, - plugin: DiscogsPlugin, + config: ConfigView, artists: list[Artist], for_album_artist: bool = False, ) -> ArtistState: return cls( artists, - plugin.config["anv"][ - "album_artist" if for_album_artist else "artist" - ].get(bool), - plugin.config["anv"]["artist_credit"].get(bool), - plugin.config["featured_string"].as_str(), - plugin.config["strip_disambiguation"].get(bool), + config["anv"]["album_artist" if for_album_artist else "artist"].get( + bool + ), + config["anv"]["artist_credit"].get(bool), + config["featured_string"].as_str(), + config["strip_disambiguation"].get(bool), ) diff --git a/beetsplug/discogs/utils.py b/beetsplug/discogs/utils.py deleted file mode 100644 index fdb1f0058..000000000 --- a/beetsplug/discogs/utils.py +++ /dev/null @@ -1,47 +0,0 @@ -# This file is part of beets. -# Copyright 2016, Adrian Sampson. -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -"""Utility resources for the Discogs plugin.""" - -import http.client -import re -import socket - -from discogs_client.exceptions import DiscogsAPIError -from requests.exceptions import ConnectionError - -# Exceptions that discogs_client should really handle but does not. -CONNECTION_ERRORS = ( - ConnectionError, - socket.error, - http.client.HTTPException, - ValueError, # JSON decoding raises a ValueError. - DiscogsAPIError, -) - -DISAMBIGUATION_RE = re.compile(r" \(\d+\)") - -TRACK_INDEX_RE = re.compile( - r""" - (.*?) # medium: everything before medium_index. - (\d*?) # medium_index: a number at the end of - # `position`, except if followed by a subtrack index. - # subtrack_index: can only be matched if medium - # or medium_index have been matched, and can be - ( - (?<=\w)\.[\w]+ # a dot followed by a string (A.1, 2.A) - | (?<=\d)[A-Z]+ # a string that follows a number (1A, B2a) - )? - """, - re.VERBOSE, -) diff --git a/test/plugins/test_discogs.py b/test/plugins/test_discogs.py index 66cbe9371..15d47db6c 100644 --- a/test/plugins/test_discogs.py +++ b/test/plugins/test_discogs.py @@ -674,7 +674,7 @@ def test_parse_featured_artists(track, expected_artist, expected_artists): """Tests the plugins ability to parse a featured artist. Ignores artists that are not listed as featured.""" plugin = DiscogsPlugin() - artistinfo = ArtistState.from_plugin(plugin, [_artist("ARTIST")]) + artistinfo = ArtistState.from_config(plugin.config, [_artist("ARTIST")]) t, _, _ = plugin.get_track_info(track, 1, 1, artistinfo) assert t.artist == expected_artist assert t.artists == expected_artists @@ -724,7 +724,7 @@ def test_get_media_and_albumtype(formats, expected_media, expected_albumtype): def test_va_buildartistinfo(given_artists, expected_info, config_va_name): config["va_name"] = config_va_name assert ( - ArtistState.from_plugin(DiscogsPlugin(), given_artists).info + ArtistState.from_config(DiscogsPlugin().config, given_artists).info == expected_info )