Commit Graph
100 Commits
Author SHA1 Message Date
vicrodh ca4480239b feat(qbzd): guard all stores/services for remote mode override
When controlling a qbzd daemon, QBZ Desktop becomes a pure remote
control. 119 invoke() call sites across 16 files now check
skipIfRemote() before executing local operations.

Local-only (skipped entirely): castStore, offlineStore,
offlineCacheState, updatesStore, titleBarStore, qobuzLegalStore,
sessionService.

Daemon-routable (guarded, daemon API routing in follow-up):
favoritesStore, albumFavoritesStore, artistFavoritesStore,
artistBlacklistStore, playlistFoldersStore, playbackPreferencesStore,
playbackContextStore.

playbackService: scrobbling, MPRIS metadata, and notifications
all skipped (daemon handles its own).

cc
2026-04-09 14:20:09 -06:00
vicrodh b49fce7662 feat(qbzd): guard all stores/services for remote mode override
cc
2026-04-09 14:19:25 -06:00
vicrodh 98286c6123 fix(qbzd): skip QConnect handoff when controlling daemon via HTTP
When playbackTarget is 'qbzd', the play-track command goes directly
to the daemon HTTP API. Previously, it also went through
handoffPlayTrackToRemoteRenderer (QConnect) which sent play to ALL
connected renderers, causing audio on both local desktop AND remote
daemon simultaneously.

Now: qbzd mode = HTTP API only. QConnect handoff only runs in
local mode. Local player stopped explicitly via invoke before
sending to daemon.

cc
2026-04-09 14:04:38 -06:00
vicrodh 39142aad3b feat(qbzd): proper playback orchestrator with gapless + repeat modes
Replaces naive auto-advance polling with full orchestrator matching
desktop behavior:

Gapless: when player signals gapless_ready (5s before track end),
downloads next track and calls play_next() for seamless transition.
Handles repeat-one (queue same track), defensive same-track check,
and upcoming queue peek (same as desktop's setGaplessGetNextTrackId).

Auto-advance: on track end, advances queue respecting repeat mode.
Repeat-one replays same track. Repeat-all wraps queue. Off stops
at queue end.

Shared download_track() helper used by play-track, play-album, and
orchestrator to avoid code duplication.

cc
2026-04-09 13:51:23 -06:00
vicrodh 30bc325f68 feat(qbzd): auto-advance queue + play-album endpoint
Auto-advance: background task monitors when track ends and
automatically downloads and plays the next track in the queue.
Polls every 500ms. Queue exhaustion logs and stops.

Play-album: POST /api/playback/play-album sets the queue with
all album tracks and starts playing from the specified index.

cc
2026-04-09 13:30:15 -06:00
vicrodh 3a01330b02 fix(ui): banner layout uses CSS variable, fix i18n key, responsive height
Banner inside now-playing-bar grows it to 128px. CSS variable
--player-bar-height drives content-area/main-content calc() so
layout adjusts automatically. No more overlap.

Fix disconnect button text: actions.disconnect -> settings.integrations.disconnect

cc
2026-04-09 13:26:15 -06:00
vicrodh 72254ac44e fix(qbzd): remove token requirement from remote API client
LAN-only mode means no token auth. The empty string token ('') was
failing the truthy check in requireRemote(), causing 'Not in remote
mode' error on every command after connecting.

Now: token header only sent when token is non-empty. requireRemote
only checks type === 'qbzd' and baseUrl presence.

This was THE bug preventing QBZ Desktop from controlling the daemon.

cc
2026-04-09 13:11:26 -06:00
vicrodh 6cf396aa8a fix(qbzd): QConnect bootstrap clears pending between commands
The second command (AskForQueueState) was failing because the
first (JoinSession) was still pending. Now clears pending slot
between commands with 500ms delay, matching desktop behavior.

Also fixed state_handle() Arc lifetime issues.

cc
2026-04-09 12:53:29 -06:00
vicrodh e9fe6b6245 fix(qbzd): QConnect form-encoded createToken + mDNS hostname + response parsing
3 fixes:
1. qws/createToken needs form body (jwt=jwt_qws, user_auth_token_needed,
   strong_auth_needed) not empty body. Was getting 400 Bad Request.
2. mDNS hostname must end with '.local.' for service registration.
3. Response parsing: jwt_qws.endpoint + jwt_qws.jwt, not tokens array.

All 3 patterns copied from working desktop implementation in
qconnect_service.rs.

cc
2026-04-09 12:29:07 -06:00
vicrodh 457c628318 feat(qbzd): implement play-track endpoint (download + play)
POST /api/playback/play-track accepts track_id + quality, downloads
the audio stream from Qobuz, and feeds it to the Player.

This was the missing piece — without this, the daemon could search
and manage queue but couldn't actually play anything.

cc
2026-04-09 12:05:16 -06:00
vicrodh ca9ef961cb fix(qbzd): full QConnect protocol + createToken Content-Length fix
QConnect now implements the full registration protocol:
1. Connect WebSocket
2. Bootstrap: CtrlSrvrJoinSession + AskForQueueState
3. Event loop listening for SESSION_STATE
4. Deferred renderer join with device_info
5. State/volume/quality reports

Fix 411 Length Required on qws/createToken (missing Content-Length: 0).
Fix renderer command handler to execute play/pause/stop/seek/volume.

cc
2026-04-09 12:00:18 -06:00
vicrodh 264302fd8e feat(qbzd): implement all wizard sections (cache, integrations, qconnect)
Cache: interactive RAM detection, L1/L2 size, prefetch config.
Saves to qbzd.toml via save_default_config.

Integrations: ListenBrainz token input, saves directly to V2
cache database. MusicBrainz enable/disable toggle.

QConnect: enable/disable + device name. Saves to qbzd.toml.

All sections read current values as defaults and persist changes.
No more placeholders.

cc
2026-04-09 11:39:22 -06:00
vicrodh c63799309f fix(ci): use native ARM64 runner instead of QEMU for aarch64 build
QEMU emulation crashes compiling ring's C code (SIGSEGV in cc).
Switch to ubuntu-24.04-arm native runner — faster and no emulation
issues. Build time drops from ~40min to ~5min.

cc
2026-04-09 11:19:52 -06:00
vicrodh 7a9f8407a8 fix(qbzd): file-based token fallback when keyring unavailable
On headless systems without gnome-keyring/kwallet, the keyring
fails with ServiceUnknown. Now saves token to ~/.local/share/qbz/.oauth-token
(mode 0600) as fallback. Load also checks file after keyring.

This fixes the 'login works but credentials lost on restart' bug
on headless machines.

cc
2026-04-09 10:57:30 -06:00
vicrodh fd1a4fade1 fix(qbzd): route ALL playback and queue calls through commandRouter
playbackService.ts: v2_play_track and v2_stop_playback now route
to daemon HTTP API when in remote mode.

9 views: bulk queue operations (add_tracks_to_queue, add_tracks_to_queue_next)
now use cmdAddTracksToQueue/cmdAddTracksToQueueNext instead of
direct invoke(). 14 call sites total.

This was the root cause of 'connects but nothing plays' — the
play/queue commands were always going to local Tauri, never to
the remote daemon.

cc
2026-04-09 10:51:20 -06:00
vicrodh e4b6024ade fix(ui): remote indicator sits above player bar, not inside it
Moves the 'Controlling: X' banner from inside the now-playing-bar
to a fixed position above it (bottom: 104px). The player bar
keeps its 104px height unchanged. Main content area shrinks
slightly when the banner is visible.

cc
2026-04-09 09:45:17 -06:00
vicrodh a9282e2091 feat(qbzd): manual daemon connect + persistence + headless login
CastPicker: manual IP:port input for connecting to daemons when
mDNS/avahi is unavailable. Saves recent connections to localStorage
(max 5). One-click reconnect from saved list. Remove button on hover.

Layout: manual input field at top, saved daemons as 'Recent',
mDNS discovered daemons below with scanning indicator.

Login: binds to 0.0.0.0 with auto-detected LAN IP so the OAuth
callback URL works from any browser on the network. 5 min timeout
for remote copy-paste workflow.

i18n: recentDaemons, discoveredDaemons in all 5 locales.

cc
2026-04-09 09:28:01 -06:00
vicrodh c21e87e1c0 fix(qbzd): headless-compatible login with LAN IP detection
qbzd login now works on remote/headless machines:
- Binds callback server to 0.0.0.0 (not 127.0.0.1)
- Detects LAN IP via UDP socket probe for redirect URL
- Always prints URL prominently for copy-paste to any browser
- Browser on same LAN redirects back to daemon's IP:port
- Timeout increased to 300s for remote copy-paste workflow
- open::that still tried but failure is silent (headless)

cc
2026-04-09 09:02:06 -06:00
vicrodh aaa183ce35 feat(ui): add Select All checkbox to multi-select mode in all track views
cc
2026-04-09 08:56:36 -06:00
vicrodh 2802527912 chore(deps): update jsdom 29.0.2, notify-rust 4.14.0
Also closed PR #298 (md-5 0.11) — breaking API change across 9
call sites for zero functionality gain.

cc
2026-04-09 08:38:19 -06:00
Victor RH b196a299dc Merge pull request #300 from vicrodh/dependabot/cargo/src-tauri/pre-release/tauri-plugin-dialog-2.7.0 2026-04-09 08:20:17 -06:00
Victor RH 1bc3eaf759 Merge pull request #299 from vicrodh/dependabot/cargo/src-tauri/pre-release/rodio-0.22.2 2026-04-09 08:20:13 -06:00
Victor RH c1cb47779d Merge pull request #297 from vicrodh/dependabot/npm_and_yarn/pre-release/tauri-apps/plugin-dialog-2.7.0 2026-04-09 08:20:10 -06:00
Victor RH 5c21e7b0d5 Merge pull request #296 from vicrodh/dependabot/npm_and_yarn/pre-release/vite-8.0.8 2026-04-09 08:20:06 -06:00
Victor RH 15c926ea6f Merge pull request #295 from vicrodh/dependabot/cargo/src-tauri/pre-release/tauri-plugin-deep-link-2.4.8 2026-04-09 08:20:02 -06:00
Victor RH 8af186797e Merge pull request #294 from vicrodh/dependabot/npm_and_yarn/pre-release/sveltejs/kit-2.57.0 2026-04-09 08:19:58 -06:00
Victor RH 2374020945 Merge pull request #292 from vicrodh/dependabot/npm_and_yarn/pre-release/svelte-5.55.2 2026-04-09 08:19:53 -06:00
vicrodh dd322a1804 fix(ci): use QEMU native build for aarch64 instead of cross-compile
Ubuntu runners' apt multi-arch is broken for arm64 cross-deps
(security.ubuntu.com 404s for arm64 packages, DEB822 format
sources not handled by sed).

Switch to uraimo/run-on-arch-action: builds natively inside a
Debian Bookworm aarch64 container via QEMU. Slower (~15 min)
but reliable. Binary is native aarch64 with Bookworm's glibc 2.36.

cc
2026-04-09 07:28:48 -06:00
vicrodh 1cba3ec18b fix(ci): force all apt sources to amd64, arm64 from ports only
The sed was only modifying /etc/apt/sources.list but security.ubuntu.com
and other sources in sources.list.d/ were also trying to fetch arm64
packages from repos that don't have them (404).

Now: force [arch=amd64] on ALL .list files, arm64 exclusively from
ports.ubuntu.com.

cc
2026-04-09 07:16:37 -06:00
vicrodh 22a34b4f3e fix(ci): target glibc 2.35 instead of 2.31
glibc 2.31 is too old for system shared libs (libasound, libdbus)
on Ubuntu 22.04 runners which have glibc 2.35. The linker rejects
symbols from libs compiled against newer glibc.

glibc 2.35 covers: Raspberry Pi OS Bookworm (2.36), Ubuntu 22.04+,
Armbian Bookworm, DietPi Bookworm. Bullseye (2.31) is EOL June 2026.

cc
2026-04-09 07:08:22 -06:00
vicrodh 7a522623cf fix(ci): add libdbus-1-dev, fix arm64 apt sources, add strip
x86_64: add libdbus-1-dev for keyring D-Bus dependency
aarch64: fix apt sources with jammy-updates, proper arch restrictions
Both: strip binaries for smaller release size (~23 MB vs 30 MB)

cc
2026-04-09 06:57:12 -06:00
vicrodh c3ce3c6667 fix(ci): drop openssl dependency, use rustls-only for all crates
reqwest default features included native-tls which pulled in
openssl-sys. This broke zigbuild cross-compilation because zig's
C compiler doesn't support the glibc version suffix for openssl
header expansion.

Fix: default-features = false on reqwest in workspace and
qbz-integrations (which had its own reqwest dep with defaults).
Now the entire daemon tree uses rustls-tls exclusively.

cc
2026-04-09 06:48:37 -06:00
vicrodh d042d21622 fix(ci): add libssl-dev + pkg-config to qbzd build
openssl-sys build requires OpenSSL headers even when rustls is the
primary TLS backend (native-tls comes as transitive dependency).

cc
2026-04-09 06:37:11 -06:00
vicrodh 8cee1c60be ci(qbzd): fix workflow paths, add cache, track Cargo.lock
Fixes:
- Rust cache via Swatinem/rust-cache (workspaces: crates -> target)
- Track crates/Cargo.lock for reproducible builds
- Bundle service files in release tarball
- Add permissions: contents: write for release job
- generate_release_notes for auto changelog

Local release build verified: 30 MB (23 MB stripped), x86_64.

cc
2026-04-09 06:22:17 -06:00
vicrodh 586d27bf43 feat(qbzd): integrate daemon discovery into CastPicker
QBZ Daemon tab appears first in the device picker alongside
Chromecast and DLNA. Discovered daemons show name + host:port.

Clicking a daemon:
1. Pings to verify it's reachable
2. Connects via playbackTargetStore
3. Starts SSE event subscription
4. Closes picker

Connected daemon shows in the connected banner with disconnect.
Disconnecting returns to local playback mode.

i18n: noQbzdFound + noQbzdFoundHint in all 5 locales.

cc
2026-04-09 06:08:08 -06:00
vicrodh eade143342 feat(qbzd): TUI setup wizard with ratatui (Phase 1.5)
Interactive menu-based wizard accessible via 'qbzd setup':
- Audio Backend & Device: lists backends, devices, saves selection
- Cache & Resources: instructions for qbzd.toml config
- Integrations: ListenBrainz token setup instructions
- QConnect: config instructions

Full ratatui TUI with keyboard navigation (up/down/enter/q).
Individual sections runnable via 'qbzd setup --section audio'.

Audio section is fully functional: enumerates backends and devices,
saves selection to audio_settings.db.

cc
2026-04-09 05:44:54 -06:00
vicrodh 632b134952 feat(qbzd): full metadata scan using MetadataExtractor
Library scan now extracts metadata (title, artist, album, duration,
sample rate, bit depth) from audio files using qbz-library's
MetadataExtractor. Tracks are inserted into the library database
with full metadata.

MetadataExtractor was already in qbz-library crate — no migration
needed.

cc
2026-04-09 05:42:27 -06:00
vicrodh 90769c0537 ci: add qbzd build workflow (x86_64 + aarch64, glibc 2.31)
Uses cargo-zigbuild to target glibc 2.31 for maximum compatibility:
- Raspberry Pi OS Bullseye and newer
- DietPi, Armbian, Ubuntu 22.04+

Triggered by qbzd-v* tags or manual dispatch.
Produces two binaries: qbzd-linux-x86_64, qbzd-linux-aarch64.
Creates pre-release on tag push.

cc
2026-04-09 05:40:41 -06:00
vicrodh e5539132da feat(qbzd): QConnect headless integration
HeadlessQconnectSink routes QConnect events to the daemon event bus.
Fetches QWS JWT credentials from Qobuz API, connects via WebSocket.

The daemon now registers as a Qobuz Connect renderer — controllable
from the Qobuz mobile app and web player. QConnect starts
automatically after login if enabled in config.

cc
2026-04-09 05:39:34 -06:00
vicrodh b3e5be39eb feat(qbzd): MPRIS metadata from CoreEvent::TrackStarted
Spawns a dedicated event bus listener that updates MPRIS metadata
(title, artist, album, duration) when a new track starts playing.
Clears metadata on logout.

MPRIS now fully functional: media keys (play/pause/next/prev) +
metadata display (track info in system media widget).

cc
2026-04-09 05:35:52 -06:00
vicrodh a5efd6ae19 feat(qbzd): setup subcommand placeholder + config save helper
CLI now shows all 5 subcommands: login, status, token, setup, help.
Setup placeholder ready for TUI wizard implementation (ratatui).
Config save helper for persisting changes to qbzd.toml.

cc
2026-04-09 05:30:12 -06:00
vicrodh e0301b6a29 feat(qbzd): MPRIS playback updates + status subcommand
Playback loop now updates MPRIS state (playing/paused/position)
on every emit cycle so media controls always reflect current state.

qbzd status: queries running daemon via HTTP and prints status.
Shows logged_in state, port, and daemon info.

cc
2026-04-09 05:26:37 -06:00
vicrodh f9d6e551e2 feat(qbzd): LAN CORS, headless MPRIS, security model
LAN-only CORS: allows any origin on local network for qbz-control PWA.

MPRIS via souvlaki (D-Bus, no window): play/pause/toggle/next/prev/stop
media keys work out of the box on any Linux desktop or headless system
with D-Bus. Metadata and playback state updates available via helpers.

Security model finalized:
- LAN: open access, no auth needed (private IP check)
- WAN: completely blocked (403 for non-private IPs)
- Remote: QConnect only (cloud, Qobuz-managed auth)

cc
2026-04-09 05:04:09 -06:00
vicrodh 892560dff4 feat(qbzd): LAN-only access restriction, no token auth
Replaces token-based authentication with LAN-only IP restriction.
Requests from non-private IPs (not 10.x, 172.16.x, 192.168.x,
127.x, link-local, ULA) are rejected with 403.

Security model: LAN = trusted, WAN = blocked.
Remote control outside LAN is exclusively via QConnect (cloud).

cc
2026-04-09 05:00:40 -06:00
vicrodh aacdeabe07 feat(qbzd): login subcommand, library scan, service files (68 endpoints)
qbzd login: interactive OAuth via system browser. Opens browser,
captures callback, exchanges code, saves token to keyring. Same
flow as desktop app. Daemon auto-logins on next start.

Library scan endpoint (POST /api/library/scan): discovers audio
files in registered folders. Full metadata extraction deferred
until MetadataExtractor moves to qbz-library crate.

Service files: systemd, OpenRC, runit + example qbzd.toml config.

cc
2026-04-09 04:52:58 -06:00
vicrodh 42a8ff5665 feat(qbzd): library management + integrations API (67 endpoints)
Library endpoints for standalone operation:
- GET /api/library/albums, artists, album tracks, search, stats
- POST/DELETE /api/library/folders (add/remove scan directories)

Integrations endpoints:
- GET/POST/DELETE /api/integrations/listenbrainz (status, connect, disconnect)
- GET /api/integrations/lastfm (status, setup deferred to TUI wizard)

ListenBrainz connects by writing token directly to the V2 cache
database (same format as desktop app). Last.fm requires OAuth
flow which will be implemented in the TUI wizard.

Total: 67 HTTP endpoints. The daemon is now fully operable
without the desktop app for all core functionality.

cc
2026-04-09 04:46:12 -06:00
vicrodh b524d62318 feat(qbzd): API tiers 2-3 — discover, playlists, favorites, labels, system (55 endpoints)
Tier 2 - Browsing & Management:
- Discover: index, playlists, featured, genres (4 endpoints)
- Favorites: get, add, remove (3)
- Playlists: CRUD + tracks + search (8)
- Extended catalog: artist page, similar, labels, tags (6)

Tier 3 - Server Management:
- System resources: RAM, cache stats (1)
- Cache clear (1)

Total: 55 HTTP endpoints covering playback, queue, search, catalog,
discover, favorites, playlists, audio settings, labels, and system.

cc
2026-04-09 04:41:49 -06:00
vicrodh 8c947c02b4 feat(qbzd): audio settings API + remote settings routing (Phase 5e)
Daemon: 5 new audio settings endpoints:
- GET /api/audio/settings (full settings)
- PATCH /api/audio/settings (partial update)
- GET /api/audio/backends (available backends)
- GET /api/audio/devices?backend= (devices for backend)
- GET /api/audio/hardware-status (current hw state)

Frontend: commandRouter gains audio settings commands that route
to daemon API when in remote mode, local invoke when local.

Total daemon endpoints: 32.

cc
2026-04-09 04:35:49 -06:00
vicrodh 8fc6111782 feat(qbzd): mDNS discovery + registration for LAN pairing
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
2026-04-09 04:30:08 -06:00
vicrodh 8dac185261 feat(qbzd): remote mode indicator in NowPlayingBar
Accent-colored banner shows 'Controlling: {device_name}' with a
disconnect button when the app is targeting a remote qbzd daemon.
Hidden in local mode.

i18n: player.controllingRemote in all 5 locales.

cc
2026-04-09 04:22:00 -06:00
vicrodh e3c1e471c6 feat(qbzd): bifurcate playback and queue stores for remote mode
New commandRouter.ts provides a single bifurcation point for all
playback and queue operations. When playbackTarget is 'qbzd', commands
route to the daemon HTTP API. When 'local', they use Tauri invoke().

Stores updated:
- playerStore.ts: pause, resume, stop, seek, volume, play_track (6 sites)
- queueStore.ts: shuffle, repeat, add, add-next, set, clear (8 sites)

No behavioral change in local mode — invoke() calls are identical.

cc
2026-04-09 04:16:28 -06:00
vicrodh 0d94f21479 feat(qbzd): add remote playback target store and API services
New files for desktop controller mode (Phase 5):
- playbackTargetStore.ts: local/remote mode switching
- remoteApi.ts: HTTP client for daemon API (GET/POST + error handling)
- remoteEvents.ts: SSE client for real-time daemon events

cc
2026-04-09 04:09:24 -06:00
vicrodh 3b8555992e feat(qbzd): HTTP API tier 1 - playback, queue, search, catalog (Phase 4)
27 endpoints covering core remote control functionality:

System (4): ping, info, status, SSE events
Playback (8): get state, play, pause, stop, next, prev, seek, volume
Queue (10): get, set, add, add-next, play-index, remove, move, clear,
  shuffle, repeat
Search (1): unified search (all/albums/tracks/artists via type param)
Catalog (4): album detail, artist detail, track detail, batch tracks

All handlers call QbzCore methods directly (no Tauri dependency).
with_daemon! macro reduces route handler boilerplate.

cc
2026-04-09 03:43:54 -06:00
vicrodh ac17787264 feat(qbzd): unified event bus + SSE endpoint (Phase 3)
Playback event loop polls QbzCore player state with adaptive timing
(250ms playing, 1s paused, 5s idle) and broadcasts PlaybackSnapshot
events to the event bus.

New SSE endpoint at /api/events streams all DaemonEvents to clients.
Broadcast channel bounded to 256 events with lagged client handling.

cc
2026-04-09 03:38:06 -06:00
vicrodh 6ee123c2a4 feat(qbzd): add session lifecycle port (Phase 2)
New session.rs module ports activate_session from Tauri to headless:
- Creates per-user directories (same layout as desktop app)
- Persists last_user_id marker for session restore
- Emits RuntimeEvent::Ready via event bus
- Skips desktop-only stores (tray, visualizer, remote control)

Session activation wired into daemon startup after auto-login.
Audio settings reload deferred until qbz-player crate exposes
reload_settings (currently on legacy player only).

cc
2026-04-09 03:23:56 -06:00
vicrodh 2916dee42c feat(qbzd): wire QbzCore, Player, AudioCache, keyring (Phase 1)
DaemonCore now initializes the real QbzCore with DaemonAdapter,
creates Player with audio settings from shared database, and
attempts auto-login from saved OAuth token in system keyring.

Working features:
- QbzCore init (bundle token extraction from Qobuz web player)
- Player audio thread (ready for playback commands)
- AudioCache with auto-detected size from RAM
- Shared audio settings with desktop app (same SQLite DB)
- Keyring credential lookup (same service as desktop)
- HTTP endpoints: /api/ping, /api/info, /api/status
- Graceful shutdown (SIGTERM/SIGINT -> stop player -> exit)

cc
2026-04-08 21:29:28 -06:00
vicrodh b0ed467664 feat(qbzd): scaffold headless daemon binary (Phase 0)
New workspace member crates/qbzd/ with:
- CLI interface (clap): port, bind, data-dir, config, token, log-level
- Config system (TOML): server, audio, cache, data, mpris, qconnect, mdns
- Resource detection (sysinfo): auto-size cache from available RAM
- DaemonAdapter: FrontendAdapter impl routing CoreEvents to broadcast channel
- DaemonCore: state container replacing Tauri's app.state
- Minimal HTTP server (Axum): /api/ping + /api/info
- Graceful shutdown on SIGTERM/SIGINT

Zero Tauri dependency. Desktop app unaffected.

cc
2026-04-08 21:18:01 -06:00
vicrodh 242b27bb1b refactor: clean up commands_v2/mod.rs
cc
2026-04-08 19:10:23 -06:00
vicrodh 77f12075da refactor: extract commands_v2/diagnostics.rs
cc
2026-04-08 18:07:54 -06:00
vicrodh 8fa11a4317 refactor: extract commands_v2/discovery.rs
cc
2026-04-08 18:05:50 -06:00
vicrodh b0ae328964 refactor: extract commands_v2/image_cache.rs
cc
2026-04-08 18:02:05 -06:00
vicrodh 709c399e31 refactor: extract commands_v2/session.rs
cc
2026-04-08 17:59:33 -06:00
vicrodh 6f1a1c8a22 refactor: extract commands_v2/settings.rs
cc
2026-04-08 17:55:33 -06:00
vicrodh 93154690e3 refactor: extract commands_v2/auth.rs
cc
2026-04-08 17:49:16 -06:00
vicrodh 45ab6c34b6 refactor: extract commands_v2/catalog.rs
cc
2026-04-08 17:46:59 -06:00
vicrodh 5f4cc58d99 refactor: extract commands_v2/playlists.rs
cc
2026-04-08 15:26:06 -06:00
vicrodh c746303f2b refactor: extract commands_v2/audio.rs
cc
2026-04-08 15:23:48 -06:00
vicrodh 98f17019d4 refactor: extract commands_v2/favorites.rs
cc
2026-04-08 15:18:37 -06:00
vicrodh 0303e40f32 refactor: extract commands_v2/search.rs
cc
2026-04-08 15:16:04 -06:00
vicrodh 6c6199a76d refactor: extract commands_v2/queue.rs
cc
2026-04-08 15:11:43 -06:00
vicrodh 02bae5b601 refactor: extract commands_v2/runtime.rs
cc
2026-04-08 15:01:25 -06:00
vicrodh 555139a6e2 refactor: extract commands_v2/playback.rs
cc
2026-04-08 14:44:55 -06:00
vicrodh f4c874fdac refactor: extract commands_v2/link_resolver.rs
cc
2026-04-08 14:30:05 -06:00
vicrodh d667c3e13c refactor: extract commands_v2/integrations.rs
cc
2026-04-08 13:47:46 -06:00
vicrodh 82e1b337a1 refactor: extract commands_v2/helpers.rs
cc
2026-04-08 13:34:51 -06:00
vicrodh d8dbcf7bd7 refactor: extract commands_v2/library.rs
cc
2026-04-08 13:15:43 -06:00
vicrodh ccce564586 refactor: extract commands_v2/legacy_compat.rs
cc
2026-04-08 12:57:08 -06:00
vicrodh 6cc75d091a refactor: convert commands_v2.rs to module directory
Rename commands_v2.rs to commands_v2/mod.rs as the first step
of splitting the 14,635-line file into domain-specific modules.
No functional changes.

cc
2026-04-08 12:34:37 -06:00
vicrodh c56bd12e46 refactor(gapless): flatten local library check from 4 nested ifs
Chain Option combinators to reduce nesting. The await boundary
requires one outer if-let, but the rest flattens into a single
and_then/map/filter pipeline.

cc
2026-04-08 12:20:07 -06:00
vicrodh 7191309f62 Merge remote-tracking branch 'origin/pre-release' into pre-release 2026-04-08 12:14:25 -06:00
Victor RH d6668a5548 Merge pull request #287 from AdamArstall/bugfix/external/local-library-gapless 2026-04-08 12:04:16 -06:00
vicrodh 94949a7f5d fix(qconnect): persist custom device name across restarts
The custom QConnect device name was stored in-memory only,
resetting to "Qbz - hostname" on every app launch.

Now persists to qconnect_settings.db in the global data dir
(~/.local/share/qbz/). Loaded on startup, saved on change.
Fail-open: any db error silently falls back to default.

Fixes #284

cc
2026-04-08 10:40:37 -06:00
vicrodh a30cfa7c71 Merge branch 'bugfix/internal/1.2.5-alsa-lifecycle' into pre-release 2026-04-08 10:29:37 -06:00
vicrodh b6035cb3ad perf(prefetch): increase cache depth to 5 tracks, 2 concurrent
- V2_PREFETCH_COUNT: 2 -> 5 (cache ~2 min ahead)
- V2_MAX_CONCURRENT_PREFETCH: 1 -> 2 (overlap downloads)
- V2_PREFETCH_LOOKAHEAD: 10 -> 15 (find more Qobuz tracks)

Combined with parallel CMAF segments from previous commit,
this keeps the cache well ahead of playback. The sliding
window re-triggers on every track play, so the 5-track
buffer advances continuously through the queue.

cc
2026-04-08 10:27:19 -06:00
vicrodh 438cc18851 perf(cmaf): parallel segment downloads for prefetch cache
Download CMAF segments concurrently (3 slots) instead of
sequentially for prefetch/cache operations. Each slot has a
500ms cooldown after download to stay under CDN rate limits.

This only affects cache prefetch (try_cmaf_full_download),
not real-time streaming which remains sequential for smooth
playback. Segments are reassembled in order after download.

Typical improvement: 10-segment track goes from ~10 serial
HTTP requests to ~4 batches of 3 concurrent downloads.

cc
2026-04-08 09:58:20 -06:00
vicrodh 5aa0df1cbc feat(api): sign all remaining Qobuz API endpoints
Add request_ts + request_sig to every API call matching the mobile
app interceptor pattern. Introduces signed_get/signed_get_auth
helpers and a generic sign_request function.

Endpoints now signed:
- album/get, album/getFeatured
- track/get, track/getList
- artist/get, artist/getSimilarArtists, artist/page, artist/getReleasesGrid
- discover/index, discover/playlists, discover/* browse endpoints
- genre/list
- label/page, label/explore, label/get
- playlist/get, playlist/getUserPlaylists, playlist/create,
  playlist/delete, playlist/addTracks, playlist/deleteTracks,
  playlist/update, playlist/subscribe, playlist/unsubscribe
- playlist/getTags
- favorite/create, favorite/delete

Excluded (pre-auth, cannot sign): user/login, oauth/callback.
Already signed (unchanged): track/getFileUrl, favorite/getUserFavorites,
session/start, file/url, all search endpoints.

cc
2026-04-08 09:41:57 -06:00
vicrodh e62b160d5d feat(login): add cancel buttons and captcha hint to OAuth flows
WebView OAuth: cancel button appears during loading, captcha hint
shows after 25s or when user closes WebView. Hint suggests trying
system browser instead with one-click action.

System browser OAuth: cancel link appears next to loading text,
immediately aborts the wait.

i18n: new keys captchaHint and trySystemBrowser in all 5 locales.

cc
2026-04-08 08:55:02 -06:00
vicrodh 6efdca0ea8 feat(oauth): add cancel commands for both OAuth flows
Add OAuthCancelState with shared Notify for system browser flow.
v2_start_system_browser_oauth uses tokio::select! to race between
code received, 120s timeout, and user cancel signal.

New commands:
- v2_cancel_system_browser_oauth: triggers cancel notify
- v2_cancel_oauth_login: closes all OAuth WebView windows

Timeout reduced from 300s to 120s for system browser flow.

cc
2026-04-08 08:49:51 -06:00
vicrodh 814bf8b968 fix(oauth): detect WebView window close to unblock login wait
When the user closes the OAuth WebView window manually, the
Destroyed event now triggers notify to unblock the 5-minute
timeout immediately, returning oauth_cancelled instead of
hanging until timeout.

cc
2026-04-08 08:37:02 -06:00
vicrodh dbc97d6bd1 feat(search): add request signing to all search endpoints
Add request_ts + request_sig (MD5 signature) to catalog/search,
album/search, track/search, artist/search, and playlist/search
endpoints matching the mobile app authentication pattern.

Migrate v2_search_all from legacy AppState to CoreBridge, removing
the last direct API call that bypassed the V2 crate architecture.

cc
2026-04-08 08:34:32 -06:00
vicrodh fb7c2a440a docs: add NixOS installation instructions, update snap note
Add NixOS/Nix flake install instructions (system-wide + Home Manager).
Update snap note since MPRIS was approved.

cc
2026-04-07 21:46:52 -06:00
vicrodh 59b34132c3 fix(lyrics): use theme accent color for active line highlight
Replace hardcoded purple (#a78bfa / rgb(99,102,241)) with
var(--accent-primary) so the karaoke gradient and glow adapt
to the active theme.

cc
2026-04-07 19:34:00 -06:00
vicrodh 42b429e86d fix(ui): enable multi-select drag in all views
Pass dragTrackIds to TrackRow in album, playlist, favorites, top-q,
weekly suggest, dynamic suggest, and fav-q views. VirtualizedTrackList
also passes selected IDs for multi-drag.

cc
2026-04-07 18:07:11 -06:00
vicrodh a7cdcd6f3d feat(ui): enable track drag & drop in artist and search views
Artist popular tracks and search results now support dragging tracks
onto sidebar playlists. Artist view also supports multi-select drag.

cc
2026-04-07 18:00:29 -06:00
vicrodh e71d634cdf fix(ui): show artist and album in track drag ghost
Single track: title (bold) + artist · album (subtle subtitle).
Multi-select: "N tracks" count as before.

cc
2026-04-07 17:55:39 -06:00
vicrodh 73b0da44ad fix(ui): use compact drag ghost for track drag & drop
Replaces the default full-row drag image with a small semi-transparent
pill showing the track title or "N tracks" count for multi-select.

cc
2026-04-07 17:50:33 -06:00
vicrodh ae8990f4a2 feat(ui): drag & drop tracks onto sidebar playlists
Tracks are now draggable from any view. Dropping onto a playlist in the
sidebar adds them directly via v2_add_tracks_to_playlist. Supports
multi-select: when tracks are selected in playlist view, dragging any
selected track carries all selected IDs.

cc
2026-04-07 17:39:45 -06:00
vicrodh 7faa4e467a fix(audio): default alsa_plugin to Hw when switching to ALSA backend
When selecting ALSA Direct as backend, alsa_plugin was never persisted
to the DB if the user didn't explicitly change it. The UI showed
"hw (Direct Hardware)" as a visual default but the DB stayed NULL,
causing isAlsaDirect to evaluate false and showing a gray DAC badge
instead of the blue HW badge.

cc
2026-04-07 17:20:17 -06:00
vicrodh b5bb2bbe03 fix(player): lock volume slider in immersive player when ALSA Direct hw active
The NowPlayingBar correctly locked the volume slider but the immersive
player (fullscreen/focus mode) was missing the volumeLocked prop chain,
allowing the slider to move visually even though the volume change was
blocked. Respects Qobuz Connect peer renderer override.

cc
2026-04-07 15:18:10 -06:00
vicrodh acd283a1a7 feat(snap): restore mpris slot after snapcraft approval
Adds back the mpris slot with name "qbz" that was removed to unblock
store updates. Now approved per forum request #50654.

cc
2026-04-07 14:35:30 -06:00