Source code for minim.api.tidal._api.videos
from __future__ import annotations
from typing import TYPE_CHECKING
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 VideosAPI(TIDALResourceAPI):
"""
Videos API endpoints for the TIDAL API.
.. important::
This class is managed by :class:`~minim.api.tidal.TIDALAPIClient`
and should not be instantiated directly.
"""
_RELATIONSHIPS = {
"albums",
"artists",
"credits",
"providers",
"replacement",
"similarVideos",
"suggestedVideos",
"thumbnailArt",
"usageRules",
}
__slots__ = ()
[docs]
@TTLCache.cached_method(ttl="popularity")
def get_videos(
self,
video_ids: int | str | Collection[int | str] | None = None,
/,
*,
isrcs: str | Collection[str] | None = None,
country_code: str | None = None,
expand: str | Collection[str] | None = None,
) -> dict[str, Any]:
"""
`Videos > Get Single Video <https://tidal-music.github.io
/tidal-api-reference/#/videos/get_videos__id_>`_: Get TIDAL
catalog information for a video․
`Videos > Get Multiple Videos <https://tidal-music.github.io
/tidal-api-reference/#/videos/get_videos__id_>`_: Get TIDAL
catalog information for multiple videos.
.. admonition:: User authentication
:class: entitlement dropdown
.. tab-set::
.. tab-item:: Optional
User authentication
Access information for a resource's owners.
.. important::
Exactly one of `video_ids` or `isrcs` must be provided. If
`video_ids` is specified, the request will always be sent to
the endpoint for multiple videos.
Parameters
----------
video_ids : int, str, or Collection[int | str]; \
positional-only; optional
TIDAL IDs of the videos.
**Examples**: :code:`53315642`, :code:`"75623239"`,
:code:`[53315642, "75623239"]`.
isrcs : str or Collection[str]; keyword-only; optional
International Standard Recording Codes (ISRCs) of the
videos.
**Examples**: :code:`"QMJMT1701237"`,
:code:`[QMJMT1701237, "USAT21404265"]`.
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:`"providers"`,
:code:`"replacement"`, :code:`"similarVideos"`,
:code:`"suggestedVideos"`, :code:`"thumbnailArt"`,
:code:`"usageRules"`.
**Examples**: :code:`"thumbnailArt"`,
:code:`["albums", "artists"]`.
Returns
-------
videos : dict[str, Any]
TIDAL metadata for the videos.
.. admonition:: Sample responses
:class: response dropdown
.. tab-set::
.. tab-item:: Single video
.. code-block::
{
"data": {
"attributes": {
"availability": <list[str]>,
"copyright": {
"text": <str>
},
"duration": <str>,
"explicit": <bool>,
"externalLinks": [
{
"href": <str>,
"meta": {
"type": <str>
}
}
],
"isrc": <str>,
"popularity": <float>,
"releaseDate": <str>,
"title": <str>
},
"id": <str>,
"relationships": {
"albums": {
"data": [
{
"id": <str>,
"type": "albums"
}
],
"links": {
"self": <str>
}
},
"artists": {
"data": [
{
"id": <str>,
"type": "artists"
}
],
"links": {
"self": <str>
}
},
"providers": {
"data": [
{
"id": <str>,
"type": "providers"
}
],
"links": {
"self": <str>
}
},
"thumbnailArt": {
"data": [
{
"id": <str>,
"type": "artworks"
}
],
"links": {
"self": <str>
}
}
},
"type": "videos"
},
"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": {
"files": [
{
"href": <str>,
"meta": {
"height": <int>,
"width": <int>
}
}
],
"mediaType": "IMAGE"
},
"id": <str>,
"relationships": {
"owners": {
"links": {
"self": <str>
}
}
},
"type": "artworks"
},
{
"attributes": {
"name": <str>
},
"id": <str>,
"type": "providers"
}
],
"links": {
"self": <str>
}
}
.. tab-item:: Multiple videos
.. code-block::
{
"data": [
{
"attributes": {
"availability": <list[str]>,
"copyright": {
"text": <str>
},
"duration": <str>,
"explicit": <bool>,
"externalLinks": [
{
"href": <str>,
"meta": {
"type": <str>
}
}
],
"isrc": <str>,
"popularity": <float>,
"releaseDate": <str>,
"title": <str>
},
"id": <str>,
"relationships": {
"albums": {
"data": [
{
"id": <str>,
"type": "albums"
}
],
"links": {
"self": <str>
}
},
"artists": {
"data": [
{
"id": <str>,
"type": "artists"
}
],
"links": {
"self": <str>
}
},
"providers": {
"data": [
{
"id": <str>,
"type": "providers"
}
],
"links": {
"self": <str>
}
},
"thumbnailArt": {
"data": [
{
"id": <str>,
"type": "artworks"
}
],
"links": {
"self": <str>
}
}
},
"type": "videos"
}
],
"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": {
"files": [
{
"href": <str>,
"meta": {
"height": <int>,
"width": <int>
}
}
],
"mediaType": "IMAGE"
},
"id": <str>,
"relationships": {
"owners": {
"links": {
"self": <str>
}
}
},
"type": "artworks"
},
{
"attributes": {
"name": <str>
},
"id": <str>,
"type": "providers"
}
],
"links": {
"self": <str>
}
}
"""
if sum(arg is not None for arg in [video_ids, isrcs]) != 1:
raise ValueError(
"Exactly one of `video_ids` or `isrcs` must be provided."
)
params = {}
if isrcs is not None:
if isinstance(isrcs, str):
isrcs = self._prepare_isrc(isrcs)
elif isinstance(isrcs, list | tuple):
isrcs = [self._prepare_isrc(isrc) for isrc in isrcs]
else:
raise ValueError(
"`isrcs` must be a string or a list of strings."
)
params["filter[isrc]"] = isrcs
return self._get_resources(
"videos",
video_ids,
country_code=country_code,
expand=expand,
)
[docs]
@TTLCache.cached_method(ttl="popularity")
def get_video_albums(
self,
video_id: int | str,
/,
country_code: str | None = None,
*,
include_metadata: bool = False,
cursor: str | None = None,
) -> dict[str, Any]:
"""
`Videos > Get Albums Relationship
<https://tidal-music.github.io/tidal-api-reference/#/videos
/get_videos__id__relationships_albums>`_: Get TIDAL catalog
information for albums containing a video.
Parameters
----------
video_id : int or str; positional-only
TIDAL ID of the video.
**Examples**: :code:`53315642`, :code:`"75623239"`.
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
video.
cursor : str; keyword-only; optional
Cursor for fetching the next page of results.
**Example**: :code:`"3nI1Esi"`.
Returns
-------
albums : dict[str, Any]
Page of TIDAL metadata for the albums containing the videos.
.. 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(
"videos",
video_id,
"albums",
country_code=country_code,
include_metadata=include_metadata,
cursor=cursor,
)
[docs]
@TTLCache.cached_method(ttl="popularity")
def get_video_artists(
self,
video_id: int | str,
/,
country_code: str | None = None,
*,
include_metadata: bool = False,
cursor: str | None = None,
) -> dict[str, Any]:
"""
`Videos > Get Artists Relationship
<https://tidal-music.github.io/tidal-api-reference/#/videos
/get_videos__id__relationships_artists>`_: Get TIDAL catalog
information for the artists of a video.
Parameters
----------
video_id : int or str; positional-only
TIDAL ID of the video.
**Examples**: :code:`53315642`, :code:`"75623239"`.
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 video's artists.
cursor : str; keyword-only; optional
Cursor for fetching the next page of results.
**Example**: :code:`"3nI1Esi"`.
Returns
-------
artists : dict[str, Any]
Page of TIDAL metadata for the video'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(
"videos",
video_id,
"artists",
country_code=country_code,
include_metadata=include_metadata,
cursor=cursor,
)
[docs]
@TTLCache.cached_method(ttl="static")
def get_video_providers(
self,
video_id: int | str,
/,
country_code: str | None = None,
*,
include_metadata: bool = False,
cursor: str | None = None,
) -> dict[str, Any]:
"""
`Videos > Get Providers Relationship
<https://tidal-music.github.io/tidal-api-reference/#/videos
/get_videos__id__relationships_providers>`_: Get TIDAL catalog
information for the providers of a video.
Parameters
----------
video_id : int or str; positional-only
TIDAL ID of the video.
**Examples**: :code:`53315642`, :code:`"75623239"`.
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 video's providers.
cursor : str; keyword-only; optional
Cursor for fetching the next page of results.
**Example**: :code:`"3nI1Esi"`.
Returns
-------
providers : dict[str, Any]
Page of TIDAL metadata for the video'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(
"videos",
video_id,
"providers",
country_code=country_code,
include_metadata=include_metadata,
cursor=cursor,
)
[docs]
@TTLCache.cached_method(ttl="static")
def get_video_thumbnail(
self,
video_id: int | str,
/,
country_code: str | None = None,
*,
include_metadata: bool = False,
cursor: str | None = None,
) -> dict[str, Any]:
"""
`Videos > Get Thumbnail Relationship
<https://tidal-music.github.io/tidal-api-reference/#/videos
/get_videos__id__relationships_thumbnailArt>`_: Get TIDAL
catalog information for the thumbnail for a video.
Parameters
----------
video_id : int or str; positional-only
TIDAL ID of the video.
**Examples**: :code:`53315642`, :code:`"75623239"`.
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 video's thumbnail.
cursor : str; keyword-only; optional
Cursor for fetching the next page of results.
**Example**: :code:`"3nI1Esi"`.
Returns
-------
thumbnail : dict[str, Any]
Page of TIDAL metadata for the video's thumbnail.
.. admonition:: Sample response
:class: response dropdown
.. code-block::
{
"data": [
{
"id": <str>,
"type": "artworks"
}
],
"included": [
{
"attributes": {
"files": [
{
"href": <str>,
"meta": {
"height": <int>,
"width": <int>
}
}
],
"mediaType": "IMAGE"
},
"id": <str>,
"relationships": {
"owners": {
"links": {
"self": <str>
}
}
},
"type": "artworks"
}
],
"links": {
"meta": {
"nextCursor": <str>
},
"next": <str>,
"self": <str>
}
}
"""
return self._get_resource_relationship(
"videos",
video_id,
"thumbnailArt",
country_code=country_code,
include_metadata=include_metadata,
cursor=cursor,
)
[docs]
@TTLCache.cached_method(ttl="static")
def get_video_usage_rules(
self,
video_id: int | str,
/,
*,
include_metadata: bool = False,
share_code: str | None = None,
) -> dict[str, Any]:
"""
`Videos > Get Usage Rules Relationship
<https://tidal-music.github.io/tidal-api-reference/#/videos
/get_videos__id__relationships_usageRules>`_: Get TIDAL catalog
information for the usage rules for a video.
Parameters
----------
video_id : int or str; positional-only
TIDAL ID of the video.
**Examples**: :code:`53315642`, :code:`"75623239"`.
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 video'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(
"videos",
video_id,
"usageRules",
include_metadata=include_metadata,
share_code=share_code,
)
[docs]
@_copy_docstring(SearchAPI.search_videos)
def search_videos(
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_videos(
query,
country_code=country_code,
include_explicit=include_explicit,
include_metadata=include_metadata,
cursor=cursor,
)
[docs]
@_copy_docstring(UsersAPI.get_user_saved_videos)
def get_user_saved_videos(
self,
*,
collection_id: str | None = None,
user_id: int | str | None = None,
country_code: str | None = None,
locale: str | None = None,
include_metadata: bool = False,
cursor: str | None = None,
sort_by: str | None = None,
descending: bool | None = None,
) -> dict[str, Any]:
return self._client.users.get_user_saved_videos(
collection_id=collection_id,
user_id=user_id,
country_code=country_code,
locale=locale,
include_metadata=include_metadata,
cursor=cursor,
sort_by=sort_by,
descending=descending,
)
[docs]
@_copy_docstring(UsersAPI.save_videos)
def save_videos(
self,
video_ids: str
| dict[str, int | str]
| Collection[int | str | dict[str, int | str]],
/,
*,
collection_id: str | None = None,
user_id: int | str | None = None,
country_code: str | None = None,
) -> None:
self._client.users.save_videos(
video_ids,
collection_id=collection_id,
user_id=user_id,
country_code=country_code,
)
[docs]
@_copy_docstring(UsersAPI.remove_saved_videos)
def remove_saved_videos(
self,
video_ids: str
| dict[str, int | str]
| Collection[int | str | dict[str, int | str]],
/,
*,
collection_id: str | None = None,
user_id: int | str | None = None,
country_code: str | None = None,
) -> None:
self._client.users.remove_saved_videos(
video_ids,
collection_id=collection_id,
user_id=user_id,
country_code=country_code,
)