add granular control over records display and troubleshooting odds ticker. Enabled Team Records for all sports

This commit is contained in:
Chuck
2025-07-20 20:52:55 -05:00
parent 5741cdee9a
commit 1dcd79f758
10 changed files with 340 additions and 71 deletions

View File

@@ -38,7 +38,7 @@
"hourly_forecast": 15, "hourly_forecast": 15,
"daily_forecast": 15, "daily_forecast": 15,
"stock_news": 20, "stock_news": 20,
"odds_ticker": 25, "odds_ticker": 45,
"nhl_live": 30, "nhl_live": 30,
"nhl_recent": 20, "nhl_recent": 20,
"nhl_upcoming": 20, "nhl_upcoming": 20,
@@ -112,9 +112,8 @@
"show_favorite_teams_only": true, "show_favorite_teams_only": true,
"enabled_leagues": ["mlb"], "enabled_leagues": ["mlb"],
"update_interval": 3600, "update_interval": 3600,
"scroll_speed": 2, "scroll_speed": 1,
"scroll_delay": 0.05, "scroll_delay": 0.001
"display_duration": 30
}, },
"calendar": { "calendar": {
"enabled": true, "enabled": true,
@@ -126,15 +125,16 @@
}, },
"nhl_scoreboard": { "nhl_scoreboard": {
"enabled": false, "enabled": false,
"show_odds": false, "show_odds": true,
"test_mode": false, "test_mode": false,
"update_interval_seconds": 3600, "update_interval_seconds": 3600,
"live_update_interval": 30, "live_update_interval": 30,
"recent_update_interval": 3600, "recent_update_interval": 3600,
"upcoming_update_interval": 3600, "upcoming_update_interval": 3600,
"recent_game_hours": 48, "recent_game_hours": 48,
"favorite_teams": ["TB", "DAL"], "favorite_teams": ["TB"],
"logo_dir": "assets/sports/nhl_logos", "logo_dir": "assets/sports/nhl_logos",
"show_records": true,
"display_modes": { "display_modes": {
"nhl_live": true, "nhl_live": true,
"nhl_recent": true, "nhl_recent": true,
@@ -142,8 +142,8 @@
} }
}, },
"nba_scoreboard": { "nba_scoreboard": {
"enabled": true, "enabled": false,
"show_odds": false, "show_odds": true,
"test_mode": false, "test_mode": false,
"update_interval_seconds": 3600, "update_interval_seconds": 3600,
"live_update_interval": 30, "live_update_interval": 30,
@@ -152,8 +152,9 @@
"recent_update_interval": 3600, "recent_update_interval": 3600,
"upcoming_update_interval": 3600, "upcoming_update_interval": 3600,
"recent_game_hours": 72, "recent_game_hours": 72,
"favorite_teams": ["DAL", "ATL"], "favorite_teams": ["DAL"],
"logo_dir": "assets/sports/nba_logos", "logo_dir": "assets/sports/nba_logos",
"show_records": true,
"display_modes": { "display_modes": {
"nba_live": true, "nba_live": true,
"nba_recent": true, "nba_recent": true,
@@ -172,6 +173,7 @@
"future_fetch_days": 7, "future_fetch_days": 7,
"favorite_teams": ["TB", "DAL"], "favorite_teams": ["TB", "DAL"],
"logo_dir": "assets/sports/nfl_logos", "logo_dir": "assets/sports/nfl_logos",
"show_records": true,
"display_modes": { "display_modes": {
"nfl_live": true, "nfl_live": true,
"nfl_recent": true, "nfl_recent": true,
@@ -180,7 +182,7 @@
}, },
"ncaa_fb_scoreboard": { "ncaa_fb_scoreboard": {
"enabled": false, "enabled": false,
"show_odds": false, "show_odds": true,
"test_mode": false, "test_mode": false,
"update_interval_seconds": 3600, "update_interval_seconds": 3600,
"live_update_interval": 30, "live_update_interval": 30,
@@ -190,30 +192,16 @@
"future_fetch_days": 7, "future_fetch_days": 7,
"favorite_teams": ["UGA", "AUB"], "favorite_teams": ["UGA", "AUB"],
"logo_dir": "assets/sports/ncaa_fbs_logos", "logo_dir": "assets/sports/ncaa_fbs_logos",
"show_records": true,
"display_modes": { "display_modes": {
"ncaa_fb_live": true, "ncaa_fb_live": true,
"ncaa_fb_recent": true, "ncaa_fb_recent": true,
"ncaa_fb_upcoming": 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": { "ncaa_baseball_scoreboard": {
"enabled": false, "enabled": false,
"show_odds": false, "show_odds": true,
"test_mode": false, "test_mode": false,
"update_interval_seconds": 3600, "update_interval_seconds": 3600,
"live_update_interval": 30, "live_update_interval": 30,
@@ -222,12 +210,29 @@
"recent_game_hours": 72, "recent_game_hours": 72,
"favorite_teams": ["UGA", "AUB"], "favorite_teams": ["UGA", "AUB"],
"logo_dir": "assets/sports/ncaa_fbs_logos", "logo_dir": "assets/sports/ncaa_fbs_logos",
"show_records": true,
"display_modes": { "display_modes": {
"ncaa_baseball_live": true, "ncaa_baseball_live": true,
"ncaa_baseball_recent": true, "ncaa_baseball_recent": true,
"ncaa_baseball_upcoming": 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": { "youtube": {
"enabled": true, "enabled": true,
"update_interval": 3600 "update_interval": 3600
@@ -245,6 +250,7 @@
"recent_game_hours": 48, "recent_game_hours": 48,
"favorite_teams": ["TB", "TEX"], "favorite_teams": ["TB", "TEX"],
"logo_dir": "assets/sports/mlb_logos", "logo_dir": "assets/sports/mlb_logos",
"show_records": true,
"display_modes": { "display_modes": {
"mlb_live": false, "mlb_live": false,
"mlb_recent": true, "mlb_recent": true,
@@ -280,6 +286,7 @@
}, },
"soccer_scoreboard": { "soccer_scoreboard": {
"enabled": false, "enabled": false,
"show_odds": true,
"test_mode": false, "test_mode": false,
"update_interval_seconds": 3600, "update_interval_seconds": 3600,
"live_update_interval": 30, "live_update_interval": 30,
@@ -287,9 +294,10 @@
"upcoming_update_interval": 3600, "upcoming_update_interval": 3600,
"recent_game_hours": 168, "recent_game_hours": 168,
"upcoming_fetch_days": 7, "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"], "leagues": ["eng.1", "esp.1", "ger.1", "ita.1", "fra.1", "uefa.champions", "usa.1"],
"logo_dir": "assets/sports/soccer_logos", "logo_dir": "assets/sports/soccer_logos",
"show_records": true,
"display_modes": { "display_modes": {
"soccer_live": true, "soccer_live": true,
"soccer_recent": true, "soccer_recent": true,

View File

@@ -24,6 +24,7 @@ class BaseMLBManager:
self.mlb_config = config.get('mlb', {}) self.mlb_config = config.get('mlb', {})
self.show_odds = self.mlb_config.get("show_odds", False) self.show_odds = self.mlb_config.get("show_odds", False)
self.favorite_teams = self.mlb_config.get('favorite_teams', []) self.favorite_teams = self.mlb_config.get('favorite_teams', [])
self.show_records = self.mlb_config.get('show_records', False)
self.cache_manager = CacheManager() self.cache_manager = CacheManager()
self.odds_manager = OddsManager(self.cache_manager, self.config) self.odds_manager = OddsManager(self.cache_manager, self.config)
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
@@ -315,7 +316,7 @@ class BaseMLBManager:
pass pass
# Draw records for upcoming and recent games # 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: try:
record_font = ImageFont.truetype("assets/fonts/4x6-font.ttf", 6) record_font = ImageFont.truetype("assets/fonts/4x6-font.ttf", 6)
except IOError: except IOError:

View File

@@ -45,6 +45,7 @@ class BaseNBAManager:
self.show_odds = self.nba_config.get("show_odds", False) self.show_odds = self.nba_config.get("show_odds", False)
self.test_mode = self.nba_config.get("test_mode", 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.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.update_interval = self.nba_config.get("update_interval_seconds", 300)
self.last_update = 0 self.last_update = 0
self.current_game = None self.current_game = None
@@ -424,6 +425,8 @@ class BaseNBAManager:
home_team = next(c for c in competitors if c.get("homeAway") == "home") 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") 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 # Format game time and date for display
game_time = "" game_time = ""
@@ -452,9 +455,11 @@ class BaseNBAManager:
"is_within_window": is_within_window, "is_within_window": is_within_window,
"home_abbr": home_team["team"]["abbreviation"], "home_abbr": home_team["team"]["abbreviation"],
"home_score": home_team.get("score", "0"), "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"), "home_logo_path": os.path.join(self.logo_dir, f"{home_team['team']['abbreviation']}.png"),
"away_abbr": away_team["team"]["abbreviation"], "away_abbr": away_team["team"]["abbreviation"],
"away_score": away_team.get("score", "0"), "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"), "away_logo_path": os.path.join(self.logo_dir, f"{away_team['team']['abbreviation']}.png"),
"game_time": game_time, "game_time": game_time,
"game_date": game_date "game_date": game_date
@@ -581,6 +586,30 @@ class BaseNBAManager:
if 'odds' in game and game['odds']: if 'odds' in game and game['odds']:
self._draw_dynamic_odds(draw, game['odds'], self.display_width, self.display_height) 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 # Display the image
self.display_manager.image.paste(main_img, (0, 0)) self.display_manager.image.paste(main_img, (0, 0))
self.display_manager.update_display() self.display_manager.update_display()

View File

@@ -26,6 +26,7 @@ class BaseNCAABaseballManager:
self.display_manager = display_manager self.display_manager = display_manager
self.ncaa_baseball_config = config.get('ncaa_baseball_scoreboard', {}) self.ncaa_baseball_config = config.get('ncaa_baseball_scoreboard', {})
self.show_odds = self.ncaa_baseball_config.get('show_odds', False) 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.favorite_teams = self.ncaa_baseball_config.get('favorite_teams', [])
self.cache_manager = CacheManager() self.cache_manager = CacheManager()
self.odds_manager = OddsManager(self.cache_manager, self.config) 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 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) 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 # Draw betting odds if available and enabled
if self.show_odds and 'odds' in game_data: if self.show_odds and 'odds' in game_data:
odds_details = game_data['odds'].get('details', 'N/A') odds_details = game_data['odds'].get('details', 'N/A')
@@ -393,6 +417,8 @@ class BaseNCAABaseballManager:
home_abbr = home_team['team'].get('abbreviation', 'N/A') home_abbr = home_team['team'].get('abbreviation', 'N/A')
away_abbr = away_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) 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, 'home_team': home_abbr,
'away_score': away_team.get('score', '0'), 'away_score': away_team.get('score', '0'),
'home_score': home_team.get('score', '0'), 'home_score': home_team.get('score', '0'),
'away_record': away_record,
'home_record': home_record,
'status': status, 'status': status,
'status_state': status_state, 'status_state': status_state,
'inning': inning, 'inning': inning,

View File

@@ -97,6 +97,7 @@ class BaseNCAAFBManager: # Renamed class
self.test_mode = self.ncaa_fb_config.get("test_mode", False) 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.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.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.last_update = 0
self.current_game = None self.current_game = None
self.fonts = self._load_fonts() self.fonts = self._load_fonts()
@@ -424,6 +425,8 @@ class BaseNCAAFBManager: # Renamed class
home_abbr = home_team["team"]["abbreviation"] home_abbr = home_team["team"]["abbreviation"]
away_abbr = away_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 # 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): 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 "is_halftime": status["type"]["state"] == "halftime", # Added halftime check
"home_abbr": home_abbr, "home_abbr": home_abbr,
"home_score": home_team.get("score", "0"), "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_logo_path": os.path.join(self.logo_dir, f"{home_abbr}.png"),
"home_timeouts": home_timeouts, "home_timeouts": home_timeouts,
"away_abbr": away_abbr, "away_abbr": away_abbr,
"away_score": away_team.get("score", "0"), "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_logo_path": os.path.join(self.logo_dir, f"{away_abbr}.png"),
"away_timeouts": away_timeouts, "away_timeouts": away_timeouts,
"game_time": game_time, "game_time": game_time,
@@ -1019,6 +1024,30 @@ class NCAAFBRecentManager(BaseNCAAFBManager): # Renamed class
if 'odds' in game and game['odds']: if 'odds' in game and game['odds']:
self._draw_dynamic_odds(draw_overlay, game['odds'], self.display_width, self.display_height) 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 # Composite and display
main_img = Image.alpha_composite(main_img, overlay) main_img = Image.alpha_composite(main_img, overlay)
main_img = main_img.convert('RGB') main_img = main_img.convert('RGB')
@@ -1213,6 +1242,30 @@ class NCAAFBUpcomingManager(BaseNCAAFBManager): # Renamed class
if 'odds' in game and game['odds']: if 'odds' in game and game['odds']:
self._draw_dynamic_odds(draw_overlay, game['odds'], self.display_width, self.display_height) 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 # Composite and display
main_img = Image.alpha_composite(main_img, overlay) main_img = Image.alpha_composite(main_img, overlay)
main_img = main_img.convert('RGB') main_img = main_img.convert('RGB')

View File

@@ -44,8 +44,9 @@ class BaseNCAAMBasketballManager:
self.is_enabled = self.ncaam_basketball_config.get("enabled", False) self.is_enabled = self.ncaam_basketball_config.get("enabled", False)
self.show_odds = self.ncaam_basketball_config.get("show_odds", False) self.show_odds = self.ncaam_basketball_config.get("show_odds", False)
self.test_mode = self.ncaam_basketball_config.get("test_mode", 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.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", 300) 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.last_update = 0
self.current_game = None self.current_game = None
self.fonts = self._load_fonts() self.fonts = self._load_fonts()
@@ -421,6 +422,8 @@ class BaseNCAAMBasketballManager:
home_team = next(c for c in competitors if c.get("homeAway") == "home") 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") 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 # Format game time and date for display
game_time = "" game_time = ""
@@ -450,9 +453,11 @@ class BaseNCAAMBasketballManager:
"is_within_window": is_within_window, "is_within_window": is_within_window,
"home_abbr": home_team["team"]["abbreviation"], "home_abbr": home_team["team"]["abbreviation"],
"home_score": home_team.get("score", "0"), "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"), "home_logo_path": os.path.join(self.logo_dir, f"{home_team['team']['abbreviation']}.png"),
"away_abbr": away_team["team"]["abbreviation"], "away_abbr": away_team["team"]["abbreviation"],
"away_score": away_team.get("score", "0"), "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"), "away_logo_path": os.path.join(self.logo_dir, f"{away_team['team']['abbreviation']}.png"),
"game_time": game_time, "game_time": game_time,
"game_date": game_date, "game_date": game_date,
@@ -589,23 +594,32 @@ class BaseNCAAMBasketballManager:
self._draw_text_with_outline(draw, clock, (clock_x, clock_y), self.fonts['time']) self._draw_text_with_outline(draw, clock, (clock_x, clock_y), self.fonts['time'])
# Display odds if available # Display odds if available
if 'odds' in game: if 'odds' in game and game['odds']:
odds = game['odds'] self._draw_dynamic_odds(draw, game['odds'], self.display_width, self.display_height)
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 # Draw records if enabled
if odds.get('spread', {}).get('team') == game['home_abbr']: if self.show_records:
text_color = (255, 100, 100) # Reddish try:
spread_x = self.display_width - draw.textlength(spread_text, font=self.fonts['status']) - 2 record_font = ImageFont.truetype("assets/fonts/4x6-font.ttf", 6)
else: except IOError:
text_color = (100, 255, 100) # Greenish record_font = ImageFont.load_default()
spread_x = 2
spread_y = self.display_height - 8 away_record = game.get('away_record', '')
self._draw_text_with_outline(draw, spread_text, (spread_x, spread_y), self.fonts['status'], fill=text_color) 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 # Display the image
self.display_manager.image.paste(main_img, (0, 0)) self.display_manager.image.paste(main_img, (0, 0))

View File

@@ -97,6 +97,7 @@ class BaseNFLManager: # Renamed class
self.test_mode = self.nfl_config.get("test_mode", False) 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.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.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.last_update = 0
self.current_game = None self.current_game = None
self.fonts = self._load_fonts() self.fonts = self._load_fonts()
@@ -424,6 +425,8 @@ class BaseNFLManager: # Renamed class
home_abbr = home_team["team"]["abbreviation"] home_abbr = home_team["team"]["abbreviation"]
away_abbr = away_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 # 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): 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 "is_halftime": status["type"]["state"] == "halftime", # Added halftime check
"home_abbr": home_abbr, "home_abbr": home_abbr,
"home_score": home_team.get("score", "0"), "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_logo_path": os.path.join(self.logo_dir, f"{home_abbr}.png"),
"home_timeouts": home_timeouts, "home_timeouts": home_timeouts,
"away_abbr": away_abbr, "away_abbr": away_abbr,
"away_score": away_team.get("score", "0"), "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_logo_path": os.path.join(self.logo_dir, f"{away_abbr}.png"),
"away_timeouts": away_timeouts, "away_timeouts": away_timeouts,
"game_time": game_time, "game_time": game_time,
@@ -1008,6 +1013,30 @@ class NFLRecentManager(BaseNFLManager): # Renamed class
if 'odds' in game and game['odds']: if 'odds' in game and game['odds']:
self._draw_dynamic_odds(draw_overlay, game['odds'], self.display_width, self.display_height) 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 # Composite and display
main_img = Image.alpha_composite(main_img, overlay) main_img = Image.alpha_composite(main_img, overlay)
main_img = main_img.convert('RGB') main_img = main_img.convert('RGB')
@@ -1205,6 +1234,30 @@ class NFLUpcomingManager(BaseNFLManager): # Renamed class
if 'odds' in game and game['odds']: if 'odds' in game and game['odds']:
self._draw_dynamic_odds(draw_overlay, game['odds'], self.display_width, self.display_height) 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 # Composite and display
main_img = Image.alpha_composite(main_img, overlay) main_img = Image.alpha_composite(main_img, overlay)
main_img = main_img.convert('RGB') main_img = main_img.convert('RGB')

View File

@@ -95,6 +95,7 @@ class BaseNHLManager:
self.test_mode = self.nhl_config.get("test_mode", False) # Use test_mode from config 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.logo_dir = self.nhl_config.get("logo_dir", "assets/sports/nhl_logos")
self.update_interval = self.nhl_config.get("update_interval_seconds", 60) 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.last_update = 0
self.current_game = None self.current_game = None
self.fonts = self._load_fonts() self.fonts = self._load_fonts()
@@ -364,6 +365,8 @@ class BaseNHLManager:
home_team = next(c for c in competitors if c.get("homeAway") == "home") 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") 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 # Format game time and date for display
game_time = "" game_time = ""
@@ -400,9 +403,11 @@ class BaseNHLManager:
"is_within_window": is_within_window, "is_within_window": is_within_window,
"home_abbr": home_team["team"]["abbreviation"], "home_abbr": home_team["team"]["abbreviation"],
"home_score": home_team.get("score", "0"), "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"), "home_logo_path": os.path.join(self.logo_dir, f"{home_team['team']['abbreviation']}.png"),
"away_abbr": away_team["team"]["abbreviation"], "away_abbr": away_team["team"]["abbreviation"],
"away_score": away_team.get("score", "0"), "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"), "away_logo_path": os.path.join(self.logo_dir, f"{away_team['team']['abbreviation']}.png"),
"game_time": game_time, "game_time": game_time,
"game_date": game_date, "game_date": game_date,
@@ -551,6 +556,30 @@ class BaseNHLManager:
spread_y = self.display_height - 8 spread_y = self.display_height - 8
self._draw_text_with_outline(draw, spread_text, (spread_x, spread_y), self.fonts['status'], fill=text_color) 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 # Display the image
self.display_manager.image.paste(main_img, (0, 0)) self.display_manager.image.paste(main_img, (0, 0))
self.display_manager.update_display() self.display_manager.update_display()

View File

@@ -102,14 +102,30 @@ class OddsTickerManager:
# This is a simplified implementation; a more robust solution would cache team data # This is a simplified implementation; a more robust solution would cache team data
try: try:
sport = 'baseball' if league == 'mlb' else 'football' if league in ['nfl', 'college-football'] else 'basketball' 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 = requests.get(url, timeout=10)
response.raise_for_status() response.raise_for_status()
data = response.json() 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: 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" return "N/A"
def _get_team_logo(self, team_abbr: str, logo_dir: str) -> Optional[Image.Image]: 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'] home_abbr = home_team['team']['abbreviation']
away_abbr = away_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 # Check if this game involves favorite teams BEFORE fetching odds
if self.show_favorite_teams_only: if self.show_favorite_teams_only:
favorite_teams = league_config.get('favorite_teams', []) 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") logger.debug(f"Game {game_id} has no valid odds data, setting odds to None")
odds_data = 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 = { game_data = {
'id': game_id, 'id': game_id,
'league': league_config['league'], 'league': league_config['league'],
@@ -385,29 +401,34 @@ class OddsTickerManager:
# "vs." text # "vs." text
vs_text = "vs." 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 # Team and record text
away_team_text = f"{game.get('away_team', 'N/A')} ({game.get('away_record', '')})" 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', '')})" home_team_text = f"{game.get('home_team', 'N/A')} ({game.get('home_record', '') or 'N/A'})"
away_team_width = temp_draw.textlength(away_team_text, font=team_font) away_team_width = int(temp_draw.textlength(away_team_text, font=team_font))
home_team_width = temp_draw.textlength(home_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) team_info_width = max(away_team_width, home_team_width)
# Odds text # Odds text
odds = game.get('odds') or {} odds = game.get('odds') or {}
home_team_odds = odds.get('home_team_odds', {}) home_team_odds = odds.get('home_team_odds', {})
away_team_odds = odds.get('away_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')
over_under = odds.get('over_under')
home_favored = home_spread is not None and home_spread < 0 # Determine the favorite and get the spread
away_favored = away_spread is not None and away_spread < 0 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')
away_odds_text = "" away_odds_text = ""
home_odds_text = "" home_odds_text = ""
# Simplified odds placement logic
if home_favored: if home_favored:
home_odds_text = f"{home_spread}" home_odds_text = f"{home_spread}"
if over_under: if over_under:
@@ -416,11 +437,11 @@ class OddsTickerManager:
away_odds_text = f"{away_spread}" away_odds_text = f"{away_spread}"
if over_under: if over_under:
home_odds_text = f"O/U {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}" home_odds_text = f"O/U {over_under}"
away_odds_width = temp_draw.textlength(away_odds_text, font=odds_font) away_odds_width = int(temp_draw.textlength(away_odds_text, font=odds_font))
home_odds_width = temp_draw.textlength(home_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) odds_width = max(away_odds_width, home_odds_width)
# --- Calculate total width --- # --- Calculate total width ---
@@ -463,8 +484,12 @@ class OddsTickerManager:
odds_font_height = odds_font.size if hasattr(odds_font, 'size') else 6 odds_font_height = odds_font.size if hasattr(odds_font, 'size') else 6
odds_y_away = 2 odds_y_away = 2
odds_y_home = height - odds_font_height - 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 return image

View File

@@ -115,6 +115,7 @@ class BaseSoccerManager:
self.test_mode = self.soccer_config.get("test_mode", False) 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.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.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.last_update = 0
self.current_game = None self.current_game = None
self.fonts = self._load_fonts() self.fonts = self._load_fonts()
@@ -552,6 +553,8 @@ class BaseSoccerManager:
home_team = next(c for c in competitors if c.get("homeAway") == "home") 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") 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_time = ""
game_date = "" game_date = ""
@@ -589,9 +592,11 @@ class BaseSoccerManager:
"is_within_window": is_within_window, "is_within_window": is_within_window,
"home_abbr": home_team["team"]["abbreviation"], "home_abbr": home_team["team"]["abbreviation"],
"home_score": home_team.get("score", "0"), "home_score": home_team.get("score", "0"),
"home_record": home_record,
"home_logo": self._load_and_resize_logo(home_team["team"]["abbreviation"]), "home_logo": self._load_and_resize_logo(home_team["team"]["abbreviation"]),
"away_abbr": away_team["team"]["abbreviation"], "away_abbr": away_team["team"]["abbreviation"],
"away_score": away_team.get("score", "0"), "away_score": away_team.get("score", "0"),
"away_record": away_record,
"away_logo": self._load_and_resize_logo(away_team["team"]["abbreviation"]), "away_logo": self._load_and_resize_logo(away_team["team"]["abbreviation"]),
"game_time": game_time, # Formatted local time (e.g., 2:30pm) "game_time": game_time, # Formatted local time (e.g., 2:30pm)
"game_date": game_date, # Formatted local date (e.g., 7/21) "game_date": game_date, # Formatted local date (e.g., 7/21)
@@ -732,6 +737,30 @@ class BaseSoccerManager:
spread_y = self.display_height - 8 spread_y = self.display_height - 8
self._draw_text_with_outline(draw, spread_text, (spread_x, spread_y), self.fonts['status'], fill=text_color) 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 --- # --- Display Image ---
self.display_manager.image.paste(main_img, (0, 0)) self.display_manager.image.paste(main_img, (0, 0))
self.display_manager.update_display() self.display_manager.update_display()