// ── DOM refs ────────────────────────────────────────────────────────────────── const dlUrl = document.querySelector("#dl-url"); const dlTitle = document.querySelector("#dl-title"); const dlBtn = document.querySelector("#dl-btn"); const dlStatus = document.querySelector("#dl-status"); const dlProgress = document.querySelector("#dl-progress"); const renameSelect = document.querySelector("#rename-select"); const renameInput = document.querySelector("#rename-input"); const renameBtn = document.querySelector("#rename-btn"); const renameStatus = document.querySelector("#rename-status"); const manageList = document.querySelector("#manage-list"); // ── Download ────────────────────────────────────────────────────────────────── dlBtn.addEventListener("click", async () => { const url = dlUrl.value.trim(); const title = dlTitle.value.trim(); if (!url) { dlStatus.textContent = "Please enter a URL."; return; } dlStatus.textContent = "Starting download\u2026"; dlBtn.disabled = true; dlProgress.hidden = false; dlProgress.removeAttribute("value"); try { const res = await fetch("/api/download", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ url, title }), }); const data = await res.json(); if (data.error) { dlStatus.textContent = `Error: ${data.error}`; dlBtn.disabled = false; dlProgress.hidden = true; return; } pollDownload(data.track_id); } catch { dlStatus.textContent = "Request failed."; dlBtn.disabled = false; dlProgress.hidden = true; } }); function pollDownload(trackId) { const iv = setInterval(async () => { try { const res = await fetch(`/api/download/status/${encodeURIComponent(trackId)}`); const data = await res.json(); if (data.status === "done") { clearInterval(iv); dlProgress.value = 100; dlStatus.textContent = "Download complete!"; dlBtn.disabled = false; dlUrl.value = ""; dlTitle.value = ""; setTimeout(() => { dlProgress.hidden = true; }, 800); refresh(); } else if (data.status === "error") { clearInterval(iv); dlStatus.textContent = `Error: ${data.error || "unknown"}`; dlBtn.disabled = false; dlProgress.hidden = true; } else { if (typeof data.progress === "number") { dlProgress.value = data.progress; } else { dlProgress.removeAttribute("value"); } const phase = data.phase === "converting" ? "Converting" : "Downloading"; const pctTxt = typeof data.progress === "number" ? ` ${data.progress}%` : ""; dlStatus.textContent = `${phase}${pctTxt}\u2026`; } } catch { clearInterval(iv); dlStatus.textContent = "Status check failed."; dlBtn.disabled = false; dlProgress.hidden = true; } }, 750); } // ── Rename ──────────────────────────────────────────────────────────────────── async function loadRenameDropdown() { const [downloads, tracks] = await Promise.all([fetchDownloads(), fetchTracks()]); renameSelect.innerHTML = ""; if (tracks.length) { const grp = document.createElement("optgroup"); grp.label = "Playlist"; tracks.forEach(t => { const opt = document.createElement("option"); opt.value = JSON.stringify({ type: "playlist", src: t.src, title: t.title }); opt.textContent = t.title; grp.appendChild(opt); }); renameSelect.appendChild(grp); } if (downloads.length) { const grp = document.createElement("optgroup"); grp.label = "Downloads"; downloads.forEach(t => { const opt = document.createElement("option"); opt.value = JSON.stringify({ type: "download", filename: t.filename, title: t.title }); opt.textContent = t.title; grp.appendChild(opt); }); renameSelect.appendChild(grp); } syncRenameInput(); } function syncRenameInput() { try { renameInput.value = JSON.parse(renameSelect.value).title; } catch {} } renameSelect.addEventListener("change", syncRenameInput); renameBtn.addEventListener("click", async () => { const newTitle = renameInput.value.trim(); if (!newTitle) return; let item; try { item = JSON.parse(renameSelect.value); } catch { return; } renameStatus.textContent = "Saving\u2026"; try { let res; if (item.type === "playlist") { res = await fetch("/api/tracks/rename", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ src: item.src, title: newTitle }), }); } else { res = await fetch(`/api/downloads/${encodeURIComponent(item.filename)}/rename`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ title: newTitle }), }); } if (res.ok) { renameStatus.textContent = "Saved!"; refresh(); } else { renameStatus.textContent = "Save failed."; } } catch { renameStatus.textContent = "Request failed."; } }); // ── Manage downloads ────────────────────────────────────────────────────────── async function loadManageList() { const tracks = await fetchDownloads(); if (!tracks.length) { manageList.innerHTML = "
No tracks downloaded yet.
"; return; } manageList.innerHTML = ""; tracks.forEach(t => { const item = document.createElement("div"); item.className = "track-item track-item--col"; const sourceLink = t.source ? `Source` : ""; item.innerHTML = `