Remove bug from display manager of missing function

This commit is contained in:
RobbStarkAustria
2026-03-30 12:13:48 +02:00
parent ba6785528d
commit 9d256788bc

View File

@@ -1811,6 +1811,55 @@ class DisplayManager:
# Screenshot capture subsystem
# -------------------------------------------------------------
def _pending_trigger_is_valid(self, meta: Dict) -> bool:
"""Return True only for fresh, actionable pending trigger metadata.
This prevents a stale/corrupt pending flag from permanently blocking
periodic updates (meta.json/latest.jpg) if simclient was down or test
data left send_immediately=True behind.
"""
try:
if not meta.get('send_immediately'):
return False
mtype = str(meta.get('type') or '')
if mtype not in ('event_start', 'event_stop'):
return False
mfile = str(meta.get('file') or '').strip()
if not mfile:
return False
file_path = os.path.join(self.screenshot_dir, mfile)
if not os.path.exists(file_path):
logging.warning(
f"Ignoring stale pending screenshot meta: missing file '{mfile}'"
)
return False
captured_at_raw = meta.get('captured_at')
if not captured_at_raw:
return False
captured_at = datetime.fromisoformat(str(captured_at_raw).replace('Z', '+00:00'))
age_s = (datetime.now(timezone.utc) - captured_at.astimezone(timezone.utc)).total_seconds()
# Guard against malformed/future timestamps that could lock
# the pipeline by appearing permanently "fresh".
if age_s < -5:
logging.warning(
f"Ignoring invalid pending screenshot meta: future captured_at (age={age_s:.1f}s)"
)
return False
# Triggered screenshots should be consumed quickly (<= 1s). Use a
# generous safety window to avoid false negatives under load.
if age_s > 30:
logging.warning(
f"Ignoring stale pending screenshot meta: type={mtype}, age={age_s:.1f}s"
)
return False
return True
except Exception:
return False
def _write_screenshot_meta(self, capture_type: str, final_path: str, send_immediately: bool = False):
"""Write screenshots/meta.json atomically so simclient can detect new captures.
@@ -1824,55 +1873,6 @@ class DisplayManager:
send_immediately: True for triggered (event) captures, False for periodic ones
"""
try:
def _pending_trigger_is_valid(meta: Dict) -> bool:
"""Return True only for fresh, actionable pending trigger metadata.
This prevents a stale/corrupt pending flag from permanently blocking
periodic updates (meta.json/latest.jpg) if simclient was down or test
data left send_immediately=True behind.
"""
try:
if not meta.get('send_immediately'):
return False
mtype = str(meta.get('type') or '')
if mtype not in ('event_start', 'event_stop'):
return False
mfile = str(meta.get('file') or '').strip()
if not mfile:
return False
file_path = os.path.join(self.screenshot_dir, mfile)
if not os.path.exists(file_path):
logging.warning(
f"Ignoring stale pending screenshot meta: missing file '{mfile}'"
)
return False
captured_at_raw = meta.get('captured_at')
if not captured_at_raw:
return False
captured_at = datetime.fromisoformat(str(captured_at_raw).replace('Z', '+00:00'))
age_s = (datetime.now(timezone.utc) - captured_at.astimezone(timezone.utc)).total_seconds()
# Guard against malformed/future timestamps that could lock
# the pipeline by appearing permanently "fresh".
if age_s < -5:
logging.warning(
f"Ignoring invalid pending screenshot meta: future captured_at (age={age_s:.1f}s)"
)
return False
# Triggered screenshots should be consumed quickly (<= 1s). Use a
# generous safety window to avoid false negatives under load.
if age_s > 30:
logging.warning(
f"Ignoring stale pending screenshot meta: type={mtype}, age={age_s:.1f}s"
)
return False
return True
except Exception:
return False
meta_path = os.path.join(self.screenshot_dir, 'meta.json')
# PROTECTION: Don't overwrite pending event-triggered metadata with periodic capture
@@ -1882,7 +1882,7 @@ class DisplayManager:
with open(meta_path, 'r', encoding='utf-8') as f:
existing_meta = json.load(f)
# If there's a pending event-triggered capture, skip this periodic write
if _pending_trigger_is_valid(existing_meta):
if self._pending_trigger_is_valid(existing_meta):
logging.debug(f"Skipping periodic meta.json to preserve pending {existing_meta.get('type')} (send_immediately=True)")
return
except Exception:
@@ -2096,29 +2096,29 @@ class DisplayManager:
# Maintain latest.jpg as an atomic copy so readers never see a missing
# or broken pointer while a new screenshot is being published.
# PROTECTION: Don't update latest.jpg for periodic captures if event-triggered is pending
should_update_latest = True
if capture_type == "periodic":
try:
meta_path = os.path.join(self.screenshot_dir, 'meta.json')
if os.path.exists(meta_path):
with open(meta_path, 'r', encoding='utf-8') as f:
existing_meta = json.load(f)
# If there's a pending event-triggered capture, don't update latest.jpg
if _pending_trigger_is_valid(existing_meta):
should_update_latest = False
logging.debug(f"Skipping latest.jpg update to preserve pending {existing_meta.get('type')} screenshot")
except Exception:
pass # If we can't read meta, proceed with updating latest.jpg
latest_link = os.path.join(self.screenshot_dir, 'latest.jpg')
if should_update_latest:
try:
latest_tmp = os.path.join(self.screenshot_dir, 'latest.jpg.tmp')
shutil.copyfile(final_path, latest_tmp)
os.replace(latest_tmp, latest_link)
except Exception as e:
logging.debug(f"Could not update latest.jpg: {e}")
# PROTECTION: Don't update latest.jpg for periodic captures if event-triggered is pending
should_update_latest = True
if capture_type == "periodic":
try:
meta_path = os.path.join(self.screenshot_dir, 'meta.json')
if os.path.exists(meta_path):
with open(meta_path, 'r', encoding='utf-8') as f:
existing_meta = json.load(f)
# If there's a pending event-triggered capture, don't update latest.jpg
if self._pending_trigger_is_valid(existing_meta):
should_update_latest = False
logging.debug(f"Skipping latest.jpg update to preserve pending {existing_meta.get('type')} screenshot")
except Exception:
pass # If we can't read meta, proceed with updating latest.jpg
latest_link = os.path.join(self.screenshot_dir, 'latest.jpg')
if should_update_latest:
try:
latest_tmp = os.path.join(self.screenshot_dir, 'latest.jpg.tmp')
shutil.copyfile(final_path, latest_tmp)
os.replace(latest_tmp, latest_link)
except Exception as e:
logging.debug(f"Could not update latest.jpg: {e}")
# Rotate old screenshots
try: