@omnimedia/omnitool 1.1.0-104 → 1.1.0-106

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 (39) hide show
  1. package/README.md +13 -0
  2. package/package.json +1 -1
  3. package/s/timeline/parts/captions.ts +2 -0
  4. package/s/timeline/parts/item.ts +18 -12
  5. package/s/timeline/parts/transitions.ts +2 -2
  6. package/s/timeline/renderers/parts/handy.ts +3 -3
  7. package/s/timeline/renderers/parts/samplers/audio/parts/init.ts +2 -0
  8. package/s/timeline/renderers/parts/samplers/visual/parts/sample.ts +10 -5
  9. package/s/timeline/renderers/parts/samplers/visual/parts/sequence.ts +3 -0
  10. package/s/timeline/renderers/renderers.test.ts +15 -0
  11. package/s/timeline/sugar/helpers.ts +30 -9
  12. package/s/timeline/sugar/o.ts +38 -9
  13. package/x/demo/demo.bundle.min.js +1 -1
  14. package/x/demo/demo.bundle.min.js.map +3 -3
  15. package/x/index.html +2 -2
  16. package/x/tests.bundle.min.js +48 -48
  17. package/x/tests.bundle.min.js.map +3 -3
  18. package/x/tests.html +1 -1
  19. package/x/timeline/parts/captions.d.ts +2 -0
  20. package/x/timeline/parts/captions.js.map +1 -1
  21. package/x/timeline/parts/item.d.ts +17 -12
  22. package/x/timeline/parts/transitions.d.ts +2 -2
  23. package/x/timeline/renderers/parts/handy.js +3 -3
  24. package/x/timeline/renderers/parts/handy.js.map +1 -1
  25. package/x/timeline/renderers/parts/samplers/audio/parts/init.js +2 -0
  26. package/x/timeline/renderers/parts/samplers/audio/parts/init.js.map +1 -1
  27. package/x/timeline/renderers/parts/samplers/visual/parts/sample.js +9 -5
  28. package/x/timeline/renderers/parts/samplers/visual/parts/sample.js.map +1 -1
  29. package/x/timeline/renderers/parts/samplers/visual/parts/sequence.js +2 -0
  30. package/x/timeline/renderers/parts/samplers/visual/parts/sequence.js.map +1 -1
  31. package/x/timeline/renderers/renderers.test.d.ts +1 -0
  32. package/x/timeline/renderers/renderers.test.js +9 -0
  33. package/x/timeline/renderers/renderers.test.js.map +1 -1
  34. package/x/timeline/sugar/helpers.d.ts +14 -5
  35. package/x/timeline/sugar/helpers.js +19 -7
  36. package/x/timeline/sugar/helpers.js.map +1 -1
  37. package/x/timeline/sugar/o.d.ts +14 -4
  38. package/x/timeline/sugar/o.js +27 -8
  39. package/x/timeline/sugar/o.js.map +1 -1
package/README.md CHANGED
@@ -56,6 +56,19 @@ const timeline = omni.timeline(o => {
56
56
  })
57
57
  ```
58
58
 
59
+ Items can have `label` and `enabled` fields:
60
+
61
+ ```ts
62
+ const visual = o.sequence(
63
+ "Main sequence",
64
+ o.video(clip, {label: "Intro"}),
65
+ o.transition.fade(500, {label: "Fade", enabled: false}),
66
+ o.text("Hello world", {label: "Title", enabled: false})
67
+ )
68
+ ```
69
+
70
+ Disabled items do not render or play audio, but still keep their timeline duration and occupy space.
71
+
59
72
  Declarative helper style (no explicit `o` in timeline declarations):
60
73
 
61
74
  ```ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omnimedia/omnitool",
3
- "version": "1.1.0-104",
3
+ "version": "1.1.0-106",
4
4
  "description": "open source video processing tools",
5
5
  "license": "MIT",
6
6
  "author": "Przemysław Gałęzki",
@@ -5,6 +5,8 @@ import {TransformOptions, Vec2} from "../types.js"
5
5
  import {Transcription, TranscriptSegment} from "../../features/speech/transcribe/types.js"
6
6
 
7
7
  export type CaptionOptions = {
8
+ label?: string
9
+ enabled?: boolean
8
10
  itemId?: Item.Caption["itemId"]
9
11
  start?: number
10
12
  duration?: number
@@ -26,11 +26,17 @@ export enum Kind {
26
26
  Image
27
27
  }
28
28
 
29
+ export type ItemBase = {
30
+ label?: string
31
+ enabled?: boolean
32
+ }
33
+
29
34
  export namespace Item {
30
35
  export type TextStyle = {
31
36
  id: Id
32
37
  kind: Kind.TextStyle
33
38
  style: TextStyleOptions
39
+ enabled?: boolean
34
40
  }
35
41
 
36
42
  export type Spatial = {
@@ -38,14 +44,14 @@ export namespace Item {
38
44
  kind: Kind.Spatial
39
45
  transform: Transform
40
46
  crop?: Crop
41
- enabled: boolean
47
+ enabled?: boolean
42
48
  }
43
49
 
44
50
  export type Animation = {
45
51
  id: Id
46
52
  kind: Kind.Animation
47
53
  anims: VisualAnimations
48
- enabled: boolean
54
+ enabled?: boolean
49
55
  }
50
56
 
51
57
  export type Filter<T extends FilterType = FilterType> = {
@@ -53,14 +59,14 @@ export namespace Item {
53
59
  kind: Kind.Filter
54
60
  type: T
55
61
  params?: FilterParams<T>
56
- enabled: boolean
62
+ enabled?: boolean
57
63
  }
58
64
 
59
65
  export type Gap = {
60
66
  id: Id
61
67
  kind: Kind.Gap
62
68
  duration: number
63
- }
69
+ } & ItemBase
64
70
 
65
71
  export type Sequence = {
66
72
  id: Id
@@ -68,7 +74,7 @@ export namespace Item {
68
74
  childrenIds: Id[]
69
75
  spatialId?: Id
70
76
  filterIds?: Id[]
71
- }
77
+ } & ItemBase
72
78
 
73
79
  export type Stack = {
74
80
  id: Id
@@ -76,7 +82,7 @@ export namespace Item {
76
82
  childrenIds: Id[]
77
83
  spatialId?: Id
78
84
  filterIds?: Id[]
79
- }
85
+ } & ItemBase
80
86
 
81
87
  export type Video = {
82
88
  id: Id
@@ -87,7 +93,7 @@ export namespace Item {
87
93
  spatialId?: Id
88
94
  animationIds?: Id[]
89
95
  filterIds?: Id[]
90
- }
96
+ } & ItemBase
91
97
 
92
98
  export type Image = {
93
99
  id: Id
@@ -97,7 +103,7 @@ export namespace Item {
97
103
  spatialId?: Id
98
104
  animationIds?: Id[]
99
105
  filterIds?: Id[]
100
- }
106
+ } & ItemBase
101
107
 
102
108
  export type Audio = {
103
109
  id: Id
@@ -106,7 +112,7 @@ export namespace Item {
106
112
  start: number
107
113
  duration: number
108
114
  gain?: number
109
- }
115
+ } & ItemBase
110
116
 
111
117
  export type Text = {
112
118
  id: Id
@@ -117,7 +123,7 @@ export namespace Item {
117
123
  animationIds?: Id[]
118
124
  styleId?: Id
119
125
  filterIds?: Id[]
120
- }
126
+ } & ItemBase
121
127
 
122
128
 
123
129
  export type Caption = {
@@ -134,14 +140,14 @@ export namespace Item {
134
140
  animationIds?: Id[]
135
141
  styleId?: Id
136
142
  filterIds?: Id[]
137
- }
143
+ } & ItemBase
138
144
 
139
145
  export type Transition = {
140
146
  id: Id
141
147
  kind: Kind.Transition
142
148
  name: TransitionName
143
149
  duration: number
144
- }
150
+ } & ItemBase
145
151
 
146
152
  export type Any = (
147
153
  | Sequence
@@ -1,4 +1,4 @@
1
- import type {Item} from "./item.js"
1
+ import type {Item, ItemBase} from "./item.js"
2
2
 
3
3
  export const transitionNames = [
4
4
  "Bounce",
@@ -78,7 +78,7 @@ export type Transition = {
78
78
  }
79
79
 
80
80
  export interface TransitionAction {
81
- (duration: number): Item.Transition
81
+ (duration: number, options?: ItemBase): Item.Transition
82
82
  }
83
83
 
84
84
  export type TransitionActions = {
@@ -110,7 +110,7 @@ function applySpatialIfAny(
110
110
  let matrix = parentMatrix
111
111
  if ("spatialId" in item && item.spatialId) {
112
112
  const spatial = items.get(item.spatialId) as Item.Spatial | undefined
113
- if (spatial?.enabled) {
113
+ if (spatial && spatial.enabled !== false) {
114
114
  const local = transformToMat6(spatial.transform)
115
115
  matrix = mul6(local, matrix)
116
116
  }
@@ -120,7 +120,7 @@ function applySpatialIfAny(
120
120
  for (const id of item.animationIds) {
121
121
  const animation = items.get(id) as Item.Animation | undefined
122
122
  const anim = animation?.anims.transform
123
- if (animation?.enabled && anim && transformActiveAt(anim, time)) {
123
+ if (animation?.enabled !== false && anim && transformActiveAt(anim, time)) {
124
124
  const local = transformToMat6(resolveTransformAnimation(time, anim))
125
125
  matrix = mul6(local, matrix)
126
126
  }
@@ -410,7 +410,7 @@ export function computeOpacity(
410
410
  for (const id of item.animationIds) {
411
411
  const animation = ctx.items.get(id) as Item.Animation | undefined
412
412
  const anim = animation?.anims.opacity
413
- if (animation?.enabled && anim && keyframesActiveAt(anim.track, time))
413
+ if (animation?.enabled !== false && anim && keyframesActiveAt(anim.track, time))
414
414
  opacity = resolveScalarAnimation(time, anim)
415
415
  }
416
416
  return opacity
@@ -15,6 +15,8 @@ export async function initStreams(
15
15
  items.map(async ({item, localTime}) => {
16
16
  if (item.kind !== Kind.Audio)
17
17
  return
18
+ if (item.enabled === false)
19
+ return
18
20
 
19
21
  const sink = await pool.getSink(item.mediaHash)
20
22
  if (!sink)
@@ -17,6 +17,9 @@ export async function sampleVisual(
17
17
  allowHandles?: boolean
18
18
  } = {}
19
19
  ): Promise<Layer[]> {
20
+ if ("enabled" in item && item.enabled === false)
21
+ return item.kind === Kind.Gap ? [{id: item.id, kind: "gap"}] : []
22
+
20
23
  const matrix = computeWorldMatrix(ctx.items, ancestors, item, time)
21
24
  const alpha = computeOpacity(ctx, item, time)
22
25
  const crop = "spatialId" in item && item.spatialId
@@ -25,7 +28,7 @@ export async function sampleVisual(
25
28
  const filters = "filterIds" in item && item.filterIds
26
29
  ? item.filterIds
27
30
  .map(id => ctx.items.get(id) as Item.Filter | undefined)
28
- .filter((filter): filter is Item.Filter => !!filter?.enabled)
31
+ .filter((filter): filter is Item.Filter => !!filter && filter.enabled !== false)
29
32
  .map(filter => ({type: filter.type, params: filter.params}) as FilterSpec)
30
33
  : undefined
31
34
 
@@ -63,9 +66,10 @@ export async function sampleVisual(
63
66
  case Kind.Text: {
64
67
  if (!options.allowHandles && (time < 0 || time >= item.duration)) return []
65
68
 
66
- const style = item.styleId
67
- ? (ctx.items.get(item.styleId) as Item.TextStyle)?.style
69
+ const textStyle = item.styleId
70
+ ? ctx.items.get(item.styleId) as Item.TextStyle | undefined
68
71
  : undefined
72
+ const style = textStyle?.enabled !== false ? textStyle?.style : undefined
69
73
 
70
74
  return [{id: item.id, kind: "text", content: item.content, style, matrix, alpha, crop, filters}]
71
75
  }
@@ -81,9 +85,10 @@ export async function sampleVisual(
81
85
  if (!segment)
82
86
  return []
83
87
 
84
- const style = item.styleId
85
- ? (ctx.items.get(item.styleId) as Item.TextStyle)?.style
88
+ const textStyle = item.styleId
89
+ ? ctx.items.get(item.styleId) as Item.TextStyle | undefined
86
90
  : undefined
91
+ const style = textStyle?.enabled !== false ? textStyle?.style : undefined
87
92
 
88
93
  return [{id: item.id, kind: "text", content: segment.text, style, matrix, alpha, crop, filters}]
89
94
  }
@@ -53,6 +53,9 @@ function sampleSequenceAt(ctx: SampleContext, seq: Item.Sequence, time: Ms) {
53
53
 
54
54
  const localTime = ms(time - cursor)
55
55
 
56
+ if (child.enabled === false)
57
+ return {isTransitioning: false, item: child, localTime} as const
58
+
56
59
  if (child.kind !== Kind.Transition)
57
60
  return {isTransitioning: false, item: child, localTime} as const
58
61
 
@@ -92,6 +92,21 @@ export default Science.suite({
92
92
  expect(textLayer[0].kind).is("text")
93
93
  }),
94
94
 
95
+ "disabled visual item keeps sequence time but does not render": test(async () => {
96
+ const {omni, videoA, resolveMedia} = await setupTest()
97
+ const {timeline} = new O({timeline: omni.timeline(o => o.sequence(
98
+ o.video(videoA, {duration: 2000, enabled: false}),
99
+ o.video(videoA, {duration: 2000}),
100
+ ))})
101
+ const sampler = createVisualSampler(resolveMedia)
102
+
103
+ const disabledLayers = await sampler.sample(timeline, ms(1000))
104
+ expect(disabledLayers.length).is(0)
105
+
106
+ const nextLayers = await sampler.sample(timeline, ms(2500))
107
+ expect(nextLayers[0].kind).is("image")
108
+ }),
109
+
95
110
  "audio mix sums overlapping chunks": test(async () => {
96
111
  const mixer = new AudioMix({chunkFrames: 4, clamp: false})
97
112
  async function *samples() {
@@ -10,7 +10,7 @@ import {filters, FilterParams, FilterType} from "../parts/filters.js"
10
10
  import {CaptionOptions, CaptionSourceItem} from "../parts/captions.js"
11
11
  import {Transcription} from "../../features/speech/transcribe/types.js"
12
12
  import {AnimationPreset, PresetOptions} from "../parts/animations/types.js"
13
- import {Crop, FilterableItem, Item, VisualAnimatableItem} from "../parts/item.js"
13
+ import {Crop, FilterableItem, Item, ItemBase, VisualAnimatableItem} from "../parts/item.js"
14
14
  import {animationPresets, visualAnimations} from "../parts/animations/registry.js"
15
15
  import {Anim, AnimateAction, Interpolation, Keyframes, TrackTransform, Transform, Vec2, VisualAnimationInput, VisualAnimations} from "../types.js"
16
16
 
@@ -22,8 +22,9 @@ type BuildPresetAnimateActions = {
22
22
  [TKey in AnimationPreset]: BuildPresetAnimateAction
23
23
  }
24
24
  type BuildTransitionActions = {
25
- [TKey in TransitionName]: (duration: number) => Build<Item.Transition>
25
+ [TKey in TransitionName]: (duration: number, options?: ItemBase) => Build<Item.Transition>
26
26
  }
27
+ type ContainerInput = [label: string, ...items: Build[]] | Build[]
27
28
 
28
29
  function createTimeline(): TimelineFile {
29
30
  return {
@@ -42,12 +43,24 @@ export function timeline(root: Build): TimelineFile {
42
43
  return o.timeline
43
44
  }
44
45
 
45
- export function sequence(...items: Build[]): Build<Item.Sequence> {
46
- return o => o.sequence(...items.map(item => item(o)))
46
+ export function sequence(...input: ContainerInput): Build<Item.Sequence> {
47
+ const [first, ...rest] = input
48
+ const label = typeof first === "string" ? first : undefined
49
+ const items = (label ? rest : input) as Build[]
50
+ return o => {
51
+ const built = items.map(item => item(o))
52
+ return label ? o.sequence(label, ...built) : o.sequence(...built)
53
+ }
47
54
  }
48
55
 
49
- export function stack(...items: Build[]): Build<Item.Stack> {
50
- return o => o.stack(...items.map(item => item(o)))
56
+ export function stack(...input: ContainerInput): Build<Item.Stack> {
57
+ const [first, ...rest] = input
58
+ const label = typeof first === "string" ? first : undefined
59
+ const items = (label ? rest : input) as Build[]
60
+ return o => {
61
+ const built = items.map(item => item(o))
62
+ return label ? o.stack(label, ...built) : o.stack(...built)
63
+ }
51
64
  }
52
65
 
53
66
  export function video(
@@ -55,6 +68,8 @@ export function video(
55
68
  options?: {
56
69
  start?: number,
57
70
  duration?: number
71
+ label?: string
72
+ enabled?: boolean
58
73
  }
59
74
  ): Build<Item.Video> {
60
75
  return o => o.video(media, options)
@@ -64,6 +79,8 @@ export function image(
64
79
  media: Media,
65
80
  options?: {
66
81
  duration?: number
82
+ label?: string
83
+ enabled?: boolean
67
84
  }
68
85
  ): Build<Item.Image> {
69
86
  return o => o.image(media, options)
@@ -75,6 +92,8 @@ export function audio(
75
92
  start?: number,
76
93
  duration?: number,
77
94
  gain?: number
95
+ label?: string
96
+ enabled?: boolean
78
97
  }
79
98
  ): Build<Item.Audio> {
80
99
  return o => o.audio(media, options)
@@ -85,6 +104,8 @@ export function text(
85
104
  options?: {
86
105
  duration?: number,
87
106
  styles?: TextStyleOptions
107
+ label?: string
108
+ enabled?: boolean
88
109
  }
89
110
  ): Build<Item.Text> {
90
111
  return o => o.text(content, options)
@@ -98,8 +119,8 @@ export function captions(
98
119
  return o => o.captions(item(o), transcript, options)
99
120
  }
100
121
 
101
- export function gap(duration: number): Build<Item.Gap> {
102
- return o => o.gap(duration)
122
+ export function gap(duration: number, options?: ItemBase): Build<Item.Gap> {
123
+ return o => o.gap(duration, options)
103
124
  }
104
125
 
105
126
  export function spatial(transform?: Transform, crop?: Crop): Build<Item.Spatial> {
@@ -232,7 +253,7 @@ export function textStyle(style: TextStyleOptions): Build<Item.TextStyle> {
232
253
 
233
254
  function makeTransitionActions(): BuildTransitionActions {
234
255
  const entries = Object.keys(transitions)
235
- .map(key => [key, (duration: number) => (o: O) => o.transition[key as TransitionName](duration)])
256
+ .map(key => [key, (duration: number, options?: ItemBase) => (o: O) => o.transition[key as TransitionName](duration, options)])
236
257
  return Object.fromEntries(entries) as BuildTransitionActions
237
258
  }
238
259
 
@@ -6,7 +6,7 @@ import {Id, TimelineFile} from "../parts/basics.js"
6
6
  import {FilterAction, FilterActions} from "../parts/filters.js"
7
7
  import {filters, FilterParams, FilterType} from "../parts/filters.js"
8
8
  import {Transcription} from "../../features/speech/transcribe/types.js"
9
- import {Crop, FilterableItem, Item, Kind, VisualAnimatableItem} from "../parts/item.js"
9
+ import {Crop, FilterableItem, Item, ItemBase, Kind, VisualAnimatableItem} from "../parts/item.js"
10
10
  import {animationPresets, makeAnimationPresets, visualAnimations} from "../parts/animations/registry.js"
11
11
  import {TransitionAction, TransitionActions, transitions, TransitionName} from "../parts/transitions.js"
12
12
  import {AnimationPreset, PresetAnimateAction, PresetAnimateActions, PresetAnimation, PresetOptions} from "../parts/animations/types.js"
@@ -17,6 +17,8 @@ type VisualAnimateActions = {
17
17
  [TKey in keyof VisualAnimations]-?: AnimateAction<TKey>
18
18
  }
19
19
 
20
+ type ContainerInput = [label: string, ...items: Item.Any[]] | Item.Any[]
21
+
20
22
  export class O {
21
23
  constructor(public state: {timeline: TimelineFile}) {}
22
24
 
@@ -50,7 +52,7 @@ export class O {
50
52
  const item = {
51
53
  id: this.getId(),
52
54
  kind: Kind.TextStyle,
53
- style
55
+ style,
54
56
  } as Item.TextStyle
55
57
  this.register(item)
56
58
  return item
@@ -62,7 +64,6 @@ export class O {
62
64
  kind: Kind.Spatial,
63
65
  transform: transform ?? this.transform(),
64
66
  crop,
65
- enabled: true
66
67
  }
67
68
  this.register(item)
68
69
  return item
@@ -73,7 +74,6 @@ export class O {
73
74
  id: this.getId(),
74
75
  kind: Kind.Animation,
75
76
  anims,
76
- enabled: true
77
77
  }
78
78
  this.register(item)
79
79
  return item
@@ -134,7 +134,6 @@ export class O {
134
134
  kind: Kind.Filter,
135
135
  type,
136
136
  params,
137
- enabled: true
138
137
  }
139
138
  this.register(item)
140
139
  return item
@@ -229,20 +228,28 @@ export class O {
229
228
  presets: this.#makePresetAnimateActions(),
230
229
  }
231
230
 
232
- sequence = (...items: Item.Any[]): Item.Sequence => {
231
+ sequence = (...input: ContainerInput): Item.Sequence => {
232
+ const [first, ...rest] = input
233
+ const label = typeof first === "string" ? first : undefined
234
+ const items = (label ? rest : input) as Item.Any[]
233
235
  const item = {
234
236
  id: this.getId(),
235
237
  kind: Kind.Sequence,
238
+ label,
236
239
  childrenIds: items.map(item => item.id)
237
240
  } as Item.Sequence
238
241
  this.register(item)
239
242
  return item
240
243
  }
241
244
 
242
- stack = (...items: Item.Any[]): Item.Stack => {
245
+ stack = (...input: ContainerInput): Item.Stack => {
246
+ const [first, ...rest] = input
247
+ const label = typeof first === "string" ? first : undefined
248
+ const items = (label ? rest : input) as Item.Any[]
243
249
  const item = {
244
250
  kind: Kind.Stack,
245
251
  id: this.getId(),
252
+ label,
246
253
  childrenIds: items.map(item => item.id)
247
254
  } as Item.Stack
248
255
  this.register(item)
@@ -254,6 +261,8 @@ export class O {
254
261
  options?: {
255
262
  start?: number,
256
263
  duration?: number
264
+ label?: string
265
+ enabled?: boolean
257
266
  }): Item.Video => {
258
267
 
259
268
  if(!media.hasVideo)
@@ -262,6 +271,8 @@ export class O {
262
271
  const item: Item.Video = {
263
272
  kind: Kind.Video,
264
273
  id: this.getId(),
274
+ label: options?.label,
275
+ enabled: options?.enabled,
265
276
  mediaHash: media.datafile.checksum.hash,
266
277
  start: options?.start ?? 0,
267
278
  duration: options?.duration ?? media.duration
@@ -274,6 +285,8 @@ export class O {
274
285
  media: Media,
275
286
  options?: {
276
287
  duration?: number
288
+ label?: string
289
+ enabled?: boolean
277
290
  }): Item.Image => {
278
291
 
279
292
  if(!media.isImage)
@@ -282,6 +295,8 @@ export class O {
282
295
  const item: Item.Image = {
283
296
  kind: Kind.Image,
284
297
  id: this.getId(),
298
+ label: options?.label,
299
+ enabled: options?.enabled,
285
300
  mediaHash: media.datafile.checksum.hash,
286
301
  duration: options?.duration ?? 2000
287
302
  }
@@ -295,6 +310,8 @@ export class O {
295
310
  start?: number,
296
311
  duration?: number,
297
312
  gain?: number
313
+ label?: string
314
+ enabled?: boolean
298
315
  }): Item.Audio => {
299
316
 
300
317
  if(!media.hasAudio)
@@ -303,6 +320,8 @@ export class O {
303
320
  const item: Item.Audio = {
304
321
  kind: Kind.Audio,
305
322
  id: this.getId(),
323
+ label: options?.label,
324
+ enabled: options?.enabled,
306
325
  mediaHash: media.datafile.checksum.hash,
307
326
  start: options?.start ?? 0,
308
327
  duration: options?.duration ?? media.duration,
@@ -315,10 +334,14 @@ export class O {
315
334
  text = (content: string, options?: {
316
335
  duration?: number,
317
336
  styles?: TextStyleOptions
337
+ label?: string
338
+ enabled?: boolean
318
339
  }): Item.Text => {
319
340
 
320
341
  const item = {
321
342
  id: this.getId(),
343
+ label: options?.label,
344
+ enabled: options?.enabled,
322
345
  content,
323
346
  kind: Kind.Text,
324
347
  duration: options?.duration ?? 2000
@@ -341,6 +364,8 @@ export class O {
341
364
  const item: Item.Caption = {
342
365
  id: this.getId(),
343
366
  kind: Kind.Caption,
367
+ label: options?.label,
368
+ enabled: options?.enabled,
344
369
  transcript,
345
370
  itemId: options?.itemId,
346
371
  start,
@@ -386,10 +411,12 @@ export class O {
386
411
  {presets: this.#makeCaptionPresetActions()}
387
412
  ) as CaptionActions
388
413
 
389
- gap = (duration: number): Item.Gap => {
414
+ gap = (duration: number, options?: ItemBase): Item.Gap => {
390
415
  const item = {
391
416
  id: this.getId(),
392
417
  kind: Kind.Gap,
418
+ label: options?.label,
419
+ enabled: options?.enabled,
393
420
  duration
394
421
  } as Item.Gap
395
422
  this.register(item)
@@ -397,10 +424,12 @@ export class O {
397
424
  }
398
425
 
399
426
  #makeTransition = (key: TransitionName): TransitionAction => {
400
- return (duration: number): Item.Transition => {
427
+ return (duration: number, options?: ItemBase): Item.Transition => {
401
428
  const item: Item.Transition = {
402
429
  id: this.getId(),
403
430
  kind: Kind.Transition,
431
+ label: options?.label,
432
+ enabled: options?.enabled,
404
433
  name: transitions[key].name,
405
434
  duration,
406
435
  }