Repository navigation
[ar-api] Add CLI video segment annotation (VID-59) - #544
Merged
Merged
Conversation
…e CLI (VID-53) Action Recognition projects take whole videos as Sources, but the CLI had no way to ingest or label one. `roboflow video` only reached the legacy async video *inference* job API. Extend the existing video group with three commands over the already-published SDK methods: - `video upload` streams original MP4/MOV bytes and reports the canonical video ID, waiting for a terminal state by default. - `video upload-status` reads ingestion state. The name keeps it unambiguous against `video status`, which still checks an inference job. - `video annotate` forwards a complete roboflow-video-coco file unchanged, so native frame indices, PTS and rational time bases survive as authored. Upload composes `upload_video(wait=False)` with `wait_for_video_upload` rather than the SDK's internal wait, so a bounded-wait timeout can still name the video ID to re-check. Credential precedence, `--json` schemas and the 0/1/2/3 exit-code contract follow the existing handlers. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…-53) Staging verification showed the import schema puts `segments` at the top level and takes `time_base` as a rational object, with `images`/`annotations` kept empty. The test fixture used a nested `annotations.segments` shape that the API would reject, so it documented something untrue even though the forwarding assertion passed. Rebuild the fixture from the MOV actually submitted to staging. Its presentation timestamps are not frame_index * ticks_per_frame, which is what makes "forwarded unchanged" worth asserting. Also split the upload ValueError hint: a missing path and an unsupported container arrive as the same exception type but need different fixes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ID-53) Upload, upload-status and the project lookup now go through the shared output_api_error helper, so a rejected API key exits 2 and only a real 404 exits 3. get_project carries the HTTP status so the lookup can tell them apart. Fold duplicated video CLI tests into table-driven cases. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…(VID-59) Route annotate rejections through output_api_error so a rejected key exits 2, and give the video-coco hint only to 400s instead of transport failures. Fold the annotate CLI tests into table-driven cases. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Without an explicit encoding, Windows decodes the file with its locale codepage and silently corrupts non-ASCII class names. A file in another encoding (e.g. UTF-16 from PowerShell redirection) now gets an actionable hint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bad --poll-interval/--poll-timeout values and a missing file are now rejected before any network call, so an upload never stores bytes and then reports failure without an ID. Upload API errors follow the exit-code contract, and their hint no longer blames the file when the failing call came after the PUT. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Let typer enforce the poll bounds with min= constraints instead of a handler-side check, collapse the metadata parsing into one error path, and drop CLI tests that only re-checked --help or shared resolver behaviour. Add a direct test for the get_project status code that the exit-code mapping relies on. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…rors Collapse the three annotation-file error branches into one, since decode and JSON errors are both ValueErrors, and drop the --help-only tests that the real dispatch tests already cover. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
click's str(BadParameter) drops the parameter name, so --json callers saw "0.0 is not in the range x>=0.1." with nothing to act on. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
iurisilvio
approved these changes
Oct 9, 2026
1 of 2 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
Action Recognition users can upload a native video from the CLI, but they could not submit its segment labels there. They had to switch to Python for the last step.
roboflow video annotatereads one complete video-coco document and sends it unchanged to the video ID thatvideo uploadreported. Native frame indices, uneven timestamps and the rational time base stay exactly as written. Users can pick a Dataset split, request--overwrite, and turn Dataset membership on or off. Without a membership flag, the API default applies: the video is added.A conflicting save tells the user to re-run with
--overwrite. An unknown video exits 3, and a rejected API key exits 2. The file is read as UTF-8 on every platform, so non-ASCII class names stay intact on Windows.Stack and dependency
Targets
main. This branch contains PR #541 (VID-53). Merge that first and this diff reduces tovideo annotate. The SDK dependency PR #534 is already merged.Changes since reopening
This PR was closed by mistake and revived on 2026-10-09 with
mainmerged in and a simplification pass. The shared upload changes are described in #541.Cannot read annotation file <path>: <cause>, with exit 1. A missing file, non-UTF-8 bytes, malformed JSON and a non-object document all land there, because decode and JSON errors areValueErrors.--help-only tests. The real dispatch tests cover each command.Verification
95bedc2: 1,229 tests pass, one skipped, also with Rich color forced as in CI.ruff check,ruff format --checkandmypy roboflowpass.d95f560: 27/27 passed againstapi.roboflow.one/model-evaluation-workspacewith the installed CLI, in 46 s. That is 11 upload/status cases from [ar-api] Add native video upload and ingestion status to CLI (VID-53) #541 and 16 annotate cases. Segments were read back through the SDK with exact native PTS. The only later commit changes one test assertion. The run's project was moved to Trash and confirmed gone.main.Staging E2E results: annotate (head d95f560, 2026-10-09)
Commands ran as
roboflow --json -k $KEY -w model-evaluation-workspace …(shown without the key and workspace).$Pis a private Action Recognition project the run created and then moved to Trash. Uploads are byte-unique stream copies of real 24 fps clips (time base 1/12288), so no shared Source is touched. The cases are scripted inrun_native_video_e2e.shfrom the e2e-test skill. A and B are the two fresh Sources.a_conflict.jsonmoves one segment's end to frame 14 (end PTS 7680).roboflow video annotate --helproboflow --json video annotate -a $SCRATCH/a.json -i <id> -p $Proboflow --json video annotate -a $SCRATCH/a.json -i <id> -p $Proboflow --json video annotate -a $SCRATCH/a_conflict.json -i <id> -p $Proboflow --json video annotate -a $SCRATCH/a_conflict.json -i <id> -p $P --overwriteroboflow video annotate -a $SCRATCH/a_conflict.json -i <id> -p $Proboflow --json video annotate -a $SCRATCH/b.json -i <id> -p $P --no-add-to-datasetroboflow --json video annotate -a $SCRATCH/b.json -i <id> -p $P --add-to-dataset -s validSDK project.image(<A>) and project.image(<B>)roboflow --json video annotate -a $SCRATCH/a.json -i doesnotexist123 -p $Proboflow --json video annotate -a $SCRATCH/a_empty.json -i <id> -p $Proboflow --json video annotate -a $SCRATCH/bad.json -i <id> -p $Proboflow --json video annotate -a $SCRATCH/list.json -i <id> -p $Proboflow --json video annotate -a $SCRATCH/nope.json -i <id> -p $Proboflow --json video annotate -a $SCRATCH/a.json -i <id> -p no-such-project-xyzroboflow --json -k bogus video annotate -a $SCRATCH/a.json -i <id> -p $PStaging E2E results: upload and upload-status at the same head
roboflow --json video upload -f $SCRATCH/unique-copy.mp4 -p $P --no-waitroboflow --json video upload-status <id> -p $Proboflow --json video upload-status <id> -p $P --wait --poll-timeout 120roboflow --json video upload -f $SCRATCH/unique-copy.mp4 -p $Proboflow --json video upload-status doesnotexist123 -p $Proboflow --json video upload -f $SCRATCH/clip.avi -p $Proboflow --json video upload -f $SCRATCH/absent.mp4 -p $Proboflow --json video upload -f $SCRATCH/unique-copy.mp4 -p no-such-project-xyzroboflow --json -k bogus video upload-status <id> -p $Proboflow --json video upload -f $SCRATCH/unique-copy.mp4 -p $P --poll-interval 0roboflow --json video upload-status <id> -p $P --wait --poll-timeout -1Notes
duplicate: trueand links the existing Source into the new project. Annotating that Source changes every project that holds it. An early E2E pass overwrote one segment of a shared staging project this way. It was restored from the original export and verified, and the E2E now refuses deduplicated uploads.--no-add-to-datasetonly skips adding. It does not remove a video that is already in the Dataset.--helphint. The sharedoutput_errorlets a server hint win for every command, so it is out of scope here.🤖 Generated with Claude Code