const form = document.querySelector("#job-form"); const jobsEl = document.querySelector("#jobs"); const logEl = document.querySelector("#log"); const activeCountEl = document.querySelector("#active-count"); const lastStateEl = document.querySelector("#last-state"); const outputsEl = document.querySelector("#outputs"); let selectedJobId = null; async function fetchJson(url, options) { const response = await fetch(url, options); const data = await response.json(); if (!response.ok) { throw new Error(data.error || "Request failed"); } return data; } function renderJobs(jobs) { jobsEl.innerHTML = ""; const running = jobs.filter((job) => ["queued", "running"].includes(job.status)); activeCountEl.textContent = `${running.length} running`; lastStateEl.textContent = jobs[0] ? `${jobs[0].id} ${jobs[0].status}` : "Ready"; if (!selectedJobId && jobs[0]) { selectedJobId = jobs[0].id; } jobs.forEach((job) => { const button = document.createElement("button"); button.type = "button"; button.className = `job-pill${job.id === selectedJobId ? " active" : ""}`; button.textContent = `${job.id} - ${job.status}`; button.addEventListener("click", () => { selectedJobId = job.id; logEl.textContent = job.log || "Waiting for log output..."; renderJobs(jobs); }); jobsEl.appendChild(button); }); const selected = jobs.find((job) => job.id === selectedJobId); if (selected) { logEl.textContent = selected.log || "Waiting for log output..."; } else if (!jobs.length) { logEl.textContent = "No jobs yet."; } } async function loadJobs() { const jobs = await fetchJson("/api/jobs"); renderJobs(jobs); } async function loadOutputs() { const outputs = await fetchJson("/api/outputs"); outputsEl.innerHTML = ""; if (!outputs.length) { const empty = document.createElement("p"); empty.className = "empty"; empty.textContent = "Finished videos will appear here."; outputsEl.appendChild(empty); return; } outputs.forEach((output) => { const link = document.createElement("a"); link.className = "output-card"; link.href = `/outputs/${encodeURIComponent(output.name)}`; link.innerHTML = `${output.name}${output.size_mb} MB`; outputsEl.appendChild(link); }); } form.addEventListener("submit", async (event) => { event.preventDefault(); const submit = form.querySelector("button[type='submit']"); submit.disabled = true; try { const job = await fetchJson("/api/jobs", { method: "POST", body: new FormData(form), }); selectedJobId = job.id; form.reset(); form.elements.lang.value = "es"; form.elements.mix_mode.value = "instrumental-only"; await loadJobs(); } catch (error) { logEl.textContent = error.message; } finally { submit.disabled = false; } }); document.querySelector("#refresh").addEventListener("click", loadJobs); document.querySelector("#reload-outputs").addEventListener("click", loadOutputs); loadJobs(); setInterval(loadJobs, 2500); setInterval(loadOutputs, 10000);