Music battles with stages, submissions, voting, and Spotify playlist generation.
This app uses:
- SvelteKit for UI
- Convex for database, functions, cron, and actions
- A central Spotify app/account to generate playlists for each stage
- Node 18+
- A Convex project (free tier is fine)
- A Spotify Developer app (created in the Spotify Dashboard)
Install deps and run the dev server:
npm install
npm run devSet the Convex URL for the client (Svelte) by exporting PUBLIC_CONVEX_URL:
# .env.local (SvelteKit)
PUBLIC_CONVEX_URL="https://YOUR-CONVEX-DEPLOYMENT.convex.cloud"You can preview a production build with:
npm run build && npm run previewLog in and link your local repo to your Convex project (or create a new one):
npx convex dev
# or
npx convex dashboardConvex environment variables are separate from Svelte’s .env. Anything used by Convex functions/actions must be set in the Convex env via npx convex env set or the dashboard.
Playlists are created under a single "vsvs" Spotify account in the background. Users do not authorize with Spotify.
You need:
- Spotify app Client ID and Client Secret
- A long‑lived Refresh Token for the vsvs account (one‑time OAuth)
Then set these in the Convex environment (not Svelte .env).
- Go to https://developer.spotify.com/dashboard and open your app.
- In Settings → Redirect URIs, add an exact URI you can use locally, e.g.
http://localhost:3000/auth/spotify/callback
- Note the Client ID and Client Secret.
This authorizes the vsvs Spotify account once and gives you a refresh token you can store in Convex.
-
Build an authorization URL (replace placeholders and URL‑encode the redirect URI):
https://accounts.spotify.com/authorize? client_id=YOUR_CLIENT_ID& response_type=code& redirect_uri=YOUR_ENCODED_REDIRECT_URI& scope=playlist-modify-public%20playlist-modify-private& show_dialog=true -
Open it in a browser, log in as the vsvs account (the playlist owner), and approve.
-
Copy the
codequery parameter from the redirect. -
Exchange the code for tokens:
BASIC=$(printf "%s:%s" "YOUR_CLIENT_ID" "YOUR_CLIENT_SECRET" | base64) curl -s -X POST https://accounts.spotify.com/api/token \ -H "Authorization: Basic $BASIC" \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "grant_type=authorization_code" \ --data-urlencode "code=YOUR_CODE_FROM_REDIRECT" \ --data-urlencode "redirect_uri=YOUR_REDIRECT_URI"
The response includes
access_token(short‑lived) andrefresh_token(long‑lived).
Troubleshooting the exchange:
invalid_grant: Theredirect_urimust exactly match the one used in the authorize URL and app settings; the code may be expired/used; double‑check client ID/secret.- No refresh_token: Add
show_dialog=true, revoke the app at https://www.spotify.com/account/apps/, and re‑authorize.
Set credentials and the refresh token in Convex env (required by actions):
npx convex env set SPOTIFY_CLIENT_ID "YOUR_CLIENT_ID"
npx convex env set SPOTIFY_CLIENT_SECRET "YOUR_CLIENT_SECRET"
npx convex env set SPOTIFY_REFRESH_TOKEN "YOUR_REFRESH_TOKEN"Optional: You can also set a temporary SPOTIFY_ACCESS_TOKEN for quick tests (expires in ~1 hour). The app will prefer the refresh token and auto‑refresh an access token on demand.
- Automatic: When a stage switches from Submission → Voting, a Convex action creates a Spotify playlist under the vsvs account, adds tracks sorted by stars, and stores the
playlistUrlon the stage. - Manual (for testing): On the battle page, the creator sees a “Generate Playlist Now” button in the Current Session section.
- On success, the playlist URL displays immediately.
Notes:
- Convex actions read env from the Convex project, not Svelte
.env. - Playlists are created via
POST /v1/me/playlistsfor the authorized account. - Only the battle creator can manually trigger generation.
- Convex logs: Check your Convex project dashboard → Logs for messages from actions (errors, token refreshes, API responses).
- Common errors:
- "Spotify token unavailable": Set
SPOTIFY_REFRESH_TOKENin Convex env or temporarilySPOTIFY_ACCESS_TOKEN. - 401 Unauthorized: Usually an expired/invalid access token; ensure the refresh token is present and valid; scopes must include
playlist-modify-public(andplaylist-modify-privateif needed).
- "Spotify token unavailable": Set
- Battles: create/join via invite code, max players, visibility
- Stages: creator adds stages with explicit deadlines; auto‑advance phases
- Submissions: per stage, single/double mode, Spotify URL validation
- Voting: 3‑star system; auto finish on all votes or at deadline; tie handling
- Spotify: playlist per stage using the vsvs account
npm run dev # Start Svelte dev server
npm run build # Build
npm run preview # Preview production build
npm run lint # Lint
npm run test # Unit tests