Opt in deezer plugin.

This commit is contained in:
Sebastian Mohr 2025-07-07 13:56:00 +02:00
parent a770cfb669
commit 6f623ee7b0

View file

@ -26,16 +26,19 @@ import unidecode
from beets import ui from beets import ui
from beets.autotag import AlbumInfo, TrackInfo from beets.autotag import AlbumInfo, TrackInfo
from beets.dbcore import types from beets.dbcore import types
from beets.plugins import BeetsPlugin, MetadataSourcePlugin, Response from beets.metadata_plugins import (
IDResponse,
SearchApiMetadataSourcePlugin,
SearchFilter,
)
if TYPE_CHECKING: if TYPE_CHECKING:
from beets.library import Item, Library from beets.library import Item, Library
from beetsplug._typing import JSONDict
from ._typing import JSONDict
class DeezerPlugin(MetadataSourcePlugin[Response], BeetsPlugin): class DeezerPlugin(SearchApiMetadataSourcePlugin[IDResponse]):
data_source = "Deezer"
item_types = { item_types = {
"deezer_track_rank": types.INTEGER, "deezer_track_rank": types.INTEGER,
"deezer_track_id": types.INTEGER, "deezer_track_id": types.INTEGER,
@ -63,7 +66,7 @@ class DeezerPlugin(MetadataSourcePlugin[Response], BeetsPlugin):
def album_for_id(self, album_id: str) -> AlbumInfo | None: def album_for_id(self, album_id: str) -> AlbumInfo | None:
"""Fetch an album by its Deezer ID or URL.""" """Fetch an album by its Deezer ID or URL."""
if not (deezer_id := self._get_id(album_id)): if not (deezer_id := self.extract_release_id(album_id)):
return None return None
album_url = f"{self.album_url}{deezer_id}" album_url = f"{self.album_url}{deezer_id}"
@ -145,11 +148,14 @@ class DeezerPlugin(MetadataSourcePlugin[Response], BeetsPlugin):
) )
def track_for_id(self, track_id: str) -> None | TrackInfo: def track_for_id(self, track_id: str) -> None | TrackInfo:
"""Fetch a track by its Deezer ID or URL. """Fetch a track by its Deezer ID or URL and return a
TrackInfo object or None if the track is not found.
:param track_id: (Optional) Deezer ID or URL for the track. Either
``track_id`` or ``track_data`` must be provided.
Returns a TrackInfo object or None if the track is not found.
""" """
if not (deezer_id := self._get_id(track_id)): if not (deezer_id := self.extract_release_id(track_id)):
self._log.debug("Invalid Deezer track_id: {}", track_id) self._log.debug("Invalid Deezer track_id: {}", track_id)
return None return None
@ -162,11 +168,13 @@ class DeezerPlugin(MetadataSourcePlugin[Response], BeetsPlugin):
# Get album's tracks to set `track.index` (position on the entire # Get album's tracks to set `track.index` (position on the entire
# release) and `track.medium_total` (total number of tracks on # release) and `track.medium_total` (total number of tracks on
# the track's disc). # the track's disc).
album_tracks_obj = self.fetch_data( if not (
self.album_url + str(track_data["album"]["id"]) + "/tracks" album_tracks_obj := self.fetch_data(
) self.album_url + str(track_data["album"]["id"]) + "/tracks"
if album_tracks_obj is None: )
):
return None return None
try: try:
album_tracks_data = album_tracks_obj["data"] album_tracks_data = album_tracks_obj["data"]
except KeyError: except KeyError:
@ -187,7 +195,6 @@ class DeezerPlugin(MetadataSourcePlugin[Response], BeetsPlugin):
"""Convert a Deezer track object dict to a TrackInfo object. """Convert a Deezer track object dict to a TrackInfo object.
:param track_data: Deezer Track object dict :param track_data: Deezer Track object dict
:return: TrackInfo object for track
""" """
artist, artist_id = self.get_artist( artist, artist_id = self.get_artist(
track_data.get("contributors", [track_data["artist"]]) track_data.get("contributors", [track_data["artist"]])
@ -211,7 +218,7 @@ class DeezerPlugin(MetadataSourcePlugin[Response], BeetsPlugin):
@staticmethod @staticmethod
def _construct_search_query( def _construct_search_query(
filters: dict[str, str], keywords: str = "" filters: SearchFilter, keywords: str = ""
) -> str: ) -> str:
"""Construct a query string with the specified filters and keywords to """Construct a query string with the specified filters and keywords to
be provided to the Deezer Search API be provided to the Deezer Search API
@ -242,14 +249,14 @@ class DeezerPlugin(MetadataSourcePlugin[Response], BeetsPlugin):
"radio", "radio",
"user", "user",
], ],
filters: dict[str, str], filters: SearchFilter,
keywords="", keywords="",
) -> Sequence[Response]: ) -> Sequence[IDResponse]:
"""Query the Deezer Search API for the specified ``keywords``, applying """Query the Deezer Search API for the specified ``keywords``, applying
the provided ``filters``. the provided ``filters``.
:param query_type: The Deezer Search API method to use. :param filters: Field filters to apply.
:param keywords: (Optional) Query keywords to use. :param keywords: Query keywords to use.
:return: JSON data for the class:`Response <Response>` object or None :return: JSON data for the class:`Response <Response>` object or None
if no search results are returned. if no search results are returned.
""" """
@ -269,7 +276,7 @@ class DeezerPlugin(MetadataSourcePlugin[Response], BeetsPlugin):
e, e,
) )
return () return ()
response_data = response.json().get("data", []) response_data: Sequence[IDResponse] = response.json().get("data", [])
self._log.debug( self._log.debug(
"Found {} result(s) from {} for '{}'", "Found {} result(s) from {} for '{}'",
len(response_data), len(response_data),