# VJ Episode Playlist Unification — Kind 30004

## Vision

The VJ page is **always locked to a playlist**. Everything revolves around selecting or creating a playlist, configuring the show, and playing through it. The playlist IS the show — all show metadata lives in the playlist event.

## New 5-Column Layout

```
Col 1: PLAYLISTS       Col 2: SHOW CONFIG      Col 3: CHAT       Col 4: SEARCH        Col 5: SETLIST
- MY PLAYLISTS list    - Stream site selector   - Chat feed       - Search bar         - Active playlist tracks
- Create new playlist  - Video preview          - Chat composer   - Search results     - Reorderable, removable
- Select existing      - Stream health/viewers                    - Albums, artists    - Currently playing highlighted
- Friends playlists    - Show selector                            - Drag/click to      - Player bar at bottom
- AI Import            - Show info fields                           add to setlist     - Edits sync to kind 30004
- Playlist profile     - OBS config                                                   - This IS the queue
                       - Episode config
                       - Go Live / End Stream
                       - Auto announce settings
```

### Key Principles
1. You always start by selecting or creating a playlist in Col 1
2. Col 2 show config is saved INTO the selected playlist event
3. Col 5 Setlist = Queue — they are the same thing, locked to the selected playlist
4. Any edit in Col 5 updates the kind 30004 playlist event
5. Searching in Col 4 and adding songs goes directly to the setlist in Col 5

## Kind 30004 Event Structure

The playlist event carries everything — tracks AND show metadata:

```
kind: 30004
tags:
  [d, {identifier}]                    # playlist identifier
  [title, {playlist/show title}]       # title
  [t, music]                           # standard
  [t, playlist]                        # standard
  [t, show-playlist]                   # marks as show playlist
  [description, {description}]         # optional
  [image, {url}]                       # cover image
  [banner, {url}]                      # banner
  [i, tidal:track:{id}]               # track references - standard format
  [track, {num}, {artist}, {title}, {ref}, {isoTime}]  # extended track info
  # Show-specific tags:
  [show, {slug}]                       # show slug
  [episode, {episodeId}]               # episode timestamp
  [a, 30311:{pubkey}:{slug}]           # stream coordinate
  [status, planned|live|ended]         # episode status
  [episode_description, {text}]        # episode description
  [streaming_url, {url}]              # streaming URL
  [streaming_site, {name}]            # streaming site name
  [viewers, {iso}, {count}, ...]       # viewer snapshots
content: JSON {tracks: [{id, title, artist, duration, cover, albumTitle}]}
```

## Implementation Plan

### Phase 1: Restructure the HTML layout

**File: `www/vj.html`**

#### 1a. Rearrange columns
Current: `vjStreamCard | Chat | musicSidebar | musicMain | musicQueuePanel`
New: `musicSidebar | vjStreamCard | Chat | musicSearch | musicSetlist`

- Col 1 `musicSidebar`: Move to far left. Contains MY PLAYLISTS, friends, AI import, playlist profile card.
- Col 2 `vjStreamCard`: Show config column. Stream site, video, show selector, show info, OBS, episode, controls, auto announce.
- Col 3 `vjChatPanel`: Chat - stays the same.
- Col 4 `musicSearch`: Rename from musicMain. Contains search bar, search results, album/artist detail. Remove playlist track view from here - that moves to Col 5.
- Col 5 `musicSetlist`: Rename from musicQueuePanel. This is the setlist = queue. Locked to the selected playlist. Player bar at bottom.

#### 1b. Update CSS grid
- Reorder `grid-template-columns` to match new column order
- Update `VJ_COLUMN_VAR_NAMES` and `VJ_COLUMN_MIN_WIDTHS` arrays
- Update gutter indices

### Phase 2: Make Setlist = Queue = Playlist

**File: `www/vj.html`**

#### 2a. Lock queue to selected playlist
- When a playlist is selected in Col 1, its tracks populate the setlist/queue in Col 5
- Add `lockedPlaylistId` state variable
- Modify `renderQueue()` to show the locked playlist's tracks
- Modify queue mutation functions to also update the playlist:
  - `addTrackToQueue()` → also adds to playlist
  - `removeFromQueue()` → also removes from playlist
  - `reorderQueue()` → also reorders playlist
  - After each mutation, call `publishPlaylistRecord()` to sync to relay

#### 2b. Playing from setlist
- `playQueueAt()` already works — it plays from the queue array
- Since queue = playlist tracks, playing works naturally
- `onTrackChanged` callback triggers auto-announce as before

#### 2c. Adding from search
- When user clicks "Add to queue" or drags from search results in Col 4, it adds to both queue and playlist
- Remove the separate "Add to playlist" action — adding to queue IS adding to playlist

### Phase 3: Publish kind 30004 with show metadata

**File: `www/js/vj-stream.mjs`**

#### 3a. Change playlist publishing to kind 30004
- Replace `PLAYLIST_KIND = 30078` with `MUSIC_PLAYLIST_KIND = 30004`
- Keep `LEGACY_PLAYLIST_KIND = 30078` for reading old events
- `buildPlaylistTags()` produces kind 30004 format with show extension tags
- `publishPlaylistEvent()` publishes kind 30004

#### 3b. Save show config into playlist event
- Show title, summary, image, episode description, streaming URL, streaming site — all saved as tags on the kind 30004 event
- When show config changes, republish the playlist event
- When loading a playlist, extract show config from its tags

#### 3c. Load playlist as show
- New function: `loadShowFromPlaylist(playlist)` 
  - Reads show tags from the playlist event
  - Populates stream config fields
  - Sets episode ID
  - Populates queue/setlist

#### 3d. Expose playlist sync API
- `getActivePlaylist()` — returns current playlist object
- `syncQueueToPlaylist()` — writes queue state back to playlist
- `onSetlistChange` callback — notifies vj.html when setlist changes

### Phase 4: Subscription and hydration

**File: `www/js/vj-stream.mjs`**

#### 4a. Subscribe to show playlists
- Query kind 30004 with `#t: [show-playlist]` and `#show: [streamDTag]`
- Also query kind 30078 for backward compat

#### 4b. Hydrate from incoming events
- When a kind 30004 event with `show-playlist` tag arrives, parse tracks and show config
- Update setlist UI in real-time

### Phase 5: Backward compatibility

#### 5a. Read old kind 30078 events
- `handleIncomingEvent()` still accepts kind 30078
- `subscribeToPlaylist()` queries both kinds
- vj-playlist.html continues to work

#### 5b. Migration path
- Old 30078 events are read-only — never updated
- All new publishes use kind 30004
- A playlist created from an old 30078 event gets a new kind 30004 identity

## Files to Modify

| File | Changes |
|------|---------|
| `www/vj.html` | Rearrange 5-column layout, lock queue to playlist, merge queue+playlist mutations |
| `www/js/vj-stream.mjs` | Publish kind 30004, save show config in playlist, load show from playlist |
| `www/js/greyscale-playlists.mjs` | No changes needed |

## User Flow

### Starting a new show
1. Click "Create new playlist" in Col 1 → name it "Saturday Reggae Ep 42"
2. Search for songs in Col 4 → add them to setlist in Col 5
3. Configure show in Col 2 (stream site, OBS key, etc.)
4. Click "Go Live" → stream starts, auto-announce begins
5. As songs play, they get announced in chat
6. Add more songs from search during the show
7. Reorder upcoming songs in the setlist
8. Click "End Stream" → playlist is complete, ready to share

### Playing a pre-existing playlist
1. Click an existing playlist in Col 1 → its tracks load into Col 5 setlist
2. Show config loads from the playlist's tags (if it was a previous show playlist)
3. Hit play → songs play through the setlist
4. Optionally go live to stream it

### Editing during a show
1. Drag to reorder songs in Col 5 → playlist event updates
2. Click X to remove a song → playlist event updates
3. Search and add new songs from Col 4 → playlist event updates
4. All changes are live and published
