feat(slint): Plex quality hydration on album-open + consistent quality badges (port slice 6)

Hydration:
- qbz-plex: additive plex_hydrate_album_quality(base, token, rating_keys) — per
  key plex_get_track_metadata (batch 5, 5s/call timeout, skip failures) then the
  COALESCE write-back plex_cache_update_track_quality; awaited before re-read.
- qbz-player/qbz-core: patch_plex_quality / patch_plex_queue_quality patch the
  in-memory queue in place by rating_key (for the now-playing stamp).
- qbz-slint: album-open trigger (gated on DB-NULL, not a value heuristic) then
  refresh of all badge surfaces — album-card grid (re-read aggregate + patch
  every model + the raw filter source), album-detail header + rows, now-playing
  stamp. Re-sync stays routed through plex_cache_save_tracks (carry-over).

Quality badge consistency (the four surfaces must agree — and pre-hydration
show the filetype, not nothing):
- One shared classifier crate::quality::badge(format, bit_depth, sample_rate) ->
  (tier, detail, tooltip); the album card, album-detail header and track rows
  all route through it (kills the per-surface duplication that produced
  "96 kHz" vs "96000 kHz"). sample_rate normalized to kHz via a >=1000 guard so
  Qobuz's already-kHz values are never re-divided (no "0.041 kHz").
- New "lossless" tier: an un-hydrated lossless file (FLAC/WAV/ALAC… with unknown
  bit depth) shows a generic filetype badge ("FLAC") instead of an empty badge,
  rendered in QualityBadge (compact chip) + QualityBadgeFull; upgrades to
  "24-bit / 96 kHz" after hydration. Local/Qobuz/mp3 unchanged.

cc
This commit is contained in:
vicrodh committed 2026-06-08 10:27:28 -06:00
1 parent a680da5ad8
commit 5939c090ca
10 files changed
+599 -38

No files matched your search

+40
View File
@@ -73,6 +73,46 @@ impl QueueManager {
.and_then(|idx| state.tracks.get(idx).cloned())
}
/// Patch the cached quality (bit depth + sample rate) of every queued track
/// whose Plex `rating_key` matches one of `updates`. Used by the Plex
/// quality-hydration path so a track that gets hydrated while it is already
/// enqueued/playing has its frozen quality snapshot upgraded in place. Plex
/// rows carry their `rating_key` in `source_item_id_hint`. `sample_rate` is
/// in kHz to match the enqueue-time snapshot (`local_queue_track`). Returns
/// true if the CURRENT track was among those patched (the caller then
/// re-pushes the now-playing stamp).
pub fn patch_plex_quality(&self, updates: &[(String, Option<u32>, Option<f64>)]) -> bool {
if updates.is_empty() {
return false;
}
let mut state = self.state.lock().unwrap();
let current_idx = state.current_index;
let mut current_patched = false;
for (idx, track) in state.tracks.iter_mut().enumerate() {
if track.source.as_deref() != Some("plex") {
continue;
}
let Some(key) = track.source_item_id_hint.as_deref() else {
continue;
};
if let Some((_, bit_depth, sample_rate)) =
updates.iter().find(|(k, _, _)| k == key)
{
if let Some(bd) = *bit_depth {
track.bit_depth = Some(bd);
track.hires = bd > 16;
}
if let Some(sr) = *sample_rate {
track.sample_rate = Some(sr);
}
if current_idx == Some(idx) {
current_patched = true;
}
}
}
current_patched
}
/// Add a track to the end of the queue
pub fn add_track(&self, track: QueueTrack) {
let mut state = self.state.lock().unwrap();