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