from __future__ import annotations
from typing import TYPE_CHECKING
from ..._shared import TTLCache
from ._shared import PrivateTIDALResourceAPI
if TYPE_CHECKING:
from typing import Any
from ...._types import Collection
[docs]
class PrivateUsersAPI(PrivateTIDALResourceAPI):
"""
User Collections and Users API endpoints for the private TIDAL API.
.. important::
This class is managed by
:class:`~minim.api.tidal.PrivateTIDALAPIClient` and should not be
instantiated directly.
"""
_SORT_FIELDS = {"DATE", "NAME"}
__slots__ = ()
@staticmethod
def _prepare_mix_ids(
mix_ids: str | Collection[str], /, *, limit: int = 100
) -> str:
"""
Validate, normalize, and serialize TIDAL mix IDs.
Parameters
----------
mix_ids : str or Collection[str]; positional-only
Comma-separated string or collection of mix IDs.
limit : int; keyword-only, default: :code:`100`
Maximum number of mix IDs that can be sent in the request.
Returns
-------
mix_ids : str
Comma-separated string of mix IDs.
"""
if not mix_ids:
raise ValueError("At least one mix ID must be specified.")
if isinstance(mix_ids, str):
return PrivateUsersAPI._prepare_mix_ids(
mix_ids.split(","), limit=limit
)
num_ids = len(mix_ids)
if num_ids > limit:
raise ValueError(
f"A maximum of {limit} mix IDs can be sent in a request."
)
for id_ in mix_ids:
if not isinstance(id_, str) or len(id_) != 30:
raise ValueError(f"Invalid mix ID {id_!r}.")
return ",".join(mix_ids)
def _get_blocked_resources(
self,
resource_type: str,
user_id: int | str | None = None,
/,
*,
limit: int | None = None,
offset: int | None = None,
) -> dict[str, Any]:
"""
Get TIDAL catalog information for resources blocked by a user.
Parameters
----------
user_id : int or str; positional-only; optional
TIDAL ID of the user. If not specified, the current user's
TIDAL ID is used.
limit : int; keyword-only; optional
Maximum number of items to return.
**Valid range**: :code:`1` to :code:`100`.
**API default**: :code:`10`.
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
-------
resources : dict[str, Any]
Page of TIDAL metadata for resources blocked by the user.
"""
if user_id is None:
user_id = self._client._resolve_user_identifier()
params = {}
if limit is not None:
self._validate_number("limit", limit, int, 1, 100)
params["limit"] = limit
if offset is not None:
self._validate_number("offset", offset, int, 0)
params["offset"] = offset
return self._client._request(
"GET", f"v1/users/{user_id}/blocks/{resource_type}", params=params
).json()
def _manage_blocked_resources(
self,
method: str,
resource_type: str,
resource_id: int | str,
/,
user_id: int | str | None = None,
) -> None:
"""
Block or unblock a resource for a user.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
resource_id : int or str; positional-only
TIDAL ID of the resource.
user_id : int or str; optional
TIDAL ID of the user. If not specified, the current user's
TIDAL ID is used.
"""
if user_id is None:
user_id = self._client._resolve_user_identifier()
if method == "POST":
self._client._request(
"POST",
f"v1/users/{user_id}/blocks/{resource_type}s",
data={f"{resource_type}Id": resource_id},
)
else:
self._client._request(
"DELETE",
f"v1/users/{user_id}/blocks/{resource_type}s/{resource_id}",
)
def _get_saved_resources(
self,
resource_type: str,
/,
user_id: int | str | None = None,
country_code: str | None = None,
*,
limit: int | None = None,
offset: int | None = None,
sort_by: str | None = None,
descending: bool | None = None,
) -> dict[str, Any]:
"""
Get TIDAL catalog information for items of a specific resource
type in a user's collection.
Parameters
----------
resource_type : str; positional-only
Resource type.
**Valid values**: :code:`"albums"`, :code:`"artists"`,
:code:`"tracks"`, :code:`"videos"`.
user_id : int or str; 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. If not provided, the
country associated with the current user account or IP
address is used.
**Example**: :code:`"US"`.
limit : int; keyword-only; optional
Maximum number of items to return.
**Valid range**: :code:`1` to :code:`100`.
**API default**: :code:`10`.
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`.
sort_by : str; keyword-only; optional
Field to sort the items by.
**Valid values**:
* :code:`"DATE"` - Date added.
* :code:`"NAME"` - Item name.
**API default**: :code:`"DATE"`.
descending : bool; keyword-only; optional
Whether to sort in descending order.
**API default**: :code:`False`.
Returns
-------
items : dict[str, Any]
Page of TIDAL catalog information for items in the user's
collection.
"""
if user_id is None:
user_id = self._client._resolve_user_identifier()
params = {}
self._client._resolve_country_code(country_code, params)
if limit is not None:
self._validate_number("limit", limit, int, 1, 100)
params["limit"] = limit
if offset is not None:
self._validate_number("offset", offset, int, 0)
params["offset"] = offset
if sort_by is not None:
if sort_by not in self._SORT_FIELDS:
raise ValueError(
f"Invalid sort field {sort_by!r}. Valid values: "
f"{self._join_values(self._SORT_FIELDS)}."
)
params["order"] = sort_by
if descending is not None:
self._validate_type("descending", descending, bool)
params["orderDirection"] = "DESC" if descending else "ASC"
return self._client._request(
"GET",
f"v1/users/{user_id}/favorites/{resource_type}",
params=params,
).json()
def _save_resources(
self,
resource_type: str,
item_ids: int | str | Collection[int | str],
/,
user_id: int | str | None = None,
country_code: str | None = None,
*,
on_missing: str | None = None,
) -> None:
"""
Add items of a specific resource type to a user's collection.
Parameters
----------
resource_type : str; positional-only
Resource type.
**Valid values**: :code:`"albums"`, :code:`"artists"`,
:code:`"tracks"`, :code:`"videos"`.
item_ids : int, str, or Collection[int | str]; positional-only
TIDAL IDs of the items.
user_id : int or str; 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. If not provided, the
country associated with the current user account or IP
address is used.
**Example**: :code:`"US"`.
on_missing : str; keyword-only; optional
Behavior when the items to be favorited cannot be found in
the TIDAL catalog.
**API default**: :code:`"FAIL"`.
"""
if user_id is None:
user_id = self._client._resolve_user_identifier()
if country_code is None:
country_code = self._client._my_country_code
else:
self._validate_country_code(country_code)
data = {
f"{resource_type[:-1]}Ids": self._prepare_tidal_ids(
item_ids, limit=1_000
)
}
if on_missing is not None:
on_missing = self._prepare_string("on_missing", on_missing).upper()
if on_missing not in {"FAIL", "SKIP"}:
raise ValueError(
f"Invalid behavior {on_missing!r} for missing "
"items. Valid values: 'FAIL', 'SKIP'."
)
data["onArtifactNotFound"] = on_missing
self._client._request(
"POST",
f"v1/users/{user_id}/favorites/{resource_type}",
params={"countryCode": country_code},
data=data,
)
def _remove_saved_resources(
self,
resource_type: str,
item_ids: int | str | Collection[int | str],
/,
user_id: int | str | None = None,
) -> None:
"""
Remove items of a specific resource type from a user's
collection.
Parameters
----------
resource_type : str; positional-only
Resource type.
**Valid values**: :code:`"albums"`, :code:`"artists"`,
:code:`"tracks"`, :code:`"videos"`.
item_ids : int, str, or Collection[int | str]; positional-only
TIDAL IDs of the items.
user_id : int or str; optional
TIDAL ID of the user. If not specified, the current user's
TIDAL ID is used.
"""
if user_id is None:
user_id = self._client._resolve_user_identifier()
self._client._request(
"DELETE",
f"v1/users/{user_id}/favorites/{resource_type}"
f"/{self._prepare_tidal_ids(item_ids, limit=1_000)}",
)
def _get_user_relationship(
self,
resource_type: str,
relationship: str,
user_id: int | str | None = None,
/,
*,
limit: int | None = None,
cursor: str | None = None,
) -> dict[str, Any]:
"""
Get TIDAL catalog information for a resource related to a user.
Parameters
----------
resource_type : str; positional-only
Resource type.
relationship : str; positional-only
Related resource type.
user_id : int or str; positional-only; optional
TIDAL ID of the user. If not specified, the current user's
TIDAL ID is used.
limit : int; keyword-only; default: :code:`50`
Maximum number of items to return.
cursor : str; keyword-only; optional
Cursor for fetching the next page of results.
Returns
-------
resource : dict[str, Any]
Page of TIDAL metadata for the related resource.
"""
if user_id is None:
user_id = self._client._resolve_user_identifier()
params = {}
if cursor is not None:
params["cursor"] = self._prepare_string("cursor", cursor)
if limit is not None:
self._validate_number("limit", limit, int, 1, 50)
params["limit"] = limit
return self._client._request(
f"v2/{resource_type}/{user_id}/{relationship}", params=params
).json()
def _get_my_playlists(
self,
subresource: str,
/,
*,
limit: int = 50,
cursor: str | None = None,
playlist_types: str | Collection[str] | None = None,
sort_by: str | None = None,
descending: bool | None = None,
params: dict[str, Any] | None = None,
) -> dict[str, Any]:
"""
Get TIDAL catalog information for playlist folders and/or
playlists in the current user's collection.
Parameters
----------
subresource : str; positional-only
Subresource of the endpoint to call.
limit : int; keyword-only; default: :code:`50`
Maximum number of playlists to return.
**Valid range**: :code:`1` to :code:`50`.
cursor : str; keyword-only; optional
Cursor for fetching the next page of results.
playlist_types : str or Collection[str]; keyword-only; optional
Playlist types to return. If not specified, all playlists
are returned.
**Valid values**:
* :code:`"FOLDER"` – Playlist folders.
* :code:`"PLAYLIST"` – All playlists.
* :code:`"FAVORITE_PLAYLIST"` – Favorited playlists.
* :code:`"USER_PLAYLIST"` – User-created playlists.
**Examples**: :code:`"USER_PLAYLIST"`,
:code:`"FOLDER,USER_PLAYLIST"`,
:code:`["FOLDER", "USER_PLAYLIST"]`.
sort_by : str; keyword-only; optional
Field to sort the playlists by.
**Valid values**:
* :code:`"DATE"` - Date added.
* :code:`"NAME"` - Playlist name.
**API default**: :code:`"DATE"`.
descending : bool; keyword-only; optional
Whether to sort in descending order.
**API default**: :code:`False`.
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
-------
playlists : dict[str, Any]
Page of TIDAL metadata for the playlist folders and/or
playlists in the current user's collection.
"""
self._validate_number("limit", limit, int, 1, 50)
if params is None:
params = {"limit": limit}
else:
params["limit"] = limit
if cursor is not None:
params["cursor"] = self._prepare_string("cursor", cursor)
if playlist_types is not None:
self._client.playlists._validate_types(playlist_types)
params["includeOnly"] = playlist_types
if sort_by is not None:
if sort_by not in self._SORT_FIELDS:
raise ValueError(
f"Invalid sort field {sort_by!r}. Valid values: "
f"{self._join_values(self._SORT_FIELDS)}."
)
params["order"] = sort_by
if descending is not None:
self._validate_type("descending", descending, bool)
params["orderDirection"] = "DESC" if descending else "ASC"
return self._client._request(
"GET", f"v2/my-collection/{subresource}", params=params
).json()
def _get_user_playlists(
self,
subresource: str,
user_id: int | str | None = None,
/,
country_code: str | None = None,
*,
limit: int | None = None,
offset: int | None = None,
sort_by: str | None = None,
descending: bool | None = None,
) -> dict[str, Any]:
"""
Get TIDAL catalog information for playlists in a user's
collection.
Parameters
----------
subresource : str; positional-only
Subresource of the endpoint to call.
user_id : int or str; positional-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. If not provided, the
country associated with the current user account or IP
address is used.
**Example**: :code:`"US"`.
limit : int; keyword-only; optional
Maximum number of playlists to return.
**Valid range**: :code:`1` to :code:`100`.
**API default**: :code:`10`.
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`.
sort_by : str; keyword-only; optional
Field to sort the playlists by.
**Valid values**:
* :code:`"DATE"` - Date added.
* :code:`"NAME"` - Playlist name.
**API default**: :code:`"DATE"`.
descending : bool; keyword-only; optional
Whether to sort in descending order.
**API default**: :code:`False`.
Returns
-------
playlists : dict[str, Any]
Page of TIDAL catalog information for playlists in the
user's collection.
"""
if user_id is None:
user_id = self._client._resolve_user_identifier()
params = {}
self._client._resolve_country_code(country_code, params)
if limit is not None:
self._validate_number("limit", limit, int, 1, 100)
params["limit"] = limit
if offset is not None:
self._validate_number("offset", offset, int, 0)
params["offset"] = offset
if sort_by is not None:
if sort_by not in self._SORT_FIELDS:
raise ValueError(
f"Invalid sort field {sort_by!r}. Valid values: "
f"{self._join_values(self._SORT_FIELDS)}."
)
params["order"] = sort_by
if descending is not None:
self._validate_type("descending", descending, bool)
params["orderDirection"] = "DESC" if descending else "ASC"
return self._client._request(
"GET", f"v1/users/{user_id}/{subresource}", params=params
).json()
def _manage_followings(self, method: str, user_id: int | str, /) -> None:
"""
Follow or unfollow a TIDAL user.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
method : str; positional-only
HTTP method.
**Valid values**: :code:`"PUT"`, :code:`"DELETE"`.
user_id : int or str; positional-only
TIDAL ID of the user.
"""
self._validate_tidal_ids(user_id, recursive=False)
self._client._request(
method, "v2/follow", params={"trn": f"trn:user:{user_id}"}
)
def _manage_blocked_users(
self, method: str, user_id: int | str, /
) -> None:
"""
Block or unblock a TIDAL user.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
method : str; positional-only
HTTP method.
**Valid values**: :code:`"PUT"`, :code:`"DELETE"`.
user_id : int or str; positional-only
TIDAL ID of the user.
"""
self._validate_tidal_ids(user_id, recursive=False)
self._client._request(method, f"v2/profiles/block/{user_id}")
[docs]
@TTLCache.cached_method(ttl="static")
def get_me(self) -> dict[str, Any]:
"""
Get TIDAL profile information for the current user.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Returns
-------
profile : dict[str, Any]
Current user's profile information.
.. admonition:: Sample response
:class: response dropdown
.. code-block::
{
"acceptedEULA": <bool>,
"accountLinkCreated": <bool>,
"address": <str>,
"appleUid": <int>,
"birthday": <int>,
"channelId": <int>,
"city": <str>,
"countryCode": <str>,
"created": <int>,
"email": <str>,
"emailVerified": <bool>,
"facebookUid": <int>,
"firstName": <str>,
"fullName": <str>,
"googleUid": <int>,
"lastName": <str>,
"newUser": <bool>,
"nickname": <str>,
"parentId": <int>,
"phoneNumber": <str>,
"postalcode": <str>,
"updated": <int>,
"usState": <str>,
"userId": <int>,
"username": <str>
}
"""
self._client._require_authentication("users.get_me")
return self._client._request(
"GET", "https://login.tidal.com/oauth2/me"
).json()
[docs]
@TTLCache.cached_method(ttl="static")
def get_session(self) -> dict[str, Any]:
"""
Get information about the current private TIDAL API session.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access client, session, and user account
information.
Returns
-------
session : dict[str, Any]
Session information.
.. admonition:: Sample response
:class: response dropdown
.. code-block::
{
"channelId": <int>,
"client": {
"authorizedForOffline": <bool>,
"authorizedForOfflineDate": <str>,
"id": <int>,
"name": <str>
},
"countryCode": <str>,
"partnerId": <int>
"sessionId": <str>,
"userId": <int>
}
"""
self._client._require_authentication("users.get_session")
return self._client._request("GET", "v1/sessions").json()
[docs]
@TTLCache.cached_method(ttl="user")
def get_user_clients(
self,
user_id: int | str | None = None,
/,
country_code: str | None = None,
) -> dict[str, Any]:
"""
Get information about a user's TIDAL clients.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access client, session, and user account
information.
Parameters
----------
user_id : int or str; positional-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. If not provided, the
country associated with the current user account or IP
address is used.
**Example**: :code:`"US"`.
Returns
-------
clients : dict[str, Any]
Information about the user's TIDAL clients.
.. admonition:: Sample response
:class: response dropdown
.. code-block::
{
"items": [
{
"application": {
"name": <str>,
"service": "TIDAL",
"type": {
"name": <str>
}
},
"authorizedForOffline": <bool>,
"authorizedForOfflineDate": <str>,
"created": <str>,
"id": <int>,
"lastLogin": <str>,
"name": <str>,
"numberOfOfflineAlbums": <int>,
"numberOfOfflinePlaylists": <int>,
"uniqueKey": <str>
}
],
"limit": <int>,
"offset": <int>,
"totalNumberOfItems": <int>
}
"""
self._client._require_authentication("users.get_user_clients")
if user_id is None:
user_id = self._client._resolve_user_identifier()
return self._get_resource_relationship(
"users", user_id, "clients", country_code=country_code
)
[docs]
@TTLCache.cached_method(ttl="user")
def get_user_subscription(
self,
user_id: int | str | None = None,
/,
country_code: str | None = None,
) -> dict[str, Any]:
"""
Get information about a user's TIDAL subscription status.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access client, session, and user account
information.
Parameters
----------
user_id : int or str; positional-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. If not provided, the
country associated with the current user account or IP
address is used.
**Example**: :code:`"US"`.
Returns
-------
subscription : dict[str, Any]
Information about the user's TIDAL subscription status.
.. admonition:: Sample response
:class: response dropdown
.. code-block::
{
"canGetTrial": <bool>,
"highestSoundQuality": <str>,
"paymentOverdue": <bool>,
"paymentType": <str>,
"premiumAccess": <bool>,
"startDate": <str>,
"status": <str>,
"subscription": {
"offlineGracePeriod": <int>,
"type": <str>
},
"validUntil": <str>
}
"""
self._client._require_authentication("users.get_user_subscription")
if user_id is None:
user_id = self._client._resolve_user_identifier()
return self._get_resource_relationship(
"users", user_id, "subscription", country_code=country_code
)
[docs]
@TTLCache.cached_method(ttl="user")
def get_user_saved_item_ids(
self, user_id: int | str | None = None, /
) -> dict[str, list[str]]:
"""
Get TIDAL IDs or UUIDs of the albums, artists, playlists,
tracks, and videos in a user's collection.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
user_id : int or str; positional-only; optional
TIDAL ID of the user. If not specified, the current user's
TIDAL ID is used.
Returns
-------
ids : `dict`
IDs or UUIDs of the items in the current user's collection.
.. admonition:: Sample response
:class: response dropdown
.. code-block::
{
"ALBUM": <list[str]>,
"ARTIST": <list[str]>,
"PLAYLIST": <list[str]>,
"TRACK": <list[str]>,
"VIDEO": <list[str]>,
}
"""
self._client._require_authentication("users.get_user_saved_item_ids")
return self._get_saved_resources("ids", user_id)
[docs]
@TTLCache.cached_method(ttl="user")
def get_user_saved_albums(
self,
user_id: int | str | None = None,
/,
country_code: str | None = None,
*,
limit: int | None = None,
offset: int | None = None,
sort_by: str | None = None,
descending: bool | None = None,
) -> dict[str, Any]:
"""
Get TIDAL catalog information for albums in a user's collection.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
user_id : int or str; positional-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. If not provided, the
country associated with the current user account or IP
address is used.
**Example**: :code:`"US"`.
limit : int; keyword-only; optional
Maximum number of albums to return.
**Valid range**: :code:`1` to :code:`100`.
**API default**: :code:`10`.
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`.
sort_by : str; keyword-only; optional
Field to sort the albums by.
**Valid values**:
* :code:`"DATE"` - Date added.
* :code:`"NAME"` - Album name.
**API default**: :code:`"DATE"`.
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-block::
{
"items": [
{
"created": <str>,
"item": {
"adSupportedStreamReady": <bool>,
"allowStreaming": <bool>,
"artist": {
"handle": <str>,
"id": <int>,
"name": <str>,
"picture": <str>,
"type": <str>
},
"artists": [
{
"handle": <str>,
"id": <int>,
"name": <str>,
"picture": <str>,
"type": <str>
}
],
"audioModes": <list[str]>,
"audioQuality": <str>,
"copyright": <str>,
"cover": <str>,
"djReady": <bool>,
"duration": <int>,
"explicit": <bool>
"id": <int>,
"mediaMetadata": {
"tags": <list[str]>
},
"numberOfTracks": <int>,
"numberOfVideos": <int>,
"numberOfVolumes": <int>,
"payToStream": <bool>,
"popularity": <int>,
"premiumStreamingOnly": <bool>,
"releaseDate": <str>,
"stemReady": <bool>,
"streamReady": <bool>,
"streamStartDate": <str>,
"title": <str>,
"type": "ALBUM",
"upc": <str>,
"upload": <bool>,
"url": <str>,
"version": <str>,
"vibrantColor": <str>,
"videoCover": <str>
}
],
"limit": <int>,
"offset": <int>,
"totalNumberOfItems": <int>
}
"""
self._client._require_authentication("users.get_user_saved_albums")
return self._get_saved_resources(
"albums",
user_id,
country_code=country_code,
limit=limit,
offset=offset,
sort_by=sort_by,
descending=descending,
)
[docs]
def save_albums(
self,
album_ids: int | str | Collection[int | str],
/,
user_id: int | str | None = None,
country_code: str | None = None,
*,
on_missing: str | None = None,
) -> None:
"""
Add albums to a user's collection.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
album_ids : int, str, or Collection[int | str]; positional-only
TIDAL IDs of the albums.
**Examples**: :code:`46369321`, :code:`"251380836"`,
:code:`"46369321,251380836"`,
:code:`[46369321, "251380836"]`.
user_id : int or str; 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. If not provided, the
country associated with the current user account or IP
address is used.
**Example**: :code:`"US"`.
on_missing : str; keyword-only; optional
Behavior when the albums to be favorited cannot be found in
the TIDAL catalog.
**API default**: :code:`"FAIL"`.
"""
self._client._require_authentication("users.save_albums")
return self._save_resources(
"albums",
album_ids,
user_id=user_id,
country_code=country_code,
on_missing=on_missing,
)
[docs]
def remove_saved_albums(
self,
album_ids: int | str | Collection[int | str],
/,
user_id: int | str | None = None,
) -> None:
"""
Remove albums from a user's collection.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
album_ids : int, str, or Collection[int | str]; positional-only
TIDAL IDs of the albums.
**Examples**: :code:`46369321`, :code:`"251380836"`,
:code:`"46369321,251380836"`,
:code:`[46369321, "251380836"]`.
user_id : int or str; optional
TIDAL ID of the user. If not specified, the current user's
TIDAL ID is used.
"""
self._client._require_authentication("users.remove_saved_albums")
return self._remove_saved_resources(
"albums", album_ids, user_id=user_id
)
[docs]
@TTLCache.cached_method(ttl="user")
def get_user_blocked_artists(
self,
user_id: int | str | None = None,
/,
*,
limit: int | None = None,
offset: int | None = None,
) -> dict[str, Any]:
"""
Get TIDAL catalog information for artists blocked by a user.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
user_id : int or str; positional-only; optional
TIDAL ID of the user. If not specified, the current user's
TIDAL ID is used.
limit : int; keyword-only; optional
Maximum number of artists to return.
**Valid range**: :code:`1` to :code:`100`.
**API default**: :code:`10`.
offset : int; keyword-only; optional
Index of the first artist to return. Use with `limit` to get
the next batch of artists.
**Minimum value**: :code:`0`.
**API default**: :code:`0`.
Returns
-------
artists : dict[str, Any]
Page of TIDAL metadata for artists blocked by the user.
.. admonition:: Sample response
:class: response dropdown
.. code-block::
{
"items": [
{
"created": <str>,
"item": {
"artistRoles": [
{
"category": <str>,
"categoryId": <int>
}
],
"artistTypes": <list[str]>,
"banner": <str>,
"handle": <str>,
"id": <int>,
"mixes": {
"ARTIST_MIX": <str>
},
"name": <str>,
"picture": <str>,
"popularity": <int>,
"selectedAlbumCoverFallback": None,
"spotlighted": <bool>,
"type": <str>,
"url": <str>,
"userId": <int>
},
"type": "ARTIST"
}
],
"limit": <int>,
"offset": <int>,
"totalNumberOfItems": <int>
}
"""
self._client._require_authentication("users.get_user_blocked_artists")
return self._get_blocked_resources(
"artists", user_id, limit=limit, offset=offset
)
[docs]
def block_artist(
self, artist_id: int | str, /, user_id: int | str | None = None
) -> None:
"""
Block an artist for a user.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
artist_id : int or str; positional-only
TIDAL ID of the artist.
**Examples**: :code:`1566`, :code:`"4676988"`.
user_id : int or str; optional
TIDAL ID of the user. If not specified, the current user's
TIDAL ID is used.
"""
self._client._require_authentication("users.block_artist")
self._manage_blocked_resources(
"POST", "artist", artist_id, user_id=user_id
)
[docs]
def unblock_artist(
self, artist_id: int | str, /, user_id: int | str | None = None
) -> None:
"""
Unblock an artist for a user.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
artist_id : int or str; positional-only
TIDAL ID of the artist.
**Examples**: :code:`1566`, :code:`"4676988"`.
user_id : int or str; optional
TIDAL ID of the user. If not specified, the current user's
TIDAL ID is used.
"""
self._client._require_authentication("users.unblock_artist")
self._manage_blocked_resources(
"DELETE", "artist", artist_id, user_id=user_id
)
[docs]
@TTLCache.cached_method(ttl="user")
def get_user_followed_artists(
self,
user_id: int | str | None = None,
/,
country_code: str | None = None,
*,
limit: int | None = None,
offset: int | None = None,
sort_by: str | None = None,
descending: bool | None = None,
) -> dict[str, Any]:
"""
Get TIDAL catalog information for artists in a user's
collection.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
user_id : int or str; positional-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. If not provided, the
country associated with the current user account or IP
address is used.
**Example**: :code:`"US"`.
limit : int; keyword-only; optional
Maximum number of artists to return.
**Valid range**: :code:`1` to :code:`100`.
**API default**: :code:`10`.
offset : int; keyword-only; optional
Index of the first artist to return. Use with `limit` to get
the next batch of artists.
**Minimum value**: :code:`0`.
**API default**: :code:`0`.
sort_by : str; keyword-only; optional
Field to sort the albums by.
**Valid values**:
* :code:`"DATE"` - Date added.
* :code:`"NAME"` - Artist name.
**API default**: :code:`"DATE"`.
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::
{
"items": [
{
"created": <str>,
"item": {
"artistRoles": [
{
"category": <str>,
"categoryId": <int>
}
],
"artistTypes": <list[str]>,
"handle": <str>,
"id": <int>,
"mixes": {
"ARTIST_MIX": <str>
},
"name": <str>,
"picture": <str>,
"popularity": <int>,
"selectedAlbumCoverFallback": None,
"spotlighted": <bool>,
"url": <str>,
"userId": <int>
}
}
"limit": <int>,
"offset": <int>,
"totalNumberOfItems": <int>
}
"""
self._client._require_authentication("users.get_user_followed_artists")
return self._get_saved_resources(
"artists",
user_id,
country_code=country_code,
limit=limit,
offset=offset,
sort_by=sort_by,
descending=descending,
)
[docs]
def follow_artists(
self,
artist_ids: int | str | Collection[int | str],
/,
user_id: int | str | None = None,
country_code: str | None = None,
*,
on_missing: str | None = None,
) -> None:
"""
Add artists to a user's collection.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
artist_ids : int, str, or Collection[int | str]; positional-only
TIDAL IDs of the artists.
**Examples**: :code:`1566`, :code:`"4676988"`,
:code:`"1566,4676988"`, :code:`[1566, "4676988"]`.
user_id : int or str; 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. If not provided, the
country associated with the current user account or IP
address is used.
**Example**: :code:`"US"`.
on_missing : str; keyword-only; optional
Behavior when the artists to be favorited cannot be found in
the TIDAL catalog.
**API default**: :code:`"FAIL"`.
"""
self._client._require_authentication("users.follow_artists")
return self._save_resources(
"artists",
artist_ids,
user_id=user_id,
country_code=country_code,
on_missing=on_missing,
)
[docs]
def unfollow_artists(
self,
artist_ids: int | str | Collection[int | str],
/,
user_id: int | str | None = None,
) -> None:
"""
Remove artists from a user's collection.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
artist_ids : int, str, or Collection[int | str]; positional-only
TIDAL IDs of the artists.
**Examples**: :code:`1566`, :code:`"4676988"`,
:code:`"1566,4676988"`, :code:`[1566, "4676988"]`.
user_id : int or str; optional
TIDAL ID of the user. If not specified, the current user's
TIDAL ID is used.
"""
self._client._require_authentication("users.unfollow_artists")
return self._remove_saved_resources(
"artists", artist_ids, user_id=user_id
)
[docs]
@TTLCache.cached_method(ttl="user")
def get_my_followed_mixes(
self,
*,
limit: int = 50,
cursor: str | None = None,
sort_by: str | None = None,
descending: bool | None = None,
) -> dict[str, Any]:
"""
Get TIDAL catalog information for mixes in the current user's
collection.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
limit : int; keyword-only; default: :code:`50`
Maximum number of mixes to return.
**Valid range**: :code:`1` to :code:`50`.
cursor : str; keyword-only; optional
Cursor for fetching the next page of results.
sort_by : str; keyword-only; optional
Field to sort the mixes by.
**Valid values**:
* :code:`"DATE"` - Date added.
* :code:`"NAME"` - Mix name.
**API default**: :code:`"DATE"`.
descending : bool; keyword-only; optional
Whether to sort in descending order.
**API default**: :code:`False`.
Returns
-------
mixes : dict[str, Any]
Page of TIDAL metadata for the mixes in the current user's
collection.
.. admonition:: Sample response
:class: response dropdown
.. code-block::
{
"cursor": <str>,
"items": [
{
"dateAdded": <str>,
"detailImages": {
"LARGE": {
"height": <int>,
"url": <str>,
"width": <int>
},
"MEDIUM": {
"height": <int>,
"url": <str>,
"width": <int>
},
"SMALL": {
"height": <int>,
"url": <str>,
"width": <int>
}
},
"id": <str>,
"images": {
"LARGE": {
"height": <int>,
"url": <str>,
"width": <int>
},
"MEDIUM": {
"height": <int>,
"url": <str>,
"width": <int>
},
"SMALL": {
"height": <int>,
"url": <str>,
"width": <int>
}
},
"master": <bool>,
"mixType": <str>,
"subTitle": <str>,
"subTitleTextInfo": {
"color": <str>,
"text": <str>
},
"title": <str>,
"titleTextInfo": {
"color": <str>,
"text": <str>
},
"updated": <str>
}
],
"lastModifiedAt": <str>
}
"""
self._client._require_authentication("users.get_my_followed_mixes")
self._validate_number("limit", limit, int, 1, 50)
params = {"limit": limit}
if cursor is not None:
params["cursor"] = self._prepare_string("cursor", cursor)
if sort_by is not None:
if sort_by not in self._SORT_FIELDS:
raise ValueError(
f"Invalid sort field {sort_by!r}. Valid values: "
f"{self._join_values(self._SORT_FIELDS)}."
)
params["order"] = sort_by
if descending is not None:
self._validate_type("descending", descending, bool)
params["orderDirection"] = "DESC" if descending else "ASC"
return self._client._request(
"GET", "v2/favorites/mixes", params=params
).json()
[docs]
@TTLCache.cached_method(ttl="user")
def get_my_followed_mix_ids(
self, *, limit: int = 50, cursor: str | None = None
) -> dict[str, Any]:
"""
Get TIDAL IDs of the mixes in the current user's collection.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
limit : int; keyword-only; default: :code:`50`
Maximum number of mix IDs to return.
**Valid range**: :code:`1` to :code:`50`.
cursor : str; keyword-only; optional
Cursor for fetching the next page of results.
Returns
-------
mix_ids : dict[str, Any]
Page of TIDAL IDs of the mixes in the user's collection.
**Sample response**:
:code:`{"content": <list[str]>, "cursor": <str>}`
"""
self._client._require_authentication("users.get_my_followed_mix_ids")
self._validate_number("limit", limit, int, 1, 50)
params = {"limit": limit}
if cursor is not None:
params["cursor"] = self._prepare_string("cursor", cursor)
return self._client._request(
"GET", "v2/favorites/mixes/ids", params=params
).json()
[docs]
def follow_mixes(
self,
mix_ids: str | Collection[str],
/,
*,
on_missing: str | None = None,
) -> None:
"""
Add mixes to the current user's collection.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
mix_ids : str or Collection[str]; positional-only
TIDAL IDs of the mixes.
**Examples**:
* :code:`"000ec0b01da1ddd752ec5dee553d48"`
* :code:`"000ec0b01da1ddd752ec5dee553d48,000dd748ceabd5508947c6a5d3880a"`
* :code:`["000ec0b01da1ddd752ec5dee553d48",
"000dd748ceabd5508947c6a5d3880a"]`
on_missing : str; keyword-only; optional
Behavior when the mixes to be favorited cannot be found in
the TIDAL catalog.
**API default**: :code:`"FAIL"`.
"""
self._client._require_authentication("users.follow_mixes")
data = {"mixIds": self._prepare_mix_ids(mix_ids)}
if on_missing is not None:
on_missing = self._prepare_string("on_missing", on_missing).upper()
if on_missing not in {"FAIL", "SKIP"}:
raise ValueError(
f"Invalid behavior {on_missing!r} for missing "
"items. Valid values: 'FAIL', 'SKIP'."
)
data["onArtifactNotFound"] = on_missing
self._client._request("PUT", "v2/favorites/mixes/add", data=data)
[docs]
def unfollow_mixes(self, mix_ids: str | Collection[str], /) -> None:
"""
Remove mixes from the current user's collection.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
mix_ids : str or Collection[str]; positional-only
TIDAL IDs of the mixes.
**Examples**:
* :code:`"000ec0b01da1ddd752ec5dee553d48"`
* :code:`"000ec0b01da1ddd752ec5dee553d48,000dd748ceabd5508947c6a5d3880a"`
* :code:`["000ec0b01da1ddd752ec5dee553d48",
"000dd748ceabd5508947c6a5d3880a"]`
"""
self._client._require_authentication("users.unfollow_mixes")
self._client._request(
"PUT",
"v2/favorites/mixes/remove",
data={"mixIds": self._prepare_mix_ids(mix_ids)},
)
[docs]
@TTLCache.cached_method(ttl="user")
def get_followed_playlists(
self,
user_id: int | str | None = None,
/,
country_code: str | None = None,
*,
limit: int | None = None,
offset: int | None = None,
sort_by: str | None = None,
descending: bool | None = None,
) -> dict[str, Any]:
"""
Get TIDAL catalog information for editorial playlists in a
user's collection.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
user_id : int or str; positional-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. If not provided, the
country associated with the current user account or IP
address is used.
**Example**: :code:`"US"`.
limit : int; keyword-only; optional
Maximum number of playlists to return.
**Valid range**: :code:`1` to :code:`100`.
**API default**: :code:`10`.
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`.
sort_by : str; keyword-only; optional
Field to sort the playlists by.
**Valid values**:
* :code:`"DATE"` - Date added.
* :code:`"NAME"` - Playlist name.
**API default**: :code:`"DATE"`.
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 editorial playlists in the
user's collection.
.. admonition:: Sample response
:class: response dropdown
.. code-block::
{
"items": [
{
"created": <str>,
"item": {
"created": <str>,
"creator": {
"id": <int>,
"name": <str>,
"picture": <str>,
"type": <str>
},
"customImageUrl": <str>,
"description": <str>,
"duration": <int>,
"image": <str>,
"lastItemAddedAt": <str>,
"lastUpdated": <str>,
"numberOfTracks": <int>,
"numberOfVideos":<int>,
"popularity": <int>,
"promotedArtists": [
{
"handle": <str>,
"id": <int>,
"name": <str>,
"picture": <str>,
"type": <str>
}
],
"publicPlaylist": <bool>,
"squareImage": <str>,
"title": <str>,
"type": <str>,
"url": <str>,
"uuid": <str>
}
}
],
"limit": <int>,
"offset": <int>,
"totalNumberOfItems": <int>
}
"""
self._client._require_authentication("users.get_followed_playlists")
return self._get_saved_resources(
"playlists",
user_id,
country_code=country_code,
limit=limit,
offset=offset,
sort_by=sort_by,
descending=descending,
)
[docs]
def follow_playlists(
self,
playlist_uuids: str | Collection[str],
/,
*,
user_id: int | str | None = None,
country_code: str | None = None,
folder_uuid: str | None = None,
api_version: int = 2,
) -> None:
"""
Add playlists to a user's collection.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access user recommendations, and view and modify
user's collection.
Parameters
----------
playlist_uuids : str or Collection[str]; positional-only; optional
Playlist UUIDs.
**Examples**:
* :code:`"0ae80812-f8d6-4fc4-90ea-b2df4ecc3861"`
* :code:`"0ae80812-f8d6-4fc4-90ea-b2df4ecc3861,24c9cc46-2fcd-4afb-bcc6-d6c42315f32e"`
* :code:`["0ae80812-f8d6-4fc4-90ea-b2df4ecc3861",
"24c9cc46-2fcd-4afb-bcc6-d6c42315f32e"]`
user_id : int or str; keyword-only; optional
TIDAL ID of the user. If not specified, the current user's
TIDAL ID is used. Only applicable when `version` is
:code:`1`.
**Example**: :code:`"US"`.
folder_uuid : str; keyword-only; optional
UUID of the TIDAL playlist folder to add playlists to. Use
:code:`"root"` or leave blank to target the top-level
"Playlists" folder. Only applicable when `version` is
:code:`2`.
api_version : int; keyword-only; default: :code:`2`
Private TIDAL API version.
**Valid values**:
* :code:`1` – Legacy
:code:`POST /v1/users/{user_id}/favorites/playlists`
endpoint.
* :code:`2` – Current
:code:`PUT /v2/my-collection/playlists/folders/add-favorites`
endpoint.
"""
self._client._require_authentication("users.follow_playlists")
params = {"uuids": self._prepare_uuids("playlist", playlist_uuids)}
self._validate_number("api_version", api_version, int, 1, 2)
if api_version == 1:
if user_id is None:
user_id = self._client._resolve_user_identifier()
if country_code is None:
country_code = self._client._my_country_code
else:
self._validate_country_code(country_code)
self._client._request(
"POST",
f"v1/users/{user_id}/favorites/playlists",
params={"countryCode": country_code},
data=params,
)
else:
if folder_uuid is not None:
if folder_uuid != "root":
self._validate_uuids(folder_uuid)
params["folderId"] = folder_uuid
self._client._request(
"PUT",
"v2/my-collection/playlists/folders/add-favorites",
params=params,
)
[docs]
def unfollow_playlists(
self,
playlist_uuids: str | Collection[str],
/,
*,
user_id: int | str | None = None,
api_version: int = 2,
) -> None:
"""
Remove playlists from a user's collection.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
playlist_uuids : str or Collection[str]; positional-only; optional
Playlist UUIDs. TIDAL resource names may be provided
only when :code:`version=2`.
**Examples**:
* :code:`"trn:playlist:0ae80812-f8d6-4fc4-90ea-b2df4ecc3861"`
* :code:`"trn:playlist:0ae80812-f8d6-4fc4-90ea-b2df4ecc3861,24c9cc46-2fcd-4afb-bcc6-d6c42315f32e"`
* :code:["trn:playlist:0ae80812-f8d6-4fc4-90ea-b2df4ecc3861",
"24c9cc46-2fcd-4afb-bcc6-d6c42315f32e"]
user_id : int or str; keyword-only; optional
TIDAL ID of the user. If not specified, the current user's
TIDAL ID is used. Only applicable when `version` is
:code:`1`.
api_version : int; keyword-only; default: :code:`2`
Private TIDAL API version.
**Valid values**:
* :code:`1` – Legacy
:code:`POST /v1/users/{user_id}/favorites/playlists`
endpoint.
* :code:`2` – Current
:code:`PUT /v2/my-collection/playlists/folders/add-favorites`
endpoint.
"""
self._client._require_authentication("users.unfollow_playlists")
self._validate_number("api_version", api_version, int, 1, 2)
if api_version == 1:
if user_id is None:
user_id = self._client._resolve_user_identifier()
self._client._request(
"DELETE",
f"v1/users/{user_id}/favorites/playlists"
f"/{self._prepare_uuids('playlist', playlist_uuids)}",
)
else:
self._client._request(
"PUT",
"v2/my-collection/playlists/folders/remove",
params={
"trns": self._prepare_uuids(
"playlist", playlist_uuids, has_prefix=True
)
},
)
[docs]
@TTLCache.cached_method(ttl="user")
def get_my_playlists(
self,
*,
limit: int = 50,
cursor: str | None = None,
playlist_types: str | Collection[str] | None = None,
sort_by: str | None = None,
descending: bool | None = None,
) -> dict[str, Any]:
"""
Get TIDAL catalog information for playlists in the current
user's collection.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
limit : int; keyword-only; default: :code:`50`
Maximum number of playlists to return.
**Valid range**: :code:`1` to :code:`50`.
cursor : str; keyword-only; optional
Cursor for fetching the next page of results.
playlist_types : str or Collection[str]; keyword-only; optional
Playlist types to return. If not specified, all playlists
are returned.
**Valid values**:
* :code:`"FOLDER"` – Playlist folders.
* :code:`"PLAYLIST"` – All playlists.
* :code:`"FAVORITE_PLAYLIST"` – Favorited playlists.
* :code:`"USER_PLAYLIST"` – User-created playlists.
**Examples**: :code:`"USER_PLAYLIST"`,
:code:`"FOLDER,USER_PLAYLIST"`,
:code:`["FOLDER", "USER_PLAYLIST"]`.
sort_by : str; keyword-only; optional
Field to sort the playlists by.
**Valid values**:
* :code:`"DATE"` - Date added.
* :code:`"NAME"` - Playlist name.
**API default**: :code:`"DATE"`.
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 in the current
user's collection.
.. admonition:: Sample response
:class: response dropdown
.. code-block::
{
"cursor": <str>,
"items": [
{
"addedAt": <str>,
"data": {
"createdAt": <str>,
"id": <str>,
"itemType": "FOLDER",
"lastModifiedAt": <str>,
"name": <str>,
"totalNumberOfItems": <int>,
"trn": <str>,
},
"itemType": "FOLDER",
"lastModifiedAt": <str>,
"name": <str>,
"parent": {
"id": <str>,
"name": <str>
},
"trn": <str>,
},
{
"addedAt": <str>,
"data": {
"contentBehavior": <str>,
"created": <str>,
"creator": {
"id": <int>,
"name": <str>,
"picture": <str>,
"type": <str>
},
"curators": [
{
"handle": <str>,
"id": <int>,
"name": <str>,
"picture": <str>
}
],
"customImageUrl": <str>,
"description": <str>,
"duration": <int>,
"image": <str>,
"itemType": "PLAYLIST",
"lastItemAddedAt": <str>,
"lastUpdated": <str>,
"numberOfTracks": <int>,
"numberOfVideos": <int>,
"promotedArtists": [
{
"id": <int>,
"name": <str>,
"type": <str>
}
],
"sharingLevel": <str>,
"source": <str>,
"squareImage": <str>,
"status": <str>,
"title": <str>,
"trn": <str>,
"type": <str>,
"url": <str>,
"uuid": <str>
},
"itemType": "PLAYLIST",
"lastModifiedAt": <str>,
"name": <str>,
"parent": {
"id": <str>,
"name": <str>
},
"trn": <str>
}
],
"lastModifiedAt": <str>
}
"""
self._client._require_authentication("playlists.get_my_playlists")
return self._get_my_playlists(
"playlists",
cursor=cursor,
limit=limit,
playlist_types=playlist_types,
sort_by=sort_by,
descending=descending,
)
[docs]
@TTLCache.cached_method(ttl="user")
def get_my_folder(
self,
folder_uuid: str | None = None,
/,
*,
limit: int = 50,
cursor: str | None = None,
playlist_types: str | Collection[str] | None = None,
sort_by: str | None = None,
descending: bool | None = None,
) -> dict[str, Any]:
"""
Get TIDAL catalog information for playlists in a playlist folder
in the current user's collection.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
folder_uuid : str; positional-only; optional
UUID of TIDAL playlist folder to retrieve playlists from.
Use :code:`"root"` or leave blank to target the top-level
"Playlists" folder.
limit : int; keyword-only; default: :code:`50`
Maximum number of playlists to return.
**Valid range**: :code:`1` to :code:`50`.
cursor : str; keyword-only; optional
Cursor for fetching the next page of results.
playlist_types : str or Collection[str]; keyword-only; optional
Playlist types to return. If not specified, all playlists
are returned.
**Valid values**:
* :code:`"FOLDER"` – Playlist folders.
* :code:`"PLAYLIST"` – All playlists.
* :code:`"FAVORITE_PLAYLIST"` – Favorited playlists.
* :code:`"USER_PLAYLIST"` – User-created playlists.
**Examples**: :code:`"USER_PLAYLIST"`,
:code:`"FOLDER,USER_PLAYLIST"`,
:code:`["FOLDER", "USER_PLAYLIST"]`.
sort_by : str; keyword-only; optional
Field to sort the playlists by.
**Valid values**:
* :code:`"DATE"` - Date added.
* :code:`"NAME"` - Playlist name.
**API default**: :code:`"DATE"`.
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 in the playlist
folder in the current user's collection.
.. admonition:: Sample response
:class: response dropdown
.. code-block::
{
"cursor": <str>,
"items": [
{
"addedAt": <str>,
"data": {
"createdAt": <str>,
"id": <str>,
"itemType": "FOLDER",
"lastModifiedAt": <str>,
"name": <str>,
"totalNumberOfItems": <int>,
"trn": <str>,
},
"itemType": "FOLDER",
"lastModifiedAt": <str>,
"name": <str>,
"parent": {
"id": <str>,
"name": <str>
},
"trn": <str>,
},
{
"addedAt": <str>,
"data": {
"contentBehavior": <str>,
"created": <str>,
"creator": {
"id": <int>,
"name": <str>,
"picture": <str>,
"type": <str>
},
"curators": [
{
"handle": <str>,
"id": <int>,
"name": <str>,
"picture": <str>
}
],
"customImageUrl": <str>,
"description": <str>,
"duration": <int>,
"image": <str>,
"itemType": "PLAYLIST",
"lastItemAddedAt": <str>,
"lastUpdated": <str>,
"numberOfTracks": <int>,
"numberOfVideos": <int>,
"promotedArtists": [
{
"id": <int>,
"name": <str>,
"type": <str>
}
],
"sharingLevel": <str>,
"source": <str>,
"squareImage": <str>,
"status": <str>,
"title": <str>,
"trn": <str>,
"type": <str>,
"url": <str>,
"uuid": <str>
},
"itemType": "PLAYLIST",
"lastModifiedAt": <str>,
"name": <str>,
"parent": {
"id": <str>,
"name": <str>
},
"trn": <str>
}
],
"lastModifiedAt": <str>,
"totalNumberOfItems": <int>
}
"""
self._client._require_authentication("playlists.get_my_folder")
params = {}
if folder_uuid is not None:
if folder_uuid != "root":
self._validate_uuids(folder_uuid)
params["folderId"] = folder_uuid
return self._get_my_playlists(
"playlists/folders",
cursor=cursor,
limit=limit,
playlist_types=playlist_types,
sort_by=sort_by,
descending=descending,
params=params,
)
[docs]
@TTLCache.cached_method(ttl="user")
def get_my_folders_and_playlists(
self,
*,
limit: int = 50,
cursor: str | None = None,
playlist_types: str | Collection[str] | None = None,
sort_by: str | None = None,
descending: bool | None = None,
) -> dict[str, Any]:
"""
Get TIDAL catalog information for all playlist folders and
playlists in the current user's collection.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
limit : int; keyword-only; default: :code:`50`
Maximum number of playlists to return.
**Valid range**: :code:`1` to :code:`50`.
cursor : str; keyword-only; optional
Cursor for fetching the next page of results.
playlist_types : str or Collection[str]; keyword-only; optional
Playlist types to return. If not specified, all playlists
are returned.
**Valid values**:
* :code:`"FOLDER"` – Playlist folders.
* :code:`"PLAYLIST"` – All playlists.
* :code:`"FAVORITE_PLAYLIST"` – Favorited playlists.
* :code:`"USER_PLAYLIST"` – User-created playlists.
**Examples**: :code:`"USER_PLAYLIST"`,
:code:`"FOLDER,USER_PLAYLIST"`,
:code:`["FOLDER", "USER_PLAYLIST"]`.
sort_by : str; keyword-only; optional
Field to sort the playlists by.
**Valid values**:
* :code:`"DATE"` - Date added.
* :code:`"NAME"` - Playlist name.
**API default**: :code:`"DATE"`.
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 playlist folders and
playlists in the current user's collection.
.. admonition:: Sample response
:class: response dropdown
.. code-block::
{
"cursor": <str>,
"items": [
{
"addedAt": <str>,
"data": {
"createdAt": <str>,
"id": <str>,
"itemType": "FOLDER",
"lastModifiedAt": <str>,
"name": <str>,
"totalNumberOfItems": <int>,
"trn": <str>,
},
"itemType": "FOLDER",
"lastModifiedAt": <str>,
"name": <str>,
"parent": {
"id": <str>,
"name": <str>
},
"trn": <str>,
},
{
"addedAt": <str>,
"data": {
"contentBehavior": <str>,
"created": <str>,
"creator": {
"id": <int>,
"name": <str>,
"picture": <str>,
"type": <str>
},
"curators": [
{
"handle": <str>,
"id": <int>,
"name": <str>,
"picture": <str>
}
],
"customImageUrl": <str>,
"description": <str>,
"duration": <int>,
"image": <str>,
"itemType": "PLAYLIST",
"lastItemAddedAt": <str>,
"lastUpdated": <str>,
"numberOfTracks": <int>,
"numberOfVideos": <int>,
"promotedArtists": [
{
"id": <int>,
"name": <str>,
"type": <str>
}
],
"sharingLevel": <str>,
"source": <str>,
"squareImage": <str>,
"status": <str>,
"title": <str>,
"trn": <str>,
"type": <str>,
"url": <str>,
"uuid": <str>
},
"itemType": "PLAYLIST",
"lastModifiedAt": <str>,
"name": <str>,
"parent": {
"id": <str>,
"name": <str>
},
"trn": <str>
}
],
"lastModifiedAt": <str>,
}
"""
self._client._require_authentication(
"playlists.get_my_folders_and_playlists"
)
return self._get_my_playlists(
"playlists/folders/flattened",
cursor=cursor,
limit=limit,
playlist_types=playlist_types,
sort_by=sort_by,
descending=descending,
)
[docs]
@TTLCache.cached_method(ttl="user")
def get_user_playlists(
self,
user_id: int | str | None = None,
/,
country_code: str | None = None,
*,
limit: int | None = None,
offset: int | None = None,
sort_by: str | None = None,
descending: bool | None = None,
) -> dict[str, Any]:
"""
Get TIDAL catalog information for editorial and user-created
playlists in a user's collection.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
user_id : int or str; positional-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. If not provided, the
country associated with the current user account or IP
address is used.
**Example**: :code:`"US"`.
limit : int; keyword-only; optional
Maximum number of playlists to return.
**Valid range**: :code:`1` to :code:`100`.
**API default**: :code:`10`.
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`.
sort_by : str; keyword-only; optional
Field to sort the playlists by.
**Valid values**:
* :code:`"DATE"` - Date added.
* :code:`"NAME"` - Playlist name.
**API default**: :code:`"DATE"`.
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 editorial and user-created
playlists in the user's collection.
.. admonition:: Sample response
:class: response dropdown
.. code-block::
{
"items": [
{
"created": <str>,
"playlist": {
"created": <str>,
"creator": {
"id": <int>,
"name": <str>,
"picture": <str>,
"type": <str>
},
"customImageUrl": <str>,
"description": <str>,
"duration": <int>,
"image": <str>,
"lastItemAddedAt": <str>,
"lastUpdated": <str>,
"numberOfTracks": <int>,
"numberOfVideos":<int>,
"popularity": <int>,
"promotedArtists": [
{
"handle": <str>,
"id": <int>,
"name": <str>,
"picture": <str>,
"type": <str>
}
],
"publicPlaylist": <bool>,
"squareImage": <str>,
"title": <str>,
"type": <str>,
"url": <str>,
"uuid": <str>
},
"type": "USER_CREATED"
},
{
"created": <str>,
"playlist": {
"created": <str>,
"creator": {
"id": <int>,
"name": <str>,
"picture": <str>,
"type": <str>
},
"customImageUrl": <str>,
"description": <str>,
"duration": <int>,
"image": <str>,
"lastItemAddedAt": <str>,
"lastUpdated": <str>,
"numberOfTracks": <int>,
"numberOfVideos":<int>,
"popularity": <int>,
"promotedArtists": [
{
"handle": <str>,
"id": <int>,
"name": <str>,
"picture": <str>,
"type": <str>
}
],
"publicPlaylist": <bool>,
"squareImage": <str>,
"title": <str>,
"type": <str>,
"url": <str>,
"uuid": <str>
},
"type": "USER_FAVORITE"
}
],
"limit": <int>,
"offset": <int>,
"totalNumberOfItems": <int>
}
"""
self._client._require_authentication("playlists.get_user_playlists")
return self._get_user_playlists(
"playlistsAndFavoritePlaylists",
user_id,
country_code=country_code,
limit=limit,
offset=offset,
sort_by=sort_by,
descending=descending,
)
[docs]
@TTLCache.cached_method(ttl="user")
def get_user_created_playlists(
self,
user_id: int | str | None = None,
/,
country_code: str | None = None,
*,
limit: int | None = None,
offset: int | None = None,
sort_by: str | None = None,
descending: bool | None = None,
) -> dict[str, Any]:
"""
Get TIDAL catalog information for user-created playlists in a
user's collection.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
user_id : int or str; positional-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. If not provided, the
country associated with the current user account or IP
address is used.
**Example**: :code:`"US"`.
limit : int; keyword-only; optional
Maximum number of playlists to return.
**Valid range**: :code:`1` to :code:`100`.
**API default**: :code:`10`.
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`.
sort_by : str; keyword-only; optional
Field to sort the playlists by.
**Valid values**:
* :code:`"DATE"` - Date added.
* :code:`"NAME"` - Playlist name.
**API default**: :code:`"DATE"`.
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 user-created playlists in the
user's collection.
.. admonition:: Sample response
:class: response dropdown
.. code-block::
{
"items": [
{
"created": <str>,
"playlist": {
"created": <str>,
"creator": {
"id": <int>,
"name": <str>,
"picture": <str>,
"type": <str>
},
"customImageUrl": <str>,
"description": <str>,
"duration": <int>,
"image": <str>,
"lastItemAddedAt": <str>,
"lastUpdated": <str>,
"numberOfTracks": <int>,
"numberOfVideos":<int>,
"popularity": <int>,
"promotedArtists": [
{
"handle": <str>,
"id": <int>,
"name": <str>,
"picture": <str>,
"type": <str>
}
],
"publicPlaylist": <bool>,
"squareImage": <str>,
"title": <str>,
"type": <str>,
"url": <str>,
"uuid": <str>
},
"type": "USER_CREATED"
}
],
"limit": <int>,
"offset": <int>,
"totalNumberOfItems": <int>
}
"""
self._client._require_authentication(
"playlists.get_user_created_playlists"
)
return self._get_user_playlists(
"playlists",
user_id,
country_code=country_code,
limit=limit,
offset=offset,
sort_by=sort_by,
descending=descending,
)
[docs]
@TTLCache.cached_method(ttl="user")
def get_user_public_playlists(
self,
user_id: int | str | None = None,
/,
*,
limit: int | None = None,
cursor: str | None = None,
) -> dict[str, Any]:
"""
Get TIDAL catalog information for public playlists in a user's
collection.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
user_id : int or str; positional-only; optional
TIDAL ID of the user. If not specified, the current user's
TIDAL ID is used.
limit : int; keyword-only; optional
Maximum number of playlists to return.
**Valid range**: :code:`1` to :code:`10_000`.
cursor : str; keyword-only; optional
Cursor for fetching the next page of results.
Returns
-------
playlists : dict[str, Any]
Page of TIDAL metadata for the public playlists in the
user's collection.
.. admonition:: Sample response
:class: response dropdown
.. code-block::
{
"cursor": <str>,
"items": [
{
"followInfo": {
"followType": "PLAYLIST",
"followed": <bool>,
"nrOfFollowers": <int>,
"tidalResourceName": <str>
},
"playlist": {
"contentBehavior": <str>,
"created": <str>,
"creator": {
"id": <int>,
"name": <str>,
"picture": <str>,
"type": "USER"
},
"curators": [
{
"handle": <str>,
"id": <int>,
"name": <str>,
"picture": <str>
}
],
"customImageUrl": <str>,
"description": <str>,
"duration": <int>,
"image": <str>,
"lastItemAddedAt": <str>,
"lastUpdated": <str>,
"numberOfTracks": <int>,
"numberOfVideos": <int>,
"promotedArtists": [
{
"contributionLinkUrl": <str>,
"handle": <str>,
"id": <int>,
"name": <str>,
"picture": <str>,
"type": <str>,
"userId": <int>
}
],
"sharingLevel": "PUBLIC",
"source": <str>,
"squareImage": <str>,
"status": <str>,
"title": <str>,
"trn": <str>,
"type": "USER",
"url": <str>,
"uuid": <str>
},
"profile": {
"color": <list[str]>,
"name": <str>,
"userId": <int>
}
}
]
}
"""
self._client._require_authentication(
"playlists.get_user_public_playlists"
)
return self._get_user_relationship(
"user-playlists",
"public",
user_id=user_id,
cursor=cursor,
limit=limit,
)
[docs]
@TTLCache.cached_method(ttl="user")
def get_user_followers(
self,
user_id: int | str | None = None,
/,
*,
limit: int | None = None,
cursor: str | None = None,
) -> dict[str, Any]:
"""
Get TIDAL profile information for users following the specified
user.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
user_id : int or str; positional-only; optional
TIDAL ID of the user. If not specified, the current user's
TIDAL ID is used.
limit : int; keyword-only; optional
Maximum number of users to return.
**Valid range**: :code:`1` to :code:`10_000`.
cursor : str; keyword-only; optional
Cursor for fetching the next page of results.
Returns
-------
followers : dict[str, Any]
TIDAL profile information for the user's followers.
**Sample response**: :code:`{'cursor': None, 'items': {}}`.
"""
self._client._require_authentication("playlists.get_user_followers")
return self._get_user_relationship(
"profiles",
"followers",
user_id=user_id,
cursor=cursor,
limit=limit,
)
[docs]
@TTLCache.cached_method(ttl="user")
def get_user_followings(
self,
user_id: int | str | None = None,
/,
*,
limit: int | None = None,
cursor: str | None = None,
) -> dict[str, Any]:
"""
Get TIDAL catalog information for the users followed by a
specified user.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
user_id : int or str; positional-only; optional
TIDAL ID of the user. If not specified, the current user's
TIDAL ID is used.
limit : int; keyword-only; optional
Maximum number of users to return.
**Valid range**: :code:`1` to :code:`10_000`.
cursor : str; keyword-only; optional
Cursor for fetching the next page of results.
Returns
-------
following : dict[str, Any]
TIDAL profile information for the followed users.
**Sample response**: :code:`{'cursor': None, 'items': {}}`.
"""
self._client._require_authentication("playlists.get_user_following")
return self._get_user_relationship(
"profiles",
"following",
user_id=user_id,
cursor=cursor,
limit=limit,
)
[docs]
def follow_user(self, user_id: int | str, /) -> None:
"""
Follow a TIDAL user.
.. caution::
This endpoint appears to have been deprecated by TIDAL.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
user_id : int or str; positional-only
TIDAL ID of the user.
"""
self._manage_followings("PUT", user_id)
[docs]
def unfollow_user(self, user_id: int | str, /) -> None:
"""
Unfollow a TIDAL user.
.. caution::
This endpoint appears to have been deprecated by TIDAL.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
user_id : int or str; positional-only
TIDAL ID of the user.
"""
self._manage_followings("DELETE", user_id)
[docs]
@TTLCache.cached_method(ttl="user")
def get_my_blocked_users(
self, *, limit: int | None = None, offset: int | None = None
) -> dict[str, Any]:
"""
Get TIDAL profile information for the users blocked by the
current user.
.. caution::
This endpoint appears to have been deprecated by TIDAL.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
limit : int; keyword-only; optional
Maximum number of users to return.
**Valid range**: :code:`1` to :code:`100`.
**API default**: :code:`10`.
offset : int; keyword-only; optional
Index of the first user to return. Use with `limit` to get
the next batch of users.
**Minimum value**: :code:`0`.
**API default**: :code:`0`.
Returns
-------
users : dict[str, Any]
Page of TIDAL profile information for the users blocked by
the current user.
**Sample response**: :code:`{'items': [], 'limit': 0,
'offset': 0, 'totalNumberOfItems': 0}`.
"""
self._client._require_authentication("playlists.get_my_blocked_users")
params = {}
if limit is not None:
self._validate_number("limit", limit, int, 1, 100)
params["limit"] = limit
if offset is not None:
self._validate_number("offset", offset, int, 0)
params["offset"] = offset
return self._client._request(
"GET", "v2/profiles/blocked-profiles", params=params
).json()
[docs]
def block_user(self, user_id: int | str, /) -> None:
"""
Block a TIDAL user.
.. caution::
This endpoint appears to have been deprecated by TIDAL.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
user_id : int or str; positional-only
TIDAL ID of the user.
"""
self._manage_blocked_users("PUT", user_id)
[docs]
def unblock_user(self, user_id: int | str, /) -> None:
"""
Unblock a TIDAL user.
.. caution::
This endpoint appears to have been deprecated by TIDAL.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
user_id : int or str; positional-only
TIDAL ID of the user.
"""
self._manage_blocked_users("DELETE", user_id)
[docs]
@TTLCache.cached_method(ttl="user")
def get_user_saved_tracks(
self,
user_id: int | str | None = None,
/,
country_code: str | None = None,
*,
limit: int | None = None,
offset: int | None = None,
sort_by: str | None = None,
descending: bool | None = None,
) -> dict[str, Any]:
"""
Get TIDAL catalog information for tracks in a user's collection.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
user_id : int or str; positional-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. If not provided, the
country associated with the current user account or IP
address is used.
**Example**: :code:`"US"`.
limit : int; keyword-only; optional
Maximum number of tracks to return.
**Valid range**: :code:`1` to :code:`100`.
**API default**: :code:`10`.
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`.
sort_by : str; keyword-only; optional
Field to sort the tracks by.
**Valid values**:
* :code:`"DATE"` - Date added.
* :code:`"NAME"` - Track name.
**API default**: :code:`"DATE"`.
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::
{
"items": [
{
"created": <str>,
"item": {
"accessType": <str>,
"adSupportedStreamReady": <bool>,
"album": {
"cover": <str>,
"id": <int>,
"title": <str>,
"vibrantColor": <str>,
"videoCover": <str>
},
"allowStreaming": <bool>,
"artist": {
"handle": <str>,
"id": <int>,
"name": <str>,
"picture": <str>,
"type": "MAIN"
},
"artists": [
{
"handle": <str>,
"id": <int>,
"name": <str>,
"picture": <str>,
"type": <str>
}
],
"audioModes": <list[str]>,
"audioQuality": <str>,
"bpm": 128,
"copyright": <str>,
"djReady": <bool>,
"duration": <int>,
"editable": <bool>,
"explicit": <bool>,
"id": <int>,
"isrc": <str>,
"key": <str>,
"keyScale": <str>,
"mediaMetadata": {
"tags": <list[str]>
},
"mixes": {
"TRACK_MIX": <str>
},
"payToStream": <bool>,
"peak": <float>,
"popularity": <int>,
"premiumStreamingOnly": <bool>,
"replayGain": <float>,
"spotlighted": <bool>,
"stemReady": <bool>,
"streamReady": <bool>,
"streamStartDate": <str>,
"title": <str>,
"trackNumber": <int>,
"upload": <bool>,
"url": <str>,
"version": <str>,
"volumeNumber": <int>,
}
}
],
"limit": <int>,
"offset": <int>,
"totalNumberOfItems": <int>
}
"""
self._client._require_authentication("users.get_user_saved_tracks")
return self._get_saved_resources(
"tracks",
user_id,
country_code=country_code,
limit=limit,
offset=offset,
sort_by=sort_by,
descending=descending,
)
[docs]
def save_tracks(
self,
track_ids: int | str | Collection[int | str],
/,
user_id: int | str | None = None,
country_code: str | None = None,
*,
on_missing: str | None = None,
) -> None:
"""
Add tracks to a user's collection.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
track_ids : int, str, or Collection[int | str]; positional-only
TIDAL IDs of the tracks.
**Examples**: :code:`46369325`, :code:`"251380837"`,
:code:`"46369325,251380837"`,
:code:`[46369325, "251380837"]`.
user_id : int or str; 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. If not provided, the
country associated with the current user account or IP
address is used.
**Example**: :code:`"US"`.
on_missing : str; keyword-only; optional
Behavior when the tracks to be favorited cannot be found in
the TIDAL catalog.
**API default**: :code:`"FAIL"`.
"""
self._client._require_authentication("users.save_tracks")
return self._save_resources(
"tracks",
track_ids,
user_id=user_id,
country_code=country_code,
on_missing=on_missing,
)
[docs]
def remove_saved_tracks(
self,
track_ids: int | str | Collection[int | str],
/,
user_id: int | str | None = None,
) -> None:
"""
Remove tracks from a user's collection.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
track_ids : int, str, or Collection[int | str]; positional-only
TIDAL IDs of the tracks.
**Examples**: :code:`46369325`, :code:`"251380837"`,
:code:`"46369325,251380837"`,
:code:`[46369325, "251380837"]`.
user_id : int or str; optional
TIDAL ID of the user. If not specified, the current user's
TIDAL ID is used.
"""
self._client._require_authentication("users.remove_saved_tracks")
return self._remove_saved_resources(
"tracks", track_ids, user_id=user_id
)
[docs]
@TTLCache.cached_method(ttl="user")
def get_user_blocked_tracks(
self,
user_id: int | str | None = None,
/,
*,
limit: int | None = None,
offset: int | None = None,
) -> dict[str, Any]:
"""
Get TIDAL catalog information for tracks blocked by a user.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
user_id : int or str; positional-only; optional
TIDAL ID of the user. If not specified, the current user's
TIDAL ID is used.
limit : int; keyword-only; optional
Maximum number of tracks to return.
**Valid range**: :code:`1` to :code:`100`.
**API default**: :code:`10`.
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 TIDAL metadata for the user's blocked tracks.
.. admonition:: Sample response
:class: response dropdown
.. code-block::
{
"items": [
{
"created": <str>,
"item": {
"accessType": <str>,
"adSupportedStreamReady": <bool>,
"album": {
"cover": <str>,
"id": <int>,
"title": <str>,
"vibrantColor": <str>,
"videoCover": <str>
},
"allowStreaming": <bool>,
"artist": {
"handle": <str>,
"id": <int>,
"name": <str>,
"picture": <str>,
"type": "MAIN"
},
"artists": [
{
"handle": <str>,
"id": <int>,
"name": <str>,
"picture": <str>,
"type": <str>
}
],
"audioModes": <list[str]>,
"audioQuality": <str>,
"bpm": <int>,
"copyright": <str>,
"djReady": <bool>,
"duration": <int>,
"editable": <bool>,
"explicit": <bool>,
"id": <int>,
"isrc": <str>,
"key": <str>,
"keyScale": <str>,
"mediaMetadata": {
"tags": <list[str]>
},
"mixes": {
"TRACK_MIX": <str>
},
"payToStream": <bool>,
"peak": <float>,
"popularity": <int>,
"premiumStreamingOnly": <bool>,
"replayGain": <float>,
"spotlighted": <bool>,
"stemReady": <bool>,
"streamReady": <bool>,
"streamStartDate": <str>,
"title": <str>,
"trackNumber": <int>,
"upload": <bool>,
"url": <str>,
"version": <str>,
"volumeNumber": <int>
},
"type": "TRACK"
}
],
"limit": <int>,
"offset": <int>,
"totalNumberOfItems": <int>
}
"""
self._client._require_authentication("users.get_user_blocked_tracks")
return self._get_blocked_resources(
"tracks", user_id, limit=limit, offset=offset
)
[docs]
def block_track(
self, track_id: int | str, /, user_id: int | str | None = None
) -> None:
"""
Block a track for a user.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
track_id : int or str; positional-only
TIDAL ID of the track.
**Examples**: :code:`46369325`, :code:`"251380837"`.
user_id : int or str; optional
TIDAL ID of the user. If not specified, the current user's
TIDAL ID is used.
"""
self._client._require_authentication("users.block_track")
self._manage_blocked_resources(
"POST", "track", track_id, user_id=user_id
)
[docs]
def unblock_track(
self, track_id: int | str, /, user_id: int | str | None = None
) -> None:
"""
Unblock a track for a user.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
track_id : int or str; positional-only
TIDAL ID of the track.
**Examples**: :code:`46369325`, :code:`"251380837"`.
user_id : int or str; optional
TIDAL ID of the user. If not specified, the current user's
TIDAL ID is used.
"""
self._client._require_authentication("users.unblock_track")
self._manage_blocked_resources(
"DELETE", "track", track_id, user_id=user_id
)
[docs]
@TTLCache.cached_method(ttl="user")
def get_user_saved_videos(
self,
user_id: int | str | None = None,
/,
country_code: str | None = None,
*,
limit: int | None = None,
offset: int | None = None,
sort_by: str | None = None,
descending: bool | None = None,
) -> dict[str, Any]:
"""
Get TIDAL catalog information for videos in a user's collection.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
user_id : int or str; positional-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. If not provided, the
country associated with the current user account or IP
address is used.
**Example**: :code:`"US"`.
limit : int; keyword-only; optional
Maximum number of videos to return.
**Valid range**: :code:`1` to :code:`100`.
**API default**: :code:`10`.
offset : int; keyword-only; optional
Index of the first video to return. Use with `limit` to get
the next batch of videos.
**Minimum value**: :code:`0`.
**API default**: :code:`0`.
sort_by : str; keyword-only; optional
Field to sort the videos by.
**Valid values**:
* :code:`"DATE"` - Date added.
* :code:`"NAME"` - Video name.
**API default**: :code:`"DATE"`.
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::
{
"items": [
{
"created": <str>,
"item": {
"adSupportedStreamReady": <bool>,
"adsPrePaywallOnly": <bool>,
"adsUrl": <str>,
"album": {},
"allowStreaming": <bool>,
"artist": {
"handle": <str>,
"id": <int>,
"name": <str>,
"picture": <str>,
"type": "MAIN"
},
"artists": [
{
"handle": <str>,
"id": <int>,
"name": <str>,
"picture": <str>,
"type": <str>
}
],
"djReady": <bool>,
"duration": <int>,
"explicit": <bool>,
"id": <int>,
"imageId": <str>,
"imagePath": <str>,
"popularity": <int>,
"quality": <str>,
"releaseDate": <str>,
"stemReady": <bool>,
"streamReady": <bool>,
"streamStartDate": <str>,
"title": <str>,
"trackNumber": <int>,
"type": <str>,
"vibrantColor": <str>,
"volumeNumber": <int>
}
}
],
"limit": <int>,
"offset": <int>,
"totalNumberOfItems": <int>
}
"""
self._client._require_authentication("users.get_user_saved_videos")
return self._get_saved_resources(
"videos",
user_id,
country_code=country_code,
limit=limit,
offset=offset,
sort_by=sort_by,
descending=descending,
)
[docs]
def save_videos(
self,
video_ids: int | str | Collection[int | str],
/,
user_id: int | str | None = None,
country_code: str | None = None,
*,
on_missing: str | None = None,
) -> None:
"""
Add videos to a user's collection.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
video_ids : int, str, or Collection[int | str]; positional-only
TIDAL IDs of the videos.
**Examples**: :code:`29597422`, :code:`"59727844"`,
:code:`"29597422,59727844"`, :code:`[29597422, "59727844"]`.
user_id : int or str; 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. If not provided, the
country associated with the current user account or IP
address is used.
**Example**: :code:`"US"`.
on_missing : str; keyword-only; optional
Behavior when the videos to be favorited cannot be found in
the TIDAL catalog.
**API default**: :code:`"FAIL"`.
"""
self._client._require_authentication("users.save_videos")
return self._save_resources(
"videos",
video_ids,
user_id=user_id,
country_code=country_code,
on_missing=on_missing,
)
[docs]
def remove_saved_videos(
self,
video_ids: int | str | Collection[int | str],
/,
user_id: int | str | None = None,
) -> None:
"""
Remove videos from a user's collection.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
video_ids : int, str, or Collection[int | str]; positional-only
TIDAL IDs of the videos.
**Examples**: :code:`29597422`, :code:`"59727844"`,
:code:`"29597422,59727844"`, :code:`[29597422, "59727844"]`.
user_id : int or str; optional
TIDAL ID of the user. If not specified, the current user's
TIDAL ID is used.
"""
self._client._require_authentication("users.remove_saved_videos")
return self._remove_saved_resources(
"videos", video_ids, user_id=user_id
)
[docs]
@TTLCache.cached_method(ttl="user")
def get_user_blocked_videos(
self,
user_id: int | str | None = None,
/,
*,
limit: int | None = None,
offset: int | None = None,
) -> dict[str, Any]:
"""
Get TIDAL catalog information for videos blocked by a user.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
user_id : int or str; positional-only; optional
TIDAL ID of the user. If not specified, the current user's
TIDAL ID is used.
limit : int; keyword-only; optional
Maximum number of videos to return.
**Valid range**: :code:`1` to :code:`100`.
**API default**: :code:`10`.
offset : int; keyword-only; optional
Index of the first video to return. Use with `limit` to get
the next batch of videos.
**Minimum value**: :code:`0`.
**API default**: :code:`0`.
Returns
-------
videos : dict[str, Any]
Page of TIDAL metadata for the videos blocked by the user.
.. admonition:: Sample response
:class: response dropdown
.. code-block::
{
"items": [
{
"created": <str>,
"item": {
"adSupportedStreamReady": <bool>,
"adsPrePaywallOnly": <bool>,
"adsUrl": <str>,
"album": {},
"allowStreaming": <bool>,
"artist": {
"handle": <str>,
"id": <int>,
"name": <str>,
"picture": <str>,
"type": "MAIN"
},
"artists": [
{
"handle": <str>,
"id": <int>,
"name": <str>,
"picture": <str>,
"type": <str>
}
],
"djReady": <bool>,
"duration": <int>,
"explicit": <bool>,
"id": <int>,
"imageId": <str>,
"imagePath": <str>,
"popularity": <int>,
"quality": <str>,
"releaseDate": <str>,
"stemReady": <bool>,
"streamReady": <bool>,
"streamStartDate": <str>,
"title": <str>,
"trackNumber": <int>,
"type": <str>,
"vibrantColor": <str>,
"volumeNumber": <int>
},
"type": "VIDEO"
}
],
"limit": <int>,
"offset": <int>,
"totalNumberOfItems": <int>
}
"""
self._client._require_authentication("users.get_user_blocked_videos")
return self._get_blocked_resources(
"videos", user_id, limit=limit, offset=offset
)
[docs]
def block_video(
self, video_id: int | str, /, user_id: int | str | None = None
) -> None:
"""
Block a video for a user.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
video_id : int or str; positional-only
TIDAL ID of the video.
**Examples**: :code:`29597422`, :code:`"59727844"`.
user_id : int or str; optional
TIDAL ID of the user. If not specified, the current user's
TIDAL ID is used.
"""
self._client._require_authentication("users.block_video")
self._manage_blocked_resources(
"POST", "video", video_id, user_id=user_id
)
[docs]
def unblock_video(
self, video_id: int | str, /, user_id: int | str | None = None
) -> None:
"""
Unblock a video for a user.
.. admonition:: User authentication
:class: entitlement
.. tab-set::
.. tab-item:: Required
User authentication
Access and manage the user's collection.
Parameters
----------
video_id : int or str; positional-only
TIDAL ID of the video.
**Examples**: :code:`29597422`, :code:`"59727844"`.
user_id : int or str; optional
TIDAL ID of the user. If not specified, the current user's
TIDAL ID is used.
"""
self._client._require_authentication("users.unblock_video")
self._manage_blocked_resources(
"DELETE", "video", video_id, user_id=user_id
)