Open-source, cross-language library for reading, writing, validating, and editing a simplified subset of [OTIO](https://opentimelineio.readthedocs.io/) (OpenTimelineIO) JSON files. The core is written in Rust; bindings are available for Python, JavaScript (WASM), and Dart.

## What it implements

- **Timeline, Stack, Tracks** — top-level structure matching the OTIO schema
- **Clips** — with `duration`, `source` (MediaSource), effects, and metadata
- **Gaps** — silence/black filler between clips
- **MediaSource** — URL-based media reference with `start` and `duration` accessors
- **Effects** — per-clip effect parameters (volume, position, rich text overlays)
- **Metadata** — arbitrary metadata, Tellers-specific data nested under the `tellers.ai` namespace
- **IDs** — optional, auto-generated (48-bit entropy, 12-char strings); can be omitted for portability
- **Time values** — seconds as `f64`

## Build and test

Requires [just](https://just.systems/) and a Rust toolchain. For Python bindings, also install [maturin](https://www.maturin.rs/).

```
# Build all crates (excludes WASM and Python bindings)
just build-all

# Run all tests (Rust + Python + WASM build check)
just test-all

# Regenerate the JSON Schema from code
just regen-schema
```

## JSON Schema

The library is validated against a JSON Schema (Draft-07) generated from the Rust types. A minimal timeline looks like:

```
{
  "OTIO_SCHEMA": "Timeline.1",
  "name": "My Edit",
  "tracks": {
    "OTIO_SCHEMA": "Stack.1",
    "children": [\
      {\
        "OTIO_SCHEMA": "Track.1",\
        "kind": "Video",\
        "children": [\
          {\
            "type": "clip",\
            "duration": 5.0,\
            "source": {\
              "url": "https://cdn.example.com/shot1.mp4",\
              "start": 0.0,\
              "duration": 5.0\
            }\
          },\
          {\
            "type": "gap",\
            "duration": 0.5\
          },\
          {\
            "type": "clip",\
            "duration": 3.2,\
            "source": {\
              "url": "https://cdn.example.com/shot2.mp4",\
              "start": 1.0,\
              "duration": 3.2\
            }\
          }\
        ]\
      }\
    ]
  }
}
```

The full schema lives at `spec/otio.schema.json` in the repository and is regenerated with `just regen-schema`.

## Bindings

### Python

Built with [maturin](https://www.maturin.rs/) (PyO3). Run `maturin develop` from `bindings/python` to install locally.

```
# Install with maturin (from the bindings/python directory)
# maturin develop

from tellers_timeline import Timeline, Track, Clip, MediaSource

tl = Timeline(name="My Edit")
track = tl.add_video_track()

clip = Clip(
    duration=5.0,
    source=MediaSource(url="https://cdn.example.com/shot1.mp4", start=0.0, duration=5.0),
)
track.append(clip)

# Serialize to OTIO JSON
print(tl.to_json())
```

### JavaScript (WASM)

Compiled to `wasm32-unknown-unknown`. Build with `cargo build -p tellers-timeline-wasm --target wasm32-unknown-unknown`.

```javascript
// Built as a WASM module — import after running scripts/generate_api.sh
import init, { Timeline } from './tellers_timeline_wasm.js';

await init();

const tl = new Timeline("My Edit");
const track = tl.addVideoTrack();
track.appendClip({ duration: 5.0, source: { url: "https://cdn.example.com/shot1.mp4", start: 0, duration: 5.0 } });

console.log(tl.toJson());
```

### Dart

Dart bindings are available under `bindings/dart` in the repository.

## Track operations

- Append, insert, move, and delete items on a track
- Resize clips (trim in/out points)
- Split a clip at a given time (IDs are kept unique)
- Add and remove tracks from the stack
- Mute tracks (Python bindings)
