@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
@@ -0,0 +1,159 @@
1
+ import {
2
+ ALL_FORMATS,
3
+ CanvasSink,
4
+ CanvasSinkOptions,
5
+ Input,
6
+ InputVideoTrack,
7
+ WrappedCanvas,
8
+ } from "mediabunny"
9
+
10
+ import {DecoderSource} from '../../driver/fns/schematic.js'
11
+ import {loadDecoderSource} from '../../driver/utils/load-decoder-source.js'
12
+
13
+ export class Filmstrip {
14
+ #sink: CanvasSink
15
+ #cache: Map<number, WrappedCanvas> = new Map()
16
+ #activeRange: TimeRange = [0, 0]
17
+
18
+ private constructor(
19
+ private videoTrack: InputVideoTrack,
20
+ private options: Required<FilmstripOptions>
21
+ ) {
22
+ this.#sink = new CanvasSink(videoTrack, options.canvasSinkOptions)
23
+ }
24
+
25
+ static async init(source: DecoderSource, options: FilmstripOptions) {
26
+ const input = new Input({
27
+ formats: ALL_FORMATS,
28
+ source: await loadDecoderSource(source)
29
+ })
30
+ const videoTrack = await input.getPrimaryVideoTrack()
31
+ if(videoTrack)
32
+ return new Filmstrip(
33
+ videoTrack, {
34
+ frequency: options.frequency ?? 1,
35
+ canvasSinkOptions: options.canvasSinkOptions ?? {width: 80, height: 50, fit: "fill"},
36
+ onChange: options.onChange
37
+ })
38
+ else throw new Error("Source has no video track")
39
+ }
40
+
41
+ /**
42
+ * Sets the frequency (granularity) of filmstrip thumbnails.
43
+ * Changing this triggers a filmstrip refresh after any ongoing update finishes.
44
+ * @param value - The new frequency in seconds.
45
+ */
46
+ set frequency(value: number) {
47
+ if(value !== this.options.frequency) {
48
+ this.options.frequency = value
49
+ this.#update()
50
+ }
51
+ }
52
+
53
+ get frequency() {
54
+ return this.options.frequency
55
+ }
56
+
57
+ #computeActiveRange([start, end]: TimeRange): TimeRange {
58
+ const tileSize = end - start
59
+ return [start - tileSize, end + tileSize]
60
+ }
61
+
62
+ async #generateTiles() {
63
+ const [rangeStart, rangeEnd] = this.#activeRange
64
+ const neededTimestamps = new Set<number>()
65
+
66
+ // duration should be computed but with trim etc also
67
+ const duration = await this.videoTrack.computeDuration()
68
+ for (
69
+ let timestamp = rangeStart;
70
+ timestamp <= rangeEnd;
71
+ timestamp += this.options.frequency
72
+ ) {
73
+ // Clamp to valid time range
74
+ if (timestamp >= 0 && timestamp <= duration)
75
+ neededTimestamps.add(+timestamp.toFixed(3))
76
+ }
77
+
78
+ const missingTimestamps = [...neededTimestamps]
79
+ .filter(t => !this.#cache.has(t))
80
+
81
+ let i = 0
82
+ for await (const canvas of this.#sink.canvasesAtTimestamps(missingTimestamps)) {
83
+ if(canvas) {
84
+ const requestedTime = missingTimestamps[i++]
85
+ this.#cache.set(requestedTime, canvas)
86
+ }
87
+ }
88
+
89
+ // Dispose canvases outside the new range
90
+ for (const key of this.#cache.keys()) {
91
+ if (!neededTimestamps.has(key)) {
92
+ this.#cache.delete(key)
93
+ }
94
+ }
95
+
96
+ const tiles = [...this.#cache.entries()]
97
+ .map(([time, canvas]) => ({time, canvas}))
98
+ this.options.onChange(tiles)
99
+ }
100
+
101
+ /**
102
+ * Updates the visible time range for the filmstrip.
103
+ *
104
+ * Triggers a thumbnails update, with extended margins to preload
105
+ * thumbnails slightly outside the visible range.
106
+ * @param visibleRange - The current timeline viewport as a [start, end] tuple in seconds.
107
+ */
108
+ set range(visibleRange: TimeRange) {
109
+ const newRange = this.#computeActiveRange(visibleRange)
110
+ // Avoid redundant updates
111
+ if (
112
+ this.#activeRange[0] === newRange[0] &&
113
+ this.#activeRange[1] === newRange[1]
114
+ )
115
+ return
116
+
117
+ this.#activeRange = newRange
118
+ this.#update()
119
+ }
120
+
121
+ #updating: Promise<void> | null = null
122
+ #shouldRunAgain = false
123
+
124
+ async #update() {
125
+ // Perform update immediately. If multiple updates are requested while updating,
126
+ // only the latest one will run after the current finishes (skips intermediate ones).
127
+ if(this.#updating) {
128
+ this.#shouldRunAgain = true
129
+ return
130
+ }
131
+
132
+ this.#updating = this.#generateTiles()
133
+ await this.#updating
134
+ this.#updating = null
135
+
136
+ if(this.#shouldRunAgain) {
137
+ this.#shouldRunAgain = false
138
+ await this.#update()
139
+ }
140
+ }
141
+ /**
142
+ * Returns the cached thumbnail (if any) for a given timestamp.
143
+ * @param time - The timestamp to retrieve the canvas for.
144
+ */
145
+ getThumbnail(time: number) {
146
+ return this.#cache.get(time)
147
+ }
148
+ }
149
+
150
+ type TimeRange = [number, number]
151
+
152
+ interface FilmstripOptions {
153
+ frequency?: number
154
+ canvasSinkOptions?: CanvasSinkOptions
155
+ onChange: (tiles: {
156
+ time: number
157
+ canvas: WrappedCanvas
158
+ }[]) => void
159
+ }
@@ -0,0 +1,58 @@
1
+
2
+ import {Id, Hash} from "./basics.js"
3
+
4
+ export enum Kind {
5
+ Sequence,
6
+ Stack,
7
+ Clip,
8
+ Text,
9
+ Transition,
10
+ }
11
+
12
+ export enum Effect {
13
+ Crossfade,
14
+ }
15
+
16
+ export namespace Item {
17
+ export type Sequence = {
18
+ id: Id
19
+ kind: Kind.Sequence
20
+ children: Id[]
21
+ }
22
+
23
+ export type Stack = {
24
+ id: Id
25
+ kind: Kind.Stack
26
+ children: Id[]
27
+ }
28
+
29
+ export type Clip = {
30
+ id: Id
31
+ kind: Kind.Clip
32
+ mediaHash: Hash
33
+ start: number
34
+ duration: number
35
+ }
36
+
37
+ export type Text = {
38
+ id: Id
39
+ kind: Kind.Text
40
+ content: string
41
+ }
42
+
43
+ export type Transition = {
44
+ id: Id
45
+ kind: Kind.Transition
46
+ effect: Effect.Crossfade
47
+ duration: number
48
+ }
49
+
50
+ export type Any = (
51
+ | Sequence
52
+ | Stack
53
+ | Clip
54
+ | Text
55
+ | Transition
56
+ )
57
+ }
58
+
@@ -0,0 +1,14 @@
1
+
2
+ import {Datafile} from "../utils/datafile.js"
3
+
4
+ export class Media {
5
+ duration = 0
6
+ constructor(public datafile: Datafile) {}
7
+
8
+ static async analyze(datafile: Datafile) {
9
+ const media = new this(datafile)
10
+ media.duration = 10
11
+ return media
12
+ }
13
+ }
14
+
@@ -0,0 +1,27 @@
1
+
2
+ import {MapG} from "@e280/stz"
3
+ import {Hash} from "./basics.js"
4
+ import {Media} from "./media.js"
5
+ import {Resource} from "./resource.js"
6
+ import {Datafile} from "../utils/datafile.js"
7
+
8
+ export class ResourcePool {
9
+ #map = new MapG<Hash, Resource.Any>
10
+
11
+ /** store a media file (avoids duplicates via hash) */
12
+ async store(datafile: Datafile) {
13
+ const media = await Media.analyze(datafile)
14
+ const {hash} = media.datafile.checksum
15
+ const {filename, bytes} = media.datafile
16
+
17
+ if (this.#map.has(hash)) {
18
+ const alreadyExists = this.#map.require(hash)
19
+ alreadyExists.filename = filename
20
+ }
21
+ else
22
+ this.#map.set(hash, {kind: "media", filename, bytes})
23
+
24
+ return media
25
+ }
26
+ }
27
+
@@ -0,0 +1,11 @@
1
+
2
+ export namespace Resource {
3
+ export type Media = {
4
+ kind: "media"
5
+ filename: string
6
+ bytes: Uint8Array
7
+ }
8
+
9
+ export type Any = Media
10
+ }
11
+
@@ -0,0 +1,62 @@
1
+ import WaveSurfer from "wavesurfer.js"
2
+
3
+ import {context} from "../../context.js"
4
+ import {DecoderSource} from "../../driver/fns/schematic.js"
5
+
6
+ export class Waveform {
7
+ wavesurfer: WaveSurfer
8
+
9
+ constructor(peaks: number[], container: HTMLElement, duration: number) {
10
+ this.wavesurfer = WaveSurfer.create({
11
+ container,
12
+ waveColor: 'rgb(200, 0, 200)',
13
+ progressColor: 'rgb(100, 0, 100)',
14
+ barWidth: 10,
15
+ barRadius: 10,
16
+ barGap: 2,
17
+ peaks: [peaks],
18
+ duration
19
+ })
20
+ }
21
+
22
+ static async init(source: DecoderSource, container: HTMLElement) {
23
+ const driver = await context.driver
24
+ const reader = driver.decode({ source }).audio.getReader()
25
+
26
+ const peaks: number[] = []
27
+ let buffer: number[] = []
28
+ const samplesPerPeak = 1024
29
+ const duration = await driver.getAudioDuration(source)
30
+
31
+ while (true) {
32
+ const {done, value: audioData} = await reader.read()
33
+ if (done) break
34
+
35
+ const frames = audioData.numberOfFrames
36
+ const plane = new Float32Array(frames)
37
+ audioData.copyTo(plane, {planeIndex: 0}) // Use left channel only
38
+
39
+ for (let i = 0; i < plane.length; i++) {
40
+ buffer.push(plane[i])
41
+ if (buffer.length >= samplesPerPeak) {
42
+ const chunk = buffer.splice(0, samplesPerPeak)
43
+ const min = Math.min(...chunk)
44
+ const max = Math.max(...chunk)
45
+ peaks.push(min, max)
46
+ }
47
+ }
48
+
49
+ audioData.close()
50
+ }
51
+
52
+ return new Waveform(peaks, container, duration ?? 0)
53
+ }
54
+
55
+ // set zoom(value: number) {
56
+ // this.wavesurfer.zoom(value)
57
+ // }
58
+
59
+ set width(value: number) {
60
+ this.wavesurfer.setOptions({width: value})
61
+ }
62
+ }
@@ -0,0 +1,60 @@
1
+
2
+ import {MapG} from "@e280/stz"
3
+ import {Id} from "../parts/basics.js"
4
+ import {Media} from "../parts/media.js"
5
+ import {Effect, Item, Kind} from "../parts/item.js"
6
+
7
+ export class O {
8
+ #nextId = 0
9
+ #items = new MapG<Id, Item.Any>()
10
+
11
+ #getId() {
12
+ return this.#nextId++
13
+ }
14
+
15
+ register(item: Item.Any) {
16
+ if (!this.#items.has(item.id))
17
+ this.#items.set(item.id, item)
18
+ return item.id
19
+ }
20
+
21
+ get items() {
22
+ return [...this.#items.values()]
23
+ }
24
+
25
+ sequence = (...items: Item.Any[]): Item.Sequence => ({
26
+ id: this.#getId(),
27
+ kind: Kind.Sequence,
28
+ children: items.map(item => this.register(item)),
29
+ })
30
+
31
+ stack = (...items: Item.Any[]): Item.Stack => ({
32
+ id: this.#getId(),
33
+ kind: Kind.Stack,
34
+ children: items.map(item => this.register(item)),
35
+ })
36
+
37
+ clip = (media: Media, start?: number, duration?: number): Item.Clip => ({
38
+ id: this.#getId(),
39
+ kind: Kind.Clip,
40
+ mediaHash: media.datafile.checksum.hash,
41
+ start: start ?? 0,
42
+ duration: duration ?? media.duration,
43
+ })
44
+
45
+ text = (content: string): Item.Text => ({
46
+ id: this.#getId(),
47
+ kind: Kind.Text,
48
+ content,
49
+ })
50
+
51
+ transition = {
52
+ crossfade: (duration: number): Item.Transition => ({
53
+ id: this.#getId(),
54
+ kind: Kind.Transition,
55
+ effect: Effect.Crossfade,
56
+ duration,
57
+ }),
58
+ }
59
+ }
60
+
@@ -0,0 +1,38 @@
1
+
2
+ import {Omni} from "./omni.js"
3
+ import {dummyData} from "../utils/dummy-data.js"
4
+
5
+ //
6
+ // create an omni context
7
+ //
8
+
9
+ const omni = new Omni()
10
+
11
+ //
12
+ // load in some media resources
13
+ //
14
+
15
+ const {mediaA, mediaB} = await omni.load({
16
+ mediaA: dummyData(),
17
+ mediaB: dummyData(),
18
+ })
19
+
20
+ //
21
+ // create a timeline
22
+ //
23
+
24
+ const timeline = omni.timeline(o => o.sequence(
25
+ o.clip(mediaA),
26
+ o.transition.crossfade(600),
27
+ o.stack(
28
+ o.clip(mediaB),
29
+ o.text("hello world"),
30
+ ),
31
+ ))
32
+
33
+ //
34
+ // log the timeline
35
+ //
36
+
37
+ console.log(JSON.stringify(timeline, undefined, " "))
38
+
@@ -0,0 +1,30 @@
1
+
2
+ import {O} from "./o.js"
3
+ import {Item} from "../parts/item.js"
4
+ import {Media} from "../parts/media.js"
5
+ import {TimelineFile} from "../parts/basics.js"
6
+ import {Datafile} from "../utils/datafile.js"
7
+ import {ResourcePool} from "../parts/resource-pool.js"
8
+
9
+ export class Omni {
10
+ resources = new ResourcePool()
11
+
12
+ load = async<S extends Record<string, Promise<Datafile>>>(spec: S) => {
13
+ return Object.fromEntries(await Promise.all(Object.entries(spec).map(
14
+ async([key, value]) => [key, await this.resources.store(await value)]
15
+ ))) as {[K in keyof S]: Media}
16
+ }
17
+
18
+ timeline = (fn: (o: O) => Item.Sequence): TimelineFile => {
19
+ const o = new O()
20
+ const sequence = fn(o)
21
+ return {
22
+ format: "timeline",
23
+ info: "https://omniclip.app/",
24
+ version: 0,
25
+ root: o.register(sequence),
26
+ items: o.items,
27
+ }
28
+ }
29
+ }
30
+
@@ -0,0 +1,19 @@
1
+
2
+ import {Hex, Thumbprint} from "@e280/stz"
3
+
4
+ export class Checksum {
5
+ constructor(
6
+ public data: Uint8Array,
7
+ public bytes: Uint8Array,
8
+ public hash: string,
9
+ public nickname: string,
10
+ ) {}
11
+
12
+ static async make(data: Uint8Array) {
13
+ const bytes = new Uint8Array(await crypto.subtle.digest("SHA-256", data))
14
+ const hash = Hex.fromBytes(bytes)
15
+ const nickname = Thumbprint.sigil.fromBytes(bytes)
16
+ return new this(data, bytes, hash, nickname)
17
+ }
18
+ }
19
+
@@ -0,0 +1,21 @@
1
+
2
+ import {Checksum} from "./checksum.js"
3
+
4
+ export class Datafile {
5
+ constructor(
6
+ public bytes: Uint8Array,
7
+ public filename: string,
8
+ public checksum: Checksum,
9
+ ) {}
10
+
11
+ static async make(bytes: Uint8Array, name?: string) {
12
+ const checksum = await Checksum.make(bytes)
13
+ const filename = name ?? checksum.nickname
14
+ return new this(bytes, filename, checksum)
15
+ }
16
+
17
+ static async load(_path: string): Promise<Datafile> {
18
+ throw new Error("TODO implement")
19
+ }
20
+ }
21
+
@@ -0,0 +1,7 @@
1
+
2
+ import {Datafile} from "./datafile.js"
3
+
4
+ export const dummyData = () => Datafile.make(
5
+ new Uint8Array([0xDE, 0xAD, 0xBE, 0xEF])
6
+ )
7
+
package/x/context.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import { Driver } from "./driver/driver.js";
2
+ export declare const context: {
3
+ driver: Promise<Driver>;
4
+ };
package/x/context.js ADDED
@@ -0,0 +1,6 @@
1
+ import { Driver } from "./driver/driver.js";
2
+ const workerUrl = new URL("../driver/driver.worker.bundle.js", import.meta.url);
3
+ export const context = {
4
+ driver: Driver.setup({ workerUrl })
5
+ };
6
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../s/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,MAAM,EAAC,MAAM,oBAAoB,CAAA;AAEzC,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,mCAAmC,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAE/E,MAAM,CAAC,MAAM,OAAO,GAAG;IACtB,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,EAAC,SAAS,EAAC,CAAC;CACjC,CAAA"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,51 @@
1
+ import { context } from "../context.js";
2
+ import { waveformTest } from "./routines/waveform-test.js";
3
+ import { filmstripTest } from "./routines/filmstrip-test.js";
4
+ import { setupTranscodeTest } from "./routines/transcode-test.js";
5
+ const driver = await context.driver;
6
+ const results = document.querySelector(".results");
7
+ const fetchButton = document.querySelector(".fetch");
8
+ const importButton = document.querySelector(".import");
9
+ fetchButton?.addEventListener("click", startDemoFetch);
10
+ importButton?.addEventListener("click", startDemoImport);
11
+ waveformTest();
12
+ // hello world test
13
+ {
14
+ await driver.thread.work.hello();
15
+ if (driver.machina.count === 1)
16
+ console.log("✅ driver works");
17
+ else
18
+ console.error("❌ FAIL driver call didn't work");
19
+ }
20
+ // transcoding tests
21
+ async function startDemoImport() {
22
+ const [fileHandle] = await window.showOpenFilePicker();
23
+ const transcode = setupTranscodeTest(driver, fileHandle);
24
+ await filmstripTest(fileHandle);
25
+ run(transcode, fileHandle.name);
26
+ }
27
+ async function startDemoFetch() {
28
+ // which videos to run tests on
29
+ const videos = [
30
+ "/assets/temp/gl.mp4",
31
+ ];
32
+ // running each test in sequence
33
+ for (const url of videos) {
34
+ const transcode = setupTranscodeTest(driver, "/assets/temp/gl.mp4");
35
+ run(transcode, url);
36
+ }
37
+ }
38
+ async function run(transcode, label) {
39
+ // create result div
40
+ const div = document.createElement("div");
41
+ results.append(div);
42
+ // add video label
43
+ const p = document.createElement("p");
44
+ p.textContent = label;
45
+ div.append(p);
46
+ // add the canvas to dom
47
+ div.append(transcode.canvas);
48
+ // run the test
49
+ await transcode.run();
50
+ }
51
+ //# sourceMappingURL=demo.bundle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"demo.bundle.js","sourceRoot":"","sources":["../../s/demo/demo.bundle.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,OAAO,EAAC,MAAM,eAAe,CAAA;AACrC,OAAO,EAAC,YAAY,EAAC,MAAM,6BAA6B,CAAA;AACxD,OAAO,EAAC,aAAa,EAAC,MAAM,8BAA8B,CAAA;AAC1D,OAAO,EAAC,kBAAkB,EAAC,MAAM,8BAA8B,CAAA;AAE/D,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,MAAM,CAAA;AACnC,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAE,CAAA;AAEnD,MAAM,WAAW,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAA;AACpD,MAAM,YAAY,GAAG,QAAQ,CAAC,aAAa,CAAC,SAAS,CAAsB,CAAA;AAE3E,WAAW,EAAE,gBAAgB,CAAC,OAAO,EAAE,cAAc,CAAC,CAAA;AACtD,YAAY,EAAE,gBAAgB,CAAC,OAAO,EAAE,eAAe,CAAC,CAAA;AAExD,YAAY,EAAE,CAAA;AAEd,mBAAmB;AACnB,CAAC;IACA,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAA;IAChC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,KAAK,CAAC;QAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAA;;QACxD,OAAO,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAA;AACrD,CAAC;AAED,oBAAoB;AACpB,KAAK,UAAU,eAAe;IAE7B,MAAM,CAAC,UAAU,CAAC,GAAG,MAAM,MAAM,CAAC,kBAAkB,EAAE,CAAA;IACtD,MAAM,SAAS,GAAG,kBAAkB,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IACxD,MAAM,aAAa,CAAC,UAAU,CAAC,CAAA;IAC/B,GAAG,CAAC,SAAS,EAAE,UAAU,CAAC,IAAI,CAAC,CAAA;AAChC,CAAC;AAED,KAAK,UAAU,cAAc;IAG5B,+BAA+B;IAC/B,MAAM,MAAM,GAAG;QACd,qBAAqB;KACrB,CAAA;IAED,gCAAgC;IAChC,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QAC1B,MAAM,SAAS,GAAG,kBAAkB,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAA;QACnE,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,CAAA;IACpB,CAAC;AACF,CAAC;AAED,KAAK,UAAU,GAAG,CAAC,SAAgD,EAAE,KAAa;IACjF,oBAAoB;IACpB,MAAM,GAAG,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAA;IACzC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IAEnB,kBAAkB;IAClB,MAAM,CAAC,GAAG,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,CAAA;IACrC,CAAC,CAAC,WAAW,GAAG,KAAK,CAAA;IACrB,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;IAEb,wBAAwB;IACxB,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;IAE5B,eAAe;IACf,MAAM,SAAS,CAAC,GAAG,EAAE,CAAA;AACtB,CAAC"}