added portuguese soccer league to documentation for soccer manager and added auto-download missing logos for soccer teams

This commit is contained in:
Chuck
2025-09-16 14:25:47 -04:00
parent 4b1b343a8f
commit 854c236a60
6 changed files with 124 additions and 37 deletions

View File

@@ -59,7 +59,7 @@ The system supports live, recent, and upcoming game information for multiple spo
- NCAA Football
- NCAA Men's Basketball
- NCAA Men's Baseball
- Soccer
- Soccer (Premier League, La Liga, Bundesliga, Serie A, Ligue 1, Liga Portugal, Champions League, Europa League, MLS)
- (Note, some of these sports seasons were not active during development and might need fine tuning when games are active)

View File

@@ -755,6 +755,20 @@ MLB Conferences/Divisions
OAK => Oakland Athletics
SEA => Seattle Mariners
TEX => Texas Rangers
Soccer Leagues:
LEAGUE_SLUGS = {
"eng.1": "Premier League",
"esp.1": "La Liga",
"ger.1": "Bundesliga",
"ita.1": "Serie A",
"fra.1": "Ligue 1",
"uefa.champions": "Champions League",
"uefa.europa": "Europa League",
"usa.1": "MLS",
"por.1": "Liga Portugal", # Add this line
}
Soccer - Premier League (England)
ARS => Arsenal
AVL => Aston Villa
@@ -886,6 +900,24 @@ Soccer - Champions League
VFB => VfB Stuttgart
VIL => Villarreal
Soccer - Liga Portugal (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
VGU => Vitória de Guimarães
VSC => Vitória de Setúbal
Soccer - Other Teams
austin => Austin FC
cf_montral => CF Montréal

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@@ -31,7 +31,17 @@ class LogoDownloader:
'ncaa_fb_all': 'https://site.api.espn.com/apis/site/v2/sports/football/college-football/teams', # Includes FCS
'fcs': 'https://site.api.espn.com/apis/site/v2/sports/football/college-football/teams', # FCS teams from same endpoint
'ncaam_basketball': 'https://site.api.espn.com/apis/site/v2/sports/basketball/mens-college-basketball/teams',
'ncaa_baseball': 'https://site.api.espn.com/apis/site/v2/sports/baseball/college-baseball/teams'
'ncaa_baseball': 'https://site.api.espn.com/apis/site/v2/sports/baseball/college-baseball/teams',
# Soccer leagues
'soccer_eng.1': 'https://site.api.espn.com/apis/site/v2/sports/soccer/eng.1/teams',
'soccer_esp.1': 'https://site.api.espn.com/apis/site/v2/sports/soccer/esp.1/teams',
'soccer_ger.1': 'https://site.api.espn.com/apis/site/v2/sports/soccer/ger.1/teams',
'soccer_ita.1': 'https://site.api.espn.com/apis/site/v2/sports/soccer/ita.1/teams',
'soccer_fra.1': 'https://site.api.espn.com/apis/site/v2/sports/soccer/fra.1/teams',
'soccer_por.1': 'https://site.api.espn.com/apis/site/v2/sports/soccer/por.1/teams',
'soccer_uefa.champions': 'https://site.api.espn.com/apis/site/v2/sports/soccer/uefa.champions/teams',
'soccer_uefa.europa': 'https://site.api.espn.com/apis/site/v2/sports/soccer/uefa.europa/teams',
'soccer_usa.1': 'https://site.api.espn.com/apis/site/v2/sports/soccer/usa.1/teams'
}
# Directory mappings for different leagues
@@ -44,7 +54,17 @@ class LogoDownloader:
'ncaa_fb_all': 'assets/sports/ncaa_fbs_logos', # FCS teams go in same directory
'fcs': 'assets/sports/ncaa_fbs_logos', # FCS teams go in same directory
'ncaam_basketball': 'assets/sports/ncaa_fbs_logos',
'ncaa_baseball': 'assets/sports/ncaa_fbs_logos'
'ncaa_baseball': 'assets/sports/ncaa_fbs_logos',
# Soccer leagues - all use the same soccer_logos directory
'soccer_eng.1': 'assets/sports/soccer_logos',
'soccer_esp.1': 'assets/sports/soccer_logos',
'soccer_ger.1': 'assets/sports/soccer_logos',
'soccer_ita.1': 'assets/sports/soccer_logos',
'soccer_fra.1': 'assets/sports/soccer_logos',
'soccer_por.1': 'assets/sports/soccer_logos',
'soccer_uefa.champions': 'assets/sports/soccer_logos',
'soccer_uefa.europa': 'assets/sports/soccer_logos',
'soccer_usa.1': 'assets/sports/soccer_logos'
}
def __init__(self, request_timeout: int = 30, retry_attempts: int = 3):
@@ -605,6 +625,20 @@ class LogoDownloader:
return converted_count, failed_count
# Helper function to map soccer league codes to logo downloader format
def get_soccer_league_key(league_code: str) -> str:
"""
Map soccer league codes to logo downloader format.
Args:
league_code: Soccer league code (e.g., 'eng.1', 'por.1')
Returns:
Logo downloader league key (e.g., 'soccer_eng.1', 'soccer_por.1')
"""
return f"soccer_{league_code}"
# Convenience function for easy integration
def download_missing_logo(team_abbreviation: str, league: str, team_name: str = None, create_placeholder: bool = True) -> bool:
"""

View File

@@ -12,6 +12,7 @@ from src.display_manager import DisplayManager
from src.cache_manager import CacheManager
from src.config_manager import ConfigManager
from src.odds_manager import OddsManager
from src.logo_downloader import download_missing_logo, get_soccer_league_key
import pytz
# Import the API counter function from web interface
@@ -32,6 +33,7 @@ LEAGUE_SLUGS = {
"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",
@@ -408,42 +410,61 @@ class BaseSoccerManager:
try:
if not os.path.exists(logo_path) and not (cache_logo_path and os.path.exists(cache_logo_path)):
self.logger.info(f"Creating placeholder logo for {team_abbrev}")
# Try to create placeholder in cache directory instead of assets directory
cache_logo_path = None
try:
# Use cache directory for placeholder logos
if hasattr(self.cache_manager, 'cache_dir') and self.cache_manager.cache_dir:
cache_logo_dir = os.path.join(self.cache_manager.cache_dir, 'placeholder_logos')
os.makedirs(cache_logo_dir, exist_ok=True)
cache_logo_path = os.path.join(cache_logo_dir, f"{team_abbrev}.png")
self.logger.info(f"Logo not found for {team_abbrev} at {logo_path}. Attempting to download from ESPN.")
# Try to download the logo from ESPN API for each configured league
download_success = False
for league_code in self.target_leagues_config:
if league_code in LEAGUE_SLUGS:
soccer_league_key = get_soccer_league_key(league_code)
self.logger.debug(f"Attempting to download {team_abbrev} logo from {league_code} ({soccer_league_key})")
# Create placeholder logo
success = download_missing_logo(team_abbrev, soccer_league_key, team_abbrev)
if success:
self.logger.info(f"Successfully downloaded logo for {team_abbrev} from {league_code}")
download_success = True
break
else:
self.logger.debug(f"Failed to download {team_abbrev} logo from {league_code}")
if not download_success:
self.logger.warning(f"Failed to download logo for {team_abbrev} from any configured league. Creating placeholder.")
# Try to create placeholder in cache directory instead of assets directory
cache_logo_path = None
try:
# Use cache directory for placeholder logos
if hasattr(self.cache_manager, 'cache_dir') and self.cache_manager.cache_dir:
cache_logo_dir = os.path.join(self.cache_manager.cache_dir, 'placeholder_logos')
os.makedirs(cache_logo_dir, exist_ok=True)
cache_logo_path = os.path.join(cache_logo_dir, f"{team_abbrev}.png")
# Create placeholder logo
logo = Image.new('RGBA', (36, 36), (random.randint(50, 200), random.randint(50, 200), random.randint(50, 200), 255))
draw = ImageDraw.Draw(logo)
# Optionally add text to placeholder
try:
script_dir = os.path.dirname(os.path.abspath(__file__))
font_4x6 = os.path.abspath(os.path.join(script_dir, "../assets/fonts/4x6-font.ttf"))
placeholder_font = ImageFont.truetype(font_4x6, 12)
text_width = draw.textlength(team_abbrev, font=placeholder_font)
text_x = (36 - text_width) // 2
text_y = 10
draw.text((text_x, text_y), team_abbrev, fill=(0,0,0,255), font=placeholder_font)
except IOError:
pass # Font not found, skip text
logo.save(cache_logo_path)
self.logger.info(f"Created placeholder logo in cache at {cache_logo_path}")
# Update logo_path to use cache version
logo_path = cache_logo_path
else:
# No cache directory available, just use in-memory placeholder
raise PermissionError("No writable cache directory available")
except (PermissionError, OSError) as pe:
self.logger.debug(f"Could not create placeholder logo file for {team_abbrev}: {pe}")
# Return a simple in-memory placeholder instead
logo = Image.new('RGBA', (36, 36), (random.randint(50, 200), random.randint(50, 200), random.randint(50, 200), 255))
draw = ImageDraw.Draw(logo)
# Optionally add text to placeholder
try:
font_4x6 = os.path.abspath(os.path.join(script_dir, "../assets/fonts/4x6-font.ttf"))
placeholder_font = ImageFont.truetype(font_4x6, 12)
text_width = draw.textlength(team_abbrev, font=placeholder_font)
text_x = (36 - text_width) // 2
text_y = 10
draw.text((text_x, text_y), team_abbrev, fill=(0,0,0,255), font=placeholder_font)
except IOError:
pass # Font not found, skip text
logo.save(cache_logo_path)
self.logger.info(f"Created placeholder logo in cache at {cache_logo_path}")
# Update logo_path to use cache version
logo_path = cache_logo_path
else:
# No cache directory available, just use in-memory placeholder
raise PermissionError("No writable cache directory available")
except (PermissionError, OSError) as pe:
self.logger.debug(f"Could not create placeholder logo file for {team_abbrev}: {pe}")
# Return a simple in-memory placeholder instead
logo = Image.new('RGBA', (36, 36), (random.randint(50, 200), random.randint(50, 200), random.randint(50, 200), 255))
self._logo_cache[team_abbrev] = logo
return logo
self._logo_cache[team_abbrev] = logo
return logo
# Try to load logo from original path or cache directory
logo_to_load = None