fix(display): Vegas excluded plugins always showing as checked (#332)

* fix(cache): check odds keys before generic live check in get_data_type_from_key

Cache keys like odds_espn_basketball_nba_<id>_live contain both 'odds'
and 'live'. The previous ordering matched the generic 'live' check first,
returning 'sports_live' (30 s TTL) instead of the correct 'odds_live'
(120 s TTL). This caused the ESPN odds API to be hit every 30 s per live
game, frequently triggering the 3-second per-request timeout and returning
no odds data.

Moving the 'odds' check above the generic 'live' block restores the
correct 120-second cache TTL for in-progress game odds.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(display): use single-quoted HTML attributes for JSON hidden inputs

Placing |tojson output (which contains double quotes) inside a
double-quoted HTML attribute broke the attribute — browsers closed
the attribute at the first inner quote, leaving JS with an empty or
truncated value. JSON.parse then failed silently, leaving excluded=[]
so all Vegas scroll plugins appeared checked (included) regardless of
the actual excluded_plugins config.

Switch to single-quoted HTML attributes so the JSON double quotes
are valid inside the attribute value.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
sarjent
2026-05-14 17:09:16 -05:00
committed by GitHub
parent 1c4d5c5271
commit 6a4644007d
2 changed files with 15 additions and 13 deletions

View File

@@ -194,18 +194,20 @@ class CacheStrategy:
""" """
key_lower = key.lower() key_lower = key.lower()
# Odds data — checked FIRST because odds keys may also contain 'live'/'current' # Odds data — checked before the generic 'live' block below because
# (e.g. odds_espn_nba_game_123_live). The odds TTL (120s for live, 1800s for # live-odds cache keys (e.g. odds_espn_basketball_nba_<id>_live) contain
# upcoming) must win over the generic sports_live TTL (30s) to avoid hitting # both 'odds' AND 'live'. Without this ordering the 'live' check below
# the ESPN odds API every 30 seconds per game. # would match first and return 'sports_live' (30 s TTL) instead of the
# correct 'odds_live' (120 s TTL).
if 'odds' in key_lower: if 'odds' in key_lower:
# For live games, use shorter cache; for upcoming games, use longer cache
if any(x in key_lower for x in ['live', 'current']): if any(x in key_lower for x in ['live', 'current']):
return 'odds_live' # Live odds change more frequently (120s TTL) return 'odds_live' # Live odds change more frequently
return 'odds' # Regular odds for upcoming games (1800s TTL) return 'odds' # Regular odds for upcoming games
# Live sports data (only reached if key does NOT contain 'odds') # Live sports data
if any(x in key_lower for x in ['live', 'current', 'scoreboard']): if any(x in key_lower for x in ['live', 'current', 'scoreboard']):
if 'soccer' in key_lower:
return 'sports_live' # Soccer live data is very time-sensitive
return 'sports_live' return 'sports_live'
# Weather data # Weather data

View File

@@ -380,8 +380,8 @@
<!-- Plugin order list will be populated by JavaScript --> <!-- Plugin order list will be populated by JavaScript -->
<p class="text-sm text-gray-500 italic">Loading plugins...</p> <p class="text-sm text-gray-500 italic">Loading plugins...</p>
</div> </div>
<input type="hidden" id="vegas_plugin_order_value" name="vegas_plugin_order" value="{{ main_config.display.get('vegas_scroll', {}).get('plugin_order', [])|tojson }}"> <input type="hidden" id="vegas_plugin_order_value" name="vegas_plugin_order" value='{{ main_config.display.get("vegas_scroll", {}).get("plugin_order", [])|tojson }}'>
<input type="hidden" id="vegas_excluded_plugins_value" name="vegas_excluded_plugins" value="{{ main_config.display.get('vegas_scroll', {}).get('excluded_plugins', [])|tojson }}"> <input type="hidden" id="vegas_excluded_plugins_value" name="vegas_excluded_plugins" value='{{ main_config.display.get("vegas_scroll", {}).get("excluded_plugins", [])|tojson }}'>
</div> </div>
</div> </div>
</div> </div>