Last updated: 2025-10-12
9layer is a local-first music player with a TypeScript backend and a modern Next.js frontend. It plays audio files from your machine that are indexed in a PostgreSQL database.
Important: You must have music files available locally for playback. The in-app YouTube download feature is experimental/untested and may not work yet.
- Local library playback with queue, ratings, sequential/random modes, and auto-advance
- Search across artists/albums/tracks with missing-audio indicators
- Modern UI built with Tailwind and shadcn/ui
- TypeScript/Fastify backend with Prisma + PostgreSQL
- REST API integration (WebSocket realtime planned)
- Listening analytics with top rated tracks, play session stats, and timeline heatmaps
- In-app toast notifications for rating changes, downloads, and analytics feedback
- Improved YouTube playlist downloads with cleaned artist/album metadata
backend/— Fastify (TypeScript), Prisma ORM, PostgreSQL, maintenance scriptsfrontend/— Next.js app (runs on port 3000+ by default)- Shared: audio files reside on your filesystem; the database stores metadata, ratings, and file paths
- Node.js 18+
- PostgreSQL 14+
- ffmpeg (recommended for future/optional conversion)
macOS:
brew install postgresql@14 ffmpegUbuntu/Debian:
sudo apt update
sudo apt install postgresql-14 ffmpegCreate a database and user (or use setup_postgres.sql in the repo as a reference):
CREATE DATABASE music_player;
CREATE USER music_user WITH PASSWORD 'your_secure_password';
GRANT ALL PRIVILEGES ON DATABASE music_player TO music_user;- Install deps
npm install- Environment
Create
backend/.env(see.env.exampleif present):
DATABASE_URL=postgresql://music_user:your_secure_password@localhost:5432/music_player
PORT=8000
CORS_ORIGIN=http://localhost:3004
If you change the frontend port, remember to update CORS_ORIGIN so it matches.
- Prisma
npx prisma generate
npx prisma migrate dev- Run the server
npm run dev
# Server listens on http://localhost:8000Notes:
- REST endpoints for playback/queue/search are under
backend/src/routes/(e.g.playback.routes.ts). - WebSocket support is planned; a polling fallback is used by the frontend today.
- Install deps
npm install- Environment (if used)
Create
frontend/.env.localand point to the backend:
NEXT_PUBLIC_API_BASE=http://localhost:8000
- Run the app
npm run dev
# By default opens at http://localhost:3000 (falls back to 3001/3002/... if busy)You need audio files on disk and corresponding rows in PostgreSQL so the player can find and play them.
Options:
-
Manual library entries (recommended for now)
- Place audio files (mp3/webm/opus) on your filesystem.
- Insert track metadata and absolute file paths into the DB using Prisma Studio:
npx prisma studio
- Or write a small seed script to create
Artist,Album,Trackrecords.
-
In-app YouTube downloader (experimental)
- The UI exposes a download form, but this path is not fully tested and may fail.
- Album and artist metadata from YouTube playlists is automatically sanitized (removes generic "Playlist" or "Topic" suffixes) for cleaner library entries.
- If you try it, ensure ffmpeg is installed. Expect bugs; contributions are welcome.
- Start servers together
- From the repository root, run
npm install -g .once to link the command. - Afterwards run
9layerto launch both servers. The backend binds tohttp://localhost:8000. The frontend will tryhttp://localhost:3000first and automatically fall back to3001,3002, etc. if earlier ports are busy. - Stop both servers any time with
9layer end(from any directory).
- From the repository root, run
- Manual start (optional)
- Start backend on 8000 and frontend on 3000 (or the next available open port) individually if you prefer.
- Play music
- Open the app, use search to locate tracks, and click play.
- Player supports previous/next, volume, sequential album order, and auto-advance after a user interaction (browser policy).
- Analytics dashboard surfaces top rated tracks, play history, and per-track listening totals.
- "No supported source" errors: ensure your file paths are valid, files exist, and the backend returns correct
content-type(e.g.,audio/mpeg). - CORS: confirm
CORS_ORIGINmatches whichever frontend URL you are using (e.g.http://localhost:3000) and the frontend points tohttp://localhost:8000. - Test file: open
frontend/public/audio-test.htmlin a browser to confirm your browser can play basic audio.
- Missing audio scan:
backend/scripts/flag_missing_audio.tschecks for tracks whose files are gone. Run withnpx ts-node backend/scripts/flag_missing_audio.ts --json backend/missing-audio-report.jsonto produce a report, then rerun with--applyto null-out missing file paths. - Portable launcher:
npm install -g .exposes the9layerCLI wrapper to start/stop both dev servers.
9layer/
├── backend/
│ ├── prisma/
│ ├── scripts/
│ └── src/
├── frontend/
│ ├── public/
│ └── src/
├── start-dev.sh
├── package.json
└── README.md
Issues and PRs are welcome. Areas of focus: downloader reliability, WebSocket realtime updates, library import tools.
MIT
