Stabilizza live YouTube/RTMP e fix crash rotazione mobile.
Pipeline YouTube con relay, overlay e publisher sync lato backend; fix GL pauseGlDuringRotation su Android per evitare crash in rotazione durante lo streaming Flutter. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
BIN
backend/public/logo-white.png
Normal file
BIN
backend/public/logo-white.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.1 KiB |
@@ -36,6 +36,41 @@
|
||||
let pendingAction = null;
|
||||
let hls = null;
|
||||
let previewStarted = false;
|
||||
let wasPausedPreview = false;
|
||||
|
||||
function hlsUrlFresh() {
|
||||
const sep = cfg.hlsUrl.includes("?") ? "&" : "?";
|
||||
return `${cfg.hlsUrl}${sep}_t=${Date.now()}`;
|
||||
}
|
||||
|
||||
function hidePreviewPlaceholder() {
|
||||
if (els.previewPlaceholder) els.previewPlaceholder.hidden = true;
|
||||
}
|
||||
|
||||
function showPreviewPlaceholder(text) {
|
||||
if (!els.previewPlaceholder) return;
|
||||
els.previewPlaceholder.textContent = text;
|
||||
els.previewPlaceholder.hidden = false;
|
||||
}
|
||||
|
||||
function destroyPreview() {
|
||||
previewStarted = false;
|
||||
if (hls) {
|
||||
hls.destroy();
|
||||
hls = null;
|
||||
}
|
||||
if (els.preview) {
|
||||
els.preview.removeAttribute("src");
|
||||
els.preview.load();
|
||||
}
|
||||
}
|
||||
|
||||
function refreshPreviewForCover() {
|
||||
if (!els.preview || !cfg.hlsUrl || cfg.streamClosed) return;
|
||||
destroyPreview();
|
||||
previewStarted = false;
|
||||
startPreview();
|
||||
}
|
||||
|
||||
function toast(msg) {
|
||||
els.toast.textContent = msg;
|
||||
@@ -169,13 +204,23 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const paused = data.paused || data.status === "paused";
|
||||
if (data.on_air) {
|
||||
if (!previewStarted) startPreview();
|
||||
else if (els.preview && els.preview.readyState >= 2) hidePreviewPlaceholder();
|
||||
if (!previewStarted) {
|
||||
startPreview();
|
||||
} else if (paused && !wasPausedPreview) {
|
||||
refreshPreviewForCover();
|
||||
} else if (paused) {
|
||||
if (els.preview && els.preview.readyState >= 2) hidePreviewPlaceholder();
|
||||
else els.preview?.play().catch(() => {});
|
||||
} else if (els.preview && els.preview.readyState >= 2) {
|
||||
hidePreviewPlaceholder();
|
||||
}
|
||||
} else {
|
||||
destroyPreview();
|
||||
showPreviewPlaceholder("Anteprima in attesa del segnale…");
|
||||
}
|
||||
wasPausedPreview = paused;
|
||||
}
|
||||
|
||||
async function pollStatus() {
|
||||
@@ -203,7 +248,13 @@
|
||||
const url = hlsUrlFresh();
|
||||
|
||||
if (window.Hls && Hls.isSupported()) {
|
||||
hls = new Hls({ lowLatencyMode: true, maxBufferLength: 8 });
|
||||
hls = new Hls({
|
||||
lowLatencyMode: false,
|
||||
maxBufferLength: 30,
|
||||
maxMaxBufferLength: 60,
|
||||
liveSyncDurationCount: 5,
|
||||
maxLiveSyncPlaybackRate: 1.0
|
||||
});
|
||||
hls.loadSource(url);
|
||||
hls.attachMedia(els.preview);
|
||||
hls.on(Hls.Events.MANIFEST_PARSED, () => {
|
||||
@@ -283,7 +334,42 @@
|
||||
document.getElementById("btn-share")?.addEventListener("click", () => shareLink());
|
||||
document.getElementById("btn-copy")?.addEventListener("click", () => copyLink());
|
||||
|
||||
function bindPreviewStallRecovery() {
|
||||
if (!els.preview) return;
|
||||
let stallAt = 0;
|
||||
let lastProgressAt = Date.now();
|
||||
let lastTime = 0;
|
||||
const markProgress = () => {
|
||||
const t = els.preview.currentTime;
|
||||
if (t > lastTime + 0.05) {
|
||||
lastTime = t;
|
||||
lastProgressAt = Date.now();
|
||||
}
|
||||
};
|
||||
els.preview.addEventListener("timeupdate", markProgress);
|
||||
els.preview.addEventListener("playing", markProgress);
|
||||
const nudge = (force) => {
|
||||
if (!previewStarted || cfg.streamClosed) return;
|
||||
const now = Date.now();
|
||||
if (!force && now - lastProgressAt < 8000) {
|
||||
els.preview.play().catch(() => {});
|
||||
return;
|
||||
}
|
||||
if (now - stallAt < 5000) return;
|
||||
stallAt = now;
|
||||
lastProgressAt = now;
|
||||
if (hls) {
|
||||
hls.startLoad(-1);
|
||||
els.preview.play().catch(() => {});
|
||||
}
|
||||
};
|
||||
setInterval(() => {
|
||||
if (previewStarted && !cfg.streamClosed && Date.now() - lastProgressAt > 12000) nudge(true);
|
||||
}, 4000);
|
||||
}
|
||||
|
||||
bindPreviewEvents();
|
||||
bindPreviewStallRecovery();
|
||||
if (!cfg.streamClosed && cfg.hlsUrl) {
|
||||
pollStatus();
|
||||
} else if (cfg.streamClosed) {
|
||||
|
||||
Reference in New Issue
Block a user