diff --git a/asteroid.asd b/asteroid.asd index f5c456c..3c2ec23 100644 --- a/asteroid.asd +++ b/asteroid.asd @@ -40,4 +40,5 @@ (:file "playlist-management") (:file "stream-control") (:file "auth-routes") + (:file "frontend-partials") (:file "asteroid"))) diff --git a/frontend-partials.lisp b/frontend-partials.lisp new file mode 100644 index 0000000..45a088d --- /dev/null +++ b/frontend-partials.lisp @@ -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 sections and extract title, listeners, etc. + (multiple-value-bind (match-start match-end) + (cl-ppcre:scan "" xml-string) + + (if match-start + (let* ((source-section (subseq xml-string match-start + (or (cl-ppcre:scan "" xml-string :start match-start) + (length xml-string)))) + (titlep (cl-ppcre:all-matches "" source-section)) + (listenersp (cl-ppcre:all-matches "<listeners>" source-section)) + (title (if titlep (cl-ppcre:regex-replace-all ".*<title>(.*?).*" source-section "\\1") "Unknown")) + (listeners (if listenersp (cl-ppcre:regex-replace-all ".*(.*?).*" 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 :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)))) diff --git a/template/partial/now-playing.chtml b/template/partial/now-playing.chtml new file mode 100644 index 0000000..4ad60de --- /dev/null +++ b/template/partial/now-playing.chtml @@ -0,0 +1,21 @@ +

Now Playing

+ + + + +

Track: The Void - Silence

+

Listeners: 1

+
+
+ + + +
+ There was an error trying to get information from stream. +
+
+
+

Track: NA

+

Listeners: NA

+
+