Compare commits

...

15 Commits

Author SHA1 Message Date
Glenn Thompson 2ed92ba003 Fix wedged player with reconnect button and volume preservation
- Add reconnect button (🔄) to frameset player bar
- Recreate audio element on reconnect to fix wedged MediaElementSource
- Properly close and reinitialize AudioContext for spectrum analyzer
- Preserve volume and muted state when reconnecting
- Show status messages for connection issues
- Reduce Now Playing update interval to 5 seconds
- Add front-page.js to player-content.ctml for Now Playing updates
- Create About pages (about.ctml and about-content.ctml)
- Add About link to navigation in both modes
2025-12-08 05:01:15 +03:00
Brian O'Reilly 8fd0b06b69 dump users in db to csv, and restore them from same
this is (hopefully) a write once run once utility, but we're moving
from sqlite to postgres, and I don't want to lose the users that have
already signed up for the site.
2025-12-07 19:44:04 -05:00
Brian O'Reilly 4ca6570d5e correct the database creation script for the expected fields. 2025-12-07 19:44:04 -05:00
Brian O'Reilly c9f6cb2aa7 ... And then we ask: How did this *ever* work??
(It did. On my machine.)
2025-12-07 19:44:04 -05:00
Brian O'Reilly 6c6732d1e0 as far as I'm concerned, this is version 1.
Bump the major version number in the asdf system definition file, for
great ceremony.
2025-12-07 19:44:04 -05:00
Brian O'Reilly 8754e7261f pull creds from the enclosing environment
When we stand up, we establish the configuration for database
authentication in the postgres docker container. Put these credentials
into environment variables for production, and default to known values
for development.
2025-12-07 19:44:04 -05:00
Brian O'Reilly e55210c332 Source database auth creds from the environment
Defaults to known values for development.
source the environment credentials so they're available at container build.
2025-12-07 19:44:04 -05:00
Brian O'Reilly d66b7b8053 this effectively reverts the previous case change of the USERS table name
I guess for now we'll leave this quirk in
2025-12-07 19:44:04 -05:00
Brian O'Reilly 44096dfb4b move m3u files into dedicated directory.
These playlist files are useful only to the installation at
https://asteroid.radio, because they reference tracks that likely do
not exist in any other library where this code might be run.
Regardless, it's useful to me, and saving this work somewhere durable
has intrinsic value to me, operationally. If nothing else, this shows
the structure of a plalist for use with this code.
2025-12-07 19:44:04 -05:00
Brian O'Reilly 9cbc0a7780 the db is connected, so init the users. 2025-12-07 19:44:04 -05:00
Brian O'Reilly 7a4e9c208a creating the users table all caps style gives us duplicates in postgres. 2025-12-07 19:44:04 -05:00
Luis Pereira aa84ff4470 fix: improve live stream text proportions
Conflicts:
	static/asteroid.css
2025-12-07 19:44:04 -05:00
Brian O'Reilly 9bb31ec88c many things, almost working, but not quite. 2025-12-07 19:44:04 -05:00
Brian O'Reilly 6c2ed75b15 include i-postgmodern interface for postgres.
Conflicts:
	asteroid.asd
2025-12-07 19:44:04 -05:00
Brian O'Reilly d2508451d0 Delete the music/library directory which held an erroneous path to a softlink that doesn't exist in prod. 2025-12-07 19:44:04 -05:00
29 changed files with 3144 additions and 165 deletions

1
.gitignore vendored
View File

@ -56,3 +56,4 @@ logs/
performance-logs/
# Temporary files
/static/asteroid.css

View File

@ -3,16 +3,20 @@
(defpackage :asteroid.app-utils
(:use :cl)
(:export :internal-disable-debugger)
(:export :internal-quit))
(:export :internal-quit
:pht))
(in-package :asteroid.app-utils)
(defun pht (ht)
(alexandria:hash-table-alist ht))
(defun internal-disable-debugger ()
(labels
((internal-exit (c h)
(declare (ignore h))
(format t "~a~%" c)
(internal-quit)))
(declare (ignore h))
(format t "~a~%" c)
(internal-quit)))
(setf *debugger-hook* #'internal-exit)))
(defun internal-quit (&optional code)

View File

@ -6,7 +6,7 @@
:author "Brian O'Reilly <fade@deepsky.com>"
:license "GNU AFFERO GENERAL PUBLIC LICENSE V.3"
:serial t
:version "0.0.0"
:version "1.0.0"
:defsystem-depends-on (:radiance)
:class "radiance:virtual-module"
:depends-on (:slynk
@ -19,6 +19,7 @@
:lass
:parenscript
:cl-json
:cl-csv
:alexandria
:local-time
:taglib
@ -28,6 +29,9 @@
:bordeaux-threads
:drakma
;; radiance interfaces
:i-log4cl
:i-postmodern
:r-clip
:r-data-model
(:interface :auth)
(:interface :database)

View File

@ -930,6 +930,20 @@
:default-stream-url (format nil "~a/asteroid.aac" *stream-base-url*)
:default-stream-encoding "audio/aac"))
;; About page (non-frameset mode)
(define-page about-page #@"/about" ()
"About Asteroid Radio"
(clip:process-to-string
(load-template "about")
:title "About - Asteroid Radio"))
;; About content (for frameset mode)
(define-page about-content #@"/about-content" ()
"About page content (displayed in content frame)"
(clip:process-to-string
(load-template "about-content")
:title "About - Asteroid Radio"))
(define-api asteroid/status () ()
"Get server status"
(api-output `(("status" . "running")
@ -1012,8 +1026,11 @@
(defun ensure-radiance-environment ()
"Ensure RADIANCE environment is properly configured for persistence"
(unless (radiance:environment)
(setf (radiance:environment) "asteroid"))
(if (radiance:environment)
(format t "~2&Startup default environment: ~A~2%" (radiance:environment))
(progn
(setf (radiance:environment) "asteroid")
(format t "~2&Set environment to: ~A~2%" (radiance:environment))))
(log:info "~2&~15A - ~A~%~15A - ~A~%~15A - ~A~%~15A - ~A~%~15A - ~A~2%"
":configuration"
@ -1025,7 +1042,16 @@
":template"
(radiance:environment-directory (radiance-core:environment) :template)
":static"
(radiance:environment-directory (radiance-core:environment) :static)))
(radiance:environment-directory (radiance-core:environment) :static))
(db:connect :main))
(defun start-slynk-server-in-new-thread (&optional (port 4009))
"Starts a Slynk server in a new thread on the specified port."
(bt:make-thread (lambda ()
(format t "~&Starting Slynk server on port ~a in a new thread.~%" port)
(slynk:create-server :port port :dont-close t))
:name (format nil "Slynk Server Thread on Port ~a" port)))
(defun -main (&optional args (debug t))
(declare (ignorable args))
@ -1035,9 +1061,10 @@
(format t "~%🎵 ASTEROID RADIO - Music for Hackers 🎵~%")
(format t "Using stream server at ~a~%" *stream-base-url*)
(format t "Starting RADIANCE web server...~%")
(when debug
(slynk:create-server :port 4009 :dont-close t))
(start-slynk-server-in-new-thread 4009))
(format t "Starting RADIANCE web server...~%")
;; Ensure proper environment setup before starting
(ensure-radiance-environment)
@ -1049,4 +1076,3 @@
;; For now, use the "Scan Library" button in the admin interface
(run-server))

View File

@ -1,17 +1,16 @@
;;;; Radiance PostgreSQL Configuration for Asteroid Radio
;;;; This file configures Radiance to use PostgreSQL instead of the default database
(in-package #:radiance-user)
(in-package #:i-postmodern)
;; PostgreSQL Database Configuration
(setf (config :database :connection)
'(:type :postgres
:host "localhost" ; Change to "asteroid-postgres" when running in Docker
;; :host "asteroid-postgres"
:port 5432
:database "asteroid"
:username "asteroid"
:password "asteroid_db_2025"))
(setf (config :default) :main
;; (config :connections :default) :main
(config :connections :main :host) "localhost"
(config :connections :main :port) 5432
(config :connections :main :user) (or (uiop:getenv "ASTEROID_DB_USER") "asteroid")
(config :connections :main :pass) (or (uiop:getenv "ASTEROID_DB_PASSWORD") "asteroid_db_2025")
(config :connections :main :database) (or (uiop:getenv "ASTEROID_DB_NAME") "asteroid"))
;; Alternative Docker configuration (uncomment when running Asteroid in Docker)
;; (setf (config :database :connection)
@ -33,6 +32,6 @@
(setf (config :database :pool-size) 10)
(setf (config :database :pool-timeout) 30)
(format t "~%✅ Radiance configured for PostgreSQL~%")
(format t "Database: asteroid@localhost:5432~%")
(format t "Connection pooling: enabled (10 connections)~%~%")
;; (format t "~%✅ Radiance configured for PostgreSQL~%")
;; (format t "Database: asteroid@localhost:5432~%")
;; (format t "Connection pooling: enabled (10 connections)~%~%")

View File

@ -32,12 +32,12 @@ services:
- asteroid-network
postgres:
image: postgres:16-alpine
image: postgres:17-alpine
container_name: asteroid-postgres
environment:
POSTGRES_DB: asteroid
POSTGRES_USER: asteroid
POSTGRES_PASSWORD: asteroid_db_2025
- POSTGRES_DB=${ASTEROID_DB_NAME:-asteroid}
- POSTGRES_USER=${ASTEROID_DB_USER:-asteroid}
- POSTGRES_PASSWORD=${ASTEROID_DB_PASSWORD:-asteroid_db_2025}
ports:
- "5432:5432"
volumes:

10
docker/environment.sh Executable file
View File

@ -0,0 +1,10 @@
#!/usr/bin/env bash
export ASTEROID_STREAM_URL='http://ice.asteroid.radio'
# source this file prior to starting the asteroid containers. Set the
# DB name and access params here.
export ASTEROID_DB_NAME=asteroid
export ASTEROID_DB_USER=asteroid
export ASTEROID_DB_PASSWORD=asteroid_db_2025

View File

@ -5,34 +5,36 @@
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- Users table
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
CREATE TABLE IF NOT EXISTS "USERS" (
_id SERIAL PRIMARY KEY,
username VARCHAR(255) UNIQUE NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
"password-hash" TEXT NOT NULL,
role VARCHAR(50) DEFAULT 'listener',
active BOOLEAN DEFAULT true,
created_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_login TIMESTAMP,
active integer DEFAULT 1,
-- "created-date" integer DEFAULT CURRENT_TIMESTAMP,
"created-date" integer,
"last-login" integer,
CONSTRAINT valid_role CHECK (role IN ('listener', 'dj', 'admin'))
);
-- Create index on username and email for faster lookups
CREATE INDEX idx_users_username ON users(username);
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_username ON "USERS"(username);
CREATE INDEX idx_users_email ON "USERS"(email);
-- Tracks table
CREATE TABLE IF NOT EXISTS tracks (
id SERIAL PRIMARY KEY,
_id SERIAL PRIMARY KEY,
title VARCHAR(500) NOT NULL,
artist VARCHAR(500),
album VARCHAR(500),
duration INTEGER DEFAULT 0,
format VARCHAR(50),
file_path TEXT NOT NULL UNIQUE,
play_count INTEGER DEFAULT 0,
added_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_played TIMESTAMP
bitrate integer,
"file-path" TEXT NOT NULL UNIQUE,
"play-count" INTEGER DEFAULT 0,
"added-date" TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
"last-played" TIMESTAMP
);
-- Create indexes for common queries
@ -42,25 +44,25 @@ CREATE INDEX idx_tracks_title ON tracks(title);
-- Playlists table
CREATE TABLE IF NOT EXISTS playlists (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
_id SERIAL PRIMARY KEY,
"user-id" INTEGER NOT NULL REFERENCES "USERS"(_id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL,
description TEXT,
created_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
modified_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP
"created-date" TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
"modified-date" TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Create index on user_id for faster user playlist lookups
CREATE INDEX idx_playlists_user_id ON playlists(user_id);
CREATE INDEX idx_playlists_user_id ON playlists("user-id");
-- Playlist tracks junction table (many-to-many relationship)
CREATE TABLE IF NOT EXISTS playlist_tracks (
id SERIAL PRIMARY KEY,
playlist_id INTEGER NOT NULL REFERENCES playlists(id) ON DELETE CASCADE,
track_id INTEGER NOT NULL REFERENCES tracks(id) ON DELETE CASCADE,
_id SERIAL PRIMARY KEY,
playlist_id INTEGER NOT NULL REFERENCES playlists(_id) ON DELETE CASCADE,
track_id INTEGER NOT NULL REFERENCES tracks(_id) ON DELETE CASCADE,
position INTEGER NOT NULL,
added_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(playlist_id, track_id, position)
UNIQUE(playlist_id, track_id, "position")
);
-- Create indexes for playlist track queries
@ -68,28 +70,28 @@ CREATE INDEX idx_playlist_tracks_playlist_id ON playlist_tracks(playlist_id);
CREATE INDEX idx_playlist_tracks_track_id ON playlist_tracks(track_id);
-- Sessions table (for Radiance session management)
CREATE TABLE IF NOT EXISTS sessions (
id VARCHAR(255) PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
data JSONB,
created_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
expires_at TIMESTAMP NOT NULL
);
-- CREATE TABLE IF NOT EXISTS sessions (
-- _id VARCHAR(255) PRIMARY KEY,
-- "user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
-- data JSONB,
-- created_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
-- expires_at TIMESTAMP NOT NULL
-- );
-- Create index on user_id and expires_at
CREATE INDEX idx_sessions_user_id ON sessions(user_id);
CREATE INDEX idx_sessions_expires_at ON sessions(expires_at);
-- CREATE INDEX idx_sessions_user_id ON sessions(user_id);
-- CREATE INDEX idx_sessions_expires_at ON sessions(expires_at);
-- Create default admin user (password: admin - CHANGE THIS!)
-- Password hash for 'admin' using bcrypt
INSERT INTO users (username, email, password_hash, role, active)
VALUES ('admin', 'admin@asteroid.radio', '$2a$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LewY5GyYqYqYqYqYq', 'admin', true)
INSERT INTO "USERS" (username, email, "password-hash", role, active)
-- VALUES ('admin', 'admin@asteroid.radio', '$2a$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LewY5GyYqYqYqYqYq', 'admin', 1)
VALUES ('admin', 'admin@asteroid.radio','8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918', 'admin', 1)
ON CONFLICT (username) DO NOTHING;
-- Create a test listener user
INSERT INTO users (username, email, password_hash, role, active)
VALUES ('listener', 'listener@asteroid.radio', '$2a$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LewY5GyYqYqYqYqYq', 'listener', true)
ON CONFLICT (username) DO NOTHING;
INSERT INTO "USERS" (username, email, "password-hash", role, active)
VALUES ('listener', 'listener@asteroid.radio', '$2a$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LewY5GyYqYqYqYqYq', 'listener', 1);
-- Grant necessary permissions
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO asteroid;

View File

@ -1,5 +1,8 @@
#!/bin/bash
# source our environment for credentials
. environment.sh
# Simple start script for Docker directory
# Run from: /home/glenn/Projects/Code/asteroid/docker/

View File

@ -8,5 +8,14 @@ echo "🛑 Stopping Asteroid Radio Docker Services..."
# Stop services
docker compose down
# if we really need to clean everything and start fresh, run the
# following commands:
# docker compose down postgres
# docker volume rm docker_postgres-data
# TODO - apply a getopt interface to this script.
echo ""
echo "✅ Services stopped."

View File

@ -1 +0,0 @@
/home/fade/Media/Music

View File

@ -7,6 +7,12 @@
(ps:ps*
'(progn
;; Stream connection state
(defvar *stream-error-count* 0)
(defvar *last-play-attempt* 0)
(defvar *is-reconnecting* false)
(defvar *reconnect-timeout* nil)
;; Stream quality configuration
(defun get-stream-config (stream-base-url encoding)
(let ((config (ps:create
@ -137,6 +143,249 @@
(ps:chain local-storage (remove-item "useFrameset"))
(setf (ps:@ window location href) "/asteroid/"))
;; Stream status UI functions
(defun show-stream-status (message status-type)
"Show a status message to the user. status-type: 'error', 'warning', 'success', 'info'"
(let ((indicator (ps:chain document (get-element-by-id "stream-status-indicator"))))
(when indicator
(setf (ps:@ indicator inner-text) message)
(setf (ps:@ indicator style display) "block")
(setf (ps:@ indicator style background)
(cond
((= status-type "error") "#550000")
((= status-type "warning") "#554400")
((= status-type "success") "#005500")
(t "#003355")))
(setf (ps:@ indicator style border)
(cond
((= status-type "error") "1px solid #ff0000")
((= status-type "warning") "1px solid #ffaa00")
((= status-type "success") "1px solid #00ff00")
(t "1px solid #00aaff"))))))
(defun hide-stream-status ()
"Hide the status indicator"
(let ((indicator (ps:chain document (get-element-by-id "stream-status-indicator"))))
(when indicator
(setf (ps:@ indicator style display) "none"))))
(defun show-reconnect-button ()
"Show the reconnect button"
(let ((btn (ps:chain document (get-element-by-id "reconnect-btn"))))
(when btn
(setf (ps:@ btn style display) "inline-block"))))
(defun hide-reconnect-button ()
"Hide the reconnect button"
(let ((btn (ps:chain document (get-element-by-id "reconnect-btn"))))
(when btn
(setf (ps:@ btn style display) "none"))))
;; Recreate audio element to fix wedged state
(defun recreate-audio-element ()
"Recreate the audio element entirely to fix wedged MediaElementSource"
(let* ((container (ps:chain document (get-element-by-id "audio-container")))
(old-audio (ps:chain document (get-element-by-id "live-audio")))
(stream-base-url (ps:chain document (get-element-by-id "stream-base-url")))
(stream-quality (or (ps:chain local-storage (get-item "stream-quality")) "aac"))
(config (get-stream-config (ps:@ stream-base-url value) stream-quality)))
(when (and container old-audio)
;; Reset spectrum analyzer before removing audio
(when (ps:@ window |resetSpectrumAnalyzer|)
(ps:chain window (reset-spectrum-analyzer)))
;; Remove old audio element
(ps:chain old-audio (pause))
(setf (ps:@ old-audio src) "")
(ps:chain old-audio (remove))
;; Create new audio element
(let ((new-audio (ps:chain document (create-element "audio"))))
(setf (ps:@ new-audio id) "live-audio")
(setf (ps:@ new-audio controls) t)
(setf (ps:@ new-audio crossorigin) "anonymous")
(setf (ps:@ new-audio style width) "100%")
(setf (ps:@ new-audio style margin) "10px 0")
;; Create source element
(let ((source (ps:chain document (create-element "source"))))
(setf (ps:@ source id) "audio-source")
(setf (ps:@ source src) (ps:@ config url))
(setf (ps:@ source type) (ps:@ config type))
(ps:chain new-audio (append-child source)))
;; Add to container
(ps:chain container (append-child new-audio))
;; Re-attach event listeners
(attach-audio-event-listeners new-audio)
(ps:chain console (log "Audio element recreated"))
new-audio))))
;; Main reconnect function
(defun reconnect-stream ()
"Reconnect the stream - called by user or automatically"
(when *is-reconnecting*
(return))
(setf *is-reconnecting* t)
(show-stream-status "🔄 Reconnecting to stream..." "info")
(hide-reconnect-button)
;; Clear any pending reconnect timeout
(when *reconnect-timeout*
(clear-timeout *reconnect-timeout*)
(setf *reconnect-timeout* nil))
(let ((audio-element (ps:chain document (get-element-by-id "live-audio"))))
(if audio-element
;; Try simple reload first
(progn
(ps:chain audio-element (pause))
(ps:chain audio-element (load))
;; Resume AudioContext if suspended
(when (ps:@ window |resetSpectrumAnalyzer|)
(ps:chain window (reset-spectrum-analyzer)))
;; Try to play after a short delay
(set-timeout
(lambda ()
(ps:chain audio-element (play)
(then (lambda ()
(setf *stream-error-count* 0)
(setf *is-reconnecting* false)
(show-stream-status "✓ Stream reconnected!" "success")
(set-timeout hide-stream-status 3000)
;; Reinitialize spectrum analyzer
(when (ps:@ window |initSpectrumAnalyzer|)
(set-timeout
(lambda ()
(ps:chain window (init-spectrum-analyzer)))
500))))
(catch (lambda (err)
(ps:chain console (log "Simple reconnect failed, recreating audio element:" err))
;; Simple reload failed, recreate the audio element
(let ((new-audio (recreate-audio-element)))
(when new-audio
(set-timeout
(lambda ()
(ps:chain new-audio (play)
(then (lambda ()
(setf *stream-error-count* 0)
(setf *is-reconnecting* false)
(show-stream-status "✓ Stream reconnected!" "success")
(set-timeout hide-stream-status 3000)))
(catch (lambda (err2)
(setf *is-reconnecting* false)
(incf *stream-error-count*)
(show-stream-status "❌ Could not reconnect. Click play to try again." "error")
(show-reconnect-button)
(ps:chain console (log "Reconnect failed:" err2))))))
500)))))))
500))
;; No audio element found, try to recreate
(let ((new-audio (recreate-audio-element)))
(if new-audio
(set-timeout
(lambda ()
(ps:chain new-audio (play)
(then (lambda ()
(setf *is-reconnecting* false)
(show-stream-status "✓ Stream connected!" "success")
(set-timeout hide-stream-status 3000)))
(catch (lambda (err)
(setf *is-reconnecting* false)
(show-stream-status "❌ Could not connect. Click play to try again." "error")
(show-reconnect-button)))))
500)
(progn
(setf *is-reconnecting* false)
(show-stream-status "❌ Could not create audio player. Please reload the page." "error")))))))
;; Attach event listeners to audio element
(defun attach-audio-event-listeners (audio-element)
"Attach all necessary event listeners to an audio element"
;; Error handler
(ps:chain audio-element
(add-event-listener "error"
(lambda (err)
(incf *stream-error-count*)
(ps:chain console (log "Stream error:" err))
(if (< *stream-error-count* 3)
;; Auto-retry for first few errors
(progn
(show-stream-status (+ "⚠️ Stream error. Reconnecting... (attempt " *stream-error-count* ")") "warning")
(setf *reconnect-timeout*
(set-timeout reconnect-stream 3000)))
;; Too many errors, show manual reconnect
(progn
(show-stream-status "❌ Stream connection lost. Click Reconnect to try again." "error")
(show-reconnect-button))))))
;; Stalled handler
(ps:chain audio-element
(add-event-listener "stalled"
(lambda ()
(ps:chain console (log "Stream stalled"))
(show-stream-status "⚠️ Stream stalled. Attempting to recover..." "warning")
(setf *reconnect-timeout*
(set-timeout
(lambda ()
;; Only reconnect if still stalled
(when (ps:@ audio-element paused)
(reconnect-stream)))
5000)))))
;; Waiting handler (buffering)
(ps:chain audio-element
(add-event-listener "waiting"
(lambda ()
(ps:chain console (log "Stream buffering..."))
(show-stream-status "⏳ Buffering..." "info"))))
;; Playing handler - clear any error states
(ps:chain audio-element
(add-event-listener "playing"
(lambda ()
(setf *stream-error-count* 0)
(hide-stream-status)
(hide-reconnect-button)
(when *reconnect-timeout*
(clear-timeout *reconnect-timeout*)
(setf *reconnect-timeout* nil)))))
;; Pause handler - track when paused for long pause detection
(ps:chain audio-element
(add-event-listener "pause"
(lambda ()
(setf *last-play-attempt* (ps:chain |Date| (now))))))
;; Play handler - detect long pauses that need reconnection
(ps:chain audio-element
(add-event-listener "play"
(lambda ()
(let ((pause-duration (- (ps:chain |Date| (now)) *last-play-attempt*)))
;; If paused for more than 30 seconds, reconnect to get fresh stream
(when (> pause-duration 30000)
(ps:chain console (log "Long pause detected, reconnecting for fresh stream..."))
(reconnect-stream))))))
;; Spectrum analyzer hooks
(when (ps:@ window |initSpectrumAnalyzer|)
(ps:chain audio-element (add-event-listener "play"
(lambda () (ps:chain window (init-spectrum-analyzer))))))
(when (ps:@ window |stopSpectrumAnalyzer|)
(ps:chain audio-element (add-event-listener "pause"
(lambda () (ps:chain window (stop-spectrum-analyzer)))))))
(defun redirect-when-frame ()
(let* ((path (ps:@ window location pathname))
(is-frameset-page (not (= (ps:@ window parent) (ps:@ window self))))
@ -164,80 +413,10 @@
;; Update now playing
(update-now-playing)
;; Auto-reconnect on stream errors
;; Attach event listeners to audio element
(let ((audio-element (ps:chain document (get-element-by-id "live-audio"))))
(when audio-element
(ps:chain audio-element
(add-event-listener
"error"
(lambda (err)
(ps:chain console (log "Stream error, attempting reconnect in 3 seconds..." err))
(set-timeout
(lambda ()
(ps:chain audio-element (load))
(ps:chain (ps:chain audio-element (play))
(catch (lambda (err)
(ps:chain console (log "Reconnect failed:" err))))))
3000))))
(ps:chain audio-element
(add-event-listener
"stalled"
(lambda ()
(ps:chain console (log "Stream stalled, reloading..."))
(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))))))))
(attach-audio-event-listeners audio-element)))
;; Check frameset preference
(let ((path (ps:@ window location pathname))
@ -249,8 +428,8 @@
(redirect-when-frame)))))
;; Update now playing every 10 seconds
(set-interval update-now-playing 10000)
;; Update now playing every 5 seconds
(set-interval update-now-playing 5000)
;; Listen for messages from popout window
(ps:chain window

View File

@ -33,9 +33,16 @@
(when *animation-id*
(cancel-animation-frame *animation-id*)
(setf *animation-id* nil))
;; Close the old AudioContext if it exists
(when *audio-context*
(ps:try
(ps:chain *audio-context* (close))
(:catch (e)
(ps:chain console (log "Error closing AudioContext:" e)))))
(setf *audio-context* nil)
(setf *analyser* nil)
(setf *media-source* nil)
(setf *current-audio-element* nil)
(ps:chain console (log "Spectrum analyzer reset for reconnection")))
(defun init-spectrum-analyzer ()

View File

@ -0,0 +1,163 @@
#EXTM3U
#EXTINF:370,Vector Lovers - City Lights From a Train
/app/music/Vector Lovers/City Lights From a Train.flac
#EXTINF:400,The Black Dog - Psil-Cosyin
/app/music/The Black Dog/Psil-Cosyin.flac
#EXTINF:320,Plaid - Eyen
/app/music/Plaid/Eyen.flac
#EXTINF:330,ISAN - Birds Over Barges
/app/music/ISAN/Birds Over Barges.flac
#EXTINF:360,Ochre - Bluebottle Farm
/app/music/Ochre/Bluebottle Farm.flac
#EXTINF:390,Arovane - Theme
/app/music/Arovane/Theme.flac
#EXTINF:380,Proem - Deep Like Airline Failure
/app/music/Proem/Deep Like Airline Failure.flac
#EXTINF:310,Solvent - My Radio (Remix)
/app/music/Solvent/My Radio (Remix).flac
#EXTINF:350,Bochum Welt - Marylebone (7th)
/app/music/Bochum Welt/Marylebone (7th).flac
#EXTINF:290,Mrs Jynx - Shibuya Lullaby
/app/music/Mrs Jynx/Shibuya Lullaby.flac
#EXTINF:340,Kettel - Whisper Me Wishes
/app/music/Kettel/Whisper Me Wishes.flac
#EXTINF:360,Christ. - Perlandine Friday
/app/music/Christ./Perlandine Friday.flac
#EXTINF:330,Cepia - Ithaca
/app/music/Cepia/Ithaca.flac
#EXTINF:340,Datassette - Vacuform
/app/music/Datassette/Vacuform.flac
#EXTINF:390,Plant43 - Dreams of the Sentient City
/app/music/Plant43/Dreams of the Sentient City.flac
#EXTINF:410,Claro Intelecto - Peace of Mind (Electrosoul)
/app/music/Claro Intelecto/Peace of Mind (Electrosoul).flac
#EXTINF:430,E.R.P. - Evoked
/app/music/E.R.P./Evoked.flac
#EXTINF:310,Der Zyklus - Formenverwandler
/app/music/Der Zyklus/Formenverwandler.flac
#EXTINF:330,Dopplereffekt - Infophysix
/app/music/Dopplereffekt/Infophysix.flac
#EXTINF:350,Drexciya - Wavejumper
/app/music/Drexciya/Wavejumper.flac
#EXTINF:375,The Other People Place - Sorrow & A Cup of Joe
/app/music/The Other People Place/Sorrow & A Cup of Joe.flac
#EXTINF:340,Arpanet - Wireless Internet
/app/music/Arpanet/Wireless Internet.flac
#EXTINF:380,Legowelt - Sturmvogel
/app/music/Legowelt/Sturmvogel.flac
#EXTINF:310,DMX Krew - Space Paranoia
/app/music/DMX Krew/Space Paranoia.flac
#EXTINF:360,Skywave Theory - Nova Drift
/app/music/Skywave Theory/Nova Drift.flac
#EXTINF:460,Pye Corner Audio - Transmission Four
/app/music/Pye Corner Audio/Transmission Four.flac
#EXTINF:390,B12 - Heaven Sent
/app/music/B12/Heaven Sent.flac
#EXTINF:450,Higher Intelligence Agency - Tortoise
/app/music/Higher Intelligence Agency/Tortoise.flac
#EXTINF:420,Biosphere - Kobresia
/app/music/Biosphere/Kobresia.flac
#EXTINF:870,Global Communication - 14:31
/app/music/Global Communication/14:31.flac
#EXTINF:500,Monolake - Cyan
/app/music/Monolake/Cyan.flac
#EXTINF:660,Deepchord - Electromagnetic
/app/music/Deepchord/Electromagnetic.flac
#EXTINF:1020,GAS - Pop 4
/app/music/GAS/Pop 4.flac
#EXTINF:600,Yagya - Rigning Nýju
/app/music/Yagya/Rigning Nýju.flac
#EXTINF:990,Voices From The Lake - Velo di Maya
/app/music/Voices From The Lake/Velo di Maya.flac
#EXTINF:3720,ASC - Time Heals All
/app/music/ASC/Time Heals All.flac
#EXTINF:540,36 - Room 237
/app/music/36/Room 237.flac
#EXTINF:900,Loscil - Endless Falls
/app/music/Loscil/Endless Falls.flac
#EXTINF:450,Kiasmos - Looped
/app/music/Kiasmos/Looped.flac
#EXTINF:590,Underworld - Rez
/app/music/Underworld/Rez.flac
#EXTINF:570,Orbital - Halcyon + On + On
/app/music/Orbital/Halcyon + On + On.flac
#EXTINF:1080,The Orb - A Huge Ever Growing Pulsating Brain
/app/music/The Orb/A Huge Ever Growing Pulsating Brain.flac
#EXTINF:360,Autechre - Slip
/app/music/Autechre/Slip.flac
#EXTINF:400,Labradford - S (Mi Media Naranja)
/app/music/Labradford/S (Mi Media Naranja).flac
#EXTINF:350,Vector Lovers - Rusting Cars and Wildflowers
/app/music/Vector Lovers/Rusting Cars and Wildflowers.flac
#EXTINF:390,The Black Dog - Raxmus
/app/music/The Black Dog/Raxmus.flac
#EXTINF:315,Plaid - Hawkmoth
/app/music/Plaid/Hawkmoth.flac
#EXTINF:320,ISAN - What This Button Did
/app/music/ISAN/What This Button Did.flac
#EXTINF:370,Ochre - Circadies
/app/music/Ochre/Circadies.flac
#EXTINF:420,Arovane - Tides
/app/music/Arovane/Tides.flac
#EXTINF:370,Proem - Nothing is as It Seems
/app/music/Proem/Nothing is as It Seems.flac
#EXTINF:300,Solvent - Loss For Words
/app/music/Solvent/Loss For Words.flac
#EXTINF:340,Bochum Welt - Saint (77sunset)
/app/music/Bochum Welt/Saint (77sunset).flac
#EXTINF:280,Mrs Jynx - Stay Home
/app/music/Mrs Jynx/Stay Home.flac
#EXTINF:330,Kettel - Church
/app/music/Kettel/Church.flac
#EXTINF:370,Christ. - Cordate
/app/music/Christ./Cordate.flac
#EXTINF:350,Datassette - Computers Elevate
/app/music/Datassette/Computers Elevate.flac
#EXTINF:420,Plant43 - The Cold Surveyor
/app/music/Plant43/The Cold Surveyor.flac
#EXTINF:380,Claro Intelecto - Section
/app/music/Claro Intelecto/Section.flac
#EXTINF:440,E.R.P. - Vox Automaton
/app/music/E.R.P./Vox Automaton.flac
#EXTINF:300,Dopplereffekt - Z-Boson
/app/music/Dopplereffekt/Z-Boson.flac
#EXTINF:380,Drexciya - Digital Tsunami
/app/music/Drexciya/Digital Tsunami.flac
#EXTINF:350,The Other People Place - You Said You Want Me
/app/music/The Other People Place/You Said You Want Me.flac
#EXTINF:370,Legowelt - Star Gazing
/app/music/Legowelt/Star Gazing.flac
#EXTINF:440,Pye Corner Audio - Electronic Rhythm Number 3
/app/music/Pye Corner Audio/Electronic Rhythm Number 3.flac
#EXTINF:460,B12 - Infinite Lites (Classic Mix)
/app/music/B12/Infinite Lites (Classic Mix).flac
#EXTINF:390,Biosphere - The Things I Tell You
/app/music/Biosphere/The Things I Tell You.flac
#EXTINF:580,Global Communication - 9:39
/app/music/Global Communication/9:39.flac
#EXTINF:460,Monolake - T-Channel
/app/music/Monolake/T-Channel.flac
#EXTINF:690,Deepchord - Vantage Isle (Variant)
/app/music/Deepchord/Vantage Isle (Variant).flac
#EXTINF:840,GAS - Königsforst 5
/app/music/GAS/Königsforst 5.flac
#EXTINF:520,Yagya - The Salt on Her Cheeks
/app/music/Yagya/The Salt on Her Cheeks.flac
#EXTINF:720,Voices From The Lake - Dream State
/app/music/Voices From The Lake/Dream State.flac
#EXTINF:510,36 - Night Rain
/app/music/36/Night Rain.flac
#EXTINF:470,Loscil - First Narrows
/app/music/Loscil/First Narrows.flac
#EXTINF:400,Kiasmos - Burnt
/app/music/Kiasmos/Burnt.flac
#EXTINF:570,Underworld - Jumbo (Extended)
/app/music/Underworld/Jumbo (Extended).flac
#EXTINF:480,Orbital - Belfast
/app/music/Orbital/Belfast.flac
#EXTINF:540,The Orb - Little Fluffy Clouds (Ambient Mix)
/app/music/The Orb/Little Fluffy Clouds (Ambient Mix).flac
#EXTINF:390,Autechre - Nine
/app/music/Autechre/Nine.flac
#EXTINF:380,Labradford - G (Mi Media Naranja)
/app/music/Labradford/G (Mi Media Naranja).flac

View File

@ -0,0 +1,163 @@
#EXTM3U
#EXTINF:370,Vector Lovers - City Lights From a Train
Vector Lovers/City Lights From a Train.flac
#EXTINF:400,The Black Dog - Psil-Cosyin
The Black Dog/Psil-Cosyin.flac
#EXTINF:320,Plaid - Eyen
Plaid/Eyen.flac
#EXTINF:330,ISAN - Birds Over Barges
ISAN/Birds Over Barges.flac
#EXTINF:360,Ochre - Bluebottle Farm
Ochre/Bluebottle Farm.flac
#EXTINF:390,Arovane - Theme
Arovane/Theme.flac
#EXTINF:380,Proem - Deep Like Airline Failure
Proem/Deep Like Airline Failure.flac
#EXTINF:310,Solvent - My Radio (Remix)
Solvent/My Radio (Remix).flac
#EXTINF:350,Bochum Welt - Marylebone (7th)
Bochum Welt/Marylebone (7th).flac
#EXTINF:290,Mrs Jynx - Shibuya Lullaby
Mrs Jynx/Shibuya Lullaby.flac
#EXTINF:340,Kettel - Whisper Me Wishes
Kettel/Whisper Me Wishes.flac
#EXTINF:360,Christ. - Perlandine Friday
Christ./Perlandine Friday.flac
#EXTINF:330,Cepia - Ithaca
Cepia/Ithaca.flac
#EXTINF:340,Datassette - Vacuform
Datassette/Vacuform.flac
#EXTINF:390,Plant43 - Dreams of the Sentient City
Plant43/Dreams of the Sentient City.flac
#EXTINF:410,Claro Intelecto - Peace of Mind (Electrosoul)
Claro Intelecto/Peace of Mind (Electrosoul).flac
#EXTINF:430,E.R.P. - Evoked
E.R.P./Evoked.flac
#EXTINF:310,Der Zyklus - Formenverwandler
Der Zyklus/Formenverwandler.flac
#EXTINF:330,Dopplereffekt - Infophysix
Dopplereffekt/Infophysix.flac
#EXTINF:350,Drexciya - Wavejumper
Drexciya/Wavejumper.flac
#EXTINF:375,The Other People Place - Sorrow & A Cup of Joe
The Other People Place/Sorrow & A Cup of Joe.flac
#EXTINF:340,Arpanet - Wireless Internet
Arpanet/Wireless Internet.flac
#EXTINF:380,Legowelt - Sturmvogel
Legowelt/Sturmvogel.flac
#EXTINF:310,DMX Krew - Space Paranoia
DMX Krew/Space Paranoia.flac
#EXTINF:360,Skywave Theory - Nova Drift
Skywave Theory/Nova Drift.flac
#EXTINF:460,Pye Corner Audio - Transmission Four
Pye Corner Audio/Transmission Four.flac
#EXTINF:390,B12 - Heaven Sent
B12/Heaven Sent.flac
#EXTINF:450,Higher Intelligence Agency - Tortoise
Higher Intelligence Agency/Tortoise.flac
#EXTINF:420,Biosphere - Kobresia
Biosphere/Kobresia.flac
#EXTINF:870,Global Communication - 14:31
Global Communication/14:31.flac
#EXTINF:500,Monolake - Cyan
Monolake/Cyan.flac
#EXTINF:660,Deepchord - Electromagnetic
Deepchord/Electromagnetic.flac
#EXTINF:1020,GAS - Pop 4
GAS/Pop 4.flac
#EXTINF:600,Yagya - Rigning Nýju
Yagya/Rigning Nýju.flac
#EXTINF:990,Voices From The Lake - Velo di Maya
Voices From The Lake/Velo di Maya.flac
#EXTINF:3720,ASC - Time Heals All
ASC/Time Heals All.flac
#EXTINF:540,36 - Room 237
36/Room 237.flac
#EXTINF:900,Loscil - Endless Falls
Loscil/Endless Falls.flac
#EXTINF:450,Kiasmos - Looped
Kiasmos/Looped.flac
#EXTINF:590,Underworld - Rez
Underworld/Rez.flac
#EXTINF:570,Orbital - Halcyon + On + On
Orbital/Halcyon + On + On.flac
#EXTINF:1080,The Orb - A Huge Ever Growing Pulsating Brain
The Orb/A Huge Ever Growing Pulsating Brain.flac
#EXTINF:360,Autechre - Slip
Autechre/Slip.flac
#EXTINF:400,Labradford - S (Mi Media Naranja)
Labradford/S (Mi Media Naranja).flac
#EXTINF:350,Vector Lovers - Rusting Cars and Wildflowers
Vector Lovers/Rusting Cars and Wildflowers.flac
#EXTINF:390,The Black Dog - Raxmus
The Black Dog/Raxmus.flac
#EXTINF:315,Plaid - Hawkmoth
Plaid/Hawkmoth.flac
#EXTINF:320,ISAN - What This Button Did
ISAN/What This Button Did.flac
#EXTINF:370,Ochre - Circadies
Ochre/Circadies.flac
#EXTINF:420,Arovane - Tides
Arovane/Tides.flac
#EXTINF:370,Proem - Nothing is as It Seems
Proem/Nothing is as It Seems.flac
#EXTINF:300,Solvent - Loss For Words
Solvent/Loss For Words.flac
#EXTINF:340,Bochum Welt - Saint (77sunset)
Bochum Welt/Saint (77sunset).flac
#EXTINF:280,Mrs Jynx - Stay Home
Mrs Jynx/Stay Home.flac
#EXTINF:330,Kettel - Church
Kettel/Church.flac
#EXTINF:370,Christ. - Cordate
Christ./Cordate.flac
#EXTINF:350,Datassette - Computers Elevate
Datassette/Computers Elevate.flac
#EXTINF:420,Plant43 - The Cold Surveyor
Plant43/The Cold Surveyor.flac
#EXTINF:380,Claro Intelecto - Section
Claro Intelecto/Section.flac
#EXTINF:440,E.R.P. - Vox Automaton
E.R.P./Vox Automaton.flac
#EXTINF:300,Dopplereffekt - Z-Boson
Dopplereffekt/Z-Boson.flac
#EXTINF:380,Drexciya - Digital Tsunami
Drexciya/Digital Tsunami.flac
#EXTINF:350,The Other People Place - You Said You Want Me
The Other People Place/You Said You Want Me.flac
#EXTINF:370,Legowelt - Star Gazing
Legowelt/Star Gazing.flac
#EXTINF:440,Pye Corner Audio - Electronic Rhythm Number 3
Pye Corner Audio/Electronic Rhythm Number 3.flac
#EXTINF:460,B12 - Infinite Lites (Classic Mix)
B12/Infinite Lites (Classic Mix).flac
#EXTINF:390,Biosphere - The Things I Tell You
Biosphere/The Things I Tell You.flac
#EXTINF:580,Global Communication - 9:39
Global Communication/9:39.flac
#EXTINF:460,Monolake - T-Channel
Monolake/T-Channel.flac
#EXTINF:690,Deepchord - Vantage Isle (Variant)
Deepchord/Vantage Isle (Variant).flac
#EXTINF:840,GAS - Königsforst 5
GAS/Königsforst 5.flac
#EXTINF:520,Yagya - The Salt on Her Cheeks
Yagya/The Salt on Her Cheeks.flac
#EXTINF:720,Voices From The Lake - Dream State
Voices From The Lake/Dream State.flac
#EXTINF:510,36 - Night Rain
36/Night Rain.flac
#EXTINF:470,Loscil - First Narrows
Loscil/First Narrows.flac
#EXTINF:400,Kiasmos - Burnt
Kiasmos/Burnt.flac
#EXTINF:570,Underworld - Jumbo (Extended)
Underworld/Jumbo (Extended).flac
#EXTINF:480,Orbital - Belfast
Orbital/Belfast.flac
#EXTINF:540,The Orb - Little Fluffy Clouds (Ambient Mix)
The Orb/Little Fluffy Clouds (Ambient Mix).flac
#EXTINF:390,Autechre - Nine
Autechre/Nine.flac
#EXTINF:380,Labradford - G (Mi Media Naranja)
Labradford/G (Mi Media Naranja).flac

View File

@ -0,0 +1,144 @@
#EXTM3U
#PLAYLIST:Escape Velocity - A Christmas Journey Through Space
#PHASE:Escape Velocity
#DURATION:12 hours (approx)
#CURATOR:Asteroid Radio
#DESCRIPTION:A festive 12-hour voyage blending Christmas classics with ambient, IDM, and space music for the holiday season
# === PHASE 1: WINTER AWAKENING (Ambient Beginnings) ===
#EXTINF:-1,Brian Eno - Snow
/app/music/Brian Eno/2020 - Roger Eno and Brian Eno - Mixing Colours/09 Snow.flac
#EXTINF:-1,Brian Eno - Wintergreen
/app/music/Brian Eno/2020 - Roger Eno and Brian Eno - Mixing Colours/04 Wintergreen.flac
#EXTINF:-1,Proem - Winter Wolves
/app/music/Proem - 2018 Modern Rope (WEB)/01. Winter Wolves.flac
#EXTINF:-1,Tim Hecker - Winter's Coming
/app/music/Tim Hecker - The North Water Original Score (2021 - WEB - FLAC)/Tim Hecker - The North Water (Original Score) - 10 Winter's Coming.flac
#EXTINF:-1,Biosphere - Drifter
/app/music/Biosphere - The Petrified Forest (2017) - CD FLAC/01. Biosphere - Drifter.flac
#EXTINF:-1,Dead Voices On Air - On Winters Gibbet
/app/music/Dead Voices On Air - Ghohst Stories (FLAC)/02 - On Winters Gibbet.flac
#EXTINF:-1,Color Therapy - Wintering
/app/music/Color Therapy - Mr. Wolf Is Dead (2015) WEB FLAC/12 - Wintering.flac
# === PHASE 2: CHRISTMAS ARRIVAL (TSO Introduction) ===
#EXTINF:-1,Trans-Siberian Orchestra - The Ghosts Of Christmas Eve
/app/music/Trans-Siberian Orchestra - The Christmas Trilogy (2004) [FLAC]/The Christmas Attic/01 The Ghosts Of Christmas Eve.flac
#EXTINF:-1,Trans-Siberian Orchestra - Christmas In The Air
/app/music/Trans-Siberian Orchestra - The Christmas Trilogy (2004) [FLAC]/The Christmas Attic/14 Christmas In The Air.flac
#EXTINF:-1,Trans-Siberian Orchestra - Christmas Canon
/app/music/Trans-Siberian Orchestra - The Christmas Trilogy (2004) [FLAC]/The Christmas Attic/08 Christmas Canon.flac
#EXTINF:-1,Trans-Siberian Orchestra - Appalachian Snowfall
/app/music/Trans-Siberian Orchestra - The Christmas Trilogy (2004) [FLAC]/The Christmas Attic/11 Appalachian Snowfall.flac
#EXTINF:-1,Trans-Siberian Orchestra - The Snow Came Down
/app/music/Trans-Siberian Orchestra - The Christmas Trilogy (2004) [FLAC]/The Christmas Attic/13 The Snow Came Down.flac
# === PHASE 3: AMBIENT INTERLUDE (Space & Atmosphere) ===
#EXTINF:-1,Biosphere - 10 Snurp 1937
/app/music/Biosphere - Sound Installations -2000-2009 [FLAC]/Biosphere - Sound Installations -2000-2009- - 10 Snurp 1937.flac
#EXTINF:-1,Biosphere - 05 Fluvialmorphologie
/app/music/Biosphere - Sound Installations -2000-2009 [FLAC]/Biosphere - Sound Installations -2000-2009- - 05 Fluvialmorphologie.flac
#EXTINF:-1,God is an Astronaut - Winter Dusk-Awakening
/app/music/God is an Astronaut - Epitaph (2018) WEB FLAC/03. Winter Dusk-Awakening.flac
#EXTINF:-1,Proem - Snow Drifts
/app/music/Proem - Twelve Tails-(2021) @FLAC [16-48]/11 - Snow Drifts.flac
#EXTINF:-1,Proem - Stick to Music Snowflake
/app/music/Proem - Until Here for Years (n5md, 2019) flac/04 - Stick to Music Snowflake.flac
#EXTINF:-1,Four Tet - 04 Tremper
/app/music/Four Tet - New Energy {CD} [FLAC] (2017)/04 Tremper.flac
# === PHASE 4: CHRISTMAS EVE STORIES ===
#EXTINF:-1,Trans-Siberian Orchestra - First Snow (Instrumental)
/app/music/Trans-Siberian Orchestra - The Christmas Trilogy (2004) [FLAC]/Christmas Eve and Other Stories/04 First Snow (Instrumental).flac
#EXTINF:-1,Trans-Siberian Orchestra - The Silent Nutcracker (Instrumental)
/app/music/Trans-Siberian Orchestra - The Christmas Trilogy (2004) [FLAC]/Christmas Eve and Other Stories/05 The Silent Nutcracker (Instrumental).flac
#EXTINF:-1,Trans-Siberian Orchestra - A Mad Russian's Christmas (Instrumental)
/app/music/Trans-Siberian Orchestra - The Christmas Trilogy (2004) [FLAC]/Christmas Eve and Other Stories/06 A Mad Russian's Christmas (Instrumental).flac
#EXTINF:-1,Trans-Siberian Orchestra - Christmas Eve,Sarajevo 12,24 (Instrumental)
/app/music/Trans-Siberian Orchestra - The Christmas Trilogy (2004) [FLAC]/Christmas Eve and Other Stories/08 Christmas Eve,Sarajevo 12,24 (Instrumental).flac
#EXTINF:-1,Trans-Siberian Orchestra - This Christmas Day
/app/music/Trans-Siberian Orchestra - The Christmas Trilogy (2004) [FLAC]/Christmas Eve and Other Stories/14 This Christmas Day.flac
# === PHASE 5: ELECTRONIC DREAMS (IDM & Ambient) ===
#EXTINF:-1,Autechre - NTS Session 1-005-Autechre-carefree counter dronal
/app/music/Autechre - 2018 - NTS Session 1/NTS Session 1-005-Autechre-carefree counter dronal.flac
#EXTINF:-1,Clark - Living Fantasy
/app/music/Clark - Death Peak (2017) [FLAC]/08 - Living Fantasy.flac
#EXTINF:-1,Clark - My Machines (Clark Remix)
/app/music/Clark - Feast Beast (2013) [24 Bit WEB FLAC] [16-44]/1.17. Battles - My Machines (Clark Remix).flac
#EXTINF:-1,Plaid - Dancers
/app/music/Plaid - Polymer (2019) [WEB FLAC]/07 - Dancers.flac
#EXTINF:-1,Faux Tales - Avalon
/app/music/Faux Tales - 2015 - Kairos [FLAC] {Kensai Records KNS006 WEB}/3 - Avalon.flac
#EXTINF:-1,Color Therapy - Expect Delays (feat. Ulrich Schnauss)
/app/music/Color Therapy - Mr. Wolf Is Dead (2015) WEB FLAC/11 - Expect Delays (feat. Ulrich Schnauss).flac
# === PHASE 6: THE LOST CHRISTMAS EVE ===
#EXTINF:-1,Trans-Siberian Orchestra - The Lost Christmas Eve
/app/music/Trans-Siberian Orchestra - The Christmas Trilogy (2004) [FLAC]/The Lost Christmas Eve/02 The Lost Christmas Eve.flac
#EXTINF:-1,Trans-Siberian Orchestra - Christmas Dreams
/app/music/Trans-Siberian Orchestra - The Christmas Trilogy (2004) [FLAC]/The Lost Christmas Eve/03 Christmas Dreams.flac
#EXTINF:-1,Trans-Siberian Orchestra - Wizards in Winter
/app/music/Trans-Siberian Orchestra - The Christmas Trilogy (2004) [FLAC]/The Lost Christmas Eve/04 Wizards in Winter.flac
#EXTINF:-1,Trans-Siberian Orchestra - Christmas Concerto
/app/music/Trans-Siberian Orchestra - The Christmas Trilogy (2004) [FLAC]/The Lost Christmas Eve/07 Christmas Concerto.flac
#EXTINF:-1,Trans-Siberian Orchestra - Queen Of The Winter Night
/app/music/Trans-Siberian Orchestra - The Christmas Trilogy (2004) [FLAC]/The Lost Christmas Eve/08 Queen Of The Winter Night.flac
#EXTINF:-1,Trans-Siberian Orchestra - Christmas Nights In Blue
/app/music/Trans-Siberian Orchestra - The Christmas Trilogy (2004) [FLAC]/The Lost Christmas Eve/09 Christmas Nights In Blue.flac
#EXTINF:-1,Trans-Siberian Orchestra - Christmas Jazz
/app/music/Trans-Siberian Orchestra - The Christmas Trilogy (2004) [FLAC]/The Lost Christmas Eve/10 Christmas Jazz.flac
#EXTINF:-1,Trans-Siberian Orchestra - Christmas Jam
/app/music/Trans-Siberian Orchestra - The Christmas Trilogy (2004) [FLAC]/The Lost Christmas Eve/11 Christmas Jam.flac
# === PHASE 7: CLASSICAL WINTER (Nutcracker & More) ===
#EXTINF:-1,Various Artists - Dance of the Sugar-Plum Fairy
/app/music/Various Artists - The 50 Darkest Pieces of Classical Music (2011) - FLAC/CD 1/02 - Tchaikovsky - The Nutcracker - Dance of the Sugar-Plum Fairy.flac
#EXTINF:-1,Quaeschning and Ulrich Schnauss - Thirst
/app/music/Quaeschning and Ulrich Schnauss - Synthwaves (2017) {vista003, GER, CD} [FLAC]/06 - Thirst.flac
#EXTINF:-1,Proem - 04. Drawing Room Anguish
/app/music/Proem - 2018 Modern Rope (WEB)/04. Drawing Room Anguish.flac
#EXTINF:-1,Dead Voices On Air - 07. Dogger Doorlopende Split
/app/music/Dead Voices On Air - Frankie Pett En De Onderzeer Boten (2017) web/07. Dogger Doorlopende Split.flac
# === PHASE 8: WISDOM & REFLECTION ===
#EXTINF:-1,Trans-Siberian Orchestra - What Is Christmas
/app/music/Trans-Siberian Orchestra - The Christmas Trilogy (2004) [FLAC]/The Lost Christmas Eve/13 What Is Christmas.flac
#EXTINF:-1,Trans-Siberian Orchestra - The Wisdom Of Snow
/app/music/Trans-Siberian Orchestra - The Christmas Trilogy (2004) [FLAC]/The Lost Christmas Eve/15 The Wisdom Of Snow.flac
#EXTINF:-1,Trans-Siberian Orchestra - Christmas Bells, Carousels & Time
/app/music/Trans-Siberian Orchestra - The Christmas Trilogy (2004) [FLAC]/The Lost Christmas Eve/18 Christmas Bells, Carousels & Time.flac
#EXTINF:-1,Trans-Siberian Orchestra - Christmas Canon Rock
/app/music/Trans-Siberian Orchestra - The Christmas Trilogy (2004) [FLAC]/The Lost Christmas Eve/21 Christmas Canon Rock.flac
#EXTINF:-1,Trans-Siberian Orchestra - Midnight Christmas Eve
/app/music/Trans-Siberian Orchestra - The Christmas Trilogy (2004) [FLAC]/The Christmas Attic/05 Midnight Christmas Eve.flac
#EXTINF:-1,Trans-Siberian Orchestra - Dream Child (A Christmas Dream)
/app/music/Trans-Siberian Orchestra - The Christmas Trilogy (2004) [FLAC]/The Christmas Attic/15 Dream Child (A Christmas Dream).flac
# === PHASE 9: DEEP SPACE JOURNEY (Extended Ambient) ===
#EXTINF:-1,Dead Voices On Air - Red Howls
/app/music/Dead Voices On Air - Ghohst Stories (FLAC)/01 - Red Howls.flac
#EXTINF:-1,Cut Copy - Airborne
/app/music/Cut Copy - Haiku From Zero (2017) [FLAC] {2557864014}/05 - Airborne.flac
#EXTINF:-1,Owl City - 01 Hot Air Balloon
/app/music/Owl City - Ocean Eyes (Deluxe Edition) [Flac,Cue,Logs]/Disc 2/01 Hot Air Balloon.flac
#EXTINF:-1,VA - What Is Loneliness (feat. Danny Claire) [Skylex Radio Edit]
/app/music/VA - Melodic Vocal Trance 2017/24. Airborn, Bogdan Vix & KeyPlayer - What Is Loneliness (feat. Danny Claire) [Skylex Radio Edit].flac
#EXTINF:-1,VA - Winter Took Over (Radio Edit)
/app/music/VA - Melodic Vocal Trance 2017/22. Bluskay, KeyPlayer & Esmee Bor Stotijn - Winter Took Over (Radio Edit).flac
#EXTINF:-1,Alison Krauss and Union Station - My Opening Farewell
/app/music/Alison Krauss and Union Station - Paper Airplane (flac)/11 - Alison Krauss & Union Station - My Opening Farewell.flac
#EXTINF:-1,Bedouin Soundclash - Money Worries (E-Clair Refix)
/app/music/Bedouin Soundclash - Sounding a Mosaic (2004) [FLAC] {SD1267}/14 - Money Worries (E-Clair Refix).flac
# === PHASE 10: RETURN TO WINTER (Closing Circle) ===
#EXTINF:-1,Brian Eno - Snow
/app/music/Brian Eno/2020 - Roger Eno and Brian Eno - Mixing Colours/09 Snow.flac
#EXTINF:-1,Proem - Winter Wolves
/app/music/Proem - 2018 Modern Rope (WEB)/01. Winter Wolves.flac
#EXTINF:-1,God is an Astronaut - Winter Dusk-Awakening
/app/music/God is an Astronaut - Epitaph (2018) WEB FLAC/03. Winter Dusk-Awakening.flac
#EXTINF:-1,Trans-Siberian Orchestra - The Snow Came Down
/app/music/Trans-Siberian Orchestra - The Christmas Trilogy (2004) [FLAC]/The Christmas Attic/13 The Snow Came Down.flac
#EXTINF:-1,Trans-Siberian Orchestra - Christmas In The Air
/app/music/Trans-Siberian Orchestra - The Christmas Trilogy (2004) [FLAC]/The Christmas Attic/14 Christmas In The Air.flac

371
playlists/stream-queue.m3u Normal file
View File

@ -0,0 +1,371 @@
#EXTM3U
#EXTINF:-1,Underworld - Underworld - Confusion The Waitress
/app/music/Underworld/1996 - Second Toughest In The Infants/03. Underworld - Confusion The Waitress.flac
#EXTINF:-1,The Orb - Towers Of Dub
/app/music/The Orb/1992 - UFOrb/04-Towers Of Dub.mp3
#EXTINF:-1,Drexciya - Drexciya - Intensified Magnetron
/app/music/Drexciya/2013 - Journey Of The Deep Sea Dweller III/04. Drexciya - Intensified Magnetron.mp3
#EXTINF:-1,Labradford - Balanced on It's Own Flame
/app/music/Labradford/1995 - A Stable Reference/6 Balanced on It's Own Flame.flac
#EXTINF:-1,Vector Lovers - City Lights From A Train
/app/music/Vector Lovers/2005 - Capsule For One/01 - City Lights From A Train.mp3
#EXTINF:-1,Labradford - Leta O'Steen. Design assistance by John Piper
/app/music/Labradford/1999 - E luxo so/6. Leta O'Steen. Design assistance by John Piper.flac
#EXTINF:-1,Tape Loop Orchestra - Tape Loop Orchestra - Chapter 1   Reel One
/app/music/Tape Loop Orchestra/2009 - 1953 Culture Festival/01 Tape Loop Orchestra - Chapter 1   Reel One.mp3
#EXTINF:-1,Orbital - Time Becomes
/app/music/Orbital/1993 - Orbital - Orbital 2 (Brown Album - TRUCD2, 828 386.2)/00. Time Becomes.mp3
#EXTINF:-1,Proem - Proem - You Shall Have Ever Been - 05 No You Are $
/app/music/Proem/2006 - You Shall Have Ever Been/Proem - You Shall Have Ever Been - 05 No You Are $.flac
#EXTINF:-1,Pye Corner Audio - Pye Corner Audio - The Simplest Equation
/app/music/Pye Corner Audio/EP's & Singles/2016 - Pye Corner Audio With Dalhous - Run For The Shadows EP (WEB, #LPS13)/02 - Pye Corner Audio - The Simplest Equation.mp3
#EXTINF:-1,Brian Eno - Emerald and Lime
/app/music/Brian Eno/2024 - Eno (Original Motion Picture Soundtrack)/12. Emerald and Lime.flac
#EXTINF:-1,Bark Psychosis - (07) [Bark Psychosis] A Street Scene
/app/music/Bark Psychosis/1994 - Game Over/(07) [Bark Psychosis] A Street Scene.flac
#EXTINF:-1,Model 500 - model_500-digital_solutions
/app/music/Model 500/2015 - Digital Solutions/08-model_500-digital_solutions.flac
#EXTINF:-1,Labradford - Banco
/app/music/Labradford/1995 - A Stable Reference/4 Banco.flac
#EXTINF:-1,Labradford - Skyward With Motion
/app/music/Labradford/1993 - Prazision LP/11 Skyward With Motion.flac
#EXTINF:-1,Pye Corner Audio - The Mirror Ball Cracked
/app/music/Pye Corner Audio/2012 - Sleep Games (WEB, #GBX017)/08 - The Mirror Ball Cracked.mp3
#EXTINF:-1,Brian Eno - Foreign Affairs
/app/music/Brian Eno/1978 - After The Heat/01 - Foreign Affairs.flac
#EXTINF:-1,The Other People Place - B1 - Moonlight Rendezvous
/app/music/The Other People Place/2017 - Lifestyles Of The Laptop Café/B1 - Moonlight Rendezvous.flac
#EXTINF:-1,Drexciya - Unknown Journey IX
/app/music/Drexciya/2013 - Journey of the Deep Sea Dweller IV/10. Unknown Journey IX.mp3
#EXTINF:-1,Orbital - Crash And Carry
/app/music/Orbital/1994 - Orbital - Snivilisation (TRUCD5, 828 536.2)/04. Crash And Carry.mp3
#EXTINF:-1,Proem - Proem - Before it finds you - 09 We can watch it burn to the ground
/app/music/Proem/2013 - Before it finds you/Proem - Before it finds you - 09 We can watch it burn to the ground.flac
#EXTINF:-1,Proem - Proem - Before it finds you - 01 Stone into gravel
/app/music/Proem/2013 - Before it finds you/Proem - Before it finds you - 01 Stone into gravel.flac
#EXTINF:-1,Drexciya - Intro (The Unknown Aquazone)
/app/music/Drexciya/2013 - Journey of the Deep Sea Dweller IV/01. Intro (The Unknown Aquazone).mp3
#EXTINF:-1,Teeth Of The Sea - Get With the Program
/app/music/Teeth Of The Sea/2023 - Hive/02 Get With the Program.flac
#EXTINF:-1,Proem - Proem - Vault ep.4-4 - 02 Little girls
/app/music/Proem/2015 - Vault ep.4-4/Proem - Vault ep.4-4 - 02 Little girls.flac
#EXTINF:-1,Drexciya - Black Sea
/app/music/Drexciya/2013 - Journey of the Deep Sea Dweller IV/14. Black Sea.mp3
#EXTINF:-1,Autechre - Yulquen
/app/music/Autechre/1994 - Amber/09 Yulquen.flac
#EXTINF:-1,The Other People Place - C2 - Running From Love
/app/music/The Other People Place/2017 - Lifestyles Of The Laptop Café/C2 - Running From Love.flac
#EXTINF:-1,Brian Eno - D2 Written, Forgotten
/app/music/Brian Eno/2011 - Small Craft On a Milk Sea/D2 Written, Forgotten.flac
#EXTINF:-1,Autechre - Stud
/app/music/Autechre/1995 - Tri Repetae/05 Stud.flac
#EXTINF:-1,Model 500 - model_500-electric_night
/app/music/Model 500/2015 - Digital Solutions/02-model_500-electric_night.flac
#EXTINF:-1,The Orb - Close Encounters
/app/music/The Orb/1992 - UFOrb/05-Close Encounters.mp3
#EXTINF:-1,Model 500 - model_500-hi_nrg
/app/music/Model 500/2015 - Digital Solutions/01-model_500-hi_nrg.flac
#EXTINF:-1,Brian Eno - B3 Bone Jump
/app/music/Brian Eno/2011 - Small Craft On a Milk Sea/B3 Bone Jump.flac
#EXTINF:-1,Labradford - by Chris Johnston, Craig Markva, Jamie Evans,
/app/music/Labradford/1999 - E luxo so/4. by Chris Johnston, Craig Markva, Jamie Evans,.flac
#EXTINF:-1,The Orb - Star 6 & 7 8 9
/app/music/The Orb/1991 - The Orb's Adventures Beyond the Ultraworld (Double Album)/09 Star 6 & 7 8 9.mp3
#EXTINF:-1,Proem - Proem - Vault ep.1-4 (Noise) - 02 Half a Heart
/app/music/Proem/2016 - Vault ep.1-4 (Noise)/Proem - Vault ep.1-4 (Noise) - 02 Half a Heart.flac
#EXTINF:-1,Dopplereffekt - Spirangle
/app/music/Dopplereffekt/2017 - Cellular Automata/08. Spirangle.flac
#EXTINF:-1,Drexciya - Unknown Journey VII
/app/music/Drexciya/2013 - Journey of the Deep Sea Dweller IV/06. Unknown Journey VII.mp3
#EXTINF:-1,Drexciya - Mantaray
/app/music/Drexciya/2013 - Journey of the Deep Sea Dweller IV/04. Mantaray.mp3
#EXTINF:-1,Pye Corner Audio - Pye Corner Audio - Untitled
/app/music/Pye Corner Audio/EP's & Singles/2017 - Pye Corner Audio, Silent Servant, Not Waving - Limited Edition EP (Vinyl, #E031COL)/02 - Pye Corner Audio - Untitled.mp3
#EXTINF:-1,Underworld - Underworld - Juanita, Kiteless, To Dream Of Love
/app/music/Underworld/1996 - Second Toughest In The Infants/01. Underworld - Juanita, Kiteless, To Dream Of Love.flac
#EXTINF:-1,Proem - Proem - As They Go - 05 In a Timeless, Lightless World
/app/music/Proem/2019 - As They Go/Proem - As They Go - 05 In a Timeless, Lightless World.flac
#EXTINF:-1,Model 500 - model_500-standing_in_tomorow
/app/music/Model 500/2015 - Digital Solutions/03-model_500-standing_in_tomorow.flac
#EXTINF:-1,The Orb - Plum Island
/app/music/The Orb/2001 - Cydonia/09-Plum Island.mp3
#EXTINF:-1,Orbital - Lush 3-2
/app/music/Orbital/1993 - Orbital - Orbital 2 (Brown Album - TRUCD2, 828 386.2)/00. Lush 3-2.mp3
#EXTINF:-1,Dopplereffekt - Exponential Decay
/app/music/Dopplereffekt/2017 - Cellular Automata/09. Exponential Decay.flac
#EXTINF:-1,Brian Eno - Garden of Stars
/app/music/Brian Eno/2022 - ForeverAndEverNoMore/04 Garden of Stars.flac
#EXTINF:-1,The Other People Place - B2 - You Said You Want Me
/app/music/The Other People Place/2017 - Lifestyles Of The Laptop Café/B2 - You Said You Want Me.flac
#EXTINF:-1,Dopplereffekt - Mandelbrot Set
/app/music/Dopplereffekt/2017 - Cellular Automata/07. Mandelbrot Set.flac
#EXTINF:-1,Autechre - Foil
/app/music/Autechre/1994 - Amber/01 Foil.flac
#EXTINF:-1,Proem - Proem - As They Go - 04 What is Needed
/app/music/Proem/2019 - As They Go/Proem - As They Go - 04 What is Needed.flac
#EXTINF:-1,Vector Lovers - Post Arctic Industries
/app/music/Vector Lovers/2005 - Capsule For One/06 - Post Arctic Industries.mp3
#EXTINF:-1,Proem - proem - Negativ - 12 Skylup
/app/music/Proem/2001 - Negativ/proem - Negativ - 12 Skylup.flac
#EXTINF:-1,Model 500 - model_500-encounter
/app/music/Model 500/2015 - Digital Solutions/04-model_500-encounter.flac
#EXTINF:-1,Kraftwerk - Pocket Calculator
/app/music/Kraftwerk/1981 - Computer World/02 - Pocket Calculator.flac
#EXTINF:-1,Tape Loop Orchestra - Tape Loop Orchestra - Chapter 13   Reel Two End
/app/music/Tape Loop Orchestra/2009 - 1953 Culture Festival/13 Tape Loop Orchestra - Chapter 13   Reel Two End.mp3
#EXTINF:-1,Pye Corner Audio - Corrupt Data
/app/music/Pye Corner Audio/2017 - Half-Light (Prower Remixed) (WEB, #MTH011)/01 - Corrupt Data.mp3
#EXTINF:-1,Kiasmos - Kiasmos - II - 04 Laced
/app/music/Kiasmos/2024 - II/Kiasmos - II - 04 Laced.flac
#EXTINF:-1,Pye Corner Audio - Mindshaft
/app/music/Pye Corner Audio/2019 - Hollow Earth (WEB, #GBX032 DL)/05 - Mindshaft.mp3
#EXTINF:-1,Labradford - G
/app/music/Labradford/1997 - Mi Media Naranja/2 G.flac
#EXTINF:-1,Dopplereffekt - Isotropy
/app/music/Dopplereffekt/2017 - Cellular Automata/04. Isotropy.flac
#EXTINF:-1,Autechre - Further
/app/music/Autechre/1994 - Amber/08 Further.flac
#EXTINF:-1,Proem - Proem - You Shall Have Ever Been - 02 Eck The Badly Drawn
/app/music/Proem/2006 - You Shall Have Ever Been/Proem - You Shall Have Ever Been - 02 Eck The Badly Drawn.flac
#EXTINF:-1,Autechre - C-Pach
/app/music/Autechre/1995 - Tri Repetae/07 C-Pach.flac
#EXTINF:-1,Kraftwerk - Neon Lights
/app/music/Kraftwerk/1978 - The Man-Machine/05 - Neon Lights.flac
#EXTINF:-1,Labradford - twenty
/app/music/Labradford/2001 - fixed..context/1 twenty.flac
#EXTINF:-1,Bark Psychosis - (01) [Bark Psychosis] Blue
/app/music/Bark Psychosis/1994 - Game Over/(01) [Bark Psychosis] Blue.flac
#EXTINF:-1,Vector Lovers - Substrata
/app/music/Vector Lovers/2005 - Capsule For One/03 - Substrata.mp3
#EXTINF:-1,Kraftwerk - Computer World
/app/music/Kraftwerk/1981 - Computer World/01 - Computer World.flac
#EXTINF:-1,Underworld - Underworld - Air Towel
/app/music/Underworld/1996 - Second Toughest In The Infants/06. Underworld - Air Towel.flac
#EXTINF:-1,Underworld - Underworld - Blueski
/app/music/Underworld/1996 - Second Toughest In The Infants/07. Underworld - Blueski.flac
#EXTINF:-1,Labradford - Experience The Gated Oscillator
/app/music/Labradford/1993 - Prazision LP/05 Experience The Gated Oscillator.flac
#EXTINF:-1,Dopplereffekt - Gestalt Intelligence
/app/music/Dopplereffekt/2017 - Cellular Automata/03. Gestalt Intelligence.flac
#EXTINF:-1,Labradford - Dulcimers played by Peter Neff. Strings played
/app/music/Labradford/1999 - E luxo so/3. Dulcimers played by Peter Neff. Strings played.flac
#EXTINF:-1,Tape Loop Orchestra - Tape Loop Orchestra - Chapter 14   Tails Out
/app/music/Tape Loop Orchestra/2009 - 1953 Culture Festival/14 Tape Loop Orchestra - Chapter 14   Tails Out.mp3
#EXTINF:-1,Labradford - up to pizmo
/app/music/Labradford/2001 - fixed..context/2 up to pizmo.flac
#EXTINF:-1,Orbital - Sad But True
/app/music/Orbital/1994 - Orbital - Snivilisation (TRUCD5, 828 536.2)/03. Sad But True.mp3
#EXTINF:-1,Orbital - Lush 3-1
/app/music/Orbital/1993 - Orbital - Orbital 2 (Brown Album - TRUCD2, 828 386.2)/00. Lush 3-1.mp3
#EXTINF:-1,Dopplereffekt - Cellular Automata
/app/music/Dopplereffekt/2017 - Cellular Automata/01. Cellular Automata.flac
#EXTINF:-1,The Orb - Little Fluffy Clouds
/app/music/The Orb/1991 - The Orb's Adventures Beyond the Ultraworld (Double Album)/01 Little Fluffy Clouds.mp3
#EXTINF:-1,Bark Psychosis - All Different Things
/app/music/Bark Psychosis/1994 - Independency/03 - All Different Things.flac
#EXTINF:-1,Orbital - Kein Trink Wasser
/app/music/Orbital/1994 - Orbital - Snivilisation (TRUCD5, 828 536.2)/07. Kein Trink Wasser.mp3
#EXTINF:-1,The Orb - Perpetual Dawn
/app/music/The Orb/1991 - The Orb's Adventures Beyond the Ultraworld (Double Album)/06 Perpetual Dawn.mp3
#EXTINF:-1,Underworld - Underworld - Stagger
/app/music/Underworld/1996 - Second Toughest In The Infants/08. Underworld - Stagger.flac
#EXTINF:-1,Dopplereffekt - von Neumann Probe
/app/music/Dopplereffekt/2017 - Cellular Automata/02. von Neumann Probe.flac
#EXTINF:-1,The Orb - EDM
/app/music/The Orb/2001 - Cydonia/12-EDM.mp3
#EXTINF:-1,Teeth Of The Sea - Reaper
/app/music/Teeth Of The Sea/2013 - Master/02 - Reaper.mp3
#EXTINF:-1,Drexciya - Drexciya - Aquabahn
/app/music/Drexciya/2013 - Journey Of The Deep Sea Dweller III/03. Drexciya - Aquabahn.mp3
#EXTINF:-1,Autechre - Rsdio
/app/music/Autechre/1995 - Tri Repetae/10 Rsdio.flac
#EXTINF:-1,Teeth Of The Sea - Transfinite
/app/music/Teeth Of The Sea/2010 - Your Mercury/01 Transfinite.mp3
#EXTINF:-1,Orbital - Philosophy By Numbers
/app/music/Orbital/1994 - Orbital - Snivilisation (TRUCD5, 828 536.2)/06. Philosophy By Numbers.mp3
#EXTINF:-1,Autechre - Dael
/app/music/Autechre/1995 - Tri Repetae/01 Dael.flac
#EXTINF:-1,Vector Lovers - Boulevard
/app/music/Vector Lovers/2005 - Capsule For One/09 - Boulevard.mp3
#EXTINF:-1,Brian Eno - Reflection
/app/music/Brian Eno/2017 - Reflection/01. Reflection.mp3
#EXTINF:-1,Kiasmos - Thrown
/app/music/Kiasmos/2012 - Thrown EP/01 - Thrown.flac
#EXTINF:-1,The Other People Place - A2 - It's Your Love
/app/music/The Other People Place/2017 - Lifestyles Of The Laptop Café/A2 - It's Your Love.flac
#EXTINF:-1,Drexciya - Unknown Journey VIII
/app/music/Drexciya/2013 - Journey of the Deep Sea Dweller IV/07. Unknown Journey VIII.mp3
#EXTINF:-1,Bark Psychosis - I Know
/app/music/Bark Psychosis/1994 - Independency/01 - I Know.flac
#EXTINF:-1,Underworld - Underworld - Rowla
/app/music/Underworld/1996 - Second Toughest In The Infants/04. Underworld - Rowla.flac
#EXTINF:-1,Orbital - Forever
/app/music/Orbital/1994 - Orbital - Snivilisation (TRUCD5, 828 536.2)/01. Forever.mp3
#EXTINF:-1,Autechre - Clipper
/app/music/Autechre/1995 - Tri Repetae/02 Clipper.flac
#EXTINF:-1,The Other People Place - C1 - Let Me Be Me
/app/music/The Other People Place/2017 - Lifestyles Of The Laptop Café/C1 - Let Me Be Me.flac
#EXTINF:-1,Kraftwerk - Metropolis
/app/music/Kraftwerk/1978 - The Man-Machine/03 - Metropolis.flac
#EXTINF:-1,Labradford - The Cipher
/app/music/Labradford/1996 - Labradford/4 The Cipher.flac
#EXTINF:-1,Autechre - Silverside
/app/music/Autechre/1994 - Amber/03 Silverside.flac
#EXTINF:-1,Autechre - Nine
/app/music/Autechre/1994 - Amber/07 Nine.flac
#EXTINF:-1,Kraftwerk - Numbers
/app/music/Kraftwerk/1981 - Computer World/03 - Numbers.flac
#EXTINF:-1,Pye Corner Audio - Recrypt
/app/music/Pye Corner Audio/2011 - Black Mill Tapes Volume 2 - Do You Synthesize (WEB, #pca002)/06 - Recrypt.mp3
#EXTINF:-1,The Other People Place - D1 - Lifestyles Of The Casual
/app/music/The Other People Place/2017 - Lifestyles Of The Laptop Café/D1 - Lifestyles Of The Casual.flac
#EXTINF:-1,Autechre - Teartear
/app/music/Autechre/1994 - Amber/11 Teartear.flac
#EXTINF:-1,Teeth Of The Sea - in the space capsule (love theme)
/app/music/Teeth Of The Sea/2011 - Hypnoticon/01 in the space capsule (love theme).mp3
#EXTINF:-1,Kiasmos - Dragged
/app/music/Kiasmos/2014 - Kiasmos/06 - Dragged.flac
#EXTINF:-1,Dopplereffekt - Ulams Spiral
/app/music/Dopplereffekt/2017 - Cellular Automata/06. Ulams Spiral.flac
#EXTINF:-1,Brian Eno - Stiff
/app/music/Brian Eno/2024 - Eno (Original Motion Picture Soundtrack)/11. Stiff.flac
#EXTINF:-1,The Orb - A Huge Ever Growing Pulsating Brain That Rules From The Centre Of The Ultraworld_ Live Mix Mk 10
/app/music/The Orb/1991 - The Orb's Adventures Beyond the Ultraworld (Double Album)/10 A Huge Ever Growing Pulsating Brain That Rules From The Centre Of The Ultraworld_ Live Mix Mk 10.mp3
#EXTINF:-1,The Orb - Sticky End
/app/music/The Orb/1992 - UFOrb/07-Sticky End.mp3
#EXTINF:-1,Vector Lovers - Microtron
/app/music/Vector Lovers/2005 - Capsule For One/04 - Microtron.mp3
#EXTINF:-1,Kraftwerk - Home Computer
/app/music/Kraftwerk/1981 - Computer World/06 - Home Computer.flac
#EXTINF:-1,Pye Corner Audio - Foreshadowed
/app/music/Pye Corner Audio/2012 - Black Mill Tapes Volume 3 - All Pathways Open (WEB, #pca003)/08 - Foreshadowed.mp3
#EXTINF:-1,Tape Loop Orchestra - Tape Loop Orchestra - Chapter 2   Yasujiro Ozu
/app/music/Tape Loop Orchestra/2009 - 1953 Culture Festival/02 Tape Loop Orchestra - Chapter 2   Yasujiro Ozu.mp3
#EXTINF:-1,Brian Eno - Verdigris
/app/music/Brian Eno/2020 - Roger Eno and Brian Eno - Mixing Colours/08 Verdigris.flac
#EXTINF:-1,Tape Loop Orchestra - The Word On My Lips Is Your Name
/app/music/Tape Loop Orchestra/2012 - The Word On My Lips Is Your Name/Disc 1 - The Word On My Lips Is Your Name/01 - The Word On My Lips Is Your Name.flac
#EXTINF:-1,Vector Lovers - Melodies And Memory
/app/music/Vector Lovers/2005 - Capsule For One/07 - Melodies And Memory.mp3
#EXTINF:-1,Vector Lovers - To The Stars
/app/music/Vector Lovers/2005 - Capsule For One/12 - To The Stars.mp3
#EXTINF:-1,Dopplereffekt - Pascal's Recursion
/app/music/Dopplereffekt/2017 - Cellular Automata/05. Pascal's Recursion.flac
#EXTINF:-1,Orbital - Walk Now
/app/music/Orbital/1993 - Orbital - Orbital 2 (Brown Album - TRUCD2, 828 386.2)/00. Walk Now.mp3
#EXTINF:-1,Orbital - Quality Seconds
/app/music/Orbital/1994 - Orbital - Snivilisation (TRUCD5, 828 536.2)/08. Quality Seconds.mp3
#EXTINF:-1,Vector Lovers - Nostalgia 4 The Future
/app/music/Vector Lovers/2005 - Capsule For One/05 - Nostalgia 4 The Future.mp3
#EXTINF:-1,Brian Eno - D3 Late Anthropocene
/app/music/Brian Eno/2011 - Small Craft On a Milk Sea/D3 Late Anthropocene.flac
#EXTINF:-1,Autechre - Gnit
/app/music/Autechre/1995 - Tri Repetae/08 Gnit.flac
#EXTINF:-1,Pye Corner Audio - Electronic Rhythm Number Seven
/app/music/Pye Corner Audio/2011 - Black Mill Tapes Volume 2 - Do You Synthesize (WEB, #pca002)/02 - Electronic Rhythm Number Seven.mp3
#EXTINF:-1,Proem - Proem - Socially Inept - 05 Pinching Point
/app/music/Proem/2004 - Socially Inept/Proem - Socially Inept - 05 Pinching Point.flac
#EXTINF:-1,Vector Lovers - Empty Buildings, Falling Rain
/app/music/Vector Lovers/2005 - Capsule For One/08 - Empty Buildings, Falling Rain.mp3
#EXTINF:-1,Model 500 - model_500-control
/app/music/Model 500/2015 - Digital Solutions/09-model_500-control.flac
#EXTINF:-1,Proem - Proem - Vault ep.2-4 (Drone) - 02 Another Dull Moment
/app/music/Proem/2016 - Vault ep.2-4 (Drone)/Proem - Vault ep.2-4 (Drone) - 02 Another Dull Moment.flac
#EXTINF:-1,Teeth Of The Sea - Her Wraith
/app/music/Teeth Of The Sea/2019 - WRAITH/06 - Her Wraith.flac
#EXTINF:-1,Brian Eno - Slow Movement Sand
/app/music/Brian Eno/2020 - Roger Eno and Brian Eno - Mixing Colours/18 Slow Movement Sand.flac
#EXTINF:-1,Drexciya - Drexciya - You Don't Know
/app/music/Drexciya/2013 - Journey Of The Deep Sea Dweller III/13. Drexciya - You Don't Know.mp3
#EXTINF:-1,Proem - Proem - You Shall Have Ever Been - 07 Reddings
/app/music/Proem/2006 - You Shall Have Ever Been/Proem - You Shall Have Ever Been - 07 Reddings.flac
#EXTINF:-1,Teeth Of The Sea - Fortean Steed
/app/music/Teeth Of The Sea/2019 - WRAITH/04 - Fortean Steed.flac
#EXTINF:-1,Kraftwerk - The Model
/app/music/Kraftwerk/1978 - The Man-Machine/04 - The Model.flac
#EXTINF:-1,Pye Corner Audio - Yesterday's Entertainment
/app/music/Pye Corner Audio/2012 - Sleep Games (WEB, #GBX017)/07 - Yesterday's Entertainment.mp3
#EXTINF:-1,Brian Eno - B2 Forms Of Anger
/app/music/Brian Eno/2011 - Small Craft On a Milk Sea/B2 Forms Of Anger.flac
#EXTINF:-1,Vector Lovers - Neon Sky Rain
/app/music/Vector Lovers/2005 - Capsule For One/10 - Neon Sky Rain.mp3
#EXTINF:-1,Vector Lovers - Capsule For One
/app/music/Vector Lovers/2005 - Capsule For One/11 - Capsule For One.mp3
#EXTINF:-1,Pye Corner Audio - Solar Waves
/app/music/Pye Corner Audio/EP's & Singles/2019 - Dark Phase EP (WEB, #AF025)/02 - Solar Waves.mp3
#EXTINF:-1,Kraftwerk - Computer Love
/app/music/Kraftwerk/1981 - Computer World/05 - Computer Love.flac
#EXTINF:-1,Kiasmos - Held (Dauwd Remix)
/app/music/Kiasmos/2015 - Looped/02 Held (Dauwd Remix).flac
#EXTINF:-1,Tape Loop Orchestra - Tape Loop Orchestra - Chapter 9   Setsu Ko Hara
/app/music/Tape Loop Orchestra/2009 - 1953 Culture Festival/09 Tape Loop Orchestra - Chapter 9   Setsu Ko Hara.mp3
#EXTINF:-1,The Orb - Back Side of the Moon
/app/music/The Orb/1991 - The Orb's Adventures Beyond the Ultraworld (Double Album)/04 Back Side of the Moon.mp3
#EXTINF:-1,Model 500 - model_500-storm
/app/music/Model 500/2015 - Digital Solutions/05-model_500-storm.flac
#EXTINF:-1,Autechre - Montreal
/app/music/Autechre/1994 - Amber/02 Montreal.flac
#EXTINF:-1,Bark Psychosis - bark psychosis - eyes & smiles
/app/music/Bark Psychosis/1994 - Hex/05 - bark psychosis - eyes & smiles.mp3
#EXTINF:-1,The Other People Place - D2 - Sunrays
/app/music/The Other People Place/2017 - Lifestyles Of The Laptop Café/D2 - Sunrays.flac
#EXTINF:-1,Bark Psychosis - (06) [Bark Psychosis] Bloodrush
/app/music/Bark Psychosis/1994 - Game Over/(06) [Bark Psychosis] Bloodrush.flac
#EXTINF:-1,Bark Psychosis - bark psychosis - pendulum man
/app/music/Bark Psychosis/1994 - Hex/07 - bark psychosis - pendulum man.mp3
#EXTINF:-1,Autechre - Slip
/app/music/Autechre/1994 - Amber/04 Slip.flac
#EXTINF:-1,Kiasmos - Swayed
/app/music/Kiasmos/2014 - Kiasmos/04 - Swayed.flac
#EXTINF:-1,The Other People Place - A1 - Eye Contact
/app/music/The Other People Place/2017 - Lifestyles Of The Laptop Café/A1 - Eye Contact.flac
#EXTINF:-1,Kiasmos - Rival Consoles - Milo
/app/music/Kiasmos/2009 - 65, Milo (Kiasmos & Rival Consoles) (WEB)/03. Rival Consoles - Milo.flac
#EXTINF:-1,Teeth Of The Sea - Butterfly House
/app/music/Teeth Of The Sea/2023 - Hive/03 Butterfly House.flac
#EXTINF:-1,The Orb - A Mile Long Lump of Lard
/app/music/The Orb/2001 - Cydonia/07-A Mile Long Lump of Lard.mp3
#EXTINF:-1,Autechre - Eutow
/app/music/Autechre/1995 - Tri Repetae/06 Eutow.flac
#EXTINF:-1,Model 500 - model_500-the_groove
/app/music/Model 500/2015 - Digital Solutions/06-model_500-the_groove.flac
#EXTINF:-1,Bark Psychosis - bark psychosis - big shot
/app/music/Bark Psychosis/1994 - Hex/04 - bark psychosis - big shot.mp3
#EXTINF:-1,Kiasmos - Rival Consoles - ARP
/app/music/Kiasmos/2009 - 65, Milo (Kiasmos & Rival Consoles) (WEB)/05. Rival Consoles - ARP.flac
#EXTINF:-1,Kraftwerk - Spacelab
/app/music/Kraftwerk/1978 - The Man-Machine/02 - Spacelab.flac
#EXTINF:-1,Brian Eno - Who Gives a Thought
/app/music/Brian Eno/2022 - ForeverAndEverNoMore/01 Who Gives a Thought.flac
#EXTINF:-1,Proem - Proem - Vault ep.4-4 - 04 v. jirku 1
/app/music/Proem/2015 - Vault ep.4-4/Proem - Vault ep.4-4 - 04 v. jirku 1.flac
#EXTINF:-1,Tape Loop Orchestra - The Burnley Brass Band Plays On In My Heart
/app/music/Tape Loop Orchestra/2012 - The Word On My Lips Is Your Name/Disc 2 - The Burnley Brass Band Plays On In My Heart/01 - The Burnley Brass Band Plays On In My Heart.flac
#EXTINF:-1,Proem - Proem - Before it finds you - 06 Pretense for piano and synth
/app/music/Proem/2013 - Before it finds you/Proem - Before it finds you - 06 Pretense for piano and synth.flac
#EXTINF:-1,Orbital - Remind
/app/music/Orbital/1993 - Orbital - Orbital 2 (Brown Album - TRUCD2, 828 386.2)/00. Remind.mp3
#EXTINF:-1,Underworld - Underworld - Pearls Girl
/app/music/Underworld/1996 - Second Toughest In The Infants/05. Underworld - Pearls Girl.flac
#EXTINF:-1,Underworld - Underworld - Banstyle Sappys Curry
/app/music/Underworld/1996 - Second Toughest In The Infants/02. Underworld - Banstyle Sappys Curry.flac
#EXTINF:-1,Orbital - Attached
/app/music/Orbital/1994 - Orbital - Snivilisation (TRUCD5, 828 536.2)/10. Attached.mp3
#EXTINF:-1,Labradford - P
/app/music/Labradford/1997 - Mi Media Naranja/7 P.flac
#EXTINF:-1,Drexciya - Drexciya - Vampire Island
/app/music/Drexciya/2013 - Journey Of The Deep Sea Dweller III/10. Drexciya - Vampire Island.mp3
#EXTINF:-1,Vector Lovers - Arrival, Metropolis
/app/music/Vector Lovers/2005 - Capsule For One/02 - Arrival, Metropolis.mp3
#EXTINF:-1,Teeth Of The Sea - Teeth Of The Sea - Highly Deadly Black Tarantula - 03 Field Punishment
/app/music/Teeth Of The Sea/2015 - Highly Deadly Black Tarantula/Teeth Of The Sea - Highly Deadly Black Tarantula - 03 Field Punishment.flac
#EXTINF:-1,Kiasmos - Swept (Tale of Us remix)
/app/music/Kiasmos/2015 - Swept EP/04 - Swept (Tale of Us remix).mp3
#EXTINF:-1,Proem - Proem - Vault ep.1-4 (Noise) - 05 Only Eat the Grey Wolves
/app/music/Proem/2016 - Vault ep.1-4 (Noise)/Proem - Vault ep.1-4 (Noise) - 05 Only Eat the Grey Wolves.flac
#EXTINF:-1,The Orb - Firestar
/app/music/The Orb/2001 - Cydonia/06-Firestar.mp3
#EXTINF:-1,Tape Loop Orchestra - Tape Loop Orchestra - Chapter 11   Late Autumn
/app/music/Tape Loop Orchestra/2009 - 1953 Culture Festival/11 Tape Loop Orchestra - Chapter 11   Late Autumn.mp3
#EXTINF:-1,Kraftwerk - The Man·Machine
/app/music/Kraftwerk/1978 - The Man-Machine/06 - The Man·Machine.flac

1462
static/asteroid.css Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1041,6 +1041,20 @@
:flex 1
:min-width "300px")
(.persistent-reconnect-btn
:background transparent
:color "#00ff00"
:border "1px solid #00ff00"
:padding "5px 10px"
:cursor "pointer"
:font-family #(main-font)
:font-size "0.85em"
:white-space nowrap
:margin-right "5px")
((:and .persistent-reconnect-btn :hover)
:background "#2a3441")
(.persistent-disable-btn
:background transparent
:color "#00ff00"

106
template/about-content.ctml Normal file
View File

@ -0,0 +1,106 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>About - Asteroid Radio</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="/asteroid/static/asteroid.css">
<script src="/asteroid/static/js/auth-ui.js"></script>
</head>
<body>
<div class="container">
<header>
<h1 style="display: flex; align-items: center; justify-content: center; gap: 15px;">
<img src="/asteroid/static/asteroid.png" alt="Asteroid" style="height: 50px; width: auto;">
<span>ABOUT ASTEROID RADIO</span>
<img src="/asteroid/static/asteroid.png" alt="Asteroid" style="height: 50px; width: auto;">
</h1>
<nav class="nav">
<a href="/asteroid/content" target="_self">Home</a>
<a href="/asteroid/player-content" target="_self">Player</a>
<a href="/asteroid/about-content" target="_self">About</a>
<a href="/asteroid/status" target="_self">Status</a>
<a href="/asteroid/profile" target="_self" data-show-if-logged-in>Profile</a>
<a href="/asteroid/admin" target="_self" data-show-if-admin>Admin</a>
<a href="/asteroid/login" target="_self" data-show-if-logged-out>Login</a>
<a href="/asteroid/register" target="_self" data-show-if-logged-out>Register</a>
<a href="/asteroid/logout" data-show-if-logged-in class="btn-logout">Logout</a>
</nav>
</header>
<main style="max-width: 800px; margin: 0 auto; padding: 20px;">
<section style="margin-bottom: 30px;">
<h2 style="color: #00ff00; border-bottom: 2px solid #00ff00; padding-bottom: 10px;">🎵 Asteroid Music for Hackers</h2>
<p style="line-height: 1.6;">
Asteroid Radio is a community-driven internet radio station born from the SystemCrafters community.
We celebrate the intersection of music, technology, and hacker culture—broadcasting for those who
appreciate both great code and great music.
</p>
<p style="line-height: 1.6;">
We met through a shared set of technical biases and a love for building systems from first principles.
Asteroid Radio embodies that ethos: <strong>music for hackers, built by hackers</strong>.
</p>
</section>
<section style="margin-bottom: 30px;">
<h2 style="color: #00ff00; border-bottom: 2px solid #00ff00; padding-bottom: 10px;">🛠️ Built with Common Lisp</h2>
<p style="line-height: 1.6;">
This entire platform is built using <strong>Common Lisp</strong>, demonstrating the power and elegance
of Lisp for modern web applications. We use:
</p>
<ul style="line-height: 1.8; margin-left: 20px;">
<li><strong><a href="https://codeberg.org/shirakumo/radiance" style="color: #00ff00;">Radiance</a></strong> - Web application framework</li>
<li><strong><a href="https://codeberg.org/shinmera/clip" style="color: #00ff00;">Clip</a></strong> - HTML5-compliant template engine</li>
<li><strong><a href="https://codeberg.org/shinmera/LASS" style="color: #00ff00;">LASS</a></strong> - Lisp Augmented Style Sheets</li>
<li><strong><a href="https://gitlab.common-lisp.net/parenscript/parenscript" style="color: #00ff00;">ParenScript</a></strong> - Lisp-to-JavaScript compiler</li>
<li><strong><a href="https://icecast.org/" style="color: #00ff00;">Icecast</a></strong> - Streaming media server</li>
<li><strong><a href="https://www.liquidsoap.info/" style="color: #00ff00;">Liquidsoap</a></strong> - Audio stream generation</li>
</ul>
<p style="line-height: 1.6;">
By building in Common Lisp, we're doubling down on our technical values and creating features
for "our people"—those who appreciate the elegance of Lisp and the power of understanding your tools deeply.
</p>
</section>
<section style="margin-bottom: 30px;">
<h2 style="color: #00ff00; border-bottom: 2px solid #00ff00; padding-bottom: 10px;">📖 Open Source & AGPL Licensed</h2>
<p style="line-height: 1.6;">
Asteroid Radio is <strong>free and open source software</strong>, licensed under the
<strong><a href="https://www.gnu.org/licenses/agpl-3.0.en.html" style="color: #00ff00;">GNU Affero General Public License v3.0 (AGPL)</a></strong>.
</p>
<p style="line-height: 1.6;">
The source code is available at:
<a href="https://github.com/Fade/asteroid" style="color: #00ff00; font-weight: bold;">https://github.com/Fade/asteroid</a>
</p>
<p style="line-height: 1.6;">
We believe in transparency, collaboration, and the freedom to study, modify, and share the software we use.
The AGPL ensures that improvements to Asteroid Radio remain free and available to everyone.
</p>
</section>
<section style="margin-bottom: 30px;">
<h2 style="color: #00ff00; border-bottom: 2px solid #00ff00; padding-bottom: 10px;">🎧 Features</h2>
<ul style="line-height: 1.8; margin-left: 20px;">
<li><strong>Live Streaming</strong> - Multiple quality options (AAC, MP3)</li>
<li><strong>Persistent Player</strong> - Frameset mode for uninterrupted playback while browsing</li>
<li><strong>Spectrum Analyzer</strong> - Real-time audio visualization with customizable themes</li>
<li><strong>Track Library</strong> - Browse and search the music collection</li>
<li><strong>User Profiles</strong> - Track your listening history</li>
<li><strong>Admin Tools</strong> - Manage tracks, users, and playlists</li>
</ul>
</section>
<section style="margin-bottom: 30px;">
<h2 style="color: #00ff00; border-bottom: 2px solid #00ff00; padding-bottom: 10px;">🤝 Community</h2>
<p style="line-height: 1.6;">
We're part of the <strong><a href="https://systemcrafters.net/" style="color: #00ff00;">SystemCrafters</a></strong>
community—a group of developers, hackers, and enthusiasts who believe in building systems from first principles,
understanding our tools deeply, and sharing knowledge freely.
</p>
<p style="line-height: 1.6;">
Join us in celebrating the intersection of great music and great code!
</p>
</section>
</main>
</div>
</body>
</html>

109
template/about.ctml Normal file
View File

@ -0,0 +1,109 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>About - Asteroid Radio</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="/asteroid/static/favicon.ico">
<link rel="icon" type="image/png" sizes="32x32" href="/asteroid/static/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/asteroid/static/favicon-16x16.png">
<link rel="stylesheet" type="text/css" href="/asteroid/static/asteroid.css">
<script src="/asteroid/static/js/auth-ui.js"></script>
</head>
<body>
<div class="container">
<header>
<h1 style="display: flex; align-items: center; justify-content: center; gap: 15px;">
<img src="/asteroid/static/asteroid.png" alt="Asteroid" style="height: 50px; width: auto;">
<span>ABOUT ASTEROID RADIO</span>
<img src="/asteroid/static/asteroid.png" alt="Asteroid" style="height: 50px; width: auto;">
</h1>
<nav class="nav">
<a href="/asteroid/">Home</a>
<a href="/asteroid/player">Player</a>
<a href="/asteroid/about">About</a>
<a href="/asteroid/status">Status</a>
<a href="/asteroid/profile" data-show-if-logged-in>Profile</a>
<a href="/asteroid/admin" data-show-if-admin>Admin</a>
<a href="/asteroid/login" data-show-if-logged-out>Login</a>
<a href="/asteroid/register" data-show-if-logged-out>Register</a>
<a href="/asteroid/logout" data-show-if-logged-in class="btn-logout">Logout</a>
</nav>
</header>
<main style="max-width: 800px; margin: 0 auto; padding: 20px;">
<section style="margin-bottom: 30px;">
<h2 style="color: #00ff00; border-bottom: 2px solid #00ff00; padding-bottom: 10px;">🎵 Asteroid Music for Hackers</h2>
<p style="line-height: 1.6;">
Asteroid Radio is a community-driven internet radio station born from the SystemCrafters community.
We celebrate the intersection of music, technology, and hacker culture—broadcasting for those who
appreciate both great code and great music.
</p>
<p style="line-height: 1.6;">
We met through a shared set of technical biases and a love for building systems from first principles.
Asteroid Radio embodies that ethos: <strong>music for hackers, built by hackers</strong>.
</p>
</section>
<section style="margin-bottom: 30px;">
<h2 style="color: #00ff00; border-bottom: 2px solid #00ff00; padding-bottom: 10px;">🛠️ Built with Common Lisp</h2>
<p style="line-height: 1.6;">
This entire platform is built using <strong>Common Lisp</strong>, demonstrating the power and elegance
of Lisp for modern web applications. We use:
</p>
<ul style="line-height: 1.8; margin-left: 20px;">
<li><strong><a href="https://codeberg.org/shirakumo/radiance" style="color: #00ff00;">Radiance</a></strong> - Web application framework</li>
<li><strong><a href="https://codeberg.org/shinmera/clip" style="color: #00ff00;">Clip</a></strong> - HTML5-compliant template engine</li>
<li><strong><a href="https://codeberg.org/shinmera/LASS" style="color: #00ff00;">LASS</a></strong> - Lisp Augmented Style Sheets</li>
<li><strong><a href="https://gitlab.common-lisp.net/parenscript/parenscript" style="color: #00ff00;">ParenScript</a></strong> - Lisp-to-JavaScript compiler</li>
<li><strong><a href="https://icecast.org/" style="color: #00ff00;">Icecast</a></strong> - Streaming media server</li>
<li><strong><a href="https://www.liquidsoap.info/" style="color: #00ff00;">Liquidsoap</a></strong> - Audio stream generation</li>
</ul>
<p style="line-height: 1.6;">
By building in Common Lisp, we're doubling down on our technical values and creating features
for "our people"—those who appreciate the elegance of Lisp and the power of understanding your tools deeply.
</p>
</section>
<section style="margin-bottom: 30px;">
<h2 style="color: #00ff00; border-bottom: 2px solid #00ff00; padding-bottom: 10px;">📖 Open Source & AGPL Licensed</h2>
<p style="line-height: 1.6;">
Asteroid Radio is <strong>free and open source software</strong>, licensed under the
<strong><a href="https://www.gnu.org/licenses/agpl-3.0.en.html" style="color: #00ff00;">GNU Affero General Public License v3.0 (AGPL)</a></strong>.
</p>
<p style="line-height: 1.6;">
The source code is available at:
<a href="https://github.com/Fade/asteroid" style="color: #00ff00; font-weight: bold;">https://github.com/Fade/asteroid</a>
</p>
<p style="line-height: 1.6;">
We believe in transparency, collaboration, and the freedom to study, modify, and share the software we use.
The AGPL ensures that improvements to Asteroid Radio remain free and available to everyone.
</p>
</section>
<section style="margin-bottom: 30px;">
<h2 style="color: #00ff00; border-bottom: 2px solid #00ff00; padding-bottom: 10px;">🎧 Features</h2>
<ul style="line-height: 1.8; margin-left: 20px;">
<li><strong>Live Streaming</strong> - Multiple quality options (AAC, MP3)</li>
<li><strong>Persistent Player</strong> - Frameset mode for uninterrupted playback while browsing</li>
<li><strong>Spectrum Analyzer</strong> - Real-time audio visualization with customizable themes</li>
<li><strong>Track Library</strong> - Browse and search the music collection</li>
<li><strong>User Profiles</strong> - Track your listening history</li>
<li><strong>Admin Tools</strong> - Manage tracks, users, and playlists</li>
</ul>
</section>
<section style="margin-bottom: 30px;">
<h2 style="color: #00ff00; border-bottom: 2px solid #00ff00; padding-bottom: 10px;">🤝 Community</h2>
<p style="line-height: 1.6;">
We're part of the <strong><a href="https://systemcrafters.net/" style="color: #00ff00;">SystemCrafters</a></strong>
community—a group of developers, hackers, and enthusiasts who believe in building systems from first principles,
understanding our tools deeply, and sharing knowledge freely.
</p>
<p style="line-height: 1.6;">
Join us in celebrating the intersection of great music and great code!
</p>
</section>
</main>
</div>
</body>
</html>

View File

@ -28,10 +28,17 @@
<span class="now-playing-mini" id="mini-now-playing">Loading...</span>
<button id="reconnect-btn" onclick="reconnectStream()" class="persistent-reconnect-btn" title="Reconnect if audio stops working">
🔄
</button>
<button onclick="disableFramesetMode()" class="persistent-disable-btn">
✕ Disable
</button>
</div>
<!-- Status indicator for connection issues -->
<div id="stream-status" style="display: none; background: #550000; color: #ff6666; padding: 4px 10px; text-align: center; font-size: 0.85em;"></div>
<script>
// Configure audio element for better streaming
@ -134,6 +141,145 @@
// Redirect parent window to regular view
window.parent.location.href = '/asteroid/';
}
// Show status message
function showStatus(message, isError) {
const status = document.getElementById('stream-status');
if (status) {
status.textContent = message;
status.style.display = 'block';
status.style.background = isError ? '#550000' : '#005500';
status.style.color = isError ? '#ff6666' : '#66ff66';
if (!isError) {
setTimeout(() => { status.style.display = 'none'; }, 3000);
}
}
}
function hideStatus() {
const status = document.getElementById('stream-status');
if (status) {
status.style.display = 'none';
}
}
// Reconnect stream - recreates audio element to fix wedged state
function reconnectStream() {
console.log('Reconnecting stream...');
showStatus('🔄 Reconnecting...', false);
const container = document.querySelector('.persistent-player');
const oldAudio = document.getElementById('persistent-audio');
const streamBaseUrl = document.getElementById('stream-base-url').value;
const streamQuality = localStorage.getItem('stream-quality') || 'aac';
const config = getStreamConfig(streamBaseUrl, streamQuality);
if (!container || !oldAudio) {
showStatus('❌ Could not reconnect - reload page', true);
return;
}
// Save current volume and muted state
const savedVolume = oldAudio.volume;
const savedMuted = oldAudio.muted;
console.log('Saving volume:', savedVolume, 'muted:', savedMuted);
// Reset spectrum analyzer if it exists
if (window.resetSpectrumAnalyzer) {
window.resetSpectrumAnalyzer();
}
// Stop and remove old audio
oldAudio.pause();
oldAudio.src = '';
oldAudio.load();
// Create new audio element
const newAudio = document.createElement('audio');
newAudio.id = 'persistent-audio';
newAudio.controls = true;
newAudio.preload = 'metadata';
newAudio.crossOrigin = 'anonymous';
// Restore volume and muted state
newAudio.volume = savedVolume;
newAudio.muted = savedMuted;
// Create source
const source = document.createElement('source');
source.id = 'audio-source';
source.src = config.url;
source.type = config.type;
newAudio.appendChild(source);
// Replace old audio with new
oldAudio.replaceWith(newAudio);
// Re-attach event listeners
attachAudioListeners(newAudio);
// Try to play
setTimeout(() => {
newAudio.play()
.then(() => {
console.log('Reconnected successfully');
showStatus('✓ Reconnected!', false);
// Reinitialize spectrum analyzer - try in this frame first
if (window.initSpectrumAnalyzer) {
setTimeout(() => window.initSpectrumAnalyzer(), 500);
}
// Also try in content frame (where spectrum canvas usually is)
try {
const contentFrame = window.parent.frames['content-frame'];
if (contentFrame && contentFrame.initSpectrumAnalyzer) {
setTimeout(() => {
if (contentFrame.resetSpectrumAnalyzer) {
contentFrame.resetSpectrumAnalyzer();
}
contentFrame.initSpectrumAnalyzer();
console.log('Spectrum analyzer reinitialized in content frame');
}, 600);
}
} catch(e) {
console.log('Could not reinit spectrum in content frame:', e);
}
})
.catch(err => {
console.log('Reconnect play failed:', err);
showStatus('Click play to start stream', false);
});
}, 300);
}
// Attach event listeners to audio element
function attachAudioListeners(audioElement) {
audioElement.addEventListener('waiting', function() {
console.log('Audio buffering...');
});
audioElement.addEventListener('playing', function() {
console.log('Audio playing');
hideStatus();
});
audioElement.addEventListener('error', function(e) {
console.error('Audio error:', e);
showStatus('⚠️ Stream error - click 🔄 to reconnect', true);
});
audioElement.addEventListener('stalled', function() {
console.log('Audio stalled');
showStatus('⚠️ Stream stalled - click 🔄 if no audio', true);
});
}
// Attach listeners to initial audio element
document.addEventListener('DOMContentLoaded', function() {
const audioElement = document.getElementById('persistent-audio');
if (audioElement) {
attachAudioListeners(audioElement);
}
});
</script>
</body>
</html>

View File

@ -51,25 +51,26 @@
</div>
<nav class="nav">
<a href="/asteroid/content" target="content-frame">Home</a>
<a href="/asteroid/player-content" target="content-frame">Player</a>
<a href="/asteroid/status" target="content-frame">Status</a>
<a href="/asteroid/profile" target="content-frame" data-show-if-logged-in>Profile</a>
<a href="/asteroid/admin" target="content-frame" data-show-if-admin>Admin</a>
<a href="/asteroid/login" target="content-frame" data-show-if-logged-out>Login</a>
<a href="/asteroid/register" target="content-frame" data-show-if-logged-out>Register</a>
<a href="/asteroid/logout" data-show-if-logged-in class="btn-logout">Logout</a>
<a href="/asteroid/content" target="_self">Home</a>
<a href="/asteroid/player-content" target="_self">Player</a>
<a href="/asteroid/about-content" target="_self">About</a>
<a href="/asteroid/status" target="_self">Status</a>
<a href="/asteroid/profile" target="_self" data-show-if-logged-in>Profile</a>
<a href="/asteroid/admin" target="_self" data-show-if-admin>Admin</a>
<a href="/asteroid/login" target="_self" data-show-if-logged-out>Login</a>
<a href="/asteroid/register" target="_self" data-show-if-logged-out>Register</a>
<a href="/asteroid/logout" data-show-if-logged-in class="btn-logout">Logout</a>
</nav>
</header>
<main>
<div class="live-stream">
<h2 style="color: #00ff00; margin: 0;"><span class="live-stream-indicator" style="font-size: 1rem;">🟢</span> LIVE STREAM</h2>
<input type="hidden" id="stream-base-url" lquery="(val stream-base-url)">
<p><strong class="live-stream-label">Stream URL:</strong> <code id="stream-url" lquery="(text default-stream-url)"></code></p>
<p><strong class="live-stream-label">Stream Quality:</strong> <span id="stream-format" lquery="(text default-stream-encoding-desc)"></span></p>
<p><strong class="live-stream-label">BROADCASTING:</strong> <span id="stream-status" style="">Asteroid music for Hackers</span></p>
<p class="frame-enable-message"><em>The live stream player is now in the persistent bar at the bottom of the page</em></p>
<input type="hidden" id="stream-base-url" lquery="(val stream-base-url)">
<p><strong class="live-stream-label">Stream URL:</strong> <code id="stream-url" lquery="(text default-stream-url)"></code></p>
<p><strong class="live-stream-label">Stream Quality:</strong> <span id="stream-format" lquery="(text default-stream-encoding-desc)"></span></p>
<p><strong class="live-stream-label">BROADCASTING:</strong> <span id="stream-status" style="">Asteroid music for Hackers</span></p>
<p class="frame-enable-message"><em>The live stream player is now in the persistent bar at the bottom of the page</em></p>
</div>
<div id="now-playing" class="now-playing"></div>

View File

@ -51,21 +51,22 @@
</div>
<nav class="nav">
<a href="/asteroid/">Home</a>
<a href="/asteroid/player">Player</a>
<a href="/asteroid/status">Status</a>
<a href="/asteroid/profile" data-show-if-logged-in>Profile</a>
<a href="/asteroid/admin" data-show-if-admin>Admin</a>
<a href="/asteroid/login" data-show-if-logged-out>Login</a>
<a href="/asteroid/register" data-show-if-logged-out>Register</a>
<a href="/asteroid/logout" data-show-if-logged-in class="btn-logout">Logout</a>
<a href="/asteroid/">Home</a>
<a href="/asteroid/player">Player</a>
<a href="/asteroid/about">About</a>
<a href="/asteroid/status">Status</a>
<a href="/asteroid/profile" data-show-if-logged-in>Profile</a>
<a href="/asteroid/admin" data-show-if-admin>Admin</a>
<a href="/asteroid/login" data-show-if-logged-out>Login</a>
<a href="/asteroid/register" data-show-if-logged-out>Register</a>
<a href="/asteroid/logout" data-show-if-logged-in class="btn-logout">Logout</a>
</nav>
</header>
<main>
<div class="live-stream">
<div style="margin-bottom: 20px;">
<h2 style="color: #00ff00; margin: 0;"><span class="live-stream-indicator" style="font-size: 1rem;">🟢</span> LIVE STREAM</h2>
<h2 style="color: #00ff00; margin: 0;"><span class="live-stream-indicator" style="font-size: 1rem;">🟢</span> LIVE STREAM</h2>
</div>
<!-- Stream Quality Selector -->
@ -84,6 +85,9 @@
<p><strong class="live-stream-label" class="live-stream-label">BROADCASTING:</strong> <span id="stream-status" style="">Asteroid music for Hackers</span></p>
<div style="display: flex; gap: 10px; justify-content: end; margin-bottom: 20px;">
<button id="reconnect-btn" class="btn btn-warning" onclick="reconnectStream()" style="font-size: 0.9em; display: none;">
🔄 Reconnect Stream
</button>
<button id="popout-btn" class="btn btn-info" onclick="openPopoutPlayer()" style="font-size: 0.9em;">
🗗 Pop Out Player
</button>
@ -92,10 +96,15 @@
</button>
</div>
<audio id="live-audio" controls crossorigin="anonymous" style="width: 100%; margin: 10px 0;">
<source id="audio-source" lquery="(attr :src default-stream-url :type default-stream-encoding)">
Your browser does not support the audio element.
</audio>
<!-- Stream connection status -->
<div id="stream-status-indicator" style="display: none; padding: 8px; margin-bottom: 10px; border-radius: 4px; text-align: center;"></div>
<div id="audio-container">
<audio id="live-audio" controls crossorigin="anonymous" style="width: 100%; margin: 10px 0;">
<source id="audio-source" lquery="(attr :src default-stream-url :type default-stream-encoding)">
Your browser does not support the audio element.
</audio>
</div>
</div>
<div id="now-playing" class="now-playing"></div>

View File

@ -6,6 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="/asteroid/static/asteroid.css">
<script src="/asteroid/static/js/auth-ui.js"></script>
<script src="/asteroid/static/js/front-page.js"></script>
<script src="/asteroid/static/js/player.js"></script>
<script src="/api/asteroid/spectrum-analyzer.js"></script>
</head>

View File

@ -9,6 +9,7 @@
<link rel="icon" type="image/png" sizes="16x16" href="/asteroid/static/favicon-16x16.png">
<link rel="stylesheet" type="text/css" href="/asteroid/static/asteroid.css">
<script src="/asteroid/static/js/auth-ui.js"></script>
<script src="/asteroid/static/js/front-page.js"></script>
<script src="/asteroid/static/js/player.js"></script>
</head>
<body>

View File

@ -311,3 +311,49 @@
(error (e)
(format t "Error initializing user system: ~a~%" e)))))
:name "user-init"))))
(defun dump-users (users)
(with-open-file (s "userdump.csv" :direction :output :if-exists :supersede)
(loop for user in users
do
(let* ((_id (dm:field user "_id"))
(username (dm:field user "username"))
(email (dm:field user "email"))
(password-hash (dm:field user "password-hash"))
(role (dm:field user "role"))
(active (dm:field user "active"))
(created-date (dm:field user "created-date"))
(last-login (dm:field user "last-login")))
(format s "~&~{~A~^,~}~%" (list _id username email password-hash role active created-date last-login))
(finish-output s)))))
(defun get-users-from-csv (filename)
(with-open-file (s filename :direction :input)
(let ((csv-data (cl-csv:read-csv s)))
csv-data)))
(defun ensure-users (filename)
(let* ((users (get-users-from-csv filename)))
(princ users)
(loop
for (_id username email password-hash role active created-date last-login) in users
do
(progn
(format t "~&_id: ~A, username: ~A, email: ~A password-hash: ~A role: ~A active: ~A created-date: ~A last-login: ~A" _id username email password-hash role active created-date last-login)
(let ((user (dm:hull "USERS")))
(setf (dm:field user "username") username)
(setf (dm:field user "email") email)
(setf (dm:field user "password-hash") password-hash)
(setf (dm:field user "role") role)
(setf (dm:field user "active") active)
(setf (dm:field user "created-date") created-date)
(setf (dm:field user "last-login") nil)
(handler-case
(db:with-transaction ()
(format t "Inserting user: ~A~%" user)
(let ((result (dm:insert user)))
(format t "Insert result: ~A~%" result)
(format t "User created: ~A (~A)~%" username role)))
(error (e)
(format t "Error creating user ~A: ~A~%" username e))))))))