Source code for minim.api.tidal._api.users

from __future__ import annotations
from typing import TYPE_CHECKING
import uuid

from ..._shared import TTLCache
from ._shared import TIDALResourceAPI

if TYPE_CHECKING:
    from typing import Any

    from ...._types import Collection


[docs] class UsersAPI(TIDALResourceAPI): """ User Collections, User Recommendations, and Users API endpoints for the TIDAL API. .. important:: This class is managed by :class:`~minim.api.tidal.TIDALAPIClient` and should not be instantiated directly. """ _COLLECTION_RELATIONSHIPS = { "albums", "artists", "owners", "playlists", "tracks", "videos", } _COLLECTION_RESOURCE_RELATIONSHIPS = {"items", "owners"} _RECOMMENDATION_RELATIONSHIPS = { "discoveryMixes", "myMixes", "newArrivalMixes", } __slots__ = () @classmethod def _prepare_resource_identifiers( cls, resource_type: str, resource_ids: int | str | dict[str, int | str] | Collection[int | str | dict[str, int | str]], /, *, recursive: bool = True, ) -> list[dict[str, str]]: """ Validate, normalize, and prepare a collection of resource identifiers. Parameters ---------- resource_type : str; positional-only Resource type. **Valid values**: :code:`"albums"`, :code:`"artists"`, :code:`"owners"`, :code:`"playlists"`, :code:`"tracks"`, :code:`"videos"`. resource_ids : str, dict[str, int | str], or \ Collection[int | str | dict[str, int | str]]; positional-only TIDAL IDs or UUIDs of the items. Returns ------- resource_identifiers : list[dict[str, str]] List of resource identifiers. """ if isinstance(resource_ids, dict): resource_id = resource_ids.get("id") if resource_ids.get("type") != resource_type: raise ValueError( f"The item with ID {resource_id!r} is not a " f"{resource_type[:-1]}." ) elif isinstance(resource_ids, int | str): resource_id = resource_ids else: num_resources = len(resource_ids) if not num_resources: raise ValueError( f"At least one {resource_type[:-1]} must be specified." ) if num_resources > 20: raise ValueError( f"A maximum of 20 {resource_type} can be sent in a request." ) return [ cls._prepare_resource_identifiers(resource_id, recursive=False) for resource_id in resource_ids ] resource_identifier = {"id": str(resource_id), "type": resource_type} if recursive: return [resource_identifier] return resource_identifier def _get_user_collection_relationship( self, relationship: str, /, *, collection_id: str | None = None, user_id: int | str | None = None, country_code: str | None = None, locale: str | None = None, include_metadata: bool = False, cursor: str | None = None, sort_by: str | None = None, descending: bool | None = None, sort_fields: set[str] | None = None, params: dict[str, Any] | None = None, ) -> dict[str, Any]: """ Get TIDAL catalog information for items of a resource type in a user's collection. Parameters ---------- relationship : str; positional-only Related resource type. **Valid values**: :code:`"albums"`, :code:`"artists"`, :code:`"owners"`, :code:`"playlists"`, :code:`"tracks"`, :code:`"videos"`. collection_id : str; keyword-only; optional TIDAL ID of the user collection. If authenticated, :code:`"me"` can be used in lieu of a TIDAL ID for the current user's collection. If not specified, :code:`"me"` is used. user_id : int or str; keyword-only; optional TIDAL ID of the user. If not specified, the TIDAL ID of current user is used. country_code : str; keyword-only; optional ISO 3166-1 alpha-2 country code. **Example**: :code:`"US"`. locale : str; keyword-only; optional IETF BCP 47 language tag. include_metadata : bool; keyword-only; default: :code:`False` Whether to include TIDAL metadata for the specified resource. cursor : str; keyword-only; optional Cursor for fetching the next page of results. **Example**: :code:`"3nI1Esi"`. sort_by : str; keyword-only; optional Field to sort the returned items by. descending : bool; keyword-only; optional Whether to sort in descending order. **API default**: :code:`False`. sort_fields : set[str]; keyword-only; optional Valid fields to sort by. params : dict[str, Any]; keyword-only; optional Query parameters to include in the request. If not provided, an empty dictionary will be created. .. note:: This `dict` is mutated in-place. Returns ------- resource : dict[str, Any] TIDAL metadata for the specified resource. """ if params is None: params = {} if user_id is None: id_ = collection_id has_user_id = False else: if collection_id is not None: raise ValueError( "At most one of 'collection_id' or 'user_id' can " "be provided." ) id_ = user_id has_user_id = True if has_user_id: if sort_by is not None: self._process_sort( sort_by, descending=descending, prefix=f"{relationship}.", sort_fields=sort_fields, params=params, ) return self._get_resource_relationship( "userCollections", id_ or self._client._my_profile["id"], relationship, country_code=country_code, locale=locale, include_metadata=include_metadata, cursor=cursor, params=params, ) if sort_by is not None: self._process_sort( sort_by, descending=descending, prefix="", sort_fields=sort_fields, params=params, ) return self._get_resource_relationship( f"userCollection{relationship.capitalize()}", id_ or "me", "items", country_code=country_code, locale=locale, include_metadata=include_metadata, cursor=cursor, params=params, ) def _modify_user_collection_resources( self, method: str, resource_type: str, resource_ids: str | dict[str, int | str] | Collection[int | str | dict[str, int | str]], /, *, collection_id: str | None = None, user_id: int | str | None = None, country_code: str | None = None, ) -> None: """ Add/remove items of a resource type to/from a user's collection. Parameters ---------- method : str; positional-only HTTP method. **Valid values**: :code:`"POST"`, :code:`"DELETE"`. resource_type : str; positional-only Resource type. **Valid values**: :code:`"albums"`, :code:`"artists"`, :code:`"owners"`, :code:`"playlists"`, :code:`"tracks"`, :code:`"videos"`. resource_ids : str, dict[str, int | str], or \ Collection[int | str | dict[str, int | str]]; positional-only TIDAL IDs, UUIDs, or resource identifiers of the items. collection_id : str; keyword-only; optional TIDAL ID of the user collection. If authenticated, :code:`"me"` can be used in lieu of a TIDAL ID for the current user's collection. If not specified, :code:`"me"` is used. user_id : int or str; keyword-only; optional TIDAL ID of the user. If not specified, the TIDAL ID of current user is used. country_code : str; optional ISO 3166-1 alpha-2 country code. """ if user_id is None: id_ = collection_id or "me" endpoint = ( f"userCollection{resource_type.capitalize()}" f"/{id_}/relationships/items" ) else: if collection_id is not None: raise ValueError( "At most one of 'collection_id' or 'user_id' can " "be provided." ) id_ = user_id or self._client._my_profile["id"] endpoint = f"userCollections/{id_}/relationships/{resource_type}" self._validate_tidal_ids(id_, recursive=False) params = {} if country_code is not None: self._validate_country_code(country_code) params["countryCode"] = country_code self._client._request( method, endpoint, headers={"Idempotency-Key": str(uuid.uuid4())}, params=params, json={ "data": self._prepare_resource_identifiers( resource_type, resource_ids ) }, )
[docs] @TTLCache.cached_method(ttl="user") def get_user_collection( self, user_id: int | str | None = None, /, *, country_code: str | None = None, locale: str | None = None, expand: str | Collection[str] | None = None, ) -> dict[str, Any]: """ `User Collections > Get Single User Collection <https://tidal-music.github.io/tidal-api-reference/# /userCollections/get_userCollections__id_>`_: Get TIDAL catalog information for a user's collection. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`collection.read` scope Read access to a user's collection. Parameters ---------- user_id : int or str; positional-only; optional TIDAL ID of the user. If not specified, the TIDAL ID of current user is used. country_code : str; keyword-only; optional ISO 3166-1 alpha-2 country code. **Example**: :code:`"US"`. locale : str; keyword-only; optional IETF BCP 47 language tag. expand : str or Collection[str]; keyword-only; optional Related resources to include metadata for in the response. **Valid values**: :code:`"albums"`, :code:`"artists"`, :code:`"owners"`, :code:`"playlists"`, :code:`"tracks"`, :code:`"videos"`. **Examples**: :code:`"albums"`, :code:`["tracks", "videos"]`. Returns ------- collection : dict[str, Any] TIDAL metadata for the user collection. .. admonition:: Sample response :class: response dropdown .. code-block:: { "data": { "attributes": {}, "id": <str>, "relationships": { "albums": { "data": [ { "id": <str>, "meta": { "addedAt": <str> }, "type": "albums" } ], "links": { "self": <str> } }, "artists": { "data": [ { "id": <str>, "meta": { "addedAt": <str> }, "type": "artists" } ], "links": { "self": <str> } }, "owners": { "data": [ { "id": <str>, "type": "users" } ], "links": { "self": <str> } }, "playlists": { "data": [ { "id": <str>, "meta": { "addedAt": <str> }, "type": "playlists" } ], "links": { "self": <str> } }, "tracks": { "data": [ { "id": <str>, "meta": { "addedAt": <str> }, "type": "tracks" } ], "links": { "self": <str> } }, "videos": { "data": [ { "id": <str>, "meta": { "addedAt": <str> }, "type": "videos" } ], "links": { "self": <str> } } }, "type": "userCollections" }, "included": [ { "attributes": { "accessType": <str>, "availability": <list[str]>, "barcodeId": <str>, "copyright": { "text": <str> }, "duration": <str>, "explicit": <bool>, "externalLinks": [ { "href": <str>, "meta": { "type": <str> } } ], "mediaTags": <list[str]>, "numberOfItems": <int>, "numberOfVolumes": <int>, "popularity": <float>, "releaseDate": <str>, "title": <str>, "type": "ALBUM" }, "id": <str>, "relationships": { "artists": { "links": { "self": <str> } }, "coverArt": { "links": { "self": <str> } }, "genres": { "links": { "self": <str> } }, "items": { "links": { "self": <str> } }, "owners": { "links": { "self": <str> } }, "providers": { "links": { "self": <str> } }, "similarAlbums": { "links": { "self": <str> } }, "suggestedCoverArts" : { "links": { "self": <str> } } }, "type": "albums" }, { "attributes": { "contributionsEnabled": <bool>, "externalLinks": [ { "href": <str>, "meta": { "type": <str> } } ], "name": <str>, "popularity": <float>, "spotlighted": <bool> }, "id": <str>, "relationships": { "albums": { "links": { "self": <str> } }, "biography": { "links": { "self": <str> } }, "followers": { "links": { "self": <str> } }, "following": { "links": { "self": <str> } }, "owners": { "links": { "self": <str> } }, "profileArt": { "links": { "self": <str> } }, "radio": { "links": { "self": <str> } }, "roles": { "links": { "self": <str> } }, "similarArtists": { "links": { "self": <str> } }, "trackProviders": { "links": { "self": <str> } }, "tracks": { "links": { "self": <str> } }, "videos": { "links": { "self": <str> } } }, "type": "artists" }, { "attributes": { "accessType": <str>, "bounded": <bool>, "createdAt": <str>, "description": <str>, "duration": <str>, "externalLinks": [ { "href": <str>, "meta": { "type": <str> } } ], "lastModifiedAt": <str>, "name": <str>, "numberOfItems": <int>, "playlistType": "USER" }, "id": <str>, "relationships": { "coverArt": { "links": { "self": <str> } }, "items": { "links": { "self": <str> } }, "owners": { "links": { "self": <str> } } }, "type": "playlists" }, { "attributes": { "accessType": <str>, "availability": <list[str]>, "bpm": <float>, "copyright": { "text": <str> }, "duration": <str>, "explicit": <bool>, "externalLinks": [ { "href": <str>, "meta": { "type": <str> } } ], "isrc": <str>, "key": <str>, "keyScale": <str>, "mediaTags": <list[str]>, "popularity": <float>, "spotlighted": <bool>, "title": <str>, "toneTags": <list[str]>, "version": <str> }, "id": <str>, "relationships": { "albums": { "links": { "self": <str> } }, "artists": { "links": { "self": <str> } }, "genres": { "links": { "self": <str> } }, "lyrics": { "links": { "self": <str> } }, "owners": { "links": { "self": <str> } }, "providers": { "links": { "self": <str> } }, "radio": { "links": { "self": <str> } }, "shares": { "links": { "self": <str> } }, "similarTracks": { "links": { "self": <str> } }, "sourceFile": { "links": { "self": <str> } }, "trackStatistics": { "links": { "self": <str> } } }, "type": "tracks" }, { "attributes": { "country": <str>, "email": <str>, "emailVerified": <bool>, "firstName": <str>, "username": <str> }, "id": <str>, "type": "users" }, { "attributes": { "availability": <list[str]>, "copyright": { "text": <str> }, "duration": <str>, "explicit": <bool>, "externalLinks": [ { "href": <str>, "meta": { "type": <str> } } ], "isrc": <str>, "popularity": <float> "releaseDate": <str>, "title": <str> }, "id": <str>, "relationships": { "albums": { "links": { "self": <str> } }, "artists": { "links": { "self": <str> } }, "providers": { "links": { "self": <str> } }, "thumbnailArt": { "links": { "self": <str> } } }, "type": "videos" } ], "links": { "self": <str> } } """ self._client._require_scopes( "users.get_user_collection", "collection.read" ) if user_id is None: user_id = self._client._my_profile["id"] else: self._validate_tidal_ids(user_id, recursive=False) return self._get_resources( "userCollections", user_id, country_code=country_code, locale=locale, expand=expand, )
[docs] @TTLCache.cached_method(ttl="static") def get_user_collection_owners( self, user_id: int | str | None = None, /, *, include_metadata: bool = False, cursor: str | None = None, ) -> dict[str, Any]: """ `User Collections > Get Owners Relationship <https://tidal-music.github.io/tidal-api-reference/# /userCollections /get_userCollections__id__relationships_owners>`_: Get TIDAL profile information for the owners of a user collection resource. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`collection.read` scope Read access to a user's collection. Parameters ---------- user_id : int or str; positional-only; optional TIDAL ID of the user. If not specified, the TIDAL ID of current user is used. include_metadata : bool; keyword-only; default: :code:`False` Whether to include metadata for the owners. cursor : str; keyword-only; optional Cursor for fetching the next page of results. **Example**: :code:`"3nI1Esi"`. Returns ------- owners : dict[str, Any] Page of TIDAL profile information for the user collection resource's owners. .. admonition:: Sample response :class: response dropdown .. code-block:: { "data": { "id": <str>, "type": "users" }, "included": [ { "attributes": { "country": <str>, "email": <str>, "emailVerified": <bool>, "firstName": <str>, "username": <str> }, "id": <str>, "type": "users" } ], "links": { "self": <str> } } """ self._client._require_scopes( "users.get_user_collection_owners", "collection.read" ) if user_id is None: user_id = self._client._my_profile["id"] else: self._validate_tidal_ids(user_id, recursive=False) return self._get_resource_relationship( "userCollections", user_id, "owners", include_metadata=include_metadata, cursor=cursor, )
[docs] @TTLCache.cached_method(ttl="user") def get_album_collection( self, collection_id: str | None = None, /, *, country_code: str | None = None, locale: str | None = None, expand: str | Collection[str] | None = None, ) -> dict[str, Any]: """ `User Collection Albums > Get User Album Collection <https://tidal-music.github.io/tidal-api-reference/# /userCollectionAlbums/get_userCollectionAlbums__id_>`_: Get TIDAL catalog information for a user album collection. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`collection.read` scope Read access to a user's collection. Parameters ---------- collection_id : str; positional-only; optional TIDAL ID of the user collection. If authenticated, :code:`"me"` can be used in lieu of a TIDAL ID for the current user's collection. If not specified, :code:`"me"` is used. country_code : str; keyword-only; optional ISO 3166-1 alpha-2 country code. **Example**: :code:`"US"`. locale : str; keyword-only; optional IETF BCP 47 language tag. expand : str or Collection[str]; keyword-only; optional Related resources to include metadata for in the response. **Valid values**: :code:`"items"`, :code:`"owners"`. **Examples**: :code:`"items"`, :code:`["items", "owners"]`. Returns ------- collection : dict[str, Any] TIDAL metadata for the user album collection. .. admonition:: Sample response :class: response dropdown .. code-block:: { "data": { "attributes": {}, "id": <str>, "relationships": { "items": { "data": [ { "id": <str>, "meta": { "addedAt": <str> }, "type": "albums" } ], "links": { "self": <str> } }, "owners": { "data": [ { "id": <str>, "type": "users" } ], "links": { "self": <str> } } }, "type": "userCollectionAlbums" }, "included": [ { "attributes": { "accessType": <str>, "albumType": <str>, "availability": <list[str]>, "barcodeId": <str>, "copyright": { "text": <str> }, "duration": <str>, "explicit": <bool>, "externalLinks": [ { "href": <str>, "meta": { "type": <str> } } ], "mediaTags": <list[str]>, "numberOfItems": <int>, "numberOfVolumes": <int>, "popularity": <float>, "releaseDate": <str>, "title": <str>, "type": <str> }, "id": <str>, "relationships": { "artists": { "links": { "self": <str> } }, "coverArt": { "links": { "self": <str> } }, "genres": { "links": { "self": <str> } }, "items": { "links": { "self": <str> } }, "owners": { "links": { "self": <str> } }, "providers": { "links": { "self": <str> } }, "similarAlbums": { "links": { "self": <str> } }, "suggestedCoverArts": { "links": { "self": <str> } } }, "type": "albums" }, { "attributes": { "country": <str>, "email": <str>, "emailVerified": <bool>, "firstName": <str>, "username": <str> }, "id": <str>, "type": "users" } ], "links": { "self": <str> } } """ self._client._require_scopes( "users.get_album_collection", "collection.read" ) return self._get_resources( "userCollectionAlbums", collection_id or "me", country_code=country_code, locale=locale, expand=expand, relationships=self._COLLECTION_RESOURCE_RELATIONSHIPS, )
[docs] @TTLCache.cached_method(ttl="user") def get_user_saved_albums( self, *, collection_id: str | None = None, user_id: int | str | None = None, country_code: str | None = None, locale: str | None = None, include_metadata: bool = False, cursor: str | None = None, sort_by: str | None = None, descending: bool | None = None, ) -> dict[str, Any]: """ `User Collection Albums > Get Items Relationship <https://tidal-music.github.io/tidal-api-reference/# /userCollectionAlbums/get_userCollectionAlbums__id_>`_: Get TIDAL catalog information for albums in a user collection․ `User Collections > Get Albums Relationship <https://tidal-music.github.io/tidal-api-reference/# /userCollections /get_userCollections__id__relationships_albums>`_: Get TIDAL catalog information for albums in a user's collection. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`collection.read` scope Read access to a user's collection. .. important:: At most one of `collection_id` or `user_id` must be provided. If `user_id` is provided, the legacy :code:`GET /userCollections/{user_id}/relationships/albums` endpoint is used. Parameters ---------- collection_id : str; keyword-only; optional TIDAL ID of the user collection. If authenticated, :code:`"me"` can be used in lieu of a TIDAL ID for the current user's collection. If not specified, :code:`"me"` is used. user_id : int or str; keyword-only; optional TIDAL ID of the user. country_code : str; keyword-only; optional ISO 3166-1 alpha-2 country code. **Example**: :code:`"US"`. locale : str; keyword-only; optional IETF BCP 47 language tag. include_metadata : bool; keyword-only; default: :code:`False` Whether to include metadata for the albums in the user's collection. cursor : str; keyword-only; optional Cursor for fetching the next page of results. **Example**: :code:`"3nI1Esi"`. sort_by : str; keyword-only; optional Field to sort the albums by. **Valid values**: :code:`"addedAt"`, :code:`"artists.name`, :code:`"releaseDate"`, :code:`"title"`. descending : bool; keyword-only; optional Whether to sort in descending order. **API default**: :code:`False`. Returns ------- albums : dict[str, Any] Page of TIDAL metadata for the albums in the user's collection. .. admonition:: Sample response :class: response dropdown .. code:: { "data": [ { "id": <str>, "meta": { "addedAt": <str> }, "type": "albums" } ], "included": [ { "attributes": { "accessType": <str>, "availability": <list[str]>, "barcodeId": <str>, "copyright": { "text": <str> }, "duration": <str>, "explicit": <bool>, "externalLinks": [ { "href": <str>, "meta": { "type": <str> } } ], "mediaTags": <list[str]>, "numberOfItems": <int>, "numberOfVolumes": <int>, "popularity": <float>, "releaseDate": <str>, "title": <str>, "type": "ALBUM" }, "id": <str>, "relationships": { "artists": { "links": { "self": <str> } }, "coverArt": { "links": { "self": <str> } }, "genres": { "links": { "self": <str> } }, "items": { "links": { "self": <str> } }, "owners": { "links": { "self": <str> } }, "providers": { "links": { "self": <str> } }, "similarAlbums": { "links": { "self": <str> } }, "suggestedCoverArts" : { "links": { "self": <str> } } }, "type": "albums" } ], "links": { "meta": { "nextCursor": <str> }, "next": <str>, "self": <str> } } """ self._client._require_scopes( "users.get_user_saved_albums", "collection.read" ) return self._get_user_collection_relationship( "albums", collection_id=collection_id, user_id=user_id, country_code=country_code, locale=locale, include_metadata=include_metadata, cursor=cursor, sort_by=sort_by, descending=descending, sort_fields={"addedAt", "artists.name", "releaseDate", "title"}, )
[docs] def save_albums( self, album_ids: int | str | dict[str, int | str] | Collection[int | str | dict[str, int | str]], /, *, collection_id: str | None = None, user_id: int | str | None = None, country_code: str | None = None, ) -> None: """ `User Collection Albums > Add to Items Relationship <https://tidal-music.github.io/tidal-api-reference/# /userCollectionAlbums /post_userCollectionAlbums__id__relationships_items>`_: Add albums to a user collection․ `User Collections > Add to Albums Relationship <https://tidal-music.github.io/tidal-api-reference/# /userCollections /post_userCollections__id__relationships_albums>`_: Add albums to a user's collection. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`collection.write` scope Write access to a user's collection. .. important:: At most one of `collection_id` or `user_id` must be provided. If `user_id` is provided, the legacy :code:`POST /userCollections/{user_id}/relationships/albums` endpoint is used. Parameters ---------- album_ids : int, str, dict[str, int | str], or \ Collection[int | str | dict[str, int | str]]; positional-only TIDAL IDs and/or resource identifiers of the albums. **Examples**: * :code:`46369321` * :code:`"251380836"` * :code:`{"id": "46369321", "types": "albums"}` * :code:`["251380836", {"id": "46369321", "types": "albums"}]` collection_id : str; keyword-only; optional TIDAL ID of the user collection. If authenticated, :code:`"me"` can be used in lieu of a TIDAL ID for the current user's collection. If not specified, :code:`"me"` is used. user_id : int or str; keyword-only; optional TIDAL ID of the user. country_code : str; keyword-only; optional ISO 3166-1 alpha-2 country code. **Example**: :code:`"US"`. """ self._client._require_scopes("users.save_albums", "collection.write") self._modify_user_collection_resources( "POST", "albums", album_ids, collection_id=collection_id, user_id=user_id, country_code=country_code, )
[docs] def remove_saved_albums( self, album_ids: int | str | dict[str, int | str] | Collection[int | str | dict[str, int | str]], /, *, collection_id: str | None = None, user_id: int | str | None = None, ) -> None: """ `User Collection Albums > Delete from Items Relationship <https://tidal-music.github.io/tidal-api-reference/# /userCollectionAlbums /delete_userCollectionAlbums__id__relationships_items>`_: Remove albums from a user collection․ `User Collections > Delete from Albums Relationship <https://tidal-music.github.io/tidal-api-reference/# /userCollections /delete_userCollections__id__relationships_albums>`_: Remove albums from a user's collection. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`collection.write` scope Write access to a user's collection. .. important:: At most one of `collection_id` or `user_id` must be provided. If `user_id` is provided, the legacy :code:`DELETE /userCollections/{user_id}/relationships /albums` endpoint is used. Parameters ---------- album_ids : int, str, dict[str, int | str], or \ Collection[int | str | dict[str, int | str]]; positional-only TIDAL IDs and/or resource identifiers of the albums. **Examples**: * :code:`46369321` * :code:`"251380836"` * :code:`{"id": "46369321", "types": "albums"}` * :code:`["251380836", {"id": "46369321", "types": "albums"}]` collection_id : str; keyword-only; optional TIDAL ID of the user collection. If authenticated, :code:`"me"` can be used in lieu of a TIDAL ID for the current user's collection. If not specified, :code:`"me"` is used. user_id : int or str; keyword-only; optional TIDAL ID of the user. """ self._client._require_scopes( "users.remove_saved_albums", "collection.write" ) self._modify_user_collection_resources( "DELETE", "albums", album_ids, collection_id=collection_id, user_id=user_id, )
[docs] @TTLCache.cached_method(ttl="user") def get_artist_collection( self, collection_id: str | None = None, /, *, country_code: str | None = None, locale: str | None = None, expand: str | Collection[str] | None = None, ) -> dict[str, Any]: """ `User Collection Artists > Get User Artist Collection <https://tidal-music.github.io/tidal-api-reference/# /userCollectionArtists/get_userCollectionArtists__id_>`_: Get TIDAL catalog information for a user artist collection. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`collection.read` scope Read access to a user's collection. Parameters ---------- collection_id : str; positional-only; optional TIDAL ID of the user collection. If authenticated, :code:`"me"` can be used in lieu of a TIDAL ID for the current user's collection. If not specified, :code:`"me"` is used. country_code : str; keyword-only; optional ISO 3166-1 alpha-2 country code. **Example**: :code:`"US"`. locale : str; keyword-only; optional IETF BCP 47 language tag. expand : str or Collection[str]; keyword-only; optional Related resources to include metadata for in the response. **Valid values**: :code:`"items"`, :code:`"owners"`. **Examples**: :code:`"items"`, :code:`["items", "owners"]`. Returns ------- collection : dict[str, Any] TIDAL metadata for the user artist collection. .. admonition:: Sample response :class: response dropdown .. code-block:: { "data": { "attributes": {}, "id": <str>, "relationships": { "items": { "data": [ { "id": <str>, "meta": { "addedAt": <str> }, "type": "artists" } ], "links": { "self": <str> } }, "owners": { "data": [ { "id": <str>, "type": "users" } ], "links": { "self": <str> } } }, "type": "userCollectionArtists" }, "included": [ { "attributes": { "externalLinks": [ { "href": <str>, "meta": { "type": <str> } } ], "name": <str>, "popularity": <float> }, "id": <str>, "relationships": { "albums": { "links": { "self": <str> } }, "biography": { "links": { "self": <str> } }, "followers": { "links": { "self": <str> } }, "following": { "links": { "self": <str> } }, "owners": { "links": { "self": <str> } }, "profileArt": { "links": { "self": <str> } }, "radio": { "links": { "self": <str> } }, "roles": { "links": { "self": <str> } }, "similarArtists": { "links": { "self": <str> } }, "trackProviders": { "links": { "self": <str> } }, "tracks": { "links": { "self": <str> } }, "videos": { "links": { "self": <str> } } }, "type": "artists" }, { "attributes": { "country": <str>, "email": <str>, "emailVerified": <bool>, "firstName": <str>, "username": <str> }, "id": <str>, "type": "users" } ], "links": { "self": <str> } } """ self._client._require_scopes( "users.get_artist_collection", "collection.read" ) return self._get_resources( "userCollectionArtists", collection_id or "me", country_code=country_code, locale=locale, expand=expand, relationships=self._COLLECTION_RESOURCE_RELATIONSHIPS, )
[docs] @TTLCache.cached_method(ttl="user") def get_user_followed_artists( self, *, collection_id: str | None = None, user_id: int | str | None = None, country_code: str | None = None, locale: str | None = None, include_metadata: bool = False, cursor: str | None = None, sort_by: str | None = None, descending: bool | None = None, ) -> dict[str, Any]: """ `User Collection Artists > Get Items Relationship <https://tidal-music.github.io/tidal-api-reference/# /userCollectionArtists/get_userCollectionArtists__id_>`_: Get TIDAL catalog information for artists in a user collection․ `User Collections > Get Artists Relationship <https://tidal-music.github.io/tidal-api-reference/# /userCollections /get_userCollections__id__relationships_artists>`_: Get TIDAL catalog information for artists in a user's collection. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`collection.read` scope Read access to a user's collection. .. important:: At most one of `collection_id` or `user_id` must be provided. If `user_id` is provided, the legacy :code:`GET /userCollections/{user_id}/relationships/artists` endpoint is used. Parameters ---------- collection_id : str; keyword-only; optional TIDAL ID of the user collection. If authenticated, :code:`"me"` can be used in lieu of a TIDAL ID for the current user's collection. If not specified, :code:`"me"` is used. user_id : int or str; keyword-only; optional TIDAL ID of the user. country_code : str; keyword-only; optional ISO 3166-1 alpha-2 country code. **Example**: :code:`"US"`. locale : str; keyword-only; optional IETF BCP 47 language tag. include_metadata : bool; keyword-only; default: :code:`False` Whether to include metadata for the artists in the user's collection. cursor : str; keyword-only; optional Cursor for fetching the next page of results. **Example**: :code:`"3nI1Esi"`. sort_by : str; keyword-only; optional Field to sort the artists by. **Valid values**: :code:`"addedAt"`, :code:`"name"`. descending : bool; keyword-only; optional Whether to sort in descending order. **API default**: :code:`False`. Returns ------- artists : dict[str, Any] Page of TIDAL metadata for the artists in the user's collection. .. admonition:: Sample response :class: response dropdown .. code-block:: { "data": [ { "id": <str>, "meta": { "addedAt": <str> }, "type": "artists" } ], "included": [ { "attributes": { "contributionsEnabled": <bool>, "externalLinks": [ { "href": <str>, "meta": { "type": <str> } } ], "name": <str>, "popularity": <float>, "spotlighted": <bool> }, "id": <str>, "relationships": { "albums": { "links": { "self": <str> } }, "biography": { "links": { "self": <str> } }, "followers": { "links": { "self": <str> } }, "following": { "links": { "self": <str> } }, "owners": { "links": { "self": <str> } }, "profileArt": { "links": { "self": <str> } }, "radio": { "links": { "self": <str> } }, "roles": { "links": { "self": <str> } }, "similarArtists": { "links": { "self": <str> } }, "trackProviders": { "links": { "self": <str> } }, "tracks": { "links": { "self": <str> } }, "videos": { "links": { "self": <str> } } }, "type": "artists" } ], "links": { "meta": { "nextCursor": <str> }, "next": <str>, "self": <str> } } """ self._client._require_scopes( "users.get_user_followed_artists", "collection.read" ) return self._get_user_collection_relationship( "artists", collection_id=collection_id, user_id=user_id, country_code=country_code, locale=locale, include_metadata=include_metadata, cursor=cursor, sort_by=sort_by, descending=descending, sort_fields={"addedAt", "name"}, )
[docs] def follow_artists( self, artist_ids: int | str | dict[str, int | str] | Collection[int | str | dict[str, int | str]], /, *, collection_id: str | None = None, user_id: int | str | None = None, country_code: str | None = None, ) -> None: """ `User Collection Artists > Add to Items Relationship <https://tidal-music.github.io/tidal-api-reference/# /userCollectionArtists /post_userCollectionArtists__id__relationships_items>`_: Add artists to a user collection․ `User Collections > Add to Artists Relationship <https://tidal-music.github.io/tidal-api-reference/# /userCollections /post_userCollections__id__relationships_artists>`_: Add artists to a user's collection. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`collection.write` scope Write access to a user's collection. .. important:: At most one of `collection_id` or `user_id` must be provided. If `user_id` is provided, the legacy :code:`POST /userCollections/{user_id}/relationships/artists` endpoint is used. Parameters ---------- artist_ids : int, str, dict[str, int | str], or \ Collection[int | str | dict[str, int | str]]; positional-only TIDAL IDs and/or resource identifiers of the artists. **Examples**: * :code:`1566` * :code:`"4676988"` * :code:`{"id": "1566", "types": "artists"}` * :code:`["4676988", {"id": "46369321", "types": "artists"}]` collection_id : str; keyword-only; optional TIDAL ID of the user collection. If authenticated, :code:`"me"` can be used in lieu of a TIDAL ID for the current user's collection. If not specified, :code:`"me"` is used. user_id : int or str; keyword-only; optional TIDAL ID of the user. country_code : str; keyword-only; optional ISO 3166-1 alpha-2 country code. **Example**: :code:`"US"`. """ self._client._require_scopes( "users.follow_artists", "collection.write" ) self._modify_user_collection_resources( "POST", "artists", artist_ids, collection_id=collection_id, user_id=user_id, country_code=country_code, )
[docs] def unfollow_artists( self, artist_ids: int | str | dict[str, int | str] | Collection[int | str | dict[str, int | str]], /, *, collection_id: str | None = None, user_id: int | str | None = None, ) -> None: """ `User Collection Artists > Delete from Items Relationship <https://tidal-music.github.io/tidal-api-reference/# /userCollectionArtists /delete_userCollectionArtists__id__relationships_items>`_: Remove artists from a user collection․ `User Collections > Delete from Items Relationship <https://tidal-music.github.io/tidal-api-reference/# /userCollections /delete_userCollections__id__relationships_artists>`_: Remove artists from a user's collection. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`collection.write` scope Write access to a user's collection. .. important:: At most one of `collection_id` or `user_id` must be provided. If `user_id` is provided, the legacy :code:`DELETE /userCollections/{user_id}/relationships /artists` endpoint is used. Parameters ---------- artist_ids : int, str, dict[str, int | str], or \ Collection[int | str | dict[str, int | str]]; positional-only TIDAL IDs and/or resource identifiers of the artists. **Examples**: * :code:`1566` * :code:`"4676988"` * :code:`{"id": "1566", "types": "artists"}` * :code:`["4676988", {"id": "46369321", "types": "artists"}]` collection_id : str; keyword-only; optional TIDAL ID of the user collection. If authenticated, :code:`"me"` can be used in lieu of a TIDAL ID for the current user's collection. If not specified, :code:`"me"` is used. user_id : int or str; keyword-only; optional TIDAL ID of the user. """ self._client._require_scopes( "users.unfollow_artists", "collection.write" ) self._modify_user_collection_resources( "DELETE", "artists", artist_ids, collection_id=collection_id, user_id=user_id, )
[docs] @TTLCache.cached_method(ttl="user") def get_playlist_collection( self, collection_id: str | None = None, /, *, country_code: str | None = None, locale: str | None = None, expand: str | Collection[str] | None = None, ) -> dict[str, Any]: """ `User Collection Playlists > Get User Playlist Collection <https://tidal-music.github.io/tidal-api-reference/# /userCollectionPlaylists/get_userCollectionPlaylists__id_>`_: Get TIDAL catalog information for a user playlist collection. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`collection.read` scope Read access to a user's collection. Parameters ---------- collection_id : str; positional-only; optional TIDAL ID of the user collection. If authenticated, :code:`"me"` can be used in lieu of a TIDAL ID for the current user's collection. If not specified, :code:`"me"` is used. country_code : str; keyword-only; optional ISO 3166-1 alpha-2 country code. **Example**: :code:`"US"`. locale : str; keyword-only; optional IETF BCP 47 language tag. expand : str or Collection[str]; keyword-only; optional Related resources to include metadata for in the response. **Valid values**: :code:`"items"`, :code:`"owners"`. **Examples**: :code:`"items"`, :code:`["items", "owners"]`. Returns ------- collection : dict[str, Any] TIDAL metadata for the user playlist collection. .. admonition:: Sample response :class: response dropdown .. code-block:: { "data": { "attributes": {}, "id": <str>, "relationships": { "items": { "data": [ { "id": <str>, "meta": { "addedAt": <str> }, "type": "playlists" } ], "links": { "self": <str> } }, "owners": { "data": [ { "id": <str>, "type": "users" } ], "links": { "self": <str> } } }, "type": "userCollectionPlaylists" }, "included": [ { "attributes": { "accessType": <str>, "bounded": <bool>, "createdAt": <str>, "description": <str>, "duration": "PT3H16M5S", "externalLinks": [ { "href": <str>, "meta": { "type": <str> } } ], "lastModifiedAt": <str>, "name": <str>, "numberOfFollowers": <int>, "numberOfItems": <int>, "playlistType": <str> }, "id": <str>, "relationships": { "coverArt": { "links": { "self": <str> } }, "items": { "links": { "self": <str> } }, "ownerProfiles": { "links": { "self": <str> } }, "owners": { "links": { "self": <str> } } }, "type": "playlists" } ], "links": { "self": <str> } } """ self._client._require_scopes( "users.get_playlist_collection", "collection.read" ) return self._get_resources( "userCollectionPlaylists", collection_id or "me", country_code=country_code, locale=locale, expand=expand, relationships=self._COLLECTION_RESOURCE_RELATIONSHIPS, )
[docs] @TTLCache.cached_method(ttl="user") def get_user_followed_playlists( self, *, collection_id: str | None = None, user_id: int | str | None = None, include_folders: bool = False, include_metadata: bool = False, cursor: str | None = None, sort_by: str | None = None, descending: bool | None = None, ) -> dict[str, Any]: """ `User Collection Playlists > Get Items Relationship <https://tidal-music.github.io/tidal-api-reference/# /userCollectionPlaylists/get_userCollectionPlaylists__id_>`_: Get TIDAL catalog information for playlists in a user collection․ `User Collections > Get Playlists Relationship <https://tidal-music.github.io/tidal-api-reference/# /userCollections /get_userCollections__id__relationships_playlists>`_: Get TIDAL catalog information for playlists in a user's collection. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`collection.read` scope Read access to a user's collection. .. important:: At most one of `collection_id` or `user_id` must be provided. If `user_id` is provided, the legacy :code:`GET /userCollections/{user_id}/relationships /playlists` endpoint is used. Parameters ---------- collection_id : str; keyword-only; optional TIDAL ID of the user collection. If authenticated, :code:`"me"` can be used in lieu of a TIDAL ID for the current user's collection. If not specified, :code:`"me"` is used. user_id : int or str; keyword-only; optional TIDAL ID of the user. include_folders : bool; keyword-only; default: :code:`False` Whether to include metadata for playlist folders in the user's collection. include_metadata : bool; keyword-only; default: :code:`False` Whether to include metadata for the playlists in the user's collection. cursor : str; keyword-only; optional Cursor for fetching the next page of results. **Example**: :code:`"3nI1Esi"`. sort_by : str; keyword-only; optional Field to sort the playlists by. **Valid values**: :code:`"addedAt"`, :code:`"lastUpdatedAt"`, :code:`"name"`. descending : bool; keyword-only; optional Whether to sort in descending order. **API default**: :code:`False`. Returns ------- playlists : dict[str, Any] Page of TIDAL metadata for the playlists (and playlist folders) in the user's collection. .. admonition:: Sample response :class: response dropdown .. code-block:: { "data": [ { "id": <str>, "meta": { "addedAt": <str> }, "type": "playlists" }, { "id": <str>, "meta": { "addedAt": <str> }, "type": "userCollectionFolders" } ], "included": [ { "attributes": { "accessType": <str>, "bounded": <bool>, "createdAt": <str>, "description": <str>, "duration": <str>, "externalLinks": [ { "href": <str>, "meta": { "type": <str> } } ], "lastModifiedAt": <str>, "name": <str>, "numberOfItems": <int>, "playlistType": "USER" }, "id": <str>, "relationships": { "coverArt": { "links": { "self": <str> } }, "items": { "links": { "self": <str> } }, "owners": { "links": { "self": <str> } } }, "type": "playlists" } ], "links": { "meta": { "nextCursor": <str> }, "next": <str>, "self": <str> } } """ self._client._require_scopes( "users.get_user_followed_playlists", "collection.read" ) params = {} if include_folders: params["collectionView"] = "FOLDERS" return self._get_user_collection_relationship( "playlists", collection_id=collection_id, user_id=user_id, include_metadata=include_metadata, cursor=cursor, sort_by=sort_by, descending=descending, sort_fields={"addedAt", "lastUpdatedAt", "name"}, params=params, )
[docs] def follow_playlists( self, playlist_uuids: str | dict[str, str] | Collection[str | dict[str, str]], /, *, collection_id: str | None = None, user_id: int | str | None = None, ) -> None: """ `User Collection Playlists > Add to Items Relationship <https://tidal-music.github.io/tidal-api-reference/# /userCollectionPlaylists /post_userCollectionPlaylists__id__relationships_items>`_: Add playlists to a user collection․ `User Collections > Add Playlists to User's Collection <https://tidal-music.github.io/tidal-api-reference/# /userCollections /post_userCollections__id__relationships_playlists>`_: Add playlists to a user's collection. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`collection.write` scope Write access to a user's collection. .. important:: At most one of `collection_id` or `user_id` must be provided. If `user_id` is provided, the legacy :code:`POST /userCollections/{user_id}/relationships /playlists` endpoint is used. Parameters ---------- playlist_uuids : str, dict[str, str], or \ Collection[str | dict[str, str]]; positional-only UUIDs and/or resource identifiers of the playlists. **Examples**: * :code:`"f0d6f5c4-081f-4348-9b65-ae677d92767b"` * .. code-block:: { "id": "1e4c73df-b805-47cd-9e44-9a8721c5cb45", "types": "playlists" } * .. code-block:: [ "f0d6f5c4-081f-4348-9b65-ae677d92767b", { "id": "1e4c73df-b805-47cd-9e44-9a8721c5cb45", "types": "playlists" } ] collection_id : str; keyword-only; optional TIDAL ID of the user collection. If authenticated, :code:`"me"` can be used in lieu of a TIDAL ID for the current user's collection. If not specified, :code:`"me"` is used. user_id : int or str; keyword-only; optional TIDAL ID of the user. """ self._client._require_scopes( "users.follow_playlists", "collection.write" ) self._modify_user_collection_resources( "POST", "playlists", playlist_uuids, collection_id=collection_id, user_id=user_id, )
[docs] def unfollow_playlists( self, playlist_uuids: str | dict[str, str] | Collection[str | dict[str, str]], /, *, collection_id: str | None = None, user_id: int | str | None = None, ) -> None: """ `User Collection Playlists > Delete from Items Relationship <https://tidal-music.github.io/tidal-api-reference/# /userCollectionPlaylists /delete_userCollectionPlaylists__id__relationships_items>`_: Remove playlists from a user collection․ `User Collections > Remove Playlists from User's Collection <https://tidal-music.github.io/tidal-api-reference/# /userCollections /delete_userCollections__id__relationships_playlists>`_: Remove playlists from a user's collection. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`collection.write` scope Write access to a user's collection. .. important:: At most one of `collection_id` or `user_id` must be provided. If `user_id` is provided, the legacy :code:`DELETE /userCollections/{user_id}/relationships /playlists` endpoint is used. Parameters ---------- playlist_uuids : str, dict[str, str], or \ Collection[str | dict[str, str]]; positional-only UUIDs and/or resource identifiers of the playlists. **Examples**: * :code:`"f0d6f5c4-081f-4348-9b65-ae677d92767b"` * .. code-block:: { "id": "1e4c73df-b805-47cd-9e44-9a8721c5cb45", "types": "playlists" } * .. code-block:: [ "f0d6f5c4-081f-4348-9b65-ae677d92767b", { "id": "1e4c73df-b805-47cd-9e44-9a8721c5cb45", "types": "playlists" } ] collection_id : str; keyword-only; optional TIDAL ID of the user collection. If authenticated, :code:`"me"` can be used in lieu of a TIDAL ID for the current user's collection. If not specified, :code:`"me"` is used. user_id : int or str; keyword-only; optional TIDAL ID of the user. """ self._client._require_scopes( "users.unfollow_playlists", "collection.write" ) self._modify_user_collection_resources( "DELETE", "playlists", playlist_uuids, collection_id=collection_id, user_id=user_id, )
[docs] @TTLCache.cached_method(ttl="user") def get_user_saved_tracks( self, *, collection_id: str | None = None, user_id: int | str | None = None, country_code: str | None = None, locale: str | None = None, include_metadata: bool = False, cursor: str | None = None, sort_by: str | None = None, descending: bool | None = None, ) -> dict[str, Any]: """ `User Collection Tracks > Get Items Relationship <https://tidal-music.github.io/tidal-api-reference/# /userCollectionTracks/get_userCollectionTracks__id_>`_: Get TIDAL catalog information for tracks in a user collection․ `User Collections > Get Tracks Relationship <https://tidal-music.github.io/tidal-api-reference/# /userCollections /get_userCollections__id__relationships_tracks>`_: Get TIDAL catalog information for tracks in a user's collection. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`collection.read` scope Read access to a user's collection. .. important:: At most one of `collection_id` or `user_id` must be provided. If `user_id` is provided, the legacy :code:`GET /userCollections/{user_id}/relationships/tracks` endpoint is used. Parameters ---------- collection_id : str; keyword-only; optional TIDAL ID of the user collection. If authenticated, :code:`"me"` can be used in lieu of a TIDAL ID for the current user's collection. If not specified, :code:`"me"` is used. user_id : int or str; keyword-only; optional TIDAL ID of the user. country_code : str; keyword-only; optional ISO 3166-1 alpha-2 country code. **Example**: :code:`"US"`. locale : str; keyword-only; optional IETF BCP 47 language tag. include_metadata : bool; keyword-only; default: :code:`False` Whether to include metadata for the tracks in the user's collection. cursor : str; keyword-only; optional Cursor for fetching the next page of results. **Example**: :code:`"3nI1Esi"`. sort_by : str; keyword-only; optional Field to sort the tracks by. **Valid values**: :code:`"addedAt"`, :code:`"albums.title"`, :code:`"artists.name"`, :code:`"duration"`, :code:`"title"`. descending : bool; keyword-only; optional Whether to sort in descending order. **API default**: :code:`False`. Returns ------- tracks : dict[str, Any] Page of TIDAL metadata for the tracks in the user's collection. .. admonition:: Sample response :class: response dropdown .. code-block:: { "data": [ { "id": <str>, "meta": { "addedAt": <str> }, "type": "tracks" } ], "included": [ { "attributes": { "accessType": <str>, "availability": <list[str]>, "bpm": <float>, "copyright": { "text": <str> }, "duration": <str>, "explicit": <bool>, "externalLinks": [ { "href": <str>, "meta": { "type": <str> } } ], "isrc": <str>, "key": <str>, "keyScale": <str>, "mediaTags": <list[str]>, "popularity": <float>, "spotlighted": <bool>, "title": <str>, "toneTags": <list[str]>, "version": <str> }, "id": <str>, "relationships": { "albums": { "links": { "self": <str> } }, "artists": { "links": { "self": <str> } }, "genres": { "links": { "self": <str> } }, "lyrics": { "links": { "self": <str> } }, "owners": { "links": { "self": <str> } }, "providers": { "links": { "self": <str> } }, "radio": { "links": { "self": <str> } }, "shares": { "links": { "self": <str> } }, "similarTracks": { "links": { "self": <str> } }, "sourceFile": { "links": { "self": <str> } }, "trackStatistics": { "links": { "self": <str> } } }, "type": "tracks" } ], "links": { "meta": { "nextCursor": <str> }, "next": <str>, "self": <str> } } """ self._client._require_scopes( "users.get_user_saved_tracks", "collection.read" ) return self._get_user_collection_relationship( "tracks", collection_id=collection_id, user_id=user_id, country_code=country_code, locale=locale, include_metadata=include_metadata, cursor=cursor, sort_by=sort_by, descending=descending, sort_fields={ "addedAt", "albums.title", "artists.name", "duration", "title", }, )
[docs] def save_tracks( self, track_ids: int | str | dict[str, int | str] | Collection[int | str | dict[str, int | str]], /, *, collection_id: str | None = None, user_id: int | str | None = None, country_code: str | None = None, ) -> None: """ `User Collection Tracks > Add to Items Relationship <https://tidal-music.github.io/tidal-api-reference/# /userCollectionTracks /post_userCollectionTracks__id__relationships_items>`_: Add tracks to a user collection․ `User Collections > Add to Tracks Relationship <https://tidal-music.github.io/tidal-api-reference/# /userCollections /post_userCollections__id__relationships_tracks>`_: Add tracks to a user's collection. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`collection.write` scope Write access to a user's collection. .. important:: At most one of `collection_id` or `user_id` must be provided. If `user_id` is provided, the legacy :code:`POST /userCollections/{user_id}/relationships/tracks` endpoint is used. Parameters ---------- track_ids : int, str, dict[str, int | str], or \ Collection[int | str | dict[str, int | str]]; positional-only TIDAL IDs and/or resource identifiers of the tracks. **Examples**: * :code:`46369325` * :code:`"75413016"` * :code:`{"id": "46369325", "types": "tracks"}` * :code:`["75413016", {"id": "46369325", "types": "tracks"}]` collection_id : str; keyword-only; optional TIDAL ID of the user collection. If authenticated, :code:`"me"` can be used in lieu of a TIDAL ID for the current user's collection. If not specified, :code:`"me"` is used. user_id : int or str; keyword-only; optional TIDAL ID of the user. country_code : str; keyword-only; optional ISO 3166-1 alpha-2 country code. **Example**: :code:`"US"`. """ self._client._require_scopes("users.save_tracks", "collection.write") self._modify_user_collection_resources( "POST", "tracks", track_ids, collection_id=collection_id, user_id=user_id, country_code=country_code, )
[docs] def remove_saved_tracks( self, track_ids: int | str | dict[str, int | str] | Collection[int | str | dict[str, int | str]], /, *, collection_id: str | None = None, user_id: int | str | None = None, ) -> None: """ `User Collection Tracks > Delete from Items Relationship <https://tidal-music.github.io/tidal-api-reference/# /userCollectionTracks /delete_userCollectionTracks__id__relationships_items>`_: Remove tracks from a user collection․ `User Collections > Delete from Tracks Relationship <https://tidal-music.github.io/tidal-api-reference/# /userCollections /delete_userCollections__id__relationships_tracks>`_: Remove tracks from a user's collection. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`collection.write` scope Write access to a user's collection. .. important:: At most one of `collection_id` or `user_id` must be provided. If `user_id` is provided, the legacy :code:`DELETE /userCollections/{user_id}/relationships /tracks` endpoint is used. Parameters ---------- track_ids : int, str, dict[str, int | str], or \ Collection[int | str | dict[str, int | str]]; positional-only TIDAL IDs and/or resource identifiers of the tracks. **Examples**: * :code:`46369325` * :code:`"75413016"` * :code:`{"id": "46369325", "types": "tracks"}` * :code:`["75413016", {"id": "46369325", "types": "tracks"}]` collection_id : str; keyword-only; optional TIDAL ID of the user collection. If authenticated, :code:`"me"` can be used in lieu of a TIDAL ID for the current user's collection. If not specified, :code:`"me"` is used. user_id : int or str; keyword-only; optional TIDAL ID of the user. """ self._client._require_scopes( "users.remove_saved_tracks", "collection.write" ) self._modify_user_collection_resources( "DELETE", "tracks", track_ids, collection_id=collection_id, user_id=user_id, )
[docs] @TTLCache.cached_method(ttl="user") def get_user_saved_videos( self, *, collection_id: str | None = None, user_id: int | str | None = None, country_code: str | None = None, locale: str | None = None, include_metadata: bool = False, cursor: str | None = None, sort_by: str | None = None, descending: bool | None = None, ) -> dict[str, Any]: """ `User Collection Videos > Get Items Relationship <https://tidal-music.github.io/tidal-api-reference/# /userCollectionVideos/get_userCollectionVideos__id_>`_: Get TIDAL catalog information for videos in a user collection․ `User Collections > Get Videos Relationship <https://tidal-music.github.io/tidal-api-reference/# /userCollections /get_userCollections__id__relationships_videos>`_: Get TIDAL catalog information for videos in a user's collection. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`collection.read` scope Read access to a user's collection. .. important:: At most one of `collection_id` or `user_id` must be provided. If `user_id` is provided, the legacy :code:`GET /userCollections/{user_id}/relationships/videos` endpoint is used. Parameters ---------- collection_id : str; keyword-only; optional TIDAL ID of the user collection. If authenticated, :code:`"me"` can be used in lieu of a TIDAL ID for the current user's collection. If not specified, :code:`"me"` is used. user_id : int or str; keyword-only; optional TIDAL ID of the user. country_code : str; keyword-only; optional ISO 3166-1 alpha-2 country code. **Example**: :code:`"US"`. locale : str; keyword-only; optional IETF BCP 47 language tag. include_metadata : bool; keyword-only; default: :code:`False` Whether to include metadata for the videos in the user's collection. cursor : str; keyword-only; optional Cursor for fetching the next page of results. **Example**: :code:`"3nI1Esi"`. sort_by : str; keyword-only; optional Field to sort the videos by. **Valid values**: :code:`"addedAt"`, :code:`"artists.name"`, :code:`"duration"`, :code:`"title"`. descending : bool; keyword-only; optional Whether to sort in descending order. **API default**: :code:`False`. Returns ------- videos : dict[str, Any] Page of TIDAL metadata for the videos in the user's collection. .. admonition:: Sample response :class: response dropdown .. code-block:: { "data": [ { "id": <str>, "meta": { "addedAt": <str> }, "type": "videos" } ], "included": [ { "attributes": { "availability": <list[str]>, "copyright": { "text": <str> }, "duration": <str>, "explicit": <bool>, "externalLinks": [ { "href": <str>, "meta": { "type": <str> } } ], "isrc": <str>, "popularity": <float> "releaseDate": <str>, "title": <str> }, "id": <str>, "relationships": { "albums": { "links": { "self": <str> } }, "artists": { "links": { "self": <str> } }, "providers": { "links": { "self": <str> } }, "thumbnailArt": { "links": { "self": <str> } } }, "type": "videos" } ], "links": { "meta": { "nextCursor": <str> }, "next": <str>, "self": <str> } } """ self._client._require_scopes( "users.get_user_saved_videos", "collection.read" ) return self._get_user_collection_relationship( "videos", collection_id=collection_id, user_id=user_id, country_code=country_code, locale=locale, include_metadata=include_metadata, cursor=cursor, sort_by=sort_by, descending=descending, sort_fields={"addedAt", "artists.name", "duration", "title"}, )
[docs] def save_videos( self, video_ids: str | dict[str, int | str] | Collection[int | str | dict[str, int | str]], /, *, collection_id: str | None = None, user_id: int | str | None = None, country_code: str | None = None, ) -> None: """ `User Collection Videos > Add to Items Relationship <https://tidal-music.github.io/tidal-api-reference/# /userCollectionVideos /post_userCollectionVideos__id__relationships_items>`_: Add videos to a user collection․ `User Collections > Add to Videos Relationship <https://tidal-music.github.io/tidal-api-reference/# /userCollections /post_userCollections__id__relationships_videos>`_: Add videos to a user's collection. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`collection.write` scope Write access to a user's collection. .. important:: At most one of `collection_id` or `user_id` must be provided. If `user_id` is provided, the legacy :code:`POST /userCollections/{user_id}/relationships/videos` endpoint is used. Parameters ---------- video_ids : int, str, dict[str, int | str], or \ Collection[int | str | dict[str, int | str]]; positional-only TIDAL IDs and/or resource identifiers of the videos. **Examples**: * :code:`53315642` * :code:`"75623239"` * :code:`{"id": "75623239", "types": "videos"}` * :code:`["53315642", {"id": "75623239", "types": "videos"}]` collection_id : str; keyword-only; optional TIDAL ID of the user collection. If authenticated, :code:`"me"` can be used in lieu of a TIDAL ID for the current user's collection. If not specified, :code:`"me"` is used. user_id : int or str; keyword-only; optional TIDAL ID of the user. country_code : str; keyword-only; optional ISO 3166-1 alpha-2 country code. **Example**: :code:`"US"`. """ self._client._require_scopes("users.save_videos", "collection.write") self._modify_user_collection_resources( "POST", "videos", video_ids, collection_id=collection_id, user_id=user_id, country_code=country_code, )
[docs] def remove_saved_videos( self, video_ids: str | dict[str, int | str] | Collection[int | str | dict[str, int | str]], /, *, collection_id: str | None = None, user_id: int | str | None = None, ) -> None: """ `User Collection Videos > Delete from Items Relationship <https://tidal-music.github.io/tidal-api-reference/# /userCollectionVideos /delete_userCollectionVideos__id__relationships_items>`_: Remove videos from a user collection․ `User Collections > Delete from Videos Relationship <https://tidal-music.github.io/tidal-api-reference/# /userCollections /delete_userCollections__id__relationships_videos>`_: Remove videos from a user's collection. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`collection.write` scope Write access to a user's collection. .. important:: At most one of `collection_id` or `user_id` must be provided. If `user_id` is provided, the legacy :code:`DELETE /userCollections/{user_id}/relationships /videos` endpoint is used. Parameters ---------- video_ids : int, str, dict[str, int | str], or \ Collection[int | str | dict[str, int | str]]; positional-only TIDAL IDs and/or resource identifiers of the videos. **Examples**: * :code:`53315642` * :code:`"75623239"` * :code:`{"id": "75623239", "types": "videos"}` * :code:`["53315642", {"id": "75623239", "types": "videos"}]` collection_id : str; keyword-only; optional TIDAL ID of the user collection. If authenticated, :code:`"me"` can be used in lieu of a TIDAL ID for the current user's collection. If not specified, :code:`"me"` is used. user_id : int or str; keyword-only; optional TIDAL ID of the user. """ self._client._require_scopes( "users.remove_saved_videos", "collection.write" ) self._modify_user_collection_resources( "DELETE", "videos", video_ids, collection_id=collection_id, user_id=user_id, )
[docs] @TTLCache.cached_method(ttl="recommendation") def get_personalized_mixes( self, *, user_id: str | None = None, country_code: str | None = None, locale: str | None = None, expand: str | Collection[str] | None = None, ) -> dict[str, Any]: """ `User Recommendations > Get Single User Recommendation <https://tidal-music.github.io/tidal-api-reference/# /userRecommendations/get_userRecommendations__id_>`_: Get TIDAL catalog information for mixes curated for a user. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`recommendations.read` scope Read access to a user's personal recommendations. Parameters ---------- user_id : int or str; keyword-only; optional TIDAL ID of the user. If not specified, the current user's TIDAL ID is used. country_code : str; optional ISO 3166-1 alpha-2 country code. **Example**: :code:`"US"`. locale : str; keyword-only; optional IETF BCP 47 language tag. expand : str or Collection[str]; keyword-only; optional Related resources to include in the response. **Valid values**: :code:`"discoveryMixes"`, :code:`"myMixes"`, :code:`"newArrivalMixes"`. **Examples**: :code:`"myMixes"`, :code:`["discoveryMixes", "newArrivalMixes"]`. Returns ------- recommendations : dict[str, Any] TIDAL metadata for the curated mixes. .. admonition:: Sample response :class: response dropdown .. code-block:: { "data": { "attributes": {}, "id": <str>, "relationships": { "discoveryMixes": { "data": [], "links": { "self": <str> } }, "myMixes": { "data": [], "links": { "self": <str> } }, "newArrivalMixes": { "data": [], "links": { "self": <str> } } }, "type": "userRecommendations" }, "included": [], "links": { "self": <str> } } """ self._client._require_scopes( "users.get_recommendations", "recommendations.read" ) if user_id is None: user_id = self._client._my_profile["id"] else: self._validate_tidal_ids(user_id, recursive=False) return self._get_resources( "userRecommendations", user_id, country_code=country_code, locale=locale, expand=expand, relationships=self._RECOMMENDATION_RELATIONSHIPS, )
[docs] @TTLCache.cached_method(ttl="daily") def get_discovery_mixes( self, *, user_id: str | None = None, country_code: str | None = None, locale: str | None = None, include_metadata: bool = False, cursor: int | None = None, ) -> dict[str, Any]: """ `User Recommendations > Get Discovery Mixes Relationship <https://tidal-music.github.io/tidal-api-reference/# /userRecommendations /get_userRecommendations__id__relationships_discoveryMixes>`_: Get TIDAL catalog information for a user's Discovery Mixes. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`recommendations.read` scope Read access to a user's personal recommendations. Parameters ---------- user_id : int or str; keyword-only; optional TIDAL ID of the user. If not specified, the current user's TIDAL ID is used. country_code : str; optional ISO 3166-1 alpha-2 country code. **Example**: :code:`"US"`. locale : str; keyword-only; optional IETF BCP 47 language tag. include_metadata : bool; keyword-only; default: :code:`False` Whether to include metadata for the user's Discovery Mixes. cursor : str; keyword-only; optional Cursor for fetching the next page of results. **Example**: :code:`"3nI1Esi"`. Returns ------- mixes : dict[str, Any] Page of TIDAL catalog information for the user's Discovery Mixes. .. admonition:: Sample response :class: response dropdown .. code-block:: { "data": [], "included": [], "links": { "meta": { "nextCursor": <str> }, "next": <str>, "self": <str> } } """ self._client._require_scopes( "users.get_discovery_mixes", "recommendations.read" ) self._get_resource_relationship( "userRecommendations", user_id, "discoveryMixes", country_code=country_code, locale=locale, include_metadata=include_metadata, cursor=cursor, )
[docs] @TTLCache.cached_method(ttl="daily") def get_user_mixes( self, *, user_id: str | None = None, country_code: str | None = None, locale: str | None = None, include_metadata: bool = False, cursor: int | None = None, ) -> dict[str, Any]: """ `User Recommendations > Get My Mixes Relationship <https://tidal-music.github.io/tidal-api-reference/# /userRecommendations /get_userRecommendations__id__relationships_mixes>`_: Get TIDAL catalog information for a user's mixes. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`recommendations.read` scope Read access to a user's personal recommendations. Parameters ---------- user_id : int or str; keyword-only; optional TIDAL ID of the user. If not specified, the current user's TIDAL ID is used. country_code : str; optional ISO 3166-1 alpha-2 country code. **Example**: :code:`"US"`. locale : str; keyword-only; optional IETF BCP 47 language tag. include_metadata : bool; keyword-only; default: :code:`False` Whether to include metadata for the user's mixes. cursor : str; keyword-only; optional Cursor for fetching the next page of results. **Example**: :code:`"3nI1Esi"`. Returns ------- mixes : dict[str, Any] Page of TIDAL catalog information for the user's mixes. .. admonition:: Sample response :class: response dropdown .. code-block:: { "data": [], "included": [], "links": { "meta": { "nextCursor": <str> }, "next": <str>, "self": <str> } } """ self._client._require_scopes("users.get_mixes", "recommendations.read") self._get_resource_relationship( "userRecommendations", user_id, "myMixes", country_code=country_code, locale=locale, include_metadata=include_metadata, cursor=cursor, )
[docs] @TTLCache.cached_method(ttl="daily") def get_new_arrival_mixes( self, *, user_id: str | None = None, country_code: str | None = None, locale: str | None = None, include_metadata: bool = False, cursor: int | None = None, ) -> dict[str, Any]: """ `User Recommendations > Get New Arrival Mixes Relationship <https://tidal-music.github.io/tidal-api-reference/# /userRecommendations /get_userRecommendations__id__relationships_newArrivalMixes>`_: Get TIDAL catalog information for the user's New Arrival Mixes. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`recommendations.read` scope Read access to a user's personal recommendations. Parameters ---------- user_id : int or str; keyword-only; optional TIDAL ID of the user. If not specified, the current user's TIDAL ID is used. country_code : str; optional ISO 3166-1 alpha-2 country code. **Example**: :code:`"US"`. locale : str; keyword-only; optional IETF BCP 47 language tag. include_metadata : bool; keyword-only; default: :code:`False` Whether to include metadata for the user's New Arrival Mixes. cursor : str; keyword-only; optional Cursor for fetching the next page of results. **Example**: :code:`"3nI1Esi"`. Returns ------- mixes : dict[str, Any] Page of TIDAL catalog information for the user's New Arrival Mixes. .. admonition:: Sample response :class: response dropdown .. code-block:: { "data": [], "included": [], "links": { "meta": { "nextCursor": <str> }, "next": <str>, "self": <str> } } """ self._client._require_scopes( "users.get_new_arrival_mixes", "recommendations.read" ) self._get_resource_relationship( "userRecommendations", user_id, "newArrivalMixes", country_code=country_code, locale=locale, include_metadata=include_metadata, cursor=cursor, )
[docs] @TTLCache.cached_method(ttl="static") def get_user(self, user_id: int | str | None = None, /) -> dict[str, Any]: """ `Users > Get Single User <https://tidal-music.github.io/tidal-api-reference/#/users /get_users__id_>`_: Get TIDAL profile information for a user. .. admonition:: Authorization scope :class: entitlement .. tab-set:: .. tab-item:: Required :code:`user.read` scope Read access to a user's account information, such as country and email address. Parameters ---------- user_id : int or str; keyword-only; optional TIDAL ID of the user. If authenticated, :code:`"me"` can be used in lieu of a TIDAL ID for the current user. If not specified, :code:`"me"` is used. Returns ------- profile : dict[str, Any] TIDAL profile information for the user. .. admonition:: Sample response :class: response dropdown .. code-block:: { "data": { "attributes": { "country": <str>, "email": <str>, "emailVerified": <bool>, "firstName": <str>, "username": <str> }, "id": <str>, "type": "users", }, "links": { "self": "/users/me" } } """ self._client._require_scopes("users.get_user", "user.read") user_id = user_id or "me" self._validate_tidal_ids(user_id) return self._client._request("GET", f"users/{user_id}").json()