Files
client/plans/vj-auto-announce-and-playlist-realtime.md
2026-04-17 16:52:51 -04:00

7.4 KiB

VJ Page: Auto-Announce Save Button & Real-Time Playlist View

Problem Analysis

Issue 1: Auto-Announce Settings Keep Reverting

Root cause: The auto-announce settings (toggle, template, URL base) use a dual-save mechanism that causes a race condition:

  1. On every input/change event, the values are saved to localStorage immediately AND queued for remote save via queueAutoAnnounceSettingsPatch().
  2. queueAutoAnnounceSettingsPatch() has a 15-second debounce (AUTO_ANNOUNCE_SAVE_DEBOUNCE_MS = 15000) before it calls queueVjSettingsPatch(), which itself has a 300ms debounce before calling patchUserSettings().
  3. Meanwhile, onUserSettings() fires whenever settings arrive from the relay/worker (including from other tabs or stale relay data). When it fires, applyVjPageSettingsFromState() overwrites all form fields with whatever the relay has — which may be the OLD values if the 15-second debounce hasn't flushed yet.

The revert flow:

  • User changes template → localStorage updated, 15s timer starts
  • Before 15s elapses, onUserSettings fires with stale relay data → applyVjPageSettingsFromState() overwrites the input fields back to old values
  • The pending patch may still fire, but it patches the OLD values that were already overwritten

Solution: Remove auto-saving for auto-announce settings entirely. Add an explicit "Save" button. Only persist when the user clicks Save.

Issue 2: No Real-Time Playlist View During Stream

Root cause: When a VJ goes live and songs are announced, appendNowPlayingToPlaylist() in vj-stream.mjs adds tracks to an in-memory playlistTracks array and publishes a Nostr event (kind 30078). However:

  1. There is no UI in vj.html that displays the current episode's playlist tracks as they build up.
  2. The vj-playlist.html page shows historical playlists but requires a page load/refresh — it does not subscribe to real-time updates.
  3. The playlistTracks array in vj-stream.mjs is internal and not exposed to the UI.

Solution: Add a real-time playlist panel in the VJ stream card area that shows tracks as they are added during a live stream, with the ability to edit (remove/reorder) tracks.


Implementation Plan

Task 1: Add Save Button for Auto-Announce Settings

File: www/vj.html

1a. Add Save button to HTML (around line 1557)

  • Add a <button id="btnSaveAutoAnnounce" class="btn" type="button">Save</button> after the template textarea inside #divAutoAnnounceCard.

1b. Remove auto-save from input event listeners (around lines 6130-6171)

  • autoAnnounceSongToggle change listener (line 6131): Remove the call to queueAutoAnnounceSettingsPatch(). Keep the localStorage write for immediate local persistence.
  • inputAutoAnnounceTemplate input listener (line 6152): Remove the call to queueAutoAnnounceSettingsPatch(). Keep the localStorage write.
  • inputAutoAnnounceUrlBase input listener (line 6162): Remove the call to queueAutoAnnounceSettingsPatch(). Keep the localStorage write.

1c. Add Save button click handler (after line 6171)

  • On click of btnSaveAutoAnnounce:
    • Read current values from the three inputs (toggle, template, URL base)
    • Call queueVjSettingsPatch() directly (bypassing the 15s debounce) with the full autoAnnounce object:
      queueVjSettingsPatch({
        autoAnnounce: {
          songEnabled: autoAnnounceSongToggle?.checked || false,
          template: String(inputAutoAnnounceTemplate?.value || '').trim() || DEFAULT_AUTO_ANNOUNCE_TEMPLATE,
          urlBase: String(inputAutoAnnounceUrlBase?.value || '').trim() || DEFAULT_AUTO_ANNOUNCE_URL_BASE,
        },
      });
      
    • Show brief visual feedback (e.g., change button text to "Saved!" for 1.5s)

1d. Guard applyVjPageSettingsFromState against overwriting dirty auto-announce fields

  • In applyVjPageSettingsFromState() (lines 2155-2176), add a check: if the auto-announce values from settings match what is already in localStorage (the user's latest local edits), apply them. Otherwise, prefer the localStorage values. This prevents relay-delivered stale settings from overwriting unsaved local changes.
  • Alternatively (simpler): skip applying auto-announce fields from remote settings entirely — only apply them on initial page load. After that, the Save button is the only way to sync.

1e. Remove queueAutoAnnounceSettingsPatch function and autoAnnouncePatchTimer variable

  • These are no longer needed since we removed the debounced auto-save.
  • Remove AUTO_ANNOUNCE_SAVE_DEBOUNCE_MS constant (line 2008).
  • Remove autoAnnouncePatchTimer variable (line 2028).
  • Remove queueAutoAnnounceSettingsPatch function (lines 2134-2143).

Task 2: Real-Time Playlist View During Stream

File: www/js/vj-stream.mjs

2a. Expose playlist tracks and add render callback

  • Add an onPlaylistChange callback option to initVjStreamPanel() config.
  • After every call to playlistTracks.push(...) in appendNowPlayingToPlaylist() (line 879), call the callback with the current tracks array.
  • Also call it when tracks are cleared (go live, create show, etc.).
  • Expose methods: getPlaylistTracks(), removePlaylistTrack(index), reorderPlaylistTrack(fromIndex, toIndex) on the returned panel object.

2b. Add playlist display section to HTML

File: www/vj.html

  • Add a new section inside #vjStreamCard (after the auto-announce card, around line 1558), or as a collapsible section:
    <div id="divEpisodePlaylist" class="divPostItem streamSection">
      <div class="musicSidebarTitle">EPISODE PLAYLIST</div>
      <div id="divEpisodePlaylistStatus" class="musicPlaylistMeta">No tracks yet.</div>
      <ul id="ulEpisodePlaylistTracks" class="musicPlaylistList"></ul>
    </div>
    

2c. Render playlist tracks in real-time

File: www/vj.html

  • Implement renderEpisodePlaylist(tracks) function that:
    • Clears and re-renders #ulEpisodePlaylistTracks with the current track list
    • Each item shows: track number, artist, title, timestamp
    • Each item has a remove button (X) and is draggable for reorder
    • Updates #divEpisodePlaylistStatus with track count

2d. Wire up the callback

File: www/vj.html

  • Pass onPlaylistChange: renderEpisodePlaylist in the initVjStreamPanel() config (around line 6093).

2e. Enable editing (remove/reorder)

File: www/js/vj-stream.mjs

  • removePlaylistTrack(index): splice from playlistTracks, re-number remaining tracks, call publishPlaylistEvent('live'), call onPlaylistChange.
  • reorderPlaylistTrack(fromIndex, toIndex): reorder in playlistTracks, re-number, call publishPlaylistEvent('live'), call onPlaylistChange.

File: www/vj.html

  • Wire remove button clicks to call vjStreamPanel.removePlaylistTrack(index).
  • Wire drag-and-drop reorder to call vjStreamPanel.reorderPlaylistTrack(from, to).

2f. Add minimal CSS for the episode playlist

File: www/vj.html

  • Style #divEpisodePlaylist, #ulEpisodePlaylistTracks items with track number, artist/title, time, and remove button.
  • Reuse existing .musicPlaylistList and .musicQueueItem patterns for consistency.

Summary of Files to Modify

File Changes
www/vj.html Add Save button HTML, remove auto-save listeners, add save handler, add episode playlist section, render function, CSS
www/js/vj-stream.mjs Expose playlist tracks, add callback, add remove/reorder methods, return new methods from panel