const queryInput = document.getElementById('query'); const searchBtn = document.getElementById('searchBtn'); const searchTypeSelect = document.getElementById('searchType'); const results = document.getElementById('results'); const statusEl = document.getElementById('status'); const dialog = document.getElementById('albumDialog'); const dialogTitle = document.getElementById('dialogTitle'); const dialogArtist = document.getElementById('dialogArtist'); const trackList = document.getElementById('trackList'); const sendBtn = document.getElementById('sendBtn'); const cleanupToggle = document.getElementById('cleanupToggle'); const updateLibraryBtn = document.getElementById('updateLibraryBtn'); const updateFrontendBtn = document.getElementById('updateFrontendBtn'); let selectedAlbum = null; const CLEANUP_KEY = 'cleanupExtras'; cleanupToggle.checked = localStorage.getItem(CLEANUP_KEY) === 'true'; cleanupToggle.addEventListener('change', () => { localStorage.setItem(CLEANUP_KEY, String(cleanupToggle.checked)); }); function setStatus(text, isError = false) { statusEl.textContent = text; statusEl.style.color = isError ? 'var(--danger)' : 'var(--text)'; } function formatDuration(ms) { const totalSec = Math.floor(ms / 1000); const min = Math.floor(totalSec / 60); const sec = totalSec % 60; return `${min}:${String(sec).padStart(2, '0')}`; } async function fetchJson(url, options = {}) { const response = await fetch(url, options); const data = await response.json(); if (!response.ok) { throw new Error(data.error || `HTTP ${response.status}`); } return data; } function createAlbumCard(album, preselectedTrackNames = []) { const card = document.createElement('article'); card.className = 'album-card'; card.innerHTML = ` ${album.image ? `${album.name}` : '
'}

${album.name}

${album.artist}

${album.totalTracks} Tracks - ${album.releaseDate || 'unbekannt'}

`; card.querySelector('button').addEventListener('click', () => openAlbumDialog(album.id, preselectedTrackNames)); return card; } function createTrackCard(track) { const card = document.createElement('article'); card.className = 'album-card'; card.innerHTML = ` ${track.image ? `${track.albumName}` : '
'}

${track.trackName}

${track.artist}

Album: ${track.albumName}

`; card.querySelector('button').addEventListener('click', () => openAlbumDialog(track.albumId, [track.trackName])); return card; } function createArtistCard(artist) { const card = document.createElement('article'); card.className = 'album-card'; card.innerHTML = ` ${artist.image ? `${artist.name}` : '
'}

${artist.name}

${artist.followers || 0} Follower

Artist

`; card.querySelector('button').addEventListener('click', () => loadArtistAlbums(artist.id, artist.name)); return card; } function renderItems(type, items) { results.innerHTML = ''; if (!items.length) { results.innerHTML = '

Keine Treffer gefunden.

'; return; } for (const item of items) { let card; if (type === 'track') card = createTrackCard(item); if (type === 'artist') card = createArtistCard(item); if (type === 'album') card = createAlbumCard(item); if (card) results.appendChild(card); } } async function openAlbumDialog(albumId, preselectedTrackNames = []) { setStatus('Lade Albumdetails...'); try { const album = await fetchJson(`/api/spotify/album/${albumId}`); const preselected = new Set(preselectedTrackNames || []); selectedAlbum = album; dialogTitle.textContent = album.name; dialogArtist.textContent = `Artist: ${album.artist}`; trackList.innerHTML = ''; for (const track of album.tracks) { const row = document.createElement('label'); row.className = 'track-row'; const isChecked = preselected.size > 0 ? preselected.has(track.name) : true; row.innerHTML = ` ${track.trackNumber}. ${track.name} (${formatDuration(track.durationMs)}) `; trackList.appendChild(row); } dialog.showModal(); setStatus('Album bereit. Tracks auswaehlen und senden.'); } catch (err) { setStatus(err.message, true); } } async function loadArtistAlbums(artistId, artistName) { setStatus(`Lade Alben von ${artistName}...`); try { const data = await fetchJson(`/api/spotify/artist/${artistId}/albums`); renderItems('album', data.albums || []); setStatus(`${data.albums.length} Alben von ${artistName} gefunden.`); } catch (err) { setStatus(err.message, true); } } async function searchSpotify() { const q = queryInput.value.trim(); const type = searchTypeSelect.value; if (!q) { setStatus('Bitte Suchbegriff eingeben.', true); return; } setStatus('Suche in Spotify...'); results.innerHTML = ''; try { const data = await fetchJson(`/api/spotify/search?q=${encodeURIComponent(q)}&type=${encodeURIComponent(type)}`); const items = data.items || []; renderItems(type, items); setStatus(`${items.length} Treffer gefunden (${type}).`); } catch (err) { setStatus(err.message, true); } } async function sendToLidarr(event) { event.preventDefault(); if (!selectedAlbum) { setStatus('Kein Album ausgewaehlt.', true); return; } const checked = Array.from(trackList.querySelectorAll('input[type="checkbox"]:checked')); const selectedTrackNames = checked.map((input) => input.dataset.trackName); if (!selectedTrackNames.length) { setStatus('Bitte mindestens einen Track auswaehlen.', true); return; } setStatus('Sende Album an Lidarr...'); sendBtn.disabled = true; try { const data = await fetchJson('/api/lidarr/send-album', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ albumName: selectedAlbum.name, artistName: selectedAlbum.artist, selectedTrackNames, cleanupExtras: cleanupToggle.checked }) }); const cleanupMsg = data.cleanup?.attempted ? ` Cleanup: ${data.cleanup.deleted} Dateien geloescht.` : ''; setStatus(`Album erfolgreich an Lidarr uebergeben (ID ${data.albumId}).${cleanupMsg}`); dialog.close(); } catch (err) { setStatus(`Fehler: ${err.message}`, true); } finally { sendBtn.disabled = false; } } async function updateLibrary() { setStatus('Starte Lidarr Bibliothek-Update...'); updateLibraryBtn.disabled = true; try { const data = await fetchJson('/api/lidarr/update-library', { method: 'POST', headers: { 'Content-Type': 'application/json' } }); setStatus(`Bibliothek-Update gestartet (${data.commandNames.join(', ')}).`); } catch (err) { setStatus(`Fehler beim Bibliothek-Update: ${err.message}`, true); } finally { updateLibraryBtn.disabled = false; } } async function updateFrontendFromGit() { setStatus('Hole aktuelle Git-Daten...'); updateFrontendBtn.disabled = true; try { const data = await fetchJson('/api/system/update-frontend', { method: 'POST', headers: { 'Content-Type': 'application/json' } }); if (data.updated) { setStatus(`Frontend aktualisiert (${data.before} -> ${data.after}). Seite wird neu geladen...`); setTimeout(() => window.location.reload(), 1200); } else { setStatus(`Bereits aktuell (${data.after}).`); } } catch (err) { setStatus(`Frontend-Update fehlgeschlagen: ${err.message}`, true); } finally { updateFrontendBtn.disabled = false; } } searchBtn.addEventListener('click', searchSpotify); queryInput.addEventListener('keydown', (event) => { if (event.key === 'Enter') { searchSpotify(); } }); sendBtn.addEventListener('click', sendToLidarr); updateLibraryBtn.addEventListener('click', updateLibrary); updateFrontendBtn.addEventListener('click', updateFrontendFromGit);