Source code for minim.api.spotify._web_api.users

from __future__ import annotations
from datetime import datetime
from typing import TYPE_CHECKING

from ..._shared import TTLCache
from ._shared import SpotifyResourceAPI

if TYPE_CHECKING:
    from typing import Any

    from ...._types import Collection


[docs] class UsersAPI(SpotifyResourceAPI): """ Users API endpoints for the Spotify Web API. .. important:: This class is managed by :class:`~minim.api.spotify.SpotifyWebAPIClient` and should not be instantiated directly. """ _PEOPLE_TYPES = {"artists", "users"} _RESOURCE_TYPES = { "track", "album", "episode", "show", "audiobook", "user", "playlist", } _TIME_RANGES = {"long_term", "medium_term", "short_term"} __slots__ = () @classmethod def _validate_time_range(cls, time_range: str, /) -> None: """ Validate time range. Parameters ---------- time_range : str; positional-only Time range. """ if ( not isinstance(time_range, str) or time_range.lower() not in cls._TIME_RANGES ): raise ValueError( f"Invalid time range {time_range!r}. Valid " f"values: {UsersAPI._join_values(cls._TIME_RANGES)}." ) def _manage_followed_people( self, method: str, resource_type: str, resource_ids: str | Collection[str], /, ) -> None: """ Follow or unfollow one or more artists or Spotify users. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-follow-modify` scope Manage your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-follow-modify>`__ Parameters ---------- method : str; positional-only HTTP method. **Valid values**: :code:`"PUT"`, :code:`"DELETE"`. resource_type : str; positional-only Resource type. **Valid values**: :code:`"artists"`, :code:`"users"`. resource_ids : str or Collection[str]; positional-only Spotify IDs of the artists or users. A maximum of 50 IDs can be sent in a request. """ self._client._request( method, "me/following", params={ "type": resource_type[:-1], "ids": self._prepare_spotify_ids( resource_ids, limit=50, enforce_length=resource_type == "artists", )[0], }, ) def _is_following_people( self, resource_type: str, resource_ids: str | Collection[str], / ) -> list[bool]: """ Check whether the current user is following one or more artists or Spotify users. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-follow-read` scope Access your followers and who you are following. `Learn more. <https://developer.spotify.com /documentation/web-api/concepts /scopes#user-follow-read>`__ Parameters ---------- resource_type : str; positional-only Resource type. **Valid values**: :code:`"artists"`, :code:`"users"`. resource_ids : str or Collection[str]; positional-only Spotify IDs of the artists or users. A maximum of 50 IDs can be sent in a request. Returns ------- following : list[bool] Whether the current user follows the specified artists or users. **Sample response**: :code:`[False, True]`. """ return self._client._request( "GET", "me/following/contains", params={ "type": resource_type[:-1], "ids": self._prepare_spotify_ids( resource_ids, limit=50, enforce_length=resource_type == "artists", )[0], }, ).json() def _get_my_saved_entities( self, resource_type: str, /, *, country_code: str | None = None, limit: int | None = None, offset: int | None = None, ) -> dict[str, Any]: """ Get Spotify catalog information for items of a resource type saved in the current user's library. .. admonition:: Authorization scope and third-party application mode :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-library-read` scope Access your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-library-read>`__ .. tab-item:: Optional Extended quota mode before November 27, 2024 Access 30-second preview URLs. `Learn more. <https://developer.spotify.com/blog /2024-11-27-changes-to-the-web-api>`__ Parameters ---------- resource_type : str; positional-only Resource type. **Valid values**: :code:`"albums"`, :code:`"audiobooks"`, :code:`"episodes"`, :code:`"shows"`, :code:`"tracks"`. country_code : str; keyword-only; optional ISO 3166-1 alpha-2 country code. If provided, only content available in that market is returned. When a user access token accompanies the request, the country associated with the user account takes priority over this parameter. .. note:: If neither the market nor the user's country are provided, the content is considered unavailable for the client. limit : int; keyword-only; optional Maximum number of items to return. **Valid range**: :code:`1` to :code:`50`. **API default**: :code:`20`. offset : int; keyword-only; optional Index of the first item to return. Use with `limit` to get the next batch of items. **Minimum value**: :code:`0`. **API default**: :code:`0`. Returns ------- items : dict[str, Any] Page of Spotify metadata for the user's saved items. """ params = {} if country_code is not None: self._client.markets._validate_market(country_code) params["market"] = country_code if limit is not None: self._validate_number("limit", limit, int, 1, 50) params["limit"] = limit if offset is not None: self._validate_number("offset", offset, int, 0) params["offset"] = offset return self._client._request( "GET", f"me/{resource_type}", params=params ).json() def _manage_saved_entities( self, method: str, resource_type: str, resource_ids: str | Collection[str], /, *, limit: int = 50, ) -> None: """ Save or remove one or more items of a resource type to or from the current user's library. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-library-modify` scope Manage your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-library-modify>`__ Parameters ---------- method : str; positional-only HTTP method. **Valid values**: :code:`"PUT"`, :code:`"DELETE"`. resource_type : str; positional-only Resource type. **Valid values**: :code:`"albums"`, :code:`"audiobooks"`, :code:`"episodes"`, :code:`"shows"`. resource_ids : str or Collection[str]; positional-only Spotify IDs of the items, provided as either a comma-separated string or a collection of strings. limit : int; keyword-only; default: :code:`50` Maximum number of Spotify IDs that can be sent in the request. """ self._client._request( method, f"me/{resource_type}", params={ "ids": self._prepare_spotify_ids(resource_ids, limit=limit)[0] }, ) def _are_entities_saved( self, resource_type: str, resource_ids: str | Collection[str], /, *, limit: int = 50, ) -> list[bool]: """ Check whether one or more items of a resource type are saved in the current user's library. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-library-read` scope Access your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-library-read>`__ Parameters ---------- resource_type : str; positional-only Resource type. **Valid values**: :code:`"albums"`, :code:`"audiobooks"`, :code:`"episodes"`, :code:`"shows"`, :code:`"tracks"`. resource_ids : str or Collection[str]; positional-only Spotify IDs of the items, provided as either a comma-separated string or a collection of strings. limit : int; keyword-only; default: :code:`50` Maximum number of Spotify IDs that can be sent in the request. Returns ------- saved : list[bool] Whether the current user has each of the specified items saved in their library. """ return self._client._request( "GET", f"me/{resource_type}/contains", params={ "ids": self._prepare_spotify_ids(resource_ids, limit=limit)[0] }, ).json()
[docs] @TTLCache.cached_method(ttl="user") def get_me(self) -> dict[str, Any]: """ `Users > Get Current User's Profile <https://developer.spotify.com/documentation/web-api/reference /get-current-users-profile>`__: Get Spotify profile information for the current user. .. admonition:: Authorization scopes and user authentication :class: entitlement .. tab-set:: .. tab-item:: Required User authentication Access private profile information. .. tab-item:: Optional :code:`user-read-private` Access your subscription details. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-read-private>`__ :code:`user-read-email` Get your real email address. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-read-email>`__ Returns ------- profile : dict[str, Any] Spotify profile information for the current user. .. admonition:: Sample response :class: response dropdown .. code-block:: { "country": <str>, "display_name": <str>, "email": <str>, "explicit_content": { "filter_enabled": <bool>, "filter_locked": <bool> }, "external_urls": { "spotify": <str> }, "followers": { "href": <str>, "total": <int> }, "href": <str>, "id": <str>, "images": [ { "height": <int>, "url": <str>, "width": <int> } ], "product": <str>, "type": "user", "uri": <str> } """ self._client._require_authentication("users.get_me") return self._client._request("GET", "me").json()
[docs] @TTLCache.cached_method(ttl="user") def get_user(self, user_id: str | None = None, /) -> dict[str, Any]: """ `Users > Get Current User's Profile <https://developer.spotify.com/documentation/web-api/reference /get-current-users-profile>`__: Get Spotify profile information for the current user․ `Users > Get User's Profile <https://developer.spotify.com /documentation/web-api/reference/get-users-profile>`__: Get Spotify profile information for a user. .. admonition:: Authorization scopes :class: entitlement .. tab-set:: .. tab-item:: Conditional User authentication Access private profile information. .. tab-item:: Optional :code:`user-read-private` Access your subscription details. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-read-private>`__ :code:`user-read-email` Get your real email address. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-read-email>`__ Parameters ---------- user_id : str; positional-only; optional Spotify user ID. If not provided, the current user's profile is returned. **Example**: :code:`"smedjan"`. Returns ------- profile : dict[str, Any] Spotify profile information for the user. .. admonition:: Sample responses :class: response dropdown .. tab-set:: .. tab-item:: Current user .. code-block:: { "country": <str>, "display_name": <str>, "email": <str>, "explicit_content": { "filter_enabled": <bool>, "filter_locked": <bool> }, "external_urls": { "spotify": <str> }, "followers": { "href": <str>, "total": <int> }, "href": <str>, "id": <str>, "images": [ { "height": <int>, "url": <str>, "width": <int> } ], "product": <str>, "type": "user", "uri": <str> } .. tab-item:: Public user .. code-block:: { "display_name": <str>, "external_urls": { "spotify": <str> }, "followers": { "href": <str>, "total": <int> }, "href": <str>, "id": <str>, "images": [ { "height": <int>, "url": <str>, "width": <int> } ], "type": "user", "uri": <str> } """ if user_id is None: return self.get_me() return self._client._request( "GET", f"users/{self._prepare_string('user_id', user_id)}" ).json()
[docs] @TTLCache.cached_method(ttl="hourly") def get_my_top_items( self, item_type: str, /, *, time_range: str | None = None, limit: int | None = None, offset: int | None = None, ) -> dict[str, Any]: """ `Users > Get User's Top Items <https://developer.spotify.com/documentation/web-api/reference /get-users-top-artists-and-tracks>`_: Get Spotify catalog information for the current user's top artists or tracks. .. admonition:: Authorization scope and third-party application mode :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-top-read` scope Read your top artists and contents. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-top-read>`__ .. tab-item:: Optional Extended quota mode before November 27, 2024 Access 30-second preview URLs. `Learn more. <https://developer.spotify.com/blog /2024-11-27-changes-to-the-web-api>`__ Parameters ---------- item_type : str; positional-only Type of item to return. **Valid values**: :code:`"artists"`, :code:`"tracks"`. time_range : str; keyword-only; optional Time frame over which the current user's listening history is analyzed to determine top artists or tracks. **Valid values**: * :code:`"long_term"` – Approximately one year of data, including all new data as it becomes available. * :code:`"medium_term"` – Approximately the last six months of data. * :code:`"short_term"` – Approximately the last four weeks of data. **API default**: :code:`"medium_term"`. limit : int; keyword-only; optional Maximum number of artists or tracks to return. **Valid range**: :code:`1` to :code:`50`. **API default**: :code:`20`. offset : int; keyword-only; optional Index of the first artist or track to return. Use with `limit` to get the next batch of artists or tracks. **Minimum value**: :code:`0`. **API default**: :code:`0`. Returns ------- items : dict[str, Any] Page of Spotify metadata for the current user's top artists or tracks. .. admonition:: Sample responses :class: response dropdown .. tab-set:: .. tab-item:: Top artists .. code-block:: { "href": <str>, "items": [ { "external_urls": { "spotify": <str> }, "followers": { "href": <str>, "total": <int> }, "genres": <list[str]>, "href": <str>, "id": <str>, "images": [ { "height": <int>, "url": <str>, "width": <int> } ], "name": <str>, "type": "artist", "uri": <str> } ], "limit": <int>, "next": <str>, "offset": <int>, "previous": <str>, "total": <int> } .. tab-item:: Top tracks .. code-block:: { "href": <str>, "items": [ { "album": { "album_type": <str>, "artists": [ { "external_urls": { "spotify": <str> }, "href": <str>, "id": <str>, "name": <str>, "type": "artist", "uri": <str> } ], "available_markets": <list[str]>, "external_urls": { "spotify": <str> }, "href": <str>, "id": <str>, "images": [ { "height": <int>, "url": <str>, "width": <int> } ], "is_playable": <bool>, "name": <str>, "release_date": <str>, "release_date_precision": <str>, "total_tracks": <int>, "type": "album", "uri": <str> }, "artists": [ { "external_urls": { "spotify": <str> }, "href": <str>, "id": <str>, "name": <str>, "type": "artist", "uri": <str></str> } ], "available_markets": <list[str]>, "disc_number": <int>, "duration_ms": <int>, "explicit": <bool>, "external_ids": { "isrc": <str> }, "external_urls": { "spotify": <str> }, "href": <str>, "id": <str>, "is_local": <bool>, "is_playable": <bool>, "name": <str>, "popularity": <int>, "preview_url": <str>, "track_number": <int>, "type": "track", "uri": <str> } ], "limit": <int>, "next": <str>, "offset": <int>, "previous": <str>, "total": <int> } """ self._client._require_scopes("users.get_my_top_items", "user-top-read") item_type = self._prepare_string("item_type", item_type).lower() if item_type not in {"artists", "tracks"}: raise ValueError( f"Invalid item type {item_type!r}. " "Valid values: 'artists', 'tracks'." ) params = {} if time_range is not None: self._client.users._validate_time_range(time_range) params["time_range"] = time_range if limit is not None: self._validate_number("limit", limit, int, 1, 50) params["limit"] = limit if offset is not None: self._validate_number("offset", offset, int, 0) params["offset"] = offset return self._client._request( "GET", f"me/top/{item_type}", params=params ).json()
[docs] def follow_playlist( self, playlist_id: str, /, *, public: bool | None = None ) -> None: """ `Users > Follow Playlist <https://developer.spotify.com /documentation/web-api/reference/follow-playlist>`_: Follow a playlist. .. admonition:: Authorization scopes and user authentication :class: entitlement .. tab-set:: .. tab-item:: Required User authentication Access and manage your library. .. tab-item:: Conditional :code:`playlist-modify-public` scope Manage your public playlists. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#playlist-modify-public>`__ :code:`playlist-modify-private` scope Manage your private playlists. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#playlist-modify-private>`__ Parameters ---------- playlist_id : str; positional-only Spotify ID of the playlist. **Example**: :code:`"3cEYpjA9oz9GiPac4AsH4n"`. public : bool; keyword-only; optional Whether the playlist is displayed on the current user's profile. **API default**: :code:`True`. """ self._client._require_authentication("users.follow_playlist") self._validate_spotify_id(playlist_id) payload = {} if isinstance(public, bool): self._client._require_scopes( "users.follow_playlist", f"playlist-modify-{'public' if public else 'private'}", ) payload["public"] = public self._client._request( "PUT", f"playlists/{playlist_id}/followers", json=payload )
[docs] def unfollow_playlist(self, playlist_id: str, /) -> None: """ `Users > Unfollow Playlist <https://developer.spotify.com /documentation/web-api/reference/unfollow-playlist>`_: Unfollow a playlist. .. admonition:: Authorization scopes and user authentication :class: entitlement .. tab-set:: .. tab-item:: Required User authentication Access and manage your library. .. tab-item:: Conditional :code:`playlist-modify-public` scope Manage your public playlists. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#playlist-modify-public>`__ :code:`playlist-modify-private` scope Manage your private playlists. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#playlist-modify-private>`__ Parameters ---------- playlist_id : str; positional-only Spotify ID of the playlist. **Example**: :code:`"3cEYpjA9oz9GiPac4AsH4n"`. """ self._client._require_authentication("users.unfollow_playlist") self._validate_spotify_id(playlist_id) self._client._request("DELETE", f"playlists/{playlist_id}/followers")
[docs] @TTLCache.cached_method(ttl="user") def get_my_followed_artists( self, *, limit: int | None = None, cursor: str | None = None ) -> dict[str, Any]: """ `Users > Get Followed Artists <https://developer.spotify.com /documentation/web-api/reference/get-followed>`_: Get Spotify catalog information for artists followed by the current user. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-follow-read` scope Access your followers and who you are following. `Learn more. <https://developer.spotify.com /documentation/web-api/concepts /scopes#user-follow-read>`__ Parameters ---------- limit : int; keyword-only; optional Maximum number of artists to return. **Valid range**: :code:`1` to :code:`50`. **API default**: :code:`20`. cursor : str; keyword-only; optional Cursor (Spotify ID of the last artist retrieved in the previous request) for fetching the next page of results. **Example**: :code:`"0I2XqVXqHScXjHhk6AYYRe"`. Returns ------- artists : dict[str, Any] Spotify metadata for the artists followed by the current user. .. admonition:: Sample response :class: response dropdown .. code-block:: { "artists": { "cursors": { "after": <str>, "before": <str> }, "href": <str>, "items": [ { "external_urls": { "spotify": <str> }, "followers": { "href": <str>, "total": <int> }, "genres": <list[str]>, "href": <str>, "id": <str>, "images": [ { "url": <str>, "height": <int>, "width": <int> } ], "name": <str>, "popularity": <int>, "type": "artist", "uri": <str> } ], "limit": <int>, "next": <str>, "total": <int> } } """ self._client._require_scopes( "users.get_my_followed_artists", "user-follow-read" ) params = {"type": "artist"} if cursor is not None: self._validate_spotify_id(cursor) params["after"] = cursor if limit is not None: self._validate_number("limit", limit, int, 1, 50) params["limit"] = limit return self._client._request( "GET", "me/following", params=params ).json()
[docs] def follow_artists(self, artist_ids: str | Collection[str], /) -> None: """ `Users > Follow Artists <https://developer.spotify.com /documentation/web-api/reference/follow-artists-users>`_: Follow one or more artists. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-follow-modify` scope Manage your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-follow-modify>`__ Parameters ---------- artist_ids : str or Collection[str]; positional-only Spotify IDs of the artists. A maximum of 50 IDs can be sent in a request. **Examples**: * :code:`"2CIMQHirSU0MQqyYHq0eOx"` * :code:`"2CIMQHirSU0MQqyYHq0eOx,57dN52uHvrHOxijzpIgu3E"` * :code:`["2CIMQHirSU0MQqyYHq0eOx", "57dN52uHvrHOxijzpIgu3E"]` """ self._client._require_scopes( "users.follow_artists", "user-follow-modify" ) return self._manage_followed_people("PUT", "artists", artist_ids)
[docs] def follow_users(self, user_ids: str | Collection[str], /) -> None: """ `Users > Follow Users <https://developer.spotify.com /documentation/web-api/reference/follow-artists-users>`_: Follow one or more Spotify users. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-follow-modify` scope Manage your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-follow-modify>`__ Parameters ---------- user_ids : str or Collection[str]; positional-only Spotify user IDs. A maximum of 50 IDs can be sent in a request. **Examples**: :code:`"smedjan"`, :code:`"smedjan,bbye98"`, :code:`["smedjan", "bbye98"]`. """ self._client._require_scopes( "users.follow_users", "user-follow-modify" ) return self._manage_followed_people("PUT, users", user_ids)
[docs] def unfollow_artists(self, artist_ids: str | Collection[str], /) -> None: """ `Users > Unfollow Artists <https://developer.spotify.com /documentation/web-api/reference/unfollow-artists-users>`_: Unfollow one or more artists. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-follow-modify` scope Manage your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-follow-modify>`__ Parameters ---------- artist_ids : str or Collection[str]; positional-only Spotify IDs of the artists. A maximum of 50 IDs can be sent in a request. **Examples**: * :code:`"2CIMQHirSU0MQqyYHq0eOx"` * :code:`"2CIMQHirSU0MQqyYHq0eOx,57dN52uHvrHOxijzpIgu3E"` * :code:`["2CIMQHirSU0MQqyYHq0eOx", "57dN52uHvrHOxijzpIgu3E"]` """ self._client._require_scopes( "users.unfollow_artists", "user-follow-modify" ) return self._manage_followed_people("DELETE", "artists", artist_ids)
[docs] def unfollow_users(self, user_ids: str | Collection[str], /) -> None: """ `Users > Unfollow Users <https://developer.spotify.com /documentation/web-api/reference/unfollow-artists-users>`_: Unfollow one or more Spotify users. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-follow-modify` scope Manage your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-follow-modify>`__ Parameters ---------- user_ids : str or Collection[str]; positional-only Spotify user IDs. A maximum of 50 IDs can be sent in a request. **Examples**: :code:`"smedjan"`, :code:`"smedjan,bbye98"`, :code:`["smedjan", "bbye98"]`. """ self._client._require_scopes( "users.unfollow_users", "user-follow-modify" ) return self._manage_followed_people("DELETE", "users", user_ids)
[docs] @TTLCache.cached_method(ttl="user") def is_following_artists( self, artist_ids: str | Collection[str], / ) -> list[bool]: """ `Users > Check If Current User Follows Artists <https://developer.spotify.com/documentation/web-api/reference /check-current-user-follows>`_: Check whether the current user is following one or more artists. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-follow-read` scope Access your followers and who you are following. `Learn more. <https://developer.spotify.com /documentation/web-api/concepts /scopes#user-follow-read>`__ Parameters ---------- artist_ids : str or Collection[str]; positional-only Spotify IDs of the artists. A maximum of 50 IDs can be sent in a request. **Examples**: * :code:`"2CIMQHirSU0MQqyYHq0eOx"` * :code:`"2CIMQHirSU0MQqyYHq0eOx,57dN52uHvrHOxijzpIgu3E"` * :code:`["2CIMQHirSU0MQqyYHq0eOx", "57dN52uHvrHOxijzpIgu3E"]` Returns ------- following : list[bool] Whether the current user follows the specified artists. **Sample response**: :code:`[False, True]`. """ self._client._require_scopes( "users.is_following_artists", "user-follow-read" ) return self._is_following_people("artists", artist_ids)
[docs] @TTLCache.cached_method(ttl="user") def is_following_users( self, user_ids: str | Collection[str], / ) -> list[bool]: """ `Users > Check If Current User Follows Users <https://developer.spotify.com/documentation/web-api/reference /check-current-user-follows>`_: Check whether the current user is following one or more Spotify users. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-follow-read` scope Access your followers and who you are following. `Learn more. <https://developer.spotify.com /documentation/web-api/concepts /scopes#user-follow-read>`__ Parameters ---------- user_ids : str or Collection[str]; positional-only Spotify user IDs. A maximum of 50 IDs can be sent in a request. **Examples**: :code:`"smedjan"`, :code:`"smedjan,bbye98"`, :code:`["smedjan", "bbye98"]`. Returns ------- following : list[bool] Whether the current user follows the specified users. **Sample response**: :code:`[False, True]`. """ self._client._require_scopes( "users.is_following_users", "user-follow-read" ) return self._is_following_people("users", user_ids)
[docs] @TTLCache.cached_method(ttl="user") def is_following_playlist(self, playlist_id: str, /) -> list[bool]: """ `Users > Check if Current User Follows Playlist <https://developer.spotify.com/documentation/web-api/reference /check-if-user-follows-playlist>`_: Check whether the current user is following a playlist. .. admonition:: Authorization scope and user authentication :class: entitlement .. tab-set:: .. tab-item:: Required User authentication Access and manage your library. .. tab-item:: Conditional :code:`playlist-read-private` scope Access your private playlists. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#playlist-read-private>`__ Parameters ---------- playlist_id : str; positional-only Spotify ID of the playlist. **Example**: :code:`"3cEYpjA9oz9GiPac4AsH4n"`. Returns ------- following : list[bool] Whether the current user follows the specified playlist. **Sample response**: :code:`[True]`. """ self._client._require_authentication("users.is_following_playlist") self._validate_spotify_id(playlist_id) return self._client._request( "GET", f"playlists/{playlist_id}/followers/contains" ).json()
[docs] @TTLCache.cached_method(ttl="user") def get_my_saved_albums( self, *, country_code: str | None = None, limit: int | None = None, offset: int | None = None, ) -> dict[str, Any]: """ `Albums > Get User's Saved Albums <https://developer.spotify.com /documentation/web-api/reference/get-users-saved-albums>`_: Get Spotify catalog information for the albums saved in the current user's library. .. admonition:: Authorization scope and third-party application mode :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-library-read` scope Access your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-library-read>`__ .. tab-item:: Optional Extended quota mode before November 27, 2024 Access 30-second preview URLs. `Learn more. <https://developer.spotify.com/blog /2024-11-27-changes-to-the-web-api>`__ Parameters ---------- country_code : str; keyword-only; optional ISO 3166-1 alpha-2 country code. If provided, only content available in that market is returned. When a user access token accompanies the request, the country associated with the user account takes priority over this parameter. .. note:: If neither a country code is provided nor a country can be determined from the user account, the content is considered unavailable for the client. **Example**: :code:`"ES"`. limit : int; keyword-only; optional Maximum number of albums to return. **Valid range**: :code:`1` to :code:`50`. **API default**: :code:`20`. offset : int; keyword-only; optional Index of the first album to return. Use with `limit` to get the next batch of albums. **Minimum value**: :code:`0`. **API default**: :code:`0`. Returns ------- albums : dict[str, Any] Page of Spotify metadata for the user's saved albums. .. admonition:: Sample response :class: response dropdown .. code-block:: { "href": <str>, "items": [ { "added_at": <str>, "album": { "album_type": <str>, "artists": [ { "external_urls": { "spotify": <str> }, "href": <str>, "id": <str>, "name": <str>, "type": "artist", "uri": <str> } ], "available_markets": <list[str]>, "copyrights": [ { "text": <str>, "type": <str> } ], "external_ids": { "ean": <str>, "isrc": <str>, "upc": <str> }, "external_urls": { "spotify": <str> }, "genres": <list[str]>, "href": <str>, "id": <str>, "images": [ { "height": <int>, "url": <str>, "width": <int> } ], "label": <str>, "name": <str>, "popularity": <int>, "release_date": <str>, "release_date_precision": <str>, "restrictions": { "reason": <str> }, "total_tracks": <int>, "tracks": { "href": <str>, "items": [ { "artists": [ { "external_urls": { "spotify": <str> }, "href": <str>, "id": <str>, "name": <str>, "type": "artist", "uri": <str> } ], "available_markets": <list[str]>, "disc_number": <int>, "duration_ms": <int>, "explicit": <bool>, "external_urls": { "spotify": <str> }, "href": <str>, "id": <str>, "is_local": <bool>, "is_playable": <bool>, "linked_from": { "external_urls": { "spotify": <str> }, "href": <str>, "id": <str>, "type": "track", "uri": <str> }, "name": <str>, "preview_url": <str>, "restrictions": { "reason": <str> }, "track_number": <int>, "type": "track", "uri": <str> } ], "limit": <int>, "next": <str>, "offset": <int>, "previous": <str>, "total": <int> }, "type": "album", "uri": <str> } } ], "limit": <int>, "next": <str>, "offset": <int>, "previous": <str>, "total": <int> } """ self._client._require_scopes( "albums.get_my_saved_albums", "user-library-read" ) return self._get_my_saved_entities( "albums", country_code=country_code, limit=limit, offset=offset )
[docs] def save_albums(self, album_ids: str | Collection[str], /) -> None: """ `Albums > Save Albums for Current User <https://developer.spotify.com/documentation/web-api/reference /save-albums-user>`_: Save one or more albums to the current user's library. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-library-modify` scope Manage your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-library-modify>`__ Parameters ---------- album_ids : str or Collection[str]; positional-only Spotify IDs of the albums. A maximum of 20 IDs can be sent in a request. **Examples**: * :code:`"382ObEPsp2rxGrnsizN5TX"` * :code:`"382ObEPsp2rxGrnsizN5TX,1A2GTWGtFfWp7KSQTwWOyo"` * :code:`["382ObEPsp2rxGrnsizN5TX", "1A2GTWGtFfWp7KSQTwWOyo"]` """ self._client._require_scopes( "albums.save_albums", "user-library-modify" ) self._manage_saved_entities("PUT", "albums", album_ids, limit=20)
[docs] def remove_saved_albums(self, album_ids: str | Collection[str], /) -> None: """ `Albums > Remove User's Saved Albums <https://developer.spotify.com/documentation/web-api/reference /remove-albums-user>`_: Remove one or more albums from the current user's library. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-library-modify` scope Manage your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-library-modify>`__ Parameters ---------- album_ids : str or Collection[str]; positional-only Spotify IDs of the albums. A maximum of 20 IDs can be sent in a request. **Examples**: * :code:`"382ObEPsp2rxGrnsizN5TX"` * :code:`"382ObEPsp2rxGrnsizN5TX,1A2GTWGtFfWp7KSQTwWOyo"` * :code:`["382ObEPsp2rxGrnsizN5TX", "1A2GTWGtFfWp7KSQTwWOyo"]` """ self._client._require_scopes( "albums.remove_saved_albums", "user-library-modify" ) self._manage_saved_entities("DELETE", "albums", album_ids, limit=20)
[docs] @TTLCache.cached_method(ttl="user") def are_albums_saved( self, album_ids: str | Collection[str], / ) -> list[bool]: """ `Albums > Check User's Saved Albums <https://developer.spotify.com/documentation/web-api/reference /check-users-saved-albums>`_: Check whether one or more albums are saved in the current user's library. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-library-read` scope Access your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-library-read>`__ Parameters ---------- album_ids : str or Collection[str]; positional-only Spotify IDs of the albums. A maximum of 20 IDs can be sent in a request. **Examples**: * :code:`"382ObEPsp2rxGrnsizN5TX"` * :code:`"382ObEPsp2rxGrnsizN5TX,1A2GTWGtFfWp7KSQTwWOyo"` * :code:`["382ObEPsp2rxGrnsizN5TX", "1A2GTWGtFfWp7KSQTwWOyo"]` Returns ------- saved : list[bool] Whether the current user has the specified albums saved in their library. """ self._client._require_scopes( "albums.are_albums_saved", "user-library-read" ) return self._are_entities_saved("albums", album_ids, limit=20)
[docs] @TTLCache.cached_method(ttl="user") def get_my_saved_audiobooks( self, *, country_code: str | None = None, limit: int | None = None, offset: int | None = None, ) -> dict[str, Any]: """ `Audiobooks > Get User's Saved Audiobooks <https://developer.spotify.com/documentation/web-api/reference /get-users-saved-audiobooks>`_: Get Spotify catalog information for the audiobooks saved in the current user's library. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-library-read` scope Access your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-library-read>`__ Parameters ---------- country_code : str; keyword-only; optional ISO 3166-1 alpha-2 country code. If provided, only content available in that market is returned. When a user access token accompanies the request, the country associated with the user account takes priority over this parameter. .. note:: If neither a country code is provided nor a country can be determined from the user account, the content is considered unavailable for the client. **Example**: :code:`"ES"`. limit : int; keyword-only; optional Maximum number of audiobooks to return. **Valid range**: :code:`1` to :code:`50`. **API default**: :code:`20`. offset : int; keyword-only; optional Index of the first audiobook to return. Use with `limit` to get the next batch of audiobooks. **Minimum value**: :code:`0`. **API default**: :code:`0`. Returns ------- audiobooks : dict[str, Any] Page of Spotify metadata for the user's saved audiobooks. .. admonition:: Sample response :class: response dropdown .. code-block:: { "href": <str>, "items": [ { "authors": [ { "name": <str> } ], "available_markets": <list[str]>, "copyrights": [ { "text": <str>, "type": <str> } ], "description": <str>, "edition": <str>, "explicit": <bool>, "external_urls": { "spotify": <str> }, "href": <str>, "html_description": <str>, "id": <str>, "images": [ { "height": <int>, "url": <str>, "width": <int> } ], "languages": <list[str]>, "media_type": <str>, "name": <str>, "narrators": [ { "name": <str> } ], "publisher": <str>, "total_chapters": <int>, "type": "audiobook", "uri": <str> } ], "limit": <int>, "next": <str>, "offset": <int>, "previous": <str>, "total": <int> } """ self._client._require_scopes( "audiobooks.get_my_saved_audiobooks", "user-library-read" ) return self._get_my_saved_entities( "audiobooks", country_code=country_code, limit=limit, offset=offset )
[docs] def save_audiobooks(self, audiobook_ids: str | Collection[str], /) -> None: """ `Audiobooks > Save Audiobooks for Current User <https://developer.spotify.com/documentation/web-api/reference /save-albums-user>`_: Save one or more audiobooks to the current user's library. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-library-modify` scope Manage your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-library-modify>`__ Parameters ---------- audiobook_ids : str or Collection[str]; positional-only Spotify IDs of the audiobooks. A maximum of 50 IDs can be sent in a request. **Examples**: * :code:`"18yVqkdbdRvS24c0Ilj2ci"` * :code:`"18yVqkdbdRvS24c0Ilj2ci,1HGw3J3NxZO1TP1BTtVhpZ"` * :code:`["18yVqkdbdRvS24c0Ilj2ci", "1HGw3J3NxZO1TP1BTtVhpZ"]` """ self._client._require_scopes( "audiobooks.save_audiobooks", "user-library-modify" ) self._manage_saved_entities("PUT", "audiobooks", audiobook_ids)
[docs] def remove_saved_audiobooks( self, audiobook_ids: str | Collection[str], / ) -> None: """ `Audiobooks > Remove User's Saved Audiobooks <https://developer.spotify.com/documentation/web-api/reference /remove-audiobooks-user>`_: Remove one or more audiobooks from the current user's library. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-library-modify` scope Manage your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-library-modify>`__ Parameters ---------- audiobook_ids : str or Collection[str]; positional-only Spotify IDs of the audiobooks. A maximum of 50 IDs can be sent in a request. **Examples**: * :code:`"18yVqkdbdRvS24c0Ilj2ci"` * :code:`"18yVqkdbdRvS24c0Ilj2ci,1HGw3J3NxZO1TP1BTtVhpZ"` * :code:`["18yVqkdbdRvS24c0Ilj2ci", "1HGw3J3NxZO1TP1BTtVhpZ"]` """ self._client._require_scopes( "audiobooks.remove_saved_audiobooks", "user-library-modify" ) self._manage_saved_entities("DELETE", "audiobooks", audiobook_ids)
[docs] @TTLCache.cached_method(ttl="user") def are_audiobooks_saved( self, audiobook_ids: str | Collection[str], / ) -> list[bool]: """ `Audiobooks > Check User's Saved Audiobooks <https://developer.spotify.com/documentation/web-api/reference /check-users-saved-audiobooks>`_: Check whether one or more audiobooks are saved in the current user's library. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-library-read` scope Access your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-library-read>`__ Parameters ---------- audiobook_ids : str or Collection[str]; positional-only Spotify IDs of the audiobooks. A maximum of 50 IDs can be sent in a request. **Examples**: * :code:`"18yVqkdbdRvS24c0Ilj2ci"` * :code:`"18yVqkdbdRvS24c0Ilj2ci,1HGw3J3NxZO1TP1BTtVhpZ"` * :code:`["18yVqkdbdRvS24c0Ilj2ci", "1HGw3J3NxZO1TP1BTtVhpZ"]` Returns ------- saved : list[bool] Whether the current user has the specified audiobooks saved in their library. """ self._client._require_scopes( "audiobooks.are_audiobooks_saved", "user-library-read" ) return self._are_entities_saved("audiobooks", audiobook_ids)
[docs] @TTLCache.cached_method(ttl="playback") def get_my_saved_episodes( self, *, country_code: str | None = None, limit: int | None = None, offset: int | None = None, ) -> dict[str, Any]: """ `Episodes > Get User's Saved Episodes <https://developer.spotify.com/documentation/web-api/reference /get-multiple-episodes>`_: Get Spotify catalog information for the show episodes saved in the current user's library. .. admonition:: Authorization scopes and third-party application mode :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-library-read` scope Access your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-library-read>`__ :code:`user-read-playback-position` scope Read your position in content you have played. `Learn more. <https://developer.spotify.com /documentation/web-api/concepts /scopes#user-read-playback-position>`__ .. tab-item:: Optional Extended quota mode before November 27, 2024 Access 30-second preview URLs. `Learn more. <https://developer.spotify.com/blog /2024-11-27-changes-to-the-web-api>`__ Parameters ---------- country_code : str; keyword-only; optional ISO 3166-1 alpha-2 country code. If provided, only content available in that market is returned. When a user access token accompanies the request, the country associated with the user account takes priority over this parameter. .. note:: If neither a country code is provided nor a country can be determined from the user account, the content is considered unavailable for the client. **Example**: :code:`"ES"`. limit : int; keyword-only; optional Maximum number of show episodes to return. **Valid range**: :code:`1` to :code:`50`. **API default**: :code:`20`. offset : int; keyword-only; optional Index of the first show episode to return. Use with `limit` to get the next batch of show episodes. **Minimum value**: :code:`0`. **API default**: :code:`0`. Returns ------- episodes : dict[str, Any] Page of Spotify metadata for the user's saved show episodes. .. admonition:: Sample response :class: response dropdown .. code-block:: { "href": <str>, "items": [ { "added_at": <str>, "episode": { "audio_preview_url": <str>, "description": <str>, "duration_ms": <int>, "explicit": <bool>, "external_urls": { "spotify": <str> }, "href": <str>, "html_description": <str>, "id": <str>, "images": [ { "height": <int>, "url": <str>, "width": <int> } ], "is_externally_hosted": <bool>, "is_playable": <bool>, "language": <str>, "languages": <list[str]>, "name": <str>, "release_date": <str>, "release_date_precision": <str>, "restrictions": { "reason": <str> }, "resume_point": { "fully_played": <bool>, "resume_position_ms": <int> }, "show": { "available_markets": <list[str]>, "copyrights": [ { "text": <str>, "type": <str> } ], "description": <str>, "explicit": <bool>, "external_urls": { "spotify": <str> }, "href": <str>, "html_description": <str>, "id": <str>, "images": [ { "height": <int>, "url": <str>, "width": <int> } ], "is_externally_hosted": <bool>, "languages": <list[str]>, "media_type": <str>, "name": <str>, "publisher": <str>, "total_episodes": <int>, "type": "show", "uri": <str> }, "type": "episode", "uri": <str> } } ], "limit": <int>, "next": <str>, "offset": <int>, "previous": <str>, "total": <int> } """ self._client._require_scopes( "episodes.get_my_saved_episodes", {"user-library-read", "user-read-playback-position"}, ) return self._get_my_saved_entities( "episodes", country_code=country_code, limit=limit, offset=offset, )
[docs] def save_episodes(self, episode_ids: str | Collection[str], /) -> None: """ `Episodes > Save Episodes for Current User <https://developer.spotify.com/documentation/web-api/reference /save-episodes-user>`_: Save one or more show episodes to the current user's library. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-library-modify` scope Manage your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-library-modify>`__ Parameters ---------- episode_ids : str or Collection[str]; positional-only Spotify IDs of the show episodes. A maximum of 50 IDs can be sent in a request. **Examples**: * :code:`"77o6BIVlYM3msb4MMIL1jH"` * :code:`"77o6BIVlYM3msb4MMIL1jH,0Q86acNRm6V9GYx55SXKwf"` * :code:`["77o6BIVlYM3msb4MMIL1jH", "0Q86acNRm6V9GYx55SXKwf"]` """ self._client._require_scopes( "episodes.save_episodes", "user-library-modify" ) self._manage_saved_entities("PUT", "episodes", episode_ids)
[docs] def remove_saved_episodes( self, episode_ids: str | Collection[str], / ) -> None: """ `Episodes > Remove User's Saved Episodes <https://developer.spotify.com/documentation/web-api/reference /remove-episodes-user>`_: Remove one or more show episodes from the current user's library. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-library-modify` scope Manage your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-library-modify>`__ Parameters ---------- episode_ids : str or Collection[str]; positional-only Spotify IDs of the show episodes. A maximum of 50 IDs can be sent in a request. **Examples**: * :code:`"77o6BIVlYM3msb4MMIL1jH"` * :code:`"77o6BIVlYM3msb4MMIL1jH,0Q86acNRm6V9GYx55SXKwf"` * :code:`["77o6BIVlYM3msb4MMIL1jH", "0Q86acNRm6V9GYx55SXKwf"]` """ self._client._require_scopes( "episodes.remove_saved_episodes", "user-library-modify" ) self._manage_saved_entities("DELETE", "episodes", episode_ids)
[docs] @TTLCache.cached_method(ttl="user") def are_episodes_saved( self, episode_ids: str | Collection[str], / ) -> list[bool]: """ `Episodes > Check User's Saved Episodes <https://developer.spotify.com/documentation/web-api/reference /check-users-saved-episodes>`_: Check whether one or more show episodes are saved in the current user's library. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-library-read` scope Access your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-library-read>`__ Parameters ---------- episode_ids : str or Collection[str]; positional-only Spotify IDs of the show episodes. A maximum of 50 IDs can be sent in a request. **Examples**: * :code:`"77o6BIVlYM3msb4MMIL1jH"` * :code:`"77o6BIVlYM3msb4MMIL1jH,0Q86acNRm6V9GYx55SXKwf"` * :code:`["77o6BIVlYM3msb4MMIL1jH", "0Q86acNRm6V9GYx55SXKwf"]` Returns ------- saved : list[bool] Whether the current user has the specified show episodes saved in their library. """ self._client._require_scopes( "episodes.are_episodes_saved", "user-library-read" ) return self._are_entities_saved("episodes", episode_ids)
[docs] @TTLCache.cached_method(ttl="user") def get_my_playlists( self, *, limit: int | None = None, offset: int | None = None ) -> dict[str, Any]: """ `Playlists > Get Current User's Playlists <https://developer.spotify.com/documentation/web-api/reference /get-a-list-of-current-users-playlists>`_: Get Spotify catalog information for playlists owned or followed by the current user. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`playlist-read-private` scope Access your private playlists. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#playlist-read-private>`__ Parameters ---------- limit : int; keyword-only; optional Maximum number of playlists to return. **Valid range**: :code:`1` to :code:`50`. **API default**: :code:`20`. offset : int; keyword-only; optional Index of the first playlist to return. Use with `limit` to get the next batch of playlists. **Minimum value**: :code:`0`. **API default**: :code:`0`. Returns ------- playlists : dict[str, Any] Page of Spotify metadata for the current user's playlists. .. admonition:: Sample response :class: response dropdown .. code-block:: { "href": <str>, "items": [ { "collaborative": <bool>, "description": <str>, "external_urls": { "spotify": <str> }, "href": <str>, "id": <str>, "images": [ { "height": <int>, "url": <str>, "width": <int> } ], "name": <str>, "owner": { "display_name": <str>, "external_urls": { "spotify": <str> }, "href": <str>, "id": <str>, "type": <str>, "uri": <str> }, "public": <bool>, "snapshot_id": <str>, "tracks": { "href": <str>, "total": <int> }, "type": <str>, "uri": <str> } ], "limit": <int>, "next": <str>, "offset": <int>, "previous": <str>, "total": <int> } """ self._client._require_scopes( "playlists.get_my_playlists", "playlist-read-private" ) params = {} if limit is not None: self._validate_number("limit", limit, int, 1, 50) params["limit"] = limit if offset is not None: self._validate_number("offset", offset, int, 0) params["offset"] = offset return self._client._request( "GET", "me/playlists", params=params ).json()
[docs] @TTLCache.cached_method(ttl="user") def get_user_playlists( self, user_id: str | None = None, /, *, limit: int | None = None, offset: int | None = None, ) -> dict[str, Any]: """ `Playlists > Get Current User's Playlists <https://developer.spotify.com/documentation/web-api/reference /get-a-list-of-current-users-playlists>`__: Get Spotify catalog information for playlists owned or followed by the current user․ `Playlists > Get User's Playlists <https://developer.spotify.com /documentation/web-api/reference/get-list-users-playlists>`__: Get Spotify catalog information for playlists owned or followed by a user. .. admonition:: Authorization scopes :class: entitlement .. tab-set:: .. tab-item:: Conditional User authentication Access and manage your library. :code:`playlist-read-private` scope Access your private playlists. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#playlist-read-private>`__ :code:`playlist-read-collaborative` scope Access your collaborative playlists. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#playlist-read-collaborative>`__ Parameters ---------- user_id : str; positional-only; optional Spotify user ID. If not provided, the current user's playlists are returned. **Example**: :code:`"smedjan"`. limit : int; keyword-only; optional Maximum number of playlists to return. **Valid range**: :code:`1` to :code:`50`. **API default**: :code:`20`. offset : int; keyword-only; optional Index of the first playlist to return. Use with `limit` to get the next batch of playlists. **Minimum value**: :code:`0`. **API default**: :code:`0`. Returns ------- playlists : dict[str, Any] Page of Spotify metadata for the user's playlists. .. admonition:: Sample response :class: response dropdown .. code-block:: { "href": <str>, "items": [ { "collaborative": <bool>, "description": <str>, "external_urls": { "spotify": <str> }, "href": <str>, "id": <str>, "images": [ { "height": <int>, "url": <str>, "width": <int> } ], "name": <str>, "owner": { "display_name": <str>, "external_urls": { "spotify": <str> }, "href": <str>, "id": <str>, "type": <str>, "uri": <str> }, "public": <bool>, "snapshot_id": <str>, "tracks": { "href": <str>, "total": <int> }, "type": <str>, "uri": <str> } ], "limit": <int>, "next": <str>, "offset": <int>, "previous": <str>, "total": <int> } """ if user_id is None: return self.get_my_playlists(limit=limit, offset=offset) params = {} if limit is not None: self._validate_number("limit", limit, int, 1, 50) params["limit"] = limit if offset is not None: self._validate_number("offset", offset, int, 0) params["offset"] = offset self._validate_spotify_id(user_id, enforce_length=False) return self._client._request( "GET", f"users/{user_id}/playlists", params=params ).json()
[docs] @TTLCache.cached_method(ttl="user") def get_my_saved_shows( self, *, limit: int | None = None, offset: int | None = None ) -> dict[str, Any]: """ `Shows > Get User's Saved Shows <https://developer.spotify.com /documentation/web-api/reference/get-users-saved-shows>`_: Get Spotify catalog information for shows saved in the current user's library. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-library-read` scope Access your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-library-read>`__ Parameters ---------- limit : int; keyword-only; optional Maximum number of shows to return. **Valid range**: :code:`1` to :code:`50`. **API default**: :code:`20`. offset : int; keyword-only; optional Index of the first show to return. Use with `limit` to get the next batch of shows. **Minimum value**: :code:`0`. **API default**: :code:`0`. Returns ------- shows : dict[str, Any] Page of Spotify metadata for the user's saved shows. .. admonition:: Sample response :class: response dropdown .. code-block:: { "href": <str>, "items": [ { "added_at": <str>, "show": { "available_markets": <list[str]>, "copyrights": [ { "text": <str>, "type": <str> } ], "description": <str>, "explicit": <bool>, "external_urls": { "spotify": <str> }, "href": <str>, "html_description": <str>, "id": <str>, "images": [ { "height": <int>, "url": <str>, "width": <int> } ], "is_externally_hosted": <bool>, "languages": <list[str]>, "media_type": <str>, "name": <str>, "publisher": <str>, "total_episodes": <int>, "type": "show", "uri": <str> } } ], "limit": <int>, "next": <str>, "offset": <int>, "previous": <str>, "total": <int> } """ self._client._require_scopes( "shows.get_my_saved_shows", "user-library-read" ) return self._get_my_saved_entities("shows", limit=limit, offset=offset)
[docs] def save_shows(self, show_ids: str | Collection[str], /) -> None: """ `Shows > Save Shows for Current User <https://developer.spotify.com/documentation/web-api/reference /save-shows-user>`_: Save one or more shows to the current user's library. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-library-modify` scope Manage your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-library-modify>`__ Parameters ---------- show_ids : str or Collection[str]; positional-only Spotify IDs of the shows. A maximum of 50 IDs can be sent in a request. **Examples**: * :code:`"5CfCWKI5pZ28U0uOzXkDHe"` * :code:`"5CfCWKI5pZ28U0uOzXkDHe,5as3aKmN2k11yfDDDSrvaZ"` * :code:`[5CfCWKI5pZ28U0uOzXkDHe", "5as3aKmN2k11yfDDDSrvaZ"]` """ self._client._require_scopes("shows.save_shows", "user-library-modify") self._manage_saved_entities("PUT", "shows", show_ids)
[docs] def remove_saved_shows(self, show_ids: str | Collection[str], /) -> None: """ `Shows > Remove User's Saved Shows <https://developer.spotify.com/documentation/web-api/reference /remove-shows-user>`_: Remove one or more shows from the current user's library. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-library-modify` scope Manage your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-library-modify>`__ Parameters ---------- show_ids : str or Collection[str]; positional-only Spotify IDs of the shows. A maximum of 50 IDs can be sent in a request. **Examples**: * :code:`"5CfCWKI5pZ28U0uOzXkDHe"` * :code:`"5CfCWKI5pZ28U0uOzXkDHe,5as3aKmN2k11yfDDDSrvaZ"` * :code:`[5CfCWKI5pZ28U0uOzXkDHe", "5as3aKmN2k11yfDDDSrvaZ"]` """ self._client._require_scopes( "shows.remove_saved_shows", "user-library-modify" ) self._manage_saved_entities("DELETE", "shows", show_ids)
[docs] @TTLCache.cached_method(ttl="user") def are_shows_saved( self, show_ids: str | Collection[str], / ) -> list[bool]: """ `Shows > Check User's Saved Shows <https://developer.spotify.com/documentation/web-api/reference /check-users-saved-shows>`_: Check whether one or more shows are saved in the current user's library. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-library-read` scope Access your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-library-read>`__ Parameters ---------- show_ids : str or Collection[str]; positional-only Spotify IDs of the shows. A maximum of 50 IDs can be sent in a request. **Examples**: * :code:`"5CfCWKI5pZ28U0uOzXkDHe"` * :code:`"5CfCWKI5pZ28U0uOzXkDHe,5as3aKmN2k11yfDDDSrvaZ"` * :code:`[5CfCWKI5pZ28U0uOzXkDHe", "5as3aKmN2k11yfDDDSrvaZ"]` Returns ------- saved : list[bool] Whether the current user has the specified shows saved in their library. """ self._client._require_scopes( "shows.are_shows_saved", "user-library-read" ) return self._are_entities_saved("shows", show_ids)
[docs] @TTLCache.cached_method(ttl="user") def get_my_saved_tracks( self, *, country_code: str | None = None, limit: int | None = None, offset: int | None = None, ) -> dict[str, Any]: """ `Tracks > Get User's Saved Tracks <https://developer.spotify.com /documentation/web-api/reference/get-users-saved-tracks>`_: Get Spotify catalog information for tracks saved in the current user's library. .. admonition:: Authorization scope and third-party application mode :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-library-read` scope Access your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-library-read>`__ .. tab-item:: Optional Extended quota mode before November 27, 2024 Access 30-second preview URLs. `Learn more. <https://developer.spotify.com/blog /2024-11-27-changes-to-the-web-api>`__ Parameters ---------- country_code : str; keyword-only; optional ISO 3166-1 alpha-2 country code. If provided, only content available in that market is returned. When a user access token accompanies the request, the country associated with the user account takes priority over this parameter. .. note:: If neither a country code is provided nor a country can be determined from the user account, the content is considered unavailable for the client. **Example**: :code:`"ES"`. limit : int; keyword-only; optional Maximum number of tracks to return. **Valid range**: :code:`1` to :code:`50`. **API default**: :code:`20`. offset : int; keyword-only; optional Index of the first track to return. Use with `limit` to get the next batch of tracks. **Minimum value**: :code:`0`. **API default**: :code:`0`. Returns ------- tracks : dict[str, Any] Page of Spotify metadata for the user's saved tracks. .. admonition:: Sample response :class: response dropdown .. code-block:: { "href": <str>, "items": [ { "added_at": <str>, "track": { "album": { "album_type": <str>, "artists": [ { "external_urls": { "spotify": <str> }, "href": <str>, "id": <str>, "name": <str>, "type": "artist", "uri": <str> } ], "available_markets": <list[str]>, "external_urls": { "spotify": <str> }, "href": <str>, "id": <str>, "images": [ { "height": <int>, "url": <str>, "width": <int> } ], "name": <str>, "release_date": <str>, "release_date_precision": <str>, "restrictions": { "reason": <str> }, "total_tracks": <int>, "type": "album", "uri": <str> }, "artists": [ { "external_urls": { "spotify": <str> }, "href": <str>, "id": <str>, "name": <str>, "type": "artist", "uri": <str> } ], "available_markets": <list[str]>, "disc_number": <int>, "duration_ms": <int>, "explicit": <bool>, "external_ids": { "ean": <str>, "isrc": <str>, "upc": <str> }, "external_urls": { "spotify": <str> }, "href": <str>, "id": <str>, "is_local": <bool>, "is_playable": <bool>, "linked_from": <dict[str, Any]>, "name": <str>, "popularity": <int>, "preview_url": <str>, "restrictions": { "reason": <str> }, "track_number": <int>, "type": "track", "uri": <str> } } ], "limit": <int>, "next": <str>, "offset": <int>, "previous": <str>, "total": <int> } """ self._client._require_scopes( "tracks.get_my_saved_tracks", "user-library-read" ) return self._get_my_saved_entities( "tracks", country_code=country_code, limit=limit, offset=offset )
[docs] def save_tracks( self, track_ids: str | tuple[str, str | datetime] | dict[str, str | datetime] | list[str | tuple[str, str | datetime] | dict[str, str | datetime]], /, ) -> None: """ `Tracks > Save Tracks for Current User <https://developer.spotify.com/documentation/web-api/reference /save-tracks-user>`_: Save one or more tracks to the current user's library. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-library-modify` scope Manage your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-library-modify>`__ Parameters ---------- track_ids : str, tuple[str, str | datetime], \ dict[str, str | datetime], or \ list[str | tuple[str, str | datetime] | dict[str, str | datetime]]; \ positional-only Spotify IDs of the tracks, optionally accompanied by timestamps to maintain a chronological order in the user's library. A maximum of 50 IDs can be sent in one request. **Examples**: * :code:`"4iV5W9uYEdYUVa79Axb7Rh"` * :code:`("4iV5W9uYEdYUVa79Axb7Rh", "2010-01-01T00:00:00Z")` * :code:`{"id": "4iV5W9uYEdYUVa79Axb7Rh", "added_at": "2010-01-01T00:00:00Z"}` * .. code-block:: [ "4iV5W9uYEdYUVa79Axb7Rh", ("11dFghVXANMlKmJXsNCbNl", "2017-05-26T00:00:00Z"), { "id": "7ouMYWpwJ422jRcDASZB7P", "added_at": "2006-06-28T00:00:00Z" } ] """ self._client._require_scopes( "tracks.save_tracks", "user-library-modify" ) if isinstance(track_ids, str): track_ids = [ { "id": track_ids, "added_at": datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ"), } ] elif isinstance(track_ids, dict): track_ids = [track_ids] elif ( isinstance(track_ids, tuple) and len(track_ids) == 2 and ( timestamp_is_str := isinstance(track_ids[1], str) and len(track_ids[1]) == 20 or isinstance(track_ids[1], datetime) ) ): track_ids = [ { "id": track_ids[0], "added_at": track_ids[1] if timestamp_is_str else track_ids[1].strftime("%Y-%m-%dT%H:%M:%SZ"), } ] else: # list for idx, track in enumerate(track_ids): if isinstance(track, str): track_ids[idx] = { "id": track, "added_at": datetime.now().strftime( "%Y-%m-%dT%H:%M:%SZ" ), } elif isinstance(track, tuple): track_ids[idx] = { "id": track[0], "added_at": timestamp if isinstance(timestamp := track[1], str) else timestamp.strftime("%Y-%m-%dT%H:%M:%SZ"), } self._client._request( "PUT", "me/tracks", json={"timestamped_ids": track_ids} )
[docs] def remove_saved_tracks(self, track_ids: str | Collection[str], /) -> None: """ `Tracks > Remove User's Saved Tracks <https://developer.spotify.com/documentation/web-api/reference /remove-tracks-user>`_: Remove one or more tracks from the current user's library. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-library-modify` scope Manage your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-library-modify>`__ Parameters ---------- track_ids : str or Collection[str]; positional-only Spotify IDs of the tracks. A maximum of 50 IDs can be sent in a request. **Examples**: * :code:`"7ouMYWpwJ422jRcDASZB7P"` * :code:`"7ouMYWpwJ422jRcDASZB7P,4VqPOruhp5EdPBeR92t6lQ"` * :code:`["7ouMYWpwJ422jRcDASZB7P", "4VqPOruhp5EdPBeR92t6lQ"]` """ self._client._require_scopes( "tracks.remove_saved_tracks", "user-library-modify" ) self._manage_saved_entities("DELETE", "tracks", track_ids)
[docs] @TTLCache.cached_method(ttl="user") def are_tracks_saved( self, track_ids: str | Collection[str], / ) -> list[bool]: """ `Tracks > Check User's Saved Tracks <https://developer.spotify.com/documentation/web-api/reference /check-users-saved-tracks>`_: Check whether one or more tracks are saved in the current user's library. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-library-read` scope Access your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-library-read>`__ Parameters ---------- track_ids : str or Collection[str]; positional-only Spotify IDs of the tracks. A maximum of 50 IDs can be sent in a request. **Examples**: * :code:`"7ouMYWpwJ422jRcDASZB7P"` * :code:`"7ouMYWpwJ422jRcDASZB7P,4VqPOruhp5EdPBeR92t6lQ"` * :code:`["7ouMYWpwJ422jRcDASZB7P", "4VqPOruhp5EdPBeR92t6lQ"]` Returns ------- saved : list[bool] Whether the current user has the specified tracks saved in their library. """ self._client._require_scopes( "tracks.are_tracks_saved", "user-library-read" ) return self._are_entities_saved("tracks", track_ids)
[docs] def save_items(self, spotify_uris: str | Collection[str], /) -> None: """ `Library > Save Items to Library <https://developer.spotify.com /documentation/web-api/reference/save-library-items>`_: Save one or more items to the current user's library. .. admonition:: Authorization scopes :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-library-modify` scope Manage your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-library-modify>`__ :code:`user-follow-modify` scope Manage your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-follow-modify>`__ :code:`playlist-modify-public` scope Manage your public playlists. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#playlist-modify-public>`__ Parameters ---------- spotify_uris : str or Collection[str]; positional-only Comma-separated string or collection of Spotify URIs. A maximum of 40 URIs can be sent in a request. """ self._client._require_scopes( "library.save_items", { "user-library-modify", "user-follow-modify", "playlist-modify-public", }, ) self._client._request( "PUT", "me/library", params={ "uris": ",".join( self._prepare_spotify_uris( spotify_uris, limit=40, resource_types=self._RESOURCE_TYPES, ) ) }, )
[docs] def remove_saved_items( self, spotify_uris: str | Collection[str], / ) -> None: """ `Library > Remove Items from Library <https://developer.spotify.com/documentation/web-api/reference /remove-library-items>`_: Remove one or more items from the current user's library. .. admonition:: Authorization scopes :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-library-modify` scope Manage your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-library-modify>`__ :code:`user-follow-modify` scope Manage your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-follow-modify>`__ :code:`playlist-modify-public` scope Manage your public playlists. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#playlist-modify-public>`__ Parameters ---------- spotify_uris : str or Collection[str]; positional-only Comma-separated string or collection of Spotify URIs. A maximum of 40 URIs can be sent in a request. """ self._client._require_scopes( "library.remove_saved_items", { "user-library-modify", "user-follow-modify", "playlist-modify-public", }, ) self._client._request( "DELETE", "me/library", params={ "uris": ",".join( self._prepare_spotify_uris( spotify_uris, limit=40, resource_types=self._RESOURCE_TYPES, ) ) }, )
[docs] @TTLCache.cached_method(ttl="user") def are_items_saved( self, spotify_uris: str | Collection[str], / ) -> list[bool]: """ `Library > Check User's Saved Items <https://developer.spotify.com/documentation/web-api/reference /check-library-contains>`_: Check whether one or more items are saved in the current user's library. .. admonition:: Authorization scopes :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user-library-read` scope Access your saved content. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#user-library-read>`__ :code:`user-follow-read` scope Access your followers and who you are following. `Learn more. <https://developer.spotify.com /documentation/web-api/concepts /scopes#user-follow-read>`__ :code:`playlist-read-private` scope Access your private playlists. `Learn more. <https://developer.spotify.com/documentation/web-api /concepts/scopes#playlist-read-private>`__ Parameters ---------- spotify_uris : str or Collection[str]; positional-only Comma-separated string or collection of Spotify URIs. A maximum of 40 URIs can be sent in a request. Returns ------- saved : list[bool] Whether the current user has the specified albums saved in their library. """ self._client._require_scopes( "library.are_items_saved", { "user-library-read", "user-follow-read", "playlist-read-private", }, ) return self._client._request( "GET", "me/library/contains", params={ "uris": ",".join( self._prepare_spotify_uris( spotify_uris, limit=40, resource_types=self._RESOURCE_TYPES, ) ) }, )