Create a production and import media

A production is the durable container for everything PostProject knows about a project’s media. An integration typically creates one production file, opens a transaction, imports media, and commits. Nothing staged in the transaction is visible to readers before the commit, and an uncommitted transaction discards its work.

The example below:

  1. creates a production file with an optional display name;

  2. begins a transaction;

  3. attaches an optional revision context, which records which tool made the change and why, so the revision feed can attribute it;

  4. imports one original media file, which creates a logical asset with one single-file representation and returns the new asset ID;

  5. commits the transaction; and

  6. lists the asset’s representations.

static pp_error_code_t create_production(const char *path, const char *media,
                                         pp_production_t **out_production,
                                         pp_uuid_t *out_asset_id,
                                         pp_error_t **error) {
  pp_production_t *production = NULL;
  pp_transaction_t *transaction = NULL;
  pp_representation_set_t *representations = NULL;

  pp_error_code_t status =
      pp_production_create(path, "Documentary", &production, error);
  if (status == PP_OK) {
    status = pp_production_begin_transaction(production, &transaction, error);
  }
  if (status == PP_OK) {
    status = pp_transaction_set_revision_context(
        transaction, "com.example.editor", "0.4.0", NULL,
        "Import camera original", error);
  }
  if (status == PP_OK) {
    status = pp_transaction_import_media(transaction, media, "Camera A",
                                         out_asset_id, error);
  }
  if (status == PP_OK) {
    status = pp_transaction_commit(transaction, error);
  }
  if (status == PP_OK) {
    status = pp_production_representations(production, out_asset_id,
                                           &representations, error);
  }
  if (status == PP_OK) {
    printf("representations: %llu\n",
           (unsigned long long)pp_representation_set_count(representations));
    *out_production = production;
    production = NULL;
  }

  pp_representation_set_release(representations);
  pp_transaction_release(transaction);
  pp_production_release(production);
  return status;
}
std::pair<postproject::Production, postproject::Uuid>
create_production(const std::string &path, const std::string &media) {
  auto production = postproject::Production::create(path, "Documentary");

  auto transaction = production.beginTransaction();
  transaction.setRevisionContext(
      {postproject::OriginIdentity{"com.example.editor", "0.4.0", std::nullopt},
       "Import camera original"});
  const auto asset_id = transaction.importMedia(media, "Camera A");
  transaction.commit();

  std::cout << "representations: "
            << production.representations(asset_id).size() << '\n';
  return {std::move(production), asset_id};
}
def create_production(path: Path, media: Path) -> tuple[Production, AssetId]:
    production = Production.create(path, "Documentary")
    with production.transaction(
        origin=OriginIdentity("com.example.editor", "0.4.0"),
        message="Import camera original",
    ) as transaction:
        asset_id = transaction.import_media(media, display_name="Camera A")

    print(f"representations: {len(production.representations[asset_id])}")
    return production, asset_id
fn create_production(path: &Path, media: &Path) -> Result<(SqliteProduction, AssetId)> {
    let mut production = SqliteProduction::create(path, Some("Documentary".to_owned()))?;

    let import = prepare_original_media(media, Some("Camera A".to_owned()), None)?;
    let asset_id = import.asset().id();
    {
        let mut transaction = production.begin_transaction()?;
        transaction.set_revision_context(RevisionContext::new(
            Some(OriginIdentity::new(
                "com.example.editor",
                Some("0.4.0".to_owned()),
                None,
            )?),
            Some("Import camera original".to_owned()),
        )?)?;
        transaction.import_original(&import)?;
        transaction.commit()?;
    }

    let representations = production.representations(asset_id)?;
    println!("representations: {}", representations.len());
    Ok((production, asset_id))
}
postproject init production.pproj --name "Documentary"
ASSET_ID=$(postproject --json media add production.pproj rushes/A001.mov \
  --name "Camera A" | jq -r .asset_id)
postproject media show production.pproj "$ASSET_ID"

Choose the language with the tabs or with the Code selector in the sidebar; the choice applies to every example on the site and is remembered in this browser.

Handles, errors, and lifetimes

The language surfaces differ only in how they express ownership and failure:

  • The C ABI returns a status code from every fallible function and reports details through an optional error handle. Every handle returned to the caller, including result sets and error handles, must be released with its documented release function. Strings returned by a result-set accessor borrow the result set.

  • The C++17 wrapper owns handles with RAII types, copies results into values, and throws postproject::Error with a typed error code.

  • The Python binding raises typed exceptions. A transaction used as a context manager commits on a clean exit and rolls back when an exception escapes.

  • Rust storage returns postproject_core::Result. The media adapter prepares an import from the filesystem before the transaction stages it.

  • The CLI commits each command as one transaction. Pass --json for structured output that scripts can parse.

Every example on this site comes from a program that CI compiles and runs against an installed package, so the listings stay in step with the public interfaces. See the C quickstart, C++ quickstart, and Python quickstart for building and running a consumer, and install a release for the packages.