pixi-effects 0.7.0 → 0.8.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 +14 -0
- package/README.md +7 -6
- package/ai/reference/cheatsheet.md +5 -1
- package/ai/template.html +3 -3
- package/dist/Composition-CM62XJKT.cjs +14 -0
- package/dist/{Composition-3KHQPRBA.cjs.map → Composition-CM62XJKT.cjs.map} +1 -1
- package/dist/Composition-RJDSQYTV.js +5 -0
- package/dist/{Composition-DT657FO5.js.map → Composition-RJDSQYTV.js.map} +1 -1
- package/dist/Controller.cjs +55 -20
- package/dist/Controller.cjs.map +1 -1
- package/dist/Controller.d.cts +31 -2
- package/dist/Controller.d.ts +31 -2
- package/dist/Controller.js +55 -21
- package/dist/Controller.js.map +1 -1
- package/dist/{Movie-BK9daiTt.d.cts → Movie-CQOhUmko.d.cts} +24 -1
- package/dist/{Movie-B0OUKlZI.d.ts → Movie-HF3pSGjQ.d.ts} +24 -1
- package/dist/{chunk-6I55T4OH.cjs → chunk-AGMNSJJH.cjs} +13 -4
- package/dist/chunk-AGMNSJJH.cjs.map +1 -0
- package/dist/{chunk-4O2NYDJG.js → chunk-FNKSXBKB.js} +13 -4
- package/dist/chunk-FNKSXBKB.js.map +1 -0
- package/dist/{chunk-36Z4RIRK.js → chunk-O4AJYUPK.js} +3 -3
- package/dist/{chunk-36Z4RIRK.js.map → chunk-O4AJYUPK.js.map} +1 -1
- package/dist/{chunk-4VSIJLU3.cjs → chunk-PDBDDGG5.cjs} +21 -21
- package/dist/{chunk-4VSIJLU3.cjs.map → chunk-PDBDDGG5.cjs.map} +1 -1
- package/dist/index.cjs +82 -23
- 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 +71 -12
- package/dist/index.js.map +1 -1
- package/dist/three.cjs +5 -5
- package/dist/three.js +1 -1
- package/docs/api.md +51 -0
- package/docs/dsl.md +14 -2
- package/llms-full.txt +70 -3
- package/package.json +1 -1
- package/dist/Composition-3KHQPRBA.cjs +0 -14
- package/dist/Composition-DT657FO5.js +0 -5
- package/dist/chunk-4O2NYDJG.js.map +0 -1
- package/dist/chunk-6I55T4OH.cjs.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as AudioInspectOptions, a as AudioReport, b as AudioSourceReport, C as ContactSheetOptions, F as FrameEvent, I as InspectOptions, c as InspectReport, L as LayerInfo, M as Movie, d as MovieOptions, P as ProgressEvent, R as RenderOptions, S as SnapshotOptions,
|
|
1
|
+
export { A as AudioInspectOptions, a as AudioReport, b as AudioSourceReport, C as ContactSheetOptions, F as FrameEvent, I as InspectOptions, c as InspectReport, L as LayerInfo, M as Movie, d as MovieErrorEvent, e as MovieOptions, P as ProgressEvent, R as RenderOptions, S as SnapshotOptions, f as SoundMeasure, T as TimelineData, g as TimelineHtmlOptions, h as TimelineRow, i as TimelineSvgOptions, j as TimelineTransition, V as VolumeEvent } from './Movie-CQOhUmko.cjs';
|
|
2
2
|
import { S as Sequence } from './Base-CSIulyIq.cjs';
|
|
3
3
|
import { S as SequenceSpec, C as CompositionShape, I as ImageSequenceSpec, a as CameraSequenceSpec, b as SequenceCommon } from './types-oJijbn-K.cjs';
|
|
4
4
|
export { A as ArcShapeSpec, c as AssetSpec, d as AudioAssetSpec, e as AudioSequenceSpec, f as AudioSfxSpec, g as ChromaKeyFilterSpec, h as CircleShapeSpec, i as CompositionSequenceSpec, j as CompositionSpec, k as CrossfadeTransition, l as CustomFilterSpec, D as DipTransition, m as DissolveTransition, E as EllipseShapeSpec, n as Expr, F as FilterSpec, G as GradientSpec, o as IrisTransition, K as Keyframe, L as LineShapeSpec, P as PathShapeSpec, p as PolygonShapeSpec, q as PropValue, r as Props, R as RectShapeSpec, s as SfxKnobs, t as SfxOptions, u as SfxPreset, v as SfxVoice, w as ShapeSequenceSpec, x as SlideTransition, T as TextSequenceSpec, y as TransitionCommon, z as TransitionSpec, V as VideoSequenceSpec, W as WipeTransition, Z as ZoomTransition } from './types-oJijbn-K.cjs';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as AudioInspectOptions, a as AudioReport, b as AudioSourceReport, C as ContactSheetOptions, F as FrameEvent, I as InspectOptions, c as InspectReport, L as LayerInfo, M as Movie, d as MovieOptions, P as ProgressEvent, R as RenderOptions, S as SnapshotOptions,
|
|
1
|
+
export { A as AudioInspectOptions, a as AudioReport, b as AudioSourceReport, C as ContactSheetOptions, F as FrameEvent, I as InspectOptions, c as InspectReport, L as LayerInfo, M as Movie, d as MovieErrorEvent, e as MovieOptions, P as ProgressEvent, R as RenderOptions, S as SnapshotOptions, f as SoundMeasure, T as TimelineData, g as TimelineHtmlOptions, h as TimelineRow, i as TimelineSvgOptions, j as TimelineTransition, V as VolumeEvent } from './Movie-HF3pSGjQ.js';
|
|
2
2
|
import { S as Sequence } from './Base-DVgwyIUj.js';
|
|
3
3
|
import { S as SequenceSpec, C as CompositionShape, I as ImageSequenceSpec, a as CameraSequenceSpec, b as SequenceCommon } from './types-oJijbn-K.js';
|
|
4
4
|
export { A as ArcShapeSpec, c as AssetSpec, d as AudioAssetSpec, e as AudioSequenceSpec, f as AudioSfxSpec, g as ChromaKeyFilterSpec, h as CircleShapeSpec, i as CompositionSequenceSpec, j as CompositionSpec, k as CrossfadeTransition, l as CustomFilterSpec, D as DipTransition, m as DissolveTransition, E as EllipseShapeSpec, n as Expr, F as FilterSpec, G as GradientSpec, o as IrisTransition, K as Keyframe, L as LineShapeSpec, P as PathShapeSpec, p as PolygonShapeSpec, q as PropValue, r as Props, R as RectShapeSpec, s as SfxKnobs, t as SfxOptions, u as SfxPreset, v as SfxVoice, w as ShapeSequenceSpec, x as SlideTransition, T as TextSequenceSpec, y as TransitionCommon, z as TransitionSpec, V as VideoSequenceSpec, W as WipeTransition, Z as ZoomTransition } from './types-oJijbn-K.js';
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { CompositionSequence } from './chunk-
|
|
2
|
-
import { ensureFilterLibrary, homeDistance, DEFAULT_FOV, DEG, resolveAt, collectPropKeys } from './chunk-
|
|
3
|
-
export { registerSequenceType } from './chunk-
|
|
1
|
+
import { CompositionSequence } from './chunk-O4AJYUPK.js';
|
|
2
|
+
import { ensureFilterLibrary, homeDistance, DEFAULT_FOV, DEG, resolveAt, collectPropKeys } from './chunk-FNKSXBKB.js';
|
|
3
|
+
export { registerSequenceType } from './chunk-FNKSXBKB.js';
|
|
4
4
|
import { warnUnknownOptions, suggestName } from './chunk-DKGG7WD4.js';
|
|
5
5
|
import * as PIXI from 'pixi.js';
|
|
6
6
|
import { extensions, CullerPlugin, Application, Container, Rectangle, Culler, CanvasTextMetrics, TextStyle, Assets, ExtensionType, Filter, UniformGroup, GpuProgram, GlProgram, defaultFilterVert } from 'pixi.js';
|
|
7
7
|
import { gsap } from 'gsap';
|
|
8
8
|
import { PixiPlugin } from 'gsap/PixiPlugin';
|
|
9
|
-
import { QUALITY_VERY_HIGH, QUALITY_HIGH, QUALITY_MEDIUM, QUALITY_LOW, QUALITY_VERY_LOW, Output, BufferTarget, CanvasSource, AudioBufferSource, Input, ALL_FORMATS, BlobSource, WavOutputFormat, Conversion, VideoSampleSink, MkvOutputFormat, WebMOutputFormat, MovOutputFormat, Mp4OutputFormat } from 'mediabunny';
|
|
9
|
+
import { QUALITY_VERY_HIGH, QUALITY_HIGH, QUALITY_MEDIUM, QUALITY_LOW, QUALITY_VERY_LOW, Output, BufferTarget, CanvasSource, AudioBufferSource, Input, ALL_FORMATS, BlobSource, WavOutputFormat, Conversion, VideoSampleSink, MkvOutputFormat, WebMOutputFormat, MovOutputFormat, Mp4OutputFormat, getFirstEncodableAudioCodec } from 'mediabunny';
|
|
10
10
|
|
|
11
11
|
var registered = false;
|
|
12
12
|
function ensureLoadersRegistered(audioContext) {
|
|
@@ -288,6 +288,28 @@ var AUDIO_CODEC_BY_FORMAT = {
|
|
|
288
288
|
webm: "opus",
|
|
289
289
|
mkv: "opus"
|
|
290
290
|
};
|
|
291
|
+
var AUDIO_PREFERENCE = {
|
|
292
|
+
mp4: ["aac", "opus", "mp3", "flac"],
|
|
293
|
+
mov: ["aac", "mp3"],
|
|
294
|
+
webm: ["opus", "vorbis"],
|
|
295
|
+
mkv: ["opus", "vorbis", "aac", "flac", "mp3"]
|
|
296
|
+
};
|
|
297
|
+
async function chooseAudioCodec(fmt, requested, container, audio, bitrate) {
|
|
298
|
+
const supported = container.getSupportedAudioCodecs?.();
|
|
299
|
+
const preferred = AUDIO_PREFERENCE[fmt].filter((c) => !supported || supported.includes(c));
|
|
300
|
+
const canEncode = (list) => getFirstEncodableAudioCodec(list, { numberOfChannels: audio.numberOfChannels ?? 2, sampleRate: audio.sampleRate, bitrate });
|
|
301
|
+
if (requested) {
|
|
302
|
+
if (await canEncode([requested])) return requested;
|
|
303
|
+
const alt = await canEncode(preferred.filter((c) => c !== requested));
|
|
304
|
+
throw new Error(`pixi-effects: this browser cannot encode "${requested}" audio for ${fmt}` + (alt ? `; "${alt}" would work: render({ format: '${fmt}', audio: { codec: '${alt}' } }), or leave audio.codec out and the best one is chosen.` : "; it has no audio encoder for this format at all (try another browser, or a movie without sound)."));
|
|
305
|
+
}
|
|
306
|
+
const chosen = await canEncode(preferred);
|
|
307
|
+
if (!chosen) throw new Error(`pixi-effects: this browser has no audio encoder for ${fmt} (it tried ${preferred.join(", ")}): try another browser, or render a movie without sound.`);
|
|
308
|
+
if (chosen !== preferred[0]) {
|
|
309
|
+
console.warn(`pixi-effects: this browser cannot encode ${preferred[0].toUpperCase()} audio (Chrome on Linux has no AAC encoder), so the ${fmt} carries ${chosen} instead; current players play it. A webm export needs no AAC.`);
|
|
310
|
+
}
|
|
311
|
+
return chosen;
|
|
312
|
+
}
|
|
291
313
|
var qualityMap = {
|
|
292
314
|
"very-low": QUALITY_VERY_LOW,
|
|
293
315
|
"low": QUALITY_LOW,
|
|
@@ -320,8 +342,10 @@ async function exportFrames(movie, options = {}) {
|
|
|
320
342
|
bitrate: qualityMap[options.audio?.bitrate ?? "high"] ?? QUALITY_HIGH
|
|
321
343
|
}
|
|
322
344
|
};
|
|
345
|
+
const container = makeOutputFormat(opts.format);
|
|
346
|
+
if (movie.audioBuffer) opts.audio.codec = await chooseAudioCodec(fmt, options.audio?.codec, container, movie.audioBuffer, opts.audio.bitrate);
|
|
323
347
|
const output = new Output({
|
|
324
|
-
format:
|
|
348
|
+
format: container,
|
|
325
349
|
target: new BufferTarget()
|
|
326
350
|
});
|
|
327
351
|
const canvasSource = new CanvasSource(movie.app.canvas, {
|
|
@@ -1418,6 +1442,8 @@ var Movie = class {
|
|
|
1418
1442
|
audioSource = null;
|
|
1419
1443
|
gainNode = null;
|
|
1420
1444
|
_cancelAudioStart = null;
|
|
1445
|
+
/** > 0 while the movie moves its own playhead (playback, render, snapshot, contactSheet): those are not the seeks `seeking` / `seeked` report. */
|
|
1446
|
+
_quiet = 0;
|
|
1421
1447
|
_volume = 1;
|
|
1422
1448
|
_muted = false;
|
|
1423
1449
|
isPlaying = false;
|
|
@@ -1545,12 +1571,32 @@ var Movie = class {
|
|
|
1545
1571
|
} catch (cleanupErr) {
|
|
1546
1572
|
console.warn("pixi-effects: cleanup after init failure also threw:", cleanupErr);
|
|
1547
1573
|
}
|
|
1574
|
+
this._emitError("init", err);
|
|
1548
1575
|
throw err;
|
|
1549
1576
|
}
|
|
1550
1577
|
}
|
|
1551
1578
|
async gotoFrame(frame, force = false) {
|
|
1552
1579
|
if (this._initState !== "ready") return;
|
|
1553
1580
|
if (!force && this.currentFrame === frame) return;
|
|
1581
|
+
if (this._quiet > 0) return this._goto(frame);
|
|
1582
|
+
const target = Math.max(0, Math.min(frame, this.totalFrames));
|
|
1583
|
+
this.emit("seeking", { frame: target, totalFrames: this.totalFrames });
|
|
1584
|
+
await this._goto(frame);
|
|
1585
|
+
this.emit("seeked", { frame: this.currentFrame, totalFrames: this.totalFrames });
|
|
1586
|
+
}
|
|
1587
|
+
/** Run `fn` with `seeking` / `seeked` silenced: the movie is moving its own playhead, not answering a seek. */
|
|
1588
|
+
async _quietly(fn) {
|
|
1589
|
+
this._quiet++;
|
|
1590
|
+
try {
|
|
1591
|
+
return await fn();
|
|
1592
|
+
} finally {
|
|
1593
|
+
this._quiet--;
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
_emitError(where, error) {
|
|
1597
|
+
this.emit("error", { where, message: error instanceof Error ? error.message : String(error), error });
|
|
1598
|
+
}
|
|
1599
|
+
async _goto(frame) {
|
|
1554
1600
|
this.currentFrame = Math.max(0, Math.min(frame, this.totalFrames));
|
|
1555
1601
|
this.timeline.time(this.currentFrame / this.frameRate);
|
|
1556
1602
|
await this._awaitVideoFrames();
|
|
@@ -1572,7 +1618,7 @@ var Movie = class {
|
|
|
1572
1618
|
async snapshot(frame = this.currentFrame, opts = {}) {
|
|
1573
1619
|
warnUnknownOptions("movie.snapshot()", opts, ["scale", "type", "as"]);
|
|
1574
1620
|
this._requireReady("snapshot");
|
|
1575
|
-
await this.gotoFrame(clampFrame(frame, this.totalFrames), true);
|
|
1621
|
+
await this._quietly(() => this.gotoFrame(clampFrame(frame, this.totalFrames), true));
|
|
1576
1622
|
const scale = opts.scale ?? 1;
|
|
1577
1623
|
const out = document.createElement("canvas");
|
|
1578
1624
|
out.width = Math.max(1, Math.round(this.width * scale));
|
|
@@ -1596,7 +1642,7 @@ var Movie = class {
|
|
|
1596
1642
|
try {
|
|
1597
1643
|
for (let i = 0; i < frames.length; i++) {
|
|
1598
1644
|
const f = frames[i];
|
|
1599
|
-
await this.gotoFrame(f, true);
|
|
1645
|
+
await this._quietly(() => this.gotoFrame(f, true));
|
|
1600
1646
|
const { x, y } = L.positions[i];
|
|
1601
1647
|
g.drawImage(this.app.canvas, x, y + LABEL_H, L.cellW, L.cellH);
|
|
1602
1648
|
g.fillStyle = "#e8ecf8";
|
|
@@ -1605,7 +1651,7 @@ var Movie = class {
|
|
|
1605
1651
|
g.fillText(`frame ${f} \xB7 ${(f / this.frameRate).toFixed(2)}s`, x + 8, y + LABEL_H / 2);
|
|
1606
1652
|
}
|
|
1607
1653
|
} finally {
|
|
1608
|
-
await this.gotoFrame(back, true);
|
|
1654
|
+
await this._quietly(() => this.gotoFrame(back, true));
|
|
1609
1655
|
}
|
|
1610
1656
|
return encodeCanvas(sheet, "image/png", opts.as ?? "blob");
|
|
1611
1657
|
}
|
|
@@ -1688,17 +1734,20 @@ var Movie = class {
|
|
|
1688
1734
|
const frame = Math.floor(elapsed * this.frameRate);
|
|
1689
1735
|
if (frame <= this.totalFrames) {
|
|
1690
1736
|
inFlight = true;
|
|
1691
|
-
this.
|
|
1737
|
+
this._goto(frame).catch((err) => {
|
|
1692
1738
|
console.warn("pixi-effects: gotoFrame failed during playback:", err);
|
|
1739
|
+
this._emitError("playback", err);
|
|
1693
1740
|
}).finally(() => {
|
|
1694
1741
|
inFlight = false;
|
|
1695
1742
|
});
|
|
1696
1743
|
this._raf = requestAnimationFrame(tick);
|
|
1697
1744
|
} else {
|
|
1698
1745
|
this.pause();
|
|
1699
|
-
this.
|
|
1746
|
+
this._goto(this.totalFrames).catch((err) => {
|
|
1700
1747
|
console.warn("pixi-effects: final gotoFrame failed:", err);
|
|
1748
|
+
this._emitError("playback", err);
|
|
1701
1749
|
});
|
|
1750
|
+
this.emit("ended");
|
|
1702
1751
|
}
|
|
1703
1752
|
};
|
|
1704
1753
|
this._raf = requestAnimationFrame(tick);
|
|
@@ -1742,15 +1791,20 @@ var Movie = class {
|
|
|
1742
1791
|
if (wasPlaying) this.emit("pause");
|
|
1743
1792
|
}
|
|
1744
1793
|
set volume(v) {
|
|
1745
|
-
|
|
1794
|
+
const next = Math.max(0, Math.min(1, v));
|
|
1795
|
+
const changed = next !== this._volume;
|
|
1796
|
+
this._volume = next;
|
|
1746
1797
|
if (this.gainNode) this.gainNode.gain.value = this._muted ? 0 : this._volume;
|
|
1798
|
+
if (changed) this.emit("volumechange", { volume: this._volume, muted: this._muted });
|
|
1747
1799
|
}
|
|
1748
1800
|
get volume() {
|
|
1749
1801
|
return this._volume;
|
|
1750
1802
|
}
|
|
1751
1803
|
set muted(v) {
|
|
1804
|
+
const changed = !!v !== this._muted;
|
|
1752
1805
|
this._muted = !!v;
|
|
1753
1806
|
if (this.gainNode) this.gainNode.gain.value = this._muted ? 0 : this._volume;
|
|
1807
|
+
if (changed) this.emit("volumechange", { volume: this._volume, muted: this._muted });
|
|
1754
1808
|
}
|
|
1755
1809
|
get muted() {
|
|
1756
1810
|
return this._muted;
|
|
@@ -1761,7 +1815,12 @@ var Movie = class {
|
|
|
1761
1815
|
}
|
|
1762
1816
|
async render(options) {
|
|
1763
1817
|
warnUnknownOptions("movie.render()", options, ["format", "video", "audio"]);
|
|
1764
|
-
|
|
1818
|
+
try {
|
|
1819
|
+
return await this._quietly(() => exportFrames(this, options));
|
|
1820
|
+
} catch (err) {
|
|
1821
|
+
this._emitError("render", err);
|
|
1822
|
+
throw err;
|
|
1823
|
+
}
|
|
1765
1824
|
}
|
|
1766
1825
|
async destroy() {
|
|
1767
1826
|
safeRun(() => this.pause());
|