New GET /player/lyrics/{id} (auth-required) returns the best available
stored structuredLyrics entry for a song — preferring a synced one —
as plain JSON lines with a start time only where the source actually
has one.
The player bar gains a lyrics toggle; the panel fetches lyrics on
every song change, renders each line, and — only for lines that
actually carry a startMs — makes them clickable (seeks + plays) and
highlights the currently active line as playback progresses. Plain
(unsynced) lyrics render as static text with no fabricated timing or
highlighting.
Note: verified via Go template parsing/rendering and the new handler
tests, plus a JS syntax check; not manually exercised in a browser in
this session.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014r98AtcBbevpscWLsfwVvv
VibeSonic
VibeSonic is a lightweight, self-hosted music/media server written in Go.
It follows the same philosophy as other small Vibe* tools: a single Go
binary, SQLite storage, server-rendered Bootstrap 5 templates, dark mode
by default, minimal JavaScript, and no external services (no Redis, no
message broker, no microservices, nothing Kubernetes-specific).
This is Phase 2 of 4. Phase 1 built the foundation (config, schema,
auth, scanner, metadata, streaming, cover art, web UI shell). Phase 2
adds a complete implementation of the original Subsonic REST API
through version 1.16.1 under /rest/* — every endpoint, backed by
real data, including transcoding, HLS, a real mpv-backed jukebox, and a
real podcast subsystem. See
docs/subsonic-compatibility.md for
the full endpoint-by-endpoint matrix. OpenSubsonic extensions are not
yet implemented (Phase 3).
Features
Phase 1: foundation
- SQLite storage (WAL mode, foreign keys, versioned migrations) with a schema already modeling artists, albums, songs, genres, contributors, playlists, favorites, ratings, scrobbles, playback history, bookmarks, play queues, shares, podcasts, internet radio, and settings
- Native authentication: Argon2id password hashing, HTTP-only signed session cookies, CSRF-protected forms, session revocation
- Optional Navidrome-style external authentication via a trusted reverse
proxy header, gated on the real TCP peer address (never on
spoofable
X-Forwarded-For/X-Real-IP/Forwardedheaders) - A library scanner supporting multiple music folders, incremental rescans, new/changed/deleted/renamed file detection, and a bounded worker pool — never a blind full rebuild
- Real metadata extraction via
ffprobe(multi-artist, multi-genre, MusicBrainz IDs, ReplayGain, embedded artwork/lyrics, sort fields, and more) - Stable, opaque, URL-safe IDs that survive restarts and rescans
- Direct media streaming with HTTP range request support, correct MIME types, and no full-file buffering
- Cover art discovery (embedded artwork or folder images) with a resized-image disk cache
- A server-rendered, dark-mode, keyboard-friendly web UI: browsing, search, playlists, favorites, a persistent bottom player, and an admin section (users, music folders, scanner status, system settings)
/healthzand/readyz
Phase 2: Subsonic REST API
- All 76 endpoints of the original Subsonic API through 1.16.1 — browsing (filesystem and ID3), search, playlists, favorites/ratings/ scrobbling, sharing, user management with the full permission model, bookmarks, play queue, chat, and library scan status
- Real ffmpeg-backed transcoding (mp3/opus/aac) and HLS segmenting for
stream/hls, bounded concurrency, killed on client disconnect - A real podcast subsystem: RSS feed refresh, bounded background downloads, restart recovery, downloaded episodes stream like any other song
- A real jukebox backed by
mpvover its JSON IPC socket — server-side playback, not a simulation; administratively unavailable (with a clear error) ifmpvisn't installed - Both original Subsonic auth modes (token/salt and password, including
enc:hex), verified against an AES-256-GCM-encrypted credential kept separate from the Argon2id hash used for native login — seedocs/subsonic-compatibility.md /rest/*authentication is entirely independent of the browser session/CSRF/external-auth flow
Quick start
go build -o vibesonic ./cmd/vibesonic
VIBESONIC_SESSION_SECRET=$(openssl rand -hex 32) ./vibesonic
Then open http://localhost:8080 — on a fresh database you'll land on
the setup wizard, which creates the administrator account, your first
music folder, and kicks off the first scan.
ffmpeg/ffprobe should be on PATH (or pointed to via
VIBESONIC_FFMPEG_PATH/VIBESONIC_FFPROBE_PATH) for metadata reading,
cover art extraction, and transcoding/HLS to work. mpv should be on
PATH (or pointed to via VIBESONIC_MPV_PATH) for the jukebox to be
available — without it, jukeboxControl returns a clear "unavailable"
error rather than pretending to work.
Docker
docker compose up -d
See docker-compose.yml — mount your music
directory read-only at /music and set VIBESONIC_SESSION_SECRET.
systemd
See systemd/vibesonic.service.
Configuration
VibeSonic is configured entirely through VIBESONIC_* environment
variables — see docs/configuration.md for the
full list and defaults. There is no separate config file.
Documentation
docs/architecture.md— how the pieces fit togetherdocs/configuration.md— every environment variabledocs/external-auth.md— trusted reverse-proxy authenticationdocs/subsonic-compatibility.md— protocol endpoint status
Development
go fmt ./...
go vet ./...
go test ./...
Most tests exercise real SQLite databases and, where relevant, the real
ffmpeg/ffprobe binaries (skipped automatically if not installed).
No mocks are used for the database.