@scenerok/sdk 0.8.0 → 0.10.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.
- package/README.md +63 -1
- package/dist/describe.d.ts +87 -0
- package/dist/describe.d.ts.map +1 -0
- package/dist/describe.js +210 -0
- package/dist/duration.d.ts +9 -3
- package/dist/duration.d.ts.map +1 -1
- package/dist/duration.js +22 -5
- package/dist/frame-rate.d.ts +30 -0
- package/dist/frame-rate.d.ts.map +1 -0
- package/dist/frame-rate.js +73 -0
- package/dist/index.d.ts +9 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -4
- package/dist/ir-builder.d.ts +61 -8
- package/dist/ir-builder.d.ts.map +1 -1
- package/dist/ir-builder.js +714 -256
- package/dist/ir.d.ts +203 -2
- package/dist/ir.d.ts.map +1 -1
- package/dist/ir.js +4 -2
- package/dist/media.d.ts +49 -5
- package/dist/media.d.ts.map +1 -1
- package/dist/media.js +223 -33
- package/dist/motion-tracks.d.ts +10 -0
- package/dist/motion-tracks.d.ts.map +1 -0
- package/dist/motion-tracks.js +150 -0
- package/dist/motion.d.ts +34 -0
- package/dist/motion.d.ts.map +1 -1
- package/dist/motion.js +51 -11
- package/dist/plugins/invoke.d.ts.map +1 -1
- package/dist/plugins/invoke.js +5 -2
- package/dist/timeline.d.ts +3 -0
- package/dist/timeline.d.ts.map +1 -1
- package/dist/timeline.js +101 -41
- package/dist/types.d.ts +214 -28
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +8 -9
- package/package.json +2 -2
- package/skills/scenerok/SKILL.md +5 -2
- package/skills/scenerok/references/catalog-index.json +1 -1
- package/skills/scenerok/references/editing-actions.md +43 -1
- package/skills/scenerok/references/sdk-guide.md +149 -49
- package/skills/scenerok/references/vidscript-guide.md +49 -13
- package/skills/scenerok/references/vidscript-strict.md +1 -1
package/dist/media.js
CHANGED
|
@@ -1,38 +1,161 @@
|
|
|
1
1
|
import { toSeconds } from './duration.js';
|
|
2
2
|
import { getAudioLength, getVideoLength } from './stdlib.js';
|
|
3
|
+
import { isPictureNode, } from './types.js';
|
|
4
|
+
/** Length of a video whose length is not known, when it is appended with `push`. */
|
|
5
|
+
const UNKNOWN_VIDEO_LENGTH = 5;
|
|
6
|
+
/** Length of a still or text appended with `push` and no explicit duration. */
|
|
7
|
+
export const DEFAULT_STILL_LENGTH = 3;
|
|
3
8
|
function normalizeAnims(animate) {
|
|
4
9
|
if (!animate)
|
|
5
10
|
return [];
|
|
6
11
|
return Array.isArray(animate) ? [...animate] : [animate];
|
|
7
12
|
}
|
|
8
|
-
function
|
|
9
|
-
if (
|
|
10
|
-
|
|
11
|
-
|
|
13
|
+
function requireFinite(value, method, what) {
|
|
14
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
15
|
+
throw new TypeError(`${method}() expected ${what}, got ${value === undefined ? 'nothing' : JSON.stringify(value)}`);
|
|
16
|
+
}
|
|
17
|
+
return value;
|
|
12
18
|
}
|
|
13
|
-
/**
|
|
14
|
-
|
|
19
|
+
/**
|
|
20
|
+
* A trim on a trimmed value is relative to what the first kept:
|
|
21
|
+
* `clip.trim(10, 20).trim(2, 4)` plays source 12–14 s. Past the kept range is an error.
|
|
22
|
+
*/
|
|
23
|
+
export function composeTrim(previous, start, end) {
|
|
24
|
+
const base = previous.trimStart ?? 0;
|
|
25
|
+
const keptEnd = previous.trimEnd ?? 0;
|
|
26
|
+
const trimStart = start !== undefined ? base + toSeconds(start) : base;
|
|
27
|
+
const trimEnd = end !== undefined ? base + toSeconds(end) : keptEnd;
|
|
28
|
+
if (keptEnd > base && (trimEnd > keptEnd || trimStart > keptEnd)) {
|
|
29
|
+
throw new RangeError(`trim() is relative to the previous trim, which kept ${keptEnd - base}s; this one runs to ${Math.max(trimStart, trimEnd) - base}s`);
|
|
30
|
+
}
|
|
31
|
+
if (trimEnd > 0 && trimEnd <= trimStart) {
|
|
32
|
+
throw new RangeError(`trim() end (${trimEnd - base}s) must be after its start (${trimStart - base}s)`);
|
|
33
|
+
}
|
|
34
|
+
return { trimStart, trimEnd };
|
|
35
|
+
}
|
|
36
|
+
/** A crop on a cropped value is relative to the first, like `trim`. */
|
|
37
|
+
export function composeCrop(previous, x, y, width, height) {
|
|
38
|
+
for (const [value, what] of [[x, 'an x offset'], [y, 'a y offset'], [width, 'a width'], [height, 'a height']]) {
|
|
39
|
+
requireFinite(value, 'crop', what);
|
|
40
|
+
}
|
|
41
|
+
if (x < 0 || y < 0) {
|
|
42
|
+
throw new RangeError(`crop() offsets count from the top-left and cannot be negative, got (${x}, ${y})`);
|
|
43
|
+
}
|
|
44
|
+
if (width <= 0 || height <= 0) {
|
|
45
|
+
throw new RangeError(`crop() needs a positive width and height, got ${width}x${height}`);
|
|
46
|
+
}
|
|
47
|
+
if (previous && (x + width > previous.width || y + height > previous.height)) {
|
|
48
|
+
throw new RangeError(`crop() is relative to the previous crop, which kept ${previous.width}x${previous.height}; this one reaches ${x + width}x${y + height}`);
|
|
49
|
+
}
|
|
50
|
+
return { x: (previous?.x ?? 0) + x, y: (previous?.y ?? 0) + y, width, height };
|
|
51
|
+
}
|
|
52
|
+
const MASK_MODES = ['alpha', 'luma', 'inverted'];
|
|
53
|
+
export function makeMask(matte, options = {}) {
|
|
54
|
+
if (!isPictureNode(matte)) {
|
|
55
|
+
throw new TypeError('mask() needs a picture for its matte (video, image, text, solid, or timeline)');
|
|
56
|
+
}
|
|
57
|
+
const mode = options.mode ?? 'alpha';
|
|
58
|
+
if (!MASK_MODES.includes(mode)) {
|
|
59
|
+
throw new RangeError(`Unknown mask mode '${String(mode)}'. Use one of: ${MASK_MODES.join(', ')}.`);
|
|
60
|
+
}
|
|
61
|
+
return { matte, mode };
|
|
62
|
+
}
|
|
63
|
+
/** A grade scoped to one value. LUTs read a file at compile time, so they stay a VidScript statement. */
|
|
64
|
+
export function makeClipEffect(name, params = {}) {
|
|
65
|
+
if (!name || !name.trim())
|
|
66
|
+
throw new TypeError('filter() needs a filter name');
|
|
67
|
+
if (name.trim().toLowerCase() === 'lut') {
|
|
68
|
+
throw new TypeError('filter("lut") reads a .cube file at compile time; use VidScript: filter "lut", file: "look.cube"');
|
|
69
|
+
}
|
|
70
|
+
return { type: 'builtin', id: `fx_${name}`, name, start: 0, end: 0, params: { ...params } };
|
|
71
|
+
}
|
|
72
|
+
/** How long a clip plays: the source range at `|speed|`, `loop` times; 0 when unknown. */
|
|
73
|
+
export function playedLength(edits, mediaDuration) {
|
|
74
|
+
const trimStart = edits.trimStart ?? 0;
|
|
75
|
+
const trimEnd = edits.trimEnd ?? 0;
|
|
76
|
+
const speed = edits.speed ?? 1;
|
|
77
|
+
const loop = edits.loop ?? 1;
|
|
78
|
+
const sourceEnd = trimEnd > trimStart ? trimEnd : ((mediaDuration ?? 0) > trimStart ? mediaDuration : 0);
|
|
79
|
+
const span = sourceEnd - trimStart;
|
|
80
|
+
if (span <= 0 || speed === 0)
|
|
81
|
+
return 0;
|
|
82
|
+
return (span / Math.abs(speed)) * Math.max(1, Math.floor(loop));
|
|
83
|
+
}
|
|
84
|
+
function editsFromOptions(options) {
|
|
85
|
+
const edits = {};
|
|
86
|
+
if (options.trimStart !== undefined)
|
|
87
|
+
edits.trimStart = toSeconds(options.trimStart);
|
|
88
|
+
if (options.trimEnd !== undefined)
|
|
89
|
+
edits.trimEnd = toSeconds(options.trimEnd);
|
|
90
|
+
if (options.speed !== undefined)
|
|
91
|
+
edits.speed = options.speed;
|
|
92
|
+
if (options.loop !== undefined)
|
|
93
|
+
edits.loop = options.loop;
|
|
94
|
+
return edits;
|
|
95
|
+
}
|
|
96
|
+
function buildMedia(state) {
|
|
97
|
+
const { kind, source, options, edits, sequence } = state;
|
|
15
98
|
const animations = normalizeAnims(options.animate);
|
|
16
|
-
const
|
|
17
|
-
const
|
|
99
|
+
const mediaDuration = kind === 'video' && options.duration !== undefined ? toSeconds(options.duration) : undefined;
|
|
100
|
+
const next = (patch) => buildMedia({ ...state, ...patch });
|
|
101
|
+
const withEdits = (patch) => next({ edits: { ...edits, ...patch } });
|
|
102
|
+
const node = {
|
|
18
103
|
__sdk: true,
|
|
19
|
-
kind
|
|
104
|
+
kind,
|
|
20
105
|
source,
|
|
21
106
|
options: { ...options },
|
|
22
|
-
|
|
107
|
+
edits: { ...edits },
|
|
108
|
+
...(sequence ? { sequence } : {}),
|
|
109
|
+
mediaDuration,
|
|
110
|
+
get duration() {
|
|
111
|
+
if (kind === 'image')
|
|
112
|
+
return DEFAULT_STILL_LENGTH;
|
|
113
|
+
const played = playedLength(edits, mediaDuration);
|
|
114
|
+
return played > 0 ? played : UNKNOWN_VIDEO_LENGTH;
|
|
115
|
+
},
|
|
23
116
|
animations,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
117
|
+
trim(start, end) {
|
|
118
|
+
return withEdits(composeTrim(edits, start, end));
|
|
119
|
+
},
|
|
120
|
+
speed(factor) {
|
|
121
|
+
return withEdits({ speed: requireFinite(factor, 'speed', 'a speed factor') });
|
|
122
|
+
},
|
|
123
|
+
loop(count) {
|
|
124
|
+
return withEdits({ loop: requireFinite(count, 'loop', 'a repeat count') });
|
|
125
|
+
},
|
|
126
|
+
resize(width, height) {
|
|
127
|
+
return withEdits({
|
|
128
|
+
width: requireFinite(width, 'resize', 'a width'),
|
|
129
|
+
height: requireFinite(height, 'resize', 'a height'),
|
|
29
130
|
});
|
|
30
131
|
},
|
|
132
|
+
opacity(value) {
|
|
133
|
+
return withEdits({ opacity: requireFinite(value, 'opacity', 'an opacity') });
|
|
134
|
+
},
|
|
135
|
+
filter(name, params) {
|
|
136
|
+
return withEdits({ effects: [...(edits.effects ?? []), makeClipEffect(name, params)] });
|
|
137
|
+
},
|
|
138
|
+
crop(x, y, width, height) {
|
|
139
|
+
return withEdits({ crop: composeCrop(edits.crop, x, y, width, height) });
|
|
140
|
+
},
|
|
141
|
+
mask(matte, maskOptions) {
|
|
142
|
+
if (edits.mask)
|
|
143
|
+
throw new Error('mask() is already applied to this value; one matte per value');
|
|
144
|
+
return withEdits({ mask: makeMask(matte, maskOptions) });
|
|
145
|
+
},
|
|
146
|
+
animate(anim) {
|
|
147
|
+
return next({ options: { ...options, animate: [...animations, ...normalizeAnims(anim)] } });
|
|
148
|
+
},
|
|
31
149
|
with(opts) {
|
|
32
|
-
|
|
150
|
+
const merged = { ...options, ...opts, animate: opts.animate ?? options.animate };
|
|
151
|
+
return next({ options: merged, edits: { ...edits, ...editsFromOptions(opts) } });
|
|
33
152
|
},
|
|
34
153
|
};
|
|
35
|
-
return
|
|
154
|
+
return node;
|
|
155
|
+
}
|
|
156
|
+
/** A video clip from a path/URL. Pass `duration` when the asset is not probed yet. */
|
|
157
|
+
export function video(source, options = {}) {
|
|
158
|
+
return buildMedia({ kind: 'video', source, options: { ...options }, edits: editsFromOptions(options) });
|
|
36
159
|
}
|
|
37
160
|
/** Probe a video duration when omitted, then create a video clip. */
|
|
38
161
|
export async function loadVideo(source, options = {}) {
|
|
@@ -41,42 +164,81 @@ export async function loadVideo(source, options = {}) {
|
|
|
41
164
|
const duration = await getVideoLength(source);
|
|
42
165
|
return video(source, { ...options, duration });
|
|
43
166
|
}
|
|
44
|
-
/**
|
|
167
|
+
/** A still image. It fills its placement window (`image ≤ video`). */
|
|
168
|
+
export function image(source, options = {}) {
|
|
169
|
+
return buildMedia({ kind: 'image', source, options: { ...options }, edits: {} });
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* A numbered-frame source (`input frames = "shot.%04d.png", count: 24, rate: 12`).
|
|
173
|
+
* Plays `count / rate` seconds; `rate` defaults to the timeline fps.
|
|
174
|
+
*/
|
|
175
|
+
export function imageSequence(pattern, options) {
|
|
176
|
+
const { first = 1, count, rate, ...rest } = options;
|
|
177
|
+
if (!/%0?\d*d/.test(pattern)) {
|
|
178
|
+
throw new TypeError(`imageSequence() needs a numbered pattern like "shot.%04d.png", got "${pattern}"`);
|
|
179
|
+
}
|
|
180
|
+
if (!Number.isInteger(count) || count <= 0) {
|
|
181
|
+
throw new RangeError(`imageSequence() needs a positive frame count, got ${String(count)}`);
|
|
182
|
+
}
|
|
183
|
+
const sequence = { first, count, ...(rate !== undefined ? { rate } : {}) };
|
|
184
|
+
const duration = rate !== undefined ? count / rate : undefined;
|
|
185
|
+
return buildMedia({
|
|
186
|
+
kind: 'video',
|
|
187
|
+
source: pattern,
|
|
188
|
+
options: { ...rest, ...(duration !== undefined ? { duration } : {}) },
|
|
189
|
+
edits: editsFromOptions(rest),
|
|
190
|
+
sequence,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
/** A text layer. */
|
|
45
194
|
export function text(content, options = {}) {
|
|
46
195
|
const animations = normalizeAnims(options.animate);
|
|
47
|
-
|
|
196
|
+
return {
|
|
48
197
|
__sdk: true,
|
|
49
198
|
kind: 'text',
|
|
50
199
|
content,
|
|
51
200
|
options: { ...options },
|
|
52
201
|
animations,
|
|
53
202
|
animate(anim) {
|
|
54
|
-
|
|
55
|
-
return text(content, {
|
|
56
|
-
...options,
|
|
57
|
-
animate: [...animations, ...next],
|
|
58
|
-
});
|
|
203
|
+
return text(content, { ...options, animate: [...animations, ...normalizeAnims(anim)] });
|
|
59
204
|
},
|
|
60
205
|
with(opts) {
|
|
61
206
|
return text(content, { ...options, ...opts, animate: opts.animate ?? options.animate });
|
|
62
207
|
},
|
|
63
208
|
};
|
|
64
|
-
return layer;
|
|
65
209
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
const
|
|
210
|
+
function buildAudio(state) {
|
|
211
|
+
const { source, options, trimEnd } = state;
|
|
212
|
+
const trimStart = options.trimStart !== undefined ? toSeconds(options.trimStart) : 0;
|
|
213
|
+
const declared = options.duration !== undefined ? toSeconds(options.duration) : undefined;
|
|
214
|
+
const known = trimEnd > trimStart ? trimEnd - trimStart : declared;
|
|
215
|
+
return {
|
|
70
216
|
__sdk: true,
|
|
71
217
|
kind: 'audio',
|
|
72
218
|
source,
|
|
73
219
|
options: { ...options },
|
|
74
|
-
|
|
220
|
+
mediaDuration: known,
|
|
221
|
+
duration: known ?? UNKNOWN_VIDEO_LENGTH,
|
|
222
|
+
trim(start, end) {
|
|
223
|
+
const composed = composeTrim({ trimStart, trimEnd }, start, end);
|
|
224
|
+
// Without an out-point the length follows the in-point.
|
|
225
|
+
const remaining = composed.trimEnd > composed.trimStart
|
|
226
|
+
? undefined
|
|
227
|
+
: (declared !== undefined ? Math.max(0, declared - (composed.trimStart - trimStart)) : undefined);
|
|
228
|
+
return buildAudio({
|
|
229
|
+
source,
|
|
230
|
+
options: { ...options, trimStart: composed.trimStart, ...(remaining !== undefined ? { duration: remaining } : {}) },
|
|
231
|
+
trimEnd: composed.trimEnd,
|
|
232
|
+
});
|
|
233
|
+
},
|
|
75
234
|
with(opts) {
|
|
76
|
-
return
|
|
235
|
+
return buildAudio({ ...state, options: { ...options, ...opts } });
|
|
77
236
|
},
|
|
78
237
|
};
|
|
79
|
-
|
|
238
|
+
}
|
|
239
|
+
/** An audio track. */
|
|
240
|
+
export function audio(source, options = {}) {
|
|
241
|
+
return buildAudio({ source, options: { ...options }, trimEnd: 0 });
|
|
80
242
|
}
|
|
81
243
|
/** Probe an audio duration when omitted, then create an audio track. */
|
|
82
244
|
export async function loadAudio(source, options = {}) {
|
|
@@ -85,7 +247,35 @@ export async function loadAudio(source, options = {}) {
|
|
|
85
247
|
const duration = await getAudioLength(source);
|
|
86
248
|
return audio(source, { ...options, duration });
|
|
87
249
|
}
|
|
88
|
-
/**
|
|
250
|
+
/** A flat-colour rectangle — full frame unless sized. With `radius`/`corners` it is a shape matte. */
|
|
251
|
+
export function solid(color, options = {}) {
|
|
252
|
+
if (typeof color !== 'string' || !color.trim())
|
|
253
|
+
throw new TypeError('solid() needs a colour');
|
|
254
|
+
const animations = normalizeAnims(options.animate);
|
|
255
|
+
return {
|
|
256
|
+
__sdk: true,
|
|
257
|
+
kind: 'solid',
|
|
258
|
+
color,
|
|
259
|
+
options: { ...options },
|
|
260
|
+
animate(anim) {
|
|
261
|
+
return solid(color, { ...options, animate: [...animations, ...normalizeAnims(anim)] });
|
|
262
|
+
},
|
|
263
|
+
with(opts) {
|
|
264
|
+
return solid(color, { ...options, ...opts, animate: opts.animate ?? options.animate });
|
|
265
|
+
},
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
/** A transparent gap: `t.push(gap(), 2)` advances the playhead by 2 s. */
|
|
269
|
+
export function gap() {
|
|
270
|
+
return { __sdk: true, kind: 'gap' };
|
|
271
|
+
}
|
|
272
|
+
/** A named instant (`t[4][4] = marker('act 2')`). Exported as an OTIO marker; never rendered. */
|
|
273
|
+
export function marker(name, metadata) {
|
|
274
|
+
if (typeof name !== 'string' || !name.trim())
|
|
275
|
+
throw new TypeError('marker() needs a name');
|
|
276
|
+
return { __sdk: true, kind: 'marker', name, ...(metadata ? { metadata: { ...metadata } } : {}) };
|
|
277
|
+
}
|
|
278
|
+
/** Builtin post-processing filter over the frame (e.g. glow, blur). */
|
|
89
279
|
export function filter(name, params = {}) {
|
|
90
280
|
return {
|
|
91
281
|
__sdk: true,
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Animation → motion-track lowering — the SDK's copy of `animationToMotionTracks`
|
|
3
|
+
* in the platform's `src/lib/motion/ir-motion.ts`. Renderers read motion tracks
|
|
4
|
+
* by target (`opacity`, `transform.position.y`, …), so an SDK animation must
|
|
5
|
+
* lower to the same tracks a VidScript `animate:` does.
|
|
6
|
+
*/
|
|
7
|
+
import type { IRAnimation, IRMotionTrack } from './ir.js';
|
|
8
|
+
export declare function animationToMotionTracks(animation: IRAnimation): IRMotionTrack[];
|
|
9
|
+
export declare function animationsToMotionTracks(animations: IRAnimation[] | undefined): IRMotionTrack[];
|
|
10
|
+
//# sourceMappingURL=motion-tracks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"motion-tracks.d.ts","sourceRoot":"","sources":["../src/motion-tracks.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAiB,MAAM,SAAS,CAAC;AAsEzE,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,WAAW,GAAG,aAAa,EAAE,CA6F/E;AAED,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,WAAW,EAAE,GAAG,SAAS,GAAG,aAAa,EAAE,CAE/F"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Animation → motion-track lowering — the SDK's copy of `animationToMotionTracks`
|
|
3
|
+
* in the platform's `src/lib/motion/ir-motion.ts`. Renderers read motion tracks
|
|
4
|
+
* by target (`opacity`, `transform.position.y`, …), so an SDK animation must
|
|
5
|
+
* lower to the same tracks a VidScript `animate:` does.
|
|
6
|
+
*/
|
|
7
|
+
const EPSILON = 0.0001;
|
|
8
|
+
function finiteNumber(value, fallback) {
|
|
9
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
|
|
10
|
+
}
|
|
11
|
+
function recordValue(value) {
|
|
12
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
13
|
+
? value
|
|
14
|
+
: {};
|
|
15
|
+
}
|
|
16
|
+
function normalizeEasingName(easing) {
|
|
17
|
+
return (easing ?? '').toLowerCase().replace(/[-_\s]/g, '');
|
|
18
|
+
}
|
|
19
|
+
function motionTrack(animation, suffix, target, from, to, behavior) {
|
|
20
|
+
const springEasing = normalizeEasingName(animation.easing) === 'spring';
|
|
21
|
+
const keyframeEasing = springEasing ? 'linear' : animation.easing;
|
|
22
|
+
const resolvedBehavior = behavior
|
|
23
|
+
|| (springEasing ? { type: 'spring' } : undefined)
|
|
24
|
+
|| (animation.loop ? { type: 'loop' } : undefined);
|
|
25
|
+
return {
|
|
26
|
+
id: `${animation.id}_${suffix}`,
|
|
27
|
+
target,
|
|
28
|
+
keyframes: [
|
|
29
|
+
{ time: animation.start, value: from },
|
|
30
|
+
{ time: animation.end, value: to, easing: keyframeEasing },
|
|
31
|
+
],
|
|
32
|
+
behavior: resolvedBehavior,
|
|
33
|
+
plugin: animation.plugin,
|
|
34
|
+
sourceAnimationId: animation.id,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function customTargetForKey(key) {
|
|
38
|
+
switch (key) {
|
|
39
|
+
case 'opacity':
|
|
40
|
+
return 'opacity';
|
|
41
|
+
case 'x':
|
|
42
|
+
case 'offsetX':
|
|
43
|
+
case 'translateX':
|
|
44
|
+
return 'transform.position.x';
|
|
45
|
+
case 'y':
|
|
46
|
+
case 'offsetY':
|
|
47
|
+
case 'translateY':
|
|
48
|
+
return 'transform.position.y';
|
|
49
|
+
case 'scale':
|
|
50
|
+
return 'transform.scale';
|
|
51
|
+
case 'scaleX':
|
|
52
|
+
return 'transform.scale.x';
|
|
53
|
+
case 'scaleY':
|
|
54
|
+
return 'transform.scale.y';
|
|
55
|
+
case 'rotation':
|
|
56
|
+
case 'degrees':
|
|
57
|
+
return 'transform.rotation.z';
|
|
58
|
+
default:
|
|
59
|
+
return key.startsWith('effect.') ? key : `custom.${key}`;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export function animationToMotionTracks(animation) {
|
|
63
|
+
const from = recordValue(animation.from);
|
|
64
|
+
const to = recordValue(animation.to);
|
|
65
|
+
const tracks = [];
|
|
66
|
+
switch (animation.type) {
|
|
67
|
+
case 'fade':
|
|
68
|
+
case 'opacity':
|
|
69
|
+
tracks.push(motionTrack(animation, 'opacity', 'opacity', finiteNumber(from.opacity, 0), finiteNumber(to.opacity, 1)));
|
|
70
|
+
break;
|
|
71
|
+
case 'slideX':
|
|
72
|
+
case 'translateX':
|
|
73
|
+
tracks.push(motionTrack(animation, 'x', 'transform.position.x', finiteNumber(from.x, 0), finiteNumber(to.x, 0)));
|
|
74
|
+
break;
|
|
75
|
+
case 'slideY':
|
|
76
|
+
case 'translateY':
|
|
77
|
+
tracks.push(motionTrack(animation, 'y', 'transform.position.y', finiteNumber(from.y, 0), finiteNumber(to.y, 0)));
|
|
78
|
+
break;
|
|
79
|
+
case 'scale': {
|
|
80
|
+
const fromScale = finiteNumber(from.scale ?? from.x, 1);
|
|
81
|
+
const toScale = finiteNumber(to.scale ?? to.x, 1);
|
|
82
|
+
tracks.push(motionTrack(animation, 'scale_x', 'transform.scale.x', fromScale, toScale));
|
|
83
|
+
tracks.push(motionTrack(animation, 'scale_y', 'transform.scale.y', finiteNumber(from.y, fromScale), finiteNumber(to.y, toScale)));
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
case 'rotate':
|
|
87
|
+
case 'rotation':
|
|
88
|
+
tracks.push(motionTrack(animation, 'rotation', 'transform.rotation.z', finiteNumber(from.rotation ?? from.degrees, 0), finiteNumber(to.rotation ?? to.degrees, 0)));
|
|
89
|
+
break;
|
|
90
|
+
case 'pop': {
|
|
91
|
+
const fromScale = finiteNumber(from.scale, 0.55);
|
|
92
|
+
const overshootScale = finiteNumber(to.scale, 1.06);
|
|
93
|
+
const settleScale = finiteNumber(to.settleScale ?? to.finalScale, 1);
|
|
94
|
+
const overshootTime = animation.start + Math.max(EPSILON, animation.end - animation.start) * 0.72;
|
|
95
|
+
const behavior = animation.loop ? { type: 'loop' } : undefined;
|
|
96
|
+
for (const axis of ['x', 'y']) {
|
|
97
|
+
tracks.push({
|
|
98
|
+
id: `${animation.id}_scale_${axis}`,
|
|
99
|
+
target: `transform.scale.${axis}`,
|
|
100
|
+
keyframes: [
|
|
101
|
+
{ time: animation.start, value: fromScale },
|
|
102
|
+
{ time: overshootTime, value: overshootScale, easing: animation.easing },
|
|
103
|
+
{ time: animation.end, value: settleScale, easing: 'easeOutCubic' },
|
|
104
|
+
],
|
|
105
|
+
behavior,
|
|
106
|
+
plugin: animation.plugin,
|
|
107
|
+
sourceAnimationId: animation.id,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
tracks.push(motionTrack(animation, 'opacity', 'opacity', finiteNumber(from.opacity, 0), finiteNumber(to.opacity, 1)));
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
case 'rise':
|
|
114
|
+
tracks.push(motionTrack(animation, 'y', 'transform.position.y', finiteNumber(from.y, 80), finiteNumber(to.y, 0)));
|
|
115
|
+
tracks.push(motionTrack(animation, 'opacity', 'opacity', finiteNumber(from.opacity, 0), finiteNumber(to.opacity, 1)));
|
|
116
|
+
break;
|
|
117
|
+
case 'swing':
|
|
118
|
+
tracks.push(motionTrack(animation, 'rotation', 'transform.rotation.z', finiteNumber(from.rotation, -14), finiteNumber(to.rotation, 0)));
|
|
119
|
+
tracks.push(motionTrack(animation, 'y', 'transform.position.y', finiteNumber(from.y, 35), finiteNumber(to.y, 0)));
|
|
120
|
+
tracks.push(motionTrack(animation, 'opacity', 'opacity', finiteNumber(from.opacity, 0), finiteNumber(to.opacity, 1)));
|
|
121
|
+
break;
|
|
122
|
+
case 'glitch':
|
|
123
|
+
tracks.push(motionTrack(animation, 'opacity', 'opacity', finiteNumber(from.opacity, 0.2), finiteNumber(to.opacity, 1)));
|
|
124
|
+
tracks.push(motionTrack(animation, 'jitter_x', 'transform.position.x', 0, 0, { type: 'wiggle', params: { amplitude: 3.5, frequency: 30 } }));
|
|
125
|
+
tracks.push(motionTrack(animation, 'jitter_y', 'transform.position.y', 0, 0, { type: 'wiggle', params: { amplitude: 2.8, frequency: 34, phase: 0.25 } }));
|
|
126
|
+
break;
|
|
127
|
+
case 'float': {
|
|
128
|
+
const amplitude = Math.abs(finiteNumber(to.y, 12) - finiteNumber(from.y, -12)) / 2;
|
|
129
|
+
const duration = Math.max(EPSILON, animation.end - animation.start);
|
|
130
|
+
tracks.push(motionTrack(animation, 'float_y', 'transform.position.y', 0, 0, {
|
|
131
|
+
type: 'wiggle',
|
|
132
|
+
params: { amplitude, frequency: 1 / duration },
|
|
133
|
+
}));
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
case 'typewriter':
|
|
137
|
+
tracks.push(motionTrack(animation, 'reveal', 'text.reveal', 0, 1, { type: 'typewriter' }));
|
|
138
|
+
break;
|
|
139
|
+
default: {
|
|
140
|
+
const keys = new Set([...Object.keys(from), ...Object.keys(to)]);
|
|
141
|
+
for (const key of keys) {
|
|
142
|
+
tracks.push(motionTrack(animation, key, customTargetForKey(key), (from[key] ?? 0), (to[key] ?? 1)));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return tracks;
|
|
147
|
+
}
|
|
148
|
+
export function animationsToMotionTracks(animations) {
|
|
149
|
+
return (animations || []).flatMap(animationToMotionTracks);
|
|
150
|
+
}
|
package/dist/motion.d.ts
CHANGED
|
@@ -1,9 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Animation descriptors — the same set as VidScript's `@scenerok/basic-animations`
|
|
3
|
+
* (`import fx from "@scenerok/basic-animations"`, `animate: fx.fadeIn(0.5s)`),
|
|
4
|
+
* with the same types and defaults, so an SDK `animate` lowers to the same IR.
|
|
5
|
+
*/
|
|
1
6
|
import { type TimeInput } from './duration.js';
|
|
2
7
|
import type { AnimationDescriptor } from './types.js';
|
|
8
|
+
export interface AnimationOptions {
|
|
9
|
+
/** Easing name (`easeOutCubic`, `spring`, `linear`, …). */
|
|
10
|
+
easing?: string;
|
|
11
|
+
/** `end` aligns the animation to the end of the placement window (exits). */
|
|
12
|
+
align?: 'start' | 'end';
|
|
13
|
+
loop?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Any animation by type — the SDK spelling of a VidScript object literal
|
|
17
|
+
* (`animate: { type: "kenBurns", from: { scale: 1 }, to: { scale: 1.1 } }`).
|
|
18
|
+
* A `duration` of 0 spans the whole placement window.
|
|
19
|
+
*/
|
|
20
|
+
export declare function animation(type: string, from: Record<string, unknown>, to: Record<string, unknown>, duration?: TimeInput, options?: AnimationOptions): AnimationDescriptor;
|
|
3
21
|
export declare function fadeIn(duration?: TimeInput): AnimationDescriptor;
|
|
4
22
|
export declare function fadeOut(duration?: TimeInput): AnimationDescriptor;
|
|
5
23
|
export declare function slideY(fromY?: number, toY?: number, duration?: TimeInput): AnimationDescriptor;
|
|
6
24
|
export declare function slideX(fromX?: number, toX?: number, duration?: TimeInput): AnimationDescriptor;
|
|
25
|
+
/** Uniform scale from → to. */
|
|
7
26
|
export declare function scale(from?: number, to?: number, duration?: TimeInput): AnimationDescriptor;
|
|
27
|
+
/** Scale + fade entrance with a slight overshoot. */
|
|
8
28
|
export declare function popIn(duration?: TimeInput): AnimationDescriptor;
|
|
29
|
+
/** Upward entrance with a fade. */
|
|
30
|
+
export declare function riseIn(duration?: TimeInput, distance?: number): AnimationDescriptor;
|
|
31
|
+
/** Rotates in while sliding and fading. */
|
|
32
|
+
export declare function swingIn(duration?: TimeInput): AnimationDescriptor;
|
|
33
|
+
/** Jittery entrance with a flash. */
|
|
34
|
+
export declare function glitchIn(duration?: TimeInput): AnimationDescriptor;
|
|
35
|
+
/** Gentle looping vertical bob. */
|
|
36
|
+
export declare function float(duration?: TimeInput, amplitude?: number): AnimationDescriptor;
|
|
37
|
+
/** Reveals text character by character (`stagger` on the text picks words or chars). */
|
|
38
|
+
export declare function typewriter(duration?: TimeInput): AnimationDescriptor;
|
|
39
|
+
/** Constant rotation in degrees over the whole window (`rotation:` in VidScript). */
|
|
40
|
+
export declare function rotate(from: number, to?: number, duration?: TimeInput): AnimationDescriptor;
|
|
41
|
+
/** Gain automation for an audio track (`animate: { type: "volume", … }`). */
|
|
42
|
+
export declare function volumeRamp(from: number, to: number, duration?: TimeInput, options?: AnimationOptions): AnimationDescriptor;
|
|
9
43
|
//# sourceMappingURL=motion.d.ts.map
|
package/dist/motion.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"motion.d.ts","sourceRoot":"","sources":["../src/motion.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"motion.d.ts","sourceRoot":"","sources":["../src/motion.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD,MAAM,WAAW,gBAAgB;IAC/B,2DAA2D;IAC3D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;IACxB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC3B,QAAQ,GAAE,SAAa,EACvB,OAAO,GAAE,gBAAqB,GAC7B,mBAAmB,CAWrB;AAED,wBAAgB,MAAM,CAAC,QAAQ,GAAE,SAAe,GAAG,mBAAmB,CAErE;AAED,wBAAgB,OAAO,CAAC,QAAQ,GAAE,SAAe,GAAG,mBAAmB,CAEtE;AAED,wBAAgB,MAAM,CAAC,KAAK,SAAK,EAAE,GAAG,SAAI,EAAE,QAAQ,GAAE,SAAe,GAAG,mBAAmB,CAE1F;AAED,wBAAgB,MAAM,CAAC,KAAK,SAAM,EAAE,GAAG,SAAI,EAAE,QAAQ,GAAE,SAAe,GAAG,mBAAmB,CAE3F;AAED,+BAA+B;AAC/B,wBAAgB,KAAK,CAAC,IAAI,SAAM,EAAE,EAAE,SAAI,EAAE,QAAQ,GAAE,SAAe,GAAG,mBAAmB,CAExF;AAED,qDAAqD;AACrD,wBAAgB,KAAK,CAAC,QAAQ,GAAE,SAAe,GAAG,mBAAmB,CAEpE;AAED,mCAAmC;AACnC,wBAAgB,MAAM,CAAC,QAAQ,GAAE,SAAgB,EAAE,QAAQ,SAAK,GAAG,mBAAmB,CAErF;AAED,2CAA2C;AAC3C,wBAAgB,OAAO,CAAC,QAAQ,GAAE,SAAe,GAAG,mBAAmB,CAEtE;AAED,qCAAqC;AACrC,wBAAgB,QAAQ,CAAC,QAAQ,GAAE,SAAgB,GAAG,mBAAmB,CAExE;AAED,mCAAmC;AACnC,wBAAgB,KAAK,CAAC,QAAQ,GAAE,SAAe,EAAE,SAAS,SAAK,GAAG,mBAAmB,CAEpF;AAED,wFAAwF;AACxF,wBAAgB,UAAU,CAAC,QAAQ,GAAE,SAAe,GAAG,mBAAmB,CAEzE;AAED,qFAAqF;AACrF,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,SAAO,EAAE,QAAQ,GAAE,SAAa,GAAG,mBAAmB,CAE5F;AAED,6EAA6E;AAC7E,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,GAAE,SAAa,EAAE,OAAO,GAAE,gBAAqB,GAAG,mBAAmB,CAEjI"}
|
package/dist/motion.js
CHANGED
|
@@ -1,31 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Animation descriptors — the same set as VidScript's `@scenerok/basic-animations`
|
|
3
|
+
* (`import fx from "@scenerok/basic-animations"`, `animate: fx.fadeIn(0.5s)`),
|
|
4
|
+
* with the same types and defaults, so an SDK `animate` lowers to the same IR.
|
|
5
|
+
*/
|
|
1
6
|
import { toSeconds } from './duration.js';
|
|
2
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Any animation by type — the SDK spelling of a VidScript object literal
|
|
9
|
+
* (`animate: { type: "kenBurns", from: { scale: 1 }, to: { scale: 1.1 } }`).
|
|
10
|
+
* A `duration` of 0 spans the whole placement window.
|
|
11
|
+
*/
|
|
12
|
+
export function animation(type, from, to, duration = 0, options = {}) {
|
|
3
13
|
return {
|
|
4
14
|
__sdk: true,
|
|
5
15
|
kind: 'animation',
|
|
6
16
|
type,
|
|
7
|
-
from,
|
|
8
|
-
to,
|
|
17
|
+
from: { ...from },
|
|
18
|
+
to: { ...to },
|
|
9
19
|
duration: toSeconds(duration),
|
|
10
20
|
plugin: '@scenerok/basic-animations',
|
|
11
|
-
...
|
|
21
|
+
...options,
|
|
12
22
|
};
|
|
13
23
|
}
|
|
14
24
|
export function fadeIn(duration = 0.5) {
|
|
15
|
-
return
|
|
25
|
+
return animation('fade', { opacity: 0 }, { opacity: 1 }, duration);
|
|
16
26
|
}
|
|
17
27
|
export function fadeOut(duration = 0.5) {
|
|
18
|
-
return
|
|
28
|
+
return animation('fade', { opacity: 1 }, { opacity: 0 }, duration, { align: 'end' });
|
|
19
29
|
}
|
|
20
30
|
export function slideY(fromY = 30, toY = 0, duration = 0.6) {
|
|
21
|
-
return
|
|
31
|
+
return animation('translateY', { y: fromY }, { y: toY }, duration);
|
|
22
32
|
}
|
|
23
33
|
export function slideX(fromX = -30, toX = 0, duration = 0.6) {
|
|
24
|
-
return
|
|
34
|
+
return animation('translateX', { x: fromX }, { x: toX }, duration);
|
|
25
35
|
}
|
|
36
|
+
/** Uniform scale from → to. */
|
|
26
37
|
export function scale(from = 0.8, to = 1, duration = 0.5) {
|
|
27
|
-
return
|
|
38
|
+
return animation('scale', { scale: from }, { scale: to }, duration);
|
|
28
39
|
}
|
|
29
|
-
|
|
30
|
-
|
|
40
|
+
/** Scale + fade entrance with a slight overshoot. */
|
|
41
|
+
export function popIn(duration = 0.7) {
|
|
42
|
+
return animation('pop', { scale: 0.55, opacity: 0 }, { scale: 1.06, opacity: 1 }, duration);
|
|
43
|
+
}
|
|
44
|
+
/** Upward entrance with a fade. */
|
|
45
|
+
export function riseIn(duration = 0.85, distance = 80) {
|
|
46
|
+
return animation('rise', { y: distance, opacity: 0 }, { y: 0, opacity: 1 }, duration);
|
|
47
|
+
}
|
|
48
|
+
/** Rotates in while sliding and fading. */
|
|
49
|
+
export function swingIn(duration = 0.9) {
|
|
50
|
+
return animation('swing', { rotation: -14, y: 35, opacity: 0 }, { rotation: 0, y: 0, opacity: 1 }, duration);
|
|
51
|
+
}
|
|
52
|
+
/** Jittery entrance with a flash. */
|
|
53
|
+
export function glitchIn(duration = 0.65) {
|
|
54
|
+
return animation('glitch', { opacity: 0 }, { opacity: 1 }, duration);
|
|
55
|
+
}
|
|
56
|
+
/** Gentle looping vertical bob. */
|
|
57
|
+
export function float(duration = 2.2, amplitude = 12) {
|
|
58
|
+
return animation('float', { y: -amplitude }, { y: amplitude }, duration, { loop: true });
|
|
59
|
+
}
|
|
60
|
+
/** Reveals text character by character (`stagger` on the text picks words or chars). */
|
|
61
|
+
export function typewriter(duration = 1.8) {
|
|
62
|
+
return animation('typewriter', {}, {}, duration);
|
|
63
|
+
}
|
|
64
|
+
/** Constant rotation in degrees over the whole window (`rotation:` in VidScript). */
|
|
65
|
+
export function rotate(from, to = from, duration = 0) {
|
|
66
|
+
return animation('rotation', { rotation: from }, { rotation: to }, duration);
|
|
67
|
+
}
|
|
68
|
+
/** Gain automation for an audio track (`animate: { type: "volume", … }`). */
|
|
69
|
+
export function volumeRamp(from, to, duration = 0, options = {}) {
|
|
70
|
+
return animation('volume', { volume: from }, { volume: to }, duration, options);
|
|
31
71
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"invoke.d.ts","sourceRoot":"","sources":["../../src/plugins/invoke.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAEzD,MAAM,MAAM,gBAAgB,GAAG,OAAO,CAAC;AACvC,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,UAAU,GAAG,gBAAgB,CAAC;AAE1E,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CACJ,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,SAAS,OAAO,EAAE,EAC9B,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACvC,OAAO,CAAC,OAAO,CAAC,CAAC;CACrB;AAsBD,mFAAmF;AACnF,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,OAAO,EAAE,GAAG;IACzD,UAAU,EAAE,OAAO,EAAE,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC,CAiBA;
|
|
1
|
+
{"version":3,"file":"invoke.d.ts","sourceRoot":"","sources":["../../src/plugins/invoke.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAEzD,MAAM,MAAM,gBAAgB,GAAG,OAAO,CAAC;AACvC,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,UAAU,GAAG,gBAAgB,CAAC;AAE1E,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CACJ,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,SAAS,OAAO,EAAE,EAC9B,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACvC,OAAO,CAAC,OAAO,CAAC,CAAC;CACrB;AAsBD,mFAAmF;AACnF,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,OAAO,EAAE,GAAG;IACzD,UAAU,EAAE,OAAO,EAAE,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC,CAiBA;AA+BD,0EAA0E;AAC1E,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,iBAAiB,CA2BlE;AAgBD,gEAAgE;AAChE,wBAAsB,iBAAiB,CACrC,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,GAAG,IAAI,EAAE,OAAO,EAAE,GACjB,OAAO,CAAC,SAAS,CAAC,CAMpB;AAED,kEAAkE;AAClE,wBAAsB,iBAAiB,CACrC,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,GAAG,IAAI,EAAE,OAAO,EAAE,GACjB,OAAO,CAAC,UAAU,CAAC,CAMrB;AAED,mEAAmE;AACnE,wBAAsB,gBAAgB,CAAC,CAAC,GAAG,OAAO,EAChD,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,GAAG,IAAI,EAAE,OAAO,EAAE,GACjB,OAAO,CAAC,CAAC,CAAC,CAGZ;AAED,mFAAmF;AACnF,wBAAsB,YAAY,CAChC,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,GAAG,IAAI,EAAE,OAAO,EAAE,GACjB,OAAO,CAAC,iBAAiB,CAAC,CAE5B"}
|
package/dist/plugins/invoke.js
CHANGED
|
@@ -55,10 +55,13 @@ export function mapPluginResult(result) {
|
|
|
55
55
|
return result;
|
|
56
56
|
const r = result;
|
|
57
57
|
if (r.type === 'video' && typeof r.url === 'string') {
|
|
58
|
+
// Like VidScript: the media's pixel size is not a placement size, and a
|
|
59
|
+
// generated clip with sound brings it along.
|
|
60
|
+
const metadata = (r.metadata && typeof r.metadata === 'object' ? r.metadata : {});
|
|
61
|
+
const nativeAudio = metadata.native_audio === true || metadata.has_audio === true;
|
|
58
62
|
return video(r.url, {
|
|
59
63
|
duration: typeof r.duration === 'number' ? r.duration : 5,
|
|
60
|
-
|
|
61
|
-
height: typeof r.height === 'number' ? r.height : undefined,
|
|
64
|
+
...(nativeAudio ? { nativeAudio: true } : {}),
|
|
62
65
|
});
|
|
63
66
|
}
|
|
64
67
|
if (r.type === 'audio' && typeof r.url === 'string') {
|
package/dist/timeline.d.ts
CHANGED
|
@@ -21,6 +21,9 @@ export interface Timeline extends TimelineLike {
|
|
|
21
21
|
* t.push(text('Tail'))
|
|
22
22
|
* return t.toIR()
|
|
23
23
|
* ```
|
|
24
|
+
*
|
|
25
|
+
* Without `width`/`height`/`fps` it takes the host canvas (the importing
|
|
26
|
+
* VidScript's output, or its parent's when nested), else 1080×1920 at 30 fps.
|
|
24
27
|
*/
|
|
25
28
|
export declare function timeline(options?: TimelineOptions): Timeline;
|
|
26
29
|
export declare function isTimeline(value: unknown): value is Timeline;
|
package/dist/timeline.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeline.d.ts","sourceRoot":"","sources":["../src/timeline.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"timeline.d.ts","sourceRoot":"","sources":["../src/timeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0C,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAYvF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,KAAK,EAGV,WAAW,EACX,WAAW,EACX,YAAY,EACZ,eAAe,EAIhB,MAAM,YAAY,CAAC;AAmEpB,MAAM,WAAW,QAAS,SAAQ,YAAY;IAC5C,mDAAmD;IACnD,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,WAAW,GAAG,WAAW,GAAG,OAAO,CAAC;IACvE,IAAI,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC3B,KAAK,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,CAAC;IAClE,IAAI,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IACrD,IAAI,IAAI,UAAU,CAAC;CACpB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,QAAQ,CAAC,OAAO,GAAE,eAAoB,GAAG,QAAQ,CAoGhE;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CAQ5D"}
|