@omnimedia/omnitool 1.0.0 → 1.1.0-3

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.
Files changed (177) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +120 -2
  3. package/package.json +56 -27
  4. package/s/_archive/types.ts +107 -0
  5. package/s/context.ts +7 -0
  6. package/s/demo/demo.bundle.ts +64 -0
  7. package/s/demo/demo.css +54 -0
  8. package/s/demo/routines/filmstrip-test.ts +68 -0
  9. package/s/demo/routines/load-video.ts +7 -0
  10. package/s/demo/routines/transcode-test.ts +44 -0
  11. package/s/demo/routines/waveform-test.ts +12 -0
  12. package/s/driver/driver.test.ts +15 -0
  13. package/s/driver/driver.ts +116 -0
  14. package/s/driver/driver.worker.bundle.ts +7 -0
  15. package/s/driver/fns/host.ts +12 -0
  16. package/s/driver/fns/schematic.ts +83 -0
  17. package/s/driver/fns/work.ts +237 -0
  18. package/s/driver/parts/constants.ts +17 -0
  19. package/s/driver/parts/machina.ts +27 -0
  20. package/s/driver/utils/load-decoder-source.ts +13 -0
  21. package/s/driver/utils/sleep.ts +3 -0
  22. package/s/index.html.ts +53 -0
  23. package/s/index.ts +2 -39
  24. package/s/tests.test.ts +8 -0
  25. package/s/timeline/index.ts +14 -0
  26. package/s/timeline/parts/basics.ts +17 -0
  27. package/s/timeline/parts/filmstrip.ts +159 -0
  28. package/s/timeline/parts/item.ts +58 -0
  29. package/s/timeline/parts/media.ts +14 -0
  30. package/s/timeline/parts/resource-pool.ts +27 -0
  31. package/s/timeline/parts/resource.ts +11 -0
  32. package/s/timeline/parts/waveform.ts +62 -0
  33. package/s/timeline/sugar/o.ts +60 -0
  34. package/s/timeline/sugar/omni-test.ts +38 -0
  35. package/s/timeline/sugar/omni.ts +30 -0
  36. package/s/timeline/utils/checksum.ts +19 -0
  37. package/s/timeline/utils/datafile.ts +21 -0
  38. package/s/timeline/utils/dummy-data.ts +7 -0
  39. package/x/context.d.ts +4 -0
  40. package/x/context.js +6 -0
  41. package/x/context.js.map +1 -0
  42. package/x/demo/demo.bundle.d.ts +1 -0
  43. package/x/demo/demo.bundle.js +51 -0
  44. package/x/demo/demo.bundle.js.map +1 -0
  45. package/x/demo/demo.bundle.min.js +118 -0
  46. package/x/demo/demo.bundle.min.js.map +7 -0
  47. package/x/demo/demo.css +54 -0
  48. package/x/demo/routines/filmstrip-test.d.ts +1 -0
  49. package/x/demo/routines/filmstrip-test.js +62 -0
  50. package/x/demo/routines/filmstrip-test.js.map +1 -0
  51. package/x/demo/routines/load-video.d.ts +1 -0
  52. package/x/demo/routines/load-video.js +6 -0
  53. package/x/demo/routines/load-video.js.map +1 -0
  54. package/x/demo/routines/transcode-test.d.ts +6 -0
  55. package/x/demo/routines/transcode-test.js +38 -0
  56. package/x/demo/routines/transcode-test.js.map +1 -0
  57. package/x/demo/routines/waveform-test.d.ts +1 -0
  58. package/x/demo/routines/waveform-test.js +11 -0
  59. package/x/demo/routines/waveform-test.js.map +1 -0
  60. package/x/driver/driver.d.ts +22 -0
  61. package/x/driver/driver.js +97 -0
  62. package/x/driver/driver.js.map +1 -0
  63. package/x/driver/driver.test.d.ts +5 -0
  64. package/x/driver/driver.test.js +12 -0
  65. package/x/driver/driver.test.js.map +1 -0
  66. package/x/driver/driver.worker.bundle.d.ts +1 -0
  67. package/x/driver/driver.worker.bundle.js +4 -0
  68. package/x/driver/driver.worker.bundle.js.map +1 -0
  69. package/x/driver/driver.worker.bundle.min.js +1148 -0
  70. package/x/driver/driver.worker.bundle.min.js.map +7 -0
  71. package/x/driver/fns/host.d.ts +18 -0
  72. package/x/driver/fns/host.js +7 -0
  73. package/x/driver/fns/host.js.map +1 -0
  74. package/x/driver/fns/schematic.d.ts +66 -0
  75. package/x/driver/fns/schematic.js +2 -0
  76. package/x/driver/fns/schematic.js.map +1 -0
  77. package/x/driver/fns/work.d.ts +19 -0
  78. package/x/driver/fns/work.js +192 -0
  79. package/x/driver/fns/work.js.map +1 -0
  80. package/x/driver/parts/constants.d.ts +2 -0
  81. package/x/driver/parts/constants.js +17 -0
  82. package/x/driver/parts/constants.js.map +1 -0
  83. package/x/driver/parts/machina.d.ts +23 -0
  84. package/x/driver/parts/machina.js +14 -0
  85. package/x/driver/parts/machina.js.map +1 -0
  86. package/x/driver/utils/load-decoder-source.d.ts +2 -0
  87. package/x/driver/utils/load-decoder-source.js +12 -0
  88. package/x/driver/utils/load-decoder-source.js.map +1 -0
  89. package/x/driver/utils/sleep.d.ts +1 -0
  90. package/x/driver/utils/sleep.js +4 -0
  91. package/x/driver/utils/sleep.js.map +1 -0
  92. package/x/index.d.ts +2 -9
  93. package/x/index.html +105 -0
  94. package/x/index.html.d.ts +2 -0
  95. package/x/index.html.js +47 -0
  96. package/x/index.html.js.map +1 -0
  97. package/x/index.js +2 -29
  98. package/x/index.js.map +1 -1
  99. package/x/tests.test.d.ts +1 -0
  100. package/x/tests.test.js +6 -0
  101. package/x/tests.test.js.map +1 -0
  102. package/x/timeline/index.d.ts +10 -0
  103. package/x/timeline/index.js +11 -0
  104. package/x/timeline/index.js.map +1 -0
  105. package/x/timeline/parts/basics.d.ts +12 -0
  106. package/x/timeline/parts/basics.js +2 -0
  107. package/x/timeline/parts/basics.js.map +1 -0
  108. package/x/timeline/parts/filmstrip.d.ts +39 -0
  109. package/x/timeline/parts/filmstrip.js +117 -0
  110. package/x/timeline/parts/filmstrip.js.map +1 -0
  111. package/x/timeline/parts/item.d.ts +42 -0
  112. package/x/timeline/parts/item.js +13 -0
  113. package/x/timeline/parts/item.js.map +1 -0
  114. package/x/timeline/parts/media.d.ts +7 -0
  115. package/x/timeline/parts/media.js +13 -0
  116. package/x/timeline/parts/media.js.map +1 -0
  117. package/x/timeline/parts/resource-pool.d.ts +7 -0
  118. package/x/timeline/parts/resource-pool.js +19 -0
  119. package/x/timeline/parts/resource-pool.js.map +1 -0
  120. package/x/timeline/parts/resource.d.ts +8 -0
  121. package/x/timeline/parts/resource.js +2 -0
  122. package/x/timeline/parts/resource.js.map +1 -0
  123. package/x/timeline/parts/waveform.d.ts +8 -0
  124. package/x/timeline/parts/waveform.js +51 -0
  125. package/x/timeline/parts/waveform.js.map +1 -0
  126. package/x/timeline/sugar/o.d.ts +14 -0
  127. package/x/timeline/sugar/o.js +48 -0
  128. package/x/timeline/sugar/o.js.map +1 -0
  129. package/x/timeline/sugar/omni-test.d.ts +1 -0
  130. package/x/timeline/sugar/omni-test.js +22 -0
  131. package/x/timeline/sugar/omni-test.js.map +1 -0
  132. package/x/timeline/sugar/omni.d.ts +11 -0
  133. package/x/timeline/sugar/omni.js +20 -0
  134. package/x/timeline/sugar/omni.js.map +1 -0
  135. package/x/timeline/utils/checksum.d.ts +8 -0
  136. package/x/timeline/utils/checksum.js +20 -0
  137. package/x/timeline/utils/checksum.js.map +1 -0
  138. package/x/timeline/utils/datafile.d.ts +9 -0
  139. package/x/timeline/utils/datafile.js +20 -0
  140. package/x/timeline/utils/datafile.js.map +1 -0
  141. package/x/timeline/utils/dummy-data.d.ts +2 -0
  142. package/x/timeline/utils/dummy-data.js +3 -0
  143. package/x/timeline/utils/dummy-data.js.map +1 -0
  144. package/s/parts/compositor.ts +0 -5
  145. package/s/parts/export.ts +0 -5
  146. package/s/parts/video-decoder.ts +0 -27
  147. package/s/parts/video-encoder.ts +0 -15
  148. package/s/tools/generate-id.ts +0 -7
  149. package/s/tools/mp4boxjs/LICENSE.md +0 -24
  150. package/s/tools/mp4boxjs/demuxer.ts +0 -106
  151. package/s/tools/mp4boxjs/mp4box.adapter.ts +0 -148
  152. package/s/tools/mp4boxjs/mp4box.js +0 -8206
  153. package/s/types.ts +0 -10
  154. package/x/parts/compositor.d.ts +0 -4
  155. package/x/parts/compositor.js +0 -5
  156. package/x/parts/compositor.js.map +0 -1
  157. package/x/parts/export.d.ts +0 -7
  158. package/x/parts/export.js +0 -5
  159. package/x/parts/export.js.map +0 -1
  160. package/x/parts/video-decoder.d.ts +0 -8
  161. package/x/parts/video-decoder.js +0 -20
  162. package/x/parts/video-decoder.js.map +0 -1
  163. package/x/parts/video-encoder.d.ts +0 -6
  164. package/x/parts/video-encoder.js +0 -12
  165. package/x/parts/video-encoder.js.map +0 -1
  166. package/x/tools/generate-id.d.ts +0 -1
  167. package/x/tools/generate-id.js +0 -8
  168. package/x/tools/generate-id.js.map +0 -1
  169. package/x/tools/mp4boxjs/demuxer.d.ts +0 -24
  170. package/x/tools/mp4boxjs/demuxer.js +0 -88
  171. package/x/tools/mp4boxjs/demuxer.js.map +0 -1
  172. package/x/tools/mp4boxjs/mp4box.adapter.d.ts +0 -128
  173. package/x/tools/mp4boxjs/mp4box.adapter.js +0 -11
  174. package/x/tools/mp4boxjs/mp4box.adapter.js.map +0 -1
  175. package/x/types.d.ts +0 -7
  176. package/x/types.js +0 -2
  177. package/x/types.js.map +0 -1
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Przemysław Gałęzki
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,2 +1,120 @@
1
- # omnitool
2
- # omnitool
1
+
2
+ # 🚧 Work In Progress
3
+
4
+ > **Note:** Omni Tools is under development. Expect breaking changes, evolving APIs, and experimental features.
5
+
6
+ ---
7
+
8
+ # 🎬 Omnitool
9
+
10
+ > Code-first video editing toolkit behind [Omniclip](https://omniclip.app) — build timelines, render videos, and automate workflows.
11
+
12
+ ---
13
+
14
+ ## 🧱 Modular by Design
15
+
16
+ Omni Tools is a collection of composable utilities for working with Omniclip timelines — via code, JSON, or CLI.
17
+
18
+ - ✅ Define timelines in JSON or TypeScript
19
+ - ✅ Automate rendering with CLI tools
20
+ - ✅ Ideal for scripting, CI/CD, and AI-generated workflows
21
+
22
+ ---
23
+
24
+ ## 🚀 Quick Start
25
+
26
+ ### Install
27
+
28
+ ```bash
29
+ npm i @omnimedia/omnitool
30
+ ```
31
+
32
+ ---
33
+
34
+ ## 📦 Example (Programmatic Timeline)
35
+
36
+ ```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
+
56
+ ```
57
+
58
+ ---
59
+
60
+ ## 🧩 Timeline Format (Omni Timeline Format)
61
+
62
+ Every timeline is defined as a graph:
63
+
64
+ ```json
65
+ {
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
+ ]
75
+ }
76
+ ```
77
+
78
+ Each item is a `[id, item]` pair. Items can reference others by ID, forming a directed graph of reusable, composable blocks.
79
+
80
+ ---
81
+
82
+ ## 🖥 CLI Usage
83
+
84
+ ```bash
85
+ # Build a timeline (manually or via AI)
86
+ omnitool build-template promo.json
87
+
88
+ # Validate structure
89
+ omnitool validate promo.json
90
+
91
+ # Render video
92
+ omnitool export promo.json --output final.mp4
93
+
94
+ # Batch render
95
+ omnitool batch-export ./projects/* --output-dir ./exports
96
+ ```
97
+
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.
119
+
120
+ → [Visit Omniclip](https://omniclip.app)
package/package.json CHANGED
@@ -1,29 +1,58 @@
1
1
  {
2
- "name": "@omnimedia/omnitool",
3
- "version": "1.0.0",
4
- "description": "open source video processing tools",
5
- "main": "x/index.js",
6
- "files": [
7
- "x",
8
- "s"
9
- ],
10
- "type": "module",
11
- "scripts": {
12
- "build": "tsc",
13
- "start": "tsc --watch",
14
- "test": "echo \"Error: no test specified\" && exit 1"
15
- },
16
- "keywords": [
17
- "tools",
18
- "video",
19
- "processing"
20
- ],
21
- "author": "zenkyu",
22
- "license": "ISC",
23
- "devDependencies": {
24
- "typescript": "^5.4.5"
25
- },
26
- "dependencies": {
27
- "@types/dom-webcodecs": "^0.1.11"
28
- }
2
+ "name": "@omnimedia/omnitool",
3
+ "version": "1.1.0-3",
4
+ "description": "open source video processing tools",
5
+ "license": "MIT",
6
+ "author": "Przemysław Gałęzki",
7
+ "type": "module",
8
+ "main": "x/index.js",
9
+ "files": [
10
+ "x",
11
+ "s"
12
+ ],
13
+ "scripts": {
14
+ "build": "run-s _clean _tsc _ln _scute",
15
+ "start": "octo 'scute -vw' 'tsc -w' 'node --watch x/tests.test.js' 'http-server x'",
16
+ "_clean": "rm -rf x && mkdir x",
17
+ "_tsc": "tsc",
18
+ "_ln": "run-s _ln-s _ln-assets",
19
+ "_ln-s": "ln -s \"$(realpath s)\" x/s",
20
+ "_ln-assets": "ln -s \"$(realpath assets)\" x/assets",
21
+ "_scute": "scute -v",
22
+ "test": "node x/tests.test.js",
23
+ "test-debug": "node inspect x/tests.test.js"
24
+ },
25
+ "devDependencies": {
26
+ "@e280/science": "^0.0.5",
27
+ "@e280/scute": "^0.0.0-6",
28
+ "@types/node": "^24.0.14",
29
+ "http-server": "^14.1.1",
30
+ "npm-run-all": "^4.1.5",
31
+ "typescript": "^5.8.3"
32
+ },
33
+ "dependencies": {
34
+ "@benev/slate": "^0.3.9",
35
+ "@e280/comrade": "^0.0.0-18",
36
+ "@e280/renraku": "^0.5.0-19",
37
+ "@e280/stz": "^0.0.0-22",
38
+ "comrade": "^0.0.3",
39
+ "mediabunny": "^1.1.1",
40
+ "mp4-muxer": "^5.2.1",
41
+ "pixi.js": "^8.10.1",
42
+ "wavesurfer.js": "^7.10.0",
43
+ "web-demuxer": "^2.3.8"
44
+ },
45
+ "homepage": "https://github.com/omni-media/omnitool#readme",
46
+ "repository": {
47
+ "type": "git",
48
+ "url": "git+https://github.com/omni-media/omnitool.git"
49
+ },
50
+ "bugs": {
51
+ "url": "https://github.com/omni-media/omnitool/issues"
52
+ },
53
+ "keywords": [
54
+ "tools",
55
+ "video",
56
+ "processing"
57
+ ]
29
58
  }
@@ -0,0 +1,107 @@
1
+ export interface Effect {
2
+ start: number
3
+ end: number
4
+ duration: number
5
+ }
6
+
7
+ export interface TextEffect {
8
+ font: Font
9
+ content: string
10
+ color: string
11
+ size: number
12
+ style: FontStyle
13
+ align: TextAlign
14
+ rect: Rect
15
+ }
16
+
17
+ export interface SubtitleEffect {
18
+ content: string
19
+ }
20
+
21
+ export interface FillEffect {
22
+ backgroundColor: string
23
+ }
24
+
25
+ export type FontStyle = "italic" | "bold" | "normal"
26
+ export type Font = "Arial" | "Lato"
27
+ export type TextAlign = "left" | "right" | "center"
28
+
29
+ export interface AudioClip extends Effect {
30
+ file: string
31
+ }
32
+
33
+ export interface VideoClip extends Effect {
34
+ file: string
35
+ rect: Rect
36
+ }
37
+
38
+ export interface ImageEffect extends Effect {
39
+ file: string
40
+ rect: Rect
41
+ }
42
+
43
+ export interface Transition {
44
+ type: TransitonType
45
+ duration: number
46
+ }
47
+
48
+ export type TransitonType = "crossfade" | "blur" | "fade"
49
+
50
+ export type AudioItem = ["audio", AudioClip]
51
+ export type VideoItem = ["video", VideoClip]
52
+ export type TransitionItem = ["transition", Transition]
53
+ export type SubtitleItem = ["subtitle", SubtitleEffect]
54
+ export type TextItem = ["text", Text]
55
+ export type ImageItem = ["image", ImageEffect]
56
+ export type FillItem = ["fill", FillEffect]
57
+ export type Sequence = ["sequence", {children: string[]}]
58
+ export type Stack = ["stack", {children: string[]}]
59
+
60
+ type Item =
61
+ | AudioItem
62
+ | VideoItem
63
+ | TransitionItem
64
+ | SubtitleItem
65
+ | TextItem
66
+ | ImageItem
67
+ | FillItem
68
+ | Sequence
69
+ | Stack
70
+
71
+ export type Items = [string, Item]
72
+
73
+ export interface Timeline {
74
+ root: string
75
+ items: Items[]
76
+ }
77
+
78
+ export interface Rect {
79
+ width: number
80
+ height: number
81
+ scaleX: number
82
+ scaleY: number
83
+ position_on_canvas: {
84
+ x: number
85
+ y: number
86
+ }
87
+ rotation: number
88
+ }
89
+
90
+ const defaultRect: Rect = {
91
+ width: 0,
92
+ height: 0,
93
+ scaleX: 0,
94
+ scaleY: 0,
95
+ position_on_canvas: {x: 0, y: 0},
96
+ rotation: 0,
97
+ }
98
+
99
+ const timeline: Timeline = {
100
+ root: "213",
101
+ items: [
102
+ [
103
+ "12321",
104
+ ["video", { file: "", start: 0, end: 5, duration: 5, rect: defaultRect }],
105
+ ],
106
+ ],
107
+ }
package/s/context.ts ADDED
@@ -0,0 +1,7 @@
1
+ import {Driver} from "./driver/driver.js"
2
+
3
+ const workerUrl = new URL("../driver/driver.worker.bundle.js", import.meta.url)
4
+
5
+ export const context = {
6
+ driver: Driver.setup({workerUrl})
7
+ }
@@ -0,0 +1,64 @@
1
+
2
+ import {context} from "../context.js"
3
+ import {waveformTest} from "./routines/waveform-test.js"
4
+ import {filmstripTest} from "./routines/filmstrip-test.js"
5
+ import {setupTranscodeTest} from "./routines/transcode-test.js"
6
+
7
+ const driver = await context.driver
8
+ const results = document.querySelector(".results")!
9
+
10
+ const fetchButton = document.querySelector(".fetch")
11
+ const importButton = document.querySelector(".import") as HTMLButtonElement
12
+
13
+ fetchButton?.addEventListener("click", startDemoFetch)
14
+ importButton?.addEventListener("click", startDemoImport)
15
+
16
+ waveformTest()
17
+
18
+ // hello world test
19
+ {
20
+ await driver.thread.work.hello()
21
+ if (driver.machina.count === 1) console.log("✅ driver works")
22
+ else console.error("❌ FAIL driver call didn't work")
23
+ }
24
+
25
+ // transcoding tests
26
+ async function startDemoImport()
27
+ {
28
+ const [fileHandle] = await window.showOpenFilePicker()
29
+ const transcode = setupTranscodeTest(driver, fileHandle)
30
+ await filmstripTest(fileHandle)
31
+ run(transcode, fileHandle.name)
32
+ }
33
+
34
+ async function startDemoFetch()
35
+ {
36
+
37
+ // which videos to run tests on
38
+ const videos = [
39
+ "/assets/temp/gl.mp4",
40
+ ]
41
+
42
+ // running each test in sequence
43
+ for (const url of videos) {
44
+ const transcode = setupTranscodeTest(driver, "/assets/temp/gl.mp4")
45
+ run(transcode, url)
46
+ }
47
+ }
48
+
49
+ async function run(transcode: ReturnType<typeof setupTranscodeTest>, label: string) {
50
+ // create result div
51
+ const div = document.createElement("div")
52
+ results.append(div)
53
+
54
+ // add video label
55
+ const p = document.createElement("p")
56
+ p.textContent = label
57
+ div.append(p)
58
+
59
+ // add the canvas to dom
60
+ div.append(transcode.canvas)
61
+
62
+ // run the test
63
+ await transcode.run()
64
+ }
@@ -0,0 +1,54 @@
1
+
2
+ @layer vars, basics, page;
3
+
4
+ @layer vars {
5
+ :root {
6
+ --link: cyan;
7
+ --color: #fffa;
8
+ --background: #111;
9
+ }
10
+ }
11
+
12
+ @layer basics {
13
+ * {
14
+ margin: 0;
15
+ padding: 0;
16
+ box-sizing: border-box;
17
+ }
18
+ }
19
+
20
+ @layer page {
21
+ html {
22
+ font-size: 10px;
23
+ color: var(--color);
24
+ background: var(--background);
25
+ }
26
+
27
+ body {
28
+ padding: 2em;
29
+ }
30
+
31
+ .results {
32
+ margin-top: 1em;
33
+
34
+ display: flex;
35
+ flex-direction: column;
36
+ gap: 1em;
37
+
38
+ > div {
39
+ font-size: 1.5em;
40
+ border: 1px solid color-mix(in lch, transparent, var(--color) 20%);
41
+ padding: 1em;
42
+ }
43
+
44
+ canvas {
45
+ width: 20em;
46
+ }
47
+ }
48
+
49
+ #filmstrip {
50
+ display: flex;
51
+ overflow-x: scroll;
52
+ }
53
+ }
54
+
@@ -0,0 +1,68 @@
1
+ import {Filmstrip} from "../../timeline/parts/filmstrip.js"
2
+
3
+ export async function filmstripTest(fileHandle: FileSystemFileHandle) {
4
+ const rangeSlider = document.querySelector(".range") as HTMLInputElement
5
+ const rangeView = document.querySelector(".range-view")!
6
+ const rangeSizeSlider = document.querySelector(".range-size")! as HTMLInputElement
7
+ const frequencySlider = document.querySelector(".frequency")! as HTMLInputElement
8
+ const frequencyView = document.querySelector(".frequency-view")!
9
+ const container = document.querySelector("#filmstrip")!
10
+ const FPS_10 = 1000/10 / 1000
11
+ let rangeSize = 0.5
12
+ const filmstrip = await Filmstrip.init(
13
+ fileHandle,
14
+ {
15
+ onChange(tiles) {
16
+ // Sort by time (optional, for clean ordering)
17
+ const sorted = tiles.sort((a, b) => a.time - b.time)
18
+ // Clear previous thumbnails
19
+ container.replaceChildren(
20
+ ...sorted.map(({ time, canvas }) => createLabeledCanvas(time, canvas.canvas as HTMLCanvasElement))
21
+ )
22
+ },
23
+ frequency: FPS_10,
24
+ canvasSinkOptions: {
25
+ width: 80,
26
+ height: 50,
27
+ fit: "fill"
28
+ }
29
+ }
30
+ )
31
+ rangeSizeSlider.addEventListener("change", () => {
32
+ rangeSize = +rangeSizeSlider.value
33
+ const [start, end] = [+rangeSlider.value, +rangeSlider.value+rangeSize]
34
+ filmstrip.range = [start, end]
35
+ rangeView.textContent = `visible time range: [${start}, ${end}]`
36
+ })
37
+ rangeSlider.addEventListener("change", () => {
38
+ const [start, end] = [+rangeSlider.value, +rangeSlider.value+rangeSize]
39
+ filmstrip.range = [start, end]
40
+ rangeView.textContent = `visible time range: [${start}, ${end}]`
41
+ })
42
+ frequencySlider.addEventListener("change", () => {
43
+ filmstrip.frequency = 1000/+frequencySlider.value/1000
44
+ frequencyView.textContent = `frame every ${filmstrip.frequency.toFixed(3)} second (${frequencySlider.value} frames per second)`
45
+ })
46
+ filmstrip.range = [10, 10.5]
47
+ }
48
+
49
+ function createLabeledCanvas(time: number, canvas: HTMLCanvasElement) {
50
+ const wrapper = document.createElement('div')
51
+ wrapper.style.position = 'relative'
52
+ wrapper.style.display = 'inline-block'
53
+ wrapper.style.marginRight = '4px'
54
+ wrapper.appendChild(canvas)
55
+ const label = document.createElement('div')
56
+ label.textContent = `${time.toFixed(2)}s`
57
+ label.style.position = 'absolute'
58
+ label.style.top = '2px'
59
+ label.style.right = '4px'
60
+ label.style.fontSize = '10px'
61
+ label.style.color = 'white'
62
+ label.style.background = 'rgba(0,0,0,0.6)'
63
+ label.style.padding = '2px 4px'
64
+ label.style.borderRadius = '4px'
65
+ label.style.pointerEvents = 'none'
66
+ wrapper.appendChild(label)
67
+ return wrapper
68
+ }
@@ -0,0 +1,7 @@
1
+
2
+ export async function loadVideo(url: string): Promise<ArrayBuffer> {
3
+ return fetch(url)
4
+ .then(response => response.bytes())
5
+ .then(bytes => bytes.buffer)
6
+ }
7
+
@@ -0,0 +1,44 @@
1
+ import {Driver} from "../../driver/driver.js"
2
+ import {DecoderSource} from "../../driver/fns/schematic.js"
3
+
4
+ export function setupTranscodeTest(driver: Driver, source: DecoderSource) {
5
+ const dimensions = {width: 1920, height: 1080}
6
+
7
+ const canvas = document.createElement("canvas")
8
+ canvas.width = dimensions.width
9
+ canvas.height = dimensions.height
10
+ const ctx = canvas.getContext("2d")
11
+
12
+ async function run() {
13
+ const readables = driver.decode({
14
+ source,
15
+ async onFrame(frame) {
16
+ const composed = await driver.composite([
17
+ {
18
+ kind: "image",
19
+ frame
20
+ },
21
+ {
22
+ kind: "text",
23
+ content: "omnitool",
24
+ fontSize: 50,
25
+ color: "green"
26
+ }
27
+ ])
28
+ frame.close()
29
+ ctx?.drawImage(composed, 0, 0)
30
+ return composed
31
+ }
32
+ })
33
+
34
+ await driver.encode({
35
+ readables,
36
+ config: {
37
+ audio: {codec: "opus", bitrate: 128000},
38
+ video: {codec: "vp9", bitrate: 1000000}
39
+ }
40
+ })
41
+ }
42
+
43
+ return {canvas, run}
44
+ }
@@ -0,0 +1,12 @@
1
+ import {Waveform} from "../../timeline/parts/waveform.js"
2
+
3
+ export async function waveformTest() {
4
+ const container = document.querySelector(".waveform-demo") as HTMLElement
5
+ const widthSlider = document.querySelector(".width") as HTMLInputElement
6
+ const waveform = await Waveform.init("/assets/temp/gl.mp4", container)
7
+
8
+ widthSlider.addEventListener("change", () => {
9
+ const width = +widthSlider.value
10
+ waveform.width = width
11
+ })
12
+ }
@@ -0,0 +1,15 @@
1
+
2
+ import {Driver} from "./driver.js"
3
+ import {Science, test, expect} from "@e280/science"
4
+
5
+ const workerUrl = new URL("./driver.worker.bundle.js", import.meta.url)
6
+
7
+ export default Science.suite({
8
+ "driver hello world": test(async() => {
9
+ const driver = await Driver.setup({workerUrl})
10
+ expect(driver.machina.count).is(0)
11
+ await driver.thread.work.hello()
12
+ expect(driver.machina.count).is(1)
13
+ }),
14
+ })
15
+