@omnimedia/omnitool 1.1.0-46 → 1.1.0-47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,120 +1,156 @@
1
1
 
2
2
  # 🚧 Work In Progress
3
3
 
4
- > **Note:** Omni Tools is under development. Expect breaking changes, evolving APIs, and experimental features.
4
+ > **Note:** Omnitool is under development. Expect breaking changes, evolving APIs, and experimental features.
5
5
 
6
6
  ---
7
7
 
8
- # 🎬 Omnitool
8
+ ## What this library is
9
9
 
10
- > Code-first video editing toolkit behind [Omniclip](https://omniclip.app) — build timelines, render videos, and automate workflows.
10
+ - API for building and rendering timelines in the browser
11
+ - Uses WebCodecs, Web Workers, and the File System Access API for export
12
+ - usage is currently JavaScript/TypeScript only, cli soon
11
13
 
12
- ---
14
+ ## 🚀 Install
13
15
 
14
- ## 🧱 Modular by Design
16
+ ```bash
17
+ npm i @omnimedia/omnitool
18
+ ```
15
19
 
16
- Omni Tools is a collection of composable utilities for working with Omniclip timelines — via code, JSON, or CLI.
20
+ ## 📦 Quick Start
17
21
 
18
- - ✅ Define timelines in JSON or TypeScript
19
- - Automate rendering with CLI tools
20
- - ✅ Ideal for scripting, CI/CD, and AI-generated workflows
22
+ ```ts
23
+ import {Driver, Omni, Datafile} from "@omnimedia/omnitool"
24
+
25
+ const driver = await Driver.setup()
26
+ const omni = new Omni(driver)
27
+
28
+ const {clip} = await omni.load({
29
+ clip: Datafile.make(file) // file is a File or Blob
30
+ })
31
+
32
+ const timeline = omni.timeline(o => {
33
+ const caption = o.text("Hello world", {
34
+ duration: 1500,
35
+ styles: {fill: "white", fontSize: 48}
36
+ })
37
+ const xfade = o.transition.crossfade(500)
38
+
39
+ return o.sequence(
40
+ o.stack(
41
+ o.video(clip, {start: 0, duration: 3000}),
42
+ caption
43
+ ),
44
+ o.gap(400),
45
+ xfade,
46
+ o.video(clip, {start: 5000, duration: 2500}),
47
+ o.audio(clip, {start: 5000, duration: 2500})
48
+ )
49
+ })
50
+ ```
21
51
 
22
- ---
52
+ ## 🧭 Spatial Transforms
23
53
 
24
- ## 🚀 Quick Start
54
+ ```ts
55
+ const timeline = omni.timeline(o => {
56
+ const move = o.spatial(o.transform({
57
+ position: [120, 40],
58
+ scale: [0.6, 0.6],
59
+ rotation: 0.2
60
+ }))
61
+
62
+ const title = o.text("Lower third", {
63
+ duration: 2000,
64
+ styles: {fill: "white", fontSize: 36}
65
+ })
66
+ o.set(title.id, {spatialId: move.id})
67
+
68
+ return o.stack(
69
+ o.video(clip, {duration: 4000}),
70
+ title
71
+ )
72
+ })
73
+ ```
25
74
 
26
- ### Install
75
+ Worker URL notes:
76
+ - `Driver.setup()` defaults to `/node_modules/@omnimedia/omnitool/x/driver/driver.worker.bundle.min.js`.
77
+ - If you serve the worker from a different location, pass `workerUrl`:
27
78
 
28
- ```bash
29
- npm i @omnimedia/omnitool
79
+ ```ts
80
+ const workerUrl = new URL(
81
+ "/path/to/driver.worker.bundle.min.js",
82
+ window.location.href
83
+ )
84
+ const driver = await Driver.setup({workerUrl})
30
85
  ```
31
86
 
32
- ---
33
-
34
- ## 📦 Example (Programmatic Timeline)
87
+ ## ▶️ Playback
35
88
 
36
89
  ```ts
37
- import { subtitle, crossfade, sequence, stack, video } from "@omni/tools"
38
-
39
- const watermark = subtitle("omniclip")
40
- const xfade = crossfade(500)
41
-
42
- const timeline = sequence(
43
- video("opening-credits.mp4"),
44
- xfade,
45
- stack(
46
- video("skateboarding.mp4"),
47
- watermark
48
- ),
49
- xfade,
50
- stack(
51
- video("biking.mp4"),
52
- watermark
53
- )
54
- )
55
-
90
+ const player = await omni.playback(timeline)
91
+
92
+ document.body.appendChild(player.canvas)
93
+ player.play()
56
94
  ```
57
95
 
58
- ---
96
+ Notes:
97
+ - Call `player.update(timeline)` if you update the timeline.
98
+
99
+ ## 📤 Export
59
100
 
60
- ## 🧩 Timeline Format (Omni Timeline Format)
101
+ ```ts
102
+ await omni.render(timeline, framerate)
103
+ ```
104
+
105
+ ## 🧩 Timeline Format (TimelineFile)
61
106
 
62
- Every timeline is defined as a graph:
107
+ All durations and timestamps are in milliseconds.
63
108
 
64
109
  ```json
65
110
  {
66
- "format": "omni-timeline@1",
67
- "root": "root-1",
68
- "items": [
69
- ["root-1", ["sequence", { "children": ["video-1", "stack-1"] }]],
70
- ["video-1", ["video", { ... }]],
71
- ["stack-1", ["stack", { "children": ["text-1", "audio-1"] }]],
72
- ["text-1", ["text", { ... }]],
73
- ["audio-1", ["audio", { ... }]]
74
- ]
111
+ "format": "timeline",
112
+ "info": "https://omniclip.app/",
113
+ "version": 0,
114
+ "rootId": 123,
115
+ "items": [
116
+ {"id": 123, "kind": 0, "childrenIds": [456, 789]},
117
+ {"id": 456, "kind": 2, "mediaHash": "...", "start": 0, "duration": 3000},
118
+ {"id": 789, "kind": 4, "content": "Hello", "duration": 1500}
119
+ ]
75
120
  }
76
121
  ```
77
122
 
78
- Each item is a `[id, item]` pair. Items can reference others by ID, forming a directed graph of reusable, composable blocks.
79
-
80
- ---
123
+ Timeline items:
124
+ - 0 `Sequence`
125
+ - 1 `Stack`
126
+ - 2 `Video`
127
+ - 3 `Audio`
128
+ - 4 `Text`
129
+ - 5 `Gap`
130
+ - 6 `Spatial`
131
+ - 7 `Transition`
132
+ - 8 `TextStyle`
81
133
 
82
- ## 🖥 CLI Usage
134
+ ## 🗺️ Roadmap
135
+ - Planned CLI commands (not available in this repo yet):
83
136
 
84
137
  ```bash
85
- # Build a timeline (manually or via AI)
138
+ # build a reusable template from a timeline
86
139
  omnitool build-template promo.json
87
-
88
- # Validate structure
140
+ # validate a timeline file
89
141
  omnitool validate promo.json
90
-
91
- # Render video
142
+ # export a timeline to a video file
92
143
  omnitool export promo.json --output final.mp4
93
-
94
- # Batch render
144
+ # batch export multiple timelines
95
145
  omnitool batch-export ./projects/* --output-dir ./exports
146
+ # headless timeline viewer
147
+ omnitool preview promo.json
148
+ # auto-fit timeline elements
149
+ omnitool optimize promo.json
150
+ # prompt-to-timeline generation
151
+ omnitool ai "make a 15s promo for tea"
96
152
  ```
97
153
 
98
- ---
99
-
100
- ## ✅ Use Cases
101
-
102
- - Render videos from scripts, templates, or AI
103
- - Build and test timelines without opening the UI
104
- - Generate video pipelines from code or prompts
105
-
106
- ---
107
-
108
- ## 📘 More to come
109
-
110
- - `omnitool preview` – headless timeline viewer
111
- - `omnitool optimize` – auto-fit timeline elements
112
- - `omnitool ai` – native prompt-to-timeline generation
113
-
114
- ---
115
-
116
- ## 🧠 Learn More
117
-
118
- Omni Tools powers AI agents, programmatic editing, and upcoming new version of omniclip video editor.
154
+ - smooth seeking
155
+ - server-side, not just browsers
119
156
 
120
- → [Visit Omniclip](https://omniclip.app)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omnimedia/omnitool",
3
- "version": "1.1.0-46",
3
+ "version": "1.1.0-47",
4
4
  "description": "open source video processing tools",
5
5
  "license": "MIT",
6
6
  "author": "Przemysław Gałęzki",
@@ -38,7 +38,7 @@ async function startDemoImport(e: Event)
38
38
 
39
39
  const {timeline, omni} = await TimelineSchemaTest(driver, file)
40
40
 
41
- playbackTest(driver, timeline, omni)
41
+ playbackTest(timeline, omni)
42
42
  exportTest(omni, timeline)
43
43
  }
44
44
  // const [fileHandle] = await window.showOpenFilePicker()
@@ -1,13 +1,12 @@
1
1
 
2
- import {Driver} from "../../driver/driver.js"
3
- import {O, Omni, TimelineFile, VideoPlayer} from "../../timeline/index.js"
2
+ import {O, Omni, TimelineFile} from "../../timeline/index.js"
4
3
 
5
- export async function playbackTest(driver: Driver, timeline: TimelineFile, omni: Omni) {
4
+ export async function playbackTest(timeline: TimelineFile, omni: Omni) {
6
5
  const playButton = document.querySelector(".play") as HTMLButtonElement
7
6
  const stopButton = document.querySelector(".stop") as HTMLButtonElement
8
7
  const seekButton = document.querySelector(".seek") as HTMLButtonElement
9
8
  const o = new O({project: timeline})
10
- const player = await VideoPlayer.create(driver, timeline, (hash) => omni.resources.require(hash).url)
9
+ const player = await omni.playback(timeline)
11
10
  document.body.appendChild(player.canvas)
12
11
 
13
12
  playButton.addEventListener("click", () => player.play())
@@ -9,8 +9,7 @@ export class Export {
9
9
  #sampler
10
10
  constructor(
11
11
  private driver: Driver,
12
- private framerate = 30,
13
- private resolveMedia: (hash: string) => DecoderSource = _hash => "/assets/temp/gl.mp4"
12
+ private resolveMedia: (hash: string) => DecoderSource
14
13
  ) {
15
14
  this.#sampler = makeWebCodecsSampler(this.driver, this.resolveMedia)
16
15
  }
@@ -21,7 +20,7 @@ export class Export {
21
20
  return await buildWebCodecsNodeTree(rootItem, items, this.#sampler)
22
21
  }
23
22
 
24
- async render(timeline: TimelineFile) {
23
+ async render(timeline: TimelineFile, framerate: number) {
25
24
  const root = await this.#build(timeline)
26
25
 
27
26
  const videoStream = new TransformStream<VideoFrame, VideoFrame>()
@@ -50,10 +49,10 @@ export class Export {
50
49
 
51
50
  const videoPromise = (async () => {
52
51
  let i = 0
53
- const dt = 1 / this.framerate
52
+ const dt = 1 / framerate
54
53
 
55
54
  await fixedStep(
56
- {fps: this.framerate, duration: root.duration ?? 0},
55
+ {fps: framerate, duration: root.duration ?? 0},
57
56
  async t => {
58
57
  const layers = await root.visuals?.sampleAt(t) ?? []
59
58
  const composed = await this.driver.composite(layers)
@@ -7,14 +7,17 @@ import {Datafile} from "../utils/datafile.js"
7
7
  import {TimelineFile} from "../parts/basics.js"
8
8
  import {Export} from "../parts/renderers/export.js"
9
9
  import {ResourcePool} from "../parts/resource-pool.js"
10
- import {RenderConfig} from "../../driver/fns/schematic.js"
10
+ import {VideoPlayer} from "../parts/renderers/playback.js"
11
11
 
12
12
  export class Omni {
13
13
  resources = new ResourcePool()
14
14
  #export: Export
15
15
 
16
16
  constructor(private driver: Driver) {
17
- this.#export = new Export(driver)
17
+ this.#export = new Export(
18
+ driver,
19
+ (hash) => this.resources.require(hash).url
20
+ )
18
21
  }
19
22
 
20
23
  load = async<S extends Record<string, Promise<Datafile>>>(spec: S) => {
@@ -38,8 +41,16 @@ export class Omni {
38
41
  return o.state.project
39
42
  }
40
43
 
41
- render = async (timeline: TimelineFile) => {
42
- await this.#export.render(timeline)
44
+ playback = async (timeline: TimelineFile) => {
45
+ return await VideoPlayer.create(
46
+ this.driver,
47
+ timeline,
48
+ (hash) => this.resources.require(hash).url
49
+ )
50
+ }
51
+
52
+ render = async (timeline: TimelineFile, framerate: number = 30) => {
53
+ await this.#export.render(timeline, framerate)
43
54
  }
44
55
  }
45
56
 
@@ -30,7 +30,7 @@ async function startDemoImport(e) {
30
30
  run(transcode, file.name);
31
31
  // await transcriber.transcribe(file)
32
32
  const { timeline, omni } = await TimelineSchemaTest(driver, file);
33
- playbackTest(driver, timeline, omni);
33
+ playbackTest(timeline, omni);
34
34
  exportTest(omni, timeline);
35
35
  }
36
36
  // const [fileHandle] = await window.showOpenFilePicker()
@@ -1 +1 @@
1
- {"version":3,"file":"demo.bundle.js","sourceRoot":"","sources":["../../s/demo/demo.bundle.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"demo.bundle.js","sourceRoot":"","sources":["../../s/demo/demo.bundle.ts"],"names":[],"mappings":"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"}