ONE component instantiated twice, because it is one form twice. Every
difference between the protocols is a property set at the call site, so
they are visible in one place instead of drifting across two files:
"Test" proves the address for Jellyfin and only "a Subsonic server
answered" for Subsonic (which has no credential-free endpoint), the
password is discarded for Jellyfin and kept for Subsonic (no session,
md5(password+salt) per request), and the sync cost is stated up front —
45.8 s per 4924 tracks against 0.81 s per 6678 — so a long sweep does not
read as a hang. The credential never leaves Rust; the panel gets
`hasCredential`.
Assets: the colour marks for cards, the monochrome ones for rows. Both
mono files arrived as BLACK ink and are invisible on a dark theme
untinted, so they were normalised to the #ffffff master ink and joined
`icon_tint_qt::MASTERS` — the port's only shader-free way to recolour an
SVG per theme. The Jellyfin colour asset had a white rounded-rect plate
behind the mark; it was stripped, because every other brand mark here is
transparent and a white tile beside them reads as a rendering bug.
Rendered with Qt itself and looked at before any of it was wired.
CAVEAT, recorded in SourceIcon: the Subsonic mark is NAVIDROME's logo,
and `subsonic` covers Gonic, Airsonic, Astiga and Ampache too. `ping`
reports the server's own type and it is already persisted, so branching
later is cheap — a known wrongness, not an unnoticed one.
THREE BUGS THAT ONLY THE RUNNING WINDOW COULD SHOW, all found by
driving it under Xvfb rather than by reading the log:
1. `MediaServerSettings is not a type` — a .qml missing from build.rs is
not in the QML module. It resolves fine on disk, compiles nothing,
and blanks the WHOLE settings page at runtime. All three existing
audits passed. `qml_module_registration_audit.py` is the missing
check and is now in the gate.
2. The toggle would not move. The panel reads its state from the
settings DOCUMENT, unlike Plex's toggle which reads a bridge
property, so every mutation has to republish it — connect, enable,
disconnect and the end of a sweep now do.
3. Every Jellyfin album in the grid drew the LOCAL HARD-DRIVE badge:
`local_rows::badge_source` had never been taught the two words and
folded them into `_ => local`. Not cosmetic — that string is what the
source chips filter on.
`play_single_from_source` also grew the arm it needed: a media-server row
from the recently-played rail has no library.db row to mint a LocalTrack
from, so it resolves through `registry.tracks_of` instead.
END TO END, from the UI, against the real server: toggle on -> type the
address -> Connect -> "jellyfin sync: 500/4924 ... 4924 saved, 0 pruned,
4924 cached" -> Local Library shows 461 albums with their covers and the
Jellyfin badge. The cached quality distribution is identical to what the
server reported in the original protocol sweep.
428/428.
cc