# VJ Playlist Page Plan

## Overview

Add playlist browsing functionality to `www/vj-playlist.html` (duplicated from template). The page will query Nostr events to discover past livestream shows and display their episode playlists (tracklists).

## Data Model (Existing Events from vj-stream.mjs)

```mermaid
graph TD
    A[Show - kind 30311] -->|d tag = show slug| B[Episode]
    B -->|episode tag = timestamp ID| C[Playlist - kind 30078]
    C -->|d tag = show-playlist:slug:episodeId| D[Track Tags]
    C -->|viewers tags| E[Viewer Snapshots]
    A -->|a tag coordinate| F[30311:pubkey:slug]
    C -->|a tag = stream coordinate| F
```

### Kind 30311 — Stream/Show Metadata
- `d` tag: show slug (e.g. `my-show`)
- `title`: show title
- `status`: planned / live / ended
- `episode`: episode ID (unix timestamp string)
- `episode_description`: episode description
- `image`: show image URL
- `streaming`: HLS playlist URL
- `web`: viewer page URL

### Kind 30078 — Playlist Event
- `d` tag: `show-playlist:{slug}:{episodeId}`
- `show` tag: show slug
- `episode` tag: episode ID
- `a` tag: stream coordinate (`30311:{pubkey}:{slug}`)
- `title` tag: e.g. `My Show Playlist`
- `status` tag: `live` or `ended`
- `episode_description` tag: optional
- `track` tags: `[track, trackNumber, artist, title, nostrRef, isoTimestamp]`
- `viewers` tags: `[viewers, isoTimestamp, count, variant:count...]`

## Architecture

The page keeps all existing template functionality (auth, sidenav, hamburger, relay UI, etc.) and adds playlist browsing in the `#divBody` area.

### Query Strategy

1. **Discover shows**: Subscribe to kind 30311 events authored by the target pubkey to find all shows
2. **Fetch playlists**: Query kind 30078 events authored by the target pubkey, filtering by `#show` tag when a show is selected
3. **Parse and display**: Extract `track` tags from playlist events and render as tracklists grouped by episode

### Auth Mode

- Default to `optional` auth mode (supports `?npub=` or `?pubkey=` URL params for viewing other users' playlists)
- If no target pubkey in URL, use the logged-in user's pubkey
- Public browsing works without login

## UI Layout

```
┌─────────────────────────────────────────────┐
│ HEADER: VJ PLAYLISTS                        │
├─────────────────────────────────────────────┤
│                                             │
│  [Show Selector Dropdown ▼]                 │
│                                             │
│  ┌─ Episode Card ─────────────────────────┐ │
│  │ Episode: 2026-04-06 • Status: ended    │ │
│  │ Description: Saturday night session    │ │
│  │ 12 tracks • 45 viewers peak            │ │
│  │                                         │ │
│  │  # │ Artist        │ Title      │ Time  │ │
│  │ 001│ Artist Name   │ Song Title │ 20:15 │ │
│  │ 002│ Artist Name   │ Song Title │ 20:19 │ │
│  │ 003│ Artist Name   │ Song Title │ 20:23 │ │
│  │  ...                                    │ │
│  └─────────────────────────────────────────┘ │
│                                             │
│  ┌─ Episode Card ─────────────────────────┐ │
│  │ Episode: 2026-04-05 • Status: ended    │ │
│  │ (collapsed - click to expand)           │ │
│  └─────────────────────────────────────────┘ │
│                                             │
├─────────────────────────────────────────────┤
│ FOOTER                                      │
└─────────────────────────────────────────────┘
```

## Implementation Steps

### 1. HTML Structure (in divBody)
Add to `#divBody`:
- Show selector dropdown
- Loading indicator
- Container for episode playlist cards
- Empty state message

### 2. Imports
Add to the existing module script:
- `queryCache` and `ndkFetchEvents` from `init-ndk.mjs` (for fetching events)
- `subscribe` is already imported

### 3. Show Discovery Logic
- On page load, determine target pubkey (from URL params or logged-in user)
- Query kind 30311 events for that pubkey
- Populate show selector dropdown with discovered shows
- Auto-select if only one show exists

### 4. Playlist Fetch Logic
- When a show is selected, query kind 30078 events with `authors: [targetPubkey]`
- Filter client-side by `show` tag matching selected show slug
- Group by episode (from `d` tag pattern `show-playlist:{slug}:{episodeId}`)
- Sort episodes by `created_at` descending (newest first)
- For addressable events (kind 30078), only the latest version per `d` tag is kept

### 5. Playlist Rendering
- Render each episode as a collapsible card
- Parse `track` tags into a table: track number, artist, title, timestamp
- Parse `viewers` tags to show peak viewer count
- Show episode metadata: episode ID (formatted as date), status, description
- First/newest episode expanded by default, rest collapsed

### 6. Styling
- Episode cards styled consistently with existing app design (border, border-radius, secondary-color background)
- Track table with monospace numbering
- Responsive: stack on mobile
- Dark mode compatible via CSS variables

### 7. URL Parameter Support
- `?npub=...` or `?pubkey=...` — view another user's playlists
- `?show=...` — pre-select a specific show slug
- Auth mode defaults to `optional` when target pubkey is in URL

## Files Modified

| File | Changes |
|------|---------|
| `www/vj-playlist.html` | Add HTML structure, styles, and JS logic for playlist browsing |

No new JS modules needed — all logic fits inline in the page script, using existing `init-ndk.mjs` exports (`subscribe`, `queryCache`, `ndkFetchEvents`).
