Entities¶
The pipeline revolves around three entity types: Shot, Asset, and Edit. All share the base Entity class.
Entity types¶
| Type | Description |
|---|---|
Shot |
A single camera shot within a sequence |
Asset |
A reusable asset (character, prop, environment) |
Edit |
An editorial cut or assembly |
What Entity provides¶
- Workfile management — versioned DCC project files stored under
work/{task}/ - Product management — versioned published outputs under
publish/{type}/v{version}/ - Metadata persistence — each entity directory contains a
kpipe-metadata.tomlthat tracks workfile and product history
Entities are identified by type, parent name and name: two Shot("010", "020") objects compare equal and hash alike. The production info (tasks, casting, frame range, …) is loaded from the production manager on first use, so creating many entities (e.g. get_shots()) is cheap.
Metadata file¶
[work]
tasks = ["comp"] # tasks with workfiles
[[work.comp]] # one entry per saved workfile
task = "comp"
version = 3
file-format = "blend"
layer = "" # "" = main layer
# dcc, date, note, source
[publish]
product-types = ["plate"] # product types with published versions
[publish.plate]
layer-names = ["main", "fx"] # layers with published versions
[[publish.plate.main]] # one entry per version
version = 1
file-type = "sequence"
file-format = "exr"
layer = ""
# source, dcc, date, note; optional frame-start/-end/-step, fps, maps, colorspaces, formats, media, extra
Reading — the Entity getters (get_workfiles, get_product_*, …) read through a process-wide cache of parsed files. Each read checks the file's stamp (st_mtime_ns, st_size, st_ino) and parses only when it changed, so writes from other processes (other artists, render farm) show up on the next read. Getters return copies; modifying them doesn't affect the cache. clear_metadata_cache() drops everything.
Change detection — entity.get_metadata_stamp() returns a token for the file's current state (None if it doesn't exist); entity.has_metadata_changed(stamp) tells whether it changed since, e.g. to refresh a UI.
Writing — change the data only within with EntityData(entity).edit() as data:. It holds a soft file lease (works across machines on the project share) while reloading the file from disk, applying the changes and saving; nothing is saved if the block raises. Files are written to a temporary file and moved into place, so readers never see a partial file. Removing the last version of a layer also removes the layer from layer-names, and the product type once it has no layers.
Version constants¶
entity.py defines five sentinel values used wherever a version number is expected:
| Constant | Value | Meaning |
|---|---|---|
VERSION_MAIN |
0 |
The un-versioned "main" slot (path uses vmain instead of v001) |
VERSION_LATEST |
-1 |
Resolve to the highest existing version |
VERSION_NEW |
-2 |
Resolve to the highest existing version + 1 (create a new version) |
VERSION_REPLACE_LATEST |
-3 |
Publish only: replace the files of the latest version (or write v001 if none exists) |
VERSION_EXTEND_LATEST |
-4 |
Publish only: keep the latest version's files and add the new ones; same-named files are overwritten (or write v001 if none exists) |
Entity exposes two resolution helpers that convert these sentinels to a concrete integer:
resolve_product_version(product_type, layer_name, version)— for published productsresolve_workfile_version(task, filter, version)— for workfiles
Pass a concrete version number to either helper and it is returned unchanged, making the helpers safe to call unconditionally. Both use the module function resolve_version(version, latest), which applies the rules of the table above to a given latest version.
The main layer is main (LAYER_MAIN_NAME); None and "" mean the same. Entries store it as layer = "", tables and layer-names use main.
parse_version(value) converts a number, numeric string or version name (main, latest, new, replace_latest, extend_latest; see VERSION_NAMES) to its int value, so configs and settings can use readable names.
The publish version is resolved once by the CollectPublishVersion action into ctx.version (concrete, for the pipeline's layer) and ctx.version_mode (default VERSION_NEW, i.e. create the next version). IntegrateMedia sets MediaPipelineData.version from ctx.get_publish_version(data.layer) and passes the mode to publish_*() (version_mode=), which decides whether an existing version directory is cleared or extended.
File naming convention¶
| Type | Pattern |
|---|---|
| Workfile | {project_key}-{sequence}_{shot}-{task}-v{version}.{ext} |
| Product | {project_key}-{sequence}_{shot}-{product_type}-v{version}.{ext} |
Templates are defined in the [anatomy] config section and resolved by path.py. Optional path segments use the </{segment}> syntax — if the segment is empty, the whole bracketed portion is dropped from the resolved path.