# Video Trim Design This document describes post-trace video trim support for the Analyze page. The goal is to let a user limit analysis to an inclusive frame range while keeping the current "trace from the current frame to the end" workflow. ## Scope For this release, trim controls are available after the movie has traceable frame data. Pre-trace trimming is out of scope because the current application only has frame 0 before tracing or frame-ZIP generation. A future change may separate frame extraction from motion tracing so users can trim before the first trace. Do not trigger retracing from page-leave or `beforeunload` behavior. Trimming must be applied by an explicit user action while the page is active. ## Stored Metadata Store trim bounds as movie-table attributes: - `trim_start_frame` - `trim_end_frame` Both values are zero-based frame numbers and the range is inclusive. Default values: - `trim_start_frame = 0` - `trim_end_frame = total_frames - 1` If either value is missing in DynamoDB when metadata is served to the movie analyzer, the server should compute the default when `total_frames` is known, and include it in the JSON response. If a JSON response handed to the movie analyzer still lacks one of the values, the JavaScript controller should compute the same defaults from `metadata.total_frames` and the loaded frame array. Add both attributes to the local pydantic `Movie` model and to the movie metadata integer coercion path. ## Validation Trim updates must satisfy: - `0 <= trim_start_frame` - `trim_start_frame <= trim_end_frame` - `trim_end_frame < total_frames` Reject invalid updates before persisting them. In particular, setting start after end or end before start is invalid. Generate an exception so that we can fix this in development, not have it silently fixed during runtime. When reading existing metadata, clamp a stored `trim_end_frame` that is beyond `total_frames - 1`; this can happen after a movie is replaced with a shorter one while stale trim metadata remains. ## Analyze Page Controls Add a checkbox under the motion controls: - Label: `Show video trim controls`. - Disabled until traceable frame data exists. - When disabled, show the tooltip text `Trace movie to enable trimming.` When checked, show trim controls: - `Set start` - `Set end` `Set start` sets `trim_start_frame` to the currently displayed frame. `Set end` sets `trim_end_frame` to the currently displayed frame. To clear the start, the user will go to the first frame (by clicking the first frame button twice) and then clicking `Set start`. To clear the end, the user will go to the last frame (by clicking the last frame button twice) and then clicking `Set end`. ## Trackpoint Rules Applying a trim does not change stored trackpoints outside the inclusive trim range. However, graphs and CSV export only expose trackpoints in the trimmed range. When moving `trim_start_frame` earlier and the new start frame has no markers, copy the old start frame's marker set to the new start frame as an editable seed; do not synthesize intermediate frames. Frames outside the trim range may still be displayed by direct navigation, but they are not part of the active analysis: - Draw a semi-transparent gray rectangle over the canvas. - Do not draw markers or path lines on the frame. - Do not allow adding, deleting, dragging, or saving trackpoints on the frame. - When tracing to the end of the movie, tracing stops at `trim_end_frame`. ## Movie Player Behavior When trim bounds are set, playback buttons use the trimmed range as the primary navigation target while still allowing explicit inspection outside the trim. | Control | Behavior | | --- | --- | | `first_button` | First press goes to `trim_start_frame`; if already on `trim_start_frame`, second press goes to frame 0. | | `play_reverse` | Plays backward to `trim_start_frame`. | | `prev_frame` | Goes to the previous frame and ignores `trim_start_frame`. | | `frame_number_field` | Goes to the requested frame and ignores trim bounds. | | `pause_button` | No trim-specific behavior. | | `next_frame` | Goes to the next frame and ignores `trim_end_frame`. | | `play_forward` | Plays forward to `trim_end_frame`. | | `last_button` | First press goes to `trim_end_frame`; if already on `trim_end_frame`, second press goes to the last frame. | ## Tracer Lambda Behavior Rename user-facing and API terminology from tracker/tracking to tracer/tracing as part of the implementation. The internal algorithm can be renamed in the same change set. The tracer endpoint should accept: - `movie_id` - `frame_start` - optional `frame_end` `frame_start` remains the edited source frame. Tracing resumes after that source frame. When `frame_end` is provided, tracing stops after producing trackpoints for `frame_end`. When `frame_end` is omitted, tracing continues to the physical end of the movie. The trim implementation should call the tracer with `frame_end = trim_end_frame` when applying trim. It should not introduce arbitrary mid-movie trace ranges beyond the current "from source frame forward" model. ## Outputs Graphs only include frames from `trim_start_frame` through `trim_end_frame`. CSV and JSON trackpoint downloads only include frames from `trim_start_frame` through `trim_end_frame`. The traced MP4 artifact is generated by the tracer and can be downloaded when `movie_traced_urn` is present. It includes only frames inside the traced trim range and renders the current marker locations and accumulated path lines through each frame. Marker edits after tracing set `needs_retracing = 1` because the stored traced MP4 may no longer match the current trackpoints. ## Unresolved Questions - Should Plant Tracer require retracing before allowing traced MP4 download after markers are moved, or should it allow users to download the last traced artifact with a visible stale warning? ## Implementation Checklist - Add `trim_start_frame` and `trim_end_frame` constants. - Add both fields to the pydantic `Movie` model. - Add both fields to movie metadata integer coercion. - Ensure `/api/get-movie-metadata` returns defaults for the movie analyzer without automatically persisting missing trim attributes. - Add a trim update endpoint for `Set start` and `Set end` that validates cross-field constraints before persisting the requested bound. - Preserve stored trackpoints outside the trim range. - Implement the required seed behavior when moving `trim_start_frame` earlier. - Update the Analyze page template with trim controls. - Update the JavaScript controller to initialize default trim values if missing. - Update movie navigation, playback, out-of-range overlay, and interaction guards. - Update graph generation and trackpoint download requests to honor trim bounds. - Rename Lambda/user-facing tracker terminology to tracer terminology. - Update the tracer endpoint, queue payload, local worker, and tracing function to accept an optional end frame. - Add focused tests for metadata defaults, validation, out-of-range trackpoint preservation, seed-marker copy, navigation behavior, graph filtering, CSV filtering, and tracer end-frame handling.