odds ticker channel logos and ensure all sports work with odds ticker manager

This commit is contained in:
Chuck
2025-07-22 12:12:42 -05:00
parent acc3e7f249
commit 9ce74800dc

View File

@@ -18,6 +18,24 @@ logger = logging.getLogger(__name__)
class OddsTickerManager: class OddsTickerManager:
"""Manager for displaying scrolling odds ticker for multiple sports leagues.""" """Manager for displaying scrolling odds ticker for multiple sports leagues."""
BROADCAST_LOGO_MAP = {
"ESPN": "espn",
"ESPN2": "espn2",
"ESPNU": "espnu",
"ESPNEWS": "espn",
"SECN": "secn",
"ACC Network": "accn",
"FOX": "fox",
"FS1": "fs1",
"FS2": "fs2",
"BTN": "btn",
"CBS": "cbs",
"CBSSN": "cbssn",
"NBC": "nbc",
"NFLN": "nfln",
"ABC": "abc"
}
def __init__(self, config: Dict[str, Any], display_manager: DisplayManager): def __init__(self, config: Dict[str, Any], display_manager: DisplayManager):
self.config = config self.config = config
self.display_manager = display_manager self.display_manager = display_manager
@@ -89,6 +107,34 @@ class OddsTickerManager:
'logo_dir': 'assets/sports/milb_logos', 'logo_dir': 'assets/sports/milb_logos',
'favorite_teams': config.get('milb', {}).get('favorite_teams', []), 'favorite_teams': config.get('milb', {}).get('favorite_teams', []),
'enabled': config.get('milb', {}).get('enabled', False) 'enabled': config.get('milb', {}).get('enabled', False)
},
'nhl': {
'sport': 'hockey',
'league': 'nhl',
'logo_dir': 'assets/sports/nhl_logos',
'favorite_teams': config.get('nhl_scoreboard', {}).get('favorite_teams', []),
'enabled': config.get('nhl_scoreboard', {}).get('enabled', False)
},
'ncaam_basketball': {
'sport': 'basketball',
'league': 'mens-college-basketball',
'logo_dir': 'assets/sports/ncaa_fbs_logos',
'favorite_teams': config.get('ncaam_basketball_scoreboard', {}).get('favorite_teams', []),
'enabled': config.get('ncaam_basketball_scoreboard', {}).get('enabled', False)
},
'ncaa_baseball': {
'sport': 'baseball',
'league': 'college-baseball',
'logo_dir': 'assets/sports/ncaa_fbs_logos',
'favorite_teams': config.get('ncaa_baseball_scoreboard', {}).get('favorite_teams', []),
'enabled': config.get('ncaa_baseball_scoreboard', {}).get('enabled', False)
},
'soccer': {
'sport': 'soccer',
'leagues': config.get('soccer_scoreboard', {}).get('leagues', []),
'logo_dir': 'assets/sports/soccer_logos',
'favorite_teams': config.get('soccer_scoreboard', {}).get('favorite_teams', []),
'enabled': config.get('soccer_scoreboard', {}).get('enabled', False)
} }
} }
@@ -236,16 +282,23 @@ class OddsTickerManager:
games_found = 0 games_found = 0
max_games_per_league = self.max_games_per_league if not self.show_favorite_teams_only else None max_games_per_league = self.max_games_per_league if not self.show_favorite_teams_only else None
sport = league_config['sport']
leagues_to_fetch = []
if sport == 'soccer':
leagues_to_fetch.extend(league_config.get('leagues', []))
else:
if league_config.get('league'):
leagues_to_fetch.append(league_config.get('league'))
for league in leagues_to_fetch:
for date in dates: for date in dates:
# Stop if we have enough games for favorite teams # Stop if we have enough games for favorite teams
if self.show_favorite_teams_only and all(team_games_found[t] >= max_games for t in favorite_teams): if self.show_favorite_teams_only and favorite_teams and all(team_games_found.get(t, 0) >= max_games for t in favorite_teams):
break # All favorite teams have enough games, stop searching break # All favorite teams have enough games, stop searching
# Stop if we have enough games for the league (when not showing favorite teams only) # Stop if we have enough games for the league (when not showing favorite teams only)
if not self.show_favorite_teams_only and max_games_per_league and games_found >= max_games_per_league: if not self.show_favorite_teams_only and max_games_per_league and games_found >= max_games_per_league:
break # We have enough games for this league, stop searching break # We have enough games for this league, stop searching
try: try:
sport = league_config['sport']
league = league_config['league']
url = f"https://site.api.espn.com/apis/site/v2/sports/{sport}/{league}/scoreboard?dates={date}" url = f"https://site.api.espn.com/apis/site/v2/sports/{sport}/{league}/scoreboard?dates={date}"
logger.debug(f"Fetching {league} games from ESPN API for date: {date}") logger.debug(f"Fetching {league} games from ESPN API for date: {date}")
response = requests.get(url, timeout=10) response = requests.get(url, timeout=10)
@@ -324,6 +377,8 @@ class OddsTickerManager:
break break
except Exception as e: except Exception as e:
logger.error(f"Error fetching games for {league_config.get('league', 'unknown')} on {date}: {e}", exc_info=True) logger.error(f"Error fetching games for {league_config.get('league', 'unknown')} on {date}: {e}", exc_info=True)
if not self.show_favorite_teams_only and max_games_per_league and games_found >= max_games_per_league:
break
return all_games return all_games
def _format_odds_text(self, game: Dict[str, Any]) -> str: def _format_odds_text(self, game: Dict[str, Any]) -> str:
@@ -420,17 +475,26 @@ class OddsTickerManager:
away_logo = self._get_team_logo(game['away_team'], game['logo_dir']) away_logo = self._get_team_logo(game['away_team'], game['logo_dir'])
broadcast_logo = None broadcast_logo = None
if self.show_channel_logos: if self.show_channel_logos:
broadcast_logo = self._get_team_logo(game.get('broadcast_info', ''), 'assets/broadcast_logos') broadcast_name = game.get('broadcast_info', '')
logo_name = self.BROADCAST_LOGO_MAP.get(broadcast_name, broadcast_name.lower())
broadcast_logo = self._get_team_logo(logo_name, 'assets/broadcast_logos')
if home_logo: if home_logo:
home_logo = home_logo.resize((logo_size, logo_size), Image.Resampling.LANCZOS) home_logo = home_logo.resize((logo_size, logo_size), Image.Resampling.LANCZOS)
if away_logo: if away_logo:
away_logo = away_logo.resize((logo_size, logo_size), Image.Resampling.LANCZOS) away_logo = away_logo.resize((logo_size, logo_size), Image.Resampling.LANCZOS)
if broadcast_logo:
broadcast_logo = broadcast_logo.resize((logo_size, logo_size), Image.Resampling.LANCZOS)
# Create a temporary draw object to measure text # Datetime column width
temp_draw = ImageDraw.Draw(Image.new('RGB', (1, 1))) day_width = int(temp_draw.textlength(day_text, font=datetime_font))
date_width = int(temp_draw.textlength(date_text, font=datetime_font))
time_width = int(temp_draw.textlength(time_text, font=datetime_font))
datetime_col_width = max(day_width, date_width, time_width)
if broadcast_logo:
# Resize broadcast logo to fit the datetime column width
ratio = datetime_col_width / broadcast_logo.width
new_height = int(broadcast_logo.height * ratio)
broadcast_logo = broadcast_logo.resize((datetime_col_width, new_height), Image.Resampling.LANCZOS)
# "vs." text # "vs." text
vs_text = "vs." vs_text = "vs."
@@ -461,15 +525,6 @@ class OddsTickerManager:
home_team_width = int(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)
# Datetime column width
day_width = int(temp_draw.textlength(day_text, font=datetime_font))
date_width = int(temp_draw.textlength(date_text, font=datetime_font))
time_width = int(temp_draw.textlength(time_text, font=datetime_font))
datetime_col_width = max(day_width, date_width, time_width)
if broadcast_logo:
datetime_col_width = max(datetime_col_width, broadcast_logo.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', {})
@@ -562,10 +617,14 @@ class OddsTickerManager:
# Datetime (stacked, 3 rows) # Datetime (stacked, 3 rows)
datetime_font_height = datetime_font.size if hasattr(datetime_font, 'size') else 6 datetime_font_height = datetime_font.size if hasattr(datetime_font, 'size') else 6
total_dt_height = 3 * datetime_font_height + 4 # Padding between lines
dt_padding_y = (height - total_dt_height) // 2
day_y = dt_padding_y # Calculate available height for the three text lines
total_text_height = (3 * datetime_font_height) + 4 # 2px padding between lines
# Center the block of text vertically
dt_start_y = (height - total_text_height) // 2
day_y = dt_start_y
date_y = day_y + datetime_font_height + 2 date_y = day_y + datetime_font_height + 2
time_y = date_y + datetime_font_height + 2 time_y = date_y + datetime_font_height + 2
@@ -574,8 +633,10 @@ class OddsTickerManager:
draw.text((current_x, time_y), time_text, font=datetime_font, fill=(255, 255, 255)) draw.text((current_x, time_y), time_text, font=datetime_font, fill=(255, 255, 255))
if broadcast_logo: if broadcast_logo:
broadcast_y = time_y + datetime_font_height + 2 # Position the broadcast logo below the time text
image.paste(broadcast_logo, (int(current_x), broadcast_y), broadcast_logo if broadcast_logo.mode == 'RGBA' else None) logo_y = time_y + datetime_font_height + 2
image.paste(broadcast_logo, (int(current_x), logo_y), broadcast_logo if broadcast_logo.mode == 'RGBA' else None)
return image return image