Skip to content

Supplied media: landing, filing and binding

Use bin/studio_media.py to import an existing file, file an unbound production-inbox item, bind existing provenance to a registered shot, or recover an interrupted landing. These commands never generate media, contact providers, or infer a verdict. They require an explicitly initialized workspace and writable scene production. Neither app, Pillow nor Node is needed.

Create an address request file such as address.json:

{"shot":"S1","kind":"take","version":1,"by":"session","reason":"Supplied rehearsal fixture"}
python bin/studio_media.py --workspace C:/StudioWorkspace land my-film C:/Fixtures/take.png address.json --operation-id supplied-take-01
python bin/studio_media.py --workspace C:/StudioWorkspace status my-film --operation-id supplied-take-01
python bin/studio_media.py --workspace C:/StudioWorkspace resume my-film --operation-id supplied-take-01
python bin/studio_media.py --workspace C:/StudioWorkspace bind my-film S1 06-takes/S1/v1/S1_v1_t01.png --operation-id bind-01 --reason "Explicit shot binding"

The paths in these examples are the numbered layout's. On the plain layout the same take is shots/S1/S1_v1_t01.png and the production inbox is work/inbox/ (docs/WORKSPACE.md § The two layouts).

Sources outside the production must be absolute paths and are copied. Relative sources resolve inside the named production. A request without shot lands in the production's inbox slot (00-inbox/<original-name> numbered, work/inbox/<original-name> plain); use kind: work or ref for supplied work/reference material. Addressed media uses the existing landing resolver for take/plate names. shot must already be registered; version is a positive integer (default 1). Omit take to allocate the next available take under the production lock, accounting for incomplete reservations. An explicit occupied take, plate version, inbox name, sidecar or workflow address refuses; no file is silently replaced or renamed over another.

For an unbound source already in this production's inbox slot, add --move-inbox to remove the source after durable filing. It refuses external or already bound sources and sources referenced by a registry or saved cut. The Studio app's existing “file to shot” operation uses this path. Its optional operation_id and expected inputs follow the same retry contract. The app does not need to run for any command above.

Existing top-level provenance and unknown fields travel with the file. The studio address and explicit binding attribution are updated; an existing verdict is retained, and absent verdicts remain unjudged. Workflow graphs travel beside the media and their workflow.graph filename follows the destination. Missing, unsafe or ambiguous graphs refuse. Shared source sidecars and workflow files are retained because sibling outputs may depend on them. Importing media without a sidecar creates address/binding metadata; it does not invent provider, prompt, cost or generation receipts.

Take allocation also reads explicit studio addresses throughout the requested shot's takes tree (06-takes/<shot>/ numbered, shots/<shot>/ plain). A legacy file can retain its original provider filename and old version directory for cut/script compatibility while its sidecar records the correct version. That number remains occupied, including when only the sidecar survives. land refuses an explicit occupied number even when its proposed filename differs. Malformed addressed take numbers refuse allocation. Historical direct writers remain outside the transaction lock.

bind changes only the chosen media's own sidecar, using the existing shot-binding validator. The file does not move. Shared fallback provenance is copied into its own sidecar so siblings remain unchanged. Use the take-decision commands for verdict/live selection; a binding does not approve a take or plate. Landing does not automatically close a phase gate or consume a legacy pending-generation declaration. Those existing provider-sync and gate workflows remain explicit, with the original matching resolver available to their callers.

Recovery

Every landing has a durable .studio/landings/<operation-id>.json record and stages hashed bytes under the matching directory. prepared means the reserved operation still needs byte/metadata completion; filed means destination metadata/history committed but source cleanup remains; completed means its requested steps finished. Status only reads. Resume is explicit. A completed status is the recorded outcome, not a guarantee that nobody later edited or removed the file.

Copies use unique temporary files, streamed SHA-256 checks, flush/fsync, then a local hard link that refuses replacement. Companion bytes install first. A transaction commits the final sidecar, landing state and history. For moves, all remaining source hashes and destination provenance are checked before cleanup; changed source bytes refuse deletion. Exact retries use the same request, operation ID and optional --expected JSON revision map. They reuse the original address and recorded outcome, even after the source was removed. A different request using that ID is a conflict.

Partial installations may be visible to readers after a crash. Their prepared record and retained source/staging bytes make them recoverable; multi-file visibility is not instantaneous. Invalid/colliding state remains in place for reconciliation. Staging and interrupted copy evidence are retained deliberately; this command does not auto-delete recovery evidence. Local hard-link support is required on the destination filesystem. External sources must remain immutable during import; other programs and historical direct writers do not share this lock. Network-share durability is not claimed.

Legacy landing.file_media, output savers, file_renders.py migration/reconciliation and low-level shots.py bind retain their existing workflows; they are outside the complete cross-process recovery contract above. This command does not migrate live productions or retire the legacy inbox.

Tests: tests/studio/test_media_operations.py, the relevant Studio HTTP cases, tests/studio/test_landing.py, tests/studio/test_reconcile.py, and the existing shot/boundary suites. All landing tests use disposable synthetic bytes.