Files
fullhouse/PROPOSAL.md

118 lines
6.2 KiB
Markdown
Raw Normal View History

# Proposal: `fullhouse` — music bingo, live-session and self-service
**Status:** proposed, build in progress. Self-contained — written so a fresh
agent with no prior conversation context can pick this up and implement it
without anything explained first.
## Origin & attribution
Inspired by [bingo-bango](https://github.com/f8al/bingo-bango) by **f8al**
a fully client-side (Spotify PKCE, no backend) music bingo card generator.
`fullhouse` keeps the core idea (Spotify playlist → bingo cards, "both
facets" title+artist marking) and the card-generation algorithm's shape, but
changes the architecture significantly enough that it's a fresh build, not a
fork: a real server-side host mode, live multiplayer sessions, cover-art
squares, and heavier use of the Spotify API. MIT license from the original
requires the copyright notice + license text be retained wherever the
original code/algorithm is directly reused — credit f8al clearly in the
README and footer regardless of how much code ends up shared verbatim.
## Motivation
The original requires *every player* to individually log into Spotify to
pick a playlist — real friction at an actual game night where guests don't
want to OAuth their own account just to get a bingo card. The fix: a proper
host/guest split.
## Modes
Both exist simultaneously, chosen per session, not one replacing the other:
1. **Host mode** — the host (the site owner) has a real server-side Spotify
connection (same shape as `goonk`'s existing `spotify.mjs`: Authorization
Code flow, refresh token persisted server-side, no per-visitor login).
Host picks from *their own* playlists (`playlist-read-private` +
`playlist-read-collaborative` scopes) as the song pool for a session.
2. **Guest self-service mode** — the original flow, kept: a visitor with no
relationship to the host logs in via their own Spotify PKCE flow and
generates a card from their own playlist, standalone, no session/host
involved. Also supports pasting a public playlist URL with **no login at
all**, via an app-level Client Credentials token (public playlists don't
need user auth to read) — simpler than PKCE for the common "just grab a
public playlist" case, and something the original doesn't offer.
## The four differentiators (all in scope for this build)
1. **Host-curated mode** — described above. Solves the real friction.
2. **Live session mode** — host starts a session, gets a join code + QR.
Guests join via the code (no login required in a host-curated session —
the host already authorized the song pool), each gets an independently
seeded card. A "caller" screen shows the current track large, for the
host to read aloud or leave visible on a shared screen. State sync is
**polling**, not WebSockets — deliberately, matching this whole family's
precedent (`keep watch`, `wisp`'s sweep): a session isn't latency-
sensitive enough to justify the operational complexity of a socket
server. Poll every 3-5s for "what's been called."
3. **Live now-playing auto-call** — if the host is actually playing the
session's pool through Spotify Connect, poll `/me/player/currently-
playing` (same pattern as `goonk`'s `now-playing` route) and
auto-mark a track "called" the moment it starts, matched against the
pool by track ID. Falls back to a manual "call next" button (classic
random-draw-without-replacement bingo caller) for hosts not using live
Spotify Connect playback during the game — auto-call is a bonus layer
over manual, not a replacement for it.
4. **Cover-art squares** — squares can show album art (from the same
playlist-track API response, `track.album.images`, already fetched for
everything else) instead of, or alongside, text. A visual variant beyond
what the original offers.
## Architecture
**Server** (small Express app, same shape as `goonk`'s `api/`):
- Host OAuth: `/auth/login`, `/auth/callback`, refresh-token persistence —
ported near-verbatim in pattern (not code) from `goonk/api/routes/
spotify.mjs`, since that flow is already proven correct in this family.
- `GET /api/host/playlists` — the host's own playlists, cached.
- `GET /api/host/playlist/:id/tracks` — full track+artist+album-art data
for a chosen playlist, cached.
- `GET /api/public-playlist?url=` — Client Credentials app token, fetch a
*public* playlist's tracks with no user login. Client Credentials tokens
are cached in memory and refreshed on expiry, same shape as the existing
user-token cache pattern.
- Session state: `POST /api/session` (host creates one from a chosen
playlist + mode), `GET /api/session/:code` (guest join — hands back pool
metadata, NOT the host's Spotify tokens), `POST /api/session/:code/call`
(host marks a track called, or the auto-call poller does), `GET /api/
session/:code/state` (guests poll this for the called-track list).
In-memory session store is fine — sessions are ephemeral (a single game
night), no need for persistence across restarts.
- `GET /api/host/now-playing` — thin wrapper the auto-call poller uses,
same call shape as `goonk`'s existing now-playing route.
**Client**: Vite + TypeScript, no framework — matches the `typo`/`latent`
family's "plain, no heavy build" ethos more than the original's React
choice. The card-generation engine is a pure, dependency-free TS module
(same shape as the original's `src/cards/`), extended with a `coverArt`
render mode alongside the text mode.
**Guest PKCE mode** stays fully client-side (no server involvement at all)
for the self-service path — same as the original, since that's the whole
point of PKCE (no secret needed, tokens never touch a server the host
controls).
## Theming
Dark/mono/green house style shared with `typo`/`flit`/`wisp` (see any of
those `style.css` files for the exact CSS custom properties), not the
original's own visual design. Reads as part of this family, not a
standalone fork.
## Non-goals
- No accounts beyond the host's own Spotify connection — guests never
create anything, a join code is the only credential a session needs.
- No persistent session history — a session lives in memory for its game
night and is gone after.
- Not trying to be a general Spotify app — scoped tightly to bingo card
generation + live session calling.