UNPKG

@diffusionstudio/core-v4

Version:

2D motion graphics and video rendering engine

1,703 lines (1,700 loc) 159 kB
/*! * @diffusionstudio/core-v4 v0.0.16 * (c) 2025 Diffusion Studio Inc. * * Licensed under the Diffusion Studio Non-Commercial License. * You may NOT use this software for any commercial purposes. * * For commercial licensing, visit: * https://diffusion.studio */ import { Input as Ft, BufferSource as ke, ALL_FORMATS as Et, UrlSource as ce, BlobSource as Mt, AudioSampleSink as Ae, CanvasSink as Fe, AudioBufferSink as Ee, VideoSampleSink as Me, BufferTarget as Ht, StreamTarget as St, CustomAudioEncoder as Te, EncodedPacket as Pe, WebMOutputFormat as De, OggOutputFormat as Ie, Mp4OutputFormat as Ne, canEncode as Xt, registerEncoder as Re, AudioSample as Le, AudioSampleSource as Ue, CanvasSource as Be, Output as ze, getEncodableAudioCodecs as $e, getEncodableVideoCodecs as je } from "./mediabunny.es.js"; class Rt { data; format; numberOfChannels; numberOfFrames; sampleRate; timestamp; duration; constructor(t) { this.data = t.data, this.format = t.format, this.numberOfChannels = t.numberOfChannels, this.numberOfFrames = t.numberOfFrames, this.sampleRate = t.sampleRate, this.timestamp = t.timestamp || 0, this.duration = this.numberOfFrames / this.sampleRate * 1e6; } copyTo(t, s) { if (!s?.format || s.format !== "s16") throw new Error("Only s16 format is supported for copyTo"); for (let i = 0; i < this.numberOfFrames; i++) for (let r = 0; r < this.numberOfChannels; r++) { const n = r * this.numberOfFrames + i, o = i * this.numberOfChannels + r, a = Math.max(-1, Math.min(1, this.data[n])); t[o] = Math.round(a * 32767); } } clone() { return new Rt({ data: new Float32Array(this.data), format: this.format, numberOfChannels: this.numberOfChannels, numberOfFrames: this.numberOfFrames, sampleRate: this.sampleRate, timestamp: this.timestamp }); } close() { this.data = new Float32Array(0); } } "AudioData" in window || Object.assign(window, { AudioData: Rt }); const Ni = 0.2, tt = 30, We = 1; class et extends Error { message; code; constructor({ message: t = "", code: s = "" }, ...i) { super(t, ...i), console.error(t), this.code = s, this.message = t; } } class W extends et { } class S extends et { } class ut extends et { } class Ri extends et { } class wt extends et { } function He(e, t = tt) { if (t < 1) throw new S({ code: "invalidArgument", message: "FPS must be greater or equal to 1" }); return Math.round(e * t); } function Li(e, t = tt) { if (t < 1) throw new S({ code: "invalidArgument", message: "FPS must be greater or equal to 1" }); return Math.round(e / t * 1e3) / 1e3; } function Ot(e, t = tt) { if (t < 1) throw new S({ code: "invalidArgument", message: "FPS must be greater or equal to 1" }); return Math.round(e / t * 1e3); } function Ui(e) { return Math.floor(e * 255).toString(16).padStart(2, "0").toUpperCase(); } function Bi(e, t) { return e.reduce( (s, i) => { const r = i[t]; return s[r] || (s[r] = []), s[r].push(i), s; }, // @ts-ignore {} ); } function ft(e, t) { return [e.slice(0, t), e.slice(t)].filter((s) => s.length > 0); } function ct(e, t) { return t ? Math.floor(Math.random() * (t - e + 1) + e) : e; } async function zi(e) { e <= 0 || await new Promise((t) => setTimeout(t, e)); } function y(e, t) { if (!e) throw new Error(t || `Assertion failed: ${String(e)}`); } function $i(e, t = 300) { let s; return (...i) => { clearTimeout(s), s = setTimeout(() => { e.apply(e, i); }, t); }; } function Xe(e, t, s) { s < 0 && (s = 0); const i = e[t]; e.splice(t, 1), e.splice(s, 0, i); } function ji() { return typeof crypto < "u" ? crypto.randomUUID().split("-").at(0) : Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15); } function Wi(e) { return e.charAt(0).toUpperCase() + e.slice(1); } function Je(e, t, s) { return Math.max(t, Math.min(s, e)); } function Hi(e) { throw new Error("This should not run!"); } function Ye(e) { if (e.numberOfChannels === 1) return e.getChannelData(0); const t = []; for (let o = 0; o < e.numberOfChannels; o++) t.push(e.getChannelData(o)); const s = Math.max(...t.map((o) => o.length)), i = new Float32Array(s * e.numberOfChannels); let r = 0, n = 0; for (; n < s; ) t.forEach((o) => { i[r++] = o[n] !== void 0 ? o[n] : 0; }), n++; return i; } function at(e, t, s) { for (let i = 0; i < s.length; i++) e.setUint8(t + i, s.charCodeAt(i)); } function Ve(e, t, s) { for (let i = 0; i < t.length; i++, s += 2) { const r = Math.max(-1, Math.min(1, t[i])); e.setInt16(s, r < 0 ? r * 32768 : r * 32767, !0); } return e; } function qe(e, t, s) { const n = t * 2, o = 8, a = 36, h = e.length * 2, l = a + h, u = new ArrayBuffer(o + l), d = new DataView(u); return at(d, 0, "RIFF"), d.setUint32(4, l, !0), at(d, 8, "WAVE"), at(d, 12, "fmt "), d.setUint32(16, 16, !0), d.setUint16(20, 1, !0), d.setUint16(22, t, !0), d.setUint32(24, s, !0), d.setUint32(28, s * n, !0), d.setUint16(32, n, !0), d.setUint16(34, 16, !0), at(d, 36, "data"), d.setUint32(40, h, !0), Ve(d, e, o + a); } function Xi(e, t = "audio/wav") { const s = Ye(e), i = qe(s, e.numberOfChannels, e.sampleRate); return new Blob([i], { type: t }); } function Ji(e) { const t = new Float32Array(e.length * e.numberOfChannels); let s = 0; for (let i = 0; i < e.numberOfChannels; i++) { const r = e.getChannelData(i); t.set(r, s), s += r.length; } return t; } function Yi(e) { const t = e.numberOfChannels, s = e.length, i = new Int16Array(s * t); for (let r = 0; r < s; r++) for (let n = 0; n < t; n++) { let o = e.getChannelData(n)[r] * 32767; o > 32767 && (o = 32767), o < -32767 && (o = -32767), i[r * t + n] = o; } return i; } async function Vi(e, t = 22050, s = Math.sqrt(2)) { const i = await e.arrayBuffer(), r = new OfflineAudioContext({ sampleRate: t, length: 1 }), n = await r.decodeAudioData(i), o = r.createBuffer(1, n.length, t); if (n.numberOfChannels >= 2) { const a = n.getChannelData(0), h = n.getChannelData(1), l = o.getChannelData(0); for (let u = 0; u < n.length; ++u) l[u] = s * (a[u] + h[u]) / 2; return o; } return n; } function qi(e, t = 44100, s = 2) { if (e.sampleRate == t && e.numberOfChannels == s) return e; const i = Math.floor(e.duration * t), n = new OfflineAudioContext(s, 1, t).createBuffer(s, i, t); for (let o = 0; o < e.numberOfChannels; o++) { const a = e.getChannelData(o), h = n.getChannelData(o), l = e.sampleRate / t; for (let u = 0; u < h.length; u++) { const d = u * l, w = Math.floor(d), f = Math.ceil(d); if (f >= a.length) h[u] = a[w]; else { const g = d - w; h[u] = a[w] * (1 - g) + a[f] * g; } } } return n; } async function Ge(e, t) { const s = document.createElement("a"); if (document.head.appendChild(s), s.target = "_blank", t ? s.download = t : e instanceof File ? s.download = e.name : e instanceof Blob ? s.download = "untitled" : typeof e == "string" && (s.download = e.split("/").pop() ?? "untitled"), typeof e == "string" && e.startsWith("data:image/svg+xml;base64,")) { const i = e.split(",")[1], r = atob(i), n = new Array(r.length); for (let h = 0; h < r.length; h++) n[h] = r.charCodeAt(h); const o = new Uint8Array(n), a = new Blob([o], { type: "image/svg+xml" }); s.href = URL.createObjectURL(a), s.download = t?.split(".")[0] + ".svg"; } else typeof e == "string" ? s.href = e : e instanceof Blob ? s.href = URL.createObjectURL(e) : s.href = URL.createObjectURL(await e.getFile()); s.click(), s.remove(); } async function Gi(e, t = !0) { return new Promise((s) => { const i = document.createElement("input"); i.type = "file", i.accept = e, i.multiple = t, i.onchange = (r) => { const n = Array.from(r.target?.files ?? []); s(n); }, i.click(); }); } function m(e) { if (!e) return new p(0); if (typeof e == "number") return p.fromFrames(e); if (e instanceof p) return e; const t = e.split(":"); if (t.length === 3) { const [s, i, r] = t.map(Number); if (isNaN(s) || isNaN(i) || isNaN(r)) throw new Error(`Invalid time format: ${e}`); return new p(0, r, i, s); } if (t.length === 2) { const [s, i] = t.map(Number); if (isNaN(s) || isNaN(i)) throw new Error(`Invalid time format: ${e}`); return new p(0, i, s); } if (typeof e == "string") { const s = parseFloat(e); if (isNaN(s)) throw new Error(`Invalid time format: ${e}`); if (e.endsWith("ms")) return new p(s); if (e.endsWith("s")) return new p(0, s); if (e.endsWith("f")) return p.fromFrames(s); if (e.endsWith("min")) return new p(0, 0, s); throw new Error(`Invalid time format: ${e}`); } throw new Error(`Invalid time format: ${e}`); } async function Ki(e) { const { fps: t, height: s, width: i, bitrate: r } = e, n = [ "avc1.640033", "avc1.4d4033", "avc1.424033", "avc1.640029", "avc1.4d4029", "avc1.424029" ], o = ["prefer-hardware", "prefer-software"], a = []; for (const l of n) for (const u of o) a.push({ codec: l, hardwareAcceleration: u, width: i, height: s, bitrate: r, framerate: t }); const h = []; if ("VideoEncoder" in window) { for (const l of a) { const u = await VideoEncoder.isConfigSupported(l); u.supported && h.push(u.config ?? l); } return h.sort(Ke)[0].codec; } } function Ke(e, t) { const s = e.hardwareAcceleration ?? "", i = t.hardwareAcceleration ?? ""; return s < i ? -1 : s > i ? 1 : 0; } const bt = { fromJSON: (e) => new Date(e) }; class p { /** * Time state in **milliseconds** */ time; /** * Create a new timestamp from various time units * @param millis - Time in milliseconds * @param seconds - Time in seconds * @param minutes - Time in minutes * @param hours - Time in hours */ constructor(t = 0, s = 0, i = 0, r = 0) { this.time = Math.round(t + s * 1e3 + i * 6e4 + r * 36e5); } /** * Base unit of the timestamp */ get millis() { return this.time; } set millis(t) { this.time = Math.round(t); } /** * Defines the time in frames at the * current frame rate */ get frames() { return He(this.millis / 1e3); } set frames(t) { this.millis = Ot(t); } /** * Convert the timestamp to seconds */ get seconds() { return this.millis / 1e3; } set seconds(t) { this.millis = t * 1e3; } /** * Equivalent to millis += x */ addMillis(t) { return this.millis = this.millis + t, this; } /** * Equivalent to frames += x */ addFrames(t) { const s = Ot(t); return this.millis = this.millis + s, this; } /** * add two timestamps the timestamp being added will adapt * its fps to the current fps * @returns A new Timestamp instance with the added frames */ add(t) { return new p(t.millis + this.millis); } /** * subtract two timestamps timestamp being subtracted * will adapt its fps to the current fps * @returns A new Timestamp instance with the added frames */ subtract(t) { return new p(this.millis - t.millis); } /** * Create a new timestamp from frames */ static fromFrames(t, s) { const i = new p(); return i.millis = Ot(t, s), i; } /** * get a copy of the object */ copy() { return new p(this.millis); } toJSON() { return `${this.seconds}s`; } fromJSON(t) { y(typeof t == "string"); const [s] = t.split("s"); return this.millis = Number(s) * 1e3, this; } static fromJSON(t) { y(typeof t == "string"); const [s] = t.split("s"); return new p(0, Number(s)); } } function Jt(e) { return `${e.hours.toString().padStart(2, "0")}:${e.minutes.toString().padStart(2, "0")}:${e.seconds.toString().padStart(2, "0")},` + e.milliseconds.toString().padStart(3, "0"); } function Yt(e) { const t = new Date(1970, 0, 1); return t.setSeconds(e), t.setMilliseconds(Math.round(e % 1 * 1e3)), { hours: t.getHours(), minutes: t.getMinutes(), seconds: t.getSeconds(), milliseconds: t.getMilliseconds() }; } class Y { words = []; constructor(t) { t && (this.words = t); } get duration() { return this.stop.subtract(this.start); } get text() { return this.words.map(({ text: t }) => t).join(" "); } get start() { return this.words.at(0)?.start ?? new p(); } get stop() { return this.words.at(-1)?.stop ?? new p(); } } var Tt = /* @__PURE__ */ ((e) => (e.en = "en", e.de = "de", e))(Tt || {}); class dt { /** * A unique identifier for the word */ id = crypto.randomUUID(); /** * Defines the text to be displayed */ text; /** * Defines the time stamp at * which the text is spoken */ start; /** * Defines the time stamp at * which the text was spoken */ stop; /** * Defines the confidence of * the predicition */ confidence; /** * Create a new Word object * @param text The string contents of the word * @param start Start in **milliseconds** * @param stop Stop in **milliseconds** * @param confidence Predicition confidence */ constructor(t, s, i, r) { this.text = t, this.start = new p(s), this.stop = new p(i), this.confidence = r; } /** * Defines the time between start * and stop returned as a timestamp */ get duration() { return this.stop.subtract(this.start); } } class F { id = crypto.randomUUID(); language = Tt.en; groups = []; get text() { return this.groups.map(({ text: t }) => t).join(" "); } get words() { return this.groups.flatMap(({ words: t }) => t); } constructor(t = [], s = Tt.en) { this.groups = t, this.language = s; } /** * Iterate over all words in groups */ *iter({ count: t, duration: s, length: i }) { for (const r of this.groups) { let n; for (const [o, a] of r.words.entries()) n && (t && n.words.length >= ct(...t) ? (yield n, n = void 0) : s && n?.duration.seconds >= ct(...s) ? (yield n, n = void 0) : i && n.text.length >= ct(...i) && (yield n, n = void 0)), n ? n.words.push(a) : n = new Y([a]), o == r.words.length - 1 && (yield n); } } /** * This method will optimize the transcipt for display */ optimize() { const t = this.groups.flatMap((s) => s.words); for (let s = 0; s < t.length - 1; s++) { const i = t[s], r = t[s + 1]; r.start.millis - i.stop.millis < 0 ? r.start.millis = i.stop.millis + 1 : i.stop.millis = r.start.millis - 1; } return this; } /** * Convert the transcript into a SRT compatible * string and downloadable blob */ toSRT(t = {}) { let s = 1, i = ""; for (const r of this.iter(t)) { const n = Yt(r.start.seconds), o = Yt(r.stop.seconds); i += `${s} ` + Jt(n) + " --> " + Jt(o) + ` ${r.text} `, s += 1; } return { text: i, blob: new Blob([i], { type: "text/plain;charset=utf8" }) }; } toJSON() { return this.groups.map( (t) => t.words.map((s) => ({ token: s.text, start: s.start.millis, stop: s.stop.millis })) ); } /** * Create a new Transcript containing the * first `{count}` words * @param count Defines the number of words required * @param startAtZero Defines if the first word should start at 0 milliseconds * @returns A new Transcript instance */ slice(t, s = !0) { let i = 0; const r = []; for (const n of this.groups) for (const o of n.words) if (r.length == 0 && s && (i = o.start.millis), r.push(new dt(o.text, o.start.millis - i, o.stop.millis - i)), r.length == t) return new F([new Y(r)]); return new F([new Y(r)]); } /** * Create a deep copy of the transcript * @returns A new Transcript instance */ copy() { return F.fromJSON(this.toJSON()); } static fromJSON(t) { const s = new F(); for (const i of t) { const r = new Y(); for (const n of i) r.words.push(new dt(n.token, n.start, n.stop)); s.groups.push(r); } return s; } /** * Create a Transcript from an input medium of the form: * `{ token: string; start: number; stop: number; }[][]` * @param input The input medium, can be a URL, Blob, or an array of captions * @returns A Transcript with processed captions */ static async from(t) { if (Array.isArray(t)) return F.fromJSON(t); if (t instanceof FileSystemFileHandle) { const i = await t.getFile(); return F.fromJSON(JSON.parse(await i.text())); } if (t instanceof Blob) return F.fromJSON(JSON.parse(await t.text())); const s = await fetch(t); if (!s.ok) throw new W({ code: "unexpectedIOError", message: "An unexpected error occurred while fetching the file" }); return F.fromJSON(await s.json()); } fromJSON(t) { y(typeof t == "object"), y(Array.isArray(t)), this.groups = []; for (const s of t) { const i = new Y(); for (const r of s) i.words.push(new dt(r.token, r.start, r.stop)); this.groups.push(i); } return this; } } class B { toJSON(t) { const s = {}, i = this.constructor.__serializableProperties || [], r = this.constructor.__displayName; r && (s.displayName = r); const n = (o) => { if (o == null || o instanceof Blob || o instanceof FileSystemFileHandle) return o; if (Array.isArray(o)) return o.map((a) => n(a)).filter((a) => a !== void 0); if (typeof o == "object" && "toJSON" in o) return o.toJSON(); if (typeof o == "object") { const a = {}; for (const h in o) { const l = n(o[h]); l !== void 0 && (a[h] = l); } return a; } return o; }; return i.forEach(({ propertyKey: o, mapTo: a }) => { if (t?.includes(o)) return; const h = this[o], l = a ?? o, u = n(h); u !== void 0 && (s[l] = u); }), s; } fromJSON(t) { return (this.constructor.__serializableProperties || []).forEach(({ propertyKey: i, deserializer: r, mapTo: n }) => { const o = n ?? i, a = r.fromJSON?.bind(r) ?? ((h) => h); if (t.hasOwnProperty(o)) try { this[i] = a(t[o]); } catch (h) { console.error(`Error deserializing property ${i}:`, h); } }), this; } static fromJSON(t) { return new this().fromJSON(t); } } function c(e = {}, t) { return function(s, i) { const r = s.constructor; r.__serializableProperties || (r.__serializableProperties = []), r.__serializableProperties = [ ...r.__serializableProperties.filter((n) => n.propertyKey !== i), { propertyKey: i, deserializer: e, mapTo: t } ]; }; } function E(e) { return function(t) { t.__displayName = e; }; } function st(e) { return class extends e { _handlers = {}; on(s, i) { if (typeof i != "function") throw new Error("The callback of an event listener needs to be a function."); const r = crypto.randomUUID(); return this._handlers[s] ? this._handlers[s][r] = i : this._handlers[s] = { [r]: i }, r; } off(s, ...i) { if (s) { if (s === "*") { this._handlers = {}; return; } for (const r of Object.values(this._handlers)) s in r && delete r[s]; for (const r of i) this.off(r); } } emit(s, i) { const r = new CustomEvent(s, { detail: i }); Object.defineProperty(r, "currentTarget", { writable: !1, value: this }); for (const n in this._handlers[s] ?? {}) this._handlers[s]?.[n](r); for (const n in this._handlers["*"] ?? {}) this._handlers["*"]?.[n](r); } bubble(s) { return this.on("*", (i) => { s.emit(i.type, i.detail); }); } resolve(s) { return (i, r) => { this.on("error", r), this.on(s, i); }; } }; } class kt extends st(B) { _key; _value; _store; loaded = !1; constructor(t, s, i) { super(), this._store = t, this._key = s, this.initValue(i); } get key() { return this._key; } get value() { return this._value; } set value(t) { this._value = t, this._store.set(this._key, t), this.emit("update", void 0); } async initValue(t) { t instanceof Promise ? this._value = await t : this._value = t, this.loaded = !0, this.emit("update", void 0); } } class Zi { storageEngine; namespace; constructor(t, s = localStorage) { this.storageEngine = s, this.namespace = t; } define(t, s, i) { const r = this.get(t); return r === null ? (this.set(t, s), new kt(this, t, s)) : i && r != null ? new kt(this, t, i(r)) : new kt(this, t, r); } set(t, s) { this.storageEngine.setItem( this.getStorageId(t), JSON.stringify({ value: s }) ); } get(t) { const s = this.storageEngine.getItem(this.getStorageId(t)); return s ? JSON.parse(s).value : null; } remove(t) { this.storageEngine.removeItem(this.getStorageId(t)); } getStorageId(t) { return this.namespace ? `${this.namespace}.${t}` : t; } } function de() { return class extends st(class { }) { }; } function tr(e, t) { return t == "lower" ? e.toLocaleLowerCase() : t == "upper" ? e.toUpperCase() : e; } function q(e = "#000000", t = 100) { return `${e}${Math.round(t / 100 * 255).toString(16)}`; } function _(e, t) { return typeof e == "number" ? e : Number.parseFloat(e.replace("%", "")) * t / 100; } function Qe(e) { const t = e.startsWith("#") ? e.slice(1) : e, s = parseInt(t, 16), i = s >> 16 & 255, r = s >> 8 & 255, n = s & 255; return { r: i, g: r, b: n }; } function Ze(e, t, s) { return `#${((1 << 24) + (Math.round(e) << 16) + (Math.round(t) << 8) + Math.round(s)).toString(16).slice(1)}`; } function Lt(e, t) { switch (t) { case "ease-in": return e * e; case "ease-out": return e * (2 - e); case "ease-in-out": return e < 0.5 ? 2 * e * e : -1 + (4 - 2 * e) * e; case "ease-out-in": if (e < 0.5) { const s = e * 2; return s * (2 - s) / 2; } else { const s = (e - 0.5) * 2; return s * s / 2 + 0.5; } default: return e; } } function Z(e, t, s) { return e + (t - e) * s; } function Vt(e) { const t = Qe(e); return ts(t.r, t.g, t.b); } function ts(e, t, s) { e /= 255, t /= 255, s /= 255; const i = Math.max(e, t, s), r = Math.min(e, t, s); let n = 0, o = 0; const a = (i + r) / 2; if (i !== r) { const h = i - r; switch (o = a > 0.5 ? h / (2 - i - r) : h / (i + r), i) { case e: n = (t - s) / h + (t < s ? 6 : 0); break; case t: n = (s - e) / h + 2; break; case s: n = (e - t) / h + 4; break; } n /= 6; } return { h: Math.round(n * 360), s: Math.round(o * 100), l: Math.round(a * 100) }; } function es(e, t, s) { t /= 100, s /= 100, e = (e + 360) % 360; function i(l, u, d) { return d < 0 && (d += 1), d > 1 && (d -= 1), d < 1 / 6 ? l + (u - l) * 6 * d : d < 1 / 2 ? u : d < 2 / 3 ? l + (u - l) * (2 / 3 - d) * 6 : l; } const r = s < 0.5 ? s * (1 + t) : s + t - s * t, n = 2 * s - r, o = i(n, r, e / 360 + 1 / 3), a = i(n, r, e / 360), h = i(n, r, e / 360 - 1 / 3); return Ze( Math.round(o * 255), Math.round(a * 255), Math.round(h * 255) ); } function ss(e, t) { const { frames: s, extrapolate: i = "clamp", easing: r } = e; if (t.millis <= m(s[0].time).millis) return s[0].value; if (t.millis >= m(s[s.length - 1].time).millis) return s[s.length - 1].value; let n, o; for (let f = 0; f < s.length - 1; f++) if (t.millis >= m(s[f].time).millis && t.millis <= m(s[f + 1].time).millis) { n = s[f], o = s[f + 1]; break; } if (!n || !o) throw new Error("Unexpected error in keyframe interpolation"); const a = (t.millis - m(n.time).millis) / (m(o.time).millis - m(n.time).millis), h = Lt(a, n.easing ?? r), l = Vt(n.value), u = Vt(o.value); let d = l.h, w = u.h; return Math.abs(w - d) > 180 && (d < w ? d += 360 : w += 360), es( Z(d, w, h), Z(l.s, u.s, h), Z(l.l, u.l, h) ); } function ue(e, t) { const { frames: s, extrapolate: i = "clamp", easing: r } = e; if (t.millis <= m(s[0].time).millis) return s[0].value; if (t.millis >= m(s[s.length - 1].time).millis) return s[s.length - 1].value; let n, o; for (let l = 0; l < s.length - 1; l++) if (t.millis >= m(s[l].time).millis && t.millis <= m(s[l + 1].time).millis) { n = s[l], o = s[l + 1]; break; } if (!n || !o) throw new Error("Unexpected error in keyframe interpolation"); const a = (t.millis - m(n.time).millis) / (m(o.time).millis - m(n.time).millis), h = Lt(a, n.easing ?? r); return typeof n.value == "number" && typeof o.value == "number" ? Z(n.value, o.value, h) : `${Z(qt(n.value), qt(o.value), h)}%`; } function qt(e) { return typeof e == "number" ? e : Number(e.replace("%", "")); } function is(e, t) { const { frames: s, extrapolate: i = "clamp" } = e; if (t.millis <= m(s[0].time).millis) return s[0].value; if (t.millis >= m(s[s.length - 1].time).millis) return s[s.length - 1].value; let r, n; for (let d = 0; d < s.length - 1; d++) if (t.millis >= m(s[d].time).millis && t.millis <= m(s[d + 1].time).millis) { r = s[d], n = s[d + 1]; break; } if (!r || !n) throw new Error("Unexpected error in keyframe interpolation"); const o = (t.millis - m(r.time).millis) / (m(n.time).millis - m(r.time).millis), a = Lt(o, r.easing), h = n.value, l = h.length, u = Math.floor(a * l); return h.slice(0, u); } var rs = Object.defineProperty, H = (e, t, s, i) => { for (var r = void 0, n = e.length - 1, o; n >= 0; n--) (o = e[n]) && (r = o(t, s, r) || r); return r && rs(t, s, r), r; }; class P extends B { /** * Unique identifier of the mask */ id = crypto.randomUUID(); type = "BASE"; width = 100; height = 100; x = 0; y = 0; fillRule; animations = []; clip; renderer; constructor(t = {}) { super(), Object.assign(this, t); } connect(t) { return this.clip = t, this; } draw(t) { return this.renderer = t, new Path2D(); } animate(t) { for (const s of this.animations) typeof s?.frames[0].value == "number" && (this[s.key] = ue(s, t.subtract(this.start))); return this; } get start() { return this.clip?.start ?? new p(); } get stop() { return this.clip?.stop ?? new p(); } get size() { return { width: this.width, height: this.height }; } get bounds() { const { width: t, height: s } = this.size; return [ { x: 0, y: 0 }, { x: t, y: 0 }, { x: t, y: s }, { x: 0, y: s } ]; } detach() { return this.clip && (this.clip.mask = void 0, this.clip = void 0), this; } } H([ c() ], P.prototype, "type"); H([ c() ], P.prototype, "width"); H([ c() ], P.prototype, "height"); H([ c() ], P.prototype, "x"); H([ c() ], P.prototype, "y"); H([ c() ], P.prototype, "fillRule"); H([ c() ], P.prototype, "animations"); var ns = Object.defineProperty, fe = (e, t, s, i) => { for (var r = void 0, n = e.length - 1, o; n >= 0; n--) (o = e[n]) && (r = o(t, s, r) || r); return r && ns(t, s, r), r; }; class Ut extends P { type = "RECT"; radius = 0; animations = []; constructor(t = {}) { super(t), Object.assign(this, t); } draw(t) { const s = super.draw(t); if (this.radius) { const i = _(this.radius, Math.min(t.height, this.height) / 2); s.roundRect( (this.x - this.width / 2) * t.resolution | 0, (this.y - this.height / 2) * t.resolution | 0, this.width * t.resolution | 0, this.height * t.resolution | 0, i * t.resolution | 0 ); } else s.rect( this.x - this.width / 2 * t.resolution | 0, this.y - this.height / 2 * t.resolution | 0, this.width * t.resolution | 0, this.height * t.resolution | 0 ); return s; } get bounds() { const { width: t, height: s } = this.size; return [ { x: this.x - t / 2, y: this.y - s / 2 }, // top left { x: this.x + t / 2, y: this.y - s / 2 }, // top right { x: this.x + t / 2, y: this.y + s / 2 }, // bottom right { x: this.x - t / 2, y: this.y + s / 2 } // bottom left ]; } } fe([ c() ], Ut.prototype, "type"); fe([ c() ], Ut.prototype, "radius"); class os { static fromJSON(t) { switch (y(typeof t == "object"), y(t != null), y("type" in t), t.type) { case "RECT": return Ut.fromJSON(t); case "CIRCLE": return pe.fromJSON(t); default: return P.fromJSON(t); } } } var as = Object.defineProperty, hs = (e, t, s, i) => { for (var r = void 0, n = e.length - 1, o; n >= 0; n--) (o = e[n]) && (r = o(t, s, r) || r); return r && as(t, s, r), r; }; class pe extends P { type = "CIRCLE"; animations = []; constructor(t = {}) { super(t), Object.assign(this, t); } draw(t) { const s = super.draw(t); return s.ellipse( this.x * t.resolution, this.y * t.resolution, this.width * t.resolution * 0.5, this.height * t.resolution * 0.5, 0, 0, 2 * Math.PI ), s; } get bounds() { const { width: t, height: s } = this.size, i = this.x - t * 0.5, r = this.y - s * 0.5; return [ { x: i, y: r }, { x: i + t, y: r }, { x: i + t, y: r + s }, { x: i, y: r + s } ]; } } hs([ c() ], pe.prototype, "type"); var ls = Object.defineProperty, cs = Object.getOwnPropertyDescriptor, xt = (e, t, s, i) => { for (var r = i > 1 ? void 0 : i ? cs(t, s) : t, n = e.length - 1, o; n >= 0; n--) (o = e[n]) && (r = (i ? o(t, s, r) : o(r)) || r); return i && r && ls(t, s, r), r; }; class G extends B { _background = "#000000"; /** * The canvas element */ canvas = document.createElement("canvas"); /** * The main 2d context of the canvas */ videoCtx = this.canvas.getContext("2d"); resolution = 1; width = 0; height = 0; /** * The scale of the text */ textScale = 4; /** * The audio context */ audioCtx; audioDestination; /** * Offset in **seconds** relative to the hardware time when the playback started */ hardwareOffset = 0; /** * Offset in **seconds** relative to 0 when the playback started */ playbackOffset = 0; /** * Defines the fps used for rendering. */ playbackFps = tt; /** * The fps used when the ticker is inactive (not playing) */ inactiveFps = We; /** * Defines the current state of the ticker */ playing = !1; /** * Defines if the ticker is active */ stopped = !0; /** * User defined fixed duration * * @deprecated Use markers.stop instead */ duration; /** * The last time the timer was updated */ lastFrameTime = 0; constructor({ width: t = 1920, height: s = 1080, background: i = "#000000", resolution: r = 1, fps: n = tt, callback: o, context: a = new AudioContext(), audioDestination: h = a.destination } = {}) { super(), this.resolution = r, this.background = i, this.playbackFps = n, this.callback = o, this.audioCtx = a, this.audioDestination = h, this.resize(t, s); } /** * The current time of the hardware in seconds */ get hardwareTime() { return this.audioCtx.currentTime; } /** * The current time of the playback in **seconds** relative to 0 */ get playbackTime() { return this.playing ? this.hardwareTime - this.hardwareOffset + this.playbackOffset : this.playbackOffset; } get playbackTimestamp() { return new p(0, this.playbackTime); } /** * The current frame that the playback is set to */ get playbackFrame() { return Math.floor(this.playbackTime * this.playbackFps); } /** * Starts the animation loop */ start() { this.stopped && (this.stopped = !1, this.timer()); } /** * Stops the animation loop */ stop() { this.stopped = !0; } /** * Starts the frame incrementation */ async play() { this.playing || this.stopped || (this.resumeAudioContext(), this.hardwareOffset = this.hardwareTime, this.playing = !0); } /** * Pauses the frame incrementation */ async pause() { this.playing && (this.playbackOffset = this.playbackTime, this.playing = !1); } /** * The animation loop */ async timer(t = 0) { if (this.stopped) return; const i = 1e3 / (this.playing ? this.playbackFps : this.inactiveFps); t - this.lastFrameTime >= i && (this.lastFrameTime = t, await this.callback?.()), requestAnimationFrame(this.timer.bind(this)); } async resumeAudioContext() { this.audioCtx.state === "suspended" && await this.audioCtx.resume(); } get background() { return this._background; } set background(t) { this.background != t && (this.canvas.style.background = t, this._background = t); } /** * Resize the canvas */ resize(t = this.width, s = this.height, i = this.resolution) { const r = Math.round(t / 2) * 2, n = Math.round(s / 2) * 2; return (r !== this.width || n !== this.height || i !== this.resolution) && (this.width = r, this.height = n, this.resolution = i, this.canvas.width = Math.round(r * this.resolution / 2) * 2, this.canvas.height = Math.round(n * this.resolution / 2) * 2, this.videoCtx.imageSmoothingEnabled = !1), this; } clear(t) { let s = 0, i = 0, r = this.width * this.resolution, n = this.height * this.resolution; return this.videoCtx.fillStyle = this._background, t && (s = (t.x ?? 0) * this.resolution, i = (t.y ?? 0) * this.resolution, r = t.width * this.resolution, n = t.height * this.resolution), this.videoCtx.clearRect(s, i, r, n), this.videoCtx.fillRect(s, i, r, n), this; } rect(t) { this.videoCtx.beginPath(); const s = Math.max(t.radius ?? 0, 0), { width: i, height: r } = t, { x: n = 0, y: o = 0 } = t; return t.radius ? this.videoCtx.roundRect( n * this.resolution | 0, o * this.resolution | 0, i * this.resolution | 0, r * this.resolution | 0, s * this.resolution | 0 ) : this.videoCtx.rect( n * this.resolution | 0, o * this.resolution | 0, i * this.resolution | 0, r * this.resolution | 0 ), this.videoCtx.closePath(), this; } circle(t) { return this.videoCtx.beginPath(), "height" in t ? this.videoCtx.ellipse( t.cx * this.resolution | 0, t.cy * this.resolution | 0, t.width * this.resolution | 0, t.height * this.resolution | 0, 0, 0, Math.PI * 2 ) : this.videoCtx.arc( t.cx * this.resolution | 0, t.cy * this.resolution | 0, t.radius * this.resolution | 0, 0, Math.PI * 2 ), this.videoCtx.closePath(), this; } image(t, s) { const { x: i = 0, y: r = 0, width: n, height: o, rotation: a } = s, h = i * this.resolution | 0, l = r * this.resolution | 0, u = n * this.resolution | 0, d = o * this.resolution | 0; if (a !== void 0 && a !== 0) { this.videoCtx.save(), this.videoCtx.translate(h + u / 2, l + d / 2); const w = a * Math.PI / 180; this.videoCtx.rotate(w); const f = Math.abs(a % 180) === 90; this.videoCtx.drawImage( t, (f ? -d / 2 : -u / 2) | 0, (f ? -u / 2 : -d / 2) | 0, (f ? d : u) | 0, (f ? u : d) | 0 ), this.videoCtx.restore(); } else this.videoCtx.drawImage(t, h, l, u, d); return this; } clip(t, s) { return t instanceof P ? this.videoCtx.clip(t.draw(this), t.fillRule) : t && this.videoCtx.clip(t, s), this; } opacity(t) { return this.videoCtx.globalAlpha *= t / 100, this; } transform(t) { return t ? (t.translate && this.videoCtx.translate( _(t.translate.x, this.width) * this.resolution | 0, _(t.translate.y, this.height) * this.resolution | 0 ), t.rotate && this.videoCtx.rotate(t.rotate * Math.PI / 180), t.scale && this.videoCtx.scale( _(t.scale.x, this.width), _(t.scale.y, this.height) ), this) : (this.videoCtx.setTransform(1, 0, 0, 1, 0, 0), this); } blendMode(t) { return t && (this.videoCtx.globalCompositeOperation = t), this; } save() { return this.videoCtx.save(), this; } restore() { return this.videoCtx.restore(), this; } filter(t) { return t ? (this.videoCtx.filter = t, this) : this; } fill(t, s = !1) { if (!t) return this.videoCtx.fillStyle = "transparent", this; if (typeof t.color == "string") this.videoCtx.fillStyle = q(t.color, t.opacity); else if ("type" in t.color) { const i = this.createGradient(t.color); this.videoCtx.fillStyle = i; } else if ("image" in t.color) { const i = this.videoCtx.createPattern(t.color.image, t.color.repetition); this.videoCtx.fillStyle = i ?? ""; } return s && this.videoCtx.fill(), this; } shadow(t) { return t ? (this.videoCtx.fillStyle = this.videoCtx.shadowColor = q(t.color, t.opacity), this.videoCtx.shadowOffsetX = (t.offsetX ?? 0) * this.resolution * this.textScale, this.videoCtx.shadowOffsetY = (t.offsetY ?? 0) * this.resolution * this.textScale, this.videoCtx.shadowBlur = (t.blur ?? 24) * this.resolution * this.textScale, this) : (this.videoCtx.shadowColor = "transparent", this.videoCtx.shadowBlur = 0, this.videoCtx.shadowOffsetX = 0, this.videoCtx.shadowOffsetY = 0, this); } stroke(t, s = !1) { return t ? (this.videoCtx.strokeStyle = q(t.color, t.opacity), this.videoCtx.lineWidth = (t.width ?? 1) * this.resolution * this.textScale, t.lineCap && (this.videoCtx.lineCap = t.lineCap), t.lineJoin && (this.videoCtx.lineJoin = t.lineJoin), t.miterLimit && (this.videoCtx.miterLimit = t.miterLimit), s && this.videoCtx.stroke(), this) : (this.videoCtx.strokeStyle = "transparent", this.videoCtx.lineWidth = 0, this.videoCtx.lineCap = "butt", this.videoCtx.lineJoin = "miter", this.videoCtx.miterLimit = 10, this); } /** * Add the renderer to the dom */ mount(t) { t.appendChild(this.canvas); } /** * Remove the renderer from the dom */ unmount() { this.canvas.parentElement?.removeChild(this.canvas); } createGradient(t) { let s; return t.type === "linear" ? s = this.videoCtx.createLinearGradient(0, 0, this.canvas.width, 0) : s = this.videoCtx.createRadialGradient( this.canvas.width / 2, this.canvas.height / 2, 0, this.canvas.width / 2, this.canvas.height / 2, this.canvas.width / 2 ), t.stops.forEach((i) => { s.addColorStop(i.offset, i.color); }), s; } } xt([ c() ], G.prototype, "resolution", 2); xt([ c() ], G.prototype, "width", 2); xt([ c() ], G.prototype, "height", 2); xt([ c() ], G.prototype, "background", 1); const ds = { fromJSON: (e) => { y(typeof e == "object"), y(e != null); const t = {}; for (const [s, i] of Object.entries(e)) t[s] = m(i); return t; } }; function us(e) { const t = new p(); for (const s of e) s.disabled || s.stop.frames > t.frames && (t.frames = s.stop.frames); return t; } var fs = Object.defineProperty, ps = Object.getOwnPropertyDescriptor, R = (e, t, s, i) => { for (var r = i > 1 ? void 0 : i ? ps(t, s) : t, n = e.length - 1, o; n >= 0; n--) (o = e[n]) && (r = (i ? o(t, s, r) : o(r)) || r); return i && r && fs(t, s, r), r; }; let k = class extends B { id = crypto.randomUUID(); data = {}; type = "BASE"; mimeType; input; name; createdAt = /* @__PURE__ */ new Date(); file; constructor(e) { super(), this.input = e.input, this.mimeType = e.mimeType, e.name ? this.name = e.name : typeof this.input == "string" ? this.name = this.input.split("/").at(-1) ?? "" : this.input instanceof File ? this.name = this.input.name : this.input instanceof FileSystemFileHandle ? this.name = this.input.name : this.name = "UNTITLED_BLOB"; } async init(e = {}) { } /** * Get the source as an array buffer */ async arrayBuffer() { return typeof this.input == "string" ? await (await fetch(this.input)).arrayBuffer() : this.input instanceof Blob ? await this.input.arrayBuffer() : await (await this.input.getFile()).arrayBuffer(); } /** * Create a checkpoint of the source. May include Blob or FileSystemFileHandle. * @param middleware A function to modify the checkpoint data * @returns A serialized representation of the source */ async createCheckpoint() { return this.toJSON(); } }; R([ c() ], k.prototype, "id", 2); R([ c() ], k.prototype, "data", 2); R([ c() ], k.prototype, "type", 2); R([ c() ], k.prototype, "mimeType", 2); R([ c() ], k.prototype, "input", 2); R([ c() ], k.prototype, "name", 2); R([ c(bt) ], k.prototype, "createdAt", 2); R([ c() ], k.prototype, "file", 2); k = R([ E("Source") ], k); function Bt(e) { class t extends e { /** * The height of the source */ height = 1080; /** * The width of the source */ width = 1920; /** * The aspect ratio of the source */ get aspectRatio() { return this.width / this.height; } } return t; } async function ms(e) { if (e instanceof FileSystemFileHandle) return (await e.getFile()).type; if (e instanceof Blob) return e.type; if (e.startsWith("<html>")) return "text/html"; let t; try { t = await fetch(e, { method: "HEAD" }); } catch { console.info("Using AbortController fallback"); const r = new AbortController(); t = await fetch(e, { signal: r.signal }), r.abort(); } if (!t.ok) throw new Error(`HTTP error! Status: ${t.status}`); const s = t.headers.get("Content-Type"); if (!s) throw new W({ message: "No content type found", code: "no_content_type_found" }); return s; } const At = 3e3; class gs extends de() { input; buffers = /* @__PURE__ */ new Map(); constructor(t) { super(), this.input = t; } /** * Decodes an audio file or URL and returns a resampled AudioBuffer. * @param input - Either a File, Blob, or URL string. * @returns Promise<AudioBuffer> */ async decode(t, s, i = !1) { let r = this.buffers.get(`${s}-${t}`); if (r) return r; let n; if (t || s ? n = new OfflineAudioContext(t ?? 2, 1, s ?? 44100) : n = new AudioContext(), typeof this.input == "string") { const a = await (await fetch(this.input)).arrayBuffer(); r = await n.decodeAudioData(a); } else if (this.input instanceof Blob) r = await n.decodeAudioData(await this.input.arrayBuffer()); else { const o = await this.input.getFile(); r = await n.decodeAudioData(await o.arrayBuffer()); } return i && this.buffers.set(`${s}-${t}`, r), r; } async dispose() { this.buffers.clear(); } } function ys(e, t = {}) { const { threshold: s = 0.02, hopSize: i = 1024, minDuration: r = 500 } = t, n = [], o = e.getChannelData(0), a = e.sampleRate, h = Math.floor(r / 1e3 * a); let l = null, u = 0; for (let d = 0; d < o.length; d += i) { let w = 0; const f = Math.min(d + i, o.length); for (let g = d; g < f; g++) w += o[g] * o[g]; w = Math.sqrt(w / (f - d)), w < s ? (u += i, l === null && (l = d)) : (l !== null && u >= h && n.push({ start: new p(0, l / a), stop: new p(0, d / a) }), l = null, u = 0); } return l !== null && u >= h && n.push({ start: new p(0, l / a), stop: new p(0, o.length / a) }), n; } var ws = Object.defineProperty, bs = (e, t, s, i) => { for (var r = void 0, n = e.length - 1, o; n >= 0; n--) (o = e[n]) && (r = o(t, s, r) || r); return r && ws(t, s, r), r; }; class K extends k { type = "AUDIO"; element = new Audio(); decoder; duration; demuxer; transcript; constructor(t) { super(t), this.decoder = new gs(t.input); } async init(t = {}) { if (this.input instanceof Blob) this.element.src = URL.createObjectURL(this.input); else if (this.input instanceof FileSystemFileHandle) { const s = await this.input.getFile(); this.element.src = URL.createObjectURL(s); } else this.element.src = this.input; await new Promise((s, i) => { this.element.readyState >= 3 && s(!0), this.element.addEventListener("loadedmetadata", () => { s(!0); }), this.element.addEventListener("error", () => { i(new S({ message: "Failed to load audio", code: "failed_to_load_audio" })); }); }), this.duration = new p(0, this.element.duration), t.prefetch && typeof this.input == "string" ? this.demuxer = fetch(this.input).then((s) => s.arrayBuffer()).then((s) => new Ft({ formats: Et, source: new ke(s) })) : this.demuxer = Promise.resolve(new Ft({ formats: Et, source: typeof this.input == "string" ? new ce(this.input) : new Mt( this.input instanceof Blob ? this.input : await this.input.getFile() ) })); } async decode(t = 2, s = 48e3, i = !1) { return this.decoder.decode(t, s, i); } /** * Find silences in the audio clip. Results are cached. * * uses default sample rate of 3000 * @param options - Silences options. * @returns An array of the silences (in ms) in the clip. */ async silences(t = {}) { const s = await this.decode(1, 24e3); return ys(s, t); } /** * Sampler that uses a window size to calculate the max value of the samples in the window. * @param options - Sampling options. * @returns An array of the max values of the samples in the window. */ async sample({ length: t = 60, start: s = 0, stop: i } = {}) { typeof s == "object" && (s = s.millis), typeof i == "object" && (i = i.millis); const r = await this.decode(1, At, !0), n = r.getChannelData(0), o = Math.floor(Math.max(s * At / 1e3, 0)), a = i ? Math.floor(Math.min(i * At / 1e3, r.length)) : r.length, h = Math.floor((a - o) / t), l = new Float32Array(t); for (let u = 0; u < t; u++) { const d = o + u * h, w = d + h; let f = 0; for (let g = d; g < w; g++) { const C = n[g]; f = Math.max(f, Math.abs(C)); } l[u] = f; } return l; } async *samplesInRange(t) { const i = await (await this.demuxer)?.getPrimaryAudioTrack(); if (!i) return; const r = m(t.startTime).seconds, n = m(t.endTime).seconds; yield* new Ae(i).samples(r, n); } async getMetadata() { const s = await (await this.demuxer)?.getPrimaryAudioTrack(); return s ? { sampleRate: s.sampleRate, numberOfChannels: s.numberOfChannels } : null; } } bs([ c(F) ], K.prototype, "transcript"); class zt extends Bt(K) { type = "VIDEO"; element = document.createElement("video"); async init(t = {}) { await super.init(t), this.height = this.element.videoHeight, this.width = this.element.videoWidth; } async *thumbnailsInRange(t) { const i = await (await this.demuxer)?.getPrimaryVideoTrack(); if (!i) return; const r = m(t.startTime).seconds, n = m(t.endTime).seconds, o = Array.from({ length: t.count }, (h, l) => r + l * (n - r) / t.count); yield* new Fe(i, { ...t, fit: "cover" }).canvasesAtTimestamps(o); } } class $t extends Bt(k) { type = "IMAGE"; element = new Image(); async init() { if (typeof this.input == "string") { const t = await fetch(this.input); if (!t.ok) throw new W({ message: "Failed to load image", code: "failed_to_load_image" }); this.element.src = URL.createObjectURL(await t.blob()); } else this.input instanceof Blob ? this.element.src = URL.createObjectURL(this.input) : this.element.src = URL.createObjectURL(await this.input.getFile()); await new Promise((t, s) => { this.element.onload = () => { this.height = this.element.naturalHeight, this.width = this.element.naturalWidth, t(); }, this.element.onerror = () => s(new W({ message: "Failed to load image", code: "failed_to_load_image" })); }); } } const Gt = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3C/svg%3E"; function xs(e) { const t = new TextEncoder().encode(e); let s = ""; const i = t.byteLength; for (let r = 0; r < i; r++) s += String.fromCharCode(t[r]); return btoa(s); } function vs(e) { if (!e || !e.body) return Gt; const t = e.body.scrollWidth, s = e.body.scrollHeight, i = e.cloneNode(!0), r = i.getElementsByTagName("style").item(0), n = i.getElementsByTagName("body").item(0); if (n?.setAttribute("xmlns", "http://www.w3.org/1999/xhtml"), !n) return Gt; const o = new XMLSerializer(), a = r ? o.serializeToString(r) : "", h = o.serializeToString(n), l = ` <svg xmlns="http://www.w3.org/2000/svg" width="${t}" height="${s}"> body { padding: 0; } ${a} <foreignObject width="100%" height="100%"> ${h} </foreignObject> </svg>`; return "data:image/svg+xml;base64," + xs(l); } class jt extends Bt(k) { type = "HTML"; element = document.createElement("iframe"); constructor(t) { super(t), this.element.style.position = "absolute", this.element.style.width = "0", this.element.style.height = "0", this.element.style.border = "0", this.element.style.visibility = "hidden", document.body.appendChild(this.element); } async init() { if (typeof this.input == "string" && this.input.startsWith("<html>")) this.element.srcdoc = this.input; else if (typeof this.input == "string") { co