asteroid/frontend-partials.lisp

110 lines
5.1 KiB
Common Lisp

(in-package :asteroid)
(defun find-track-by-title (title)
"Find a track in the database by its title. Returns track ID or nil.
Handles 'Artist - Title' format from stream metadata."
(when (and title (not (string= title "Unknown")))
(handler-case
(with-db
;; Parse 'Artist - Title' format if present
(let* ((parts (cl-ppcre:split " - " title :limit 2))
(has-artist (> (length parts) 1))
(artist-part (when has-artist (first parts)))
(title-part (if has-artist (second parts) title))
(result
(if has-artist
;; Search by both artist and title
(postmodern:query
(:limit
(:select '_id
:from 'tracks
:where (:and (:ilike 'artist (format nil "%~a%" artist-part))
(:ilike 'title (format nil "%~a%" title-part))))
1)
:single)
;; Fallback: search by title only
(postmodern:query
(:limit
(:select '_id
:from 'tracks
:where (:ilike 'title (format nil "%~a%" title-part)))
1)
:single))))
result))
(error (e)
(declare (ignore e))
nil))))
(defun get-now-playing-stats (&optional (mount "asteroid.mp3"))
"Get now-playing stats from the Harmony pipeline.
Returns an alist with :listenurl, :title, :listeners, :track-id, :favorite-count."
(harmony-now-playing mount))
(define-api-with-limit asteroid/partial/now-playing (&optional mount) (:limit 10 :timeout 1)
"Get Partial HTML with live now-playing status.
Optional MOUNT parameter specifies which stream to get metadata from.
Returns partial HTML with current track info."
(with-error-handling
(let* ((mount-name (or mount "asteroid.mp3"))
(now-playing-stats (get-now-playing-stats mount-name)))
(if now-playing-stats
(let* ((title (cdr (assoc :title now-playing-stats)))
(favorite-count (or (get-track-favorite-count title) 0)))
(setf (header "Content-Type") "text/html")
(clip:process-to-string
(load-template "partial/now-playing")
:stats now-playing-stats
:track-id (cdr (assoc :track-id now-playing-stats))
:favorite-count favorite-count))
(progn
(setf (header "Content-Type") "text/html")
(clip:process-to-string
(load-template "partial/now-playing")
:connection-error t
:stats nil))))))
(define-api-with-limit asteroid/partial/now-playing-inline (&optional mount) (:limit 10 :timeout 1)
"Get inline text with now playing info (for admin dashboard and widgets).
Optional MOUNT parameter specifies which stream to get metadata from."
(with-error-handling
(let* ((mount-name (or mount "asteroid.mp3"))
(now-playing-stats (get-now-playing-stats mount-name)))
(if now-playing-stats
(progn
(setf (header "Content-Type") "text/plain")
(cdr (assoc :title now-playing-stats)))
(progn
(setf (header "Content-Type") "text/plain")
"Stream Offline")))))
(define-api-with-limit asteroid/partial/now-playing-json (&optional mount) (:limit 10 :timeout 1)
"Get JSON with now playing info including track ID for favorites.
Optional MOUNT parameter specifies which stream to get metadata from."
;; Register web listener for geo stats (keeps listener active during playback)
(register-web-listener)
(with-error-handling
(let* ((mount-name (or mount "asteroid.mp3"))
(now-playing-stats (get-now-playing-stats mount-name)))
(if now-playing-stats
(let* ((title (cdr (assoc :title now-playing-stats)))
(favorite-count (or (get-track-favorite-count title) 0))
(parsed (parse-track-title title))
(artist (getf parsed :artist))
(song (getf parsed :song))
(search-url (generate-music-search-url artist song)))
(api-output `(("status" . "success")
("title" . ,title)
("listeners" . ,(cdr (assoc :listeners now-playing-stats)))
("track_id" . ,(cdr (assoc :track-id now-playing-stats)))
("favorite_count" . ,favorite-count)
("search_url" . ,search-url))))
(api-output `(("status" . "offline")
("title" . "Stream Offline")
("track_id" . nil)))))))
(define-api-with-limit asteroid/channel-name () (:limit 180 :timeout 60)
"Get the current curated channel name for live updates.
Returns JSON with the channel name from the current playlist's PHASE header."
(with-error-handling
(api-output `(("channel_name" . ,(get-curated-channel-name))))))