Files
qbz/README.md
T
vicrodh 47db92ff92 chore: remove qbzd headless daemon and its client integration
qbzd was a premature daemon experiment that never shipped (gated off
behind QBZD_ENABLED, no user access). Its ratatui 0.29 dependency pinned
unicode-width =0.2.0, which conflicts with slint's annotate-snippets in a
shared workspace. Remove it entirely; it will be reimplemented from
scratch later on the clean, UI-agnostic shell.

- Delete crates/qbzd daemon crate + workspace member (drops ratatui,
  crossterm and the other qbzd-only deps from the lock).
- Delete src-tauri qbzd_discovery (mDNS) + its commands/state wiring in
  lib.rs; drop the direct mdns-sd dep (Chromecast keeps its own via
  qbz-cast).
- Delete remoteApi/remoteEvents/playbackTargetStore; neutralize
  commandRouter to local-only dispatch (isRemote/skipIfRemote are now
  always-false no-ops so the ~100 guard call-sites keep compiling).
- Strip qbzd branches from playbackService and the remote banner from
  NowPlayingBar; excise qbzd from CastPicker (Chromecast/DLNA kept).
- Remove qbzd i18n keys (5 locales), README daemon node, gitignore note.

cargo check (crates + src-tauri) and svelte-check all green.

cc
2026-05-25 15:08:14 -06:00

435 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<p align="center">
<img src="static/logo.png" alt="QBZ logo" width="180" />
</p>
<p align="center">
<a href="https://github.com/vicrodh/qbz"><img src="https://img.shields.io/badge/github-vicrodh%2Fqbz-0b0b0b?style=flat-square&logo=github" alt="GitHub repo" /></a>
<a href="https://github.com/vicrodh/qbz/releases"><img src="https://img.shields.io/github/v/release/vicrodh/qbz?style=flat-square" alt="Release" /></a>
<a href="https://aur.archlinux.org/packages/qbz-bin"><img src="https://img.shields.io/aur/version/qbz-bin?style=flat-square&logo=archlinux" alt="AUR" /></a>
<a href="https://snapcraft.io/qbz-player"><img src="https://img.shields.io/badge/snap-qbz--player-0b0b0b?style=flat-square&logo=snapcraft" alt="Snap" /></a>
<a href="https://flathub.org/apps/com.blitzfc.qbz"><img src="https://img.shields.io/flathub/v/com.blitzfc.qbz?style=flat-square&logo=flathub" alt="Flathub" /></a>
<a href="https://github.com/vicrodh/qbz"><img src="https://img.shields.io/github/license/vicrodh/qbz?style=flat-square" alt="License" /></a>
<a href="https://github.com/vicrodh/qbz"><img src="https://img.shields.io/badge/platform-Linux-0b0b0b?style=flat-square&logo=linux" alt="Platform" /></a>
<a href="https://github.com/vicrodh/qbz"><img src="https://img.shields.io/badge/macOS-experimental-0b0b0b?style=flat-square&logo=apple" alt="macOS (experimental)" /></a>
</p>
<p align="center">
<a href="https://techforpalestine.org/learn-more"><img src="https://raw.githubusercontent.com/Safouene1/support-palestine-banner/master/StandWithPalestine.svg" alt="StandWithPalestine" /></a>
</p>
# QBZ
QBZ is a free and open source high-fidelity streaming client for Linux (with experimental macOS support) with native playback. It is a real desktop application — not a web wrapper — with DAC passthrough, per-track sample rate switching, exclusive mode, and bit-perfect audio delivery.
No API keys needed. No telemetry. No tracking. Just music.
## Legal / Branding
- This application uses the Qobuz API but is not certified by Qobuz.
- Qobuz is a trademark of Qobuz. QBZ is not affiliated with, endorsed by, or certified by Qobuz.
- **Offline cache** is a temporary playback store for listening without an internet connection while you have a valid subscription. If your subscription becomes invalid, QBZ will remove all cached content after 3 days.
- **Local library** is a "bring your own music" feature — play your own files with bit-perfect audio and the full QBZ interface, no streaming subscription required.
- Qobuz Terms of Service: https://www.qobuz.com/us-en/legal/terms
## Why QBZ
Browsers cap audio output at 48 kHz and resample everything through WebAudio. QBZ uses a native playback pipeline with direct device control so your DAC receives the original resolution — up to 24-bit / 192 kHz — with no forced resampling.
## Installation
### Arch Linux (AUR)
```bash
yay -S qbz-bin # or paru -S qbz-bin
```
### Flatpak (Flathub)
```bash
flatpak install flathub com.blitzfc.qbz
```
> **Audiophiles:** Flatpak sandboxing limits PipeWire bit-perfect. Use ALSA Direct backend for guaranteed bit-perfect in Flatpak, or install via native packages for full PipeWire support.
### Snap
```bash
sudo snap install qbz-player
sudo snap connect qbz-player:alsa
sudo snap connect qbz-player:pipewire
```
> **Note:** After installing, connect ALSA and PipeWire interfaces for full audio support. MPRIS media keys work out of the box.
### APT Repository (Debian/Ubuntu/Mint)
```bash
curl -fsSL https://vicrodh.github.io/qbz-apt/qbz-archive-keyring.gpg | gpg --dearmor | sudo tee /usr/share/keyrings/qbz-archive-keyring.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/qbz-archive-keyring.gpg arch=$(dpkg --print-architecture)] https://vicrodh.github.io/qbz-apt stable main" | sudo tee /etc/apt/sources.list.d/qbz.list
sudo apt update && sudo apt install qbz
```
> Requires glibc 2.38+ (Ubuntu 24.04+, Debian 13+). For older releases use Flatpak, Snap, or AppImage.
### RPM (Fedora/openSUSE)
Download from [Releases](https://github.com/vicrodh/qbz/releases): `sudo dnf install ./qbz-*.rpm`
> Requires glibc 2.38+ (Fedora 39+, openSUSE Tumbleweed).
### Gentoo
```bash
eselect repository add qbz-overlay git https://github.com/vicrodh/qbz-overlay.git
emerge --sync qbz-overlay
emerge media-sound/qbz-bin # prebuilt binary
# or
emerge media-sound/qbz # build from source
```
### NixOS / Nix
Add the flake input to your `flake.nix`:
```nix
inputs.qbz.url = "github:vicrodh/qbz";
```
**NixOS (system-wide):**
```nix
{pkgs, inputs, ...}:
{
environment.systemPackages = [
inputs.qbz.packages.${pkgs.system}.default
];
}
```
**Home Manager:**
```nix
{pkgs, inputs, ...}:
{
home.packages = [
inputs.qbz.packages.${pkgs.system}.default
];
}
```
> QBZ is also available in [nixpkgs](https://github.com/NixOS/nixpkgs) as `qbz`.
### AppImage
Download from [Releases](https://github.com/vicrodh/qbz/releases): `chmod +x QBZ.AppImage && ./QBZ.AppImage`
### macOS (Experimental)
> **QBZ is a Linux-first application.** macOS support is experimental and limited. Features like PipeWire, ALSA Direct, casting, and device control are unavailable.
Download the unsigned DMG from [Releases](https://github.com/vicrodh/qbz/releases).
Since the DMG is unsigned, you may need to allow it in System Settings > Privacy & Security after first launch.
## Features
### Audio and Playback
- **Bit-perfect playback** with DAC passthrough and per-track sample rate switching (44.1–192 kHz)
- **Four audio backends:** PipeWire, ALSA, ALSA Direct (hw: bypass), PulseAudio
- **HiFi Wizard** — guided bit-perfect configuration with real DAC capability detection
- Native decoding: FLAC, MP3, AAC, ALAC, WavPack, Ogg Vorbis, Opus (Symphonia)
- Gapless playback on all backends
- **Loudness normalization** (EBU R128) with ReplayGain support
- Two-level audio cache with next-track prefetching
- Streaming playback — start listening before download completes
### Queue and Library
- Queue with shuffle, repeat (track/queue/off), and history
- Favorites and playlists from your Qobuz account
- **Qobuz playlist follow/unfollow** — subscribe natively, syncs across all Qobuz clients
- **Local library** — directory scanning, metadata extraction, CUE sheets, SQLite indexing
- Tag editor with sidecar storage (preserves original files)
- Virtualized lists for large libraries
### Qobuz Connect
Multi-device playback control using Qobuz's real-time streaming protocol.
- **Renderer mode** — receive playback commands from your phone, tablet, or web player
- **Controller mode** — control remote devices from QBZ
- Server-authoritative queue sync across all devices
- Bidirectional transport: play, pause, skip, seek, shuffle, repeat, volume
### Casting
- **Chromecast** and **DLNA/UPnP** discovery and streaming
- Seamless playback handoff to network devices
### Integrations
- **MPRIS** media controls and media keys
- **Last.fm** scrobbling and now-playing
- **ListenBrainz** scrobbling with offline queue
- **MusicBrainz** artist enrichment, musician credits, relationships (no telemetry — one-way pull)
- **Discogs** artwork for local library
- Playlist import from Spotify, Apple Music, Tidal, Deezer
- Desktop notifications with artwork
### Immersive Player
- Full-screen player with tabbed panel system
- **17+ visualization panels:** spectrum, oscilloscope, spectrogram, Linebed (3D terrain), Laser, Tunnel, Comet, coverflow, and more
- Synchronized lyrics with line-by-line display
- Queue, track info, history, and suggestions panels
### Discovery
- **Scene Discovery** — explore artists by location and musical scene (MusicBrainz-powered)
- **3-tab Home:** customizable Home, Editor's Picks, personalized For You
- Genre filtering, artist similarity engine, radio stations
- Musician pages, label pages, album credits
### Interface
- 26+ themes (Dark, OLED, Nord, Dracula, Tokyo Night, Catppuccin, Breeze, Adwaita...)
- Auto-theme from DE, wallpaper, or custom image
- Focus mode, mini player, PDF booklet viewer
- Configurable keyboard shortcuts, UI zoom 80–200%
- **5 languages:** English, Spanish, German, French, Portuguese
- Offline mode with automatic reconnection
## Tech Stack
| Layer | Technology |
|-------|-----------|
| **Desktop shell** | Rust + Tauri 2.0 |
| **Frontend** | SvelteKit + Svelte 5 (runes) + TypeScript + Vite |
| **Audio decoding** | Symphonia (all codecs) via rodio 0.22 |
| **Audio backends** | PipeWire, ALSA (alsa-rs), ALSA Direct (hw:), PulseAudio |
| **Networking** | reqwest (rustls-tls), axum (local API server) |
| **Database** | rusqlite (bundled SQLite, WAL mode) |
| **PDF** | MuPDF 0.6 (native rendering) |
| **Desktop** | souvlaki (MPRIS), ashpd (XDG notifications), keyring |
| **Casting** | rust_cast (Chromecast), rupnp (DLNA/UPnP), mdns-sd |
| **i18n** | svelte-i18n (5 locales) |
### Multi-Crate Architecture
```
crates/
qbz-models/ Shared domain types
qbz-audio/ Audio backends, loudness, device management
qbz-player/ Playback engine, streaming, queue
qbz-qobuz/ Qobuz API client and auth
qbz-core/ Orchestrator (player + audio + API)
qbz-library/ Local library scanning and metadata
qbz-integrations/ Last.fm, ListenBrainz, MusicBrainz, Discogs
qbz-cache/ L1 memory + L2 disk audio caching
qbz-cast/ Chromecast, DLNA/UPnP
qconnect-protocol/ Qobuz Connect protobuf wire format
qconnect-core/ Queue and renderer domain models
qconnect-app/ Application logic and concurrency
qconnect-transport-ws/ WebSocket transport with qcloud framing
```
## Building from Source
### Prerequisites
- Rust (latest stable), Node.js 20+, Linux or macOS with audio support
### System Dependencies
**Debian/Ubuntu:**
```bash
sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev libasound2-dev \
libayatana-appindicator3-dev librsvg2-dev libssl-dev pkg-config
```
**Fedora:**
```bash
sudo dnf install webkit2gtk4.1-devel gtk3-devel alsa-lib-devel \
libappindicator-gtk3-devel librsvg2-devel openssl-devel pkg-config
```
**Arch Linux:**
```bash
sudo pacman -S webkit2gtk-4.1 gtk3 alsa-lib libappindicator-gtk3 \
librsvg openssl pkg-config
```
**Gentoo:**
```bash
sudo emerge net-libs/webkit-gtk:4.1 x11-libs/gtk+:3 media-libs/alsa-lib \
dev-libs/libayatana-appindicator gnome-base/librsvg dev-libs/openssl virtual/pkgconfig
```
### Build
```bash
git clone https://github.com/vicrodh/qbz.git && cd qbz
npm install
npm run tauri dev # development
npm run tauri build # production (DEB, RPM, AppImage)
```
### API Proxy
Last.fm, Discogs, Tidal, Spotify-import, and MusicBrainz traffic goes through a hosted Cloudflare Workers proxy (`qbz-api-proxy.blitzkriegfc.workers.dev`) that holds all credentials server-side. Both pre-built releases and source builds use it out of the box — **no API keys or `.env` file required**.
If you want to run against your own proxy (for development, or if you fork QBZ), the proxy source lives at [`vicrodh/qbz-api-proxy`](https://github.com/vicrodh/qbz-api-proxy). Deploy it with `wrangler deploy` and then edit the `*_PROXY_URL` constants in `src-tauri/src/lastfm/mod.rs`, `discogs/mod.rs`, `playlist_import/providers/tidal.rs`, and `musicbrainz/client.rs` to point at your worker before rebuilding.
### Environment Variables
| Variable | Effect |
|----------|--------|
| `QBZ_HARDWARE_ACCEL=0` | Disable GPU rendering (crash recovery) |
| `QBZ_FORCE_X11=1` | Use XWayland (NVIDIA Wayland issues) |
| `QBZ_SOFTWARE_RENDER=1` | Force Mesa llvmpipe (VMs) |
| `QBZ_DISABLE_DMABUF=1` | Disable DMA-BUF (Intel Arc EGL crashes) |
If QBZ crashes on startup: `qbz --reset-graphics`
## Known Issues
- **Hi-Res seeking** — seeking in tracks >96kHz can take 10-20s (decoder must scan from start). Use prev/next for instant navigation.
- **ALSA Direct** — exclusive access blocks other apps. Use DAC/amplifier physical volume control.
- **PipeWire bit-perfect in Flatpak** — limited by sandbox. Use ALSA Direct or native packages.
## Documentation
User guides, audio configuration, integrations, and troubleshooting: **[QBZ Wiki](https://github.com/vicrodh/qbz/wiki)** (work in progress).
## Diagram
```mermaid
flowchart TD
subgraph group_frontend["Frontend"]
node_ui_shell["Svelte UI<br/>sveltekit app"]
node_ui_state["Stores & Services<br/>ui orchestration"]
node_ui_views["Views<br/>screen layer"]
node_ui_remote["Remote Runtime<br/>qconnect client<br/>[qconnectRuntime.ts]"]
end
subgraph group_tauri["Desktop App"]
node_tauri_backend["Tauri Backend<br/>native backend"]
node_cmd_v2["Commands v2<br/>backend api"]
node_cmd_legacy["Legacy Commands<br/>compat api"]
node_app_audio["App Audio<br/>backend audio"]
node_app_player["App Player<br/>playback engine"]
node_app_library["App Library<br/>library service"]
node_app_cache["Offline Cache<br/>local cache"]
node_app_cast["Casting<br/>device handoff"]
node_app_connect["Qconnect Service<br/>remote control bridge"]
end
subgraph group_crates["Rust Crates"]
node_rust_player["qbz-player<br/>playback core"]
node_rust_audio["qbz-audio<br/>audio backend"]
node_rust_cache["qbz-cache<br/>audio cache"]
node_rust_qobuz["qbz-qobuz<br/>api client"]
node_rust_library["qbz-library<br/>library index"]
node_rust_cast["qbz-cast<br/>cast stack"]
node_rust_connect["qconnect stack<br/>protocol stack"]
node_rust_integrations["qbz-integrations<br/>external services"]
end
subgraph group_packaging["Packaging"]
node_packaging_ci["Packaging & CI<br/>release automation<br/>[workflows]"]
end
node_ui_shell -->|"uses"| node_ui_state
node_ui_shell -->|"renders"| node_ui_views
node_ui_state -->|"calls"| node_cmd_v2
node_ui_state -->|"syncs"| node_ui_remote
node_ui_remote -->|"bridges"| node_app_connect
node_tauri_backend -->|"exposes"| node_cmd_v2
node_tauri_backend -.->|"keeps"| node_cmd_legacy
node_cmd_v2 -->|"drives"| node_app_player
node_cmd_v2 -->|"queries"| node_app_library
node_cmd_v2 -->|"reads/writes"| node_app_cache
node_cmd_v2 -->|"controls"| node_app_cast
node_cmd_v2 -->|"syncs"| node_rust_integrations
node_app_player -->|"uses"| node_rust_player
node_app_audio -->|"uses"| node_rust_audio
node_app_player -->|"prefetches"| node_rust_cache
node_app_library -->|"indexes"| node_rust_library
node_app_library -->|"enriches"| node_rust_qobuz
node_app_cast -->|"delegates"| node_rust_cast
node_app_connect -->|"implements"| node_rust_connect
node_packaging_ci -.->|"builds"| node_tauri_backend
node_packaging_ci -.->|"bundles"| node_ui_shell
click node_ui_shell "https://github.com/vicrodh/qbz/tree/main/src"
click node_ui_state "https://github.com/vicrodh/qbz/tree/main/src/lib/stores"
click node_ui_views "https://github.com/vicrodh/qbz/tree/main/src/lib/components/views"
click node_ui_remote "https://github.com/vicrodh/qbz/blob/main/src/lib/services/qconnectRuntime.ts"
click node_tauri_backend "https://github.com/vicrodh/qbz/tree/main/src-tauri/src"
click node_cmd_v2 "https://github.com/vicrodh/qbz/tree/main/src-tauri/src/commands_v2"
click node_cmd_legacy "https://github.com/vicrodh/qbz/tree/main/src-tauri/src/commands"
click node_app_audio "https://github.com/vicrodh/qbz/tree/main/src-tauri/src/audio"
click node_app_player "https://github.com/vicrodh/qbz/tree/main/src-tauri/src/player"
click node_app_library "https://github.com/vicrodh/qbz/tree/main/src-tauri/src/library"
click node_app_cache "https://github.com/vicrodh/qbz/tree/main/src-tauri/src/offline_cache"
click node_app_cast "https://github.com/vicrodh/qbz/tree/main/src-tauri/src/cast"
click node_app_connect "https://github.com/vicrodh/qbz/blob/main/src-tauri/src/qconnect_service.rs"
click node_rust_player "https://github.com/vicrodh/qbz/tree/main/crates/qbz-player/src"
click node_rust_audio "https://github.com/vicrodh/qbz/tree/main/crates/qbz-audio/src"
click node_rust_cache "https://github.com/vicrodh/qbz/tree/main/crates/qbz-cache/src"
click node_rust_qobuz "https://github.com/vicrodh/qbz/tree/main/crates/qbz-qobuz/src"
click node_rust_library "https://github.com/vicrodh/qbz/tree/main/crates/qbz-library/src"
click node_rust_cast "https://github.com/vicrodh/qbz/tree/main/crates/qbz-cast/src"
click node_rust_connect "https://github.com/vicrodh/qbz/tree/main/crates/qconnect-protocol/src"
click node_rust_integrations "https://github.com/vicrodh/qbz/tree/main/crates/qbz-integrations/src"
click node_packaging_ci "https://github.com/vicrodh/qbz/blob/main/.github/workflows"
classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_ui_shell,node_ui_state,node_ui_views,node_ui_remote toneBlue
class node_tauri_backend,node_cmd_v2,node_cmd_legacy,node_app_audio,node_app_player,node_app_library,node_app_cache,node_app_cast,node_app_connect toneAmber
class node_rust_player,node_rust_audio,node_rust_cache,node_rust_qobuz,node_rust_library,node_rust_cast,node_rust_connect,node_rust_integrations toneMint
class node_packaging_ci toneIndigo
```
## Open Source
QBZ is MIT-licensed. No telemetry, no tracking, no hidden services. Built for Linux audio enthusiasts, with experimental macOS support.
## Contributing
Contributions welcome. Please read `CONTRIBUTING.md` before submitting issues or pull requests.
### Contributors
- [@vorce](https://github.com/vorce)
- [@boxdot](https://github.com/boxdot)
- [@arminfelder](https://github.com/arminfelder)
- [@afonsojramos](https://github.com/afonsojramos) — macOS port
- [@Vudgekek](https://github.com/Vudgekek) — macOS audio
- [@GwendalBeaumont](https://github.com/GwendalBeaumont) — i18n
- [@AdamArstall](https://github.com/AdamArstall)
- [@DoubleGate](https://github.com/DoubleGate)
## License
MIT
## Fancy charts
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=vicrodh/qbz&type=date&theme=dark&legend=top-left" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=vicrodh/qbz&type=date&legend=top-left" />
<img alt="Star History Chart" src="https://api.star-history.com/chart?repos=vicrodh/qbz&type=date&legend=top-left" />
</picture>