Mixtapes & Collections release. Bumps all version references
(Cargo.toml, tauri.conf.json, package.json, snapcraft.yaml, both
Cargo.lock, package-lock.json), adds the 1.2.8 entry to the Flatpak
metainfo, and sets the About modal codename to "My QBZ".
cc
Full surface for create/get/list/rename/description/play_mode/kind/
delete/touch_play on collections and add/list/remove/reorder on items.
Dedup on exact (source, source_item_id) within a collection. Artist
collection kind is locked (cannot be converted to/from). Cascade
delete covers items. Position compaction on remove; atomic transaction
on reorder.
Covered by 10 unit tests using in-memory SQLite.
cc
Adds columns to cached_tracks for the v2 format that stores Qobuz's raw
CMAF segments bit-identical on disk, with the per-track AES content key
wrapped via qbz-secrets (OS keyring or KDF fallback).
New columns (added via idempotent ALTER TABLE, safe to re-run):
- cache_format INTEGER DEFAULT 1 — 1 = legacy plain FLAC (existing),
2 = raw CMAF bundle (new)
- init_path TEXT — path to init.mp4 (FLAC header +
segment table)
- content_key_wrapped BLOB — wrapped 16-byte AES content key
- infos_wrapped BLOB — wrapped session infos salt
- format_id INTEGER — Qobuz format id (5/6/7/27)
- n_segments INTEGER — number of audio segments
Existing rows keep cache_format=1 and continue to play from the plain
FLAC at file_path — zero regression for users with populated caches
when the upgrade ships. New downloads will land as cache_format=2. The
two formats coexist until v1 rows age out naturally (user re-download
or subscription-lapse cache wipe).
Adds set_cmaf_bundle() / get_cmaf_bundle() helpers + a CmafBundleRow
struct so the download path and the playback path have one authoritative
DB surface to talk to.
Also adds qbz-secrets as a direct src-tauri dep so the next commit
(rewiring spawn_track_cache_download) can wrap keys right at the
download site.
cc
AirPlay was scaffolded in both crates/qbz-cast/src/airplay and
src-tauri/src/cast/airplay but never worked at runtime: the RAOP
sender required an Apple MFi-licensed stack with no free/open
alternative, and the backend state was already disabled via the
commented `.manage(airplay_state)` line in src-tauri/src/lib.rs.
The CastPicker UI already hid the AirPlay tab pending streaming
support that was never going to ship.
Delete:
- crates/qbz-cast/src/airplay/ (entire directory)
- src-tauri/src/cast/airplay/ (entire directory)
- 10x v2_airplay_* commands from commands_v2/library.rs + their
tauri::generate_handler entries
- AirPlayError from qbz-cast errors
- 'airplay' from CastProtocol + every case arm in castStore.ts,
CastPicker.svelte, AudioOutputBadges.svelte
- Corresponding i18n keys if present
Zero behavior change for Chromecast / DLNA / QConnect / MPRIS. If
AirPlay ever becomes feasible we'll revisit from scratch with a
real streaming backend.
cc
* src-tauri/Cargo.toml and Cargo.lock: 1.2.4 -> 1.2.5
* src-tauri/tauri.conf.json: 1.2.4 -> 1.2.5
* package.json (+ package-lock.json regenerated): 1.2.4 -> 1.2.5
* snapcraft.yaml: 1.2.4 -> 1.2.5
* packaging/flatpak/com.blitzfc.qbz.metainfo.xml: new <release> entry
dated 2026-04-15 covering the visual refresh (new logo + login
branding), Plasma/Klassy auto-detect for window controls,
opt-in match-system-chrome rounded corners, Editor's Picks
ribbon cleanup, the two sidebar menu positioning fixes shipped
alongside this bump, and the DAC wizard contrast fix.
Tag not applied per request — release notes are staged, ready for
the screenshots pass and manual tag afterward.
cc
Tauri's default tray uses libayatana-appindicator, which by design does
not dispatch primary-click events to its client — libayatana delegates
every left-click to the libdbusmenu-managed menu. The existing
show_menu_on_left_click(false) setting and TrayIconEvent::Click handler
never fire on Linux. Confirmed present in tray-icon 0.21.x and 0.22.0
(the newest release).
Switch the Linux backend to ksni, a pure-Rust StatusNotifierItem
implementation that exposes the full SNI protocol including Activate
(left-click), SecondaryActivate (middle), and ContextMenu. Same SNI
compatibility as the previous backend — works wherever the panel
already handled our tray (KDE, GNOME+AppIndicator, XFCE/Cinnamon/MATE,
wlroots trays). Flatpak just needs the --talk-name=org.kde.
StatusNotifierWatcher permission which is already in our manifest.
Tauri's tray stays on macOS (no Windows client is shipped). The
dispatch lives in tray.rs behind cfg(target_os); Linux delegates to
the new tray_linux_ksni module. Playback-control menu events
(tray:play_pause, tray:next, tray:previous) still emit into the main
webview window, so frontend handlers keep working unchanged.
cc
Makes tauri-plugin-updater and tauri-plugin-process optional
dependencies enabled by a default-on `updater` Cargo feature.
Sandboxed packaging targets (Flatpak, Snap) will build with
`--no-default-features` to exclude the plugins entirely,
satisfying store policies that prohibit competing auto-updaters.
Changes:
- Cargo.toml: add `updater` feature; plugins are now optional deps
- lib.rs: gate plugin registration on the feature
- updates/mod.rs: is_auto_update_eligible() returns false when
the feature is off (catch-all for unusual build configs)
- capabilities/default.json: remove updater/process permissions
- build.rs: emit capabilities/auto-updater.json only when the
feature is enabled; remove it otherwise. The file is gitignored
since it is fully build-driven.
cc
Adds tauri-plugin-updater and tauri-plugin-process crates, the updater
config block in tauri.conf.json (with PLACEHOLDER_PUBLIC_KEY to be
replaced before release), capabilities for updater/process, and the
v2_is_auto_update_eligible command that returns true on macOS and
Linux AppImage installs only.
Cherry-picked from #305 by @afonsojramos.
cc
Daemon side: registers _qbz._tcp.local mDNS service on startup
so desktop apps can discover it on the LAN. Uses hostname as
service name (configurable via qbzd.toml).
Desktop side: new qbzd_discovery.rs module with Tauri commands:
- v2_qbzd_start_discovery: browse _qbz._tcp services
- v2_qbzd_stop_discovery: stop browsing
- v2_qbzd_get_devices: list discovered daemons
Uses same mdns-sd crate already used for AirPlay discovery.
cc
Add per-app secret from org.freedesktop.portal.Secret as entropy
in the AES-256-GCM key derivation. DE-agnostic (works with GNOME,
KDE, etc.) and Flatpak-safe (portals auto-accessible in sandbox).
Falls back to machine_id + salt when portal is unavailable (headless,
old DEs, non-Linux). Cached for session lifetime via OnceLock.
Users will need to re-login once on upgrade (key derivation changed).
cc
Cargo.toml was left at 1.2.2 because the version bump sed command
only matched JSON format ("version": "1.2.2") not TOML format
(version = "1.2.2"). This caused CARGO_PKG_VERSION to report 1.2.2
at runtime, triggering the update-available modal on every launch.
cc
Enable async-secret-service + crypto-rust features on the keyring
crate, activating GNOME Keyring and KDE Wallet support. The keyring
integration code was already written but the crate was compiled
without any platform backend, so it silently failed every time.
OAuth tokens will now be stored in the system keyring when available,
with encrypted file as automatic fallback.
Added org.freedesktop.secrets to Flatpak finish-args for sandbox
keyring access.
Fixes#215
cc
Download tracks via Qobuz Akamai CDN with encrypted CMAF segments.
Derives session key (HKDF), unwraps content key (AES-CBC), decrypts
frames (AES-CTR), reassembles into standard FLAC. Falls back to legacy
track/getFileUrl if CMAF fails.
- Add qbz-cmaf dependency to src-tauri
- Make ensure_cmaf_session public on QobuzClient
- Add ensure_cmaf_session and get_file_url passthroughs to CoreBridge
- Add download_audio_cmaf and try_cmaf_download helpers
- Wire CMAF as primary download path in v2_play_track with legacy fallback
cc
Remove dead alsa dependency from src-tauri/Cargo.toml (code lives in
qbz-audio since V2 migration) and bump qbz-audio to alsa 0.10.
Eliminates duplicate alsa versions in Cargo.lock.
Breaking changes in 0.10 (Format/ChmapPosition reorder) do not affect
QBZ — we use catch-all matches and never rely on PartialOrd.
Closes#260
cc
- Upgrade rust_cast 0.18.1 → 0.21 (switches from openssl to rustls,
removing the openssl-sys dependency that blocked x86_64 cross-compile)
- Split mupdf sys-lib features to Linux-only (on macOS there are no GTK
symbol conflicts, and system libs don't support x86_64 cross-compile
from Apple Silicon)
- Update release-macos workflow to build both aarch64 and x86_64 DMGs
using a matrix strategy
Add idle_inhibit module using ashpd's Inhibit portal to prevent
screen blanking and system suspend while audio is playing.
- Acquires Idle+Suspend inhibit when playback starts
- Releases when paused, stopped, or track cleared
- Works on GNOME, KDE, Sway, XFCE
- Works inside Flatpak/Snap (no extra permissions needed)
- Graceful fallback if portal unavailable
Addresses #229
cc
Add tauri-plugin-deep-link to handle qobuzapp:// URLs on macOS, where
the OS delivers URLs via Apple Events instead of CLI arguments.
- Register qobuzapp scheme in tauri.conf.json (injected into Info.plist)
- Add on_open_url handler for Apple Event URL delivery
- Extract is_qobuz_link() and handle_qobuz_link() helpers to deduplicate
URL matching across single-instance, first-launch, and deep-link paths
- Tighten URL matching from contains() to starts_with() for security
- Log emit errors instead of silently discarding them
mupdf 0.5 had struct size mismatches on Arch Linux (mupdf-sys bindings
incompatible with system MuPDF headers). Upgrading to mupdf 0.6 fixes
the bindings.
- Removed the booklet feature gate — mupdf is now a direct dependency
again, matching the v1.1.19 configuration
- Removed stub implementation (always compile real MuPDF backend)
- sys-lib-libjpeg remains excluded (cross-distro libjpeg incompatibility)
cc
mupdf-sys 0.5.0 bindings are incompatible with the current system
MuPDF library (struct size assertions overflow). Gate mupdf behind
a booklet feature flag (disabled by default). When disabled, booklet
commands return an error instead of crashing the build.
cc
Root cause: Qobuz CDN returns "end of file before message length
reached" for specific tracks - sends Content-Length header then
closes connection without body data. Consistently reproducible for
certain tracks while others from the same album work fine.
Add retry logic at all download paths: on failure, request a fresh
stream URL from the API and retry once (CDN may route to different
edge). Streaming probe failures now fall back to standard download
path instead of failing completely. Also stream response body in
chunks for better partial-read diagnostics.