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()) try:
font_main = self.fonts.get('upcoming_main', ImageFont.load_default()) # Load and resize logos
font_vs = self.fonts.get('upcoming_vs', ImageFont.load_default()) home_logo_path = os.path.join(self.logo_dir, f"{game_details['home_abbr']}.png")
img = draw.im away_logo_path = os.path.join(self.logo_dir, f"{game_details['away_abbr']}.png")
logging.debug("[NHL] Drawing upcoming game layout.") # Load and process home logo
home_logo = None
logo_padding = 2 if os.path.exists(home_logo_path):
logo_max_h = self.display_height - (logo_padding * 2) try:
logo_area_width = int(self.display_width * 0.4) home_logo = Image.open(home_logo_path)
logo_max_w = logo_area_width - logo_padding if home_logo.mode != 'RGBA':
logo_size = (logo_max_w, logo_max_h) home_logo = home_logo.convert('RGBA')
# Resize maintaining aspect ratio
away_logo_x = logo_padding max_size = min(int(self.display_width / 3), int(self.display_height / 2))
home_logo_x = self.display_width - logo_area_width + logo_padding home_logo.thumbnail((max_size, max_size), Image.Resampling.LANCZOS)
except Exception as e:
# Draw Away Logo logging.error(f"Error loading home logo {home_logo_path}: {e}")
if game_details.get("away_logo_path"):
try: # Load and process away logo
# Load and resize logo away_logo = None
away_logo = Image.open(game_details["away_logo_path"]).convert("RGBA") if os.path.exists(away_logo_path):
aspect_ratio = away_logo.width / away_logo.height try:
if aspect_ratio > 1: # wider than tall away_logo = Image.open(away_logo_path)
new_width = min(logo_max_w, int(logo_max_h * aspect_ratio)) if away_logo.mode != 'RGBA':
new_height = int(new_width / aspect_ratio) away_logo = away_logo.convert('RGBA')
else: # taller than wide # Resize maintaining aspect ratio
new_height = min(logo_max_h, int(logo_max_w / aspect_ratio)) max_size = min(int(self.display_width / 3), int(self.display_height / 2))
new_width = int(new_height * aspect_ratio) away_logo.thumbnail((max_size, max_size), Image.Resampling.LANCZOS)
except Exception as e:
away_logo = away_logo.resize((new_width, new_height), Image.Resampling.LANCZOS) logging.error(f"Error loading away logo {away_logo_path}: {e}")
paste_y = (self.display_height - new_height) // 2
# Calculate positions
# Convert to RGB before pasting width = self.display_width
away_logo_rgb = away_logo.convert('RGB') height = self.display_height
img.paste(away_logo_rgb, (away_logo_x, paste_y))
# Draw team logos
logging.debug(f"[NHL] Successfully rendered away logo: {game_details['away_logo_path']}") if home_logo:
except Exception as e: home_x = width // 4 - home_logo.width // 2
logging.error(f"[NHL] Error rendering upcoming away logo {game_details['away_logo_path']}: {e}") home_y = height // 4 - home_logo.height // 2
draw.text((away_logo_x, 5), game_details.get("away_abbr", "?"), font=font_team, fill="white") draw.im.paste(home_logo, (home_x, home_y), home_logo)
else:
draw.text((away_logo_x, 5), game_details.get("away_abbr", "?"), font=font_team, fill="white") if away_logo:
away_x = width // 4 - away_logo.width // 2
# Draw Home Logo away_y = 3 * height // 4 - away_logo.height // 2
if game_details.get("home_logo_path"): draw.im.paste(away_logo, (away_x, away_y), away_logo)
try:
# Load and resize logo # Draw game time
home_logo = Image.open(game_details["home_logo_path"]).convert("RGBA") game_time = game_details.get('clock', '')
aspect_ratio = home_logo.width / home_logo.height time_x = width // 2 - 20
if aspect_ratio > 1: # wider than tall time_y = height // 2 - 8
new_width = min(logo_max_w, int(logo_max_h * aspect_ratio)) draw.text((time_x, time_y), game_time, font=self.fonts['time'], fill=(255, 255, 255))
new_height = int(new_width / aspect_ratio)
else: # taller than wide except Exception as e:
new_height = min(logo_max_h, int(logo_max_w / aspect_ratio)) logging.error(f"Error in _draw_upcoming_layout: {e}")
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:
logging.error(f"[NHL] Error rendering upcoming home logo {game_details['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
center_start_x = logo_area_width
center_end_x = self.display_width - logo_area_width
center_x = (center_start_x + center_end_x) // 2
# Prepare Text
start_utc = game_details.get("start_time_utc")
date_str = "???"
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)
line_height_approx = font_main.getbbox("Aj")[3] - font_main.getbbox("Aj")[1] + 2
vs_height = font_vs.getbbox("VS")[3] - font_vs.getbbox("VS")[1]
total_text_height = (line_height_approx * 2) + vs_height
start_y = (self.display_height - total_text_height) // 2
date_y = start_y
time_y = date_y + line_height_approx
vs_y = time_y + line_height_approx
# Draw Text
draw.text((center_x, date_y), date_str, font=font_main, fill='white', anchor="mt")
draw.text((center_x, time_y), time_str, font=font_main, fill='white', anchor="mt")
draw.text((center_x, vs_y), vs_str, font=font_vs, fill='white', anchor="mt")
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()) try:
font_time = self.fonts.get('time', ImageFont.load_default()) # Load and resize logos
font_team = self.fonts.get('team', ImageFont.load_default()) home_logo_path = os.path.join(self.logo_dir, f"{game_details['home_abbr']}.png")
font_status = self.fonts.get('status', ImageFont.load_default()) away_logo_path = os.path.join(self.logo_dir, f"{game_details['away_abbr']}.png")
img = draw.im
# Load and process home logo
logging.debug("[NHL] Drawing live/final game layout.") home_logo = None
if os.path.exists(home_logo_path):
# Layout Calculations try:
logo_max_h = self.display_height - 4 home_logo = Image.open(home_logo_path)
logo_max_w = int(self.display_width * 0.25) if home_logo.mode != 'RGBA':
logo_size = (logo_max_w, logo_max_h) home_logo = home_logo.convert('RGBA')
away_logo_x = 2 # Resize maintaining aspect ratio
score_width_approx = 25 max_size = min(int(self.display_width / 3), int(self.display_height / 2))
away_score_x = away_logo_x + logo_max_w + 4 home_logo.thumbnail((max_size, max_size), Image.Resampling.LANCZOS)
home_logo_x = self.display_width - logo_max_w - 2 except Exception as e:
home_score_x = home_logo_x - score_width_approx - 4 logging.error(f"Error loading home logo {home_logo_path}: {e}")
center_x = self.display_width // 2
time_y = 2 # Load and process away logo
period_y = 15 away_logo = None
if os.path.exists(away_logo_path):
# --- Draw Away Team --- try:
away_logo_drawn_size = (0,0) away_logo = Image.open(away_logo_path)
if game_details.get("away_logo_path"): if away_logo.mode != 'RGBA':
try: away_logo = away_logo.convert('RGBA')
# Load and resize logo # Resize maintaining aspect ratio
away_logo = Image.open(game_details["away_logo_path"]).convert("RGBA") max_size = min(int(self.display_width / 3), int(self.display_height / 2))
aspect_ratio = away_logo.width / away_logo.height away_logo.thumbnail((max_size, max_size), Image.Resampling.LANCZOS)
if aspect_ratio > 1: # wider than tall except Exception as e:
new_width = min(logo_max_w, int(logo_max_h * aspect_ratio)) logging.error(f"Error loading away logo {away_logo_path}: {e}")
new_height = int(new_width / aspect_ratio)
else: # taller than wide # Calculate positions
new_height = min(logo_max_h, int(logo_max_w / aspect_ratio)) width = self.display_width
new_width = int(new_height * aspect_ratio) height = self.display_height
away_logo = away_logo.resize((new_width, new_height), Image.Resampling.LANCZOS) # Draw team logos
away_logo_drawn_size = (new_width, new_height) if home_logo:
paste_y = (self.display_height - new_height) // 2 home_x = width // 4 - home_logo.width // 2
home_y = height // 4 - home_logo.height // 2
# Convert to RGB before pasting draw.im.paste(home_logo, (home_x, home_y), home_logo)
away_logo_rgb = away_logo.convert('RGB')
img.paste(away_logo_rgb, (away_logo_x, paste_y)) if away_logo:
away_x = width // 4 - away_logo.width // 2
logging.debug(f"[NHL] Successfully rendered away logo: {game_details['away_logo_path']}") away_y = 3 * height // 4 - away_logo.height // 2
except Exception as e: draw.im.paste(away_logo, (away_x, away_y), away_logo)
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") # Draw scores
else: score_color = (255, 255, 255)
draw.text((away_logo_x + 2, 5), game_details.get("away_abbr", "?"), font=font_team, fill="white") home_score = str(game_details['home_score'])
away_score = str(game_details['away_score'])
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 score
home_score_x = width // 2 - 10
# --- Draw Home Team --- home_score_y = height // 4 - 8
home_logo_drawn_size = (0,0) draw.text((home_score_x, home_score_y), home_score, font=self.fonts['score'], fill=score_color)
if game_details.get("home_logo_path"):
try: # Draw away score
# Load and resize logo away_score_x = width // 2 - 10
home_logo = Image.open(game_details["home_logo_path"]).convert("RGBA") away_score_y = 3 * height // 4 - 8
aspect_ratio = home_logo.width / home_logo.height draw.text((away_score_x, away_score_y), away_score, font=self.fonts['score'], fill=score_color)
if aspect_ratio > 1: # wider than tall
new_width = min(logo_max_w, int(logo_max_h * aspect_ratio)) # Draw game status
new_height = int(new_width / aspect_ratio) status_text = game_details.get('status', '')
else: # taller than wide status_x = width // 2 - 20
new_height = min(logo_max_h, int(logo_max_w / aspect_ratio)) status_y = height // 2 - 8
new_width = int(new_height * aspect_ratio) draw.text((status_x, status_y), status_text, font=self.fonts['status'], fill=score_color)
home_logo = home_logo.resize((new_width, new_height), Image.Resampling.LANCZOS) except Exception as e:
home_logo_drawn_size = (new_width, new_height) logging.error(f"Error in _draw_scorebug_layout: {e}")
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:
logging.error(f"[NHL] Error rendering home logo {game_details['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
draw.text((current_home_score_x, (self.display_height - 12) // 2), str(game_details.get("home_score", "0")), font=font_score, fill='white')
# --- Draw Center Info ---
if game_details.get("is_live"):
period = game_details.get('period', 0)
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", "")
clock_text = game_details.get("clock", "")
if status_name == "STATUS_HALFTIME" or "intermission" in game_details.get("status_text", "").lower():
period_str = "INTER"
clock_text = ""
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")
elif game_details.get("is_final"):
draw.text((center_x, time_y), "FINAL", font=font_status, fill='red', anchor="mt")
period = game_details.get('period', 0)
final_period_str = ""
if period > 3:
final_period_str = f"OT{period - 3 if period < 7 else ''}"
elif game_details.get("status_type_name") == "STATUS_SHOOTOUT":
final_period_str = "SO"
if final_period_str:
draw.text((center_x, period_y), final_period_str, font=font_time, fill='red', anchor="mt")
else: # Should not happen if logic is correct, but fallback
status_text = game_details.get("status_text", "Error")
draw.text((center_x, time_y), status_text, font=font_time, fill='grey', anchor="mt")
if __name__ == "__main__": if __name__ == "__main__":