SIGN IN SIGN UP

feat(serve): dense-stream segmented timelapse mode

Wire core::park's SegmentSelector + run_segment_camera into `bambu serve` as
a new `segment` capture slot, alongside park/smooth/plain. It's the robust
capture for the brief native park that the park image-change miner misses and
the sparse smooth burst catches only ~4% of: each worker segments its camera's
continuous stream by the live print LAYER and median-subtract-selects the
parked frame per layer.

The one thing the camera stream can't supply is the layer — so run_segment
owns an Arc<AtomicI64> it feeds from MQTT layer_num each status tick and hands
to every worker (which run_segment_camera reads per frame). Lifecycle otherwise
mirrors run_park: lazy spawn on first active, stop at FINISH/cancel.

API: POST /api/timelapse/start {mode:"segment", window_ms} resolves the
segment-capable cameras (stream_url + park_tuning for fps + select_tuning for
the knobs — fully calibrated for park capture), 400 if none qualify. Output is
the same latest_park.jpg/parks.jsonl layout, so /api/camera/{id}/park reads it
unchanged; live_park_source now prefers a RUNNING park-style run (segment ->
park -> smooth) so the preview follows the active capture. stop/status learn
the new slot. The park image-change mode stays so the two can be A/B'd on the
next print.

The ffmpeg-spawn + cancel/prune aux thread (shared ParkFfmpeg helper) and the
progress->status adapter (park_progress/report_park_run) are factored out so
the park and segment runners don't duplicate the I/O seam.

Tested software-only: the slot lifecycle spawns one worker per camera and the
live MQTT layer propagates through the shared atomic to the worker; the API
gates an uncalibrated camera and an out-of-range window. No ffmpeg/printer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
S
sksat committed
fbfcbe1d74334f3cb89ef6d59befda9272fc5a71
Parent: 6a58fe3