framewebworker 0.1.0 → 0.1.2
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 +8 -8
- package/dist/index.cjs +286 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -3
- package/dist/index.d.ts +16 -3
- package/dist/index.js +285 -6
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +1 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +16 -3
- package/dist/react/index.d.ts +16 -3
- package/dist/react/index.js +1 -1
- package/dist/react/index.js.map +1 -1
- package/dist/render-worker.js +177 -0
- package/dist/render-worker.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Browser-native video rendering and clip export.** Trim, caption, and export MP4 Blobs entirely in the browser — no server, no upload, no backend.
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/framewebworker)
|
|
6
6
|
[](https://github.com/nareshipme/frameworker/actions)
|
|
7
7
|
[](LICENSE)
|
|
8
8
|
|
|
@@ -12,14 +12,14 @@
|
|
|
12
12
|
- **Overlay captions** with built-in style presets (`hormozi`, `modern`, `minimal`, `bold`)
|
|
13
13
|
- **Stitch** multiple clips into one
|
|
14
14
|
- **Pluggable renderer backend** (default: ffmpeg.wasm)
|
|
15
|
-
- **Framework-agnostic core** + React hooks (`
|
|
15
|
+
- **Framework-agnostic core** + React hooks (`framewebworker/react`)
|
|
16
16
|
- **TypeScript-first** with full type exports
|
|
17
17
|
- Respects `AbortSignal` for cancellation, reports progress 0–1
|
|
18
18
|
|
|
19
19
|
## Install
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
-
npm install
|
|
22
|
+
npm install framewebworker @ffmpeg/ffmpeg @ffmpeg/util
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
> `@ffmpeg/ffmpeg` and `@ffmpeg/util` are optional peer dependencies required only by the default ffmpeg.wasm backend. If you supply your own backend you don't need them.
|
|
@@ -27,7 +27,7 @@ npm install frameworker @ffmpeg/ffmpeg @ffmpeg/util
|
|
|
27
27
|
## Quick Start
|
|
28
28
|
|
|
29
29
|
```ts
|
|
30
|
-
import { createFrameWorker } from '
|
|
30
|
+
import { createFrameWorker } from 'framewebworker';
|
|
31
31
|
|
|
32
32
|
const fw = createFrameWorker();
|
|
33
33
|
|
|
@@ -55,8 +55,8 @@ const url = URL.createObjectURL(blob);
|
|
|
55
55
|
## React Example
|
|
56
56
|
|
|
57
57
|
```tsx
|
|
58
|
-
import { createFrameWorker } from '
|
|
59
|
-
import { useRender } from '
|
|
58
|
+
import { createFrameWorker } from 'framewebworker';
|
|
59
|
+
import { useRender } from 'framewebworker/react';
|
|
60
60
|
|
|
61
61
|
const fw = createFrameWorker();
|
|
62
62
|
|
|
@@ -124,7 +124,7 @@ captions: {
|
|
|
124
124
|
Implement the `RendererBackend` interface to use any encoder:
|
|
125
125
|
|
|
126
126
|
```ts
|
|
127
|
-
import type { RendererBackend, FrameData, EncodeOptions } from '
|
|
127
|
+
import type { RendererBackend, FrameData, EncodeOptions } from 'framewebworker';
|
|
128
128
|
|
|
129
129
|
const myBackend: RendererBackend = {
|
|
130
130
|
name: 'my-encoder',
|
|
@@ -187,7 +187,7 @@ Returns a `FrameWorker` object:
|
|
|
187
187
|
| `onProgress` | `(p: number) => void` | Progress callback 0–1 |
|
|
188
188
|
| `signal` | `AbortSignal` | Cancellation signal |
|
|
189
189
|
|
|
190
|
-
### React Hooks (`
|
|
190
|
+
### React Hooks (`framewebworker/react`)
|
|
191
191
|
|
|
192
192
|
#### `useRender(frameWorker)`
|
|
193
193
|
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
3
4
|
var __defProp = Object.defineProperty;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
6
|
var __esm = (fn, res) => function __init() {
|
|
@@ -470,24 +471,301 @@ function seekVideo(video, time) {
|
|
|
470
471
|
});
|
|
471
472
|
}
|
|
472
473
|
|
|
474
|
+
// src/worker/pool.ts
|
|
475
|
+
var WorkerPool = class {
|
|
476
|
+
constructor(maxConcurrency) {
|
|
477
|
+
this.workers = [];
|
|
478
|
+
this.idleWorkers = [];
|
|
479
|
+
this.queue = [];
|
|
480
|
+
const count = Math.min(maxConcurrency, navigator.hardwareConcurrency || 2, 4);
|
|
481
|
+
for (let i = 0; i < count; i++) {
|
|
482
|
+
const worker = new Worker(
|
|
483
|
+
new URL("./render-worker.js", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))),
|
|
484
|
+
{ type: "module" }
|
|
485
|
+
);
|
|
486
|
+
this.workers.push(worker);
|
|
487
|
+
this.idleWorkers.push(worker);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
dispatch(jobId, clip, options, onProgress) {
|
|
491
|
+
return new Promise((resolve, reject) => {
|
|
492
|
+
this.queue.push({ jobId, clip, options, onProgress, resolve, reject });
|
|
493
|
+
this.processQueue();
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
processQueue() {
|
|
497
|
+
while (this.queue.length > 0 && this.idleWorkers.length > 0) {
|
|
498
|
+
const worker = this.idleWorkers.pop();
|
|
499
|
+
const job = this.queue.shift();
|
|
500
|
+
this.runJob(worker, job);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
async runJob(worker, job) {
|
|
504
|
+
try {
|
|
505
|
+
const frames = await this.executeJob(worker, job);
|
|
506
|
+
job.resolve(frames);
|
|
507
|
+
} catch (err) {
|
|
508
|
+
job.reject(err instanceof Error ? err : new Error(String(err)));
|
|
509
|
+
} finally {
|
|
510
|
+
this.idleWorkers.push(worker);
|
|
511
|
+
this.processQueue();
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
async executeJob(worker, job) {
|
|
515
|
+
const { jobId, clip, options, onProgress } = job;
|
|
516
|
+
const { fps, width: outW, height: outH, signal } = options;
|
|
517
|
+
let srcUrl;
|
|
518
|
+
let needsRevoke = false;
|
|
519
|
+
if (typeof clip.source === "string") {
|
|
520
|
+
srcUrl = clip.source;
|
|
521
|
+
} else if (clip.source instanceof HTMLVideoElement) {
|
|
522
|
+
srcUrl = clip.source.src;
|
|
523
|
+
} else {
|
|
524
|
+
srcUrl = URL.createObjectURL(clip.source);
|
|
525
|
+
needsRevoke = true;
|
|
526
|
+
}
|
|
527
|
+
const video = document.createElement("video");
|
|
528
|
+
video.muted = true;
|
|
529
|
+
video.crossOrigin = "anonymous";
|
|
530
|
+
video.preload = "auto";
|
|
531
|
+
try {
|
|
532
|
+
await new Promise((res, rej) => {
|
|
533
|
+
video.onloadedmetadata = () => res();
|
|
534
|
+
video.onerror = () => rej(new Error(`Failed to load video: ${srcUrl}`));
|
|
535
|
+
video.src = srcUrl;
|
|
536
|
+
});
|
|
537
|
+
} catch (err) {
|
|
538
|
+
if (needsRevoke) URL.revokeObjectURL(srcUrl);
|
|
539
|
+
throw err;
|
|
540
|
+
}
|
|
541
|
+
const duration = video.duration;
|
|
542
|
+
const startSec = clip.startTime ?? 0;
|
|
543
|
+
const endSec = clip.endTime ?? duration;
|
|
544
|
+
const clipDuration = endSec - startSec;
|
|
545
|
+
const totalFrames = Math.ceil(clipDuration * fps);
|
|
546
|
+
const captionSegments = clip.captions?.segments ?? [];
|
|
547
|
+
const baseStylePreset = clip.captions?.style?.preset ?? "modern";
|
|
548
|
+
const captionStyle = mergeStyle(STYLE_PRESETS[baseStylePreset], clip.captions?.style);
|
|
549
|
+
const offscreen = new OffscreenCanvas(outW, outH);
|
|
550
|
+
const initMsg = {
|
|
551
|
+
type: "init",
|
|
552
|
+
jobId,
|
|
553
|
+
canvas: offscreen,
|
|
554
|
+
meta: { captions: captionSegments, captionStyle, width: outW, height: outH, totalFrames }
|
|
555
|
+
};
|
|
556
|
+
worker.postMessage(initMsg, [offscreen]);
|
|
557
|
+
const resultFrames = [];
|
|
558
|
+
await new Promise((res, rej) => {
|
|
559
|
+
let aborted = false;
|
|
560
|
+
const onMessage = (event) => {
|
|
561
|
+
const msg = event.data;
|
|
562
|
+
if (msg.jobId !== jobId) return;
|
|
563
|
+
if (msg.type === "progress") {
|
|
564
|
+
onProgress(msg.currentFrame, msg.totalFrames);
|
|
565
|
+
} else if (msg.type === "done") {
|
|
566
|
+
worker.removeEventListener("message", onMessage);
|
|
567
|
+
for (const tf of msg.frames) {
|
|
568
|
+
resultFrames.push({
|
|
569
|
+
imageData: new ImageData(new Uint8ClampedArray(tf.buffer), tf.width, tf.height),
|
|
570
|
+
timestamp: tf.timestamp,
|
|
571
|
+
width: tf.width,
|
|
572
|
+
height: tf.height
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
res();
|
|
576
|
+
} else if (msg.type === "error") {
|
|
577
|
+
worker.removeEventListener("message", onMessage);
|
|
578
|
+
rej(new Error(msg.message));
|
|
579
|
+
}
|
|
580
|
+
};
|
|
581
|
+
worker.addEventListener("message", onMessage);
|
|
582
|
+
if (signal) {
|
|
583
|
+
signal.addEventListener("abort", () => {
|
|
584
|
+
if (aborted) return;
|
|
585
|
+
aborted = true;
|
|
586
|
+
const abortMsg = { type: "abort", jobId };
|
|
587
|
+
worker.postMessage(abortMsg);
|
|
588
|
+
worker.removeEventListener("message", onMessage);
|
|
589
|
+
rej(new DOMException("Render cancelled", "AbortError"));
|
|
590
|
+
}, { once: true });
|
|
591
|
+
}
|
|
592
|
+
(async () => {
|
|
593
|
+
try {
|
|
594
|
+
for (let i = 0; i < totalFrames; i++) {
|
|
595
|
+
if (signal?.aborted || aborted) break;
|
|
596
|
+
const t = startSec + i / fps;
|
|
597
|
+
await seekVideo2(video, t);
|
|
598
|
+
const vw = video.videoWidth;
|
|
599
|
+
const vh = video.videoHeight;
|
|
600
|
+
let sx = 0, sy = 0, sw = vw, sh = vh;
|
|
601
|
+
if (clip.crop) {
|
|
602
|
+
sx = clip.crop.x * vw;
|
|
603
|
+
sy = clip.crop.y * vw;
|
|
604
|
+
sw = clip.crop.width * vw;
|
|
605
|
+
sh = clip.crop.height * vh;
|
|
606
|
+
} else {
|
|
607
|
+
const videoAR = vw / vh;
|
|
608
|
+
const outAR = outW / outH;
|
|
609
|
+
if (videoAR > outAR) {
|
|
610
|
+
sw = vh * outAR;
|
|
611
|
+
sx = (vw - sw) / 2;
|
|
612
|
+
} else if (videoAR < outAR) {
|
|
613
|
+
sh = vw / outAR;
|
|
614
|
+
sy = (vh - sh) / 2;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
const bitmap = await createImageBitmap(video, sx, sy, sw, sh, {
|
|
618
|
+
resizeWidth: outW,
|
|
619
|
+
resizeHeight: outH
|
|
620
|
+
});
|
|
621
|
+
const frameMsg = {
|
|
622
|
+
type: "frame",
|
|
623
|
+
jobId,
|
|
624
|
+
frameIndex: i,
|
|
625
|
+
bitmap,
|
|
626
|
+
timestamp: t - startSec
|
|
627
|
+
};
|
|
628
|
+
worker.postMessage(frameMsg, [bitmap]);
|
|
629
|
+
}
|
|
630
|
+
if (!signal?.aborted && !aborted) {
|
|
631
|
+
const endMsg = { type: "end", jobId };
|
|
632
|
+
worker.postMessage(endMsg);
|
|
633
|
+
}
|
|
634
|
+
} catch (err) {
|
|
635
|
+
if (!aborted) rej(err instanceof Error ? err : new Error(String(err)));
|
|
636
|
+
}
|
|
637
|
+
})();
|
|
638
|
+
});
|
|
639
|
+
if (needsRevoke) URL.revokeObjectURL(srcUrl);
|
|
640
|
+
return resultFrames;
|
|
641
|
+
}
|
|
642
|
+
terminate() {
|
|
643
|
+
for (const worker of this.workers) {
|
|
644
|
+
worker.terminate();
|
|
645
|
+
}
|
|
646
|
+
this.workers = [];
|
|
647
|
+
this.idleWorkers = [];
|
|
648
|
+
}
|
|
649
|
+
};
|
|
650
|
+
function seekVideo2(video, time) {
|
|
651
|
+
return new Promise((resolve) => {
|
|
652
|
+
if (Math.abs(video.currentTime - time) < 1e-3) {
|
|
653
|
+
resolve();
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
const onSeeked = () => {
|
|
657
|
+
video.removeEventListener("seeked", onSeeked);
|
|
658
|
+
resolve();
|
|
659
|
+
};
|
|
660
|
+
video.addEventListener("seeked", onSeeked);
|
|
661
|
+
video.currentTime = time;
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
|
|
473
665
|
// src/stitch.ts
|
|
474
666
|
async function stitchClips(clips, backend, options) {
|
|
475
667
|
const fps = options.fps ?? 30;
|
|
476
668
|
const width = options.width ?? 1280;
|
|
477
669
|
const height = options.height ?? 720;
|
|
670
|
+
const onProgress = options.onProgress;
|
|
671
|
+
if (!("OffscreenCanvas" in globalThis) || !("Worker" in globalThis)) {
|
|
672
|
+
return sequentialStitch(clips, backend, options, fps, width, height);
|
|
673
|
+
}
|
|
674
|
+
const concurrency = Math.min(clips.length, navigator.hardwareConcurrency || 2, 4);
|
|
675
|
+
const pool = new WorkerPool(concurrency);
|
|
676
|
+
const clipStatuses = clips.map(() => "queued");
|
|
677
|
+
const clipProgresses = clips.map(() => 0);
|
|
678
|
+
const emitProgress = () => {
|
|
679
|
+
if (!onProgress) return;
|
|
680
|
+
const overall = clipProgresses.reduce((a, b) => a + b, 0) / clips.length;
|
|
681
|
+
const clipData = clips.map((_, i) => ({
|
|
682
|
+
index: i,
|
|
683
|
+
status: clipStatuses[i],
|
|
684
|
+
progress: clipProgresses[i]
|
|
685
|
+
}));
|
|
686
|
+
onProgress({ overall, clips: clipData });
|
|
687
|
+
};
|
|
688
|
+
try {
|
|
689
|
+
const frameArrays = await Promise.all(
|
|
690
|
+
clips.map(async (clip, i) => {
|
|
691
|
+
clipStatuses[i] = "rendering";
|
|
692
|
+
emitProgress();
|
|
693
|
+
try {
|
|
694
|
+
const frames = await pool.dispatch(
|
|
695
|
+
`clip-${i}`,
|
|
696
|
+
clip,
|
|
697
|
+
{ fps, width, height, signal: options.signal },
|
|
698
|
+
(current, total) => {
|
|
699
|
+
clipProgresses[i] = current / total;
|
|
700
|
+
emitProgress();
|
|
701
|
+
}
|
|
702
|
+
);
|
|
703
|
+
return frames;
|
|
704
|
+
} catch (err) {
|
|
705
|
+
clipStatuses[i] = "failed";
|
|
706
|
+
emitProgress();
|
|
707
|
+
throw err;
|
|
708
|
+
}
|
|
709
|
+
})
|
|
710
|
+
);
|
|
711
|
+
const blobs = [];
|
|
712
|
+
for (let i = 0; i < frameArrays.length; i++) {
|
|
713
|
+
const blob = await backend.encode(frameArrays[i], {
|
|
714
|
+
width,
|
|
715
|
+
height,
|
|
716
|
+
fps,
|
|
717
|
+
mimeType: options.mimeType ?? "video/mp4",
|
|
718
|
+
quality: options.quality ?? 0.92,
|
|
719
|
+
encoderOptions: options.encoderOptions,
|
|
720
|
+
signal: options.signal
|
|
721
|
+
});
|
|
722
|
+
clipStatuses[i] = "done";
|
|
723
|
+
clipProgresses[i] = 1;
|
|
724
|
+
emitProgress();
|
|
725
|
+
blobs.push(blob);
|
|
726
|
+
}
|
|
727
|
+
if (blobs.length === 1) {
|
|
728
|
+
onProgress?.({
|
|
729
|
+
overall: 1,
|
|
730
|
+
clips: clips.map((_, i) => ({ index: i, status: "done", progress: 1 }))
|
|
731
|
+
});
|
|
732
|
+
return blobs[0];
|
|
733
|
+
}
|
|
734
|
+
return backend.concat(blobs, {
|
|
735
|
+
width,
|
|
736
|
+
height,
|
|
737
|
+
fps,
|
|
738
|
+
mimeType: options.mimeType ?? "video/mp4",
|
|
739
|
+
quality: options.quality ?? 0.92,
|
|
740
|
+
signal: options.signal
|
|
741
|
+
});
|
|
742
|
+
} finally {
|
|
743
|
+
pool.terminate();
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
async function sequentialStitch(clips, backend, options, fps, width, height) {
|
|
478
747
|
const onProgress = options.onProgress;
|
|
479
748
|
const blobs = [];
|
|
480
749
|
for (let ci = 0; ci < clips.length; ci++) {
|
|
481
750
|
const clip = clips[ci];
|
|
482
|
-
const
|
|
483
|
-
onProgress
|
|
751
|
+
const emitClipProgress = (p) => {
|
|
752
|
+
if (!onProgress) return;
|
|
753
|
+
const overall = (ci + p) / clips.length;
|
|
754
|
+
onProgress({
|
|
755
|
+
overall,
|
|
756
|
+
clips: clips.map((_, i) => ({
|
|
757
|
+
index: i,
|
|
758
|
+
status: i < ci ? "done" : i === ci ? "rendering" : "queued",
|
|
759
|
+
progress: i < ci ? 1 : i === ci ? p : 0
|
|
760
|
+
}))
|
|
761
|
+
});
|
|
484
762
|
};
|
|
485
763
|
const frames = await extractFrames(clip, {
|
|
486
764
|
...options,
|
|
487
765
|
width,
|
|
488
766
|
height,
|
|
489
767
|
fps,
|
|
490
|
-
onProgress:
|
|
768
|
+
onProgress: (p) => emitClipProgress(p * 0.9)
|
|
491
769
|
});
|
|
492
770
|
const blob = await backend.encode(frames, {
|
|
493
771
|
width,
|
|
@@ -496,13 +774,16 @@ async function stitchClips(clips, backend, options) {
|
|
|
496
774
|
mimeType: options.mimeType ?? "video/mp4",
|
|
497
775
|
quality: options.quality ?? 0.92,
|
|
498
776
|
encoderOptions: options.encoderOptions,
|
|
499
|
-
onProgress: (p) =>
|
|
777
|
+
onProgress: (p) => emitClipProgress(0.9 + p * 0.1),
|
|
500
778
|
signal: options.signal
|
|
501
779
|
});
|
|
502
780
|
blobs.push(blob);
|
|
503
781
|
}
|
|
504
782
|
if (blobs.length === 1) {
|
|
505
|
-
onProgress?.(
|
|
783
|
+
onProgress?.({
|
|
784
|
+
overall: 1,
|
|
785
|
+
clips: clips.map((_, i) => ({ index: i, status: "done", progress: 1 }))
|
|
786
|
+
});
|
|
506
787
|
return blobs[0];
|
|
507
788
|
}
|
|
508
789
|
return backend.concat(blobs, {
|
|
@@ -511,7 +792,6 @@ async function stitchClips(clips, backend, options) {
|
|
|
511
792
|
fps,
|
|
512
793
|
mimeType: options.mimeType ?? "video/mp4",
|
|
513
794
|
quality: options.quality ?? 0.92,
|
|
514
|
-
onProgress: (p) => onProgress?.((clips.length - 1 + p) / clips.length),
|
|
515
795
|
signal: options.signal
|
|
516
796
|
});
|
|
517
797
|
}
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/backends/ffmpeg.ts","../src/captions.ts","../src/index.ts","../src/compositor.ts","../src/stitch.ts"],"names":["FFmpegBackend","data","createFFmpegBackend"],"mappings":";;;;;;;;;;;;;AAAA,IAAA,cAAA,GAAA,EAAA;AAAA,QAAA,CAAA,cAAA,EAAA;AAAA,EAAA,aAAA,EAAA,MAAAA,qBAAA;AAAA,EAAA,mBAAA,EAAA,MAAA;AAAA,CAAA,CAAA;AAgIO,SAAS,mBAAA,GAAqC;AACnD,EAAA,OAAO,IAAIA,qBAAA,EAAc;AAC3B;AAhIaA;AAFb,IAAA,WAAA,GAAA,KAAA,CAAA;AAAA,EAAA,wBAAA,GAAA;AAEO,IAAMA,wBAAN,MAA+C;AAAA,MAA/C,WAAA,GAAA;AACL,QAAA,IAAA,CAAS,IAAA,GAAO,aAAA;AAGhB;AAAA,QAAA,IAAA,CAAQ,MAAA,GAAc,IAAA;AACtB,QAAA,IAAA,CAAQ,WAAA,GAAc,KAAA;AAEtB;AAAA,QAAA,IAAA,CAAQ,UAAA,GAAkB,IAAA;AAAA,MAAA;AAAA,MAE1B,MAAM,IAAA,GAAsB;AAC1B,QAAA,IAAI,KAAK,WAAA,EAAa;AAEtB,QAAA,MAAM,EAAE,QAAO,GAAI,MAAM,OAAO,gBAAgB,CAAA,CAAE,MAAM,MAAM;AAC5D,UAAA,MAAM,IAAI,KAAA;AAAA,YACR;AAAA,WACF;AAAA,QACF,CAAC,CAAA;AACD,QAAA,MAAM,EAAE,SAAA,EAAW,SAAA,EAAU,GAAI,MAAM,OAAO,cAAc,CAAA;AAE5D,QAAA,MAAM,MAAA,GAAS,IAAI,MAAA,EAAO;AAE1B,QAAA,MAAM,OAAA,GAAU,gDAAA;AAChB,QAAA,MAAM,OAAO,IAAA,CAAK;AAAA,UAChB,SAAS,MAAM,SAAA,CAAU,CAAA,EAAG,OAAO,mBAAmB,iBAAiB,CAAA;AAAA,UACvE,SAAS,MAAM,SAAA,CAAU,CAAA,EAAG,OAAO,qBAAqB,kBAAkB;AAAA,SAC3E,CAAA;AAED,QAAA,IAAA,CAAK,UAAA,GAAa,SAAA;AAClB,QAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,QAAA,IAAA,CAAK,WAAA,GAAc,IAAA;AAAA,MACrB;AAAA,MAEA,MAAM,MAAA,CAAO,MAAA,EAAqB,OAAA,EAAuC;AACvE,QAAA,MAAM,KAAK,IAAA,EAAK;AAChB,QAAA,MAAM,EAAE,MAAA,EAAQ,UAAA,EAAY,SAAA,EAAU,GAAI,IAAA;AAC1C,QAAA,MAAM,EAAE,GAAA,EAAK,KAAA,EAAO,MAAA,EAAQ,UAAA,EAAY,QAAO,GAAI,OAAA;AAEnD,QAAA,MAAM,QAAQ,MAAA,CAAO,MAAA;AACrB,QAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,EAAO,CAAA,EAAA,EAAK;AAC9B,UAAA,IAAI,QAAQ,OAAA,EAAS,MAAM,IAAI,YAAA,CAAa,oBAAoB,YAAY,CAAA;AAE5E,UAAA,MAAM,KAAA,GAAQ,OAAO,CAAC,CAAA;AACtB,UAAA,MAAM,SAAA,GAAY,IAAI,eAAA,CAAgB,KAAA,EAAO,MAAM,CAAA;AACnD,UAAA,MAAM,GAAA,GAAM,SAAA,CAAU,UAAA,CAAW,IAAI,CAAA;AACrC,UAAA,GAAA,CAAI,YAAA,CAAa,KAAA,CAAM,SAAA,EAAW,CAAA,EAAG,CAAC,CAAA;AACtC,UAAA,MAAM,OAAO,MAAM,SAAA,CAAU,cAAc,EAAE,IAAA,EAAM,aAAa,CAAA;AAChE,UAAA,MAAMC,KAAAA,GAAO,MAAM,SAAA,CAAU,IAAI,CAAA;AACjC,UAAA,MAAM,MAAA,CAAO,SAAA,CAAU,CAAA,KAAA,EAAQ,MAAA,CAAO,CAAC,CAAA,CAAE,QAAA,CAAS,CAAA,EAAG,GAAG,CAAC,CAAA,IAAA,CAAA,EAAQA,KAAI,CAAA;AAErE,UAAA,UAAA,GAAa,CAAA,GAAI,QAAQ,GAAG,CAAA;AAAA,QAC9B;AAEA,QAAA,MAAM,OAAO,IAAA,CAAK;AAAA,UAChB,YAAA;AAAA,UAAc,OAAO,GAAG,CAAA;AAAA,UACxB,IAAA;AAAA,UAAM,eAAA;AAAA,UACN,MAAA;AAAA,UAAQ,SAAA;AAAA,UACR,UAAA;AAAA,UAAY,SAAA;AAAA,UACZ,SAAA;AAAA,UAAW,MAAA;AAAA,UACX,MAAA;AAAA,UAAQ,IAAA;AAAA,UACR,WAAA;AAAA,UAAa,YAAA;AAAA,UACb;AAAA,SACD,CAAA;AAED,QAAA,UAAA,GAAa,IAAI,CAAA;AAEjB,QAAA,MAAM,IAAA,GAAO,MAAM,MAAA,CAAO,QAAA,CAAS,YAAY,CAAA;AAE/C,QAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,EAAO,CAAA,EAAA,EAAK;AAC9B,UAAA,MAAM,MAAA,CAAO,UAAA,CAAW,CAAA,KAAA,EAAQ,MAAA,CAAO,CAAC,CAAA,CAAE,QAAA,CAAS,CAAA,EAAG,GAAG,CAAC,CAAA,IAAA,CAAM,CAAA,CAAE,MAAM,MAAM;AAAA,UAAC,CAAC,CAAA;AAAA,QAClF;AACA,QAAA,MAAM,MAAA,CAAO,UAAA,CAAW,YAAY,CAAA,CAAE,MAAM,MAAM;AAAA,QAAC,CAAC,CAAA;AAEpD,QAAA,UAAA,GAAa,CAAC,CAAA;AAEd,QAAA,OAAO,IAAI,IAAA,CAAK,CAAC,IAAA,CAAK,KAAA,EAAM,CAAE,MAAM,CAAA,EAAG,EAAE,IAAA,EAAM,WAAA,EAAa,CAAA;AAAA,MAC9D;AAAA,MAEA,MAAM,MAAA,CAAO,KAAA,EAAe,OAAA,EAAuC;AACjE,QAAA,MAAM,KAAK,IAAA,EAAK;AAChB,QAAA,MAAM,EAAE,MAAA,EAAQ,UAAA,EAAY,SAAA,EAAU,GAAI,IAAA;AAC1C,QAAA,MAAM,EAAE,YAAW,GAAI,OAAA;AAEvB,QAAA,MAAM,YAAsB,EAAC;AAC7B,QAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,QAAQ,CAAA,EAAA,EAAK;AACrC,UAAA,MAAM,IAAA,GAAO,OAAO,CAAC,CAAA,IAAA,CAAA;AACrB,UAAA,MAAM,IAAA,GAAO,MAAM,SAAA,CAAU,KAAA,CAAM,CAAC,CAAC,CAAA;AACrC,UAAA,MAAM,MAAA,CAAO,SAAA,CAAU,IAAA,EAAM,IAAI,CAAA;AACjC,UAAA,SAAA,CAAU,IAAA,CAAK,CAAA,MAAA,EAAS,IAAI,CAAA,CAAA,CAAG,CAAA;AAC/B,UAAA,UAAA,GAAa,CAAA,GAAI,KAAA,CAAM,MAAA,GAAS,GAAG,CAAA;AAAA,QACrC;AAEA,QAAA,MAAM,OAAA,GAAU,IAAI,WAAA,EAAY;AAChC,QAAA,MAAM,MAAA,CAAO,UAAU,YAAA,EAAc,OAAA,CAAQ,OAAO,SAAA,CAAU,IAAA,CAAK,IAAI,CAAC,CAAC,CAAA;AAEzE,QAAA,MAAM,OAAO,IAAA,CAAK;AAAA,UAChB,IAAA;AAAA,UAAM,QAAA;AAAA,UACN,OAAA;AAAA,UAAS,GAAA;AAAA,UACT,IAAA;AAAA,UAAM,YAAA;AAAA,UACN,IAAA;AAAA,UAAM,MAAA;AAAA,UACN;AAAA,SACD,CAAA;AAED,QAAA,UAAA,GAAa,GAAG,CAAA;AAEhB,QAAA,MAAM,GAAA,GAAM,MAAM,MAAA,CAAO,QAAA,CAAS,cAAc,CAAA;AAEhD,QAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,QAAQ,CAAA,EAAA,EAAK;AACrC,UAAA,MAAM,OAAO,UAAA,CAAW,CAAA,IAAA,EAAO,CAAC,CAAA,IAAA,CAAM,CAAA,CAAE,MAAM,MAAM;AAAA,UAAC,CAAC,CAAA;AAAA,QACxD;AACA,QAAA,MAAM,MAAA,CAAO,UAAA,CAAW,YAAY,CAAA,CAAE,MAAM,MAAM;AAAA,QAAC,CAAC,CAAA;AACpD,QAAA,MAAM,MAAA,CAAO,UAAA,CAAW,cAAc,CAAA,CAAE,MAAM,MAAM;AAAA,QAAC,CAAC,CAAA;AAEtD,QAAA,UAAA,GAAa,CAAC,CAAA;AAEd,QAAA,OAAO,IAAI,IAAA,CAAK,CAAC,GAAA,CAAI,KAAA,EAAM,CAAE,MAAM,CAAA,EAAG,EAAE,IAAA,EAAM,WAAA,EAAa,CAAA;AAAA,MAC7D;AAAA,MAEA,MAAM,OAAA,GAAyB;AAC7B,QAAA,IAAI,KAAK,MAAA,EAAQ;AACf,UAAA,MAAM,IAAA,CAAK,OAAO,SAAA,IAAY;AAC9B,UAAA,IAAA,CAAK,MAAA,GAAS,IAAA;AACd,UAAA,IAAA,CAAK,WAAA,GAAc,KAAA;AAAA,QACrB;AAAA,MACF;AAAA,KACF;AAAA,EAAA;AAAA,CAAA,CAAA;;;AC5HO,IAAM,aAAA,GAA0D;AAAA,EACrE,OAAA,EAAS;AAAA,IACP,MAAA,EAAQ,SAAA;AAAA,IACR,UAAA,EAAY,mCAAA;AAAA,IACZ,QAAA,EAAU,EAAA;AAAA,IACV,UAAA,EAAY,KAAA;AAAA,IACZ,KAAA,EAAO,SAAA;AAAA,IACP,WAAA,EAAa,SAAA;AAAA,IACb,WAAA,EAAa,CAAA;AAAA,IACb,eAAA,EAAiB,aAAA;AAAA,IACjB,iBAAA,EAAmB,CAAA;AAAA,IACnB,gBAAA,EAAkB,CAAA;AAAA,IAClB,QAAA,EAAU,QAAA;AAAA,IACV,SAAA,EAAW,QAAA;AAAA,IACX,UAAA,EAAY,GAAA;AAAA,IACZ,QAAA,EAAU,GAAA;AAAA,IACV,MAAA,EAAQ,IAAA;AAAA,IACR,WAAA,EAAa,iBAAA;AAAA,IACb,UAAA,EAAY,CAAA;AAAA,IACZ,aAAA,EAAe,CAAA;AAAA,IACf,aAAA,EAAe,CAAA;AAAA,IACf,SAAA,EAAW,IAAA;AAAA,IACX,aAAA,EAAe,IAAA;AAAA,IACf,kBAAA,EAAoB,SAAA;AAAA,IACpB,sBAAA,EAAwB;AAAA,GAC1B;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,MAAA,EAAQ,QAAA;AAAA,IACR,UAAA,EAAY,8CAAA;AAAA,IACZ,QAAA,EAAU,EAAA;AAAA,IACV,UAAA,EAAY,KAAA;AAAA,IACZ,KAAA,EAAO,SAAA;AAAA,IACP,WAAA,EAAa,aAAA;AAAA,IACb,WAAA,EAAa,CAAA;AAAA,IACb,eAAA,EAAiB,kBAAA;AAAA,IACjB,iBAAA,EAAmB,EAAA;AAAA,IACnB,gBAAA,EAAkB,CAAA;AAAA,IAClB,QAAA,EAAU,QAAA;AAAA,IACV,SAAA,EAAW,QAAA;AAAA,IACX,UAAA,EAAY,GAAA;AAAA,IACZ,QAAA,EAAU,IAAA;AAAA,IACV,MAAA,EAAQ,KAAA;AAAA,IACR,WAAA,EAAa,aAAA;AAAA,IACb,UAAA,EAAY,CAAA;AAAA,IACZ,aAAA,EAAe,CAAA;AAAA,IACf,aAAA,EAAe,CAAA;AAAA,IACf,SAAA,EAAW,KAAA;AAAA,IACX,aAAA,EAAe,KAAA;AAAA,IACf,kBAAA,EAAoB,SAAA;AAAA,IACpB,sBAAA,EAAwB;AAAA,GAC1B;AAAA,EACA,OAAA,EAAS;AAAA,IACP,MAAA,EAAQ,SAAA;AAAA,IACR,UAAA,EAAY,qCAAA;AAAA,IACZ,QAAA,EAAU,EAAA;AAAA,IACV,UAAA,EAAY,KAAA;AAAA,IACZ,KAAA,EAAO,SAAA;AAAA,IACP,WAAA,EAAa,aAAA;AAAA,IACb,WAAA,EAAa,CAAA;AAAA,IACb,eAAA,EAAiB,aAAA;AAAA,IACjB,iBAAA,EAAmB,CAAA;AAAA,IACnB,gBAAA,EAAkB,CAAA;AAAA,IAClB,QAAA,EAAU,QAAA;AAAA,IACV,SAAA,EAAW,QAAA;AAAA,IACX,UAAA,EAAY,GAAA;AAAA,IACZ,QAAA,EAAU,GAAA;AAAA,IACV,MAAA,EAAQ,IAAA;AAAA,IACR,WAAA,EAAa,iBAAA;AAAA,IACb,UAAA,EAAY,CAAA;AAAA,IACZ,aAAA,EAAe,CAAA;AAAA,IACf,aAAA,EAAe,CAAA;AAAA,IACf,SAAA,EAAW,KAAA;AAAA,IACX,aAAA,EAAe,KAAA;AAAA,IACf,kBAAA,EAAoB,SAAA;AAAA,IACpB,sBAAA,EAAwB;AAAA,GAC1B;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,MAAA,EAAQ,MAAA;AAAA,IACR,UAAA,EAAY,oDAAA;AAAA,IACZ,QAAA,EAAU,EAAA;AAAA,IACV,UAAA,EAAY,KAAA;AAAA,IACZ,KAAA,EAAO,SAAA;AAAA,IACP,WAAA,EAAa,SAAA;AAAA,IACb,WAAA,EAAa,CAAA;AAAA,IACb,eAAA,EAAiB,aAAA;AAAA,IACjB,iBAAA,EAAmB,CAAA;AAAA,IACnB,gBAAA,EAAkB,CAAA;AAAA,IAClB,QAAA,EAAU,QAAA;AAAA,IACV,SAAA,EAAW,QAAA;AAAA,IACX,UAAA,EAAY,GAAA;AAAA,IACZ,QAAA,EAAU,IAAA;AAAA,IACV,MAAA,EAAQ,IAAA;AAAA,IACR,WAAA,EAAa,eAAA;AAAA,IACb,UAAA,EAAY,CAAA;AAAA,IACZ,aAAA,EAAe,CAAA;AAAA,IACf,aAAA,EAAe,CAAA;AAAA,IACf,SAAA,EAAW,IAAA;AAAA,IACX,aAAA,EAAe,KAAA;AAAA,IACf,kBAAA,EAAoB,SAAA;AAAA,IACpB,sBAAA,EAAwB;AAAA;AAE5B;AAEO,SAAS,UAAA,CACd,MACA,SAAA,EACc;AACd,EAAA,OAAO,YAAY,EAAE,GAAG,IAAA,EAAM,GAAG,WAAU,GAAI,IAAA;AACjD;AAEO,SAAS,iBAAA,CACd,UACA,WAAA,EACkB;AAClB,EAAA,OAAO,QAAA,CAAS,MAAA;AAAA,IACd,CAAC,GAAA,KAAQ,WAAA,IAAe,GAAA,CAAI,SAAA,IAAa,cAAc,GAAA,CAAI;AAAA,GAC7D;AACF;AAEA,SAAS,QAAA,CACP,GAAA,EACA,IAAA,EACA,QAAA,EACU;AACV,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AAC5B,EAAA,MAAM,QAAkB,EAAC;AACzB,EAAA,IAAI,OAAA,GAAU,EAAA;AAEd,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,MAAM,OAAO,OAAA,GAAU,CAAA,EAAG,OAAO,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA,GAAK,IAAA;AAC9C,IAAA,IAAI,IAAI,WAAA,CAAY,IAAI,CAAA,CAAE,KAAA,GAAQ,YAAY,OAAA,EAAS;AACrD,MAAA,KAAA,CAAM,KAAK,OAAO,CAAA;AAClB,MAAA,OAAA,GAAU,IAAA;AAAA,IACZ,CAAA,MAAO;AACL,MAAA,OAAA,GAAU,IAAA;AAAA,IACZ;AAAA,EACF;AACA,EAAA,IAAI,OAAA,EAAS,KAAA,CAAM,IAAA,CAAK,OAAO,CAAA;AAC/B,EAAA,OAAO,KAAA;AACT;AAEO,SAAS,aAAA,CACd,GAAA,EACA,OAAA,EACA,aAAA,EACA,aACA,YAAA,EACM;AACN,EAAA,MAAM,KAAA,GAAQ,aAAA;AACd,EAAA,MAAM,OAAO,KAAA,CAAM,SAAA,GAAY,QAAQ,IAAA,CAAK,WAAA,KAAgB,OAAA,CAAQ,IAAA;AAEpE,EAAA,GAAA,CAAI,IAAA,EAAK;AAET,EAAA,MAAM,cAAA,GAAkB,KAAA,CAAM,QAAA,GAAW,IAAA,GAAQ,YAAA;AACjD,EAAA,GAAA,CAAI,IAAA,GAAO,GAAG,KAAA,CAAM,UAAU,IAAI,cAAc,CAAA,GAAA,EAAM,MAAM,UAAU,CAAA,CAAA;AACtE,EAAA,GAAA,CAAI,YAAY,KAAA,CAAM,SAAA;AACtB,EAAA,GAAA,CAAI,YAAA,GAAe,QAAA;AAEnB,EAAA,MAAM,KAAA,GAAQ,MAAM,QAAA,GAAW,WAAA;AAC/B,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,GAAA,EAAK,IAAA,EAAM,KAAK,CAAA;AACvC,EAAA,MAAM,KAAA,GAAQ,iBAAiB,KAAA,CAAM,UAAA;AACrC,EAAA,MAAM,MAAA,GAAS,MAAM,MAAA,GAAS,KAAA;AAE9B,EAAA,IAAI,KAAA;AACJ,EAAA,IAAI,KAAA,CAAM,aAAa,KAAA,EAAO;AAC5B,IAAA,KAAA,GAAQ,cAAA,GAAiB,GAAA;AAAA,EAC3B,CAAA,MAAA,IAAW,KAAA,CAAM,QAAA,KAAa,QAAA,EAAU;AACtC,IAAA,KAAA,GAAQ,YAAA,GAAe,CAAA,GAAI,MAAA,GAAS,CAAA,GAAI,KAAA;AAAA,EAC1C,CAAA,MAAO;AACL,IAAA,KAAA,GAAQ,eAAe,cAAA,GAAiB,GAAA;AAAA,EAC1C;AAEA,EAAA,MAAM,KAAK,WAAA,GAAc,CAAA;AAEzB,EAAA,KAAA,CAAM,OAAA,CAAQ,CAAC,IAAA,EAAM,CAAA,KAAM;AACzB,IAAA,MAAM,CAAA,GAAI,QAAQ,CAAA,GAAI,KAAA;AAGtB,IAAA,IAAI,KAAA,CAAM,eAAA,IAAmB,KAAA,CAAM,eAAA,KAAoB,aAAA,EAAe;AACpE,MAAA,MAAM,OAAA,GAAU,GAAA,CAAI,WAAA,CAAY,IAAI,CAAA;AACpC,MAAA,MAAM,EAAA,GAAK,OAAA,CAAQ,KAAA,GAAQ,KAAA,CAAM,iBAAA,GAAoB,CAAA;AACrD,MAAA,MAAM,EAAA,GAAK,QAAQ,KAAA,CAAM,iBAAA;AACzB,MAAA,MAAM,EAAA,GAAK,KAAK,EAAA,GAAK,CAAA;AACrB,MAAA,MAAM,KAAK,CAAA,GAAI,KAAA;AAEf,MAAA,GAAA,CAAI,YAAY,KAAA,CAAM,eAAA;AACtB,MAAA,IAAI,KAAA,CAAM,mBAAmB,CAAA,EAAG;AAC9B,QAAA,SAAA,CAAU,KAAK,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,MAAM,gBAAgB,CAAA;AACrD,QAAA,GAAA,CAAI,IAAA,EAAK;AAAA,MACX,CAAA,MAAO;AACL,QAAA,GAAA,CAAI,QAAA,CAAS,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,EAAE,CAAA;AAAA,MAC7B;AAAA,IACF;AAGA,IAAA,IAAI,MAAM,MAAA,EAAQ;AAChB,MAAA,GAAA,CAAI,cAAc,KAAA,CAAM,WAAA;AACxB,MAAA,GAAA,CAAI,aAAa,KAAA,CAAM,UAAA;AACvB,MAAA,GAAA,CAAI,gBAAgB,KAAA,CAAM,aAAA;AAC1B,MAAA,GAAA,CAAI,gBAAgB,KAAA,CAAM,aAAA;AAAA,IAC5B;AAGA,IAAA,IAAI,KAAA,CAAM,WAAA,GAAc,CAAA,IAAK,KAAA,CAAM,gBAAgB,aAAA,EAAe;AAChE,MAAA,GAAA,CAAI,YAAY,KAAA,CAAM,WAAA;AACtB,MAAA,GAAA,CAAI,cAAc,KAAA,CAAM,WAAA;AACxB,MAAA,GAAA,CAAI,UAAA,CAAW,IAAA,EAAM,EAAA,EAAI,CAAC,CAAA;AAAA,IAC5B;AAGA,IAAA,GAAA,CAAI,WAAA,GAAc,aAAA;AAClB,IAAA,GAAA,CAAI,UAAA,GAAa,CAAA;AACjB,IAAA,GAAA,CAAI,YAAY,KAAA,CAAM,KAAA;AACtB,IAAA,GAAA,CAAI,QAAA,CAAS,IAAA,EAAM,EAAA,EAAI,CAAC,CAAA;AAAA,EAC1B,CAAC,CAAA;AAED,EAAA,GAAA,CAAI,OAAA,EAAQ;AACd;AAEA,SAAS,UACP,GAAA,EACA,CAAA,EACA,CAAA,EACA,CAAA,EACA,GACA,CAAA,EACM;AACN,EAAA,GAAA,CAAI,SAAA,EAAU;AACd,EAAA,GAAA,CAAI,MAAA,CAAO,CAAA,GAAI,CAAA,EAAG,CAAC,CAAA;AACnB,EAAA,GAAA,CAAI,MAAA,CAAO,CAAA,GAAI,CAAA,GAAI,CAAA,EAAG,CAAC,CAAA;AACvB,EAAA,GAAA,CAAI,iBAAiB,CAAA,GAAI,CAAA,EAAG,GAAG,CAAA,GAAI,CAAA,EAAG,IAAI,CAAC,CAAA;AAC3C,EAAA,GAAA,CAAI,MAAA,CAAO,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,IAAI,CAAC,CAAA;AAC3B,EAAA,GAAA,CAAI,gBAAA,CAAiB,IAAI,CAAA,EAAG,CAAA,GAAI,GAAG,CAAA,GAAI,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAC,CAAA;AACnD,EAAA,GAAA,CAAI,MAAA,CAAO,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAC,CAAA;AACvB,EAAA,GAAA,CAAI,iBAAiB,CAAA,EAAG,CAAA,GAAI,GAAG,CAAA,EAAG,CAAA,GAAI,IAAI,CAAC,CAAA;AAC3C,EAAA,GAAA,CAAI,MAAA,CAAO,CAAA,EAAG,CAAA,GAAI,CAAC,CAAA;AACnB,EAAA,GAAA,CAAI,gBAAA,CAAiB,CAAA,EAAG,CAAA,EAAG,CAAA,GAAI,GAAG,CAAC,CAAA;AACnC,EAAA,GAAA,CAAI,SAAA,EAAU;AAChB;;;AC/NA,WAAA,EAAA;;;ACdA,IAAM,gBAAA,GAAqD;AAAA,EACzD,MAAA,EAAQ,CAAC,EAAA,EAAI,CAAC,CAAA;AAAA,EACd,MAAA,EAAQ,CAAC,CAAA,EAAG,EAAE,CAAA;AAAA,EACd,KAAA,EAAO,CAAC,CAAA,EAAG,CAAC,CAAA;AAAA,EACZ,KAAA,EAAO,CAAC,CAAA,EAAG,CAAC,CAAA;AAAA,EACZ,KAAA,EAAO,CAAC,CAAA,EAAG,CAAC,CAAA;AAAA,EACZ,QAAA,EAAU,CAAC,CAAA,EAAG,CAAC;AACjB,CAAA;AAEA,SAAS,uBAAA,CACP,IAAA,EACA,UAAA,EACA,WAAA,EACA,OAAA,EACkB;AAClB,EAAA,MAAM,EAAA,GAAK,KAAK,WAAA,IAAe,UAAA;AAC/B,EAAA,MAAM,QAAQ,gBAAA,CAAiB,EAAE,CAAA,IAAK,CAAC,GAAG,CAAC,CAAA;AAE3C,EAAA,IAAI,KAAA,CAAM,CAAC,CAAA,KAAM,CAAA,EAAG;AAClB,IAAA,OAAO,CAAC,OAAA,CAAQ,KAAA,IAAS,UAAA,EAAY,OAAA,CAAQ,UAAU,WAAW,CAAA;AAAA,EACpE;AAEA,EAAA,MAAM,CAAA,GAAI,QAAQ,KAAA,IAAS,IAAA;AAC3B,EAAA,MAAM,CAAA,GAAI,KAAK,KAAA,CAAM,CAAA,IAAK,MAAM,CAAC,CAAA,GAAI,KAAA,CAAM,CAAC,CAAA,CAAE,CAAA;AAC9C,EAAA,OAAO,CAAC,GAAG,CAAC,CAAA;AACd;AAEA,eAAsB,aAAA,CACpB,MACA,OAAA,EACsB;AACtB,EAAA,MAAM,GAAA,GAAM,QAAQ,GAAA,IAAO,EAAA;AAC3B,EAAA,MAAM,aAAa,OAAA,CAAQ,UAAA;AAC3B,EAAA,MAAM,SAAS,OAAA,CAAQ,MAAA;AAEvB,EAAA,IAAI,MAAA;AACJ,EAAA,IAAI,WAAA,GAAc,KAAA;AAElB,EAAA,IAAI,OAAO,IAAA,CAAK,MAAA,KAAW,QAAA,EAAU;AACnC,IAAA,MAAA,GAAS,IAAA,CAAK,MAAA;AAAA,EAChB,CAAA,MAAA,IAAW,IAAA,CAAK,MAAA,YAAkB,gBAAA,EAAkB;AAClD,IAAA,MAAA,GAAS,KAAK,MAAA,CAAO,GAAA;AAAA,EACvB,CAAA,MAAO;AACL,IAAA,MAAA,GAAS,GAAA,CAAI,eAAA,CAAgB,IAAA,CAAK,MAAc,CAAA;AAChD,IAAA,WAAA,GAAc,IAAA;AAAA,EAChB;AAEA,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,aAAA,CAAc,OAAO,CAAA;AAC5C,EAAA,KAAA,CAAM,KAAA,GAAQ,IAAA;AACd,EAAA,KAAA,CAAM,WAAA,GAAc,WAAA;AACpB,EAAA,KAAA,CAAM,OAAA,GAAU,MAAA;AAEhB,EAAA,MAAM,IAAI,OAAA,CAAc,CAAC,OAAA,EAAS,MAAA,KAAW;AAC3C,IAAA,KAAA,CAAM,gBAAA,GAAmB,MAAM,OAAA,EAAQ;AACvC,IAAA,KAAA,CAAM,OAAA,GAAU,MAAM,MAAA,CAAO,IAAI,MAAM,CAAA,sBAAA,EAAyB,MAAM,EAAE,CAAC,CAAA;AACzE,IAAA,KAAA,CAAM,GAAA,GAAM,MAAA;AAAA,EACd,CAAC,CAAA;AAED,EAAA,MAAM,WAAW,KAAA,CAAM,QAAA;AACvB,EAAA,MAAM,SAAA,GAAY,KAAK,SAAA,IAAa,CAAA;AACpC,EAAA,MAAM,OAAA,GAAU,KAAK,OAAA,IAAW,QAAA;AAChC,EAAA,MAAM,eAAe,OAAA,GAAU,SAAA;AAE/B,EAAA,MAAM,CAAC,IAAA,EAAM,IAAI,CAAA,GAAI,uBAAA;AAAA,IACnB,IAAA;AAAA,IACA,KAAA,CAAM,UAAA;AAAA,IACN,KAAA,CAAM,WAAA;AAAA,IACN;AAAA,GACF;AAEA,EAAA,MAAM,MAAA,GAAS,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AAC9C,EAAA,MAAA,CAAO,KAAA,GAAQ,IAAA;AACf,EAAA,MAAA,CAAO,MAAA,GAAS,IAAA;AAChB,EAAA,MAAM,MAAM,MAAA,CAAO,UAAA,CAAW,MAAM,EAAE,kBAAA,EAAoB,MAAM,CAAA;AAEhE,EAAA,MAAM,WAAA,GAAc,IAAA,CAAK,IAAA,CAAK,YAAA,GAAe,GAAG,CAAA;AAChD,EAAA,MAAM,SAAsB,EAAC;AAE7B,EAAA,MAAM,eAAA,GAAkB,IAAA,CAAK,QAAA,EAAU,QAAA,IAAY,EAAC;AACpD,EAAA,MAAM,eAAA,GAAkB,IAAA,CAAK,QAAA,EAAU,KAAA,EAAO,MAAA,IAAU,QAAA;AACxD,EAAA,MAAM,SAAA,GAAY,UAAA;AAAA,IAChB,cAAc,eAAe,CAAA;AAAA,IAC7B,KAAK,QAAA,EAAU;AAAA,GACjB;AAEA,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,WAAA,EAAa,CAAA,EAAA,EAAK;AACpC,IAAA,IAAI,QAAQ,OAAA,EAAS,MAAM,IAAI,YAAA,CAAa,oBAAoB,YAAY,CAAA;AAE5E,IAAA,MAAM,CAAA,GAAI,YAAa,CAAA,GAAI,GAAA;AAE3B,IAAA,MAAM,SAAA,CAAU,OAAO,CAAC,CAAA;AACxB,IAAA,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,CAAA,EAAG,IAAA,EAAM,IAAI,CAAA;AAE9B,IAAA,cAAA,CAAe,GAAA,EAAK,KAAA,EAAO,IAAA,EAAM,IAAA,EAAM,IAAI,CAAA;AAE3C,IAAA,IAAI,eAAA,CAAgB,SAAS,CAAA,EAAG;AAC9B,MAAA,MAAM,MAAA,GAAS,iBAAA,CAAkB,eAAA,EAAiB,CAAA,GAAI,SAAS,CAAA;AAC/D,MAAA,KAAA,MAAW,OAAO,MAAA,EAAQ;AACxB,QAAA,MAAM,QAAA,GAAW,UAAA,CAAW,SAAA,EAAW,GAAA,CAAI,KAAK,CAAA;AAChD,QAAA,aAAA,CAAc,GAAA,EAAK,GAAA,EAAK,QAAA,EAAU,IAAA,EAAM,IAAI,CAAA;AAAA,MAC9C;AAAA,IACF;AAEA,IAAA,MAAM,YAAY,GAAA,CAAI,YAAA,CAAa,CAAA,EAAG,CAAA,EAAG,MAAM,IAAI,CAAA;AACnD,IAAA,MAAA,CAAO,IAAA,CAAK,EAAE,SAAA,EAAW,SAAA,EAAW,CAAA,GAAI,WAAW,KAAA,EAAO,IAAA,EAAM,MAAA,EAAQ,IAAA,EAAM,CAAA;AAE9E,IAAA,IAAI,UAAA,EAAY,UAAA,CAAW,CAAA,GAAI,WAAW,CAAA;AAAA,EAC5C;AAEA,EAAA,IAAI,WAAA,EAAa,GAAA,CAAI,eAAA,CAAgB,MAAM,CAAA;AAE3C,EAAA,OAAO,MAAA;AACT;AAEA,SAAS,cAAA,CACP,GAAA,EACA,KAAA,EACA,IAAA,EACA,MACA,IAAA,EACM;AACN,EAAA,MAAM,KAAK,KAAA,CAAM,UAAA;AACjB,EAAA,MAAM,KAAK,KAAA,CAAM,WAAA;AAEjB,EAAA,IAAI,KAAK,IAAA,EAAM;AACb,IAAA,MAAM,EAAE,CAAA,EAAG,CAAA,EAAG,KAAA,EAAO,MAAA,KAAW,IAAA,CAAK,IAAA;AACrC,IAAA,GAAA,CAAI,SAAA;AAAA,MACF,KAAA;AAAA,MACA,CAAA,GAAI,EAAA;AAAA,MAAI,CAAA,GAAI,EAAA;AAAA,MAAI,KAAA,GAAQ,EAAA;AAAA,MAAI,MAAA,GAAS,EAAA;AAAA,MACrC,CAAA;AAAA,MAAG,CAAA;AAAA,MAAG,IAAA;AAAA,MAAM;AAAA,KACd;AAAA,EACF,CAAA,MAAO;AACL,IAAA,MAAM,UAAU,EAAA,GAAK,EAAA;AACrB,IAAA,MAAM,QAAQ,IAAA,GAAO,IAAA;AAErB,IAAA,IAAI,KAAK,CAAA,EAAG,EAAA,GAAK,CAAA,EAAG,EAAA,GAAK,IAAI,EAAA,GAAK,EAAA;AAClC,IAAA,IAAI,UAAU,KAAA,EAAO;AACnB,MAAA,EAAA,GAAK,EAAA,GAAK,KAAA;AACV,MAAA,EAAA,GAAA,CAAM,KAAK,EAAA,IAAM,CAAA;AAAA,IACnB,CAAA,MAAA,IAAW,UAAU,KAAA,EAAO;AAC1B,MAAA,EAAA,GAAK,EAAA,GAAK,KAAA;AACV,MAAA,EAAA,GAAA,CAAM,KAAK,EAAA,IAAM,CAAA;AAAA,IACnB;AACA,IAAA,GAAA,CAAI,SAAA,CAAU,OAAO,EAAA,EAAI,EAAA,EAAI,IAAI,EAAA,EAAI,CAAA,EAAG,CAAA,EAAG,IAAA,EAAM,IAAI,CAAA;AAAA,EACvD;AACF;AAEA,SAAS,SAAA,CAAU,OAAyB,IAAA,EAA6B;AACvE,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,KAAY;AAC9B,IAAA,IAAI,KAAK,GAAA,CAAI,KAAA,CAAM,WAAA,GAAc,IAAI,IAAI,IAAA,EAAO;AAC9C,MAAA,OAAA,EAAQ;AACR,MAAA;AAAA,IACF;AACA,IAAA,MAAM,WAAW,MAAM;AACrB,MAAA,KAAA,CAAM,mBAAA,CAAoB,UAAU,QAAQ,CAAA;AAC5C,MAAA,OAAA,EAAQ;AAAA,IACV,CAAA;AACA,IAAA,KAAA,CAAM,gBAAA,CAAiB,UAAU,QAAQ,CAAA;AACzC,IAAA,KAAA,CAAM,WAAA,GAAc,IAAA;AAAA,EACtB,CAAC,CAAA;AACH;;;AChKA,eAAsB,WAAA,CACpB,KAAA,EACA,OAAA,EACA,OAAA,EACe;AACf,EAAA,MAAM,GAAA,GAAM,QAAQ,GAAA,IAAO,EAAA;AAC3B,EAAA,MAAM,KAAA,GAAQ,QAAQ,KAAA,IAAS,IAAA;AAC/B,EAAA,MAAM,MAAA,GAAS,QAAQ,MAAA,IAAU,GAAA;AACjC,EAAA,MAAM,aAAa,OAAA,CAAQ,UAAA;AAE3B,EAAA,MAAM,QAAgB,EAAC;AAEvB,EAAA,KAAA,IAAS,EAAA,GAAK,CAAA,EAAG,EAAA,GAAK,KAAA,CAAM,QAAQ,EAAA,EAAA,EAAM;AACxC,IAAA,MAAM,IAAA,GAAO,MAAM,EAAE,CAAA;AACrB,IAAA,MAAM,YAAA,GAAe,CAAC,CAAA,KAAc;AAClC,MAAA,UAAA,GAAA,CAAe,EAAA,GAAK,CAAA,GAAI,GAAA,IAAO,KAAA,CAAM,MAAO,CAAA;AAAA,IAC9C,CAAA;AAEA,IAAA,MAAM,MAAA,GAAS,MAAM,aAAA,CAAc,IAAA,EAAM;AAAA,MACvC,GAAG,OAAA;AAAA,MACH,KAAA;AAAA,MACA,MAAA;AAAA,MACA,GAAA;AAAA,MACA,UAAA,EAAY;AAAA,KACb,CAAA;AAED,IAAA,MAAM,IAAA,GAAO,MAAM,OAAA,CAAQ,MAAA,CAAO,MAAA,EAAQ;AAAA,MACxC,KAAA;AAAA,MACA,MAAA;AAAA,MACA,GAAA;AAAA,MACA,QAAA,EAAU,QAAQ,QAAA,IAAY,WAAA;AAAA,MAC9B,OAAA,EAAS,QAAQ,OAAA,IAAW,IAAA;AAAA,MAC5B,gBAAgB,OAAA,CAAQ,cAAA;AAAA,MACxB,YAAY,CAAC,CAAA,KAAM,YAAA,CAAa,GAAA,GAAM,IAAI,GAAG,CAAA;AAAA,MAC7C,QAAQ,OAAA,CAAQ;AAAA,KACjB,CAAA;AAED,IAAA,KAAA,CAAM,KAAK,IAAI,CAAA;AAAA,EACjB;AAEA,EAAA,IAAI,KAAA,CAAM,WAAW,CAAA,EAAG;AACtB,IAAA,UAAA,GAAa,CAAC,CAAA;AACd,IAAA,OAAO,MAAM,CAAC,CAAA;AAAA,EAChB;AAEA,EAAA,OAAO,OAAA,CAAQ,OAAO,KAAA,EAAO;AAAA,IAC3B,KAAA;AAAA,IACA,MAAA;AAAA,IACA,GAAA;AAAA,IACA,QAAA,EAAU,QAAQ,QAAA,IAAY,WAAA;AAAA,IAC9B,OAAA,EAAS,QAAQ,OAAA,IAAW,IAAA;AAAA,IAC5B,UAAA,EAAY,CAAC,CAAA,KAAM,UAAA,GAAA,CAAc,MAAM,MAAA,GAAS,CAAA,GAAI,CAAA,IAAK,KAAA,CAAM,MAAM,CAAA;AAAA,IACrE,QAAQ,OAAA,CAAQ;AAAA,GACjB,CAAA;AACH;;;AFlCO,SAAS,iBAAA,CAAkB,MAAA,GAA4B,EAAC,EAAgB;AAC7E,EAAA,MAAM,GAAA,GAAM,OAAO,GAAA,IAAO,EAAA;AAC1B,EAAA,MAAM,KAAA,GAAQ,OAAO,KAAA,IAAS,IAAA;AAC9B,EAAA,MAAM,MAAA,GAAS,OAAO,MAAA,IAAU,GAAA;AAEhC,EAAA,IAAI,QAAA,GAAmC,OAAO,OAAA,IAAW,IAAA;AAEzD,EAAA,eAAe,UAAA,GAAuC;AACpD,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,MAAM,EAAE,mBAAA,EAAAC,oBAAAA,EAAoB,GAAI,MAAM,OAAA,CAAA,OAAA,EAAA,CAAA,IAAA,CAAA,OAAA,WAAA,EAAA,EAAA,cAAA,CAAA,CAAA;AACtC,MAAA,QAAA,GAAWA,oBAAAA,EAAoB;AAAA,IACjC;AACA,IAAA,MAAM,SAAS,IAAA,EAAK;AACpB,IAAA,OAAO,QAAA;AAAA,EACT;AAEA,EAAA,eAAe,MAAA,CAAO,IAAA,EAAiB,OAAA,GAAyB,EAAC,EAAkB;AACjF,IAAA,MAAM,aAA4B,EAAE,GAAA,EAAK,KAAA,EAAO,MAAA,EAAQ,GAAG,OAAA,EAAQ;AACnE,IAAA,MAAM,OAAA,GAAU,MAAM,UAAA,EAAW;AAEjC,IAAA,MAAM,aAAa,UAAA,CAAW,UAAA;AAC9B,IAAA,MAAM,MAAA,GAAS,MAAM,aAAA,CAAc,IAAA,EAAM;AAAA,MACvC,GAAG,UAAA;AAAA,MACH,YAAY,UAAA,GAAa,CAAC,MAAM,UAAA,CAAW,CAAA,GAAI,IAAI,CAAA,GAAI;AAAA,KACxD,CAAA;AAED,IAAA,OAAO,OAAA,CAAQ,OAAO,MAAA,EAAQ;AAAA,MAC5B,KAAA,EAAO,WAAW,KAAA,IAAS,KAAA;AAAA,MAC3B,MAAA,EAAQ,WAAW,MAAA,IAAU,MAAA;AAAA,MAC7B,GAAA,EAAK,WAAW,GAAA,IAAO,GAAA;AAAA,MACvB,QAAA,EAAU,WAAW,QAAA,IAAY,WAAA;AAAA,MACjC,OAAA,EAAS,WAAW,OAAA,IAAW,IAAA;AAAA,MAC/B,gBAAgB,UAAA,CAAW,cAAA;AAAA,MAC3B,UAAA,EAAY,aAAa,CAAC,CAAA,KAAM,WAAW,IAAA,GAAO,CAAA,GAAI,IAAI,CAAA,GAAI,MAAA;AAAA,MAC9D,QAAQ,UAAA,CAAW;AAAA,KACpB,CAAA;AAAA,EACH;AAEA,EAAA,eAAe,WAAA,CAAY,MAAiB,OAAA,EAA0C;AACpF,IAAA,MAAM,IAAA,GAAO,MAAM,MAAA,CAAO,IAAA,EAAM,OAAO,CAAA;AACvC,IAAA,OAAO,GAAA,CAAI,gBAAgB,IAAI,CAAA;AAAA,EACjC;AAEA,EAAA,eAAe,MAAA,CAAO,KAAA,EAAoB,OAAA,GAAyB,EAAC,EAAkB;AACpF,IAAA,MAAM,aAA4B,EAAE,GAAA,EAAK,KAAA,EAAO,MAAA,EAAQ,GAAG,OAAA,EAAQ;AACnE,IAAA,MAAM,OAAA,GAAU,MAAM,UAAA,EAAW;AACjC,IAAA,OAAO,WAAA,CAAY,KAAA,EAAO,OAAA,EAAS,UAAU,CAAA;AAAA,EAC/C;AAEA,EAAA,eAAe,WAAA,CAAY,OAAoB,OAAA,EAA0C;AACvF,IAAA,MAAM,IAAA,GAAO,MAAM,MAAA,CAAO,KAAA,EAAO,OAAO,CAAA;AACxC,IAAA,OAAO,GAAA,CAAI,gBAAgB,IAAI,CAAA;AAAA,EACjC;AAEA,EAAA,OAAO,EAAE,MAAA,EAAQ,WAAA,EAAa,MAAA,EAAQ,WAAA,EAAY;AACpD","file":"index.cjs","sourcesContent":["import type { RendererBackend, FrameData, EncodeOptions } from '../types.js';\n\nexport class FFmpegBackend implements RendererBackend {\n readonly name = 'ffmpeg.wasm';\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private ffmpeg: any = null;\n private initialized = false;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private _fetchFile: any = null;\n\n async init(): Promise<void> {\n if (this.initialized) return;\n\n const { FFmpeg } = await import('@ffmpeg/ffmpeg').catch(() => {\n throw new Error(\n '[FrameWorker] @ffmpeg/ffmpeg is required. Install it: npm install @ffmpeg/ffmpeg @ffmpeg/util'\n );\n });\n const { fetchFile, toBlobURL } = await import('@ffmpeg/util');\n\n const ffmpeg = new FFmpeg();\n\n const baseURL = 'https://unpkg.com/@ffmpeg/core@0.12.6/dist/esm';\n await ffmpeg.load({\n coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'),\n wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'),\n });\n\n this._fetchFile = fetchFile;\n this.ffmpeg = ffmpeg;\n this.initialized = true;\n }\n\n async encode(frames: FrameData[], options: EncodeOptions): Promise<Blob> {\n await this.init();\n const { ffmpeg, _fetchFile: fetchFile } = this;\n const { fps, width, height, onProgress, signal } = options;\n\n const total = frames.length;\n for (let i = 0; i < total; i++) {\n if (signal?.aborted) throw new DOMException('Render cancelled', 'AbortError');\n\n const frame = frames[i];\n const offscreen = new OffscreenCanvas(width, height);\n const ctx = offscreen.getContext('2d')!;\n ctx.putImageData(frame.imageData, 0, 0);\n const blob = await offscreen.convertToBlob({ type: 'image/png' });\n const data = await fetchFile(blob);\n await ffmpeg.writeFile(`frame${String(i).padStart(6, '0')}.png`, data);\n\n onProgress?.(i / total * 0.8);\n }\n\n await ffmpeg.exec([\n '-framerate', String(fps),\n '-i', 'frame%06d.png',\n '-c:v', 'libx264',\n '-pix_fmt', 'yuv420p',\n '-preset', 'fast',\n '-crf', '23',\n '-movflags', '+faststart',\n 'output.mp4',\n ]);\n\n onProgress?.(0.95);\n\n const data = await ffmpeg.readFile('output.mp4') as Uint8Array;\n\n for (let i = 0; i < total; i++) {\n await ffmpeg.deleteFile(`frame${String(i).padStart(6, '0')}.png`).catch(() => {});\n }\n await ffmpeg.deleteFile('output.mp4').catch(() => {});\n\n onProgress?.(1);\n\n return new Blob([data.slice().buffer], { type: 'video/mp4' });\n }\n\n async concat(blobs: Blob[], options: EncodeOptions): Promise<Blob> {\n await this.init();\n const { ffmpeg, _fetchFile: fetchFile } = this;\n const { onProgress } = options;\n\n const listLines: string[] = [];\n for (let i = 0; i < blobs.length; i++) {\n const name = `clip${i}.mp4`;\n const data = await fetchFile(blobs[i]);\n await ffmpeg.writeFile(name, data);\n listLines.push(`file '${name}'`);\n onProgress?.(i / blobs.length * 0.6);\n }\n\n const encoder = new TextEncoder();\n await ffmpeg.writeFile('concat.txt', encoder.encode(listLines.join('\\n')));\n\n await ffmpeg.exec([\n '-f', 'concat',\n '-safe', '0',\n '-i', 'concat.txt',\n '-c', 'copy',\n 'stitched.mp4',\n ]);\n\n onProgress?.(0.9);\n\n const out = await ffmpeg.readFile('stitched.mp4') as Uint8Array;\n\n for (let i = 0; i < blobs.length; i++) {\n await ffmpeg.deleteFile(`clip${i}.mp4`).catch(() => {});\n }\n await ffmpeg.deleteFile('concat.txt').catch(() => {});\n await ffmpeg.deleteFile('stitched.mp4').catch(() => {});\n\n onProgress?.(1);\n\n return new Blob([out.slice().buffer], { type: 'video/mp4' });\n }\n\n async destroy(): Promise<void> {\n if (this.ffmpeg) {\n await this.ffmpeg.terminate?.();\n this.ffmpeg = null;\n this.initialized = false;\n }\n }\n}\n\nexport function createFFmpegBackend(): FFmpegBackend {\n return new FFmpegBackend();\n}\n","import type { CaptionSegment, CaptionStyle, CaptionStylePreset } from './types.js';\n\nexport const STYLE_PRESETS: Record<CaptionStylePreset, CaptionStyle> = {\n hormozi: {\n preset: 'hormozi',\n fontFamily: 'Impact, \"Arial Black\", sans-serif',\n fontSize: 64,\n fontWeight: '900',\n color: '#FFFFFF',\n strokeColor: '#000000',\n strokeWidth: 4,\n backgroundColor: 'transparent',\n backgroundPadding: 0,\n backgroundRadius: 0,\n position: 'bottom',\n textAlign: 'center',\n lineHeight: 1.1,\n maxWidth: 0.9,\n shadow: true,\n shadowColor: 'rgba(0,0,0,0.9)',\n shadowBlur: 6,\n shadowOffsetX: 2,\n shadowOffsetY: 2,\n uppercase: true,\n wordHighlight: true,\n wordHighlightColor: '#FFD700',\n wordHighlightTextColor: '#000000',\n },\n modern: {\n preset: 'modern',\n fontFamily: '\"Inter\", \"Helvetica Neue\", Arial, sans-serif',\n fontSize: 42,\n fontWeight: '700',\n color: '#FFFFFF',\n strokeColor: 'transparent',\n strokeWidth: 0,\n backgroundColor: 'rgba(0,0,0,0.65)',\n backgroundPadding: 12,\n backgroundRadius: 8,\n position: 'bottom',\n textAlign: 'center',\n lineHeight: 1.3,\n maxWidth: 0.85,\n shadow: false,\n shadowColor: 'transparent',\n shadowBlur: 0,\n shadowOffsetX: 0,\n shadowOffsetY: 0,\n uppercase: false,\n wordHighlight: false,\n wordHighlightColor: '#3B82F6',\n wordHighlightTextColor: '#FFFFFF',\n },\n minimal: {\n preset: 'minimal',\n fontFamily: '\"Helvetica Neue\", Arial, sans-serif',\n fontSize: 36,\n fontWeight: '400',\n color: '#FFFFFF',\n strokeColor: 'transparent',\n strokeWidth: 0,\n backgroundColor: 'transparent',\n backgroundPadding: 0,\n backgroundRadius: 0,\n position: 'bottom',\n textAlign: 'center',\n lineHeight: 1.4,\n maxWidth: 0.8,\n shadow: true,\n shadowColor: 'rgba(0,0,0,0.8)',\n shadowBlur: 8,\n shadowOffsetX: 0,\n shadowOffsetY: 2,\n uppercase: false,\n wordHighlight: false,\n wordHighlightColor: '#FFFFFF',\n wordHighlightTextColor: '#000000',\n },\n bold: {\n preset: 'bold',\n fontFamily: '\"Arial Black\", \"Helvetica Neue\", Arial, sans-serif',\n fontSize: 56,\n fontWeight: '900',\n color: '#FFFF00',\n strokeColor: '#000000',\n strokeWidth: 5,\n backgroundColor: 'transparent',\n backgroundPadding: 0,\n backgroundRadius: 0,\n position: 'center',\n textAlign: 'center',\n lineHeight: 1.2,\n maxWidth: 0.88,\n shadow: true,\n shadowColor: 'rgba(0,0,0,1)',\n shadowBlur: 4,\n shadowOffsetX: 3,\n shadowOffsetY: 3,\n uppercase: true,\n wordHighlight: false,\n wordHighlightColor: '#FF0000',\n wordHighlightTextColor: '#FFFFFF',\n },\n};\n\nexport function mergeStyle(\n base: CaptionStyle,\n overrides?: Partial<CaptionStyle>\n): CaptionStyle {\n return overrides ? { ...base, ...overrides } : base;\n}\n\nexport function getActiveCaptions(\n segments: CaptionSegment[],\n currentTime: number\n): CaptionSegment[] {\n return segments.filter(\n (seg) => currentTime >= seg.startTime && currentTime < seg.endTime\n );\n}\n\nfunction wrapText(\n ctx: CanvasRenderingContext2D,\n text: string,\n maxWidth: number\n): string[] {\n const words = text.split(' ');\n const lines: string[] = [];\n let current = '';\n\n for (const word of words) {\n const test = current ? `${current} ${word}` : word;\n if (ctx.measureText(test).width > maxWidth && current) {\n lines.push(current);\n current = word;\n } else {\n current = test;\n }\n }\n if (current) lines.push(current);\n return lines;\n}\n\nexport function renderCaption(\n ctx: CanvasRenderingContext2D,\n segment: CaptionSegment,\n resolvedStyle: CaptionStyle,\n canvasWidth: number,\n canvasHeight: number\n): void {\n const style = resolvedStyle;\n const text = style.uppercase ? segment.text.toUpperCase() : segment.text;\n\n ctx.save();\n\n const scaledFontSize = (style.fontSize / 1080) * canvasHeight;\n ctx.font = `${style.fontWeight} ${scaledFontSize}px ${style.fontFamily}`;\n ctx.textAlign = style.textAlign;\n ctx.textBaseline = 'bottom';\n\n const maxPx = style.maxWidth * canvasWidth;\n const lines = wrapText(ctx, text, maxPx);\n const lineH = scaledFontSize * style.lineHeight;\n const totalH = lines.length * lineH;\n\n let baseY: number;\n if (style.position === 'top') {\n baseY = scaledFontSize * 1.5;\n } else if (style.position === 'center') {\n baseY = canvasHeight / 2 - totalH / 2 + lineH;\n } else {\n baseY = canvasHeight - scaledFontSize * 1.2;\n }\n\n const cx = canvasWidth / 2;\n\n lines.forEach((line, i) => {\n const y = baseY + i * lineH;\n\n // Background box\n if (style.backgroundColor && style.backgroundColor !== 'transparent') {\n const metrics = ctx.measureText(line);\n const bw = metrics.width + style.backgroundPadding * 2;\n const bh = lineH + style.backgroundPadding;\n const bx = cx - bw / 2;\n const by = y - lineH;\n\n ctx.fillStyle = style.backgroundColor;\n if (style.backgroundRadius > 0) {\n roundRect(ctx, bx, by, bw, bh, style.backgroundRadius);\n ctx.fill();\n } else {\n ctx.fillRect(bx, by, bw, bh);\n }\n }\n\n // Shadow\n if (style.shadow) {\n ctx.shadowColor = style.shadowColor;\n ctx.shadowBlur = style.shadowBlur;\n ctx.shadowOffsetX = style.shadowOffsetX;\n ctx.shadowOffsetY = style.shadowOffsetY;\n }\n\n // Stroke\n if (style.strokeWidth > 0 && style.strokeColor !== 'transparent') {\n ctx.lineWidth = style.strokeWidth;\n ctx.strokeStyle = style.strokeColor;\n ctx.strokeText(line, cx, y);\n }\n\n // Fill\n ctx.shadowColor = 'transparent';\n ctx.shadowBlur = 0;\n ctx.fillStyle = style.color;\n ctx.fillText(line, cx, y);\n });\n\n ctx.restore();\n}\n\nfunction roundRect(\n ctx: CanvasRenderingContext2D,\n x: number,\n y: number,\n w: number,\n h: number,\n r: number\n): void {\n ctx.beginPath();\n ctx.moveTo(x + r, y);\n ctx.lineTo(x + w - r, y);\n ctx.quadraticCurveTo(x + w, y, x + w, y + r);\n ctx.lineTo(x + w, y + h - r);\n ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);\n ctx.lineTo(x + r, y + h);\n ctx.quadraticCurveTo(x, y + h, x, y + h - r);\n ctx.lineTo(x, y + r);\n ctx.quadraticCurveTo(x, y, x + r, y);\n ctx.closePath();\n}\n","export type {\n ClipInput,\n CaptionSegment,\n CaptionStyle,\n CaptionStylePreset,\n AspectRatio,\n CropOptions,\n CaptionOptions,\n RenderOptions,\n EncodeOptions,\n FrameData,\n RendererBackend,\n FrameWorkerConfig,\n FrameWorker,\n} from './types.js';\n\nexport { STYLE_PRESETS } from './captions.js';\nexport { FFmpegBackend, createFFmpegBackend } from './backends/ffmpeg.js';\n\nimport type { ClipInput, RenderOptions, FrameWorkerConfig, FrameWorker, RendererBackend } from './types.js';\nimport { extractFrames } from './compositor.js';\nimport { stitchClips } from './stitch.js';\n\nexport function createFrameWorker(config: FrameWorkerConfig = {}): FrameWorker {\n const fps = config.fps ?? 30;\n const width = config.width ?? 1280;\n const height = config.height ?? 720;\n\n let _backend: RendererBackend | null = config.backend ?? null;\n\n async function getBackend(): Promise<RendererBackend> {\n if (!_backend) {\n const { createFFmpegBackend } = await import('./backends/ffmpeg.js');\n _backend = createFFmpegBackend();\n }\n await _backend.init();\n return _backend;\n }\n\n async function render(clip: ClipInput, options: RenderOptions = {}): Promise<Blob> {\n const mergedOpts: RenderOptions = { fps, width, height, ...options };\n const backend = await getBackend();\n\n const onProgress = mergedOpts.onProgress;\n const frames = await extractFrames(clip, {\n ...mergedOpts,\n onProgress: onProgress ? (p) => onProgress(p * 0.85) : undefined,\n });\n\n return backend.encode(frames, {\n width: mergedOpts.width ?? width,\n height: mergedOpts.height ?? height,\n fps: mergedOpts.fps ?? fps,\n mimeType: mergedOpts.mimeType ?? 'video/mp4',\n quality: mergedOpts.quality ?? 0.92,\n encoderOptions: mergedOpts.encoderOptions,\n onProgress: onProgress ? (p) => onProgress(0.85 + p * 0.15) : undefined,\n signal: mergedOpts.signal,\n });\n }\n\n async function renderToUrl(clip: ClipInput, options?: RenderOptions): Promise<string> {\n const blob = await render(clip, options);\n return URL.createObjectURL(blob);\n }\n\n async function stitch(clips: ClipInput[], options: RenderOptions = {}): Promise<Blob> {\n const mergedOpts: RenderOptions = { fps, width, height, ...options };\n const backend = await getBackend();\n return stitchClips(clips, backend, mergedOpts);\n }\n\n async function stitchToUrl(clips: ClipInput[], options?: RenderOptions): Promise<string> {\n const blob = await stitch(clips, options);\n return URL.createObjectURL(blob);\n }\n\n return { render, renderToUrl, stitch, stitchToUrl };\n}\n","import type { ClipInput, FrameData, RenderOptions } from './types.js';\nimport { STYLE_PRESETS, mergeStyle, getActiveCaptions, renderCaption } from './captions.js';\n\nconst ASPECT_RATIO_MAP: Record<string, [number, number]> = {\n '16:9': [16, 9],\n '9:16': [9, 16],\n '1:1': [1, 1],\n '4:3': [4, 3],\n '3:4': [3, 4],\n original: [0, 0],\n};\n\nfunction resolveOutputDimensions(\n clip: ClipInput,\n videoWidth: number,\n videoHeight: number,\n options: RenderOptions\n): [number, number] {\n const ar = clip.aspectRatio ?? 'original';\n const ratio = ASPECT_RATIO_MAP[ar] ?? [0, 0];\n\n if (ratio[0] === 0) {\n return [options.width ?? videoWidth, options.height ?? videoHeight];\n }\n\n const w = options.width ?? 1280;\n const h = Math.round(w * (ratio[1] / ratio[0]));\n return [w, h];\n}\n\nexport async function extractFrames(\n clip: ClipInput,\n options: RenderOptions\n): Promise<FrameData[]> {\n const fps = options.fps ?? 30;\n const onProgress = options.onProgress;\n const signal = options.signal;\n\n let srcUrl: string;\n let needsRevoke = false;\n\n if (typeof clip.source === 'string') {\n srcUrl = clip.source;\n } else if (clip.source instanceof HTMLVideoElement) {\n srcUrl = clip.source.src;\n } else {\n srcUrl = URL.createObjectURL(clip.source as Blob);\n needsRevoke = true;\n }\n\n const video = document.createElement('video');\n video.muted = true;\n video.crossOrigin = 'anonymous';\n video.preload = 'auto';\n\n await new Promise<void>((resolve, reject) => {\n video.onloadedmetadata = () => resolve();\n video.onerror = () => reject(new Error(`Failed to load video: ${srcUrl}`));\n video.src = srcUrl;\n });\n\n const duration = video.duration;\n const startTime = clip.startTime ?? 0;\n const endTime = clip.endTime ?? duration;\n const clipDuration = endTime - startTime;\n\n const [outW, outH] = resolveOutputDimensions(\n clip,\n video.videoWidth,\n video.videoHeight,\n options\n );\n\n const canvas = document.createElement('canvas');\n canvas.width = outW;\n canvas.height = outH;\n const ctx = canvas.getContext('2d', { willReadFrequently: true })!;\n\n const totalFrames = Math.ceil(clipDuration * fps);\n const frames: FrameData[] = [];\n\n const captionSegments = clip.captions?.segments ?? [];\n const baseStylePreset = clip.captions?.style?.preset ?? 'modern';\n const baseStyle = mergeStyle(\n STYLE_PRESETS[baseStylePreset],\n clip.captions?.style\n );\n\n for (let i = 0; i < totalFrames; i++) {\n if (signal?.aborted) throw new DOMException('Render cancelled', 'AbortError');\n\n const t = startTime + (i / fps);\n\n await seekVideo(video, t);\n ctx.clearRect(0, 0, outW, outH);\n\n drawVideoFrame(ctx, video, clip, outW, outH);\n\n if (captionSegments.length > 0) {\n const active = getActiveCaptions(captionSegments, t - startTime);\n for (const seg of active) {\n const segStyle = mergeStyle(baseStyle, seg.style);\n renderCaption(ctx, seg, segStyle, outW, outH);\n }\n }\n\n const imageData = ctx.getImageData(0, 0, outW, outH);\n frames.push({ imageData, timestamp: t - startTime, width: outW, height: outH });\n\n if (onProgress) onProgress(i / totalFrames);\n }\n\n if (needsRevoke) URL.revokeObjectURL(srcUrl);\n\n return frames;\n}\n\nfunction drawVideoFrame(\n ctx: CanvasRenderingContext2D,\n video: HTMLVideoElement,\n clip: ClipInput,\n outW: number,\n outH: number\n): void {\n const vw = video.videoWidth;\n const vh = video.videoHeight;\n\n if (clip.crop) {\n const { x, y, width, height } = clip.crop;\n ctx.drawImage(\n video,\n x * vw, y * vh, width * vw, height * vh,\n 0, 0, outW, outH\n );\n } else {\n const videoAR = vw / vh;\n const outAR = outW / outH;\n\n let sx = 0, sy = 0, sw = vw, sh = vh;\n if (videoAR > outAR) {\n sw = vh * outAR;\n sx = (vw - sw) / 2;\n } else if (videoAR < outAR) {\n sh = vw / outAR;\n sy = (vh - sh) / 2;\n }\n ctx.drawImage(video, sx, sy, sw, sh, 0, 0, outW, outH);\n }\n}\n\nfunction seekVideo(video: HTMLVideoElement, time: number): Promise<void> {\n return new Promise((resolve) => {\n if (Math.abs(video.currentTime - time) < 0.001) {\n resolve();\n return;\n }\n const onSeeked = () => {\n video.removeEventListener('seeked', onSeeked);\n resolve();\n };\n video.addEventListener('seeked', onSeeked);\n video.currentTime = time;\n });\n}\n","import type { ClipInput, RenderOptions, RendererBackend } from './types.js';\nimport { extractFrames } from './compositor.js';\n\nexport async function stitchClips(\n clips: ClipInput[],\n backend: RendererBackend,\n options: RenderOptions\n): Promise<Blob> {\n const fps = options.fps ?? 30;\n const width = options.width ?? 1280;\n const height = options.height ?? 720;\n const onProgress = options.onProgress;\n\n const blobs: Blob[] = [];\n\n for (let ci = 0; ci < clips.length; ci++) {\n const clip = clips[ci];\n const clipProgress = (p: number) => {\n onProgress?.(((ci + p * 0.9) / clips.length));\n };\n\n const frames = await extractFrames(clip, {\n ...options,\n width,\n height,\n fps,\n onProgress: clipProgress,\n });\n\n const blob = await backend.encode(frames, {\n width,\n height,\n fps,\n mimeType: options.mimeType ?? 'video/mp4',\n quality: options.quality ?? 0.92,\n encoderOptions: options.encoderOptions,\n onProgress: (p) => clipProgress(0.9 + p * 0.1),\n signal: options.signal,\n });\n\n blobs.push(blob);\n }\n\n if (blobs.length === 1) {\n onProgress?.(1);\n return blobs[0];\n }\n\n return backend.concat(blobs, {\n width,\n height,\n fps,\n mimeType: options.mimeType ?? 'video/mp4',\n quality: options.quality ?? 0.92,\n onProgress: (p) => onProgress?.((clips.length - 1 + p) / clips.length),\n signal: options.signal,\n });\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/backends/ffmpeg.ts","../src/captions.ts","../src/index.ts","../src/compositor.ts","../src/worker/pool.ts","../src/stitch.ts"],"names":["FFmpegBackend","data","seekVideo","createFFmpegBackend"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { RendererBackend, FrameData, EncodeOptions } from '../types.js';\n\nexport class FFmpegBackend implements RendererBackend {\n readonly name = 'ffmpeg.wasm';\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private ffmpeg: any = null;\n private initialized = false;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private _fetchFile: any = null;\n\n async init(): Promise<void> {\n if (this.initialized) return;\n\n const { FFmpeg } = await import('@ffmpeg/ffmpeg').catch(() => {\n throw new Error(\n '[FrameWorker] @ffmpeg/ffmpeg is required. Install it: npm install @ffmpeg/ffmpeg @ffmpeg/util'\n );\n });\n const { fetchFile, toBlobURL } = await import('@ffmpeg/util');\n\n const ffmpeg = new FFmpeg();\n\n const baseURL = 'https://unpkg.com/@ffmpeg/core@0.12.6/dist/esm';\n await ffmpeg.load({\n coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'),\n wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'),\n });\n\n this._fetchFile = fetchFile;\n this.ffmpeg = ffmpeg;\n this.initialized = true;\n }\n\n async encode(frames: FrameData[], options: EncodeOptions): Promise<Blob> {\n await this.init();\n const { ffmpeg, _fetchFile: fetchFile } = this;\n const { fps, width, height, onProgress, signal } = options;\n\n const total = frames.length;\n for (let i = 0; i < total; i++) {\n if (signal?.aborted) throw new DOMException('Render cancelled', 'AbortError');\n\n const frame = frames[i];\n const offscreen = new OffscreenCanvas(width, height);\n const ctx = offscreen.getContext('2d')!;\n ctx.putImageData(frame.imageData, 0, 0);\n const blob = await offscreen.convertToBlob({ type: 'image/png' });\n const data = await fetchFile(blob);\n await ffmpeg.writeFile(`frame${String(i).padStart(6, '0')}.png`, data);\n\n onProgress?.(i / total * 0.8);\n }\n\n await ffmpeg.exec([\n '-framerate', String(fps),\n '-i', 'frame%06d.png',\n '-c:v', 'libx264',\n '-pix_fmt', 'yuv420p',\n '-preset', 'fast',\n '-crf', '23',\n '-movflags', '+faststart',\n 'output.mp4',\n ]);\n\n onProgress?.(0.95);\n\n const data = await ffmpeg.readFile('output.mp4') as Uint8Array;\n\n for (let i = 0; i < total; i++) {\n await ffmpeg.deleteFile(`frame${String(i).padStart(6, '0')}.png`).catch(() => {});\n }\n await ffmpeg.deleteFile('output.mp4').catch(() => {});\n\n onProgress?.(1);\n\n return new Blob([data.slice().buffer], { type: 'video/mp4' });\n }\n\n async concat(blobs: Blob[], options: EncodeOptions): Promise<Blob> {\n await this.init();\n const { ffmpeg, _fetchFile: fetchFile } = this;\n const { onProgress } = options;\n\n const listLines: string[] = [];\n for (let i = 0; i < blobs.length; i++) {\n const name = `clip${i}.mp4`;\n const data = await fetchFile(blobs[i]);\n await ffmpeg.writeFile(name, data);\n listLines.push(`file '${name}'`);\n onProgress?.(i / blobs.length * 0.6);\n }\n\n const encoder = new TextEncoder();\n await ffmpeg.writeFile('concat.txt', encoder.encode(listLines.join('\\n')));\n\n await ffmpeg.exec([\n '-f', 'concat',\n '-safe', '0',\n '-i', 'concat.txt',\n '-c', 'copy',\n 'stitched.mp4',\n ]);\n\n onProgress?.(0.9);\n\n const out = await ffmpeg.readFile('stitched.mp4') as Uint8Array;\n\n for (let i = 0; i < blobs.length; i++) {\n await ffmpeg.deleteFile(`clip${i}.mp4`).catch(() => {});\n }\n await ffmpeg.deleteFile('concat.txt').catch(() => {});\n await ffmpeg.deleteFile('stitched.mp4').catch(() => {});\n\n onProgress?.(1);\n\n return new Blob([out.slice().buffer], { type: 'video/mp4' });\n }\n\n async destroy(): Promise<void> {\n if (this.ffmpeg) {\n await this.ffmpeg.terminate?.();\n this.ffmpeg = null;\n this.initialized = false;\n }\n }\n}\n\nexport function createFFmpegBackend(): FFmpegBackend {\n return new FFmpegBackend();\n}\n","import type { CaptionSegment, CaptionStyle, CaptionStylePreset } from './types.js';\n\nexport const STYLE_PRESETS: Record<CaptionStylePreset, CaptionStyle> = {\n hormozi: {\n preset: 'hormozi',\n fontFamily: 'Impact, \"Arial Black\", sans-serif',\n fontSize: 64,\n fontWeight: '900',\n color: '#FFFFFF',\n strokeColor: '#000000',\n strokeWidth: 4,\n backgroundColor: 'transparent',\n backgroundPadding: 0,\n backgroundRadius: 0,\n position: 'bottom',\n textAlign: 'center',\n lineHeight: 1.1,\n maxWidth: 0.9,\n shadow: true,\n shadowColor: 'rgba(0,0,0,0.9)',\n shadowBlur: 6,\n shadowOffsetX: 2,\n shadowOffsetY: 2,\n uppercase: true,\n wordHighlight: true,\n wordHighlightColor: '#FFD700',\n wordHighlightTextColor: '#000000',\n },\n modern: {\n preset: 'modern',\n fontFamily: '\"Inter\", \"Helvetica Neue\", Arial, sans-serif',\n fontSize: 42,\n fontWeight: '700',\n color: '#FFFFFF',\n strokeColor: 'transparent',\n strokeWidth: 0,\n backgroundColor: 'rgba(0,0,0,0.65)',\n backgroundPadding: 12,\n backgroundRadius: 8,\n position: 'bottom',\n textAlign: 'center',\n lineHeight: 1.3,\n maxWidth: 0.85,\n shadow: false,\n shadowColor: 'transparent',\n shadowBlur: 0,\n shadowOffsetX: 0,\n shadowOffsetY: 0,\n uppercase: false,\n wordHighlight: false,\n wordHighlightColor: '#3B82F6',\n wordHighlightTextColor: '#FFFFFF',\n },\n minimal: {\n preset: 'minimal',\n fontFamily: '\"Helvetica Neue\", Arial, sans-serif',\n fontSize: 36,\n fontWeight: '400',\n color: '#FFFFFF',\n strokeColor: 'transparent',\n strokeWidth: 0,\n backgroundColor: 'transparent',\n backgroundPadding: 0,\n backgroundRadius: 0,\n position: 'bottom',\n textAlign: 'center',\n lineHeight: 1.4,\n maxWidth: 0.8,\n shadow: true,\n shadowColor: 'rgba(0,0,0,0.8)',\n shadowBlur: 8,\n shadowOffsetX: 0,\n shadowOffsetY: 2,\n uppercase: false,\n wordHighlight: false,\n wordHighlightColor: '#FFFFFF',\n wordHighlightTextColor: '#000000',\n },\n bold: {\n preset: 'bold',\n fontFamily: '\"Arial Black\", \"Helvetica Neue\", Arial, sans-serif',\n fontSize: 56,\n fontWeight: '900',\n color: '#FFFF00',\n strokeColor: '#000000',\n strokeWidth: 5,\n backgroundColor: 'transparent',\n backgroundPadding: 0,\n backgroundRadius: 0,\n position: 'center',\n textAlign: 'center',\n lineHeight: 1.2,\n maxWidth: 0.88,\n shadow: true,\n shadowColor: 'rgba(0,0,0,1)',\n shadowBlur: 4,\n shadowOffsetX: 3,\n shadowOffsetY: 3,\n uppercase: true,\n wordHighlight: false,\n wordHighlightColor: '#FF0000',\n wordHighlightTextColor: '#FFFFFF',\n },\n};\n\nexport function mergeStyle(\n base: CaptionStyle,\n overrides?: Partial<CaptionStyle>\n): CaptionStyle {\n return overrides ? { ...base, ...overrides } : base;\n}\n\nexport function getActiveCaptions(\n segments: CaptionSegment[],\n currentTime: number\n): CaptionSegment[] {\n return segments.filter(\n (seg) => currentTime >= seg.startTime && currentTime < seg.endTime\n );\n}\n\nfunction wrapText(\n ctx: CanvasRenderingContext2D,\n text: string,\n maxWidth: number\n): string[] {\n const words = text.split(' ');\n const lines: string[] = [];\n let current = '';\n\n for (const word of words) {\n const test = current ? `${current} ${word}` : word;\n if (ctx.measureText(test).width > maxWidth && current) {\n lines.push(current);\n current = word;\n } else {\n current = test;\n }\n }\n if (current) lines.push(current);\n return lines;\n}\n\nexport function renderCaption(\n ctx: CanvasRenderingContext2D,\n segment: CaptionSegment,\n resolvedStyle: CaptionStyle,\n canvasWidth: number,\n canvasHeight: number\n): void {\n const style = resolvedStyle;\n const text = style.uppercase ? segment.text.toUpperCase() : segment.text;\n\n ctx.save();\n\n const scaledFontSize = (style.fontSize / 1080) * canvasHeight;\n ctx.font = `${style.fontWeight} ${scaledFontSize}px ${style.fontFamily}`;\n ctx.textAlign = style.textAlign;\n ctx.textBaseline = 'bottom';\n\n const maxPx = style.maxWidth * canvasWidth;\n const lines = wrapText(ctx, text, maxPx);\n const lineH = scaledFontSize * style.lineHeight;\n const totalH = lines.length * lineH;\n\n let baseY: number;\n if (style.position === 'top') {\n baseY = scaledFontSize * 1.5;\n } else if (style.position === 'center') {\n baseY = canvasHeight / 2 - totalH / 2 + lineH;\n } else {\n baseY = canvasHeight - scaledFontSize * 1.2;\n }\n\n const cx = canvasWidth / 2;\n\n lines.forEach((line, i) => {\n const y = baseY + i * lineH;\n\n // Background box\n if (style.backgroundColor && style.backgroundColor !== 'transparent') {\n const metrics = ctx.measureText(line);\n const bw = metrics.width + style.backgroundPadding * 2;\n const bh = lineH + style.backgroundPadding;\n const bx = cx - bw / 2;\n const by = y - lineH;\n\n ctx.fillStyle = style.backgroundColor;\n if (style.backgroundRadius > 0) {\n roundRect(ctx, bx, by, bw, bh, style.backgroundRadius);\n ctx.fill();\n } else {\n ctx.fillRect(bx, by, bw, bh);\n }\n }\n\n // Shadow\n if (style.shadow) {\n ctx.shadowColor = style.shadowColor;\n ctx.shadowBlur = style.shadowBlur;\n ctx.shadowOffsetX = style.shadowOffsetX;\n ctx.shadowOffsetY = style.shadowOffsetY;\n }\n\n // Stroke\n if (style.strokeWidth > 0 && style.strokeColor !== 'transparent') {\n ctx.lineWidth = style.strokeWidth;\n ctx.strokeStyle = style.strokeColor;\n ctx.strokeText(line, cx, y);\n }\n\n // Fill\n ctx.shadowColor = 'transparent';\n ctx.shadowBlur = 0;\n ctx.fillStyle = style.color;\n ctx.fillText(line, cx, y);\n });\n\n ctx.restore();\n}\n\nfunction roundRect(\n ctx: CanvasRenderingContext2D,\n x: number,\n y: number,\n w: number,\n h: number,\n r: number\n): void {\n ctx.beginPath();\n ctx.moveTo(x + r, y);\n ctx.lineTo(x + w - r, y);\n ctx.quadraticCurveTo(x + w, y, x + w, y + r);\n ctx.lineTo(x + w, y + h - r);\n ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);\n ctx.lineTo(x + r, y + h);\n ctx.quadraticCurveTo(x, y + h, x, y + h - r);\n ctx.lineTo(x, y + r);\n ctx.quadraticCurveTo(x, y, x + r, y);\n ctx.closePath();\n}\n","export type {\n ClipInput,\n CaptionSegment,\n CaptionStyle,\n CaptionStylePreset,\n AspectRatio,\n CropOptions,\n CaptionOptions,\n RenderOptions,\n StitchOptions,\n ClipStatus,\n ClipProgress,\n RichProgress,\n EncodeOptions,\n FrameData,\n RendererBackend,\n FrameWorkerConfig,\n FrameWorker,\n} from './types.js';\n\nexport { STYLE_PRESETS } from './captions.js';\nexport { FFmpegBackend, createFFmpegBackend } from './backends/ffmpeg.js';\n\nimport type { ClipInput, RenderOptions, StitchOptions, FrameWorkerConfig, FrameWorker, RendererBackend } from './types.js';\nimport { extractFrames } from './compositor.js';\nimport { stitchClips } from './stitch.js';\n\nexport function createFrameWorker(config: FrameWorkerConfig = {}): FrameWorker {\n const fps = config.fps ?? 30;\n const width = config.width ?? 1280;\n const height = config.height ?? 720;\n\n let _backend: RendererBackend | null = config.backend ?? null;\n\n async function getBackend(): Promise<RendererBackend> {\n if (!_backend) {\n const { createFFmpegBackend } = await import('./backends/ffmpeg.js');\n _backend = createFFmpegBackend();\n }\n await _backend.init();\n return _backend;\n }\n\n async function render(clip: ClipInput, options: RenderOptions = {}): Promise<Blob> {\n const mergedOpts: RenderOptions = { fps, width, height, ...options };\n const backend = await getBackend();\n\n const onProgress = mergedOpts.onProgress;\n const frames = await extractFrames(clip, {\n ...mergedOpts,\n onProgress: onProgress ? (p) => onProgress(p * 0.85) : undefined,\n });\n\n return backend.encode(frames, {\n width: mergedOpts.width ?? width,\n height: mergedOpts.height ?? height,\n fps: mergedOpts.fps ?? fps,\n mimeType: mergedOpts.mimeType ?? 'video/mp4',\n quality: mergedOpts.quality ?? 0.92,\n encoderOptions: mergedOpts.encoderOptions,\n onProgress: onProgress ? (p) => onProgress(0.85 + p * 0.15) : undefined,\n signal: mergedOpts.signal,\n });\n }\n\n async function renderToUrl(clip: ClipInput, options?: RenderOptions): Promise<string> {\n const blob = await render(clip, options);\n return URL.createObjectURL(blob);\n }\n\n async function stitch(clips: ClipInput[], options: StitchOptions = {}): Promise<Blob> {\n const mergedOpts: StitchOptions = { fps, width, height, ...options };\n const backend = await getBackend();\n return stitchClips(clips, backend, mergedOpts);\n }\n\n async function stitchToUrl(clips: ClipInput[], options?: StitchOptions): Promise<string> {\n const blob = await stitch(clips, options);\n return URL.createObjectURL(blob);\n }\n\n return { render, renderToUrl, stitch, stitchToUrl };\n}\n","import type { ClipInput, FrameData, RenderOptions } from './types.js';\nimport { STYLE_PRESETS, mergeStyle, getActiveCaptions, renderCaption } from './captions.js';\n\nconst ASPECT_RATIO_MAP: Record<string, [number, number]> = {\n '16:9': [16, 9],\n '9:16': [9, 16],\n '1:1': [1, 1],\n '4:3': [4, 3],\n '3:4': [3, 4],\n original: [0, 0],\n};\n\nfunction resolveOutputDimensions(\n clip: ClipInput,\n videoWidth: number,\n videoHeight: number,\n options: RenderOptions\n): [number, number] {\n const ar = clip.aspectRatio ?? 'original';\n const ratio = ASPECT_RATIO_MAP[ar] ?? [0, 0];\n\n if (ratio[0] === 0) {\n return [options.width ?? videoWidth, options.height ?? videoHeight];\n }\n\n const w = options.width ?? 1280;\n const h = Math.round(w * (ratio[1] / ratio[0]));\n return [w, h];\n}\n\nexport async function extractFrames(\n clip: ClipInput,\n options: RenderOptions\n): Promise<FrameData[]> {\n const fps = options.fps ?? 30;\n const onProgress = options.onProgress;\n const signal = options.signal;\n\n let srcUrl: string;\n let needsRevoke = false;\n\n if (typeof clip.source === 'string') {\n srcUrl = clip.source;\n } else if (clip.source instanceof HTMLVideoElement) {\n srcUrl = clip.source.src;\n } else {\n srcUrl = URL.createObjectURL(clip.source as Blob);\n needsRevoke = true;\n }\n\n const video = document.createElement('video');\n video.muted = true;\n video.crossOrigin = 'anonymous';\n video.preload = 'auto';\n\n await new Promise<void>((resolve, reject) => {\n video.onloadedmetadata = () => resolve();\n video.onerror = () => reject(new Error(`Failed to load video: ${srcUrl}`));\n video.src = srcUrl;\n });\n\n const duration = video.duration;\n const startTime = clip.startTime ?? 0;\n const endTime = clip.endTime ?? duration;\n const clipDuration = endTime - startTime;\n\n const [outW, outH] = resolveOutputDimensions(\n clip,\n video.videoWidth,\n video.videoHeight,\n options\n );\n\n const canvas = document.createElement('canvas');\n canvas.width = outW;\n canvas.height = outH;\n const ctx = canvas.getContext('2d', { willReadFrequently: true })!;\n\n const totalFrames = Math.ceil(clipDuration * fps);\n const frames: FrameData[] = [];\n\n const captionSegments = clip.captions?.segments ?? [];\n const baseStylePreset = clip.captions?.style?.preset ?? 'modern';\n const baseStyle = mergeStyle(\n STYLE_PRESETS[baseStylePreset],\n clip.captions?.style\n );\n\n for (let i = 0; i < totalFrames; i++) {\n if (signal?.aborted) throw new DOMException('Render cancelled', 'AbortError');\n\n const t = startTime + (i / fps);\n\n await seekVideo(video, t);\n ctx.clearRect(0, 0, outW, outH);\n\n drawVideoFrame(ctx, video, clip, outW, outH);\n\n if (captionSegments.length > 0) {\n const active = getActiveCaptions(captionSegments, t - startTime);\n for (const seg of active) {\n const segStyle = mergeStyle(baseStyle, seg.style);\n renderCaption(ctx, seg, segStyle, outW, outH);\n }\n }\n\n const imageData = ctx.getImageData(0, 0, outW, outH);\n frames.push({ imageData, timestamp: t - startTime, width: outW, height: outH });\n\n if (onProgress) onProgress(i / totalFrames);\n }\n\n if (needsRevoke) URL.revokeObjectURL(srcUrl);\n\n return frames;\n}\n\nfunction drawVideoFrame(\n ctx: CanvasRenderingContext2D,\n video: HTMLVideoElement,\n clip: ClipInput,\n outW: number,\n outH: number\n): void {\n const vw = video.videoWidth;\n const vh = video.videoHeight;\n\n if (clip.crop) {\n const { x, y, width, height } = clip.crop;\n ctx.drawImage(\n video,\n x * vw, y * vh, width * vw, height * vh,\n 0, 0, outW, outH\n );\n } else {\n const videoAR = vw / vh;\n const outAR = outW / outH;\n\n let sx = 0, sy = 0, sw = vw, sh = vh;\n if (videoAR > outAR) {\n sw = vh * outAR;\n sx = (vw - sw) / 2;\n } else if (videoAR < outAR) {\n sh = vw / outAR;\n sy = (vh - sh) / 2;\n }\n ctx.drawImage(video, sx, sy, sw, sh, 0, 0, outW, outH);\n }\n}\n\nfunction seekVideo(video: HTMLVideoElement, time: number): Promise<void> {\n return new Promise((resolve) => {\n if (Math.abs(video.currentTime - time) < 0.001) {\n resolve();\n return;\n }\n const onSeeked = () => {\n video.removeEventListener('seeked', onSeeked);\n resolve();\n };\n video.addEventListener('seeked', onSeeked);\n video.currentTime = time;\n });\n}\n","import type { ClipInput, FrameData } from '../types.js';\nimport type { WorkerInbound, WorkerOutbound, TransferableFrame } from './protocol.js';\nimport { STYLE_PRESETS, mergeStyle } from '../captions.js';\n\nexport interface InternalRenderOptions {\n fps: number;\n width: number;\n height: number;\n signal?: AbortSignal;\n}\n\ninterface QueuedJob {\n jobId: string;\n clip: ClipInput;\n options: InternalRenderOptions;\n onProgress: (current: number, total: number) => void;\n resolve: (frames: FrameData[]) => void;\n reject: (err: Error) => void;\n}\n\nexport class WorkerPool {\n private workers: Worker[] = [];\n private idleWorkers: Worker[] = [];\n private queue: QueuedJob[] = [];\n\n constructor(maxConcurrency: number) {\n const count = Math.min(maxConcurrency, navigator.hardwareConcurrency || 2, 4);\n for (let i = 0; i < count; i++) {\n const worker = new Worker(\n new URL('./render-worker.js', import.meta.url),\n { type: 'module' }\n );\n this.workers.push(worker);\n this.idleWorkers.push(worker);\n }\n }\n\n dispatch(\n jobId: string,\n clip: ClipInput,\n options: InternalRenderOptions,\n onProgress: (current: number, total: number) => void\n ): Promise<FrameData[]> {\n return new Promise((resolve, reject) => {\n this.queue.push({ jobId, clip, options, onProgress, resolve, reject });\n this.processQueue();\n });\n }\n\n private processQueue(): void {\n while (this.queue.length > 0 && this.idleWorkers.length > 0) {\n const worker = this.idleWorkers.pop()!;\n const job = this.queue.shift()!;\n this.runJob(worker, job);\n }\n }\n\n private async runJob(worker: Worker, job: QueuedJob): Promise<void> {\n try {\n const frames = await this.executeJob(worker, job);\n job.resolve(frames);\n } catch (err) {\n job.reject(err instanceof Error ? err : new Error(String(err)));\n } finally {\n this.idleWorkers.push(worker);\n this.processQueue();\n }\n }\n\n private async executeJob(worker: Worker, job: QueuedJob): Promise<FrameData[]> {\n const { jobId, clip, options, onProgress } = job;\n const { fps, width: outW, height: outH, signal } = options;\n\n // Load video on main thread\n let srcUrl: string;\n let needsRevoke = false;\n\n if (typeof clip.source === 'string') {\n srcUrl = clip.source;\n } else if (clip.source instanceof HTMLVideoElement) {\n srcUrl = clip.source.src;\n } else {\n srcUrl = URL.createObjectURL(clip.source as Blob);\n needsRevoke = true;\n }\n\n const video = document.createElement('video');\n video.muted = true;\n video.crossOrigin = 'anonymous';\n video.preload = 'auto';\n\n try {\n await new Promise<void>((res, rej) => {\n video.onloadedmetadata = () => res();\n video.onerror = () => rej(new Error(`Failed to load video: ${srcUrl}`));\n video.src = srcUrl;\n });\n } catch (err) {\n if (needsRevoke) URL.revokeObjectURL(srcUrl);\n throw err;\n }\n\n const duration = video.duration;\n const startSec = clip.startTime ?? 0;\n const endSec = clip.endTime ?? duration;\n const clipDuration = endSec - startSec;\n const totalFrames = Math.ceil(clipDuration * fps);\n\n const captionSegments = clip.captions?.segments ?? [];\n const baseStylePreset = clip.captions?.style?.preset ?? 'modern';\n const captionStyle = mergeStyle(STYLE_PRESETS[baseStylePreset], clip.captions?.style);\n\n // Transfer OffscreenCanvas to worker\n const offscreen = new OffscreenCanvas(outW, outH);\n const initMsg: WorkerInbound = {\n type: 'init',\n jobId,\n canvas: offscreen,\n meta: { captions: captionSegments, captionStyle, width: outW, height: outH, totalFrames },\n };\n worker.postMessage(initMsg, [offscreen]);\n\n // Collect reassembled frames from worker 'done' message\n const resultFrames: FrameData[] = [];\n\n await new Promise<void>((res, rej) => {\n let aborted = false;\n\n const onMessage = (event: MessageEvent<WorkerOutbound>) => {\n const msg = event.data;\n if (msg.jobId !== jobId) return;\n\n if (msg.type === 'progress') {\n onProgress(msg.currentFrame, msg.totalFrames);\n } else if (msg.type === 'done') {\n worker.removeEventListener('message', onMessage);\n for (const tf of msg.frames) {\n resultFrames.push({\n imageData: new ImageData(new Uint8ClampedArray(tf.buffer), tf.width, tf.height),\n timestamp: tf.timestamp,\n width: tf.width,\n height: tf.height,\n });\n }\n res();\n } else if (msg.type === 'error') {\n worker.removeEventListener('message', onMessage);\n rej(new Error(msg.message));\n }\n };\n\n worker.addEventListener('message', onMessage);\n\n if (signal) {\n signal.addEventListener('abort', () => {\n if (aborted) return;\n aborted = true;\n const abortMsg: WorkerInbound = { type: 'abort', jobId };\n worker.postMessage(abortMsg);\n worker.removeEventListener('message', onMessage);\n rej(new DOMException('Render cancelled', 'AbortError'));\n }, { once: true });\n }\n\n // Seek and stream frames from main thread to worker\n (async () => {\n try {\n for (let i = 0; i < totalFrames; i++) {\n if (signal?.aborted || aborted) break;\n\n const t = startSec + i / fps;\n await seekVideo(video, t);\n\n // Handle crop/aspect-ratio on the main thread via createImageBitmap\n const vw = video.videoWidth;\n const vh = video.videoHeight;\n let sx = 0, sy = 0, sw = vw, sh = vh;\n\n if (clip.crop) {\n sx = clip.crop.x * vw;\n sy = clip.crop.y * vw;\n sw = clip.crop.width * vw;\n sh = clip.crop.height * vh;\n } else {\n const videoAR = vw / vh;\n const outAR = outW / outH;\n if (videoAR > outAR) {\n sw = vh * outAR;\n sx = (vw - sw) / 2;\n } else if (videoAR < outAR) {\n sh = vw / outAR;\n sy = (vh - sh) / 2;\n }\n }\n\n const bitmap = await createImageBitmap(video, sx, sy, sw, sh, {\n resizeWidth: outW,\n resizeHeight: outH,\n });\n\n const frameMsg: WorkerInbound = {\n type: 'frame',\n jobId,\n frameIndex: i,\n bitmap,\n timestamp: t - startSec,\n };\n worker.postMessage(frameMsg, [bitmap]);\n }\n\n if (!signal?.aborted && !aborted) {\n const endMsg: WorkerInbound = { type: 'end', jobId };\n worker.postMessage(endMsg);\n }\n } catch (err) {\n if (!aborted) rej(err instanceof Error ? err : new Error(String(err)));\n }\n })();\n });\n\n if (needsRevoke) URL.revokeObjectURL(srcUrl);\n return resultFrames;\n }\n\n terminate(): void {\n for (const worker of this.workers) {\n worker.terminate();\n }\n this.workers = [];\n this.idleWorkers = [];\n }\n}\n\nfunction seekVideo(video: HTMLVideoElement, time: number): Promise<void> {\n return new Promise((resolve) => {\n if (Math.abs(video.currentTime - time) < 0.001) {\n resolve();\n return;\n }\n const onSeeked = () => {\n video.removeEventListener('seeked', onSeeked);\n resolve();\n };\n video.addEventListener('seeked', onSeeked);\n video.currentTime = time;\n });\n}\n","import type { ClipInput, FrameData, RendererBackend, ClipStatus, RichProgress } from './types.js';\nimport type { StitchOptions } from './types.js';\nimport { WorkerPool } from './worker/pool.js';\nimport { extractFrames } from './compositor.js';\n\nexport async function stitchClips(\n clips: ClipInput[],\n backend: RendererBackend,\n options: StitchOptions\n): Promise<Blob> {\n const fps = options.fps ?? 30;\n const width = options.width ?? 1280;\n const height = options.height ?? 720;\n const onProgress = options.onProgress;\n\n // Fall back to sequential path if OffscreenCanvas / Worker not available\n if (!('OffscreenCanvas' in globalThis) || !('Worker' in globalThis)) {\n return sequentialStitch(clips, backend, options, fps, width, height);\n }\n\n const concurrency = Math.min(clips.length, navigator.hardwareConcurrency || 2, 4);\n const pool = new WorkerPool(concurrency);\n\n const clipStatuses: ClipStatus[] = clips.map(() => 'queued');\n const clipProgresses: number[] = clips.map(() => 0);\n\n const emitProgress = () => {\n if (!onProgress) return;\n const overall = clipProgresses.reduce((a, b) => a + b, 0) / clips.length;\n const clipData = clips.map((_, i) => ({\n index: i,\n status: clipStatuses[i],\n progress: clipProgresses[i],\n }));\n onProgress({ overall, clips: clipData });\n };\n\n try {\n // Phase 1: render all clips in parallel (limited by pool concurrency)\n const frameArrays: FrameData[][] = await Promise.all(\n clips.map(async (clip, i) => {\n clipStatuses[i] = 'rendering';\n emitProgress();\n try {\n const frames = await pool.dispatch(\n `clip-${i}`,\n clip,\n { fps, width, height, signal: options.signal },\n (current, total) => {\n clipProgresses[i] = current / total;\n emitProgress();\n }\n );\n return frames;\n } catch (err) {\n clipStatuses[i] = 'failed';\n emitProgress();\n throw err;\n }\n })\n );\n\n // Phase 2: encode each clip sequentially (FFmpeg.wasm is single-instance)\n const blobs: Blob[] = [];\n for (let i = 0; i < frameArrays.length; i++) {\n const blob = await backend.encode(frameArrays[i], {\n width,\n height,\n fps,\n mimeType: options.mimeType ?? 'video/mp4',\n quality: options.quality ?? 0.92,\n encoderOptions: options.encoderOptions,\n signal: options.signal,\n });\n clipStatuses[i] = 'done';\n clipProgresses[i] = 1;\n emitProgress();\n blobs.push(blob);\n }\n\n if (blobs.length === 1) {\n onProgress?.({\n overall: 1,\n clips: clips.map((_, i) => ({ index: i, status: 'done', progress: 1 })),\n });\n return blobs[0];\n }\n\n return backend.concat(blobs, {\n width,\n height,\n fps,\n mimeType: options.mimeType ?? 'video/mp4',\n quality: options.quality ?? 0.92,\n signal: options.signal,\n });\n } finally {\n pool.terminate();\n }\n}\n\nasync function sequentialStitch(\n clips: ClipInput[],\n backend: RendererBackend,\n options: StitchOptions,\n fps: number,\n width: number,\n height: number\n): Promise<Blob> {\n const onProgress = options.onProgress;\n const blobs: Blob[] = [];\n\n for (let ci = 0; ci < clips.length; ci++) {\n const clip = clips[ci];\n\n const emitClipProgress = (p: number) => {\n if (!onProgress) return;\n const overall = (ci + p) / clips.length;\n onProgress({\n overall,\n clips: clips.map((_, i) => ({\n index: i,\n status: i < ci ? 'done' : i === ci ? 'rendering' : 'queued',\n progress: i < ci ? 1 : i === ci ? p : 0,\n })),\n });\n };\n\n const frames = await extractFrames(clip, {\n ...options,\n width,\n height,\n fps,\n onProgress: (p) => emitClipProgress(p * 0.9),\n });\n\n const blob = await backend.encode(frames, {\n width,\n height,\n fps,\n mimeType: options.mimeType ?? 'video/mp4',\n quality: options.quality ?? 0.92,\n encoderOptions: options.encoderOptions,\n onProgress: (p) => emitClipProgress(0.9 + p * 0.1),\n signal: options.signal,\n });\n\n blobs.push(blob);\n }\n\n if (blobs.length === 1) {\n onProgress?.({\n overall: 1,\n clips: clips.map((_, i) => ({ index: i, status: 'done', progress: 1 })),\n });\n return blobs[0];\n }\n\n return backend.concat(blobs, {\n width,\n height,\n fps,\n mimeType: options.mimeType ?? 'video/mp4',\n quality: options.quality ?? 0.92,\n signal: options.signal,\n });\n}\n"]}
|