Update index.js
Code angepasst und auf verschiedene Dateien aufgeteilt.
This commit is contained in:
parent
1f565b62ca
commit
3d4f6bb803
1 changed files with 166 additions and 339 deletions
505
index.js
505
index.js
|
|
@ -1,214 +1,93 @@
|
||||||
// Public Domain
|
// Public Domain - Hauptlogik des Radioplayers (Schaltzentrale)
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
// --- UI ELEMENTE ---
|
||||||
const presetContainer = document.getElementById('preset-container');
|
const presetContainer = document.getElementById('preset-container');
|
||||||
const stopButton = document.querySelector('.stop-button');
|
const stopButton = document.querySelector('.stop-button');
|
||||||
const currentStationName = document.getElementById('current-station-name');
|
const currentStationName = document.getElementById('current-station-name');
|
||||||
const presetModal = document.getElementById('preset-modal');
|
const presetModal = document.getElementById('preset-modal');
|
||||||
const tableBody = document.querySelector('#sortable-tbody');
|
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 currentSound = null;
|
||||||
let presets = [];
|
|
||||||
let sleepTimerTimeout = null;
|
|
||||||
let sleepTimerEndTime = null;
|
|
||||||
|
|
||||||
// Benutzerdefiniertes Modal für Alerts und Bestätigungen
|
// --- INITIALISIERUNG ---
|
||||||
function showAlert(title, message) {
|
if (window.UI) window.UI.initializeFooter();
|
||||||
const modal = document.getElementById("custom-modal");
|
|
||||||
const titleElement = document.getElementById("custom-modal-title");
|
|
||||||
const messageElement = document.getElementById("custom-modal-message");
|
|
||||||
const cancelButton = document.getElementById("custom-modal-cancel");
|
|
||||||
|
|
||||||
titleElement.textContent = title;
|
|
||||||
messageElement.textContent = message;
|
|
||||||
cancelButton.classList.add("hidden");
|
|
||||||
|
|
||||||
modal.classList.remove("hidden");
|
|
||||||
|
|
||||||
document.getElementById("custom-modal-ok").onclick = function() {
|
|
||||||
modal.classList.add("hidden");
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function showConfirm(title, message, callback) {
|
|
||||||
const modal = document.getElementById("custom-modal");
|
|
||||||
const titleElement = document.getElementById("custom-modal-title");
|
|
||||||
const messageElement = document.getElementById("custom-modal-message");
|
|
||||||
const cancelButton = document.getElementById("custom-modal-cancel");
|
|
||||||
|
|
||||||
titleElement.textContent = title;
|
|
||||||
messageElement.textContent = message;
|
|
||||||
cancelButton.classList.remove("hidden");
|
|
||||||
|
|
||||||
modal.classList.remove("hidden");
|
|
||||||
|
|
||||||
document.getElementById("custom-modal-ok").onclick = function() {
|
|
||||||
modal.classList.add("hidden");
|
|
||||||
callback(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
cancelButton.onclick = function() {
|
|
||||||
modal.classList.add("hidden");
|
|
||||||
callback(false);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
initializeFooter();
|
|
||||||
loadPresets();
|
|
||||||
initializeEventListeners();
|
initializeEventListeners();
|
||||||
|
|
||||||
function initializeFooter() {
|
// Manager verknüpfen
|
||||||
const currentYear = new Date().getFullYear();
|
if (window.SleepTimer) {
|
||||||
document.getElementById('current-year').textContent = currentYear;
|
window.SleepTimer.init(() => stopPlayback());
|
||||||
document.getElementById('server-name').textContent = window.location.hostname;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateTitle(stationName) {
|
if (window.PresetManager) {
|
||||||
document.title = stationName ? `${stationName} - Radioplayer mit Presets` : "Radioplayer mit Presets";
|
window.PresetManager.load((loadedPresets) => {
|
||||||
|
displayPresets(loadedPresets);
|
||||||
|
initSortable();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- EVENT LISTENER ---
|
||||||
function initializeEventListeners() {
|
function initializeEventListeners() {
|
||||||
stopButton.addEventListener('click', stopPlayback);
|
// Stop Button
|
||||||
editPresetsButton.addEventListener('click', openModal);
|
if (stopButton) {
|
||||||
}
|
stopButton.onclick = stopPlayback;
|
||||||
|
}
|
||||||
async function loadPresets() {
|
|
||||||
presets = JSON.parse(localStorage.getItem('presets')) || [];
|
// Preset Modal öffnen
|
||||||
if (presets.length === 0) {
|
const editBtn = document.getElementById('edit-presets-button');
|
||||||
try {
|
if (editBtn) {
|
||||||
const response = await fetch('webradio_presets.json');
|
editBtn.onclick = openPresetModal;
|
||||||
if (!response.ok) throw new Error('Datei konnte nicht geladen werden');
|
|
||||||
presets = await response.json();
|
|
||||||
localStorage.setItem('presets', JSON.stringify(presets));
|
|
||||||
} catch (error) {
|
|
||||||
showAlert("Fehler", 'Fehler beim Laden der Presets: ' + error.message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
displayPresets(presets);
|
|
||||||
renderPresets();
|
|
||||||
initSortable();
|
|
||||||
}
|
|
||||||
|
|
||||||
function displayPresets(presets) {
|
// Sleep Timer Modal öffnen
|
||||||
presetContainer.innerHTML = '';
|
const sleepBtn = document.getElementById('open-sleep-timer-modal');
|
||||||
presets.forEach(preset => addNewPreset(preset.name, preset.url));
|
if (sleepBtn) {
|
||||||
}
|
sleepBtn.onclick = openSleepModal;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sleep Timer Modal schließen (X-Button)
|
||||||
|
const closeSleepBtn = document.getElementById('close-sleep-timer-modal');
|
||||||
|
if (closeSleepBtn) {
|
||||||
|
closeSleepBtn.onclick = () => {
|
||||||
|
const sm = document.getElementById('sleep-timer-modal');
|
||||||
|
sm.classList.add('hidden');
|
||||||
|
sm.style.display = 'none';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function addNewPreset(name, url) {
|
// Zeit-Optionen im Sleep Timer
|
||||||
const presetItem = document.createElement('div');
|
document.querySelectorAll('.time-option').forEach(btn => {
|
||||||
presetItem.classList.add('preset-item');
|
btn.onclick = () => {
|
||||||
presetItem.dataset.streamUrl = url;
|
const minutes = parseInt(btn.getAttribute('data-time')) / 60000;
|
||||||
presetItem.innerHTML = `<h3>${name}</h3>`;
|
window.SleepTimer.start(minutes);
|
||||||
presetItem.addEventListener('click', () => playStream(url, presetItem));
|
const sm = document.getElementById('sleep-timer-modal');
|
||||||
presetContainer.appendChild(presetItem);
|
sm.classList.add('hidden');
|
||||||
}
|
sm.style.display = 'none';
|
||||||
|
};
|
||||||
function renderPresets() {
|
|
||||||
tableBody.innerHTML = '';
|
|
||||||
presets.forEach((preset, index) => {
|
|
||||||
const row = document.createElement('tr');
|
|
||||||
row.setAttribute('data-index', index);
|
|
||||||
row.innerHTML = `
|
|
||||||
<td class="drag-handle">☰</td>
|
|
||||||
<td><input type="text" value="${preset.name}" onchange="changeName(${index}, this.value)"></td>
|
|
||||||
<td><input type="text" value="${preset.url}" onchange="changeURL(${index}, this.value)"></td>
|
|
||||||
<td><button class="delete-btn" onclick="deletePreset(${index})"><i class="fas fa-trash-alt" style="color:grey"></i></button></td>
|
|
||||||
`;
|
|
||||||
tableBody.appendChild(row);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
openSleepTimerModalButton.addEventListener('click', function () {
|
// --- PLAYER LOGIK ---
|
||||||
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 = `<i class="fas fa-hourglass-half"></i> ${remainingTime} Minuten`;
|
|
||||||
if (remainingTime > 0) {
|
|
||||||
setTimeout(updateSleepTimerButton, 60000);
|
|
||||||
} 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) {
|
function playStream(url, item) {
|
||||||
stopPlayback();
|
stopPlayback();
|
||||||
|
if (window.MetaHandler) window.MetaHandler.stop();
|
||||||
|
|
||||||
currentSound = new Howl({
|
currentSound = new Howl({
|
||||||
src: [url],
|
src: [url],
|
||||||
html5: true,
|
html5: true,
|
||||||
volume: 0.5,
|
volume: 0.5,
|
||||||
onplay: () => {
|
onplay: () => {
|
||||||
stopButton.classList.remove('hidden');
|
stopButton.classList.remove('hidden');
|
||||||
updateTitle(item.querySelector('h3').textContent);
|
const stationName = item.querySelector('h3').textContent;
|
||||||
currentStationName.textContent = 'Es läuft: ' + item.querySelector('h3').textContent;
|
if (window.UI) window.UI.updateDocumentTitle(stationName);
|
||||||
|
currentStationName.textContent = 'Es läuft: ' + stationName;
|
||||||
|
if (window.MetaHandler) window.MetaHandler.start(url);
|
||||||
},
|
},
|
||||||
onstop: () => {
|
onstop: () => {
|
||||||
stopButton.classList.add('hidden');
|
stopButton.classList.add('hidden');
|
||||||
currentStationName.textContent = 'Wähle einen Sender aus';
|
currentStationName.textContent = 'Wähle einen Sender aus';
|
||||||
updateTitle('');
|
if (window.UI) window.UI.updateDocumentTitle('');
|
||||||
|
if (window.MetaHandler) window.MetaHandler.stop();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
currentSound.play();
|
currentSound.play();
|
||||||
|
|
@ -221,185 +100,133 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function openModal() {
|
// --- PRESET UI ---
|
||||||
|
function displayPresets(presets) {
|
||||||
|
presetContainer.innerHTML = '';
|
||||||
|
presets.forEach(preset => {
|
||||||
|
const presetItem = document.createElement('div');
|
||||||
|
presetItem.classList.add('preset-item');
|
||||||
|
presetItem.innerHTML = `<h3>${preset.name}</h3>`;
|
||||||
|
presetItem.onclick = () => playStream(preset.url, presetItem);
|
||||||
|
presetContainer.appendChild(presetItem);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderPresetsTable() {
|
||||||
|
if (!tableBody) return;
|
||||||
|
tableBody.innerHTML = '';
|
||||||
|
window.PresetManager.presets.forEach((preset, index) => {
|
||||||
|
const row = document.createElement('tr');
|
||||||
|
row.setAttribute('data-index', index);
|
||||||
|
row.innerHTML = `
|
||||||
|
<td class="drag-handle">☰</td>
|
||||||
|
<td><input type="text" value="${preset.name}" onchange="changeName(${index}, this.value)"></td>
|
||||||
|
<td><input type="text" value="${preset.url}" onchange="changeURL(${index}, this.value)"></td>
|
||||||
|
<td><button class="delete-btn" onclick="deletePreset(${index})"><i class="fas fa-trash-alt"></i></button></td>
|
||||||
|
`;
|
||||||
|
tableBody.appendChild(row);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- MODAL WRAPPER ---
|
||||||
|
function openPresetModal() {
|
||||||
|
if (!presetModal) return;
|
||||||
presetModal.classList.remove('hidden');
|
presetModal.classList.remove('hidden');
|
||||||
presetModal.style.display = 'block';
|
presetModal.style.display = 'block';
|
||||||
renderPresets();
|
renderPresetsTable();
|
||||||
generateExportCode();
|
if (window.PresetManager) window.PresetManager.generateExportCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
window.closeModal = function() {
|
function openSleepModal() {
|
||||||
presetModal.classList.add('hidden');
|
const sm = document.getElementById('sleep-timer-modal');
|
||||||
presetModal.style.display = 'none';
|
if (sm) {
|
||||||
|
sm.classList.remove('hidden');
|
||||||
|
sm.style.display = 'block';
|
||||||
|
if (window.SleepTimer) window.SleepTimer.updateButtonUI();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addPreset = function() {
|
// --- GLOBALE HELFER (für HTML onclicks & Manager) ---
|
||||||
presets.push({ name: 'Neuer Sender', url: '' });
|
window.closeModal = () => {
|
||||||
renderPresets();
|
if (presetModal) {
|
||||||
generateExportCode();
|
presetModal.classList.add('hidden');
|
||||||
}
|
presetModal.style.display = 'none';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addPreset = () => {
|
||||||
|
window.PresetManager.presets.push({ name: 'Neuer Sender', url: '' });
|
||||||
|
renderPresetsTable();
|
||||||
|
};
|
||||||
|
|
||||||
window.changeName = function(index, newName) {
|
window.deletePreset = (index) => {
|
||||||
presets[index].name = newName;
|
window.UI.showConfirm("Löschen", "Sender wirklich entfernen?", (conf) => {
|
||||||
generateExportCode();
|
if (conf) {
|
||||||
}
|
window.PresetManager.presets.splice(index, 1);
|
||||||
|
renderPresetsTable();
|
||||||
window.changeURL = function(index, newURL) {
|
window.PresetManager.generateExportCode();
|
||||||
presets[index].url = newURL;
|
|
||||||
generateExportCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
window.deletePreset = function(index) {
|
|
||||||
showConfirm("Löschen", "Möchtest du diesen Sender wirklich löschen?", function(confirmed) {
|
|
||||||
if (confirmed) {
|
|
||||||
presets.splice(index, 1);
|
|
||||||
renderPresets();
|
|
||||||
generateExportCode();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
window.saveAndClose = function() {
|
window.saveAndClose = () => {
|
||||||
savePresets();
|
window.PresetManager.save();
|
||||||
closeModal();
|
|
||||||
location.reload();
|
location.reload();
|
||||||
}
|
};
|
||||||
|
|
||||||
function savePresets() {
|
window.changeName = (i, v) => {
|
||||||
localStorage.setItem('presets', JSON.stringify(presets));
|
window.PresetManager.presets[i].name = v;
|
||||||
}
|
window.PresetManager.generateExportCode();
|
||||||
|
};
|
||||||
|
|
||||||
|
window.changeURL = (i, v) => {
|
||||||
|
window.PresetManager.presets[i].url = v;
|
||||||
|
window.PresetManager.generateExportCode();
|
||||||
|
};
|
||||||
|
|
||||||
|
window.exportPreset = () => window.PresetManager.exportToFile();
|
||||||
|
|
||||||
|
window.importPreset = () => {
|
||||||
|
window.UI.showConfirm("Vorsicht", "Alle Sender werden überschrieben!", (conf) => {
|
||||||
|
if (conf) {
|
||||||
|
const input = document.createElement('input');
|
||||||
|
input.type = 'file';
|
||||||
|
input.accept = 'application/json';
|
||||||
|
input.onchange = e => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = ev => {
|
||||||
|
try {
|
||||||
|
window.PresetManager.presets = JSON.parse(ev.target.result);
|
||||||
|
window.PresetManager.save();
|
||||||
|
location.reload();
|
||||||
|
} catch (err) { window.UI.showAlert("Fehler", "Ungültige Datei."); }
|
||||||
|
};
|
||||||
|
reader.readAsText(e.target.files[0]);
|
||||||
|
};
|
||||||
|
input.click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
window.importFromString = () => {
|
||||||
|
const field = document.getElementById('import-string-field');
|
||||||
|
const code = field ? field.value.trim() : "";
|
||||||
|
if (!code) return window.UI.showAlert("Fehler", "Bitte Code einfügen.");
|
||||||
|
window.UI.showConfirm("Vorsicht", "Alle Sender werden überschrieben!", (conf) => {
|
||||||
|
if (conf) window.PresetManager.importFromString(code);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
function initSortable() {
|
function initSortable() {
|
||||||
const sortable = new Sortable(tableBody, {
|
if (!tableBody) return;
|
||||||
|
new Sortable(tableBody, {
|
||||||
handle: '.drag-handle',
|
handle: '.drag-handle',
|
||||||
animation: 150,
|
animation: 150,
|
||||||
onEnd: function (evt) {
|
onEnd: () => {
|
||||||
const newOrder = Array.from(tableBody.children).map(row => parseInt(row.getAttribute('data-index')));
|
const newOrder = Array.from(tableBody.children).map(row => parseInt(row.getAttribute('data-index')));
|
||||||
presets = newOrder.map(index => presets[index]);
|
window.PresetManager.presets = newOrder.map(index => window.PresetManager.presets[index]);
|
||||||
generateExportCode();
|
window.PresetManager.generateExportCode();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
// --- NEUE EXPORT/IMPORT-FUNKTIONEN ---
|
|
||||||
window.generateExportCode = function() {
|
|
||||||
try {
|
|
||||||
const jsonString = JSON.stringify(presets);
|
|
||||||
const base64String = btoa(unescape(encodeURIComponent(jsonString)));
|
|
||||||
const exportField = document.getElementById('export-string-field');
|
|
||||||
if (exportField) exportField.value = base64String;
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Export-Fehler:", e);
|
|
||||||
showAlert("Fehler", "Export-Fehler: " + e.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export String mit Kompression (Gzip)
|
|
||||||
window.generateExportCode = async function() {
|
|
||||||
try {
|
|
||||||
if (!presets || presets.length === 0) return;
|
|
||||||
|
|
||||||
// 1. Struktur-Diät: Nur Werte speichern [["Name","URL"],["Name","URL"]]
|
|
||||||
const compactData = presets.map(p => [p.name, p.url]);
|
|
||||||
const jsonString = JSON.stringify(compactData);
|
|
||||||
|
|
||||||
// 2. Kompression mittels Gzip
|
|
||||||
const stream = new Blob([jsonString]).stream();
|
|
||||||
const compressedStream = stream.pipeThrough(new CompressionStream('gzip'));
|
|
||||||
const response = new Response(compressedStream);
|
|
||||||
const buffer = await response.arrayBuffer();
|
|
||||||
|
|
||||||
// 3. In Base64 umwandeln
|
|
||||||
const base64String = btoa(String.fromCharCode(...new Uint8Array(buffer)));
|
|
||||||
|
|
||||||
const exportField = document.getElementById('export-string-field');
|
|
||||||
if (exportField) exportField.value = base64String;
|
|
||||||
|
|
||||||
console.log("Kompression erfolgreich. Länge:", base64String.length);
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Export-Fehler:", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export nach JSON-Datei
|
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Import String mit Dekomprimierung
|
|
||||||
window.importFromString = function() {
|
|
||||||
const field = document.getElementById('import-string-field');
|
|
||||||
const code = field.value.trim();
|
|
||||||
if (!code) return showAlert("Fehler", "Bitte Code einfügen.");
|
|
||||||
|
|
||||||
showConfirm("Vorsicht", "Es werden alle bestehenden Sender überschrieben. Möchtest du fortfahren?", async function(confirmed) {
|
|
||||||
if (!confirmed) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 1. Base64 zurück in Bytes
|
|
||||||
const binary = atob(code);
|
|
||||||
const bytes = Uint8Array.from(binary, c => c.charCodeAt(0));
|
|
||||||
|
|
||||||
// 2. Dekomprimieren (Gzip)
|
|
||||||
const stream = new Blob([bytes]).stream();
|
|
||||||
const decompressedStream = stream.pipeThrough(new DecompressionStream('gzip'));
|
|
||||||
const response = new Response(decompressedStream);
|
|
||||||
const resultText = await response.text();
|
|
||||||
|
|
||||||
// 3. Struktur wiederherstellen (Array zu Objekten)
|
|
||||||
const importedArray = JSON.parse(resultText);
|
|
||||||
const restoredPresets = importedArray.map(item => ({
|
|
||||||
name: item[0],
|
|
||||||
url: item[1]
|
|
||||||
}));
|
|
||||||
|
|
||||||
if (Array.isArray(restoredPresets)) {
|
|
||||||
presets = restoredPresets;
|
|
||||||
savePresets();
|
|
||||||
location.reload();
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Import-Fehler:", e);
|
|
||||||
showAlert("Fehler", "Code ungültig oder beschädigt!");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Import aus JSON-Datei mit Warnung
|
|
||||||
window.importPreset = function() {
|
|
||||||
showConfirm("Vorsicht", "Es werden alle bestehenden Sender überschrieben. Möchtest du fortfahren?", function(confirmed) {
|
|
||||||
if (!confirmed) return;
|
|
||||||
|
|
||||||
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;
|
|
||||||
savePresets();
|
|
||||||
location.reload();
|
|
||||||
} catch (error) {
|
|
||||||
showAlert("Fehler", 'Fehler beim Importieren der Presets: Ungültige Datei.');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
reader.readAsText(file);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
input.click();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
});
|
|
||||||
Loading…
Reference in a new issue