A cinematic Drake discovery app: four rooms over a persistent Toronto nightscape, connected by a branching timeline of eras, records, tracks, artists, and milestones. The app preserves the original 414-track collection, 117 releases, 104 credited artists, and six career chapters.
Use Node 24 LTS (or Node 22+), then:
npm ci
npm run dev
Open http://127.0.0.1:3000. No service credentials are needed to explore the public app. It runs its SQL queries against an embedded, in-memory PostgreSQL database (PGlite), seeded from the versioned catalog. The database is rebuilt when that server process restarts. This mode has no curator access or downloadable audio.
The former static index.html has been replaced by Next.js routes. npm run build and npm start provide the production server; opening a local HTML file is no longer the entrypoint.
/ — four entrances; persistent room switcher, skyline, and effects control./records — featured projects or all releases, with /records/[id] detail pages./eras — six chapters with their own /eras/[id] stories and records./listening-room — server-side search, mood/release/era/explicit filters, sorting, pagination, and browser-local favorites. Press / to search./tracks/[spotifyId] — musical traits, related tracks with explanations, Spotify, and published MP3 downloads./legacy — sourced stories and milestones./connections — an expandable chronological graph with an equivalent list view. Tab to a node and press Enter or Space to expand it. Optional ?root=era:the-blue-hour, release:take-care-deluxe, or track:<Spotify ID> opens a neighborhood./admin — protected metadata, drafts, publishing, connections, and audio management when hosted services are configured.Spotify loads only after Listen is selected. Its iframe remains mounted during room navigation and while the tray is collapsed; closing the tray removes it. Playback availability depends on Spotify and the visitor’s account/browser. External Spotify and Apple Music links remain available. No audio is autoplayed or extracted from Spotify.
Favorites reuse the existing weloveovo.favorites storage key and Spotify IDs. If storage is blocked, they work for the current visit. Effects honor reduced motion, pause when the page is hidden, and can be disabled independently. There are no public accounts, comments, ratings, locked chapters, or progress gates.
flowchart LR
Visitor[Public rooms and interactions] --> Next[Next.js server pages and read APIs]
Next --> Catalog[Shared SQL catalog services]
Catalog --> DB[(Supabase PostgreSQL)]
Catalog -. local development .-> Local[(PGlite)]
Curator[Curator studio] --> Auth[Supabase Auth and server authorization]
Auth --> Editing[Validated edits and publication]
Editing --> DB
Curator -->|Signed resumable uploads| Storage[Private audio storage]
Next -->|Published track download link| Storage
Visitor -->|User initiated| Spotify[Official Spotify embed]
raw.curators. Every mutation validates the origin and authorization. Public/authenticated direct table writes are denied; privileged keys stay in server modules..env.example to .env.local and set:
DATABASE_URL: the project’s PostgreSQL connection string. A session/transaction pooler connection is supported; prepared statements are disabled.NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY.SUPABASE_SERVICE_ROLE_KEY: server only; never prefix this with NEXT_PUBLIC_.CURATOR_EMAILS: comma-separated invited email addresses.NEXT_PUBLIC_SITE_URL: the exact origin used in the browser, such as http://127.0.0.1:3000 locally or your production HTTPS origin.Apply schema and policies, then import the catalog:
npm run db:migrate
npm run db:seed
In Supabase Auth, disable public signups and invite/create your curator user. Add its UUID with the SQL editor:
insert into public.curators (user_id)
values ('YOUR-INVITED-USER-UUID')
on conflict do nothing;
Add the app origin to the Supabase Site URL and allow the /auth/confirm callback. Configure the magic-link email template to use:
/auth/confirm?token_hash=&type=email
/admin, request a sign-in link, and use the link in your email. The app never creates users through the public login form.The migration creates a private track-audio bucket with a 100 MB per-file limit and MP3 MIME restriction. Supabase project-level storage limits must also permit your file sizes. Authenticated visitors cannot grant themselves curator status. Both database and bucket access policies are versioned with the schema.
.mp3 filename. Unpublish removes the download from the app immediately; an already-issued URL can remain usable for up to 60 seconds.Use audio you are authorized to distribute. Spotify embeds and supplied download files are separate sources. No empty links are shown for tracks without a published file. Curator uploads require a configured Supabase project; this repository does not include the owner’s audio collection.
| Endpoint | Behavior |
|---|---|
GET /api/tracks |
Search using q, release, artist, era, year, mood, clean, sort, page, limit, and optional comma-separated favorite ids. Returns tracks, total, page, pages, and limit. |
GET /api/tracks/:id/related |
Related tracks with deterministic scores and explanations. |
GET /api/graph?root=… |
Nodes and labeled edges for an era, release, or track neighborhood. |
GET /api/tracks/:id/download |
Redirect to a signed attachment URL for published audio; 404 when unavailable. |
Search limits are validated: page size 1–50, query length up to 120 characters, and an explicit sort/mood allowlist. Bad filters return 400; missing tracks return 404; unexpected service failures return a recoverable 503. Admin JSON endpoints under /api/admin are session-protected and are not public content APIs.
The download endpoint also accepts Accept: application/json to return { url } without redirecting. The track page uses this to display retry feedback if signing fails; the MP3 still downloads directly from storage.
npm run typecheck
npm test
npm run test:ui
npm run build
npm run test:e2e
npm run format:check
Backend tests run real PostgreSQL queries through PGlite, including metadata preservation, search, imports, draft visibility, publication transitions, and access policies with isolated Auth/Storage schema fixtures. Audio tests parse synthetic MPEG frames and exercise storage failure/retry and signed-download behavior with a test provider; they do not claim to verify a live Supabase account. Curator component tests cover matching review, resumed transfers, verification retries after reload, and explicit editorial/audio publication.
Playwright covers direct routes, history, URL filters, favorite migration/persistence, map/list navigation, persistent embeds, mobile widths, reduced motion, and anonymous access rejection. It uses installed Google Chrome on macOS when available; elsewhere run npx playwright install chromium. Set PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH to use another Chrome/Chromium installation. Visual captures and failure traces go into the ignored test-results/ directory.
To run these browser checks against the production build, stop other servers on port 3000, run npm run build, then PLAYWRIGHT_PRODUCTION=true npm run test:e2e.
NEXT_PUBLIC_SITE_URL. Apply migrations and seed once to the connected Supabase project before enabling curator access.Without hosted credentials the public app runs the bundled PostgreSQL catalog; curator features stay unavailable. For the full hosted experience, monitor Vercel function logs and Supabase database, authentication, storage, and egress usage. Service provisioning, SMTP delivery, and real audio availability depend on the owner’s configured accounts and supplied files.
The collection reflects the supplied library, not a claim to contain every recording or every version. data/tracks.json preserves the original source and data/catalog.json is its normalized seed. To add catalog content through version control, maintain the normalized seed and run the idempotent import; public metadata can be edited through the curator studio.
Independent fan-made project. Not affiliated with Drake, OVO, Spotify, or Apple Music.