From 1dcd79f758e4cf0bfa8260ea186420a0d75ce0a1 Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Sun, 20 Jul 2025 20:52:55 -0500 Subject: [PATCH] add granular control over records display and troubleshooting odds ticker. Enabled Team Records for all sports --- config/config.json | 62 +++++++++++++++------------ src/mlb_manager.py | 3 +- src/nba_managers.py | 29 +++++++++++++ src/ncaa_baseball_managers.py | 28 ++++++++++++ src/ncaa_fb_managers.py | 53 +++++++++++++++++++++++ src/ncaam_basketball_managers.py | 52 ++++++++++++++--------- src/nfl_managers.py | 53 +++++++++++++++++++++++ src/nhl_managers.py | 29 +++++++++++++ src/odds_ticker_manager.py | 73 +++++++++++++++++++++----------- src/soccer_managers.py | 29 +++++++++++++ 10 files changed, 340 insertions(+), 71 deletions(-) diff --git a/config/config.json b/config/config.json index c954fc22..1e30eb3f 100644 --- a/config/config.json +++ b/config/config.json @@ -38,7 +38,7 @@ "hourly_forecast": 15, "daily_forecast": 15, "stock_news": 20, - "odds_ticker": 25, + "odds_ticker": 45, "nhl_live": 30, "nhl_recent": 20, "nhl_upcoming": 20, @@ -112,9 +112,8 @@ "show_favorite_teams_only": true, "enabled_leagues": ["mlb"], "update_interval": 3600, - "scroll_speed": 2, - "scroll_delay": 0.05, - "display_duration": 30 + "scroll_speed": 1, + "scroll_delay": 0.001 }, "calendar": { "enabled": true, @@ -126,15 +125,16 @@ }, "nhl_scoreboard": { "enabled": false, - "show_odds": false, + "show_odds": true, "test_mode": false, "update_interval_seconds": 3600, "live_update_interval": 30, "recent_update_interval": 3600, "upcoming_update_interval": 3600, "recent_game_hours": 48, - "favorite_teams": ["TB", "DAL"], + "favorite_teams": ["TB"], "logo_dir": "assets/sports/nhl_logos", + "show_records": true, "display_modes": { "nhl_live": true, "nhl_recent": true, @@ -142,8 +142,8 @@ } }, "nba_scoreboard": { - "enabled": true, - "show_odds": false, + "enabled": false, + "show_odds": true, "test_mode": false, "update_interval_seconds": 3600, "live_update_interval": 30, @@ -152,8 +152,9 @@ "recent_update_interval": 3600, "upcoming_update_interval": 3600, "recent_game_hours": 72, - "favorite_teams": ["DAL", "ATL"], + "favorite_teams": ["DAL"], "logo_dir": "assets/sports/nba_logos", + "show_records": true, "display_modes": { "nba_live": true, "nba_recent": true, @@ -172,6 +173,7 @@ "future_fetch_days": 7, "favorite_teams": ["TB", "DAL"], "logo_dir": "assets/sports/nfl_logos", + "show_records": true, "display_modes": { "nfl_live": true, "nfl_recent": true, @@ -180,7 +182,7 @@ }, "ncaa_fb_scoreboard": { "enabled": false, - "show_odds": false, + "show_odds": true, "test_mode": false, "update_interval_seconds": 3600, "live_update_interval": 30, @@ -190,30 +192,16 @@ "future_fetch_days": 7, "favorite_teams": ["UGA", "AUB"], "logo_dir": "assets/sports/ncaa_fbs_logos", + "show_records": true, "display_modes": { "ncaa_fb_live": true, "ncaa_fb_recent": true, "ncaa_fb_upcoming": true } }, - "ncaam_basketball_scoreboard": { - "enabled": false, - "show_odds": false, - "test_mode": false, - "update_interval_seconds": 3600, - "live_update_interval": 30, - "recent_game_hours": 72, - "favorite_teams": ["UGA", "AUB"], - "logo_dir": "assets/sports/ncaa_fbs_logos", - "display_modes": { - "ncaam_basketball_live": true, - "ncaam_basketball_recent": true, - "ncaam_basketball_upcoming": true - } - }, "ncaa_baseball_scoreboard": { "enabled": false, - "show_odds": false, + "show_odds": true, "test_mode": false, "update_interval_seconds": 3600, "live_update_interval": 30, @@ -222,12 +210,29 @@ "recent_game_hours": 72, "favorite_teams": ["UGA", "AUB"], "logo_dir": "assets/sports/ncaa_fbs_logos", + "show_records": true, "display_modes": { "ncaa_baseball_live": true, "ncaa_baseball_recent": true, "ncaa_baseball_upcoming": true } }, + "ncaam_basketball_scoreboard": { + "enabled": false, + "show_odds": true, + "test_mode": false, + "update_interval_seconds": 3600, + "live_update_interval": 30, + "recent_game_hours": 72, + "favorite_teams": ["UGA", "AUB"], + "logo_dir": "assets/sports/ncaa_fbs_logos", + "show_records": true, + "display_modes": { + "ncaam_basketball_live": true, + "ncaam_basketball_recent": true, + "ncaam_basketball_upcoming": true + } + }, "youtube": { "enabled": true, "update_interval": 3600 @@ -245,6 +250,7 @@ "recent_game_hours": 48, "favorite_teams": ["TB", "TEX"], "logo_dir": "assets/sports/mlb_logos", + "show_records": true, "display_modes": { "mlb_live": false, "mlb_recent": true, @@ -280,6 +286,7 @@ }, "soccer_scoreboard": { "enabled": false, + "show_odds": true, "test_mode": false, "update_interval_seconds": 3600, "live_update_interval": 30, @@ -287,9 +294,10 @@ "upcoming_update_interval": 3600, "recent_game_hours": 168, "upcoming_fetch_days": 7, - "favorite_teams": ["BAR"], + "favorite_teams": ["LIV"], "leagues": ["eng.1", "esp.1", "ger.1", "ita.1", "fra.1", "uefa.champions", "usa.1"], "logo_dir": "assets/sports/soccer_logos", + "show_records": true, "display_modes": { "soccer_live": true, "soccer_recent": true, diff --git a/src/mlb_manager.py b/src/mlb_manager.py index 01d36a6a..bc130c11 100644 --- a/src/mlb_manager.py +++ b/src/mlb_manager.py @@ -24,6 +24,7 @@ class BaseMLBManager: self.mlb_config = config.get('mlb', {}) self.show_odds = self.mlb_config.get("show_odds", False) self.favorite_teams = self.mlb_config.get('favorite_teams', []) + self.show_records = self.mlb_config.get('show_records', False) self.cache_manager = CacheManager() self.odds_manager = OddsManager(self.cache_manager, self.config) self.logger = logging.getLogger(__name__) @@ -315,7 +316,7 @@ class BaseMLBManager: pass # Draw records for upcoming and recent games - if game_data['status'] in ['status_scheduled', 'status_final', 'final', 'completed']: + if self.show_records and game_data['status'] in ['status_scheduled', 'status_final', 'final', 'completed']: try: record_font = ImageFont.truetype("assets/fonts/4x6-font.ttf", 6) except IOError: diff --git a/src/nba_managers.py b/src/nba_managers.py index b1822d75..947d626e 100644 --- a/src/nba_managers.py +++ b/src/nba_managers.py @@ -45,6 +45,7 @@ class BaseNBAManager: self.show_odds = self.nba_config.get("show_odds", False) self.test_mode = self.nba_config.get("test_mode", False) self.logo_dir = self.nba_config.get("logo_dir", "assets/sports/nba_logos") + self.show_records = self.nba_config.get('show_records', False) self.update_interval = self.nba_config.get("update_interval_seconds", 300) self.last_update = 0 self.current_game = None @@ -424,6 +425,8 @@ class BaseNBAManager: home_team = next(c for c in competitors if c.get("homeAway") == "home") away_team = next(c for c in competitors if c.get("homeAway") == "away") + home_record = home_team.get('records', [{}])[0].get('summary', '') if home_team.get('records') else '' + away_record = away_team.get('records', [{}])[0].get('summary', '') if away_team.get('records') else '' # Format game time and date for display game_time = "" @@ -452,9 +455,11 @@ class BaseNBAManager: "is_within_window": is_within_window, "home_abbr": home_team["team"]["abbreviation"], "home_score": home_team.get("score", "0"), + "home_record": home_record, "home_logo_path": os.path.join(self.logo_dir, f"{home_team['team']['abbreviation']}.png"), "away_abbr": away_team["team"]["abbreviation"], "away_score": away_team.get("score", "0"), + "away_record": away_record, "away_logo_path": os.path.join(self.logo_dir, f"{away_team['team']['abbreviation']}.png"), "game_time": game_time, "game_date": game_date @@ -581,6 +586,30 @@ class BaseNBAManager: if 'odds' in game and game['odds']: self._draw_dynamic_odds(draw, game['odds'], self.display_width, self.display_height) + # Draw records if enabled + if self.show_records: + try: + record_font = ImageFont.truetype("assets/fonts/4x6-font.ttf", 6) + except IOError: + record_font = ImageFont.load_default() + + away_record = game.get('away_record', '') + home_record = game.get('home_record', '') + + record_bbox = draw.textbbox((0,0), "0-0", font=record_font) + record_height = record_bbox[3] - record_bbox[1] + record_y = self.display_height - record_height - 1 + + if away_record: + away_record_x = 2 + self._draw_text_with_outline(draw, away_record, (away_record_x, record_y), record_font) + + if home_record: + home_record_bbox = draw.textbbox((0,0), home_record, font=record_font) + home_record_width = home_record_bbox[2] - home_record_bbox[0] + home_record_x = self.display_width - home_record_width - 2 + self._draw_text_with_outline(draw, home_record, (home_record_x, record_y), record_font) + # Display the image self.display_manager.image.paste(main_img, (0, 0)) self.display_manager.update_display() diff --git a/src/ncaa_baseball_managers.py b/src/ncaa_baseball_managers.py index 9e45c04f..af527a38 100644 --- a/src/ncaa_baseball_managers.py +++ b/src/ncaa_baseball_managers.py @@ -26,6 +26,7 @@ class BaseNCAABaseballManager: self.display_manager = display_manager self.ncaa_baseball_config = config.get('ncaa_baseball_scoreboard', {}) self.show_odds = self.ncaa_baseball_config.get('show_odds', False) + self.show_records = self.ncaa_baseball_config.get('show_records', False) self.favorite_teams = self.ncaa_baseball_config.get('favorite_teams', []) self.cache_manager = CacheManager() self.odds_manager = OddsManager(self.cache_manager, self.config) @@ -276,6 +277,29 @@ class BaseNCAABaseballManager: score_y = height - score_font.getmetrics()[0] - 2 # Adjusted for font metrics self._draw_text_with_outline(draw, score_text, (score_x, score_y), score_font) + if self.show_records and game_data['status'] in ['status_scheduled', 'status_final', 'final', 'completed']: + try: + record_font = ImageFont.truetype("assets/fonts/4x6-font.ttf", 6) + except IOError: + record_font = ImageFont.load_default() + + away_record = game_data.get('away_record', '') + home_record = game_data.get('home_record', '') + + record_bbox = draw.textbbox((0, 0), "0-0", font=record_font) + record_height = record_bbox[3] - record_bbox[1] + record_y = height - record_height - 1 + + if away_record: + away_record_x = 2 + self._draw_text_with_outline(draw, away_record, (away_record_x, record_y), record_font) + + if home_record: + home_record_bbox = draw.textbbox((0, 0), home_record, font=record_font) + home_record_width = home_record_bbox[2] - home_record_bbox[0] + home_record_x = width - home_record_width - 2 + self._draw_text_with_outline(draw, home_record, (home_record_x, record_y), record_font) + # Draw betting odds if available and enabled if self.show_odds and 'odds' in game_data: odds_details = game_data['odds'].get('details', 'N/A') @@ -393,6 +417,8 @@ class BaseNCAABaseballManager: home_abbr = home_team['team'].get('abbreviation', 'N/A') away_abbr = away_team['team'].get('abbreviation', 'N/A') + home_record = home_team.get('records', [{}])[0].get('summary', '') if home_team.get('records') else '' + away_record = away_team.get('records', [{}])[0].get('summary', '') if away_team.get('records') else '' is_favorite_game = (home_abbr in self.favorite_teams or away_abbr in self.favorite_teams) @@ -455,6 +481,8 @@ class BaseNCAABaseballManager: 'home_team': home_abbr, 'away_score': away_team.get('score', '0'), 'home_score': home_team.get('score', '0'), + 'away_record': away_record, + 'home_record': home_record, 'status': status, 'status_state': status_state, 'inning': inning, diff --git a/src/ncaa_fb_managers.py b/src/ncaa_fb_managers.py index cdcd7979..4ae508e9 100644 --- a/src/ncaa_fb_managers.py +++ b/src/ncaa_fb_managers.py @@ -97,6 +97,7 @@ class BaseNCAAFBManager: # Renamed class self.test_mode = self.ncaa_fb_config.get("test_mode", False) self.logo_dir = self.ncaa_fb_config.get("logo_dir", "assets/sports/ncaa_fbs_logos") # Changed logo dir self.update_interval = self.ncaa_fb_config.get("update_interval_seconds", 60) + self.show_records = self.ncaa_fb_config.get('show_records', False) self.last_update = 0 self.current_game = None self.fonts = self._load_fonts() @@ -424,6 +425,8 @@ class BaseNCAAFBManager: # Renamed class home_abbr = home_team["team"]["abbreviation"] away_abbr = away_team["team"]["abbreviation"] + home_record = home_team.get('records', [{}])[0].get('summary', '') if home_team.get('records') else '' + away_record = away_team.get('records', [{}])[0].get('summary', '') if away_team.get('records') else '' # Filter by favorite teams if the list is not empty if self.favorite_teams and not (home_abbr in self.favorite_teams or away_abbr in self.favorite_teams): @@ -497,10 +500,12 @@ class BaseNCAAFBManager: # Renamed class "is_halftime": status["type"]["state"] == "halftime", # Added halftime check "home_abbr": home_abbr, "home_score": home_team.get("score", "0"), + "home_record": home_record, "home_logo_path": os.path.join(self.logo_dir, f"{home_abbr}.png"), "home_timeouts": home_timeouts, "away_abbr": away_abbr, "away_score": away_team.get("score", "0"), + "away_record": away_record, "away_logo_path": os.path.join(self.logo_dir, f"{away_abbr}.png"), "away_timeouts": away_timeouts, "game_time": game_time, @@ -1019,6 +1024,30 @@ class NCAAFBRecentManager(BaseNCAAFBManager): # Renamed class if 'odds' in game and game['odds']: self._draw_dynamic_odds(draw_overlay, game['odds'], self.display_width, self.display_height) + # Draw records if enabled + if self.show_records: + try: + record_font = ImageFont.truetype("assets/fonts/4x6-font.ttf", 6) + except IOError: + record_font = ImageFont.load_default() + + away_record = game.get('away_record', '') + home_record = game.get('home_record', '') + + 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 + + if away_record: + away_record_x = 2 + self._draw_text_with_outline(draw_overlay, away_record, (away_record_x, record_y), record_font) + + if home_record: + home_record_bbox = draw_overlay.textbbox((0,0), home_record, font=record_font) + home_record_width = home_record_bbox[2] - home_record_bbox[0] + home_record_x = self.display_width - home_record_width - 2 + self._draw_text_with_outline(draw_overlay, home_record, (home_record_x, record_y), record_font) + # Composite and display main_img = Image.alpha_composite(main_img, overlay) main_img = main_img.convert('RGB') @@ -1213,6 +1242,30 @@ class NCAAFBUpcomingManager(BaseNCAAFBManager): # Renamed class if 'odds' in game and game['odds']: self._draw_dynamic_odds(draw_overlay, game['odds'], self.display_width, self.display_height) + # Draw records if enabled + if self.show_records: + try: + record_font = ImageFont.truetype("assets/fonts/4x6-font.ttf", 6) + except IOError: + record_font = ImageFont.load_default() + + away_record = game.get('away_record', '') + home_record = game.get('home_record', '') + + 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 + + if away_record: + away_record_x = 2 + self._draw_text_with_outline(draw_overlay, away_record, (away_record_x, record_y), record_font) + + if home_record: + home_record_bbox = draw_overlay.textbbox((0,0), home_record, font=record_font) + home_record_width = home_record_bbox[2] - home_record_bbox[0] + home_record_x = self.display_width - home_record_width - 2 + self._draw_text_with_outline(draw_overlay, home_record, (home_record_x, record_y), record_font) + # Composite and display main_img = Image.alpha_composite(main_img, overlay) main_img = main_img.convert('RGB') diff --git a/src/ncaam_basketball_managers.py b/src/ncaam_basketball_managers.py index c151a8d8..c588f162 100644 --- a/src/ncaam_basketball_managers.py +++ b/src/ncaam_basketball_managers.py @@ -44,8 +44,9 @@ class BaseNCAAMBasketballManager: self.is_enabled = self.ncaam_basketball_config.get("enabled", False) self.show_odds = self.ncaam_basketball_config.get("show_odds", False) self.test_mode = self.ncaam_basketball_config.get("test_mode", False) - self.logo_dir = self.ncaam_basketball_config.get("logo_dir", "assets/sports/ncaam_logos") - self.update_interval = self.ncaam_basketball_config.get("update_interval_seconds", 300) + self.logo_dir = self.ncaam_basketball_config.get("logo_dir", "assets/sports/ncaa_fbs_logos") + self.update_interval = self.ncaam_basketball_config.get("update_interval_seconds", 60) + self.show_records = self.ncaam_basketball_config.get('show_records', False) self.last_update = 0 self.current_game = None self.fonts = self._load_fonts() @@ -421,6 +422,8 @@ class BaseNCAAMBasketballManager: home_team = next(c for c in competitors if c.get("homeAway") == "home") away_team = next(c for c in competitors if c.get("homeAway") == "away") + home_record = home_team.get('records', [{}])[0].get('summary', '') if home_team.get('records') else '' + away_record = away_team.get('records', [{}])[0].get('summary', '') if away_team.get('records') else '' # Format game time and date for display game_time = "" @@ -450,9 +453,11 @@ class BaseNCAAMBasketballManager: "is_within_window": is_within_window, "home_abbr": home_team["team"]["abbreviation"], "home_score": home_team.get("score", "0"), + "home_record": home_record, "home_logo_path": os.path.join(self.logo_dir, f"{home_team['team']['abbreviation']}.png"), "away_abbr": away_team["team"]["abbreviation"], "away_score": away_team.get("score", "0"), + "away_record": away_record, "away_logo_path": os.path.join(self.logo_dir, f"{away_team['team']['abbreviation']}.png"), "game_time": game_time, "game_date": game_date, @@ -589,23 +594,32 @@ class BaseNCAAMBasketballManager: self._draw_text_with_outline(draw, clock, (clock_x, clock_y), self.fonts['time']) # Display odds if available - if 'odds' in game: - odds = game['odds'] - spread = odds.get('spread', {}).get('point', None) - if spread is not None: - # Format spread text - spread_text = f"{spread:+.1f}" if spread > 0 else f"{spread:.1f}" - - # Choose color and position based on which team has the spread - if odds.get('spread', {}).get('team') == game['home_abbr']: - text_color = (255, 100, 100) # Reddish - spread_x = self.display_width - draw.textlength(spread_text, font=self.fonts['status']) - 2 - else: - text_color = (100, 255, 100) # Greenish - spread_x = 2 - - spread_y = self.display_height - 8 - self._draw_text_with_outline(draw, spread_text, (spread_x, spread_y), self.fonts['status'], fill=text_color) + if 'odds' in game and game['odds']: + self._draw_dynamic_odds(draw, game['odds'], self.display_width, self.display_height) + + # Draw records if enabled + if self.show_records: + try: + record_font = ImageFont.truetype("assets/fonts/4x6-font.ttf", 6) + except IOError: + record_font = ImageFont.load_default() + + away_record = game.get('away_record', '') + home_record = game.get('home_record', '') + + record_bbox = draw.textbbox((0,0), "0-0", font=record_font) + record_height = record_bbox[3] - record_bbox[1] + record_y = self.display_height - record_height - 1 + + if away_record: + away_record_x = 2 + self._draw_text_with_outline(draw, away_record, (away_record_x, record_y), record_font) + + if home_record: + home_record_bbox = draw.textbbox((0,0), home_record, font=record_font) + home_record_width = home_record_bbox[2] - home_record_bbox[0] + home_record_x = self.display_width - home_record_width - 2 + self._draw_text_with_outline(draw, home_record, (home_record_x, record_y), record_font) # Display the image self.display_manager.image.paste(main_img, (0, 0)) diff --git a/src/nfl_managers.py b/src/nfl_managers.py index cb128f34..0edc45b4 100644 --- a/src/nfl_managers.py +++ b/src/nfl_managers.py @@ -97,6 +97,7 @@ class BaseNFLManager: # Renamed class self.test_mode = self.nfl_config.get("test_mode", False) self.logo_dir = self.nfl_config.get("logo_dir", "assets/sports/nfl_logos") # Changed logo dir self.update_interval = self.nfl_config.get("update_interval_seconds", 60) + self.show_records = self.nfl_config.get('show_records', False) self.last_update = 0 self.current_game = None self.fonts = self._load_fonts() @@ -424,6 +425,8 @@ class BaseNFLManager: # Renamed class home_abbr = home_team["team"]["abbreviation"] away_abbr = away_team["team"]["abbreviation"] + home_record = home_team.get('records', [{}])[0].get('summary', '') if home_team.get('records') else '' + away_record = away_team.get('records', [{}])[0].get('summary', '') if away_team.get('records') else '' # Filter by favorite teams if the list is not empty if self.favorite_teams and not (home_abbr in self.favorite_teams or away_abbr in self.favorite_teams): @@ -498,10 +501,12 @@ class BaseNFLManager: # Renamed class "is_halftime": status["type"]["state"] == "halftime", # Added halftime check "home_abbr": home_abbr, "home_score": home_team.get("score", "0"), + "home_record": home_record, "home_logo_path": os.path.join(self.logo_dir, f"{home_abbr}.png"), "home_timeouts": home_timeouts, "away_abbr": away_abbr, "away_score": away_team.get("score", "0"), + "away_record": away_record, "away_logo_path": os.path.join(self.logo_dir, f"{away_abbr}.png"), "away_timeouts": away_timeouts, "game_time": game_time, @@ -1008,6 +1013,30 @@ class NFLRecentManager(BaseNFLManager): # Renamed class if 'odds' in game and game['odds']: self._draw_dynamic_odds(draw_overlay, game['odds'], self.display_width, self.display_height) + # Draw records if enabled + if self.show_records: + try: + record_font = ImageFont.truetype("assets/fonts/4x6-font.ttf", 6) + except IOError: + record_font = ImageFont.load_default() + + away_record = game.get('away_record', '') + home_record = game.get('home_record', '') + + 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 + + if away_record: + away_record_x = 2 + self._draw_text_with_outline(draw_overlay, away_record, (away_record_x, record_y), record_font) + + if home_record: + home_record_bbox = draw_overlay.textbbox((0,0), home_record, font=record_font) + home_record_width = home_record_bbox[2] - home_record_bbox[0] + home_record_x = self.display_width - home_record_width - 2 + self._draw_text_with_outline(draw_overlay, home_record, (home_record_x, record_y), record_font) + # Composite and display main_img = Image.alpha_composite(main_img, overlay) main_img = main_img.convert('RGB') @@ -1205,6 +1234,30 @@ class NFLUpcomingManager(BaseNFLManager): # Renamed class if 'odds' in game and game['odds']: self._draw_dynamic_odds(draw_overlay, game['odds'], self.display_width, self.display_height) + # Draw records if enabled + if self.show_records: + try: + record_font = ImageFont.truetype("assets/fonts/4x6-font.ttf", 6) + except IOError: + record_font = ImageFont.load_default() + + away_record = game.get('away_record', '') + home_record = game.get('home_record', '') + + 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 + + if away_record: + away_record_x = 2 + self._draw_text_with_outline(draw_overlay, away_record, (away_record_x, record_y), record_font) + + if home_record: + home_record_bbox = draw_overlay.textbbox((0,0), home_record, font=record_font) + home_record_width = home_record_bbox[2] - home_record_bbox[0] + home_record_x = self.display_width - home_record_width - 2 + self._draw_text_with_outline(draw_overlay, home_record, (home_record_x, record_y), record_font) + # Composite and display main_img = Image.alpha_composite(main_img, overlay) main_img = main_img.convert('RGB') diff --git a/src/nhl_managers.py b/src/nhl_managers.py index ddb00393..1efb1e8f 100644 --- a/src/nhl_managers.py +++ b/src/nhl_managers.py @@ -95,6 +95,7 @@ class BaseNHLManager: self.test_mode = self.nhl_config.get("test_mode", False) # Use test_mode from config self.logo_dir = self.nhl_config.get("logo_dir", "assets/sports/nhl_logos") self.update_interval = self.nhl_config.get("update_interval_seconds", 60) + self.show_records = self.nhl_config.get('show_records', False) self.last_update = 0 self.current_game = None self.fonts = self._load_fonts() @@ -364,6 +365,8 @@ class BaseNHLManager: home_team = next(c for c in competitors if c.get("homeAway") == "home") away_team = next(c for c in competitors if c.get("homeAway") == "away") + home_record = home_team.get('records', [{}])[0].get('summary', '') if home_team.get('records') else '' + away_record = away_team.get('records', [{}])[0].get('summary', '') if away_team.get('records') else '' # Format game time and date for display game_time = "" @@ -400,9 +403,11 @@ class BaseNHLManager: "is_within_window": is_within_window, "home_abbr": home_team["team"]["abbreviation"], "home_score": home_team.get("score", "0"), + "home_record": home_record, "home_logo_path": os.path.join(self.logo_dir, f"{home_team['team']['abbreviation']}.png"), "away_abbr": away_team["team"]["abbreviation"], "away_score": away_team.get("score", "0"), + "away_record": away_record, "away_logo_path": os.path.join(self.logo_dir, f"{away_team['team']['abbreviation']}.png"), "game_time": game_time, "game_date": game_date, @@ -551,6 +556,30 @@ class BaseNHLManager: spread_y = self.display_height - 8 self._draw_text_with_outline(draw, spread_text, (spread_x, spread_y), self.fonts['status'], fill=text_color) + # Draw records if enabled + if self.show_records: + try: + record_font = ImageFont.truetype("assets/fonts/4x6-font.ttf", 6) + except IOError: + record_font = ImageFont.load_default() + + away_record = game.get('away_record', '') + home_record = game.get('home_record', '') + + record_bbox = draw.textbbox((0,0), "0-0", font=record_font) + record_height = record_bbox[3] - record_bbox[1] + record_y = self.display_height - record_height - 1 + + if away_record: + away_record_x = 2 + self._draw_text_with_outline(draw, away_record, (away_record_x, record_y), record_font) + + if home_record: + home_record_bbox = draw.textbbox((0,0), home_record, font=record_font) + home_record_width = home_record_bbox[2] - home_record_bbox[0] + home_record_x = self.display_width - home_record_width - 2 + self._draw_text_with_outline(draw, home_record, (home_record_x, record_y), record_font) + # Display the image self.display_manager.image.paste(main_img, (0, 0)) self.display_manager.update_display() diff --git a/src/odds_ticker_manager.py b/src/odds_ticker_manager.py index 48138730..75af7227 100644 --- a/src/odds_ticker_manager.py +++ b/src/odds_ticker_manager.py @@ -102,14 +102,30 @@ class OddsTickerManager: # This is a simplified implementation; a more robust solution would cache team data try: sport = 'baseball' if league == 'mlb' else 'football' if league in ['nfl', 'college-football'] else 'basketball' - url = f"https://site.api.espn.com/apis/site/v2/sports/{sport}/{league}/teams/{team_abbr}" + + # Use a more specific endpoint for college sports + if league == 'college-football': + url = f"https://site.api.espn.com/apis/site/v2/sports/football/college-football/teams/{team_abbr}" + else: + url = f"https://site.api.espn.com/apis/site/v2/sports/{sport}/{league}/teams/{team_abbr}" + response = requests.get(url, timeout=10) response.raise_for_status() data = response.json() - record = data.get('team', {}).get('record', {}).get('summary', 'N/A') - return record + + # Different path for college sports records + if league == 'college-football': + record_items = data.get('team', {}).get('record', {}).get('items', []) + if record_items: + return record_items[0].get('summary', 'N/A') + else: + return 'N/A' + else: + record = data.get('team', {}).get('record', {}).get('summary', 'N/A') + return record + except Exception as e: - logger.error(f"Error fetching record for {team_abbr}: {e}") + logger.error(f"Error fetching record for {team_abbr} in league {league}: {e}") return "N/A" def _get_team_logo(self, team_abbr: str, logo_dir: str) -> Optional[Image.Image]: @@ -219,6 +235,10 @@ class OddsTickerManager: home_abbr = home_team['team']['abbreviation'] away_abbr = away_team['team']['abbreviation'] + # Get records directly from the scoreboard feed + home_record = home_team.get('records', [{}])[0].get('summary', '') if home_team.get('records') else '' + away_record = away_team.get('records', [{}])[0].get('summary', '') if away_team.get('records') else '' + # Check if this game involves favorite teams BEFORE fetching odds if self.show_favorite_teams_only: favorite_teams = league_config.get('favorite_teams', []) @@ -253,10 +273,6 @@ class OddsTickerManager: logger.debug(f"Game {game_id} has no valid odds data, setting odds to None") odds_data = None - # Fetch team records - home_record = self._fetch_team_record(home_abbr, league) - away_record = self._fetch_team_record(away_abbr, league) - game_data = { 'id': game_id, 'league': league_config['league'], @@ -385,29 +401,34 @@ class OddsTickerManager: # "vs." text vs_text = "vs." - vs_width = temp_draw.textlength(vs_text, font=vs_font) + vs_width = int(temp_draw.textlength(vs_text, font=vs_font)) # Team and record text - away_team_text = f"{game.get('away_team', 'N/A')} ({game.get('away_record', '')})" - home_team_text = f"{game.get('home_team', 'N/A')} ({game.get('home_record', '')})" - away_team_width = temp_draw.textlength(away_team_text, font=team_font) - home_team_width = temp_draw.textlength(home_team_text, font=team_font) + away_team_text = f"{game.get('away_team', 'N/A')} ({game.get('away_record', '') or 'N/A'})" + home_team_text = f"{game.get('home_team', 'N/A')} ({game.get('home_record', '') or 'N/A'})" + away_team_width = int(temp_draw.textlength(away_team_text, font=team_font)) + home_team_width = int(temp_draw.textlength(home_team_text, font=team_font)) team_info_width = max(away_team_width, home_team_width) # Odds text odds = game.get('odds') or {} home_team_odds = odds.get('home_team_odds', {}) away_team_odds = odds.get('away_team_odds', {}) - home_spread = home_team_odds.get('spread_odds') - away_spread = away_team_odds.get('spread_odds') + + # Determine the favorite and get the spread + home_spread = home_team_odds.get('point_spread') + away_spread = away_team_odds.get('point_spread') + + # Check for valid spread values before comparing + home_favored = isinstance(home_spread, (int, float)) and home_spread < 0 + away_favored = isinstance(away_spread, (int, float)) and away_spread < 0 + over_under = odds.get('over_under') - home_favored = home_spread is not None and home_spread < 0 - away_favored = away_spread is not None and away_spread < 0 - away_odds_text = "" home_odds_text = "" + # Simplified odds placement logic if home_favored: home_odds_text = f"{home_spread}" if over_under: @@ -416,11 +437,11 @@ class OddsTickerManager: away_odds_text = f"{away_spread}" if over_under: home_odds_text = f"O/U {over_under}" - elif over_under: # No clear favorite, put O/U on home line + elif over_under: home_odds_text = f"O/U {over_under}" - - away_odds_width = temp_draw.textlength(away_odds_text, font=odds_font) - home_odds_width = temp_draw.textlength(home_odds_text, font=odds_font) + + away_odds_width = int(temp_draw.textlength(away_odds_text, font=odds_font)) + home_odds_width = int(temp_draw.textlength(home_odds_text, font=odds_font)) odds_width = max(away_odds_width, home_odds_width) # --- Calculate total width --- @@ -463,8 +484,12 @@ class OddsTickerManager: odds_font_height = odds_font.size if hasattr(odds_font, 'size') else 6 odds_y_away = 2 odds_y_home = height - odds_font_height - 2 - draw.text((current_x, odds_y_away), away_odds_text, font=odds_font, fill=(255, 255, 0)) # Yellow for odds - draw.text((current_x, odds_y_home), home_odds_text, font=odds_font, fill=(0, 255, 0)) # Green for favorite + + # Use a consistent color for all odds text + odds_color = (255, 255, 0) # Yellow + + draw.text((current_x, odds_y_away), away_odds_text, font=odds_font, fill=odds_color) + draw.text((current_x, odds_y_home), home_odds_text, font=odds_font, fill=odds_color) return image diff --git a/src/soccer_managers.py b/src/soccer_managers.py index 8a0e868f..6af50b31 100644 --- a/src/soccer_managers.py +++ b/src/soccer_managers.py @@ -115,6 +115,7 @@ class BaseSoccerManager: self.test_mode = self.soccer_config.get("test_mode", False) self.logo_dir = self.soccer_config.get("logo_dir", "assets/sports/soccer_logos") # Soccer logos self.update_interval = self.soccer_config.get("update_interval_seconds", 60) # General fallback + self.show_records = self.soccer_config.get('show_records', False) self.last_update = 0 self.current_game = None self.fonts = self._load_fonts() @@ -552,6 +553,8 @@ class BaseSoccerManager: home_team = next(c for c in competitors if c.get("homeAway") == "home") away_team = next(c for c in competitors if c.get("homeAway") == "away") + home_record = home_team.get('records', [{}])[0].get('summary', '') if home_team.get('records') else '' + away_record = away_team.get('records', [{}])[0].get('summary', '') if away_team.get('records') else '' game_time = "" game_date = "" @@ -589,9 +592,11 @@ class BaseSoccerManager: "is_within_window": is_within_window, "home_abbr": home_team["team"]["abbreviation"], "home_score": home_team.get("score", "0"), + "home_record": home_record, "home_logo": self._load_and_resize_logo(home_team["team"]["abbreviation"]), "away_abbr": away_team["team"]["abbreviation"], "away_score": away_team.get("score", "0"), + "away_record": away_record, "away_logo": self._load_and_resize_logo(away_team["team"]["abbreviation"]), "game_time": game_time, # Formatted local time (e.g., 2:30pm) "game_date": game_date, # Formatted local date (e.g., 7/21) @@ -732,6 +737,30 @@ class BaseSoccerManager: spread_y = self.display_height - 8 self._draw_text_with_outline(draw, spread_text, (spread_x, spread_y), self.fonts['status'], fill=text_color) + # Draw records if enabled + if self.show_records: + try: + record_font = ImageFont.truetype("assets/fonts/4x6-font.ttf", 6) + except IOError: + record_font = ImageFont.load_default() + + away_record = game.get('away_record', '') + home_record = game.get('home_record', '') + + record_bbox = draw.textbbox((0,0), "0-0", font=record_font) + record_height = record_bbox[3] - record_bbox[1] + record_y = self.display_height - record_height - 1 + + if away_record: + away_record_x = 2 + self._draw_text_with_outline(draw, away_record, (away_record_x, record_y), record_font) + + if home_record: + home_record_bbox = draw.textbbox((0,0), home_record, font=record_font) + home_record_width = home_record_bbox[2] - home_record_bbox[0] + home_record_x = self.display_width - home_record_width - 2 + self._draw_text_with_outline(draw, home_record, (home_record_x, record_y), record_font) + # --- Display Image --- self.display_manager.image.paste(main_img, (0, 0)) self.display_manager.update_display()