mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-04-10 21:03:01 +00:00
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:
12
README.md
12
README.md
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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':
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user