from typing import Any, ClassVar from pydantic import SecretStr from resonance_engine.adapters.spotify import SpotifyClient from resonance_engine.dsp.backends.base import DSPBackend from resonance_engine.dsp.enums import DSPId, DSPResource from resonance_engine.dsp.types import TokenResult from resonance_engine.utils.functional import exclude class SpotifyDSPBackend(DSPBackend): dsp_id = DSPId.spotify required_scopes: ClassVar[dict[DSPResource, str]] = { DSPResource.top_artists: "user-top-read", DSPResource.top_tracks: "user-top-read", DSPResource.recently_played: "user-read-recently-played", DSPResource.playlists: "playlist-read-private", DSPResource.saved_albums: "user-library-read", DSPResource.saved_tracks: "user-library-read", DSPResource.followed_artists: "user-follow-read", } def __init__(self, client: SpotifyClient) -> None: self._client = client def close(self) -> None: self._client.close() def refresh_token(self, refresh_token: SecretStr) -> TokenResult: response = self._client.refresh_token(refresh_token) result: TokenResult = TokenResult( access_token=response["access_token"], ) new_refresh_token = response.get("refresh_token") if new_refresh_token: result["refresh_token"] = new_refresh_token scope = response.get("scope") if scope: result["scope"] = scope return result def get_profile(self, access_token: SecretStr) -> dict[str, Any]: return self._client.get_current_user_profile(access_token) def get_top_artists(self, access_token: SecretStr) -> list[dict[str, Any]]: return exclude( self._client.get_current_user_top_artists(access_token, max_items=50), keys={"images", "external_urls", "available_markets"}, recursive=True, ) def get_top_tracks(self, access_token: SecretStr) -> list[dict[str, Any]]: return exclude( self._client.get_current_user_top_tracks(access_token, max_items=50), keys={"images", "external_urls", "available_markets"}, recursive=True, ) def get_recently_played( self, access_token: SecretStr, *, after: int | None = None ) -> list[dict[str, Any]]: return exclude( self._client.get_current_user_recently_played( access_token, after=after, max_items=200 ), keys={"images", "external_urls", "available_markets"}, recursive=True, ) def get_playlists(self, access_token: SecretStr) -> list[dict[str, Any]]: return exclude( self._client.get_current_user_playlists(access_token, max_items=50), keys={"images", "external_urls", "available_markets"}, recursive=True, ) def get_saved_albums(self, access_token: SecretStr) -> list[dict[str, Any]]: return exclude( self._client.get_current_user_saved_albums(access_token, max_items=50), keys={"images", "external_urls", "available_markets"}, recursive=True, ) def get_saved_tracks(self, access_token: SecretStr) -> list[dict[str, Any]]: return exclude( self._client.get_current_user_saved_tracks(access_token, max_items=100), keys={"images", "external_urls", "available_markets"}, recursive=True, ) def get_followed_artists(self, access_token: SecretStr) -> list[dict[str, Any]]: return exclude( self._client.get_current_user_followed_artists(access_token, max_items=100), keys={"images", "external_urls", "available_markets"}, recursive=True, )