Files
oak-editor/crates/oak-cli
Mike-Solar 061aab9180 fix(app): full-res worker crash + source playback frozen + cli/worker DS batch
- full-res source jobs carried only the footage node box: dropping the
  project mid-flight left the node dangling (crash in the worker's free
  path) and the node was also freed twice (at renderer creation AND at
  release). The request now carries an addref'd project copy and the
  node is freed exactly once; regression test drops the project before
  the worker runs
- the source clock ticked at the SEQUENCE length, so playing footage
  with an empty sequence froze the source playhead at 0; the source
  clock now loops at the selected footage's probed duration
- oak-cli/oak-worker: DeepSeek's refactor batch (clap migration, engine
  FFI consumers); the stale exporter-family test flipped to the real
  contract (mp4 is written)
- engine: render_audio smoke test on an empty sequence
2026-08-16 19:07:07 +08:00
..

oak-cli (Rust)

Headless command-line consumer of the liboakengine C ABI facade — the Rust rewrite of cli/main.cpp (which stays in the tree until cutover). Same subcommands, same output format, same exit codes:

exit meaning
0 success
1 general error (bad project/media file, no sequence, I/O failure)
2 rendering unavailable or failed (e.g. no GL render backend)
64 usage error

Build and test

cargo build --release      # binary: target/release/oak-cli
cargo test                 # unit + integration tests (29 tests)

The crate builds standalone: its only dependency besides clap is the oakengine rlib (../oakengine), which has no third-party dependencies.

Subcommands

Every subcommand of the C++ original is implemented:

oak-cli info <project.ove> <start> <end> <out_dir>   project name/sequences/footage
oak-cli render <project.ove> <start_seconds> <end_seconds> <out_dir>
oak-cli probe <mediafile>
oak-cli transcode <input_media> <out> [width] [--format ppm|mp4]

Argument validation is faithful to the C++ (invalid start seconds, invalid width, unknown --format … all exit 64). The output formatters (src/fmt.rs) reproduce the C++ printf output byte for byte and are golden-tested against the output captured from the C++ binary on the test fixtures (tests/project_with_footage.ove, tests/demo.mp4); the PPM and WAV writers (src/ppm.rs, src/wav.rs) are the exact ports of the C++ write_ppm/write_wav and are unit-tested.

Facade status: everything is currently deferred

All four subcommands depend on facade families that are still deferred in the oakengine crate (crates/oakengine/src/deferred.rs), so today each subcommand validates its arguments, then prints a clear "not yet available" error naming the missing families and the reasons, and exits with the C++-compatible code — it never crashes and never fakes output:

subcommand needs current behavior
info init + node (project/footage) + timeline "not yet available", exit 1
probe init + node (footage) "not yet available", exit 1
render init + node + timeline + render "not yet available", exit 2
transcode init + node + timeline + render + exporter "not yet available", exit 2

The deferral registry is src/deferred.rs (field-for-field in sync with the facade's own deferred.rs). When a family is wrapped by the facade:

  1. remove its entry from src/deferred.rs,
  2. wire the call-through in src/cmd/ using the extern declarations in src/ffi.rs (verbatim mirrors of the engine headers) and the tested formatters/writers — no manifest or signature change is needed, because the externs resolve against the already-linked oakfacade rlib.

Layout

src/
  main.rs       clap surface, --help/-h + unknown-command handling, dispatch
  ffi.rs        the oakengine_* surface oak-cli consumes (declarations only)
  deferred.rs   facade-family availability registry (mirror of facade deferred.rs)
  fmt.rs        golden output formatters (info/probe)
  ppm.rs        P6 PPM writer (f32/u8 frames)
  wav.rs        PCM s16 WAV writer (interleaved float samples)
  cmd/          per-subcommand validation + deferred gate
tests/cli.rs    binary-level tests (exit codes, messages, usage errors)