diff --git a/lib/streaming_providers/providers/livgolf/event_manager.py b/lib/streaming_providers/providers/livgolf/event_manager.py index b986967..5295327 100644 --- a/lib/streaming_providers/providers/livgolf/event_manager.py +++ b/lib/streaming_providers/providers/livgolf/event_manager.py @@ -83,6 +83,9 @@ class LivGolfEventManager: # Cached preferred CDN base URL (None = not yet resolved) self._preferred_cdn: Optional[str] = None + # Cache for events keyed by content_id (video_id) + self._events_cache: Dict[str, Event] = {} + logger.info("[LivGolfEventManager] Initialised") # ------------------------------------------------------------------ @@ -122,15 +125,22 @@ class LivGolfEventManager: ) events: List[Event] = [] + new_cache: Dict[str, Event] = {} + for stream in team_streams: event = self._build_event(stream, preferred_cdn, stream_kind="team") if event: events.append(event) + new_cache[event.content_id] = event for stream in group_streams: event = self._build_event(stream, preferred_cdn, stream_kind="group") if event: events.append(event) + new_cache[event.content_id] = event + + # Update cache with fresh results + self._events_cache = new_cache logger.info( f"[LivGolfEventManager] champion={champion_id}: " @@ -139,6 +149,26 @@ class LivGolfEventManager: ) return events + def get_event( + self, content_id: str, champion_id: str = DEFAULT_CHAMPION_ID + ) -> Optional[Event]: + """ + Return a single Event by its content_id. + + If the event is not in the cache, it triggers a fresh get_events() call + to discover it. + """ + event = self._events_cache.get(content_id) + if event: + return event + + logger.info( + f"[LivGolfEventManager] Event '{content_id}' not in cache — " + "triggering fresh fetch" + ) + self.get_events(champion_id=champion_id) + return self._events_cache.get(content_id) + # ------------------------------------------------------------------ # Region / CDN resolution # ------------------------------------------------------------------ diff --git a/lib/streaming_providers/providers/livgolf/provider.py b/lib/streaming_providers/providers/livgolf/provider.py index 661ee3c..c4ceac6 100644 --- a/lib/streaming_providers/providers/livgolf/provider.py +++ b/lib/streaming_providers/providers/livgolf/provider.py @@ -15,7 +15,7 @@ long-lived (~1 year) and is persisted between sessions by the base authenticator's settings_manager. """ -from typing import ClassVar, Dict, List, Optional, Tuple +from typing import ClassVar, Dict, List, Optional from ...base.models import Event, StreamingChannel from ...base.models.proxy_models import ProxyConfig @@ -104,11 +104,6 @@ class LivGolfProvider(StreamingProvider): # Manifest URLs are stable for the duration of a tournament round. return False - @property - def epg_window(self) -> Tuple[int, int]: - # No EPG support. - return 0, 0 - @property def catchup_window(self) -> int: return 0 @@ -182,10 +177,28 @@ class LivGolfProvider(StreamingProvider): def get_manifest(self, content_id: str, **kwargs) -> Optional[str]: """ - Manifest URLs are embedded directly in Event objects; nothing to look - up dynamically. + Retrieve the manifest URL for a specific event ID. + + If the event is not in the event manager's cache, it triggers a fresh + fetch. LIV Golf events embed the manifest URL directly. """ - return None + champion_id = kwargs.get("champion_id", self._champion_id) + logger.info(f"[LivGolfProvider] get_manifest(content_id={content_id})") + + try: + event = self.event_manager.get_event( + content_id, champion_id=champion_id + ) + if event: + logger.info(f"[LivGolfProvider] Found manifest for '{content_id}'") + return event.manifest + + logger.warning(f"[LivGolfProvider] Event '{content_id}' not found") + return None + + except Exception as exc: + logger.error(f"[LivGolfProvider] get_manifest failed: {exc}") + return None def get_catchup_manifest( self, channel_id: str, start_time: int, end_time: int, **kwargs @@ -202,11 +215,6 @@ class LivGolfProvider(StreamingProvider): ) -> Optional[str]: return None - def enrich_channel_data( - self, channel: StreamingChannel, **kwargs - ) -> Optional[StreamingChannel]: - return None - @staticmethod def validate_credentials() -> bool: """