Skip to content

Video metadata (schema.org · IIIF · provenance)

ecmanim knows a video’s structure at render time — duration, dimensions, and the scene’s nextSection() boundaries — so it can emit the web’s video metadata standards directly, without reverse-engineering them from a file.

  • schema.org VideoObject (JSON-LD) — discovery: “what is this video?”
  • IIIF Presentation 3.0 Manifest — presentation/navigation, with chapters derived from nextSection(): “how do I view and move through it?”
  • provenance sliver — “generated by ecmanim” + the IPTC digital-source-type for algorithmic media.

These are export adapters that read from the render result (and scene sections); there is no second normalized model. resolveIIIFVideo is the one importer, used by VideoMobject to ingest an IIIF-described clip. All of it is isomorphic (no Node/DOM at import) and exported from ecmanim.

Every function takes a VideoMetaInput — supply what you have; unknown fields are omitted. A Node render() result carries most of it:

import { render, toVideoObject, toIIIFManifest } from "ecmanim/node";
const r = await render(MyScene, { output: "demo.mp4", fps: 30 });
// r = { output, frames, fps, pixelWidth, pixelHeight, sections, ... }
const input = {
frames: r.frames, fps: r.fps, width: r.pixelWidth, height: r.pixelHeight,
sections: r.sections, // -> chapters (from nextSection())
id: "https://example.org/demo", // canonical IRI
contentUrl: "https://example.org/demo.mp4",
name: "Demo", description: "", uploadDate: "2026-07-02",
encodingFormat: "video/mp4",
provenance: true, // adds the ecmanim creator + IPTC term
};

durationSeconds may be given directly instead of frames/fps. Chapters come from sections (a SceneSection[]), or pass explicit chapters to override.

const jsonld = toVideoObject(input);
// { "@context":"https://schema.org", "@type":"VideoObject", name, duration:"PT…",
// contentUrl, width, height, hasPart:[{ "@type":"Clip", … }], creator:{…}, … }
// or a ready-to-embed <script> tag:
import { toVideoObjectScript } from "ecmanim";
page.head += toVideoObjectScript(input);

Chapters become hasPart Clips (Google understands these as key moments). provenance adds a creator SoftwareApplication and an additionalProperty carrying the IPTC digital-source-type.

The Web Component injects the JSON-LD for you when you set metadata (off otherwise):

const player = document.querySelector("manim-player");
player.metadata = { name: "Demo", contentUrl: "demo.mp4", provenance: true };
// -> one <script type="application/ld+json"> child; width/height/fps/duration are
// filled from the player automatically. Also: player.getVideoObject().
const manifest = toIIIFManifest(input);

Produces a v3 Manifest → Canvas (duration, width/height) → painting Annotation whose body is a Video resource. nextSection() boundaries become structures Ranges targeting temporal fragments (…/canvas/1#t=0,2), i.e. real chapters a IIIF viewer can navigate. provenance adds a metadata block.

loadVideo (Node and browser) accepts a IIIF manifest — an already-parsed object, or a URL with { iiif: true } — resolves the video body URL, and attaches the manifest’s chapters to the returned mobject:

import { loadVideo } from "ecmanim/node"; // or "ecmanim/browser"
const clip = await loadVideo(manifest, { fps: 30 }); // object form
const clip2 = await loadVideo(manifestUrl, { iiif: true }); // fetch + parse
clip.chapters; // [{ label, start, end }, …] from the manifest's structures

Node reads the resolved URL through ffmpeg (local paths or remote http); remote URLs are cached by URL (no mtime). resolveIIIFVideo(manifest) is exported if you want the parsed { url, width, height, duration, chapters } directly.

  • Export-first. VideoObject + IIIF are generated from the render/scene; there is no round-trip metadata compiler in core. resolveIIIFVideo is the only importer (for ingestion).
  • IPTC Video Metadata Hub is represented only by the provenance sliver (generator + digital-source-type); the full VMH property set is out of scope.
  • Getting manifests validator-clean for a specific viewer may need viewer-specific tweaks (thumbnails, rendering, rights) — extend the returned object as needed.