Fix player video cycling bug and connecting overlay during cached playback

Clear pending advance timers when switching content items to prevent stale
image/widget duration timers from interrupting video playback. Also skip
showing "Connecting..." overlay when cached playlist is already playing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
ScreenTinker 2026-04-13 22:34:48 -05:00
parent d73abc809d
commit ad3095cdf5

View file

@ -109,6 +109,7 @@
let layout = null;
let zones = {};
let userHasInteracted = false;
let advanceTimer = null;
// Track user interaction for autoplay policy
['click', 'touchstart', 'keydown'].forEach(evt => {
@ -191,7 +192,7 @@
tapOverlay.onclick = () => {
unlockAudio();
tapOverlay.remove();
showStatus('Connecting...');
if (!isPlaying) showStatus('Connecting...');
connect(config.serverUrl);
};
document.body.appendChild(tapOverlay);
@ -200,7 +201,7 @@
setTimeout(() => {
if (tapOverlay.parentNode) {
tapOverlay.remove();
showStatus('Connecting (audio muted)...');
if (!isPlaying) showStatus('Connecting (audio muted)...');
connect(config.serverUrl);
}
}, 5000);
@ -700,6 +701,9 @@
}
function renderContent(item) {
// Clear any pending advance timer from previous content (image/widget duration timers)
if (advanceTimer) { clearTimeout(advanceTimer); advanceTimer = null; }
const container = document.getElementById('playerContainer');
container.style.display = 'block';
container.innerHTML = '';
@ -727,7 +731,7 @@
video.style.cssText = 'width:100%;height:100%;object-fit:contain;background:#000';
video.loop = (playlist.length === 1);
video.onended = () => { if (!video.loop) nextItem(); };
video.onerror = (e) => { console.error('Video error:', src, e); setTimeout(nextItem, 3000); };
video.onerror = (e) => { console.error('Video error:', src, e); advanceTimer = setTimeout(nextItem, 3000); };
video.onloadeddata = () => {
console.log('Video loaded:', item.filename, 'muted:', video.muted);
};
@ -740,10 +744,10 @@
const img = document.createElement('img');
img.src = src;
img.style.cssText = 'width:100%;height:100%;object-fit:contain';
img.onerror = () => { console.error('Image error'); setTimeout(nextItem, 3000); };
img.onerror = () => { console.error('Image error'); advanceTimer = setTimeout(nextItem, 3000); };
container.appendChild(img);
// Auto advance for images
setTimeout(nextItem, (item.duration_sec || 10) * 1000);
advanceTimer = setTimeout(nextItem, (item.duration_sec || 10) * 1000);
} else if (item.widget_id) {
const iframe = document.createElement('iframe');
iframe.src = `${serverUrl}/api/widgets/${item.widget_id}/render`;
@ -751,7 +755,7 @@
iframe.allow = 'autoplay; fullscreen';
container.appendChild(iframe);
// Auto advance for widgets
setTimeout(nextItem, (item.duration_sec || 30) * 1000);
advanceTimer = setTimeout(nextItem, (item.duration_sec || 30) * 1000);
}
}
}
@ -793,7 +797,7 @@
img.src = src;
img.style.cssText = `width:100%;height:100%;object-fit:${zone.fit_mode || 'cover'}`;
div.appendChild(img);
if (playlist.length > 1) setTimeout(nextItem, (assignment.duration_sec || 10) * 1000);
if (playlist.length > 1) advanceTimer = setTimeout(nextItem, (assignment.duration_sec || 10) * 1000);
}
container.appendChild(div);