Compare commits

..

4 Commits

Author SHA1 Message Date
Luis Pereira ff651e6a36 fix: merged status and status-content 2026-01-15 12:58:45 -05:00
Luis Pereira 2118f4ed5a fix: merged about and about-content 2026-01-15 12:58:45 -05:00
Luis Pereira b862097ca2 fix: merged player and player-content 2026-01-15 12:58:45 -05:00
Luis Pereira 01cb0366c0 fix: merged frontend and frontend-content 2026-01-15 12:58:45 -05:00
9 changed files with 209 additions and 530 deletions

View File

@ -853,7 +853,8 @@
(define-page front-page-content #@"/content" ()
"Front page content (displayed in content frame)"
(clip:process-to-string
(load-template "front-page-content")
(load-template "front-page")
:framesetp t
:title "ASTEROID RADIO"
:station-name "ASTEROID RADIO"
:status-message "🟢 LIVE - Broadcasting asteroid music for hackers"
@ -1284,7 +1285,8 @@
(define-page-with-limit player-content #@"/player-content" (:limit-group "public")
"Player page content (displayed in content frame)"
(clip:process-to-string
(load-template "player-content")
(load-template "player")
:framesetp t
:title "Asteroid Radio - Web Player"
:stream-base-url *stream-base-url*
:default-stream-url (format nil "~a/asteroid.aac" *stream-base-url*)
@ -1310,14 +1312,22 @@
(define-page-with-limit about-content #@"/about-content" (:limit-group "public")
"About page content (displayed in content frame)"
(clip:process-to-string
(load-template "about-content")
(load-template "about")
:framesetp t
:title "About - Asteroid Radio"))
(define-page-with-limit status-page #@"/status" (:limit-group "public")
"Status page content"
(clip:process-to-string
(load-template "status")
:title "Status - Asteroid Radio"))
;; Status content (for frameset mode)
(define-page-with-limit status-content #@"/status-content" (:limit-group "public")
"Status page content (displayed in content frame)"
(clip:process-to-string
(load-template "status-content")
(load-template "status")
:framesetp t
:title "Status - Asteroid Radio"))
(define-api-with-limit asteroid/status () ()

View File

@ -1,106 +0,0 @@
<!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>

View File

@ -18,16 +18,32 @@
<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>
<c:if test="(not framesetp)">
<c:then>
<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>
</c:then>
<c:else>
<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-content" 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>
</c:else>
</c:if>
</nav>
</header>
<main style="max-width: 800px; margin: 0 auto; padding: 20px;">

View File

@ -1,125 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title data-text="title">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>
<script src="/asteroid/static/js/front-page.js"></script>
<script src="/asteroid/static/js/recently-played.js"></script>
<script src="/api/asteroid/spectrum-analyzer.js"></script>
</head>
<body>
<div class="container">
<header>
<h1 class="page-title">
<img src="/asteroid/static/asteroid.png" alt="Asteroid" style="height: 60px; width: auto;">
<span data-text="station-name">ASTEROID RADIO</span>
<img src="/asteroid/static/asteroid.png" alt="Asteroid" style="height: 60px; width: auto;">
</h1>
<h3 class="page-subtitle">The Station at the End of Time</h3>
<!-- Spectrum Analyzer Canvas -->
<div style="text-align: center; margin: 15px 0;">
<canvas id="spectrum-canvas" width="800" height="100" style="max-width: 100%; border: 1px solid #00ff00; background: #000; border-radius: 4px;"></canvas>
<div style="margin-top: 8px; font-size: 0.9em;">
<label style="margin-right: 10px;">
Style:
<select id="spectrum-style-selector" onchange="setSpectrumStyle(this.value)" style="padding: 3px; background: #000; color: #00ff00; border: 1px solid #00ff00;">
<option value="bars">Bars</option>
<option value="wave">Wave</option>
<option value="dots">Dots</option>
</select>
</label>
<label>
Theme:
<select id="spectrum-theme-selector" onchange="setSpectrumTheme(this.value)" style="padding: 3px; background: #000; color: #00ff00; border: 1px solid #00ff00;">
<option value="monotone">Monotone</option>
<option value="green">Green</option>
<option value="blue">Blue</option>
<option value="purple">Purple</option>
<option value="red">Red</option>
<option value="amber">Amber</option>
<option value="rainbow">Rainbow</option>
</select>
</label>
</div>
</div>
<nav class="nav">
<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-content" 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>
<!-- Channel Selector -->
<div class="live-stream-quality" style="margin-bottom: 15px;">
<label for="stream-channel" class="live-stream-label"><strong>Channel:</strong></label>
<select id="stream-channel" onchange="changeChannel()">
<option value="curated">🎧 <c:splice lquery="(text curated-channel-name)">Curated</c:splice></option>
<option value="shuffle">🎲 Shuffle</option>
</select>
</div>
<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>
<!-- Recently Played Tracks -->
<div id="recently-played-panel" class="recently-played-panel">
<h3>Recently Played</h3>
<div id="recently-played-list" class="recently-played-list">
<p class="loading">Loading...</p>
</div>
</div>
<!-- Track Request Section -->
<div class="request-panel">
<h3>🎵 Request a Track</h3>
<p class="request-description">Want to hear something specific? Submit a request and an admin will review it.</p>
<div class="request-form">
<input type="text" id="request-title" class="request-input" placeholder="Suggest a track, artist, or album...">
<input type="text" id="request-message" class="request-input" placeholder="Why do you want to hear this? (optional)">
<button class="btn btn-primary" onclick="submitTrackRequest()">Submit Request</button>
</div>
<div id="request-status" class="request-status" style="display: none;"></div>
<div class="recent-requests">
<h4>Recently Played Requests</h4>
<div id="recent-requests-list">
<p class="no-requests">Loading...</p>
</div>
</div>
</div>
</main>
<footer class="site-footer">
<span>Listed on <a href="http://www.internet-radio.com/stations/ambient/" target="_blank" rel="noopener">Internet Radio</a></span>
<span class="craftering">
<a href="https://craftering.systemcrafters.net/@asteroid/previous">←</a>
<a href="https://craftering.systemcrafters.net/">craftering</a>
<a href="https://craftering.systemcrafters.net/@asteroid/next">→</a>
</span>
</footer>
</div>
</body>
</html>

View File

@ -51,15 +51,30 @@
</div>
<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>
<c:if test="(not framesetp)">
<c:then>
<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>
</c:then>
<c:else>
<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-content" 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>
</c:else>
</c:if>
</nav>
</header>
@ -77,43 +92,54 @@
<option value="shuffle">🎲 Shuffle</option>
</select>
</div>
<!-- Stream Quality Selector -->
<div class="live-stream-quality" style="margin-bottom: 15px;">
<label for="stream-quality" class="live-stream-label" ><strong>Quality:</strong></label>
<select id="stream-quality" onchange="changeStreamQuality()">
<option value="aac">AAC 96kbps (Recommended)</option>
<option value="mp3">MP3 128kbps (Compatible)</option>
<option value="low">MP3 64kbps (Low Bandwidth)</option>
</select>
</div>
<c:if test="(not framesetp)">
<c:then>
<!-- Stream Quality Selector -->
<div class="live-stream-quality" style="margin-bottom: 15px;">
<label for="stream-quality" class="live-stream-label" ><strong>Quality:</strong></label>
<select id="stream-quality" onchange="changeStreamQuality()">
<option value="aac">AAC 96kbps (Recommended)</option>
<option value="mp3">MP3 128kbps (Compatible)</option>
<option value="low">MP3 64kbps (Low Bandwidth)</option>
</select>
</div>
</c:then>
</c:if>
<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" 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;">
<c:if test="(not framesetp)">
<c:then>
<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;">
</button>
<button id="popout-btn" class="btn btn-info" onclick="openPopoutPlayer()" style="font-size: 0.9em;">
🗗 Pop Out Player
</button>
<button id="frameset-btn" class="btn btn-secondary" onclick="enableFramesetMode()" style="font-size: 0.9em;">
</button>
<button id="frameset-btn" class="btn btn-secondary" onclick="enableFramesetMode()" style="font-size: 0.9em;">
🖼️ Enable Persistent Player
</button>
</div>
<!-- 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>
</button>
</div>
<!-- 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>
</c:then>
<c:else>
<p class="frame-enable-message"><em>The live stream player is now in the persistent bar at the bottom of the page</em></p>
</c:else>
</c:if>
</div>
<div id="now-playing" class="now-playing"></div>

View File

@ -1,157 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title data-text="title">Asteroid Radio - Web Player</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>
<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>
<body>
<div class="container">
<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>WEB PLAYER</span>
<img src="/asteroid/static/asteroid.png" alt="Asteroid" style="height: 50px; width: auto;">
</h1>
<!-- Spectrum Analyzer Canvas -->
<div style="text-align: center; margin: 15px 0;">
<canvas id="spectrum-canvas" width="800" height="100" style="max-width: 100%; border: 1px solid #00ff00; background: #000; border-radius: 4px;"></canvas>
<div style="margin-top: 8px; font-size: 0.9em;">
<label style="margin-right: 10px;">
Style:
<select id="spectrum-style-selector" onchange="setSpectrumStyle(this.value)" style="padding: 3px; background: #000; color: #00ff00; border: 1px solid #00ff00;">
<option value="bars">Bars</option>
<option value="wave">Wave</option>
<option value="dots">Dots</option>
</select>
</label>
<label>
Theme:
<select id="spectrum-theme-selector" onchange="setSpectrumTheme(this.value)" style="padding: 3px; background: #000; color: #00ff00; border: 1px solid #00ff00;">
<option value="monotone">Monotone</option>
<option value="green">Green</option>
<option value="blue">Blue</option>
<option value="purple">Purple</option>
<option value="red">Red</option>
<option value="amber">Amber</option>
<option value="rainbow">Rainbow</option>
</select>
</label>
</div>
</div>
<div class="nav">
<a href="/asteroid/content" target="content-frame">Home</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>
</div>
<!-- Live Stream Section - Note about persistent player -->
<div class="player-section">
<h2 style="color: #00ff00;">
<span class="live-stream-indicator" style="font-size: 1rem;">🟢 </span>
Live Radio Stream
</h2>
<p><em>The live stream player is now in the persistent bar at the bottom of the page. It will continue playing as you navigate between pages!</em></p>
</div>
<div id="now-playing" class="now-playing"></div>
<!-- Track Browser -->
<div class="player-section">
<h2>Personal Track Library</h2>
<div class="track-browser">
<input type="text" id="search-tracks" placeholder="Search tracks..." class="search-input">
<select id="library-tracks-per-page" class="sort-select" onchange="changeLibraryTracksPerPage()" style="margin: 10px 0px;">
<option value="10">10 per page</option>
<option value="20" selected>20 per page</option>
<option value="50">50 per page</option>
</select>
<div id="track-list" class="track-list">
<div class="loading">Loading tracks...</div>
</div>
<!-- Pagination Controls -->
<div id="library-pagination-controls" style="display: none; margin-top: 20px; text-align: center;">
<button onclick="libraryGoToPage(1)" class="btn btn-secondary">« First</button>
<button onclick="libraryPreviousPage()" class="btn btn-secondary"> Prev</button>
<span id="library-page-info" style="margin: 0 15px; font-weight: bold;">Page 1 of 1</span>
<button onclick="libraryNextPage()" class="btn btn-secondary">Next </button>
<button onclick="libraryGoToLastPage()" class="btn btn-secondary">Last »</button>
</div>
</div>
</div>
<!-- Audio Player Widget -->
<div class="player-section">
<h2>Audio Player</h2>
<div class="audio-player">
<div class="now-playing">
<div class="track-art">🎵</div>
<div class="track-details">
<div class="track-title" id="current-title">No track selected</div>
<div class="track-artist" id="current-artist">Unknown Artist</div>
<div class="track-album" id="current-album">Unknown Album</div>
</div>
</div>
<audio id="audio-player" controls preload="none" style="width: 100%; margin: 20px 0; display: none;">
Your browser does not support the audio element.
</audio>
<div class="player-controls">
<button id="prev-btn" class="btn btn-secondary">⏮️ Previous</button>
<button id="play-pause-btn" class="btn btn-primary">▶️ Play</button>
<button id="next-btn" class="btn btn-secondary">⏭️ Next</button>
<button id="shuffle-btn" class="btn btn-info">🔀 Shuffle</button>
<button id="repeat-btn" class="btn btn-warning">🔁 Repeat</button>
</div>
<div class="player-info">
<div class="time-display">
<span id="current-time">0:00</span> / <span id="total-time">0:00</span>
</div>
<div class="volume-control">
<label for="volume-slider">🔊</label>
<input type="range" id="volume-slider" min="0" max="100" value="50" class="volume-slider">
</div>
</div>
</div>
</div>
<!-- Playlist Management -->
<div class="player-section">
<h2>Playlists</h2>
<div class="playlist-controls">
<input type="text" id="new-playlist-name" placeholder="New playlist name..." class="playlist-input">
<button id="create-playlist" class="btn btn-success"> Create Playlist</button>
</div>
<div class="playlist-list">
<div id="playlists-container">
<div class="no-playlists">No playlists created yet.</div>
</div>
</div>
</div>
<!-- Queue -->
<div class="player-section">
<h2>Play Queue</h2>
<div class="queue-controls">
<button id="clear-queue" class="btn btn-danger">🗑️ Clear Queue</button>
<button id="save-queue" class="btn btn-info">💾 Save as Playlist</button>
</div>
<div id="play-queue" class="play-queue">
<div class="empty-queue">Queue is empty</div>
</div>
</div>
</div>
</body>
</html>

View File

@ -11,6 +11,11 @@
<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>
<c:if test="framesetp">
<c:then>
<script src="/api/asteroid/spectrum-analyzer.js"></script>
</c:then>
</c:if>
</head>
<body>
<div class="container">
@ -19,39 +24,90 @@
<span>WEB PLAYER</span>
<img src="/asteroid/static/asteroid.png" alt="Asteroid" style="height: 50px; width: auto;">
</h1>
<!-- Spectrum Analyzer Canvas -->
<c:if test="framesetp">
<c:then>
<div style="text-align: center; margin: 15px 0;">
<canvas id="spectrum-canvas" width="800" height="100" style="max-width: 100%; border: 1px solid #00ff00; background: #000; border-radius: 4px;"></canvas>
<div style="margin-top: 8px; font-size: 0.9em;">
<label style="margin-right: 10px;">
Style:
<select id="spectrum-style-selector" onchange="setSpectrumStyle(this.value)" style="padding: 3px; background: #000; color: #00ff00; border: 1px solid #00ff00;">
<option value="bars">Bars</option>
<option value="wave">Wave</option>
<option value="dots">Dots</option>
</select>
</label>
<label>
Theme:
<select id="spectrum-theme-selector" onchange="setSpectrumTheme(this.value)" style="padding: 3px; background: #000; color: #00ff00; border: 1px solid #00ff00;">
<option value="monotone">Monotone</option>
<option value="green">Green</option>
<option value="blue">Blue</option>
<option value="purple">Purple</option>
<option value="red">Red</option>
<option value="amber">Amber</option>
<option value="rainbow">Rainbow</option>
</select>
</label>
</div>
</div>
</c:then>
</c:if>
<div class="nav">
<a href="/asteroid">Home</a>
<a href="/asteroid/profile">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>
<c:if test="(not framesetp)">
<c:then>
<a href="/asteroid">Home</a>
<a href="/asteroid/profile">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>
</c:then>
<c:else>
<a href="/asteroid/content" target="content-frame">Home</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>
</c:else>
</c:if>
</div>
<!-- Live Stream Section -->
<div class="player-section">
<h2 style="color: #00ff00;">
<span class="live-stream-indicator" style="font-size: 1rem;">🟢 </span>
Live Radio Stream
</h2>
<div class="live-stream">
<input type="hidden" id="stream-base-url" lquery="(val stream-base-url)">
<!-- Stream Quality Selector -->
<div class="live-stream-quality">
<label for="live-stream-quality"><strong>Quality:</strong></label>
<select id="live-stream-quality" onchange="changeLiveStreamQuality()">
<option value="aac">AAC 96kbps (Recommended)</option>
<option value="mp3">MP3 128kbps (Compatible)</option>
<option value="low">MP3 64kbps (Low Bandwidth)</option>
</select>
</div>
<audio id="live-stream-audio" controls style="width: 100%; margin-top: 20px;">
<source id="live-stream-source" lquery="(attr :src default-stream-url)" type="audio/aac">
Your browser does not support the audio element.
</audio>
<p><em>Listen to the live Asteroid Radio stream</em></p>
</div>
<c:if test="(not framesetp)">
<c:then>
<div class="live-stream">
<input type="hidden" id="stream-base-url" lquery="(val stream-base-url)">
<!-- Stream Quality Selector -->
<div class="live-stream-quality">
<label for="live-stream-quality"><strong>Quality:</strong></label>
<select id="live-stream-quality" onchange="changeLiveStreamQuality()">
<option value="aac">AAC 96kbps (Recommended)</option>
<option value="mp3">MP3 128kbps (Compatible)</option>
<option value="low">MP3 64kbps (Low Bandwidth)</option>
</select>
</div>
<audio id="live-stream-audio" controls style="width: 100%; margin-top: 20px;">
<source id="live-stream-source" lquery="(attr :src default-stream-url)" type="audio/aac">
Your browser does not support the audio element.
</audio>
<p><em>Listen to the live Asteroid Radio stream</em></p>
</div>
</c:then>
<c:else>
<p><em>The live stream player is now in the persistent bar at the bottom of the page. It will continue playing as you navigate between pages!</em></p>
</c:else>
</c:if>
</div>
<div id="now-playing" class="now-playing"></div>

View File

@ -1,57 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Asteroid Radio - Status</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>📡 SYSTEM STATUS</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-content" 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;">🟢 Server Status</h2>
<p style="line-height: 1.6;">
Asteroid Radio is currently online and broadcasting.
</p>
</section>
<section style="margin-bottom: 30px;">
<h2 style="color: #00ff00; border-bottom: 2px solid #00ff00; padding-bottom: 10px;">📊 Stream Information</h2>
<ul style="line-height: 1.8;">
<li><strong>Status:</strong> 🟢 Live</li>
<li><strong>Formats:</strong> AAC 96kbps, MP3 128kbps, MP3 64kbps</li>
<li><strong>Server:</strong> Icecast</li>
</ul>
</section>
<section style="margin-bottom: 30px;">
<h2 style="color: #00ff00; border-bottom: 2px solid #00ff00; padding-bottom: 10px;"> Additional Information</h2>
<p style="line-height: 1.6;">
For detailed system status and administration, please visit the <a href="/asteroid/admin" style="color: #00ff00;" data-show-if-admin>Admin Dashboard</a>.
</p>
</section>
</main>
</div>
</body>
</html>

View File

@ -15,16 +15,32 @@
<span>📡 SYSTEM STATUS</span>
<img src="/asteroid/static/asteroid.png" alt="Asteroid" style="height: 50px; width: auto;">
</h1>
<nav class="nav">
<a href="/asteroid/frameset">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" onclick="event.preventDefault(); fetch('/asteroid/logout').then(() => window.location.href='/asteroid/frameset');">Logout</a>
<c:if test="(not framesetp)">
<c:then>
<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>
</c:then>
<c:else>
<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-content" 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>
</c:else>
</c:if>
</nav>
</header>