mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-04-10 13:02:59 +00:00
Create Basketball Base Class and Consolidate Basketball Managers (#97)
* Create basketball Base class * Migrate NBA * Add NCAA Mens Basketball * Add NCAA Women's Basketball * Add WNBA --------- Co-authored-by: Alex Resnick <adr8282@gmail.com>
This commit is contained in:
0
assets/sports/wnba_logos/.keep
Normal file
0
assets/sports/wnba_logos/.keep
Normal file
BIN
assets/sports/wnba_logos/LV.png
Normal file
BIN
assets/sports/wnba_logos/LV.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
BIN
assets/sports/wnba_logos/PHX.png
Normal file
BIN
assets/sports/wnba_logos/PHX.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 59 KiB |
@@ -267,6 +267,7 @@
|
||||
"recent_games_to_show": 1,
|
||||
"upcoming_games_to_show": 1,
|
||||
"show_favorite_teams_only": true,
|
||||
"show_all_live": false,
|
||||
"favorite_teams": [
|
||||
"DAL"
|
||||
],
|
||||
@@ -285,6 +286,40 @@
|
||||
"nba_upcoming": true
|
||||
}
|
||||
},
|
||||
"wnba_scoreboard": {
|
||||
"enabled": false,
|
||||
"live_priority": true,
|
||||
"live_game_duration": 20,
|
||||
"show_odds": true,
|
||||
"test_mode": false,
|
||||
"update_interval_seconds": 3600,
|
||||
"live_update_interval": 30,
|
||||
"live_odds_update_interval": 3600,
|
||||
"odds_update_interval": 3600,
|
||||
"recent_update_interval": 3600,
|
||||
"upcoming_update_interval": 3600,
|
||||
"recent_games_to_show": 1,
|
||||
"upcoming_games_to_show": 1,
|
||||
"show_favorite_teams_only": true,
|
||||
"show_all_live": false,
|
||||
"favorite_teams": [
|
||||
"CHI"
|
||||
],
|
||||
"logo_dir": "assets/sports/wnba_logos",
|
||||
"show_records": true,
|
||||
"background_service": {
|
||||
"enabled": true,
|
||||
"max_workers": 3,
|
||||
"request_timeout": 30,
|
||||
"max_retries": 3,
|
||||
"priority": 2
|
||||
},
|
||||
"display_modes": {
|
||||
"wnba_live": true,
|
||||
"wnba_recent": true,
|
||||
"wnba_upcoming": true
|
||||
}
|
||||
},
|
||||
"nfl_scoreboard": {
|
||||
"enabled": false,
|
||||
"live_priority": true,
|
||||
@@ -388,6 +423,7 @@
|
||||
"recent_games_to_show": 1,
|
||||
"upcoming_games_to_show": 1,
|
||||
"show_favorite_teams_only": true,
|
||||
"show_all_live": false,
|
||||
"favorite_teams": [
|
||||
"UGA",
|
||||
"AUB"
|
||||
@@ -400,6 +436,30 @@
|
||||
"ncaam_basketball_upcoming": true
|
||||
}
|
||||
},
|
||||
"ncaaw_basketball_scoreboard": {
|
||||
"enabled": false,
|
||||
"live_priority": true,
|
||||
"live_game_duration": 20,
|
||||
"show_odds": true,
|
||||
"test_mode": false,
|
||||
"update_interval_seconds": 3600,
|
||||
"live_update_interval": 30,
|
||||
"recent_games_to_show": 1,
|
||||
"upcoming_games_to_show": 1,
|
||||
"show_favorite_teams_only": true,
|
||||
"show_all_live": false,
|
||||
"favorite_teams": [
|
||||
"UGA",
|
||||
"AUB"
|
||||
],
|
||||
"logo_dir": "assets/sports/ncaa_logos",
|
||||
"show_records": true,
|
||||
"display_modes": {
|
||||
"ncaaw_basketball_live": true,
|
||||
"ncaaw_basketball_recent": true,
|
||||
"ncaaw_basketball_upcoming": true
|
||||
}
|
||||
},
|
||||
"ncaam_hockey_scoreboard": {
|
||||
"enabled": false,
|
||||
"live_priority": true,
|
||||
|
||||
309
src/base_classes/basketball.py
Normal file
309
src/base_classes/basketball.py
Normal file
@@ -0,0 +1,309 @@
|
||||
import logging
|
||||
import time
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
from src.base_classes.data_sources import ESPNDataSource
|
||||
from src.base_classes.sports import SportsCore, SportsLive
|
||||
from src.cache_manager import CacheManager
|
||||
from src.display_manager import DisplayManager
|
||||
|
||||
|
||||
class Basketball(SportsCore):
|
||||
"""Base class for basketball sports with common functionality."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: Dict[str, Any],
|
||||
display_manager: DisplayManager,
|
||||
cache_manager: CacheManager,
|
||||
logger: logging.Logger,
|
||||
sport_key: str,
|
||||
):
|
||||
super().__init__(config, display_manager, cache_manager, logger, sport_key)
|
||||
self.data_source = ESPNDataSource(logger)
|
||||
self.sport = "basketball"
|
||||
|
||||
def _extract_game_details(self, game_event: Dict) -> Optional[Dict]:
|
||||
"""Extract relevant game details from ESPN NCAA FB API response."""
|
||||
# --- THIS METHOD MAY NEED ADJUSTMENTS FOR NCAA FB API DIFFERENCES ---
|
||||
details, home_team, away_team, status, situation = (
|
||||
self._extract_game_details_common(game_event)
|
||||
)
|
||||
if details is None or home_team is None or away_team is None or status is None:
|
||||
return
|
||||
try:
|
||||
# Format period/quarter
|
||||
period = status.get("period", 0)
|
||||
period_text = ""
|
||||
if status["type"]["state"] == "in":
|
||||
if period == 0:
|
||||
period_text = "Start" # Before kickoff
|
||||
elif period >= 1 and period <= 4:
|
||||
period_text = f"Q{period}" # OT starts after Q4
|
||||
elif period > 4:
|
||||
period_text = f"OT{period - 4}" # OT starts after Q4
|
||||
elif status["type"]["state"] == "halftime" or status["type"]["name"] == "STATUS_HALFTIME": # Check explicit halftime state
|
||||
period_text = "HALF"
|
||||
elif status["type"]["state"] == "post":
|
||||
if period > 4 : period_text = "Final/OT"
|
||||
else: period_text = "Final"
|
||||
elif status["type"]["state"] == "pre":
|
||||
period_text = details.get("game_time", "") # Show time for upcoming
|
||||
|
||||
details.update({
|
||||
"period": period,
|
||||
"period_text": period_text, # Formatted quarter/status
|
||||
"clock": status.get("displayClock", "0:00"),
|
||||
})
|
||||
|
||||
# Basic validation (can be expanded)
|
||||
if not details["home_abbr"] or not details["away_abbr"]:
|
||||
self.logger.warning(
|
||||
f"Missing team abbreviation in event: {details['id']}"
|
||||
)
|
||||
return None
|
||||
|
||||
self.logger.debug(
|
||||
f"Extracted: {details['away_abbr']}@{details['home_abbr']}, Status: {status['type']['name']}, Live: {details['is_live']}, Final: {details['is_final']}, Upcoming: {details['is_upcoming']}"
|
||||
)
|
||||
|
||||
return details
|
||||
except Exception as e:
|
||||
# Log the problematic event structure if possible
|
||||
self.logger.error(
|
||||
f"Error extracting game details: {e} from event: {game_event.get('id')}",
|
||||
exc_info=True,
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
class BasketballLive(Basketball, SportsLive):
|
||||
def __init__(
|
||||
self,
|
||||
config: Dict[str, Any],
|
||||
display_manager: DisplayManager,
|
||||
cache_manager: CacheManager,
|
||||
logger: logging.Logger,
|
||||
sport_key: str,
|
||||
):
|
||||
super().__init__(config, display_manager, cache_manager, logger, sport_key)
|
||||
|
||||
def _test_mode_update(self):
|
||||
if self.current_game and self.current_game["is_live"]:
|
||||
# For testing, we'll just update the clock to show it's working
|
||||
minutes = int(self.current_game["clock"].split(":")[0])
|
||||
seconds = int(self.current_game["clock"].split(":")[1])
|
||||
seconds -= 1
|
||||
if seconds < 0:
|
||||
seconds = 59
|
||||
minutes -= 1
|
||||
if minutes < 0:
|
||||
minutes = 19
|
||||
if self.current_game["period"] < 3:
|
||||
self.current_game["period"] += 1
|
||||
else:
|
||||
self.current_game["period"] = 1
|
||||
self.current_game["clock"] = f"{minutes:02d}:{seconds:02d}"
|
||||
# Always update display in test mode
|
||||
|
||||
def _draw_scorebug_layout(self, game: Dict, force_clear: bool = False) -> None:
|
||||
"""Draw the detailed scorebug layout for a live Basketball game.""" # Updated docstring
|
||||
try:
|
||||
main_img = Image.new(
|
||||
"RGBA", (self.display_width, self.display_height), (0, 0, 0, 255)
|
||||
)
|
||||
overlay = Image.new(
|
||||
"RGBA", (self.display_width, self.display_height), (0, 0, 0, 0)
|
||||
)
|
||||
draw_overlay = ImageDraw.Draw(
|
||||
overlay
|
||||
) # Draw text elements on overlay first
|
||||
home_logo = self._load_and_resize_logo(
|
||||
game["home_id"],
|
||||
game["home_abbr"],
|
||||
game["home_logo_path"],
|
||||
game.get("home_logo_url"),
|
||||
)
|
||||
away_logo = self._load_and_resize_logo(
|
||||
game["away_id"],
|
||||
game["away_abbr"],
|
||||
game["away_logo_path"],
|
||||
game.get("away_logo_url"),
|
||||
)
|
||||
|
||||
if not home_logo or not away_logo:
|
||||
self.logger.error(
|
||||
f"Failed to load logos for live game: {game.get('id')}"
|
||||
) # Changed log prefix
|
||||
# Draw placeholder text if logos fail
|
||||
draw_final = ImageDraw.Draw(main_img.convert("RGB"))
|
||||
self._draw_text_with_outline(
|
||||
draw_final, "Logo Error", (5, 5), self.fonts["status"]
|
||||
)
|
||||
self.display_manager.image.paste(main_img.convert("RGB"), (0, 0))
|
||||
self.display_manager.update_display()
|
||||
return
|
||||
|
||||
center_y = self.display_height // 2
|
||||
|
||||
# Draw logos (shifted slightly more inward than NHL perhaps)
|
||||
home_x = (
|
||||
self.display_width - home_logo.width + 10
|
||||
) # adjusted from 18 # Adjust position as needed
|
||||
home_y = center_y - (home_logo.height // 2)
|
||||
main_img.paste(home_logo, (home_x, home_y), home_logo)
|
||||
|
||||
away_x = -10 # adjusted from 18 # Adjust position as needed
|
||||
away_y = center_y - (away_logo.height // 2)
|
||||
main_img.paste(away_logo, (away_x, away_y), away_logo)
|
||||
|
||||
# --- Draw Text Elements on Overlay ---
|
||||
# Note: Rankings are now handled in the records/rankings section below
|
||||
|
||||
# Period/Quarter and Clock (Top center)
|
||||
period_clock_text = (
|
||||
f"{game.get('period_text', '')} {game.get('clock', '')}".strip()
|
||||
)
|
||||
|
||||
status_width = draw_overlay.textlength(
|
||||
period_clock_text, font=self.fonts["time"]
|
||||
)
|
||||
status_x = (self.display_width - status_width) // 2
|
||||
status_y = 1 # Position at top
|
||||
self._draw_text_with_outline(
|
||||
draw_overlay,
|
||||
period_clock_text,
|
||||
(status_x, status_y),
|
||||
self.fonts["time"],
|
||||
)
|
||||
|
||||
# Scores (centered, slightly above bottom)
|
||||
home_score = str(game.get("home_score", "0"))
|
||||
away_score = str(game.get("away_score", "0"))
|
||||
score_text = f"{away_score}-{home_score}"
|
||||
score_width = draw_overlay.textlength(score_text, font=self.fonts["score"])
|
||||
score_x = (self.display_width - score_width) // 2
|
||||
score_y = (
|
||||
self.display_height // 2
|
||||
) - 3 # centered #from 14 # Position score higher
|
||||
self._draw_text_with_outline(
|
||||
draw_overlay, score_text, (score_x, score_y), self.fonts["score"]
|
||||
)
|
||||
|
||||
# Draw odds if available
|
||||
if "odds" in game and game["odds"]:
|
||||
self._draw_dynamic_odds(
|
||||
draw_overlay, game["odds"], self.display_width, self.display_height
|
||||
)
|
||||
|
||||
# Draw records or rankings if enabled
|
||||
if self.show_records or self.show_ranking:
|
||||
try:
|
||||
record_font = ImageFont.truetype("assets/fonts/4x6-font.ttf", 6)
|
||||
self.logger.debug(f"Loaded 6px record font successfully")
|
||||
except IOError:
|
||||
record_font = ImageFont.load_default()
|
||||
self.logger.warning(
|
||||
f"Failed to load 6px font, using default font (size: {record_font.size})"
|
||||
)
|
||||
|
||||
# Get team abbreviations
|
||||
away_abbr = game.get("away_abbr", "")
|
||||
home_abbr = game.get("home_abbr", "")
|
||||
|
||||
record_bbox = draw_overlay.textbbox((0, 0), "0-0", font=record_font)
|
||||
record_height = record_bbox[3] - record_bbox[1]
|
||||
record_y = self.display_height - record_height - 1
|
||||
self.logger.debug(
|
||||
f"Record positioning: height={record_height}, record_y={record_y}, display_height={self.display_height}"
|
||||
)
|
||||
|
||||
# Display away team info
|
||||
if away_abbr:
|
||||
if self.show_ranking and self.show_records:
|
||||
# When both rankings and records are enabled, rankings replace records completely
|
||||
away_rank = self._team_rankings_cache.get(away_abbr, 0)
|
||||
if away_rank > 0:
|
||||
away_text = f"#{away_rank}"
|
||||
else:
|
||||
# Show nothing for unranked teams when rankings are prioritized
|
||||
away_text = ""
|
||||
elif self.show_ranking:
|
||||
# Show ranking only if available
|
||||
away_rank = self._team_rankings_cache.get(away_abbr, 0)
|
||||
if away_rank > 0:
|
||||
away_text = f"#{away_rank}"
|
||||
else:
|
||||
away_text = ""
|
||||
elif self.show_records:
|
||||
# Show record only when rankings are disabled
|
||||
away_text = game.get("away_record", "")
|
||||
else:
|
||||
away_text = ""
|
||||
|
||||
if away_text:
|
||||
away_record_x = 3
|
||||
self.logger.debug(
|
||||
f"Drawing away ranking '{away_text}' at ({away_record_x}, {record_y}) with font size {record_font.size if hasattr(record_font, 'size') else 'unknown'}"
|
||||
)
|
||||
self._draw_text_with_outline(
|
||||
draw_overlay,
|
||||
away_text,
|
||||
(away_record_x, record_y),
|
||||
record_font,
|
||||
)
|
||||
|
||||
# Display home team info
|
||||
if home_abbr:
|
||||
if self.show_ranking and self.show_records:
|
||||
# When both rankings and records are enabled, rankings replace records completely
|
||||
home_rank = self._team_rankings_cache.get(home_abbr, 0)
|
||||
if home_rank > 0:
|
||||
home_text = f"#{home_rank}"
|
||||
else:
|
||||
# Show nothing for unranked teams when rankings are prioritized
|
||||
home_text = ""
|
||||
elif self.show_ranking:
|
||||
# Show ranking only if available
|
||||
home_rank = self._team_rankings_cache.get(home_abbr, 0)
|
||||
if home_rank > 0:
|
||||
home_text = f"#{home_rank}"
|
||||
else:
|
||||
home_text = ""
|
||||
elif self.show_records:
|
||||
# Show record only when rankings are disabled
|
||||
home_text = game.get("home_record", "")
|
||||
else:
|
||||
home_text = ""
|
||||
|
||||
if home_text:
|
||||
home_record_bbox = draw_overlay.textbbox(
|
||||
(0, 0), home_text, font=record_font
|
||||
)
|
||||
home_record_width = home_record_bbox[2] - home_record_bbox[0]
|
||||
home_record_x = self.display_width - home_record_width - 3
|
||||
self.logger.debug(
|
||||
f"Drawing home ranking '{home_text}' at ({home_record_x}, {record_y}) with font size {record_font.size if hasattr(record_font, 'size') else 'unknown'}"
|
||||
)
|
||||
self._draw_text_with_outline(
|
||||
draw_overlay,
|
||||
home_text,
|
||||
(home_record_x, record_y),
|
||||
record_font,
|
||||
)
|
||||
|
||||
# Composite the text overlay onto the main image
|
||||
main_img = Image.alpha_composite(main_img, overlay)
|
||||
main_img = main_img.convert("RGB") # Convert for display
|
||||
|
||||
# Display the final image
|
||||
self.display_manager.image.paste(main_img, (0, 0))
|
||||
self.display_manager.update_display() # Update display here for live
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(
|
||||
f"Error displaying live Hockey game: {e}", exc_info=True
|
||||
) # Changed log prefix
|
||||
@@ -419,6 +419,7 @@ class SportsCore(ABC):
|
||||
if not home_team or not away_team:
|
||||
self.logger.warning(f"Could not find home or away team in event: {game_event.get('id')}")
|
||||
return None, None, None, None, None
|
||||
|
||||
try:
|
||||
home_abbr = home_team["team"]["abbreviation"]
|
||||
except KeyError:
|
||||
|
||||
@@ -23,6 +23,7 @@ from src.odds_ticker_manager import OddsTickerManager
|
||||
from src.leaderboard_manager import LeaderboardManager
|
||||
from src.nhl_managers import NHLLiveManager, NHLRecentManager, NHLUpcomingManager
|
||||
from src.nba_managers import NBALiveManager, NBARecentManager, NBAUpcomingManager
|
||||
from src.wnba_managers import WNBALiveManager, WNBARecentManager, WNBAUpcomingManager
|
||||
from src.mlb_manager import MLBLiveManager, MLBRecentManager, MLBUpcomingManager
|
||||
from src.milb_manager import MiLBLiveManager, MiLBRecentManager, MiLBUpcomingManager
|
||||
from src.soccer_managers import SoccerLiveManager, SoccerRecentManager, SoccerUpcomingManager
|
||||
@@ -30,6 +31,7 @@ from src.nfl_managers import NFLLiveManager, NFLRecentManager, NFLUpcomingManage
|
||||
from src.ncaa_fb_managers import NCAAFBLiveManager, NCAAFBRecentManager, NCAAFBUpcomingManager
|
||||
from src.ncaa_baseball_managers import NCAABaseballLiveManager, NCAABaseballRecentManager, NCAABaseballUpcomingManager
|
||||
from src.ncaam_basketball_managers import NCAAMBasketballLiveManager, NCAAMBasketballRecentManager, NCAAMBasketballUpcomingManager
|
||||
from src.ncaaw_basketball_managers import NCAAWBasketballLiveManager, NCAAWBasketballRecentManager, NCAAWBasketballUpcomingManager
|
||||
from src.ncaam_hockey_managers import NCAAMHockeyLiveManager, NCAAMHockeyRecentManager, NCAAMHockeyUpcomingManager
|
||||
from src.ncaaw_hockey_managers import NCAAWHockeyLiveManager, NCAAWHockeyRecentManager, NCAAWHockeyUpcomingManager
|
||||
from src.youtube_display import YouTubeDisplay
|
||||
@@ -134,6 +136,21 @@ class DisplayController:
|
||||
self.nba_recent = None
|
||||
self.nba_upcoming = None
|
||||
logger.info("NBA managers initialized in %.3f seconds", time.time() - nba_time)
|
||||
|
||||
# Initialize WNBA managers if enabled
|
||||
wnba_time = time.time()
|
||||
wnba_enabled = self.config.get('wnba_scoreboard', {}).get('enabled', False)
|
||||
wnba_display_modes = self.config.get('wnba_scoreboard', {}).get('display_modes', {})
|
||||
|
||||
if wnba_enabled:
|
||||
self.wnba_live = WNBALiveManager(self.config, self.display_manager, self.cache_manager) if wnba_display_modes.get('wnba_live', True) else None
|
||||
self.wnba_recent = WNBARecentManager(self.config, self.display_manager, self.cache_manager) if wnba_display_modes.get('wnba_recent', True) else None
|
||||
self.wnba_upcoming = WNBAUpcomingManager(self.config, self.display_manager, self.cache_manager) if wnba_display_modes.get('wnba_upcoming', True) else None
|
||||
else:
|
||||
self.wnba_live = None
|
||||
self.wnba_recent = None
|
||||
self.wnba_upcoming = None
|
||||
logger.info("WNBA managers initialized in %.3f seconds", time.time() - wnba_time)
|
||||
|
||||
# Initialize MLB managers if enabled
|
||||
mlb_time = time.time()
|
||||
@@ -242,6 +259,21 @@ class DisplayController:
|
||||
self.ncaam_basketball_upcoming = None
|
||||
logger.info("NCAA Men's Basketball managers initialized in %.3f seconds", time.time() - ncaam_basketball_time)
|
||||
|
||||
# Initialize NCAA Womens's Basketball managers if enabled
|
||||
ncaaw_basketball_time = time.time()
|
||||
ncaaw_basketball_enabled = self.config.get('ncaaw_basketball_scoreboard', {}).get('enabled', False)
|
||||
ncaaw_basketball_display_modes = self.config.get('ncaaw_basketball_scoreboard', {}).get('display_modes', {})
|
||||
|
||||
if ncaaw_basketball_enabled:
|
||||
self.ncaaw_basketball_live = NCAAWBasketballLiveManager(self.config, self.display_manager, self.cache_manager) if ncaaw_basketball_display_modes.get('ncaaw_basketball_live', True) else None
|
||||
self.ncaaw_basketball_recent = NCAAWBasketballRecentManager(self.config, self.display_manager, self.cache_manager) if ncaaw_basketball_display_modes.get('ncaaw_basketball_recent', True) else None
|
||||
self.ncaaw_basketball_upcoming = NCAAWBasketballUpcomingManager(self.config, self.display_manager, self.cache_manager) if ncaaw_basketball_display_modes.get('ncaaw_basketball_upcoming', True) else None
|
||||
else:
|
||||
self.ncaaw_basketball_live = None
|
||||
self.ncaaw_basketball_recent = None
|
||||
self.ncaaw_basketball_upcoming = None
|
||||
logger.info("NCAA Womens's Basketball managers initialized in %.3f seconds", time.time() - ncaaw_basketball_time)
|
||||
|
||||
# Initialize NCAA Men's Hockey managers if enabled
|
||||
ncaam_hockey_time = time.time()
|
||||
ncaam_hockey_enabled = self.config.get('ncaam_hockey_scoreboard', {}).get('enabled', False)
|
||||
@@ -281,6 +313,7 @@ class DisplayController:
|
||||
# Read live_priority flags for all sports
|
||||
self.nhl_live_priority = self.config.get('nhl_scoreboard', {}).get('live_priority', True)
|
||||
self.nba_live_priority = self.config.get('nba_scoreboard', {}).get('live_priority', True)
|
||||
self.wnba_live_priority = self.config.get('wnba_scoreboard', {}).get('live_priority', True)
|
||||
self.mlb_live_priority = self.config.get('mlb_scoreboard', {}).get('live_priority', True)
|
||||
self.milb_live_priority = self.config.get('milb_scoreboard', {}).get('live_priority', True)
|
||||
self.soccer_live_priority = self.config.get('soccer_scoreboard', {}).get('live_priority', True)
|
||||
@@ -288,6 +321,7 @@ class DisplayController:
|
||||
self.ncaa_fb_live_priority = self.config.get('ncaa_fb_scoreboard', {}).get('live_priority', True)
|
||||
self.ncaa_baseball_live_priority = self.config.get('ncaa_baseball_scoreboard', {}).get('live_priority', True)
|
||||
self.ncaam_basketball_live_priority = self.config.get('ncaam_basketball_scoreboard', {}).get('live_priority', True)
|
||||
self.ncaaw_basketball_live_priority = self.config.get('ncaaw_basketball_scoreboard', {}).get('live_priority', True)
|
||||
self.ncaam_hockey_live_priority = self.config.get('ncaam_hockey_scoreboard', {}).get('live_priority', True)
|
||||
self.ncaaw_hockey_live_priority = self.config.get('ncaaw_hockey_scoreboard', {}).get('live_priority', True)
|
||||
|
||||
@@ -315,6 +349,9 @@ class DisplayController:
|
||||
if nba_enabled:
|
||||
if self.nba_recent: self.available_modes.append('nba_recent')
|
||||
if self.nba_upcoming: self.available_modes.append('nba_upcoming')
|
||||
if wnba_enabled:
|
||||
if self.wnba_recent: self.available_modes.append('wnba_recent')
|
||||
if self.wnba_upcoming: self.available_modes.append('wnba_upcoming')
|
||||
if mlb_enabled:
|
||||
if self.mlb_recent: self.available_modes.append('mlb_recent')
|
||||
if self.mlb_upcoming: self.available_modes.append('mlb_upcoming')
|
||||
@@ -336,6 +373,9 @@ class DisplayController:
|
||||
if ncaam_basketball_enabled:
|
||||
if self.ncaam_basketball_recent: self.available_modes.append('ncaam_basketball_recent')
|
||||
if self.ncaam_basketball_upcoming: self.available_modes.append('ncaam_basketball_upcoming')
|
||||
if ncaaw_basketball_enabled:
|
||||
if self.ncaaw_basketball_recent: self.available_modes.append('ncaaw_basketball_recent')
|
||||
if self.ncaaw_basketball_upcoming: self.available_modes.append('ncaaw_basketball_upcoming')
|
||||
if ncaam_hockey_enabled:
|
||||
if self.ncaam_hockey_recent: self.available_modes.append('ncaam_hockey_recent')
|
||||
if self.ncaam_hockey_upcoming: self.available_modes.append('ncaam_hockey_upcoming')
|
||||
@@ -366,6 +406,11 @@ class DisplayController:
|
||||
self.nba_favorite_teams = self.config.get('nba_scoreboard', {}).get('favorite_teams', [])
|
||||
self.in_nba_rotation = False
|
||||
|
||||
self.wnba_current_team_index = 0
|
||||
self.wnba_showing_recent = True
|
||||
self.wnba_favorite_teams = self.config.get('wnba_scoreboard', {}).get('favorite_teams', [])
|
||||
self.in_wnba_rotation = False
|
||||
|
||||
self.soccer_current_team_index = 0 # Soccer rotation state
|
||||
self.soccer_showing_recent = True
|
||||
self.soccer_favorite_teams = self.config.get('soccer_scoreboard', {}).get('favorite_teams', [])
|
||||
@@ -394,6 +439,12 @@ class DisplayController:
|
||||
self.ncaam_basketball_showing_recent = True
|
||||
self.ncaam_basketball_favorite_teams = self.config.get('ncaam_basketball_scoreboard', {}).get('favorite_teams', [])
|
||||
self.in_ncaam_basketball_rotation = False
|
||||
|
||||
# Add NCAA Womens's Basketball rotation state
|
||||
self.ncaaw_basketball_current_team_index = 0
|
||||
self.ncaaw_basketball_showing_recent = True
|
||||
self.ncaaw_basketball_favorite_teams = self.config.get('ncaaw_basketball_scoreboard', {}).get('favorite_teams', [])
|
||||
self.in_ncaaw_basketball_rotation = False
|
||||
|
||||
# Update display durations to include all modes
|
||||
self.display_durations = self.config['display'].get('display_durations', {})
|
||||
@@ -423,6 +474,9 @@ class DisplayController:
|
||||
'nba_live': 30,
|
||||
'nba_recent': 20,
|
||||
'nba_upcoming': 20,
|
||||
'wnba_live': 30,
|
||||
'wnba_recent': 20,
|
||||
'wnba_upcoming': 20,
|
||||
'mlb_live': 30,
|
||||
'mlb_recent': 20,
|
||||
'mlb_upcoming': 20,
|
||||
@@ -445,6 +499,9 @@ class DisplayController:
|
||||
'ncaam_basketball_live': 30, # Added NCAA Men's Basketball durations
|
||||
'ncaam_basketball_recent': 15,
|
||||
'ncaam_basketball_upcoming': 15,
|
||||
'ncaaw_basketball_live': 30, # Added NCAA Womens's Basketball durations
|
||||
'ncaaw_basketball_recent': 15,
|
||||
'ncaaw_basketball_upcoming': 15,
|
||||
'ncaam_hockey_live': 30, # Added NCAA Men's Hockey durations
|
||||
'ncaam_hockey_recent': 15,
|
||||
'ncaam_hockey_upcoming': 15,
|
||||
@@ -462,6 +519,8 @@ class DisplayController:
|
||||
logger.info(f"NHL Favorite teams: {self.nhl_favorite_teams}")
|
||||
if nba_enabled:
|
||||
logger.info(f"NBA Favorite teams: {self.nba_favorite_teams}")
|
||||
if wnba_enabled:
|
||||
logger.info(f"WNBA Favorite teams: {self.wnba_favorite_teams}")
|
||||
if mlb_enabled:
|
||||
logger.info(f"MLB Favorite teams: {self.mlb_favorite_teams}")
|
||||
if milb_enabled:
|
||||
@@ -476,6 +535,8 @@ class DisplayController:
|
||||
logger.info(f"NCAA Baseball Favorite teams: {self.ncaa_baseball_favorite_teams}")
|
||||
if ncaam_basketball_enabled: # Check if NCAA Men's Basketball is enabled
|
||||
logger.info(f"NCAA Men's Basketball Favorite teams: {self.ncaam_basketball_favorite_teams}")
|
||||
if ncaaw_basketball_enabled: # Check if NCAA Womens's Basketball is enabled
|
||||
logger.info(f"NCAA Womens's Basketball Favorite teams: {self.ncaaw_basketball_favorite_teams}")
|
||||
|
||||
logger.info(f"Available display modes: {self.available_modes}")
|
||||
logger.info(f"Initial display mode: {self.current_display_mode}")
|
||||
@@ -674,6 +735,10 @@ class DisplayController:
|
||||
if self.nba_live: self.nba_live.update()
|
||||
if self.nba_recent: self.nba_recent.update()
|
||||
if self.nba_upcoming: self.nba_upcoming.update()
|
||||
elif current_sport == 'wnba':
|
||||
if self.wnba_live: self.wnba_live.update()
|
||||
if self.wnba_recent: self.wnba_recent.update()
|
||||
if self.wnba_upcoming: self.wnba_upcoming.update()
|
||||
elif current_sport == 'mlb':
|
||||
if self.mlb_live: self.mlb_live.update()
|
||||
if self.mlb_recent: self.mlb_recent.update()
|
||||
@@ -702,6 +767,10 @@ class DisplayController:
|
||||
if self.ncaam_basketball_live: self.ncaam_basketball_live.update()
|
||||
if self.ncaam_basketball_recent: self.ncaam_basketball_recent.update()
|
||||
if self.ncaam_basketball_upcoming: self.ncaam_basketball_upcoming.update()
|
||||
elif current_sport == 'ncaaw_basketball':
|
||||
if self.ncaaw_basketball_live: self.ncaaw_basketball_live.update()
|
||||
if self.ncaaw_basketball_recent: self.ncaaw_basketball_recent.update()
|
||||
if self.ncaaw_basketball_upcoming: self.ncaaw_basketball_upcoming.update()
|
||||
elif current_sport == 'ncaam_hockey':
|
||||
if self.ncaam_hockey_live: self.ncaam_hockey_live.update()
|
||||
if self.ncaam_hockey_recent: self.ncaam_hockey_recent.update()
|
||||
@@ -721,6 +790,10 @@ class DisplayController:
|
||||
if self.nba_recent: self.nba_recent.update()
|
||||
if self.nba_upcoming: self.nba_upcoming.update()
|
||||
|
||||
if self.wnba_live: self.wnba_live.update()
|
||||
if self.wnba_recent: self.wnba_recent.update()
|
||||
if self.wnba_upcoming: self.wnba_upcoming.update()
|
||||
|
||||
if self.mlb_live: self.mlb_live.update()
|
||||
if self.mlb_recent: self.mlb_recent.update()
|
||||
if self.mlb_upcoming: self.mlb_upcoming.update()
|
||||
@@ -749,6 +822,10 @@ class DisplayController:
|
||||
if self.ncaam_basketball_recent: self.ncaam_basketball_recent.update()
|
||||
if self.ncaam_basketball_upcoming: self.ncaam_basketball_upcoming.update()
|
||||
|
||||
if self.ncaaw_basketball_live: self.ncaaw_basketball_live.update()
|
||||
if self.ncaaw_basketball_recent: self.ncaaw_basketball_recent.update()
|
||||
if self.ncaaw_basketball_upcoming: self.ncaaw_basketball_upcoming.update()
|
||||
|
||||
if self.ncaam_hockey_live: self.ncaam_hockey_live.update()
|
||||
if self.ncaam_hockey_recent: self.ncaam_hockey_recent.update()
|
||||
if self.ncaam_hockey_upcoming: self.ncaam_hockey_upcoming.update()
|
||||
@@ -770,6 +847,8 @@ class DisplayController:
|
||||
live_checks['nhl'] = self.nhl_live and self.nhl_live.live_games
|
||||
if 'nba_scoreboard' in self.config and self.config['nba_scoreboard'].get('enabled', False):
|
||||
live_checks['nba'] = self.nba_live and self.nba_live.live_games
|
||||
if 'wnba_scoreboard' in self.config and self.config['wnba_scoreboard'].get('enabled', False):
|
||||
live_checks['wnba'] = self.wnba_live and self.wnba_live.live_games
|
||||
if 'mlb' in self.config and self.config['mlb'].get('enabled', False):
|
||||
live_checks['mlb'] = self.mlb_live and self.mlb_live.live_games
|
||||
if 'milb' in self.config and self.config['milb'].get('enabled', False):
|
||||
@@ -784,6 +863,8 @@ class DisplayController:
|
||||
live_checks['ncaa_baseball'] = self.ncaa_baseball_live and self.ncaa_baseball_live.live_games
|
||||
if 'ncaam_basketball_scoreboard' in self.config and self.config['ncaam_basketball_scoreboard'].get('enabled', False):
|
||||
live_checks['ncaam_basketball'] = self.ncaam_basketball_live and self.ncaam_basketball_live.live_games
|
||||
if 'ncaaw_basketball_scoreboard' in self.config and self.config['ncaaw_basketball_scoreboard'].get('enabled', False):
|
||||
live_checks['ncaaw_basketball'] = self.ncaaw_basketball_live and self.ncaaw_basketball_live.live_games
|
||||
if 'ncaam_hockey_scoreboard' in self.config and self.config['ncaam_hockey_scoreboard'].get('enabled', False):
|
||||
live_checks['ncaam_hockey'] = self.ncaam_hockey_live and self.ncaam_hockey_live.live_games
|
||||
if 'ncaaw_hockey_scoreboard' in self.config and self.config['ncaaw_hockey_scoreboard'].get('enabled', False):
|
||||
@@ -818,6 +899,9 @@ class DisplayController:
|
||||
elif sport == 'nba':
|
||||
manager_recent = self.nba_recent
|
||||
manager_upcoming = self.nba_upcoming
|
||||
elif sport == 'wnba':
|
||||
manager_recent = self.wnba_recent
|
||||
manager_upcoming = self.wnba_upcoming
|
||||
elif sport == 'mlb':
|
||||
manager_recent = self.mlb_recent
|
||||
manager_upcoming = self.mlb_upcoming
|
||||
@@ -872,6 +956,10 @@ class DisplayController:
|
||||
favorite_teams = self.nba_favorite_teams
|
||||
manager_recent = self.nba_recent
|
||||
manager_upcoming = self.nba_upcoming
|
||||
elif sport == 'wnba':
|
||||
favorite_teams = self.wnba_favorite_teams
|
||||
manager_recent = self.wnba_recent
|
||||
manager_upcoming = self.wnba_upcoming
|
||||
elif sport == 'mlb':
|
||||
favorite_teams = self.mlb_favorite_teams
|
||||
manager_recent = self.mlb_recent
|
||||
@@ -895,73 +983,6 @@ class DisplayController:
|
||||
|
||||
return bool(favorite_teams and (manager_recent or manager_upcoming))
|
||||
|
||||
def _rotate_team_games(self, sport: str = 'nhl') -> None:
|
||||
"""Rotate through games for favorite teams. (No longer used directly in loop)"""
|
||||
# This logic is now mostly handled within each manager's display/update
|
||||
# Keeping the structure in case direct rotation is needed later.
|
||||
if not self._has_team_games(sport):
|
||||
return
|
||||
|
||||
if sport == 'nhl':
|
||||
if not self.nhl_favorite_teams: return
|
||||
current_team = self.nhl_favorite_teams[self.nhl_current_team_index]
|
||||
# ... (rest of NHL rotation logic - now less relevant)
|
||||
elif sport == 'nba':
|
||||
if not self.nba_favorite_teams: return
|
||||
current_team = self.nba_favorite_teams[self.nba_current_team_index]
|
||||
# ... (rest of NBA rotation logic)
|
||||
elif sport == 'mlb':
|
||||
if not self.mlb_favorite_teams: return
|
||||
current_team = self.mlb_favorite_teams[self.mlb_current_team_index]
|
||||
# ... (rest of MLB rotation logic)
|
||||
elif sport == 'milb':
|
||||
if not self.config.get('milb_scoreboard', {}).get('favorite_teams', []): return
|
||||
current_team = self.config['milb_scoreboard']['favorite_teams'][self.milb_current_team_index]
|
||||
# ... (rest of MiLB rotation logic)
|
||||
elif sport == 'soccer':
|
||||
if not self.soccer_favorite_teams: return
|
||||
current_team = self.soccer_favorite_teams[self.soccer_current_team_index]
|
||||
# Try to find games for current team (recent first)
|
||||
found_games = self._get_team_games(current_team, 'soccer', self.soccer_showing_recent)
|
||||
if not found_games:
|
||||
# Try opposite type (upcoming/recent)
|
||||
self.soccer_showing_recent = not self.soccer_showing_recent
|
||||
found_games = self._get_team_games(current_team, 'soccer', self.soccer_showing_recent)
|
||||
|
||||
if not found_games:
|
||||
# Move to next team if no games found for current one
|
||||
self.soccer_current_team_index = (self.soccer_current_team_index + 1) % len(self.soccer_favorite_teams)
|
||||
self.soccer_showing_recent = True # Reset to recent for the new team
|
||||
# Maybe try finding game for the *new* team immediately? Optional.
|
||||
elif sport == 'nfl':
|
||||
if not self.nfl_favorite_teams: return
|
||||
current_team = self.nfl_favorite_teams[self.nfl_current_team_index]
|
||||
# Try to find games for current team (recent first)
|
||||
found_games = self._get_team_games(current_team, 'nfl', self.nfl_showing_recent)
|
||||
if not found_games:
|
||||
# Try opposite type (upcoming/recent)
|
||||
self.nfl_showing_recent = not self.nfl_showing_recent
|
||||
found_games = self._get_team_games(current_team, 'nfl', self.nfl_showing_recent)
|
||||
|
||||
if not found_games:
|
||||
# Move to next team if no games found for current one
|
||||
self.nfl_current_team_index = (self.nfl_current_team_index + 1) % len(self.nfl_favorite_teams)
|
||||
self.nfl_showing_recent = True # Reset to recent for the new team
|
||||
elif sport == 'ncaa_fb': # Add NCAA FB case
|
||||
if not self.ncaa_fb_favorite_teams: return
|
||||
current_team = self.ncaa_fb_favorite_teams[self.ncaa_fb_current_team_index]
|
||||
# Try to find games for current team (recent first)
|
||||
found_games = self._get_team_games(current_team, 'ncaa_fb', self.ncaa_fb_showing_recent)
|
||||
if not found_games:
|
||||
# Try opposite type (upcoming/recent)
|
||||
self.ncaa_fb_showing_recent = not self.ncaa_fb_showing_recent
|
||||
found_games = self._get_team_games(current_team, 'ncaa_fb', self.ncaa_fb_showing_recent)
|
||||
|
||||
if not found_games:
|
||||
# Move to next team if no games found for current one
|
||||
self.ncaa_fb_current_team_index = (self.ncaa_fb_current_team_index + 1) % len(self.ncaa_fb_favorite_teams)
|
||||
self.ncaa_fb_showing_recent = True # Reset to recent for the new team
|
||||
|
||||
# --- SCHEDULING METHODS ---
|
||||
def _load_schedule_config(self):
|
||||
"""Load schedule configuration once at startup."""
|
||||
@@ -1031,6 +1052,7 @@ class DisplayController:
|
||||
# Check if each sport is enabled before processing
|
||||
nhl_enabled = self.config.get('nhl_scoreboard', {}).get('enabled', False)
|
||||
nba_enabled = self.config.get('nba_scoreboard', {}).get('enabled', False)
|
||||
wnba_enabled = self.config.get('wnba_scoreboard', {}).get('enabled', False)
|
||||
mlb_enabled = self.config.get('mlb_scoreboard', {}).get('enabled', False)
|
||||
milb_enabled = self.config.get('milb_scoreboard', {}).get('enabled', False)
|
||||
soccer_enabled = self.config.get('soccer_scoreboard', {}).get('enabled', False)
|
||||
@@ -1038,11 +1060,13 @@ class DisplayController:
|
||||
ncaa_fb_enabled = self.config.get('ncaa_fb_scoreboard', {}).get('enabled', False)
|
||||
ncaa_baseball_enabled = self.config.get('ncaa_baseball_scoreboard', {}).get('enabled', False)
|
||||
ncaam_basketball_enabled = self.config.get('ncaam_basketball_scoreboard', {}).get('enabled', False)
|
||||
ncaaw_basketball_enabled = self.config.get('ncaaw_basketball_scoreboard', {}).get('enabled', False)
|
||||
ncaam_hockey_enabled = self.config.get('ncaam_hockey_scoreboard', {}).get('enabled', False)
|
||||
ncaaw_hockey_enabled = self.config.get('ncaaw_hockey_scoreboard', {}).get('enabled', False)
|
||||
|
||||
update_mode('nhl_live', getattr(self, 'nhl_live', None), self.nhl_live_priority, nhl_enabled)
|
||||
update_mode('nba_live', getattr(self, 'nba_live', None), self.nba_live_priority, nba_enabled)
|
||||
update_mode('wnba_live', getattr(self, 'wnba_live', None), self.wnba_live_priority, wnba_enabled)
|
||||
update_mode('mlb_live', getattr(self, 'mlb_live', None), self.mlb_live_priority, mlb_enabled)
|
||||
update_mode('milb_live', getattr(self, 'milb_live', None), self.milb_live_priority, milb_enabled)
|
||||
update_mode('soccer_live', getattr(self, 'soccer_live', None), self.soccer_live_priority, soccer_enabled)
|
||||
@@ -1050,6 +1074,7 @@ class DisplayController:
|
||||
update_mode('ncaa_fb_live', getattr(self, 'ncaa_fb_live', None), self.ncaa_fb_live_priority, ncaa_fb_enabled)
|
||||
update_mode('ncaa_baseball_live', getattr(self, 'ncaa_baseball_live', None), self.ncaa_baseball_live_priority, ncaa_baseball_enabled)
|
||||
update_mode('ncaam_basketball_live', getattr(self, 'ncaam_basketball_live', None), self.ncaam_basketball_live_priority, ncaam_basketball_enabled)
|
||||
update_mode('ncaaw_basketball_live', getattr(self, 'ncaaw_basketball_live', None), self.ncaaw_basketball_live_priority, ncaaw_basketball_enabled)
|
||||
update_mode('ncaam_hockey_live', getattr(self, 'ncaam_hockey_live', None), self.ncaam_hockey_live_priority, ncaam_hockey_enabled)
|
||||
update_mode('ncaaw_hockey_live', getattr(self, 'ncaaw_hockey_live', None), self.ncaaw_hockey_live_priority, ncaaw_hockey_enabled)
|
||||
|
||||
@@ -1094,6 +1119,7 @@ class DisplayController:
|
||||
for sport, attr, priority in [
|
||||
('nhl', 'nhl_live', self.nhl_live_priority),
|
||||
('nba', 'nba_live', self.nba_live_priority),
|
||||
('wnba', 'wnba_live', self.wnba_live_priority),
|
||||
('mlb', 'mlb_live', self.mlb_live_priority),
|
||||
('milb', 'milb_live', self.milb_live_priority),
|
||||
('soccer', 'soccer_live', self.soccer_live_priority),
|
||||
@@ -1101,6 +1127,7 @@ class DisplayController:
|
||||
('ncaa_fb', 'ncaa_fb_live', self.ncaa_fb_live_priority),
|
||||
('ncaa_baseball', 'ncaa_baseball_live', self.ncaa_baseball_live_priority),
|
||||
('ncaam_basketball', 'ncaam_basketball_live', self.ncaam_basketball_live_priority),
|
||||
('ncaaw_basketball', 'ncaaw_basketball_live', self.ncaaw_basketball_live_priority),
|
||||
('ncaam_hockey', 'ncaam_hockey_live', self.ncaam_hockey_live_priority),
|
||||
('ncaaw_hockey', 'ncaaw_hockey_live', self.ncaaw_hockey_live_priority)
|
||||
]:
|
||||
@@ -1264,6 +1291,10 @@ class DisplayController:
|
||||
manager_to_display = self.nba_recent
|
||||
elif self.current_display_mode == 'nba_upcoming' and self.nba_upcoming:
|
||||
manager_to_display = self.nba_upcoming
|
||||
elif self.current_display_mode == 'wnba_recent' and self.wnba_recent:
|
||||
manager_to_display = self.wnba_recent
|
||||
elif self.current_display_mode == 'wnba_upcoming' and self.wnba_upcoming:
|
||||
manager_to_display = self.wnba_upcoming
|
||||
elif self.current_display_mode == 'nfl_recent' and self.nfl_recent:
|
||||
manager_to_display = self.nfl_recent
|
||||
elif self.current_display_mode == 'nfl_upcoming' and self.nfl_upcoming:
|
||||
@@ -1280,6 +1311,10 @@ class DisplayController:
|
||||
manager_to_display = self.ncaam_basketball_recent
|
||||
elif self.current_display_mode == 'ncaam_basketball_upcoming' and self.ncaam_basketball_upcoming:
|
||||
manager_to_display = self.ncaam_basketball_upcoming
|
||||
elif self.current_display_mode == 'ncaaw_basketball_recent' and self.ncaaw_basketball_recent:
|
||||
manager_to_display = self.ncaaw_basketball_recent
|
||||
elif self.current_display_mode == 'ncaaw_basketball_upcoming' and self.ncaaw_basketball_upcoming:
|
||||
manager_to_display = self.ncaaw_basketball_upcoming
|
||||
elif self.current_display_mode == 'mlb_recent' and self.mlb_recent:
|
||||
manager_to_display = self.mlb_recent
|
||||
elif self.current_display_mode == 'mlb_upcoming' and self.mlb_upcoming:
|
||||
@@ -1298,6 +1333,8 @@ class DisplayController:
|
||||
manager_to_display = self.nhl_live
|
||||
elif self.current_display_mode == 'nba_live' and self.nba_live:
|
||||
manager_to_display = self.nba_live
|
||||
elif self.current_display_mode == 'wnba_live' and self.wnba_live:
|
||||
manager_to_display = self.wnba_live
|
||||
elif self.current_display_mode == 'nfl_live' and self.nfl_live:
|
||||
manager_to_display = self.nfl_live
|
||||
elif self.current_display_mode == 'ncaa_fb_live' and self.ncaa_fb_live:
|
||||
@@ -1306,6 +1343,8 @@ class DisplayController:
|
||||
manager_to_display = self.ncaa_baseball_live
|
||||
elif self.current_display_mode == 'ncaam_basketball_live' and self.ncaam_basketball_live:
|
||||
manager_to_display = self.ncaam_basketball_live
|
||||
elif self.current_display_mode == 'ncaaw_basketball_live' and self.ncaaw_basketball_live:
|
||||
manager_to_display = self.ncaaw_basketball_live
|
||||
elif self.current_display_mode == 'ncaam_hockey_live' and self.ncaam_hockey_live:
|
||||
manager_to_display = self.ncaam_hockey_live
|
||||
elif self.current_display_mode == 'ncaam_hockey_recent' and self.ncaam_hockey_recent:
|
||||
@@ -1383,6 +1422,10 @@ class DisplayController:
|
||||
self.ncaam_basketball_recent.display(force_clear=self.force_clear)
|
||||
elif self.current_display_mode == 'ncaam_basketball_upcoming' and self.ncaam_basketball_upcoming:
|
||||
self.ncaam_basketball_upcoming.display(force_clear=self.force_clear)
|
||||
elif self.current_display_mode == 'ncaaw_basketball_recent' and self.ncaaw_basketball_recent:
|
||||
self.ncaaw_basketball_recent.display(force_clear=self.force_clear)
|
||||
elif self.current_display_mode == 'ncaaw_basketball_upcoming' and self.ncaaw_basketball_upcoming:
|
||||
self.ncaaw_basketball_upcoming.display(force_clear=self.force_clear)
|
||||
elif self.current_display_mode == 'ncaa_baseball_recent' and self.ncaa_baseball_recent:
|
||||
self.ncaa_baseball_recent.display(force_clear=self.force_clear)
|
||||
elif self.current_display_mode == 'ncaa_baseball_upcoming' and self.ncaa_baseball_upcoming:
|
||||
|
||||
@@ -49,6 +49,7 @@ class LogoDownloader:
|
||||
LOGO_DIRECTORIES = {
|
||||
'nfl': 'assets/sports/nfl_logos',
|
||||
'nba': 'assets/sports/nba_logos',
|
||||
'wnba': 'assets/sports/wnba_logos',
|
||||
'mlb': 'assets/sports/mlb_logos',
|
||||
'nhl': 'assets/sports/nhl_logos',
|
||||
# NCAA sports use same directory
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
274
src/ncaaw_basketball_managers.py
Normal file
274
src/ncaaw_basketball_managers.py
Normal file
@@ -0,0 +1,274 @@
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
import pytz
|
||||
import requests
|
||||
|
||||
from src.base_classes.basketball import Basketball, BasketballLive
|
||||
from src.base_classes.sports import SportsRecent, SportsUpcoming
|
||||
from src.cache_manager import CacheManager
|
||||
from src.display_manager import DisplayManager
|
||||
|
||||
# Import the API counter function from web interface
|
||||
try:
|
||||
from web_interface_v2 import increment_api_counter
|
||||
except ImportError:
|
||||
# Fallback if web interface is not available
|
||||
def increment_api_counter(kind: str, count: int = 1):
|
||||
pass
|
||||
|
||||
|
||||
# Constants
|
||||
ESPN_NCAAWB_SCOREBOARD_URL = "https://site.api.espn.com/apis/site/v2/sports/basketball/womens-college-basketball/scoreboard"
|
||||
|
||||
|
||||
class BaseNCAAWBasketballManager(Basketball):
|
||||
"""Base class for NCAA WB managers with common functionality."""
|
||||
|
||||
# Class variables for warning tracking
|
||||
_no_data_warning_logged = False
|
||||
_last_warning_time = 0
|
||||
_warning_cooldown = 60 # Only log warnings once per minute
|
||||
_last_log_times = {}
|
||||
_shared_data = None
|
||||
_last_shared_update = 0
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: Dict[str, Any],
|
||||
display_manager: DisplayManager,
|
||||
cache_manager: CacheManager,
|
||||
):
|
||||
self.logger = logging.getLogger("NCAAWB") # Changed logger name
|
||||
super().__init__(
|
||||
config=config,
|
||||
display_manager=display_manager,
|
||||
cache_manager=cache_manager,
|
||||
logger=self.logger,
|
||||
sport_key="ncaaw_basketball",
|
||||
)
|
||||
|
||||
# Check display modes to determine what data to fetch
|
||||
display_modes = self.mode_config.get("display_modes", {})
|
||||
self.recent_enabled = display_modes.get("ncaaw_basketball_recent", False)
|
||||
self.upcoming_enabled = display_modes.get("ncaaw_basketball_upcoming", False)
|
||||
self.live_enabled = display_modes.get("ncaaw_basketball_live", False)
|
||||
|
||||
self.logger.info(
|
||||
f"Initialized NCAA Womens Basketball manager with display dimensions: {self.display_width}x{self.display_height}"
|
||||
)
|
||||
self.logger.info(f"Logo directory: {self.logo_dir}")
|
||||
self.logger.info(
|
||||
f"Display modes - Recent: {self.recent_enabled}, Upcoming: {self.upcoming_enabled}, Live: {self.live_enabled}"
|
||||
)
|
||||
self.league = "womens-college-basketball"
|
||||
|
||||
def _fetch_ncaaw_basketball_api_data(
|
||||
self, use_cache: bool = True
|
||||
) -> Optional[Dict]:
|
||||
"""
|
||||
Fetches the full season schedule for NCAA Womens Basketball using background threading.
|
||||
Returns cached data immediately if available, otherwise starts background fetch.
|
||||
"""
|
||||
now = datetime.now(pytz.utc)
|
||||
season_year = now.year
|
||||
cache_key = f"{self.sport_key}_schedule_{season_year}"
|
||||
|
||||
# Check cache first
|
||||
if use_cache:
|
||||
cached_data = self.cache_manager.get(cache_key)
|
||||
if cached_data:
|
||||
# Validate cached data structure
|
||||
if isinstance(cached_data, dict) and "events" in cached_data:
|
||||
self.logger.info(f"Using cached schedule for {season_year}")
|
||||
return cached_data
|
||||
elif isinstance(cached_data, list):
|
||||
# Handle old cache format (list of events)
|
||||
self.logger.info(
|
||||
f"Using cached schedule for {season_year} (legacy format)"
|
||||
)
|
||||
return {"events": cached_data}
|
||||
else:
|
||||
self.logger.warning(
|
||||
f"Invalid cached data format for {season_year}: {type(cached_data)}"
|
||||
)
|
||||
# Clear invalid cache
|
||||
self.cache_manager.clear_cache(cache_key)
|
||||
|
||||
# If background service is disabled, fall back to synchronous fetch
|
||||
if not self.background_enabled or not self.background_service:
|
||||
return self._fetch_ncaaw_basketball_api_data_sync(use_cache)
|
||||
|
||||
# Start background fetch
|
||||
self.logger.info(
|
||||
f"Starting background fetch for {season_year} season schedule..."
|
||||
)
|
||||
|
||||
def fetch_callback(result):
|
||||
"""Callback when background fetch completes."""
|
||||
if result.success:
|
||||
self.logger.info(
|
||||
f"Background fetch completed for {season_year}: {len(result.data.get('events'))} events"
|
||||
)
|
||||
else:
|
||||
self.logger.error(
|
||||
f"Background fetch failed for {season_year}: {result.error}"
|
||||
)
|
||||
|
||||
# Clean up request tracking
|
||||
if season_year in self.background_fetch_requests:
|
||||
del self.background_fetch_requests[season_year]
|
||||
|
||||
# Get background service configuration
|
||||
background_config = self.mode_config.get("background_service", {})
|
||||
timeout = background_config.get("request_timeout", 30)
|
||||
max_retries = background_config.get("max_retries", 3)
|
||||
priority = background_config.get("priority", 2)
|
||||
|
||||
# Submit background fetch request
|
||||
request_id = self.background_service.submit_fetch_request(
|
||||
sport="ncaa_womens_basketball",
|
||||
year=season_year,
|
||||
url=ESPN_NCAAWB_SCOREBOARD_URL,
|
||||
cache_key=cache_key,
|
||||
params={"dates": season_year, "limit": 1000},
|
||||
headers=self.headers,
|
||||
timeout=timeout,
|
||||
max_retries=max_retries,
|
||||
priority=priority,
|
||||
callback=fetch_callback,
|
||||
)
|
||||
|
||||
# Track the request
|
||||
self.background_fetch_requests[season_year] = request_id
|
||||
|
||||
# For immediate response, try to get partial data
|
||||
partial_data = self._get_weeks_data()
|
||||
if partial_data:
|
||||
return partial_data
|
||||
|
||||
return None
|
||||
|
||||
def _fetch_ncaaw_basketball_api_data_sync(
|
||||
self, use_cache: bool = True
|
||||
) -> Optional[Dict]:
|
||||
"""
|
||||
Synchronous fallback for fetching NFL data when background service is disabled.
|
||||
"""
|
||||
now = datetime.now(pytz.utc)
|
||||
current_year = now.year
|
||||
cache_key = f"ncaa_womens_basketball_schedule_{current_year}"
|
||||
|
||||
self.logger.info(
|
||||
f"Fetching full {current_year} season schedule from ESPN API (sync mode)..."
|
||||
)
|
||||
try:
|
||||
response = self.session.get(
|
||||
ESPN_NCAAWB_SCOREBOARD_URL,
|
||||
params={"dates": current_year, "limit": 1000},
|
||||
headers=self.headers,
|
||||
timeout=15,
|
||||
)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
events = data.get("events", [])
|
||||
|
||||
if use_cache:
|
||||
self.cache_manager.set(cache_key, events)
|
||||
|
||||
self.logger.info(
|
||||
f"Successfully fetched {len(events)} events for the {current_year} season."
|
||||
)
|
||||
return {"events": events}
|
||||
except requests.exceptions.RequestException as e:
|
||||
self.logger.error(f"API error fetching full schedule: {e}")
|
||||
return None
|
||||
|
||||
def _fetch_data(self) -> Optional[Dict]:
|
||||
"""Fetch data using shared data mechanism or direct fetch for live."""
|
||||
if isinstance(self, NCAAWBasketballLiveManager):
|
||||
# Live games should fetch only current games, not entire season
|
||||
return self._fetch_todays_games()
|
||||
else:
|
||||
# Recent and Upcoming managers should use cached season data
|
||||
return self._fetch_ncaaw_basketball_api_data(use_cache=True)
|
||||
|
||||
|
||||
class NCAAWBasketballLiveManager(BaseNCAAWBasketballManager, BasketballLive):
|
||||
"""Manager for live NCAA WB games."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: Dict[str, Any],
|
||||
display_manager: DisplayManager,
|
||||
cache_manager: CacheManager,
|
||||
):
|
||||
super().__init__(config, display_manager, cache_manager)
|
||||
self.logger = logging.getLogger(
|
||||
"NCAAWBasketballLiveManager"
|
||||
) # Changed logger name
|
||||
|
||||
if self.test_mode:
|
||||
# More detailed test game for NCAA WB
|
||||
self.current_game = {
|
||||
"id": "test001",
|
||||
"home_abbr": "AUB",
|
||||
"home_id": "123",
|
||||
"away_abbr": "GT",
|
||||
"away_id": "asdf",
|
||||
"home_score": "21",
|
||||
"away_score": "17",
|
||||
"period": 3,
|
||||
"period_text": "Q3",
|
||||
"clock": "5:24",
|
||||
"home_logo_path": Path(self.logo_dir, "AUB.png"),
|
||||
"away_logo_path": Path(self.logo_dir, "GT.png"),
|
||||
"is_live": True,
|
||||
"is_final": False,
|
||||
"is_upcoming": False,
|
||||
"is_halftime": False,
|
||||
}
|
||||
self.live_games = [self.current_game]
|
||||
self.logger.info(
|
||||
"Initialized NCAAWBasketballLiveManager with test game: GT vs AUB"
|
||||
)
|
||||
else:
|
||||
self.logger.info(" Initialized NCAAWBasketballLiveManager in live mode")
|
||||
|
||||
|
||||
class NCAAWBasketballRecentManager(BaseNCAAWBasketballManager, SportsRecent):
|
||||
"""Manager for recently completed NCAA WB games."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: Dict[str, Any],
|
||||
display_manager: DisplayManager,
|
||||
cache_manager: CacheManager,
|
||||
):
|
||||
super().__init__(config, display_manager, cache_manager)
|
||||
self.logger = logging.getLogger(
|
||||
"NCAAWBasketballRecentManager"
|
||||
) # Changed logger name
|
||||
self.logger.info(
|
||||
f"Initialized NCAAWBasketballRecentManager with {len(self.favorite_teams)} favorite teams"
|
||||
)
|
||||
|
||||
|
||||
class NCAAWBasketballUpcomingManager(BaseNCAAWBasketballManager, SportsUpcoming):
|
||||
"""Manager for upcoming NCAA WB games."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: Dict[str, Any],
|
||||
display_manager: DisplayManager,
|
||||
cache_manager: CacheManager,
|
||||
):
|
||||
super().__init__(config, display_manager, cache_manager)
|
||||
self.logger = logging.getLogger(
|
||||
"NCAAWBasketballUpcomingManager"
|
||||
) # Changed logger name
|
||||
self.logger.info(
|
||||
f"Initialized NCAAWBasketballUpcomingManager with {len(self.favorite_teams)} favorite teams"
|
||||
)
|
||||
@@ -23,13 +23,10 @@ class BaseNFLManager(Football): # Renamed class
|
||||
_warning_cooldown = 60 # Only log warnings once per minute
|
||||
_shared_data = None
|
||||
_last_shared_update = 0
|
||||
|
||||
|
||||
def __init__(self, config: Dict[str, Any], display_manager: DisplayManager, cache_manager: CacheManager):
|
||||
self.logger = logging.getLogger('NFL') # Changed logger name
|
||||
super().__init__(config=config, display_manager=display_manager, cache_manager=cache_manager, logger=self.logger, sport_key="nfl")
|
||||
|
||||
# Configuration is already set in base class
|
||||
# self.logo_dir and self.update_interval are already configured
|
||||
|
||||
# Check display modes to determine what data to fetch
|
||||
display_modes = self.mode_config.get("display_modes", {})
|
||||
|
||||
304
src/wnba_managers.py
Normal file
304
src/wnba_managers.py
Normal file
@@ -0,0 +1,304 @@
|
||||
import logging
|
||||
import time
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
import pytz
|
||||
import requests
|
||||
|
||||
from src.base_classes.basketball import Basketball, BasketballLive
|
||||
from src.base_classes.sports import SportsRecent, SportsUpcoming
|
||||
from src.cache_manager import CacheManager
|
||||
from src.display_manager import DisplayManager
|
||||
|
||||
# Import the API counter function from web interface
|
||||
try:
|
||||
from web_interface_v2 import increment_api_counter
|
||||
except ImportError:
|
||||
# Fallback if web interface is not available
|
||||
def increment_api_counter(kind: str, count: int = 1):
|
||||
pass
|
||||
|
||||
|
||||
# Constants
|
||||
ESPN_WNBA_SCOREBOARD_URL = (
|
||||
"https://site.api.espn.com/apis/site/v2/sports/basketball/wnba/scoreboard"
|
||||
)
|
||||
|
||||
|
||||
class BaseWNBAManager(Basketball):
|
||||
"""Base class for WNBA managers with common functionality."""
|
||||
|
||||
# Class variables for warning tracking
|
||||
_no_data_warning_logged = False
|
||||
_last_warning_time = 0
|
||||
_warning_cooldown = 60 # Only log warnings once per minute
|
||||
_last_log_times = {}
|
||||
_shared_data = None
|
||||
_last_shared_update = 0
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: Dict[str, Any],
|
||||
display_manager: DisplayManager,
|
||||
cache_manager: CacheManager,
|
||||
):
|
||||
self.logger = logging.getLogger("WNBA") # Changed logger name
|
||||
super().__init__(
|
||||
config=config,
|
||||
display_manager=display_manager,
|
||||
cache_manager=cache_manager,
|
||||
logger=self.logger,
|
||||
sport_key="wnba",
|
||||
)
|
||||
|
||||
# Check display modes to determine what data to fetch
|
||||
display_modes = self.mode_config.get("display_modes", {})
|
||||
self.recent_enabled = display_modes.get("wnba_recent", False)
|
||||
self.upcoming_enabled = display_modes.get("wnba_upcoming", False)
|
||||
self.live_enabled = display_modes.get("wnba_live", False)
|
||||
|
||||
self.logger.info(
|
||||
f"Initialized WNBA manager with display dimensions: {self.display_width}x{self.display_height}"
|
||||
)
|
||||
self.logger.info(f"Logo directory: {self.logo_dir}")
|
||||
self.logger.info(
|
||||
f"Display modes - Recent: {self.recent_enabled}, Upcoming: {self.upcoming_enabled}, Live: {self.live_enabled}"
|
||||
)
|
||||
self.league = "wnba"
|
||||
|
||||
def _fetch_wnba_api_data(self, use_cache: bool = True) -> Optional[Dict]:
|
||||
"""
|
||||
Fetches the full season schedule for WNBA using background threading.
|
||||
Returns cached data immediately if available, otherwise starts background fetch.
|
||||
"""
|
||||
now = datetime.now(pytz.utc)
|
||||
season_year = now.year
|
||||
if now.month < 2:
|
||||
season_year = now.year - 1
|
||||
datestring = f"{season_year}0401-{season_year+1}1101"
|
||||
cache_key = f"{self.sport_key}_schedule_{season_year}"
|
||||
|
||||
# Check cache first
|
||||
if use_cache:
|
||||
cached_data = self.cache_manager.get(cache_key)
|
||||
if cached_data:
|
||||
# Validate cached data structure
|
||||
if isinstance(cached_data, dict) and "events" in cached_data:
|
||||
self.logger.info(f"Using cached schedule for {season_year}")
|
||||
return cached_data
|
||||
elif isinstance(cached_data, list):
|
||||
# Handle old cache format (list of events)
|
||||
self.logger.info(
|
||||
f"Using cached schedule for {season_year} (legacy format)"
|
||||
)
|
||||
return {"events": cached_data}
|
||||
else:
|
||||
self.logger.warning(
|
||||
f"Invalid cached data format for {season_year}: {type(cached_data)}"
|
||||
)
|
||||
# Clear invalid cache
|
||||
self.cache_manager.clear_cache(cache_key)
|
||||
|
||||
# If background service is disabled, fall back to synchronous fetch
|
||||
if not self.background_enabled or not self.background_service:
|
||||
return self._fetch_wnba_api_data_sync(use_cache)
|
||||
|
||||
# Start background fetch
|
||||
self.logger.info(
|
||||
f"Starting background fetch for {season_year} season schedule..."
|
||||
)
|
||||
|
||||
def fetch_callback(result):
|
||||
"""Callback when background fetch completes."""
|
||||
if result.success:
|
||||
self.logger.info(
|
||||
f"Background fetch completed for {season_year}: {len(result.data.get('events'))} events"
|
||||
)
|
||||
else:
|
||||
self.logger.error(
|
||||
f"Background fetch failed for {season_year}: {result.error}"
|
||||
)
|
||||
|
||||
# Clean up request tracking
|
||||
if season_year in self.background_fetch_requests:
|
||||
del self.background_fetch_requests[season_year]
|
||||
|
||||
# Get background service configuration
|
||||
background_config = self.mode_config.get("background_service", {})
|
||||
timeout = background_config.get("request_timeout", 30)
|
||||
max_retries = background_config.get("max_retries", 3)
|
||||
priority = background_config.get("priority", 2)
|
||||
|
||||
# Submit background fetch request
|
||||
request_id = self.background_service.submit_fetch_request(
|
||||
sport="nba",
|
||||
year=season_year,
|
||||
url=ESPN_WNBA_SCOREBOARD_URL,
|
||||
cache_key=cache_key,
|
||||
params={"dates": datestring, "limit": 1000},
|
||||
headers=self.headers,
|
||||
timeout=timeout,
|
||||
max_retries=max_retries,
|
||||
priority=priority,
|
||||
callback=fetch_callback,
|
||||
)
|
||||
|
||||
# Track the request
|
||||
self.background_fetch_requests[season_year] = request_id
|
||||
|
||||
# For immediate response, try to get partial data
|
||||
partial_data = self._get_weeks_data()
|
||||
if partial_data:
|
||||
return partial_data
|
||||
|
||||
return None
|
||||
|
||||
def _fetch_wnba_api_data_sync(self, use_cache: bool = True) -> Optional[Dict]:
|
||||
"""
|
||||
Synchronous fallback for fetching WNBA data when background service is disabled.
|
||||
"""
|
||||
now = datetime.now(pytz.utc)
|
||||
current_year = now.year
|
||||
cache_key = f"nba_schedule_{current_year}"
|
||||
|
||||
self.logger.info(
|
||||
f"Fetching full {current_year} season schedule from ESPN API (sync mode)..."
|
||||
)
|
||||
try:
|
||||
response = self.session.get(
|
||||
ESPN_WNBA_SCOREBOARD_URL,
|
||||
params={"dates": current_year, "limit": 1000},
|
||||
headers=self.headers,
|
||||
timeout=15,
|
||||
)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
events = data.get("events", [])
|
||||
|
||||
if use_cache:
|
||||
self.cache_manager.set(cache_key, events)
|
||||
|
||||
self.logger.info(
|
||||
f"Successfully fetched {len(events)} events for the {current_year} season."
|
||||
)
|
||||
return {"events": events}
|
||||
except requests.exceptions.RequestException as e:
|
||||
self.logger.error(f"API error fetching full schedule: {e}")
|
||||
return None
|
||||
|
||||
def _fetch_data(self) -> Optional[Dict]:
|
||||
"""Fetch data using shared data mechanism or direct fetch for live."""
|
||||
if isinstance(self, WNBALiveManager):
|
||||
# Live games should fetch only current games, not entire season
|
||||
return self._fetch_todays_games()
|
||||
else:
|
||||
# Recent and Upcoming managers should use cached season data
|
||||
return self._fetch_wnba_api_data(use_cache=True)
|
||||
|
||||
|
||||
class WNBALiveManager(BaseWNBAManager, BasketballLive):
|
||||
"""Manager for live NBA games."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: Dict[str, Any],
|
||||
display_manager: DisplayManager,
|
||||
cache_manager: CacheManager,
|
||||
):
|
||||
super().__init__(config, display_manager, cache_manager)
|
||||
self.logger = logging.getLogger("WNBALiveManager") # Changed logger name
|
||||
|
||||
if self.test_mode:
|
||||
# More detailed test game for NBA
|
||||
self.current_game = {
|
||||
"id": "test001",
|
||||
"home_abbr": "CHI",
|
||||
"home_id": "123",
|
||||
"away_abbr": "ATL",
|
||||
"away_id": "asdf",
|
||||
"home_score": "21",
|
||||
"away_score": "17",
|
||||
"period": 3,
|
||||
"period_text": "Q3",
|
||||
"clock": "5:24",
|
||||
"home_logo_path": Path(self.logo_dir, "CHI.png"),
|
||||
"away_logo_path": Path(self.logo_dir, "ATL.png"),
|
||||
"is_live": True,
|
||||
"is_final": False,
|
||||
"is_upcoming": False,
|
||||
"is_halftime": False,
|
||||
}
|
||||
self.live_games = [self.current_game]
|
||||
self.logger.info("Initialized WNBALiveManager with test game: BUF vs KC")
|
||||
else:
|
||||
self.logger.info(" Initialized WNBALiveManager in live mode")
|
||||
|
||||
|
||||
class WNBARecentManager(BaseWNBAManager, SportsRecent):
|
||||
"""Manager for recently completed WNBA games."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: Dict[str, Any],
|
||||
display_manager: DisplayManager,
|
||||
cache_manager: CacheManager,
|
||||
):
|
||||
super().__init__(config, display_manager, cache_manager)
|
||||
self.logger = logging.getLogger("WNBARecentManager") # Changed logger name
|
||||
self.logger.info(
|
||||
f"Initialized WNBARecentManager with {len(self.favorite_teams)} favorite teams"
|
||||
)
|
||||
|
||||
|
||||
class WNBAUpcomingManager(BaseWNBAManager, SportsUpcoming):
|
||||
"""Manager for upcoming WNBA games."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: Dict[str, Any],
|
||||
display_manager: DisplayManager,
|
||||
cache_manager: CacheManager,
|
||||
):
|
||||
super().__init__(config, display_manager, cache_manager)
|
||||
self.logger = logging.getLogger("WNBAUpcomingManager") # Changed logger name
|
||||
self.logger.info(
|
||||
f"Initialized WNBAUpcomingManager with {len(self.favorite_teams)} favorite teams"
|
||||
)
|
||||
|
||||
"""Display upcoming games."""
|
||||
if not self.upcoming_games:
|
||||
return
|
||||
|
||||
try:
|
||||
current_time = time.time()
|
||||
|
||||
# Check if it's time to switch games
|
||||
if (
|
||||
len(self.upcoming_games) > 1
|
||||
and current_time - self.last_game_switch >= self.game_display_duration
|
||||
):
|
||||
# Move to next game
|
||||
self.current_game_index = (self.current_game_index + 1) % len(
|
||||
self.upcoming_games
|
||||
)
|
||||
self.current_game = self.upcoming_games[self.current_game_index]
|
||||
self.last_game_switch = current_time
|
||||
force_clear = True
|
||||
|
||||
# Log team switching
|
||||
if self.current_game:
|
||||
away_abbr = self.current_game.get("away_abbr", "UNK")
|
||||
home_abbr = self.current_game.get("home_abbr", "UNK")
|
||||
self.logger.info(
|
||||
f"[NBA Upcoming] Showing {away_abbr} vs {home_abbr}"
|
||||
)
|
||||
|
||||
# Draw the scorebug layout
|
||||
self._draw_scorebug_layout(self.current_game, force_clear)
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(
|
||||
f"[NBA] Error displaying upcoming game: {e}", exc_info=True
|
||||
)
|
||||
Reference in New Issue
Block a user