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
13 changes: 13 additions & 0 deletions lghorizon/lghorizon_device_state_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ async def process_state(
"""Process the device state based on the status message."""
device_state.reset()
device_state.state = status_message.running_state
if status_message.running_state in (
LGHorizonRunningState.ONLINE_STANDBY,
LGHorizonRunningState.OFFLINE,
):
device_state.clear_linear_metadata_cache()

async def process_ui_state(
self,
Expand Down Expand Up @@ -121,6 +126,11 @@ async def _process_apps_state(
device_state: LGHorizonDeviceState,
apps_state: LGHorizonAppsState,
) -> None:
if device_state.is_launcher_app(apps_state.app_name, apps_state.logo_path):
if device_state.restore_linear_metadata():
device_state.ui_state_type = LGHorizonUIStateType.MAINUI
return

device_state.id = apps_state.id
device_state.show_title = apps_state.app_name
device_state.image = apps_state.logo_path
Expand Down Expand Up @@ -171,6 +181,7 @@ async def _process_linear_state(
f"{channel.stream_image}{join_param}{str(random.randrange(1000000))}"
)
device_state.image = image_url
device_state.cache_linear_metadata()

async def _process_reviewbuffer_state(
self,
Expand Down Expand Up @@ -218,6 +229,7 @@ async def _process_reviewbuffer_state(
f"{channel.stream_image}{join_param}{str(random.randrange(1000000))}"
)
device_state.image = image_url
device_state.cache_linear_metadata()

async def _process_replay_state(
self,
Expand Down Expand Up @@ -258,6 +270,7 @@ async def _process_replay_state(
device_state.position = int(player_state.relative_position / 1000)
# Add random number to url to force refresh
device_state.image = await self._get_intent_image_url(replay_event.event_id)
device_state.cache_linear_metadata()

async def _process_vod_state(
self,
Expand Down
59 changes: 59 additions & 0 deletions lghorizon/lghorizon_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -972,6 +972,7 @@ class LGHorizonDeviceState:
start_time: Optional[int] = None
end_time: Optional[int] = None
last_position_update: Optional[int] = None
_last_good_linear_metadata: Dict[str, Any] = field(default_factory=dict)
ad_breaks: List[LGHorizonAdBreak] = field(default_factory=list)

@property
Expand Down Expand Up @@ -1027,6 +1028,64 @@ def reset(self) -> None:
self.reset_progress()


def cache_linear_metadata(self) -> None:
"""Cache current linear metadata for fallback when app overlays appear."""
if not self.channel_name or not self.show_title:
return
self._last_good_linear_metadata = {
"channel_id": self.channel_id,
"channel_name": self.channel_name,
"show_title": self.show_title,
"episode_title": self.episode_title,
"season_number": self.season_number,
"episode_number": self.episode_number,
"image": self.image,
"start_time": self.start_time,
"end_time": self.end_time,
"duration": self.duration,
"position": self.position,
"last_position_update": self.last_position_update,
"source_type": self.source_type,
"media_type": self.media_type,
}

def restore_linear_metadata(self) -> bool:
"""Restore cached linear metadata. Returns True if restored."""
if not self._last_good_linear_metadata:
return False
self.channel_id = self._last_good_linear_metadata.get("channel_id")
self.channel_name = self._last_good_linear_metadata.get("channel_name")
self.show_title = self._last_good_linear_metadata.get("show_title")
self.episode_title = self._last_good_linear_metadata.get("episode_title")
self.season_number = self._last_good_linear_metadata.get("season_number")
self.episode_number = self._last_good_linear_metadata.get("episode_number")
self.image = self._last_good_linear_metadata.get("image")
self.start_time = self._last_good_linear_metadata.get("start_time")
self.end_time = self._last_good_linear_metadata.get("end_time")
self.duration = self._last_good_linear_metadata.get("duration")
self.position = self._last_good_linear_metadata.get("position")
self.last_position_update = self._last_good_linear_metadata.get("last_position_update")
self.source_type = self._last_good_linear_metadata.get("source_type", LGHorizonSourceType.LINEAR)
self.media_type = self._last_good_linear_metadata.get("media_type", LGHorizonMediaType.CHANNEL)
return True

def clear_linear_metadata_cache(self) -> None:
"""Clear the cached linear metadata."""
self._last_good_linear_metadata = {}

@staticmethod
def is_launcher_app(app_name: str, logo_path: str) -> bool:
"""Check if app state looks like a launcher overlay (e.g., BBC Launcher)."""
if not app_name:
return False
name_lower = app_name.lower()
logo_lower = (logo_path or "").lower()
if "launcher" in name_lower:
return True
if "appstore" in logo_lower:
return True
return False


class LGHorizonEntitlements:
"""Class to represent entitlements."""
Expand Down