Compare commits

...

4 Commits

Author SHA1 Message Date
Luis Pereira 043c0d8610 fix: stream service containers lock to localhost access 2025-11-03 18:39:11 -05:00
Luis Pereira 4404b416eb fix: stream information update on frameset view 2025-11-03 18:36:40 -05:00
Luis Pereira aa9a2cf225 feat: persist stream quality in local storage 2025-11-03 18:36:40 -05:00
Luis Pereira 559893ed64 fix: redirection when navigating between frameset 2025-11-03 18:36:40 -05:00
9 changed files with 87 additions and 29 deletions

View File

@ -3,7 +3,7 @@ services:
image: infiniteproject/icecast:latest image: infiniteproject/icecast:latest
container_name: asteroid-icecast container_name: asteroid-icecast
ports: ports:
- "8000:8000" - "127.0.0.1:8000:8000"
volumes: volumes:
- ./icecast.xml:/etc/icecast.xml - ./icecast.xml:/etc/icecast.xml
environment: environment:
@ -20,7 +20,7 @@ services:
dockerfile: Dockerfile.liquidsoap dockerfile: Dockerfile.liquidsoap
container_name: asteroid-liquidsoap container_name: asteroid-liquidsoap
ports: ports:
- "1234:1234" - "127.0.0.1:1234:1234"
depends_on: depends_on:
- icecast - icecast
volumes: volumes:

View File

@ -30,15 +30,11 @@ function changeStreamQuality() {
const streamBaseUrl = document.getElementById('stream-base-url'); const streamBaseUrl = document.getElementById('stream-base-url');
const config = getStreamConfig(streamBaseUrl.value, selector.value); const config = getStreamConfig(streamBaseUrl.value, selector.value);
// Update UI elements // Save preference
document.getElementById('stream-url').textContent = config.url; localStorage.setItem('stream-quality', selector.value);
document.getElementById('stream-format').textContent = config.format;
// Update Station Status stream quality display // Update stream information with new selection
const statusQuality = document.querySelector('[data-text="stream-quality"]'); updateStreamInformation()
if (statusQuality) {
statusQuality.textContent = config.format;
}
// Update audio player // Update audio player
const audioElement = document.getElementById('live-audio'); const audioElement = document.getElementById('live-audio');
@ -74,19 +70,35 @@ async function updateNowPlaying() {
} }
} }
// Initialize stream quality display on page load // Update stream information
window.addEventListener('DOMContentLoaded', function() { function updateStreamInformation() {
// Set initial quality display to match the selected stream // Set initial quality display to match the selected stream
const selector = document.getElementById('stream-quality'); const selector = document.getElementById('stream-quality');
const streamBaseUrl = document.getElementById('stream-base-url'); const streamBaseUrl = document.getElementById('stream-base-url');
const config = getStreamConfig(streamBaseUrl.value, selector.value); const streamQuality = localStorage.getItem('stream-quality') || 'aac';
if (selector && selector.value !== streamQuality) {
selector.value = streamQuality;
selector.dispatchEvent(new Event('change'));
}
if (streamBaseUrl) {
const config = getStreamConfig(streamBaseUrl.value, streamQuality);
document.getElementById('stream-url').textContent = config.url; document.getElementById('stream-url').textContent = config.url;
document.getElementById('stream-format').textContent = config.format; document.getElementById('stream-format').textContent = config.format;
const statusQuality = document.querySelector('[data-text="stream-quality"]'); const statusQuality = document.querySelector('[data-text="stream-quality"]');
if (statusQuality) { if (statusQuality) {
statusQuality.textContent = config.format; statusQuality.textContent = config.format;
} }
}
}
// Initialize stream quality display on page load
window.addEventListener('DOMContentLoaded', function() {
// Set initial quality display to match the selected stream
updateStreamInformation()
// Periodicaly updates stream information if on frameset.
// This handles changes from the player frame
const isFramesetPage = window.parent !== window.self;
if (isFramesetPage) setInterval(updateStreamInformation, 1000);
// Update playing information right after load // Update playing information right after load
updateNowPlaying(); updateNowPlaying();
@ -185,14 +197,28 @@ function disableFramesetMode() {
window.location.href = '/asteroid/'; window.location.href = '/asteroid/';
} }
function redirectWhenFrame() {
const path = window.location.pathname;
const isFramesetPage = window.parent !== window.self;
const isContentFrame = path.includes('asteroid/content');
if (isFramesetPage && !isContentFrame) {
window.location.href = '/asteroid/content';
}
if (!isFramesetPage && isContentFrame) {
window.location.href = '/asteroid';
}
}
// Check if user prefers frameset mode on page load // Check if user prefers frameset mode on page load
window.addEventListener('DOMContentLoaded', function() { window.addEventListener('DOMContentLoaded', function() {
const path = window.location.pathname; const path = window.location.pathname;
const isFramesetPage = path.includes('/frameset') || path.includes('/content') || const isFramesetPage = window.parent !== window.self;
path.includes('/audio-player-frame') || path.includes('/player-content');
if (localStorage.getItem('useFrameset') === 'true' && !isFramesetPage && path === '/asteroid/') { if (localStorage.getItem('useFrameset') === 'true' && !isFramesetPage && path.includes('/asteroid')) {
// User wants frameset but is on regular front page, redirect // User wants frameset but is on regular front page, redirect
window.location.href = '/asteroid/frameset'; window.location.href = '/asteroid/frameset';
} }
redirectWhenFrame();
}); });

View File

@ -14,6 +14,7 @@ let filteredLibraryTracks = [];
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
audioPlayer = document.getElementById('audio-player'); audioPlayer = document.getElementById('audio-player');
redirectWhenFrame();
loadTracks(); loadTracks();
loadPlaylists(); loadPlaylists();
setupEventListeners(); setupEventListeners();
@ -26,8 +27,28 @@ document.addEventListener('DOMContentLoaded', function() {
// Reduce buffer to minimize delay // Reduce buffer to minimize delay
liveAudio.preload = 'none'; liveAudio.preload = 'none';
} }
// Restore user quality preference
const selector = document.getElementById('live-stream-quality');
const streamQuality = localStorage.getItem('stream-quality') || 'aac';
if (selector && selector.value !== streamQuality) {
selector.value = streamQuality;
selector.dispatchEvent(new Event('change'));
}
}); });
function redirectWhenFrame () {
const path = window.location.pathname;
const isFramesetPage = window.parent !== window.self;
const isContentFrame = path.includes('player-content');
if (isFramesetPage && !isContentFrame) {
window.location.href = '/asteroid/player-content';
}
if (!isFramesetPage && isContentFrame) {
window.location.href = '/asteroid/player';
}
}
function setupEventListeners() { function setupEventListeners() {
// Search // Search
document.getElementById('search-tracks').addEventListener('input', filterTracks); document.getElementById('search-tracks').addEventListener('input', filterTracks);

View File

@ -12,8 +12,8 @@
<div class="container"> <div class="container">
<h1>🎛️ ADMIN DASHBOARD</h1> <h1>🎛️ ADMIN DASHBOARD</h1>
<div class="nav"> <div class="nav">
<a href="/asteroid/content" target="content-frame">Home</a> <a href="/asteroid" target="content-frame">Home</a>
<a href="/asteroid/player-content" target="content-frame">Player</a> <a href="/asteroid/player" target="content-frame">Player</a>
<a href="/asteroid/profile" target="content-frame">Profile</a> <a href="/asteroid/profile" target="content-frame">Profile</a>
<a href="/asteroid/admin/users">👥 Users</a> <a href="/asteroid/admin/users">👥 Users</a>
<a href="/asteroid/logout" class="btn-logout">Logout</a> <a href="/asteroid/logout" class="btn-logout">Logout</a>

View File

@ -104,6 +104,13 @@
audioElement.addEventListener('error', function(e) { audioElement.addEventListener('error', function(e) {
console.error('Audio error:', e); console.error('Audio error:', e);
}); });
const selector = document.getElementById('stream-quality');
const streamQuality = localStorage.getItem('stream-quality') || 'aac';
if (selector && selector.value !== streamQuality) {
selector.value = streamQuality;
selector.dispatchEvent(new Event('change'));
}
}); });
// Stream quality configuration // Stream quality configuration
@ -131,6 +138,9 @@
const streamBaseUrl = document.getElementById('stream-base-url').value; const streamBaseUrl = document.getElementById('stream-base-url').value;
const config = getStreamConfig(streamBaseUrl, selector.value); const config = getStreamConfig(streamBaseUrl, selector.value);
// Save preference
localStorage.setItem('stream-quality', selector.value);
const audioElement = document.getElementById('persistent-audio'); const audioElement = document.getElementById('persistent-audio');
const sourceElement = document.getElementById('audio-source'); const sourceElement = document.getElementById('audio-source');

View File

@ -35,6 +35,7 @@
<div class="live-stream"> <div class="live-stream">
<h2 style="color: #00ff00;">🟢 LIVE STREAM</h2> <h2 style="color: #00ff00;">🟢 LIVE STREAM</h2>
<p><em>The live stream player is now in the persistent bar at the bottom of the page.</em></p> <p><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>Stream URL:</strong> <code id="stream-url" lquery="(text default-stream-url)"></code></p> <p><strong>Stream URL:</strong> <code id="stream-url" lquery="(text default-stream-url)"></code></p>
<p><strong>Format:</strong> <span id="stream-format" lquery="(text default-stream-encoding-desc)"></span></p> <p><strong>Format:</strong> <span id="stream-format" lquery="(text default-stream-encoding-desc)"></span></p>
<p><strong>Status:</strong> <span id="stream-status" style="color: #00ff00;">● BROADCASTING</span></p> <p><strong>Status:</strong> <span id="stream-status" style="color: #00ff00;">● BROADCASTING</span></p>

View File

@ -47,7 +47,6 @@
<!-- Stream Quality Selector --> <!-- Stream Quality Selector -->
<div class="live-stream-quality"> <div class="live-stream-quality">
<input type="hidden" id="stream-base-url" lquery="(val stream-base-url)">
<label for="stream-quality" ><strong>Quality:</strong></label> <label for="stream-quality" ><strong>Quality:</strong></label>
<select id="stream-quality" onchange="changeStreamQuality()"> <select id="stream-quality" onchange="changeStreamQuality()">
<option value="aac">AAC 96kbps (Recommended)</option> <option value="aac">AAC 96kbps (Recommended)</option>
@ -56,6 +55,7 @@
</select> </select>
</div> </div>
<input type="hidden" id="stream-base-url" lquery="(val stream-base-url)">
<p><strong>Stream URL:</strong> <code id="stream-url" lquery="(text default-stream-url)"></code></p> <p><strong>Stream URL:</strong> <code id="stream-url" lquery="(text default-stream-url)"></code></p>
<p><strong>Format:</strong> <span id="stream-format" lquery="(text default-stream-encoding-desc)"></span></p> <p><strong>Format:</strong> <span id="stream-format" lquery="(text default-stream-encoding-desc)"></span></p>
<p><strong>Status:</strong> <span id="stream-status" style="color: #00ff00;">● BROADCASTING</span></p> <p><strong>Status:</strong> <span id="stream-status" style="color: #00ff00;">● BROADCASTING</span></p>

View File

@ -12,8 +12,8 @@
<div class="container"> <div class="container">
<h1>👤 USER PROFILE</h1> <h1>👤 USER PROFILE</h1>
<div class="nav"> <div class="nav">
<a href="/asteroid/content" target="content-frame">Home</a> <a href="/asteroid" target="content-frame">Home</a>
<a href="/asteroid/player-content" target="content-frame">Player</a> <a href="/asteroid/player" target="content-frame">Player</a>
<a href="/asteroid/admin" target="content-frame" data-show-if-admin>Admin</a> <a href="/asteroid/admin" target="content-frame" data-show-if-admin>Admin</a>
<a href="/asteroid/logout" class="btn-logout">Logout</a> <a href="/asteroid/logout" class="btn-logout">Logout</a>
</div> </div>

View File

@ -11,7 +11,7 @@
<div class="container"> <div class="container">
<h1>👥 USER MANAGEMENT</h1> <h1>👥 USER MANAGEMENT</h1>
<div class="nav"> <div class="nav">
<a href="/asteroid/content" target="content-frame">Home</a> <a href="/asteroid" target="content-frame">Home</a>
<a href="/asteroid/admin" target="content-frame">Admin</a> <a href="/asteroid/admin" target="content-frame">Admin</a>
<a href="/asteroid/logout" class="btn-logout">Logout</a> <a href="/asteroid/logout" class="btn-logout">Logout</a>
</div> </div>