Simplify NHL logo rendering to match weather/stock icon approach - Use direct RGBA mode for logo loading and rendering - Simplify image resizing with thumbnail method - Improve error handling and logging - Use relative positioning for better layout

This commit is contained in:
ChuckBuilds
2025-04-18 10:11:22 -05:00
parent c5e99bb9a3
commit 8850387564

View File

@@ -6,6 +6,7 @@ from PIL import Image, ImageDraw, ImageFont
import logging import logging
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from typing import List, Dict, Optional, Any from typing import List, Dict, Optional, Any
import os
try: try:
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
except ImportError: except ImportError:
@@ -1121,224 +1122,133 @@ class NHLScoreboardManager:
msg, font=font, fill='grey') msg, font=font, fill='grey')
def _draw_upcoming_layout(self, draw: ImageDraw.ImageDraw, game_details: Dict[str, Any]): def _draw_upcoming_layout(self, draw: ImageDraw.ImageDraw, game_details: Dict[str, Any]):
"""Draws the layout for an upcoming game.""" """Draws the upcoming game layout with team logos and game time."""
font_team = self.fonts.get('team', ImageFont.load_default())
font_main = self.fonts.get('upcoming_main', ImageFont.load_default())
font_vs = self.fonts.get('upcoming_vs', ImageFont.load_default())
img = draw.im
logging.debug("[NHL] Drawing upcoming game layout.")
logo_padding = 2
logo_max_h = self.display_height - (logo_padding * 2)
logo_area_width = int(self.display_width * 0.4)
logo_max_w = logo_area_width - logo_padding
logo_size = (logo_max_w, logo_max_h)
away_logo_x = logo_padding
home_logo_x = self.display_width - logo_area_width + logo_padding
# Draw Away Logo
if game_details.get("away_logo_path"):
try: try:
# Load and resize logo # Load and resize logos
away_logo = Image.open(game_details["away_logo_path"]).convert("RGBA") home_logo_path = os.path.join(self.logo_dir, f"{game_details['home_abbr']}.png")
aspect_ratio = away_logo.width / away_logo.height away_logo_path = os.path.join(self.logo_dir, f"{game_details['away_abbr']}.png")
if aspect_ratio > 1: # wider than tall
new_width = min(logo_max_w, int(logo_max_h * aspect_ratio))
new_height = int(new_width / aspect_ratio)
else: # taller than wide
new_height = min(logo_max_h, int(logo_max_w / aspect_ratio))
new_width = int(new_height * aspect_ratio)
away_logo = away_logo.resize((new_width, new_height), Image.Resampling.LANCZOS) # Load and process home logo
paste_y = (self.display_height - new_height) // 2 home_logo = None
if os.path.exists(home_logo_path):
# Convert to RGB before pasting
away_logo_rgb = away_logo.convert('RGB')
img.paste(away_logo_rgb, (away_logo_x, paste_y))
logging.debug(f"[NHL] Successfully rendered away logo: {game_details['away_logo_path']}")
except Exception as e:
logging.error(f"[NHL] Error rendering upcoming away logo {game_details['away_logo_path']}: {e}")
draw.text((away_logo_x, 5), game_details.get("away_abbr", "?"), font=font_team, fill="white")
else:
draw.text((away_logo_x, 5), game_details.get("away_abbr", "?"), font=font_team, fill="white")
# Draw Home Logo
if game_details.get("home_logo_path"):
try: try:
# Load and resize logo home_logo = Image.open(home_logo_path)
home_logo = Image.open(game_details["home_logo_path"]).convert("RGBA") if home_logo.mode != 'RGBA':
aspect_ratio = home_logo.width / home_logo.height home_logo = home_logo.convert('RGBA')
if aspect_ratio > 1: # wider than tall # Resize maintaining aspect ratio
new_width = min(logo_max_w, int(logo_max_h * aspect_ratio)) max_size = min(int(self.display_width / 3), int(self.display_height / 2))
new_height = int(new_width / aspect_ratio) home_logo.thumbnail((max_size, max_size), Image.Resampling.LANCZOS)
else: # taller than wide
new_height = min(logo_max_h, int(logo_max_w / aspect_ratio))
new_width = int(new_height * aspect_ratio)
home_logo = home_logo.resize((new_width, new_height), Image.Resampling.LANCZOS)
paste_y = (self.display_height - new_height) // 2
# Convert to RGB before pasting
home_logo_rgb = home_logo.convert('RGB')
img.paste(home_logo_rgb, (home_logo_x, paste_y))
logging.debug(f"[NHL] Successfully rendered home logo: {game_details['home_logo_path']}")
except Exception as e: except Exception as e:
logging.error(f"[NHL] Error rendering upcoming home logo {game_details['home_logo_path']}: {e}") logging.error(f"Error loading home logo {home_logo_path}: {e}")
draw.text((home_logo_x, 5), game_details.get("home_abbr", "?"), font=font_team, fill="white")
else:
draw.text((home_logo_x, 5), game_details.get("home_abbr", "?"), font=font_team, fill="white")
# Center Text Area # Load and process away logo
center_start_x = logo_area_width away_logo = None
center_end_x = self.display_width - logo_area_width if os.path.exists(away_logo_path):
center_x = (center_start_x + center_end_x) // 2 try:
away_logo = Image.open(away_logo_path)
if away_logo.mode != 'RGBA':
away_logo = away_logo.convert('RGBA')
# Resize maintaining aspect ratio
max_size = min(int(self.display_width / 3), int(self.display_height / 2))
away_logo.thumbnail((max_size, max_size), Image.Resampling.LANCZOS)
except Exception as e:
logging.error(f"Error loading away logo {away_logo_path}: {e}")
# Prepare Text # Calculate positions
start_utc = game_details.get("start_time_utc") width = self.display_width
date_str = "???" height = self.display_height
time_str = "??:??"
if start_utc:
start_local = start_utc.astimezone(self.local_timezone)
now_local = datetime.now(self.local_timezone)
today_local = now_local.date()
start_date_local = start_local.date()
if start_date_local == today_local: date_str = "TODAY"
elif start_date_local == today_local + timedelta(days=1): date_str = "TOMORROW"
else: date_str = start_local.strftime("%a %b %d").upper()
time_str = start_local.strftime("%H:%M")
vs_str = "VS"
# Calculate Positions (adjust line_height as needed) # Draw team logos
line_height_approx = font_main.getbbox("Aj")[3] - font_main.getbbox("Aj")[1] + 2 if home_logo:
vs_height = font_vs.getbbox("VS")[3] - font_vs.getbbox("VS")[1] home_x = width // 4 - home_logo.width // 2
total_text_height = (line_height_approx * 2) + vs_height home_y = height // 4 - home_logo.height // 2
start_y = (self.display_height - total_text_height) // 2 draw.im.paste(home_logo, (home_x, home_y), home_logo)
date_y = start_y if away_logo:
time_y = date_y + line_height_approx away_x = width // 4 - away_logo.width // 2
vs_y = time_y + line_height_approx away_y = 3 * height // 4 - away_logo.height // 2
draw.im.paste(away_logo, (away_x, away_y), away_logo)
# Draw Text # Draw game time
draw.text((center_x, date_y), date_str, font=font_main, fill='white', anchor="mt") game_time = game_details.get('clock', '')
draw.text((center_x, time_y), time_str, font=font_main, fill='white', anchor="mt") time_x = width // 2 - 20
draw.text((center_x, vs_y), vs_str, font=font_vs, fill='white', anchor="mt") time_y = height // 2 - 8
draw.text((time_x, time_y), game_time, font=self.fonts['time'], fill=(255, 255, 255))
except Exception as e:
logging.error(f"Error in _draw_upcoming_layout: {e}")
def _draw_scorebug_layout(self, draw: ImageDraw.ImageDraw, game_details: Dict[str, Any]): def _draw_scorebug_layout(self, draw: ImageDraw.ImageDraw, game_details: Dict[str, Any]):
"""Draws the standard score bug layout for live or final games.""" """Draw the scorebug layout with team logos, scores, and game status."""
font_score = self.fonts.get('score', ImageFont.load_default())
font_time = self.fonts.get('time', ImageFont.load_default())
font_team = self.fonts.get('team', ImageFont.load_default())
font_status = self.fonts.get('status', ImageFont.load_default())
img = draw.im
logging.debug("[NHL] Drawing live/final game layout.")
# Layout Calculations
logo_max_h = self.display_height - 4
logo_max_w = int(self.display_width * 0.25)
logo_size = (logo_max_w, logo_max_h)
away_logo_x = 2
score_width_approx = 25
away_score_x = away_logo_x + logo_max_w + 4
home_logo_x = self.display_width - logo_max_w - 2
home_score_x = home_logo_x - score_width_approx - 4
center_x = self.display_width // 2
time_y = 2
period_y = 15
# --- Draw Away Team ---
away_logo_drawn_size = (0,0)
if game_details.get("away_logo_path"):
try: try:
# Load and resize logo # Load and resize logos
away_logo = Image.open(game_details["away_logo_path"]).convert("RGBA") home_logo_path = os.path.join(self.logo_dir, f"{game_details['home_abbr']}.png")
aspect_ratio = away_logo.width / away_logo.height away_logo_path = os.path.join(self.logo_dir, f"{game_details['away_abbr']}.png")
if aspect_ratio > 1: # wider than tall
new_width = min(logo_max_w, int(logo_max_h * aspect_ratio))
new_height = int(new_width / aspect_ratio)
else: # taller than wide
new_height = min(logo_max_h, int(logo_max_w / aspect_ratio))
new_width = int(new_height * aspect_ratio)
away_logo = away_logo.resize((new_width, new_height), Image.Resampling.LANCZOS) # Load and process home logo
away_logo_drawn_size = (new_width, new_height) home_logo = None
paste_y = (self.display_height - new_height) // 2 if os.path.exists(home_logo_path):
# Convert to RGB before pasting
away_logo_rgb = away_logo.convert('RGB')
img.paste(away_logo_rgb, (away_logo_x, paste_y))
logging.debug(f"[NHL] Successfully rendered away logo: {game_details['away_logo_path']}")
except Exception as e:
logging.error(f"[NHL] Error rendering away logo {game_details['away_logo_path']}: {e}")
draw.text((away_logo_x + 2, 5), game_details.get("away_abbr", "?"), font=font_team, fill="white")
else:
draw.text((away_logo_x + 2, 5), game_details.get("away_abbr", "?"), font=font_team, fill="white")
current_away_score_x = (away_logo_x + away_logo_drawn_size[0] + 4) if away_logo_drawn_size[0] > 0 else away_score_x
draw.text((current_away_score_x, (self.display_height - 12) // 2), str(game_details.get("away_score", "0")), font=font_score, fill='white')
# --- Draw Home Team ---
home_logo_drawn_size = (0,0)
if game_details.get("home_logo_path"):
try: try:
# Load and resize logo home_logo = Image.open(home_logo_path)
home_logo = Image.open(game_details["home_logo_path"]).convert("RGBA") if home_logo.mode != 'RGBA':
aspect_ratio = home_logo.width / home_logo.height home_logo = home_logo.convert('RGBA')
if aspect_ratio > 1: # wider than tall # Resize maintaining aspect ratio
new_width = min(logo_max_w, int(logo_max_h * aspect_ratio)) max_size = min(int(self.display_width / 3), int(self.display_height / 2))
new_height = int(new_width / aspect_ratio) home_logo.thumbnail((max_size, max_size), Image.Resampling.LANCZOS)
else: # taller than wide
new_height = min(logo_max_h, int(logo_max_w / aspect_ratio))
new_width = int(new_height * aspect_ratio)
home_logo = home_logo.resize((new_width, new_height), Image.Resampling.LANCZOS)
home_logo_drawn_size = (new_width, new_height)
paste_y = (self.display_height - new_height) // 2
# Convert to RGB before pasting
home_logo_rgb = home_logo.convert('RGB')
img.paste(home_logo_rgb, (home_logo_x, paste_y))
logging.debug(f"[NHL] Successfully rendered home logo: {game_details['home_logo_path']}")
except Exception as e: except Exception as e:
logging.error(f"[NHL] Error rendering home logo {game_details['home_logo_path']}: {e}") logging.error(f"Error loading home logo {home_logo_path}: {e}")
draw.text((home_logo_x + 2, 5), game_details.get("home_abbr", "?"), font=font_team, fill="white")
else:
draw.text((home_logo_x + 2, 5), game_details.get("home_abbr", "?"), font=font_team, fill="white")
current_home_score_x = home_logo_x - score_width_approx - 4 # Load and process away logo
draw.text((current_home_score_x, (self.display_height - 12) // 2), str(game_details.get("home_score", "0")), font=font_score, fill='white') away_logo = None
if os.path.exists(away_logo_path):
try:
away_logo = Image.open(away_logo_path)
if away_logo.mode != 'RGBA':
away_logo = away_logo.convert('RGBA')
# Resize maintaining aspect ratio
max_size = min(int(self.display_width / 3), int(self.display_height / 2))
away_logo.thumbnail((max_size, max_size), Image.Resampling.LANCZOS)
except Exception as e:
logging.error(f"Error loading away logo {away_logo_path}: {e}")
# --- Draw Center Info --- # Calculate positions
if game_details.get("is_live"): width = self.display_width
period = game_details.get('period', 0) height = self.display_height
period_str = f"{period}{'st' if period==1 else 'nd' if period==2 else 'rd' if period==3 else 'th'}".upper() if period > 0 and period <= 3 else "OT" if period > 3 else ""
status_name = game_details.get("status_type_name", "") # Draw team logos
clock_text = game_details.get("clock", "") if home_logo:
if status_name == "STATUS_HALFTIME" or "intermission" in game_details.get("status_text", "").lower(): home_x = width // 4 - home_logo.width // 2
period_str = "INTER" home_y = height // 4 - home_logo.height // 2
clock_text = "" draw.im.paste(home_logo, (home_x, home_y), home_logo)
draw.text((center_x, time_y), clock_text, font=font_time, fill='yellow', anchor="mt")
draw.text((center_x, period_y), period_str, font=font_time, fill='yellow', anchor="mt") if away_logo:
elif game_details.get("is_final"): away_x = width // 4 - away_logo.width // 2
draw.text((center_x, time_y), "FINAL", font=font_status, fill='red', anchor="mt") away_y = 3 * height // 4 - away_logo.height // 2
period = game_details.get('period', 0) draw.im.paste(away_logo, (away_x, away_y), away_logo)
final_period_str = ""
if period > 3: # Draw scores
final_period_str = f"OT{period - 3 if period < 7 else ''}" score_color = (255, 255, 255)
elif game_details.get("status_type_name") == "STATUS_SHOOTOUT": home_score = str(game_details['home_score'])
final_period_str = "SO" away_score = str(game_details['away_score'])
if final_period_str:
draw.text((center_x, period_y), final_period_str, font=font_time, fill='red', anchor="mt") # Draw home score
else: # Should not happen if logic is correct, but fallback home_score_x = width // 2 - 10
status_text = game_details.get("status_text", "Error") home_score_y = height // 4 - 8
draw.text((center_x, time_y), status_text, font=font_time, fill='grey', anchor="mt") draw.text((home_score_x, home_score_y), home_score, font=self.fonts['score'], fill=score_color)
# Draw away score
away_score_x = width // 2 - 10
away_score_y = 3 * height // 4 - 8
draw.text((away_score_x, away_score_y), away_score, font=self.fonts['score'], fill=score_color)
# Draw game status
status_text = game_details.get('status', '')
status_x = width // 2 - 20
status_y = height // 2 - 8
draw.text((status_x, status_y), status_text, font=self.fonts['status'], fill=score_color)
except Exception as e:
logging.error(f"Error in _draw_scorebug_layout: {e}")
if __name__ == "__main__": if __name__ == "__main__":