8.0 KiB
8.0 KiB
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
- You always start by selecting or creating a playlist in Col 1
- Col 2 show config is saved INTO the selected playlist event
- Col 5 Setlist = Queue — they are the same thing, locked to the selected playlist
- Any edit in Col 5 updates the kind 30004 playlist event
- 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-columnsto match new column order - Update
VJ_COLUMN_VAR_NAMESandVJ_COLUMN_MIN_WIDTHSarrays - 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
lockedPlaylistIdstate variable - Modify
renderQueue()to show the locked playlist's tracks - Modify queue mutation functions to also update the playlist:
addTrackToQueue()→ also adds to playlistremoveFromQueue()→ also removes from playlistreorderQueue()→ 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
onTrackChangedcallback 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 = 30078withMUSIC_PLAYLIST_KIND = 30004 - Keep
LEGACY_PLAYLIST_KIND = 30078for reading old events buildPlaylistTags()produces kind 30004 format with show extension tagspublishPlaylistEvent()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 objectsyncQueueToPlaylist()— writes queue state back to playlistonSetlistChangecallback — 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-playlisttag 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 30078subscribeToPlaylist()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
- Click "Create new playlist" in Col 1 → name it "Saturday Reggae Ep 42"
- Search for songs in Col 4 → add them to setlist in Col 5
- Configure show in Col 2 (stream site, OBS key, etc.)
- Click "Go Live" → stream starts, auto-announce begins
- As songs play, they get announced in chat
- Add more songs from search during the show
- Reorder upcoming songs in the setlist
- Click "End Stream" → playlist is complete, ready to share
Playing a pre-existing playlist
- Click an existing playlist in Col 1 → its tracks load into Col 5 setlist
- Show config loads from the playlist's tags (if it was a previous show playlist)
- Hit play → songs play through the setlist
- Optionally go live to stream it
Editing during a show
- Drag to reorder songs in Col 5 → playlist event updates
- Click X to remove a song → playlist event updates
- Search and add new songs from Col 4 → playlist event updates
- All changes are live and published