Commit Graph
3635 Commits
Author SHA1 Message Date
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
dependabot[bot] e6bacb7507 chore(deps): bump tauri-plugin-dialog from 2.6.0 to 2.7.0 in /src-tauri
Bumps [tauri-plugin-dialog](https://github.com/tauri-apps/plugins-workspace) from 2.6.0 to 2.7.0.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](https://github.com/tauri-apps/plugins-workspace/compare/log-v2.6.0...log-v2.7.0)

---
updated-dependencies:
- dependency-name: tauri-plugin-dialog
  dependency-version: 2.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-09 05:37:52 +00:00
dependabot[bot] 608f9508ef chore(deps): bump rodio from 0.22.1 to 0.22.2 in /src-tauri
Bumps [rodio](https://github.com/RustAudio/rodio) from 0.22.1 to 0.22.2.
- [Changelog](https://github.com/RustAudio/rodio/blob/master/CHANGELOG.md)
- [Commits](https://github.com/RustAudio/rodio/compare/v0.22.1...v0.22.2)

---
updated-dependencies:
- dependency-name: rodio
  dependency-version: 0.22.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-09 05:37:36 +00:00
dependabot[bot] 80886b8948 chore(deps): bump @tauri-apps/plugin-dialog from 2.6.0 to 2.7.0
Bumps [@tauri-apps/plugin-dialog](https://github.com/tauri-apps/plugins-workspace) from 2.6.0 to 2.7.0.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](https://github.com/tauri-apps/plugins-workspace/compare/log-v2.6.0...log-v2.7.0)

---
updated-dependencies:
- dependency-name: "@tauri-apps/plugin-dialog"
  dependency-version: 2.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-09 05:37:12 +00:00
dependabot[bot] 6d40aacfa6 chore(deps-dev): bump vite from 8.0.5 to 8.0.8
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 8.0.5 to 8.0.8.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v8.0.8/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 8.0.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-09 05:37:03 +00:00
dependabot[bot] b2527d829b chore(deps): bump tauri-plugin-deep-link in /src-tauri
Bumps [tauri-plugin-deep-link](https://github.com/tauri-apps/plugins-workspace) from 2.4.7 to 2.4.8.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](https://github.com/tauri-apps/plugins-workspace/compare/deep-link-v2.4.7...deep-link-v2.4.8)

---
updated-dependencies:
- dependency-name: tauri-plugin-deep-link
  dependency-version: 2.4.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-09 05:36:58 +00:00
dependabot[bot] e2a4e8307c chore(deps-dev): bump @sveltejs/kit from 2.55.0 to 2.57.0
Bumps [@sveltejs/kit](https://github.com/sveltejs/kit/tree/HEAD/packages/kit) from 2.55.0 to 2.57.0.
- [Release notes](https://github.com/sveltejs/kit/releases)
- [Changelog](https://github.com/sveltejs/kit/blob/main/packages/kit/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/kit/commits/@sveltejs/kit@2.57.0/packages/kit)

---
updated-dependencies:
- dependency-name: "@sveltejs/kit"
  dependency-version: 2.57.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-09 05:36:54 +00:00
dependabot[bot] 2817ed6fba chore(deps-dev): bump svelte from 5.55.1 to 5.55.2
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.55.1 to 5.55.2.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.55.2/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.55.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-09 05:36:38 +00: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
AdamArstall 56ec473e14 Merge remote-tracking branch 'upstream/pre-release' into bugfix/external/local-library-gapless 2026-04-08 17:21:24 +01: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