pixi-effects 0.16.3 → 0.17.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/CHANGELOG.md +29 -0
- package/README.md +6 -6
- package/ai/CHAT.md +4 -0
- package/ai/SKILL.md +2 -1
- package/ai/chat-template.html +3 -3
- package/ai/reference/cheatsheet.md +4 -2
- package/ai/reference/pitfalls.md +3 -0
- package/ai/reference/recipes.md +67 -0
- package/ai/template.html +5 -5
- package/ai/tools/check.mjs +162 -18
- package/ai/tools/render.mjs +21 -7
- package/dist/Composition-HYAOYPAX.js +5 -0
- package/dist/{Composition-VJWY2NDL.js.map → Composition-HYAOYPAX.js.map} +1 -1
- package/dist/Composition-LMVEASHF.cjs +14 -0
- package/dist/{Composition-3WWNVRR5.cjs.map → Composition-LMVEASHF.cjs.map} +1 -1
- package/dist/Controller.d.cts +1 -1
- package/dist/Controller.d.ts +1 -1
- package/dist/{Movie-oA0SUAyb.d.cts → Movie-CoxF2Z7t.d.cts} +82 -2
- package/dist/{Movie-DilBC348.d.ts → Movie-DHJRrUfI.d.ts} +82 -2
- package/dist/Presenter.d.cts +1 -1
- package/dist/Presenter.d.ts +1 -1
- package/dist/{chunk-2ROVCPSL.cjs → chunk-ETAIVOHP.cjs} +32 -27
- package/dist/chunk-ETAIVOHP.cjs.map +1 -0
- package/dist/{chunk-FJA5VUOF.cjs → chunk-LBJRXU7N.cjs} +15 -3
- package/dist/chunk-LBJRXU7N.cjs.map +1 -0
- package/dist/{chunk-ZNKQH356.js → chunk-ORTYBA42.js} +15 -4
- package/dist/chunk-ORTYBA42.js.map +1 -0
- package/dist/{chunk-XTIHKODH.js → chunk-Q3IVBYT7.js} +10 -5
- package/dist/chunk-Q3IVBYT7.js.map +1 -0
- package/dist/index.cjs +351 -41
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +329 -19
- package/dist/index.js.map +1 -1
- package/dist/loader.css +1 -1
- package/dist/three.cjs +5 -5
- package/dist/three.js +1 -1
- package/docs/dsl.md +2 -1
- package/llms-full.txt +78 -4
- package/llms.txt +1 -1
- package/package.json +6 -3
- package/dist/Composition-3WWNVRR5.cjs +0 -14
- package/dist/Composition-VJWY2NDL.js +0 -5
- package/dist/chunk-2ROVCPSL.cjs.map +0 -1
- package/dist/chunk-FJA5VUOF.cjs.map +0 -1
- package/dist/chunk-XTIHKODH.js.map +0 -1
- package/dist/chunk-ZNKQH356.js.map +0 -1
package/ai/tools/render.mjs
CHANGED
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
*
|
|
14
14
|
* Options: -o, --out FILE (default ./<page name>.<format>; the container follows the extension: mp4 webm mov mkv) ·
|
|
15
15
|
* --format mp4|webm|mov|mkv|pdf (a .pdf is the deck as pages: one picture per page of a movie with `stops`; --all-stops makes a page of every stop) · --quality very-low|low|medium|high|very-high (video and audio bitrate, default high) ·
|
|
16
|
+
* --range A:B (only seconds A to B; 2: runs to the end, :5 starts at the beginning) · --scene NAME (the span of a top-level layer: a movie's scene) · --scale S (output size, 0 < S <= 1: smaller picture, smaller file) ·
|
|
17
|
+
* --draft (for looking: half size, low quality, no motion blur; a --quality you give still wins) ·
|
|
16
18
|
* --motion-blur SAMPLES (2-64: each frame is drawn that many times over the shutter and averaged; overrides the page's own motionBlur) ·
|
|
17
19
|
* --shutter FRACTION (with --motion-blur: how long the shutter is open, 0-1 of a frame, default 0.5) ·
|
|
18
20
|
* --video-codec C · --audio-codec C · --query "a=1&b=2" (added to the page URL) · --fail-on-warn (exit 1 when the page
|
|
@@ -41,7 +43,7 @@ export function defaultOutput(page, format) {
|
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
export function parseRenderArgs(argv) {
|
|
44
|
-
const o = { page: null, out: null, format: null, quality: 'high', videoCodec: null, audioCodec: null, allStops: false, motionBlur: null, shutter: null, query: null, failOnWarn: false, quiet: false, timeout: 900, root: null, chrome: null };
|
|
46
|
+
const o = { range: null, scene: null, scale: null, draft: false, qualityGiven: false, page: null, out: null, format: null, quality: 'high', videoCodec: null, audioCodec: null, allStops: false, motionBlur: null, shutter: null, query: null, failOnWarn: false, quiet: false, timeout: 900, root: null, chrome: null };
|
|
45
47
|
const need = (i, name) => { if (i + 1 >= argv.length) throw new Error(`${name} needs a value`); return argv[i + 1]; };
|
|
46
48
|
for (let i = 0; i < argv.length; i++) {
|
|
47
49
|
const a = argv[i];
|
|
@@ -50,6 +52,7 @@ export function parseRenderArgs(argv) {
|
|
|
50
52
|
o.format = need(i++, a).toLowerCase();
|
|
51
53
|
if (!FORMATS.includes(o.format)) throw new Error(`--format must be mp4, webm, mov, mkv or pdf (got "${o.format}")`);
|
|
52
54
|
} else if (a === '--quality') {
|
|
55
|
+
o.qualityGiven = true;
|
|
53
56
|
o.quality = need(i++, a);
|
|
54
57
|
if (!QUALITIES.includes(o.quality)) throw new Error(`--quality must be very-low, low, medium, high or very-high (got "${o.quality}")`);
|
|
55
58
|
} else if (a === '--video-codec') o.videoCodec = need(i++, a);
|
|
@@ -60,7 +63,16 @@ export function parseRenderArgs(argv) {
|
|
|
60
63
|
} else if (a === '--shutter') {
|
|
61
64
|
o.shutter = Number(need(i++, a));
|
|
62
65
|
if (!(o.shutter > 0 && o.shutter <= 1)) throw new Error(`--shutter must be above 0 and at most 1 (0.5 is a 180° shutter), got "${argv[i]}"`);
|
|
63
|
-
} else if (a === '--
|
|
66
|
+
} else if (a === '--range') {
|
|
67
|
+
const m = need(i++, a).match(/^(\d+(?:\.\d+)?)?:(\d+(?:\.\d+)?)?$/);
|
|
68
|
+
if (!m || (m[1] === undefined && m[2] === undefined)) throw new Error(`--range must look like 2:5 (seconds; 2: runs to the end, :5 starts at the beginning), got "${argv[i]}"`);
|
|
69
|
+
o.range = [m[1] === undefined ? null : Number(m[1]), m[2] === undefined ? null : Number(m[2])];
|
|
70
|
+
} else if (a === '--scene') o.scene = need(i++, a);
|
|
71
|
+
else if (a === '--scale') {
|
|
72
|
+
o.scale = Number(need(i++, a));
|
|
73
|
+
if (!(o.scale > 0 && o.scale <= 1)) throw new Error(`--scale must be above 0 and at most 1 (0.5 is half size), got "${argv[i]}"`);
|
|
74
|
+
} else if (a === '--draft') o.draft = true;
|
|
75
|
+
else if (a === '--query') o.query = need(i++, a).replace(/^\?/, '');
|
|
64
76
|
else if (a === '--all-stops') o.allStops = true;
|
|
65
77
|
else if (a === '--fail-on-warn') o.failOnWarn = true;
|
|
66
78
|
else if (a === '--quiet') o.quiet = true;
|
|
@@ -74,6 +86,8 @@ export function parseRenderArgs(argv) {
|
|
|
74
86
|
if (o.shutter !== null && o.motionBlur === null) throw new Error('--shutter goes with --motion-blur SAMPLES');
|
|
75
87
|
if (o.allStops && o.format !== 'pdf' && !(o.out && formatFromPath(o.out) === 'pdf')) throw new Error('--all-stops goes with a PDF (-o deck.pdf)');
|
|
76
88
|
o.format ??= (o.out ? formatFromPath(o.out) : null) ?? 'mp4';
|
|
89
|
+
if (o.range && o.scene !== null) throw new Error('--range and --scene cannot be used together: a scene is a range');
|
|
90
|
+
if (o.format === 'pdf' && (o.range || o.scene !== null || o.scale !== null || o.draft)) throw new Error('--range, --scene, --scale and --draft are not for a PDF (a PDF is the pages of a deck)');
|
|
77
91
|
return o;
|
|
78
92
|
}
|
|
79
93
|
|
|
@@ -81,16 +95,16 @@ const UPLOAD = '/__pixi_effects_out';
|
|
|
81
95
|
const kb = n => (n >= 1048576 ? `${(n / 1048576).toFixed(2)} MB` : `${Math.max(1, Math.round(n / 1024))} KB`);
|
|
82
96
|
|
|
83
97
|
/** The page's side: render, and PUT the Blob to our server (so a long video never goes through base64). */
|
|
84
|
-
const renderScript = (format, quality, videoCodec, audioCodec, motionBlur, shutter, allStops) => `(() => {
|
|
98
|
+
const renderScript = (format, quality, videoCodec, audioCodec, motionBlur, shutter, allStops, part = {}) => `(() => {
|
|
85
99
|
window.__r = { state: 'running', progress: 0 };
|
|
86
100
|
movie.on('progress', e => { window.__r.progress = e.progress; });
|
|
87
101
|
(async () => {
|
|
88
102
|
try {
|
|
89
|
-
const video = { bitrate: ${JSON.stringify(quality)}
|
|
103
|
+
const video = { ${[part.draft && !part.qualityGiven ? null : `bitrate: ${JSON.stringify(quality)}`, videoCodec ? `codec: ${JSON.stringify(videoCodec)}` : null].filter(Boolean).join(', ')} };
|
|
90
104
|
const audio = { bitrate: ${JSON.stringify(quality)}${audioCodec ? `, codec: ${JSON.stringify(audioCodec)}` : ''} };
|
|
91
105
|
const blob = ${JSON.stringify(format)} === 'pdf'
|
|
92
106
|
? await movie.exportPDF({ which: ${allStops ? "'stops'" : "'pages'"}, title: document.title })
|
|
93
|
-
: await movie.render({ format: ${JSON.stringify(format)}, video, audio${motionBlur ? `, motionBlur: { samples: ${motionBlur}${shutter ? `, shutter: ${shutter}` : ''} }` : ''} });
|
|
107
|
+
: await movie.render({ format: ${JSON.stringify(format)}, video, audio${part.range ? `, range: [${part.range[0] ?? 0}, ${part.range[1] ?? 'movie.duration'}]` : ''}${part.scene ? `, range: ${JSON.stringify(part.scene)}` : ''}${part.scale ? `, scale: ${part.scale}` : ''}${part.draft ? ', draft: true' : ''}${motionBlur ? `, motionBlur: { samples: ${motionBlur}${shutter ? `, shutter: ${shutter}` : ''} }` : ''} });
|
|
94
108
|
const res = await fetch(${JSON.stringify(UPLOAD)}, { method: 'PUT', body: blob });
|
|
95
109
|
if (!res.ok) throw new Error('could not hand the file to the command (' + res.status + ')');
|
|
96
110
|
window.__r = { state: 'done', bytes: blob.size, type: blob.type };
|
|
@@ -156,7 +170,7 @@ export async function runRender(opts, log = console.log, progress = () => {}) {
|
|
|
156
170
|
}
|
|
157
171
|
Object.assign(result, { duration: info.duration, frames: info.totalFrames, width: info.width, height: info.height, frameRate: info.frameRate });
|
|
158
172
|
|
|
159
|
-
await cdp.eval(renderScript(opts.format, opts.quality, opts.videoCodec, opts.audioCodec, opts.motionBlur, opts.shutter, opts.allStops));
|
|
173
|
+
await cdp.eval(renderScript(opts.format, opts.quality, opts.videoCodec, opts.audioCodec, opts.motionBlur, opts.shutter, opts.allStops, { range: opts.range, scene: opts.scene, scale: opts.scale, draft: opts.draft, qualityGiven: opts.qualityGiven }));
|
|
160
174
|
let state;
|
|
161
175
|
for (;;) {
|
|
162
176
|
left();
|
|
@@ -190,7 +204,7 @@ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href)
|
|
|
190
204
|
if (typeof WebSocket === 'undefined') throw new Error('Node >= 22 is needed (built-in WebSocket)');
|
|
191
205
|
const opts = parseRenderArgs(process.argv.slice(2));
|
|
192
206
|
if (!opts.page) {
|
|
193
|
-
console.error('usage: node ai/tools/render.mjs <page.html> [-o out.mp4] [--format mp4|webm|mov|mkv|pdf] [--quality very-low|low|medium|high|very-high] [--query a=1] [--fail-on-warn] [--quiet] [--timeout S] [--root DIR] [--chrome PATH]');
|
|
207
|
+
console.error('usage: node ai/tools/render.mjs <page.html> [-o out.mp4] [--format mp4|webm|mov|mkv|pdf] [--range A:B | --scene NAME] [--scale S] [--draft] [--quality very-low|low|medium|high|very-high] [--query a=1] [--fail-on-warn] [--quiet] [--timeout S] [--root DIR] [--chrome PATH]');
|
|
194
208
|
process.exit(2);
|
|
195
209
|
}
|
|
196
210
|
let last = -1;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-HYAOYPAX.js"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkETAIVOHP_cjs = require('./chunk-ETAIVOHP.cjs');
|
|
4
|
+
require('./chunk-LBJRXU7N.cjs');
|
|
5
|
+
require('./chunk-UQBGW5DO.cjs');
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
Object.defineProperty(exports, "CompositionSequence", {
|
|
10
|
+
enumerable: true,
|
|
11
|
+
get: function () { return chunkETAIVOHP_cjs.CompositionSequence; }
|
|
12
|
+
});
|
|
13
|
+
//# sourceMappingURL=Composition-LMVEASHF.cjs.map
|
|
14
|
+
//# sourceMappingURL=Composition-LMVEASHF.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-LMVEASHF.cjs"}
|
package/dist/Controller.d.cts
CHANGED
package/dist/Controller.d.ts
CHANGED
|
@@ -5,6 +5,12 @@ import { U as Stop, e as AssetSpec, c as CompositionSpec } from './types-SS51lP6
|
|
|
5
5
|
interface AudioInspectOptions {
|
|
6
6
|
/** Width of each loudness window in seconds. Default 0.1. */
|
|
7
7
|
window?: number;
|
|
8
|
+
/** The scenes to measure one by one (`movie.inspectAudio()` fills this from the movie's named top-level compositions). Default: the whole movie as one. */
|
|
9
|
+
scenes?: Array<{
|
|
10
|
+
name: string;
|
|
11
|
+
start: number;
|
|
12
|
+
end: number;
|
|
13
|
+
}>;
|
|
8
14
|
}
|
|
9
15
|
/** One sound measured ON ITS OWN (not mixed), so overlapping layers do not blur it. */
|
|
10
16
|
interface SoundMeasure {
|
|
@@ -44,8 +50,44 @@ interface AudioReport {
|
|
|
44
50
|
peakDb: number;
|
|
45
51
|
brightnessHz: number;
|
|
46
52
|
}>;
|
|
47
|
-
/** Problems worth fixing: limiting, inaudible layers, sounds cut off by the end of the movie. Read this first. */
|
|
53
|
+
/** Problems worth fixing: limiting, clipping, a true peak above 0 dBTP, inaudible layers, sounds cut off by the end of the movie. Read this first. */
|
|
48
54
|
issues: string[];
|
|
55
|
+
/** The mix as a whole (ITU-R BS.1770 loudness; `integratedLufs` is null for a mix shorter than 0.4 s or with nothing above −70 LUFS). */
|
|
56
|
+
loudness: {
|
|
57
|
+
integratedLufs: number | null;
|
|
58
|
+
truePeakDb: number;
|
|
59
|
+
clippedSamples: number;
|
|
60
|
+
silences: Array<{
|
|
61
|
+
from: number;
|
|
62
|
+
to: number;
|
|
63
|
+
}>;
|
|
64
|
+
};
|
|
65
|
+
/** The same measure for each scene (`silent`: nothing above −60 dBFS). */
|
|
66
|
+
scenes: Array<{
|
|
67
|
+
name: string;
|
|
68
|
+
from: number;
|
|
69
|
+
to: number;
|
|
70
|
+
lufs: number | null;
|
|
71
|
+
peakDb: number;
|
|
72
|
+
silent: boolean;
|
|
73
|
+
}>;
|
|
74
|
+
/** When each sound starts: what a waveform marks. */
|
|
75
|
+
cues: Array<{
|
|
76
|
+
t: number;
|
|
77
|
+
name: string;
|
|
78
|
+
}>;
|
|
79
|
+
/** Advice, not faults: a mix that is quiet or loud for web video, little headroom, silent stretches. */
|
|
80
|
+
notes: string[];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
interface FontReport {
|
|
84
|
+
/** Text layers none of whose fonts is available here: they are drawn in a fallback. */
|
|
85
|
+
missing: Array<{
|
|
86
|
+
layer: string;
|
|
87
|
+
family: string;
|
|
88
|
+
}>;
|
|
89
|
+
/** Web fonts (`@font-face`) whose file could not be loaded. */
|
|
90
|
+
failed: string[];
|
|
49
91
|
}
|
|
50
92
|
|
|
51
93
|
interface Rect {
|
|
@@ -196,6 +238,15 @@ interface RenderOptions {
|
|
|
196
238
|
codec?: string;
|
|
197
239
|
bitrate?: 'very-low' | 'low' | 'medium' | 'high' | 'very-high';
|
|
198
240
|
};
|
|
241
|
+
/**
|
|
242
|
+
* Only part of the movie: `[from, to]` in seconds, or the name of a top-level layer (its start to its end). The file starts at 0
|
|
243
|
+
* and its sound is cut the same way (with a 10 ms fade on a cut edge). Past the end, backwards or an unknown name is an error.
|
|
244
|
+
*/
|
|
245
|
+
range?: [number, number] | string;
|
|
246
|
+
/** Output size relative to the canvas, above 0 and at most 1. Each frame is drawn at full size and copied smaller before it is encoded: the file and the encoding get smaller, the drawing is not faster. */
|
|
247
|
+
scale?: number;
|
|
248
|
+
/** For looking, not for delivering: `scale: 0.5`, `video.bitrate: 'low'` and no motion blur. Anything you set yourself wins. */
|
|
249
|
+
draft?: boolean;
|
|
199
250
|
}
|
|
200
251
|
interface SnapshotOptions {
|
|
201
252
|
/** Motion blur for this picture (see `MovieOptions.motionBlur`); overrides the movie's own setting, `false` turns it off. */
|
|
@@ -248,6 +299,19 @@ interface ContactSheetOptions {
|
|
|
248
299
|
cellWidth?: number;
|
|
249
300
|
as?: 'blob' | 'dataURL';
|
|
250
301
|
}
|
|
302
|
+
interface OnionSkinOptions {
|
|
303
|
+
/** Where the movement starts, in seconds. Default 0. */
|
|
304
|
+
from?: number;
|
|
305
|
+
/** Where it ends, in seconds. Default: the end of the movie. */
|
|
306
|
+
to?: number;
|
|
307
|
+
/** How many frames are overlaid, 1–64, spread evenly from `from` to `to`. Default 8. */
|
|
308
|
+
count?: number;
|
|
309
|
+
/** Output size relative to the canvas, above 0 and at most 4. Default 1. */
|
|
310
|
+
scale?: number;
|
|
311
|
+
/** Motion blur for these pictures (see `MovieOptions.motionBlur`); overrides the movie's own setting, `false` turns it off. */
|
|
312
|
+
motionBlur?: MotionBlurSpec;
|
|
313
|
+
as?: 'blob' | 'dataURL';
|
|
314
|
+
}
|
|
251
315
|
interface FrameEvent {
|
|
252
316
|
frame: number;
|
|
253
317
|
totalFrames: number;
|
|
@@ -419,6 +483,17 @@ declare class Movie {
|
|
|
419
483
|
motionBlur?: MotionBlurSpec;
|
|
420
484
|
onImage?: (image: StopImage) => void;
|
|
421
485
|
}): Promise<Blob>;
|
|
486
|
+
/**
|
|
487
|
+
* One picture of a movement: `count` frames from `from` to `to` drawn one over the other, each later one stronger, so a thing that
|
|
488
|
+
* moves leaves a trail (its path and its easing: even steps = constant speed, bunched = slow) and what stays still stays itself.
|
|
489
|
+
* The movie is left where it was.
|
|
490
|
+
*/
|
|
491
|
+
onionSkin(opts?: OnionSkinOptions & {
|
|
492
|
+
as?: 'blob';
|
|
493
|
+
}): Promise<Blob>;
|
|
494
|
+
onionSkin(opts: OnionSkinOptions & {
|
|
495
|
+
as: 'dataURL';
|
|
496
|
+
}): Promise<string>;
|
|
422
497
|
/**
|
|
423
498
|
* Several frames on ONE image, each labelled with its frame number and time — the cheapest way to
|
|
424
499
|
* check a whole animation by eye. Restores the current frame afterwards.
|
|
@@ -447,6 +522,11 @@ declare class Movie {
|
|
|
447
522
|
* The soundtrack, measured: when every audio layer plays, how loud the mix is over time, and `issues`
|
|
448
523
|
* (limiting, inaudible layers, sounds cut off by the end). Use it to CHECK sound you cannot hear.
|
|
449
524
|
*/
|
|
525
|
+
/**
|
|
526
|
+
* The fonts of the text layers: `missing` lists layers none of whose `fontFamily` entries is available in this browser (they are
|
|
527
|
+
* drawn in a fallback font), `failed` lists web fonts (`@font-face`) whose file could not be loaded.
|
|
528
|
+
*/
|
|
529
|
+
inspectFonts(): FontReport;
|
|
450
530
|
inspectAudio(opts?: AudioInspectOptions): AudioReport;
|
|
451
531
|
/**
|
|
452
532
|
* Draw the stage now. PixiJS's CullerPlugin only culls inside `app.render()` (the ticker), and we render
|
|
@@ -509,4 +589,4 @@ declare class Movie {
|
|
|
509
589
|
destroy(): Promise<void>;
|
|
510
590
|
}
|
|
511
591
|
|
|
512
|
-
export { type AudioInspectOptions as A, type ContactSheetOptions as C, type
|
|
592
|
+
export { type AudioInspectOptions as A, type ContactSheetOptions as C, type FontReport as F, type InspectOptions as I, type LayerInfo as L, type MotionBlurOptions as M, type OnionSkinOptions as O, type ProgressEvent as P, type RenderOptions as R, type SnapshotOptions as S, type TimelineData as T, type VolumeEvent as V, type AudioReport as a, type AudioSourceReport as b, type FrameEvent as c, type InspectReport as d, type LoaderOption as e, type MotionBlurSpec as f, Movie as g, type MovieErrorEvent as h, type MovieOptions as i, type SoundMeasure as j, type StopEvent as k, type StopImage as l, type StopImagesOptions as m, type TimelineHtmlOptions as n, type TimelineRow as o, type TimelineSvgOptions as p, type TimelineTransition as q, dismissLoader as r, failLoader as s, resolveLoader as t };
|
|
@@ -5,6 +5,12 @@ import { U as Stop, e as AssetSpec, c as CompositionSpec } from './types-SS51lP6
|
|
|
5
5
|
interface AudioInspectOptions {
|
|
6
6
|
/** Width of each loudness window in seconds. Default 0.1. */
|
|
7
7
|
window?: number;
|
|
8
|
+
/** The scenes to measure one by one (`movie.inspectAudio()` fills this from the movie's named top-level compositions). Default: the whole movie as one. */
|
|
9
|
+
scenes?: Array<{
|
|
10
|
+
name: string;
|
|
11
|
+
start: number;
|
|
12
|
+
end: number;
|
|
13
|
+
}>;
|
|
8
14
|
}
|
|
9
15
|
/** One sound measured ON ITS OWN (not mixed), so overlapping layers do not blur it. */
|
|
10
16
|
interface SoundMeasure {
|
|
@@ -44,8 +50,44 @@ interface AudioReport {
|
|
|
44
50
|
peakDb: number;
|
|
45
51
|
brightnessHz: number;
|
|
46
52
|
}>;
|
|
47
|
-
/** Problems worth fixing: limiting, inaudible layers, sounds cut off by the end of the movie. Read this first. */
|
|
53
|
+
/** Problems worth fixing: limiting, clipping, a true peak above 0 dBTP, inaudible layers, sounds cut off by the end of the movie. Read this first. */
|
|
48
54
|
issues: string[];
|
|
55
|
+
/** The mix as a whole (ITU-R BS.1770 loudness; `integratedLufs` is null for a mix shorter than 0.4 s or with nothing above −70 LUFS). */
|
|
56
|
+
loudness: {
|
|
57
|
+
integratedLufs: number | null;
|
|
58
|
+
truePeakDb: number;
|
|
59
|
+
clippedSamples: number;
|
|
60
|
+
silences: Array<{
|
|
61
|
+
from: number;
|
|
62
|
+
to: number;
|
|
63
|
+
}>;
|
|
64
|
+
};
|
|
65
|
+
/** The same measure for each scene (`silent`: nothing above −60 dBFS). */
|
|
66
|
+
scenes: Array<{
|
|
67
|
+
name: string;
|
|
68
|
+
from: number;
|
|
69
|
+
to: number;
|
|
70
|
+
lufs: number | null;
|
|
71
|
+
peakDb: number;
|
|
72
|
+
silent: boolean;
|
|
73
|
+
}>;
|
|
74
|
+
/** When each sound starts: what a waveform marks. */
|
|
75
|
+
cues: Array<{
|
|
76
|
+
t: number;
|
|
77
|
+
name: string;
|
|
78
|
+
}>;
|
|
79
|
+
/** Advice, not faults: a mix that is quiet or loud for web video, little headroom, silent stretches. */
|
|
80
|
+
notes: string[];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
interface FontReport {
|
|
84
|
+
/** Text layers none of whose fonts is available here: they are drawn in a fallback. */
|
|
85
|
+
missing: Array<{
|
|
86
|
+
layer: string;
|
|
87
|
+
family: string;
|
|
88
|
+
}>;
|
|
89
|
+
/** Web fonts (`@font-face`) whose file could not be loaded. */
|
|
90
|
+
failed: string[];
|
|
49
91
|
}
|
|
50
92
|
|
|
51
93
|
interface Rect {
|
|
@@ -196,6 +238,15 @@ interface RenderOptions {
|
|
|
196
238
|
codec?: string;
|
|
197
239
|
bitrate?: 'very-low' | 'low' | 'medium' | 'high' | 'very-high';
|
|
198
240
|
};
|
|
241
|
+
/**
|
|
242
|
+
* Only part of the movie: `[from, to]` in seconds, or the name of a top-level layer (its start to its end). The file starts at 0
|
|
243
|
+
* and its sound is cut the same way (with a 10 ms fade on a cut edge). Past the end, backwards or an unknown name is an error.
|
|
244
|
+
*/
|
|
245
|
+
range?: [number, number] | string;
|
|
246
|
+
/** Output size relative to the canvas, above 0 and at most 1. Each frame is drawn at full size and copied smaller before it is encoded: the file and the encoding get smaller, the drawing is not faster. */
|
|
247
|
+
scale?: number;
|
|
248
|
+
/** For looking, not for delivering: `scale: 0.5`, `video.bitrate: 'low'` and no motion blur. Anything you set yourself wins. */
|
|
249
|
+
draft?: boolean;
|
|
199
250
|
}
|
|
200
251
|
interface SnapshotOptions {
|
|
201
252
|
/** Motion blur for this picture (see `MovieOptions.motionBlur`); overrides the movie's own setting, `false` turns it off. */
|
|
@@ -248,6 +299,19 @@ interface ContactSheetOptions {
|
|
|
248
299
|
cellWidth?: number;
|
|
249
300
|
as?: 'blob' | 'dataURL';
|
|
250
301
|
}
|
|
302
|
+
interface OnionSkinOptions {
|
|
303
|
+
/** Where the movement starts, in seconds. Default 0. */
|
|
304
|
+
from?: number;
|
|
305
|
+
/** Where it ends, in seconds. Default: the end of the movie. */
|
|
306
|
+
to?: number;
|
|
307
|
+
/** How many frames are overlaid, 1–64, spread evenly from `from` to `to`. Default 8. */
|
|
308
|
+
count?: number;
|
|
309
|
+
/** Output size relative to the canvas, above 0 and at most 4. Default 1. */
|
|
310
|
+
scale?: number;
|
|
311
|
+
/** Motion blur for these pictures (see `MovieOptions.motionBlur`); overrides the movie's own setting, `false` turns it off. */
|
|
312
|
+
motionBlur?: MotionBlurSpec;
|
|
313
|
+
as?: 'blob' | 'dataURL';
|
|
314
|
+
}
|
|
251
315
|
interface FrameEvent {
|
|
252
316
|
frame: number;
|
|
253
317
|
totalFrames: number;
|
|
@@ -419,6 +483,17 @@ declare class Movie {
|
|
|
419
483
|
motionBlur?: MotionBlurSpec;
|
|
420
484
|
onImage?: (image: StopImage) => void;
|
|
421
485
|
}): Promise<Blob>;
|
|
486
|
+
/**
|
|
487
|
+
* One picture of a movement: `count` frames from `from` to `to` drawn one over the other, each later one stronger, so a thing that
|
|
488
|
+
* moves leaves a trail (its path and its easing: even steps = constant speed, bunched = slow) and what stays still stays itself.
|
|
489
|
+
* The movie is left where it was.
|
|
490
|
+
*/
|
|
491
|
+
onionSkin(opts?: OnionSkinOptions & {
|
|
492
|
+
as?: 'blob';
|
|
493
|
+
}): Promise<Blob>;
|
|
494
|
+
onionSkin(opts: OnionSkinOptions & {
|
|
495
|
+
as: 'dataURL';
|
|
496
|
+
}): Promise<string>;
|
|
422
497
|
/**
|
|
423
498
|
* Several frames on ONE image, each labelled with its frame number and time — the cheapest way to
|
|
424
499
|
* check a whole animation by eye. Restores the current frame afterwards.
|
|
@@ -447,6 +522,11 @@ declare class Movie {
|
|
|
447
522
|
* The soundtrack, measured: when every audio layer plays, how loud the mix is over time, and `issues`
|
|
448
523
|
* (limiting, inaudible layers, sounds cut off by the end). Use it to CHECK sound you cannot hear.
|
|
449
524
|
*/
|
|
525
|
+
/**
|
|
526
|
+
* The fonts of the text layers: `missing` lists layers none of whose `fontFamily` entries is available in this browser (they are
|
|
527
|
+
* drawn in a fallback font), `failed` lists web fonts (`@font-face`) whose file could not be loaded.
|
|
528
|
+
*/
|
|
529
|
+
inspectFonts(): FontReport;
|
|
450
530
|
inspectAudio(opts?: AudioInspectOptions): AudioReport;
|
|
451
531
|
/**
|
|
452
532
|
* Draw the stage now. PixiJS's CullerPlugin only culls inside `app.render()` (the ticker), and we render
|
|
@@ -509,4 +589,4 @@ declare class Movie {
|
|
|
509
589
|
destroy(): Promise<void>;
|
|
510
590
|
}
|
|
511
591
|
|
|
512
|
-
export { type AudioInspectOptions as A, type ContactSheetOptions as C, type
|
|
592
|
+
export { type AudioInspectOptions as A, type ContactSheetOptions as C, type FontReport as F, type InspectOptions as I, type LayerInfo as L, type MotionBlurOptions as M, type OnionSkinOptions as O, type ProgressEvent as P, type RenderOptions as R, type SnapshotOptions as S, type TimelineData as T, type VolumeEvent as V, type AudioReport as a, type AudioSourceReport as b, type FrameEvent as c, type InspectReport as d, type LoaderOption as e, type MotionBlurSpec as f, Movie as g, type MovieErrorEvent as h, type MovieOptions as i, type SoundMeasure as j, type StopEvent as k, type StopImage as l, type StopImagesOptions as m, type TimelineHtmlOptions as n, type TimelineRow as o, type TimelineSvgOptions as p, type TimelineTransition as q, dismissLoader as r, failLoader as s, resolveLoader as t };
|
package/dist/Presenter.d.cts
CHANGED
package/dist/Presenter.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkLBJRXU7N_cjs = require('./chunk-LBJRXU7N.cjs');
|
|
4
4
|
var chunkUQBGW5DO_cjs = require('./chunk-UQBGW5DO.cjs');
|
|
5
5
|
var pixi_js = require('pixi.js');
|
|
6
6
|
var gsap = require('gsap');
|
|
@@ -65,8 +65,8 @@ function readLayerTransform(target) {
|
|
|
65
65
|
x: c.x,
|
|
66
66
|
y: c.y,
|
|
67
67
|
z: c.z ?? 0,
|
|
68
|
-
rotationX: (c.rotationX ?? 0) *
|
|
69
|
-
rotationY: (c.rotationY ?? 0) *
|
|
68
|
+
rotationX: (c.rotationX ?? 0) * chunkLBJRXU7N_cjs.DEG,
|
|
69
|
+
rotationY: (c.rotationY ?? 0) * chunkLBJRXU7N_cjs.DEG,
|
|
70
70
|
rotationZ: c.rotation,
|
|
71
71
|
scaleX: c.scale.x,
|
|
72
72
|
scaleY: c.scale.y,
|
|
@@ -111,19 +111,19 @@ var Layer3D = class {
|
|
|
111
111
|
if (!(needW >= 1 && needH >= 1) || !Number.isFinite(bounds.x) || !Number.isFinite(bounds.y)) return this.hide();
|
|
112
112
|
const size = this.sizeFor(needW, needH);
|
|
113
113
|
const frame = { x: bounds.x - pad, y: bounds.y - pad, width: size.w, height: size.h };
|
|
114
|
-
const projected =
|
|
114
|
+
const projected = chunkLBJRXU7N_cjs.projectLayer(readLayerTransform(target), frame, basis);
|
|
115
115
|
this.depth = projected.depth;
|
|
116
116
|
if (!projected.visible) {
|
|
117
117
|
if (!this.warnedBehind) {
|
|
118
118
|
this.warnedBehind = true;
|
|
119
119
|
if (this.seq.spec.hideBehindCamera) return this.hide();
|
|
120
|
-
if (projected.depth >
|
|
120
|
+
if (projected.depth > chunkLBJRXU7N_cjs.NEAR) {
|
|
121
121
|
console.warn(
|
|
122
|
-
`pixi-effects: ${
|
|
122
|
+
`pixi-effects: ${chunkLBJRXU7N_cjs.describeLayer(this.seq.spec)}: part of it (a corner) reaches the camera plane (z = ${(target.z ?? 0).toFixed(0)}, rotationX ${(target.rotationX ?? 0).toFixed(0)}\xB0, rotationY ${(target.rotationY ?? 0).toFixed(0)}\xB0), and a layer is hidden whole when any corner is at or behind it. Tilt it less (rotationX / rotationY), make it smaller, or keep the camera further away from it (the default camera sits at z = (height/2)/tan(fov/2), about 989 for 720p at fov 40).`
|
|
123
123
|
);
|
|
124
124
|
} else {
|
|
125
125
|
console.warn(
|
|
126
|
-
`pixi-effects: ${
|
|
126
|
+
`pixi-effects: ${chunkLBJRXU7N_cjs.describeLayer(this.seq.spec)} is at or behind the camera plane (z = ${(target.z ?? 0).toFixed(0)}) and is hidden. If that is unintended, lower its z or move the camera back (the default camera sits at z = (height/2)/tan(fov/2), about 989 for 720p at fov 40).`
|
|
127
127
|
);
|
|
128
128
|
}
|
|
129
129
|
}
|
|
@@ -211,8 +211,8 @@ var CAMERA_PROPS = ["x", "y", "z", "fov", "lookAtX", "lookAtY", "lookAtZ", "offs
|
|
|
211
211
|
var NEEDS_THREE_D = ["z", "rotationX", "rotationY"];
|
|
212
212
|
var SKEW_KEYS = ["skew", "skewX", "skewY"];
|
|
213
213
|
function lintSequence(spec, warn = defaultWarn) {
|
|
214
|
-
const who =
|
|
215
|
-
const keys =
|
|
214
|
+
const who = chunkLBJRXU7N_cjs.describeLayer(spec);
|
|
215
|
+
const keys = chunkLBJRXU7N_cjs.collectPropKeys(spec);
|
|
216
216
|
const explained = /* @__PURE__ */ new Set();
|
|
217
217
|
for (const k of keys) {
|
|
218
218
|
const hint = LAYER_ALIASES[k] ?? (spec.type === "camera" ? CAMERA_ALIASES[k] : void 0);
|
|
@@ -258,7 +258,7 @@ function applyBlendMode(spec, display) {
|
|
|
258
258
|
const mode = spec.blendMode;
|
|
259
259
|
if (mode === void 0) return;
|
|
260
260
|
if (!MODES.includes(mode)) {
|
|
261
|
-
console.warn(`pixi-effects: ${
|
|
261
|
+
console.warn(`pixi-effects: ${chunkLBJRXU7N_cjs.describeLayer(spec)}: blendMode "${mode}" is not supported (use ${MODES.join(", ")})`);
|
|
262
262
|
return;
|
|
263
263
|
}
|
|
264
264
|
const filters = display.filters;
|
|
@@ -270,7 +270,7 @@ function applyBlendMode(spec, display) {
|
|
|
270
270
|
}
|
|
271
271
|
|
|
272
272
|
// src/sequences/Composition.ts
|
|
273
|
-
var CompositionSequence = class extends
|
|
273
|
+
var CompositionSequence = class extends chunkLBJRXU7N_cjs.Sequence {
|
|
274
274
|
_innerContainer = null;
|
|
275
275
|
_compositionShape = null;
|
|
276
276
|
_children = [];
|
|
@@ -304,18 +304,18 @@ var CompositionSequence = class extends chunkFJA5VUOF_cjs.Sequence {
|
|
|
304
304
|
const lateKeyframes = [];
|
|
305
305
|
for (const s of this.spec.sequences ?? []) {
|
|
306
306
|
lintSequence(s);
|
|
307
|
-
|
|
308
|
-
|
|
307
|
+
chunkLBJRXU7N_cjs.lintTiming(s, this.duration, (message, kind) => kind === "late-keyframe" ? lateKeyframes.push(message) : console.warn(message));
|
|
308
|
+
chunkLBJRXU7N_cjs.lintText(s);
|
|
309
309
|
}
|
|
310
|
-
for (const message of
|
|
311
|
-
this._children = await
|
|
310
|
+
for (const message of chunkLBJRXU7N_cjs.summarizeWarnings(lateKeyframes)) console.warn(message);
|
|
311
|
+
this._children = await chunkLBJRXU7N_cjs.buildSequenceTree(
|
|
312
312
|
this.spec.sequences ?? [],
|
|
313
313
|
this._compositionShape,
|
|
314
314
|
this.root
|
|
315
315
|
);
|
|
316
316
|
this._resolveParents();
|
|
317
317
|
for (const child of this._children) {
|
|
318
|
-
if (child instanceof
|
|
318
|
+
if (child instanceof chunkLBJRXU7N_cjs.CameraSequence) {
|
|
319
319
|
this._cameras.push(child);
|
|
320
320
|
continue;
|
|
321
321
|
}
|
|
@@ -330,7 +330,7 @@ var CompositionSequence = class extends chunkFJA5VUOF_cjs.Sequence {
|
|
|
330
330
|
this._visual.push({ seq: child, layer });
|
|
331
331
|
applyBlendMode(child.spec, layer.display);
|
|
332
332
|
if (maskSpec) {
|
|
333
|
-
console.warn(`pixi-effects: ${
|
|
333
|
+
console.warn(`pixi-effects: ${chunkLBJRXU7N_cjs.describeLayer(child.spec)}: mask is not supported on threeD layers yet; ignored`);
|
|
334
334
|
}
|
|
335
335
|
continue;
|
|
336
336
|
}
|
|
@@ -340,13 +340,18 @@ var CompositionSequence = class extends chunkFJA5VUOF_cjs.Sequence {
|
|
|
340
340
|
applyBlendMode(child.spec, child.target);
|
|
341
341
|
if (maskSpec) {
|
|
342
342
|
const lifetime = maskSpec.at === void 0 ? { at: child.at, duration: maskSpec.duration ?? child.duration } : {};
|
|
343
|
-
const built = await
|
|
343
|
+
const built = await chunkLBJRXU7N_cjs.buildSequenceTree([{ ...maskSpec, ...lifetime }], this._compositionShape, this.root);
|
|
344
344
|
const maskSeq = built[0];
|
|
345
345
|
if (maskSeq?.target) {
|
|
346
346
|
into.addChild(maskSeq.target);
|
|
347
347
|
const inverse = child.spec.maskInverted ?? false;
|
|
348
348
|
const t = child.target;
|
|
349
|
-
if (
|
|
349
|
+
if (inverse || maskSpec.type === "text") {
|
|
350
|
+
const effect = new pixi_js.AlphaMask({ mask: maskSeq.target });
|
|
351
|
+
effect.inverse = inverse;
|
|
352
|
+
t.setMask?.({ mask: void 0, inverse });
|
|
353
|
+
t.addEffect(effect);
|
|
354
|
+
} else if (typeof t.setMask === "function") {
|
|
350
355
|
t.setMask({ mask: maskSeq.target, inverse });
|
|
351
356
|
} else {
|
|
352
357
|
t.mask = maskSeq.target;
|
|
@@ -390,7 +395,7 @@ var CompositionSequence = class extends chunkFJA5VUOF_cjs.Sequence {
|
|
|
390
395
|
for (let p = this._parentOf.get(seq); p; p = this._parentOf.get(p)) out.push(p);
|
|
391
396
|
return out;
|
|
392
397
|
};
|
|
393
|
-
return [...this._visual.map((v) => v.seq), ...this._nested].filter((seq) => !(seq instanceof
|
|
398
|
+
return [...this._visual.map((v) => v.seq), ...this._nested].filter((seq) => !(seq instanceof chunkLBJRXU7N_cjs.NullSequence)).map((seq) => {
|
|
394
399
|
const layer = this._visual.find((v) => v.seq === seq)?.layer ?? null;
|
|
395
400
|
return {
|
|
396
401
|
seq,
|
|
@@ -402,14 +407,14 @@ var CompositionSequence = class extends chunkFJA5VUOF_cjs.Sequence {
|
|
|
402
407
|
}
|
|
403
408
|
/** Match every `parent: 'name'` to a null layer; anything that cannot work is said out loud and drawn unparented. */
|
|
404
409
|
_resolveParents() {
|
|
405
|
-
const nulls = this._children.filter((c) => c instanceof
|
|
410
|
+
const nulls = this._children.filter((c) => c instanceof chunkLBJRXU7N_cjs.NullSequence);
|
|
406
411
|
const nullNames = nulls.map((n) => n.spec.name).filter((n) => !!n);
|
|
407
412
|
const warned = /* @__PURE__ */ new Set();
|
|
408
413
|
for (const child of this._children) {
|
|
409
414
|
const want = child.spec.parent;
|
|
410
415
|
if (want === void 0) continue;
|
|
411
|
-
const who =
|
|
412
|
-
if (child instanceof
|
|
416
|
+
const who = chunkLBJRXU7N_cjs.describeLayer(child.spec);
|
|
417
|
+
if (child instanceof chunkLBJRXU7N_cjs.CameraSequence) {
|
|
413
418
|
console.warn(`pixi-effects: ${who}: a camera cannot have a parent; ignored`);
|
|
414
419
|
continue;
|
|
415
420
|
}
|
|
@@ -423,7 +428,7 @@ var CompositionSequence = class extends chunkFJA5VUOF_cjs.Sequence {
|
|
|
423
428
|
console.warn(`pixi-effects: ${who}: parent "${want}": no layer with that name${hint ? `; did you mean "${hint}"?` : ""} (the layer is drawn without a parent)`);
|
|
424
429
|
continue;
|
|
425
430
|
}
|
|
426
|
-
const holder = same.find((c) => c instanceof
|
|
431
|
+
const holder = same.find((c) => c instanceof chunkLBJRXU7N_cjs.NullSequence);
|
|
427
432
|
if (!holder) {
|
|
428
433
|
console.warn(`pixi-effects: ${who}: parent "${want}" is a ${same[0].spec.type} layer; only { type: 'null' } layers can be parents (the layer is drawn without a parent)`);
|
|
429
434
|
continue;
|
|
@@ -466,7 +471,7 @@ var CompositionSequence = class extends chunkFJA5VUOF_cjs.Sequence {
|
|
|
466
471
|
t,
|
|
467
472
|
compEnd
|
|
468
473
|
);
|
|
469
|
-
const basis =
|
|
474
|
+
const basis = chunkLBJRXU7N_cjs.cameraBasis(active ? active.cam.state() : chunkLBJRXU7N_cjs.homeCamera(width, height), width, height);
|
|
470
475
|
for (const layer of this._layers3d) layer.update(host, basis);
|
|
471
476
|
const order = assignDepthOrder(
|
|
472
477
|
this._visual.map((v, i) => ({ stackIndex: i, threeD: v.layer !== null, depth: v.layer?.depth ?? 0 }))
|
|
@@ -495,5 +500,5 @@ function frameOf(child) {
|
|
|
495
500
|
}
|
|
496
501
|
|
|
497
502
|
exports.CompositionSequence = CompositionSequence;
|
|
498
|
-
//# sourceMappingURL=chunk-
|
|
499
|
-
//# sourceMappingURL=chunk-
|
|
503
|
+
//# sourceMappingURL=chunk-ETAIVOHP.cjs.map
|
|
504
|
+
//# sourceMappingURL=chunk-ETAIVOHP.cjs.map
|