Rendi affidabile pausa con slate e conferma i controlli laterali.

Evita buchi HLS/YouTube in pausa (path slate ricreata, reload player, no patch recording inutili) e richiede conferma su tasti laterali app/regia.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-20 12:44:23 +02:00
co-authored by Cursor
parent e09b3ffcf8
commit 0702cb699c
37 changed files with 1186 additions and 83 deletions
+72 -13
View File
@@ -33,6 +33,18 @@
muteError: "Errore silenziamento audio",
closeError: "Errore chiusura",
closeConfirm: "Chiudere definitivamente la diretta?",
pauseConfirmTitle: "Mettere in pausa?",
pauseConfirmBody: "La diretta passerà alla copertina finché non la riprendi.",
resumeConfirmTitle: "Riprendere la diretta?",
resumeConfirmBody: "La telecamera tornerà in onda al posto della copertina.",
muteConfirmTitle: "Silenziare l'audio?",
muteConfirmBody: "Il microfono non verrà inviato allo stream finché non lo riattivi.",
unmuteConfirmTitle: "Riattivare l'audio?",
unmuteConfirmBody: "Il microfono tornerà attivo sullo stream.",
advancePeriodTitle: "Passare al periodo successivo?",
advancePeriodBody: "Il tabellone passerà al periodo successivo e il punteggio del periodo corrente sarà salvato.",
confirmAction: "Conferma",
stopConfirmTitle: "Terminare la diretta?",
linkUnavailable: "Link non disponibile",
linkCopied: "Link copiato",
copyPrompt: "Copia il link:",
@@ -389,13 +401,18 @@
if (cfg.board === "volley" || cfg.board === "racket") {
if (data.set_won && action !== "close_set") {
const winner = data.winner === "home" ? cfg.homeName : cfg.awayName;
openModal(I18N.setWonTitle, fillTemplate(I18N.setWonBodyTemplate, { winner }), () => postScore("close_set"));
openModal(I18N.setWonTitle, fillTemplate(I18N.setWonBodyTemplate, { winner }), () => postScore("close_set"), I18N.closeSetTitle);
}
}
if (data.match_won) {
const winner = data.winner === "home" ? cfg.homeName : cfg.awayName;
openModal(I18N.matchWonTitle, fillTemplate(I18N.matchWonBodyTemplate, { winner }), () => stopStream());
}
if (data.match_won) {
const winner = data.winner === "home" ? cfg.homeName : cfg.awayName;
openModal(
I18N.matchWonTitle,
fillTemplate(I18N.matchWonBodyTemplate, { winner }),
() => performStopStream(),
I18N.confirmAction
);
}
} catch (e) {
if (prevHome != null && els.homePoints) els.homePoints.textContent = prevHome;
const awayEl = document.getElementById("away-points");
@@ -406,9 +423,12 @@
}
}
function openModal(title, body, onConfirm) {
function openModal(title, body, onConfirm, confirmLabel) {
els.modalTitle.textContent = title;
els.modalBody.textContent = body;
if (els.modalConfirm) {
els.modalConfirm.textContent = confirmLabel || I18N.confirmAction;
}
els.modal.classList.add("open");
pendingAction = onConfirm;
}
@@ -440,7 +460,9 @@
btn.addEventListener("click", () => {
const action = btn.dataset.action;
if (action === "close_set") {
openModal(I18N.closeSetTitle, I18N.closeSetConfirm, () => postScore("close_set"));
openModal(I18N.closeSetTitle, I18N.closeSetConfirm, () => postScore("close_set"), I18N.closeSetTitle);
} else if (action === "advance_period") {
openModal(I18N.advancePeriodTitle, I18N.advancePeriodBody, () => handleAction("advance_period"));
} else {
handleAction(action);
}
@@ -689,9 +711,8 @@
toast(audioMuted ? I18N.muted : I18N.unmuted);
}
async function stopStream() {
const ok = confirm(I18N.closeConfirm);
if (!ok) return null;
async function performStopStream() {
if (!cfg.stopUrl) return null;
const res = await fetch(cfg.stopUrl, { method: "POST", headers: { Accept: "application/json" } });
if (!res.ok) throw new Error(I18N.closeError);
const data = await res.json();
@@ -701,9 +722,47 @@
return data;
}
els.btnPause?.addEventListener("click", () => togglePauseStream().catch((e) => toast(e.message)));
els.btnAudioMute?.addEventListener("click", () => toggleMuteAudio().catch((e) => toast(e.message)));
document.getElementById("btn-stop")?.addEventListener("click", () => stopStream().catch((e) => toast(e.message)));
function requestStopStream() {
openModal(I18N.stopConfirmTitle, I18N.closeConfirm, () => performStopStream(), I18N.confirmAction);
}
function requestPauseToggle() {
if (streamPaused) {
openModal(I18N.resumeConfirmTitle, I18N.resumeConfirmBody, async () => {
await togglePauseStream();
return null;
});
} else {
openModal(I18N.pauseConfirmTitle, I18N.pauseConfirmBody, async () => {
await togglePauseStream();
return null;
});
}
}
function requestMuteToggle() {
if (audioMuted) {
openModal(I18N.unmuteConfirmTitle, I18N.unmuteConfirmBody, async () => {
await toggleMuteAudio();
return null;
});
} else {
openModal(I18N.muteConfirmTitle, I18N.muteConfirmBody, async () => {
await toggleMuteAudio();
return null;
});
}
}
async function stopStream() {
// Compatibilità: conferma via modal (non window.confirm).
requestStopStream();
return null;
}
els.btnPause?.addEventListener("click", () => requestPauseToggle());
els.btnAudioMute?.addEventListener("click", () => requestMuteToggle());
document.getElementById("btn-stop")?.addEventListener("click", () => requestStopStream());
let minQualityBusy = false;