Fix: capture timelapse baseline on expected-archive on_print_start branch (#1403 follow-up)

The snapshot-diff strategy in _scan_for_timelapse_with_retries needs
  _timelapse_baselines[printer_id] populated at print start so the
  completion-time scan can find the new MP4 by set-difference (mtime is
  unreliable — LAN-only printers don't sync NTP).

  The baseline-capture call was only in on_print_start's new-archive branch.
  Queue / VP-dispatched / reprinted jobs take the expected-archive branch
  which returns earlier, so the dict stayed empty and the completion-time
  scan fell into the "take baseline now" fallback that snapshots after the
  new file has already landed — no diff ever matches.

  Extract the snapshot into _capture_timelapse_baseline_at_start and call
  it from both branches.
This commit is contained in:
maziggy
2026-05-20 08:12:17 +02:00
parent 74f759468c
commit bfd3fc755d
3 changed files with 145 additions and 10 deletions
+35 -10
View File
@@ -2121,6 +2121,14 @@ async def on_print_start(printer_id: int, data: dict):
except Exception as e:
logger.warning("[SPOOLMAN] Failed to store tracking data: %s", e)
# Capture timelapse file baseline for snapshot-diff on completion
# (mirrors the new-archive branch). Queue / VP-dispatched prints
# hit this branch — without the baseline the completion-time scan
# falls into its "take baseline now" fallback, which snapshots
# AFTER the new MP4 already exists and never matches a diff
# (#1403 follow-up — see pwostran's 2026-05-18 support bundle).
await _capture_timelapse_baseline_at_start(printer, printer_id, logger)
return # Skip creating a new archive
# Check if there's already a "printing" archive for this printer/file
@@ -2725,16 +2733,7 @@ async def on_print_start(printer_id: int, data: dict):
logger.warning("[SPOOLMAN] Failed to store tracking data: %s", e)
# Capture timelapse file baseline for snapshot-diff on completion
try:
baseline_files, _ = await _list_timelapse_videos(printer)
_timelapse_baselines[printer_id] = {f.get("name", "") for f in baseline_files}
logger.info(
"[TIMELAPSE] Baseline at print start: %s video files for printer %s",
len(_timelapse_baselines[printer_id]),
printer_id,
)
except Exception as e:
logger.warning("[TIMELAPSE] Failed to capture baseline at print start: %s", e)
await _capture_timelapse_baseline_at_start(printer, printer_id, logger)
finally:
# Keep temp_path around until print completes so the cover endpoint
# can reuse it (#972). Cache eviction in on_print_complete deletes
@@ -2779,6 +2778,32 @@ async def _list_timelapse_videos(printer) -> tuple[list[dict], str | None]:
return [], None
async def _capture_timelapse_baseline_at_start(printer, printer_id: int, logger: logging.Logger) -> None:
"""Snapshot the printer's timelapse directory at print start so the
completion-time scan can pick the new file by set-difference.
Must be called from every on_print_start path that proceeds to a real
print — both the new-archive branch and the expected-archive branch (which
queue / VP-dispatched prints take). Without a baseline,
_scan_for_timelapse_with_retries falls into its "take baseline now"
fallback that runs AFTER the new MP4 has already landed on the SD card,
so the new file ends up in the "baseline" set and no diff ever matches.
Bambu printers in LAN-only mode don't sync NTP, so mtime ordering is
unreliable — the snapshot-diff approach sidesteps that entirely.
"""
try:
baseline_files, _ = await _list_timelapse_videos(printer)
_timelapse_baselines[printer_id] = {f.get("name", "") for f in baseline_files}
logger.info(
"[TIMELAPSE] Baseline at print start: %s video files for printer %s",
len(_timelapse_baselines[printer_id]),
printer_id,
)
except Exception as e:
logger.warning("[TIMELAPSE] Failed to capture baseline at print start: %s", e)
async def _scan_for_timelapse_with_retries(archive_id: int, baseline_names: set[str] | None = None):
"""
Scan for timelapse with retries using a snapshot-diff approach.