diff --git a/assets/sports/soccer_logos/AJX.png b/assets/sports/soccer_logos/AJX.png new file mode 100644 index 00000000..d0982e82 Binary files /dev/null and b/assets/sports/soccer_logos/AJX.png differ diff --git a/assets/sports/soccer_logos/ARO.png b/assets/sports/soccer_logos/ARO.png new file mode 100644 index 00000000..dfb41fb2 Binary files /dev/null and b/assets/sports/soccer_logos/ARO.png differ diff --git a/assets/sports/soccer_logos/AUS.png b/assets/sports/soccer_logos/AUS.png new file mode 100644 index 00000000..fe5910a9 Binary files /dev/null and b/assets/sports/soccer_logos/AUS.png differ diff --git a/assets/sports/soccer_logos/BAY.png b/assets/sports/soccer_logos/BAY.png new file mode 100644 index 00000000..ac3805e7 Binary files /dev/null and b/assets/sports/soccer_logos/BAY.png differ diff --git a/assets/sports/soccer_logos/BRA.png b/assets/sports/soccer_logos/BRA.png new file mode 100644 index 00000000..1dd00bb9 Binary files /dev/null and b/assets/sports/soccer_logos/BRA.png differ diff --git a/assets/sports/soccer_logos/BUR.png b/assets/sports/soccer_logos/BUR.png new file mode 100644 index 00000000..c6a30821 Binary files /dev/null and b/assets/sports/soccer_logos/BUR.png differ diff --git a/assets/sports/soccer_logos/CHA.png b/assets/sports/soccer_logos/CHA.png new file mode 100644 index 00000000..3688da6f Binary files /dev/null and b/assets/sports/soccer_logos/CHA.png differ diff --git a/assets/sports/soccer_logos/DOR.png b/assets/sports/soccer_logos/DOR.png new file mode 100644 index 00000000..f5333df3 Binary files /dev/null and b/assets/sports/soccer_logos/DOR.png differ diff --git a/assets/sports/soccer_logos/EST.png b/assets/sports/soccer_logos/EST.png new file mode 100644 index 00000000..73266852 Binary files /dev/null and b/assets/sports/soccer_logos/EST.png differ diff --git a/assets/sports/soccer_logos/FAM.png b/assets/sports/soccer_logos/FAM.png new file mode 100644 index 00000000..9473610d Binary files /dev/null and b/assets/sports/soccer_logos/FAM.png differ diff --git a/assets/sports/soccer_logos/GIL.png b/assets/sports/soccer_logos/GIL.png new file mode 100644 index 00000000..3a51f4b7 Binary files /dev/null and b/assets/sports/soccer_logos/GIL.png differ diff --git a/assets/sports/soccer_logos/KOL.png b/assets/sports/soccer_logos/KOL.png new file mode 100644 index 00000000..29b7c001 Binary files /dev/null and b/assets/sports/soccer_logos/KOL.png differ diff --git a/assets/sports/soccer_logos/LEV.png b/assets/sports/soccer_logos/LEV.png new file mode 100644 index 00000000..082a37c2 Binary files /dev/null and b/assets/sports/soccer_logos/LEV.png differ diff --git a/assets/sports/soccer_logos/LUT.png b/assets/sports/soccer_logos/LUT.png new file mode 100644 index 00000000..6a7c088a Binary files /dev/null and b/assets/sports/soccer_logos/LUT.png differ diff --git a/assets/sports/soccer_logos/LYON.png b/assets/sports/soccer_logos/LYON.png new file mode 100644 index 00000000..7d76c4fa Binary files /dev/null and b/assets/sports/soccer_logos/LYON.png differ diff --git a/assets/sports/soccer_logos/MAR.png b/assets/sports/soccer_logos/MAR.png new file mode 100644 index 00000000..e5341a62 Binary files /dev/null and b/assets/sports/soccer_logos/MAR.png differ diff --git a/assets/sports/soccer_logos/MOR.png b/assets/sports/soccer_logos/MOR.png new file mode 100644 index 00000000..e4d4e605 Binary files /dev/null and b/assets/sports/soccer_logos/MOR.png differ diff --git a/assets/sports/soccer_logos/MTL.png b/assets/sports/soccer_logos/MTL.png new file mode 100644 index 00000000..82be626c Binary files /dev/null and b/assets/sports/soccer_logos/MTL.png differ diff --git a/assets/sports/soccer_logos/NICE.png b/assets/sports/soccer_logos/NICE.png new file mode 100644 index 00000000..6fc6ab98 Binary files /dev/null and b/assets/sports/soccer_logos/NICE.png differ diff --git a/assets/sports/soccer_logos/NSC.png b/assets/sports/soccer_logos/NSC.png new file mode 100644 index 00000000..39615fc2 Binary files /dev/null and b/assets/sports/soccer_logos/NSC.png differ diff --git a/assets/sports/soccer_logos/NYC.png b/assets/sports/soccer_logos/NYC.png new file mode 100644 index 00000000..b950e986 Binary files /dev/null and b/assets/sports/soccer_logos/NYC.png differ diff --git a/assets/sports/soccer_logos/NYR.png b/assets/sports/soccer_logos/NYR.png new file mode 100644 index 00000000..0e54f376 Binary files /dev/null and b/assets/sports/soccer_logos/NYR.png differ diff --git a/assets/sports/soccer_logos/PSG.png b/assets/sports/soccer_logos/PSG.png new file mode 100644 index 00000000..115a38b3 Binary files /dev/null and b/assets/sports/soccer_logos/PSG.png differ diff --git a/assets/sports/soccer_logos/PTM.png b/assets/sports/soccer_logos/PTM.png new file mode 100644 index 00000000..590adaab Binary files /dev/null and b/assets/sports/soccer_logos/PTM.png differ diff --git a/assets/sports/soccer_logos/RIO.png b/assets/sports/soccer_logos/RIO.png new file mode 100644 index 00000000..42ef4019 Binary files /dev/null and b/assets/sports/soccer_logos/RIO.png differ diff --git a/assets/sports/soccer_logos/SHU.png b/assets/sports/soccer_logos/SHU.png new file mode 100644 index 00000000..591ae33c Binary files /dev/null and b/assets/sports/soccer_logos/SHU.png differ diff --git a/assets/sports/soccer_logos/STU.png b/assets/sports/soccer_logos/STU.png new file mode 100644 index 00000000..0ac971a3 Binary files /dev/null and b/assets/sports/soccer_logos/STU.png differ diff --git a/assets/sports/soccer_logos/VGU.png b/assets/sports/soccer_logos/VGU.png new file mode 100644 index 00000000..522dcc19 Binary files /dev/null and b/assets/sports/soccer_logos/VGU.png differ diff --git a/assets/sports/soccer_logos/VSC.png b/assets/sports/soccer_logos/VSC.png new file mode 100644 index 00000000..0f462b40 Binary files /dev/null and b/assets/sports/soccer_logos/VSC.png differ diff --git a/test/README_soccer_logos.md b/test/README_soccer_logos.md new file mode 100644 index 00000000..9d5614ba --- /dev/null +++ b/test/README_soccer_logos.md @@ -0,0 +1,96 @@ +# Soccer Logo Checker and Downloader + +## Overview + +The `check_soccer_logos.py` script automatically checks for missing logos of major teams from supported soccer leagues and downloads them from ESPN API if missing. + +## Supported Leagues + +- **Premier League** (eng.1) - 20 teams +- **La Liga** (esp.1) - 15 teams +- **Bundesliga** (ger.1) - 15 teams +- **Serie A** (ita.1) - 14 teams +- **Ligue 1** (fra.1) - 12 teams +- **Liga Portugal** (por.1) - 15 teams +- **Champions League** (uefa.champions) - 13 major teams +- **Europa League** (uefa.europa) - 11 major teams +- **MLS** (usa.1) - 25 teams + +**Total: 140 major teams across 9 leagues** + +## Usage + +```bash +cd test +python check_soccer_logos.py +``` + +## What It Does + +1. **Checks Existing Logos**: Scans `assets/sports/soccer_logos/` for existing logo files +2. **Identifies Missing Logos**: Compares against the list of major teams +3. **Downloads from ESPN**: Automatically fetches missing logos from ESPN API +4. **Creates Placeholders**: If download fails, creates colored placeholder logos +5. **Provides Summary**: Shows detailed statistics of the process + +## Output + +The script provides detailed logging showing: +- ✅ Existing logos found +- ⬇️ Successfully downloaded logos +- ❌ Failed downloads (with placeholders created) +- 📊 Summary statistics + +## Example Output + +``` +🔍 Checking por.1 (Liga Portugal) +📊 Found 2 existing logos, 13 missing +✅ Existing: BEN, POR +❌ Missing: ARO (Arouca), BRA (SC Braga), CHA (Chaves), ... + +Downloading ARO (Arouca) from por.1 +✅ Successfully downloaded ARO (Arouca) +... + +📈 SUMMARY +✅ Existing logos: 25 +⬇️ Downloaded: 115 +❌ Failed downloads: 0 +📊 Total teams checked: 140 +``` + +## Logo Storage + +All logos are stored in: `assets/sports/soccer_logos/` + +Format: `{TEAM_ABBREVIATION}.png` (e.g., `BEN.png`, `POR.png`, `LIV.png`) + +## Integration with LEDMatrix + +These logos are automatically used by the soccer manager when displaying: +- Live games +- Recent games +- Upcoming games +- Odds ticker +- Leaderboards + +The system will automatically download missing logos on-demand during normal operation, but this script ensures all major teams have logos available upfront. + +## Notes + +- **Real Logos**: Downloaded from ESPN's official API +- **Placeholders**: Created for teams not found in ESPN data +- **Caching**: Logos are cached locally to avoid repeated downloads +- **Format**: All logos converted to RGBA PNG format for LEDMatrix compatibility +- **Size**: Logos are optimized for LED matrix display (typically 36x36 pixels) + +## Troubleshooting + +If downloads fail: +1. Check internet connectivity +2. Verify ESPN API is accessible +3. Some teams may not be in current league rosters +4. Placeholder logos will be created as fallback + +The script is designed to be robust and will always provide some form of logo for every team. diff --git a/test/check_soccer_logos.py b/test/check_soccer_logos.py new file mode 100644 index 00000000..f6506829 --- /dev/null +++ b/test/check_soccer_logos.py @@ -0,0 +1,315 @@ +#!/usr/bin/env python3 +""" +Soccer Logo Checker and Downloader + +This script checks for missing logos of major teams from supported soccer leagues +and downloads them from ESPN API if missing. + +Supported Leagues: +- Premier League (eng.1) +- La Liga (esp.1) +- Bundesliga (ger.1) +- Serie A (ita.1) +- Ligue 1 (fra.1) +- Liga Portugal (por.1) +- Champions League (uefa.champions) +- Europa League (uefa.europa) +- MLS (usa.1) +""" + +import os +import sys +import logging +from pathlib import Path +from typing import Dict, List, Tuple + +# Add src directory to path for imports +sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'src')) + +from logo_downloader import download_missing_logo, get_soccer_league_key, LogoDownloader + +# Configure logging +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s', + datefmt='%Y-%m-%d %H:%M:%S' +) +logger = logging.getLogger(__name__) + +# Major teams for each league (with their ESPN abbreviations) +MAJOR_TEAMS = { + 'eng.1': { # Premier League + 'ARS': 'Arsenal', + 'AVL': 'Aston Villa', + 'BHA': 'Brighton & Hove Albion', + 'BOU': 'AFC Bournemouth', + 'BRE': 'Brentford', + 'BUR': 'Burnley', + 'CHE': 'Chelsea', + 'CRY': 'Crystal Palace', + 'EVE': 'Everton', + 'FUL': 'Fulham', + 'LIV': 'Liverpool', + 'LUT': 'Luton Town', + 'MCI': 'Manchester City', + 'MUN': 'Manchester United', + 'NEW': 'Newcastle United', + 'NFO': 'Nottingham Forest', + 'SHU': 'Sheffield United', + 'TOT': 'Tottenham Hotspur', + 'WHU': 'West Ham United', + 'WOL': 'Wolverhampton Wanderers' + }, + 'esp.1': { # La Liga + 'ALA': 'Alavés', + 'ATH': 'Athletic Bilbao', + 'ATM': 'Atlético Madrid', + 'BAR': 'Barcelona', + 'BET': 'Real Betis', + 'CEL': 'Celta Vigo', + 'ESP': 'Espanyol', + 'GET': 'Getafe', + 'GIR': 'Girona', + 'LEG': 'Leganés', + 'RAY': 'Rayo Vallecano', + 'RMA': 'Real Madrid', + 'SEV': 'Sevilla', + 'VAL': 'Valencia', + 'VLD': 'Valladolid' + }, + 'ger.1': { # Bundesliga + 'BOC': 'VfL Bochum', + 'DOR': 'Borussia Dortmund', + 'FCA': 'FC Augsburg', + 'FCB': 'Bayern Munich', + 'FCU': 'FC Union Berlin', + 'KOL': '1. FC Köln', + 'LEV': 'Bayer Leverkusen', + 'M05': 'Mainz 05', + 'RBL': 'RB Leipzig', + 'SCF': 'SC Freiburg', + 'SGE': 'Eintracht Frankfurt', + 'STU': 'VfB Stuttgart', + 'SVW': 'Werder Bremen', + 'TSG': 'TSG Hoffenheim', + 'WOB': 'VfL Wolfsburg' + }, + 'ita.1': { # Serie A + 'ATA': 'Atalanta', + 'CAG': 'Cagliari', + 'EMP': 'Empoli', + 'FIO': 'Fiorentina', + 'INT': 'Inter Milan', + 'JUV': 'Juventus', + 'LAZ': 'Lazio', + 'MIL': 'AC Milan', + 'MON': 'Monza', + 'NAP': 'Napoli', + 'ROM': 'Roma', + 'TOR': 'Torino', + 'UDI': 'Udinese', + 'VER': 'Hellas Verona' + }, + 'fra.1': { # Ligue 1 + 'LIL': 'Lille', + 'LYON': 'Lyon', + 'MAR': 'Marseille', + 'MON': 'Monaco', + 'NAN': 'Nantes', + 'NICE': 'Nice', + 'OL': 'Olympique Lyonnais', + 'OM': 'Olympique de Marseille', + 'PAR': 'Paris Saint-Germain', + 'PSG': 'Paris Saint-Germain', + 'REN': 'Rennes', + 'STR': 'Strasbourg' + }, + 'por.1': { # Liga Portugal + 'ARO': 'Arouca', + 'BEN': 'SL Benfica', + 'BRA': 'SC Braga', + 'CHA': 'Chaves', + 'EST': 'Estoril Praia', + 'FAM': 'Famalicão', + 'GIL': 'Gil Vicente', + 'MOR': 'Moreirense', + 'POR': 'FC Porto', + 'PTM': 'Portimonense', + 'RIO': 'Rio Ave', + 'SR': 'Sporting CP', + 'SCP': 'Sporting CP', # Alternative abbreviation + 'VGU': 'Vitória de Guimarães', + 'VSC': 'Vitória de Setúbal' + }, + 'uefa.champions': { # Champions League (major teams) + 'AJX': 'Ajax', + 'ATM': 'Atlético Madrid', + 'BAR': 'Barcelona', + 'BAY': 'Bayern Munich', + 'CHE': 'Chelsea', + 'INT': 'Inter Milan', + 'JUV': 'Juventus', + 'LIV': 'Liverpool', + 'MCI': 'Manchester City', + 'MUN': 'Manchester United', + 'PSG': 'Paris Saint-Germain', + 'RMA': 'Real Madrid', + 'TOT': 'Tottenham Hotspur' + }, + 'uefa.europa': { # Europa League (major teams) + 'ARS': 'Arsenal', + 'ATM': 'Atlético Madrid', + 'BAR': 'Barcelona', + 'CHE': 'Chelsea', + 'INT': 'Inter Milan', + 'JUV': 'Juventus', + 'LIV': 'Liverpool', + 'MUN': 'Manchester United', + 'NAP': 'Napoli', + 'ROM': 'Roma', + 'SEV': 'Sevilla' + }, + 'usa.1': { # MLS + 'ATL': 'Atlanta United', + 'AUS': 'Austin FC', + 'CHI': 'Chicago Fire', + 'CIN': 'FC Cincinnati', + 'CLB': 'Columbus Crew', + 'DAL': 'FC Dallas', + 'DC': 'D.C. United', + 'HOU': 'Houston Dynamo', + 'LA': 'LA Galaxy', + 'LAFC': 'Los Angeles FC', + 'MIA': 'Inter Miami', + 'MIN': 'Minnesota United', + 'MTL': 'CF Montréal', + 'NSC': 'Nashville SC', + 'NYC': 'New York City FC', + 'NYR': 'New York Red Bulls', + 'ORL': 'Orlando City', + 'PHI': 'Philadelphia Union', + 'POR': 'Portland Timbers', + 'RSL': 'Real Salt Lake', + 'SEA': 'Seattle Sounders', + 'SJ': 'San Jose Earthquakes', + 'SKC': 'Sporting Kansas City', + 'TOR': 'Toronto FC', + 'VAN': 'Vancouver Whitecaps' + } +} + +def check_logo_exists(team_abbr: str, logo_dir: str) -> bool: + """Check if a logo file exists for the given team abbreviation.""" + logo_path = os.path.join(logo_dir, f"{team_abbr}.png") + return os.path.exists(logo_path) + +def download_team_logo(team_abbr: str, team_name: str, league_code: str) -> bool: + """Download a team logo from ESPN API.""" + try: + soccer_league_key = get_soccer_league_key(league_code) + logger.info(f"Downloading {team_abbr} ({team_name}) from {league_code}") + + success = download_missing_logo(team_abbr, soccer_league_key, team_name) + if success: + logger.info(f"✅ Successfully downloaded {team_abbr} ({team_name})") + return True + else: + logger.warning(f"❌ Failed to download {team_abbr} ({team_name})") + return False + except Exception as e: + logger.error(f"❌ Error downloading {team_abbr} ({team_name}): {e}") + return False + +def check_league_logos(league_code: str, teams: Dict[str, str], logo_dir: str) -> Tuple[int, int]: + """Check and download missing logos for a specific league.""" + logger.info(f"\n🔍 Checking {league_code} ({LEAGUE_NAMES.get(league_code, league_code)})") + + missing_logos = [] + existing_logos = [] + + # Check which logos are missing + for team_abbr, team_name in teams.items(): + if check_logo_exists(team_abbr, logo_dir): + existing_logos.append(team_abbr) + else: + missing_logos.append((team_abbr, team_name)) + + logger.info(f"📊 Found {len(existing_logos)} existing logos, {len(missing_logos)} missing") + + if existing_logos: + logger.info(f"✅ Existing: {', '.join(existing_logos)}") + + if missing_logos: + logger.info(f"❌ Missing: {', '.join([f'{abbr} ({name})' for abbr, name in missing_logos])}") + + # Download missing logos + downloaded_count = 0 + failed_count = 0 + + for team_abbr, team_name in missing_logos: + if download_team_logo(team_abbr, team_name, league_code): + downloaded_count += 1 + else: + failed_count += 1 + + return downloaded_count, failed_count + +def main(): + """Main function to check and download all soccer logos.""" + logger.info("⚽ Soccer Logo Checker and Downloader") + logger.info("=" * 50) + + # Ensure logo directory exists + logo_dir = "assets/sports/soccer_logos" + os.makedirs(logo_dir, exist_ok=True) + logger.info(f"📁 Logo directory: {logo_dir}") + + # League names for display + global LEAGUE_NAMES + LEAGUE_NAMES = { + 'eng.1': 'Premier League', + 'esp.1': 'La Liga', + 'ger.1': 'Bundesliga', + 'ita.1': 'Serie A', + 'fra.1': 'Ligue 1', + 'por.1': 'Liga Portugal', + 'uefa.champions': 'Champions League', + 'uefa.europa': 'Europa League', + 'usa.1': 'MLS' + } + + total_downloaded = 0 + total_failed = 0 + total_existing = 0 + + # Check each league + for league_code, teams in MAJOR_TEAMS.items(): + downloaded, failed = check_league_logos(league_code, teams, logo_dir) + total_downloaded += downloaded + total_failed += failed + total_existing += len(teams) - downloaded - failed + + # Summary + logger.info("\n" + "=" * 50) + logger.info("📈 SUMMARY") + logger.info("=" * 50) + logger.info(f"✅ Existing logos: {total_existing}") + logger.info(f"⬇️ Downloaded: {total_downloaded}") + logger.info(f"❌ Failed downloads: {total_failed}") + logger.info(f"📊 Total teams checked: {total_existing + total_downloaded + total_failed}") + + if total_failed > 0: + logger.warning(f"\n⚠️ {total_failed} logos failed to download. This might be due to:") + logger.warning(" - Network connectivity issues") + logger.warning(" - ESPN API rate limiting") + logger.warning(" - Team abbreviations not matching ESPN's format") + logger.warning(" - Teams not currently in the league") + + if total_downloaded > 0: + logger.info(f"\n🎉 Successfully downloaded {total_downloaded} new logos!") + logger.info(" These logos are now available for use in the LEDMatrix display.") + + logger.info(f"\n📁 All logos are stored in: {os.path.abspath(logo_dir)}") + +if __name__ == "__main__": + main()