Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 55 additions & 27 deletions plugins/masters-tournament/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"""

import logging
import os
import time
from datetime import datetime
from typing import Any, Dict, List, Optional
Expand Down Expand Up @@ -54,7 +55,7 @@ def __init__(self, plugin_id, config, display_manager, cache_manager, plugin_man
self.display_duration = config.get("display_duration", 20)

# Initialize components
self.logo_loader = MastersLogoLoader(self.plugin_dir)
self.logo_loader = MastersLogoLoader(os.path.dirname(os.path.abspath(__file__)))
self.data_source = MastersDataSource(cache_manager, config)

# Use enhanced renderer for 64x32+, base for tiny displays
Expand Down Expand Up @@ -110,6 +111,14 @@ def __init__(self, plugin_id, config, display_manager, cache_manager, plugin_man
self._fact_index = 0
self._fact_scroll = 0

# Internal timers for modes that rotate content within a display cycle
self._last_hole_advance = {} # per-mode hole timers
self._hole_switch_interval = config.get("hole_display_duration", 15)
self._last_fact_advance = 0
self._fact_advance_interval = 2 # seconds between scroll steps
self._last_page_advance = {} # per-mode page timers
self._page_interval = config.get("page_display_duration", 15)

# Player card rotation
self._player_card_index = 0

Expand Down Expand Up @@ -357,23 +366,32 @@ def display(self, force_clear: bool = False, display_mode: Optional[str] = None)
self.logger.warning(f"Unknown display mode: {display_mode}")
return False

def _advance_page(self, key: str) -> int:
"""Return current page for a mode, advancing only after page_interval seconds."""
now = time.time()
last = self._last_page_advance.get(key, 0)
if last > 0 and now - last >= self._page_interval:
self._page[key] = self._page.get(key, 0) + 1
self._last_page_advance[key] = now
elif last == 0:
self._last_page_advance[key] = now
return self._page.get(key, 0)

def _show_image(self, image: Optional[Image.Image]) -> bool:
"""Helper to display an image if it exists."""
if image:
self.display_manager.draw_image(image, 0, 0)
self.display_manager.image.paste(image, (0, 0))
self.display_manager.update_display()
return True
return False

def _display_leaderboard(self, force_clear: bool) -> bool:
if not self._leaderboard_data:
return False
page = self._page["leaderboard"]
result = self._show_image(
page = self._advance_page("leaderboard")
return self._show_image(
self.renderer.render_leaderboard(self._leaderboard_data, show_favorites=True, page=page)
)
self._page["leaderboard"] = page + 1
return result

def _display_player_cards(self, force_clear: bool) -> bool:
if not self._leaderboard_data:
Expand All @@ -385,36 +403,43 @@ def _display_player_cards(self, force_clear: bool) -> bool:
return self._show_image(self.renderer.render_player_card(player))

def _display_course_tour(self, force_clear: bool) -> bool:
result = self._show_image(self.renderer.render_hole_card(self._current_hole))
self._current_hole = (self._current_hole % 18) + 1
return result
now = time.time()
last = self._last_hole_advance.get("course_tour", 0)
if last > 0 and now - last >= self._hole_switch_interval:
self._current_hole = (self._current_hole % 18) + 1
self._last_hole_advance["course_tour"] = now
elif last == 0:
self._last_hole_advance["course_tour"] = now
return self._show_image(self.renderer.render_hole_card(self._current_hole))

def _display_amen_corner(self, force_clear: bool) -> bool:
return self._show_image(self.renderer.render_amen_corner())

def _display_past_champions(self, force_clear: bool) -> bool:
page = self._page["champions"]
result = self._show_image(self.renderer.render_past_champions(page=page))
self._page["champions"] = page + 1
return result
page = self._advance_page("champions")
return self._show_image(self.renderer.render_past_champions(page=page))

def _display_hole_by_hole(self, force_clear: bool) -> bool:
"""Display hole-by-hole course tour (same as course_tour)."""
return self._display_course_tour(force_clear)

def _display_featured_holes(self, force_clear: bool) -> bool:
featured = [12, 13, 15, 16]
now = time.time()
last = self._last_hole_advance.get("featured", 0)
if last > 0 and now - last >= self._hole_switch_interval:
self._featured_hole_index += 1
self._last_hole_advance["featured"] = now
elif last == 0:
self._last_hole_advance["featured"] = now
hole = featured[self._featured_hole_index % len(featured)]
self._featured_hole_index += 1
return self._show_image(self.renderer.render_hole_card(hole))

def _display_schedule(self, force_clear: bool) -> bool:
page = self._page["schedule"]
result = self._show_image(
page = self._advance_page("schedule")
return self._show_image(
self.renderer.render_schedule(self._schedule_data, page=page)
)
self._page["schedule"] = page + 1
return result

def _display_live_action(self, force_clear: bool) -> bool:
"""Show live alert if enhanced renderer available, else leaderboard."""
Expand All @@ -431,16 +456,17 @@ def _display_live_action(self, force_clear: bool) -> bool:
return self._display_leaderboard(force_clear)

def _display_tournament_stats(self, force_clear: bool) -> bool:
page = self._page["stats"]
result = self._show_image(self.renderer.render_tournament_stats(page=page))
self._page["stats"] = page + 1
return result
page = self._advance_page("stats")
return self._show_image(self.renderer.render_tournament_stats(page=page))

def _display_fun_facts(self, force_clear: bool) -> bool:
result = self._show_image(
self.renderer.render_fun_fact(self._fact_index, scroll_offset=self._fact_scroll)
)
self._fact_scroll += 1
now = time.time()
if now - self._last_fact_advance >= self._fact_advance_interval:
self._fact_scroll += 1
self._last_fact_advance = now
# Move to next fact after scrolling through
if self._fact_scroll > 5:
self._fact_index += 1
Expand Down Expand Up @@ -468,10 +494,8 @@ def _display_field_overview(self, force_clear: bool) -> bool:

def _display_course_overview(self, force_clear: bool) -> bool:
if hasattr(self.renderer, "render_course_overview"):
page = self._page["course_overview"]
result = self._show_image(self.renderer.render_course_overview(page=page))
self._page["course_overview"] = page + 1
return result
page = self._advance_page("course_overview")
return self._show_image(self.renderer.render_course_overview(page=page))
return self._display_amen_corner(force_clear)

def get_vegas_content(self) -> Optional[List[Image.Image]]:
Expand Down Expand Up @@ -522,6 +546,10 @@ def on_config_change(self, new_config):
super().on_config_change(new_config)
self._update_interval = new_config.get("update_interval", 30)
self.display_duration = new_config.get("display_duration", 20)
self._hole_switch_interval = new_config.get("hole_display_duration", 15)
self._page_interval = new_config.get("page_display_duration", 15)
self._last_hole_advance.clear()
self._last_page_advance.clear()
self.modes = self._build_enabled_modes()
self._last_update = 0

Expand Down
78 changes: 63 additions & 15 deletions plugins/masters-tournament/masters_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -807,20 +807,25 @@ def render_schedule(self, schedule_data: List[Dict], page: int = 0) -> Optional[
# COUNTDOWN - Centered and spacious
# ═══════════════════════════════════════════════════════════

def _draw_logo_with_glow(self, img, logo, lx, ly, glow_pad=2):
"""Paste a logo with a black glow outline for visibility."""
if logo and logo.mode == "RGBA":
alpha = logo.split()[3]
shadow = Image.new("RGBA", logo.size, (0, 0, 0, 0))
shadow.paste((0, 0, 0), mask=alpha)
for ox in range(-glow_pad, glow_pad + 1):
for oy in range(-glow_pad, glow_pad + 1):
if ox == 0 and oy == 0:
continue
img.paste(shadow, (lx + ox, ly + oy), shadow)
if logo:
img.paste(logo, (lx, ly), logo if logo.mode == "RGBA" else None)

def render_countdown(self, days: int, hours: int, minutes: int) -> Optional[Image.Image]:
img = self._draw_gradient_bg(COLORS["masters_dark"], COLORS["masters_green"])
draw = ImageDraw.Draw(img)

# Masters logo centered at top
logo = self.logo_loader.get_masters_logo(
max_width=min(self.width - 10, 48),
max_height=min(self.height // 3, 20),
)
if logo:
lx = (self.width - logo.width) // 2
img.paste(logo, (lx, 3), logo if logo.mode == "RGBA" else None)

# Countdown number - big and centered
# Countdown text
if days > 0:
count_text = str(days)
unit_text = "DAYS" if days > 1 else "DAY"
Expand All @@ -831,20 +836,63 @@ def render_countdown(self, days: int, hours: int, minutes: int) -> Optional[Imag
count_text = "NOW"
unit_text = ""

mid_y = self.height // 2
# Two-column layout only on large displays (width > 64)
min_right_width = 40
if self.tier == "large":
logo = self.logo_loader.get_masters_logo(
max_width=int(self.width * 0.55),
max_height=self.height - 6,
)
if logo and (self.width - logo.width - 12) >= min_right_width:
lx = 4
ly = (self.height - logo.height) // 2
self._draw_logo_with_glow(img, logo, lx, ly)
right_x = lx + logo.width + 6
right_w = self.width - right_x - 2
right_cx = right_x + right_w // 2

until_text = "UNTIL THE MASTERS"
utw = self._text_width(draw, until_text, self.font_detail)
if utw > right_w:
until_text = "TO MASTERS"
utw = self._text_width(draw, until_text, self.font_detail)

detail_h = self._text_height(draw, "A", self.font_detail)
count_h = self._text_height(draw, count_text, self.font_score)
block_h = detail_h + 2 + count_h + 2 + detail_h
block_y = max(2, (self.height - block_h) // 2)

draw.text((right_cx - utw // 2, block_y),
until_text, fill=COLORS["white"], font=self.font_detail)
cw = self._text_width(draw, count_text, self.font_score)
count_y = block_y + detail_h + 2
self._text_shadow(draw, (right_cx - cw // 2, count_y),
count_text, self.font_score, COLORS["masters_yellow"])
if unit_text:
uw = self._text_width(draw, unit_text, self.font_detail)
draw.text((right_cx - uw // 2, count_y + count_h + 2),
unit_text, fill=COLORS["light_gray"], font=self.font_detail)
return img

# Compact layout: logo centered at top, countdown below
logo = self.logo_loader.get_masters_logo(
max_width=min(self.width - 10, 48),
max_height=min(self.height // 3, 20),
)
if logo:
lx = (self.width - logo.width) // 2
self._draw_logo_with_glow(img, logo, lx, 3)

# "UNTIL THE MASTERS" centered
until_text = "UNTIL THE MASTERS" if self.tier != "tiny" else "TO MASTERS"
mid_y = self.height // 2
until_text = "TO MASTERS" if self.tier == "tiny" else "UNTIL THE MASTERS"
uw = self._text_width(draw, until_text, self.font_detail)
draw.text(((self.width - uw) // 2, mid_y - 6),
until_text, fill=COLORS["white"], font=self.font_detail)

# Big countdown number
cw = self._text_width(draw, count_text, self.font_score)
self._text_shadow(draw, ((self.width - cw) // 2, mid_y + 4),
count_text, self.font_score, COLORS["masters_yellow"])

# Unit below
if unit_text:
uw2 = self._text_width(draw, unit_text, self.font_detail)
draw.text(((self.width - uw2) // 2, mid_y + 16),
Expand Down
Loading