Files
qbz/docs/release-2.0.0/CHANGELOG.md
T
vicrodh 7252918d4d chore: bump version to 2.0.0
QBZ 2.0 "Rebuild". Workspace, Tauri manifests, npm manifests and the
Flatpak metainfo all move to 2.0.0; TAG_DETAILS for the release notes.
snapcraft.yaml uses version: git and needs no bump.

cc
2026-07-03 18:36:06 -06:00

20 KiB

QBZ 2.0.0 — Technical Changelog (v1.2.15 → v2.0.0)

824 commits on slint-mvp (2026-05-18 → 2026-07-03). This release replaces the Tauri 2.0/WebView (SvelteKit) frontend with a native Slint UI running in the same process as the Rust core — no web engine, no IPC bridge. Nearly every feature below was rebuilt from scratch against the new shell; the sections group work by area rather than by commit date.


1. Architecture

  • Frontend rewrite: the SvelteKit/WebView frontend is replaced by a native Slint UI (qbz-ui library crate + qbz binary crate). Single Rust process, no embedded browser, no Tauri IPC — the UI calls into the core crates directly (f1748383, c8ef2a1b).
  • qbzd removed: the dormant standalone TUI/daemon crate was dropped from the workspace; it pinned an old unicode-width that blocked the Slint upgrade past 1.14 (eddfaed4).
  • Frontend-agnostic core crates extracted from src-tauri (ADR-005/006 — no legacy dependency, no tauri::State in core logic), so the same code runs headless (Slint, future TUI/CLI):
    • qbz-credentials — keyring + AES-256-GCM encrypted token store (aa64f3fa).
    • qbz-radio — smart-radio builder/db/engine (4a9cbdeb).
    • qbz-offline-cache — offline download pipeline, metadata/migration, CacheEvent sink, purge logic (d6e9345f…c8158bf4).
    • qbz-plex — Plex integration, ~1700 lines moved out of Tauri (2fe7008e).
    • qbz-mixtape — Mixtapes/Collections backend (5458f3aa).
    • qbz-lyrics — synced-lyrics provider chain and cache (46c15f50).
    • qbz-theme — theme/token registry with WCAG/APCA contrast math (ff5536a0).
    • qbz-i18n — pure-Rust gettext bundling (f92c95ee).
    • qbz-reco — recommendation/taste-vector engine (65de9849).
    • qbz-dsd — DSD demux/decode/output engine (landing in the 2.0.0 tag).
    • qbz-dac-wizard, qbz-text-utils, qbz-slint-common — further splits off qbz-ui for build parallelism (7678bceb, 6f9ad466, d1437c70).
  • In-process communication model: state that used to cross the Tauri IPC boundary (tauri::State, invoke/event channels) is now shared directly between the Slint controller layer and QbzCore/qbz-app — no serialization boundary between UI and playback/session state.
  • Qobuz Connect cross-frontend seam: a 16-method QconnectRendererEngine trait is implemented once by each frontend (CoreBridge for Tauri, SlintRendererEngine for Slint) so renderer orchestration (echo-seek rejection, cursor align, queue materialize) lives in qconnect-app a single time instead of being re-derived per shell (e5e8d4a8, e8ac6185, 88e72c3e).
  • CMAF streaming, playback caches, and the offline-tier resolver moved into the frontend-agnostic qbz-player/qbz-core, so both frontends share one playback path (29b6c205, d0a4a329, 506ba5ef).

2. UI shell & views

  • Three-state sidebar (open/mini/closed), right-flyout section nav, live theme switching, Discovery-style segmented tab bar, browser-like back/forward navigation history (682719ca, 12ed5a7c).
  • Custom title bar: frameless window with drawn controls, drag-to-move, double-click-to-maximize, edge resize (Linux opt-in, macOS overlay default on), plus an opt-in compact icon-only header nav (a7f538b3).
  • Theming: 24 standard themes transcribed 1:1 from the old app.css into a Rust registry, alpha polarity resolved per-theme rather than by a flat light/dark flag, plus 6 redesigned high-contrast accessibility themes with WCAG/APCA contrast tests and a broad Theme.is-high-contrast tokenization pass across sliders, tabs, toasts, badges and focus rings (40bd77f7, 48eefa3c, ff5536a0).
  • NowPlayingBar: rebuilt with four selectable layouts — New, Classic, Small ("Mode C", 42px compact row with floating art-hover preview), and Large ("Mode B") — plus a borderless miniplayer window with 5 surfaces (micro/compact/artwork/queue/lyrics), mirror-driven state, no extra poll loop (da7758b9, 1ad0d7c1, 8fa03a8c, 9d6b3533, c974b8a9).
  • System tray, MPRIS/SMTC media controls, and notifications: Linux tray via ksni (Wayland-safe hide/show, since winit's set_visible is a no-op on Wayland), macOS/Windows tray icon (later hand-rolled objc2 menu for correct icon sizing), MPRIS on Linux and SMTC/MediaRemote on Windows/macOS with content-sniffed album art, now-playing system notifications (8a78c225, a91d629c, 7a7e5f82, d08c5f65).
  • UI scale presets (XS/S/Default/L/XL via a settings-driven scale factor) shipped for interface density (b7549790).

3. Playback & audio engine

  • CMAF streaming ported into qbz-player: play_track fetches the init segment first, derives sample rate/bit depth, then background-fetches and decrypts subsequent segments, falling back to the legacy path on setup failure (29b6c205, 74ab627c, 655a90f4).
  • Gapless + prefetch: L1+L2 instant-replay cache and gapless-prefetch helpers wired into the Slint playback controller (7b84fa9a, 3a6aae19).
  • Device routing (issue #263): a three-tier fix stops the PipeWire global clock force-rate write from firing while a device lock is held, fixes a clock.force-rate leak on stop, resumes a suspended sink after exclusive access ends, and finally routes locked output to the actually-selected sink via PIPEWIRE_NODE instead of silently following the system default (d19312fe, b139392b, 64fb77ad, d2c4aaf9).
  • New JACK backend: lock-free SPSC ring buffer, allocation-free RT process callback, feeder thread resampling to the JACK graph rate, auto-connect to physical ports — offered as an opt-in backend, explicitly not bit-perfect (single fixed graph rate) (fbc03f41, 68bc9bd6).
  • PulseAudio works out of the box alongside the existing ALSA/PipeWire backends, with output-device list dedup, a searchable device selector, and Flatpak --device=all for direct /dev/snd access (c75a0af5, bdb9cada, f98d92d8).
  • Bit-perfect playback preserved throughout the rewrite: format-first quality badges, per-device bit-perfect-capable marking, and a HiFi Wizard self-test with bit-perfect read-back (see section 12).

4. DSD support (landing in the 2.0.0 tag)

New qbz-dsd crate with in-house DSF/DFF demuxers. Three playback modes are selectable in Settings (ALSA backend):

  • Convert — dsd2pcm (Gesemann-derived 6x256 LUT engine) with halfband decimation down to 88.2 kHz/24-bit PCM. Works on any output device.
  • DoP — DSD-over-PCM packing (0x05/0xFA markers over an S32 carrier) via direct ALSA hardware access, with gapless DSD playback and a pause mode that keeps the DAC locked using DSD silence bytes.
  • Native — raw DSD_U32_BE/LE output for DACs and kernels that support it, covering DSD64/128/256.

Also included: ID3 tag and artwork extraction for DSF/DFF, library-scanner recognition of .dsf/.dff with DSD64/128/256 quality badges, 5.1 SACD rips played back via ITU BS.775 stereo downmix in Convert mode, and a local-FLAC gapless bug fixed along the way. This work lands just ahead of the 2.0.0 tag and is uncommitted at the time of writing.

  • "Intelligent Search": a frontend-agnostic SearchService in qbz-app with a result cache (Capa A) and interaction-based ranking (Capa B), a live header dropdown with skeleton loading, stale-while-revalidate, 220ms debounce, keyboard nav, per-section caps, and an opt-out setting (22fb4162).
  • Search "cortinilla" — a glass search overlay for Albums/Artists/ Playlists that swaps playback without leaving Immersive mode, with a configurable action (replace queue / play next / add to queue), later extended to Local Library, Plex, and per-source artwork (22fb4162, b1ac402c).
  • Hardening: the dropdown no longer resurrects on navigation, auto-closes after 4.5s idle, and a typing guard was extended across remaining text inputs so hotkeys don't fire while typing (12b1b312, 31436325, c65bd9c2).

6. Offline mode & Local Library

  • Offline mode: a new frontend-agnostic qbz_app::offline_mode state machine (Online / RealOffline session-scoped / InducedOffline persisted) backed by a layered connectivity actor (default-route signal, audio-liveness window, vendor-diverse probes, hysteresis, captive-portal/suspend-resume handling), with all Qobuz HTTP traffic routed through a single offline gate (edae9d24, ba702bc3). Slint wiring covers offline session entry, a recovery banner, tri-state Settings, offline artwork policy, disk-first cached favorites, first-class local/offline-only playlists, and QConnect offline gating.
  • Local Library (new feature area, folder-based/self-managed music): server-paginated Albums/Tracks tabs with local+offline playback and source-aware auto-advance, Folders tab (flat then flat+tree), Artists tab as a two-column master/detail browser, folder management in Settings, and a library scan engine with progress (835a6cad, 56b3ca79, df351ec6, aa7c320b).
  • Tag editing: a full tag editor built on lofty (sidecar-default, opt-in write-to-file), a manual-edit modal, and remote metadata lookup against MusicBrainz + Discogs (7e0d970f, 4e79386d, 3c2f46b7).
  • Plex integration ported end to end: credential store, Settings auth/PIN, albums/artists/tracks in Local Library, progressive/fast-play streaming, source-aware artwork everywhere (now-playing bar, queue, MPRIS), server-transcoded thumbnails, and same-title album disambiguation into selectable versions (f7619a92, a45840d1, 06b2bea9, 334ddf31).
  • Offline cache manager: a Settings view for remove/redownload, covers, sort, and a failed-only filter, plus per-row offline buttons on every track row and whole-playlist caching (3e0a718e, a7f2283c).
  • Scrobbling: Last.fm and ListenBrainz scrobblers wired to the player and Settings (952846b6).

7. Qobuz Connect

  • Hardening (merged f3d37dac): unified cast-eligibility checks, strict source parsing (unknown sources blocked, not allowed through), mixed-queue guards with a Rust-side per-track origin backstop, a 12s renderer-liveness watchdog, half-open WebSocket detection via keepalive pong-timeout, idle-retry/startup-backoff recovery, takeover arbitration on SESSION_STATE, and full resync (state + queue) on reconnect (f3686c19, 434c2218, a05e8abc, 7628c284, ed1d7aee, 6c48266a).
  • Cross-frontend extraction: session/liveness logic, device_uuid persistence, and diagnostics relocated to shared crates; the QconnectRendererEngine trait now carries renderer orchestration once for both frontends (see Architecture) (f100cf4d, e5e8d4a8).
  • Slint port: renderer + controller integration, device picker with cast-aware now-playing state, peer-renderer playback reflected on the now-playing bar, peer volume with a 50% safety clamp, bit-perfect volume lock/force-100 parity, per-device-type icons, and takeback resume at the handed-off position (44876d01, 0d7ac793, 9bb5e6db, 8e1bf18f).

8. Discovery & recommendations

  • qbz-reco (new crate, ADR-006): SparseVector math, relationship weights, a 3-table SQLite ArtistVectorStore, and a direct-weight-ranking suggestions engine, trained from per-user play events (favorite-add, playlist-add) to back DailyQ/WeeklyQ mixes, a Rediscover row, and reordered "For You" favorites (65de9849, 4828e54c).
  • External recommendations: a Last.fm + ListenBrainz → Qobuz resolution engine wired as a 4th Discover tab, redesigned artist/album-centric with common/recent split and a 48h results cache, plus a Last.fm similar-albums carousel on AlbumView (408b3c83, 124d97e7).
  • Playlist "Suggested Songs" shipped, and a Discover section configurator lets users reorder/toggle Home/For You sections (2e1d8e43, 2a37f2ad).

9. Lyrics

  • New qbz-track-lyrics Qobuz endpoint with verified wire DTOs, feeding a headless qbz-lyrics engine: Qobuz-first provider chain, LRCLIB (search + scorer) fallback, then lyrics.ovh; LRC parser/emitter preserving word-level wsync, a per-user SQLite cache (WAL, additive migration), offline cache-only mode, in-flight dedupe, and 55 unit tests (46c15f50, d1203937).
  • Slint UI: static rendering evolved into a full sync engine with karaoke fill and auto-center scroll, plus a controls flyout for prefs and cache settings (5a4dbd87, 5f9fd5bd, eea336fd).

10. Content blocking

  • Artist blacklist: headless service extracted from src-tauri, applied across search, track rows (greyout/inert), albums/playlists/favorites/ suggest rows, all play/queue builders, and a dedicated Blacklist Manager view (ef93c864, 836dd249).
  • Album blacklist: a parallel String-keyed album_blacklist table alongside the artist table, sharing the same enabled flag, enforced across the same consumers with a Manager "Albums" tab (3fbb166b, c85ad5f6, 66889af4).

11. Immersive mode & visualizers

  • Full-screen "Immersive" now-playing experience (new epic, not present in Tauri): reusable atmosphere generation (art-derived blur/vignette), a zero-IPC 30fps visualizer feed (VizFrame/VizSink) lifted off tauri::AppHandle into qbz-audio (3adb5172).
  • Visual modes: Album Reactive, Static, Coverflow (2D fan), Spectrum Visualizer (~2240 to ~28 gradient bars for performance) (92052a3c, c067d237, 03dfd661).
  • wgpu fragment-shader underlay: renderer swapped femtovg(GL) to femtovg-wgpu, with a WGSL plasma shader driven by the visualizer feed, then extended to Tunnel and Aurora "milkdrop" scenes plus a scene picker; macOS stays on the Skia renderer since femtovg-wgpu pegs CPU on Metal (e46e8554, 653a449d, fb0aa619). Later scenes added: Wave Bed, Spectral Ribbon, Line Bed (226c2118); shader scenes hide automatically on non-wgpu renderer tiers (a050e639).
  • Focus/split layouts: lyrics panel, queue/history panel, track-info panel, and a live-query suggestions panel (77068e3b…e6f9c3b4).

12. HiFi Wizard revamp

Built headless-first: native pw-dump device enumeration with pactl fallback, DAC negotiated-rate probing via /proc/asound, audio-stack health and distro/init-system detection (systemd/antiX/Artix/NixOS), and Flatpak/Snap sandbox-aware host detection (381eafdf, f08943ad, 93da676f). Slint UI: a 6-step guided modal (check → select DACs → test → review/apply → done) with a self-service playback test and bit-perfect read-back (9bb392c2, 9c5febda, f87f2986).

13. Internationalization

  • New pure-Rust qbz-i18n crate: gettext .po catalogs bundled at compile time (no gettext C dependency), plural rules, and a t/tn/t_args/tf API (f92c95ee).
  • Full extraction and seeding pipeline from the old Tauri JSON strings across every module (local library, MyQBZ, discover/home/search, artist/album/ label, playback/offline, integrations/DAC wizard) (f14c5130…e167936c).
  • Live language switching without restart, locale-aware dates, and dynamic msgid registration via mark() (69d85f19, 8243f2a8).
  • Seven bundled locales at full coverage: English, Spanish, German, French, Portuguese, Russian, Japanese — the last two (be487dca) plus a macOS CJK font-fallback fix (vendored i-slint-common, fa3ee1e4) so Japanese kanji render correctly on Mac builds.

14. Performance

  • Renderer tier auto-detect (wgpu → GL → software) with a Settings override and auto-revert if a tier misbehaves (fe111c3f).
  • Artwork memory bounding: decoded-artwork memory capped and covers decoded at display size instead of full resolution (f37b6b6a).
  • Leak/idle fixes: shader-underlay GPU leak and idle 30Hz wakeups fixed, palette computation moved off the UI thread, idle now-playing pushes skipped, immersive atmosphere animation stopped while paused (dbe818c7, d7716d7c, e71f0ded).
  • Viewport windowing (phase 1) applied to the Local Library albums grid; virtualized ListViews (instead of Flickable + for) applied to Favorites Tracks and Playlist track lists for 2000+ item lists (23b572e5, 85223b03, 4465a367).
  • Reduced motion on GL/software renderer tiers (d9b82e7b).

15. macOS

  • Stable native build with a dedicated dev build/run script (85518699).
  • CJK font fallback fix so Japanese/Chinese/Korean text renders correctly (vendored i-slint-common, fa3ee1e4).
  • macOS Dock policy handling for the tray icon, then a hand-rolled objc2 tray menu for correct icon sizing/click routing (a91d629c, b9434a50).
  • Overlay title bar as the macOS default, distinct from the Linux frameless mode (a7f538b3).
  • Renderer forced to Skia on macOS since femtovg-wgpu pegs CPU under Metal (fb0aa619).
  • Contributor work from Afonso on macOS-specific fixes throughout the range.

16. Build, CI & packaging

  • Crate split for build memory: qbz-slint split into a qbz-ui library crate (owns the full .slint tree + bundled translations) and a qbz binary crate reclaiming the qbz binary name from the retired Tauri app — halves the ~20GB single-crate rustc peak into two independently cacheable units, with further extractions (qbz-dac-wizard, qbz-text-utils, qbz-slint-common) (c8ef2a1b, 7678bceb).
  • aarch64 Linux build script added (native ARM runner + x86-64 cross path); documents the ~30GB qbz_ui RAM wall that rules out 8GB Macs and 4GB Raspberry Pis for native builds (b89ee385, 5fdbdb3a).
  • CI rewrite: GitHub Actions build for qbz-slint on Linux stood up and hardened (runner disk exhaustion fixed, swap sized for the ~30GB peak, mupdf-sys system libs added), then moved to a self-hosted runner for real artifact builds (5806b0c0…b9eb3005).
  • All-hosted Snap pipeline added; deb/rpm packaging metadata added for the Slint qbz binary; release workflows rewritten and channel packagers repointed at the Slint binary; release profile now strips symbols (77a476c8, 9c37e456, 1b933299, 6e6153b2).
  • Linux binary kept compatible with glibc older than 2.43 (cafd521b).
  • Faster local dev builds via the mold linker and a memory-aware slint-run.sh to dodge the OOM wall (ec4b10fc, 14e800ea).

17. Other new features

  • Purchases ("My Purchases", 13-slice epic): wire models, HTTP + CDN fetch, pagination, download state machine, list/detail views with format and quality pickers, Settings toggle (default off) (aceaf9b0…9fbfcb55).
  • Awards pages ported (landing, album listing, follow) (9ccfbd38).
  • Playlist importer ported (Open Playlist Importer) (3689a5fd).
  • Keyboard multi-select ported and extended to Mix/Label/Local Library Albums, with a three-column shortcuts cheatsheet and matching customize editor (53e1e8cd, 1a9c1327).
  • Chromecast/DLNA casting: a shared external-stream resolver (CMAF-first, UltraHiRes-first), qbz-cast crate, picker modal, and offline-gate routing (fdf2fec3, 3e1101ff).
  • Discord Rich Presence (opt-in) ported from Tauri (ef38e7a0).
  • Session persistence across restarts: startup page, window geometry, and playback state (dd32f573).

Fixes

  • Output-device routing/locking bugs from issue #263 (see Playback section).
  • Local-FLAC gapless playback fixed (landed alongside the DSD work).
  • Half-open QConnect WebSocket sessions now detected via keepalive pong-timeout instead of hanging indefinitely (7628c284).
  • Startup now restores the exact last view, not just the top-level tab (e44aa07a).
  • Album credit fixed to read the album-level composer field instead of per-track (0ddcad40); missing pagination fields in album responses are now tolerated instead of erroring (9e2a1d12).
  • Single-click bug on button primitives (a FocusScope ate the first press) fixed app-wide (88f1967d); single-click album context menus (8c182875).
  • Secret-vault open moved off the async pool to avoid a zbus panic (40414991).
  • Artist follow heart made a real working toggle, reflected across every card surface (d906e556).
  • Qobuz playlist Follow/Copy state fixed across surfaces, with owner-aware remove (delete vs. unfollow) (bf4ffbd6, de32c168).
  • /radio/* deserialization repaired and every radio entry point rewired (6520c007).
  • rustls CryptoProvider installed at startup for Qobuz Connect (90705471).
  • Don't cache empty ListenBrainz results, which previously made Weekly mixes vanish (1e78884e).

Full changelog: https://github.com/vicrodh/qbz/compare/v1.2.15...v2.0.0