Compare commits

..

No commits in common. "20ed7ecb0216bce12a44e92b9b482b06ce8840e1" and "76d331248b36bcdd43fe0b47da665c326f2dea3c" have entirely different histories.

10 changed files with 28 additions and 310 deletions

@ -1 +1 @@
Subproject commit cc4215d1c663c5aed4e9758c755a944016fa6aaa Subproject commit 23b5ee1e35e6c967cab12751ac7b8930d21b3307

View File

@ -120,14 +120,12 @@
(artist (getf parsed :artist)) (artist (getf parsed :artist))
(song (getf parsed :song)) (song (getf parsed :song))
(search-url (generate-music-search-url artist song))) (search-url (generate-music-search-url artist song)))
(let ((remaining (cdr (assoc :remaining now-playing-stats)))) (api-output `(("status" . "success")
(api-output `(("status" . "success") ("title" . ,title)
("title" . ,title) ("listeners" . ,(cdr (assoc :listeners now-playing-stats)))
("listeners" . ,(cdr (assoc :listeners now-playing-stats))) ("track_id" . ,(cdr (assoc :track-id now-playing-stats)))
("track_id" . ,(cdr (assoc :track-id now-playing-stats))) ("favorite_count" . ,favorite-count)
("favorite_count" . ,favorite-count) ("search_url" . ,search-url))))
("search_url" . ,search-url)
,@(when remaining `(("remaining" . ,remaining)))))))
(api-output `(("status" . "offline") (api-output `(("status" . "offline")
("title" . "Stream Offline") ("title" . "Stream Offline")
("track_id" . nil))))))) ("track_id" . nil)))))))

View File

@ -866,52 +866,7 @@
(ps:chain console (log "Could not load recent requests:" error)))))))) (ps:chain console (log "Could not load recent requests:" error))))))))
;; Load recent requests on page load ;; Load recent requests on page load
(load-recent-requests) (load-recent-requests)))
;; Main page countdown timer
(defvar *main-remaining* nil)
(defun format-countdown (seconds)
(let ((m (ps:chain -math (floor (/ seconds 60))))
(s (ps:chain -math (floor (mod seconds 60)))))
(+ (if (< m 10) (+ "0" m) m) ":" (if (< s 10) (+ "0" s) s))))
(defun poll-now-playing ()
(let ((mount (or (ps:chain local-storage (get-item "stream-mount")) "asteroid.mp3")))
(ps:chain
(fetch (+ "/api/asteroid/partial/now-playing-json?mount=" mount))
(then (lambda (response)
(if (ps:@ response ok)
(ps:chain response (json))
nil)))
(then (lambda (data)
(when data
(let ((title (or (ps:@ data data title) (ps:@ data title)))
(remaining (or (ps:@ data data remaining) (ps:@ data remaining)))
(listeners (or (ps:@ data data listeners) (ps:@ data listeners)))
(title-el (ps:chain document (get-element-by-id "current-track-title")))
(listener-el (ps:chain document (get-element-by-id "current-listeners"))))
(when (and title-el title)
(setf (ps:@ title-el text-content) title))
(when (and listener-el listeners)
(setf (ps:@ listener-el text-content) listeners))
(when remaining
(setf *main-remaining* remaining))))))
(catch (lambda (error) nil)))))
;; Start polling and countdown ticker on the main page
(set-timeout poll-now-playing 2000)
(set-interval poll-now-playing 15000)
(set-interval
(lambda ()
(let ((el (ps:chain document (get-element-by-id "track-countdown-main"))))
(when el
(if (and *main-remaining* (> *main-remaining* 0))
(progn
(decf *main-remaining*)
(setf (ps:@ el text-content) (+ "[" (format-countdown *main-remaining*) "]")))
(setf (ps:@ el text-content) "")))))
1000)))
"Compiled JavaScript for front-page - generated at load time") "Compiled JavaScript for front-page - generated at load time")
(defun generate-front-page-js () (defun generate-front-page-js ()

View File

@ -336,30 +336,6 @@
;; Track last notified title to avoid duplicate notifications ;; Track last notified title to avoid duplicate notifications
(defvar *last-notified-title* nil) (defvar *last-notified-title* nil)
;; Countdown timer state
(defvar *track-remaining-seconds* nil)
(defvar *countdown-interval* nil)
(defun format-countdown (seconds)
(let ((m (ps:chain -math (floor (/ seconds 60))))
(s (ps:chain -math (floor (mod seconds 60)))))
(+ (if (< m 10) (+ "0" m) m) ":" (if (< s 10) (+ "0" s) s))))
(defun start-countdown-ticker ()
(when *countdown-interval*
(clear-interval *countdown-interval*))
(setf *countdown-interval*
(set-interval
(lambda ()
(let ((el (ps:chain document (get-element-by-id "track-countdown"))))
(when el
(if (and *track-remaining-seconds* (> *track-remaining-seconds* 0))
(progn
(decf *track-remaining-seconds*)
(setf (ps:@ el text-content) (+ "[" (format-countdown *track-remaining-seconds*) "]")))
(setf (ps:@ el text-content) "")))))
1000)))
;; Check if notifications are enabled in localStorage ;; Check if notifications are enabled in localStorage
(defun notifications-enabled-p () (defun notifications-enabled-p ()
(= (ps:chain local-storage (get-item "notifications-enabled")) "true")) (= (ps:chain local-storage (get-item "notifications-enabled")) "true"))
@ -434,12 +410,6 @@
;; Show a system notification for track change ;; Show a system notification for track change
(defun show-track-notification (title body) (defun show-track-notification (title body)
(ps:chain console (log "[NOTIFY] show-track-notification called:"
"supported=" (notifications-supported-p)
"permission=" (get-notification-permission)
"enabled=" (notifications-enabled-p)
"last=" *last-notified-title*
"title=" title))
(when (and (notifications-supported-p) (when (and (notifications-supported-p)
(= (get-notification-permission) "granted") (= (get-notification-permission) "granted")
(notifications-enabled-p) (notifications-enabled-p)
@ -452,7 +422,6 @@
:tag "asteroid-track-change" :tag "asteroid-track-change"
:renotify true :renotify true
:silent false))))) :silent false)))))
(ps:chain console (log "[NOTIFY] Notification created successfully"))
;; Auto-close after 5 seconds ;; Auto-close after 5 seconds
(set-timeout (lambda () (ps:chain notification (close))) 5000) (set-timeout (lambda () (ps:chain notification (close))) 5000)
;; Click to focus the window ;; Click to focus the window
@ -461,7 +430,7 @@
(ps:chain window (focus)) (ps:chain window (focus))
(ps:chain notification (close))))) (ps:chain notification (close)))))
(:catch (e) (:catch (e)
(ps:chain console (log "[NOTIFY] Notification error:" e)))))) (ps:chain console (log "Notification error:" e))))))
;; Notify track change (called from update-mini-now-playing) ;; Notify track change (called from update-mini-now-playing)
(defun notify-track-change (title) (defun notify-track-change (title)
@ -545,15 +514,16 @@
(when el (when el
;; Check if track changed and record to history + notify ;; Check if track changed and record to history + notify
(when (not (= (ps:@ el text-content) title)) (when (not (= (ps:@ el text-content) title))
(ps:chain console (log "[STREAM-SYNC] Title changed:" title))
(record-track-listen title) (record-track-listen title)
(notify-track-change title)) (notify-track-change title))
(setf (ps:@ el text-content) title) (setf (ps:@ el text-content) title)
;; Check if this track is in user's favorites
(check-favorite-status-mini)) (check-favorite-status-mini))
(update-media-session title) (update-media-session title)
(when track-id-el (when track-id-el
(let ((track-id (or (ps:@ data data track_id) (ps:@ data track_id)))) (let ((track-id (or (ps:@ data data track_id) (ps:@ data track_id))))
(setf (ps:@ track-id-el value) (or track-id "")))) (setf (ps:@ track-id-el value) (or track-id ""))))
;; Update favorite count display
(let ((count-el (ps:chain document (get-element-by-id "favorite-count-mini"))) (let ((count-el (ps:chain document (get-element-by-id "favorite-count-mini")))
(fav-count (or (ps:@ data data favorite_count) (ps:@ data favorite_count) 0))) (fav-count (or (ps:@ data data favorite_count) (ps:@ data favorite_count) 0)))
(when count-el (when count-el
@ -561,10 +531,7 @@
((= fav-count 0) (setf (ps:@ count-el text-content) "")) ((= fav-count 0) (setf (ps:@ count-el text-content) ""))
((= fav-count 1) (setf (ps:@ count-el text-content) "1 ❤️")) ((= fav-count 1) (setf (ps:@ count-el text-content) "1 ❤️"))
(t (setf (ps:@ count-el text-content) (+ fav-count " ❤️")))))) (t (setf (ps:@ count-el text-content) (+ fav-count " ❤️"))))))
;; Sync countdown timer from server ;; Update MusicBrainz search link
(let ((remaining (or (ps:@ data data remaining) (ps:@ data remaining))))
(when remaining
(setf *track-remaining-seconds* remaining)))
(let ((mb-link (ps:chain document (get-element-by-id "mini-musicbrainz-link"))) (let ((mb-link (ps:chain document (get-element-by-id "mini-musicbrainz-link")))
(search-url (or (ps:@ data data search_url) (ps:@ data search_url)))) (search-url (or (ps:@ data data search_url) (ps:@ data search_url))))
(when mb-link (when mb-link
@ -757,35 +724,6 @@
(catch (lambda (err) (catch (lambda (err)
(ps:chain console (log "Reconnect failed:" err)))))) (ps:chain console (log "Reconnect failed:" err))))))
;; Buffer bloat detection and reset
(defvar *max-buffer-seconds* 15)
(defvar *buffer-check-interval* nil)
(defun get-buffer-ahead (audio-element)
"Return seconds of audio buffered ahead of current playback position."
(ps:try
(when (and (ps:@ audio-element buffered)
(> (ps:@ audio-element buffered length) 0))
(- (ps:chain audio-element buffered (end (- (ps:@ audio-element buffered length) 1)))
(ps:@ audio-element current-time)))
(:catch (e) 0)))
(defun start-buffer-monitor (audio-element)
(when *buffer-check-interval*
(clear-interval *buffer-check-interval*))
(setf *buffer-check-interval*
(set-interval
(lambda ()
(when (and (not (ps:@ audio-element paused))
(not *is-reconnecting*))
(let ((ahead (get-buffer-ahead audio-element)))
(when (and ahead (> ahead 5))
(ps:chain console (log (+ "[BUFFER] " (ps:chain ahead (to-fixed 1)) "s ahead"))))
(when (and ahead (> ahead *max-buffer-seconds*))
(ps:chain console (log (+ "[BUFFER] Bloat detected (" (ps:chain ahead (to-fixed 1)) "s), resetting stream")))
(reconnect-stream)))))
30000)))
;; Attach event listeners to audio element ;; Attach event listeners to audio element
(defun attach-audio-listeners (audio-element) (defun attach-audio-listeners (audio-element)
(ps:chain audio-element (ps:chain audio-element
@ -969,9 +907,8 @@
:artist "Asteroid Radio" :artist "Asteroid Radio"
:album "Live Broadcast"))))) :album "Live Broadcast")))))
;; Attach event listeners and buffer monitor ;; Attach event listeners
(attach-audio-listeners audio-element) (attach-audio-listeners audio-element)
(start-buffer-monitor audio-element)
;; Restore user channel preference ;; Restore user channel preference
(let ((channel-selector (ps:chain document (get-element-by-id "stream-channel"))) (let ((channel-selector (ps:chain document (get-element-by-id "stream-channel")))
@ -1021,10 +958,9 @@
(ps:chain console (log "Could not fetch channel name:" error)))))) (ps:chain console (log "Could not fetch channel name:" error))))))
15000)) ;; Poll every 15 seconds 15000)) ;; Poll every 15 seconds
;; Start now playing updates and countdown ticker ;; Start now playing updates
(set-timeout update-mini-now-playing 1000) (set-timeout update-mini-now-playing 1000)
(set-interval update-mini-now-playing 15000) (set-interval update-mini-now-playing 15000))))
(start-countdown-ticker))))
;; Initialize popout player ;; Initialize popout player
(defun init-popout-player () (defun init-popout-player ()

View File

@ -1,113 +0,0 @@
#EXTM3U
#PLAYLIST:Deep Focus
#PHASE:Deep Focus
#DURATION:5 hours (approx)
#CURATOR:Asteroid Radio
#DESCRIPTION:Upbeat and cheerful electronic music for long coding sessions - IDM, shoegaze electronica, melodic techno, and ambient rhythms
#EXTINF:-1,Tycho - Glider
/app/music/Tycho - Epoch (Deluxe Version) (2019) [WEB FLAC16-44.1]/01 - Glider.flac
#EXTINF:-1,Boards of Canada - Spectrum
/app/music/Boards of Canada/A Few Old Tunes/01 - Spectrum.mp3
#EXTINF:-1,Ulrich Schnauss - Melts into Air
/app/music/Ulrich Schnauss - No Further Ahead Than Tomorrow (2020) - WEB FLAC/01. Melts into Air (2019 Version).flac
#EXTINF:-1,Plaid - Do Matter
/app/music/Plaid - The Digging Remedy (2016) [FLAC]/01 - Do Matter.flac
#EXTINF:-1,Four Tet - Two Thousand And Seventeen
/app/music/Four Tet - New Energy {CD} [FLAC] (2017)/02 Two Thousand And Seventeen.flac
#EXTINF:-1,Kiasmos - Lit
/app/music/Kiasmos/2014 - Kiasmos/01 - Lit.flac
#EXTINF:-1,Proem - A Good Soaking
/app/music/Proem - Twelve Tails-(2021) @FLAC [16-48]/12 - A Good Soaking.flac
#EXTINF:-1,Cut Copy - Standing in the Middle of the Field
/app/music/Cut Copy - Haiku From Zero (2017) [FLAC] {2557864014}/01 - Standing in the Middle of the Field.flac
#EXTINF:-1,Marconi Union - Sleeper
/app/music/Marconi Union - Ghost Stations (2016 - WEB - FLAC)/01. Marconi Union - Sleeper.flac
#EXTINF:-1,Clark - Peak Magnetic
/app/music/Clark - Death Peak (2017) [FLAC]/03 - Peak Magnetic.flac
#EXTINF:-1,Tycho - Weather
/app/music/Tycho - Simulcast (2020) [WEB FLAC]/01 - Weather.flac
#EXTINF:-1,Boards of Canada - Happy Cycling
/app/music/Boards of Canada/A Few Old Tunes/06 - Happy Cycling.mp3
#EXTINF:-1,Ulrich Schnauss & Jonas Munk - Asteroid 2467
/app/music/Ulrich Schnauss & Jonas Munk - Eight Fragments Of An Illusion (2021) - WEB FLAC/01. Asteroid 2467.flac
#EXTINF:-1,Faux Tales - Avalon
/app/music/Faux Tales - 2015 - Kairos [FLAC] {Kensai Records KNS006 WEB}/3 - Avalon.flac
#EXTINF:-1,Vector Lovers - City Lights From A Train
/app/music/Vector Lovers/2005 - Capsule For One/01 - City Lights From A Train.mp3
#EXTINF:-1,Plaid - Maru
/app/music/Plaid - Polymer (2019) [WEB FLAC]/03 - Maru.flac
#EXTINF:-1,Four Tet - Baby
/app/music/Four Tet - Sixteen Oceans (2020) {Text Records - TEXT051} [CD FLAC]/02 - Four Tet - Baby.flac
#EXTINF:-1,Bluetech - Laika
/app/music/Bluetech - Spacehop Chronicles Vol. 1 (flac)/01. Laika.flac
#EXTINF:-1,Kiasmos - Looped
/app/music/Kiasmos/2014 - Kiasmos/03 - Looped.flac
#EXTINF:-1,Proem - Snow Drifts
/app/music/Proem - Twelve Tails-(2021) @FLAC [16-48]/11 - Snow Drifts.flac
#EXTINF:-1,Thievery Corporation - Fragments (Tycho Remix)
/app/music/Thievery Corporation and Tycho - Fragments Ascension EP (flac)/2. Thievery Corporation - Fragments (Tycho Remix).flac
#EXTINF:-1,Ulrich Schnauss - Love Grows Out of Thin Air
/app/music/Ulrich Schnauss - No Further Ahead Than Tomorrow (2020) - WEB FLAC/02. Love Grows Out of Thin Air (2019 Version).flac
#EXTINF:-1,Clark - Kiri's Glee
/app/music/Clark - Kiri Variations (2019) [WEB FLAC]/04 - Kiri's Glee.flac
#EXTINF:-1,Cut Copy - Airborne
/app/music/Cut Copy - Haiku From Zero (2017) [FLAC] {2557864014}/05 - Airborne.flac
#EXTINF:-1,Marconi Union - Riser
/app/music/Marconi Union - Ghost Stations (2016 - WEB - FLAC)/04. Marconi Union - Riser.flac
#EXTINF:-1,Boards of Canada - Forest Moon
/app/music/Boards of Canada/A Few Old Tunes/09 - Forest Moon.mp3
#EXTINF:-1,Tycho - Ascension
/app/music/Thievery Corporation and Tycho - Fragments Ascension EP (flac)/3. Tycho - Ascension.flac
#EXTINF:-1,Plaid - Dancers
/app/music/Plaid - Polymer (2019) [WEB FLAC]/07 - Dancers.flac
#EXTINF:-1,Quaeschning & Ulrich Schnauss - A Calm but Steady Flow
/app/music/Quaeschning and Ulrich Schnauss - Synthwaves (2017) {vista003, GER, CD} [FLAC]/05 - A Calm but Steady Flow.flac
#EXTINF:-1,Bitcrush - Engale
/app/music/Bitcrush - Enarc (flac)/01 - Engale.flac
#EXTINF:-1,Four Tet - Parallel 1
/app/music/Four Tet - Parallel (2020) - WEB FLAC/01. Parallel 1.flac
#EXTINF:-1,woob - INNA
/app/music/woob - Mass Distraction EP [WEB FLAC 24]/woob - Mass Distraction EP - 01 INNA.flac
#EXTINF:-1,Vector Lovers - Nostalgia 4 The Future
/app/music/Vector Lovers/2005 - Capsule For One/05 - Nostalgia 4 The Future.mp3
#EXTINF:-1,Kiasmos - Swayed
/app/music/Kiasmos/2014 - Kiasmos/04 - Swayed.flac
#EXTINF:-1,Proem - Keep This Whole
/app/music/Proem - Twelve Tails-(2021) @FLAC [16-48]/10 - Keep This Whole.flac
#EXTINF:-1,Ulrich Schnauss - Her and the Sea
/app/music/Ulrich Schnauss - A Long Way To Fall - Rebound (2020) - WEB FLAC/01. Her and the Sea.flac
#EXTINF:-1,Faux Tales - Oceania
/app/music/Faux Tales - 2015 - Kairos [FLAC] {Kensai Records KNS006 WEB}/4 - Oceania.flac
#EXTINF:-1,Cut Copy - Stars Last Me a Lifetime
/app/music/Cut Copy - Haiku From Zero (2017) [FLAC] {2557864014}/04 - Stars Last Me a Lifetime.flac
#EXTINF:-1,Plaid - The Bee
/app/music/Plaid - The Digging Remedy (2016) [FLAC]/04 - The Bee.flac
#EXTINF:-1,Clark - Living Fantasy
/app/music/Clark - Death Peak (2017) [FLAC]/08 - Living Fantasy.flac
#EXTINF:-1,Tycho - Japan (Satin Jackets Remix)
/app/music/Tycho - Weather Remixes (2020) - WEB FLAC/03. Japan (Satin Jackets Remix).flac
#EXTINF:-1,Boards of Canada - Skimming Stones
/app/music/Boards of Canada/A Few Old Tunes/10 - Skimming Stones.mp3
#EXTINF:-1,Ulrich Schnauss & Jonas Munk - Perpetual Motion
/app/music/Ulrich Schnauss & Jonas Munk - Eight Fragments Of An Illusion (2021) - WEB FLAC/04. Perpetual Motion.flac
#EXTINF:-1,Bluetech - Skybox
/app/music/Bluetech - Spacehop Chronicles Vol. 1 (flac)/04. Skybox.flac
#EXTINF:-1,God is an Astronaut - Epitaph
/app/music/God is an Astronaut - Epitaph (2018) WEB FLAC/01. Epitaph.flac
#EXTINF:-1,Seba & Ulrich Schnauss - M7
/app/music/Seba & Ulrich Schnauss - Snöflingor EP [2017] [WEB_FLAC]/01. Seba & Ulrich Schnauss - M7.flac
#EXTINF:-1,Quaeschning & Ulrich Schnauss - Prism
/app/music/Quaeschning and Ulrich Schnauss - Synthwaves (2017) {vista003, GER, CD} [FLAC]/08 - Prism.flac
#EXTINF:-1,High Energy Protons - The Heavens (Monolith mix)
/app/music/High Energy Protons/03 - The Heavens (Monolith mix).mp3
#EXTINF:-1,woob - Subterranean District
/app/music/woob - Tokyo Run - Series 8 Keycard - Lv.6 [2017] WEB [FLAC24] [16-44]/Tokyo Run - 24 Bit Masters/06 woob - Subterranean District.flac
#EXTINF:-1,Bitcrush - Two Go From There
/app/music/Bitcrush - Enarc (flac)/03 - Two Go From There.flac
#EXTINF:-1,Daft Punk - Derezzed
/app/music/Daft Punk - TRON Legacy - The Complete Edition (2020 - WEB - FLAC)/13 Derezzed.flac
#EXTINF:-1,God is an Astronaut - Komorebi
/app/music/God is an Astronaut - Epitaph (2018) WEB FLAC/05. Komorebi.flac
#EXTINF:-1,Daft Punk - The Son of Flynn
/app/music/Daft Punk - TRON Legacy - The Complete Edition (2020 - WEB - FLAC)/03 The Son of Flynn.flac

View File

@ -1372,14 +1372,6 @@ body.persistent-player-container .now-playing-mini{
min-width: 300px; min-width: 300px;
} }
body.persistent-player-container .track-countdown-mini{
color: #888;
font-size: 0.75em;
font-family: monospace;
margin-left: 6px;
white-space: nowrap;
}
body.persistent-player-container .persistent-reconnect-btn{ body.persistent-player-container .persistent-reconnect-btn{
background: transparent; background: transparent;
color: #00ff00; color: #00ff00;
@ -1603,13 +1595,6 @@ body.popout-body .status-mini{
margin: 0 5px; margin: 0 5px;
} }
.track-countdown{
color: #888;
font-size: 0.8em;
font-family: monospace;
margin-left: 8px;
}
.now-playing-track{ .now-playing-track{
display: flex; display: flex;
align-items: center; align-items: center;

View File

@ -1108,13 +1108,6 @@
:flex 1 :flex 1
:min-width "300px") :min-width "300px")
(.track-countdown-mini
:color "#888"
:font-size "0.75em"
:font-family "monospace"
:margin-left "6px"
:white-space "nowrap")
(.persistent-reconnect-btn (.persistent-reconnect-btn
:background transparent :background transparent
:color "#00ff00" :color "#00ff00"
@ -1295,12 +1288,6 @@
(.craftering (.craftering
(a :margin "0 5px"))) (a :margin "0 5px")))
(.track-countdown
:color "#888"
:font-size "0.8em"
:font-family "monospace"
:margin-left "8px")
;; Now playing favorite button ;; Now playing favorite button
(.now-playing-track (.now-playing-track
:display "flex" :display "flex"

View File

@ -148,11 +148,6 @@
(setf *current-playlist-path* playlist-path) (setf *current-playlist-path* playlist-path)
(log:info "Playlist now active: ~A" (file-namestring playlist-path))) (log:info "Playlist now active: ~A" (file-namestring playlist-path)))
(defvar *pending-save-file* nil
"The file path that will be saved on the NEXT track change.
This one-track delay ensures we persist the track that was actually playing,
not the one being loaded during crossfade.")
(defun on-harmony-track-change (pipeline track-info) (defun on-harmony-track-change (pipeline track-info)
"Called by cl-streamer when a track changes. "Called by cl-streamer when a track changes.
Updates recently-played lists and finds the track in the database." Updates recently-played lists and finds the track in the database."
@ -173,10 +168,9 @@
:track-id track-id) :track-id track-id)
:curated) :curated)
(setf *last-known-track-curated* display-title)) (setf *last-known-track-curated* display-title))
;; Save the PREVIOUS track (which was actually playing) and queue this one ;; Persist current track for resume-on-restart
(when *pending-save-file* (when file-path
(save-playback-state *pending-save-file*)) (save-playback-state file-path))
(setf *pending-save-file* file-path)
(log:info "Track change: ~A (track-id: ~A)" display-title track-id))) (log:info "Track change: ~A (track-id: ~A)" display-title track-id)))
(defun find-track-by-file-path (file-path) (defun find-track-by-file-path (file-path)
@ -197,41 +191,19 @@
(defun harmony-now-playing (&optional (mount "asteroid.mp3")) (defun harmony-now-playing (&optional (mount "asteroid.mp3"))
"Get now-playing information from cl-streamer pipeline. "Get now-playing information from cl-streamer pipeline.
Uses the metadata timeline to report what listeners are actually hearing, Returns an alist with now-playing data, or NIL if the pipeline is not running."
accounting for ring buffer and browser decode buffering."
(when (and *harmony-pipeline* (when (and *harmony-pipeline*
(cl-streamer/harmony:pipeline-current-track *harmony-pipeline*)) (cl-streamer/harmony:pipeline-current-track *harmony-pipeline*))
(let* ((server (cl-streamer/harmony:pipeline-server *harmony-pipeline*)) (let* ((track-info (cl-streamer/harmony:pipeline-current-track *harmony-pipeline*))
(listener-title (when server (display-title (or (getf track-info :display-title) "Unknown"))
(cl-streamer:get-listener-now-playing
server (format nil "/~A" mount))))
(track-info (cl-streamer/harmony:pipeline-current-track *harmony-pipeline*))
(display-title (or listener-title
(getf track-info :display-title)
"Unknown"))
(listeners (cl-streamer:pipeline-listener-count *harmony-pipeline*)) (listeners (cl-streamer:pipeline-listener-count *harmony-pipeline*))
(track-id (or (find-track-by-title display-title) (track-id (or (find-track-by-title display-title)
(find-track-by-file-path (getf track-info :file)))) (find-track-by-file-path (getf track-info :file)))))
(pipeline-title (getf track-info :display-title)) `((:listenurl . ,(format nil "~A/~A" *stream-base-url* mount))
(raw-remaining (cl-streamer/harmony:pipeline-track-remaining *harmony-pipeline*)) (:title . ,display-title)
(titles-match (or (null listener-title) (:listeners . ,(or listeners 0))
(null pipeline-title) (:track-id . ,track-id)
(string= listener-title pipeline-title))) (:favorite-count . ,(or (get-track-favorite-count display-title) 0))))))
;; Only show remaining when titles match (delay has passed).
;; During the transition window the countdown would be inaccurate.
(remaining (when (and raw-remaining titles-match)
(max 0 (floor raw-remaining)))))
;; Diagnostic: log when listener-title differs from pipeline title
(when (and listener-title pipeline-title
(not (string= listener-title pipeline-title)))
(log:info "[SYNC-DIAG] API returning ~S (pipeline has ~S, delay=~As)"
listener-title pipeline-title cl-streamer::*browser-buffer-seconds*))
`((:listenurl . ,(format nil "~A/~A" *stream-base-url* mount))
(:title . ,display-title)
(:listeners . ,(or listeners 0))
(:track-id . ,track-id)
(:favorite-count . ,(or (get-track-favorite-count display-title) 0))
,@(when remaining `((:remaining . ,remaining)))))))
;;; ---- Pipeline Lifecycle ---- ;;; ---- Pipeline Lifecycle ----

View File

@ -39,7 +39,6 @@
<a id="mini-musicbrainz-link" href="#" target="_blank" title="Search on MusicBrainz" style="display: none; margin-right: 4px; text-decoration: none;">🔗</a> <a id="mini-musicbrainz-link" href="#" target="_blank" title="Search on MusicBrainz" style="display: none; margin-right: 4px; text-decoration: none;">🔗</a>
<span class="now-playing-mini" id="mini-now-playing">Loading...</span> <span class="now-playing-mini" id="mini-now-playing">Loading...</span>
<span id="track-countdown" class="track-countdown-mini"></span>
<span class="favorite-count-mini" id="favorite-count-mini"></span> <span class="favorite-count-mini" id="favorite-count-mini"></span>
<button class="btn-favorite-mini" id="favorite-btn-mini" onclick="toggleFavoriteMini()" title="Add to favorites"> <button class="btn-favorite-mini" id="favorite-btn-mini" onclick="toggleFavoriteMini()" title="Add to favorites">

View File

@ -3,8 +3,7 @@
<c:then> <c:then>
<c:using value="stats"> <c:using value="stats">
<div class="now-playing-track"> <div class="now-playing-track">
<p>Track: <span lquery="(text title)" id="current-track-title">The Void - Silence</span> <p>Track: <span lquery="(text title)" id="current-track-title">The Void - Silence</span></p>
<span id="track-countdown-main" class="track-countdown"></span></p>
<button class="btn-favorite" id="favorite-btn" onclick="toggleFavorite()" title="Add to favorites"> <button class="btn-favorite" id="favorite-btn" onclick="toggleFavorite()" title="Add to favorites">
<span class="star-icon">☆</span> <span class="star-icon">☆</span>
</button> </button>