93 lines
2.8 KiB
JavaScript
93 lines
2.8 KiB
JavaScript
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 = `
|
|
<header>
|
|
<strong>${shader.name}</strong>
|
|
<span>${shader.type}</span>
|
|
</header>
|
|
<label>
|
|
<span>${amount.label}</span>
|
|
<input type="range" min="${amount.min}" max="${amount.max}" step="0.01" value="${amount.value}">
|
|
<output>${Number(amount.value).toFixed(2)}</output>
|
|
</label>
|
|
`;
|
|
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();
|