|
| 1 | +# CLAUDE.md — Spotifort Project Brief |
| 2 | + |
| 3 | +## Project Overview |
| 4 | + |
| 5 | +**Spotifort** is a client-side web application that matches a user's Spotify Liked Songs against the Treefort Music Fest 2026 lineup (March 25–29, Boise, Idaho). It identifies which artists the user already likes that are playing the festival. |
| 6 | + |
| 7 | +- **Live site:** https://spotifort.com |
| 8 | +- **Repo:** https://github.com/commitconfirm/spotifort |
| 9 | +- **Project manager thread:** This project is managed via a dedicated Claude.ai chat thread. Architectural decisions, scope changes, and priorities are tracked there. |
| 10 | + |
| 11 | +## Architecture & Constraints |
| 12 | + |
| 13 | +### Client-Side Only — No Backend |
| 14 | + |
| 15 | +This is a **privacy-first** application. All processing happens in the browser. There is no server, no database, no analytics, no cookies, no tracking. The Spotify access token lives in a JS variable in memory and is never persisted to localStorage, sessionStorage, or any storage API. When the user closes the tab, everything is gone. |
| 16 | + |
| 17 | +### Spotify API — PKCE Auth Flow |
| 18 | + |
| 19 | +- **Auth method:** Authorization Code with PKCE (no client secret) |
| 20 | +- **Scopes required:** `user-library-read` (for Liked Songs) |
| 21 | +- **Future scope:** `user-top-read` (for top artists feature in List B — not in MVP) |
| 22 | +- **Dev redirect URI:** `http://127.0.0.1:9090/callback` |
| 23 | +- **Prod redirect URI:** `https://spotifort.com/callback` |
| 24 | +- **Client ID:** Stored in `.env.local` as `VITE_SPOTIFY_CLIENT_ID` (not committed to repo) |
| 25 | + |
| 26 | +### Spotify API Restrictions (February 2026 Dev Mode Changes) |
| 27 | + |
| 28 | +Development Mode is limited to 5 authorized users. The app owner must have Spotify Premium. Key constraints: |
| 29 | +- Playlist items are only returned for playlists the user owns/collaborates on (cannot read the official Treefort Spotify playlist via API) |
| 30 | +- Several endpoints were removed (bulk metadata, new releases, etc.) |
| 31 | +- Still available and used by this project: `GET /me/tracks`, `GET /artists/{id}/related-artists`, `GET /me/top/artists`, `GET /artists/{id}` |
| 32 | + |
| 33 | +Future plan: Transition to "Bring Your Own Client ID" (Option A) so any user with Spotify Premium can use the app without being added to our allowlist. This means the UI will eventually need a Client ID input field with a link to setup instructions. |
| 34 | + |
| 35 | +### Treefort Lineup Data |
| 36 | + |
| 37 | +The lineup is stored as a **static JSON file** (`public/lineup.json`) maintained manually. This is intentional — API restrictions prevent reading the official Treefort Spotify playlist, and scraping the Treefort website is fragile. |
| 38 | + |
| 39 | +The JSON file should include: |
| 40 | +- `lastUpdated` — ISO date string, displayed on the site |
| 41 | +- `artists` — array of objects, each with: `name`, `spotifyId` (nullable), `spotifyUrl` (nullable), `notOnSpotify` (boolean) |
| 42 | + |
| 43 | +Matching is done on `spotifyId`, NOT string comparison of names. This avoids issues with artist name variants. |
| 44 | + |
| 45 | +## Tech Stack |
| 46 | + |
| 47 | +- **Language:** Vanilla JavaScript (no TypeScript, no framework) |
| 48 | +- **Build tool:** Vite |
| 49 | +- **Styling:** Plain CSS (no Tailwind, no CSS framework) |
| 50 | +- **Font:** Ubuntu (Google Fonts) for headings, bold; system sans-serif for body |
| 51 | +- **Hosting:** Cloudflare Pages (auto-deploy from `main` branch) |
| 52 | +- **Dev server port:** 9090 (configure in `vite.config.js`) |
| 53 | + |
| 54 | +## Project Structure |
| 55 | + |
| 56 | +``` |
| 57 | +spotifort/ |
| 58 | +├── public/ |
| 59 | +│ └── lineup.json # Treefort 2026 artist data (manual) |
| 60 | +├── src/ |
| 61 | +│ ├── index.html # Entry point |
| 62 | +│ ├── main.js # App initialization, orchestration |
| 63 | +│ ├── auth.js # Spotify PKCE auth flow |
| 64 | +│ ├── spotify.js # Spotify API calls |
| 65 | +│ ├── matcher.js # Matching logic (liked songs vs lineup) |
| 66 | +│ ├── ui.js # DOM rendering and interaction |
| 67 | +│ └── style.css # All styles |
| 68 | +├── scripts/ |
| 69 | +│ └── fetch-lineup.js # One-time utility to generate lineup.json (not part of app) |
| 70 | +├── .env.local # VITE_SPOTIFY_CLIENT_ID (gitignored) |
| 71 | +├── .gitignore |
| 72 | +├── CLAUDE.md |
| 73 | +├── LICENSE # MIT |
| 74 | +├── README.md |
| 75 | +├── package.json |
| 76 | +└── vite.config.js |
| 77 | +``` |
| 78 | + |
| 79 | +## MVP Scope (List A Only) |
| 80 | + |
| 81 | +The MVP delivers one core feature: show the user which artists from their Liked Songs are playing Treefort 2026. |
| 82 | + |
| 83 | +### User Flow |
| 84 | + |
| 85 | +1. User lands on spotifort.com |
| 86 | +2. User sees a brief explanation of what the app does, disclaimers, and a "Connect Spotify" button |
| 87 | +3. User clicks the button → redirected to Spotify auth → grants `user-library-read` permission |
| 88 | +4. Redirected back to spotifort.com/callback with auth code |
| 89 | +5. App exchanges code for access token (PKCE), stores in memory |
| 90 | +6. App fetches all Liked Songs (paginated, 50/request), extracts unique artist IDs |
| 91 | +7. App loads `lineup.json`, matches artist IDs |
| 92 | +8. App displays the matched artists in List A |
| 93 | + |
| 94 | +### UI Design |
| 95 | + |
| 96 | +- **Color scheme:** Black and white only |
| 97 | +- **Typography:** Ubuntu font (Google Fonts), bold, for all headings/titles. System sans-serif for body. |
| 98 | +- **Layout:** Two-column on desktop (CSS Grid or Flexbox), single column stacked on mobile. Breakpoint ~768px. |
| 99 | +- **Lists:** Displayed in boxes with thick black borders |
| 100 | +- **List items:** Each artist is a bullet with `[+]` as the marker, which is clickable. In MVP, `[+]` links to the artist's Spotify page. In future, it expands to show similar artists at Treefort. |
| 101 | +- **Bottom section:** List of Treefort artists not found on Spotify (from `lineup.json` where `notOnSpotify: true`) |
| 102 | + |
| 103 | +### Required Disclaimers (visible on the page) |
| 104 | + |
| 105 | +- "Not affiliated with, endorsed by, or associated with Treefort Music Fest or Spotify" |
| 106 | +- "Lineup data last updated: [date from lineup.json]" |
| 107 | +- "Use at your own risk — no guarantees of accuracy" |
| 108 | +- "FOSS — MIT License" with link to repo |
| 109 | +- "Built with Claude Code" with link |
| 110 | +- Spotify logo/attribution per their branding requirements |
| 111 | + |
| 112 | +## Logging |
| 113 | + |
| 114 | +Use a simple logging utility gated on `import.meta.env.DEV`: |
| 115 | + |
| 116 | +```javascript |
| 117 | +const log = { |
| 118 | + info: (...args) => import.meta.env.DEV && console.log('[spotifort]', ...args), |
| 119 | + warn: (...args) => import.meta.env.DEV && console.warn('[spotifort]', ...args), |
| 120 | + error: (...args) => console.error('[spotifort]', ...args), // errors always log |
| 121 | +}; |
| 122 | +``` |
| 123 | + |
| 124 | +Verbose logging in dev (auth flow steps, API call counts, match results). Silent in production except for errors. |
| 125 | + |
| 126 | +## Future Enhancements (NOT in MVP) |
| 127 | + |
| 128 | +These are tracked for later and should NOT be built yet: |
| 129 | + |
| 130 | +- **List B:** Top artists from user's listening history that aren't at Treefort, with similar artists who ARE at Treefort (uses `GET /me/top/artists` + `GET /artists/{id}/related-artists`) |
| 131 | +- **[+] expansion:** Click `[+]` on any artist to expand and show similar artists also playing Treefort |
| 132 | +- **Bring Your Own Client ID:** UI for users to enter their own Spotify Client ID, with a setup guide page |
| 133 | +- **Schedule/venue data:** When bands are playing and where during Treefort |
| 134 | +- **Bandcamp/Soundcloud:** Support for artists not on Spotify |
| 135 | +- **Rate limiting / progress UI:** Progress bar for users with large libraries |
| 136 | + |
| 137 | +## Commands |
| 138 | + |
| 139 | +```bash |
| 140 | +# Install dependencies |
| 141 | +npm install |
| 142 | + |
| 143 | +# Run dev server (port 9090) |
| 144 | +npm run dev |
| 145 | + |
| 146 | +# Build for production |
| 147 | +npm run build |
| 148 | + |
| 149 | +# Preview production build locally |
| 150 | +npm run preview |
| 151 | +``` |
| 152 | + |
| 153 | +## Key Decisions Log |
| 154 | + |
| 155 | +1. **Client-side only** — privacy-first, no backend, no data storage |
| 156 | +2. **PKCE auth** — recommended by Spotify for SPAs, no client secret needed |
| 157 | +3. **Static lineup.json** — more reliable than scraping or API calls to third-party playlists |
| 158 | +4. **Match on Spotify artist IDs** — not string names, to avoid mismatches |
| 159 | +5. **Vanilla JS + Vite** — minimal dependencies, fast builds, simple |
| 160 | +6. **spotifort.com** — standalone domain, Cloudflare Pages |
| 161 | +7. **MVP = List A only** — ship matches before Treefort starts March 25 |
| 162 | +8. **Option B → Option A** — use own Client ID for dev, transition to BYOCID for public launch |
0 commit comments