@pixerate/editor 0.90.0 → 0.91.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.
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5; var _class6;
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5; var _class6; var _class7;
2
2
 
3
3
  var _chunkWAJQKWQUcjs = require('../chunk-WAJQKWQU.cjs');
4
4
 
@@ -852,12 +852,33 @@ function parseVideoAsset(v, path = "asset") {
852
852
  height: num(v.height, `${path}.height`, { optional: true, positive: true }),
853
853
  hasAudio: bool(v.hasAudio, `${path}.hasAudio`),
854
854
  poster: str(v.poster, `${path}.poster`, true),
855
- transcript: parseVideoTranscriptWords(v.transcript, `${path}.transcript`)
855
+ transcript: parseVideoTranscriptWords(v.transcript, `${path}.transcript`),
856
+ proxies: parseVideoAssetProxies(v.proxies, `${path}.proxies`)
856
857
  });
858
+ if (kind !== "video" && asset.proxies) fail(`${path}.proxies`, "are only for video assets");
857
859
  if (kind === "image" && asset.transcript) fail(`${path}.transcript`, "is only for video and audio assets");
858
860
  if (kind === "image" && asset.duration !== void 0) fail(`${path}.duration`, "is only for video and audio assets");
859
861
  return asset;
860
862
  }
863
+ function parseVideoAssetProxies(v, path) {
864
+ if (v === void 0) return void 0;
865
+ if (!Array.isArray(v)) return fail(path, "must be a list of { scale, src }");
866
+ const out = v.map((p, i) => {
867
+ const at = `${path}[${i}]`;
868
+ if (!isObject(p)) return fail(at, "must be an object { scale, src }");
869
+ const unknown = Object.keys(p).find((k) => !["scale", "src", "width", "height"].includes(k));
870
+ if (unknown) fail(`${at}.${unknown}`, "is not a proxy setting; use scale, src, width, height");
871
+ const scale = num(p.scale, `${at}.scale`, { positive: true });
872
+ if (scale >= 1) fail(`${at}.scale`, "must be less than 1 (0.5 is half size)");
873
+ return compact({ scale, src: str(p.src, `${at}.src`), width: num(p.width, `${at}.width`, { optional: true, positive: true }), height: num(p.height, `${at}.height`, { optional: true, positive: true }) });
874
+ });
875
+ const scales = /* @__PURE__ */ new Set();
876
+ for (const [i, p] of out.entries()) {
877
+ if (scales.has(p.scale)) fail(`${path}[${i}].scale`, `repeats ${p.scale}; give each proxy its own scale`);
878
+ scales.add(p.scale);
879
+ }
880
+ return out.length ? out : void 0;
881
+ }
861
882
  function parseVideoClip(v, path = "clip") {
862
883
  if (!isObject(v)) return fail(path, "must be an object");
863
884
  const type = oneOf(_nullishCoalesce(v.type, () => ( "media")), VIDEO_CLIP_TYPES, `${path}.type`, false);
@@ -3219,7 +3240,7 @@ var color = (c) => {
3219
3240
  const h = hex[1].length === 3 ? [...hex[1]].map((x) => x + x).join("") : hex[1];
3220
3241
  return `0x${h.slice(0, 6)}${h.length === 8 ? `@${(parseInt(h.slice(6), 16) / 255).toFixed(3)}` : ""}`;
3221
3242
  };
3222
- var shellQuote = (s) => /^[\w@%+=:,./-]+$/.test(s) ? s : `'${s.replace(/'/g, `'\\''`)}'`;
3243
+ var videoShellQuote = (s) => /^[\w@%+=:,./-]+$/.test(s) ? s : `'${s.replace(/'/g, `'\\''`)}'`;
3223
3244
  function atempo(speed) {
3224
3245
  const out = [];
3225
3246
  let s = speed;
@@ -3515,7 +3536,7 @@ function buildFfmpeg(project, options, measure) {
3515
3536
  const filterComplex = graph.join(";");
3516
3537
  if (measure) {
3517
3538
  const args2 = ["-hide_banner", "-nostats", ...inputs.flat(), "-filter_complex", filterComplex, "-map", "[aout]", "-f", "null", "-"];
3518
- return { args: args2, command: ["ffmpeg", ...args2].map(shellQuote).join(" "), filterComplex, duration: D, notes };
3539
+ return { args: args2, command: ["ffmpeg", ...args2].map(videoShellQuote).join(" "), filterComplex, duration: D, notes };
3519
3540
  }
3520
3541
  const args = [
3521
3542
  "-y",
@@ -3536,20 +3557,349 @@ function buildFfmpeg(project, options, measure) {
3536
3557
  sec(D),
3537
3558
  _nullishCoalesce(options.output, () => ( "output.mp4"))
3538
3559
  ];
3539
- return { args, command: ["ffmpeg", ...args].map(shellQuote).join(" "), filterComplex, duration: D, notes };
3560
+ return { args, command: ["ffmpeg", ...args].map(videoShellQuote).join(" "), filterComplex, duration: D, notes };
3561
+ }
3562
+
3563
+ // src/video-editor/proxy.ts
3564
+ var VIDEO_PROXY_SCALES = [0.5, 0.25];
3565
+ function videoAssetProxy(asset, scale) {
3566
+ if (!_optionalChain([asset, 'optionalAccess', _88 => _88.proxies, 'optionalAccess', _89 => _89.length]) || !(scale > 0) || scale >= 1) return null;
3567
+ let best = null;
3568
+ for (const p of asset.proxies) if (p.scale >= scale - 1e-9 && p.scale < 1 && (!best || p.scale < best.scale)) best = p;
3569
+ return best;
3570
+ }
3571
+ var videoPreviewSrc = (asset, scale, fallback = _nullishCoalesce(_optionalChain([asset, 'optionalAccess', _90 => _90.src]), () => ( ""))) => _nullishCoalesce(_optionalChain([videoAssetProxy, 'call', _91 => _91(asset, scale), 'optionalAccess', _92 => _92.src]), () => ( fallback));
3572
+ function resolveVideoPreviewScale(scale, shown) {
3573
+ if (scale === void 0 || scale === "auto") return Math.min(1, Math.max(0, shown));
3574
+ if (scale === "full") return 1;
3575
+ return Math.min(1, Math.max(0, scale));
3576
+ }
3577
+ function videoProxyName(src, scale = 0.5) {
3578
+ const clean = src.replace(/[?#].*$/, "");
3579
+ const base = clean.replace(/\.[^./\\]+$/, "");
3580
+ return `${base}.proxy-${Math.round(scale * 100)}.mp4`;
3581
+ }
3582
+ function videoProxyFfmpeg(asset, options = {}) {
3583
+ if (asset.kind !== "video") throw new Error(`Asset "${asset.id}" is ${asset.kind === "image" ? "an image" : "audio"}; only video assets have proxies.`);
3584
+ const scale = _nullishCoalesce(options.scale, () => ( 0.5));
3585
+ if (!(scale > 0 && scale < 1)) throw new Error(`A proxy's scale must be between 0 and 1 (not including either), not ${scale}; 0.5 is half size.`);
3586
+ const gop = Math.max(1, Math.round(_nullishCoalesce(options.gop, () => ( 12))));
3587
+ const output = _nullishCoalesce(options.output, () => ( videoProxyName(asset.src, scale)));
3588
+ const src = options.resolve ? options.resolve(asset) : asset.src;
3589
+ const size = `scale=trunc(iw*${scale}/2)*2:trunc(ih*${scale}/2)*2`;
3590
+ const args = [
3591
+ "-y",
3592
+ "-i",
3593
+ src,
3594
+ "-vf",
3595
+ size,
3596
+ "-c:v",
3597
+ "libx264",
3598
+ "-preset",
3599
+ "veryfast",
3600
+ "-tune",
3601
+ "fastdecode",
3602
+ "-crf",
3603
+ String(_nullishCoalesce(options.crf, () => ( 28))),
3604
+ "-g",
3605
+ String(gop),
3606
+ "-keyint_min",
3607
+ String(gop),
3608
+ "-sc_threshold",
3609
+ "0",
3610
+ "-bf",
3611
+ "0",
3612
+ "-pix_fmt",
3613
+ "yuv420p",
3614
+ ...asset.hasAudio === false ? ["-an"] : ["-c:a", "aac", "-b:a", "128k"],
3615
+ "-movflags",
3616
+ "+faststart",
3617
+ output
3618
+ ];
3619
+ const proxy = { scale, src: output, ...asset.width && asset.height ? { width: Math.trunc(asset.width * scale / 2) * 2, height: Math.trunc(asset.height * scale / 2) * 2 } : {} };
3620
+ return { asset: asset.id, args, command: ["ffmpeg", ...args].map(videoShellQuote).join(" "), proxy };
3621
+ }
3622
+ function videoProxyPlan(project, options = {}) {
3623
+ const scale = _nullishCoalesce(options.scale, () => ( 0.5));
3624
+ const wanted = options.assets ? new Set(options.assets) : null;
3625
+ if (options.assets) for (const id of options.assets) {
3626
+ const a = project.assets.find((x) => x.id === id);
3627
+ if (!a) throw new Error(`There is no asset "${id}"; the assets are ${project.assets.map((x) => `"${x.id}"`).join(", ") || "none"}.`);
3628
+ if (a.kind !== "video") throw new Error(`Asset "${id}" is ${a.kind === "image" ? "an image" : "audio"}; only video assets have proxies.`);
3629
+ }
3630
+ return project.assets.filter((a) => a.kind === "video" && (!wanted || wanted.has(a.id)) && !_optionalChain([a, 'access', _93 => _93.proxies, 'optionalAccess', _94 => _94.some, 'call', _95 => _95((p) => Math.abs(p.scale - scale) < 1e-9)])).map((a) => videoProxyFfmpeg(a, { ...options, scale }));
3631
+ }
3632
+ var VIDEO_PREVIEW_SCALE_OPTIONS = [
3633
+ { value: "auto", label: "Auto", scale: "auto" },
3634
+ { value: "full", label: "Full", scale: "full" },
3635
+ ...VIDEO_PROXY_SCALES.map((s) => ({ value: String(s), label: s === 0.5 ? "\xBD" : s === 0.25 ? "\xBC" : `${s * 100}%`, scale: s }))
3636
+ ];
3637
+ var parseVideoPreviewScale = (value) => value === "full" ? "full" : Number(value) > 0 && Number(value) <= 1 ? Number(value) : "auto";
3638
+ var videoProjectHasProxies = (project) => project.assets.some((a) => !!_optionalChain([a, 'access', _96 => _96.proxies, 'optionalAccess', _97 => _97.length]));
3639
+
3640
+ // src/video-editor/frame-cache.ts
3641
+ var videoSourceFrame = (sourceTime, fps) => Math.max(0, Math.floor(sourceTime * fps / 1e3 + 1e-6));
3642
+ var videoSourceFrameTime = (frame, fps) => (frame + 0.5) * 1e3 / fps;
3643
+ var videoFrameCacheKey = (src, frame) => `${frame}@${src}`;
3644
+ function videoFrameRequestsAt(project, time, srcFor = (l) => l.src) {
3645
+ const out = [];
3646
+ for (const layer of videoFrameAt(project, time).layers) {
3647
+ if (layer.type !== "media" || layer.assetKind !== "video") continue;
3648
+ const src = srcFor(layer);
3649
+ if (!src) continue;
3650
+ const frame = videoSourceFrame(_nullishCoalesce(layer.sourceTime, () => ( 0)), project.fps);
3651
+ out.push({ key: videoFrameCacheKey(src, frame), src, frame, sourceTime: videoSourceFrameTime(frame, project.fps), time });
3652
+ }
3653
+ return out;
3654
+ }
3655
+ function planVideoPrefetch(project, time, options = {}) {
3656
+ const window2 = _nullishCoalesce(options.window, () => ( 2e3));
3657
+ const dir = _nullishCoalesce(options.direction, () => ( 1));
3658
+ const step = 1e3 / project.fps;
3659
+ const seen = /* @__PURE__ */ new Set();
3660
+ const out = [];
3661
+ for (let i = 0; i * step <= window2 + 1e-6; i++) {
3662
+ const t = time + dir * i * step;
3663
+ if (t < 0) break;
3664
+ for (const r of videoFrameRequestsAt(project, t, options.srcFor)) {
3665
+ if (seen.has(r.key)) continue;
3666
+ seen.add(r.key);
3667
+ out.push(r);
3668
+ }
3669
+ }
3670
+ return out;
3671
+ }
3672
+ function videoCacheEvictions(entries, options) {
3673
+ let total = entries.reduce((n2, e) => n2 + e.bytes, 0);
3674
+ if (total <= options.maxBytes) return [];
3675
+ const dir = _nullishCoalesce(options.direction, () => ( 1));
3676
+ const behind = _nullishCoalesce(options.behind, () => ( 2));
3677
+ const cost = (e) => {
3678
+ const d = (e.time - options.playhead) * dir;
3679
+ return d >= 0 ? d : -d * behind;
3680
+ };
3681
+ const out = [];
3682
+ for (const e of entries.filter((x) => !_optionalChain([options, 'access', _98 => _98.protect, 'optionalAccess', _99 => _99.has, 'call', _100 => _100(x.key)])).sort((a, b) => cost(b) - cost(a))) {
3683
+ if (total <= options.maxBytes) break;
3684
+ out.push(e.key);
3685
+ total -= e.bytes;
3686
+ }
3687
+ return out;
3688
+ }
3689
+ var defaultCapture = async (source) => {
3690
+ if (typeof createImageBitmap !== "function") return null;
3691
+ const image = await createImageBitmap(source);
3692
+ return { image, width: image.width, height: image.height };
3693
+ };
3694
+ var VideoFrameCache = (_class5 = class {
3695
+ constructor(options = {}) {;_class5.prototype.__init13.call(this);_class5.prototype.__init14.call(this);_class5.prototype.__init15.call(this);_class5.prototype.__init16.call(this);_class5.prototype.__init17.call(this);_class5.prototype.__init18.call(this);_class5.prototype.__init19.call(this);_class5.prototype.__init20.call(this);_class5.prototype.__init21.call(this);_class5.prototype.__init22.call(this);_class5.prototype.__init23.call(this);_class5.prototype.__init24.call(this);
3696
+ this.options = options;
3697
+ this.maxBytes = _nullishCoalesce(options.maxBytes, () => ( 512 * 1024 * 1024));
3698
+ this.prefetch = _nullishCoalesce(options.prefetch, () => ( 2e3));
3699
+ this.decode = options.decode !== void 0 ? options.decode : typeof document !== "undefined" ? createVideoElementDecoder() : null;
3700
+ }
3701
+
3702
+ __init13() {this.entries = /* @__PURE__ */ new Map()}
3703
+ __init14() {this.inFlight = /* @__PURE__ */ new Set()}
3704
+ __init15() {this.queue = []}
3705
+ __init16() {this.protect = /* @__PURE__ */ new Set()}
3706
+ __init17() {this.listeners = /* @__PURE__ */ new Set()}
3707
+ __init18() {this.playhead = 0}
3708
+ __init19() {this.direction = 1}
3709
+ __init20() {this.hits = 0}
3710
+ __init21() {this.misses = 0}
3711
+ __init22() {this.bytes = 0}
3712
+ __init23() {this.generation = 0}
3713
+ __init24() {this.destroyed = false}
3714
+
3715
+
3716
+
3717
+
3718
+ /** The cached frame of `src` showing at a source time (ms), on the project's `fps` grid. Counts a hit or a miss. */
3719
+ get(src, sourceTime, fps) {
3720
+ const entry = this.entries.get(videoFrameCacheKey(src, videoSourceFrame(sourceTime, fps)));
3721
+ if (entry) this.hits++;
3722
+ else this.misses++;
3723
+ return _optionalChain([entry, 'optionalAccess', _101 => _101.frame, 'access', _102 => _102.image]);
3724
+ }
3725
+ /** Whether a frame is cached, without counting it. */
3726
+ has(src, sourceTime, fps) {
3727
+ return this.entries.has(videoFrameCacheKey(src, videoSourceFrame(sourceTime, fps)));
3728
+ }
3729
+ /** Keeps a decoded frame (replacing any with the same key), then evicts to the budget. */
3730
+ put(request, frame) {
3731
+ if (this.destroyed) return void _optionalChain([frame, 'access', _103 => _103.image, 'access', _104 => _104.close, 'optionalCall', _105 => _105()]);
3732
+ const old = this.entries.get(request.key);
3733
+ if (old) this.drop(old);
3734
+ const bytes = Math.max(1, frame.width * frame.height * 4);
3735
+ this.entries.set(request.key, { key: request.key, time: request.time, bytes, frame });
3736
+ this.bytes += bytes;
3737
+ this.evict();
3738
+ this.emit();
3739
+ }
3740
+ /** Keeps what a ready media element shows now as the frame of `src` at `sourceTime` (async; does nothing if it is cached). */
3741
+ capture(src, sourceTime, fps, time, source) {
3742
+ const frame = videoSourceFrame(sourceTime, fps);
3743
+ const key = videoFrameCacheKey(src, frame);
3744
+ if (this.entries.has(key) || this.inFlight.has(key)) return;
3745
+ this.inFlight.add(key);
3746
+ void (_nullishCoalesce(this.options.capture, () => ( defaultCapture)))(source).then((f) => {
3747
+ if (f) this.put({ key, time }, f);
3748
+ }).catch(() => {
3749
+ }).finally(() => this.inFlight.delete(key));
3750
+ }
3751
+ /**
3752
+ * Follows the playhead: protects the prefetch window, evicts to the budget, and queues the
3753
+ * window's missing frames for decoding (nearest first). `srcFor` gives the file a layer plays.
3754
+ */
3755
+ update(project, time, options = {}) {
3756
+ if (this.destroyed) return;
3757
+ this.direction = _nullishCoalesce(options.direction, () => ( (time < this.playhead && !options.playing ? -1 : 1)));
3758
+ this.playhead = time;
3759
+ this.srcFor = options.srcFor;
3760
+ const plan = this.prefetch > 0 ? planVideoPrefetch(project, time, { window: this.prefetch, direction: this.direction, srcFor: options.srcFor }) : videoFrameRequestsAt(project, time, options.srcFor);
3761
+ this.protect = new Set(plan.map((r) => r.key));
3762
+ for (const r of plan) {
3763
+ const e = this.entries.get(r.key);
3764
+ if (e) e.time = r.time;
3765
+ }
3766
+ this.evict();
3767
+ this.generation++;
3768
+ this.queue = this.decode ? plan.filter((r) => !this.entries.has(r.key) && !this.inFlight.has(r.key)) : [];
3769
+ this.pump();
3770
+ }
3771
+ /**
3772
+ * The stretches of the timeline whose video frames are all cached, between `from` and `to` (ms),
3773
+ * for the files the preview last played (its `srcFor`, e.g. proxies) unless `srcFor` is given.
3774
+ * `step` (ms, at least a frame) samples more coarsely.
3775
+ */
3776
+ cachedRanges(project, options = {}) {
3777
+ const step = Math.max(1e3 / project.fps, _nullishCoalesce(options.step, () => ( 0)));
3778
+ const from = Math.max(0, _nullishCoalesce(options.from, () => ( 0)));
3779
+ const to = _nullishCoalesce(options.to, () => ( Math.max(...project.tracks.flatMap((t) => t.clips.map((c) => c.start + c.duration)), 0)));
3780
+ const out = [];
3781
+ for (let t = from; t < to - 1e-6; t += step) {
3782
+ const requests = videoFrameRequestsAt(project, t, _nullishCoalesce(options.srcFor, () => ( this.srcFor)));
3783
+ const cached = requests.length > 0 && requests.every((r) => this.entries.has(r.key));
3784
+ const last = out[out.length - 1];
3785
+ if (!cached) continue;
3786
+ const end = Math.min(to, t + step);
3787
+ if (last && Math.abs(last.end - t) < 1e-6) last.end = end;
3788
+ else out.push({ start: t, end });
3789
+ }
3790
+ return out.map((r) => ({ start: Math.round(r.start * 10) / 10, end: Math.round(r.end * 10) / 10 }));
3791
+ }
3792
+ stats() {
3793
+ return { frames: this.entries.size, bytes: this.bytes, maxBytes: this.maxBytes, hits: this.hits, misses: this.misses, pending: this.inFlight.size + this.queue.length, prefetch: this.prefetch };
3794
+ }
3795
+ /** Called when frames arrive or leave. */
3796
+ subscribe(listener) {
3797
+ this.listeners.add(listener);
3798
+ return () => {
3799
+ this.listeners.delete(listener);
3800
+ };
3801
+ }
3802
+ /** Drops every frame (closing them) and the queue. */
3803
+ clear() {
3804
+ for (const e of [...this.entries.values()]) this.drop(e);
3805
+ this.queue = [];
3806
+ this.generation++;
3807
+ this.emit();
3808
+ }
3809
+ /** Clears and stops decoding for good. */
3810
+ destroy() {
3811
+ this.clear();
3812
+ this.destroyed = true;
3813
+ this.listeners.clear();
3814
+ }
3815
+ pump() {
3816
+ const limit = Math.max(1, _nullishCoalesce(this.options.concurrency, () => ( 2)));
3817
+ while (this.decode && this.inFlight.size < limit && this.queue.length) {
3818
+ const r = this.queue.shift();
3819
+ if (this.entries.has(r.key) || this.inFlight.has(r.key)) continue;
3820
+ if (this.bytes >= this.maxBytes && [...this.entries.keys()].every((k) => this.protect.has(k))) {
3821
+ this.queue = [];
3822
+ break;
3823
+ }
3824
+ const generation = this.generation;
3825
+ this.inFlight.add(r.key);
3826
+ this.decode(r.src, r.sourceTime).then(
3827
+ (frame) => {
3828
+ this.inFlight.delete(r.key);
3829
+ if (generation === this.generation || this.bytes + frame.width * frame.height * 4 <= this.maxBytes) this.put(r, frame);
3830
+ else _optionalChain([frame, 'access', _106 => _106.image, 'access', _107 => _107.close, 'optionalCall', _108 => _108()]);
3831
+ },
3832
+ () => this.inFlight.delete(r.key)
3833
+ ).finally(() => this.pump());
3834
+ }
3835
+ }
3836
+ evict() {
3837
+ const drop = videoCacheEvictions([...this.entries.values()], { playhead: this.playhead, maxBytes: this.maxBytes, direction: this.direction, behind: this.options.behind, protect: this.protect });
3838
+ for (const key of drop) this.drop(this.entries.get(key));
3839
+ }
3840
+ drop(e) {
3841
+ this.entries.delete(e.key);
3842
+ this.bytes -= e.bytes;
3843
+ _optionalChain([e, 'access', _109 => _109.frame, 'access', _110 => _110.image, 'access', _111 => _111.close, 'optionalCall', _112 => _112()]);
3844
+ }
3845
+ emit() {
3846
+ for (const l of [...this.listeners]) l();
3847
+ }
3848
+ }, _class5);
3849
+ function createVideoElementDecoder(options = {}) {
3850
+ const videos = /* @__PURE__ */ new Map();
3851
+ const make = _nullishCoalesce(options.createVideo, () => ( (() => {
3852
+ const el = document.createElement("video");
3853
+ el.muted = true;
3854
+ el.preload = "auto";
3855
+ el.crossOrigin = "anonymous";
3856
+ el.playsInline = true;
3857
+ return el;
3858
+ })));
3859
+ const timeout = _nullishCoalesce(options.timeout, () => ( 5e3));
3860
+ const once = (el, type) => new Promise((resolve, reject) => {
3861
+ const timer = setTimeout(() => reject(new Error(`The video did not ${type} within ${timeout} ms.`)), timeout);
3862
+ el.addEventListener(type, () => (clearTimeout(timer), resolve()), { once: true });
3863
+ el.addEventListener("error", () => (clearTimeout(timer), reject(new Error("The video could not be loaded."))), { once: true });
3864
+ });
3865
+ return (src, sourceTime) => {
3866
+ let v = videos.get(src);
3867
+ if (!v) {
3868
+ const el2 = make();
3869
+ el2.src = src;
3870
+ v = { el: el2, chain: el2.readyState >= 1 ? Promise.resolve() : once(el2, "loadedmetadata") };
3871
+ videos.set(src, v);
3872
+ }
3873
+ const { el } = v;
3874
+ const next = v.chain.then(async () => {
3875
+ const presented = "requestVideoFrameCallback" in el ? new Promise((r) => el.requestVideoFrameCallback(() => r())) : null;
3876
+ const seeked = once(el, "seeked");
3877
+ el.currentTime = sourceTime / 1e3;
3878
+ await seeked;
3879
+ if (presented) await Promise.race([presented, new Promise((r) => setTimeout(r, 100))]);
3880
+ const image = await createImageBitmap(el);
3881
+ return { image, width: image.width, height: image.height };
3882
+ });
3883
+ v.chain = next.catch(() => {
3884
+ });
3885
+ return next;
3886
+ };
3540
3887
  }
3541
3888
 
3542
3889
  // src/video-editor/preview.ts
3543
- var VideoPreviewEngine = (_class5 = class {
3544
- constructor(source, canvas2, options = {}) {;_class5.prototype.__init13.call(this);_class5.prototype.__init14.call(this);_class5.prototype.__init15.call(this);_class5.prototype.__init16.call(this);_class5.prototype.__init17.call(this);_class5.prototype.__init18.call(this);_class5.prototype.__init19.call(this);_class5.prototype.__init20.call(this);_class5.prototype.__init21.call(this);_class5.prototype.__init22.call(this);_class5.prototype.__init23.call(this);_class5.prototype.__init24.call(this);
3890
+ var VideoPreviewEngine = (_class6 = class {
3891
+ constructor(source, canvas2, options = {}) {;_class6.prototype.__init25.call(this);_class6.prototype.__init26.call(this);_class6.prototype.__init27.call(this);_class6.prototype.__init28.call(this);_class6.prototype.__init29.call(this);_class6.prototype.__init30.call(this);_class6.prototype.__init31.call(this);_class6.prototype.__init32.call(this);_class6.prototype.__init33.call(this);_class6.prototype.__init34.call(this);_class6.prototype.__init35.call(this);_class6.prototype.__init36.call(this);_class6.prototype.__init37.call(this);_class6.prototype.__init38.call(this);_class6.prototype.__init39.call(this);
3545
3892
  this.source = source;
3546
3893
  this.options = options;
3547
3894
  this.canvas = canvas2;
3548
- this.ctx = options.context !== void 0 ? options.context : _nullishCoalesce(_optionalChain([canvas2, 'access', _91 => _91.getContext, 'optionalCall', _92 => _92("2d")]), () => ( null));
3895
+ this.ctx = options.context !== void 0 ? options.context : _nullishCoalesce(_optionalChain([canvas2, 'access', _117 => _117.getContext, 'optionalCall', _118 => _118("2d")]), () => ( null));
3549
3896
  this.unsubscribe = source.subscribe(() => this.update());
3550
3897
  this.lutCache = options.luts !== void 0 ? options.luts : _nullishCoalesce(source.luts, () => ( null));
3551
3898
  this.unsubscribeLuts = this.lutCache ? this.lutCache.subscribe(() => this.redraw()) : () => {
3552
3899
  };
3900
+ this.frameCache = options.frameCache !== void 0 ? options.frameCache : _nullishCoalesce(source.frameCache, () => ( null));
3901
+ this.unsubscribeFrames = this.frameCache ? this.frameCache.subscribe(() => this.redraw()) : () => {
3902
+ };
3553
3903
  const pixels = options.pixels !== void 0 ? options.pixels : source.previewPixels;
3554
3904
  this.detachPixels = pixels ? pixels.attach(() => this.readPixels()) : () => {
3555
3905
  };
@@ -3560,24 +3910,29 @@ var VideoPreviewEngine = (_class5 = class {
3560
3910
 
3561
3911
 
3562
3912
 
3563
- __init13() {this.media = /* @__PURE__ */ new Map()}
3564
- __init14() {this.images = /* @__PURE__ */ new Map()}
3913
+ __init25() {this.media = /* @__PURE__ */ new Map()}
3914
+ __init26() {this.images = /* @__PURE__ */ new Map()}
3915
+
3916
+ __init27() {this.scheduled = false}
3917
+ __init28() {this.destroyed = false}
3918
+ __init29() {this.filterHost = null}
3919
+ __init30() {this.filters = /* @__PURE__ */ new Set()}
3920
+
3565
3921
 
3566
- __init15() {this.scheduled = false}
3567
- __init16() {this.destroyed = false}
3568
- __init17() {this.filterHost = null}
3569
- __init18() {this.filters = /* @__PURE__ */ new Set()}
3922
+ __init31() {this.drawn = null}
3923
+ __init32() {this.complete = true}
3570
3924
 
3571
3925
 
3572
- __init19() {this.drawn = null}
3573
- __init20() {this.complete = true}
3574
3926
 
3927
+ __init33() {this.srcFor = (layer) => layer.src}
3928
+ __init34() {this.playing = false}
3929
+ __init35() {this.fps = 30}
3575
3930
 
3576
3931
 
3577
3932
  /** The canvas's pixels and the frame they show, or `null` when its context cannot be read. */
3578
3933
  readPixels() {
3579
3934
  const ctx = this.ctx;
3580
- if (!_optionalChain([ctx, 'optionalAccess', _93 => _93.getImageData]) || !this.drawn) return null;
3935
+ if (!_optionalChain([ctx, 'optionalAccess', _119 => _119.getImageData]) || !this.drawn) return null;
3581
3936
  return { ...this.drawn, pixels: ctx.getImageData(0, 0, this.canvas.width, this.canvas.height) };
3582
3937
  }
3583
3938
  createMedia(kind) {
@@ -3615,15 +3970,22 @@ var VideoPreviewEngine = (_class5 = class {
3615
3970
  entry.media.pause();
3616
3971
  entry.media.removeEventListener("seeked", entry.onReady);
3617
3972
  entry.media.removeEventListener("loadeddata", entry.onReady);
3618
- _optionalChain([entry, 'access', _94 => _94.media, 'access', _95 => _95.removeAttribute, 'optionalCall', _96 => _96("src")]);
3619
- _optionalChain([entry, 'access', _97 => _97.media, 'access', _98 => _98.load, 'optionalCall', _99 => _99()]);
3973
+ _optionalChain([entry, 'access', _120 => _120.media, 'access', _121 => _121.removeAttribute, 'optionalCall', _122 => _122("src")]);
3974
+ _optionalChain([entry, 'access', _123 => _123.media, 'access', _124 => _124.load, 'optionalCall', _125 => _125()]);
3620
3975
  this.media.delete(entry.clip);
3621
3976
  }
3622
3977
  /** Brings the media elements in step with the editor and draws the frame. */
3623
3978
  update() {
3624
3979
  if (this.destroyed) return;
3625
- const { project, time, playing } = this.source.getState();
3980
+ const state = this.source.getState();
3981
+ const { project, time, playing } = state;
3626
3982
  const frame = videoFrameAt(project, time);
3983
+ const scale = resolveVideoPreviewScale(_nullishCoalesce(this.options.previewScale, () => ( state.previewScale)), this.resolutionFor(project.width));
3984
+ const assets = new Map(project.assets.map((a) => [a.id, a]));
3985
+ this.srcFor = (layer) => videoPreviewSrc(assets.get(_nullishCoalesce(layer.asset, () => ( ""))), scale, layer.src);
3986
+ this.playing = playing;
3987
+ this.fps = project.fps;
3988
+ _optionalChain([this, 'access', _126 => _126.frameCache, 'optionalAccess', _127 => _127.update, 'call', _128 => _128(project, time, { playing, srcFor: this.srcFor })]);
3627
3989
  const tolerance = (_nullishCoalesce(this.options.tolerance, () => ( 200))) / 1e3;
3628
3990
  const active = /* @__PURE__ */ new Set();
3629
3991
  const sync = (clip, src, kind, sourceTime, speed, gain) => {
@@ -3650,7 +4012,7 @@ var VideoPreviewEngine = (_class5 = class {
3650
4012
  for (const layer of frame.layers) {
3651
4013
  if (layer.type !== "media" || layer.assetKind !== "video" || !layer.src) continue;
3652
4014
  const sound = sounding.get(layer.clip);
3653
- sync(layer.clip, layer.src, "video", _nullishCoalesce(layer.sourceTime, () => ( 0)), _nullishCoalesce(_optionalChain([sound, 'optionalAccess', _100 => _100.speed]), () => ( 1)), sound ? sound.gain : null);
4015
+ sync(layer.clip, this.srcFor(layer), "video", _nullishCoalesce(layer.sourceTime, () => ( 0)), _nullishCoalesce(_optionalChain([sound, 'optionalAccess', _129 => _129.speed]), () => ( 1)), sound ? sound.gain : null);
3654
4016
  sounding.delete(layer.clip);
3655
4017
  }
3656
4018
  for (const a of sounding.values()) sync(a.clip, a.src, "audio", a.sourceTime, a.speed, a.gain);
@@ -3665,21 +4027,35 @@ var VideoPreviewEngine = (_class5 = class {
3665
4027
  if (!this.destroyed) this.draw(videoFrameAt(this.source.getState().project, this.source.getState().time));
3666
4028
  });
3667
4029
  }
3668
- __init21() {this.sourceFor = (layer) => {
4030
+ __init36() {this.sourceFor = (layer) => {
3669
4031
  if (layer.assetKind === "image" && layer.src) {
3670
4032
  const img = this.image(layer.src);
3671
4033
  return img.complete ? img : null;
3672
4034
  }
3673
4035
  const entry = this.media.get(layer.clip);
3674
- if (!entry || entry.media.readyState < 2) return null;
4036
+ const ready = !!entry && entry.media.readyState >= 2;
4037
+ const cache = this.frameCache;
4038
+ const src = _nullishCoalesce(_optionalChain([entry, 'optionalAccess', _130 => _130.src]), () => ( this.srcFor(layer)));
4039
+ const sourceTime = _nullishCoalesce(layer.sourceTime, () => ( 0));
4040
+ if (cache && src && (!this.playing || !ready || entry.media.seeking)) {
4041
+ const hit = cache.get(src, sourceTime, this.fps);
4042
+ if (hit) return hit;
4043
+ }
4044
+ if (!ready) return null;
3675
4045
  if (entry.media.seeking) this.complete = false;
4046
+ else if (cache && !this.playing) cache.capture(entry.src, sourceTime, this.fps, this.source.getState().time, entry.media);
3676
4047
  return entry.media;
3677
4048
  }}
4049
+ /** Canvas pixels per frame pixel: the option, else the displayed width at the device pixel ratio (at most 1). */
4050
+ resolutionFor(frameWidth) {
4051
+ if (this.options.resolution !== void 0) return this.options.resolution;
4052
+ const shown = this.canvas.clientWidth || frameWidth;
4053
+ const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
4054
+ return Math.min(1, shown * dpr / frameWidth);
4055
+ }
3678
4056
  draw(frame) {
3679
4057
  if (!this.ctx) return;
3680
- const shown = this.canvas.clientWidth || frame.width;
3681
- const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
3682
- const resolution = _nullishCoalesce(this.options.resolution, () => ( Math.min(1, shown * dpr / frame.width)));
4058
+ const resolution = this.resolutionFor(frame.width);
3683
4059
  const w = Math.max(1, Math.round(frame.width * resolution));
3684
4060
  const h = Math.max(1, Math.round(frame.height * resolution));
3685
4061
  if (this.canvas.width !== w) this.canvas.width = w;
@@ -3692,16 +4068,16 @@ var VideoPreviewEngine = (_class5 = class {
3692
4068
  };
3693
4069
  drawVideoFrame(this.ctx, frame, sourceFor, resolution, { defineSvgFilter: this.defineSvgFilter, lutFor: this.lutFor, scratch: this.scratch });
3694
4070
  this.drawn = { time: frame.time, complete: this.complete };
3695
- _optionalChain([this, 'access', _101 => _101.options, 'access', _102 => _102.onFrame, 'optionalCall', _103 => _103(frame)]);
3696
- _optionalChain([this, 'access', _104 => _104.pixelsTarget, 'optionalAccess', _105 => _105.drawn, 'call', _106 => _106(frame.time, this.complete)]);
4071
+ _optionalChain([this, 'access', _131 => _131.options, 'access', _132 => _132.onFrame, 'optionalCall', _133 => _133(frame)]);
4072
+ _optionalChain([this, 'access', _134 => _134.pixelsTarget, 'optionalAccess', _135 => _135.drawn, 'call', _136 => _136(frame.time, this.complete)]);
3697
4073
  }
3698
4074
  /** A grade's LUT once loaded; while it loads (or if it fails) the frame is drawn without it and counts as incomplete. */
3699
- __init22() {this.lutFor = (src) => {
3700
- const lut = _optionalChain([this, 'access', _107 => _107.lutCache, 'optionalAccess', _108 => _108.get, 'call', _109 => _109(src)]);
4075
+ __init37() {this.lutFor = (src) => {
4076
+ const lut = _optionalChain([this, 'access', _137 => _137.lutCache, 'optionalAccess', _138 => _138.get, 'call', _139 => _139(src)]);
3701
4077
  if (!lut) this.complete = false;
3702
4078
  return lut;
3703
4079
  }}
3704
- __init23() {this.scratch = (width, height) => {
4080
+ __init38() {this.scratch = (width, height) => {
3705
4081
  if (this.scratchCanvas === void 0) {
3706
4082
  if (this.options.createScratch) this.scratchCanvas = this.options.createScratch(width, height);
3707
4083
  else if (typeof document !== "undefined") {
@@ -3717,7 +4093,7 @@ var VideoPreviewEngine = (_class5 = class {
3717
4093
  if (c.height !== height) c.height = height;
3718
4094
  return s;
3719
4095
  }}
3720
- __init24() {this.defineSvgFilter = (id, markup) => {
4096
+ __init39() {this.defineSvgFilter = (id, markup) => {
3721
4097
  if (this.options.defineSvgFilter === null) return;
3722
4098
  if (this.options.defineSvgFilter) return this.options.defineSvgFilter(id, markup);
3723
4099
  if (this.filters.has(id) || typeof document === "undefined") return;
@@ -3738,43 +4114,49 @@ var VideoPreviewEngine = (_class5 = class {
3738
4114
  this.destroyed = true;
3739
4115
  this.detachPixels();
3740
4116
  this.unsubscribeLuts();
3741
- _optionalChain([this, 'access', _110 => _110.filterHost, 'optionalAccess', _111 => _111.remove, 'call', _112 => _112()]);
4117
+ this.unsubscribeFrames();
4118
+ _optionalChain([this, 'access', _140 => _140.filterHost, 'optionalAccess', _141 => _141.remove, 'call', _142 => _142()]);
3742
4119
  this.filterHost = null;
3743
4120
  this.filters.clear();
3744
4121
  this.unsubscribe();
3745
4122
  for (const entry of [...this.media.values()]) this.release(entry);
3746
4123
  this.images.clear();
3747
4124
  }
3748
- }, _class5);
4125
+ }, _class6);
3749
4126
 
3750
4127
  // src/video-editor/controller.ts
3751
- var VideoEditorController = (_class6 = class {
4128
+ var VideoEditorController = (_class7 = class {
3752
4129
 
3753
4130
 
3754
4131
  /** Sound levels per asset, loaded on demand for waveforms and agents. */
3755
4132
 
3756
4133
  /** The pixels the preview last drew, for scopes and `get_video_scopes` (a `VideoPreviewEngine` fills it). */
3757
- __init25() {this.previewPixels = new VideoPreviewPixels()}
4134
+ __init40() {this.previewPixels = new VideoPreviewPixels()}
3758
4135
  /** Grades' LUTs, loaded on demand for the preview, SVG frames and agents. */
3759
4136
 
4137
+ /** The host's frame cache, if it gave one (the preview fills and reads it). */
4138
+
3760
4139
 
3761
4140
 
3762
- __init26() {this.selection = []}
4141
+ __init41() {this.selection = []}
3763
4142
 
3764
- __init27() {this.error = null}
3765
- __init28() {this.revision = 0}
3766
- __init29() {this.applying = false}
3767
4143
 
3768
- __init30() {this.listeners = /* @__PURE__ */ new Set()}
3769
- __init31() {this.unsubscribers = []}
4144
+ __init42() {this.error = null}
4145
+ __init43() {this.revision = 0}
4146
+ __init44() {this.applying = false}
3770
4147
 
3771
- constructor(options = {}) {;_class6.prototype.__init25.call(this);_class6.prototype.__init26.call(this);_class6.prototype.__init27.call(this);_class6.prototype.__init28.call(this);_class6.prototype.__init29.call(this);_class6.prototype.__init30.call(this);_class6.prototype.__init31.call(this);_class6.prototype.__init32.call(this);_class6.prototype.__init33.call(this);
4148
+ __init45() {this.listeners = /* @__PURE__ */ new Set()}
4149
+ __init46() {this.unsubscribers = []}
4150
+
4151
+ constructor(options = {}) {;_class7.prototype.__init40.call(this);_class7.prototype.__init41.call(this);_class7.prototype.__init42.call(this);_class7.prototype.__init43.call(this);_class7.prototype.__init44.call(this);_class7.prototype.__init45.call(this);_class7.prototype.__init46.call(this);_class7.prototype.__init47.call(this);_class7.prototype.__init48.call(this);
3772
4152
  this.history = _nullishCoalesce(options.history, () => ( new (0, _chunkQKLRAA4Tcjs.HistoryManager)()));
3773
4153
  this.audioPeaks = options.audioPeaks instanceof VideoAudioPeaksCache ? options.audioPeaks : new VideoAudioPeaksCache(options.audioPeaks);
3774
4154
  this.luts = options.luts instanceof VideoLutCache ? options.luts : new VideoLutCache(options.luts);
4155
+ this.frameCache = _nullishCoalesce(options.frameCache, () => ( null));
3775
4156
  this.project = options.project ? parseVideoProject(options.project) : createVideoProject();
3776
4157
  this.issues = checkVideoProject(this.project);
3777
4158
  this.readOnly = _nullishCoalesce(options.readOnly, () => ( false));
4159
+ this.previewScale = checkPreviewScale(_nullishCoalesce(options.previewScale, () => ( "auto")));
3778
4160
  this.onChange = options.onChange;
3779
4161
  this.player = new (0, _chunk2FGRTYJNcjs.AnimationPlayer)({ clock: options.clock, duration: () => videoProjectDuration(this.project), loop: "none" });
3780
4162
  this.state = this.computeState();
@@ -3794,8 +4176,8 @@ var VideoEditorController = (_class6 = class {
3794
4176
  this.player.pause();
3795
4177
  this.unsubscribers.splice(0).forEach((u) => u());
3796
4178
  }
3797
- __init32() {this.getState = () => this.state}
3798
- __init33() {this.subscribe = (listener) => {
4179
+ __init47() {this.getState = () => this.state}
4180
+ __init48() {this.subscribe = (listener) => {
3799
4181
  this.connect();
3800
4182
  this.listeners.add(listener);
3801
4183
  return () => {
@@ -3824,6 +4206,7 @@ var VideoEditorController = (_class6 = class {
3824
4206
  canRedo: h.canRedo,
3825
4207
  undoLabel: h.undoName,
3826
4208
  redoLabel: h.redoName,
4209
+ previewScale: this.previewScale,
3827
4210
  revision: this.revision
3828
4211
  };
3829
4212
  }
@@ -3851,7 +4234,7 @@ var VideoEditorController = (_class6 = class {
3851
4234
  this.applying = false;
3852
4235
  }
3853
4236
  this.notify();
3854
- _optionalChain([this, 'access', _113 => _113.onChange, 'optionalCall', _114 => _114(project)]);
4237
+ _optionalChain([this, 'access', _143 => _143.onChange, 'optionalCall', _144 => _144(project)]);
3855
4238
  }
3856
4239
  commit(label, next) {
3857
4240
  const before = this.project;
@@ -3913,9 +4296,20 @@ var VideoEditorController = (_class6 = class {
3913
4296
  this.readOnly = readOnly;
3914
4297
  this.notify();
3915
4298
  }
4299
+ /**
4300
+ * Sets how large a picture the preview plays: `full` (the originals), a fraction such as 0.5
4301
+ * (the smallest proxy at least that large, else the original) or `auto` (as large as the canvas
4302
+ * shows). A view setting, not part of the project: no undo step.
4303
+ */
4304
+ setPreviewScale(scale) {
4305
+ const next = checkPreviewScale(scale);
4306
+ if (next === this.previewScale) return;
4307
+ this.previewScale = next;
4308
+ this.notify();
4309
+ }
3916
4310
  /** Selects clips (unknown ids are dropped). `add` toggles them into the current selection. */
3917
4311
  select(clips, options = {}) {
3918
- const known = _nullishCoalesce(_optionalChain([clips, 'optionalAccess', _115 => _115.filter, 'call', _116 => _116((id) => findVideoClip(this.project, id))]), () => ( []));
4312
+ const known = _nullishCoalesce(_optionalChain([clips, 'optionalAccess', _145 => _145.filter, 'call', _146 => _146((id) => findVideoClip(this.project, id))]), () => ( []));
3919
4313
  let next;
3920
4314
  if (options.add) {
3921
4315
  next = [...this.selection];
@@ -3996,7 +4390,12 @@ var VideoEditorController = (_class6 = class {
3996
4390
  toEdl(options = {}) {
3997
4391
  return toVideoEdl(this.project, options);
3998
4392
  }
3999
- }, _class6);
4393
+ }, _class7);
4394
+ function checkPreviewScale(scale) {
4395
+ if (scale === "auto" || scale === "full") return scale;
4396
+ if (typeof scale !== "number" || !(scale > 0 && scale <= 1)) throw new Error(`A preview scale is "auto", "full" or a fraction above 0 up to 1 (0.5 is half size), not ${JSON.stringify(scale)}.`);
4397
+ return scale === 1 ? "full" : scale;
4398
+ }
4000
4399
 
4001
4400
  // src/video-editor/ui.ts
4002
4401
  var STEPS = [40, 100, 200, 500, 1e3, 2e3, 5e3, 1e4, 15e3, 3e4, 6e4, 12e4, 3e5, 6e5];
@@ -4066,7 +4465,7 @@ function videoClipLabel(clip, asset) {
4066
4465
  if (clip.name) return clip.name;
4067
4466
  if (clip.type === "text") return clip.text.split("\n")[0];
4068
4467
  if (clip.type === "color") return clip.color;
4069
- const base = _nullishCoalesce(_nullishCoalesce(_optionalChain([asset, 'optionalAccess', _117 => _117.name]), () => ( _optionalChain([asset, 'optionalAccess', _118 => _118.src, 'access', _119 => _119.replace, 'call', _120 => _120(/^.*[\\/]/, "")]))), () => ( clip.asset));
4468
+ const base = _nullishCoalesce(_nullishCoalesce(_optionalChain([asset, 'optionalAccess', _147 => _147.name]), () => ( _optionalChain([asset, 'optionalAccess', _148 => _148.src, 'access', _149 => _149.replace, 'call', _150 => _150(/^.*[\\/]/, "")]))), () => ( clip.asset));
4070
4469
  const speed = videoClipSpeed(clip);
4071
4470
  return speed !== 1 ? `${base} (${Math.round(speed * 100)}%)` : base;
4072
4471
  }
@@ -4110,7 +4509,7 @@ function videoAssetDropEdit(project, assetId, options = {}) {
4110
4509
  const asset = videoAssetMap(project).get(assetId);
4111
4510
  if (!asset) return null;
4112
4511
  const kind = asset.kind === "audio" ? "audio" : "video";
4113
- const track = _nullishCoalesce(options.track, () => ( _optionalChain([project, 'access', _121 => _121.tracks, 'access', _122 => _122.find, 'call', _123 => _123((t) => t.kind === kind && !t.locked), 'optionalAccess', _124 => _124.id])));
4512
+ const track = _nullishCoalesce(options.track, () => ( _optionalChain([project, 'access', _151 => _151.tracks, 'access', _152 => _152.find, 'call', _153 => _153((t) => t.kind === kind && !t.locked), 'optionalAccess', _154 => _154.id])));
4114
4513
  if (!track) return null;
4115
4514
  return { op: "add_clip", track, clip: { type: "media", asset: asset.id, ...options.start !== void 0 ? { start: options.start } : {} }, ...options.ripple ? { ripple: true } : {}, ...options.mode && options.mode !== "refuse" ? { mode: options.mode } : {} };
4116
4515
  }
@@ -4159,14 +4558,14 @@ var KEY_LABELS = {
4159
4558
  scale: { label: "Scale", step: 0.05 },
4160
4559
  rotation: { label: "Rotation", step: 1 }
4161
4560
  };
4162
- var animatableOf = (clip, property) => VIDEO_TRANSFORM_PROPERTIES.includes(property) ? _optionalChain([clip, 'access', _125 => _125.transform, 'optionalAccess', _126 => _126[property]]) : property === "volume" ? clip.type === "media" ? clip.volume : void 0 : clip.opacity;
4561
+ var animatableOf = (clip, property) => VIDEO_TRANSFORM_PROPERTIES.includes(property) ? _optionalChain([clip, 'access', _155 => _155.transform, 'optionalAccess', _156 => _156[property]]) : property === "volume" ? clip.type === "media" ? clip.volume : void 0 : clip.opacity;
4163
4562
  var playheadInClip = (clip, time) => Math.round(Math.min(clip.duration, Math.max(0, time - clip.start)));
4164
4563
  function videoKeyframeRows(project, clipId, time) {
4165
4564
  const at = findVideoClip(project, clipId);
4166
4565
  if (!at) return [];
4167
4566
  const { clip } = at;
4168
4567
  const asset = clip.type === "media" ? videoAssetMap(project).get(clip.asset) : void 0;
4169
- const visual = !(_optionalChain([asset, 'optionalAccess', _127 => _127.kind]) === "audio" || at.track.kind === "audio");
4568
+ const visual = !(_optionalChain([asset, 'optionalAccess', _157 => _157.kind]) === "audio" || at.track.kind === "audio");
4170
4569
  const audible = clip.type === "media" && videoAssetHasSound(asset);
4171
4570
  const properties = [...visual ? ["opacity"] : [], ...audible ? ["volume"] : [], ...visual ? VIDEO_TRANSFORM_PROPERTIES : []];
4172
4571
  const clipTime = playheadInClip(clip, time);
@@ -4447,14 +4846,14 @@ function createVideoEditorToolProvider(controller, options = {}) {
4447
4846
  return null;
4448
4847
  };
4449
4848
  const placed = (o) => ({
4450
- ..._optionalChain([o, 'access', _128 => _128.created, 'optionalAccess', _129 => _129.length]) ? { created: o.created } : {},
4451
- ..._optionalChain([o, 'access', _130 => _130.trimmed, 'optionalAccess', _131 => _131.length]) ? { trimmed: o.trimmed } : {},
4452
- ..._optionalChain([o, 'access', _132 => _132.removed, 'optionalAccess', _133 => _133.length]) ? { removed: o.removed } : {},
4849
+ ..._optionalChain([o, 'access', _158 => _158.created, 'optionalAccess', _159 => _159.length]) ? { created: o.created } : {},
4850
+ ..._optionalChain([o, 'access', _160 => _160.trimmed, 'optionalAccess', _161 => _161.length]) ? { trimmed: o.trimmed } : {},
4851
+ ..._optionalChain([o, 'access', _162 => _162.removed, 'optionalAccess', _163 => _163.length]) ? { removed: o.removed } : {},
4453
4852
  ...o.rippled ? { rippled: o.rippled } : {}
4454
4853
  });
4455
4854
  const keysOut = (p, clip, property) => {
4456
4855
  const c = clipOut(p, clip);
4457
- const value = VIDEO_TRANSFORM_PROPERTIES.includes(property) ? _optionalChain([c, 'optionalAccess', _134 => _134.transform, 'optionalAccess', _135 => _135[property]]) : _optionalChain([c, 'optionalAccess', _136 => _136[property]]);
4856
+ const value = VIDEO_TRANSFORM_PROPERTIES.includes(property) ? _optionalChain([c, 'optionalAccess', _164 => _164.transform, 'optionalAccess', _165 => _165[property]]) : _optionalChain([c, 'optionalAccess', _166 => _166[property]]);
4458
4857
  return typeof value === "object" ? { keys: value.keys } : { keys: [], value: _nullishCoalesce(value, () => ( null)) };
4459
4858
  };
4460
4859
  const getProject = {
@@ -4466,7 +4865,7 @@ function createVideoEditorToolProvider(controller, options = {}) {
4466
4865
  handler: () => {
4467
4866
  const state = controller.getState();
4468
4867
  const reason = readOnlyReason();
4469
- return { ...describeVideoProject(state.project), playhead: state.time, selection: state.selection, issues: issueSummary(state.issues), editable: !reason, ...reason ? { readOnlyReason: reason } : {} };
4868
+ return { ...describeVideoProject(state.project), playhead: state.time, previewScale: state.previewScale, selection: state.selection, issues: issueSummary(state.issues), editable: !reason, ...reason ? { readOnlyReason: reason } : {} };
4470
4869
  }
4471
4870
  };
4472
4871
  const getFrame = {
@@ -4515,7 +4914,7 @@ function createVideoEditorToolProvider(controller, options = {}) {
4515
4914
  annotations: { title: "Render a frame", ...READ },
4516
4915
  handler: async ({ time, times, columns, format = "svg" }) => {
4517
4916
  const p = project();
4518
- const lutSrcs = [...new Set(p.tracks.flatMap((t) => t.clips.map((c) => _optionalChain([c, 'access', _137 => _137.grade, 'optionalAccess', _138 => _138.lut, 'optionalAccess', _139 => _139.src])).filter((x) => !!x)))];
4917
+ const lutSrcs = [...new Set(p.tracks.flatMap((t) => t.clips.map((c) => _optionalChain([c, 'access', _167 => _167.grade, 'optionalAccess', _168 => _168.lut, 'optionalAccess', _169 => _169.src])).filter((x) => !!x)))];
4519
4918
  const missing = (await Promise.all(lutSrcs.map((src) => controller.luts.load(src).then(() => null, (e) => e.message)))).filter((m) => !!m);
4520
4919
  const svgOptions = { lutFor: (src) => controller.luts.peek(src), ...columns ? { columns } : {} };
4521
4920
  const svg = times ? videoContactSheetSvg(p, times, svgOptions) : videoProjectSvg(p, _nullishCoalesce(time, () => ( controller.getState().time)), svgOptions);
@@ -4634,7 +5033,7 @@ function createVideoEditorToolProvider(controller, options = {}) {
4634
5033
  name: "update_video_asset",
4635
5034
  title: "Change media details",
4636
5035
  description: "Changes an asset's src, name, duration, size, sound or poster (e.g. relinking moved media). Clips that now run past its end are refused. Returns the asset.",
4637
- inputSchema: object({ asset: ASSET_ID, set: object(ASSET_FIELDS, [], "Fields to replace."), remove: { type: "array", items: { type: "string", enum: ["name", "duration", "width", "height", "hasAudio", "poster", "transcript"] }, description: "Fields to clear (transcript removes its words)." } }, ["asset"]),
5036
+ inputSchema: object({ asset: ASSET_ID, set: object(ASSET_FIELDS, [], "Fields to replace."), remove: { type: "array", items: { type: "string", enum: ["name", "duration", "width", "height", "hasAudio", "poster", "transcript", "proxies"] }, description: "Fields to clear (transcript removes its words, proxies its proxies)." } }, ["asset"]),
4638
5037
  annotations: { title: "Change media details", ...WRITE_IDEMPOTENT },
4639
5038
  handler: ({ asset, set, remove }) => apply({ op: "update_asset", asset, set, remove }, "Change media", (r) => ({ asset: videoAssetOut(r.project.assets.find((a) => a.id === asset)) }))
4640
5039
  };
@@ -4756,7 +5155,7 @@ function createVideoEditorToolProvider(controller, options = {}) {
4756
5155
  const at = findVideoClip(p, id);
4757
5156
  if (!at) throw new Error(`There is no clip "${id}"; get_video_project lists the clips.`);
4758
5157
  const { clip } = at;
4759
- if (at.track.kind !== "video" || clip.type === "media" && _optionalChain([p, 'access', _140 => _140.assets, 'access', _141 => _141.find, 'call', _142 => _142((a) => a.id === clip.asset), 'optionalAccess', _143 => _143.kind]) === "audio")
5158
+ if (at.track.kind !== "video" || clip.type === "media" && _optionalChain([p, 'access', _170 => _170.assets, 'access', _171 => _171.find, 'call', _172 => _172((a) => a.id === clip.asset), 'optionalAccess', _173 => _173.kind]) === "audio")
4760
5159
  throw new Error(`Clip "${id}" is sound only; grades change pictures. Pass clips on video tracks.`);
4761
5160
  }
4762
5161
  };
@@ -4779,7 +5178,7 @@ function createVideoEditorToolProvider(controller, options = {}) {
4779
5178
  const grade = videoGradeLook(findVideoClip(project(), clip).clip.grade, preset, _nullishCoalesce(amount, () => ( 1)));
4780
5179
  return grade ? { op: "update_clip", clip, set: { grade } } : { op: "update_clip", clip, remove: ["grade"] };
4781
5180
  });
4782
- return apply(edits, `Apply ${preset} look`, (r) => ({ clips: clips.map((id) => ({ id, grade: _nullishCoalesce(_optionalChain([findVideoClip, 'call', _144 => _144(r.project, id), 'optionalAccess', _145 => _145.clip, 'access', _146 => _146.grade]), () => ( null)) })) }));
5181
+ return apply(edits, `Apply ${preset} look`, (r) => ({ clips: clips.map((id) => ({ id, grade: _nullishCoalesce(_optionalChain([findVideoClip, 'call', _174 => _174(r.project, id), 'optionalAccess', _175 => _175.clip, 'access', _176 => _176.grade]), () => ( null)) })) }));
4783
5182
  }
4784
5183
  };
4785
5184
  const copyGrade = {
@@ -4815,7 +5214,7 @@ function createVideoEditorToolProvider(controller, options = {}) {
4815
5214
  const lut = await controller.luts.load(src);
4816
5215
  const label = _nullishCoalesce(name, () => ( lut.title));
4817
5216
  const setting = { src, ...strength !== void 0 && strength !== 1 ? { strength } : {}, ...label ? { name: label } : {} };
4818
- return apply(videoLutEdits(project(), clips, setting), "Apply LUT", (r) => ({ lut: { src, ...lut.title ? { title: lut.title } : {}, size: lut.size }, clips: clips.map((id) => ({ id, grade: _nullishCoalesce(_optionalChain([findVideoClip, 'call', _147 => _147(r.project, id), 'optionalAccess', _148 => _148.clip, 'access', _149 => _149.grade]), () => ( null)) })) }));
5217
+ return apply(videoLutEdits(project(), clips, setting), "Apply LUT", (r) => ({ lut: { src, ...lut.title ? { title: lut.title } : {}, size: lut.size }, clips: clips.map((id) => ({ id, grade: _nullishCoalesce(_optionalChain([findVideoClip, 'call', _177 => _177(r.project, id), 'optionalAccess', _178 => _178.clip, 'access', _179 => _179.grade]), () => ( null)) })) }));
4819
5218
  }
4820
5219
  };
4821
5220
  const removeLut = {
@@ -4827,9 +5226,82 @@ function createVideoEditorToolProvider(controller, options = {}) {
4827
5226
  handler: ({ clips }) => {
4828
5227
  gradeTargets(clips);
4829
5228
  const p = project();
4830
- const withLut = clips.filter((id) => _optionalChain([findVideoClip, 'call', _150 => _150(p, id), 'access', _151 => _151.clip, 'access', _152 => _152.grade, 'optionalAccess', _153 => _153.lut]));
5229
+ const withLut = clips.filter((id) => _optionalChain([findVideoClip, 'call', _180 => _180(p, id), 'access', _181 => _181.clip, 'access', _182 => _182.grade, 'optionalAccess', _183 => _183.lut]));
4831
5230
  if (!withLut.length) throw new Error(`None of ${clips.map((c) => `"${c}"`).join(", ")} has a LUT; get_video_project shows each clip's grade.`);
4832
- return apply(videoLutEdits(p, withLut, null), "Remove LUT", (r) => ({ clips: clips.map((id) => ({ id, grade: _nullishCoalesce(_optionalChain([findVideoClip, 'call', _154 => _154(r.project, id), 'optionalAccess', _155 => _155.clip, 'access', _156 => _156.grade]), () => ( null)) })) }));
5231
+ return apply(videoLutEdits(p, withLut, null), "Remove LUT", (r) => ({ clips: clips.map((id) => ({ id, grade: _nullishCoalesce(_optionalChain([findVideoClip, 'call', _184 => _184(r.project, id), 'optionalAccess', _185 => _185.clip, 'access', _186 => _186.grade]), () => ( null)) })) }));
5232
+ }
5233
+ };
5234
+ const PROXY_SCALE = { type: "number", exclusiveMinimum: 0, exclusiveMaximum: 1, description: "Proxy size as a fraction of the original: 0.5 (half width and height, the default) or 0.25 for very large footage." };
5235
+ const planProxies = {
5236
+ name: "plan_video_proxies",
5237
+ title: "Plan proxy files",
5238
+ description: 'Returns the ffmpeg commands that make smaller proxy copies of video assets for smooth preview playback, without changing anything: one per video asset (or each of "assets") that has no proxy at that scale yet, each with { asset, args, command, proxy }. Proxies are scaled, H.264 with a keyframe every few frames so scrubbing is quick, and keep the sound. After the host runs a command, attach its "proxy" with set_video_proxies. The export always uses the originals.',
5239
+ inputSchema: object({
5240
+ scale: PROXY_SCALE,
5241
+ assets: { type: "array", items: ASSET_ID, minItems: 1, uniqueItems: true, description: "Only these video assets (default: every video asset without a proxy at this scale)." },
5242
+ gop: { type: "integer", minimum: 1, maximum: 300, description: "Frames between keyframes (default 12): fewer makes seeking cheaper and files larger." },
5243
+ crf: { type: "integer", minimum: 0, maximum: 51, description: "x264 quality, lower is better (default 28: proxies are for editing)." }
5244
+ }),
5245
+ annotations: { title: "Plan proxy files", ...READ },
5246
+ handler: ({ scale, assets, gop, crf }) => {
5247
+ const commands = videoProxyPlan(project(), { scale, assets, gop, crf });
5248
+ return { scale: _nullishCoalesce(scale, () => ( 0.5)), commands, ...commands.length ? {} : { note: "Every video asset already has a proxy at this scale." } };
5249
+ }
5250
+ };
5251
+ const setProxies = {
5252
+ name: "set_video_proxies",
5253
+ title: "Attach proxies",
5254
+ description: "Sets a video asset's proxies, the smaller copies the preview plays instead of the original (one undo step); an empty list removes them. Each proxy must match the original's length and timing (plan_video_proxies makes such files). The export, EDL and source times always use the original. Returns the asset.",
5255
+ inputSchema: object(
5256
+ {
5257
+ asset: { ...ASSET_ID, description: "A video asset id from get_video_project." },
5258
+ proxies: {
5259
+ type: "array",
5260
+ maxItems: 8,
5261
+ description: "Its proxies, one per scale (empty removes them all).",
5262
+ items: object({ scale: PROXY_SCALE, src: { type: "string", minLength: 1, description: "Where the preview loads the proxy from (URL or path)." }, width: { type: "number", exclusiveMinimum: 0, description: "Its width in pixels." }, height: { type: "number", exclusiveMinimum: 0, description: "Its height in pixels." } }, ["scale", "src"], "One proxy.")
5263
+ }
5264
+ },
5265
+ ["asset", "proxies"]
5266
+ ),
5267
+ annotations: { title: "Attach proxies", ...WRITE_IDEMPOTENT },
5268
+ handler: ({ asset, proxies }) => {
5269
+ const a = project().assets.find((x) => x.id === asset);
5270
+ if (!a) throw new Error(`There is no asset "${asset}"; the assets are ${project().assets.map((x) => `"${x.id}"`).join(", ") || "none"}.`);
5271
+ if (a.kind !== "video") throw new Error(`Asset "${asset}" is ${a.kind === "image" ? "an image" : "audio"}; only video assets have proxies.`);
5272
+ const edit = proxies.length ? { op: "update_asset", asset, set: { proxies } } : { op: "update_asset", asset, remove: ["proxies"] };
5273
+ return apply(edit, proxies.length ? "Attach proxies" : "Remove proxies", (r) => ({ asset: videoAssetOut(r.project.assets.find((x) => x.id === asset)) }));
5274
+ }
5275
+ };
5276
+ const setPreviewScale = {
5277
+ name: "set_video_preview_scale",
5278
+ title: "Set the preview size",
5279
+ description: 'Sets how large a picture the preview plays, without changing the project (no undo step): "full" plays the originals, a fraction (0.5, 0.25) plays the smallest proxy at least that large (or the original where there is none), "auto" (the default) picks by the size the preview is shown at. Use a proxy scale when playback stutters on large footage. Returns the setting and, for each video asset, what the preview plays at a fixed scale.',
5280
+ inputSchema: object(
5281
+ { scale: { anyOf: [{ type: "string", enum: ["auto", "full"] }, { type: "number", exclusiveMinimum: 0, maximum: 1 }], description: '"auto", "full", or a fraction above 0 up to 1 (0.5 is half size).' } },
5282
+ ["scale"]
5283
+ ),
5284
+ annotations: { title: "Set the preview size", ...WRITE_IDEMPOTENT },
5285
+ handler: ({ scale }) => {
5286
+ controller.setPreviewScale(scale);
5287
+ const s = controller.getState().previewScale;
5288
+ const fixed = s === "full" ? 1 : typeof s === "number" ? s : null;
5289
+ const plays = fixed === null ? void 0 : project().assets.filter((a) => a.kind === "video").map((a) => ({ asset: a.id, src: _nullishCoalesce(_optionalChain([videoAssetProxy, 'call', _187 => _187(a, fixed), 'optionalAccess', _188 => _188.src]), () => ( a.src)), proxy: !!videoAssetProxy(a, fixed) }));
5290
+ return { previewScale: s, ...plays ? { plays } : { note: "auto picks the smallest proxy at least as large as the preview canvas shows, so it depends on the preview's size." } };
5291
+ }
5292
+ };
5293
+ const previewCache = {
5294
+ name: "get_video_preview_cache",
5295
+ title: "Read the preview's frame cache",
5296
+ description: `Reports the preview's cache of decoded frames, without changing anything: whether the host has one, how many frames and bytes it holds against its budget, hits and misses, frames still decoding, how far ahead it prefetches (ms), and the stretches of the timeline between "from" and "to" whose frames are all cached (these play and scrub without waiting). Use it to tell whether stuttering is the cache filling, and before asking the person to review a passage. With no cache, says so.`,
5297
+ inputSchema: object({ from: MS("Start of the stretch to report cached ranges for (default 0)"), to: MS("End of it (default: the end of the project)") }),
5298
+ annotations: { title: "Read the preview's frame cache", ...READ },
5299
+ handler: ({ from, to }) => {
5300
+ const cache = controller.frameCache;
5301
+ if (!cache) return { enabled: false, reason: "The host has not given this editor a frame cache (VideoEditorController frameCache), so the preview decodes each frame as it plays." };
5302
+ if (from !== void 0 && to !== void 0 && !(to > from)) throw new Error(`"to" (${to} ms) must be after "from" (${from} ms).`);
5303
+ const ranges = cache.cachedRanges(project(), { from, to });
5304
+ return { enabled: true, ...cache.stats(), cachedRanges: ranges };
4833
5305
  }
4834
5306
  };
4835
5307
  const TRANSCRIPT_TRACK = { ...TRACK_ID, description: "Track whose words to read (default: the first audio track with a transcribed clip, else the first video track with one)." };
@@ -4883,7 +5355,7 @@ function createVideoEditorToolProvider(controller, options = {}) {
4883
5355
  handler: ({ track, from, to, limit }) => {
4884
5356
  const all = timelineWords(track).filter((w) => w.end > (_nullishCoalesce(from, () => ( 0))) && w.start < (_nullishCoalesce(to, () => ( Infinity))));
4885
5357
  const words = all.slice(0, _nullishCoalesce(limit, () => ( 1e3)));
4886
- return { track: _nullishCoalesce(_optionalChain([words, 'access', _157 => _157[0], 'optionalAccess', _158 => _158.track]), () => ( videoTranscriptTrack(project(), track))), text: words.map((w) => w.word).join(" "), words: words.map((w) => ({ word: w.word, start: round(w.start), end: round(w.end), clip: w.clip })), more: all.length > words.length };
5358
+ return { track: _nullishCoalesce(_optionalChain([words, 'access', _189 => _189[0], 'optionalAccess', _190 => _190.track]), () => ( videoTranscriptTrack(project(), track))), text: words.map((w) => w.word).join(" "), words: words.map((w) => ({ word: w.word, start: round(w.start), end: round(w.end), clip: w.clip })), more: all.length > words.length };
4887
5359
  }
4888
5360
  };
4889
5361
  const findText = {
@@ -4945,7 +5417,7 @@ function createVideoEditorToolProvider(controller, options = {}) {
4945
5417
  if (!at) throw new Error(`There is no clip "${id}"; get_video_project lists the clips.`);
4946
5418
  const { clip } = at;
4947
5419
  if (clip.type !== "media") throw new Error(`Clip "${id}" is a ${clip.type === "text" ? "title" : "colour"}, so it has no source to change the speed of; use a video or audio clip.`);
4948
- if (_optionalChain([project, 'call', _159 => _159(), 'access', _160 => _160.assets, 'access', _161 => _161.find, 'call', _162 => _162((a) => a.id === clip.asset), 'optionalAccess', _163 => _163.kind]) === "image") throw new Error(`Clip "${id}" is a still, so it has no motion to change the speed of.`);
5420
+ if (_optionalChain([project, 'call', _191 => _191(), 'access', _192 => _192.assets, 'access', _193 => _193.find, 'call', _194 => _194((a) => a.id === clip.asset), 'optionalAccess', _195 => _195.kind]) === "image") throw new Error(`Clip "${id}" is a still, so it has no motion to change the speed of.`);
4949
5421
  return clip;
4950
5422
  };
4951
5423
  const rampOut = (r, id) => {
@@ -5273,8 +5745,8 @@ function createVideoEditorToolProvider(controller, options = {}) {
5273
5745
  return { time: t, timecode: formatVideoTimecode(t, p.fps), width: pixels.width, height: pixels.height, ...summarizeVideoScopes(pixels, { bins, columns }) };
5274
5746
  }
5275
5747
  };
5276
- const reads = [getProject, getFrame, getTranscript, findText, findFillers, findPauses, findGaps, checkProject, renderFrame, getScopes, exportFfmpeg, exportLoudnessMeasure, exportEdl, audioLevels, findSilences, seek];
5277
- const writes = [setSettings, addAsset, updateAsset, removeAsset, addTrack, updateTrack, removeTrack, moveTrack, addClip, moveClip, trimClip, splitClip, deleteClips, updateClip, setTransition, setSpeedRamp, addFreeze, removeRamp, setTranscript, removeRanges, applyGradePreset, copyGrade, setLut, removeLut, setKeyframes, removeKeyframes, slipClip, rollEdit, slideClip, liftRange, extractRange, splitAt, closeGaps, addMarker, removeMarker, editVideo, undo, redo];
5748
+ const reads = [getProject, getFrame, getTranscript, findText, findFillers, findPauses, findGaps, checkProject, renderFrame, getScopes, previewCache, planProxies, exportFfmpeg, exportLoudnessMeasure, exportEdl, audioLevels, findSilences, seek, setPreviewScale];
5749
+ const writes = [setSettings, addAsset, updateAsset, removeAsset, setProxies, addTrack, updateTrack, removeTrack, moveTrack, addClip, moveClip, trimClip, splitClip, deleteClips, updateClip, setTransition, setSpeedRamp, addFreeze, removeRamp, setTranscript, removeRanges, applyGradePreset, copyGrade, setLut, removeLut, setKeyframes, removeKeyframes, slipClip, rollEdit, slideClip, liftRange, extractRange, splitAt, closeGaps, addMarker, removeMarker, editVideo, undo, redo];
5278
5750
  return {
5279
5751
  id: _nullishCoalesce(options.id, () => ( "video")),
5280
5752
  namespace: options.namespace,
@@ -5494,5 +5966,25 @@ function createVideoEditorToolProvider(controller, options = {}) {
5494
5966
 
5495
5967
 
5496
5968
 
5497
- exports.VIDEO_BLEND_MODES = VIDEO_BLEND_MODES; exports.VIDEO_BLEND_MODE_LABELS = VIDEO_BLEND_MODE_LABELS; exports.VIDEO_CLIP_ANIMATION_DURATION = VIDEO_CLIP_ANIMATION_DURATION; exports.VIDEO_CLIP_ANIMATION_EFFECTS = VIDEO_CLIP_ANIMATION_EFFECTS; exports.VIDEO_CLIP_ANIMATION_LABELS = VIDEO_CLIP_ANIMATION_LABELS; exports.VIDEO_CLIP_TYPES = VIDEO_CLIP_TYPES; exports.VIDEO_DEFAULT_FILLERS = VIDEO_DEFAULT_FILLERS; exports.VIDEO_DEFAULT_STILL_DURATION = VIDEO_DEFAULT_STILL_DURATION; exports.VIDEO_DEFAULT_TITLE_DURATION = VIDEO_DEFAULT_TITLE_DURATION; exports.VIDEO_EASE_NAMES = VIDEO_EASE_NAMES; exports.VIDEO_EDIT_OPS = VIDEO_EDIT_OPS; exports.VIDEO_FADE_CURVES = VIDEO_FADE_CURVES; exports.VIDEO_FADE_CURVE_LABELS = VIDEO_FADE_CURVE_LABELS; exports.VIDEO_FITS = VIDEO_FITS; exports.VIDEO_GRADE_CONTROLS = VIDEO_GRADE_CONTROLS; exports.VIDEO_GRADE_PRESETS = VIDEO_GRADE_PRESETS; exports.VIDEO_GRADE_PRESET_NAMES = VIDEO_GRADE_PRESET_NAMES; exports.VIDEO_ISSUE_CODES = VIDEO_ISSUE_CODES; exports.VIDEO_KEYFRAME_LIMITS = VIDEO_KEYFRAME_LIMITS; exports.VIDEO_KEYFRAME_PROPERTIES = VIDEO_KEYFRAME_PROPERTIES; exports.VIDEO_LUT_MAX_SIZE = VIDEO_LUT_MAX_SIZE; exports.VIDEO_PEAK_BLOCK = VIDEO_PEAK_BLOCK; exports.VIDEO_PLACEMENT_MODES = VIDEO_PLACEMENT_MODES; exports.VIDEO_SCOPE_KINDS = VIDEO_SCOPE_KINDS; exports.VIDEO_TIME_EPSILON = VIDEO_TIME_EPSILON; exports.VIDEO_TRANSFORM_PROPERTIES = VIDEO_TRANSFORM_PROPERTIES; exports.VIDEO_TRANSITION_LABELS = VIDEO_TRANSITION_LABELS; exports.VIDEO_TRANSITION_TYPES = VIDEO_TRANSITION_TYPES; exports.VIDEO_VECTORSCOPE_TARGETS = VIDEO_VECTORSCOPE_TARGETS; exports.VIDEO_WIPE_DIRECTIONS = VIDEO_WIPE_DIRECTIONS; exports.VideoAudioPeaksCache = VideoAudioPeaksCache; exports.VideoEditorController = VideoEditorController; exports.VideoLutCache = VideoLutCache; exports.VideoPreviewEngine = VideoPreviewEngine; exports.VideoPreviewPixels = VideoPreviewPixels; exports.allVideoClipIds = allVideoClipIds; exports.applyVideoEdits = applyVideoEdits; exports.applyVideoGrade = applyVideoGrade; exports.applyVideoLut = applyVideoLut; exports.applyVideoLutToPixels = applyVideoLutToPixels; exports.buildVideoAudioPeaks = buildVideoAudioPeaks; exports.checkVideoProject = checkVideoProject; exports.checkVideoTimeRemap = checkVideoTimeRemap; exports.createVideoEditorToolProvider = createVideoEditorToolProvider; exports.createVideoProject = createVideoProject; exports.decodeVideoAudioPeaks = decodeVideoAudioPeaks; exports.describeVideoProject = describeVideoProject; exports.drawVideoFrame = drawVideoFrame; exports.ffmpegFilterValue = ffmpegFilterValue; exports.findVideoAudioSilences = findVideoAudioSilences; exports.findVideoClip = findVideoClip; exports.findVideoFillers = findVideoFillers; exports.findVideoGaps = findVideoGaps; exports.findVideoTranscriptPauses = findVideoTranscriptPauses; exports.findVideoTranscriptText = findVideoTranscriptText; exports.formatVideoSeconds = formatVideoSeconds; exports.formatVideoTimecode = formatVideoTimecode; exports.formatVideoTimecodeFrames = formatVideoTimecodeFrames; exports.isVideoDropFrameRate = isVideoDropFrameRate; exports.isVideoKeyframes = isVideoKeyframes; exports.mergeVideoRanges = mergeVideoRanges; exports.nextFreeVideoSlot = nextFreeVideoSlot; exports.normalizeVideoGrade = normalizeVideoGrade; exports.normalizeVideoGradeLut = normalizeVideoGradeLut; exports.normalizeVideoWord = normalizeVideoWord; exports.parseVideoAnimatable = parseVideoAnimatable; exports.parseVideoAsset = parseVideoAsset; exports.parseVideoClip = parseVideoClip; exports.parseVideoClipAnimation = parseVideoClipAnimation; exports.parseVideoCubeLut = parseVideoCubeLut; exports.parseVideoGrade = parseVideoGrade; exports.parseVideoGradeLut = parseVideoGradeLut; exports.parseVideoKeyframes = parseVideoKeyframes; exports.parseVideoLoudnessMeasurement = parseVideoLoudnessMeasurement; exports.parseVideoMarker = parseVideoMarker; exports.parseVideoProject = parseVideoProject; exports.parseVideoTimeRemap = parseVideoTimeRemap; exports.parseVideoTrack = parseVideoTrack; exports.parseVideoTranscript = parseVideoTranscript; exports.parseVideoTranscriptWords = parseVideoTranscriptWords; exports.parseVideoTransform = parseVideoTransform; exports.parseVideoTransition = parseVideoTransition; exports.placeVideoRect = placeVideoRect; exports.planVideoClipDrag = planVideoClipDrag; exports.probeVideoMedia = probeVideoMedia; exports.readVideoScopesPanel = readVideoScopesPanel; exports.removeVideoKey = removeVideoKey; exports.retimeVideoAnimatable = retimeVideoAnimatable; exports.setVideoKey = setVideoKey; exports.snapToVideoFrame = snapToVideoFrame; exports.snapVideoTime = snapVideoTime; exports.sortVideoKeys = sortVideoKeys; exports.sortedVideoClips = sortedVideoClips; exports.splitVideoAnimatable = splitVideoAnimatable; exports.summarizeVideoScopes = summarizeVideoScopes; exports.toVideoEdl = toVideoEdl; exports.toVideoFfmpeg = toVideoFfmpeg; exports.toVideoLoudnessMeasure = toVideoLoudnessMeasure; exports.videoAssetDropEdit = videoAssetDropEdit; exports.videoAssetHasSound = videoAssetHasSound; exports.videoAssetKindOf = videoAssetKindOf; exports.videoAssetMap = videoAssetMap; exports.videoAudioLevels = videoAudioLevels; exports.videoAudioPeakRange = videoAudioPeakRange; exports.videoAudioPeakSpans = videoAudioPeakSpans; exports.videoAudioPeaksDuration = videoAudioPeaksDuration; exports.videoChroma = videoChroma; exports.videoClipAfter = videoClipAfter; exports.videoClipAnimated = videoClipAnimated; exports.videoClipAnimationProperties = videoClipAnimationProperties; exports.videoClipAnimationSpans = videoClipAnimationSpans; exports.videoClipBefore = videoClipBefore; exports.videoClipEnd = videoClipEnd; exports.videoClipHandles = videoClipHandles; exports.videoClipKeyframes = videoClipKeyframes; exports.videoClipLabel = videoClipLabel; exports.videoClipOverhang = videoClipOverhang; exports.videoClipShowsWaveform = videoClipShowsWaveform; exports.videoClipSoundGain = videoClipSoundGain; exports.videoClipSourceRange = videoClipSourceRange; exports.videoClipSourceTime = videoClipSourceTime; exports.videoClipSpeed = videoClipSpeed; exports.videoClipSpeedAt = videoClipSpeedAt; exports.videoClipTint = videoClipTint; exports.videoClipWaveform = videoClipWaveform; exports.videoClipWaveformPath = videoClipWaveformPath; exports.videoClipsAt = videoClipsAt; exports.videoContactSheetSvg = videoContactSheetSvg; exports.videoDragLabel = videoDragLabel; exports.videoDropTracks = videoDropTracks; exports.videoEaseBezier = videoEaseBezier; exports.videoEaseProgress = videoEaseProgress; exports.videoExposureNotes = videoExposureNotes; exports.videoExposureStats = videoExposureStats; exports.videoFadeCurveGain = videoFadeCurveGain; exports.videoFrameAt = videoFrameAt; exports.videoFrameDuration = videoFrameDuration; exports.videoFrameToSvg = videoFrameToSvg; exports.videoFrames = videoFrames; exports.videoFreezeFrameEdit = videoFreezeFrameEdit; exports.videoGradeCssFilter = videoGradeCssFilter; exports.videoGradeFfmpegFilters = videoGradeFfmpegFilters; exports.videoGradeGainsFilterId = videoGradeGainsFilterId; exports.videoGradeLook = videoGradeLook; exports.videoGradePreset = videoGradePreset; exports.videoGradeStepMatrix = videoGradeStepMatrix; exports.videoGradeStepSvgValues = videoGradeStepSvgValues; exports.videoGradeSteps = videoGradeSteps; exports.videoGradeSvgDefs = videoGradeSvgDefs; exports.videoGradeSvgFilter = videoGradeSvgFilter; exports.videoHistogram = videoHistogram; exports.videoHistogramImage = videoHistogramImage; exports.videoHourFrames = videoHourFrames; exports.videoIrisRadius = videoIrisRadius; exports.videoIssueKey = videoIssueKey; exports.videoKeyframeBreakpoints = videoKeyframeBreakpoints; exports.videoKeyframeFfmpegExpr = videoKeyframeFfmpegExpr; exports.videoKeyframeRows = videoKeyframeRows; exports.videoKeyframeToggleEdit = videoKeyframeToggleEdit; exports.videoKeyframeValueEdit = videoKeyframeValueEdit; exports.videoKeysOf = videoKeysOf; exports.videoLuma = videoLuma; exports.videoLumaWaveform = videoLumaWaveform; exports.videoLutEdits = videoLutEdits; exports.videoLutFromFunction = videoLutFromFunction; exports.videoLutSvgApproximation = videoLutSvgApproximation; exports.videoLutSvgPrimitives = videoLutSvgPrimitives; exports.videoLutToCube = videoLutToCube; exports.videoOverlap = videoOverlap; exports.videoPeakDb = videoPeakDb; exports.videoProjectDuration = videoProjectDuration; exports.videoProjectSvg = videoProjectSvg; exports.videoRemoveRangesEdits = videoRemoveRangesEdits; exports.videoRulerTicks = videoRulerTicks; exports.videoScopeImage = videoScopeImage; exports.videoSlowMotionRamp = videoSlowMotionRamp; exports.videoSnapPoints = videoSnapPoints; exports.videoSpeedRampEdit = videoSpeedRampEdit; exports.videoSpeedRampKeys = videoSpeedRampKeys; exports.videoTextStyle = videoTextStyle; exports.videoTimeRemapBreakpoints = videoTimeRemapBreakpoints; exports.videoTimeRemapSpan = videoTimeRemapSpan; exports.videoTimecodeFrames = videoTimecodeFrames; exports.videoTimelineTranscript = videoTimelineTranscript; exports.videoTitleFrame = videoTitleFrame; exports.videoTrackSyncLocked = videoTrackSyncLocked; exports.videoTranscriptEdit = videoTranscriptEdit; exports.videoTranscriptTrack = videoTranscriptTrack; exports.videoTransformAt = videoTransformAt; exports.videoTransitionAudioCurve = videoTransitionAudioCurve; exports.videoTransitionEase = videoTransitionEase; exports.videoTransitionHasDirection = videoTransitionHasDirection; exports.videoValueAt = videoValueAt; exports.videoVectorscope = videoVectorscope; exports.videoVectorscopeAngle = videoVectorscopeAngle; exports.videoVectorscopeImage = videoVectorscopeImage; exports.videoVectorscopePoint = videoVectorscopePoint; exports.videoWaveformImage = videoWaveformImage; exports.videoWaveformPath = videoWaveformPath; exports.videoWipeRect = videoWipeRect; exports.videoWordAt = videoWordAt; exports.videoWordsRange = videoWordsRange;
5969
+
5970
+
5971
+
5972
+
5973
+
5974
+
5975
+
5976
+
5977
+
5978
+
5979
+
5980
+
5981
+
5982
+
5983
+
5984
+
5985
+
5986
+
5987
+
5988
+
5989
+ exports.VIDEO_BLEND_MODES = VIDEO_BLEND_MODES; exports.VIDEO_BLEND_MODE_LABELS = VIDEO_BLEND_MODE_LABELS; exports.VIDEO_CLIP_ANIMATION_DURATION = VIDEO_CLIP_ANIMATION_DURATION; exports.VIDEO_CLIP_ANIMATION_EFFECTS = VIDEO_CLIP_ANIMATION_EFFECTS; exports.VIDEO_CLIP_ANIMATION_LABELS = VIDEO_CLIP_ANIMATION_LABELS; exports.VIDEO_CLIP_TYPES = VIDEO_CLIP_TYPES; exports.VIDEO_DEFAULT_FILLERS = VIDEO_DEFAULT_FILLERS; exports.VIDEO_DEFAULT_STILL_DURATION = VIDEO_DEFAULT_STILL_DURATION; exports.VIDEO_DEFAULT_TITLE_DURATION = VIDEO_DEFAULT_TITLE_DURATION; exports.VIDEO_EASE_NAMES = VIDEO_EASE_NAMES; exports.VIDEO_EDIT_OPS = VIDEO_EDIT_OPS; exports.VIDEO_FADE_CURVES = VIDEO_FADE_CURVES; exports.VIDEO_FADE_CURVE_LABELS = VIDEO_FADE_CURVE_LABELS; exports.VIDEO_FITS = VIDEO_FITS; exports.VIDEO_GRADE_CONTROLS = VIDEO_GRADE_CONTROLS; exports.VIDEO_GRADE_PRESETS = VIDEO_GRADE_PRESETS; exports.VIDEO_GRADE_PRESET_NAMES = VIDEO_GRADE_PRESET_NAMES; exports.VIDEO_ISSUE_CODES = VIDEO_ISSUE_CODES; exports.VIDEO_KEYFRAME_LIMITS = VIDEO_KEYFRAME_LIMITS; exports.VIDEO_KEYFRAME_PROPERTIES = VIDEO_KEYFRAME_PROPERTIES; exports.VIDEO_LUT_MAX_SIZE = VIDEO_LUT_MAX_SIZE; exports.VIDEO_PEAK_BLOCK = VIDEO_PEAK_BLOCK; exports.VIDEO_PLACEMENT_MODES = VIDEO_PLACEMENT_MODES; exports.VIDEO_PREVIEW_SCALE_OPTIONS = VIDEO_PREVIEW_SCALE_OPTIONS; exports.VIDEO_PROXY_SCALES = VIDEO_PROXY_SCALES; exports.VIDEO_SCOPE_KINDS = VIDEO_SCOPE_KINDS; exports.VIDEO_TIME_EPSILON = VIDEO_TIME_EPSILON; exports.VIDEO_TRANSFORM_PROPERTIES = VIDEO_TRANSFORM_PROPERTIES; exports.VIDEO_TRANSITION_LABELS = VIDEO_TRANSITION_LABELS; exports.VIDEO_TRANSITION_TYPES = VIDEO_TRANSITION_TYPES; exports.VIDEO_VECTORSCOPE_TARGETS = VIDEO_VECTORSCOPE_TARGETS; exports.VIDEO_WIPE_DIRECTIONS = VIDEO_WIPE_DIRECTIONS; exports.VideoAudioPeaksCache = VideoAudioPeaksCache; exports.VideoEditorController = VideoEditorController; exports.VideoFrameCache = VideoFrameCache; exports.VideoLutCache = VideoLutCache; exports.VideoPreviewEngine = VideoPreviewEngine; exports.VideoPreviewPixels = VideoPreviewPixels; exports.allVideoClipIds = allVideoClipIds; exports.applyVideoEdits = applyVideoEdits; exports.applyVideoGrade = applyVideoGrade; exports.applyVideoLut = applyVideoLut; exports.applyVideoLutToPixels = applyVideoLutToPixels; exports.buildVideoAudioPeaks = buildVideoAudioPeaks; exports.checkVideoProject = checkVideoProject; exports.checkVideoTimeRemap = checkVideoTimeRemap; exports.createVideoEditorToolProvider = createVideoEditorToolProvider; exports.createVideoElementDecoder = createVideoElementDecoder; exports.createVideoProject = createVideoProject; exports.decodeVideoAudioPeaks = decodeVideoAudioPeaks; exports.describeVideoProject = describeVideoProject; exports.drawVideoFrame = drawVideoFrame; exports.ffmpegFilterValue = ffmpegFilterValue; exports.findVideoAudioSilences = findVideoAudioSilences; exports.findVideoClip = findVideoClip; exports.findVideoFillers = findVideoFillers; exports.findVideoGaps = findVideoGaps; exports.findVideoTranscriptPauses = findVideoTranscriptPauses; exports.findVideoTranscriptText = findVideoTranscriptText; exports.formatVideoSeconds = formatVideoSeconds; exports.formatVideoTimecode = formatVideoTimecode; exports.formatVideoTimecodeFrames = formatVideoTimecodeFrames; exports.isVideoDropFrameRate = isVideoDropFrameRate; exports.isVideoKeyframes = isVideoKeyframes; exports.mergeVideoRanges = mergeVideoRanges; exports.nextFreeVideoSlot = nextFreeVideoSlot; exports.normalizeVideoGrade = normalizeVideoGrade; exports.normalizeVideoGradeLut = normalizeVideoGradeLut; exports.normalizeVideoWord = normalizeVideoWord; exports.parseVideoAnimatable = parseVideoAnimatable; exports.parseVideoAsset = parseVideoAsset; exports.parseVideoAssetProxies = parseVideoAssetProxies; exports.parseVideoClip = parseVideoClip; exports.parseVideoClipAnimation = parseVideoClipAnimation; exports.parseVideoCubeLut = parseVideoCubeLut; exports.parseVideoGrade = parseVideoGrade; exports.parseVideoGradeLut = parseVideoGradeLut; exports.parseVideoKeyframes = parseVideoKeyframes; exports.parseVideoLoudnessMeasurement = parseVideoLoudnessMeasurement; exports.parseVideoMarker = parseVideoMarker; exports.parseVideoPreviewScale = parseVideoPreviewScale; exports.parseVideoProject = parseVideoProject; exports.parseVideoTimeRemap = parseVideoTimeRemap; exports.parseVideoTrack = parseVideoTrack; exports.parseVideoTranscript = parseVideoTranscript; exports.parseVideoTranscriptWords = parseVideoTranscriptWords; exports.parseVideoTransform = parseVideoTransform; exports.parseVideoTransition = parseVideoTransition; exports.placeVideoRect = placeVideoRect; exports.planVideoClipDrag = planVideoClipDrag; exports.planVideoPrefetch = planVideoPrefetch; exports.probeVideoMedia = probeVideoMedia; exports.readVideoScopesPanel = readVideoScopesPanel; exports.removeVideoKey = removeVideoKey; exports.resolveVideoPreviewScale = resolveVideoPreviewScale; exports.retimeVideoAnimatable = retimeVideoAnimatable; exports.setVideoKey = setVideoKey; exports.snapToVideoFrame = snapToVideoFrame; exports.snapVideoTime = snapVideoTime; exports.sortVideoKeys = sortVideoKeys; exports.sortedVideoClips = sortedVideoClips; exports.splitVideoAnimatable = splitVideoAnimatable; exports.summarizeVideoScopes = summarizeVideoScopes; exports.toVideoEdl = toVideoEdl; exports.toVideoFfmpeg = toVideoFfmpeg; exports.toVideoLoudnessMeasure = toVideoLoudnessMeasure; exports.videoAssetDropEdit = videoAssetDropEdit; exports.videoAssetHasSound = videoAssetHasSound; exports.videoAssetKindOf = videoAssetKindOf; exports.videoAssetMap = videoAssetMap; exports.videoAssetProxy = videoAssetProxy; exports.videoAudioLevels = videoAudioLevels; exports.videoAudioPeakRange = videoAudioPeakRange; exports.videoAudioPeakSpans = videoAudioPeakSpans; exports.videoAudioPeaksDuration = videoAudioPeaksDuration; exports.videoCacheEvictions = videoCacheEvictions; exports.videoChroma = videoChroma; exports.videoClipAfter = videoClipAfter; exports.videoClipAnimated = videoClipAnimated; exports.videoClipAnimationProperties = videoClipAnimationProperties; exports.videoClipAnimationSpans = videoClipAnimationSpans; exports.videoClipBefore = videoClipBefore; exports.videoClipEnd = videoClipEnd; exports.videoClipHandles = videoClipHandles; exports.videoClipKeyframes = videoClipKeyframes; exports.videoClipLabel = videoClipLabel; exports.videoClipOverhang = videoClipOverhang; exports.videoClipShowsWaveform = videoClipShowsWaveform; exports.videoClipSoundGain = videoClipSoundGain; exports.videoClipSourceRange = videoClipSourceRange; exports.videoClipSourceTime = videoClipSourceTime; exports.videoClipSpeed = videoClipSpeed; exports.videoClipSpeedAt = videoClipSpeedAt; exports.videoClipTint = videoClipTint; exports.videoClipWaveform = videoClipWaveform; exports.videoClipWaveformPath = videoClipWaveformPath; exports.videoClipsAt = videoClipsAt; exports.videoContactSheetSvg = videoContactSheetSvg; exports.videoDragLabel = videoDragLabel; exports.videoDropTracks = videoDropTracks; exports.videoEaseBezier = videoEaseBezier; exports.videoEaseProgress = videoEaseProgress; exports.videoExposureNotes = videoExposureNotes; exports.videoExposureStats = videoExposureStats; exports.videoFadeCurveGain = videoFadeCurveGain; exports.videoFrameAt = videoFrameAt; exports.videoFrameCacheKey = videoFrameCacheKey; exports.videoFrameDuration = videoFrameDuration; exports.videoFrameRequestsAt = videoFrameRequestsAt; exports.videoFrameToSvg = videoFrameToSvg; exports.videoFrames = videoFrames; exports.videoFreezeFrameEdit = videoFreezeFrameEdit; exports.videoGradeCssFilter = videoGradeCssFilter; exports.videoGradeFfmpegFilters = videoGradeFfmpegFilters; exports.videoGradeGainsFilterId = videoGradeGainsFilterId; exports.videoGradeLook = videoGradeLook; exports.videoGradePreset = videoGradePreset; exports.videoGradeStepMatrix = videoGradeStepMatrix; exports.videoGradeStepSvgValues = videoGradeStepSvgValues; exports.videoGradeSteps = videoGradeSteps; exports.videoGradeSvgDefs = videoGradeSvgDefs; exports.videoGradeSvgFilter = videoGradeSvgFilter; exports.videoHistogram = videoHistogram; exports.videoHistogramImage = videoHistogramImage; exports.videoHourFrames = videoHourFrames; exports.videoIrisRadius = videoIrisRadius; exports.videoIssueKey = videoIssueKey; exports.videoKeyframeBreakpoints = videoKeyframeBreakpoints; exports.videoKeyframeFfmpegExpr = videoKeyframeFfmpegExpr; exports.videoKeyframeRows = videoKeyframeRows; exports.videoKeyframeToggleEdit = videoKeyframeToggleEdit; exports.videoKeyframeValueEdit = videoKeyframeValueEdit; exports.videoKeysOf = videoKeysOf; exports.videoLuma = videoLuma; exports.videoLumaWaveform = videoLumaWaveform; exports.videoLutEdits = videoLutEdits; exports.videoLutFromFunction = videoLutFromFunction; exports.videoLutSvgApproximation = videoLutSvgApproximation; exports.videoLutSvgPrimitives = videoLutSvgPrimitives; exports.videoLutToCube = videoLutToCube; exports.videoOverlap = videoOverlap; exports.videoPeakDb = videoPeakDb; exports.videoPreviewSrc = videoPreviewSrc; exports.videoProjectDuration = videoProjectDuration; exports.videoProjectHasProxies = videoProjectHasProxies; exports.videoProjectSvg = videoProjectSvg; exports.videoProxyFfmpeg = videoProxyFfmpeg; exports.videoProxyName = videoProxyName; exports.videoProxyPlan = videoProxyPlan; exports.videoRemoveRangesEdits = videoRemoveRangesEdits; exports.videoRulerTicks = videoRulerTicks; exports.videoScopeImage = videoScopeImage; exports.videoShellQuote = videoShellQuote; exports.videoSlowMotionRamp = videoSlowMotionRamp; exports.videoSnapPoints = videoSnapPoints; exports.videoSourceFrame = videoSourceFrame; exports.videoSourceFrameTime = videoSourceFrameTime; exports.videoSpeedRampEdit = videoSpeedRampEdit; exports.videoSpeedRampKeys = videoSpeedRampKeys; exports.videoTextStyle = videoTextStyle; exports.videoTimeRemapBreakpoints = videoTimeRemapBreakpoints; exports.videoTimeRemapSpan = videoTimeRemapSpan; exports.videoTimecodeFrames = videoTimecodeFrames; exports.videoTimelineTranscript = videoTimelineTranscript; exports.videoTitleFrame = videoTitleFrame; exports.videoTrackSyncLocked = videoTrackSyncLocked; exports.videoTranscriptEdit = videoTranscriptEdit; exports.videoTranscriptTrack = videoTranscriptTrack; exports.videoTransformAt = videoTransformAt; exports.videoTransitionAudioCurve = videoTransitionAudioCurve; exports.videoTransitionEase = videoTransitionEase; exports.videoTransitionHasDirection = videoTransitionHasDirection; exports.videoValueAt = videoValueAt; exports.videoVectorscope = videoVectorscope; exports.videoVectorscopeAngle = videoVectorscopeAngle; exports.videoVectorscopeImage = videoVectorscopeImage; exports.videoVectorscopePoint = videoVectorscopePoint; exports.videoWaveformImage = videoWaveformImage; exports.videoWaveformPath = videoWaveformPath; exports.videoWipeRect = videoWipeRect; exports.videoWordAt = videoWordAt; exports.videoWordsRange = videoWordsRange;
5498
5990
  //# sourceMappingURL=index.cjs.map