Fix: Add dynamic stream URL detection for multi-environment support

- Add get-stream-base-url() function that detects host from HTTP request
- Replace hardcoded *stream-base-url* with dynamic function calls in all templates
- Supports localhost, Tailscale, and VPS deployments automatically
- Fixes JavaScript error 'streamBaseUrl is null' on remote access
- Maintains backward compatibility with ASTEROID_STREAM_URL env var
This commit is contained in:
Glenn Thompson 2025-10-24 05:54:26 +03:00
parent 90bb9a1650
commit 69d97a74da
1 changed files with 78 additions and 55 deletions

View File

@ -21,6 +21,17 @@
(defparameter *supported-formats* '("mp3" "flac" "ogg" "wav")) (defparameter *supported-formats* '("mp3" "flac" "ogg" "wav"))
(defparameter *stream-base-url* "http://localhost:8000") (defparameter *stream-base-url* "http://localhost:8000")
(defun get-stream-base-url ()
"Get the stream base URL, using the request host if available"
(if (boundp 'radiance:*request*)
(let* ((host (or (header "Host") "localhost:8080"))
;; Remove port if present and add :8000
(host-without-port (if (position #\: host)
(subseq host 0 (position #\: host))
host)))
(format nil "http://~a:8000" host-without-port))
*stream-base-url*))
;; Configure JSON as the default API format ;; Configure JSON as the default API format
(define-api-format json (data) (define-api-format json (data)
"JSON API format for Radiance" "JSON API format for Radiance"
@ -443,21 +454,23 @@
;; Front page - regular view by default ;; Front page - regular view by default
(define-page front-page #@"/" () (define-page front-page #@"/" ()
"Main front page" "Main front page"
(clip:process-to-string (let ((template-path (merge-pathnames "template/front-page.chtml"
(load-template "front-page") (asdf:system-source-directory :asteroid))))
:title "🎵 ASTEROID RADIO 🎵" (clip:process-to-string
:station-name "🎵 ASTEROID RADIO 🎵" (plump:parse (alexandria:read-file-into-string template-path))
:status-message "🟢 LIVE - Broadcasting asteroid music for hackers" :title "🎵 ASTEROID RADIO 🎵"
:listeners "0" :station-name "🎵 ASTEROID RADIO 🎵"
:stream-quality "128kbps MP3" :status-message "🟢 LIVE - Broadcasting asteroid music for hackers"
:stream-base-url *stream-base-url* :listeners "0"
:default-stream-url (format nil "~a/asteroid.aac" *stream-base-url*) :stream-quality "128kbps MP3"
:default-stream-encoding "audio/aac" :stream-base-url (get-stream-base-url)
:default-stream-encoding-desc "AAC 96kbps Stereo" :default-stream-url (concatenate 'string (get-stream-base-url) "/asteroid.aac")
:now-playing-artist "The Void" :default-stream-encoding "audio/aac"
:now-playing-track "Silence" :default-stream-encoding-desc "AAC 96kbps Stereo"
:now-playing-album "Startup Sounds" :now-playing-artist "The Void"
:now-playing-duration "∞")) :now-playing-track "Silence"
:now-playing-album "Startup Sounds"
:now-playing-duration "∞")))
;; Frameset wrapper for persistent player mode ;; Frameset wrapper for persistent player mode
(define-page frameset-wrapper #@"/frameset" () (define-page frameset-wrapper #@"/frameset" ()
@ -469,27 +482,31 @@
;; Content frame - front page content without player ;; Content frame - front page content without player
(define-page front-page-content #@"/content" () (define-page front-page-content #@"/content" ()
"Front page content (displayed in content frame)" "Front page content (displayed in content frame)"
(clip:process-to-string (let ((template-path (merge-pathnames "template/front-page-content.chtml"
(load-template "front-page-content") (asdf:system-source-directory :asteroid))))
:title "🎵 ASTEROID RADIO 🎵" (clip:process-to-string
:station-name "🎵 ASTEROID RADIO 🎵" (plump:parse (alexandria:read-file-into-string template-path))
:status-message "🟢 LIVE - Broadcasting asteroid music for hackers" :title "🎵 ASTEROID RADIO 🎵"
:listeners "0" :station-name "🎵 ASTEROID RADIO 🎵"
:stream-quality "128kbps MP3" :status-message "🟢 LIVE - Broadcasting asteroid music for hackers"
:stream-base-url *stream-base-url* :listeners "0"
:now-playing-artist "The Void" :stream-quality "128kbps MP3"
:now-playing-track "Silence" :stream-base-url (get-stream-base-url)
:now-playing-album "Startup Sounds" :now-playing-artist "The Void"
:now-playing-duration "∞")) :now-playing-track "Silence"
:now-playing-album "Startup Sounds"
:now-playing-duration "∞")))
;; Persistent audio player frame (bottom frame) ;; Persistent audio player frame (bottom frame)
(define-page audio-player-frame #@"/audio-player-frame" () (define-page audio-player-frame #@"/audio-player-frame" ()
"Persistent audio player frame (bottom of page)" "Persistent audio player frame (bottom of page)"
(clip:process-to-string (let ((template-path (merge-pathnames "template/audio-player-frame.chtml"
(load-template "audio-player-frame") (asdf:system-source-directory :asteroid))))
:stream-base-url *stream-base-url* (clip:process-to-string
:default-stream-url (format nil "~a/asteroid.aac" *stream-base-url*) (plump:parse (alexandria:read-file-into-string template-path))
:default-stream-encoding "audio/aac")) :stream-base-url (get-stream-base-url)
:default-stream-url (concatenate 'string (get-stream-base-url) "/asteroid.aac")
:default-stream-encoding "audio/aac")))
;; Configure static file serving for other files ;; Configure static file serving for other files
(define-page static #@"/static/(.*)" (:uri-groups (path)) (define-page static #@"/static/(.*)" (:uri-groups (path))
@ -536,8 +553,8 @@
:icecast-status (check-icecast-status) :icecast-status (check-icecast-status)
:track-count (format nil "~d" track-count) :track-count (format nil "~d" track-count)
:library-path "/home/glenn/Projects/Code/asteroid/music/library/" :library-path "/home/glenn/Projects/Code/asteroid/music/library/"
:stream-base-url *stream-base-url* :stream-base-url (get-stream-base-url)
:default-stream-url (format nil "~a/asteroid.aac" *stream-base-url*)))) :default-stream-url (concatenate 'string (get-stream-base-url) "/asteroid.aac"))))
;; User Management page (requires authentication) ;; User Management page (requires authentication)
(define-page users-management #@"/admin/user" () (define-page users-management #@"/admin/user" ()
@ -761,34 +778,40 @@
:success-message "")))) :success-message ""))))
(define-page player #@"/player" () (define-page player #@"/player" ()
(clip:process-to-string (let ((template-path (merge-pathnames "template/player.chtml"
(load-template "player") (asdf:system-source-directory :asteroid))))
:title "Asteroid Radio - Web Player" (clip:process-to-string
:stream-base-url *stream-base-url* (plump:parse (alexandria:read-file-into-string template-path))
:default-stream-url (format nil "~a/asteroid.aac" *stream-base-url*) :title "Asteroid Radio - Web Player"
:bitrate "128kbps MP3" :stream-base-url (get-stream-base-url)
:now-playing-artist "The Void" :default-stream-url (concatenate 'string (get-stream-base-url) "/asteroid.aac")
:now-playing-track "Silence" :bitrate "128kbps MP3"
:now-playing-album "Startup Sounds" :now-playing-artist "The Void"
:player-status "Stopped")) :now-playing-track "Silence"
:now-playing-album "Startup Sounds"
:player-status "Stopped")))
;; Player content frame (for frameset mode) ;; Player content frame (for frameset mode)
(define-page player-content #@"/player-content" () (define-page player-content #@"/player-content" ()
"Player page content (displayed in content frame)" "Player page content (displayed in content frame)"
(clip:process-to-string (let ((template-path (merge-pathnames "template/player-content.chtml"
(load-template "player-content") (asdf:system-source-directory :asteroid))))
:title "Asteroid Radio - Web Player" (clip:process-to-string
:stream-base-url *stream-base-url* (plump:parse (alexandria:read-file-into-string template-path))
:default-stream-url (format nil "~a/asteroid.aac" *stream-base-url*) :title "Asteroid Radio - Web Player"
:default-stream-encoding "audio/aac")) :stream-base-url (get-stream-base-url)
:default-stream-url (concatenate 'string (get-stream-base-url) "/asteroid.aac")
:default-stream-encoding "audio/aac")))
(define-page popout-player #@"/popout-player" () (define-page popout-player #@"/popout-player" ()
"Pop-out player window" "Pop-out player window"
(clip:process-to-string (let ((template-path (merge-pathnames "template/popout-player.chtml"
(load-template "popout-player") (asdf:system-source-directory :asteroid))))
:stream-base-url *stream-base-url* (clip:process-to-string
:default-stream-url (format nil "~a/asteroid.aac" *stream-base-url*) (plump:parse (alexandria:read-file-into-string template-path))
:default-stream-encoding "audio/aac")) :stream-base-url (get-stream-base-url)
:default-stream-url (concatenate 'string (get-stream-base-url) "/asteroid.aac")
:default-stream-encoding "audio/aac")))
(define-api asteroid/status () () (define-api asteroid/status () ()
"Get server status" "Get server status"