A local music player rebuilt with TanStack Start, TanStack Router, TanStack Query, Drizzle, and PostgreSQL.
This app plays audio from a local tracks/ folder, stores music and playlist data in Postgres, serves album/playlist artwork from a local covers/ folder, and provides a desktop-style library UI for browsing, searching, playback, and playlist management.
pnpm installCreate a .env file with a Postgres connection string:
POSTGRES_URL=postgres://postgres:postgres@localhost:54322/postgresYou can also use the setup script to create a local Docker Postgres database:
pnpm db:setupThe setup script may still ask for BLOB_READ_WRITE_TOKEN; this project stores uploaded covers locally, so that value is not required.
Add audio files to a top-level tracks/ folder:
mkdir -p tracksSupported seed formats:
.mp3.flac
One way to download a YouTube playlist into local files is yt-dlp:
yt-dlp -x --audio-format mp3 --add-metadata --embed-thumbnail "https://www.youtube.com/playlist?list=..."Embedded cover art is extracted during seeding and written to covers/.
Run migrations and seed songs from tracks/:
pnpm db:migrate
pnpm db:seedOpen Drizzle Studio:
pnpm db:studioUseful database commands:
pnpm db:generate: generate migrations fromsrc/lib/db/schema.tspnpm db:migrate: run migrations and enablepg_trgmfor fuzzy searchpnpm db:seed: clear songs/playlists, then scantracks/and insert songspnpm db:enhance: enhance existing metadatapnpm db:truncate: clear song and playlist datapnpm db:push: push schema changes directlypnpm db:drop: drop Drizzle-managed database objects
If POSTGRES_URL is not set, the app still starts with empty library states.
pnpm devOpen http://localhost:3000.
Build the app:
pnpm buildRun the built Nitro server:
pnpm previewThe production server entry is .output/server/index.mjs.
- Local music library backed by PostgreSQL
- File-based routing with TanStack Router
- Server functions and API routes with TanStack Start
- TanStack Query for playlists, songs, mutations, and cache refresh
- MediaSession API support for system playback controls
- All tracks view
- Playlist detail pages
- Create, rename, delete, and browse playlists
- Add tracks to playlists
- Upload playlist covers to local
covers/ - Serve audio from local
tracks/ - Basic search across tracks, artists, and albums
- Keyboard navigation for track list and sidebar
- Spacebar play/pause shortcut
- Progress bar seeking
- Volume controls
- Mobile library drawer
- Empty states for no music and no playlists
src/routes/ File-based routes and API routes
src/components/ Player, playlist, dialog, and table components
src/hooks/ Playback and playlist hooks
src/lib/db/ Drizzle schema, queries, migrations, and seed scripts
src/lib/server/ TanStack Start server functions
tracks/ Local audio files, git ignored
covers/ Local cover images, git ignored- This project does not use Vercel Blob.
- Local audio is served through
/api/audio/$filename. - Local cover images are served through
/api/cover/$filename. - Search uses Postgres
pg_trgm, which is enabled duringpnpm db:migrate.