UNPKG

@diffusionstudio/core

Version:

2D motion graphics and video rendering engine

1,752 lines (1,749 loc) 490 kB
/*! * @diffusionstudio/core v3.5.1 * (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 */ let zr = class gi { data; format; numberOfChannels; numberOfFrames; sampleRate; timestamp; duration; constructor(e) { this.data = e.data, this.format = e.format, this.numberOfChannels = e.numberOfChannels, this.numberOfFrames = e.numberOfFrames, this.sampleRate = e.sampleRate, this.timestamp = e.timestamp || 0, this.duration = this.numberOfFrames / this.sampleRate * 1e6; } copyTo(e, 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 a = r * this.numberOfFrames + i, n = i * this.numberOfChannels + r, o = Math.max(-1, Math.min(1, this.data[a])); e[n] = Math.round(o * 32767); } } clone() { return new gi({ 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: zr }); const Yc = 0.2, pt = 30, Br = 1; class Xe extends Error { message; code; constructor({ message: e = "", code: s = "" }, ...i) { super(e, ...i), console.error(e), this.code = s, this.message = e; } } class be extends Xe { } class K extends Xe { } class $e extends Xe { } class Jc extends Xe { } class gt extends Xe { } function Ur(t, e = pt) { if (e < 1) throw new K({ code: "invalidArgument", message: "FPS must be greater or equal to 1" }); return Math.round(t * e); } function Zc(t, e = pt) { if (e < 1) throw new K({ code: "invalidArgument", message: "FPS must be greater or equal to 1" }); return Math.round(t / e * 1e3) / 1e3; } function Et(t, e = pt) { if (e < 1) throw new K({ code: "invalidArgument", message: "FPS must be greater or equal to 1" }); return Math.round(t / e * 1e3); } 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(e = 0, s = 0, i = 0, r = 0) { this.time = Math.round(e + s * 1e3 + i * 6e4 + r * 36e5); } /** * Base unit of the timestamp */ get millis() { return this.time; } set millis(e) { this.time = Math.round(e); } /** * Defines the time in frames at the * current frame rate */ get frames() { return Ur(this.millis / 1e3); } set frames(e) { this.millis = Et(e); } /** * Convert the timestamp to seconds */ get seconds() { return this.millis / 1e3; } set seconds(e) { this.millis = e * 1e3; } /** * Equivalent to millis += x */ addMillis(e) { return this.millis = this.millis + e, this; } /** * Equivalent to frames += x */ addFrames(e) { const s = Et(e); 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(e) { return new P(e.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(e) { return new P(this.millis - e.millis); } /** * Create a new timestamp from frames */ static fromFrames(e, s) { const i = new P(); return i.millis = Et(e, s), i; } /** * get a copy of the object */ copy() { return new P(this.millis); } toJSON() { return this.millis; } static fromJSON(e) { return new P(e); } } function el(t) { return Math.floor(t * 255).toString(16).padStart(2, "0").toUpperCase(); } function tl(t, e) { return t.reduce( (s, i) => { const r = i[e]; return s[r] || (s[r] = []), s[r].push(i), s; }, // @ts-ignore {} ); } function ct(t, e) { return [t.slice(0, e), t.slice(e)].filter((s) => s.length > 0); } function at(t, e) { return e ? Math.floor(Math.random() * (e - t + 1) + t) : t; } async function sl(t) { t <= 0 || await new Promise((e) => setTimeout(e, t)); } function il(t) { if (!t) throw "Assertion failed!"; } function rl(t, e = 300) { let s; return (...i) => { clearTimeout(s), s = setTimeout(() => { t.apply(t, i); }, e); }; } function Dr(t, e, s) { s < 0 && (s = 0); const i = t[e]; t.splice(e, 1), t.splice(s, 0, i); } function al() { return typeof crypto < "u" ? crypto.randomUUID().split("-").at(0) : Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15); } function nl(t) { return t.charAt(0).toUpperCase() + t.slice(1); } function Vr(t) { if (t.numberOfChannels === 1) return t.getChannelData(0); const e = []; for (let n = 0; n < t.numberOfChannels; n++) e.push(t.getChannelData(n)); const s = Math.max(...e.map((n) => n.length)), i = new Float32Array(s * t.numberOfChannels); let r = 0, a = 0; for (; a < s; ) e.forEach((n) => { i[r++] = n[a] !== void 0 ? n[a] : 0; }), a++; return i; } function et(t, e, s) { for (let i = 0; i < s.length; i++) t.setUint8(e + i, s.charCodeAt(i)); } function Nr(t, e, s) { for (let i = 0; i < e.length; i++, s += 2) { const r = Math.max(-1, Math.min(1, e[i])); t.setInt16(s, r < 0 ? r * 32768 : r * 32767, !0); } return t; } function Lr(t, e, s) { const a = e * 2, n = 8, o = 36, c = t.length * 2, l = o + c, h = new ArrayBuffer(n + l), d = new DataView(h); return et(d, 0, "RIFF"), d.setUint32(4, l, !0), et(d, 8, "WAVE"), et(d, 12, "fmt "), d.setUint32(16, 16, !0), d.setUint16(20, 1, !0), d.setUint16(22, e, !0), d.setUint32(24, s, !0), d.setUint32(28, s * a, !0), d.setUint16(32, a, !0), d.setUint16(34, 16, !0), et(d, 36, "data"), d.setUint32(40, c, !0), Nr(d, t, n + o); } function ol(t, e = "audio/wav") { const s = Vr(t), i = Lr(s, t.numberOfChannels, t.sampleRate); return new Blob([i], { type: e }); } function cl(t) { const e = new Float32Array(t.length * t.numberOfChannels); let s = 0; for (let i = 0; i < t.numberOfChannels; i++) { const r = t.getChannelData(i); e.set(r, s), s += r.length; } return e; } function ll(t) { const e = t.numberOfChannels, s = t.length, i = new Int16Array(s * e); for (let r = 0; r < s; r++) for (let a = 0; a < e; a++) { let n = t.getChannelData(a)[r] * 32767; n > 32767 && (n = 32767), n < -32767 && (n = -32767), i[r * e + a] = n; } return i; } async function dl(t, e = 22050, s = Math.sqrt(2)) { const i = await t.arrayBuffer(), r = new OfflineAudioContext({ sampleRate: e, length: 1 }), a = await r.decodeAudioData(i), n = r.createBuffer(1, a.length, e); if (a.numberOfChannels >= 2) { const o = a.getChannelData(0), c = a.getChannelData(1), l = n.getChannelData(0); for (let h = 0; h < a.length; ++h) l[h] = s * (o[h] + c[h]) / 2; return n; } return a; } function hl(t, e = 44100, s = 2) { if (t.sampleRate == e && t.numberOfChannels == s) return t; const i = Math.floor(t.duration * e), a = new OfflineAudioContext(s, 1, e).createBuffer(s, i, e); for (let n = 0; n < t.numberOfChannels; n++) { const o = t.getChannelData(n), c = a.getChannelData(n), l = t.sampleRate / e; for (let h = 0; h < c.length; h++) { const d = h * l, u = Math.floor(d), f = Math.ceil(d); if (f >= o.length) c[h] = o[u]; else { const p = d - u; c[h] = o[u] * (1 - p) + o[f] * p; } } } return a; } async function wi(t, e) { const s = document.createElement("a"); if (document.head.appendChild(s), s.target = "_blank", e ? s.download = e : t instanceof File ? s.download = t.name : t instanceof Blob ? s.download = "untitled" : typeof t == "string" && (s.download = t.split("/").pop() ?? "untitled"), typeof t == "string" && t.startsWith("data:image/svg+xml;base64,")) { const i = t.split(",")[1], r = atob(i), a = new Array(r.length); for (let c = 0; c < r.length; c++) a[c] = r.charCodeAt(c); const n = new Uint8Array(a), o = new Blob([n], { type: "image/svg+xml" }); s.href = URL.createObjectURL(o), s.download = e?.split(".")[0] + ".svg"; } else typeof t == "string" ? s.href = t : s.href = URL.createObjectURL(t); s.click(), s.remove(); } async function ul(t, e = !0) { return new Promise((s) => { const i = document.createElement("input"); i.type = "file", i.accept = t, i.multiple = e, i.onchange = (r) => { const a = Array.from(r.target?.files ?? []); s(a); }, i.click(); }); } function O(t) { if (!t) return new P(0); if (typeof t == "number") return P.fromFrames(t); if (t instanceof P) return t; const e = t.split(":"); if (e.length === 3) { const [s, i, r] = e.map(Number); if (isNaN(s) || isNaN(i) || isNaN(r)) throw new Error(`Invalid time format: ${t}`); return new P(0, r, i, s); } if (e.length === 2) { const [s, i] = e.map(Number); if (isNaN(s) || isNaN(i)) throw new Error(`Invalid time format: ${t}`); return new P(0, i, s); } if (typeof t == "string") { const s = parseFloat(t); if (isNaN(s)) throw new Error(`Invalid time format: ${t}`); if (t.endsWith("ms")) return new P(s); if (t.endsWith("s")) return new P(0, s); if (t.endsWith("f")) return P.fromFrames(s); if (t.endsWith("min")) return new P(0, 0, s); throw new Error(`Invalid time format: ${t}`); } throw new Error(`Invalid time format: ${t}`); } async function ml(t) { const { fps: e, height: s, width: i, bitrate: r } = t, a = [ "avc1.640033", "avc1.4d4033", "avc1.424033", "avc1.640029", "avc1.4d4029", "avc1.424029" ], n = ["prefer-hardware", "prefer-software"], o = []; for (const l of a) for (const h of n) o.push({ codec: l, hardwareAcceleration: h, width: i, height: s, bitrate: r, framerate: e }); const c = []; if ("VideoEncoder" in window) { for (const l of o) { const h = await VideoEncoder.isConfigSupported(l); h.supported && c.push(h.config ?? l); } return c.sort(Wr)[0].codec; } } function Wr(t, e) { const s = t.hardwareAcceleration ?? "", i = e.hardwareAcceleration ?? ""; return s < i ? -1 : s > i ? 1 : 0; } function _s(t) { return `${t.hours.toString().padStart(2, "0")}:${t.minutes.toString().padStart(2, "0")}:${t.seconds.toString().padStart(2, "0")},` + t.milliseconds.toString().padStart(3, "0"); } function Rs(t) { const e = new Date(1970, 0, 1); return e.setSeconds(t), e.setMilliseconds(Math.round(t % 1 * 1e3)), { hours: e.getHours(), minutes: e.getMinutes(), seconds: e.getSeconds(), milliseconds: e.getMilliseconds() }; } class We { words = []; constructor(e) { e && (this.words = e); } get duration() { return this.stop.subtract(this.start); } get text() { return this.words.map(({ text: e }) => e).join(" "); } get start() { return this.words.at(0)?.start ?? new P(); } get stop() { return this.words.at(-1)?.stop ?? new P(); } } var Lt = /* @__PURE__ */ ((t) => (t.en = "en", t.de = "de", t))(Lt || {}); class Wt { /** * 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(e, s, i, r) { this.text = e, 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 re { id = crypto.randomUUID(); language = Lt.en; groups = []; get text() { return this.groups.map(({ text: e }) => e).join(" "); } get words() { return this.groups.flatMap(({ words: e }) => e); } constructor(e = [], s = Lt.en) { this.groups = e, this.language = s; } /** * Iterate over all words in groups */ *iter({ count: e, duration: s, length: i }) { for (const r of this.groups) { let a; for (const [n, o] of r.words.entries()) a && (e && a.words.length >= at(...e) ? (yield a, a = void 0) : s && a?.duration.seconds >= at(...s) ? (yield a, a = void 0) : i && a.text.length >= at(...i) && (yield a, a = void 0)), a ? a.words.push(o) : a = new We([o]), n == r.words.length - 1 && (yield a); } } /** * This method will optimize the transcipt for display */ optimize() { const e = this.groups.flatMap((s) => s.words); for (let s = 0; s < e.length - 1; s++) { const i = e[s], r = e[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(e = {}) { let s = 1, i = ""; for (const r of this.iter(e)) { const a = Rs(r.start.seconds), n = Rs(r.stop.seconds); i += `${s} ` + _s(a) + " --> " + _s(n) + ` ${r.text} `, s += 1; } return { text: i, blob: new Blob([i], { type: "text/plain;charset=utf8" }) }; } toJSON() { return this.groups.map( (e) => e.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(e, s = !0) { let i = 0; const r = []; for (const a of this.groups) for (const n of a.words) if (r.length == 0 && s && (i = n.start.millis), r.push(new Wt(n.text, n.start.millis - i, n.stop.millis - i)), r.length == e) return new re([new We(r)]); return new re([new We(r)]); } /** * Create a deep copy of the transcript * @returns A new Transcript instance */ copy() { return re.fromJSON(this.toJSON()); } static fromJSON(e) { const s = new re(); for (const i of e) { const r = new We(); for (const a of i) r.words.push(new Wt(a.token, a.start, a.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(e) { if (Array.isArray(e)) return re.fromJSON(e); if (e instanceof Blob) return re.fromJSON(JSON.parse(await e.text())); const s = await fetch(e); if (!s.ok) throw new be({ code: "unexpectedIOError", message: "An unexpected error occurred while fetching the file" }); return re.fromJSON(await s.json()); } } class Fe { /** * Unique identifier of the object */ id = crypto.randomUUID(); toJSON() { const e = {}; return (this.constructor.__serializableProperties || []).forEach(({ propertyKey: i, serializer: r }) => { const a = this[i]; r && a instanceof r ? e[i] = a.toJSON() : e[i] = a; }), e; } static fromJSON(e) { const s = new this(); return (this.__serializableProperties || []).forEach(({ propertyKey: r, serializer: a }) => { if (e.hasOwnProperty(r)) if (a) { const n = a.fromJSON(e[r]); s[r] = n; } else s[r] = e[r]; }), s; } } function I(t) { return function(e, s) { e.constructor.__serializableProperties || (e.constructor.__serializableProperties = []), e.constructor.__serializableProperties.push({ propertyKey: s, serializer: t }); }; } function Ke(t) { return class extends t { _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 a in this._handlers[s] ?? {}) this._handlers[s]?.[a](r); for (const a in this._handlers["*"] ?? {}) this._handlers["*"]?.[a](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 Pt extends Ke(Fe) { _key; _value; _store; loaded = !1; constructor(e, s, i) { super(), this._store = e, this._key = s, this.initValue(i); } get key() { return this._key; } get value() { return this._value; } set value(e) { this._value = e, this._store.set(this._key, e), this.emit("update", void 0); } async initValue(e) { e instanceof Promise ? this._value = await e : this._value = e, this.loaded = !0, this.emit("update", void 0); } } class pl { storageEngine; namespace; constructor(e, s = localStorage) { this.storageEngine = s, this.namespace = e; } define(e, s, i) { const r = this.get(e); return r === null ? (this.set(e, s), new Pt(this, e, s)) : i && r != null ? new Pt(this, e, i(r)) : new Pt(this, e, r); } set(e, s) { this.storageEngine.setItem( this.getStorageId(e), JSON.stringify({ value: s }) ); } get(e) { const s = this.storageEngine.getItem(this.getStorageId(e)); return s ? JSON.parse(s).value : null; } remove(e) { this.storageEngine.removeItem(this.getStorageId(e)); } getStorageId(e) { return this.namespace ? `${this.namespace}.${e}` : e; } } function yi() { return class extends Ke(class { }) { }; } function gl(t, e) { return e == "lower" ? t.toLocaleLowerCase() : e == "upper" ? t.toUpperCase() : t; } function Pe(t = "#000000", e = 100) { return `${t}${Math.round(e / 100 * 255).toString(16)}`; } function x(t, e) { return typeof t == "number" ? t : Number.parseFloat(t.replace("%", "")) * e / 100; } function Hr(t) { const e = t.startsWith("#") ? t.slice(1) : t, s = parseInt(e, 16), i = s >> 16 & 255, r = s >> 8 & 255, a = s & 255; return { r: i, g: r, b: a }; } function $r(t, e, s) { return `#${((1 << 24) + (Math.round(t) << 16) + (Math.round(e) << 8) + Math.round(s)).toString(16).slice(1)}`; } function ns(t, e) { switch (e) { case "ease-in": return t * t; case "ease-out": return t * (2 - t); case "ease-in-out": return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t; case "ease-out-in": if (t < 0.5) { const s = t * 2; return s * (2 - s) / 2; } else { const s = (t - 0.5) * 2; return s * s / 2 + 0.5; } default: return t; } } function je(t, e, s) { return t + (e - t) * s; } function Fs(t) { const e = Hr(t); return jr(e.r, e.g, e.b); } function jr(t, e, s) { t /= 255, e /= 255, s /= 255; const i = Math.max(t, e, s), r = Math.min(t, e, s); let a = 0, n = 0; const o = (i + r) / 2; if (i !== r) { const c = i - r; switch (n = o > 0.5 ? c / (2 - i - r) : c / (i + r), i) { case t: a = (e - s) / c + (e < s ? 6 : 0); break; case e: a = (s - t) / c + 2; break; case s: a = (t - e) / c + 4; break; } a /= 6; } return { h: Math.round(a * 360), s: Math.round(n * 100), l: Math.round(o * 100) }; } function qr(t, e, s) { e /= 100, s /= 100, t = (t + 360) % 360; function i(l, h, d) { return d < 0 && (d += 1), d > 1 && (d -= 1), d < 1 / 6 ? l + (h - l) * 6 * d : d < 1 / 2 ? h : d < 2 / 3 ? l + (h - l) * (2 / 3 - d) * 6 : l; } const r = s < 0.5 ? s * (1 + e) : s + e - s * e, a = 2 * s - r, n = i(a, r, t / 360 + 1 / 3), o = i(a, r, t / 360), c = i(a, r, t / 360 - 1 / 3); return $r( Math.round(n * 255), Math.round(o * 255), Math.round(c * 255) ); } function Qr(t, e) { const { frames: s, extrapolate: i = "clamp", easing: r } = t; if (e.millis <= O(s[0].time).millis) return s[0].value; if (e.millis >= O(s[s.length - 1].time).millis) return s[s.length - 1].value; let a, n; for (let f = 0; f < s.length - 1; f++) if (e.millis >= O(s[f].time).millis && e.millis <= O(s[f + 1].time).millis) { a = s[f], n = s[f + 1]; break; } if (!a || !n) throw new Error("Unexpected error in keyframe interpolation"); const o = (e.millis - O(a.time).millis) / (O(n.time).millis - O(a.time).millis), c = ns(o, a.easing ?? r), l = Fs(a.value), h = Fs(n.value); let d = l.h, u = h.h; return Math.abs(u - d) > 180 && (d < u ? d += 360 : u += 360), qr( je(d, u, c), je(l.s, h.s, c), je(l.l, h.l, c) ); } function bi(t, e) { const { frames: s, extrapolate: i = "clamp", easing: r } = t; if (e.millis <= O(s[0].time).millis) return s[0].value; if (e.millis >= O(s[s.length - 1].time).millis) return s[s.length - 1].value; let a, n; for (let l = 0; l < s.length - 1; l++) if (e.millis >= O(s[l].time).millis && e.millis <= O(s[l + 1].time).millis) { a = s[l], n = s[l + 1]; break; } if (!a || !n) throw new Error("Unexpected error in keyframe interpolation"); const o = (e.millis - O(a.time).millis) / (O(n.time).millis - O(a.time).millis), c = ns(o, a.easing ?? r); return typeof a.value == "number" && typeof n.value == "number" ? je(a.value, n.value, c) : `${je(Os(a.value), Os(n.value), c)}%`; } function Os(t) { return typeof t == "number" ? t : Number(t.replace("%", "")); } function Xr(t, e) { const { frames: s, extrapolate: i = "clamp" } = t; if (e.millis <= O(s[0].time).millis) return s[0].value; if (e.millis >= O(s[s.length - 1].time).millis) return s[s.length - 1].value; let r, a; for (let d = 0; d < s.length - 1; d++) if (e.millis >= O(s[d].time).millis && e.millis <= O(s[d + 1].time).millis) { r = s[d], a = s[d + 1]; break; } if (!r || !a) throw new Error("Unexpected error in keyframe interpolation"); const n = (e.millis - O(r.time).millis) / (O(a.time).millis - O(r.time).millis), o = ns(n, r.easing), c = a.value, l = c.length, h = Math.floor(o * l); return c.slice(0, h); } class os extends Fe { width; height; fillRule; animations = []; clip; renderer; constructor({ width: e = 100, height: s = 100 } = {}) { super(), this.width = e, this.height = s; } connect(e) { return this.clip = e, this; } draw(e) { return this.renderer = e, new Path2D(); } animate(e) { for (const s of this.animations) { const i = s?.frames[0].value; (typeof i == "number" || typeof i == "string" && i.match(/^[0-9]+(\.[0-9]+)?%$/)) && (this[s.key] = bi(s, e.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: x(this.width, this.renderer?.width ?? 0), height: x(this.height, this.renderer?.height ?? 0) }; } get bounds() { const { width: e, height: s } = this.size; return [ { x: 0, y: 0 }, { x: e, y: 0 }, { x: e, y: s }, { x: 0, y: s } ]; } detach() { return this.clip && (this.clip.mask = void 0, this.clip = void 0), this; } } class wl extends os { cx; cy; constructor({ cx: e = 0, cy: s = 0, radius: i, animations: r, ...a } = {}) { super(a), this.cx = e, this.cy = s, i && (this.radius = i), this.animations = r ?? []; } set x(e) { this.cx = e; } set y(e) { this.cy = e; } get x() { return this.cx; } get y() { return this.cy; } get radius() { const e = this.size; return Math.min(e.width, e.height) * 0.5; } set radius(e) { typeof e == "number" ? (this.height = e * 2, this.width = e * 2) : (this.height = `${Number(e.replace("%", "")) * 2}%`, this.width = `${Number(e.replace("%", "")) * 2}%`); } draw(e) { const s = super.draw(e); return s.ellipse( x(this.cx, e.width) * e.resolution, x(this.cy, e.height) * e.resolution, x(this.width, e.width) * e.resolution * 0.5, x(this.height, e.height) * e.resolution * 0.5, 0, 0, 2 * Math.PI ), s; } get bounds() { const { width: e, height: s } = this.size, { cx: i, cy: r } = this, a = x(i, this.renderer?.width ?? 0) - e * 0.5, n = x(r, this.renderer?.height ?? 0) - s * 0.5; return [ { x: a, y: n }, { x: a + e, y: n }, { x: a + e, y: n + s }, { x: a, y: n + s } ]; } } class yl extends os { x; y; radius; constructor({ x: e = 0, y: s = 0, radius: i = 0, animations: r, ...a } = {}) { super(a), this.x = e, this.y = s, this.radius = i, this.animations = r ?? []; } draw(e) { const s = super.draw(e); if (this.radius) { const i = x(this.width, e.width), r = x(this.height, e.height), a = x(this.radius, Math.min(i, r)); s.roundRect( x(this.x, e.width) * e.resolution, x(this.y, e.height) * e.resolution, i * e.resolution, r * e.resolution, a * e.resolution ); } else s.rect( x(this.x, e.width) * e.resolution, x(this.y, e.height) * e.resolution, x(this.width, e.width) * e.resolution, x(this.height, e.height) * e.resolution ); return s; } get bounds() { return super.bounds.map((e) => ({ x: e.x + x(this.x, this.renderer?.width ?? 0), y: e.y + x(this.y, this.renderer?.height ?? 0) })); } } class ki { /** * The canvas element */ canvas = document.createElement("canvas"); /** * The main 2d context of the canvas */ context = this.canvas.getContext("2d"); /** * The resolution of the canvas */ resolution = 1; /** * The width of the canvas */ width = 1920; /** * The height of the canvas */ height = 1080; /** * The background color of the canvas */ background = "#000000"; constructor({ width: e = 1920, height: s = 1080, background: i = "#000000", resolution: r = 1 } = {}) { this.canvas.style.background = i, this.resolution = r, this.background = i, this.resize(e, s); } /** * Resize the canvas */ resize(e, s) { return this.width = Math.round(e), this.height = Math.round(s), this.canvas.width = Math.round(this.width * this.resolution), this.canvas.height = Math.round(this.height * this.resolution), this.context.imageSmoothingEnabled = !1, this; } clear(e) { let s = 0, i = 0, r = this.width * this.resolution, a = this.height * this.resolution; return this.context.fillStyle = this.background, e && (s = x(e.x ?? 0, this.width) * this.resolution, i = x(e.y ?? 0, this.height) * this.resolution, r = x(e.width, this.width) * this.resolution, a = x(e.height, this.height) * this.resolution), this.context.clearRect(s, i, r, a), this.context.fillRect(s, i, r, a), this; } rect(e) { if (this.context.beginPath(), e.radius) { const s = x(e.width, this.width), i = x(e.height, this.height), r = Math.max(x(e.radius, Math.min(s, i)), 0); this.context.roundRect( x(e.x ?? 0, this.width) * this.resolution | 0, x(e.y ?? 0, this.height) * this.resolution | 0, s * this.resolution | 0, i * this.resolution | 0, r * this.resolution | 0 ); } else this.context.rect( x(e.x ?? 0, this.width) * this.resolution | 0, x(e.y ?? 0, this.height) * this.resolution | 0, x(e.width, this.width) * this.resolution | 0, x(e.height, this.height) * this.resolution | 0 ); return this.context.closePath(), this; } circle(e) { return this.context.beginPath(), "height" in e ? this.context.ellipse( x(e.cx, this.width) * this.resolution | 0, x(e.cy, this.height) * this.resolution | 0, x(e.width, this.width) * this.resolution | 0, x(e.height, this.height) * this.resolution | 0, 0, 0, Math.PI * 2 ) : this.context.arc( x(e.cx, this.width) * this.resolution | 0, x(e.cy, this.height) * this.resolution | 0, x(e.radius, Math.min(this.width, this.height) * 0.5) * this.resolution | 0, 0, Math.PI * 2 ), this.context.closePath(), this; } image(e, s) { const { x: i = 0, y: r = 0, width: a, height: n, rotation: o } = s, c = x(i, this.width) * this.resolution | 0, l = x(r, this.height) * this.resolution | 0, h = x(a, this.width) * this.resolution | 0, d = x(n, this.height) * this.resolution | 0; if (o !== void 0 && o !== 0) { this.context.save(), this.context.translate(c + h / 2, l + d / 2); const u = o * Math.PI / 180; this.context.rotate(u); const f = Math.abs(o % 180) === 90; this.context.drawImage( e, (f ? -d / 2 : -h / 2) | 0, (f ? -h / 2 : -d / 2) | 0, (f ? d : h) | 0, (f ? h : d) | 0 ), this.context.restore(); } else this.context.drawImage(e, c, l, h, d); return this; } clip(e, s) { return e instanceof os ? this.context.clip(e.draw(this), e.fillRule) : e && this.context.clip(e, s), this; } opacity(e) { return this.context.globalAlpha = e / 100, this; } transform(e) { return e ? (e.translate && this.context.translate( x(e.translate.x, this.width) * this.resolution | 0, x(e.translate.y, this.height) * this.resolution | 0 ), e.rotate && this.context.rotate(e.rotate * Math.PI / 180), e.scale && this.context.scale( x(e.scale.x, this.width), x(e.scale.y, this.height) ), this) : (this.context.setTransform(1, 0, 0, 1, 0, 0), this); } blendMode(e) { return e && (this.context.globalCompositeOperation = e), this; } save() { return this.context.save(), this; } restore() { return this.context.restore(), this; } filter(e) { return e ? (this.context.filter = e, this) : this; } fill(e, s = !1) { if (!e) return this.context.fillStyle = "transparent", this; if (typeof e.color == "string") this.context.fillStyle = Pe(e.color, e.opacity); else if ("type" in e.color) { const i = this.createGradient(e.color); this.context.fillStyle = i; } else if ("image" in e.color) { const i = this.context.createPattern(e.color.image, e.color.repetition); this.context.fillStyle = i ?? ""; } return s && this.context.fill(), this; } shadow(e) { return e ? (this.context.fillStyle = this.context.shadowColor = Pe(e.color, e.opacity), this.context.shadowOffsetX = (e.offsetX ?? 0) * this.resolution * this.textScale, this.context.shadowOffsetY = (e.offsetY ?? 0) * this.resolution * this.textScale, this.context.shadowBlur = (e.blur ?? 24) * this.resolution * this.textScale, this) : (this.context.shadowColor = "transparent", this.context.shadowBlur = 0, this.context.shadowOffsetX = 0, this.context.shadowOffsetY = 0, this); } stroke(e, s = !1) { return e ? (this.context.strokeStyle = Pe(e.color, e.opacity), this.context.lineWidth = (e.width ?? 1) * this.resolution * this.textScale, e.lineCap && (this.context.lineCap = e.lineCap), e.lineJoin && (this.context.lineJoin = e.lineJoin), e.miterLimit && (this.context.miterLimit = e.miterLimit), s && this.context.stroke(), this) : (this.context.strokeStyle = "transparent", this.context.lineWidth = 0, this.context.lineCap = "butt", this.context.lineJoin = "miter", this.context.miterLimit = 10, this); } /** * Add the renderer to the dom */ mount(e) { e.appendChild(this.canvas); } /** * Remove the renderer from the dom */ unmount() { this.canvas.parentElement?.removeChild(this.canvas); } createGradient(e) { let s; return e.type === "linear" ? s = this.context.createLinearGradient(0, 0, this.canvas.width, 0) : s = this.context.createRadialGradient( this.canvas.width / 2, this.canvas.height / 2, 0, this.canvas.width / 2, this.canvas.height / 2, this.canvas.width / 2 ), e.stops.forEach((i) => { s.addColorStop(i.offset, i.color); }), s; } /** * Draw a watermark on the canvas */ watermark(e) { !e || !this.watermark.length || (this.context.save(), this.context.font = `${46 * this.resolution}px Verdana`, this.context.fillStyle = "#ffffff", this.context.textAlign = "center", this.context.textBaseline = "middle", this.context.strokeStyle = "#000000", this.context.lineWidth = 8 * this.resolution, this.context.strokeText(e, this.width * this.resolution * 0.5, this.height * this.resolution * 0.9), this.context.fillText(e, this.width * this.resolution * 0.5, this.height * this.resolution * 0.9), this.context.restore()); } /** * The scale of the text * @deprecated */ textScale = 4; /** * @deprecated use Text node instead */ text(e) { return e.font && (this.context.font = this.createFontString( e.font.style, e.font.weight, e.font.size * this.resolution, e.font.family )), e.color && (this.context.fillStyle = e.color), e.alignment && (this.context.textAlign = e.alignment), e.baseline && (this.context.textBaseline = e.baseline), this; } /** * @deprecated use Text node instead */ strokeText(e, s) { return this.context.strokeText( e, x(s.x, this.width) * this.resolution, x(s.y, this.height) * this.resolution ), this; } /** * @deprecated use Text node instead */ fillText(e, s) { return this.context.fillText( e, x(s.x, this.width) * this.resolution, x(s.y, this.height) * this.resolution ), this; } /** * @deprecated use Text node instead */ measureText(e, s) { return this.context.font = this.createFontString( s.style, s.weight, s.size, s.family ), this.context.measureText(e); } /** * @deprecated use Text node instead */ createFontString(e, s, i, r) { return `${e ?? "normal"} ${s ?? "400"} ${(i ?? 16) * this.textScale}px ${r ?? "Arial"}`.trim(); } } class Si { context; /** * 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 = pt; /** * The fps used when the ticker is inactive (not playing) */ inactiveFps = Br; /** * 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; /** * Creates a new ticker * @param callback - The function to call when the ticker is updated */ constructor(e = {}) { this.callback = e.callback, this.context = e.context ?? new AudioContext(), this.playbackFps = e.fps ?? 30; } /** * The current time of the hardware in seconds */ get hardwareTime() { return this.context.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(e = 0) { if (this.stopped) return; const i = 1e3 / (this.playing ? this.playbackFps : this.inactiveFps); e - this.lastFrameTime >= i && (this.lastFrameTime = e, await this.callback?.()), requestAnimationFrame(this.timer.bind(this)); } async resumeAudioContext() { this.context.state === "suspended" && await this.context.resume(); } } function Kr(t) { const e = new P(); for (const s of t) s.disabled || s.stop.frames > e.frames && (e.frames = s.stop.frames); return e; } var Gr = Object.defineProperty, Oe = (t, e, s, i) => { for (var r = void 0, a = t.length - 1, n; a >= 0; a--) (n = t[a]) && (r = n(e, s, r) || r); return r && Gr(e, s, r), r; }; const Se = class Ti extends Ke(Fe) { _name; _delay = new P(); _duration = new P(0, 16); data = {}; /** * Flag to check if the clip has been initialized */ initialized = !1; /** * Defines the type of the clip */ type = "base"; /** * Defines the source of the clip which can be * shared with other clips for more efficient * memory usage */ source; /** * Flag to check if the clip has been rendered */ rendered = !1; /** * Timestamp when the clip has been created */ createdAt = /* @__PURE__ */ new Date(); disabled = !1; animations = []; /** * Access the parent layer */ layer; /** * The input that was used to create the clip */ input; /** * Human readable identifier ot the clip */ get name() { return this._name ?? this.source?.name; } set name(e) { this._name = e; } /** * Get the first visible frame */ get start() { return this._delay; } /** * Get the last visible frame */ get stop() { return this._delay.add(this._duration); } /** * Get the delay of the clip */ get delay() { return this._delay; } /** * Get the duration of the clip */ get duration() { return this._duration; } constructor(e = {}) { super(), Object.assign(this, e); } /** * Set the animation time of the clip * and interpolate the values * @param time the current absolute time to render */ animate(e) { return this; } /** * Method for connecting the layer with the clip */ async connect(e) { this.layer = e, this.emit("attach", void 0); } /** * Change clip's offset to zero in seconds. Can be negative */ set delay(e) { this._delay = O(e), this.emit("frame", this._delay.frames); } /** * Set the duration of the clip, needs to be positive */ set duration(e) { this._duration = O(e), this._duration.millis <= 0 && (this._duration.frames = 1), this.emit("frame", this._duration.frames); } /** * Offsets the clip by a given frame number */ offset(e) { return e = O(e), this.delay.addMillis(e.millis), this.emit("offset", e), this.emit("frame", void 0), this; } /** * Triggered when the clip is * added to the composition */ async init(e) { } /** * Triggered when the clip enters the scene */ async enter(e) { } /** * Triggered for each redraw of the scene. */ async update(e) { } /** * Triggered after the clip was updated */ render(e) { } /** * Triggered when the clip exits the scene */ async exit(e) { } /** * Seek the clip to a specific absolute time */ async seek(e) { } /** * Play the clip */ async play(e) { } /** * Pause the clip */ async pause(e) { } /** * Remove the clip from the layer */ detach() { return this.layer?.remove(this), this; } /** * Cleanup the clip after it has been removed from the layer */ cleanup() { this.off("*"); } /** * Trim the clip to the specified start and stop */ trim(e = this.start, s = this.stop) { return e = O(e), s = O(s), this.delay = e, this.duration = s.subtract(e), this; } /** * Split the clip into two clips at the specified time * @param time split, will use the current frame of the composition * a fallback * @returns The clip that was created by performing this action */ async split(e) { if (e = O(e ?? this.layer?.composition?.audio.playbackFrame), !e || e.millis <= this.start.millis || e.millis >= this.stop.millis) throw new K({ code: "split_out_of_range", message: "Cannot split clip at the specified time" }); if (!this.layer) throw new K({ code: "layer_not_attached", message: "Layer must be attached to a layer" }); const s = this.animate(e).copy(); this.duration = e.subtract(this.delay), s.trim(e.addMillis(1), s.stop), s.animations = []; const i = this.layer.clips.findIndex((r) => r.id == this.id); return await this.layer.add(s, i + 1), s; } /** * Create a copy of the clip */ copy() { return Ti.fromJSON(JSON.parse(JSON.stringify(this))); } }; Oe([ I() ], Se.prototype, "_name"); Oe([ I(P) ], Se.prototype, "_delay"); Oe([ I(P) ], Se.prototype, "_duration"); Oe([ I() ], Se.prototype, "data"); Oe([ I() ], Se.prototype, "disabled"); Oe([ I() ], Se.prototype, "animations"); let Te = Se; class cs { /** * Data associated with the source */ data = {}; id = crypto.randomUUID(); type = "base"; mimeType; input; name; constructor(e) { 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.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() : await this.input.arrayBuffer(); } /** * Downloads the file */ async download() { wi(this.input, this.name); } /** * Get a visulization of the source * as an html element */ async thumbnail() { return document.createElement("div"); } } function ls(t) { class e extends t { /** * 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 e; } async function Yr(t) { if (t instanceof Blob) return t.type; if (t.startsWith("<html>")) return "text/html"; let e; try { e = await fetch(t, { method: "HEAD" }); } catch { console.info("Using AbortController fallback"); const r = new AbortController(); e = await fetch(t, { signal: r.signal }), r.abort(); } if (!e.ok) throw new Error(`HTTP error! Status: ${e.status}`); const s = e.headers.get("Content-Type"); if (!s) throw new be({ message: "No content type found", code: "no_content_type_found" }); return s; } function m(t) { if (!t) throw new Error("Assertion failed."); } var ds = (t) => { const e = (t % 360 + 360) % 360; if (e === 0 || e === 90 || e === 180 || e === 270) return e; throw new Error(`Invalid rotation ${t}.`); }, V = (t) => t && t[t.length - 1], Re = (t) => t >= 0 && t < 2 ** 32, E = (t, e, s) => { let i = 0; for (let r = e; r < s; r++) { const a = Math.floor(r / 8), n = t[a], o = 7 - (r & 7), c = (n & 1 << o) >> o; i <<= 1, i |= c; } return i; }, Jr = (t, e, s, i) => { for (let r = e; r < s; r++) { const a = Math.floor(r / 8); let n = t[a]; const o = 7 - (r & 7); n &= ~(1 << o), n |= (i & 1 << s - r - 1) >> s - r - 1 << o, t[a] = n; } }, J = (t) => t instanceof ArrayBuffer ? new Uint8Array(t) : new Uint8Array(t.buffer, t.byteOffset, t.byteLength), ve = (t) => t instanceof ArrayBuffer ? new DataView(t) : new DataView(t.buffer, t.byteOffset, t.byteLength), ce = new TextEncoder(), hs = (t) => Object.fromEntries(Object.entries(t).map(([e, s]) => [s, e])), Me = { bt709: 1, // ITU-R BT.709 bt470bg: 5, // ITU-R BT.470BG smpte170m: 6, // ITU-R BT.601 525 - SMPTE 170M bt2020: 9, // ITU-R BT.202 smpte432: 12 // SMPTE EG 432-1 }, vi = hs(Me), Ae = { bt709: 1, // ITU-R BT.709 smpte170m: 6, // SMPTE 170M linear: 8, // Linear transfer characteristics "iec61966-2-1": 13, // IEC 61966-2-1 pg: 16, // Rec. ITU-R BT.2100-2 perceptual quantization (PQ) system hlg: 18 // Rec. ITU-R BT.2100-2 hybrid loggamma (HLG) system }, xi = hs(Ae), ze = { rgb: 0, // Identity bt709: 1, // ITU-R BT.709 bt470bg: 5, // ITU-R BT.470BG smpte170m: 6, // SMPTE 170M "bt2020-ncl": 9 // ITU-R BT.2020-2 (non-constant luminance) }, Ci = hs(ze), Ii = (t) => !!t && !!t.primaries && !!t.transfer && !!t.matrix && t.fullRange !== void 0, wt = (t) => t instanceof ArrayBuffer || typeof SharedArrayBuffer < "u" && t instanceof SharedArrayBuffer || ArrayBuffer.isView(t) && !(t instanceof DataView), yt = class { constructor() { this.currentPromise = Promise.resolve(); } async acquire() { let t; const e = new Promise((i) => { t = i; }), s = this.currentPromise; return this.currentPromise = e, await s, t; } }, Zr = (t) => [...t].map((e) => e.toString(16).padStart(2, "0")).join(""), ea = (t) => (t = t >> 1 & 1431655765 | (t & 1431655765) << 1, t = t >> 2 & 858993459 | (t & 858993459) << 2, t = t >> 4 & 252645135 | (t & 252645135) << 4, t = t >> 8 & 16711935 | (t & 16711935) << 8, t = t >> 16 & 65535 | (t & 65535) << 16, t >>> 0), W = (t, e, s) => { let i = 0, r = t.length - 1, a = -1; for (; i <= r; ) { const n = i + r >> 1, o = s(t[n]); o === e ? (a = n, r = n - 1) : o < e ? i = n + 1 : r = n - 1; } return a; }, z = (t, e, s) => { let i = -1, r = 0, a = t.length - 1; for (; r <= a; ) { const n = r + (a - r + 1) / 2 | 0; s(t[n]) <= e ? (i = n, r = n + 1) : a = n - 1; } return i; }, X = () => { let t, e; return { promise: new Promise((i, r) => { t = i, e = r; }), resolve: t, reject: e }; }, ta = (t, e) => { const s = t.indexOf(e); s !== -1 && t.splice(s, 1); }, Ei = (t, e) => { for (let s = t.length - 1; s >= 0; s--) if (e(t[s])) return t[s]; }, Pi = (t, e) => { for (let s = t.length - 1; s >= 0; s--) if (e(t[s])) return s; return -1; }, sa = async function* (t) { Symbol.iterator in t ? yield* t[Symbol.iterator]() : yield* t[Symbol.asyncIterator](); }, ia = (t) => { if (!(Symbol.iterator in t) && !(Symbol.asyncIterator in t)) throw new TypeError("Argument must be an iterable or async iterable."); }, _i = (t, e, s) => { const i = t.getUint8(e), r = t.getUint8(e + 1), a = t.getUint8(e + 2); return s ? i | r << 8 | a << 16 : i << 16 | r << 8 | a; }, ra = (t, e, s) => _i(t, e, s) << 8 >> 8, Ri = (t, e, s, i) => { s = s >>> 0, s = s & 16777215, i ? (t.setUint8(e, s & 255), t.setUint8(e + 1, s >>> 8 & 255), t.setUint8(e + 2, s >>> 16 & 255)) : (t.setUint8(e, s >>> 16 & 255), t.setUint8(e + 1, s >>> 8 & 255), t.setUint8(e + 2, s & 255)); }, aa = (t, e, s, i) => { s = Q(s, -8388608, 8388607), s < 0 && (s = s + 16777216 & 16777215), Ri(t, e, s, i); }, na = (t, e, s, i) => { t.setUint32(e + 0, s, !0), t.setInt32(e + 4, Math.floor(s / 2 ** 32), !0); }, Ms = (t, e) => ({ async next() { const s = await t.next(); return s.done ? { value: void 0, done: !0 } : { value: e(s.value), done: !1 }; }, return() { return t.return(); }, throw(s) { return t.throw(s); }, [Symbol.asyncIterator]() { return this; } }), Q = (t, e, s) => Math.max(e, Math.min(s, t)), le = "und", us = (t, e) => { const s = 10 ** e; return Math.round(t * s) / s; }, oa = (t, e) => Math.round(t / e) * e, ca = (t) => { let e = 0; for (; t; ) e++, t >>= 1; return e; }, la = /^[a-z]{3}$/, ms = (t) => la.test(t), ue = 1e6 * (1 + Number.EPSILON), Ht = (t, e) => { const s = { ...t }; for (const i in e) typeof t[i] == "object" && t[i] !== null && typeof e[i] == "object" && e[i] !== null ? s[i] = Ht( t[i], e[i] ) : s[i] = e[i]; return s; }, As = async (t, e, s) => { let i = 0; for (; ; ) try { return await fetch(t, e); } catch (r) { console.error("Retrying failed f