Music Player (written in Rust)
An extensible music player daemon, server and client, written in Rust — like mpd or Mopidy.
Audio decoding and playback are powered by the Rockbox firmware's battle-tested engine, via the rockbox-playback, rockbox-dsp and rockbox-metadata crates: 40+ audio formats, gapless-grade buffering, EQ/crossfade/ReplayGain DSP, and native HTTP streaming. The daemon indexes your library into SQLite (with FTS5 full-text search) and exposes it over gRPC, GraphQL and a web UI — controllable from the terminal UI, the browser, or the Tauri desktop app.
[!NOTE] Looking for more? If you're interested in this project, you might want to check out Rockbox Zig, a music player daemon built on the Rockbox Open Source Firmware. It offers advanced audio playback features, bringing the best of Rockbox to modern platforms with the power of Zig and Rust.
Table of Contents
- Features
- Installation
- Start the server
- Usage
- Terminal UI
- Web UI & Desktop
- GraphQL API
- Search
- Configuration
- Casting
- Star History
Features
- 🎵 Rockbox playback engine — 40+ formats (MP3, FLAC, Vorbis, Opus, MP4/AAC/ALAC, WavPack, APE, WMA, chiptunes, …) with the Rockbox DSP chain (EQ presets, crossfade, ReplayGain)
- 🔎 Instant full-text search backed by SQLite FTS5, kept in sync automatically by database triggers
- 🖥️ Terminal UI (ratatui) with an fzf-style fuzzy finder, neovim-inspired status line and
?help overlay - 🌐 Web UI (React 18 + TanStack Query + Jotai) with live progress and seek/fast-forward
- 🖱️ Desktop app built on Tauri 2
- 📡 gRPC + GraphQL APIs (tonic 0.14, grpc-web enabled) for building your own clients
- ☁️ Browse & stream from Subsonic/Navidrome and Jellyfin servers
- 📻 Cast to Chromecast and UPnP/DLNA renderers, or control another music-player daemon
- 🎧 Rocksky scrobbling — scrobble your plays to Rocksky on the AT Protocol
- 🔌 Flexible audio output: system device (cpal), stdout, FIFO, Unix or TCP socket
Installation
Compiling from source, without Nix:
# Install dependencies
# Compile
&&
With Nix:
&&
Using npm (downloads the prebuilt binary from GitHub releases):
# pin a specific release: MUSIC_PLAYER_VERSION=v0.2.0 npx music-player
macOS/Linux
Using Homebrew:
Using Nix:
Or download the latest release for your platform here.
📦 Downloads
Latest (Desktop):
Mac: arm64: music-player-desktop_v0.2.0_aarch64-apple-darwin.tar.gz intel: Music_Player_v0.2.0_x64.dmgLinux: music-player_v0.2.0_amd64.debWindows: Music_Player_x64_en-US.msi
Latest (CLI):
Mac: arm64: music-player_v0.2.0_aarch64-apple-darwin.tar.gz intel: music-player_v0.2.0_x86_64-apple-darwin.tar.gzLinux: music-player_v0.2.0_x86_64-unknown-linux-gnu.tar.gzWindows: music-player_x86_64-pc-windows-gnu.tar.gz
Start the server
The daemon scans your music directory ($HOME/Music by default), serves gRPC on :5051, WebSocket events on :5052, and the web UI + GraphQL on :5053.
Usage
USAGE:
music-player [SUBCOMMAND]
OPTIONS:
-h, --help Print help information
-V, --version Print version information
SUBCOMMANDS:
albums List all albums
artists List all artists
help Print this message or the help of the given subcommand(s)
next Play the next song
pause Pause the current song
open Open audio file
playlist Manage playlists
prev Play the previous song
queue Manage the queue
play Resume the current song
scan Scan music library: $HOME/Music
search Search for a song, album, artist or playlist
stop Stop the current song
tracks List all tracks
Terminal UI
Run music-player while a daemon is running (or connect to a remote one with music-player connect -s <host>) to open the TUI. It ships with:
- an fzf-style fuzzy finder (
/) over tracks, albums and artists, ranked as you type with match highlighting - a neovim-inspired status line: mode indicator, now playing, position/duration, volume, and the connected server
- a context-sensitive keybinding hint bar, and a full help overlay on
?
Main keys:
| Key | Action |
|---|---|
? |
Help overlay with all keybindings |
/ |
Fuzzy search (Tab switches Tracks/Albums/Artists scope) |
Space |
Play / pause |
n / p |
Next / previous track |
< / > |
Seek −5s / +5s |
+ / - |
Volume up / down |
z |
Add selected track to the queue |
q / Esc |
Back / quit |
Web UI & Desktop
The web UI is served by the daemon at http://localhost:5053 — React 18, TanStack Query and Jotai, with live playback position (GraphQL subscriptions) and a seekable progress bar.
The desktop app wraps the same UI with Tauri 2:
GraphQL API
# Start the server
Open http://localhost:5053/graphiql in your browser.
Search
The library is indexed into SQLite FTS5 virtual tables that are kept in sync by database triggers — no separate index to maintain, and search works instantly over tracks (title/artist/album/genre), albums and artists with prefix matching:
# GraphQL: query { search(keyword: "fire") { tracks { title } albums { title } artists { name } } }
Configuration
Settings live in ~/.config/music-player/settings.toml (created on first run). Every key can also be set through a MUSIC_PLAYER_* environment variable (e.g. MUSIC_PLAYER_HTTP_PORT=5053).
= "/home/me/Music"
= 5051 # gRPC
= 5052 # WebSocket events
= 5053 # Web UI + GraphQL
= "Music Player"
= 30 # rescan the music directory every N minutes (0 = off)
The library can also be refreshed manually at any time — music-player scan from the CLI, or the scan mutation in GraphQL. Re-scans only pick up what's new; existing entries are untouched.
Audio output
By default audio goes to the system output device. audio_output redirects the decoded stream (raw S16LE stereo) somewhere else:
= "cpal" # system audio device (default)
= "stdout" # raw PCM to stdout
= "fifo:/tmp/mp.pcm" # named pipe
= "unix:/tmp/mp.sock" # unix socket
= "tcp:0.0.0.0:9000" # tcp socket, e.g.: ffplay -f s16le -ar 44100 -ac 2 tcp://host:9000
Subsonic / Navidrome & Jellyfin
Browse and stream your remote library from any Subsonic-compatible server (Navidrome, Airsonic, gonic) or Jellyfin:
= "https://music.example.com"
= "alice"
= "secret"
= "https://jellyfin.example.com"
= "alice"
= "secret"
Restart the daemon: the servers show up as source devices (in the web UI's device picker, or listDevices in GraphQL). Connect to one and its artists/albums/tracks/playlists are browsable, with tracks streamed straight from the server.
Rocksky scrobbling
If you're logged into Rocksky (rocksky login writes ~/.rocksky/token.json), the daemon scrobbles what you play — using the classic rule (half the track, or 4 minutes, whichever comes first). Disable it with:
= false
Casting
Playback isn't limited to the machine running the daemon — from the web UI or GraphQL you can cast to:
- Chromecast devices
- UPnP/DLNA media renderers
- another music-player daemon on your network (auto-discovered via mDNS)