Models and reports#

Typed data models for tidalpy.

Plain dataclasses with to_dict() for JSON serialisation. The catalog-facing dicts intentionally mirror the camelCase key names of the Tidal v1 JSON API (title, numberOfTracks, releaseDate, …) because downstream consumers — in particular tidal_transfer’s TidalTask.info JSONField and its admin UI — were written against that shape. Resource.to_dict() emits {"type": ..., "data": ...} exactly like the legacy CLI’s --json mode.

class tidalpy.models.Album(id: 'int | str | None' = None, title: 'str | None' = None, version: 'str | None' = None, release_date: 'str | None' = None, copyright_label: 'str | None' = None, number_of_tracks: 'int' = 0, number_of_volumes: 'int' = 0, album_type: 'str | None' = None, explicit: 'bool' = False, audio_quality: 'str | None' = None, artwork_id: 'str | None' = None, artists: 'list[Artist]' = <factory>, popularity: 'float' = 0.0, upc: 'str | None' = None, vibrant_color: 'str | None' = None, duration: 'int' = 0, pay_to_stream: 'bool' = False, stream_start_date: 'str | None' = None)[source]#

Bases: object

Parameters:
  • id (int | str | None)

  • title (str | None)

  • version (str | None)

  • release_date (str | None)

  • copyright_label (str | None)

  • number_of_tracks (int)

  • number_of_volumes (int)

  • album_type (str | None)

  • explicit (bool)

  • audio_quality (str | None)

  • artwork_id (str | None)

  • artists (list[Artist])

  • popularity (float)

  • upc (str | None)

  • vibrant_color (str | None)

  • duration (int)

  • pay_to_stream (bool)

  • stream_start_date (str | None)

__init__(id=None, title=None, version=None, release_date=None, copyright_label=None, number_of_tracks=0, number_of_volumes=0, album_type=None, explicit=False, audio_quality=None, artwork_id=None, artists=<factory>, popularity=0.0, upc=None, vibrant_color=None, duration=0, pay_to_stream=False, stream_start_date=None)#
Parameters:
  • id (int | str | None)

  • title (str | None)

  • version (str | None)

  • release_date (str | None)

  • copyright_label (str | None)

  • number_of_tracks (int)

  • number_of_volumes (int)

  • album_type (str | None)

  • explicit (bool)

  • audio_quality (str | None)

  • artwork_id (str | None)

  • artists (list[Artist])

  • popularity (float)

  • upc (str | None)

  • vibrant_color (str | None)

  • duration (int)

  • pay_to_stream (bool)

  • stream_start_date (str | None)

Return type:

None

to_dict()[source]#
Return type:

dict[str, Any]

album_type: str | None = None#
artists: list[Artist]#
artwork_id: str | None = None#
audio_quality: str | None = None#
copyright_label: str | None = None#
duration: int = 0#
explicit: bool = False#
id: int | str | None = None#

https://tidal.com/album/<id> (None without an id).

number_of_tracks: int = 0#
number_of_volumes: int = 0#
pay_to_stream: bool = False#
popularity: float = 0.0#
property primary_artist: Artist | None#
release_date: str | None = None#
stream_start_date: str | None = None#
title: str | None = None#
upc: str | None = None#
version: str | None = None#
vibrant_color: str | None = None#
property year: int | None#

The release year (release_date’s leading four digits) — the card’s year label, None when the catalog has none.

class tidalpy.models.Artist(id: 'int | str | None' = None, name: 'str | None' = None, picture: 'str | None' = None, popularity: 'float' = 0.0, handle: 'str | None' = None, follower_count: 'int' = 0, mixes: 'dict[str, str]' = <factory>)[source]#

Bases: object

Parameters:
__init__(id=None, name=None, picture=None, popularity=0.0, handle=None, follower_count=0, mixes=<factory>)#
Parameters:
Return type:

None

to_dict()[source]#
Return type:

dict[str, Any]

follower_count: int = 0#
handle: str | None = None#
id: int | str | None = None#

https://tidal.com/artist/<id> (None without an id).

The id form, not the handle form: tidalpy.api.parse_tidal_url() validates artist ids as numeric, so this is the form that round-trips through the library.

mixes: dict[str, str]#
name: str | None = None#
picture: str | None = None#
popularity: float = 0.0#
class tidalpy.models.DownloadReport(kind, title=None, requested_quality='', items=<factory>, cancelled=False)[source]#

Bases: object

Structured outcome of a whole download run.

Parameters:
__init__(kind, title=None, requested_quality='', items=<factory>, cancelled=False)#
Parameters:
Return type:

None

summary()[source]#

A compact, JSON-safe status line for the whole run.

Hosts that display run state (a Django admin column, a batch UI, a log line) get the counts and the failed items’ identity without walking items; use to_dict() for the full per-item detail. failures rows are compact: id, title, the machine code, and the human reason.

Keys (camelCase, as in to_dict()): kind, title, requestedQuality, ok, cancelled, total, okCount, skippedCount, failedCount, totalBytes, failures.

Return type:

dict[str, Any]

to_dict()[source]#

JSON-safe dict of the whole run. Top-level keys are camelCase (same convention as the catalog metadata() output); the items rows are snake_case (the legacy diagnose row shape).

Return type:

dict[str, Any]

cancelled: bool = False#
property failures: list[ItemReport]#
items: list[ItemReport]#
kind: str#
property ok: bool#
requested_quality: str = ''#
property skipped_existing: int#
title: str | None = None#
property total_bytes: int#
class tidalpy.models.ItemReport(index, kind, id=None, title=None, status='pending', reason='', path=None, delivered_quality=None, codec=None, code=None, retryable=False, size=0)[source]#

Bases: object

Outcome of one item (track) inside a download run.

Parameters:
  • index (int)

  • kind (str)

  • id (int | str | None)

  • title (str | None)

  • status (str)

  • reason (str)

  • path (str | None)

  • delivered_quality (str | None)

  • codec (str | None)

  • code (str | None)

  • retryable (bool)

  • size (int)

__init__(index, kind, id=None, title=None, status='pending', reason='', path=None, delivered_quality=None, codec=None, code=None, retryable=False, size=0)#
Parameters:
  • index (int)

  • kind (str)

  • id (int | str | None)

  • title (str | None)

  • status (str)

  • reason (str)

  • path (str | None)

  • delivered_quality (str | None)

  • codec (str | None)

  • code (str | None)

  • retryable (bool)

  • size (int)

Return type:

None

to_dict()[source]#

JSON-safe row for this item (snake_case keys, matching the legacy client’s per-item diagnose rows). All fields are present; unset optional fields are null.

Return type:

dict[str, Any]

code: str | None = None#
codec: str | None = None#
delivered_quality: str | None = None#
id: int | str | None = None#
index: int#
kind: str#

The Tidal URL for this item (None without an id).

The same canonical, id-form, round-trip-stable shape as the catalog models’ link — a host rendering a failed-item row can offer a “view on Tidal” link without re-deriving https://tidal.com/<kind>/<id>.

path: str | None = None#
reason: str = ''#
retryable: bool = False#
size: int = 0#
status: str = 'pending'#
title: str | None = None#
class tidalpy.models.Playlist(id=None, title=None, description=None, number_of_tracks=0, explicit=False, artwork_id=None, is_mix=False, creator=None, creation_time=None, last_modified=None, duration=0, playlist_type=None, follower_count=0, popularity=0.0, is_public=None, promoted_artists=<factory>, last_item_added=None, items=<factory>)[source]#

Bases: object

A playlist or mix (is_mix distinguishes the two).

Parameters:
  • id (int | str | None)

  • title (str | None)

  • description (str | None)

  • number_of_tracks (int)

  • explicit (bool)

  • artwork_id (str | None)

  • is_mix (bool)

  • creator (str | None)

  • creation_time (str | None)

  • last_modified (str | None)

  • duration (int)

  • playlist_type (str | None)

  • follower_count (int)

  • popularity (float)

  • is_public (bool | None)

  • promoted_artists (list[Artist])

  • last_item_added (str | None)

  • items (list[Track | None])

__init__(id=None, title=None, description=None, number_of_tracks=0, explicit=False, artwork_id=None, is_mix=False, creator=None, creation_time=None, last_modified=None, duration=0, playlist_type=None, follower_count=0, popularity=0.0, is_public=None, promoted_artists=<factory>, last_item_added=None, items=<factory>)#
Parameters:
  • id (int | str | None)

  • title (str | None)

  • description (str | None)

  • number_of_tracks (int)

  • explicit (bool)

  • artwork_id (str | None)

  • is_mix (bool)

  • creator (str | None)

  • creation_time (str | None)

  • last_modified (str | None)

  • duration (int)

  • playlist_type (str | None)

  • follower_count (int)

  • popularity (float)

  • is_public (bool | None)

  • promoted_artists (list[Artist])

  • last_item_added (str | None)

  • items (list[Track | None])

Return type:

None

to_dict()[source]#
Return type:

dict[str, Any]

artwork_id: str | None = None#
creation_time: str | None = None#
creator: str | None = None#
description: str | None = None#
duration: int = 0#
explicit: bool = False#
follower_count: int = 0#
id: int | str | None = None#
is_mix: bool = False#
is_public: bool | None = None#
items: list[Track | None]#
property kind: str#
last_item_added: str | None = None#
last_modified: str | None = None#

The linkable URL (None without an id).

Mixes use the player host (https://play.tidal.com/mc/<id>), everything else https://tidal.com/playlist/<id> (the id is the uuid for user playlists, the numeric id for editorial ones). Both forms round-trip through tidalpy.api.parse_tidal_url().

number_of_tracks: int = 0#
playlist_type: str | None = None#
popularity: float = 0.0#
promoted_artists: list[Artist]#
title: str | None = None#
class tidalpy.models.Resource(kind, data)[source]#

Bases: object

A resolved catalog entry: album / track / playlist / mix / artist.

Parameters:
__init__(kind, data)#
Parameters:
Return type:

None

to_dict()[source]#

{"type": ..., "data": {...}} — the legacy --json shape.

type is the reference client’s Type enum name (capitalised: "Track", "Album", "Playlist", "Mix", "Artist") — the same value its CLI --json, wrapper json-mode, and RPC metadata all emit, which the host contract reads. (The reports use the lowercase kind instead — different convention, different consumer, see DownloadReport.)

Return type:

dict[str, Any]

data: Album | Track | Playlist | Artist#
kind: str#

The resolved entry’s linkable URL (delegates to the model).

property title: str | None#
property track_count: int | None#
class tidalpy.models.SessionInfo(user_id=None, country_code=None, access_token='', refresh_token=None, expires_in=None, client_id=None, token_type='device', subscription=None)[source]#

Bases: object

A live login session (device OAuth or pasted web token).

Parameters:
  • user_id (int | str | None)

  • country_code (str | None)

  • access_token (str)

  • refresh_token (str | None)

  • expires_in (int | None)

  • client_id (str | None)

  • token_type (str)

  • subscription (dict[str, Any] | None)

__init__(user_id=None, country_code=None, access_token='', refresh_token=None, expires_in=None, client_id=None, token_type='device', subscription=None)#
Parameters:
  • user_id (int | str | None)

  • country_code (str | None)

  • access_token (str)

  • refresh_token (str | None)

  • expires_in (int | None)

  • client_id (str | None)

  • token_type (str)

  • subscription (dict[str, Any] | None)

Return type:

None

to_dict()[source]#
Return type:

dict[str, Any]

access_token: str = ''#
client_id: str | None = None#
country_code: str | None = None#
expires_in: int | None = None#
refresh_token: str | None = None#
subscription: dict[str, Any] | None = None#
token_type: str = 'device'#
user_id: int | str | None = None#
class tidalpy.models.StreamInfo(track_id, urls, codec, delivered_quality, requested_quality, manifest_mime, encryption_key=None, bandwidth=None, sample_rate=None, bit_depth=None, channels=None, representation_id=None, manifest_hash=None, replaygain=<factory>, preview=False)[source]#

Bases: object

A resolved stream: where to fetch the media bytes from and what it is.

urls has one entry for bento (single file) streams and several for DASH (initialization + segments, concatenated in order). encryption_key is the base64 security token from a bento manifest (keyId); None means the bytes are plain.

Parameters:
  • track_id (int | str)

  • urls (list[str])

  • codec (str)

  • delivered_quality (str)

  • requested_quality (str)

  • manifest_mime (str)

  • encryption_key (str | None)

  • bandwidth (int | None)

  • sample_rate (int | None)

  • bit_depth (int | None)

  • channels (int | None)

  • representation_id (str | None)

  • manifest_hash (str | None)

  • replaygain (dict[str, str])

  • preview (bool)

__init__(track_id, urls, codec, delivered_quality, requested_quality, manifest_mime, encryption_key=None, bandwidth=None, sample_rate=None, bit_depth=None, channels=None, representation_id=None, manifest_hash=None, replaygain=<factory>, preview=False)#
Parameters:
  • track_id (int | str)

  • urls (list[str])

  • codec (str)

  • delivered_quality (str)

  • requested_quality (str)

  • manifest_mime (str)

  • encryption_key (str | None)

  • bandwidth (int | None)

  • sample_rate (int | None)

  • bit_depth (int | None)

  • channels (int | None)

  • representation_id (str | None)

  • manifest_hash (str | None)

  • replaygain (dict[str, str])

  • preview (bool)

Return type:

None

to_dict()[source]#
Return type:

dict[str, Any]

bandwidth: int | None = None#
bit_depth: int | None = None#
channels: int | None = None#
codec: str#
delivered_quality: str#
encryption_key: str | None = None#
manifest_hash: str | None = None#
manifest_mime: str#
property multi_segment: bool#
preview: bool = False#
replaygain: dict[str, str]#
representation_id: str | None = None#
requested_quality: str#
sample_rate: int | None = None#
track_id: int | str#
property url: str#
urls: list[str]#
class tidalpy.models.Track(id: 'int | str | None' = None, title: 'str | None' = None, version: 'str | None' = None, duration: 'int' = 0, track_number: 'int' = 0, volume_number: 'int' = 0, position: 'int' = 0, explicit: 'bool' = False, isrc: 'str | None' = None, copyright_text: 'str | None' = None, audio_quality: 'str | None' = None, media_tags: 'list[str]' = <factory>, artists: 'list[Artist]' = <factory>, album: 'Album | None' = None, replaygain: 'dict[str, str]' = <factory>, bpm: 'int | float | None' = None, key: 'str | None' = None, key_scale: 'str | None' = None, pay_to_stream: 'bool' = False, audio_modes: 'tuple[str, ...]' = (), stream_start_date: 'str | None' = None, mixes: 'dict[str, str]' = <factory>)[source]#

Bases: object

Parameters:
__init__(id=None, title=None, version=None, duration=0, track_number=0, volume_number=0, position=0, explicit=False, isrc=None, copyright_text=None, audio_quality=None, media_tags=<factory>, artists=<factory>, album=None, replaygain=<factory>, bpm=None, key=None, key_scale=None, pay_to_stream=False, audio_modes=(), stream_start_date=None, mixes=<factory>)#
Parameters:
Return type:

None

to_dict()[source]#
Return type:

dict[str, Any]

album: Album | None = None#
property artist_name: str#
artists: list[Artist]#
audio_modes: tuple[str, ...] = ()#
audio_quality: str | None = None#
bpm: int | float | None = None#
copyright_text: str | None = None#
duration: int = 0#
property duration_label: str#

The track length in M:SS display form ("3:38") — the duration column of a player/search UI. "" when the catalog has no duration (search rows); the rare hour-plus track formats as H:MM:SS.

explicit: bool = False#
id: int | str | None = None#
isrc: str | None = None#
key: str | None = None#
property key_label: str#

the pitch class in display form (Sharp → #, Flat → b) with the "m" scale suffix when minor; "" when the catalog has no key facts (search rows).

Type:

A short pitch label ("F#m") for players/DJ tools

key_scale: str | None = None#

https://tidal.com/track/<id> (None without an id).

media_tags: list[str]#
mixes: dict[str, str]#
property number_label: str#

the volume is never part of the filename — multi-disc albums use CD{n} subfolders, see tidalpy.naming.cd_folder()).

The reference client numbers a track by its position in the playlist when the track is playlist-bound, and by its album track number otherwise — hence position takes precedence here (it is only set for playlist items).

Type:

Zero-padded track number (TMD parity

pay_to_stream: bool = False#
position: int = 0#
property primary_artist: Artist | None#
replaygain: dict[str, str]#
stream_start_date: str | None = None#
title: str | None = None#
track_number: int = 0#
version: str | None = None#
volume_number: int = 0#