@pixerate/editor 0.78.0 → 0.79.0

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.
@@ -5,6 +5,84 @@ import '../easing-lM0Xo9Ni.cjs';
5
5
  import '../types-DsiXGmjo.cjs';
6
6
  import '../schema-Di0CS6Pe.cjs';
7
7
 
8
+ /**
9
+ * Keyframes for a clip's opacity, volume and transform. A property is either a plain number or
10
+ * `{ keys }`: values at clip-relative times (ms from the clip's start), each with the easing of the
11
+ * segment that leaves it. Pure functions only, so previews, renders and exports agree.
12
+ *
13
+ * Easing is stored as CSS `cubic-bezier(x1, y1, x2, y2)` control points (named easings are the CSS
14
+ * ones), which is also what After Effects / Lottie speed-and-influence handles convert to exactly.
15
+ */
16
+ /** How a segment moves from its key to the next: a named curve, `hold` (jump at the next key) or CSS cubic-bezier points. */
17
+ type VideoEase = 'linear' | 'hold' | 'ease-in' | 'ease-out' | 'ease-in-out' | [number, number, number, number];
18
+ declare const VIDEO_EASE_NAMES: readonly ["linear", "hold", "ease-in", "ease-out", "ease-in-out"];
19
+ interface VideoKey {
20
+ /** Clip-relative time (ms from the clip's start). */
21
+ t: number;
22
+ v: number;
23
+ /** The segment from this key to the next (default linear). Ignored on the last key. */
24
+ ease?: VideoEase;
25
+ }
26
+ interface VideoKeyframes {
27
+ /** In time order, at least one. */
28
+ keys: VideoKey[];
29
+ }
30
+ /** A number, or keyframes for it. */
31
+ type VideoAnimatable = number | VideoKeyframes;
32
+ /** The clip properties that can be keyframed: `opacity`, `volume` (media) and the transform's fields. */
33
+ type VideoKeyframeProperty = 'opacity' | 'volume' | 'x' | 'y' | 'scale' | 'rotation';
34
+ declare const VIDEO_KEYFRAME_PROPERTIES: readonly ["opacity", "volume", "x", "y", "scale", "rotation"];
35
+ declare const VIDEO_TRANSFORM_PROPERTIES: readonly ["x", "y", "scale", "rotation"];
36
+ /** Each property's default and allowed range. */
37
+ declare const VIDEO_KEYFRAME_LIMITS: Record<VideoKeyframeProperty, {
38
+ default: number;
39
+ min?: number;
40
+ max?: number;
41
+ }>;
42
+ declare const isVideoKeyframes: (v: unknown) => v is VideoKeyframes;
43
+ /** The cubic-bezier points for an ease (`null` for linear and hold). */
44
+ declare function videoEaseBezier(ease: VideoEase | undefined): [number, number, number, number] | null;
45
+ /** How far (0–1 in, any y out) an ease has got at progress `p` (0–1) through its segment. */
46
+ declare function videoEaseProgress(ease: VideoEase | undefined, p: number): number;
47
+ /**
48
+ * A property's value at clip-relative time `clipTime` (ms): the number itself, or the keys
49
+ * interpolated (held before the first and after the last). `fallback` is used when it is unset.
50
+ */
51
+ declare function videoValueAt(prop: VideoAnimatable | undefined, clipTime: number, fallback?: number): number;
52
+ /** A property's keys (a plain number or unset gives none). */
53
+ declare const videoKeysOf: (prop: VideoAnimatable | undefined) => VideoKey[];
54
+ /** Shifts every key by `dt` ms (or scales times by `k` first); numbers pass through. */
55
+ declare function retimeVideoAnimatable<T extends VideoAnimatable | undefined>(prop: T, dt: number, k?: number): T;
56
+ /**
57
+ * Splits keyframes at clip time `at`: the keys before it (with a key added at `at`) and the keys
58
+ * after it, moved to start at 0 (with a key added at 0). Eased segments across the cut are split
59
+ * into two curves that trace the same path, so the two halves play exactly as the whole did.
60
+ */
61
+ declare function splitVideoAnimatable<T extends VideoAnimatable | undefined>(prop: T, at: number): [T, T];
62
+ /** The keys sorted by time, refusing two at the same time. */
63
+ declare function sortVideoKeys(keys: readonly VideoKey[]): VideoKey[];
64
+ /**
65
+ * Keys with one set (or replaced) at `t`: a plain value becomes keys holding it and the new one.
66
+ * For a keyframe toggle or "add key at playhead".
67
+ */
68
+ declare function setVideoKey(prop: VideoAnimatable | undefined, key: VideoKey, fallback: number): VideoKeyframes;
69
+ /** Keys with the one at `t` removed; the value it then holds when none are left. */
70
+ declare function removeVideoKey(prop: VideoKeyframes, t: number): VideoAnimatable;
71
+ /**
72
+ * Breakpoints `[clipTime, value]` tracing a property as straight lines: linear segments as they
73
+ * are, holds as steps, eased segments sampled every `step` ms (about two frames).
74
+ */
75
+ declare function videoKeyframeBreakpoints(prop: VideoKeyframes, step: number): {
76
+ points: [number, number][];
77
+ holds: Set<number>;
78
+ };
79
+ /**
80
+ * An ffmpeg expression for a property, given `time`: an expression for the clip-relative time in
81
+ * ms (e.g. `(t*1000-1500)`). Piecewise linear `if(lt(…))` chains; eased segments are baked to
82
+ * breakpoints every `step` ms.
83
+ */
84
+ declare function videoKeyframeFfmpegExpr(prop: VideoAnimatable | undefined, time: string, fallback: number, step: number): string;
85
+
8
86
  /**
9
87
  * A video project is plain JSON: the media it uses (assets), tracks of clips placed on a timeline,
10
88
  * and markers. Every time is in milliseconds; `fps` sets the frame grid edits snap to and exports use.
@@ -12,6 +90,7 @@ import '../schema-Di0CS6Pe.cjs';
12
90
  * Tracks are listed top to bottom as a timeline shows them: on screen, a clip on an earlier video
13
91
  * track draws over one on a later track. Clips on one track never overlap.
14
92
  */
93
+
15
94
  type VideoAssetKind = 'video' | 'audio' | 'image';
16
95
  /** A piece of source media. The project refers to it by id; `src` is whatever the host can load. */
17
96
  interface VideoAsset {
@@ -45,15 +124,15 @@ interface VideoTrack {
45
124
  volume?: number;
46
125
  clips: VideoClip[];
47
126
  }
48
- /** Where a picture sits in the frame, after it has been fitted. */
127
+ /** Where a picture sits in the frame, after it has been fitted. Each field may be keyframed. */
49
128
  interface VideoTransform {
50
129
  /** Offset of the centre from the frame's centre, in pixels (default 0). */
51
- x?: number;
52
- y?: number;
130
+ x?: VideoAnimatable;
131
+ y?: VideoAnimatable;
53
132
  /** 1 = fitted size (default 1). */
54
- scale?: number;
133
+ scale?: VideoAnimatable;
55
134
  /** Degrees clockwise (default 0). */
56
- rotation?: number;
135
+ rotation?: VideoAnimatable;
57
136
  }
58
137
  /** How a picture is fitted to the frame before its transform: like CSS `object-fit`. */
59
138
  type VideoFit = 'contain' | 'cover' | 'fill' | 'none';
@@ -83,8 +162,8 @@ interface VideoClipBase {
83
162
  start: number;
84
163
  /** How long it lasts on the timeline (ms). */
85
164
  duration: number;
86
- /** 0–1 (default 1). */
87
- opacity?: number;
165
+ /** 0–1 (default 1), or keyframes (`{ keys: [{ t, v, ease }] }`, `t` in ms from the clip's start). */
166
+ opacity?: VideoAnimatable;
88
167
  transform?: VideoTransform;
89
168
  /** Default contain. */
90
169
  fit?: VideoFit;
@@ -103,8 +182,8 @@ interface VideoMediaClip extends VideoClipBase {
103
182
  in?: number;
104
183
  /** Playback speed (default 1): it uses `duration × speed` of the source. */
105
184
  speed?: number;
106
- /** Gain (1 = unchanged, default 1). */
107
- volume?: number;
185
+ /** Gain (1 = unchanged, default 1), or keyframes. */
186
+ volume?: VideoAnimatable;
108
187
  /** Silences this clip. */
109
188
  muted?: boolean;
110
189
  }
@@ -153,6 +232,20 @@ interface VideoProject {
153
232
  markers?: VideoMarker[];
154
233
  }
155
234
 
235
+ /**
236
+ * Keyframes `{ keys: [{ t, v, ease }] }`: at least one key, in strictly increasing time (clip-relative
237
+ * ms), each value within `range`.
238
+ */
239
+ declare function parseVideoKeyframes(v: unknown, path: string, { min, max }?: {
240
+ min?: number;
241
+ max?: number;
242
+ }): VideoKeyframes;
243
+ /** A number within `range`, or keyframes for one. */
244
+ declare function parseVideoAnimatable(v: unknown, path: string, range?: {
245
+ min?: number;
246
+ max?: number;
247
+ optional?: boolean;
248
+ }): VideoAnimatable | undefined;
156
249
  declare function parseVideoTransform(v: unknown, path: string): VideoTransform | undefined;
157
250
  declare function parseVideoTransition(v: unknown, path: string): VideoTransition | undefined;
158
251
  declare function parseVideoAsset(v: unknown, path?: string): VideoAsset;
@@ -264,8 +357,13 @@ declare function videoClipsAt(project: VideoProject, time: number): {
264
357
  clip: VideoClip;
265
358
  }[];
266
359
 
360
+ /** A clip's keyframed properties (`opacity`, `volume`, `x`…) with their keys. */
361
+ declare function videoClipKeyframes(clip: VideoClip): {
362
+ property: string;
363
+ keys: ReturnType<typeof videoKeysOf>;
364
+ }[];
267
365
  type VideoIssueSeverity = 'error' | 'warning' | 'note';
268
- declare const VIDEO_ISSUE_CODES: readonly ["missing-asset", "wrong-track", "overlap", "source-overrun", "transition-without-cut", "transition-too-long", "transition-handles", "fades-too-long", "unknown-duration", "unused-asset", "marker-past-end", "empty"];
366
+ declare const VIDEO_ISSUE_CODES: readonly ["missing-asset", "wrong-track", "overlap", "source-overrun", "transition-without-cut", "transition-too-long", "transition-handles", "fades-too-long", "keyframes-outside-clip", "unknown-duration", "unused-asset", "marker-past-end", "empty"];
269
367
  type VideoIssueCode = (typeof VIDEO_ISSUE_CODES)[number];
270
368
  interface VideoIssue {
271
369
  code: VideoIssueCode;
@@ -380,6 +478,13 @@ type VideoEdit = ({
380
478
  op: 'set_transition';
381
479
  clip: string;
382
480
  transition: VideoTransition | null;
481
+ }
482
+ /** Keys for one property (in any order; clip-relative ms), or null to stop animating it and keep its value at the clip's start. */
483
+ | {
484
+ op: 'set_keyframes';
485
+ clip: string;
486
+ property: VideoKeyframeProperty;
487
+ keys: VideoKey[] | null;
383
488
  } | {
384
489
  op: 'close_gaps';
385
490
  track: string;
@@ -493,22 +598,31 @@ interface VideoFrame {
493
598
  layers: VideoLayer[];
494
599
  audio: VideoAudioSource[];
495
600
  }
496
- /** Places a picture of `size` in the frame by `fit`, then the clip's transform. */
601
+ /** A transform's values at clip-relative time `clipTime` (ms), keyframes resolved and defaults filled in. */
602
+ declare function videoTransformAt(transform: VideoClip['transform'], clipTime?: number): {
603
+ x: number;
604
+ y: number;
605
+ scale: number;
606
+ rotation: number;
607
+ };
608
+ /** Places a picture of `size` in the frame by `fit`, then the clip's transform at clip-relative `clipTime` (ms). */
497
609
  declare function placeVideoRect(frame: {
498
610
  width: number;
499
611
  height: number;
500
612
  }, size: {
501
613
  width: number;
502
614
  height: number;
503
- }, fit?: VideoFit, transform?: VideoClip['transform']): VideoRect;
615
+ }, fit?: VideoFit, transform?: VideoClip['transform'], clipTime?: number): VideoRect;
504
616
  /** A title's text and style with the defaults filled in. */
505
617
  declare function videoTextStyle(clip: VideoTextClip): NonNullable<VideoLayer['text']>;
506
618
  /**
507
619
  * A frame holding only one title, fully opaque on a transparent background, placed as the preview
508
620
  * places it: draw it with `drawVideoFrame` and save the canvas as a PNG to give the ffmpeg export
509
621
  * titles without `drawtext` (`VideoFfmpegOptions.titleImage`). Fades and opacity are left to the export.
622
+ * A keyframed transform is placed as it is at `clipTime` (the export passes titles with keyframed
623
+ * transforms without them, and moves the image itself).
510
624
  */
511
- declare function videoTitleFrame(project: VideoProject, clip: VideoTextClip): VideoFrame;
625
+ declare function videoTitleFrame(project: VideoProject, clip: VideoTextClip, clipTime?: number): VideoFrame;
512
626
  /**
513
627
  * What plays at `time` (ms): the pictures to draw, back to front, with their placement, opacity
514
628
  * and the moment of source each shows, and the sounds with their gain. A pure function of the
@@ -854,13 +968,13 @@ declare function describeVideoProject(project: VideoProject): {
854
968
  asset: string;
855
969
  in?: number;
856
970
  speed?: number;
857
- volume?: number;
971
+ volume?: VideoAnimatable;
858
972
  muted?: boolean;
859
973
  id: string;
860
974
  name?: string;
861
975
  start: number;
862
976
  duration: number;
863
- opacity?: number;
977
+ opacity?: VideoAnimatable;
864
978
  transform?: VideoTransform;
865
979
  fit?: VideoFit;
866
980
  fadeIn?: number;
@@ -883,7 +997,7 @@ declare function describeVideoProject(project: VideoProject): {
883
997
  name?: string;
884
998
  start: number;
885
999
  duration: number;
886
- opacity?: number;
1000
+ opacity?: VideoAnimatable;
887
1001
  transform?: VideoTransform;
888
1002
  fit?: VideoFit;
889
1003
  fadeIn?: number;
@@ -901,7 +1015,7 @@ declare function describeVideoProject(project: VideoProject): {
901
1015
  name?: string;
902
1016
  start: number;
903
1017
  duration: number;
904
- opacity?: number;
1018
+ opacity?: VideoAnimatable;
905
1019
  transform?: VideoTransform;
906
1020
  fit?: VideoFit;
907
1021
  fadeIn?: number;
@@ -999,5 +1113,38 @@ declare function videoAssetKindOf(typeOrName: string): VideoAssetKind | null;
999
1113
  * `<video>`, `<audio>` or `<img>`. Rejects with a readable error when it cannot be loaded.
1000
1114
  */
1001
1115
  declare function probeVideoMedia(src: string, kind: VideoAssetKind, timeout?: number): Promise<VideoMediaProbe>;
1116
+ /** One animatable property of a clip as an inspector shows it at the playhead. */
1117
+ interface VideoKeyframeRow {
1118
+ property: VideoKeyframeProperty;
1119
+ label: string;
1120
+ /** Its value at the playhead (the playhead held inside the clip). */
1121
+ value: number;
1122
+ /** Whether it has keyframes. */
1123
+ keyed: boolean;
1124
+ /** How many keys it has. */
1125
+ keys: number;
1126
+ /** Whether a key sits at the playhead. */
1127
+ onKey: boolean;
1128
+ /** The playhead in clip time (ms), where new keys go. */
1129
+ clipTime: number;
1130
+ step: number;
1131
+ min?: number;
1132
+ max?: number;
1133
+ }
1134
+ /**
1135
+ * The clip's animatable properties for an inspector at timeline `time`: opacity and the transform
1136
+ * for pictures, volume for sound, each with its value there and whether it is keyed.
1137
+ */
1138
+ declare function videoKeyframeRows(project: VideoProject, clipId: string, time: number): VideoKeyframeRow[];
1139
+ /**
1140
+ * Typing a value in an inspector: on a keyed property it sets (or moves) the key at the playhead;
1141
+ * otherwise it sets the plain value (`undefined` resets it).
1142
+ */
1143
+ declare function videoKeyframeValueEdit(project: VideoProject, clipId: string, property: VideoKeyframeProperty, time: number, value: number | undefined): VideoEdit | null;
1144
+ /**
1145
+ * The keyframe toggle: adds a key at the playhead holding the current value (the first key turns
1146
+ * the plain value into keys), or removes the key there (the last one leaves its value plain).
1147
+ */
1148
+ declare function videoKeyframeToggleEdit(project: VideoProject, clipId: string, property: VideoKeyframeProperty, time: number): VideoEdit | null;
1002
1149
 
1003
- export { type CreateVideoProjectOptions, type NewVideoClip, VIDEO_CLIP_TYPES, VIDEO_DEFAULT_STILL_DURATION, VIDEO_DEFAULT_TITLE_DURATION, VIDEO_EDIT_OPS, VIDEO_FITS, VIDEO_ISSUE_CODES, VIDEO_TIME_EPSILON, VIDEO_TRANSITION_TYPES, VIDEO_WIPE_DIRECTIONS, type VideoAsset, type VideoAssetKind, type VideoAudioSource, type VideoCanvasContext, type VideoClip, type VideoClipDragInput, type VideoClipDragMode, type VideoClipDragPlan, type VideoClipLocation, type VideoClipType, type VideoColorClip, type VideoEdit, type VideoEditOp, type VideoEditOutcome, type VideoEditResult, VideoEditorController, type VideoEditorOptions, type VideoEditorState, type VideoEditorToolProviderOptions, type VideoEdlOptions, type VideoFfmpegExport, type VideoFfmpegOptions, type VideoFit, type VideoFrame, type VideoGap, type VideoIssue, type VideoIssueCode, type VideoIssueSeverity, type VideoLayer, type VideoMarker, type VideoMediaClip, type VideoMediaProbe, VideoPreviewEngine, type VideoPreviewImage, type VideoPreviewMedia, type VideoPreviewOptions, type VideoPreviewSource, type VideoProject, type VideoRect, type VideoRulerTick, type VideoSnapOptions, type VideoSvgOptions, type VideoTextClip, type VideoTrack, type VideoTrackKind, type VideoTransform, type VideoTransition, type VideoTransitionType, type VideoWipeDirection, allVideoClipIds, applyVideoEdits, checkVideoProject, createVideoEditorToolProvider, createVideoProject, describeVideoProject, drawVideoFrame, ffmpegFilterValue, findVideoClip, findVideoGaps, formatVideoSeconds, formatVideoTimecode, nextFreeVideoSlot, parseVideoAsset, parseVideoClip, parseVideoMarker, parseVideoProject, parseVideoTrack, parseVideoTransform, parseVideoTransition, placeVideoRect, planVideoClipDrag, probeVideoMedia, snapToVideoFrame, snapVideoTime, sortedVideoClips, toVideoEdl, toVideoFfmpeg, videoAssetDropEdit, videoAssetKindOf, videoAssetMap, videoClipAfter, videoClipBefore, videoClipEnd, videoClipHandles, videoClipLabel, videoClipOverhang, videoClipSourceRange, videoClipSpeed, videoClipTint, videoClipsAt, videoContactSheetSvg, videoDropTracks, videoFrameAt, videoFrameDuration, videoFrameToSvg, videoFrames, videoIssueKey, videoOverlap, videoProjectDuration, videoProjectSvg, videoRulerTicks, videoSnapPoints, videoTextStyle, videoTitleFrame, videoWipeRect };
1150
+ export { type CreateVideoProjectOptions, type NewVideoClip, VIDEO_CLIP_TYPES, VIDEO_DEFAULT_STILL_DURATION, VIDEO_DEFAULT_TITLE_DURATION, VIDEO_EASE_NAMES, VIDEO_EDIT_OPS, VIDEO_FITS, VIDEO_ISSUE_CODES, VIDEO_KEYFRAME_LIMITS, VIDEO_KEYFRAME_PROPERTIES, VIDEO_TIME_EPSILON, VIDEO_TRANSFORM_PROPERTIES, VIDEO_TRANSITION_TYPES, VIDEO_WIPE_DIRECTIONS, type VideoAnimatable, type VideoAsset, type VideoAssetKind, type VideoAudioSource, type VideoCanvasContext, type VideoClip, type VideoClipDragInput, type VideoClipDragMode, type VideoClipDragPlan, type VideoClipLocation, type VideoClipType, type VideoColorClip, type VideoEase, type VideoEdit, type VideoEditOp, type VideoEditOutcome, type VideoEditResult, VideoEditorController, type VideoEditorOptions, type VideoEditorState, type VideoEditorToolProviderOptions, type VideoEdlOptions, type VideoFfmpegExport, type VideoFfmpegOptions, type VideoFit, type VideoFrame, type VideoGap, type VideoIssue, type VideoIssueCode, type VideoIssueSeverity, type VideoKey, type VideoKeyframeProperty, type VideoKeyframeRow, type VideoKeyframes, type VideoLayer, type VideoMarker, type VideoMediaClip, type VideoMediaProbe, VideoPreviewEngine, type VideoPreviewImage, type VideoPreviewMedia, type VideoPreviewOptions, type VideoPreviewSource, type VideoProject, type VideoRect, type VideoRulerTick, type VideoSnapOptions, type VideoSvgOptions, type VideoTextClip, type VideoTrack, type VideoTrackKind, type VideoTransform, type VideoTransition, type VideoTransitionType, type VideoWipeDirection, allVideoClipIds, applyVideoEdits, checkVideoProject, createVideoEditorToolProvider, createVideoProject, describeVideoProject, drawVideoFrame, ffmpegFilterValue, findVideoClip, findVideoGaps, formatVideoSeconds, formatVideoTimecode, isVideoKeyframes, nextFreeVideoSlot, parseVideoAnimatable, parseVideoAsset, parseVideoClip, parseVideoKeyframes, parseVideoMarker, parseVideoProject, parseVideoTrack, parseVideoTransform, parseVideoTransition, placeVideoRect, planVideoClipDrag, probeVideoMedia, removeVideoKey, retimeVideoAnimatable, setVideoKey, snapToVideoFrame, snapVideoTime, sortVideoKeys, sortedVideoClips, splitVideoAnimatable, toVideoEdl, toVideoFfmpeg, videoAssetDropEdit, videoAssetKindOf, videoAssetMap, videoClipAfter, videoClipBefore, videoClipEnd, videoClipHandles, videoClipKeyframes, videoClipLabel, videoClipOverhang, videoClipSourceRange, videoClipSpeed, videoClipTint, videoClipsAt, videoContactSheetSvg, videoDropTracks, videoEaseBezier, videoEaseProgress, videoFrameAt, videoFrameDuration, videoFrameToSvg, videoFrames, videoIssueKey, videoKeyframeBreakpoints, videoKeyframeFfmpegExpr, videoKeyframeRows, videoKeyframeToggleEdit, videoKeyframeValueEdit, videoKeysOf, videoOverlap, videoProjectDuration, videoProjectSvg, videoRulerTicks, videoSnapPoints, videoTextStyle, videoTitleFrame, videoTransformAt, videoValueAt, videoWipeRect };
@@ -5,6 +5,84 @@ import '../easing-lM0Xo9Ni.js';
5
5
  import '../types-DsiXGmjo.js';
6
6
  import '../schema-Di0CS6Pe.js';
7
7
 
8
+ /**
9
+ * Keyframes for a clip's opacity, volume and transform. A property is either a plain number or
10
+ * `{ keys }`: values at clip-relative times (ms from the clip's start), each with the easing of the
11
+ * segment that leaves it. Pure functions only, so previews, renders and exports agree.
12
+ *
13
+ * Easing is stored as CSS `cubic-bezier(x1, y1, x2, y2)` control points (named easings are the CSS
14
+ * ones), which is also what After Effects / Lottie speed-and-influence handles convert to exactly.
15
+ */
16
+ /** How a segment moves from its key to the next: a named curve, `hold` (jump at the next key) or CSS cubic-bezier points. */
17
+ type VideoEase = 'linear' | 'hold' | 'ease-in' | 'ease-out' | 'ease-in-out' | [number, number, number, number];
18
+ declare const VIDEO_EASE_NAMES: readonly ["linear", "hold", "ease-in", "ease-out", "ease-in-out"];
19
+ interface VideoKey {
20
+ /** Clip-relative time (ms from the clip's start). */
21
+ t: number;
22
+ v: number;
23
+ /** The segment from this key to the next (default linear). Ignored on the last key. */
24
+ ease?: VideoEase;
25
+ }
26
+ interface VideoKeyframes {
27
+ /** In time order, at least one. */
28
+ keys: VideoKey[];
29
+ }
30
+ /** A number, or keyframes for it. */
31
+ type VideoAnimatable = number | VideoKeyframes;
32
+ /** The clip properties that can be keyframed: `opacity`, `volume` (media) and the transform's fields. */
33
+ type VideoKeyframeProperty = 'opacity' | 'volume' | 'x' | 'y' | 'scale' | 'rotation';
34
+ declare const VIDEO_KEYFRAME_PROPERTIES: readonly ["opacity", "volume", "x", "y", "scale", "rotation"];
35
+ declare const VIDEO_TRANSFORM_PROPERTIES: readonly ["x", "y", "scale", "rotation"];
36
+ /** Each property's default and allowed range. */
37
+ declare const VIDEO_KEYFRAME_LIMITS: Record<VideoKeyframeProperty, {
38
+ default: number;
39
+ min?: number;
40
+ max?: number;
41
+ }>;
42
+ declare const isVideoKeyframes: (v: unknown) => v is VideoKeyframes;
43
+ /** The cubic-bezier points for an ease (`null` for linear and hold). */
44
+ declare function videoEaseBezier(ease: VideoEase | undefined): [number, number, number, number] | null;
45
+ /** How far (0–1 in, any y out) an ease has got at progress `p` (0–1) through its segment. */
46
+ declare function videoEaseProgress(ease: VideoEase | undefined, p: number): number;
47
+ /**
48
+ * A property's value at clip-relative time `clipTime` (ms): the number itself, or the keys
49
+ * interpolated (held before the first and after the last). `fallback` is used when it is unset.
50
+ */
51
+ declare function videoValueAt(prop: VideoAnimatable | undefined, clipTime: number, fallback?: number): number;
52
+ /** A property's keys (a plain number or unset gives none). */
53
+ declare const videoKeysOf: (prop: VideoAnimatable | undefined) => VideoKey[];
54
+ /** Shifts every key by `dt` ms (or scales times by `k` first); numbers pass through. */
55
+ declare function retimeVideoAnimatable<T extends VideoAnimatable | undefined>(prop: T, dt: number, k?: number): T;
56
+ /**
57
+ * Splits keyframes at clip time `at`: the keys before it (with a key added at `at`) and the keys
58
+ * after it, moved to start at 0 (with a key added at 0). Eased segments across the cut are split
59
+ * into two curves that trace the same path, so the two halves play exactly as the whole did.
60
+ */
61
+ declare function splitVideoAnimatable<T extends VideoAnimatable | undefined>(prop: T, at: number): [T, T];
62
+ /** The keys sorted by time, refusing two at the same time. */
63
+ declare function sortVideoKeys(keys: readonly VideoKey[]): VideoKey[];
64
+ /**
65
+ * Keys with one set (or replaced) at `t`: a plain value becomes keys holding it and the new one.
66
+ * For a keyframe toggle or "add key at playhead".
67
+ */
68
+ declare function setVideoKey(prop: VideoAnimatable | undefined, key: VideoKey, fallback: number): VideoKeyframes;
69
+ /** Keys with the one at `t` removed; the value it then holds when none are left. */
70
+ declare function removeVideoKey(prop: VideoKeyframes, t: number): VideoAnimatable;
71
+ /**
72
+ * Breakpoints `[clipTime, value]` tracing a property as straight lines: linear segments as they
73
+ * are, holds as steps, eased segments sampled every `step` ms (about two frames).
74
+ */
75
+ declare function videoKeyframeBreakpoints(prop: VideoKeyframes, step: number): {
76
+ points: [number, number][];
77
+ holds: Set<number>;
78
+ };
79
+ /**
80
+ * An ffmpeg expression for a property, given `time`: an expression for the clip-relative time in
81
+ * ms (e.g. `(t*1000-1500)`). Piecewise linear `if(lt(…))` chains; eased segments are baked to
82
+ * breakpoints every `step` ms.
83
+ */
84
+ declare function videoKeyframeFfmpegExpr(prop: VideoAnimatable | undefined, time: string, fallback: number, step: number): string;
85
+
8
86
  /**
9
87
  * A video project is plain JSON: the media it uses (assets), tracks of clips placed on a timeline,
10
88
  * and markers. Every time is in milliseconds; `fps` sets the frame grid edits snap to and exports use.
@@ -12,6 +90,7 @@ import '../schema-Di0CS6Pe.js';
12
90
  * Tracks are listed top to bottom as a timeline shows them: on screen, a clip on an earlier video
13
91
  * track draws over one on a later track. Clips on one track never overlap.
14
92
  */
93
+
15
94
  type VideoAssetKind = 'video' | 'audio' | 'image';
16
95
  /** A piece of source media. The project refers to it by id; `src` is whatever the host can load. */
17
96
  interface VideoAsset {
@@ -45,15 +124,15 @@ interface VideoTrack {
45
124
  volume?: number;
46
125
  clips: VideoClip[];
47
126
  }
48
- /** Where a picture sits in the frame, after it has been fitted. */
127
+ /** Where a picture sits in the frame, after it has been fitted. Each field may be keyframed. */
49
128
  interface VideoTransform {
50
129
  /** Offset of the centre from the frame's centre, in pixels (default 0). */
51
- x?: number;
52
- y?: number;
130
+ x?: VideoAnimatable;
131
+ y?: VideoAnimatable;
53
132
  /** 1 = fitted size (default 1). */
54
- scale?: number;
133
+ scale?: VideoAnimatable;
55
134
  /** Degrees clockwise (default 0). */
56
- rotation?: number;
135
+ rotation?: VideoAnimatable;
57
136
  }
58
137
  /** How a picture is fitted to the frame before its transform: like CSS `object-fit`. */
59
138
  type VideoFit = 'contain' | 'cover' | 'fill' | 'none';
@@ -83,8 +162,8 @@ interface VideoClipBase {
83
162
  start: number;
84
163
  /** How long it lasts on the timeline (ms). */
85
164
  duration: number;
86
- /** 0–1 (default 1). */
87
- opacity?: number;
165
+ /** 0–1 (default 1), or keyframes (`{ keys: [{ t, v, ease }] }`, `t` in ms from the clip's start). */
166
+ opacity?: VideoAnimatable;
88
167
  transform?: VideoTransform;
89
168
  /** Default contain. */
90
169
  fit?: VideoFit;
@@ -103,8 +182,8 @@ interface VideoMediaClip extends VideoClipBase {
103
182
  in?: number;
104
183
  /** Playback speed (default 1): it uses `duration × speed` of the source. */
105
184
  speed?: number;
106
- /** Gain (1 = unchanged, default 1). */
107
- volume?: number;
185
+ /** Gain (1 = unchanged, default 1), or keyframes. */
186
+ volume?: VideoAnimatable;
108
187
  /** Silences this clip. */
109
188
  muted?: boolean;
110
189
  }
@@ -153,6 +232,20 @@ interface VideoProject {
153
232
  markers?: VideoMarker[];
154
233
  }
155
234
 
235
+ /**
236
+ * Keyframes `{ keys: [{ t, v, ease }] }`: at least one key, in strictly increasing time (clip-relative
237
+ * ms), each value within `range`.
238
+ */
239
+ declare function parseVideoKeyframes(v: unknown, path: string, { min, max }?: {
240
+ min?: number;
241
+ max?: number;
242
+ }): VideoKeyframes;
243
+ /** A number within `range`, or keyframes for one. */
244
+ declare function parseVideoAnimatable(v: unknown, path: string, range?: {
245
+ min?: number;
246
+ max?: number;
247
+ optional?: boolean;
248
+ }): VideoAnimatable | undefined;
156
249
  declare function parseVideoTransform(v: unknown, path: string): VideoTransform | undefined;
157
250
  declare function parseVideoTransition(v: unknown, path: string): VideoTransition | undefined;
158
251
  declare function parseVideoAsset(v: unknown, path?: string): VideoAsset;
@@ -264,8 +357,13 @@ declare function videoClipsAt(project: VideoProject, time: number): {
264
357
  clip: VideoClip;
265
358
  }[];
266
359
 
360
+ /** A clip's keyframed properties (`opacity`, `volume`, `x`…) with their keys. */
361
+ declare function videoClipKeyframes(clip: VideoClip): {
362
+ property: string;
363
+ keys: ReturnType<typeof videoKeysOf>;
364
+ }[];
267
365
  type VideoIssueSeverity = 'error' | 'warning' | 'note';
268
- declare const VIDEO_ISSUE_CODES: readonly ["missing-asset", "wrong-track", "overlap", "source-overrun", "transition-without-cut", "transition-too-long", "transition-handles", "fades-too-long", "unknown-duration", "unused-asset", "marker-past-end", "empty"];
366
+ declare const VIDEO_ISSUE_CODES: readonly ["missing-asset", "wrong-track", "overlap", "source-overrun", "transition-without-cut", "transition-too-long", "transition-handles", "fades-too-long", "keyframes-outside-clip", "unknown-duration", "unused-asset", "marker-past-end", "empty"];
269
367
  type VideoIssueCode = (typeof VIDEO_ISSUE_CODES)[number];
270
368
  interface VideoIssue {
271
369
  code: VideoIssueCode;
@@ -380,6 +478,13 @@ type VideoEdit = ({
380
478
  op: 'set_transition';
381
479
  clip: string;
382
480
  transition: VideoTransition | null;
481
+ }
482
+ /** Keys for one property (in any order; clip-relative ms), or null to stop animating it and keep its value at the clip's start. */
483
+ | {
484
+ op: 'set_keyframes';
485
+ clip: string;
486
+ property: VideoKeyframeProperty;
487
+ keys: VideoKey[] | null;
383
488
  } | {
384
489
  op: 'close_gaps';
385
490
  track: string;
@@ -493,22 +598,31 @@ interface VideoFrame {
493
598
  layers: VideoLayer[];
494
599
  audio: VideoAudioSource[];
495
600
  }
496
- /** Places a picture of `size` in the frame by `fit`, then the clip's transform. */
601
+ /** A transform's values at clip-relative time `clipTime` (ms), keyframes resolved and defaults filled in. */
602
+ declare function videoTransformAt(transform: VideoClip['transform'], clipTime?: number): {
603
+ x: number;
604
+ y: number;
605
+ scale: number;
606
+ rotation: number;
607
+ };
608
+ /** Places a picture of `size` in the frame by `fit`, then the clip's transform at clip-relative `clipTime` (ms). */
497
609
  declare function placeVideoRect(frame: {
498
610
  width: number;
499
611
  height: number;
500
612
  }, size: {
501
613
  width: number;
502
614
  height: number;
503
- }, fit?: VideoFit, transform?: VideoClip['transform']): VideoRect;
615
+ }, fit?: VideoFit, transform?: VideoClip['transform'], clipTime?: number): VideoRect;
504
616
  /** A title's text and style with the defaults filled in. */
505
617
  declare function videoTextStyle(clip: VideoTextClip): NonNullable<VideoLayer['text']>;
506
618
  /**
507
619
  * A frame holding only one title, fully opaque on a transparent background, placed as the preview
508
620
  * places it: draw it with `drawVideoFrame` and save the canvas as a PNG to give the ffmpeg export
509
621
  * titles without `drawtext` (`VideoFfmpegOptions.titleImage`). Fades and opacity are left to the export.
622
+ * A keyframed transform is placed as it is at `clipTime` (the export passes titles with keyframed
623
+ * transforms without them, and moves the image itself).
510
624
  */
511
- declare function videoTitleFrame(project: VideoProject, clip: VideoTextClip): VideoFrame;
625
+ declare function videoTitleFrame(project: VideoProject, clip: VideoTextClip, clipTime?: number): VideoFrame;
512
626
  /**
513
627
  * What plays at `time` (ms): the pictures to draw, back to front, with their placement, opacity
514
628
  * and the moment of source each shows, and the sounds with their gain. A pure function of the
@@ -854,13 +968,13 @@ declare function describeVideoProject(project: VideoProject): {
854
968
  asset: string;
855
969
  in?: number;
856
970
  speed?: number;
857
- volume?: number;
971
+ volume?: VideoAnimatable;
858
972
  muted?: boolean;
859
973
  id: string;
860
974
  name?: string;
861
975
  start: number;
862
976
  duration: number;
863
- opacity?: number;
977
+ opacity?: VideoAnimatable;
864
978
  transform?: VideoTransform;
865
979
  fit?: VideoFit;
866
980
  fadeIn?: number;
@@ -883,7 +997,7 @@ declare function describeVideoProject(project: VideoProject): {
883
997
  name?: string;
884
998
  start: number;
885
999
  duration: number;
886
- opacity?: number;
1000
+ opacity?: VideoAnimatable;
887
1001
  transform?: VideoTransform;
888
1002
  fit?: VideoFit;
889
1003
  fadeIn?: number;
@@ -901,7 +1015,7 @@ declare function describeVideoProject(project: VideoProject): {
901
1015
  name?: string;
902
1016
  start: number;
903
1017
  duration: number;
904
- opacity?: number;
1018
+ opacity?: VideoAnimatable;
905
1019
  transform?: VideoTransform;
906
1020
  fit?: VideoFit;
907
1021
  fadeIn?: number;
@@ -999,5 +1113,38 @@ declare function videoAssetKindOf(typeOrName: string): VideoAssetKind | null;
999
1113
  * `<video>`, `<audio>` or `<img>`. Rejects with a readable error when it cannot be loaded.
1000
1114
  */
1001
1115
  declare function probeVideoMedia(src: string, kind: VideoAssetKind, timeout?: number): Promise<VideoMediaProbe>;
1116
+ /** One animatable property of a clip as an inspector shows it at the playhead. */
1117
+ interface VideoKeyframeRow {
1118
+ property: VideoKeyframeProperty;
1119
+ label: string;
1120
+ /** Its value at the playhead (the playhead held inside the clip). */
1121
+ value: number;
1122
+ /** Whether it has keyframes. */
1123
+ keyed: boolean;
1124
+ /** How many keys it has. */
1125
+ keys: number;
1126
+ /** Whether a key sits at the playhead. */
1127
+ onKey: boolean;
1128
+ /** The playhead in clip time (ms), where new keys go. */
1129
+ clipTime: number;
1130
+ step: number;
1131
+ min?: number;
1132
+ max?: number;
1133
+ }
1134
+ /**
1135
+ * The clip's animatable properties for an inspector at timeline `time`: opacity and the transform
1136
+ * for pictures, volume for sound, each with its value there and whether it is keyed.
1137
+ */
1138
+ declare function videoKeyframeRows(project: VideoProject, clipId: string, time: number): VideoKeyframeRow[];
1139
+ /**
1140
+ * Typing a value in an inspector: on a keyed property it sets (or moves) the key at the playhead;
1141
+ * otherwise it sets the plain value (`undefined` resets it).
1142
+ */
1143
+ declare function videoKeyframeValueEdit(project: VideoProject, clipId: string, property: VideoKeyframeProperty, time: number, value: number | undefined): VideoEdit | null;
1144
+ /**
1145
+ * The keyframe toggle: adds a key at the playhead holding the current value (the first key turns
1146
+ * the plain value into keys), or removes the key there (the last one leaves its value plain).
1147
+ */
1148
+ declare function videoKeyframeToggleEdit(project: VideoProject, clipId: string, property: VideoKeyframeProperty, time: number): VideoEdit | null;
1002
1149
 
1003
- export { type CreateVideoProjectOptions, type NewVideoClip, VIDEO_CLIP_TYPES, VIDEO_DEFAULT_STILL_DURATION, VIDEO_DEFAULT_TITLE_DURATION, VIDEO_EDIT_OPS, VIDEO_FITS, VIDEO_ISSUE_CODES, VIDEO_TIME_EPSILON, VIDEO_TRANSITION_TYPES, VIDEO_WIPE_DIRECTIONS, type VideoAsset, type VideoAssetKind, type VideoAudioSource, type VideoCanvasContext, type VideoClip, type VideoClipDragInput, type VideoClipDragMode, type VideoClipDragPlan, type VideoClipLocation, type VideoClipType, type VideoColorClip, type VideoEdit, type VideoEditOp, type VideoEditOutcome, type VideoEditResult, VideoEditorController, type VideoEditorOptions, type VideoEditorState, type VideoEditorToolProviderOptions, type VideoEdlOptions, type VideoFfmpegExport, type VideoFfmpegOptions, type VideoFit, type VideoFrame, type VideoGap, type VideoIssue, type VideoIssueCode, type VideoIssueSeverity, type VideoLayer, type VideoMarker, type VideoMediaClip, type VideoMediaProbe, VideoPreviewEngine, type VideoPreviewImage, type VideoPreviewMedia, type VideoPreviewOptions, type VideoPreviewSource, type VideoProject, type VideoRect, type VideoRulerTick, type VideoSnapOptions, type VideoSvgOptions, type VideoTextClip, type VideoTrack, type VideoTrackKind, type VideoTransform, type VideoTransition, type VideoTransitionType, type VideoWipeDirection, allVideoClipIds, applyVideoEdits, checkVideoProject, createVideoEditorToolProvider, createVideoProject, describeVideoProject, drawVideoFrame, ffmpegFilterValue, findVideoClip, findVideoGaps, formatVideoSeconds, formatVideoTimecode, nextFreeVideoSlot, parseVideoAsset, parseVideoClip, parseVideoMarker, parseVideoProject, parseVideoTrack, parseVideoTransform, parseVideoTransition, placeVideoRect, planVideoClipDrag, probeVideoMedia, snapToVideoFrame, snapVideoTime, sortedVideoClips, toVideoEdl, toVideoFfmpeg, videoAssetDropEdit, videoAssetKindOf, videoAssetMap, videoClipAfter, videoClipBefore, videoClipEnd, videoClipHandles, videoClipLabel, videoClipOverhang, videoClipSourceRange, videoClipSpeed, videoClipTint, videoClipsAt, videoContactSheetSvg, videoDropTracks, videoFrameAt, videoFrameDuration, videoFrameToSvg, videoFrames, videoIssueKey, videoOverlap, videoProjectDuration, videoProjectSvg, videoRulerTicks, videoSnapPoints, videoTextStyle, videoTitleFrame, videoWipeRect };
1150
+ export { type CreateVideoProjectOptions, type NewVideoClip, VIDEO_CLIP_TYPES, VIDEO_DEFAULT_STILL_DURATION, VIDEO_DEFAULT_TITLE_DURATION, VIDEO_EASE_NAMES, VIDEO_EDIT_OPS, VIDEO_FITS, VIDEO_ISSUE_CODES, VIDEO_KEYFRAME_LIMITS, VIDEO_KEYFRAME_PROPERTIES, VIDEO_TIME_EPSILON, VIDEO_TRANSFORM_PROPERTIES, VIDEO_TRANSITION_TYPES, VIDEO_WIPE_DIRECTIONS, type VideoAnimatable, type VideoAsset, type VideoAssetKind, type VideoAudioSource, type VideoCanvasContext, type VideoClip, type VideoClipDragInput, type VideoClipDragMode, type VideoClipDragPlan, type VideoClipLocation, type VideoClipType, type VideoColorClip, type VideoEase, type VideoEdit, type VideoEditOp, type VideoEditOutcome, type VideoEditResult, VideoEditorController, type VideoEditorOptions, type VideoEditorState, type VideoEditorToolProviderOptions, type VideoEdlOptions, type VideoFfmpegExport, type VideoFfmpegOptions, type VideoFit, type VideoFrame, type VideoGap, type VideoIssue, type VideoIssueCode, type VideoIssueSeverity, type VideoKey, type VideoKeyframeProperty, type VideoKeyframeRow, type VideoKeyframes, type VideoLayer, type VideoMarker, type VideoMediaClip, type VideoMediaProbe, VideoPreviewEngine, type VideoPreviewImage, type VideoPreviewMedia, type VideoPreviewOptions, type VideoPreviewSource, type VideoProject, type VideoRect, type VideoRulerTick, type VideoSnapOptions, type VideoSvgOptions, type VideoTextClip, type VideoTrack, type VideoTrackKind, type VideoTransform, type VideoTransition, type VideoTransitionType, type VideoWipeDirection, allVideoClipIds, applyVideoEdits, checkVideoProject, createVideoEditorToolProvider, createVideoProject, describeVideoProject, drawVideoFrame, ffmpegFilterValue, findVideoClip, findVideoGaps, formatVideoSeconds, formatVideoTimecode, isVideoKeyframes, nextFreeVideoSlot, parseVideoAnimatable, parseVideoAsset, parseVideoClip, parseVideoKeyframes, parseVideoMarker, parseVideoProject, parseVideoTrack, parseVideoTransform, parseVideoTransition, placeVideoRect, planVideoClipDrag, probeVideoMedia, removeVideoKey, retimeVideoAnimatable, setVideoKey, snapToVideoFrame, snapVideoTime, sortVideoKeys, sortedVideoClips, splitVideoAnimatable, toVideoEdl, toVideoFfmpeg, videoAssetDropEdit, videoAssetKindOf, videoAssetMap, videoClipAfter, videoClipBefore, videoClipEnd, videoClipHandles, videoClipKeyframes, videoClipLabel, videoClipOverhang, videoClipSourceRange, videoClipSpeed, videoClipTint, videoClipsAt, videoContactSheetSvg, videoDropTracks, videoEaseBezier, videoEaseProgress, videoFrameAt, videoFrameDuration, videoFrameToSvg, videoFrames, videoIssueKey, videoKeyframeBreakpoints, videoKeyframeFfmpegExpr, videoKeyframeRows, videoKeyframeToggleEdit, videoKeyframeValueEdit, videoKeysOf, videoOverlap, videoProjectDuration, videoProjectSvg, videoRulerTicks, videoSnapPoints, videoTextStyle, videoTitleFrame, videoTransformAt, videoValueAt, videoWipeRect };