--progress jsonl — machine-readable progress events

photomap, flightmap, embed, check, doctor, convert, validate, and verify-sun 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 bump v; consumers must ignore fields and event types they do not recognise. Breaking changes bump v.
  • 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

flightmap

  • One progress event per .SRT file scanned; start carries no total (the file count is discovered during the scan — take total from the first progress event).
  • One warning per SRT without GPS telemetry (message is "No GPS telemetry", the file name is in item), emitted after the scan.
  • summary: {"flights": N, "skipped": N, "joined_files": N}joined_files counts source files that were chained into multi-segment flights.

photomap

  • No progress events in v1 (the photo scan is a single batch ExifTool call); start has no total. Expect start → warnings → result.
  • summary: {"photos": N, "skipped": N} (mapped vs no-GPS).
  • --serve cannot be combined with --progress jsonl (serving blocks forever; frontends open the written HTML themselves).

embed

  • One progress event per video file.
  • summary: {"processed": N, "total": N, "warnings": N, "errors": N, "output_directory": "..."}; outputs = [output_directory].
  • ok is false when any file errored (see terminal rule above).

check

  • One progress event per path argument.
  • outputs is empty. summary: {"checked": N, "files": {"<path>": {<metadata flags>}}} — the per-file object is check's existing detection result: {"gps": bool, "altitude": bool, "creation_time": bool}. A path that does not exist or cannot be read yields {} and a warning event ("Not found or unreadable") — the run still ends in result with "ok": true.
  • A directory argument stands for its top-level media files (*.mp4/*.mov/*.jpg/*.jpeg/*.dng, case-insensitive, no recursion): start.total, the progress events and summary.files all name the expanded files, never the directory itself. A directory with no media files contributes nothing and yields one warning event ("No media files found", item = the directory) — the run still ends in result with "ok": true. A directory that cannot be listed (e.g. a permissions error) is handled the same way: it contributes nothing and yields one warning event ("Not found or unreadable", item = the directory) rather than aborting the run.

doctor

  • No progress events. One warning per missing tool (item = tool name). outputs is 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": false with exit code 0 (same reading rule as embed).
  • The opt-in online update check never runs under --progress jsonl; consent for going online stays interactive-only.

convert

  • Single-file mode: start carries total: 1 and one progress event fires for the input. Batch mode (-b): start has no total (the directory is scanned inside the run); one progress event per candidate file, total on the progress events.
  • Batch files whose telemetry cannot be read (Mp4TelemetryError) are skipped with one warning each (item = file name); in single-file mode the same failure is fatal (error event, non-zero exit).
  • outputs = absolute paths of every file written. summary: {"converted": N, "skipped": N, "format": "gpx"}.

validate

  • No progress events; start has no total. One warning per issue found. outputs is empty; summary is 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 result with "ok": true — read summary.issues. (Text mode keeps the non-zero VALIDATION_ERROR exit for scripts.)
  • --format json cannot be combined with --progress jsonl (the result summary already carries the full report).

verify-sun

  • No progress events. One warning per analysis flag (night, very_low_sun, sun_not_computable — the flag name is the message). Flags are findings about the footage, not failures: "ok": true.
  • outputs is empty; summary is the sun summary dict (file, points, sun_computed, utc_start/utc_end, elevation/azimuth stats, flags).
  • --format json cannot be combined with --progress jsonl.

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.