diff --git a/plugins.json b/plugins.json index aa1ad9bb..5aa8ac90 100644 --- a/plugins.json +++ b/plugins.json @@ -1,6 +1,6 @@ { "version": "1.0.0", - "last_updated": "2026-07-08", + "last_updated": "2026-07-10", "plugins": [ { "id": "cricket-scoreboard", @@ -27,7 +27,7 @@ "last_updated": "2026-07-10", "verified": true, "screenshot": "", - "latest_version": "1.0.0" + "latest_version": "1.0.1" }, { "id": "7-segment-clock", diff --git a/plugins/cricket-scoreboard/config_schema.json b/plugins/cricket-scoreboard/config_schema.json index d64262b3..224166f7 100644 --- a/plugins/cricket-scoreboard/config_schema.json +++ b/plugins/cricket-scoreboard/config_schema.json @@ -13,6 +13,7 @@ "type": "array", "items": { "type": "string", + "minLength": 1, "description": "National team name or abbreviation (e.g. 'India', 'IND', 'Australia', 'England')" }, "uniqueItems": true, diff --git a/plugins/cricket-scoreboard/cricket_data_fetcher.py b/plugins/cricket-scoreboard/cricket_data_fetcher.py index 110d06b0..924c8f7c 100644 --- a/plugins/cricket-scoreboard/cricket_data_fetcher.py +++ b/plugins/cricket-scoreboard/cricket_data_fetcher.py @@ -108,6 +108,8 @@ def overs_to_balls(overs: float) -> int: overs = float(overs) except (TypeError, ValueError): return 0 + if overs < 0: + return 0 whole = int(math.floor(overs)) balls = int(round((overs - whole) * 10)) if balls >= 6: @@ -202,7 +204,11 @@ def _get_json(self, url: str) -> Optional[Dict[str, Any]]: try: resp = self.session.get(url, headers=_HEADERS, timeout=self.request_timeout) if resp.status_code == 200: - return resp.json() + payload = resp.json() + if isinstance(payload, dict): + return payload + logger.warning("Cricket API %s returned non-object JSON", url) + return None logger.warning("Cricket API %s returned HTTP %s", url, resp.status_code) except Exception as e: # pragma: no cover - network failure path logger.warning("Cricket API request failed for %s: %s", url, e) @@ -245,16 +251,17 @@ def discover_series(self, force: bool = False) -> List[Dict[str, Any]]: and now - self._last_discovery < self._discovery_interval): return self._resolved_series - cache_key = "cricket:series_ids" + enabled_keys = list(self.config.get("favorite_competitions", + ["international", "ipl", "bbl"])) + favorite_teams = [t.lower() for t in self.config.get("favorite_teams", [])] + + cache_key = "cricket:series_ids:" + "|".join( + sorted(enabled_keys) + sorted(favorite_teams)) cached = self._cache_get(cache_key, max_age=self._discovery_interval) if cached and not force: self._resolved_series = cached self._last_discovery = now return cached - - enabled_keys = list(self.config.get("favorite_competitions", - ["international", "ipl", "bbl"])) - favorite_teams = [t.lower() for t in self.config.get("favorite_teams", [])] follow_international = "international" in enabled_keys data = self._get_json(HEADER_URL) diff --git a/plugins/cricket-scoreboard/logo_downloader.py b/plugins/cricket-scoreboard/cricket_logo_downloader.py similarity index 97% rename from plugins/cricket-scoreboard/logo_downloader.py rename to plugins/cricket-scoreboard/cricket_logo_downloader.py index 84eab918..f100695a 100644 --- a/plugins/cricket-scoreboard/logo_downloader.py +++ b/plugins/cricket-scoreboard/cricket_logo_downloader.py @@ -100,6 +100,9 @@ def download_missing_logo(sport_key: str, team_id: str, team_abbr: str, logo_pat return False # If we have a logo URL, try to download it + if logo_url and not logo_url.lower().startswith("https://"): + logger.warning(f"Skipping non-HTTPS logo URL for {team_abbr}: {logo_url}") + logo_url = None if logo_url: try: response = _downloader.session.get(logo_url, headers=_downloader.headers, timeout=_downloader.request_timeout) diff --git a/plugins/cricket-scoreboard/manager.py b/plugins/cricket-scoreboard/manager.py index 38e32524..5ceee124 100644 --- a/plugins/cricket-scoreboard/manager.py +++ b/plugins/cricket-scoreboard/manager.py @@ -37,7 +37,7 @@ from cricket_renderer import CricketRenderer try: - from logo_downloader import download_missing_logo + from cricket_logo_downloader import download_missing_logo except ImportError: # pragma: no cover download_missing_logo = None @@ -241,10 +241,13 @@ def _ensure_logos(self, match: Dict[str, Any]) -> None: return for team in match.get("teams", []): abbr = (team.get("abbr") or "").upper() + abbr = "".join(c for c in abbr if c.isalnum() or c in ("&", "_")) url = team.get("logo_url") if not abbr or not url: continue path = self.logos_dir / f"{abbr}.png" + if path.parent != self.logos_dir: + continue if path.exists(): continue try: @@ -435,10 +438,15 @@ def on_config_change(self, new_config: Dict[str, Any]) -> None: try: super().on_config_change(new_config) except Exception: - pass - # Re-run __init__ derived state without recreating the object. + self.logger.debug("BasePlugin on_config_change failed", exc_info=True) + # Re-run __init__ derived state without recreating the object, but + # preserve the lock in place -- another thread may be holding it + # inside update()/display(), and replacing it would break mutual + # exclusion. + lock = self._lock self.__init__(self.plugin_id, new_config, self.display_manager, self.cache_manager, self.plugin_manager) + self._lock = lock # Force a re-discovery + refetch on next update. self._last_update = 0.0 diff --git a/plugins/cricket-scoreboard/manifest.json b/plugins/cricket-scoreboard/manifest.json index 86c70be5..cfb8479a 100644 --- a/plugins/cricket-scoreboard/manifest.json +++ b/plugins/cricket-scoreboard/manifest.json @@ -1,7 +1,7 @@ { "id": "cricket-scoreboard", "name": "Cricket Scoreboard", - "version": "1.0.0", + "version": "1.0.1", "author": "ChuckBuilds", "description": "Live, recent, and upcoming cricket matches across international tours (Test/ODI/T20I) and major domestic T20 leagues including the IPL, Big Bash League, The Hundred, PSL, CPL, SA20 and more. Shows runs/wickets/overs, run rates, targets, and match results.", "category": "sports", @@ -22,6 +22,12 @@ "cricket_upcoming" ], "versions": [ + { + "released": "2026-07-10", + "version": "1.0.1", + "notes": "Fix CodeRabbit findings: reject empty favorite_teams entries, reject negative overs in overs_to_balls, validate ESPN JSON response shape, scope series discovery cache by configured filters, rename logo_downloader.py to cricket_logo_downloader.py to avoid cross-plugin module collisions, require HTTPS logo URLs and sanitize team abbreviations used in logo filenames, and preserve the update lock across on_config_change.", + "ledmatrix_min": "2.0.0" + }, { "released": "2026-07-10", "version": "1.0.0",