Keep search, settings and link shortcuts usable while editing, dismiss focus on outside clicks and navigation, and guard deferred focus against hidden inputs. Add Qt Quick regression coverage.
Refs #753
cx
Handle explicit MPRIS Play/Pause without toggling paused playback, remove the 10,000-favorite cutoff, and sort albums by original release dates. Isolate Library filters by tab, include purchased release dates, and support server-ranked artist popularity with safe pagination.
Validated with 766 Qt tests, QML checks, release-sort regressions, and a release build. Refs #750, #751.
cx
Integrate the combined build containing the remote album ID correction and SACD artwork sidecars. The KEF DLNA fix remains on its separate branch pending reporter feedback.
cx
Refresh missing artwork for unchanged SACD images when a folder cover is added after scanning. Enable image-specific metadata and cover sidecars in the Qt editor while preserving physical track numbers and never writing the ISO.
Validated with SACD regressions, the real Heathen image, 13 Qt editor tests, QML audits and the combined release build.
cx
Keep orphan Jellyfin and Subsonic tracks indexed without exposing internal grouping keys as server album IDs. Align album cards, artist counts and track navigation, and rebuild derived catalogs with schema 8.
Regression fails on the previous projection (three cards for one real album) and passes with the fix for both providers. Catalog suite: 45 passed, 2 ignored scale benchmarks.
cx
Use collection artwork when Jellyfin or Subsonic tracks have no image, preserving existing track artwork. Rebuild derived catalogs so existing libraries receive the correction.
cx
Keep http 1.5 and tolerate empty or relative service URLs in a local rupnp patch. Cover standard DLNA SOAP requests and malformed descriptions.
Avoid logger locks in fatal handlers, flush emitted log batches, and preserve CI symbols after smoke failures.
cx
The packaged AppDir has an intermittent SIGSEGV during startup, in the
window between QGuiApplication construction and the QML engine coming up,
with the tokio workers, the audio thread, the loudness analyzer, the
visualizer producer and connectivity all running beside it. It hit twice
on 2026-09-08 on unrelated commits: the v2.1.0 tag's own release-flatpak
attempt 1, which then passed on a re-run of the identical commit, and the
#745 bugfix branch. Both left the same evidence - "status -11" and a log
that stops mid-startup - which is nothing to act on, and a user who hits
this has no re-run to reach for.
The process now reports its own death. A handler for SIGSEGV, SIGBUS,
SIGILL, SIGFPE and SIGABRT writes the signal, the faulting address, the
thread and a backtrace to stderr and flushes the file sink, so the next
occurrence names a frame instead of costing another round trip. It runs
on a dedicated signal stack so a stack-overflow fault can still report,
latches against re-entry, and arms alarm() BEFORE capturing anything:
taking a backtrace allocates and takes loader locks, so a fault inside
malloc could otherwise deadlock the handler and turn a crash into a hang.
The signal is re-raised with the default disposition restored, so the
wait status stays the signal and CI, the smoke harness and any core dump
still see a crash. The integration test asserts exactly that pair on a
child segfaulted on purpose, and it is a named gate in test-crates and
its local mirror.
qbzd installs it too: a headless daemon that dies of a signal leaves even
less behind than the GUI.
cc
qbz_log::install rotates qbz.log to qbz.log.prev and creates a fresh
file. The internal preflight children (QBZ_INTERNAL_AUTO_PREFLIGHT_CHILD,
QBZ_INTERNAL_GPU_PREFLIGHT_CHILD) re-enter main and ran it too, so a
child renamed the LIVE log out from under the parent, which kept writing
to the renamed inode. On Linux that happens on every launch: qbz.log
ended at the child's own first two lines while the whole session went to
qbz.log.prev, unread. Reported in #749, where the second of those two
lines ("explicit QT_SCALE_FACTOR preserved") was the child inheriting the
variable the parent had just set, not a user setting.
main now reads the child env markers before installing the logger and
takes install_without_file_sink, which keeps stderr and the ring. A child
loses nothing: the parent already captures its stdout proof line and logs
its output tail on failure. The in-app diagnostics export reads the ring
and was never affected.
Second half of the same problem: the file sink is an 8 KiB BufWriter that
only flushed on exit or on a repeat summary, so a process force-killed
while wedged - the state every hang report is filed from - lost the last
and most interesting lines. It now flushes on WARN or worse and otherwise
at most every 200 ms, which bounds the loss without turning a log burst
into one write syscall per record.
Tests cover the rotation contract (a second open renames the live file
away) and the flush policy (bounded window, urgent records never wait).
cc
rupnp parses a device description's SCPDURL / controlURL / eventSubURL as
http::uri::PathAndQuery. http 1.5.0 rewrote scan_path_and_query and now
rejects two shapes 1.4.0 accepted: an empty element (ErrorKind::Empty,
printed as "empty string") and any relative URL without a leading slash
(PathDoesNotStartWithSlash). Both are common in old UPnP firmware, so the
transitive bump made rupnp drop those renderers during discovery.
Measured in the lockfiles: v2.0.2 resolved http 1.4.0, v2.1.0 resolved
1.5.0, and nothing in qbz-cast changed between them. A KEF LSX gen 1 that
worked in 2.0.2 reported "Visible Renderers: 0" in 2.1.0 with
"Invalid response: empty string", which is exactly
InvalidResponse(InvalidUri(Empty)) reproduced by the new test.
qbz-cast takes http as a direct dependency at "=1.4.0" so cargo unifies
the workspace on it, and device.rs now uses that crate instead of the
rupnp re-export. The new integration test serves two renderer
descriptions over loopback, one with empty and one with relative service
sub-URLs, and requires both to parse: it is red on 1.5.0 and green on
1.4.0, so it fails loudly if the pin is ever lifted. test-crates and its
local mirror gain the named gate.
Closes-when-confirmed: #745
cc
The flake shipped with srcHash = lib.fakeHash, so consuming it with
inputs.qbz.url = "github:vicrodh/qbz" failed to evaluate. Set the
published v2.1.0 tag hash (verified with nix-prefetch-url --unpack).
Closes#744.
cc
The flake shipped with srcHash = lib.fakeHash, so consuming it with
inputs.qbz.url = "github:vicrodh/qbz" failed to evaluate. Set the
published v2.1.0 tag hash (verified with nix-prefetch-url --unpack).
Closes#744.
cc
The qbz/qbzd source builds set only !lto; makepkg then injects -C
debuginfo=2 for its debug package, which inflates the final rustc link
enough to trip the OOM killer on the qbz-qt binary. Add !debug so the
source packages link within a normal builder's memory. Verified by
clean container builds of qbz and qbzd 2.1.0.
cc
Two release-pipeline fixes surfaced by the 2.1.0 tag:
- release-linux-aarch64: the GUI and qbzd tarballs (and their internal dirs)
were named with DEB_ARCH (arm64), but every source consumer — release-aur,
the Gentoo binary ebuilds, the Flathub manifest — fetches them as aarch64,
which matches the rpm/AppImage suffix. They 404'd on the tag and needed a
manual re-upload. Name the tarballs and dirs with ARCH (aarch64); the .deb
files keep DEB_ARCH (arm64), the Debian convention.
- release-gentoo: the metadata validation step calls xmllint, which is not on
the runner by default; install libxml2-utils before using it.
cc
The qbz/qbzd source builds set only !lto; makepkg then injects -C
debuginfo=2 for its debug package, which inflates the final rustc link
enough to trip the OOM killer on the qbz-qt binary. Add !debug so the
source packages link within a normal builder's memory. Verified by
clean container builds of qbz and qbzd 2.1.0.
cc
Two release-pipeline fixes surfaced by the 2.1.0 tag:
- release-linux-aarch64: the GUI and qbzd tarballs (and their internal dirs)
were named with DEB_ARCH (arm64), but every source consumer — release-aur,
the Gentoo binary ebuilds, the Flathub manifest — fetches them as aarch64,
which matches the rpm/AppImage suffix. They 404'd on the tag and needed a
manual re-upload. Name the tarballs and dirs with ARCH (aarch64); the .deb
files keep DEB_ARCH (arm64), the Debian convention.
- release-gentoo: the metadata validation step calls xmllint, which is not on
the runner by default; install libxml2-utils before using it.
cc
Merge the Qt frontend release (pre-release) into main for 2.1.0.
Conflicts resolved by taking pre-release's versions, verified equivalent:
- .github/workflows/release-windows.yml: main's copy was a verbatim older
snapshot ('No behavior change'); pre-release carries the current workflow.
- README.md: pre-release's Qt rewrite already covers main's Aug-31 additions
(native/not-a-web-player, bit-perfect DAC, no EQ/DSP, TDL/SPTFY/DZR, offline
locks) in its dedicated 'What QBZ is and what it is not' section.
cc
# Conflicts:
# .github/workflows/release-windows.yml
# README.md
Release copy for 2.1.0 "Rebuild Q (You Can (Not) Redo)": the feature inventory
that feeds the Discussion and the tag details rendered by the website and the
in-app About modal. The Discussion link points to discussions/743; the h1
codename matches the About modal literal (about_qt.rs CODENAME).
cc
A mixed Qobuz playlist whose Plex (or Jellyfin/Subsonic) sidecar track no
longer resolves renders an "Unavailable track" row, and "Remove from playlist"
from the row menu did nothing:
playlist remove: no plex key for row plex:47032
playlist remove: non-numeric row id plex:47032
remove_sidecar_row resolved the ref ONLY through the live queue
(local_picker_ref_for_row), but an unavailable row has no queue entry, so the
lookup returned None, the plex arm bailed, and the fallthrough tried to parse
the "plex:<key>" display id as a Qobuz u64 and failed. Nothing was removed —
and it must not depend on Plex being reachable, since the removal is a local
SQLite delete.
Derive the removal target from the display id itself, which already encodes
"<source>:<reference>", falling back to it when the queue ref is absent. This
also closes the same gap for Jellyfin/Subsonic sidecars (previously unhandled),
via remove_remote_track_from_playlist. Pure helper `sidecar_removal` with unit
tests covering resolved, unavailable-plex, unavailable-remote, local and Qobuz
rows.
cc
os.exec* is emulated on Windows (spawn + terminate the caller) and crashed
the release-windows build: it died with "Segmentation fault ... exit code
139" immediately after the wrapper printed the SDK fingerprint, before cargo
produced any output. The POSIX Linux and macOS builds using the same wrapper
were unaffected, which localised it to the Windows exec emulation.
Spawn cargo with subprocess.run and propagate its exit code. Portable across
all three targets; cargo output still streams through the inherited stdio and
CXXFLAGS/QMAKE plumbing is unchanged. Verified locally: the wrapper reaches
cargo and returns its status.
cc
The Up Next and History delegates in KioskNowPlaying both declared
`id: queueRow`. qmlcachegen and the runtime tolerate the collision, but
qmllint fails to resolve the inline QueueRowLite type in the second delegate
and the pre-release qt gate rejected it. Give the history delegate its own id.
cc
Address the owner's kiosk feedback on top of the release hardening:
- Chrome: the back bar is a drag surface and shrank to 44px with one shared
button form for Back/Forward/Connect/Cast; Qobuz Connect and Cast are a
permanent cluster in the bar; the dead Share-logs button left the kiosk
Settings header.
- Transport bar: the hidden overflow menu is gone; Volume is always visible
with a vertical sheet, and Desktop mode has its own button.
- Now Playing: one centred transport row (New-bar order); lyrics are centred
(shared LyricsLinesView gains an opt-in centered, desktop default false).
- Local Library: the media funnel and an Open (ephemeral) entry point reach
the kiosk; Artists is its own multi-column round-card grid (kiosk-only, the
desktop LocalArtistsTab is untouched); the Artists drill-down query no
longer hangs off a stale derived guard.
- Search: the All-tab dashboard shelves render again — they were mounted in a
Loader Component that read the delegate id under reuse and bound an empty
model; they are direct children now.
- Album detail: icon actions the height of the quality badge plus play next /
later / queue, an inline floating quality badge, and zebra rows; both album
headers show a quality badge.
- Immersive: a quiet auto-hiding kiosk exit moved to the bottom, clear of the
window controls.
Regression coverage in scripts/test_qt_kiosk_feedback.mjs (wired into the
runtime gate). Verified on a private authenticated Xvfb; QML audits 6/6,
Node gate 5/5, release build 0 warnings.
cc
Integrate the Kiosk profile hardening round: 12 dedicated Kiosk views plus
22 shared opt-in hosts, shared navigation history with Back/Forward, Albums
as the default library tab, native paged Local Library (artists/tracks),
lightweight MyQBZ grid and detail, Ephemeral lifecycle, bounded lists and
artwork sized to the physical frame, static skeletons, 64px transport bar,
Settings touch reflow and a persistent Immersive exit.
Desktop keeps its prior behavior: every shared component defaults to the
desktop path and only opts into Kiosk behavior through explicit properties
(origin-relative ScrollMemory, viewport-bounded artwork requests).
Regression coverage: kiosk navigation and artwork Node tests added to the
runtime gate, scroll-origin fixtures for fixed and grouped rows, i18n
strings for all eight locales.
Verified by 750 release tests, six QML audits, Kiosk smokes at 800x480 and
1280x800 and a Desktop A/B with seven pixel-identical captures.
cc
Add the Windows experimental badge and source build prerequisites for Arch, Gentoo, NixOS and Windows. Clarify how to build against an installed Windows Qt SDK and invite help with documentation.
cx
Observe process survival through the full smoke window, isolate profiles and the session bus, restore hidden SDKs and reap private process groups. Reuse the Qt log gate and retain smoke evidence on failures. Add subprocess regressions to the test-crates gate.
cx
Translate the project statement, document the implemented offline grace window, and link the bundled DST provenance and license notices. Align the release codename, ARM64 requirements and AppStream date.
cx
Detect sector layout from Master TOC signatures and strip physical framing while preserving logical addressing, bounds and file mutation checks. Add reader and scanner regressions with synchronized CI presence guards.
Verified full decoding of 53 tracks across five real images, seek and previous-reader comparisons, isolated release UI opening and scanning, and disc, DSD, library and Qt tests.
cx
Publish the open sequence after the active session and its document on the Qt thread. Keep boot restore and metadata updates from requesting navigation.
cx
Retain artist IDs in new history snapshots and enable artist links in the carousel and View all. Route local sources to Local Library Artists and resolve older Qobuz snapshots through the existing album lookup.
cx
Record the outgoing tab state and scroll before switching, restore same-view history entries, and route sidebar tab requests through the same path without duplicating the initial landing. Add shared CI and local regressions for source filtering and tab transitions.
cx
Filter physical album variants in the compatibility detail view and cancel stale native queries and unfiltered retries. Load album rows when switching out of the native model.
cx
Carry transport failure into Qt startup with a process-local fail-fast bus address. Preserve a responsive bus when remote forwarding fails, and retry the original session on the next launch. Add the silent-bus xcb release regression to the shared CI/local runtime gate.
cx
Classify one-bit queue metadata as DSD before PCM formatting. Display older misclassified snapshots as DSD64, DSD128 and higher multiples without rewriting the stored history, and pass the detail to both carousel and full-page cards.
Verify native local-track to queue metadata and old snapshots in the passing 741-test Qt suite. Audio behavior is unchanged.
cx
Use one mapper for the carousel and full history page. Carry the stored routing source and normalized badge vocabulary through both surfaces, including existing rows, downloads and media servers.
Cover existing snapshots and catalog aliases in the Qt suite; all 741 tests pass with temporary XDG data.
cx
Save the local album identifier and active filter in the account navigation file at clean shutdown. Restore it on remembered startup, preserve launcher link and crash recovery precedence, and fall back to Local Library when the album is unavailable.
Cover profile isolation, a later Home exit, invalid context and startup policy with persistence fixtures. All 738 Qt tests pass with a temporary XDG profile.
cx