music-player 0.2.0

An extensible music player daemon written in Rust
music-player-0.2.0 is not a library.

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

  • 🎵 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
brew install protobuf # macOS
sudo apt-get install -y libasound2-dev protobuf-compiler # Ubuntu/Debian
choco install protoc # Windows using Chocolatey Package Manager
# Compile
git clone https://github.com/tsirysndr/music-player.git
cd music-player/webui/musicplayer
nvm install # install node version specified in .nvmrc (optional on windows)
bun install && bun run build # build webui
cd ../..
cargo install --path .

With Nix:

git clone https://github.com/tsirysndr/music-player.git
cd music-player
nix develop --experimental-features "nix-command flakes"
cd webui/musicplayer
bun install && bun run build # build webui
cd ../..
cargo install --path .

Using npm (downloads the prebuilt binary from GitHub releases):

npm install -g music-player   # or: npx music-player
# pin a specific release: MUSIC_PLAYER_VERSION=v0.2.0 npx music-player

macOS/Linux

Using Homebrew:

brew install tsirysndr/tap/musicplayer

Using Nix:

cachix use tsirysndr
nix profile install --experimental-features "nix-command flakes" github:tsirysndr/music-player

Or download the latest release for your platform here.

📦 Downloads

Latest (Desktop):

Latest (CLI):

Other version...

Start the server

music-player

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:

cd webui/musicplayer
bun install
bun run tauri dev   # or: bun run tauri build

GraphQL API

# Start the server
music-player

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:

music-player search "fire"        # CLI
# 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).

music_directory = "/home/me/Music"
port = 5051        # gRPC
ws_port = 5052     # WebSocket events
http_port = 5053   # Web UI + GraphQL
device_name = "Music Player"
library_refresh_interval = 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:

audio_output = "cpal"              # system audio device (default)
audio_output = "stdout"            # raw PCM to stdout
audio_output = "fifo:/tmp/mp.pcm"  # named pipe
audio_output = "unix:/tmp/mp.sock" # unix socket
audio_output = "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:

subsonic_url = "https://music.example.com"
subsonic_username = "alice"
subsonic_password = "secret"

jellyfin_url = "https://jellyfin.example.com"
jellyfin_username = "alice"
jellyfin_password = "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:

scrobble = 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)

✨ Star History