13996a0566d21f62c3a739a82590ee9ada757098
2
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
b6cf06ff2a |
feat(source): wire My QBZ through the seam, and land the owner's container rules
Stage 2. The measured win: opening a 34-item collection now costs **2** library.db opens instead of 23-34 — the local source owns a pooled handle and hands out no guard, only `with(|db| ...)`, so a `!Send` `&LibraryDatabase` cannot cross an await either. Bug 1 from the seam design is dead by construction, not by care. The owner's rules, decided today, now live in one place instead of eight: - **Collection** takes albums, singles and EPs from ANY source — qobuz, folders, Plex, and whatever is added next. **Mixtape** takes albums, playlists and tracks. This was a single boolean (`restrict_to_mixtape`) that could express neither release types nor "collections accept local". - An unsupported combination must never be VISIBLE: the picker stops offering the impossible action, and the error stays a log-only safety net. Owner: "el bloqueo no debería ser visible nunca, más bien esto hay que ajustarlo en la UI". - **Ephemeral honours its name** — playable, queueable, and offerable to nothing else. - A row's source comes from the ROW. Eight QML sites built the add-to-mixtape payload with `"source": "qobuz"` written as a literal, so a Plex or local track was stored as Qobuz and afterwards could not play at all: the app asked the Qobuz API for an id that never existed there. `QobuzSource` reads the client through a lens on every call instead of holding a clone. That is not style: `qbz-core` REPLACES its client (core.rs:346 and :384, both on paths that run before `set_session`), so a cached clone goes stale and fails silently — the hazard the crate's own doc comment described. The lens is installed in `on_boot`, first of everything, and finding out why is the lesson of this commit. The gate was green — build, 142 tests, both QML audits, a clean offscreen smoke — and the feature was visibly broken: every Qobuz row showed "No results found", an empty Quality cell and a wrong "SINGLE" type, while the Plex rows resolved fine. The registry is a OnceLock, so whoever touches it first BUILDS it, and one built without the lens leaves Qobuz permanently detached. Only driving the real app surfaced it; the crate's error string named the cause exactly, which is the one thing that made it a ten-minute fix instead of an afternoon. cc |
||
|
|
f1b4241342 |
feat(source): add qbz-source, the source-agnostic media seam
Stage 1 of the seam the owner asked for: "son vistas súper híbridas donde se mezcla Qobuz/Local(Folders/Plex) ... hay que hacer una solución agnóstica porque no tardamos en meter a Jellyfin y quizás Navidrome". Nothing is wired yet — this lands the crate, its three source implementations and its tests. `qbz-qt` and `qbz` are untouched, so this commit changes no behaviour. The problem it exists to end, measured rather than assumed: 129 source-branching sites across 30 files in qbz-qt alone, SEVEN distinct item->tracks resolvers none of which can reach the others, FIVE non-interchangeable artwork pipelines over one correct taxonomy, and six incompatible source vocabularies — three of which cannot express Plex at all, including the PERSISTED one (`AlbumSource` is `Qobuz|Local`). Every hybrid view re-wires the per-source handling by hand, which is why something is always missing. One `Source` trait covering the four concerns together — tracks, metadata and origin, artwork, playback — because splitting them is exactly what lets a view forget one. A `SourceRegistry` with claim-priority ordering (Qobuz, Plex, then Local; Plex-before-Local is load-bearing). `RegistryResolver` implements `qbz_mixtape::ItemResolver`, so that crate and the Slint glue stay byte- untouched and keep compiling. The three measured bugs die structurally, not by discipline: - The local DB was reopened PER CALL, so a My QBZ collection logged one "Opening library database" per item (34 measured live). `LocalSource` owns a pooled handle, and it hands out no guard — only `with(|db| ...)` — so a `!Send` `&LibraryDatabase` provably cannot cross an await. `qbz-mixtape`'s sync-resolver constraint becomes an API property instead of a comment. - A `plex:<hash>` album key reached an API that wants a rating key. `MediaRef` has no public constructor: only `registry.claim` mints one, and `PlexSource::claim` rejects that shape in track position. The survey also found a second, undiscovered face of the same bug — "Go to album" on a Plex track row opens an empty page. - Local rows in a hybrid list rendered placeholder discs. Traced to its real mechanism: those items are stored with `artwork_url: None` and the only backfill is behind a Qobuz-client gate, so offline or pre-login the art is on disk and never shown. `Source::artwork` is one call per row whatever the source, and keeps the cheap/cold split the local path already had. Adding Jellyfin is one new file, four diff lines inside this crate, and one line in `auth_qt.rs` — and the design states this only holds BECAUSE artwork sits on the trait. 36 tests. The id tests derive their fixtures from `PLEX_TRACK_ID_FLOOR` instead of transcribing them, which is not pedantry: the survey's own id table — the artefact meant to be the definitive reference — paired a namespaced id with the wrong rating key, and the transcribed test caught it on the first run. Design and adversarial review (6 BLOCKER / 10 MAJOR / 7 MINOR, all corrected): qbz-nix-docs/qt-frontend/2026-07-30-source-seam/. cc |