const state = { shaders: [] }; const el = (id) => document.getElementById(id); async function api(path, options = {}) { const response = await fetch(path, { headers: { "content-type": "application/json" }, ...options }); if (!response.ok) { const text = await response.text(); throw new Error(text || response.statusText); } return response.json(); } function renderStatus(status) { el("device").textContent = status.deviceName || "No DeckLink device selected"; el("running").textContent = status.running ? "Running" : "Stopped"; el("mode").textContent = status.mode || "No signal"; el("outputFormat").textContent = status.outputFormat || "Unavailable"; el("frames").textContent = `${status.framesCaptured ?? 0} / ${status.framesOutput ?? 0}`; el("frameRate").textContent = Number(status.frameRate || 0).toFixed(2); el("dropped").textContent = status.framesDropped ?? 0; el("error").textContent = status.error || ""; } function renderShaders(payload) { state.shaders = payload.shaders || []; const host = el("shaders"); host.innerHTML = ""; for (const shader of state.shaders) { const card = document.createElement("div"); card.className = "shader"; const amount = shader.parameters.find((p) => p.id === "amount"); card.innerHTML = `
${shader.name} ${shader.type}
`; const input = card.querySelector("input"); const output = card.querySelector("output"); input.addEventListener("input", async () => { output.textContent = Number(input.value).toFixed(2); await api(`/api/shaders/${shader.id}/parameters`, { method: "PATCH", body: JSON.stringify({ amount: Number(input.value) }) }); }); host.appendChild(card); } } async function refresh() { renderStatus(await api("/api/status")); renderShaders(await api("/api/shaders")); } function connectWs() { const ws = new WebSocket(`ws://${location.host}/ws`); ws.addEventListener("message", (event) => { const message = JSON.parse(event.data); if (message.type === "state") { renderStatus(message.status); renderShaders(message.shaders); } }); ws.addEventListener("close", () => setTimeout(connectWs, 1000)); } el("start").addEventListener("click", async () => { try { renderStatus(await api("/api/pipeline/start", { method: "POST" })); } catch (error) { el("error").textContent = error.message; } }); el("stop").addEventListener("click", async () => { renderStatus(await api("/api/pipeline/stop", { method: "POST" })); }); refresh(); connectWs();