Migrate the Tidal provider to the official API where possible.#4875
Migrate the Tidal provider to the official API where possible.#4875jozefKruszynski wants to merge 11 commits into
Conversation
🔒 Dependency Security Report✅ No dependency changes detected in this PR. |
Merging this PR will not alter performance
Comparing Footnotes
|
There was a problem hiding this comment.
Pull request overview
This PR migrates large parts of the Tidal provider (catalog/search/library/favorites/playlists) from the unofficial api.tidal.com/v1 API to the official openapi.tidal.com/v2 JSON:API, adds parsers/models for the new payloads, and introduces track-id “churn” healing via ISRC-based resolution and cached redirects.
Changes:
- Add JSON:API support to the Tidal API client (read/write + cursor pagination) and new v2 parsers/fixtures/tests.
- Implement stale-track-id resolution (
resolve_live_track_id) and best-effort library mapping healing. - Switch playlist + library write operations (add/remove) to official JSON:API relationship endpoints.
Reviewed changes
Copilot reviewed 35 out of 40 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/providers/tidal/test_streaming.py | Updates streaming tests to use the new resolve_live_track_id fallback instead of ISRC lookup in streaming manager. |
| tests/providers/tidal/test_provider.py | Adds tests for cached redirect + live-id resolution + mapping heal scheduling behavior. |
| tests/providers/tidal/test_playlist.py | Rewrites playlist create/add/remove tests for JSON:API write endpoints and healing behavior. |
| tests/providers/tidal/test_parsers_v2.py | Adds comprehensive tests for new v2 JSON:API parsers (track/album/artist/playlist, images, durations, credits). |
| tests/providers/tidal/test_media.py | Updates media manager tests for official search and relationship endpoints; keeps lyrics on unofficial API. |
| tests/providers/tidal/test_media_extended.py | Updates extended media tests for v2 similar-tracks/search-empty behavior. |
| tests/providers/tidal/test_library.py | Rewrites library manager tests to use official user collections + healing on add-track. |
| tests/providers/tidal/test_jsonapi.py | Adds unit tests for JsonApiDocument relationship resolution and cursor parsing. |
| tests/providers/tidal/test_api_client.py | Adds tests for official/unofficial param scoping, JSON:API writes, and cursor pagination. |
| tests/providers/tidal/fixtures/v2/track.json | Adds captured official v2 track document fixture. |
| tests/providers/tidal/fixtures/v2/track_credits.json | Adds captured official v2 track-with-credits document fixture. |
| tests/providers/tidal/fixtures/v2/artist.json | Adds captured official v2 artist (bio/profileArt) fixture. |
| tests/providers/tidal/fixtures/v2/artist_wimplink.json | Adds captured fixture validating stripping of [wimpLink] biography markup. |
| tests/providers/tidal/fixtures/v2/album.json | Adds captured official v2 album document fixture. |
| tests/providers/tidal/fixtures/v2/album_items.json | Adds captured album relationship/items fixture to validate ordering metadata parsing. |
| scripts/tidal_openapi/README.md | Documents the vendored spec + model generation workflow for official API TypedDicts. |
| scripts/tidal_openapi/generate_models.py | Adds script to generate TypedDict models from vendored OpenAPI spec (via uvx datamodel-codegen). |
| scripts/tidal_openapi/init.py | Marks scripts.tidal_openapi as a module. |
| scripts/lint_baselines/private_methods_not_last.txt | Updates baseline after moving private methods to the end in tidal provider/media. |
| pyproject.toml | Excludes Tidal fixtures from codespell. |
| music_assistant/providers/tidal/streaming.py | Switches stream-details fallback to provider-level live-id resolution and removes streaming-local ISRC lookup. |
| music_assistant/providers/tidal/provider.py | Adds cached redirect + ISRC-based live-id resolution + mapping healing; adds similar-artists feature. |
| music_assistant/providers/tidal/playlist.py | Migrates playlist create/add/remove to official JSON:API writes; adds stale-id healing retry logic. |
| music_assistant/providers/tidal/parsers_v2.py | Adds new official JSON:API parsers for artist/album/track/playlist + helpers (images, durations, popularity, links). |
| music_assistant/providers/tidal/media.py | Migrates search + several “relationship” reads to official JSON:API; keeps mixes/lyrics on unofficial endpoints. |
| music_assistant/providers/tidal/library.py | Migrates library CRUD to official user collections; adds track-add healing retry. |
| music_assistant/providers/tidal/jsonapi.py | Introduces JsonApiDocument helper for included-resource indexing, relationship resolution, and cursor parsing. |
| music_assistant/providers/tidal/constants.py | Adds JSON:API content-type constant and removes legacy favorites endpoint constants. |
| music_assistant/providers/tidal/api_client.py | Adds JSON:API read/write helpers and cursor pagination; scopes sessionId to unofficial API only. |
| music_assistant/providers/tidal/_openapi_models.py | Adds generated TypedDicts for selected official API attribute schemas. |
| body = {"data": [{"type": resource_type, "id": item_id}]} | ||
| await self.api.write_jsonapi(method, f"{resource_name}/me/relationships/items", body) | ||
| return True | ||
| except ClientError, MediaNotFoundError, ResourceTemporarilyUnavailable: |
| playlist = parse_playlist_v2(self.provider, doc, doc.data) | ||
| # The create response doesn't include the "owners" relationship, so | ||
| # parse_playlist_v2 can't infer editability from it. The authenticated | ||
| # user is by definition the owner of a playlist they just created. | ||
| playlist.is_editable = True | ||
| playlist.owner = ( |
What does this implement/fix?
Migrates the Tidal provider's catalog, search, library, favourites and playlist
operations from the unofficial
api.tidal.com/v1to the officialopenapi.tidal.com/v2JSON:API. Playback, lyrics and mixes stay on theunofficial API (official playback is DRM-only, mixes are deprecated/404).
Highlights:
tracks under new ids. Adding such a track to a playlist or favourites used to
silently do nothing; it now resolves the live id via ISRC and actually adds it.
id from dead to live, so the library corrects itself as you use it.
links, track version and finer-grained popularity now populate.
Models/spec and captured response fixtures account for the bulk of the diff.
All Tidal provider tests pass; mypy and pre-commit clean.
Related issue (if applicable):
Types of changes
bugfixnew-featureenhancementnew-providerbreaking-changerefactordocumentationmaintenancecidependenciesChecklist
pre-commit run --all-filespasses.pytestpasses, and tests have been added/updated undertests/where applicable.music-assistant/modelsis linked.music-assistant/frontendis linked.