diff --git a/index.html b/index.html new file mode 100644 index 0000000..10877b6 --- /dev/null +++ b/index.html @@ -0,0 +1,85 @@ + + + + + + + Radioplayer mit Presets + + + + + + + + + + + +
+
+ + +
+ + + + + + + + + diff --git a/script.js b/script.js new file mode 100644 index 0000000..8e9d501 --- /dev/null +++ b/script.js @@ -0,0 +1,271 @@ +// Public Domain + +document.addEventListener('DOMContentLoaded', function () { + const presetContainer = document.getElementById('preset-container'); + const stopButton = document.querySelector('.stop-button'); + const currentStationName = document.getElementById('current-station-name'); + const presetModal = document.getElementById('preset-modal'); + const tableBody = document.querySelector('#sortable-tbody'); + const editPresetsButton = document.getElementById('edit-presets-button'); + const openSleepTimerModalButton = document.getElementById('open-sleep-timer-modal'); + const sleepTimerModal = document.getElementById('sleep-timer-modal'); + const closeSleepTimerModalButton = document.getElementById('close-sleep-timer-modal'); + const offSleepTimerModalButton = document.getElementById('off-sleep-timer-modal'); + let currentSound = null; + let presets = []; + let sleepTimerTimeout = null; + let sleepTimerEndTime = null; + + initializeFooter(); + loadPresets(); + initializeEventListeners(); + + function initializeFooter() { + const currentYear = new Date().getFullYear(); + document.getElementById('current-year').textContent = currentYear; + document.getElementById('server-name').textContent = window.location.hostname; + } + + function updateTitle(stationName) { + document.title = stationName ? `${stationName} - Radioplayer mit Presets` : "Radioplayer mit Presets"; + } + + function initializeEventListeners() { + stopButton.addEventListener('click', stopPlayback); + editPresetsButton.addEventListener('click', openModal); + } + + async function loadPresets() { + presets = JSON.parse(localStorage.getItem('presets')) || []; + if (presets.length === 0) { + try { + const response = await fetch('webradio_presets.json'); + if (!response.ok) throw new Error('Datei konnte nicht geladen werden'); + presets = await response.json(); + localStorage.setItem('presets', JSON.stringify(presets)); + } catch (error) { + alert('Fehler beim Laden der Presets: ' + error.message); + } + } + displayPresets(presets); + renderPresets(); + initSortable(); + } + + function displayPresets(presets) { + presetContainer.innerHTML = ''; + presets.forEach(preset => addNewPreset(preset.name, preset.url)); + } + + function addNewPreset(name, url) { + const presetItem = document.createElement('div'); + presetItem.classList.add('preset-item'); + presetItem.dataset.streamUrl = url; + presetItem.innerHTML = `

${name}

`; + presetItem.addEventListener('click', () => playStream(url, presetItem)); + presetContainer.appendChild(presetItem); + } + + function renderPresets() { + tableBody.innerHTML = ''; + presets.forEach((preset, index) => { + const row = document.createElement('tr'); + row.setAttribute('data-index', index); + row.innerHTML = ` + ☰ + + + + `; + tableBody.appendChild(row); + }); + } + + openSleepTimerModalButton.addEventListener('click', function () { + sleepTimerModal.classList.remove('hidden'); + sleepTimerModal.style.display = 'block'; + updateOffButtonVisibility(); + }); + + closeSleepTimerModalButton.addEventListener('click', function () { + sleepTimerModal.classList.add('hidden'); + sleepTimerModal.style.display = 'none'; + }); + + offSleepTimerModalButton.addEventListener('click', function () { + clearSleepTimer(); + sleepTimerModal.classList.add('hidden'); + sleepTimerModal.style.display = 'none'; + }); + + document.querySelectorAll('.time-option').forEach(button => { + button.addEventListener('click', function () { + const timeInMilliseconds = button.getAttribute('data-time'); + startSleepTimer(parseInt(timeInMilliseconds)); + sleepTimerModal.classList.add('hidden'); + sleepTimerModal.style.display = 'none'; + }); + }); + + function startSleepTimer(duration) { + if (sleepTimerTimeout) { + clearTimeout(sleepTimerTimeout); + } + sleepTimerEndTime = Date.now() + duration; + sleepTimerTimeout = setTimeout(() => { + stopPlayback(); + updateSleepTimerButton(); + updateOffButtonVisibility(); + }, duration); + updateSleepTimerButton(); + updateOffButtonVisibility(); + } + + function clearSleepTimer() { + if (sleepTimerTimeout) { + clearTimeout(sleepTimerTimeout); + sleepTimerTimeout = null; + sleepTimerEndTime = null; + openSleepTimerModalButton.textContent = 'Sleep Timer'; + updateOffButtonVisibility(); + } + } + + function updateSleepTimerButton() { + if (sleepTimerEndTime) { + const remainingTime = Math.max(0, Math.ceil((sleepTimerEndTime - Date.now()) / 60000)); + openSleepTimerModalButton.innerHTML = ` ${remainingTime} Minuten`; + if (remainingTime > 0) { + setTimeout(updateSleepTimerButton, 60000); // Update every minute + } else { + openSleepTimerModalButton.textContent = 'Sleep Timer'; + sleepTimerEndTime = null; + updateOffButtonVisibility(); + } + } + } + + function updateOffButtonVisibility() { + if (sleepTimerTimeout) { + offSleepTimerModalButton.style.display = 'inline-block'; + } else { + offSleepTimerModalButton.style.display = 'none'; + } + } + + function playStream(url, item) { + stopPlayback(); + currentSound = new Howl({ + src: [url], + html5: true, + volume: 0.5, + onplay: () => { + stopButton.classList.remove('hidden'); + updateTitle(item.querySelector('h3').textContent); + currentStationName.textContent = 'Es läuft: ' + item.querySelector('h3').textContent; + }, + onstop: () => { + stopButton.classList.add('hidden'); + currentStationName.textContent = 'Wähle einen Sender aus'; + updateTitle(''); + } + }); + currentSound.play(); + } + + function stopPlayback() { + if (currentSound) { + currentSound.stop(); + currentSound = null; + } + } + + function openModal() { + presetModal.classList.remove('hidden'); + presetModal.style.display = 'block'; + renderPresets(); + } + + window.closeModal = function() { + presetModal.classList.add('hidden'); + presetModal.style.display = 'none'; + } + + window.addPreset = function() { + presets.push({ name: 'Neuer Sender', url: '' }); + renderPresets(); + } + + window.changeName = function(index, newName) { + presets[index].name = newName; + } + + window.changeURL = function(index, newURL) { + presets[index].url = newURL; + } + + window.deletePreset = function(index) { + presets.splice(index, 1); + renderPresets(); + } + + window.saveAndClose = function() { + savePresets(); + closeModal(); + location.reload(); + } + + function savePresets() { + localStorage.setItem('presets', JSON.stringify(presets)); + } + + function initSortable() { + const sortable = new Sortable(tableBody, { + handle: '.drag-handle', + animation: 150, + onEnd: function (evt) { + const newOrder = Array.from(tableBody.children).map(row => parseInt(row.getAttribute('data-index'))); + presets = newOrder.map(index => presets[index]); + } + }); + } + + window.exportPreset = function() { + const presetsJSON = JSON.stringify(presets, null, 2); + const blob = new Blob([presetsJSON], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = 'radio_presets.json'; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + } + + window.importPreset = function() { + const input = document.createElement('input'); + input.type = 'file'; + input.accept = 'application/json'; + input.onchange = function(event) { + const file = event.target.files[0]; + if (file) { + const reader = new FileReader(); + reader.onload = function(e) { + try { + const importedPresets = JSON.parse(e.target.result); + presets = importedPresets; + localStorage.setItem('presets', JSON.stringify(presets)); + renderPresets(); + displayPresets(presets); + location.reload(); + } catch (error) { + alert('Fehler beim Importieren der Presets: Ungültige Datei.'); + } + }; + reader.readAsText(file); + } + }; + input.click(); + } +}); diff --git a/style.css b/style.css new file mode 100644 index 0000000..c0e8674 --- /dev/null +++ b/style.css @@ -0,0 +1,184 @@ +/* Public Domain */ + +:root { + --primary-color: #ff9999; + --header-height: 120px; + --padding-top: calc(10px + var(--header-height)); + --box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +body { + font-family: Arial, sans-serif; + text-align: center; + margin: 0; + padding: 20px; + padding-top: var(--padding-top); +} + +.header { + background-color: #FFFFE3; + height: var(--header-height); + padding-bottom: 10px; + position: fixed; + top: 0; + left: 0; + width: 100%; + z-index: 1000; + box-shadow: var(--box-shadow); +} + +.titel { + display: flex; + align-items: center; + justify-content: center; + gap: 10px; +} + +.titel img { + height: 30px; + width: auto; +} + +.hidden { + display: none; +} + +.stop-button { + background-color: var(--primary-color); + color: #fff; + font-weight: bold; + border: none; + border-radius: 5px; + height: 30px; + padding: 4px 10px; + font-size: 18px; + margin-left: auto; + cursor: pointer; + transition: background-color 0.3s ease; + position: relative; + top: -2px; +} + +.stop-button:focus { + outline: none; + box-shadow: 0 0 5px rgba(255, 77, 77, 0.75); +} + +.current-station { + font-weight: bold; + font-size: 16px; + margin-bottom: 15px; +} + +#aktueller-sender { + background-color: #d2f8d2; + border-radius: 5px; + padding: 10px 20px; +} + +.preset-container { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + grid-gap: 20px; + margin: 30px 0; +} + +.preset-item { + background-color: #f1f1f1; + padding: 15px; + border-radius: 5px; + box-shadow: var(--box-shadow); + cursor: pointer; + transition: background-color 0.3s ease; + display: flex; + align-items: center; +} + +.preset-item h3 { + margin: 0; + flex: 1; +} + +.preset-item:hover { + background-color: #e6e6e6; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); +} + +.button-container { + display: flex; + justify-content: center; + gap: 10px; +} + +.footer { + display: flex; + align-items: center; + justify-content: center; + margin: 20px 0; + font-size: 12px; + color: #666; +} + +.footer a { + color: #666; + text-decoration: none; +} + +.footer a:hover { + color: #333; +} + +@media (max-width: 600px) { + .preset-container { + grid-template-columns: 1fr; + } +} + +/* ===== Modal Styles ===== */ +.modal { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-color: white; + padding: 20px; + border-radius: 10px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); + z-index: 1000; + max-width: 800px; + width: 90%; + max-height: 80vh; + overflow-y: auto; + display: none; +} + +.modal-content { + display: flex; + flex-direction: column; + gap: 20px; +} + +.modal-content table { + width: 100%; + border-collapse: collapse; +} + +.modal-content th, +.modal-content td { + border: 1px solid #ddd; + padding: 8px; +} + +.modal-content th { + background-color: #f2f2f2; +} + +.modal-content .drag-handle { + cursor: grab; + text-align: center; +} + +.modal-content input[type="text"] { + width: 100%; + box-sizing: border-box; +}