Stabilizza regia, live web e sync punteggi app nativa.

Corregge scadenza token regia oltre le 8h, tabellone HTML sulla pagina live,
pulsanti pausa/ripresa in regia, link admin e broadcast ActionCable diretto.
App Android: overlay branding, sync score da regia via WebSocket con reconnect e poll.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-08 09:30:43 +02:00
parent 08e800120a
commit ae36d17adb
55 changed files with 2819 additions and 285 deletions

View File

@@ -7,6 +7,7 @@
statusUrl: root.dataset.statusUrl,
scoreUrl: root.dataset.scoreUrl,
pauseUrl: root.dataset.pauseUrl,
resumeUrl: root.dataset.resumeUrl,
stopUrl: root.dataset.stopUrl,
cableUrl: root.dataset.cableUrl,
hlsUrl: root.dataset.hlsUrl,
@@ -30,13 +31,15 @@
modalConfirm: document.getElementById("modal-confirm"),
modalCancel: document.getElementById("modal-cancel"),
preview: document.getElementById("regia-preview"),
previewPlaceholder: document.getElementById("regia-preview-placeholder")
previewPlaceholder: document.getElementById("regia-preview-placeholder"),
btnPause: document.getElementById("btn-pause")
};
let pendingAction = null;
let hls = null;
let previewStarted = false;
let wasPausedPreview = false;
let streamPaused = root.dataset.initialPaused === "true";
function hlsUrlFresh() {
const sep = cfg.hlsUrl.includes("?") ? "&" : "?";
@@ -108,7 +111,15 @@
}
}
function syncPauseButton(data) {
if (!els.btnPause) return;
const paused = !!(data.paused || data.status === "paused");
streamPaused = paused;
els.btnPause.textContent = paused ? "Riprendi diretta" : "Metti in pausa";
}
function setBadge(data) {
syncPauseButton(data);
if (data.stream_closed) {
els.badge.textContent = "Terminata";
els.badge.className = "regia-badge regia-badge--ended";
@@ -139,6 +150,8 @@
}
async function handleAction(action) {
const prevHome = els.homePoints?.textContent;
const prevAway = document.getElementById("away-points")?.textContent;
try {
const data = await postScore(action);
applyScorePayload(data);
@@ -152,6 +165,9 @@
openModal("Partita vinta", `${winner} ha vinto la partita. Chiudere la diretta?`, () => stopStream());
}
} catch (e) {
if (prevHome != null && els.homePoints) els.homePoints.textContent = prevHome;
const awayEl = document.getElementById("away-points");
if (prevAway != null && awayEl) awayEl.textContent = prevAway;
toast(e.message || "Errore");
}
}
@@ -284,10 +300,19 @@
showPreviewPlaceholder("Diretta terminata");
}
async function pauseStream() {
await fetch(cfg.pauseUrl, { method: "POST", headers: { Accept: "application/json" } });
toast("Diretta in pausa — copertina in onda");
pollStatus();
async function togglePauseStream() {
const url = streamPaused ? cfg.resumeUrl : cfg.pauseUrl;
const res = await fetch(url, { method: "POST", headers: { Accept: "application/json" } });
if (!res.ok) {
const body = await res.json().catch(() => ({}));
throw new Error(body.error || (streamPaused ? "Errore ripresa diretta" : "Errore pausa diretta"));
}
const data = await res.json();
const wasPaused = streamPaused;
applyScorePayload(data);
setBadge(data);
syncPreviewFromStatus(data);
toast(wasPaused ? "Diretta ripresa" : "Diretta in pausa — copertina in onda");
}
async function stopStream() {
@@ -302,7 +327,7 @@
return data;
}
document.getElementById("btn-pause")?.addEventListener("click", () => pauseStream().catch((e) => toast(e.message)));
els.btnPause?.addEventListener("click", () => togglePauseStream().catch((e) => toast(e.message)));
document.getElementById("btn-stop")?.addEventListener("click", () => stopStream().catch((e) => toast(e.message)));
async function shareLink() {