- introduce the in-process metadata engine and fresh provider adapters - normalize external identity through source_id while keeping id as a compatibility alias - route metadata callers through the new seam and add regression coverage
100 lines
4 KiB
Python
100 lines
4 KiB
Python
"""Deezer metadata adapter."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, Optional
|
|
|
|
from core.metadata.providers.base import BaseMetadataAdapter
|
|
|
|
|
|
class DeezerMetadataAdapter(BaseMetadataAdapter):
|
|
provider_name = "deezer"
|
|
min_api_interval = 1.0
|
|
timeout = 15.0
|
|
|
|
BASE_URL = "https://api.deezer.com"
|
|
|
|
def __init__(self) -> None:
|
|
super().__init__()
|
|
self._access_token: Optional[str] = None
|
|
|
|
def reload_config(self) -> None:
|
|
self._access_token = None
|
|
|
|
def _read_access_token(self) -> Optional[str]:
|
|
try:
|
|
from config.settings import config_manager
|
|
|
|
token = config_manager.get("deezer.access_token", None)
|
|
except Exception:
|
|
token = None
|
|
return (str(token).strip() or None) if token else None
|
|
|
|
def _request_api(self, endpoint: str, params: Optional[dict[str, Any]] = None, timeout: int = 15) -> Optional[dict[str, Any]]:
|
|
url = f"{self.BASE_URL}/{endpoint.lstrip('/')}"
|
|
params = dict(params or {})
|
|
token = self._read_access_token()
|
|
if token and "access_token" not in params:
|
|
params["access_token"] = token
|
|
payload = self._request_json("GET", url, params=params, timeout=timeout)
|
|
return payload if isinstance(payload, dict) else None
|
|
|
|
def is_authenticated(self) -> bool:
|
|
return True
|
|
|
|
def search_tracks_raw(self, query: str, limit: int = 20) -> list[dict[str, Any]]:
|
|
payload = self._request_api("search/track", {"q": query, "limit": min(limit, 100)})
|
|
return list(payload.get("data") or []) if payload else []
|
|
|
|
def search_artists_raw(self, query: str, limit: int = 20) -> list[dict[str, Any]]:
|
|
payload = self._request_api("search/artist", {"q": query, "limit": min(limit, 100)})
|
|
return list(payload.get("data") or []) if payload else []
|
|
|
|
def search_albums_raw(self, query: str, limit: int = 20) -> list[dict[str, Any]]:
|
|
payload = self._request_api("search/album", {"q": query, "limit": min(limit, 100)})
|
|
return list(payload.get("data") or []) if payload else []
|
|
|
|
def get_track_raw(self, track_id: str) -> Optional[dict[str, Any]]:
|
|
return self._request_api(f"track/{track_id}")
|
|
|
|
def get_album_raw(self, album_id: str) -> Optional[dict[str, Any]]:
|
|
return self._request_api(f"album/{album_id}")
|
|
|
|
def get_artist_raw(self, artist_id: str) -> Optional[dict[str, Any]]:
|
|
return self._request_api(f"artist/{artist_id}")
|
|
|
|
def get_album_tracks_raw(self, album_id: str, limit: int = 500, max_pages: int = 0) -> Optional[dict[str, Any]]:
|
|
data = self._request_api(f"album/{album_id}/tracks", {"limit": min(limit, 500)})
|
|
if not data or not data.get("data"):
|
|
album_data = self._request_api(f"album/{album_id}")
|
|
if album_data and album_data.get("tracks") and album_data["tracks"].get("data"):
|
|
data = album_data["tracks"]
|
|
else:
|
|
return None
|
|
|
|
album_info = self._request_api(f"album/{album_id}") or {}
|
|
items = list(data.get("data") or [])
|
|
return {
|
|
"items": items,
|
|
"total": len(items),
|
|
"limit": len(items),
|
|
"next": None,
|
|
"album": album_info,
|
|
}
|
|
|
|
def get_artist_albums_raw(self, artist_id: str, album_type: str = "album,single", limit: int = 200, max_pages: int = 0) -> list[dict[str, Any]]:
|
|
albums: list[dict[str, Any]] = []
|
|
offset = 0
|
|
page_size = 100
|
|
while offset < limit:
|
|
fetch_limit = min(page_size, limit - offset)
|
|
data = self._request_api(f"artist/{artist_id}/albums", {"limit": fetch_limit, "index": offset})
|
|
if not data or not data.get("data"):
|
|
break
|
|
albums.extend(list(data.get("data") or []))
|
|
if len(data.get("data") or []) < fetch_limit:
|
|
break
|
|
offset += len(data.get("data") or [])
|
|
if max_pages and offset >= max_pages * page_size:
|
|
break
|
|
return albums[:limit]
|