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
106 lines
4.5 KiB
Plaintext
106 lines
4.5 KiB
Plaintext
// Full quality badge — a faithful port of the Tauri QualityBadgeStatic
|
|
// (full mode): a contained pill with the format icon, the tier label, and
|
|
// the exact bit-depth / sample-rate line.
|
|
//
|
|
// Intentionally not a creative reinterpretation — the Tauri layout is the
|
|
// contract. Used wherever a track list or detail view advertises its
|
|
// stored quality. `compact` scales it ~20% smaller for the now-playing
|
|
// AudioStamp without touching the other call sites.
|
|
|
|
import { Theme } from "../foundation/semantic-colors.slint";
|
|
import { QbzIcon } from "QbzIcon.slint";
|
|
|
|
export component QualityBadgeFull inherits Rectangle {
|
|
// "hires" | "cd" | "mp3" | "".
|
|
in property <string> tier;
|
|
// Exact quality, e.g. "24-bit / 96 kHz".
|
|
in property <string> detail;
|
|
// Whether to show the format icon (Hi-Res / CD / MP3 mark). The track-row
|
|
// variant turns it off.
|
|
in property <bool> show-icon: true;
|
|
// "bare" = no chip background or border, text centered — blends into a
|
|
// track row instead of looking like a contained badge.
|
|
in property <bool> bare: false;
|
|
// ~20%-smaller variant for the now-playing AudioStamp.
|
|
in property <bool> compact: false;
|
|
// Extra proportional scale on top of `compact` (1.0 = none). The
|
|
// now-playing stamp drops this to 0.9 below the responsive break so it
|
|
// never crowds the song card.
|
|
in property <float> scale-factor: 1.0;
|
|
|
|
property <string> tier-label: root.tier == "hires"
|
|
? "HI-RES"
|
|
: (root.tier == "mp3"
|
|
? "MP3"
|
|
: (root.tier == "lossless" ? "LOSSLESS" : "CD"));
|
|
|
|
visible: root.tier != "";
|
|
height: (root.compact ? 29px : 36px) * root.scale-factor;
|
|
width: root.bare
|
|
? content.preferred-width
|
|
: Math.max(content.preferred-width, (root.compact ? 108px : 136px) * root.scale-factor);
|
|
border-radius: 4px;
|
|
background: root.bare ? transparent : Theme.surface-hover;
|
|
border-width: root.bare ? 0px : 1px;
|
|
border-color: Theme.border-subtle;
|
|
|
|
content := HorizontalLayout {
|
|
padding-left: root.bare ? 0px : (root.compact ? 8px : 10px) * root.scale-factor;
|
|
padding-right: root.bare ? 0px : (root.compact ? 8px : 10px) * root.scale-factor;
|
|
spacing: root.bare ? 0px : (root.compact ? 5px : 6px) * root.scale-factor;
|
|
alignment: root.bare ? center : start;
|
|
|
|
// Format icon. The Hi-Res brand mark keeps its own colors; the
|
|
// CD / MP3 marks render in muted grey.
|
|
if root.show-icon: VerticalLayout {
|
|
alignment: center;
|
|
if root.tier == "hires": Image {
|
|
source: @image-url("../assets/hi-res.svg");
|
|
width: (root.compact ? 19px : 24px) * root.scale-factor;
|
|
height: (root.compact ? 19px : 24px) * root.scale-factor;
|
|
image-fit: contain;
|
|
}
|
|
if root.tier == "cd": QbzIcon {
|
|
source: @image-url("../assets/cd.svg");
|
|
width: (root.compact ? 13px : 16px) * root.scale-factor;
|
|
height: (root.compact ? 13px : 16px) * root.scale-factor;
|
|
tint: #b0b0b0;
|
|
}
|
|
if root.tier == "mp3": QbzIcon {
|
|
source: @image-url("../assets/mp3.svg");
|
|
width: (root.compact ? 13px : 16px) * root.scale-factor;
|
|
height: (root.compact ? 13px : 16px) * root.scale-factor;
|
|
tint: #b0b0b0;
|
|
}
|
|
// Un-hydrated lossless (filetype known, bit-depth/rate not yet):
|
|
// reuse the neutral disc mark; the detail line carries "FLAC".
|
|
if root.tier == "lossless": QbzIcon {
|
|
source: @image-url("../assets/cd.svg");
|
|
width: (root.compact ? 13px : 16px) * root.scale-factor;
|
|
height: (root.compact ? 13px : 16px) * root.scale-factor;
|
|
tint: #b0b0b0;
|
|
}
|
|
}
|
|
|
|
VerticalLayout {
|
|
alignment: center;
|
|
spacing: 1px;
|
|
Text {
|
|
text: root.tier-label;
|
|
color: #b0b0b0;
|
|
font-size: (root.compact ? 7px : 8px) * root.scale-factor;
|
|
font-weight: 100;
|
|
letter-spacing: 0.5px;
|
|
horizontal-alignment: root.bare ? center : left;
|
|
}
|
|
Text {
|
|
text: root.detail;
|
|
color: #999999;
|
|
font-size: (root.compact ? 8px : 9px) * root.scale-factor;
|
|
font-weight: 100;
|
|
horizontal-alignment: root.bare ? center : left;
|
|
}
|
|
}
|
|
}
|
|
}
|