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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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