From 5ddf8b1aea81806c3c7da29fff36c3cea9785f19 Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Fri, 27 Mar 2026 20:40:06 -0400 Subject: [PATCH] fix: live priority now interrupts long display durations (#196) (#298) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: check live priority during display loops to interrupt long durations (#196) _check_live_priority() was only called once per main loop iteration, before entering the display duration loop. With dynamic duration enabled, the loop could run for 60-120+ seconds without ever checking if a favorite team's live game started — so the display stayed on leaderboard, weather, etc. while the live game played. Now both the high-FPS and normal FPS display loops check for live priority every ~30 seconds (throttled to avoid overhead). When live content is detected, the loop breaks immediately and switches to the live game mode. Co-Authored-By: Claude Opus 4.6 (1M context) * fix: update rotation index when live priority interrupts display loop The live priority break set current_display_mode but not current_mode_index, so the post-loop rotation logic (which checks the old active_mode) would overwrite the live mode on the next advance. Now both loops also set current_mode_index to match the live mode, mirroring the existing pattern at the top of the main loop (line 1385). Co-Authored-By: Claude Opus 4.6 (1M context) * fix: use timestamp throttle for live priority and skip post-loop rotation Two issues fixed: 1. The modulo-based throttle (elapsed % 30.0 < display_interval) could miss the narrow 8ms window due to timing jitter. Replaced with an explicit timestamp check (_next_live_priority_check) that fires reliably every 30 seconds. 2. After breaking out of the display loop for live priority, the post-loop code (remaining-duration sleep and rotation advancement) would still run and overwrite the live mode. Now a continue skips directly to the next main loop iteration when current_display_mode was changed during the loop. Co-Authored-By: Claude Opus 4.6 (1M context) --------- Co-authored-by: Claude Opus 4.6 (1M context) --- src/display_controller.py | 47 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/src/display_controller.py b/src/display_controller.py index 55e2ed61..6fed01b6 100644 --- a/src/display_controller.py +++ b/src/display_controller.py @@ -79,7 +79,8 @@ class DisplayController: logger.info("Display modes initialized in %.3f seconds", time.time() - init_time) self.force_change = False - + self._next_live_priority_check = 0.0 # timestamp for throttled live priority checks + # All sports and content managers now handled via plugins logger.info("All sports and content managers now handled via plugin system") @@ -1790,11 +1791,29 @@ class DisplayController: self._poll_on_demand_requests() self._check_on_demand_expiration() + # Check for live priority every ~30s so live + # games can interrupt long display durations + elapsed = time.time() - start_time + now = time.time() + if not self.on_demand_active and now >= self._next_live_priority_check: + self._next_live_priority_check = now + 30.0 + live_mode = self._check_live_priority() + if live_mode and live_mode != active_mode: + logger.info("Live priority detected during high-FPS loop: %s", live_mode) + self.current_display_mode = live_mode + self.force_change = True + try: + self.current_mode_index = self.available_modes.index(live_mode) + except ValueError: + pass + # continue the main while loop to skip + # post-loop rotation/sleep logic + break + if self.current_display_mode != active_mode: logger.debug("Mode changed during high-FPS loop, breaking early") break - elapsed = time.time() - start_time if elapsed >= target_duration: logger.debug( "Reached high-FPS target duration %.2fs for mode %s", @@ -1853,6 +1872,23 @@ class DisplayController: self._poll_on_demand_requests() self._check_on_demand_expiration() + + # Check for live priority every ~30s so live + # games can interrupt long display durations + now = time.time() + if not self.on_demand_active and now >= self._next_live_priority_check: + self._next_live_priority_check = now + 30.0 + live_mode = self._check_live_priority() + if live_mode and live_mode != active_mode: + logger.info("Live priority detected during display loop: %s", live_mode) + self.current_display_mode = live_mode + self.force_change = True + try: + self.current_mode_index = self.available_modes.index(live_mode) + except ValueError: + pass + break + if self.current_display_mode != active_mode: logger.info("Mode changed during display loop from %s to %s, breaking early", active_mode, self.current_display_mode) break @@ -1866,6 +1902,13 @@ class DisplayController: loop_completed = True break + # If live priority preempted the display loop, skip + # all post-loop logic (remaining sleep, rotation) and + # restart the main loop so the live mode displays + # immediately. + if self.current_display_mode != active_mode: + continue + # Ensure we honour minimum duration when not dynamic and loop ended early if ( not dynamic_enabled