diff --git a/config/config.json b/config/config.json index 09de464b..b0227eaa 100644 --- a/config/config.json +++ b/config/config.json @@ -1,7 +1,7 @@ { "web_display_autostart": true, "schedule": { - "enabled": false, + "enabled": true, "start_time": "07:00", "end_time": "23:00" }, @@ -74,18 +74,18 @@ "use_short_date_format": true }, "clock": { - "enabled": false, + "enabled": true, "format": "%I:%M %p", "update_interval": 1 }, "weather": { - "enabled": false, + "enabled": true, "update_interval": 1800, "units": "imperial", "display_format": "{temp}°F\n{condition}" }, "stocks": { - "enabled": false, + "enabled": true, "update_interval": 600, "symbols": [ "ASTS", "SCHD", "INTC", "NVDA", "T", "VOO", "SMCI" @@ -93,7 +93,7 @@ "display_format": "{symbol}: ${price} ({change}%)" }, "crypto": { - "enabled": false, + "enabled": true, "update_interval": 600, "symbols": [ "BTC-USD", "ETH-USD" @@ -101,7 +101,7 @@ "display_format": "{symbol}: ${price} ({change}%)" }, "stock_news": { - "enabled": false, + "enabled": true, "update_interval": 3600, "scroll_speed": 1, "scroll_delay": 0.01, @@ -285,6 +285,7 @@ "favorite_teams": ["TAM"], "logo_dir": "assets/sports/milb_logos", "show_records": true, + "upcoming_fetch_days": 7, "display_modes": { "milb_live": false, "milb_recent": true, diff --git a/src/odds_ticker_manager.py b/src/odds_ticker_manager.py index 7505cbb9..dc40138b 100644 --- a/src/odds_ticker_manager.py +++ b/src/odds_ticker_manager.py @@ -31,6 +31,8 @@ class OddsTickerManager: "ESPN3": "espn3", "ESPNU": "espnu", "ESPNEWS": "espn", + "ESPN+": "espn", + "ESPN Plus": "espn", "FOX": "fox", "FS1": "fs1", "FS2": "fs2", @@ -44,7 +46,12 @@ class OddsTickerManager: "SECN": "espn-sec-us", "TBS": "tbs", "TNT": "tnt", - "truTV": "tru" + "truTV": "tru", + "Peacock": "nbc", + "Paramount+": "cbs", + "Hulu": "espn", + "Disney+": "espn", + "Apple TV+": "nbc" } def __init__(self, config: Dict[str, Any], display_manager: DisplayManager): @@ -368,7 +375,19 @@ class OddsTickerManager: broadcasts = event.get('competitions', [{}])[0].get('broadcasts', []) if broadcasts: broadcast_info = [b.get('media', {}).get('shortName', '') for b in broadcasts if b.get('media', {}).get('shortName')] - logger.debug(f"Found broadcast channels for game {game_id}: {broadcast_info}") + logger.info(f"Found broadcast channels for game {game_id}: {broadcast_info}") + logger.debug(f"Raw broadcasts data for game {game_id}: {broadcasts}") + # Log the first broadcast structure for debugging + if broadcasts: + logger.debug(f"First broadcast structure: {broadcasts[0]}") + if 'media' in broadcasts[0]: + logger.debug(f"Media structure: {broadcasts[0]['media']}") + else: + logger.debug(f"No broadcasts data found for game {game_id}") + # Log the competitions structure to see what's available + competitions = event.get('competitions', []) + if competitions: + logger.debug(f"Competitions structure for game {game_id}: {competitions[0].keys()}") # Only process favorite teams if enabled if self.show_favorite_teams_only: @@ -527,34 +546,43 @@ class OddsTickerManager: home_logo = self._get_team_logo(game['home_team'], game['logo_dir']) away_logo = self._get_team_logo(game['away_team'], game['logo_dir']) broadcast_logo = None + + # Enhanced broadcast logo debugging if self.show_channel_logos: broadcast_names = game.get('broadcast_info', []) # This is now a list - logger.debug(f"Game {game.get('id')}: Raw broadcast info from API: {broadcast_names}") + logger.info(f"Game {game.get('id')}: Raw broadcast info from API: {broadcast_names}") + logger.info(f"Game {game.get('id')}: show_channel_logos setting: {self.show_channel_logos}") if broadcast_names: logo_name = None # Sort keys by length, descending, to match more specific names first (e.g., "ESPNEWS" before "ESPN") sorted_keys = sorted(self.BROADCAST_LOGO_MAP.keys(), key=len, reverse=True) + logger.debug(f"Game {game.get('id')}: Available broadcast logo keys: {sorted_keys}") for b_name in broadcast_names: + logger.debug(f"Game {game.get('id')}: Checking broadcast name: '{b_name}'") for key in sorted_keys: if key in b_name: logo_name = self.BROADCAST_LOGO_MAP[key] + logger.info(f"Game {game.get('id')}: Matched '{key}' to logo '{logo_name}' for broadcast '{b_name}'") break # Found the best match for this b_name if logo_name: break # Found a logo, stop searching through broadcast list - logger.debug(f"Game {game.get('id')}: Mapped logo name: '{logo_name}' from broadcast names: {broadcast_names}") + logger.info(f"Game {game.get('id')}: Final mapped logo name: '{logo_name}' from broadcast names: {broadcast_names}") if logo_name: broadcast_logo = self._get_team_logo(logo_name, 'assets/broadcast_logos') if broadcast_logo: - logger.debug(f"Game {game.get('id')}: Successfully loaded broadcast logo for '{logo_name}'") + logger.info(f"Game {game.get('id')}: Successfully loaded broadcast logo for '{logo_name}' - Size: {broadcast_logo.size}") else: logger.warning(f"Game {game.get('id')}: Failed to load broadcast logo for '{logo_name}'") + # Check if the file exists + logo_path = os.path.join('assets', 'broadcast_logos', f"{logo_name}.png") + logger.warning(f"Game {game.get('id')}: Logo file exists: {os.path.exists(logo_path)}") else: logger.warning(f"Game {game.get('id')}: No mapping found for broadcast names {broadcast_names} in BROADCAST_LOGO_MAP") else: - logger.debug(f"Game {game.get('id')}: No broadcast info available.") + logger.info(f"Game {game.get('id')}: No broadcast info available.") if home_logo: home_logo = home_logo.resize((logo_size, logo_size), Image.Resampling.LANCZOS) @@ -569,7 +597,8 @@ class OddsTickerManager: b_logo_w = int(broadcast_logo.width * ratio) broadcast_logo = broadcast_logo.resize((b_logo_w, b_logo_h), Image.Resampling.LANCZOS) broadcast_logo_col_width = b_logo_w - + logger.info(f"Game {game.get('id')}: Resized broadcast logo to {broadcast_logo.size}, column width: {broadcast_logo_col_width}") + # Format date and time into 3 parts game_time = game['start_time'] timezone_str = self.config.get('timezone', 'UTC') @@ -659,7 +688,9 @@ class OddsTickerManager: # Add width for the broadcast logo if it exists if broadcast_logo: - total_width += broadcast_logo_col_width + total_width += broadcast_logo_col_width + h_padding # Add padding after broadcast logo + + logger.info(f"Game {game.get('id')}: Total width calculation - logo_size: {logo_size}, vs_width: {vs_width}, team_info_width: {team_info_width}, odds_width: {odds_width}, datetime_col_width: {datetime_col_width}, broadcast_logo_col_width: {broadcast_logo_col_width}, total_width: {total_width}") # --- Create final image --- image = Image.new('RGB', (int(total_width), height), color=(0, 0, 0)) @@ -726,8 +757,12 @@ class OddsTickerManager: if broadcast_logo: # Position the broadcast logo in its own column logo_y = (height - broadcast_logo.height) // 2 - logger.debug(f"Pasting broadcast logo at ({int(current_x)}, {logo_y})") + logger.info(f"Game {game.get('id')}: Pasting broadcast logo at ({int(current_x)}, {logo_y})") + logger.info(f"Game {game.get('id')}: Broadcast logo size: {broadcast_logo.size}, image total width: {image.width}") image.paste(broadcast_logo, (int(current_x), logo_y), broadcast_logo if broadcast_logo.mode == 'RGBA' else None) + logger.info(f"Game {game.get('id')}: Successfully pasted broadcast logo") + else: + logger.info(f"Game {game.get('id')}: No broadcast logo to paste") return image diff --git a/test_broadcast_logos.py b/test_broadcast_logos.py new file mode 100644 index 00000000..5e041fbd --- /dev/null +++ b/test_broadcast_logos.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 +""" +Test script to debug broadcast logo display in odds ticker +""" + +import os +import sys +import logging +from PIL import Image + +# Add the src directory to the path +sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src')) + +from odds_ticker_manager import OddsTickerManager +from display_manager import DisplayManager +from config_manager import ConfigManager + +# Set up logging +logging.basicConfig(level=logging.DEBUG) +logger = logging.getLogger(__name__) + +def test_broadcast_logo_loading(): + """Test broadcast logo loading functionality""" + + # Load config + config_manager = ConfigManager() + config = config_manager.get_config() + + # Create a mock display manager + class MockDisplayManager: + def __init__(self): + self.matrix = type('Matrix', (), {'width': 64, 'height': 32})() + self.image = None + self.draw = None + + def update_display(self): + pass + + display_manager = MockDisplayManager() + + # Create odds ticker manager + odds_ticker = OddsTickerManager(config, display_manager) + + # Test broadcast logo mapping + print("Testing broadcast logo mapping...") + test_broadcast_names = [ + ["ESPN"], + ["FOX"], + ["CBS"], + ["NBC"], + ["ESPN2"], + ["FS1"], + ["ESPNEWS"], + ["ABC"], + ["TBS"], + ["TNT"], + ["Unknown Channel"], + [] + ] + + for broadcast_names in test_broadcast_names: + print(f"\nTesting broadcast names: {broadcast_names}") + + # Simulate the logo mapping logic + logo_name = None + sorted_keys = sorted(odds_ticker.BROADCAST_LOGO_MAP.keys(), key=len, reverse=True) + + for b_name in broadcast_names: + for key in sorted_keys: + if key in b_name: + logo_name = odds_ticker.BROADCAST_LOGO_MAP[key] + break + if logo_name: + break + + print(f"Mapped logo name: '{logo_name}'") + + if logo_name: + # Test loading the actual logo + logo_path = os.path.join('assets', 'broadcast_logos', f"{logo_name}.png") + print(f"Logo path: {logo_path}") + print(f"File exists: {os.path.exists(logo_path)}") + + if os.path.exists(logo_path): + try: + logo = Image.open(logo_path) + print(f"Successfully loaded logo: {logo.size} pixels") + except Exception as e: + print(f"Error loading logo: {e}") + else: + print("Logo file not found!") + +def test_game_with_broadcast_info(): + """Test creating a game display with broadcast info""" + + # Load config + config_manager = ConfigManager() + config = config_manager.get_config() + + # Create a mock display manager + class MockDisplayManager: + def __init__(self): + self.matrix = type('Matrix', (), {'width': 64, 'height': 32})() + self.image = None + self.draw = None + + def update_display(self): + pass + + display_manager = MockDisplayManager() + + # Create odds ticker manager + odds_ticker = OddsTickerManager(config, display_manager) + + # Create a test game with broadcast info + test_game = { + 'id': 'test_game_1', + 'home_team': 'TB', + 'away_team': 'BOS', + 'home_team_name': 'Tampa Bay Rays', + 'away_team_name': 'Boston Red Sox', + 'start_time': '2024-01-15T19:00:00Z', + 'home_record': '95-67', + 'away_record': '78-84', + 'broadcast_info': ['ESPN'], + 'logo_dir': 'assets/sports/mlb_logos' + } + + print(f"\nTesting game display with broadcast info: {test_game['broadcast_info']}") + + try: + # Create the game display + game_image = odds_ticker._create_game_display(test_game) + print(f"Successfully created game image: {game_image.size} pixels") + + # Save the image for inspection + output_path = 'test_broadcast_logo_output.png' + game_image.save(output_path) + print(f"Saved test image to: {output_path}") + + except Exception as e: + print(f"Error creating game display: {e}") + import traceback + traceback.print_exc() + +if __name__ == "__main__": + print("=== Testing Broadcast Logo Functionality ===\n") + + # Test 1: Logo loading + test_broadcast_logo_loading() + + # Test 2: Game display with broadcast info + test_game_with_broadcast_info() + + print("\n=== Test Complete ===") \ No newline at end of file diff --git a/test_broadcast_logos_rpi.py b/test_broadcast_logos_rpi.py new file mode 100644 index 00000000..9fb4e33d --- /dev/null +++ b/test_broadcast_logos_rpi.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python3 +""" +Diagnostic script for broadcast logo display on Raspberry Pi +Run this on the Pi to test broadcast logo functionality +""" + +import os +import sys +import logging +from PIL import Image + +# Add the src directory to the path +sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src')) + +from odds_ticker_manager import OddsTickerManager +from display_manager import DisplayManager +from config_manager import ConfigManager + +# Set up logging to see what's happening +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger(__name__) + +def test_broadcast_logo_files(): + """Test if broadcast logo files exist and can be loaded""" + print("=== Testing Broadcast Logo Files ===") + + broadcast_logos_dir = "assets/broadcast_logos" + if not os.path.exists(broadcast_logos_dir): + print(f"ERROR: Broadcast logos directory not found: {broadcast_logos_dir}") + return False + + print(f"Found broadcast logos directory: {broadcast_logos_dir}") + + # Test a few key logos + test_logos = ["espn", "fox", "cbs", "nbc", "tbs", "tnt"] + + for logo_name in test_logos: + logo_path = os.path.join(broadcast_logos_dir, f"{logo_name}.png") + if os.path.exists(logo_path): + try: + logo = Image.open(logo_path) + print(f"✓ {logo_name}.png - Size: {logo.size}") + except Exception as e: + print(f"✗ {logo_name}.png - Error loading: {e}") + else: + print(f"✗ {logo_name}.png - File not found") + + return True + +def test_broadcast_logo_mapping(): + """Test the broadcast logo mapping logic""" + print("\n=== Testing Broadcast Logo Mapping ===") + + # Load config + config_manager = ConfigManager() + config = config_manager.get_config() + + # Create a mock display manager + class MockDisplayManager: + def __init__(self): + self.matrix = type('Matrix', (), {'width': 64, 'height': 32})() + self.image = None + self.draw = None + + def update_display(self): + pass + + display_manager = MockDisplayManager() + + # Create odds ticker manager + odds_ticker = OddsTickerManager(config, display_manager) + + # Test various broadcast names that might appear in the API + test_cases = [ + ["ESPN"], + ["FOX"], + ["CBS"], + ["NBC"], + ["ESPN2"], + ["FS1"], + ["ESPNEWS"], + ["ESPN+"], + ["ESPN Plus"], + ["Peacock"], + ["Paramount+"], + ["ABC"], + ["TBS"], + ["TNT"], + ["Unknown Channel"], + [] + ] + + for broadcast_names in test_cases: + print(f"\nTesting broadcast names: {broadcast_names}") + + # Simulate the logo mapping logic + logo_name = None + sorted_keys = sorted(odds_ticker.BROADCAST_LOGO_MAP.keys(), key=len, reverse=True) + + for b_name in broadcast_names: + for key in sorted_keys: + if key in b_name: + logo_name = odds_ticker.BROADCAST_LOGO_MAP[key] + break + if logo_name: + break + + print(f" Mapped logo name: '{logo_name}'") + + if logo_name: + # Test loading the actual logo + logo_path = os.path.join('assets', 'broadcast_logos', f"{logo_name}.png") + print(f" Logo path: {logo_path}") + print(f" File exists: {os.path.exists(logo_path)}") + + if os.path.exists(logo_path): + try: + logo = Image.open(logo_path) + print(f" ✓ Successfully loaded logo: {logo.size} pixels") + except Exception as e: + print(f" ✗ Error loading logo: {e}") + else: + print(" ✗ Logo file not found!") + +def test_game_display_with_broadcast(): + """Test creating a game display with broadcast info""" + print("\n=== Testing Game Display with Broadcast Info ===") + + # Load config + config_manager = ConfigManager() + config = config_manager.get_config() + + # Create a mock display manager + class MockDisplayManager: + def __init__(self): + self.matrix = type('Matrix', (), {'width': 64, 'height': 32})() + self.image = None + self.draw = None + + def update_display(self): + pass + + display_manager = MockDisplayManager() + + # Create odds ticker manager + odds_ticker = OddsTickerManager(config, display_manager) + + # Test cases with different broadcast info + test_games = [ + { + 'id': 'test_game_1', + 'home_team': 'TB', + 'away_team': 'BOS', + 'home_team_name': 'Tampa Bay Rays', + 'away_team_name': 'Boston Red Sox', + 'start_time': '2024-01-15T19:00:00Z', + 'home_record': '95-67', + 'away_record': '78-84', + 'broadcast_info': ['ESPN'], + 'logo_dir': 'assets/sports/mlb_logos' + }, + { + 'id': 'test_game_2', + 'home_team': 'NY', + 'away_team': 'LA', + 'home_team_name': 'New York Yankees', + 'away_team_name': 'Los Angeles Dodgers', + 'start_time': '2024-01-15T20:00:00Z', + 'home_record': '82-80', + 'away_record': '100-62', + 'broadcast_info': ['FOX'], + 'logo_dir': 'assets/sports/mlb_logos' + }, + { + 'id': 'test_game_3', + 'home_team': 'CHI', + 'away_team': 'MIA', + 'home_team_name': 'Chicago Cubs', + 'away_team_name': 'Miami Marlins', + 'start_time': '2024-01-15T21:00:00Z', + 'home_record': '83-79', + 'away_record': '84-78', + 'broadcast_info': [], # No broadcast info + 'logo_dir': 'assets/sports/mlb_logos' + } + ] + + for i, test_game in enumerate(test_games): + print(f"\n--- Test Game {i+1}: {test_game['away_team']} @ {test_game['home_team']} ---") + print(f"Broadcast info: {test_game['broadcast_info']}") + + try: + # Create the game display + game_image = odds_ticker._create_game_display(test_game) + print(f"✓ Successfully created game image: {game_image.size} pixels") + + # Save the image for inspection + output_path = f'test_broadcast_logo_output_{i+1}.png' + game_image.save(output_path) + print(f"✓ Saved test image to: {output_path}") + + except Exception as e: + print(f"✗ Error creating game display: {e}") + import traceback + traceback.print_exc() + +if __name__ == "__main__": + print("=== Broadcast Logo Diagnostic Script ===\n") + + # Test 1: Check if broadcast logo files exist + test_broadcast_logo_files() + + # Test 2: Test broadcast logo mapping + test_broadcast_logo_mapping() + + # Test 3: Test game display with broadcast info + test_game_display_with_broadcast() + + print("\n=== Diagnostic Complete ===") + print("Check the generated PNG files to see if broadcast logos are being included.") \ No newline at end of file