Simple music player for Raspberry Pi
- Go 65.6%
- JavaScript 18.4%
- CSS 12.5%
- HTML 3%
- Shell 0.5%
| docs | ||
| internal | ||
| scripts | ||
| web | ||
| .gitignore | ||
| CLAUDE.md | ||
| go.mod | ||
| go.sum | ||
| main.go | ||
| mjusik.service | ||
| README.md | ||
mjusik
A music box for kids, built on a Raspberry Pi 4. Plays local music files over Bluetooth headsets, controlled via a web UI.
Features
- Library scanner that indexes local music files by reading metadata from tags (via
dhowden/tagandffprobe) - Compilation/soundtrack albums grouped correctly by album artist
- Album browsing with embedded cover art
- Now playing view with play/pause, skip, repeat, and favorites
- One favorites playlist
- Bluetooth headset management via BlueZ D-Bus
- Web-based remote control with live state updates over WebSocket
Architecture
+-----------------------------------------------+
| Go binary (mjusik) |
| |
| +----------+ +----------+ +-----------+ |
| | Web UI | | Library | | Playback | |
| | (HTTP + |--| Scanner |--| Engine | |
| | WS) | | + SQLite | | (ffmpeg + | |
| +----------+ +----------+ | PipeWire) | |
| +-----+-----+ |
| | |
| +----------+-------+ |
| | Bluetooth Mgmt | |
| | (BlueZ D-Bus) | |
| +------------------+ |
+-----------------------------------------------+
| | |
Web browser Music files BT headsets
on disk via PipeWire
Single Go binary with four internal packages:
- library — Recursive file scanner reads tags with
dhowden/tag, usesffprobefor multi-value fields (artist, album artist). Stores tracks, albums, and favorites in SQLite. Extracts embedded cover art to disk. - player — Decodes audio via an
ffmpegsubprocess (any format → raw PCM). Outputs to PipeWire through the PulseAudio compatibility layer (jfreymuth/pulse). Manages queue, play/pause, skip, and repeat-one. - bluetooth — Talks to BlueZ over D-Bus (
godbus/dbus) for device discovery, pairing, and connection. Phase 1: single adapter/headset. Phase 2: dual adapters for simultaneous playback. - api — HTTP server (stdlib
net/http) serving a JSON API, WebSocket for live playback state (nhooyr.io/websocket), and the embedded web UI (vanilla JS viaembed.FS).
Hardware
- Raspberry Pi 4 (Debian Trixie, aarch64, headless)
- USB Bluetooth adapter(s) for headset connectivity
Dependencies (on the Pi)
ffmpeg(audio decoding + tag reading via ffprobe)- PipeWire + WirePlumber (audio output)
- BlueZ (Bluetooth)
No Go runtime needed — just the cross-compiled binary.
Build & Deploy
# Build and deploy in one step
bash scripts/deploy.sh musicbox
# Or manually
GOOS=linux GOARCH=arm64 go build -o mjusik .
scp mjusik musicbox:~/mjusik
The deploy script also installs a systemd user service that auto-starts on boot and restarts on failure.
Configuration
Flags (or MJUSIK_* environment variables):
| Flag | Env var | Default | Description |
|---|---|---|---|
--music-dir |
MJUSIK_MUSIC_DIR |
/home/music |
Path to music files |
--listen |
MJUSIK_LISTEN_ADDR |
:8080 |
HTTP listen address |
--db |
MJUSIK_DB_PATH |
mjusik.db |
SQLite database path |
--cover-dir |
MJUSIK_COVER_DIR |
covers |
Cover art cache directory |