diff --git a/frontend-partials.lisp b/frontend-partials.lisp
index a38aa63..aad4d31 100644
--- a/frontend-partials.lisp
+++ b/frontend-partials.lisp
@@ -1,56 +1,50 @@
(in-package :asteroid)
(defun icecast-now-playing (icecast-base-url)
+ "Fetch now-playing information from Icecast server.
+
+ ICECAST-BASE-URL - Base URL of the Icecast server (e.g. http://localhost:8000)
+
+ Returns a plist with :listenurl, :title, and :listeners, or NIL on error."
(let* ((icecast-url (format nil "~a/admin/stats.xml" icecast-base-url))
(response (drakma:http-request icecast-url
:want-stream nil
:basic-authorization '("admin" "asteroid_admin_2024"))))
(format t "DEBUG: Fetching Icecast stats from ~a~%" icecast-url)
(when response
- (let ((xml-string (if (stringp response)
- response
- (babel:octets-to-string response :encoding :utf-8))))
- ;; Simple XML parsing to extract source information and aggregate listeners
- ;; Get title from main mp3 stream
- (let* ((mp3-match (cl-ppcre:scan "" xml-string))
- (title (if mp3-match
- (let* ((source-section (subseq xml-string mp3-match
- (or (cl-ppcre:scan "" xml-string :start mp3-match)
- (length xml-string))))
- (titlep (cl-ppcre:all-matches "
" source-section)))
- (if titlep
- (cl-ppcre:regex-replace-all ".*(.*?).*" source-section "\\1")
- "Unknown"))
- "Unknown"))
- ;; Aggregate listeners from all three streams
- (total-listeners 0))
- ;; Count listeners from each mount point
- (dolist (mount '("/asteroid\\.mp3" "/asteroid\\.aac" "/asteroid-low\\.mp3"))
- (let ((match-pos (cl-ppcre:scan (format nil "" mount) xml-string)))
- (when match-pos
- (let* ((source-section (subseq xml-string match-pos
- (or (cl-ppcre:scan "" xml-string :start match-pos)
- (length xml-string))))
- (listenersp (cl-ppcre:all-matches "" source-section)))
- (when listenersp
- (let ((listener-count-str (cl-ppcre:regex-replace-all ".*(.*?).*" source-section "\\1"))
- (count (parse-integer (cl-ppcre:regex-replace-all ".*(.*?).*" source-section "\\1") :junk-allowed t)))
- (format t "DEBUG: Mount ~a has ~a listeners~%" mount count)
- (incf total-listeners count)))))))
- (let ((result `((:listenurl . ,(format nil "~a/asteroid.mp3" *stream-base-url*))
- (:title . ,title)
- (:listeners . ,total-listeners))))
- (format t "DEBUG: Parsed title=~a, total-listeners=~a~%" title total-listeners)
- result))))))
+ (let ((xml-string (if (stringp response)
+ response
+ (babel:octets-to-string response :encoding :utf-8))))
+ ;; Extract total listener count from root tag (sums all mount points)
+ ;; Extract title from asteroid.mp3 mount point
+ (let* ((total-listeners (multiple-value-bind (match groups)
+ (cl-ppcre:scan-to-strings "(\\d+)" xml-string)
+ (if (and match groups)
+ (parse-integer (aref groups 0) :junk-allowed t)
+ 0)))
+ ;; Get title from asteroid.mp3 mount point
+ (mount-start (cl-ppcre:scan "" xml-string))
+ (title (if mount-start
+ (let* ((source-section (subseq xml-string mount-start
+ (or (cl-ppcre:scan "" xml-string :start mount-start)
+ (length xml-string)))))
+ (multiple-value-bind (match groups)
+ (cl-ppcre:scan-to-strings "(.*?)" source-section)
+ (if (and match groups)
+ (aref groups 0)
+ "Unknown")))
+ "Unknown")))
+ (format t "DEBUG: Parsed title=~a, total-listeners=~a~%" title total-listeners)
+ `((:listenurl . ,(format nil "~a/asteroid.mp3" *stream-base-url*))
+ (:title . ,title)
+ (:listeners . ,total-listeners)))))))
(define-api asteroid/partial/now-playing () ()
"Get Partial HTML with live status from Icecast server"
- (handler-case
+ (with-error-handling
(let ((now-playing-stats (icecast-now-playing *stream-base-url*)))
(if now-playing-stats
(progn
- ;; TODO: it should be able to define a custom api-output for this
- ;; (api-output :format "html"))
(setf (header "Content-Type") "text/html")
(clip:process-to-string
(load-template "partial/now-playing")
@@ -60,14 +54,7 @@
(clip:process-to-string
(load-template "partial/now-playing")
:connection-error t
- :stats nil))))
- (error ()
- (format t "Error in now-playing endpoint~%")
- (setf (header "Content-Type") "text/html")
- (clip:process-to-string
- (load-template "partial/now-playing")
- :connection-error t
- :stats nil))))
+ :stats nil))))))
(define-api asteroid/partial/now-playing-inline () ()
"Get inline text with now playing info (for admin dashboard and widgets)"