Source code for minim.api.tidal._api.tracks
from __future__ import annotations
from typing import TYPE_CHECKING
from ...._types import COLLECTION_TYPES, ORDERED_COLLECTION_TYPES
from ..._shared import TTLCache, _copy_docstring
from ._shared import TIDALResourceAPI
from .search import SearchAPI
from .users import UsersAPI
if TYPE_CHECKING:
from typing import Any
from ...._types import Collection
[docs]
class TracksAPI(TIDALResourceAPI):
"""
Tracks and Track Manifests API endpoints for the TIDAL API.
.. important::
This class is managed by :class:`~minim.api.tidal.TIDALAPIClient`
and should not be instantiated directly.
"""
_FORMATS = {"HEAACV1", "AACLC", "FLAC", "FLAC_HIRES", "EAC3_JOC"}
_MANIFEST_TYPES = {"HLS", "MPEG_DASH"}
_RELATIONSHIPS = {
"albums",
"artists",
"credits",
"download",
"genres",
"lyrics",
"metadataStatus",
"owners",
"priceConfig",
"providers",
"radio",
"shares",
"similarTracks",
"sourceFile",
"suggestedTracks",
"trackStatistics",
"usageRules",
}
_SORT_FIELDS = {"createdAt", "title"}
_URI_SCHEMES = {"DATA", "HTTPS"}
_USAGES = {"DOWNLOAD", "PLAYBACK"}
__slots__ = ()
@classmethod
def _prepare_formats(cls, formats: str | Collection[str], /) -> list[str]:
"""
Validate, normalize and serialize audio formats.
Parameters
----------
formats : str or Collection[str]; positional-only
Audio formats.
**Valid values**: :code:`"HEAACV1"`, :code:`"AACLC"`,
:code:`"FLAC"`, :code:`"FLAC_HIRES"`, :code:`"EAC3_JOC"`.
Returns
-------
formats : list[str]
List of audio formats.
"""
if isinstance(formats, str):
formats = cls._prepare_string("formats", formats)
if formats not in cls._FORMATS:
raise ValueError(
f"Invalid audio format {formats!r}. Valid values: "
f"{cls._join_values(cls._FORMATS)}."
)
return formats
if isinstance(formats, COLLECTION_TYPES):
return [cls._prepare_formats(format_) for format_ in formats]
raise TypeError(
"`formats` must be `None`, a string, or a collection of strings."
)
[docs]
@TTLCache.cached_method(ttl="hourly")
def get_track_media_info(
self,
track_id: int | str,
/,
*,
manifest_type: str = "MPEG_DASH",
formats: str | Collection[str] | None = None,
uri_scheme: str = "HTTPS",
intent: str = "PLAYBACK",
adaptive: bool = True,
share_code: str | None = None,
) -> dict[str, Any]:
"""
`Track Manifests > Get Single Track Manifest
<https://tidal-music.github.io/tidal-api-reference/#
/trackManifests/get_trackManifests__id_>`_: Get TIDAL media
information for a track.
.. admonition:: Subscription
:class: entitlement dropdown
.. tab-set::
.. tab-item:: Optional
TIDAL streaming plan
Stream full-length and high-resolution audio.
`Learn more. <https://tidal.com/pricing>`__
Parameters
----------
track_id : int or str; positional-only
TIDAL ID of the track.
**Examples**: :code:`46369325`, :code:`"251380837"`.
manifest_type : str; keyword-only; default: :code:`"MPEG_DASH"`
Streaming protocol to use for the manifest.
**Valid values**:
* :code:`"HLS"` – HTTP Live Streaming.
* :code:`"MPEG_DASH"` – Dynamic Adaptive Streaming over
HTTP.
formats : str or Collection[str]; keyword-only; \
default: :code:`None`
Requested audio formats. If a collection, the highest
quality format available will be returned. If not specified,
all audio formats are requested.
**Valid values**: :code:`"HEAACV1"`, :code:`"AACLC"`,
:code:`"FLAC"`, :code:`"FLAC_HIRES"`, :code:`"EAC3_JOC"`.
uri_scheme : str; keyword-only; default: :code:`"HTTPS"`
Whether to return a remote URL or a Base64 manifest.
**Valid values**:
* :code:`"HTTPS"` – Remote URL for the manifest.
* :code:`"DATA"` – Base64 manifest.
intent : str; keyword-only; default: :code:`"PLAYBACK"`
Playback mode or intended use of the track.
**Valid values**:
* :code:`"DOWNLOAD"` – Offline download.
* :code:`"PLAYBACK"` – Streaming playback.
adaptive : bool; keyword-only; default: :code:`True`
Whether the manifest should support adaptive bitrate
switching based on network conditions.
share_code : str; keyword-only; optional
Share code that grants access to unlisted resources.
Returns
-------
media_info : dict[str, Any]
TIDAL media information for the track.
.. admonition:: Sample response
:class: response dropdown
.. code-block::
{
"data": {
"attributes": {
"albumAudioNormalizationData": {
"peakAmplitude": <float>,
"replayGain": <float>
},
"formats": <list[str]>,
"hash": <str>,
"previewReason": <str>,
"trackAudioNormalizationData": {
"peakAmplitude": <float>,
"replayGain": <float>
},
"trackPresentation": <str>,
"uri": <str>
},
"id": <str>,
"type": "trackManifests"
},
"links": {
"self": <str>
}
}
"""
params = {}
manifest_type = self._prepare_string(
"manifest_type", manifest_type
).upper()
if manifest_type not in self._MANIFEST_TYPES:
raise ValueError(
f"Invalid manifest type {manifest_type!r}. Valid "
f"values: {self._join_values(self._MANIFEST_TYPES)}."
)
params["manifestType"] = manifest_type
params["formats"] = (
sorted(self._FORMATS)
if formats is None
else self._prepare_formats(formats)
)
uri_scheme = self._prepare_string("uri_scheme", uri_scheme).upper()
if uri_scheme not in self._URI_SCHEMES:
raise ValueError(
f"Invalid URI scheme {uri_scheme!r}. Valid values: "
f"{self._join_values(self._URI_SCHEMES)}."
)
params["uriScheme"] = uri_scheme
intent = self._prepare_string("intent", intent).upper()
if intent not in self._USAGES:
raise ValueError(
f"Invalid intent {intent!r}. Valid values: "
f"{self._join_values(self._USAGES)}."
)
params["usage"] = intent
self._validate_type("adaptive", adaptive, bool)
params["adaptive"] = adaptive
return self._get_resources(
"trackManifests", track_id, share_code=share_code, params=params
)
[docs]
@TTLCache.cached_method(ttl="popularity")
def get_tracks(
self,
track_ids: int | str | Collection[int | str] | None = None,
/,
*,
isrcs: str | Collection[str] | None = None,
owner_ids: int | str | Collection[int | str] | None = None,
country_code: str | None = None,
expand: str | Collection[str] | None = None,
cursor: str | None = None,
sort_by: str | None = None,
share_code: str | None = None,
) -> dict[str, Any]:
"""
`Tracks > Get Single Track <https://tidal-music.github.io
/tidal-api-reference/#/tracks/get_tracks__id_>`_: Get TIDAL
catalog information for a track․
`Tracks > Get Multiple Tracks <https://tidal-music.github.io
/tidal-api-reference/#/tracks/get_tracks>`_: Get TIDAL catalog
information for multiple tracks.
.. admonition:: User authentication
:class: entitlement dropdown
.. tab-set::
.. tab-item:: Optional
User authentication
Access information for a resource's owners.
.. important::
Exactly one of `track_ids`, `isrcs`, or `owner_ids` must
be provided. When `isrcs` or `owner_ids` is specified, the
request will always be sent to the endpoint for multiple
tracks.
Parameters
----------
track_ids : int, str, or Collection[int | str]; \
positional-only; optional
TIDAL IDs of the tracks.
**Examples**: :code:`46369325`, :code:`"75413016"`,
:code:`[46369325, "75413016"]`.
isrcs : str or Collection[str]; keyword-only; optional
International Standard Recording Codes (ISRCs) of the
tracks.
**Examples**: :code:`"QMJMT1701237"`,
:code:`["QMJMT1701237", "USAT21404265"]`.
owner_ids : int, str, or Collection[int | str]; keyword-only; \
optional
TIDAL IDs of the track resources' owners.
**Examples**: :code:`123456`, :code:`"123456"`,
:code:`[123456, "654321"]`.
country_code : str; keyword-only; optional
ISO 3166-1 alpha-2 country code.
**Example**: :code:`"US"`.
expand : str or Collection[str]; keyword-only; optional
Related resources to include metadata for in the response.
**Valid values**: :code:`"albums"`, :code:`"artists"`,
:code:`"credits"`, :code:`"download"`, :code:`"genres"`,
:code:`"lyrics"`, :code:`"metadataStatus"`,
:code:`"owners"`, :code:`"priceConfig"`,
:code:`"providers"`, :code:`"radio"`, :code:`"replacement"`,
:code:`"shares"`, :code:`"similarTracks"`,
:code:`"sourceFile"`, :code:`"suggestedTracks"`,
:code:`"trackStatistics"`, :code:`"usageRules"`.
**Examples**: :code:`"lyrics"`,
:code:`["albums", "artists"]`.
cursor : str; keyword-only; optional
Cursor for fetching the next page of results when requesting
multiple tracks.
**Example**: :code:`"3nI1Esi"`.
sort_by : str; keyword-only; optional
Field to sort the tracks by.
**Valid values**: :code:`"createdAt"`, :code:`"title"`.
share_code : str; keyword-only; optional
Share code that grants access to unlisted resources.
Returns
-------
tracks : dict[str, Any]
Page of TIDAL metadata for the tracks.
.. admonition:: Sample responses
:class: response dropdown
.. tab-set::
.. tab-item:: Single track
.. code-block::
{
"data": {
"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": {
"data": [
{
"id": <str>,
"type": "albums"
}
],
"links": {
"self": <str>
}
},
"artists": {
"data": [
{
"id": <str>,
"type": "artists"
}
],
"links": {
"self": <str>
}
},
"genres": {
"data": [],
"links": {
"self": <str>
}
},
"lyrics": {
"data": [],
"links": {
"self": <str>
}
},
"owners": {
"data": [],
"links": {
"self": <str>
}
},
"providers": {
"data": [
{
"id": <str>,
"type": "providers"
}
],
"links": {
"self": <str>
}
},
"radio": {
"data": [
{
"id": <str>,
"type": "playlists"
}
],
"links": {
"self": <str>
}
},
"shares": {
"data": [],
"links": {
"self": <str>
}
},
"similarTracks": {
"data": [
{
"id": <str>,
"type": "tracks"
}
],
"links": {
"meta": {
"nextCursor": <str>
},
"next": <str>,
"self": <str>
}
},
"sourceFile": {
"links": {
"self": <str>
}
},
"trackStatistics": {
"links": {
"self": <str>
}
}
},
"type": "tracks"
},
"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>,
"externalLinks": [
{
"href": <str>,
"meta": {
"type": <str>
}
}
],
"lastModifiedAt": <str>,
"name": <str>,
"playlistType": "MIX"
},
"id": <str>,
"relationships": {
"coverArt": {
"links": {
"self": <str>
}
},
"items": {
"links": {
"self": <str>
}
},
"owners": {
"links": {
"self": <str>
}
}
},
"type": "playlists"
},
{
"attributes": {
"name": <str>
},
"id": <str>,
"type": "providers"
},
{
"attributes": {
"accessType": <str>,
"availability": <list[str]>,
"bpm": <float>,
"copyright": {
"text": <str>
},
"createdAt": <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>
}
}
.. tab-item:: Multiple tracks
.. code-block::
{
"data": [
{
"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": {
"data": [
{
"id": <str>,
"type": "albums"
}
],
"links": {
"self": <str>
}
},
"artists": {
"data": [
{
"id": <str>,
"type": "artists"
}
],
"links": {
"self": <str>
}
},
"genres": {
"data": [],
"links": {
"self": <str>
}
},
"lyrics": {
"data": [],
"links": {
"self": <str>
}
},
"owners": {
"data": [],
"links": {
"self": <str>
}
},
"providers": {
"data": [
{
"id": <str>,
"type": "providers"
}
],
"links": {
"self": <str>
}
},
"radio": {
"data": [
{
"id": <str>,
"type": "playlists"
}
],
"links": {
"self": <str>
}
},
"shares": {
"data": [],
"links": {
"self": <str>
}
},
"similarTracks": {
"data": [
{
"id": <str>,
"type": "tracks"
}
],
"links": {
"meta": {
"nextCursor": <str>
},
"next": <str>,
"self": <str>
}
},
"sourceFile": {
"links": {
"self": <str>
}
},
"trackStatistics": {
"links": {
"self": <str>
}
}
},
"type": "tracks"
}
],
"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>,
"externalLinks": [
{
"href": <str>,
"meta": {
"type": <str>
}
}
],
"lastModifiedAt": <str>,
"name": <str>,
"playlistType": "MIX"
},
"id": <str>,
"relationships": {
"coverArt": {
"links": {
"self": <str>
}
},
"items": {
"links": {
"self": <str>
}
},
"owners": {
"links": {
"self": <str>
}
}
},
"type": "playlists"
},
{
"attributes": {
"name": <str>
},
"id": <str>,
"type": "providers"
},
{
"attributes": {
"accessType": <str>,
"availability": <list[str]>,
"bpm": <float>,
"copyright": {
"text": <str>
},
"createdAt": <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>
}
}
"""
if sum(arg is not None for arg in (track_ids, isrcs, owner_ids)) != 1:
raise ValueError(
"Exactly one of `track_ids`, `isrcs`, or "
"`owner_ids` must be provided."
)
params = {}
if isrcs is not None:
if isinstance(isrcs, str):
isrcs = self._prepare_isrc(isrcs)
elif isinstance(isrcs, COLLECTION_TYPES):
isrcs = [self._prepare_isrc(isrc) for isrc in isrcs]
else:
raise ValueError(
"`isrcs` must be a string or a collection of strings."
)
params["filter[isrc]"] = isrcs
elif owner_ids is not None:
self._validate_tidal_ids(owner_ids)
params["filter[owners.id]"] = (
owner_ids
if isinstance(owner_ids, ORDERED_COLLECTION_TYPES)
else sorted(owner_ids)
)
if sort_by is not None:
self._process_sort(
sort_by,
prefix="",
sort_fields=self._SORT_FIELDS,
params=params,
)
return self._get_resources(
"tracks",
track_ids,
country_code=country_code,
expand=expand,
cursor=cursor,
share_code=share_code,
)
[docs]
@TTLCache.cached_method(ttl="popularity")
def get_track_albums(
self,
track_id: int | str,
/,
country_code: str | None = None,
*,
include_metadata: bool = False,
cursor: str | None = None,
share_code: str | None = None,
) -> dict[str, Any]:
"""
`Tracks > Get Albums Relationship <https://tidal-music.github.io
/tidal-api-reference/#/tracks
/get_tracks__id__relationships_albums>`_: Get TIDAL catalog
information for albums containing a track.
Parameters
----------
track_id : int or str; positional-only
TIDAL ID of the track.
**Examples**: :code:`46369325`, :code:`"75413016"`.
country_code : str; optional
ISO 3166-1 alpha-2 country code.
**Example**: :code:`"US"`.
include_metadata : bool; keyword-only; default: :code:`False`
Whether to include metadata for the albums containing the
track.
cursor : str; keyword-only; optional
Cursor for fetching the next page of results.
**Example**: :code:`"3nI1Esi"`.
share_code : str; keyword-only; optional
Share code that grants access to unlisted resources.
Returns
-------
albums : dict[str, Any]
Page of TIDAL metadata for the albums containing the track.
.. admonition:: Sample response
:class: response dropdown
.. code-block::
{
"data": [
{
"id": <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>
}
}
"""
return self._get_resource_relationship(
"tracks",
track_id,
"albums",
country_code=country_code,
include_metadata=include_metadata,
cursor=cursor,
share_code=share_code,
)
[docs]
@TTLCache.cached_method(ttl="popularity")
def get_track_artists(
self,
track_id: int | str,
/,
country_code: str | None = None,
*,
include_metadata: bool = False,
cursor: str | None = None,
share_code: str | None = None,
) -> dict[str, Any]:
"""
`Tracks > Get Artists Relationship
<https://tidal-music.github.io/tidal-api-reference/#/tracks
/get_tracks__id__relationships_artists>`_: Get TIDAL catalog
information for the artists of a track.
Parameters
----------
track_id : int or str; positional-only
TIDAL ID of the track.
**Examples**: :code:`46369325`, :code:`"75413016"`.
country_code : str; optional
ISO 3166-1 alpha-2 country code.
**Example**: :code:`"US"`.
include_metadata : bool; keyword-only; default: :code:`False`
Whether to include metadata for the track's artists.
cursor : str; keyword-only; optional
Cursor for fetching the next page of results.
**Example**: :code:`"3nI1Esi"`.
share_code : str; keyword-only; optional
Share code that grants access to unlisted resources.
Returns
-------
artists : dict[str, Any]
Page of TIDAL metadata for the track's artists.
.. admonition:: Sample response
:class: response dropdown
.. code-block::
{
"data": [
{
"id": <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>
}
}
"""
return self._get_resource_relationship(
"tracks",
track_id,
"artists",
country_code=country_code,
include_metadata=include_metadata,
cursor=cursor,
share_code=share_code,
)
[docs]
@TTLCache.cached_method(ttl="static")
def get_track_owners(
self,
track_id: int | str,
/,
*,
include_metadata: bool = False,
cursor: str | None = None,
share_code: str | None = None,
) -> dict[str, Any]:
"""
`Tracks > Get Owners Relationship <https://tidal-music.github.io
/tidal-api-reference/#/tracks
/get_tracks__id__relationships_owners>`_: Get TIDAL profile
information for the owners of a track resource.
Parameters
----------
track_id : int or str; positional-only
TIDAL ID of the track.
**Examples**: :code:`46369325`, :code:`"75413016"`.
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"`.
share_code : str; keyword-only; optional
Share code that grants access to unlisted resources.
Returns
-------
owners : dict[str, Any]
Page of TIDAL profile information for the track resource's
owners.
.. admonition:: Sample response
:class: response dropdown
.. code-block::
{
"data": [],
"included": [],
"links": {
"meta": {
"nextCursor": <str>
},
"next": <str>,
"self": <str>
}
}
"""
return self._get_resource_relationship(
"tracks",
track_id,
"owners",
include_metadata=include_metadata,
cursor=cursor,
share_code=share_code,
)
[docs]
@TTLCache.cached_method(ttl="static")
def get_track_providers(
self,
track_id: int | str,
/,
country_code: str | None = None,
*,
include_metadata: bool = False,
cursor: str | None = None,
share_code: str | None = None,
) -> dict[str, Any]:
"""
`Tracks > Get Providers Relationship
<https://tidal-music.github.io/tidal-api-reference/#/tracks
/get_tracks__id__relationships_providers>`_: Get TIDAL catalog
information for the providers of a track.
Parameters
----------
track_id : int or str; positional-only
TIDAL ID of the track.
**Examples**: :code:`46369325`, :code:`"75413016"`.
country_code : str; optional
ISO 3166-1 alpha-2 country code.
**Example**: :code:`"US"`.
include_metadata : bool; keyword-only; default: :code:`False`
Whether to include metadata for the track's providers.
cursor : str; keyword-only; optional
Cursor for fetching the next page of results.
**Example**: :code:`"3nI1Esi"`.
share_code : str; keyword-only; optional
Share code that grants access to unlisted resources.
Returns
-------
providers : dict[str, Any]
Page of TIDAL metadata for the track's providers.
.. admonition:: Sample response
:class: response dropdown
.. code-block::
{
"data": [
{
"id": <str>,
"type": "providers"
}
],
"included": [
{
"attributes": {
"name": <str>
},
"id": <str>,
"type": "providers"
}
],
"links": {
"meta": {
"nextCursor": <str>
},
"next": <str>,
"self": <str>
}
}
"""
return self._get_resource_relationship(
"tracks",
track_id,
"providers",
country_code=country_code,
include_metadata=include_metadata,
cursor=cursor,
share_code=share_code,
)
[docs]
@TTLCache.cached_method(ttl="daily")
def get_track_mix(
self,
track_id: int | str,
/,
*,
include_metadata: bool = False,
cursor: str | None = None,
share_code: str | None = None,
) -> dict[str, Any]:
"""
`Tracks > Get Radio Relationship <https://tidal-music.github.io
/tidal-api-reference/#/tracks
/get_tracks__id__relationships_radio>`_: Get TIDAL catalog
information for a track's mix.
Parameters
----------
track_id : int or str; positional-only
TIDAL ID of the track.
**Examples**: :code:`46369325`, :code:`"75413016"`.
include_metadata : bool; keyword-only; default: :code:`False`
Whether to include metadata for the track's mix.
cursor : str; keyword-only; optional
Cursor for fetching the next page of results.
**Example**: :code:`"3nI1Esi"`.
share_code : str; keyword-only; optional
Share code that grants access to unlisted resources.
Returns
-------
mix : dict[str, Any]
Page of TIDAL metadata for the track's mix.
.. admonition:: Sample response
:class: response dropdown
.. code-block::
{
"data": [
{
"id": <str>,
"type": "playlists"
}
],
"included": [
{
"attributes": {
"accessType": <str>,
"bounded": <bool>,
"createdAt": <str>,
"description": <str>,
"externalLinks": [
{
"href": <str>,
"meta": {
"type": <str>
}
}
],
"lastModifiedAt": <str>,
"name": <str>,
"playlistType": "MIX"
},
"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>
}
}
"""
return self._get_resource_relationship(
"tracks",
track_id,
"radio",
include_metadata=include_metadata,
cursor=cursor,
share_code=share_code,
)
[docs]
@TTLCache.cached_method(ttl="popularity")
def get_similar_tracks(
self,
track_id: int | str,
/,
country_code: str | None = None,
*,
include_metadata: bool = False,
cursor: str | None = None,
share_code: str | None = None,
) -> dict[str, Any]:
"""
`Tracks > Get Similar Tracks Relationship
<https://tidal-music.github.io/tidal-api-reference/#/tracks
/get_tracks__id__relationships_similarTracks>`_: Get TIDAL
catalog information for similar tracks.
Parameters
----------
track_id : int or str; positional-only
TIDAL ID of the track.
**Examples**: :code:`46369325`, :code:`"75413016"`.
country_code : str; optional
ISO 3166-1 alpha-2 country code.
**Example**: :code:`"US"`.
include_metadata : bool; keyword-only; default: :code:`False`
Whether to include metadata for the similar tracks.
cursor : str; keyword-only; optional
Cursor for fetching the next page of results.
**Example**: :code:`"3nI1Esi"`.
share_code : str; keyword-only; optional
Share code that grants access to unlisted resources.
Returns
-------
tracks : dict[str, Any]
Page of TIDAL metadata for the similar tracks.
.. admonition:: Sample response
:class: response dropdown
.. code-block::
{
"data": [
{
"id": <str>,
"type": "tracks"
}
],
"included": [
{
"attributes": {
"accessType": <str>,
"availability": <list[str]>,
"bpm": <float>,
"copyright": {
"text": <str>
},
"createdAt": <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>
}
}
"""
return self._get_resource_relationship(
"tracks",
track_id,
"similarTracks",
country_code=country_code,
include_metadata=include_metadata,
cursor=cursor,
share_code=share_code,
)
[docs]
@TTLCache.cached_method(ttl="static")
def get_track_source_file(
self,
track_id: int | str,
/,
*,
include_metadata: bool = False,
share_code: str | None = None,
) -> dict[str, Any]:
"""
`Tracks > Get Source File Relationship
<https://tidal-music.github.io/tidal-api-reference/#/tracks
/get_tracks__id__relationships_sourceFile>`_: Get TIDAL
catalog information for the source file for a track.
Parameters
----------
track_id : int or str; positional-only
TIDAL ID of the track.
**Examples**: :code:`46369325`, :code:`"75413016"`.
include_metadata : bool; keyword-only; default: :code:`False`
Whether to include metadata for the track's source file.
cursor : str; keyword-only; optional
Cursor for fetching the next page of results.
**Example**: :code:`"3nI1Esi"`.
share_code : str; keyword-only; optional
Share code that grants access to unlisted resources.
Returns
-------
source_file : dict[str, Any]
Page of TIDAL metadata for the track's source file.
.. admonition:: Sample response
:class: response dropdown
.. code-block::
{
"data": {
"id": <str>,
"type": "sourceFile"
},
"included": [],
"links": {
"meta": {
"nextCursor": <str>
},
"next": <str>,
"self": <str>
}
}
"""
return self._get_resource_relationship(
"tracks",
track_id,
"sourceFile",
include_metadata=include_metadata,
share_code=share_code,
)
[docs]
@TTLCache.cached_method(ttl="static")
def get_track_usage_rules(
self,
track_id: int | str,
/,
*,
include_metadata: bool = False,
share_code: str | None = None,
) -> dict[str, Any]:
"""
`Tracks > Get Usage Rules Relationship
<https://tidal-music.github.io/tidal-api-reference/#/tracks
/get_tracks__id__relationships_usageRules>`_: Get TIDAL catalog
information for the usage rules for a track.
Parameters
----------
track_id : int or str; positional-only
TIDAL ID of the track.
**Examples**: :code:`46369325`, :code:`"75413016"`.
include_metadata : bool; keyword-only; default: :code:`False`
Whether to include metadata for the track's usage rules.
share_code : str; keyword-only; optional
Share code that grants access to unlisted resources.
Returns
-------
usage_rules : dict[str, Any]
TIDAL metadata for the track's usage rules.
.. admonition:: Sample response
:class: response dropdown
.. code-block::
{
"data": {
"id": <str>,
"type": "usageRules"
},
"included": [
{
"attributes": {
"countryCode": <str>,
"free": <list[str]>,
"paid": <list[str]>,,
"subscription": <list[str]>,
},
"id": <str>,
"type": "usageRules"
}
],
"links": {
"meta": {
"nextCursor": <str>
},
"next": <str>,
"self": <str>
}
}
"""
return self._get_resource_relationship(
"tracks",
track_id,
"usageRules",
include_metadata=include_metadata,
share_code=share_code,
)
[docs]
@_copy_docstring(SearchAPI.search_tracks)
def search_tracks(
self,
query: str,
/,
country_code: str | None = None,
*,
include_explicit: bool | None = None,
include_metadata: bool = False,
cursor: str | None = None,
) -> dict[str, Any]:
return self._client.search.search_tracks(
query,
country_code=country_code,
include_explicit=include_explicit,
include_metadata=include_metadata,
cursor=cursor,
)
[docs]
@_copy_docstring(UsersAPI.get_user_saved_tracks)
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]:
return self._client.users.get_user_saved_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,
)
[docs]
@_copy_docstring(UsersAPI.save_tracks)
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:
self._client.users.save_tracks(
track_ids,
collection_id=collection_id,
user_id=user_id,
country_code=country_code,
)
[docs]
@_copy_docstring(UsersAPI.remove_saved_tracks)
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,
country_code: str | None = None,
) -> None:
self._client.users.remove_saved_tracks(
track_ids,
collection_id=collection_id,
user_id=user_id,
country_code=country_code,
)