Setup-Zuordnung korrigiert (cam2/cam3), Button 'Setup alle', Stream nach Setup neu laden

This commit is contained in:
Joachim Hummel committed 2026-10-17 21:13:45 +02:00
1 parent 70b63cb215
commit 7c80b3402e
2 files changed
+27 -8

No files matched your search

+1 -1
View File
@@ -5,7 +5,7 @@ CAMERA_cam2=http://IP:PORT/pfad
CAMERA_cam3=http://IP:PORT/pfad CAMERA_cam3=http://IP:PORT/pfad
# Optional: Konfigurations-URL pro Kamera (Button "Setup" im Frontend), z.B. ESP32-CAM Auflösung # Optional: Konfigurations-URL pro Kamera (Button "Setup" im Frontend), z.B. ESP32-CAM Auflösung
#SETUP_cam1=http://IP/control?var=framesize&val=11 #SETUP_cam2=http://IP/control?var=framesize&val=11
PORT=8000 PORT=8000
# Optional: Login (nur sinnvoll mit HTTPS in Zoraxy) # Optional: Login (nur sinnvoll mit HTTPS in Zoraxy)
+26 -7
View File
@@ -61,6 +61,7 @@ main{padding:22px;max-width:1800px;margin:0 auto}
<header> <header>
<h1><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M23 7l-7 5 7 5V7z"/><rect x="1" y="5" width="15" height="14" rx="2"/></svg><a id="home">Webcams</a></h1> <h1><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M23 7l-7 5 7 5V7z"/><rect x="1" y="5" width="15" height="14" rx="2"/></svg><a id="home">Webcams</a></h1>
<span class="sp"></span> <span class="sp"></span>
<button class="btn" id="setupAll" hidden><span>Setup alle</span></button>
<span class="pill" id="count"></span> <span class="pill" id="count"></span>
<span class="pill" id="clock"></span> <span class="pill" id="clock"></span>
</header> </header>
@@ -72,6 +73,7 @@ const app = document.getElementById("app");
document.getElementById("home").href = ROOT; document.getElementById("home").href = ROOT;
const banner = document.getElementById("banner"); const banner = document.getElementById("banner");
const tiles = {}; const tiles = {};
let setupNames = [];
let order = [], visible = true; let order = [], visible = true;
const el = (tag, cls, text) => { const el = (tag, cls, text) => {
@@ -88,6 +90,15 @@ const ICONS = {
}; };
const icon = k => { const s = document.createElementNS("http://www.w3.org/2000/svg", "svg"); s.setAttribute("viewBox", "0 0 24 24"); s.setAttribute("fill", "none"); s.setAttribute("stroke", "currentColor"); s.setAttribute("stroke-width", "2"); s.setAttribute("stroke-linecap", "round"); s.setAttribute("stroke-linejoin", "round"); s.innerHTML = ICONS[k]; return s; }; const icon = k => { const s = document.createElementNS("http://www.w3.org/2000/svg", "svg"); s.setAttribute("viewBox", "0 0 24 24"); s.setAttribute("fill", "none"); s.setAttribute("stroke", "currentColor"); s.setAttribute("stroke-width", "2"); s.setAttribute("stroke-linecap", "round"); s.setAttribute("stroke-linejoin", "round"); s.innerHTML = ICONS[k]; return s; };
const btn = (label, ic, fn, cls) => { const b = el("button", "btn" + (cls ? " " + cls : "")); b.title = label; b.append(icon(ic), el("span", "", label)); b.onclick = fn; return b; }; const btn = (label, ic, fn, cls) => { const b = el("button", "btn" + (cls ? " " + cls : "")); b.title = label; b.append(icon(ic), el("span", "", label)); b.onclick = fn; return b; };
const setupAllBtn = document.getElementById("setupAll");
setupAllBtn.prepend(icon("cog"));
setupAllBtn.onclick = async () => {
setupAllBtn.disabled = true;
const label = setupAllBtn.querySelector("span");
const res = await Promise.all(setupNames.map(runSetup));
label.textContent = res.every(Boolean) ? "Gesendet ✓" : "Fehler ✗";
setTimeout(() => { label.textContent = "Setup alle"; setupAllBtn.disabled = false; }, 2500);
};
// mode: "live" = MJPEG-Stream, "thumb" = Einzelbild im Intervall // mode: "live" = MJPEG-Stream, "thumb" = Einzelbild im Intervall
function makeTile(name, mode, hasSetup) { function makeTile(name, mode, hasSetup) {
@@ -115,13 +126,7 @@ function makeTile(name, mode, hasSetup) {
const setup = btn("Setup", "cog", async () => { const setup = btn("Setup", "cog", async () => {
setup.disabled = true; setup.disabled = true;
const label = setup.querySelector("span"); const label = setup.querySelector("span");
try { label.textContent = (await runSetup(name)) ? "Gesendet ✓" : "Fehler ✗";
const r = await fetch(ROOT + name + "/setup", { method: "POST" });
if (!r.ok) throw new Error(r.status);
label.textContent = "Gesendet ✓";
} catch (e) {
label.textContent = "Fehler ✗";
}
setTimeout(() => { label.textContent = "Setup"; setup.disabled = false; }, 2500); setTimeout(() => { label.textContent = "Setup"; setup.disabled = false; }, 2500);
}, "alt"); }, "alt");
actions.append(setup); actions.append(setup);
@@ -140,6 +145,18 @@ function makeTile(name, mode, hasSetup) {
return t; return t;
} }
async function runSetup(name) {
try {
const r = await fetch(ROOT + name + "/setup", { method: "POST" });
if (!r.ok) throw new Error(r.status);
// Kamera startet den Stream mit neuer Auflösung; Bild kurz danach neu laden
setTimeout(() => tiles[name] && connect(tiles[name]), 800);
return true;
} catch (e) {
return false;
}
}
function connect(o) { function connect(o) {
clearTimeout(o.retry); clearTimeout(o.retry);
clearInterval(o.timer); clearInterval(o.timer);
@@ -192,6 +209,8 @@ async function poll() {
if (names.join() !== order.join()) build(data.cameras); if (names.join() !== order.join()) build(data.cameras);
data.cameras.forEach(c => setOnline(tiles[c.name], c.online)); data.cameras.forEach(c => setOnline(tiles[c.name], c.online));
const n = data.cameras.filter(c => c.online).length; const n = data.cameras.filter(c => c.online).length;
setupNames = data.cameras.filter(c => c.setup).map(c => c.name);
setupAllBtn.hidden = !setupNames.length;
document.getElementById("count").innerHTML = "<b>" + n + "</b> / " + data.cameras.length + " online"; document.getElementById("count").innerHTML = "<b>" + n + "</b> / " + data.cameras.length + " online";
} catch (e) { } catch (e) {
banner.style.display = "block"; banner.style.display = "block";