mirror of
https://github.com/screentinker/screentinker.git
synced 2026-08-15 23:03:14 -06:00
Reconcile advanceTimer on mode enter/exit via reconcileAdvanceTimerForMode in applyWallMode/applyGroupSync — fixes the group-entry zombie timer and the solo-exit frozen image. Closes #200.
This commit is contained in:
parent
3efae1d2d6
commit
8c0bf77428
|
|
@ -1397,6 +1397,26 @@
|
||||||
// stage so this device's tile is the visible portion. Each tile is
|
// stage so this device's tile is the visible portion. Each tile is
|
||||||
// 100vw × 100vh; the stage is the full grid, translated by this tile's
|
// 100vw × 100vh; the stage is the full grid, translated by this tile's
|
||||||
// grid position (plus bezel offsets in px between tiles).
|
// grid position (plus bezel offsets in px between tiles).
|
||||||
|
// #200: keep advanceTimer consistent with the current playback mode. A schedule-driven device (wall
|
||||||
|
// FOLLOWER or group member) runs NO local advance timer — its tick drives the index, and a stale solo
|
||||||
|
// timer would spuriously advance and desync (zombie timer, Bug B). A solo player or wall LEADER
|
||||||
|
// advances locally, so on the way OUT of a schedule-driven mode a surviving image/widget item (which
|
||||||
|
// never armed a timer while driven) would freeze (Bug C) — re-render it (buffered, no flash) to re-arm.
|
||||||
|
// Video/YouTube self-advance via their own end handlers and re-rendering would restart them, so leave
|
||||||
|
// those alone. Called on every mode enter/exit (applyWallMode/applyGroupSync), which is the single
|
||||||
|
// chokepoint every transition path routes through.
|
||||||
|
function reconcileAdvanceTimerForMode() {
|
||||||
|
if (isWallFollower() || groupSync) {
|
||||||
|
if (advanceTimer) { clearTimeout(advanceTimer); advanceTimer = null; }
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!isPlaying) return;
|
||||||
|
const item = playlist[currentIndex];
|
||||||
|
if (!item || advanceTimer) return; // solo/leader that already has its timer armed -> nothing to do
|
||||||
|
const needsSoloTimer = !!item.widget_id || (typeof item.mime_type === 'string' && item.mime_type.startsWith('image/'));
|
||||||
|
if (needsSoloTimer) playCurrentItem(); // re-render buffered + re-arm the solo advance timer
|
||||||
|
}
|
||||||
|
|
||||||
function applyWallMode(config) {
|
function applyWallMode(config) {
|
||||||
const container = document.getElementById('playerContainer');
|
const container = document.getElementById('playerContainer');
|
||||||
// Tear down previous wall mode (clear sync timer regardless of new state)
|
// Tear down previous wall mode (clear sync timer regardless of new state)
|
||||||
|
|
@ -1407,6 +1427,7 @@
|
||||||
wallConfig = null;
|
wallConfig = null;
|
||||||
container.classList.remove('wall-mode');
|
container.classList.remove('wall-mode');
|
||||||
console.log('[wall] exited wall mode');
|
console.log('[wall] exited wall mode');
|
||||||
|
reconcileAdvanceTimerForMode(); // #200: back to solo -> re-arm a surviving image/widget's timer
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
wallConfig = config;
|
wallConfig = config;
|
||||||
|
|
@ -1443,6 +1464,7 @@
|
||||||
socket.emit('wall:sync-request', { wall_id: config.wall_id });
|
socket.emit('wall:sync-request', { wall_id: config.wall_id });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
reconcileAdvanceTimerForMode(); // #200: follower -> kill any stale solo timer; leader keeps its own
|
||||||
}
|
}
|
||||||
|
|
||||||
function emitWallSync() {
|
function emitWallSync() {
|
||||||
|
|
@ -1562,6 +1584,7 @@
|
||||||
if (!cfg) {
|
if (!cfg) {
|
||||||
if (groupSync) groupReport('info', 'group-sync exited'); groupSync = null; console.log('[group-sync] exited');
|
if (groupSync) groupReport('info', 'group-sync exited'); groupSync = null; console.log('[group-sync] exited');
|
||||||
if (groupPreloadEl) { try { groupPreloadEl.remove(); } catch (e) {} groupPreloadEl = null; groupPreloadIdx = -1; }
|
if (groupPreloadEl) { try { groupPreloadEl.remove(); } catch (e) {} groupPreloadEl = null; groupPreloadIdx = -1; }
|
||||||
|
reconcileAdvanceTimerForMode(); // #200: back to solo -> re-arm a surviving image/widget's timer
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const first = !groupSync;
|
const first = !groupSync;
|
||||||
|
|
@ -1571,6 +1594,7 @@
|
||||||
groupReport('info', 'group-sync ' + (first ? 'entered' : 'refresh') + ' group=' + String(cfg.group_id).slice(0, 8) + ' off=' + clockOffsetMs + 'ms');
|
groupReport('info', 'group-sync ' + (first ? 'entered' : 'refresh') + ' group=' + String(cfg.group_id).slice(0, 8) + ' off=' + clockOffsetMs + 'ms');
|
||||||
groupScheduleTick(); // align immediately
|
groupScheduleTick(); // align immediately
|
||||||
groupSyncTimer = setInterval(groupScheduleTick, 250); // 4Hz local correction
|
groupSyncTimer = setInterval(groupScheduleTick, 250); // 4Hz local correction
|
||||||
|
reconcileAdvanceTimerForMode(); // #200: group member runs no solo timer -> kill any zombie
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map the player rect into this device's viewport using vw/vh so the
|
// Map the player rect into this device's viewport using vw/vh so the
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue