feat: gracefully stop and restart recordings during display resize#158
feat: gracefully stop and restart recordings during display resize#158hiroTamada wants to merge 3 commits intomainfrom
Conversation
Instead of refusing display resize requests (409) when recordings are active, PatchDisplay now stops active recordings, performs the resize, then restarts them with the same ID and params. The pre-resize segment is preserved by renaming it (e.g. `<id>-before-resize-<ts>.mp4`). Live view sessions still block the resize as before. Co-authored-by: Cursor <cursoragent@cursor.com>
- Don't block recording restart when rename of old file fails (rename is best-effort to preserve the pre-resize segment) - Deep copy pointer fields in FFmpegRecordingParams.clone() so Params() callers cannot mutate recorder internals Co-authored-by: Cursor <cursoragent@cursor.com>
| if !ok { | ||
| log.Warn("cannot capture params from non-FFmpeg recorder, skipping", "id", id) | ||
| continue | ||
| } |
There was a problem hiding this comment.
Non-FFmpeg recordings bypass resize safety
High Severity
stopActiveRecordings silently skips active recorders that aren’t *recorder.FFmpegRecorder. This allows PATCH /display to proceed while a non-FFmpeg recording is still running, which regresses the prior “block while recording” behavior and can lead to active recording sessions being resized mid-capture.
If DeregisterRecorder fails, the old recorder remains registered. Appending to the stopped list would cause RegisterRecorder to fail with a duplicate ID during restart. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
| } | ||
|
|
||
| log.Info("recording restarted after resize", "id", info.id) | ||
| } |
There was a problem hiding this comment.
Restart may not use original output path
Medium Severity
restartRecordings captures outputPath for rename purposes but recreates the recorder via s.factory(info.id, info.params), which derives its own outputPath. If the original recorder’s outputPath didn’t match the factory’s derived path (custom naming, different extension, etc.), the restarted recording won’t continue at the same path and the “preserve then continue” segmentation behavior becomes inconsistent.


Summary
PATCH /displayresize, the endpoint now gracefully stops active recordings, performs the resize, then restarts them with the same recorder ID and params.<id>-before-resize-<timestamp>.mp4) before the new recording begins.Changes
server/lib/recorder/ffmpeg.goParams()andOutputPath()accessors toFFmpegRecorder(thread-safe).server/cmd/api/api/display.gostopActiveRecordings()— stops each activeFFmpegRecorder, waits for shutdown, deregisters it, and captures its ID/params/outputPath for restart. Tolerates finalization errors as long as the process actually stopped.restartRecordings()— renames the old segment file, creates a new recorder via the factory with the same ID and params, registers and starts it. Usesdeferwithcontext.WithoutCancelso recordings are restarted even if the resize itself fails.server/lib/recorder/ffmeg_test.goParams()andOutputPath().server/cmd/api/api/display_test.go(new)TestStopActiveRecordings— 4 sub-tests: stops/deregisters active recording, handles multiple recordings, skips idle recorders, handles empty manager.TestRestartRecordings— 2 sub-tests: renames old file and starts new recording, handles missing old file.TestStopAndRestartRecordings_RoundTrip— full stop→restart cycle verifying the same recorder ID is reused.Test plan
go test ./lib/recorder/— all pass (including new accessor tests)go test ./cmd/api/api/— all pass (including 7 new display tests)PATCH /displaywith new resolution, verify recording restarts at new resolution and pre-resize segment is preserved on diskMade with Cursor
Note
Medium Risk
Touches display-resize and recording lifecycle orchestration; failures could interrupt recordings or overwrite files despite best-effort renaming and restart logic.
Overview
PATCH /displaynow blocks only on active live view/replay sessions, but will otherwise gracefully stop any active FFmpeg recordings, perform the resize, then restart those recordings (same recorder ID/params) afterward.To support this,
FFmpegRecordergains thread-safeParams()andOutputPath()accessors (with deep-copy of params), and the API adds stop/restart helpers that preserve pre-resize segments by renaming the finalized.mp4to*-before-resize-<timestamp>.mp4before starting a new segment. New unit tests cover the new recorder accessors and the stop→restart behavior end-to-end.Written by Cursor Bugbot for commit 0820c4a. This will update automatically on new commits. Configure here.