diff --git a/asteroid.lisp b/asteroid.lisp index 1f45c77..eb07dcf 100644 --- a/asteroid.lisp +++ b/asteroid.lisp @@ -277,6 +277,19 @@ ("message" . ,(format nil "Error reordering queue: ~a" e))) :status 500)))) +(define-api asteroid/stream/queue/load-m3u () () + "Load stream queue from stream-queue.m3u file" + (require-role :admin) + (handler-case + (let ((count (load-queue-from-m3u-file))) + (api-output `(("status" . "success") + ("message" . "Queue loaded from M3U file") + ("count" . ,count)))) + (error (e) + (api-output `(("status" . "error") + ("message" . ,(format nil "Error loading from M3U: ~a" e))) + :status 500)))) + (defun get-track-by-id (track-id) "Get a track by its ID - handles type mismatches" ;; Try direct query first @@ -824,6 +837,16 @@ :now-playing-album "Startup Sounds" :player-status "Stopped"))) +(define-page popout-player #@"/popout-player" () + "Pop-out player window" + (let ((template-path (merge-pathnames "template/popout-player.chtml" + (asdf:system-source-directory :asteroid)))) + (clip:process-to-string + (plump:parse (alexandria:read-file-into-string template-path)) + :stream-base-url *stream-base-url* + :default-stream-url (concatenate 'string *stream-base-url* "/asteroid.aac") + :default-stream-encoding "audio/aac"))) + (define-api asteroid/status () () "Get server status" (api-output `(("status" . "running") diff --git a/docker/asteroid-radio-docker.liq b/docker/asteroid-radio-docker.liq index 6831702..953b67e 100644 --- a/docker/asteroid-radio-docker.liq +++ b/docker/asteroid-radio-docker.liq @@ -9,6 +9,11 @@ set("init.allow_root", true) # Set log level for debugging log.level.set(4) +# Audio buffering settings to prevent choppiness +settings.frame.audio.samplerate.set(44100) +settings.frame.audio.channels.set(2) +settings.audio.converter.samplerate.libsamplerate.quality.set("best") + # Enable telnet server for remote control settings.server.telnet.set(true) settings.server.telnet.port.set(1234) @@ -19,8 +24,8 @@ settings.server.telnet.bind_addr.set("0.0.0.0") # Falls back to directory scan if playlist file doesn't exist radio = playlist( mode="normal", # Play in order (not randomized) - reload=5, # Check for playlist updates every 5 seconds - reload_mode="watch", # Watch file for changes + reload=30, # Check for playlist updates every 30 seconds + reload_mode="seconds", # Reload every N seconds (prevents running out of tracks) "/app/stream-queue.m3u" ) @@ -34,24 +39,11 @@ radio_fallback = playlist.safe( # Use main playlist, fall back to directory scan radio = fallback(track_sensitive=false, [radio, radio_fallback]) -# Add some audio processing -# Use ReplayGain for consistent volume without pumping -radio = amplify(1.0, override="replaygain", radio) - -# Add smooth crossfade between tracks (5 seconds) +# Simple crossfade for smooth transitions radio = crossfade( - duration=5.0, # 5 second crossfade - fade_in=3.0, # 3 second fade in - fade_out=3.0, # 3 second fade out - radio -) - -# Add a compressor to prevent clipping -radio = compress( - ratio=3.0, # Compression ratio - threshold=-15.0, # Threshold in dB - attack=50.0, # Attack time in ms - release=400.0, # Release time in ms + duration=3.0, # 3 second crossfade + fade_in=2.0, # 2 second fade in + fade_out=2.0, # 2 second fade out radio ) diff --git a/static/js/admin.js b/static/js/admin.js index 0626b27..a69f9ca 100644 --- a/static/js/admin.js +++ b/static/js/admin.js @@ -28,11 +28,13 @@ document.addEventListener('DOMContentLoaded', function() { // Queue controls const refreshQueueBtn = document.getElementById('refresh-queue'); + const loadFromM3uBtn = document.getElementById('load-from-m3u'); const clearQueueBtn = document.getElementById('clear-queue-btn'); const addRandomBtn = document.getElementById('add-random-tracks'); const queueSearchInput = document.getElementById('queue-track-search'); if (refreshQueueBtn) refreshQueueBtn.addEventListener('click', loadStreamQueue); + if (loadFromM3uBtn) loadFromM3uBtn.addEventListener('click', loadQueueFromM3U); if (clearQueueBtn) clearQueueBtn.addEventListener('click', clearStreamQueue); if (addRandomBtn) addRandomBtn.addEventListener('click', addRandomTracks); if (queueSearchInput) queueSearchInput.addEventListener('input', searchTracksForQueue); @@ -103,8 +105,6 @@ function renderPage() {