Compare commits
38 Commits
5163a577b3
...
3c44bd5f37
| Author | SHA1 | Date |
|---|---|---|
|
|
3c44bd5f37 | |
|
|
53658fb023 | |
|
|
6817e27483 | |
|
|
7493885e4e | |
|
|
4bfc31a3c3 | |
|
|
61570fe2e6 | |
|
|
070e8d8dac | |
|
|
ed13589202 | |
|
|
30ff73a3e2 | |
|
|
f691a1edc8 | |
|
|
27f362f954 | |
|
|
e3e3a144d4 | |
|
|
578306f06f | |
|
|
6bbc3d0b6a | |
|
|
a08e42f752 | |
|
|
24e6859aa0 | |
|
|
d540c87cfc | |
|
|
16d81e8ccc | |
|
|
5f77b4cd4f | |
|
|
d0e40cccad | |
|
|
263dc8a800 | |
|
|
022b1d8b96 | |
|
|
cc79ba7330 | |
|
|
3d7b08119a | |
|
|
c35ae5a1f0 | |
|
|
0d50f01a07 | |
|
|
3c2ddf84c0 | |
|
|
b12e366d2c | |
|
|
3c76436e81 | |
|
|
8b839daf0a | |
|
|
ec00843a90 | |
|
|
aa4ed06d7f | |
|
|
cec3763403 | |
|
|
c198775083 | |
|
|
d187a01641 | |
|
|
f498008d2a | |
|
|
96a3ce2b64 | |
|
|
63d606b39b |
|
|
@ -558,9 +558,11 @@
|
|||
(cond
|
||||
;; Serve ParenScript-compiled auth-ui.js
|
||||
((string= path "js/auth-ui.js")
|
||||
(format t "~%=== SERVING PARENSCRIPT auth-ui.js ===~%")
|
||||
(setf (content-type *response*) "application/javascript")
|
||||
(handler-case
|
||||
(let ((js (generate-auth-ui-js)))
|
||||
(format t "DEBUG: Generated JS length: ~a~%" (if js (length js) "NIL"))
|
||||
(if js js "// Error: No JavaScript generated"))
|
||||
(error (e)
|
||||
(format t "ERROR generating auth-ui.js: ~a~%" e)
|
||||
|
|
@ -568,9 +570,11 @@
|
|||
|
||||
;; Serve ParenScript-compiled front-page.js
|
||||
((string= path "js/front-page.js")
|
||||
(format t "~%=== SERVING PARENSCRIPT front-page.js ===~%")
|
||||
(setf (content-type *response*) "application/javascript")
|
||||
(handler-case
|
||||
(let ((js (generate-front-page-js)))
|
||||
(format t "DEBUG: Generated JS length: ~a~%" (if js (length js) "NIL"))
|
||||
(if js js "// Error: No JavaScript generated"))
|
||||
(error (e)
|
||||
(format t "ERROR generating front-page.js: ~a~%" e)
|
||||
|
|
@ -578,9 +582,11 @@
|
|||
|
||||
;; Serve ParenScript-compiled profile.js
|
||||
((string= path "js/profile.js")
|
||||
(format t "~%=== SERVING PARENSCRIPT profile.js ===~%")
|
||||
(setf (content-type *response*) "application/javascript")
|
||||
(handler-case
|
||||
(let ((js (generate-profile-js)))
|
||||
(format t "DEBUG: Generated JS length: ~a~%" (if js (length js) "NIL"))
|
||||
(if js js "// Error: No JavaScript generated"))
|
||||
(error (e)
|
||||
(format t "ERROR generating profile.js: ~a~%" e)
|
||||
|
|
@ -588,9 +594,11 @@
|
|||
|
||||
;; Serve ParenScript-compiled users.js
|
||||
((string= path "js/users.js")
|
||||
(format t "~%=== SERVING PARENSCRIPT users.js ===~%")
|
||||
(setf (content-type *response*) "application/javascript")
|
||||
(handler-case
|
||||
(let ((js (generate-users-js)))
|
||||
(format t "DEBUG: Generated JS length: ~a~%" (if js (length js) "NIL"))
|
||||
(if js js "// Error: No JavaScript generated"))
|
||||
(error (e)
|
||||
(format t "ERROR generating users.js: ~a~%" e)
|
||||
|
|
@ -598,9 +606,11 @@
|
|||
|
||||
;; Serve ParenScript-compiled admin.js
|
||||
((string= path "js/admin.js")
|
||||
(format t "~%=== SERVING PARENSCRIPT admin.js ===~%")
|
||||
(setf (content-type *response*) "application/javascript")
|
||||
(handler-case
|
||||
(let ((js (generate-admin-js)))
|
||||
(format t "DEBUG: Generated JS length: ~a~%" (if js (length js) "NIL"))
|
||||
(if js js "// Error: No JavaScript generated"))
|
||||
(error (e)
|
||||
(format t "ERROR generating admin.js: ~a~%" e)
|
||||
|
|
@ -608,9 +618,11 @@
|
|||
|
||||
;; Serve ParenScript-compiled player.js
|
||||
((string= path "js/player.js")
|
||||
(format t "~%=== SERVING PARENSCRIPT player.js ===~%")
|
||||
(setf (content-type *response*) "application/javascript")
|
||||
(handler-case
|
||||
(let ((js (generate-player-js)))
|
||||
(format t "DEBUG: Generated JS length: ~a~%" (if js (length js) "NIL"))
|
||||
(if js js "// Error: No JavaScript generated"))
|
||||
(error (e)
|
||||
(format t "ERROR generating player.js: ~a~%" e)
|
||||
|
|
@ -618,9 +630,11 @@
|
|||
|
||||
;; Serve ParenScript-compiled recently-played.js
|
||||
((string= path "js/recently-played.js")
|
||||
(format t "~%=== SERVING PARENSCRIPT recently-played.js ===~%")
|
||||
(setf (content-type *response*) "application/javascript")
|
||||
(handler-case
|
||||
(let ((js (generate-recently-played-js)))
|
||||
(format t "DEBUG: Generated JS length: ~a~%" (if js (length js) "NIL"))
|
||||
(if js js "// Error: No JavaScript generated"))
|
||||
(error (e)
|
||||
(format t "ERROR generating recently-played.js: ~a~%" e)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
(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
|
||||
|
|
@ -33,6 +34,7 @@
|
|||
(aref groups 0)
|
||||
"Unknown")))
|
||||
"Unknown")))
|
||||
(format t "DEBUG: Parsed title=~a, total-listeners=~a~%" title total-listeners)
|
||||
|
||||
;; Track recently played if title changed
|
||||
(when (and title
|
||||
|
|
|
|||
|
|
@ -367,11 +367,6 @@
|
|||
|
||||
;; Live stream info update
|
||||
(defun update-live-stream-info ()
|
||||
;; Don't update if stream is paused
|
||||
(let ((live-audio (ps:chain document (get-element-by-id "live-stream-audio"))))
|
||||
(when (and live-audio (ps:@ live-audio paused))
|
||||
(return)))
|
||||
|
||||
(ps:chain
|
||||
(fetch "/api/asteroid/partial/now-playing-inline")
|
||||
(then (lambda (response)
|
||||
|
|
|
|||
|
|
@ -188,56 +188,7 @@
|
|||
(ps:chain audio-element (load))
|
||||
(ps:chain (ps:chain audio-element (play))
|
||||
(catch (lambda (err)
|
||||
(ps:chain console (log "Reload failed:" err))))))))
|
||||
|
||||
(let ((pause-timestamp nil)
|
||||
(is-reconnecting false)
|
||||
(needs-reconnect false)
|
||||
(pause-reconnect-threshold 10000))
|
||||
|
||||
(ps:chain audio-element
|
||||
(add-event-listener "pause"
|
||||
(lambda ()
|
||||
(setf pause-timestamp (ps:chain |Date| (now)))
|
||||
(ps:chain console (log "Stream paused at:" pause-timestamp)))))
|
||||
|
||||
(ps:chain audio-element
|
||||
(add-event-listener "play"
|
||||
(lambda ()
|
||||
(when (and (not is-reconnecting)
|
||||
pause-timestamp
|
||||
(> (- (ps:chain |Date| (now)) pause-timestamp) pause-reconnect-threshold))
|
||||
(setf needs-reconnect true)
|
||||
(ps:chain console (log "Long pause detected, will reconnect when playing starts...")))
|
||||
(setf pause-timestamp nil))))
|
||||
|
||||
(ps:chain audio-element
|
||||
(add-event-listener "playing"
|
||||
(lambda ()
|
||||
(when (and needs-reconnect (not is-reconnecting))
|
||||
(setf is-reconnecting true)
|
||||
(setf needs-reconnect false)
|
||||
(ps:chain console (log "Reconnecting stream after long pause to clear stale buffers..."))
|
||||
|
||||
(ps:chain audio-element (pause))
|
||||
|
||||
(when (ps:@ window |resetSpectrumAnalyzer|)
|
||||
(ps:chain window (reset-spectrum-analyzer)))
|
||||
|
||||
(ps:chain audio-element (load))
|
||||
|
||||
(set-timeout
|
||||
(lambda ()
|
||||
(ps:chain audio-element (play)
|
||||
(catch (lambda (err)
|
||||
(ps:chain console (log "Reconnect play failed:" err)))))
|
||||
|
||||
(when (ps:@ window |initSpectrumAnalyzer|)
|
||||
(ps:chain window (init-spectrum-analyzer))
|
||||
(ps:chain console (log "Spectrum analyzer reinitialized after reconnect")))
|
||||
|
||||
(setf is-reconnecting false))
|
||||
200))))))))
|
||||
(ps:chain console (log "Reload failed:" err))))))))))
|
||||
|
||||
;; Check frameset preference
|
||||
(let ((path (ps:@ window location pathname))
|
||||
|
|
|
|||
|
|
@ -34,62 +34,11 @@
|
|||
(update-player-display)
|
||||
(update-volume)
|
||||
|
||||
;; Setup live stream with reduced buffering and reconnect logic
|
||||
;; Setup live stream with reduced buffering
|
||||
(let ((live-audio (ps:chain document (get-element-by-id "live-stream-audio"))))
|
||||
(when live-audio
|
||||
;; Reduce buffer to minimize delay
|
||||
(setf (ps:@ live-audio preload) "none")
|
||||
|
||||
;; Add reconnect logic for long pauses
|
||||
(let ((pause-timestamp nil)
|
||||
(is-reconnecting false)
|
||||
(needs-reconnect false)
|
||||
(pause-reconnect-threshold 10000))
|
||||
|
||||
(ps:chain live-audio
|
||||
(add-event-listener "pause"
|
||||
(lambda ()
|
||||
(setf pause-timestamp (ps:chain |Date| (now)))
|
||||
(ps:chain console (log "Live stream paused at:" pause-timestamp)))))
|
||||
|
||||
(ps:chain live-audio
|
||||
(add-event-listener "play"
|
||||
(lambda ()
|
||||
(when (and (not is-reconnecting)
|
||||
pause-timestamp
|
||||
(> (- (ps:chain |Date| (now)) pause-timestamp) pause-reconnect-threshold))
|
||||
(setf needs-reconnect true)
|
||||
(ps:chain console (log "Long pause detected, will reconnect when playing starts...")))
|
||||
(setf pause-timestamp nil))))
|
||||
|
||||
(ps:chain live-audio
|
||||
(add-event-listener "playing"
|
||||
(lambda ()
|
||||
(when (and needs-reconnect (not is-reconnecting))
|
||||
(setf is-reconnecting true)
|
||||
(setf needs-reconnect false)
|
||||
(ps:chain console (log "Reconnecting live stream after long pause to clear stale buffers..."))
|
||||
|
||||
(ps:chain live-audio (pause))
|
||||
|
||||
(when (ps:@ window |resetSpectrumAnalyzer|)
|
||||
(ps:chain window (reset-spectrum-analyzer)))
|
||||
|
||||
(ps:chain live-audio (load))
|
||||
|
||||
(set-timeout
|
||||
(lambda ()
|
||||
(ps:chain live-audio (play)
|
||||
(catch (lambda (err)
|
||||
(ps:chain console (log "Reconnect play failed:" err)))))
|
||||
|
||||
(when (ps:@ window |initSpectrumAnalyzer|)
|
||||
(ps:chain window (init-spectrum-analyzer))
|
||||
(ps:chain console (log "Spectrum analyzer reinitialized after reconnect")))
|
||||
|
||||
(setf is-reconnecting false))
|
||||
200)))))
|
||||
)))
|
||||
(setf (ps:@ live-audio preload) "none")))
|
||||
|
||||
;; Restore user quality preference
|
||||
(let ((selector (ps:chain document (get-element-by-id "live-stream-quality")))
|
||||
|
|
|
|||
|
|
@ -12,30 +12,6 @@
|
|||
(defvar *canvas* nil)
|
||||
(defvar *canvas-ctx* nil)
|
||||
(defvar *animation-id* nil)
|
||||
(defvar *media-source* nil)
|
||||
(defvar *current-audio-element* nil)
|
||||
(defvar *current-theme* "green")
|
||||
(defvar *current-style* "bars")
|
||||
|
||||
;; Color themes for spectrum analyzer
|
||||
(defvar *themes*
|
||||
(ps:create
|
||||
"green" (ps:create "top" "#00ff00" "mid" "#00aa00" "bottom" "#005500")
|
||||
"blue" (ps:create "top" "#00ffff" "mid" "#0088ff" "bottom" "#0044aa")
|
||||
"purple" (ps:create "top" "#ff00ff" "mid" "#aa00aa" "bottom" "#550055")
|
||||
"red" (ps:create "top" "#ff0000" "mid" "#aa0000" "bottom" "#550000")
|
||||
"amber" (ps:create "top" "#ffaa00" "mid" "#ff6600" "bottom" "#aa3300")
|
||||
"rainbow" (ps:create "top" "#ff00ff" "mid" "#00ffff" "bottom" "#00ff00")))
|
||||
|
||||
(defun reset-spectrum-analyzer ()
|
||||
"Reset the spectrum analyzer to allow reconnection after audio element reload"
|
||||
(when *animation-id*
|
||||
(cancel-animation-frame *animation-id*)
|
||||
(setf *animation-id* nil))
|
||||
(setf *audio-context* nil)
|
||||
(setf *analyser* nil)
|
||||
(setf *media-source* nil)
|
||||
(ps:chain console (log "Spectrum analyzer reset for reconnection")))
|
||||
|
||||
(defun init-spectrum-analyzer ()
|
||||
"Initialize the spectrum analyzer"
|
||||
|
|
@ -61,35 +37,27 @@
|
|||
(:catch (e)
|
||||
(ps:chain console (log "Cross-frame access error:" e)))))
|
||||
|
||||
(when (and audio-element canvas-element)
|
||||
;; Store current audio element
|
||||
(setf *current-audio-element* audio-element)
|
||||
(when (and audio-element canvas-element (not *audio-context*))
|
||||
;; Create Audio Context
|
||||
(setf *audio-context* (ps:new (or (ps:@ window |AudioContext|)
|
||||
(ps:@ window |webkitAudioContext|))))
|
||||
|
||||
;; Only create audio context and media source once
|
||||
(when (not *audio-context*)
|
||||
;; Create Audio Context
|
||||
(setf *audio-context* (ps:new (or (ps:@ window |AudioContext|)
|
||||
(ps:@ window |webkitAudioContext|))))
|
||||
;; Create Analyser Node
|
||||
(setf *analyser* (ps:chain *audio-context* (create-analyser)))
|
||||
(setf (ps:@ *analyser* |fftSize|) 256)
|
||||
(setf (ps:@ *analyser* |smoothingTimeConstant|) 0.8)
|
||||
|
||||
;; Create Analyser Node
|
||||
(setf *analyser* (ps:chain *audio-context* (create-analyser)))
|
||||
(setf (ps:@ *analyser* |fftSize|) 256)
|
||||
(setf (ps:@ *analyser* |smoothingTimeConstant|) 0.8)
|
||||
|
||||
;; Connect audio source to analyser (can only be done once per element)
|
||||
(setf *media-source* (ps:chain *audio-context* (create-media-element-source audio-element)))
|
||||
(ps:chain *media-source* (connect *analyser*))
|
||||
(ps:chain *analyser* (connect (ps:@ *audio-context* destination)))
|
||||
|
||||
(ps:chain console (log "Spectrum analyzer audio context created")))
|
||||
;; Connect audio source to analyser
|
||||
(let ((source (ps:chain *audio-context* (create-media-element-source audio-element))))
|
||||
(ps:chain source (connect *analyser*))
|
||||
(ps:chain *analyser* (connect (ps:@ *audio-context* destination))))
|
||||
|
||||
;; Setup canvas
|
||||
(setf *canvas* canvas-element)
|
||||
(setf *canvas-ctx* (ps:chain *canvas* (get-context "2d")))
|
||||
|
||||
;; Start visualization if not already running
|
||||
(when (not *animation-id*)
|
||||
(draw-spectrum)))))
|
||||
;; Start visualization
|
||||
(draw-spectrum))))
|
||||
|
||||
(defun draw-spectrum ()
|
||||
"Draw the spectrum analyzer visualization"
|
||||
|
|
@ -101,8 +69,7 @@
|
|||
(height (ps:@ *canvas* height))
|
||||
(bar-width (/ width buffer-length))
|
||||
(bar-height 0)
|
||||
(x 0)
|
||||
(is-muted (and *current-audio-element* (ps:@ *current-audio-element* muted))))
|
||||
(x 0))
|
||||
|
||||
(ps:chain *analyser* (get-byte-frequency-data data-array))
|
||||
|
||||
|
|
@ -110,68 +77,21 @@
|
|||
(setf (ps:@ *canvas-ctx* |fillStyle|) "rgba(0, 0, 0, 0.2)")
|
||||
(ps:chain *canvas-ctx* (fill-rect 0 0 width height))
|
||||
|
||||
;; Get current theme colors
|
||||
(let ((theme (ps:getprop *themes* *current-theme*)))
|
||||
(cond
|
||||
;; Bar graph style
|
||||
((= *current-style* "bars")
|
||||
(setf x 0)
|
||||
(dotimes (i buffer-length)
|
||||
(setf bar-height (/ (* (aref data-array i) height) 256))
|
||||
;; Draw bars
|
||||
(dotimes (i buffer-length)
|
||||
(setf bar-height (/ (* (aref data-array i) height) 256))
|
||||
|
||||
;; Create gradient for each bar using theme colors
|
||||
(let ((gradient (ps:chain *canvas-ctx*
|
||||
(create-linear-gradient 0 (- height bar-height) 0 height))))
|
||||
(ps:chain gradient (add-color-stop 0 (ps:@ theme top)))
|
||||
(ps:chain gradient (add-color-stop 0.5 (ps:@ theme mid)))
|
||||
(ps:chain gradient (add-color-stop 1 (ps:@ theme bottom)))
|
||||
;; Create gradient for each bar
|
||||
(let ((gradient (ps:chain *canvas-ctx*
|
||||
(create-linear-gradient 0 (- height bar-height) 0 height))))
|
||||
(ps:chain gradient (add-color-stop 0 "#00ff00"))
|
||||
(ps:chain gradient (add-color-stop 0.5 "#00aa00"))
|
||||
(ps:chain gradient (add-color-stop 1 "#005500"))
|
||||
|
||||
(setf (ps:@ *canvas-ctx* |fillStyle|) gradient)
|
||||
(ps:chain *canvas-ctx* (fill-rect x (- height bar-height) bar-width bar-height))
|
||||
(setf (ps:@ *canvas-ctx* |fillStyle|) gradient)
|
||||
(ps:chain *canvas-ctx* (fill-rect x (- height bar-height) bar-width bar-height))
|
||||
|
||||
(incf x bar-width))))
|
||||
|
||||
;; Wave/line style
|
||||
((= *current-style* "wave")
|
||||
(setf x 0)
|
||||
(ps:chain *canvas-ctx* (begin-path))
|
||||
(setf (ps:@ *canvas-ctx* |lineWidth|) 2)
|
||||
(setf (ps:@ *canvas-ctx* |strokeStyle|) (ps:@ theme top))
|
||||
|
||||
(dotimes (i buffer-length)
|
||||
(setf bar-height (/ (* (aref data-array i) height) 256))
|
||||
(let ((y (- height bar-height)))
|
||||
(if (= i 0)
|
||||
(ps:chain *canvas-ctx* (move-to x y))
|
||||
(ps:chain *canvas-ctx* (line-to x y)))
|
||||
(incf x bar-width)))
|
||||
|
||||
(ps:chain *canvas-ctx* (stroke)))
|
||||
|
||||
;; Dots/particles style
|
||||
((= *current-style* "dots")
|
||||
(setf x 0)
|
||||
(setf (ps:@ *canvas-ctx* |fillStyle|) (ps:@ theme top))
|
||||
(dotimes (i buffer-length)
|
||||
(let* ((value (aref data-array i))
|
||||
(normalized-height (/ (* value height) 256))
|
||||
(y (- height normalized-height))
|
||||
(dot-radius (ps:max 2 (/ normalized-height 20))))
|
||||
|
||||
(when (> value 0)
|
||||
(ps:chain *canvas-ctx* (begin-path))
|
||||
(ps:chain *canvas-ctx* (arc x y dot-radius 0 6.283185307179586))
|
||||
(ps:chain *canvas-ctx* (fill)))
|
||||
|
||||
(incf x bar-width))))))
|
||||
|
||||
;; Draw MUTED indicator if audio is muted
|
||||
(when is-muted
|
||||
(setf (ps:@ *canvas-ctx* |fillStyle|) "rgba(255, 0, 0, 0.8)")
|
||||
(setf (ps:@ *canvas-ctx* font) "bold 20px monospace")
|
||||
(setf (ps:@ *canvas-ctx* |textAlign|) "right")
|
||||
(setf (ps:@ *canvas-ctx* |textBaseline|) "top")
|
||||
(ps:chain *canvas-ctx* (fill-text "MUTED" (- width 10) 10)))))
|
||||
(incf x bar-width)))))
|
||||
|
||||
(defun stop-spectrum-analyzer ()
|
||||
"Stop the spectrum analyzer"
|
||||
|
|
@ -179,47 +99,9 @@
|
|||
(cancel-animation-frame *animation-id*)
|
||||
(setf *animation-id* nil)))
|
||||
|
||||
(defun set-spectrum-theme (theme-name)
|
||||
"Change the spectrum analyzer color theme"
|
||||
(when (ps:getprop *themes* theme-name)
|
||||
(setf *current-theme* theme-name)
|
||||
(ps:chain local-storage (set-item "spectrum-theme" theme-name))
|
||||
(ps:chain console (log (+ "Spectrum theme changed to: " theme-name)))))
|
||||
|
||||
(defun get-available-themes ()
|
||||
"Return array of available theme names"
|
||||
(ps:chain |Object| (keys *themes*)))
|
||||
|
||||
(defun set-spectrum-style (style-name)
|
||||
"Change the spectrum analyzer visualization style"
|
||||
(when (or (= style-name "bars") (= style-name "wave") (= style-name "dots"))
|
||||
(setf *current-style* style-name)
|
||||
(ps:chain local-storage (set-item "spectrum-style" style-name))
|
||||
(ps:chain console (log (+ "Spectrum style changed to: " style-name)))))
|
||||
|
||||
(defun get-available-styles ()
|
||||
"Return array of available visualization styles"
|
||||
(array "bars" "wave" "dots"))
|
||||
|
||||
;; Initialize when audio starts playing
|
||||
(ps:chain document (add-event-listener "DOMContentLoaded"
|
||||
(lambda ()
|
||||
;; Load saved theme and style preferences
|
||||
(let ((saved-theme (ps:chain local-storage (get-item "spectrum-theme")))
|
||||
(saved-style (ps:chain local-storage (get-item "spectrum-style"))))
|
||||
(when (and saved-theme (ps:getprop *themes* saved-theme))
|
||||
(setf *current-theme* saved-theme))
|
||||
(when (and saved-style (or (= saved-style "bars") (= saved-style "wave") (= saved-style "dots")))
|
||||
(setf *current-style* saved-style))
|
||||
|
||||
;; Update UI selectors if they exist
|
||||
(let ((theme-selector (ps:chain document (get-element-by-id "spectrum-theme-selector")))
|
||||
(style-selector (ps:chain document (get-element-by-id "spectrum-style-selector"))))
|
||||
(when theme-selector
|
||||
(setf (ps:@ theme-selector value) *current-theme*))
|
||||
(when style-selector
|
||||
(setf (ps:@ style-selector value) *current-style*))))
|
||||
|
||||
(let ((audio-element (or (ps:chain document (get-element-by-id "live-audio"))
|
||||
(ps:chain document (get-element-by-id "persistent-audio")))))
|
||||
|
||||
|
|
|
|||
|
|
@ -1161,12 +1161,10 @@
|
|||
:opacity 1
|
||||
:background-color #(color-accented-black)))
|
||||
|
||||
;; Live stream pulse animation (like old MacBook sleep indicator)
|
||||
;; Live stream blink animation
|
||||
(.live-stream-indicator
|
||||
:animation "asteroid-pulse 2s ease-in-out infinite")
|
||||
:animation "asteroid-blink 1s steps(5, start) infinite")
|
||||
|
||||
(:keyframes asteroid-pulse
|
||||
(0% :opacity 1)
|
||||
(50% :opacity 0.3)
|
||||
(100% :opacity 1))
|
||||
(:keyframes asteroid-blink
|
||||
(to :visibility "hidden"))
|
||||
) ;; End of let block
|
||||
|
|
|
|||
|
|
@ -26,27 +26,6 @@
|
|||
<!-- Spectrum Analyzer Canvas -->
|
||||
<div style="text-align: center; margin: 15px 0;">
|
||||
<canvas id="spectrum-canvas" width="800" height="100" style="max-width: 100%; border: 1px solid #00ff00; background: #000; border-radius: 4px;"></canvas>
|
||||
<div style="margin-top: 8px; font-size: 0.9em;">
|
||||
<label style="margin-right: 10px;">
|
||||
Style:
|
||||
<select id="spectrum-style-selector" onchange="setSpectrumStyle(this.value)" style="padding: 3px; background: #000; color: #00ff00; border: 1px solid #00ff00;">
|
||||
<option value="bars">Bars</option>
|
||||
<option value="wave">Wave</option>
|
||||
<option value="dots">Dots</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Theme:
|
||||
<select id="spectrum-theme-selector" onchange="setSpectrumTheme(this.value)" style="padding: 3px; background: #000; color: #00ff00; border: 1px solid #00ff00;">
|
||||
<option value="green">Green</option>
|
||||
<option value="blue">Blue</option>
|
||||
<option value="purple">Purple</option>
|
||||
<option value="red">Red</option>
|
||||
<option value="amber">Amber</option>
|
||||
<option value="rainbow">Rainbow</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav">
|
||||
|
|
|
|||
|
|
@ -26,27 +26,6 @@
|
|||
<!-- Spectrum Analyzer Canvas -->
|
||||
<div style="text-align: center; margin: 15px 0;">
|
||||
<canvas id="spectrum-canvas" width="800" height="100" style="max-width: 100%; border: 1px solid #00ff00; background: #000; border-radius: 4px;"></canvas>
|
||||
<div style="margin-top: 8px; font-size: 0.9em;">
|
||||
<label style="margin-right: 10px;">
|
||||
Style:
|
||||
<select id="spectrum-style-selector" onchange="setSpectrumStyle(this.value)" style="padding: 3px; background: #000; color: #00ff00; border: 1px solid #00ff00;">
|
||||
<option value="bars">Bars</option>
|
||||
<option value="wave">Wave</option>
|
||||
<option value="dots">Dots</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Theme:
|
||||
<select id="spectrum-theme-selector" onchange="setSpectrumTheme(this.value)" style="padding: 3px; background: #000; color: #00ff00; border: 1px solid #00ff00;">
|
||||
<option value="green">Green</option>
|
||||
<option value="blue">Blue</option>
|
||||
<option value="purple">Purple</option>
|
||||
<option value="red">Red</option>
|
||||
<option value="amber">Amber</option>
|
||||
<option value="rainbow">Rainbow</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav">
|
||||
|
|
|
|||
|
|
@ -20,27 +20,6 @@
|
|||
<!-- Spectrum Analyzer Canvas -->
|
||||
<div style="text-align: center; margin: 15px 0;">
|
||||
<canvas id="spectrum-canvas" width="800" height="100" style="max-width: 100%; border: 1px solid #00ff00; background: #000; border-radius: 4px;"></canvas>
|
||||
<div style="margin-top: 8px; font-size: 0.9em;">
|
||||
<label style="margin-right: 10px;">
|
||||
Style:
|
||||
<select id="spectrum-style-selector" onchange="setSpectrumStyle(this.value)" style="padding: 3px; background: #000; color: #00ff00; border: 1px solid #00ff00;">
|
||||
<option value="bars">Bars</option>
|
||||
<option value="wave">Wave</option>
|
||||
<option value="dots">Dots</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Theme:
|
||||
<select id="spectrum-theme-selector" onchange="setSpectrumTheme(this.value)" style="padding: 3px; background: #000; color: #00ff00; border: 1px solid #00ff00;">
|
||||
<option value="green">Green</option>
|
||||
<option value="blue">Blue</option>
|
||||
<option value="purple">Purple</option>
|
||||
<option value="red">Red</option>
|
||||
<option value="amber">Amber</option>
|
||||
<option value="rainbow">Rainbow</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="nav">
|
||||
|
|
|
|||
Loading…
Reference in New Issue