This commit is contained in:
2025-12-30 19:09:50 +01:00
parent b2f1b48d06
commit eec1cff25f
5 changed files with 1541 additions and 16 deletions

View File

@@ -26,6 +26,7 @@ const castOverlay = document.getElementById('cast-overlay');
const closeOverlayBtn = document.getElementById('close-overlay');
const deviceListEl = document.getElementById('device-list');
const logoTextEl = document.querySelector('.station-logo-text');
const logoImgEl = document.getElementById('station-logo-img');
// Init
async function init() {
@@ -37,7 +38,30 @@ async function init() {
async function loadStations() {
try {
const resp = await fetch('stations.json');
stations = await resp.json();
const raw = await resp.json();
// Normalize station objects so the rest of the app can rely on `name` and `url`.
stations = raw
.map((s) => {
// If already in the old format, keep as-is
if (s.name && s.url) return s;
const name = s.title || s.id || s.name || 'Unknown';
// Prefer liveAudio, fall back to liveVideo or any common fields
const url = s.liveAudio || s.liveVideo || s.liveStream || s.url || '';
return {
id: s.id || name,
name,
url,
logo: s.logo || s.poster || '',
enabled: typeof s.enabled === 'boolean' ? s.enabled : true,
raw: s,
};
})
// Filter out disabled stations and those without a stream URL
.filter((s) => s.enabled !== false && s.url && s.url.length > 0);
if (stations.length > 0) {
currentIndex = 0;
loadStation(currentIndex);
@@ -72,8 +96,7 @@ function setupEventListeners() {
// Menu button - explicit functionality or placeholder?
// For now just log or maybe show about
document.getElementById('menu-btn').addEventListener('click', () => {
// Future: Settings menu
console.log('Menu clicked');
openStationsOverlay();
});
// Hotkeys?
@@ -88,11 +111,22 @@ function loadStation(index) {
// Update Logo Text (First letter or number)
// Simple heuristic: if name has a number, use it, else first letter
const numberMatch = station.name.match(/\d+/);
if (numberMatch) {
logoTextEl.textContent = numberMatch[0];
// If station has a logo URL, show the image; otherwise show the text fallback
if (station.logo && station.logo.length > 0) {
logoImgEl.src = station.logo;
logoImgEl.classList.remove('hidden');
logoTextEl.classList.add('hidden');
} else {
logoTextEl.textContent = station.name.charAt(0).toUpperCase();
// Fallback to single-letter/logo text
logoImgEl.src = '';
logoImgEl.classList.add('hidden');
const numberMatch = station.name.match(/\d+/);
if (numberMatch) {
logoTextEl.textContent = numberMatch[0];
} else {
logoTextEl.textContent = station.name.charAt(0).toUpperCase();
}
logoTextEl.classList.remove('hidden');
}
}
@@ -280,3 +314,42 @@ async function selectCastDevice(deviceName) {
}
window.addEventListener('DOMContentLoaded', init);
// Open overlay and show list of stations (used by menu/hamburger)
function openStationsOverlay() {
castOverlay.classList.remove('hidden');
castOverlay.setAttribute('aria-hidden', 'false');
deviceListEl.innerHTML = '<li class="device"><div class="device-main">Loading...</div><div class="device-sub">Preparing stations</div></li>';
// If stations not loaded yet, show message
if (!stations || stations.length === 0) {
deviceListEl.innerHTML = '<li class="device"><div class="device-main">No stations found</div><div class="device-sub">Check your stations.json</div></li>';
return;
}
deviceListEl.innerHTML = '';
stations.forEach((s, idx) => {
const li = document.createElement('li');
li.className = 'device' + (currentIndex === idx ? ' selected' : '');
const subtitle = (s.raw && s.raw.www) ? s.raw.www : (s.id || '');
li.innerHTML = `<div class="device-main">${s.name}</div><div class="device-sub">${subtitle}</div>`;
li.onclick = async () => {
// Always switch to local playback when selecting from stations menu
currentMode = 'local';
currentCastDevice = null;
castBtn.style.color = 'var(--text-main)';
// Select and play
currentIndex = idx;
loadStation(currentIndex);
closeCastOverlay();
try {
await play();
} catch (e) {
console.error('Failed to play station from menu', e);
}
};
deviceListEl.appendChild(li);
});
}