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