Quality profiles#

Quality profiles, the playback downgrade ladder, and codec helpers.

Tidal’s playback API accepts a small set of API qualities (LOW/HIGH/LOSSLESS/HI_RES_LOSSLESS/HI_RES). Hosts think in profiles (what the user asked for). This module maps between the two and decides, in order, which API qualities to attempt when the requested one cannot be delivered.

The ladder encodes real Tidal behaviour (subscribed tier, per-track availability, spatial-audio quirks) and is the single place it lives.

class tidalpy.quality.Quality(*values)[source]#

Bases: str, Enum

Host-facing quality profiles, ordered from lossy to highest lossless.

HIGH = 'HIGH'#
HI_RES_LOSSLESS = 'HI_RES_LOSSLESS'#
LOSSLESS = 'LOSSLESS'#
LOW = 'LOW'#
tidalpy.quality.is_lossless_codec(codec)[source]#
Parameters:

codec (str | None)

Return type:

bool

tidalpy.quality.is_spatial_codec(codec)[source]#
Parameters:

codec (str | None)

Return type:

bool

tidalpy.quality.needs_stereo_fallback(requested_api_quality, codec)[source]#

A lossless request that came back spatial/proprietary/lossy.

Tidal sometimes answers a lossless request with a spatial or proprietary (MQA/E-AC3) rendition, which is useless for the DJ/CDJ use case; the caller should retry at LOSSLESS.

Parameters:
  • requested_api_quality (str)

  • codec (str | None)

Return type:

bool

tidalpy.quality.normalize_codec(codec)[source]#

Canonical short codec name (AAC/FLAC/ALAC/…).

Parameters:

codec (str | None)

Return type:

str

tidalpy.quality.parse_iso8601_duration(value)[source]#

PT4M48S -> whole seconds (0 for missing/invalid).

Accepts a plain seconds value too (int/float, or a bare number string): the API paths disagree on the shape (v2 sends ISO-8601, v1 and search rows send plain seconds), and a bare number must not parse as 0 the way the unit regex would.

Parameters:

value (str | int | float | None)

Return type:

int

tidalpy.quality.parse_quality(value, default=Quality.LOSSLESS)[source]#

Parse a human quality label into a Quality profile.

Accepts enum members, API quality names, and common aliases ("Master", "hifi", "24/192", …). Falls back to default for empty/unknown input.

Parameters:
Return type:

Quality

tidalpy.quality.playback_attempts(profile, media_tags=None, catalog_quality=None)[source]#

Ordered API qualities to attempt for a playback request.

Parameters:
Return type:

list[str]

tidalpy.quality.quality_label(quality)[source]#

A short human label for an API quality ("HI_RES_LOSSLESS" -> "Max"). Unknown non-empty values pass through unchanged; empty/None give "" — never "Unknown" in a file name.

Parameters:

quality (str | None)

Return type:

str

tidalpy.quality.quality_meets_profile(profile, delivered_quality, codec)[source]#

Whether a delivered stream satisfies the host’s profile.

Lossy profiles are always satisfied (Tidal may deliver HIGH for a LOW request, which is fine). Lossless profiles require an actually lossless delivery (API quality or codec).

Parameters:
  • profile (Quality)

  • delivered_quality (str | None)

  • codec (str | None)

Return type:

bool

tidalpy.quality.representation_key(rep)[source]#

Fidelity ranking for DASH representations.

Higher is better: lossless first, then bit depth, sample rate, bandwidth, channels. Selection uses this key instead of trusting the XML order Tidal emits.

Parameters:

rep (dict)

Return type:

tuple

tidalpy.quality.resolve_api_quality(profile, media_tags=None, catalog_quality=None)[source]#

The primary API quality to request for a profile and a track.

catalog_quality is the track/album’s advertised audioQuality (e.g. HI_RES_LOSSLESS); media_tags are its mediaMetadata.tags. When the catalog already says the track is not hi-res, requesting HI_RES_LOSSLESS just burns a 404, so the primary tier is lowered before any network call.

When the catalog is unknown (playlist tracks are not album-bound), the primary tier stays HI_RES_LOSSLESS — deliberately optimistic where the reference client falls back to LOSSLESS: hi-res playlist tracks would be silently downgraded otherwise, and the v2-first FLAC manifest keeps the cost of a miss to one extra request.

Parameters:
Return type:

str

tidalpy.quality.select_representation(representations, requested_api_quality)[source]#

Pick the best representation for a requested API quality.

  • non-audio / spatial representations are filtered out

  • lossless requests get the best lossless representation (for HI_RES_LOSSLESS the highest-rate FLAC specifically)

  • everything else falls back to the best AAC, then the best overall

Parameters:
  • representations (list[dict])

  • requested_api_quality (str)

Return type:

dict

tidalpy.quality.stereo_media_tags(tags)[source]#

Media tags with spatial-audio tags removed (stereo delivery only).

Parameters:

tags (list[str] | tuple[str, ...] | None)

Return type:

list[str]

tidalpy.quality.QUALITY_LABELS: dict[str, str] = {'HIGH': 'High', 'HI_RES': 'HiRes', 'HI_RES_LOSSLESS': 'Max', 'LOSSLESS': 'HiFi', 'LOW': 'Low'}#

Short human labels for API qualities — display and the {quality} naming placeholder. Mirrors the reference client’s label set (its "Master (legacy)" becomes "HiRes": the old label is confusing in a file name).