Dynamic channel name updates and playlist crossfade transition

- Fix channel name not updating in frame player when playlist changes
- Use localStorage polling (2s interval) for cross-frame communication
- Fix API response access: use bracket notation for 'channel-name' property
- Add skip command after playlist load to trigger crossfade to new playlist
- Add #PHASE metadata to Asteroid-Low-Orbit.m3u playlist
This commit is contained in:
Glenn Thompson 2025-12-15 01:17:09 +03:00 committed by Brian O'Reilly
parent 18c251c8c4
commit 987d01beaa
4 changed files with 69 additions and 3 deletions

View File

@ -429,10 +429,17 @@
;; Copy playlist to stream-queue.m3u
(copy-playlist-to-stream-queue playlist-path)
;; Load into in-memory queue
(let ((count (load-queue-from-m3u-file)))
(let ((count (load-queue-from-m3u-file))
(channel-name (get-curated-channel-name)))
;; Skip current track to trigger crossfade to new playlist
(handler-case
(liquidsoap-command "stream-queue_m3u.skip")
(error (e)
(format *error-output* "Warning: Could not skip track: ~a~%" e)))
(api-output `(("status" . "success")
("message" . ,(format nil "Loaded playlist: ~a" name))
("count" . ,count)
("channel-name" . ,channel-name)
("paths" . ,(read-m3u-file-paths stream-queue-path))))))
(api-output `(("status" . "error")
("message" . "Playlist file not found"))

View File

@ -653,7 +653,12 @@
(if (= (ps:@ data status) "success")
(progn
(show-toast (+ "✓ Loaded " (ps:@ data count) " tracks from " name))
(load-current-queue))
(load-current-queue)
;; Update channel name in all channel selectors
;; Use bracket notation because API returns "channel-name" with hyphen
(let ((channel-name (aref data "channel-name")))
(when channel-name
(update-channel-selector-name channel-name))))
(alert (+ "Error loading playlist: " (or (ps:@ data message) "Unknown error")))))))
(catch (lambda (error)
(ps:chain console (error "Error loading playlist:" error))
@ -705,6 +710,29 @@
(setf html (+ html "</div>"))
(setf (ps:@ container inner-h-t-m-l) html))))))
;; Update channel selector name in UI after loading a new playlist
(defun update-channel-selector-name (channel-name)
"Update the curated channel option text in all channel selectors"
(ps:chain console (log "Updating channel name to:" channel-name))
;; Store in localStorage so popout player can pick it up
(ps:chain local-storage (set-item "curated-channel-name" channel-name))
;; Update in current document
(let ((channel-selector (ps:chain document (get-element-by-id "stream-channel"))))
(when channel-selector
(let ((curated-option (ps:chain channel-selector (query-selector "option[value='curated']"))))
(when curated-option
(setf (ps:@ curated-option text-content) (+ "🎧 " channel-name))))))
;; Use postMessage to notify all frames about the channel name change
(when (and (ps:@ window top)
(not (= (ps:@ window top) window)))
;; Post to top window which will relay to all frames
(ps:chain window top (post-message
(ps:create :type "channel-name-update" :channel-name channel-name)
"*"))))
;; Save current queue to stream-queue.m3u
(defun save-stream-queue ()
(ps:chain

View File

@ -556,6 +556,24 @@
;; Update quality selector state based on channel
(update-quality-selector-state)
;; Check for channel name changes from localStorage periodically
(let ((last-channel-name (ps:chain local-storage (get-item "curated-channel-name"))))
(ps:chain console (log "Frame player: initial channel name from localStorage:" last-channel-name))
(set-interval
(lambda ()
(let ((current-channel-name (ps:chain local-storage (get-item "curated-channel-name"))))
(when (and current-channel-name
(not (= current-channel-name last-channel-name)))
(ps:chain console (log "Frame player: channel name changed from" last-channel-name "to" current-channel-name))
(setf last-channel-name current-channel-name)
(let ((channel-selector (ps:chain document (get-element-by-id "stream-channel"))))
(when channel-selector
(let ((curated-option (ps:chain channel-selector (query-selector "option[value='curated']"))))
(when curated-option
(ps:chain console (log "Frame player: updating curated option text"))
(setf (ps:@ curated-option text-content) (+ "🎧 " current-channel-name)))))))))
2000))
;; Start now playing updates
(set-timeout update-mini-now-playing 1000)
(set-interval update-mini-now-playing 5000))))
@ -582,6 +600,16 @@
;; Update quality selector state based on channel
(update-quality-selector-state)
;; Listen for channel name changes from localStorage
(ps:chain window (add-event-listener "storage"
(lambda (e)
(when (= (ps:@ e key) "curated-channel-name")
(let ((channel-selector (ps:chain document (get-element-by-id "popout-stream-channel"))))
(when channel-selector
(let ((curated-option (ps:chain channel-selector (query-selector "option[value='curated']"))))
(when curated-option
(setf (ps:@ curated-option text-content) (+ "🎧 " (ps:@ e new-value)))))))))))
;; Start now playing updates
(update-popout-now-playing)
(set-interval update-popout-now-playing 5000)

View File

@ -1,6 +1,9 @@
#EXTM3U
#PLAYLIST:Asteroid Low Orbit - Ambient Electronic Journey
#PLAYLIST:Low Orbit
#PHASE:Low Orbit
#DURATION:12 hours (approx)
#CURATOR:Asteroid Radio
#DESCRIPTION:A 12-hour voyage through ambient, IDM, and space music
#EXTINF:-1,Brian Eno - Emerald And Lime
/app/music/Brian Eno/2011 - Small Craft On a Milk Sea/A1 Emerald And Lime.flac