Repository navigation
feat: tidal joins the provider selector, with its protected streams refused rather than worked around - #11
Merged
Conversation
…efused rather than worked around
… the way spotify's already is
…d the account menu signs in to tidal like it does to spotify
…nt line only once there is an account or a code
…ad of a guessed one
… login email only when there is none
…t, a sign-out outlives the exchange it interrupts, a long album gives up all its tracks, and the heart on a tidal track stops going to youtube
Author
|
Done on my end — CI green, release-check passes. Two calls are yours before this goes in: the baked-in client pair, and the playback terms quoted at the bottom of the description. Happy to pull the credentials back out if you’d rather not carry them. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
TIDAL is a fourth provider, reachable from the same selector as YouTube Music, Spotify and SoundCloud.
crates/tidal(new): OAuth device and PKCE sign-in, catalogue (search, track, album, artist, playlist, favourites), and stream resolution. 30 unit tests.crates/core/src/tidal_bridge.rs(new): TIDAL's models onto the sharedSongItem/BrowseItem/AlbumPage/PlaylistPageshapes, so the client renders TIDAL with no new page code. 7 unit tests.Provider::Tidal,tidal:id helpers, the session blob inAppState, and the playback branch inresolve.providerOf/sourceGlyph, and the account block in Settings and in the account menu. 2 new QML tests.Two refusals are typed rather than swallowed.
tidal_protectedis a manifest that carries a content key or declares DASHContentProtection;tidal_previewis anassetPresentationother thanFULL, which is what TIDAL returns when the account is not entitled to the tier requested. Neither is retried, and no key is held, derived or applied anywhere in the crate.Why
TIDAL was the one streaming service of the four Ryotunes users ask for with no path into the app. The provider seam already carried three catalogues, so the work was a provider crate plus a bridge rather than anything structural.
Surface area
provider, Home, Search, Library, playback, Settings
Validation
cargo fmt --all -- --check./scripts/release-check.shcd ui && pnpm check && pnpm buildcargo test --workspace --lockedpasses in full, includingryotunes-tidal31 andryotunes-core150.qmltestrunner -input client/tests67 passed, though note CI does not run the QML suite, so that figure is a local check rather than an enforced one.scripts/check-release-invariants.py,check-source-shapes.pyandcheck-rust-structure.pyall pass.Regression contracts
None of the listed contracts are touched: no WebKit, MPRIS, DOM, theme-bridge, window-rule or packaging change. The only shared-path edits are the provider branches in
methods.rsand theresolvearm, both additive.Evidence
The crate carries
examples/probe.rs, a standalone sign-in and resolve that prints the served quality, codec, sample rate and the refusal reason if there is one:tests/live_smoke.rsis#[ignore]d and reads a token from the environment; it checks the profile-name read against a real account without putting a credential in the repository.Three decisions worth your judgement rather than mine:
A client credential pair IS baked in, and this is the point to argue with.
crates/tidal/src/auth.rsships a device id and secret and a PKCE id and secret asDEFAULT_*constants, so sign-in is one button and the provider works out of the box. TIDAL publishes no credentials for third-party playback, so these come out of TIDAL's own applications, as they do in every other third-party client. That means the repository now carries someone else's credential and the project is bound to it: if TIDAL rotates or revokes the pair, sign-in breaks for everyone until a release ships a new one. A user-supplied pair still overrides the shipped one field by field, so anyone registering their own client keeps using it. If you would rather not carry these, say so and I will take them back out and restore the Settings-only path, at the cost of the provider being inert until the user pastes a pair.Hi-res needs a second sign-in, and the UI cannot complete it yet. The device flow tops out at
LOSSLESS; only a PKCE token is entitled toHI_RES_LOSSLESS, and the entitlement is fixed when the token is minted.Entitlement::ceiling()clamps the request, because asking above it returns a preview rather than an error. The PKCE redirect goes tohttps://tidal.com/android/login/authrather than a loopback address, so there is nothing for the client to capture: the crate implements the flow and the UI cannot drive it. Hi-res is therefore reachable fromexamples/probe.rsand not from the app. Wiring it needs either a loopback redirect on a registered client or a paste-the-URL step, and I did not want to pick one for you.This delivers hi-res streams, not bit-perfect output.
crates/playersets noaudio-samplerate, noaoand no exclusive mode, and inserts a float volume filter whenever a track carries a loudness value, so a 24/96 FLAC decodes correctly and is then resampled by the audio server. Changing that is a separate PR and I did not touch it here.And one thing for you to weigh rather than me. TIDAL's API & SDK overview states that the SDK's Player module is "the only allowed way for third-party applications to incorporate playback of TIDAL content", and that what a third-party application may play through it is previews. This branch does neither: it resolves and plays full streams off the private API, using TIDAL's own application credentials. Every open-source TIDAL client works this way, which is context rather than permission. If that is a line you do not want this project to cross, this is the moment to say so, and the credential question above becomes moot.
Follow-up
tidalglyph is drawn in the same simplified stroke language as the existingspotifyandsoundcloudentries rather than traced from the trademark, since those two are simplified impressions too. Swap it if you source brand marks differently.