# fullhouse Music bingo, generated from a real Spotify playlist. Based on [bingo-bango](https://github.com/f8al/bingo-bango) by **f8al** (MIT licensed) — same core idea (a playlist becomes bingo cards, a square is either an exact song title or an artist, marking a track lights up both the title square and any of that artist's squares), reworked with a real host/guest split, live multiplayer sessions, and cover-art squares. See [PROPOSAL.md](PROPOSAL.md) for the original design writeup. ## Three ways to play 1. **Host a live session** — connect your own Spotify (once, server-side), pick a playlist, get a join code + QR. A caller screen shows what's playing; guests' cards sync automatically, no login required on their end at all. 2. **Join a session** — enter a host's code, get a card, watch it sync. 3. **Play solo** — log into your own Spotify (PKCE, fully client-side, no server involvement) or paste any public playlist URL, get a one-off card with no session. ## Real bugs found and fixed while building this Worth documenting since they weren't obvious and would trip up anyone extending this: - **Spotify quietly renamed `/playlists/{id}/tracks` to `/playlists/{id}/items`**, with a reshaped response (`entry.item` instead of `entry.track`). The old path returns a bare 403 with no useful error message — looks exactly like a permissions problem, isn't one. Confirmed directly against the live API, not assumed from docs. - **Reading playlist tracks needs real user authentication, even for fully public playlists** — a pure Client Credentials (app-only) token gets `401 "Valid user authentication required"` on the `/items` endpoint. The public-playlist-by-URL feature routes through the host's already-authenticated connection instead of a dead-end app token — guests still never log into anything, it's just genuinely user-authenticated on the host's behalf. - **Algorithmic/Spotify-owned editorial playlists** (Discover Weekly, your own "Top 100" style auto-generated lists, etc.) are restricted from third-party API access — this is real and documented (Spotify's Nov 2024 API policy change), not a bug. They just won't appear as usable pools. - **Never point the client at an absolute cross-origin API URL** — it gets silently CORS-blocked with no server-side CORS headers configured, and default error handling can mask that as "not connected" instead of a clear network error. Use relative `/api/*` paths everywhere; Vite's dev proxy and nginx's prod proxy both handle the same-origin routing. - **Vite's dev server can bind to IPv6 loopback (`[::1]`) only**, depending on the host's DNS resolver order — silently unreachable via `127.0.0.1`, which matters a lot here since Spotify's redirect-URI security policy only exempts the literal `127.0.0.1` IP, not `localhost`. `vite.config.ts` sets `server.host` explicitly. ## Local development ```bash # server cd server && npm install && npm run dev # :3010 # client (separate terminal) cd client && npm install && npm run dev # :3080 in prod, :5190 locally ``` Register **four** redirect URIs in the Spotify dashboard — one pair per environment, host mode and guest mode each need their own. Exact string match, trailing slash included: guest mode is `${window.location.origin}/` (`client/src/lib/pkce.ts`) — that trailing slash is not optional, ever, in any environment. | Environment | Host mode (`/api/spotify/callback`) | Guest PKCE mode (root, no path) | |---|---|---| | Local dev | `http://127.0.0.1:3010/api/spotify/callback` | `http://127.0.0.1:5190/` | | Production | `https:///api/spotify/callback` | `https:///` | (`127.0.0.1`, not `localhost`, for local dev — Spotify's HTTPS-required-for-redirect policy only exempts the literal loopback IP.) All four need to actually exist in the dashboard's list — host mode and guest mode failing to match are indistinguishable from the outside ("redirect_uri: Not matching configuration" either way), so if this error shows up, check the browser's actual address bar at the point of failure to see which flow's redirect_uri is actually being sent before assuming which one is missing. ## Deploying `docker compose up` using `docker-compose.yml` / `.env` (copy `.env.example`). Two images: `web` (nginx serving the built client, proxying `/api/*` same-origin to the api container) and `api` (the Express server). CI builds and pushes both on push to `main`. `VITE_SPOTIFY_CLIENT_ID` gets baked into the client at **build time** (Vite env vars are compile-time, not runtime) — passed as a Docker build arg in CI, sourced from a `SPOTIFY_CLIENT_ID` repo secret. ## Status Verified end-to-end against a real Spotify account, not just built and assumed working: host OAuth, real playlist listing and track reading (after finding and fixing the `/items` rename), a live two-browser session with the guest's card syncing on poll, self-mark toggling, guest PKCE login and playlist reading, the public-playlist-by-URL path, and auto-call correctly detecting a real track playing through Spotify Connect within one poll tick. ## License MIT. Retains attribution to f8al's original bingo-bango design.