Client Lambda API¶
The browser calls the lambda-resize HTTP API for operations that require video or frame access. Flask owns HTML and metadata APIs; lambda-resize owns first frame extraction, playback URL generation, and retracing.
See ArchitectureDesign.md for the current service boundary.
Lambda API Base URL¶
Flask injects the Lambda API base URL into pages as the browser global
LAMBDA_API_BASE.
Template:
src/app/templates/base.htmlServer:
src/app/apikey.py, viaget_lambda_api_base()Local override:
PLANTTRACER_LAMBDA_API_BASE=http://127.0.0.1:9811/Deployed same-origin stack:
https://{stack}.planttracer.com/Fallback when no explicit base is configured:
https://{HOSTNAME}.{DOMAIN}/, or the current request origin when those variables are absent.
All client calls are authorized. The browser sends the current api_key; the
Lambda validates it against DynamoDB.
Static JavaScript and CSS are not part of LAMBDA_API_BASE; they are served
same-origin by Flask/lambda-web under /static/* until there is a versioned
asset plan for external static hosting.
Endpoints¶
Operation |
Method |
Path |
Auth |
Purpose |
|---|---|---|---|---|
Ping |
GET |
|
none |
Health check; returns |
Complete upload (local adapter) |
POST |
|
|
Completes a MinIO staging upload through the same service used by the AWS EventBridge handler. |
Finish camera capture |
POST |
|
|
After STOP and all frame uploads finish, assembles ordered JPEG frames into the durable source MP4 and starts normal movie processing. |
First frame |
GET |
|
query |
Returns JPEG frame 0 with saved rotation applied and scaled to the analysis size. |
Movie data |
GET |
|
query |
Returns signed playback/download URLs as JSON. |
Movie data redirect |
GET |
|
query |
302 redirect to signed movie URL. |
Movie zip redirect |
GET |
|
query |
302 redirect to signed frame ZIP URL if present. |
Trace movie |
POST |
|
|
Queues retracing from a user-edited source frame through an optional end frame. |
Complete Upload Request¶
POST /resize-api/v1/process-upload
x-api-key: <api_key>
Content-Type: application/json
{ "movie_id": "m..." }
This is a local-development compatibility adapter for MinIO. Deployed browsers
do not call it; S3 Object Created events reach lambda-resize through
EventBridge. The caller must be allowed to edit the movie. Lambda-resize reads
the staging object with HeadObject, rejects a missing object or a byte count
different from upload_bytes_expected, copies it to the durable key, records
upload metadata, deletes staging, and starts post-upload processing. Read-only
superauditors cannot complete or otherwise mutate movies; superadmins can.
The EventBridge invocation is not a public HTTP endpoint. Its Pydantic envelope validation additionally checks the event source/type, bucket, deployment prefix, course/movie identifiers, and corresponding DynamoDB row.
Finish Camera Capture Request¶
POST /resize-api/v1/finish-camera
x-api-key: <api_key>
Content-Type: application/json
{ "movie_id": "m..." }
The caller must have edit access to a camera movie still in uploading
state. The service verifies that uploaded frame numbers are contiguous from
zero, claims the movie’s processing lease, and queues camera assembly. A
concurrent STOP that loses the lease claim returns HTTP 409. The
worker encodes the frames into the durable source MOV/MP4 object, writes capture
and research attribution metadata, records frame and byte counts, and invokes
normal post-upload processing. After processing succeeds, it removes the
temporary camera JPEGs; retries repeat this cleanup safely. Invalid credentials
or missing movie access return HTTP 403, while missing frames or a movie that is
not accepting STOP return HTTP 409. Local development processes immediately
unless local async queue mode is enabled; deployed stacks publish the job to
EventBridge.
Trace Movie Request¶
POST /resize-api/v1/trace-movie
Headers:
x-api-key: <api_key>
Content-Type: application/json
Body:
{ "movie_id": "m...", "frame_start": 12, "frame_end": 200 }
frame_start is the edited source frame. Analyze saves its visible markers before
queuing. Plant Tracer requires those markers, preserves that frame, clears stored
trackpoints after it through frame_end when supplied, marks the movie as
tracing, and dispatches work. In local mode the work goes to the
in-process queue; in deployed mode a stack-scoped EventBridge rule pushes the
custom work event to lambda-resize without idle polling.
Rejected source frames return HTTP 403 with JSON error and message fields
that Analyze displays to the user. If the source becomes empty after the
request consumes its Analyze lease, the response also includes
lease_reacquire_required: true; Analyze becomes read-only until reopened.
If publishing the queued work fails, the API releases a lease that has not yet
been claimed and returns HTTP 503 with the same reacquire flag. A worker that
already claimed the job retains its lease.
An HTTP 409 for a request carrying an Analyze lease also includes the flag;
this covers retries whose first response was lost after consuming that lease.
Failures before trace-lease acquisition return HTTP 503 without that flag;
Analyze keeps its current editing lease.
After tracing completes, Analyze reacquires an editing lease before enabling
marker edits or Reset Trace again.
Local Development¶
Use the Makefile instead of hand-built commands:
make run-local-lambda-debug
make run-local-debug
run-local-lambda-debug starts a Flask bridge that converts local HTTP requests
into API Gateway v2 events and calls resize_app.main.lambda_handler().