A private, single-user Spotify manager for understanding listening habits, cleaning playlists, and improving shuffle behavior.
FixSpotify connects to Spotify through OAuth, stores listening history in a local SQLite database, and runs continuously in Docker. It is designed for one Spotify account and works with Spotify's Development Mode.
- Imports Spotify Extended Streaming History.
- Automatically syncs recent plays every 15 minutes, with a manual sync from the home page.
- Home product tour that walks through major tools, with optional product
screenshots under
public/demos/that crossfade as you switch features. Signed-in users also see total plays logged. - Ranks playlist tracks by least-listened over 6 months, 1 year, or lifetime.
- Identifies tracks that Spotify marks unavailable in the listener's market.
- Searches and filters dashboard tracks by title, artist, availability, and play-count ranges.
- Plays a single track on the active Spotify device, or opens it in Spotify.
- Scroll-focused track lists that shift the full-viewport color to each song’s album/artist palette and show a floating album-cover preview.
- Merges play counts across alternate Spotify track IDs using title/artist matching and ISRC identity (so localized titles like Arabic/Latin versions of the same recording count together).
- Removes tracks and keeps a seven-day recently deleted list.
- Restores deleted tracks without adding duplicates.
- Provides Fisher–Yates fair shuffle.
- Supports fresh shuffles and persistent no-repeat shuffle decks.
- Offers a weighted shuffle that favors tracks with fewer lifetime plays.
- Mixes multiple preferred playlists into one shuffle pool (master mix).
- Skips unplayable tracks when shuffling and previews the resulting order (Spotify playback accepts up to 100 URIs per request). Disables Spotify’s native shuffle so the custom order is respected.
- Analyzes playlist fit with Gemini using your written playlist intents, artist-cohesion rules (keep an artist's songs together when most already live in one playlist), and optional per-track "keep here because…" notes. Shows belong / possible misfit / needs AI status for each track. Suggests a better preferred playlist when a track looks misfiled.
- Adds tracks to suggested playlists without duplicates, and can remove them from the current playlist afterward.
- Lets you select and order the playlists shown in the app, and write short intents used by AI playlist sort.
- Caches large playlist track lists in SQLite to reduce Spotify API usage, with a Refresh from Spotify control when you change playlists outside the app.
- Insights page with Recharts visualizations from listening history and preferred playlists: summary stats, most/least played tracks and artists, plays over time (30-day, 12-month, lifetime), and per-playlist health (score, never-played share, 90-day stale share, size vs plays, play concentration, and a full details table).
- Next.js with TypeScript and the App Router
- NextAuth with the Spotify provider
- Prisma and SQLite
- Tailwind CSS
- Recharts for Insights charts
node-cronfor scheduled synchronization and cleanup- Docker Compose and Caddy for production deployment and HTTPS
- Node.js 22+
- npm
- A Spotify Developer application
- Spotify Premium for playback controls
- Docker and Docker Compose for production deployment
Create an application in the Spotify Developer Dashboard.
For local development, register:
http://127.0.0.1:3000/api/auth/callback/spotify
For production, register:
https://your-domain.example.com/api/auth/callback/spotify
The app requests permissions for private playlist access, playlist editing, recent listening history, and playback control.
Install dependencies:
npm installCopy the environment template:
cp .env.example .envOn PowerShell:
Copy-Item .env.example .envFill in .env:
SPOTIFY_CLIENT_ID=
SPOTIFY_CLIENT_SECRET=
NEXTAUTH_URL=http://127.0.0.1:3000
NEXTAUTH_SECRET=
DATABASE_URL="file:./prisma/dev.db"
# Free key from https://aistudio.google.com/apikey
GEMINI_API_KEY=
# Optional override (default: gemini-flash-latest)
# GEMINI_MODEL=gemini-flash-latestGenerate a NextAuth secret with:
openssl rand -base64 32Create the database and generate the Prisma client:
npm run db:migrate
npm run db:generateStart the development server:
npm run devOpen http://127.0.0.1:3000.
The 15-minute scheduler only has access to Spotify's 50 most recent plays. FixSpotify therefore includes a one-time importer for Spotify Extended Streaming History exports.
Place the extracted export files in:
spotify_history/
Then run:
npm run history:importThe importer:
- Reads audio and video streaming-history JSON files.
- Imports only tracks played for more than 30 seconds.
- Stores track and artist names for robust play-count matching.
- Ignores podcasts, audiobooks, and invalid records.
- Deduplicates records within the export.
- Deduplicates against plays already stored in SQLite.
- Backfills missing artist names on existing play rows when re-run.
- Can be run repeatedly without creating duplicates.
After the initial import, the 15-minute scheduler keeps the database current. Running the importer again is only necessary when importing a newer export, rebuilding the database, or backfilling artist names after a schema update.
If plays were imported before artist names were stored, drop the export back
into spotify_history/ and re-run npm run history:import. You can also run
npm run history:backfill-artists to fill names from playlist caches for
tracks that already appear in your preferred playlists.
npm run dev # Start the development server
npm run build # Create a production build
npm run start # Start the production server
npm run lint # Run ESLint
npm run db:generate # Generate the Prisma client
npm run db:migrate # Create and apply a development migration
npm run history:import # Import extended Spotify history
npm run history:backfill-artists # Fill missing artist names from cachesSQLite stores:
- Listening history
- Spotify refresh/access tokens for scheduled synchronization
- Playlist preferences
- Recently deleted tracks
- No-repeat shuffle deck progress
- Playlist track cache
- Playlist intent descriptions and AI sort cache
- Per-track placement notes for playlist sort
Playlist track lists are cached for six hours. Changes made inside FixSpotify invalidate the relevant cache immediately. The dashboard also has a Refresh from Spotify button for changes made in the Spotify client.
The database contains sensitive listening and authentication data. Do not commit it or expose it publicly.
Copy the production environment template:
cp .env.production.example .env.productionSet:
APP_DOMAIN=your-domain.example.com
SPOTIFY_CLIENT_ID=
SPOTIFY_CLIENT_SECRET=
NEXTAUTH_URL=https://your-domain.example.com
NEXTAUTH_SECRET=
DATABASE_URL=file:/app/data/prod.db
GEMINI_API_KEY=Build and start:
docker compose --env-file .env.production up -d --buildThe Compose stack includes:
- The Next.js application
- Caddy as an HTTPS reverse proxy
- A persistent Docker volume for SQLite
- Persistent Caddy certificate storage
Pending Prisma migrations are applied automatically when the app container starts.
Useful production commands:
docker compose --env-file .env.production ps
docker compose --env-file .env.production logs -f app
docker compose --env-file .env.production restart
docker compose --env-file .env.production up -d --build.github/workflows/deploy.yml deploys every push to main. It synchronizes
the repository to the server over SSH and rebuilds the Docker Compose stack.
Add these GitHub repository secrets:
SSH_HOST
SSH_USER
SSH_PRIVATE_KEY
The workflow excludes .env.production, SQLite database files, build output,
and Spotify history exports. Production data remains in the Docker volume
across deployments.
The app runs two in-process cron tasks:
- Every 15 minutes: fetch and store recent Spotify plays.
- Daily: delete recently deleted records older than seven days.
The application container must remain running for these jobs to execute.
If automatic deployment is unavailable:
cd ~/FixSpotify
docker compose --env-file .env.production up -d --buildIf the server is a Git checkout, pull first:
git pull- Keep
.env,.env.production, the SQLite database, and SSH keys private. - Restrict the Spotify app to your own account in Development Mode.
- Caddy automatically obtains and renews TLS certificates.
- The application is intended for private, single-user use.