--progress jsonl — machine-readable progress events
map, photomap, flightmap, embed, check, doctor, convert,
validate, verify-sun, and fetch-log accept --progress jsonl. In
this mode a command writes one JSON object per line to stdout and nothing
else — human/informational output is suppressed, and warnings and log
messages go to stderr. A non-zero exit code always means the run failed;
for success, read the result event's ok field (see the embed nuance
under the terminal rule below). Lines are pure ASCII (non-ASCII text is
JSON-escaped and round-trips intact through any JSON parser), so the stream
is safe to read under any pipe encoding. This is the interface for frontends
and scripts:
dji-embed flightmap D:\Drone\trip --progress jsonl
{"v":1,"event":"start","command":"flightmap"}
{"v":1,"event":"progress","current":1,"total":3,"item":"DJI_0001"}
{"v":1,"event":"progress","current":2,"total":3,"item":"DJI_0002"}
{"v":1,"event":"progress","current":3,"total":3,"item":"movie"}
{"v":1,"event":"warning","message":"No GPS telemetry","item":"movie"}
{"v":1,"event":"result","ok":true,"outputs":["D:/Drone/trip/flightmap.html"],"summary":{"flights":2,"skipped":1,"joined_files":0}}
Stability
- Every event carries
"v": 1. Additive changes (new fields, new event types) do not bumpv; consumers must ignore fields and event types they do not recognise. Breaking changes bumpv. - The machine-checkable contract lives in
progress_jsonl.schema.json(JSON Schema, draft 2020-12, validates any single event line). The test suite validates every emitted line against it.
Events
| event | fields | meaning |
|---|---|---|
start |
command (required), total (optional) |
First event of every run. total is omitted when the item count is not known up front (render an indeterminate progress bar). |
progress |
current, total (required), item (optional) |
One item finished being picked up for processing. current counts from 1 to total. |
warning |
message (required), item (optional) |
Non-fatal problem; the run continues. |
result |
ok, outputs, summary (all required) |
Terminal event of a run that completed. outputs = absolute paths of files written (may be empty). summary is command-specific (below). |
error |
message (required), item (optional) |
Terminal event of a run that failed; the process exits non-zero. No result follows. |
Terminal rule: a stream starts with start and ends with exactly one of
result or error — including on unexpected internal failures, which
surface as an error event before the non-zero exit. With one documented
nuance: embed processes files independently and keeps its existing
exit-code behaviour, so a run where some files failed still exits 0 and
ends in result with "ok": false (per-file problems are warning events;
the counts are in summary). For embed, therefore, treat result.ok —
not the exit code — as the success signal. For every other command result
implies "ok": true.
Warnings are not live: warning events for skipped items are typically
emitted after the scan completes (once the skipped set is known), not
interleaved with the progress events. Attribute them by their item
field, never by arrival order.
Per-command notes
map
- One
progressevent per.SRTfile scanned, exactly likeflightmap— the photo scan is a single batch ExifTool call and emits none;startcarries nototal(the file count is discovered during the scan — taketotalfrom the firstprogressevent). - One
warningper photo without GPS (messageis"No GPS data", the file name is initem) and one per SRT without GPS telemetry (messageis"No GPS telemetry", the file name is initem), both emitted after their respective scans complete. summary:{"photos": N, "flights": N, "skipped": N}—skippedis the combined count of GPS-less photos and telemetry-less SRTs.--servecannot be combined with--progress jsonl(serving blocks forever; frontends open the written HTML themselves), same rule asphotomap.
flightmap
- One
progressevent per.SRTfile scanned;startcarries nototal(the file count is discovered during the scan — taketotalfrom the firstprogressevent). - One
warningper SRT without GPS telemetry (messageis"No GPS telemetry", the file name is initem), emitted after the scan. summary:{"flights": N, "skipped": N, "joined_files": N}—joined_filescounts source files that were chained into multi-segment flights.
photomap
- No
progressevents in v1 (the photo scan is a single batch ExifTool call);starthas nototal. Expectstart→ warnings →result. summary:{"photos": N, "skipped": N}(mapped vs no-GPS).--servecannot be combined with--progress jsonl(serving blocks forever; frontends open the written HTML themselves).
embed
- One
progressevent per video file. summary:{"processed": N, "total": N, "warnings": N, "errors": N, "output_directory": "..."};outputs=[output_directory].okisfalsewhen any file errored (see terminal rule above).
check
- One
progressevent per path argument. outputsis empty.summary:{"checked": N, "files": {"<path>": {<metadata flags>}}}— the per-file object ischeck's existing detection result:{"gps": bool, "altitude": bool, "creation_time": bool}. A path that does not exist or cannot be read yields{}and awarningevent ("Not found or unreadable") — the run still ends inresultwith"ok": true.- A directory argument stands for its top-level media files
(
*.mp4/*.mov/*.jpg/*.jpeg/*.dng, case-insensitive, no recursion):start.total, theprogressevents andsummary.filesall name the expanded files, never the directory itself. A directory with no media files contributes nothing and yields onewarningevent ("No media files found",item= the directory) — the run still ends inresultwith"ok": true. A directory that cannot be listed (e.g. a permissions error) is handled the same way: it contributes nothing and yields onewarningevent ("Not found or unreadable",item= the directory) rather than aborting the run.
doctor
- No
progressevents. Onewarningper missing tool (item= tool name).outputsis empty.summary:{"ok": bool, "tools": {"ffmpeg": {"present": bool}, "exiftool": {"present": bool, "version", "source", "path", "decode"}}, "system": {...}}— the exiftool extras appear only when it is present and its version is readable. - Missing dependencies are a report, not a crash:
"ok": falsewith exit code 0 (same reading rule asembed). summary.update_check(#319):{"consent": true|false|null, "hard_disabled": bool, "current": "<this version>", "releases_url": "<GitHub releases page>"}. Nobody is prompted, and a plain run never goes online:consentis only the remembereddoctor --online/--offlinechoice (null = never answered). Passing the explicit--onlineflag is the consent — it is persisted exactly as on the interactive path and the check runs, adding"latest": "<PyPI version>"|null,"newer": bool|null(null when offline/PyPI unreachable — never an error) and, when newer,"hint"(the environment-aware upgrade command).--offlinepersists the refusal.DJIEMBED_NO_UPDATE_CHECK=1blocks both the network and the persisting. This is how the desktop app offers the one-time question and the launch note without a second consent store.
convert
- Single-file mode:
startcarriestotal: 1and oneprogressevent fires for the input. Batch mode (-b):starthas nototal(the directory is scanned inside the run); oneprogressevent per candidate file,totalon theprogressevents. - Batch files whose telemetry cannot be read (
Mp4TelemetryError) are skipped with onewarningeach (item= file name); in single-file mode the same failure is fatal (errorevent, non-zero exit). outputs= absolute paths of every file written.summary:{"converted": N, "skipped": N, "format": "gpx"}.
validate
- No
progressevents;starthas nototal. Onewarningper issue found.outputsis empty;summaryis the full validation report (total_files,valid_pairs,issues,warnings,file_analyses). - Findings are a report, not a command failure: a run with issues still
exits 0 and ends in
resultwith"ok": true— readsummary.issues. (Text mode keeps the non-zeroVALIDATION_ERRORexit for scripts.) --format jsoncannot be combined with--progress jsonl(theresultsummary already carries the full report).
verify-sun
- No
progressevents. Onewarningper analysis flag (night,very_low_sun,sun_not_computable— the flag name is themessage). Flags are findings about the footage, not failures:"ok": true. outputsis empty;summaryis the sun summary dict (file,points,sun_computed,utc_start/utc_end, elevation/azimuth stats,flags).--format jsoncannot be combined with--progress jsonl.
fetch-log
--progress jsonlrequires--yes: the consent prompt cannot be answered under jsonl (there is no stdin round-trip in the contract), so consent must be obtained by the frontend before invoking the command; omitting--yesis a usage error. It also requiresFLIGHTREADER_API_KEYto already be set in the environment — there is no key prompt under jsonl either.- One
progressevent is not emitted per record; records that already have a cached CSV are skipped silently and counted insummary.cached. - A successful run — including a run where every record was a cache hit and
nothing was uploaded — ends in
resultwithoutputs= absolute paths of every available CSV (both previously cached and newly fetched) andsummary:{"fetched": N, "cached": N, "failed": 0}.failedis always0in aresultevent: any per-record failure instead ends the run inerror(see next point), so a non-zerofailednever reachesresult. - A record that fails to fetch (e.g. the API rejects it) is reported as a
warningevent (item= the record's file name) and the run continues to the remaining records; if any record failed, the stream still terminates with a singleerrorevent (notresult) and a non-zero exit code, per the terminal rule.
Relation to --log-json
--log-json (env DJIEMBED_LOG_JSON) is an older, separate feature: it
formats log lines as JSON for the commands that read it (e.g. doctor)
and is independent of this event stream. The progress-wired commands
do not change their logging format based on it; logs are human-readable
text on stderr for every command, with or without --progress jsonl.
Parse stdout events, treat stderr as free-form diagnostics.