Add the prober: run ffprobe on a presigned URL and classify the result #4

Merged
sophie merged 5 commits from probing into main 2026-10-16 20:54:37 +11:00
Owner

Adds Prober, which runs ffprobe -show_format -show_streams on a presigned URL and returns a MediaProbeSubmission, or throws ProbeError for a transient failure. The loop that calls it comes in the next PR.

  • Classification: a clean exit with complete JSON is identified. Any HTTP or connection error is transient, even when ffprobe also says "Invalid data" (an expired URL during the seek to a trailing moov atom). Other "Invalid data" and unusable JSON are permanent. Anything not recognised is transient. Error messages have the URL stripped, because it carries the presigning signature.
  • Timeout: new PROBE_TIMEOUT_SECONDS (default 120). ffprobe is killed when it runs out, and that counts as transient.
  • Bytes read: ffprobe reads 64–230 KB of the 3–36 MB seed clips, including one with its moov atom at the end. No -probesize needed.
  • Check: hdr is null rather than false when no colour transfer is tagged.

Tests run real ffprobe on clips generated with ffmpeg (skipped without it), including one probe over an in-process Range server. The Dockerfile's test stage now installs ffmpeg so they also run on Linux.

🤖 Generated with Claude Code

Adds `Prober`, which runs `ffprobe -show_format -show_streams` on a presigned URL and returns a `MediaProbeSubmission`, or throws `ProbeError` for a transient failure. The loop that calls it comes in the next PR. - **Classification:** a clean exit with complete JSON is identified. Any HTTP or connection error is transient, even when ffprobe also says "Invalid data" (an expired URL during the seek to a trailing `moov` atom). Other "Invalid data" and unusable JSON are permanent. Anything not recognised is transient. Error messages have the URL stripped, because it carries the presigning signature. - **Timeout:** new `PROBE_TIMEOUT_SECONDS` (default 120). ffprobe is killed when it runs out, and that counts as transient. - **Bytes read:** ffprobe reads 64–230 KB of the 3–36 MB seed clips, including one with its `moov` atom at the end. No `-probesize` needed. - **Check:** `hdr` is `null` rather than `false` when no colour transfer is tagged. Tests run real ffprobe on clips generated with ffmpeg (skipped without it), including one probe over an in-process Range server. The Dockerfile's `test` stage now installs ffmpeg so they also run on Linux. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A clean run with complete JSON is identified; any network or HTTP error
on stderr is transient even when ffprobe also says "Invalid data";
otherwise unusable JSON or "Invalid data" is permanent and anything
else is retried. Messages are stripped of the input URL.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Prober runs ffprobe as a subprocess, drains both pipes, kills it after
the timeout (transient) and classifies the run. Tests generate tiny
clips with ffmpeg (skipped without it) and probe one over an in-process
Range server: ffprobe fetches about 2% of a 26 MB file whose moov atom
is at the end.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
sophie merged commit 4bac455325 into main 2026-10-16 20:54:37 +11:00
sophie deleted branch probing 2026-10-16 20:57:12 +11:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
sophie/simpleplayer-helper!4
No description provided.