| |
| |
| |
| |
|
|
| const API_BASE = ""; |
|
|
| |
| |
| |
|
|
| const state = { |
| currentTask: "transcribe", |
| loading: false, |
| }; |
|
|
| |
| |
| |
|
|
| function el(id) { |
| const node = document.getElementById(id); |
| if (!node) console.warn(`[UI] Missing element: ${id}`); |
| return node; |
| } |
|
|
| |
| |
| |
|
|
| function nav(task) { |
| state.currentTask = task; |
|
|
| document.querySelectorAll(".section").forEach((s) => |
| s.classList.remove("active") |
| ); |
|
|
| const page = el(task); |
| if (page) page.classList.add("active"); |
|
|
| document.querySelectorAll(".nav-item").forEach((n) => |
| n.classList.remove("active") |
| ); |
|
|
| if (event && event.target) { |
| event.target.classList.add("active"); |
| } |
|
|
| location.hash = task; |
| } |
|
|
| |
| window.addEventListener("load", () => { |
| const hash = location.hash.replace("#", ""); |
| if (hash && el(hash)) nav(hash); |
| }); |
|
|
| |
| |
| |
|
|
| function setLoading(task, isLoading) { |
| const btn = el(`btn-${task}`); |
| if (!btn) return; |
|
|
| btn.disabled = isLoading; |
| btn.innerText = isLoading ? "Processing..." : "Execute"; |
| } |
|
|
| |
| |
| |
|
|
| async function safeFetch(url, options, retries = 2) { |
| try { |
| const res = await fetch(url, options); |
|
|
| if (!res.ok) { |
| const text = await res.text(); |
| throw new Error(`HTTP ${res.status}: ${text}`); |
| } |
|
|
| return res; |
| } catch (err) { |
| if (retries > 0) { |
| await new Promise((r) => setTimeout(r, 800)); |
| return safeFetch(url, options, retries - 1); |
| } |
| throw err; |
| } |
| } |
|
|
| |
| |
| |
|
|
| async function parseResponse(res, task) { |
| const contentType = res.headers.get("content-type") || ""; |
|
|
| |
| if ( |
| contentType.includes("video") || |
| contentType.includes("octet-stream") |
| ) { |
| const blob = await res.blob(); |
| return { |
| type: "blob", |
| url: URL.createObjectURL(blob), |
| }; |
| } |
|
|
| |
| const data = await res.json(); |
| return { |
| type: "json", |
| data, |
| }; |
| } |
|
|
| |
| |
| |
|
|
| function collectInput(task) { |
| const file = el(`file-${task}`)?.files?.[0]; |
| const url = el(`url-${task}`)?.value?.trim(); |
|
|
| const fd = new FormData(); |
|
|
| if (file) fd.append("file", file); |
| if (url) fd.append("url_input", url); |
|
|
| return fd; |
| } |
|
|
| |
| |
| |
|
|
| async function run(task) { |
| if (state.loading) return; |
|
|
| state.loading = true; |
| setLoading(task, true); |
|
|
| try { |
| const fd = collectInput(task); |
|
|
| const res = await safeFetch(`${API_BASE}/execute/${task}`, { |
| method: "POST", |
| body: fd, |
| }); |
|
|
| const parsed = await parseResponse(res, task); |
|
|
| renderOutput(task, parsed); |
| } catch (err) { |
| console.error(err); |
| renderError(task, err.message); |
| } finally { |
| state.loading = false; |
| setLoading(task, false); |
| } |
| } |
|
|
| |
| |
| |
|
|
| function renderOutput(task, result) { |
| const outputBox = el(`result-${task}`); |
| const videoBox = el(`video-${task}`); |
|
|
| if (result.type === "blob") { |
| if (videoBox) { |
| videoBox.src = result.url; |
| videoBox.style.display = "block"; |
| } |
| return; |
| } |
|
|
| if (outputBox) { |
| outputBox.textContent = JSON.stringify(result.data, null, 2); |
| } |
| } |
|
|
| |
| |
| |
|
|
| function renderError(task, message) { |
| const outputBox = el(`result-${task}`); |
|
|
| if (outputBox) { |
| outputBox.textContent = `ERROR:\n${message}`; |
| outputBox.style.color = "#ff4d4d"; |
| } |
| } |
|
|
| |
| |
| |
|
|
| async function checkHealth() { |
| try { |
| const res = await fetch("/health"); |
| return await res.json(); |
| } catch { |
| return { status: "offline" }; |
| } |
| } |
|
|
| |
| |
| |
|
|
| function toggleSidebar() { |
| const sidebar = document.querySelector(".sidebar"); |
| if (!sidebar) return; |
|
|
| sidebar.classList.toggle("open"); |
| } |
|
|
| |
| document.addEventListener("click", (e) => { |
| if (e.target.classList.contains("nav-item")) { |
| const sidebar = document.querySelector(".sidebar"); |
| if (sidebar) sidebar.classList.remove("open"); |
| } |
| }); |