Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 18 additions & 18 deletions plugins.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@
"last_updated": "2026-10-05",
"verified": true,
"screenshot": "",
"latest_version": "1.58.0",
"latest_version": "1.59.0",
"ledmatrix_min_version": "3.8.1",
"commit": "540fd24022abf04f66b0657d923cac8ee0453aad"
"commit": "80aba717c9cd67d91d047ae9c5eb9607b9d584ad"
},
{
"id": "basketball-scoreboard",
Expand All @@ -107,9 +107,9 @@
"last_updated": "2026-10-05",
"verified": true,
"screenshot": "",
"latest_version": "1.43.0",
"latest_version": "1.44.0",
"ledmatrix_min_version": "3.8.1",
"commit": "540fd24022abf04f66b0657d923cac8ee0453aad"
"commit": "80aba717c9cd67d91d047ae9c5eb9607b9d584ad"
},
{
"id": "calendar",
Expand Down Expand Up @@ -283,9 +283,9 @@
"last_updated": "2026-10-05",
"verified": true,
"screenshot": "",
"latest_version": "3.19.0",
"latest_version": "3.20.0",
"ledmatrix_min_version": "3.8.1",
"commit": "540fd24022abf04f66b0657d923cac8ee0453aad"
"commit": "80aba717c9cd67d91d047ae9c5eb9607b9d584ad"
},
{
"id": "geochron",
Expand Down Expand Up @@ -385,10 +385,10 @@
"last_updated": "2026-10-05",
"verified": true,
"screenshot": "",
"latest_version": "1.43.0",
"latest_version": "1.44.0",
"icon": "fas fa-hockey-puck",
"ledmatrix_min_version": "3.8.1",
"commit": "540fd24022abf04f66b0657d923cac8ee0453aad"
"commit": "80aba717c9cd67d91d047ae9c5eb9607b9d584ad"
},
{
"id": "lacrosse-scoreboard",
Expand Down Expand Up @@ -710,12 +710,12 @@
"plugin_path": "plugins/odds-ticker",
"stars": 0,
"downloads": 0,
"last_updated": "2026-10-02",
"last_updated": "2026-10-05",
"verified": true,
"screenshot": "",
"latest_version": "1.7.7",
"ledmatrix_min_version": "3.4.0",
"commit": "886d2038d22a35c962beac0ea261543339cffec9"
"latest_version": "1.8.0",
"ledmatrix_min_version": "3.8.1",
"commit": "80aba717c9cd67d91d047ae9c5eb9607b9d584ad"
},
{
"id": "of-the-day",
Expand Down Expand Up @@ -847,9 +847,9 @@
"last_updated": "2026-10-05",
"verified": true,
"screenshot": "",
"latest_version": "2.40.0",
"latest_version": "2.41.0",
"ledmatrix_min_version": "3.8.1",
"commit": "540fd24022abf04f66b0657d923cac8ee0453aad"
"commit": "80aba717c9cd67d91d047ae9c5eb9607b9d584ad"
},
{
"id": "static-image",
Expand Down Expand Up @@ -1164,10 +1164,10 @@
"downloads": 0,
"verified": true,
"screenshot": "",
"latest_version": "1.36.0",
"latest_version": "1.37.0",
"last_updated": "2026-10-05",
"ledmatrix_min_version": "3.8.1",
"commit": "540fd24022abf04f66b0657d923cac8ee0453aad"
"commit": "80aba717c9cd67d91d047ae9c5eb9607b9d584ad"
},
{
"id": "tidbyt-baseball-scoreboard",
Expand Down Expand Up @@ -1213,9 +1213,9 @@
"last_updated": "2026-10-05",
"verified": true,
"screenshot": "",
"latest_version": "1.35.0",
"latest_version": "1.36.0",
"ledmatrix_min_version": "3.8.1",
"commit": "540fd24022abf04f66b0657d923cac8ee0453aad"
"commit": "80aba717c9cd67d91d047ae9c5eb9607b9d584ad"
},
{
"id": "jellyfin-now-playing",
Expand Down
10 changes: 10 additions & 0 deletions plugins/afl-scoreboard/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## [1.37.0] - 2026-10-05

### Changed
- Schedule windows are cached under core's shared ESPN scoreboard key
(espn_scoreboard_cache_key, fetch service stage 2), so every plugin showing
a league shares one fetch and one cached copy. The key this plugin used
before is still read, so an upgrade serves the copy already on disk instead
of refetching every league at once; a window that moves on a day deletes its
copy from the day before. Requires LEDMatrix core 3.8.1.

## [1.36.0] - 2026-10-05

### Changed
Expand Down
8 changes: 6 additions & 2 deletions plugins/afl-scoreboard/afl_managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
Live/Recent/Upcoming managers.
"""

from src.common.espn_dates import espn_scoreboard_cache_key_for_url
import logging
from datetime import datetime, timedelta
from typing import Any, Dict, Optional
Expand Down Expand Up @@ -92,12 +93,15 @@ def _fetch_afl_api_data(self, use_cache: bool = True) -> Optional[Dict]:
end_date = now + timedelta(days=self.schedule_lookahead_days)
date_str = f"{start_date.strftime('%Y%m%d')}-{end_date.strftime('%Y%m%d')}"

cache_key = f"afl_schedule_{date_str}"
url = f"{ESPN_AFL_BASE_URL}/{self.league_key}/scoreboard"
# The canonical key (core espn_scoreboard_cache_key), shared with
# every plugin showing this league; the old one is read until it ages out.
legacy_key = f"afl_schedule_{date_str}"
cache_key = espn_scoreboard_cache_key_for_url(url, date_str) or legacy_key

# Check cache first
if use_cache:
cached_data = self.cache_manager.get(cache_key)
cached_data = self._cached_schedule(cache_key, (legacy_key,))
if cached_data:
# Validate cached data structure
if isinstance(cached_data, dict) and "events" in cached_data:
Expand Down
8 changes: 7 additions & 1 deletion plugins/afl-scoreboard/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "afl-scoreboard",
"name": "AFL Scoreboard",
"version": "1.36.0",
"version": "1.37.0",
"author": "ChuckBuilds",
"description": "Live, recent, and upcoming AFL (Australian Football League) games with real-time scores and game status.",
"category": "sports",
Expand All @@ -18,6 +18,12 @@
"afl_upcoming"
],
"versions": [
{
"version": "1.37.0",
"released": "2026-10-05",
"ledmatrix_min_version": "3.8.1",
"notes": "Schedule windows are cached under core's shared ESPN scoreboard key (espn_scoreboard_cache_key, fetch service stage 2), so every plugin showing a league shares one fetch and one cached copy. The key this plugin used before is still read, so an upgrade serves the copy already on disk instead of refetching every league at once; a window that moves on a day deletes its copy from the day before. Requires LEDMatrix core 3.8.1."
},
{
"version": "1.36.0",
"released": "2026-10-05",
Expand Down
13 changes: 13 additions & 0 deletions plugins/baseball-scoreboard/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# Changelog

## [1.59.0] - 2026-10-05

### Changed
- Schedule windows are cached under core's shared ESPN scoreboard key
(espn_scoreboard_cache_key, fetch service stage 2), so every plugin showing
a league shares one fetch and one cached copy. The key this plugin used
before is still read, so an upgrade serves the copy already on disk instead
of refetching every league at once; a window that moves on a day deletes its
copy from the day before. Requires LEDMatrix core 3.8.1.
- The live poll also leaves today's scoreboard under that key (it still asks
ESPN on every poll), so odds-ticker reuses it instead of fetching the same
day again.

## [1.58.0] - 2026-10-05

### Changed
Expand Down
8 changes: 7 additions & 1 deletion plugins/baseball-scoreboard/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "baseball-scoreboard",
"name": "Baseball Scoreboard",
"version": "1.58.0",
"version": "1.59.0",
"update_interval": 60,
"author": "ChuckBuilds",
"description": "Live, recent, and upcoming baseball games across MLB, MiLB, and NCAA Baseball with real-time scores and schedules",
Expand Down Expand Up @@ -31,6 +31,12 @@
"branch": "main",
"plugin_path": "plugins/baseball-scoreboard",
"versions": [
{
"version": "1.59.0",
"released": "2026-10-05",
"ledmatrix_min_version": "3.8.1",
"notes": "Schedule windows are cached under core's shared ESPN scoreboard key (espn_scoreboard_cache_key, fetch service stage 2), so every plugin showing a league shares one fetch and one cached copy. The key this plugin used before is still read, so an upgrade serves the copy already on disk instead of refetching every league at once; a window that moves on a day deletes its copy from the day before. The live poll also leaves today's scoreboard under that key (it still asks ESPN on every poll), so odds-ticker reuses it instead of fetching the same day again. Requires LEDMatrix core 3.8.1."
},
{
"version": "1.58.0",
"released": "2026-10-05",
Expand Down
7 changes: 5 additions & 2 deletions plugins/baseball-scoreboard/mlb_managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,14 @@ def _fetch_mlb_api_data(self, use_cache: bool = True) -> Optional[Dict]:
season_year = now.year - 1
# Only what Recent and Upcoming can show; see _schedule_window.
datestring, window = self._schedule_window()
cache_key = f"{self.sport_key}_schedule_{window}"
# The canonical key (core espn_scoreboard_cache_key), shared with
# every plugin showing this league; the old one is read until it ages out.
cache_key = self._schedule_cache_key(datestring)
legacy_key = f"{self.sport_key}_schedule_{window}"

# Check cache first
if use_cache:
cached_data = self.cache_manager.get(cache_key)
cached_data = self._cached_schedule(cache_key, (legacy_key,))
if cached_data:
# Validate cached data structure
if isinstance(cached_data, dict) and "events" in cached_data:
Expand Down
7 changes: 5 additions & 2 deletions plugins/baseball-scoreboard/ncaa_baseball_managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,13 @@ def _fetch_ncaa_baseball_api_data(self, use_cache: bool = True) -> Optional[Dict
season_year = now.year - 1
# Only what Recent and Upcoming can show; see _schedule_window.
datestring, window = self._schedule_window()
cache_key = f"ncaa_baseball_schedule_{window}"
# The canonical key (core espn_scoreboard_cache_key), shared with
# every plugin showing this league; the old one is read until it ages out.
cache_key = self._schedule_cache_key(datestring)
legacy_key = f"ncaa_baseball_schedule_{window}"

if use_cache:
cached_data = self.cache_manager.get(cache_key)
cached_data = self._cached_schedule(cache_key, (legacy_key,))
if cached_data:
# Validate cached data structure
if isinstance(cached_data, dict) and "events" in cached_data:
Expand Down
21 changes: 13 additions & 8 deletions plugins/baseball-scoreboard/sports.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@ def resolve_font_name(font_name: str) -> str:
from src.base_odds_manager import BaseOddsManager
from data_sources import ESPNDataSource
# ESPN date-range helper: core ships it from 3.5.0, the manifest's floor.
from src.common.espn_dates import ESPN_MAX_LIMIT, fetch_espn_scoreboard
from src.common.espn_dates import (
ESPN_MAX_LIMIT,
fetch_espn_scoreboard,
get_espn_scoreboard,
)
from baseball_timezone import resolve_timezone
from src.common.sports_shared import (
SportsCoreSharedMixin, SportsLiveSharedMixin, SportsRecentSharedMixin)
Expand Down Expand Up @@ -1413,13 +1417,14 @@ def _fetch_todays_games(self) -> Optional[Dict]:
f"Fetching games for {self.sport}/{self.league} over date range "
f"{dates_param}"
)
data = fetch_espn_scoreboard(
self.session,
url,
params={"dates": dates_param, "limit": ESPN_MAX_LIMIT},
headers=self.headers,
timeout=10,
logger=self.logger,
# Always asked (max_age=0: the live poll's own interval decides),
# and left under core's shared key for this scoreboard and date
# (espn_scoreboard_cache_key), so odds-ticker and anything else
# showing the same day reuse it instead of fetching it again.
data = get_espn_scoreboard(
self.session, self.sport, self.league, dates_param,
cache_manager=self.cache_manager, max_age=0,
headers=self.headers, timeout=10, logger=self.logger,
)
events = data.get("events", [])

Expand Down
14 changes: 14 additions & 0 deletions plugins/basketball-scoreboard/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Changelog

## [1.44.0] - 2026-10-05

### Changed
- Schedule windows are cached under core's shared ESPN scoreboard key
(espn_scoreboard_cache_key, fetch service stage 2), so every plugin showing
a league shares one fetch and one cached copy. The key this plugin used
before is still read, so an upgrade serves the copy already on disk instead
of refetching every league at once; a window that moves on a day deletes its
copy from the day before. Requires LEDMatrix core 3.8.1.
- The live poll's 30-second cache moves from `<sport_key>_scoreboard_current`,
whatever the dates, to the shared key for the dates it asks, so odds-ticker
and the scoreboard read each other's copy; the old key is read for a
release.

## [1.43.0] - 2026-10-05

### Changed
Expand Down
8 changes: 7 additions & 1 deletion plugins/basketball-scoreboard/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "basketball-scoreboard",
"name": "Basketball Scoreboard",
"version": "1.43.0",
"version": "1.44.0",
"update_interval": 60,
"description": "Live, recent, and upcoming basketball games across NBA, NCAA Men's, NCAA Women's, and WNBA with real-time scores, schedules, and March Madness tournament support",
"author": "ChuckBuilds",
Expand All @@ -19,6 +19,12 @@
"branch": "main",
"plugin_path": "plugins/basketball-scoreboard",
"versions": [
{
"version": "1.44.0",
"released": "2026-10-05",
"ledmatrix_min_version": "3.8.1",
"notes": "Schedule windows are cached under core's shared ESPN scoreboard key (espn_scoreboard_cache_key, fetch service stage 2), so every plugin showing a league shares one fetch and one cached copy. The key this plugin used before is still read, so an upgrade serves the copy already on disk instead of refetching every league at once; a window that moves on a day deletes its copy from the day before. The live poll's 30-second cache moves from <sport_key>_scoreboard_current, whatever the dates, to the shared key for the dates it asks, so odds-ticker and the scoreboard read each other's copy; the old key is read for a release. Requires LEDMatrix core 3.8.1."
},
{
"version": "1.43.0",
"released": "2026-10-05",
Expand Down
7 changes: 5 additions & 2 deletions plugins/basketball-scoreboard/nba_managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,14 @@ def _fetch_nba_api_data(self, use_cache: bool = True) -> Optional[Dict]:
season_year = now.year - 1
# Only what Recent and Upcoming can show; see _schedule_window.
datestring, window = self._schedule_window()
cache_key = f"{self.sport_key}_schedule_{window}"
# The canonical key (core espn_scoreboard_cache_key), shared with
# every plugin showing this league; the old one is read until it ages out.
cache_key = self._schedule_cache_key(datestring)
legacy_key = f"{self.sport_key}_schedule_{window}"

# Check cache first
if use_cache:
cached_data = self.cache_manager.get(cache_key)
cached_data = self._cached_schedule(cache_key, (legacy_key,))
if cached_data:
# Validate cached data structure
if isinstance(cached_data, dict) and "events" in cached_data:
Expand Down
32 changes: 21 additions & 11 deletions plugins/basketball-scoreboard/sports.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@
from dynamic_team_resolver import DynamicTeamResolver
from data_sources import ESPNDataSource
# ESPN date-range helper: core ships it from 3.5.0, the manifest's floor.
from src.common.espn_dates import ESPN_MAX_LIMIT, fetch_espn_scoreboard
from src.common.espn_dates import (
ESPN_MAX_LIMIT,
espn_scoreboard_cache_key,
fetch_espn_scoreboard,
read_espn_scoreboard_cache,
store_espn_scoreboard_cache,
)
from basketball_timezone import resolve_timezone

# Import main logo downloader (same as football plugin)
Expand Down Expand Up @@ -1652,14 +1658,6 @@ def _fetch_todays_games(self) -> Optional[Dict]:
# This works around the date range limitation
url = f"https://site.api.espn.com/apis/site/v2/sports/{self.sport}/{self.league}/scoreboard"

# Check cache first (short TTL for live data)
cache_key = f"{self.sport_key}_scoreboard_current"
cached_data = self.cache_manager.get(cache_key, max_age=30) # 30s cache for live data
if cached_data:
if isinstance(cached_data, dict) and "events" in cached_data:
self.logger.debug(f"Using cached current scoreboard for {self.sport}/{self.league}")
return cached_data

# For NCAA Basketball, don't use dates parameter (it causes 404)
# For other sports, use today's date
if self.league in ["mens-college-basketball", "womens-college-basketball"]:
Expand All @@ -1681,6 +1679,18 @@ def _fetch_todays_games(self) -> Optional[Dict]:
params = {"dates": dates_param, "limit": ESPN_MAX_LIMIT}
self.logger.debug(f"Fetching today's games for {self.sport}/{self.league} on dates {dates_param}")

# Shared with any plugin showing the same scoreboard and dates
# (core espn_scoreboard_cache_key; this was
# "<sport_key>_scoreboard_current" whatever the dates). At most
# 30 s old, as before; the old key is still read for a release.
cache_key = espn_scoreboard_cache_key(self.sport, self.league, params.get("dates"))
cached_data = read_espn_scoreboard_cache(
self.cache_manager, cache_key, 30,
legacy_keys=(f"{self.sport_key}_scoreboard_current",))
if cached_data and "events" in cached_data:
self.logger.debug(f"Using cached current scoreboard for {self.sport}/{self.league}")
return cached_data

data = fetch_espn_scoreboard(
self.session,
url,
Expand All @@ -1707,8 +1717,8 @@ def _fetch_todays_games(self) -> Optional[Dict]:
f"shortDetail={status_type.get('shortDetail', 'N/A')}"
)

# Cache the result (short TTL for live data)
self.cache_manager.set(cache_key, data)
# Cache the result for the next 30 s, for every reader
store_espn_scoreboard_cache(self.cache_manager, cache_key, data)
return {"events": events}
except requests.exceptions.RequestException as e:
self.logger.error(
Expand Down
Loading
Loading