visual improvements - separator between match up info, MILB team record logic to hide if no record found. Customization settings to see more information in the odds ticker manager

This commit is contained in:
Chuck
2025-07-21 15:07:36 -05:00
parent 7464244dfa
commit d3ab27b221
4 changed files with 87 additions and 35 deletions

View File

@@ -361,12 +361,22 @@ The odds ticker displays betting odds for upcoming sports games. To configure it
- **`enabled`**: Enable/disable the odds ticker (default: false)
- **`show_favorite_teams_only`**: Show only games involving favorite teams (default: false)
- **`games_per_favorite_team`**: Number of upcoming games to show per favorite team, per league (default: 1)
- **`max_games_per_league`**: Maximum number of games to show per league (default: 5)
- **`show_odds_only`**: If true, only show games that have odds available (default: false)
- **`sort_order`**: How to sort games in the ticker. Options: `soonest` (default; by start time). (More options can be added in the future.)
- **`enabled_leagues`**: Array of leagues to display (options: "nfl", "nba", "mlb", "ncaa_fb")
- **`update_interval`**: How often to fetch new odds data in seconds (default: 3600)
- **`scroll_speed`**: Pixels to scroll per update (default: 2)
- **`scroll_speed`**: Pixels to scroll per update (default: 1)
- **`scroll_delay`**: Delay between scroll updates in seconds (default: 0.05)
- **`display_duration`**: How long to show each game in seconds (default: 30)
**How it works:**
- If `show_favorite_teams_only` is true, the ticker will show the next `games_per_favorite_team` games for each favorite team in each enabled league, deduplicated and capped at `max_games_per_league` per league.
- If `show_favorite_teams_only` is false, the ticker will show all games for all teams (up to `max_games_per_league` per league).
- If `show_odds_only` is true, only games with odds will be shown.
- Games are sorted by `sort_order` (default: soonest).
### Display Format
The odds ticker shows information in this format:

View File

@@ -110,10 +110,15 @@
"odds_ticker": {
"enabled": true,
"show_favorite_teams_only": true,
"enabled_leagues": ["mlb", "nfl", "ncaa_fb"],
"games_per_favorite_team": 1,
"max_games_per_league": 5,
"show_odds_only": false,
"sort_order": "soonest",
"enabled_leagues": ["nfl","mlb", "ncaa_fb"],
"update_interval": 3600,
"scroll_speed": 1,
"scroll_delay": 0.01
"scroll_delay": 0.05,
"display_duration": 30
},
"calendar": {
"enabled": true,

View File

@@ -367,12 +367,20 @@ class BaseMiLBManager:
self.logger.debug(f"Could not find team abbreviation for '{away_team_name}'. Using '{away_abbr}'.")
# Get team records
away_record = event['teams']['away'].get('record', {}).get('wins', 0)
away_losses = event['teams']['away'].get('record', {}).get('losses', 0)
home_record = event['teams']['home'].get('record', {}).get('wins', 0)
home_losses = event['teams']['home'].get('record', {}).get('losses', 0)
away_record_str = f"{away_record}-{away_losses}"
home_record_str = f"{home_record}-{home_losses}"
away_record_data = event['teams']['away'].get('record', {})
home_record_data = event['teams']['home'].get('record', {})
away_record = away_record_data.get('wins')
away_losses = away_record_data.get('losses')
home_record = home_record_data.get('wins')
home_losses = home_record_data.get('losses')
if away_record is not None and away_losses is not None and (away_record != 0 or away_losses != 0):
away_record_str = f"{away_record}-{away_losses}"
else:
away_record_str = ''
if home_record is not None and home_losses is not None and (home_record != 0 or home_losses != 0):
home_record_str = f"{home_record}-{home_losses}"
else:
home_record_str = ''
is_favorite_game = (home_abbr in self.favorite_teams or away_abbr in self.favorite_teams)
@@ -399,8 +407,8 @@ class BaseMiLBManager:
'status': mapped_status,
'status_state': mapped_status_state,
'start_time': event['gameDate'],
'away_record': f"{event['teams']['away'].get('record', {}).get('wins', 0)}-{event['teams']['away'].get('record', {}).get('losses', 0)}",
'home_record': f"{event['teams']['home'].get('record', {}).get('wins', 0)}-{event['teams']['home'].get('record', {}).get('losses', 0)}"
'away_record': away_record_str,
'home_record': home_record_str
}
if status_state == 'Live':

View File

@@ -24,6 +24,10 @@ class OddsTickerManager:
self.odds_ticker_config = config.get('odds_ticker', {})
self.is_enabled = self.odds_ticker_config.get('enabled', False)
self.show_favorite_teams_only = self.odds_ticker_config.get('show_favorite_teams_only', False)
self.games_per_favorite_team = self.odds_ticker_config.get('games_per_favorite_team', 1)
self.max_games_per_league = self.odds_ticker_config.get('max_games_per_league', 5)
self.show_odds_only = self.odds_ticker_config.get('show_odds_only', False)
self.sort_order = self.odds_ticker_config.get('sort_order', 'soonest')
self.enabled_leagues = self.odds_ticker_config.get('enabled_leagues', ['nfl', 'nba', 'mlb'])
self.update_interval = self.odds_ticker_config.get('update_interval', 3600)
self.scroll_speed = self.odds_ticker_config.get('scroll_speed', 2)
@@ -144,7 +148,7 @@ class OddsTickerManager:
return None
def _fetch_upcoming_games(self) -> List[Dict[str, Any]]:
"""Fetch upcoming games with odds for all enabled leagues."""
"""Fetch upcoming games with odds for all enabled leagues, with user-defined granularity."""
games_data = []
now = datetime.now(timezone.utc)
@@ -163,31 +167,50 @@ class OddsTickerManager:
continue
try:
# Fetch upcoming games for this league
games = self._fetch_league_games(league_config, now)
logger.debug(f"Found {len(games)} games for {league_key}")
games_data.extend(games)
# Fetch all upcoming games for this league
all_games = self._fetch_league_games(league_config, now)
logger.debug(f"Found {len(all_games)} games for {league_key}")
league_games = []
if self.show_favorite_teams_only:
# For each favorite team, find their next N games
favorite_teams = league_config.get('favorite_teams', [])
seen_game_ids = set()
for team in favorite_teams:
# Find games where this team is home or away
team_games = [g for g in all_games if (g['home_team'] == team or g['away_team'] == team)]
# Sort by start_time
team_games.sort(key=lambda x: x.get('start_time', datetime.max))
# Only keep games with odds if show_odds_only is set
if self.show_odds_only:
team_games = [g for g in team_games if g.get('odds')]
# Take the next N games for this team
for g in team_games[:self.games_per_favorite_team]:
if g['id'] not in seen_game_ids:
league_games.append(g)
seen_game_ids.add(g['id'])
# Cap at max_games_per_league
league_games = league_games[:self.max_games_per_league]
else:
# Show all games, optionally only those with odds
league_games = all_games
if self.show_odds_only:
league_games = [g for g in league_games if g.get('odds')]
# Sort by start_time
league_games.sort(key=lambda x: x.get('start_time', datetime.max))
league_games = league_games[:self.max_games_per_league]
# Sorting (default is soonest)
if self.sort_order == 'soonest':
league_games.sort(key=lambda x: x.get('start_time', datetime.max))
# (Other sort options can be added here)
games_data.extend(league_games)
except Exception as e:
logger.error(f"Error fetching games for {league_key}: {e}")
# Sort games by start time
games_data.sort(key=lambda x: x.get('start_time', datetime.max))
# Filter for favorite teams if enabled (now handled in _fetch_league_games)
# This filtering is now redundant since we filter before fetching odds
if self.show_favorite_teams_only:
logger.debug("Favorite team filtering already applied during game fetching")
logger.debug(f"Total games found: {len(games_data)}")
# Log details about found games
if games_data:
logger.debug("Games found:")
for i, game in enumerate(games_data):
odds_status = "Has odds" if game.get('odds') else "No odds"
logger.debug(f" {i+1}. {game['away_team']} @ {game['home_team']} - {odds_status}")
return games_data
def _fetch_league_games(self, league_config: Dict[str, Any], now: datetime) -> List[Dict[str, Any]]:
@@ -518,7 +541,7 @@ class OddsTickerManager:
odds_y_home = height - odds_font_height - 2
# Use a consistent color for all odds text
odds_color = (255, 255, 0) # Yellow
odds_color = (0, 255, 0) # Green
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)
@@ -557,9 +580,15 @@ class OddsTickerManager:
self.ticker_image = Image.new('RGB', (total_width, height), color=(0, 0, 0))
current_x = 0
for img in game_images:
for idx, img in enumerate(game_images):
self.ticker_image.paste(img, (current_x, 0))
current_x += img.width + gap_width
current_x += img.width
# Draw a 1px white vertical bar between games, except after the last one
if idx < len(game_images) - 1:
bar_x = current_x + gap_width // 2
for y in range(height):
self.ticker_image.putpixel((bar_x, y), (255, 255, 255))
current_x += gap_width
if self.ticker_image and self.scroll_speed > 0 and self.scroll_delay > 0:
# Duration for the ticker to scroll its full width