feat: add HTML partial hidration for now-playing

This commit is contained in:
Luis Pereira 2025-10-13 16:58:54 +01:00 committed by Brian O'Reilly
parent 136fa2fa74
commit d0efc89e33
3 changed files with 77 additions and 0 deletions

View File

@ -40,4 +40,5 @@
(:file "playlist-management")
(:file "stream-control")
(:file "auth-routes")
(:file "frontend-partials")
(:file "asteroid")))

55
frontend-partials.lisp Normal file
View File

@ -0,0 +1,55 @@
(in-package :asteroid)
(defun icecast-now-playing (icecast-base-url)
(let* ((icecast-url (concatenate 'string icecast-base-url "/admin/stats.xml"))
(response (drakma:http-request icecast-url
:want-stream nil
:basic-authorization '("admin" "asteroid_admin_2024"))))
(when response
(let ((xml-string (if (stringp response)
response
(babel:octets-to-string response :encoding :utf-8))))
;; Simple XML parsing to extract source information
;; Look for <source mount="/asteroid.mp3"> sections and extract title, listeners, etc.
(multiple-value-bind (match-start match-end)
(cl-ppcre:scan "<source mount=\"/asteroid\\.mp3\">" xml-string)
(if match-start
(let* ((source-section (subseq xml-string match-start
(or (cl-ppcre:scan "</source>" xml-string :start match-start)
(length xml-string))))
(titlep (cl-ppcre:all-matches "<title>" source-section))
(listenersp (cl-ppcre:all-matches "<listeners>" source-section))
(title (if titlep (cl-ppcre:regex-replace-all ".*<title>(.*?)</title>.*" source-section "\\1") "Unknown"))
(listeners (if listenersp (cl-ppcre:regex-replace-all ".*<listeners>(.*?)</listeners>.*" source-section "\\1") "0")))
`((:listenurl . ,(concatenate 'string *stream-base-url* "/asteroid.mp3"))
(:title . ,title)
(:listeners . ,(parse-integer listeners :junk-allowed t))))
`((:listenurl . ,(concatenate 'string *stream-base-url* "/asteroid.mp3"))
(:title . "Unknown")
(:listeners . "Unknown"))))))))
(define-api asteroid/partial/now-playing () ()
"Get Partial HTML with live status from Icecast server"
(handler-case
(let ((now-playing-stats (icecast-now-playing *stream-base-url*))
(template-path (merge-pathnames "template/partial/now-playing.chtml"
(asdf:system-source-directory :asteroid))))
(if now-playing-stats
(progn
;; TODO: it should be able to define a custom api-output for this
;; (api-output <clip-parser> :format "html"))
(setf (header "Content-Type") "text/html")
(clip:process-to-string
(plump:parse (alexandria:read-file-into-string template-path))
:stats now-playing-stats))
(progn
(setf (header "Content-Type") "text/html")
(clip:process-to-string
(plump:parse (alexandria:read-file-into-string template-path))
:connection-error t
:stats nil))))
(error (e)
(api-output `(("status" . "error")
("message" . ,(format nil "Error loading profile: ~a" e)))
:status 500))))

View File

@ -0,0 +1,21 @@
<h2>Now Playing</h2>
<c:if test="stats">
<c:then>
<c:using value="stats">
<!--<p>Artist: <span>The Void</span></p>-->
<p>Track: <span lquery="(text title)">The Void - Silence</span></p>
<p>Listeners: <span lquery="(text listeners)">1</span></p>
</c:using>
</c:then>
<c:else>
<c:if test="connection-error">
<c:then>
<div class="message error">
<span>There was an error trying to get information from stream.</span>
</div>
</c:then>
</c:if>
<p>Track: <span>NA</span></p>
<p>Listeners: <span>NA</span></p>
</c:else>
</c:if>