Import a playblast as a previz version (#159) #169

Merged
crowmaki merged 2 commits from issue-159-import-playblast into main 2026-10-10 22:54:06 +00:00
Owner

Closes #159. Part of #139.

What changed

  • V46 previz_version: an append-only record per Shot. It holds the number, content hash, path, duration, frame count, pixel size, arrival (imported / picked_up) and time. An update trigger guards it like Reference versions. missing_at marks a file that went missing outside the app, as on a Rendition. shot_id has no foreign key, because undo can bring a deleted Shot back, the same as keyframe jobs.
  • One ingest command (ingest_playblast; import_playblast is the public entry). It copies the file into playblasts/<blake3>.<ext> in the media folder and hashes it in the same pass. It then probes the copy with ffmpeg (framecrc over a stream copy, which gives an exact frame count, duration and size without decoding). It refuses anything outside 2–15 s with a clear message and keeps nothing. It adds the version as one undoable edit ("Import playblast"). The pickup ticket can call it with PickedUp.
  • Status: the current previz is the newest version that has its file. A complex Shot's previz reads current or mismatched against its frame count and effective aspect ratio as the Shot is now, so changing the ratio or splitting the Shot updates the dot. Unmarking keeps the versions, and marking again brings them back. A split leaves them on the first part, which keeps the Shot's id.
  • Storage: playblast files are never offered as orphan files, so Purge can't take a kept version. When Storage opens it marks missing Playblasts, as it does for Renditions.
  • Tauri: shot_previz and import_playblast commands, and studio:previz/<id>, which streams with Range support and shares serve_video with Renditions.
  • Frontend: ShotPreviz.svelte sits in a complex Shot's panel. It has "Import playblast…", a player for the current playblast and the version list (newest first, with times and Current / Missing / Mismatched badges). The dot from #158 now shows real statuses.

Tests

  • crates/studio-core/tests/previz.rs has 16 new integration tests. Their playblasts come from the bundled ffmpeg. They cover:
    • append-only versions where the newest is current
    • copies the source file can't change
    • refusal under 2 s and over 15 s, of non-videos and of Shots that aren't complex
    • length and ratio mismatches
    • undo and redo as one edit
    • unmarking and re-marking a Shot complex
    • splitting
    • reopening the Project
    • missing files
    • Storage orphans
  • One bun test for the playblast caption helper.
  • Passing: cargo test -p studio-core --features test-support, cargo test -p ai-studio, bun run test and bun run check.

Notes

  • The Shot's dot reads missing_at, so a playblast deleted outside the app keeps counting for the dot until Storage opens. The previz section and the player check the disk and see it at once. Renditions behave the same way.
  • Undoing an import leaves the copied file. It shows up in Storage as an orphan, as a Rendition's leftovers do.
  • cargo test --workspace doesn't build scene_cast without --features test-support, and that was already so on main.
  • I haven't clicked through the import in the running app.
Closes #159. Part of #139. ## What changed - **V46 `previz_version`**: an append-only record per Shot. It holds the number, content hash, path, duration, frame count, pixel size, arrival (`imported` / `picked_up`) and time. An update trigger guards it like Reference versions. `missing_at` marks a file that went missing outside the app, as on a Rendition. `shot_id` has no foreign key, because undo can bring a deleted Shot back, the same as keyframe jobs. - **One ingest command** (`ingest_playblast`; `import_playblast` is the public entry). It copies the file into `playblasts/<blake3>.<ext>` in the media folder and hashes it in the same pass. It then probes the copy with ffmpeg (`framecrc` over a stream copy, which gives an exact frame count, duration and size without decoding). It refuses anything outside 2–15 s with a clear message and keeps nothing. It adds the version as one undoable edit ("Import playblast"). The pickup ticket can call it with `PickedUp`. - **Status**: the current previz is the newest version that has its file. A complex Shot's `previz` reads `current` or `mismatched` against its frame count and effective aspect ratio *as the Shot is now*, so changing the ratio or splitting the Shot updates the dot. Unmarking keeps the versions, and marking again brings them back. A split leaves them on the first part, which keeps the Shot's id. - **Storage**: playblast files are never offered as orphan files, so Purge can't take a kept version. When Storage opens it marks missing Playblasts, as it does for Renditions. - **Tauri**: `shot_previz` and `import_playblast` commands, and `studio:previz/<id>`, which streams with Range support and shares `serve_video` with Renditions. - **Frontend**: `ShotPreviz.svelte` sits in a complex Shot's panel. It has "Import playblast…", a player for the current playblast and the version list (newest first, with times and Current / Missing / Mismatched badges). The dot from #158 now shows real statuses. ## Tests - `crates/studio-core/tests/previz.rs` has 16 new integration tests. Their playblasts come from the bundled ffmpeg. They cover: - append-only versions where the newest is current - copies the source file can't change - refusal under 2 s and over 15 s, of non-videos and of Shots that aren't complex - length and ratio mismatches - undo and redo as one edit - unmarking and re-marking a Shot complex - splitting - reopening the Project - missing files - Storage orphans - One bun test for the playblast caption helper. - Passing: `cargo test -p studio-core --features test-support`, `cargo test -p ai-studio`, `bun run test` and `bun run check`. ## Notes - The Shot's dot reads `missing_at`, so a playblast deleted outside the app keeps counting for the dot until Storage opens. The previz section and the player check the disk and see it at once. Renditions behave the same way. - Undoing an import leaves the copied file. It shows up in Storage as an orphan, as a Rendition's leftovers do. - `cargo test --workspace` doesn't build `scene_cast` without `--features test-support`, and that was already so on main. - I haven't clicked through the import in the running app.
A complex Shot takes a Playblast imported by hand as its next previz
version (V46): an append-only record of the video's content hash,
length, frame count, pixel size and how it arrived, with the video
copied content-hashed into playblasts/ in the media folder. One ingest
command probes the copy with ffmpeg, refuses anything outside 2-15 s,
and adds the version as one undoable edit; later tickets reuse it for
pickup. The current previz is the newest version whose file is there,
and the Shot's previz status reads current or mismatched against its
frame count and effective aspect ratio. Storage marks missing
Playblasts and never offers one as an orphan file. The Shot panel gets
a previz section with Import playblast..., a player for the current one
and the version list, served from studio:previz/<id>.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
crowmaki deleted branch issue-159-import-playblast 2026-10-10 22:54:06 +00:00
Sign in to join this conversation.
No description provided.