centered team logos in odds ticker and removed dynamic display duration

This commit is contained in:
Chuck
2025-07-21 16:42:24 -05:00
parent 49378f79cf
commit 8dccebff01
11 changed files with 57 additions and 29 deletions

View File

@@ -370,6 +370,7 @@ The odds ticker displays betting odds for upcoming sports games. To configure it
- **`scroll_speed`**: Pixels to scroll per update (default: 1) - **`scroll_speed`**: Pixels to scroll per update (default: 1)
- **`scroll_delay`**: Delay between scroll updates in seconds (default: 0.05) - **`scroll_delay`**: Delay between scroll updates in seconds (default: 0.05)
- **`display_duration`**: How long to show each game in seconds (default: 30) - **`display_duration`**: How long to show each game in seconds (default: 30)
- **`loop`**: Whether to continuously loop the scroll animation (default: true). When false, the scroll stops when it reaches the end of the content.
**How it works:** **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 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.

View File

@@ -119,7 +119,8 @@
"scroll_speed": 1, "scroll_speed": 1,
"scroll_delay": 0.01, "scroll_delay": 0.01,
"display_duration": 60, "display_duration": 60,
"future_fetch_days": 45 "future_fetch_days": 45,
"loop": true
}, },
"calendar": { "calendar": {
"enabled": true, "enabled": true,

View File

@@ -448,11 +448,6 @@ class DisplayController:
"""Get the duration for the current display mode.""" """Get the duration for the current display mode."""
mode_key = self.current_display_mode mode_key = self.current_display_mode
if mode_key == 'odds_ticker' and self.odds_ticker:
dynamic_duration = self.odds_ticker.get_dynamic_duration()
if dynamic_duration > 0:
return dynamic_duration
# Simplify weather key handling # Simplify weather key handling
if mode_key.startswith('weather_'): if mode_key.startswith('weather_'):
return self.display_durations.get(mode_key, 15) return self.display_durations.get(mode_key, 15)

View File

@@ -427,6 +427,12 @@ class BaseNBAManager:
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 '' 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 '' away_record = away_team.get('records', [{}])[0].get('summary', '') if away_team.get('records') else ''
# Don't show "0-0" records - set to blank instead
if home_record == "0-0":
home_record = ''
if away_record == "0-0":
away_record = ''
# Format game time and date for display # Format game time and date for display
game_time = "" game_time = ""

View File

@@ -418,6 +418,12 @@ class BaseNCAABaseballManager:
home_record = home_team.get('records', [{}])[0].get('summary', '') if home_team.get('records') else '' 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 '' away_record = away_team.get('records', [{}])[0].get('summary', '') if away_team.get('records') else ''
# Don't show "0-0" records - set to blank instead
if home_record == "0-0":
home_record = ''
if away_record == "0-0":
away_record = ''
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)
if is_favorite_game: if is_favorite_game:

View File

@@ -427,6 +427,12 @@ class BaseNCAAFBManager: # Renamed class
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 '' 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 '' away_record = away_team.get('records', [{}])[0].get('summary', '') if away_team.get('records') else ''
# Don't show "0-0" records - set to blank instead
if home_record == "0-0":
home_record = ''
if away_record == "0-0":
away_record = ''
# 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):

View File

@@ -424,6 +424,12 @@ class BaseNCAAMBasketballManager:
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 '' 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 '' away_record = away_team.get('records', [{}])[0].get('summary', '') if away_team.get('records') else ''
# Don't show "0-0" records - set to blank instead
if home_record == "0-0":
home_record = ''
if away_record == "0-0":
away_record = ''
# Format game time and date for display # Format game time and date for display
game_time = "" game_time = ""

View File

@@ -427,6 +427,12 @@ class BaseNFLManager: # Renamed class
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 '' 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 '' away_record = away_team.get('records', [{}])[0].get('summary', '') if away_team.get('records') else ''
# Don't show "0-0" records - set to blank instead
if home_record == "0-0":
home_record = ''
if away_record == "0-0":
away_record = ''
# 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):

View File

@@ -367,6 +367,12 @@ class BaseNHLManager:
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 '' 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 '' away_record = away_team.get('records', [{}])[0].get('summary', '') if away_team.get('records') else ''
# Don't show "0-0" records - set to blank instead
if home_record == "0-0":
home_record = ''
if away_record == "0-0":
away_record = ''
# Format game time and date for display # Format game time and date for display
game_time = "" game_time = ""

View File

@@ -34,6 +34,7 @@ class OddsTickerManager:
self.scroll_delay = self.odds_ticker_config.get('scroll_delay', 0.05) self.scroll_delay = self.odds_ticker_config.get('scroll_delay', 0.05)
self.display_duration = self.odds_ticker_config.get('display_duration', 30) self.display_duration = self.odds_ticker_config.get('display_duration', 30)
self.future_fetch_days = self.odds_ticker_config.get('future_fetch_days', 7) self.future_fetch_days = self.odds_ticker_config.get('future_fetch_days', 7)
self.loop = self.odds_ticker_config.get('loop', True)
# Initialize managers # Initialize managers
self.cache_manager = CacheManager() self.cache_manager = CacheManager()
@@ -47,7 +48,6 @@ class OddsTickerManager:
self.current_game_index = 0 self.current_game_index = 0
self.ticker_image = None # This will hold the single, wide image self.ticker_image = None # This will hold the single, wide image
self.last_display_time = 0 self.last_display_time = 0
self.dynamic_display_duration = 0
# Font setup # Font setup
self.fonts = self._load_fonts() self.fonts = self._load_fonts()
@@ -500,7 +500,7 @@ class OddsTickerManager:
# Away Logo # Away Logo
if away_logo: if away_logo:
y_pos = logo_margin y_pos = (height - logo_size) // 2 # Center the logo vertically
image.paste(away_logo, (int(current_x), y_pos), away_logo if away_logo.mode == 'RGBA' else None) image.paste(away_logo, (int(current_x), y_pos), away_logo if away_logo.mode == 'RGBA' else None)
current_x += logo_size + vs_padding current_x += logo_size + vs_padding
@@ -511,7 +511,7 @@ class OddsTickerManager:
# Home Logo # Home Logo
if home_logo: if home_logo:
y_pos = logo_margin y_pos = (height - logo_size) // 2 # Center the logo vertically
image.paste(home_logo, (int(current_x), y_pos), home_logo if home_logo.mode == 'RGBA' else None) image.paste(home_logo, (int(current_x), y_pos), home_logo if home_logo.mode == 'RGBA' else None)
current_x += logo_size + section_padding current_x += logo_size + section_padding
@@ -578,23 +578,6 @@ class OddsTickerManager:
self.ticker_image.putpixel((bar_x, y), (255, 255, 255)) self.ticker_image.putpixel((bar_x, y), (255, 255, 255))
current_x += gap_width 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
self.dynamic_display_duration = (self.ticker_image.width / self.scroll_speed) * self.scroll_delay
logger.info(f"[OddsTickerManager] Calculated dynamic display duration: {self.dynamic_display_duration:.2f} seconds for a width of {self.ticker_image.width}px, scroll_speed={self.scroll_speed}, scroll_delay={self.scroll_delay}")
else:
# Fallback to the configured duration if something is wrong
self.dynamic_display_duration = self.display_duration
logger.warning(f"[OddsTickerManager] Using fallback display duration. ticker_image exists: {self.ticker_image is not None}, scroll_speed: {self.scroll_speed}, scroll_delay: {self.scroll_delay}")
if self.ticker_image:
logger.info(f"[OddsTickerManager] Ticker image width: {self.ticker_image.width}px")
else:
logger.warning("[OddsTickerManager] No ticker image available")
def get_dynamic_duration(self) -> float:
"""Return the calculated dynamic duration for the ticker to complete one full scroll."""
return self.dynamic_display_duration
def _draw_text_with_outline(self, draw: ImageDraw.Draw, text: str, position: tuple, font: ImageFont.FreeTypeFont, def _draw_text_with_outline(self, draw: ImageDraw.Draw, text: str, position: tuple, font: ImageFont.FreeTypeFont,
fill: tuple = (255, 255, 255), outline_color: tuple = (0, 0, 0)) -> None: fill: tuple = (255, 255, 255), outline_color: tuple = (0, 0, 0)) -> None:
"""Draw text with a black outline for better readability.""" """Draw text with a black outline for better readability."""
@@ -676,9 +659,15 @@ class OddsTickerManager:
width = self.display_manager.matrix.width width = self.display_manager.matrix.width
height = self.display_manager.matrix.height height = self.display_manager.matrix.height
# Reset position when we've scrolled past the end for a continuous loop # Handle looping based on configuration
if self.scroll_position >= self.ticker_image.width: if self.loop:
self.scroll_position = 0 # Reset position when we've scrolled past the end for a continuous loop
if self.scroll_position >= self.ticker_image.width:
self.scroll_position = 0
else:
# Stop scrolling when we reach the end
if self.scroll_position >= self.ticker_image.width - width:
self.scroll_position = self.ticker_image.width - width
# Create the visible part of the image by pasting from the ticker_image # Create the visible part of the image by pasting from the ticker_image
visible_image = Image.new('RGB', (width, height)) visible_image = Image.new('RGB', (width, height))

View File

@@ -555,6 +555,12 @@ class BaseSoccerManager:
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 '' 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 '' away_record = away_team.get('records', [{}])[0].get('summary', '') if away_team.get('records') else ''
# Don't show "0-0" records - set to blank instead
if home_record == "0-0":
home_record = ''
if away_record == "0-0":
away_record = ''
game_time = "" game_time = ""
game_date = "" game_date = ""