Waveform is a local-first music player built with Next.js, SQLite, and filesystem media storage.
It supports manual uploads and Spotify-link ingestion with automatic provider/quality selection (Tidal/Qobuz/Amazon via backend resolver).
Tech Stack
- Next.js 16 (App Router)
- React 19
- SQLite (
better-sqlite3) - NextAuth (credentials)
- Zustand (player + likes state)
- Local filesystem object storage for audio, cover art, and lyrics
Quick Start
bun install cp .env.example .env bun run dev
SQLite schema is auto-applied from db/schema.sql at startup.
Current App Features
- Library views:
- Home, Liked Songs, and Playlist pages
- Grid/List modes with persisted preference
- Upload-date sorting (newest/oldest)
- Virtualized list rendering for large libraries
- Duplicate rows deduped in song collections/search
- Upload flow:
- Spotify link mode (default)
- Manual file upload mode
- Spotify fetch card actions: preview, download lyrics, download cover, check availability
- Missing cover/lyrics prompts during import with ignore/upload choices
- Duplicate song detection with replace confirmation
- Automatic folder organization under:
music/<artist>/<title>/audiomusic/<artist>/<title>/covermusic/<artist>/<title>/lyrics
- Playback:
- Global player bar (seek, volume, shuffle, repeat, crossfade)
- Right now-playing sidebar + mobile sheet
- Lyrics hidden by default and toggleable
- Library management:
- Likes with optimistic updates
- Per-song edit mode (title/artist/cover/lyrics) from Settings
- Song quality metadata support (
bit-depth/sample-rate)
- Navigation:
- Command+K / Ctrl+K search palette on home
Settings (Current UI)
- Crossfade toggle + duration
- Edit mode toggle (enables per-song edit controls)
- Download quality profile (
Max,24-bit/48kHz,16-bit/44.1kHz)
Environment Variables
Required:
SQLITE_DB_PATHNEXTAUTH_SECRETADMIN_SECRET
Storage and import defaults:
LOCAL_MEDIA_ROOTLOCAL_MUSIC_SOURCE_DIRLOCAL_IMPORT_USE_COVER_FILESLOCAL_IMPORT_USE_LYRICS_FILES
Upload and rate limits:
UPLOAD_MAX_IMAGE_BYTESUPLOAD_MAX_AUDIO_BYTESRATE_LIMIT_AUTH_MAXRATE_LIMIT_AUTH_WINDOW_MSRATE_LIMIT_REGISTER_MAXRATE_LIMIT_REGISTER_WINDOW_MSRATE_LIMIT_ADMIN_MAXRATE_LIMIT_ADMIN_WINDOW_MS
API Endpoints
Core:
GET /api/songslist songsPOST /api/songscreate song (manual upload or Spotify/link mode)PATCH /api/songs/:idupdate song metadata (title/artist)POST /api/songs/:id/assetsupdate song cover and/or lyricsGET /api/files/[...key]stream local files (supports range requests)GET/POST/DELETE /api/likesliked songs management
Spotify helpers:
POST /api/songs/spotifyactions:fetch,availability,lyricsGET /api/songs/spotify/covercover proxy/download
Auth:
POST /api/registerGET/POST /api/auth/[...nextauth]
Import/admin utilities (API-only):
GET/POST /api/library/importlocal library import defaults + authenticated importPOST /api/admin/batch-uploadadmin-secret protected batch import
Misc:
GET /api/artwork/[...file]
Scripts
bun run devbun run buildbun run startbun run lint
Notes
ffmpegis required inPATHfor local-library conversion/import paths.- Media files are stored under
LOCAL_MEDIA_ROOTand gitignored. - Runtime SQLite DB files are gitignored.