Changes in version 0.1.0 (2026-06-19) First CRAN release: a pure-R OpenTimelineIO document model, with constructors for the OTIO object model, functional builders, the OTIO edit algorithms, schema upgrade/downgrade, and canonical .otio / OTIOD bundle IO through jsonlite. The optional RcppOTIO package (cornball-ai drat repository) validates output against the OpenTimelineIO C++ library. - Relicensed from MIT to Apache License 2.0 to match upstream OpenTimelineIO. - CRAN pre-submission pass: single-quote 'OpenTimelineIO' and add its URL in DESCRIPTION, add URL/BugReports, fix the maintainer email, and declare Additional_repositories (cornball-ai drat) so the suggested RcppOTIO oracle is installable. - Every exported function now documents its return value and ships a small runnable example. - Drop the local %||% definition in favour of base R's (new floor: R >= 4.4.0). - Test harness redirects the R user cache/data/config dirs to tempfiles. Changes in version 0.0.2.31 - Renamed the package from nle.api to rotio. This pure-R OpenTimelineIO package takes the rotio name; the Rcpp/C++ binding is now RcppOTIO, which this package validates against (Suggests: RcppOTIO). Changes in version 0.0.2.29 Fixes - target_schema_versions is validated (named, unique, whole numbers) like RcppOTIO. - Parsed known-schema objects materialize current-schema default fields (effects/markers/enabled/color), fixing enabled becoming NULL. Changes in version 0.0.2.28 Phase 6 completion: schema migrations + downgrade - Built-in Clip/Marker upgrades + Clip downgrade; target_schema_versions on to_json_string/to_json_file; TRUE/FALSE registration semantics; fill Fit emits a base Item. Validated against RcppOTIO. Changes in version 0.0.2.27 Phase 6: schema machinery - schema_name, schema_version, is_unknown_schema, type_version_map, register_upgrade_function, register_downgrade_function. Completes phases 1-6. Changes in version 0.0.2.26 Fix: overwrite/insert remove the correct item - No longer replicate OTIO 0.18.1's remove_child-by-pointer bug; remove the intended child instead. Correct where RcppOTIO is buggy. Changes in version 0.0.2.25 Phase 5 edit algorithms (complete) - overwrite and fill added; all 10 edit algorithms validated against RcppOTIO, including replicating OTIO 0.18.1's remove_child-by-pointer behaviour. Changes in version 0.0.2.24 Phase 5 edit algorithms (part 2) - slice, insert, remove ported from the OTIO source and validated against RcppOTIO. Changes in version 0.0.2.23 Phase 5 edit algorithms (part 1) - slip, ripple, slide, trim, roll ported from the OTIO source and validated against RcppOTIO (confirmed via treesitR to delegate 1:1 to upstream). Changes in version 0.0.2.22 Fixes - trimmed_range_in_parent errors on exact boundary contact with the parent source range (no overlap), matching opentime. Validated against RcppOTIO. Changes in version 0.0.2.21 Fixes: source-fidelity pass across phases 1-4 - Audited against the OTIO C++/Python source: to_frames/from_frames truncate; end_time_exclusive at duration rate; to_time_string negatives; to_timecode infers drop-frame; frame_for_time without frame_step quantization; ISR URL separator; trimmed_range_in_parent parent-coords + out-of-range error; Track available_range edge transitions; flatten disabled-track filter only for Stack; Transition duration(). All validated against RcppOTIO. Changes in version 0.0.2.20 Fixes: flatten length-normalization - flatten_stack pads a shorter top track so longer tracks below show through; filters disabled tracks. track_trimmed_to_range mirrors the OTIO source. Found by reading OpenTimelineIO C++ source directly. Changes in version 0.0.2.19 Fixes: transition parity in trim and flatten - track_trimmed_to_range keeps/drops/errors transitions correctly; flatten_stack is gap-filling and preserves transitions. Validated against RcppOTIO. Changes in version 0.0.2.18 Fixes: Stack parallel semantics + transitions in algorithms - Stack children are parallel (range_in_parent starts at 0; available_range is the max child span); track_trimmed_to_range and flatten_stack handle tracks with transitions. Validated against RcppOTIO. Changes in version 0.0.2.17 Fixes: ImageSequenceReference parity (round 2) - end_frame correct for non-divisible durations; frame_for_time errors out of range; negative image numbers extrapolate (no error); signed zero-padding. Changes in version 0.0.2.16 Fixes: coordinate model (transitions, tracks, rates) - range_in_parent/visible_range handle Transitions; available_range/ trimmed_range work for tracks; track_trimmed_to_range is rate-faithful; flatten_stack honours enabled; visible/overlapping corrected. Validated against RcppOTIO. Changes in version 0.0.2.15 Fixes: ImageSequenceReference parity - end_frame (correct for frame_step>1 and no available_range), out-of-range errors for target_url_for_image_number/presentation_time_for_image_number, no zero-padding when frame_zero_padding == 0, and frame_for_time (the ImageSequenceReference method). Validated against RcppOTIO. Changes in version 0.0.2.14 Phase 4 of full OTIO parity: composition coordinate model - range_in_parent, trimmed_range, trimmed_range_in_parent, visible_range, item available_range, video_tracks/audio_tracks, global_start_time, is_equivalent_to, visible, overlapping, track_trimmed_to_range, and flatten_stack -- positions from gaps + child order, validated against RcppOTIO. Changes in version 0.0.2.13 Fixes: time-model rate fidelity - range_from_start_end_time/extended_by keep the start rate; clamped uses opentime operator- rate; end_time_inclusive returns the start for spans <= 1 frame; almost_equal rescales to the second arg; to_timecode/from_timecode are faithful for non-integer and drop-frame rates; from_time_string keeps the fractional value. Validated against RcppOTIO (value AND rate). Changes in version 0.0.2.12 Phase 3 of full OTIO parity: object surface - Media-reference subtypes (MediaReference, GeneratorReference, ImageSequenceReference with its computed frame/url methods), Marker, Transition (composable), and TimeEffect/FreezeFrame -- JSON-shape and behavior matched to RcppOTIO. Changes in version 0.0.2.11 Phase 2 of full OTIO parity: time model - RationalTime/TimeRange arithmetic and queries matching libopentime exactly (validated against RcppOTIO): almost_equal, end_time_exclusive/inclusive, range_from_start_end_time, contains, intersects, overlaps, extended_by, clamped, to_timecode/from_timecode, to_time_string/from_time_string, and TimeTransform. Changes in version 0.0.2.10 Phase 1 of full OTIO parity: environment-backed core - The object model is now environment-backed with reference semantics (internal .parent pointers, never serialized), the foundation for full RcppOTIO parity (see PLAN.md / OTIO_PARITY.md). Mutating tree ops mirror RcppOTIO: append_child, insert_child, set_child, set_children, remove_child, clear_children (attaching an already-parented child errors); plus parent, children, has_child, has_clips, is_parent_of, index_of_child, find_clips, clone (deep, parent-aware), color, kind<-, media_reference<-, media-reference keys, and SerializableCollection. - The functional builders (add_child/add_track/add_effect) remain as value-semantics sugar over the mutating core, so existing callers (licuadora) are unaffected. Serialized OTIO JSON is unchanged (still RcppOTIO-equivalent). Changes in version 0.0.2.9 New: OTIO effects - Effect() and LinearTimeWarp() constructors (mirroring RcppOTIO's JSON shapes and defaults), plus functional add_effect() and the accessors effects(), effect_name()/<-, time_scalar()/<-, and enabled()/<- (a disabled clip is muted; a disabled effect is bypassed). Effects round-trip through JSON and are accepted by the real OTIO library via RcppOTIO. This is the surface the blendR bridge uses to carry speed (LinearTimeWarp) and Blender compositing. Changes in version 0.0.2.8 Rewrite: pure-R OpenTimelineIO ("rotiolite") - rotio is now a dependency-light, pure-R OTIO document layer. All compiled code is gone — no Rcpp, no LinkingTo, no SystemRequirements, no libopentimelineio. Imports: jsonlite only; RcppOTIO moves to Suggests. (Reason: RcppOTIO's OTIO C++ is hard to get on CRAN; rotio becomes the CRAN-clean layer the cornyverse can depend on.) - New object model: list-based constructors Timeline(), Track(), Stack(), Clip(), Gap(), ExternalReference(), MissingReference(), RationalTime(), TimeRange() — names and JSON shapes match RcppOTIO so output is interchangeable. - Functional, value-semantics builders: add_child() and add_track() return a new object rather than mutating in place. Accessors metadata()/metadata<-, name()/name<-, kind(), children(), tracks(), source_range(), media_reference(), target_url(), plus time helpers value(), rate(), start_time(), duration(), to_seconds()/from_seconds(), to_frames()/from_frames(), rescaled_to(). - Serialization: to_json_string() / to_json_file() emit canonical OTIO JSON via jsonlite; from_json_string() / from_json_file() parse it back. Verified byte-equivalent against RcppOTIO (and thus real libopentimelineio). - OTIOD bundles: read_otiod() / write_otiod() for content.otio + media/ directory bundles. - Optional validate_with_rotio() round-trips emitted JSON through the real OTIO library when RcppOTIO is installed; unverified otherwise. - Removed (return in a later pass, re-backed on the new objects): the nle_timeline verb layer (clip_*/track_*/ripple_delete/shift_after), the driver registry, the timeline.md Markdown carrier, and the old otio_* Rcpp object/ time wrappers. Changes in version 0.0.2.7 New: generic clip effects (clone-based) - clip_effect_add(), clip_effects(), clip_effect_params(), clip_effect_remove() — attach, list, read, and remove arbitrary OTIO Effects on a clip. An effect is an effect_name plus a metadata dictionary, modelled directly on OTIO's Effect schema; parameters are stored as individual OTIO metadata entries. A driver maps effect_name + params to its engine (e.g. Kdenlive/MLT filters). Transform, crop, colour, etc. are generic effects via this API rather than fixed fields. - These verbs are clone-based: each deep-clones the timeline (OTIO clone() copies every clip/gap/effect/metadata), mutates the target clip, and returns the clone, leaving the input untouched. Effects added this way survive subsequent structural edits — the scalar-table rebuild clones non-time effects forward by clip id — and survive JSON round-trips. - Removed the Blender-shaped clip_transform/clip_crop/clip_set stubs; compositing is now generic OTIO effects via clip_effect_add(). clip_speed (an OTIO LinearTimeWarp) is unchanged and lists alongside generic effects in clip_effects(). Changes in version 0.0.2.6 Changes - OTIO migration PR 4 (effects, part 1): clip_speed() is implemented, modelled on OTIO's own effect schema. Speed is recorded as a LinearTimeWarp (time_scalar = speed, >1 faster) on the clip. Per OTIO's model the warp is an annotation, so it does not change the clip's timeline footprint — the source range still defines what is shown and a player/driver applies the rate. clip_add() accepts a speed, and timeline$clips gains a speed column. Speed survives serialization and structural edits. - Wrapped OTIO's LinearTimeWarp (the speed effect). The effect set OTIO codes is Effect -> TimeEffect -> LinearTimeWarp -> FreezeFrame; OTIO has no spatial-transform or crop schema, so those will be generic Effect objects. Still deferred - clip_transform, clip_crop, clip_set — these become a generic OTIO Effect API (clip_effect_add/clip_effects), modelled on OTIO rather than Blender-shaped fixed fields. That needs a clone-based mutation path (the scalar-table rebuild can't carry arbitrary per-clip effects), which lands in its own PR. They error with a pointer for now. Changes in version 0.0.2.5 New verbs Structural edit verbs that round out the surface (no new C++; all expressible in the gap model): - track_delete() - remove a track and its clips. - track_move() - reorder a track in the compositing stack. - ripple_delete() - delete a clip and close the gap (vs clip_delete, which leaves one). - clip_slip() - shift a clip's source in/out without moving its timeline position. - clip_duplicate() - copy a clip to a new position (defaults to right after the original). Changes in version 0.0.2.4 Changes - Renamed the sequence vocabulary to OTIO's timeline throughout. OTIO's top-level object is a Timeline, so the wrapper now speaks the format's own language. Exported renames: new_sequence -> new_timeline, nle_sequence (class) -> nle_timeline, is_sequence -> is_timeline, seq_fps -> timeline_fps, seq_duration_frames -> timeline_duration_frames, sequence_summary -> timeline_summary, read_sequence/write_sequence -> read_timeline/write_timeline, sequence_to_json/sequence_from_json -> timeline_to_json/timeline_from_json, validate_sequence -> validate_timeline, extract_sequence_state_md/replace_sequence_state_md -> *_timeline_state_md, dump_sequence/apply_sequence -> dump_timeline/apply_timeline. - The canonical artifact is now timeline.md with a state block; inst/schema/SEQUENCE_SCHEMA.md -> TIMELINE_SCHEMA.md. - No behaviour change. (blendR's driver bridge picks up the new names in PR 5.) Changes in version 0.0.2.3 Changes - OTIO migration PR 3: the sequence model is now OTIO-backed. new_sequence() returns an nle_sequence wrapping a live OTIO Timeline; seq$clips and seq$tracks materialise data.frame views from C++ on read, and edits go through the verbs only. - Structural verbs reimplemented on OTIO: track_add, clip_add, clip_delete, clip_move, clip_trim, clip_split, shift_after. Each reads the current state, applies the edit, and rebuilds the Timeline. - Gap model A. A track is sequential; a clip's timeline position is encoded by OTIO Gaps computed from tl_in. Overlapping clips on one track are now an error (use separate tracks). fps/canvas/sample_rate live in the Timeline metadata and survive serialization. - sequence.md now carries raw OTIO JSON in a block (was cornball.sequence.v1). read_sequence/write_sequence and sequence_to_json/sequence_from_json go through OTIO's serializer. Old cornball.sequence.v1 files no longer load. - New OTIO object API exposed: otio_external_reference(), otio_target_url(), otio_set_target_url(), otio_set_name(), otio_set_kind(). - Codegen extended: tools/otio_codegen.R now emits trivial constructors and scalar setters (in addition to getters), each validated against the headers via treesitR. The gap-model rebuild/materialize is bespoke and hand-written. - R/json.R and R/coords.R removed (OTIO owns serialization; coordinate transforms return with effects). inst/schema/TIMELINE_SCHEMA.md rewritten to describe the carrier and point at the OTIO docs. Deferred to PR 4 - clip_speed, clip_transform, clip_crop, clip_set, and clip_add's speed != 1 error with a pointer to PR 4; they migrate to OTIO Effects / LinearTimeWarp / per-clip metadata. Changes in version 0.0.2.2 Changes - OTIO migration PR 2: wrap the OpenTimelineIO object model. Adds C++ bindings for Timeline, Track, Clip, and ExternalReference, with construction, population (otio_add_track, otio_add_clip, otio_remove_clip), data.frame views (otio_tracks, otio_clips), and JSON (de)serialization through OTIO's own serializer (otio_to_json / otio_from_json). A timeline can be built, serialized to canonical .otio JSON, and parsed back entirely through the wrapped C++ — no R-side schema code in the data path. - Objects use OTIO's intrusive ref-counting (SerializableObject::Retainer) held through Rcpp::XPtr; OTIO objects are never deleted directly. - Header-driven codegen: tools/otio_introspect.R parses the OTIO C++ headers with treesitR to extract each class's API; tools/otio_codegen.R validates a binding manifest against those headers and emits the trivial scalar field getters (src/otio_gen.cpp). The lifetime-sensitive code is hand-written. - The pure-R rational_time()/new_sequence() model and otio_time() are unchanged. The user-facing verb layer migrates onto these objects in PR 3. Changes in version 0.0.2.1 Changes - Begin the OpenTimelineIO migration (PLAN.md, PR 1: build chain). rotio now links the C++ OpenTimelineIO library through Rcpp. OTIO is built from source and installed under /usr/local (it is not packaged for apt and ships no pkg-config file); src/Makevars hard-codes the include and link paths. - Add otio_time(), an OTIO-backed time value wrapping opentime::RationalTime as an external pointer, with otio_value(), otio_rate(), otio_to_seconds(), otio_to_frames(), otio_rescaled_to(), and otio_timecode(). All conversions are computed by the OTIO library rather than reimplemented in R. otio_version() reports the linked version. - The pure-R rational_time() and the existing sequence model are unchanged; otio_time() is additive. The data model migrates onto the C++ objects in later PRs (Timeline/Clip in PR 2). Dependencies - New Imports: Rcpp and LinkingTo: Rcpp. - New SystemRequirements: C++17 and libopentimelineio (>= 0.17), built from source. See PLAN.md for the recipe.