Commit Graph
2411 Commits
Author SHA1 Message Date
vicrodh 66f49467f2 fix(purchases): file a downloaded album under its album artist
The download path used the track performer as the artist folder — the
reference did the same — so a Various-Artists compilation landed as one
folder per performer with one track each (fourteen for "Audiophile
Analog Collection Vol.3", smoke 2026-09-01) and the cover only beside
the first track. Use the album artist from the download context for the
folder; the ARTIST tag keeps the performer.

cc
2026-09-01 23:42:14 -06:00
vicrodh d5f4ced66f feat(purchases): entitlement-driven formats and a DSD-safe download path
The purchases listing carries `downloadable_format_ids` per album, and
it is the only honest source for the download menu: a DSD64 purchase
streams as 16/44.1 (`hires:false`), so the catalog-derived ladder
(27/7/6/5) offered a CD FLAC the account never bought and could not
reach the DSD it did. Model the field (plus `hires_purchased`), build
the menu from it highest quality first, and keep the synthesis only as
the fallback for a listing without the field. Fixture from the first
populated capture, redacted.

The download grant is a distinct signed operation: `intent` is part of
the `getFileUrl` preimage, and a DSD entitlement answers 400 to
`intentstream` and 200 to `intentdownload` (measured live). Sign the
purchase grant with `download`, stream the body straight into the
`.part` instead of holding a 650 MB master in memory, take the file
extension from the served container (`audio/x-dsf` -> `.dsf`), verify
its magic before the rename, and leave neither `.part` nor registry row
behind on a truncated or foreign body. Tags stay FLAC/MP3-only.

The tab counters now come from `getUserPurchases?type=...&limit=1`;
`getUserPurchasesIds` counts album tracks as tracks (34 against 0 on
the smoke account). The list card and detail header badge what was
bought: 55/56 ride the hires tier with a DSD64/DSD128 detail.

cc
2026-09-01 23:40:33 -06:00
vicrodh 3c8ab80be5 refactor(purchases): drop the blind-mode disclaimer modal
Purchases is now exercised against an account with real purchases, so
the one-time "built in blind mode" notice no longer describes the
feature. Remove the modal, its controller state, the bridge invokable,
the persisted pref and the three strings from the seven catalogs that
carried them.

cc
2026-09-01 23:40:33 -06:00
vicrodh 1cb988954e fix(qt): rename on-prefixed QML properties and gate the pattern
A property named `on<Upper>` is filed as a signal handler as soon as a
sibling member `<upper>` exists: its initializer never binds, it sits at
the type default, and nothing warns. `PurchasesView.qml` declared
`onAlbums` next to `albums`, so the Albums tab bound `[]` once and never
re-evaluated, which blanked Purchases for every account with real
purchases. Reproduced standalone with the `qml` runtime: the identical
expression under another name reads `true`.

Rename it to `albumsTab` and the four other `on<Upper>` properties
(`collectionsTab`, `surfaceTint`, `accentFloor`, `litColor`) so the rule
can be exact, and add `qml_on_prefixed_property_audit.py` to the four
QML audit sites (qt-run, cargo-test, test-crates, qt-gate-windows).

cc
2026-09-01 23:40:33 -06:00
vicrodh db1ac051f2 fix(local-catalog): prune obsolete generations
cx
2026-09-01 20:12:36 -06:00
vicrodh f6e1c4e023 fix(qconnect): harden queue ownership handoffs
Preserve local playback when it is authoritative, make renderer conflicts explicit and persist the selected policy. Filter unsupported queue rows, restore portable Qobuz identities after disconnect, and fence takeover commands to avoid stale queue and playback loops.

cx
2026-09-01 17:23:14 -06:00
vicrodh 74b0c8e512 fix(qt): correct immersive visuals on Windows
cx
2026-09-01 17:22:50 -06:00
vicrodh d563d9aa63 fix(qt): align the playlist picker footer
cx
2026-09-01 17:22:45 -06:00
vicrodh bb1dfe2d19 fix(connectivity): use the defined Microsoft NCSI probe
cx
2026-09-01 17:22:38 -06:00
vicrodh 53a77791c6 fix(windows): zero the safe-area paddings on Windows too
The Qt 6.9 SafeArea padding neutralizer was gated to macOS in eada3bd12,
which was correct THEN — the Windows ExpandedClientAreaHint arm did not
exist yet. 415cba57d added it without widening the gate, so on Windows
the caption-height safe-area margin auto-padded the contentItem down and
the strip rendered with the window's #1a1a1a background: a dark grey bar
above the QBZ header, pushing the drawn window controls down with it.
The Loader stays imperative, so Linux/Qt 6.8 still never instantiates it.

cc
2026-09-01 07:45:45 -06:00
vicrodh e2e414b56b fix(qt): wrap the split-card title at full size; themed badge on the song card
The cinematic split card's shared shrink-to-fit typeScale is retired: a
long title now word-wraps at its nominal size instead of scaling the
whole metadata block down, and the album/artist lines elide instead of
dictating a reduction. The bottom-right song card's badge also drops the
overAmbient chip — that card only mounts over dark surfaces and the dark
backdrop read as a bigger badge; the themed look was already right.

cc
2026-08-31 16:51:05 -06:00
vicrodh f6574cdb93 fix(linux): present each track notification under a fresh portal id
The track toast published under ONE stable portal id, replacing the
previous notification in place — and Plasma never re-presents a same-id
replacement as a banner, show-as-new hint included (measured on-desktop:
a same-id replacement with the hint produced no popup while a fresh id
bannered every time). So only the first track after boot could ever
toast. Each publish now uses track-now-playing-<generation> and removes
the previously published id first, keeping the no-pileup lifecycle;
withdraw (stop, opt-out, shutdown) removes whichever id was last
published.

cc
2026-08-31 16:38:29 -06:00
vicrodh f64b8480de fix(qt): restore the window position after un-hiding from the tray
Qt keeps the window SIZE across hide()/show(), but the remap is a fresh
mapping and KWin's placement policy re-placed it at the horizontal centre
of the screen. The persisted geometry cannot help (it carries only
width/height + flags), so the position is captured at hideToTray and
re-applied around showFromTray — seeded before the show and re-asserted
once 50ms after the map, since a placement WM can overwrite the hint on
the map itself. Windowed frames only: maximized/fullscreen restore via
their latches and an iconified window's x/y are not trustworthy. The
miniplayer enter/exit pair rides the same verbs and inherits the fix.

cc
2026-08-31 16:06:52 -06:00
vicrodh e41ec15e0e feat(qt): Add to playlist for local albums everywhere
Playlists can be fully local or mixed, so hiding the row on local albums
was wrong — the resolution logic already existed end to end. The quick
view drops its !localAlbum gates (album and track menus) and a new
'playlist' arm hands the overlay's row snapshot to the picker in LOCAL
MODE, fired before close so the generation guard cannot refuse it. The
local album cards and list rows reuse the existing bulk 'album' scope
(group key -> rows -> picker). My QBZ's heterogeneous cells stay out via
an explicit opt-in, since their local ids are container items the bulk
seam cannot resolve.

cc
2026-08-31 16:00:11 -06:00
vicrodh 8e562f4768 feat(qt): album-level Add to playlist on the album card menu
The album ⋯ menus on AlbumView's header and the quick-view overlay
already offered Add to playlist, but the AlbumCard menu (Home, Search,
Artist, Label, feeds) never did. The card only carries the album id, so a
new QbzPlaylistPicker.openForAlbum fetches the album off-thread (the
add_to_mixtape pattern), collects its streamable tracks' catalog ids and
seeds the existing picker. Catalog-gated like the mixtape row.

cc
2026-08-31 15:47:50 -06:00
vicrodh 2e3c765616 feat(qt): polarity-aware ambient veil and weak-ramp hardening for light themes
The ambient legibility scrim was a fixed BLACK layer at ambientDim — right
for dark themes, but over a LIGHT theme it pushed the field to mid
luminance, the worst ground for dark text, and the grey secondary/muted
ramp drowned. Light themes now veil with their own near-white surfaceMain
a notch stronger, with the in-shader (mode 1) and in-atmosphere (mode 2)
darkening turned off there; dark themes keep the exact previous look.
QbzTheme additionally steps textSecondary/textMuted toward textPrimary
while ambient is on under a light theme, so the weak ramp keeps a
contrast floor everywhere without call-site changes.

cc
2026-08-31 15:47:44 -06:00
vicrodh c51df2a061 feat(qt): ambient accent links and larger header in immersive track info
The artist/label/musician links kept the theme accent, which has no
contrast guarantee over the ambient field — over ambient they now use the
AmbientAccent album-palette color. The panel header also reads at a
distance, so its title/album/artist step up to 24/15/20px there; the
desktop modal keeps 16/legal/16.

cc
2026-08-31 15:35:25 -06:00
vicrodh aeeb55198d feat(qt): add minimize and maximize to the immersive window capsule
The window-controls capsule only offered fullscreen and exit; on
platforms without native traffic lights there was no way to minimize or
maximize from immersive. Non-macOS expands to four slots (minus,
square/copy for maximize/restore, fullscreen, X) with the same
expansion-bool gating; macOS keeps the two-slot capsule since the native
controls already cover the rest.

cc
2026-08-31 15:03:25 -06:00
vicrodh 460c0cca40 feat(qt): rework the cinematic split card metadata block
The card's waveform, seek fill, elapsed time and Now Playing indicator
now share the AmbientAccent album-palette color (the local complement
derivation is gone). The Now Playing indicator and the quality badge grow
1.5x (EqualizerBars gains a barScale knob), and the metadata stacks three
lines - title, album, artist - with the shared type scale measuring all
three. The lyrics variant dims its karaoke base like the other immersive
lyric surfaces.

cc
2026-08-31 15:03:20 -06:00
vicrodh 1a4797085a feat(qt): derive immersive accents from the album palette
New theme/AmbientAccent helper: reads ambientPrimary (the album's own
dominant hue) and lifts its lightness into a legible band. The shell's
ambientAccent is the +180-degree complement, which is how a deep-blue
cover ended up with orange chrome; accents now stay in the album palette.
ImmersiveTrackMeta tints its Now Playing indicator with it instead of the
theme accent (no contrast guarantee over ambient), opts its badge into
overAmbient and shadows its text rows for light covers.

cc
2026-08-31 15:03:14 -06:00
vicrodh 9493993124 feat(qt): overAmbient legibility mode for the quality badge and track info
Immersive surfaces sit directly on the ambient field, where theme tokens
have no contrast guarantee — a light cover washed the split Track Info
panel and the quality badges out completely. QualityBadgeFull,
TrackInfoBody, InfoMetaCell and InfoCreditCell gain an opt-in overAmbient
mode: fixed light text with the restrained native shadow the lyrics
surfaces already use, and the badge swaps the theme surface for its own
translucent dark scrim covering only the chip. Defaults stay off, so the
desktop modal and every themed mount are untouched. The immersive Track
Info panel and song card opt in.

cc
2026-08-31 15:03:09 -06:00
vicrodh 68eeb10173 feat(qt): dim the unsung part of the immersive karaoke line
LyricsLineRow gains inactiveColor (default theme.textSecondary, so the
desktop lyrics panel is unchanged) and LyricsLinesView forwards it. The
immersive lyric surfaces pass 35% white: their active overlay is solid
white and the theme's textSecondary base was barely dimmer, so the timed
karaoke fill read as static text.

cc
2026-08-31 15:03:00 -06:00
vicrodh 69e5066d07 fix(qt): wheel-lock sweep across plain-Item modal overlays
Plain-Item overlays get no modal grab, so a MouseArea that swallows
presses still lets the wheel scroll the page underneath (the rule and
its wording live in DiscoverConfigModal.qml). Every scrim and panel
swallow across the modal set now accepts the wheel; the modal Popups
(TrackInfo, CastPicker, AlbumInfo, QconnectDev, LocalMediaInfo) already
had the Overlay grab, and CoverLightbox — a non-modal Popup — gets its
own.

cc
2026-08-31 11:50:32 -06:00
vicrodh bb3a971c0c feat(qt): About for 2.1.0 — codename, contributor sweep, sponsors
Codename becomes 'Rebuild Q (You Can (Not) Redo)'. The contributor list
gains the ten people whose work shipped since the last sweep — merged
PRs (b0bbywan, Ronjar, eldios, herder, PhilipVinc, Mazipani) and the
house rule for adopted work without a merge (pbaart's Dutch locale,
stshow's DLNA strict-renderer handling, RayneGit's Wayland clipboard
feature flag, LuckyTheCoder's Liquid Glass icon from issue #712) — and
the README list is now identical to the modal's.

New Sponsors section: public GitHub sponsors (linked chips) then Ko-fi
supporters by their public display names, in a fixed three-row strip
that scrolls itself, closed by the translated anonymous-sponsors line.
The panel grows to 920x860 and the modal now eats the wheel on scrim
and panel.

cc
2026-08-31 11:50:32 -06:00
vicrodh 4f065eb45f chore(release): bump workspace version to 2.1.0
Workspace version + lock, and the metainfo gains its 2.1.0 release entry
(the release workflow re-stamps version and date at tag time). flake.nix
and the Flathub manifest are post-tag and stay untouched; the snap-qt
recipe stamps its version from the tag.

cc
2026-08-31 11:39:20 -06:00
vicrodh ad4d48eeef feat(qt): one-time blind-mode disclaimer on Purchases
The WindowsDisclaimerModal mechanism on the purchases pref seam: a
purchases_disclaimer_seen key seeded on entry, a disclaimerVisible field
in the list document, and an OK that persists the dismissal for good. The
overlay uses the DiscoverConfigModal wheel-lock shape; the scrim does not
dismiss, so a stray click can never acknowledge it. Body ships as msgids
in all locales with the Star Wars line kept in English everywhere.

cc
2026-08-31 11:35:45 -06:00
vicrodh 5e6daf0e81 feat(qt): Open containing album on cortinilla track rows
CortRow gains an albumId — the Qobuz album id for catalog tracks, the
album group key for local ones (both are what crate::open_album routes),
empty elsewhere. The track menu gates its new entry on the field, the
action funnels through cortinillaMenuAction like its siblings, and the
new msgid ships in the seven non-English locales.

cc
2026-08-31 11:26:17 -06:00
vicrodh 7f702f118d feat(qt): edit context menu on the header search input
QML TextInput has no system edit menu; a RightButton overlay opens a
CardMenu with Cut / Copy / Paste / Select all. Menu cut/paste are
programmatic edits that never emit textEdited, so the live-query hook is
factored out and re-run after them. New scissors/clipboard glyphs baked
across the six tints; Cut and Paste msgids added to the seven non-English
locales.

cc
2026-08-31 11:26:12 -06:00
vicrodh 712a0d1903 feat(qt): focus-driven cortinilla lifetime
Drop the Slint-inherited 4.5s/30s idle auto-close (and its hover-latch
machinery): while the header search field keeps focus the dropdown stays
open, like a combobox. What closes it is explicit — the click-outside
scrims (which now also hand the keyboard to the shell root, the same
duck-walk Escape uses), Escape, activating a row, or a page change. One
timer fewer, per the repaint-pulse doctrine.

cc
2026-08-31 11:25:57 -06:00
vicrodh de4c4b487f feat(qt): Create Artist Collection on the artist card menu
Same Discography Builder seam ArtistView's overflow uses; QbzDisco.open
takes the artist id, so no open view is required.

cc
2026-08-31 11:12:58 -06:00
vicrodh d3dc801bc1 feat(qt): mixtape and offline actions on the playlist card menu
Add to mixtape opens the MyQBZ picker with the call-site payload idiom
(trackCount omitted: the card contract has no member count and the store
persists the value); Make available offline runs the shared playlist
cache preflight. The card only mounts on Qobuz playlist rows, so neither
entry needs a gate.

cc
2026-08-31 11:12:58 -06:00
vicrodh 3b413f4b81 feat(qt): mixtape and offline actions on the album card menu
Add to mixtape and Make available offline (label swaps to Refresh offline
copy on a fully cached aggregate), gated on catalogAffordances — the
catalog-id guarantee both by-id invokables need — and refused for pulled
albums, matching the PopularTrackRow refusals.

cc
2026-08-31 11:12:58 -06:00
vicrodh 6cdbd12bd0 feat(qt): Play all later in the Popular Tracks group menu
The section-level menu offered Play all next and Add all to queue with no
block-tail option; enqueue_track_list_mode already carries the "later"
arm, so only the entry was missing. New msgid translated in the seven
non-English locales.

cc
2026-08-31 11:09:53 -06:00
vicrodh c2d5a30bfa feat(qt): Play later in the album overflow and quick-view menus
AlbumView's album menu gains the row and stops mapping its append action
to mode "later" (that aliasing only worked while the backend collapsed
the two). AlbumQuickView drops the local-only gate on Play later now that
catalog albums own a real block-tail insert.

cc
2026-08-31 11:09:48 -06:00
vicrodh f51a6e4e81 feat(qt): real block-tail arm for album enqueue
enqueue_album collapsed "later" into a plain append, so the album-level
"Play later" rows did exactly what "Add to queue" does. Route the mode
verbatim to the peer arm (which always had a later batch path) and feed
add_track_later locally, matching enqueue_track_list_mode and the local
enqueue_rows funnel.

cc
2026-08-31 11:09:48 -06:00
mazipani 658524c404 fix(cast): accept X.509 v1 device certificates in TLS handshake
Connecting to Cast devices failed with:
Connection error: invalid peer certificate: Other(OtherError(UnsupportedCertVersion))

Many Cast devices (notably AV receivers with Chromecast built-in) serve a
self-signed X.509 version 1 certificate on port 8009. rust_cast 0.21's
NoCertificateVerification verifier accepts any certificate in
verify_server_cert, but its signature checks delegate to rustls helpers
that parse the certificate with webpki, which only supports version 3
certificates. The handshake therefore aborts for v1 device certificates;
this affects every rustls-based rust_cast release (0.19+) and only devices
happening to serve v3 certificates can connect.

Vendors rust_cast 0.21.0 under crates/vendor with a minimal patch: the
verifier's verify_tls12_signature/verify_tls13_signature now assert
validity, matching the verifier's no-validation contract and the rustls
danger verifier examples. Wired up via [patch.crates-io]; see
crates/vendor/rust_cast/QBZ_PATCH.md for the full analysis and how to drop
the patch once upstream releases the fix.

Verified against a real device serving a v1 certificate (TLS 1.2,
ECDHE-RSA-CHACHA20-POLY1305): handshake, receiver channel and heartbeat
now establish; unpatched fails at the first channel write. cargo
check/test -p qbz-cast pass (13 tests).

Refs: rustls/rustls#1298, rustls/webpki#29, #439
2026-08-31 14:50:01 +02:00
vicrodh bb1b1a5648 merge: library favorites freshness and reco cleanup
cc
2026-08-31 03:35:43 -06:00
vicrodh 4c4647bdf4 merge: add-to-playlist redesign — membership index, picker, local drag
cc
2026-08-31 03:35:35 -06:00
vicrodh c37768459a merge: library explorer collapse tooltip and filter strip
cc

# Conflicts:
#	crates/qbz-i18n/translations/de/LC_MESSAGES/qbz-ui.po
#	crates/qbz-i18n/translations/en/LC_MESSAGES/qbz-ui.po
#	crates/qbz-i18n/translations/es/LC_MESSAGES/qbz-ui.po
#	crates/qbz-i18n/translations/fr/LC_MESSAGES/qbz-ui.po
#	crates/qbz-i18n/translations/ja/LC_MESSAGES/qbz-ui.po
#	crates/qbz-i18n/translations/nl/LC_MESSAGES/qbz-ui.po
#	crates/qbz-i18n/translations/pt/LC_MESSAGES/qbz-ui.po
#	crates/qbz-i18n/translations/ru/LC_MESSAGES/qbz-ui.po
2026-08-31 03:35:35 -06:00
vicrodh 0c874404b6 fix(qt): explain blocked QConnect logout
Keep fail-closed teardown semantics while telling the user how to recover when logout cannot proceed safely.

cx
2026-08-31 03:26:43 -06:00
vicrodh a9046df218 fix(qconnect-lan): harden discovery compatibility
Advertise every eligible IPv4 interface and tolerate future handoff fields while retaining canonical-field validation.

cx
2026-08-31 03:26:24 -06:00
vicrodh 2e55f01e26 refactor(qconnect): share delegation lifecycle
Move authority fences, preflight, rejoin watchdogs, LAN projection, and physical runtime teardown into qconnect-app for both Qt and qbzd.

cx
2026-08-31 03:26:01 -06:00
vicrodh de289bbf46 fix(qconnect): align buffering state reports
Preserve playing intent while buffering and emit ready only at initial decode or stall recovery across both adapters.

cx
2026-08-31 03:25:11 -06:00
vicrodh 94904f70c6 fix(qconnect): follow official renderer authority
Reproduce the server-authored shuffle order, defer renderer detach to topology, and project remote volume changes into the Qt UI.

cx
2026-08-31 03:23:49 -06:00
vicrodh aed82733ef fix(cast): serialize renderer ownership handoffs
cx
2026-08-30 22:22:57 -06:00
vicrodh 9d84f0c384 feat(qt): harden qconnect ownership lifecycle
cx
2026-08-30 22:22:48 -06:00
vicrodh c927eb9c9c feat(qbzd): harden qconnect ownership lifecycle
cx
2026-08-30 22:22:39 -06:00
vicrodh d3cf7ae53d chore(qconnect): wire LAN runtime dependencies
cx
2026-08-30 22:22:31 -06:00
vicrodh b4404f45ba fix(qconnect): enforce shared lifecycle authority
cx
2026-08-30 22:22:08 -06:00
vicrodh 0740a7368f chore(reco): drop the unused rand dependency
Declared since the identity-guardrail round, never used; verified by
grep and a clean build without it.

cc
2026-08-30 22:17:41 -06:00