From 47bda040a28969a26759b4ec73ffb4ff9e59fac1 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 30 Jul 2026 22:29:06 -0500 Subject: [PATCH] Re-render when a screen leaves a sync group or a video wall MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Taking a display out of a sync group, or deleting the wall it belonged to, froze it on whatever was playing. The clip looped forever and every later refresh took the "unchanged" branch, because the element was attached, playing and un-errored — healthy by every check the player makes. Only a reboot cleared it. On the web player, reconcileAdvanceTimerForMode re-arms a solo timer for widgets and images but skips video and YouTube, on the grounds that they "self-advance via their own end handlers". The handler that is live at that moment, though, was built for the mode being left: a group-rendered video was created with `loop = !!groupSync`, a wall-follower video with `isFollower` true, and both are captured in the closure at render time. A looping element never fires `ended`, and a follower's handler declines to advance — so nothing self-advances and nothing re-renders. It now re-renders whenever the element on screen is still looping, rather than guessing which media types can look after themselves. Tizen had the same freeze by a different route. GroupSyncController.exit and WallController.exit both call player.invalidate() for exactly this purpose, but invalidate only cleared the change signature — and load() returns at the continuity check ("current item survives, just retarget the index") before reaching any render, so the invalidate was a no-op. It now forces the next load to re-render, which is what those call sites always intended. On Tizen this froze every item type, not just video, because `single` skips the timer in all of the renderers. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Uaeo9MvzKoyXuN6ZsbhtkL --- server/player/index.html | 16 +++++++++++++++- tizen/js/player.js | 14 ++++++++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/server/player/index.html b/server/player/index.html index 95681ee..317bbe2 100644 --- a/server/player/index.html +++ b/server/player/index.html @@ -1606,7 +1606,21 @@ 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/')); + // Video and YouTube were excluded here on the grounds that they "self-advance via their own + // end handlers" — but the handler that is live right now was built for the mode we have just + // LEFT. A group-rendered video was created with `loop = !!groupSync` and a wall-follower + // video with `isFollower` true, and both are captured in the closure at render time. So on + // leaving a sync group or a wall, that element loops forever and nothing re-renders: the + // screen sits on one clip permanently, and later refreshes take the "unchanged" branch + // because the