import { S } from './state.js'; import { esc, toast } from './utils.js'; import { itunesSearch } from './api.js'; // Circular imports — safe (only used inside function bodies) import { makeGlobalTrackItem } from './components.js'; import { openDlModal } from './ui.js'; // ─── SPOTIFY IMPORTER PAGE ─── export function loadSpotifyImporter() { if (!document.getElementById('spotify-link-input')?.value) { const hint = document.getElementById('spotify-importer-hint'); const preview = document.getElementById('spotify-preview'); if (hint) hint.style.display = ''; if (preview) preview.style.display = 'none'; } } export function clearSpotifyPreview() { if (!document.getElementById('spotify-link-input')?.value) { const hint = document.getElementById('spotify-importer-hint'); const preview = document.getElementById('spotify-preview'); if (hint) hint.style.display = ''; if (preview) preview.style.display = 'none'; } } export async function resolveSpotifyLink() { const url = document.getElementById('spotify-link-input')?.value.trim(); if (!url) return; if (!S.cfg.backendUrl) { toast('⚠ Backend URL in Einstellungen konfigurieren'); return; } const hint = document.getElementById('spotify-importer-hint'); const preview = document.getElementById('spotify-preview'); const inner = document.getElementById('spotify-preview-inner'); const section = document.getElementById('spotify-tracks-section'); if (hint) hint.style.display = 'none'; preview.style.display = ''; section.style.display = 'none'; inner.innerHTML = '
'; try { const res = await fetch(`${S.cfg.backendUrl}/api/spotify/resolve`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ url }), }); if (!res.ok) { const err = await res.json().catch(() => ({})); throw new Error(err.error || `HTTP ${res.status}`); } const data = await res.json(); window._spotifyResolved = data; _renderSpotifyPreview(data); } catch(e) { inner.innerHTML = `
${esc(e.message)}
`; } } export function spotifyDlAll(cbId, nameId) { const data = window._spotifyResolved; if (!data) return; window.executeSpotifyDownload({ type: 'playlist', title: document.getElementById(nameId)?.value || data.name, artist: data.artist, artworkUrl: data.artworkUrl, tracks: data.tracks, createPlaylist: document.getElementById(cbId)?.checked || false, playlistName: document.getElementById(nameId)?.value || data.name, }); } function _renderSpotifyPreview(data) { const inner = document.getElementById('spotify-preview-inner'); const section = document.getElementById('spotify-tracks-section'); const list = document.getElementById('spotify-tracks-list'); const titleEl = document.getElementById('spotify-tracks-title'); const typeLabel = { track: 'Spotify Track', album: 'Spotify Album', playlist: 'Spotify Playlist' }[data.type] || 'Spotify'; const art = (data.artworkUrl || '').replace(/\/\d+x\d+bb/, '/300x300bb').replace('100x100', '300x300'); const count = data.tracks?.length || 0; const uid = Date.now(); const plCbId = `sp-cb-${uid}`, plNmId = `sp-pln-${uid}`; let actionsHtml = ''; if (data.type === 'track' && count > 0) { actionsHtml = `
${data.tracks[0]?.collectionName ? `` : ''}
`; } else if (data.type === 'album') { actionsHtml = ` `; } else if (data.type === 'playlist') { actionsHtml = `
`; } const artSize = data.type === 'playlist' ? '80px' : '110px'; const artRadius = data.type === 'playlist' ? '8px' : '10px'; const artHtml = art ? `` : ''; inner.innerHTML = `
${artHtml}
🎵
${typeLabel}
${esc(data.name)}
${esc(data.artist)}${count > 1 ? ` · ${count} Tracks` : ''}
${actionsHtml}
`; titleEl.textContent = `${count} Tracks`; list.innerHTML = ''; (data.tracks || []).forEach((t, i) => list.appendChild(makeGlobalTrackItem(t, i, data.tracks))); section.style.display = ''; // Lazy-load correct per-track artwork via iTunes in the background if ((data.type === 'playlist' || data.type === 'album') && count > 0 && S.cfg.backendUrl) { _loadSpotifyTrackArtwork(data.tracks, list); } } async function _loadSpotifyTrackArtwork(tracks, listEl) { const artEls = listEl.querySelectorAll('.gtrack-art'); // Show any Spotify artwork that's already present immediately tracks.forEach((t, i) => { const url = t.artworkUrl100 || t.artworkUrl60 || ''; if (!url) return; const el = artEls[i]; if (!el) return; _setArt(el, url); }); // Enrich each track with iTunes data — same call as global search const CONC = 4; for (let i = 0; i < tracks.length; i += CONC) { const batch = tracks.slice(i, i + CONC); await Promise.all(batch.map(async (t, bi) => { const idx = i + bi; try { const firstArtist = (t.artistName || '').split(/\s*[,/•]\s*/)[0].trim(); const results = await itunesSearch(`${t.trackName} ${firstArtist}`, 'musicTrack', 1); const match = results[0]; if (!match) return; // Enrich track object so downloads get correct metadata if (match.artworkUrl100) t.artworkUrl100 = match.artworkUrl100; if (match.collectionName) t.collectionName = match.collectionName; if (match.releaseDate) t.releaseDate = match.releaseDate; if (match.trackNumber) t.trackNumber = match.trackNumber; if (match.trackExplicitness) t.trackExplicitness = match.trackExplicitness; if (window._spotifyResolved?.tracks?.[idx]) Object.assign(window._spotifyResolved.tracks[idx], t); const url = match.artworkUrl100 || match.artworkUrl60 || ''; if (url) _setArt(artEls[idx], url); } catch { /* non-critical */ } })); } } function _setArt(el, url) { if (!el) return; const img = document.createElement('img'); img.loading = 'lazy'; img.onerror = () => { el.innerHTML = '
🎵
'; }; img.src = url; el.innerHTML = ''; el.appendChild(img); }