UNPKG

@diffusionstudio/core-v2

Version:
1,697 lines (1,694 loc) 143 kB
/*! * :::::::: :::::::: ::::::::: :::::::::: * :+: :+: :+: :+: :+: :+: :+: * +:+ +:+ +:+ +:+ +:+ +:+ * +#+ +#+ +:+ +#++:++#: +#++:++# * +#+ +#+ +#+ +#+ +#+ +#+ * #+# #+# #+# #+# #+# #+# #+# * ######## ######## ### ### ########## * ::: ::: :::::::: * :+: :+: :+: :+: * +:+ +:+ +:+ * +#+ +:+ +#+ * +#+ +#+ +#+ * #+#+#+# #+# * ### ########## * * @diffusionstudio/core-v2 v2.0.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 */ import { ArrayBufferTarget as ue, StreamTarget as fe, FileSystemWritableFileStreamTarget as me, Muxer as pe } from "mp4-muxer"; const G = 30; class et extends Error { message; code; constructor({ message: t = "", code: e = "" }, ...s) { super(t, ...s), console.error(t), this.code = e, this.message = t; } } class S extends et { } class w extends et { } class D extends et { } class Et extends et { } function ge(r, t = G) { if (t < 1) throw new w({ code: "invalidArgument", message: "FPS must be greater or equal to 1" }); return Math.round(r * t); } function js(r, t = G) { if (t < 1) throw new w({ code: "invalidArgument", message: "FPS must be greater or equal to 1" }); return Math.round(r / t * 1e3) / 1e3; } function Q(r, t = G) { if (t < 1) throw new w({ code: "invalidArgument", message: "FPS must be greater or equal to 1" }); return Math.round(r / t * 1e3); } class f { /** * 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, e = 0, s = 0, i = 0) { this.time = Math.round(t + e * 1e3 + s * 6e4 + i * 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 ge(this.millis / 1e3); } set frames(t) { this.millis = Q(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 e = Q(t); return this.millis = this.millis + e, 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 f(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 f(this.millis - t.millis); } /** * Create a new timestamp from frames */ static fromFrames(t, e) { const s = new f(); return s.millis = Q(t, e), s; } /** * get a copy of the object */ copy() { return new f(this.millis); } toJSON() { return this.millis; } static fromJSON(t) { return new f(t); } } function Hs(r) { return Math.floor(r * 255).toString(16).padStart(2, "0").toUpperCase(); } function _s(r, t) { return r.reduce( (e, s) => { const i = s[t]; return e[i] || (e[i] = []), e[i].push(s), e; }, // @ts-ignore {} ); } function Ct(r, t) { return [r.slice(0, t), r.slice(t)].filter((e) => e.length > 0); } function A(r, t) { return t ? Math.floor(Math.random() * (t - r + 1) + r) : r; } async function Qs(r) { r <= 0 || await new Promise((t) => setTimeout(t, r)); } function As(r) { if (!r) throw "Assertion failed!"; } function Ks(r, t = 300) { let e; return (...s) => { clearTimeout(e), e = setTimeout(() => { r.apply(r, s); }, t); }; } function ye(r, t, e) { e < 0 && (e = 0); const s = r[t]; r.splice(t, 1), r.splice(e, 0, s); } function Ds() { return crypto.randomUUID().split("-").at(0); } function Ot(r) { return typeof r != "function" ? !1 : /^class\s/.test(Function.prototype.toString.call(r)); } function $s(r) { return r.charAt(0).toUpperCase() + r.slice(1); } function we(r) { if (r.numberOfChannels === 1) return r.getChannelData(0); const t = []; for (let o = 0; o < r.numberOfChannels; o++) t.push(r.getChannelData(o)); const e = Math.max(...t.map((o) => o.length)), s = new Float32Array(e * r.numberOfChannels); let i = 0, n = 0; for (; n < e; ) t.forEach((o) => { s[i++] = o[n] !== void 0 ? o[n] : 0; }), n++; return s; } function H(r, t, e) { for (let s = 0; s < e.length; s++) r.setUint8(t + s, e.charCodeAt(s)); } function be(r, t, e) { for (let s = 0; s < t.length; s++, e += 2) { const i = Math.max(-1, Math.min(1, t[s])); r.setInt16(e, i < 0 ? i * 32768 : i * 32767, !0); } return r; } function xe(r, t, e) { const n = t * 2, o = 8, a = 36, l = r.length * 2, c = a + l, u = new ArrayBuffer(o + c), h = new DataView(u); return H(h, 0, "RIFF"), h.setUint32(4, c, !0), H(h, 8, "WAVE"), H(h, 12, "fmt "), h.setUint32(16, 16, !0), h.setUint16(20, 1, !0), h.setUint16(22, t, !0), h.setUint32(24, e, !0), h.setUint32(28, e * n, !0), h.setUint16(32, n, !0), h.setUint16(34, 16, !0), H(h, 36, "data"), h.setUint32(40, l, !0), be(h, r, o + a); } function qs(r, t = "audio/wav") { const e = we(r), s = xe(e, r.numberOfChannels, r.sampleRate); return new Blob([s], { type: t }); } function Ze(r) { const t = new Float32Array(r.length * r.numberOfChannels); let e = 0; for (let s = 0; s < r.numberOfChannels; s++) { const i = r.getChannelData(s); t.set(i, e), e += i.length; } return t; } function Ve(r) { const t = r.numberOfChannels, e = r.length, s = new Int16Array(e * t); for (let i = 0; i < e; i++) for (let n = 0; n < t; n++) { let o = r.getChannelData(n)[i] * 32767; o > 32767 && (o = 32767), o < -32767 && (o = -32767), s[i * t + n] = o; } return s; } async function ti(r, t = 22050, e = Math.sqrt(2)) { const s = await r.arrayBuffer(), i = new OfflineAudioContext({ sampleRate: t, length: 1 }), n = await i.decodeAudioData(s), o = i.createBuffer(1, n.length, t); if (n.numberOfChannels >= 2) { const a = n.getChannelData(0), l = n.getChannelData(1), c = o.getChannelData(0); for (let u = 0; u < n.length; ++u) c[u] = e * (a[u] + l[u]) / 2; return o; } return n; } function ei(r, t = 44100, e = 2) { if (r.sampleRate == t && r.numberOfChannels == e) return r; const s = Math.floor(r.duration * t), n = new OfflineAudioContext(e, 1, t).createBuffer(e, s, t); for (let o = 0; o < r.numberOfChannels; o++) { const a = r.getChannelData(o), l = n.getChannelData(o), c = r.sampleRate / t; for (let u = 0; u < l.length; u++) { const h = u * c, p = Math.floor(h), g = Math.ceil(h); if (g >= a.length) l[u] = a[p]; else { const y = h - p; l[u] = a[p] * (1 - y) + a[g] * y; } } } return n; } async function ke(r) { const { fps: t, height: e, width: s, bitrate: i } = r, n = [ "avc1.640034", "avc1.4d0034", "avc1.640028", "avc1.640C32", "avc1.64001f", "avc1.42001E" // TODO: 'hev1.1.6.L93.B0', 'hev1.2.4.L93.B0', 'vp09.00.10.08', 'av01.0.04M.08', 'vp8', ], o = ["prefer-hardware", "prefer-software"], a = []; for (const c of n) for (const u of o) a.push({ codec: c, hardwareAcceleration: u, width: s, height: e, bitrate: i, framerate: t }); const l = []; if (!("VideoEncoder" in window)) return l; for (const c of a) { const u = await VideoEncoder.isConfigSupported(c); u.supported && l.push(u.config ?? c); } return l.sort(ve); } async function We(r) { const { sampleRate: t, numberOfChannels: e, bitrate: s } = r, i = ["mp4a.40.2", "opus"], n = []; if (!("AudioEncoder" in window)) return n; for (const o of i) { const a = await AudioEncoder.isConfigSupported({ codec: o, numberOfChannels: e, bitrate: s, sampleRate: t }); a.supported && a.config && n.push(a.config); } return n; } async function Re(r) { const t = await We(r.audio), e = await ke(r.video); if (!e.length) throw new D({ message: "Encoder can't be configured with any of the tested codecs", code: "codecsNotSupported" }); return [e[0], t[0]]; } function ve(r, t) { const e = r.hardwareAcceleration ?? "", s = t.hardwareAcceleration ?? ""; return e < s ? -1 : e > s ? 1 : 0; } async function Qt(r, t = "untitled") { const e = document.createElement("a"); if (document.head.appendChild(e), e.download = t, typeof r == "string" && r.startsWith("data:image/svg+xml;base64,")) { const s = r.split(",")[1], i = atob(s), n = new Array(i.length); for (let l = 0; l < i.length; l++) n[l] = i.charCodeAt(l); const o = new Uint8Array(n), a = new Blob([o], { type: "image/svg+xml" }); e.href = URL.createObjectURL(a), e.download = t.split(".")[0] + ".svg"; } else typeof r == "string" ? e.href = r : e.href = URL.createObjectURL(r); e.click(), e.remove(); } async function si(r, t = !0) { return new Promise((e) => { const s = document.createElement("input"); s.type = "file", s.accept = r, s.multiple = t, s.onchange = (i) => { const n = Array.from(i.target?.files ?? []); e(n); }, s.click(); }); } function Yt(r) { return `${r.hours.toString().padStart(2, "0")}:${r.minutes.toString().padStart(2, "0")}:${r.seconds.toString().padStart(2, "0")},` + r.milliseconds.toString().padStart(3, "0"); } function Jt(r) { const t = new Date(1970, 0, 1); return t.setSeconds(r), t.setMilliseconds(Math.round(r % 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 f(); } get stop() { return this.words.at(-1)?.stop ?? new f(); } } var wt = /* @__PURE__ */ ((r) => (r.en = "en", r.de = "de", r))(wt || {}); class bt { /** * 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, e, s, i) { this.text = t, this.start = new f(e), this.stop = new f(s), this.confidence = i; } /** * Defines the time between start * and stop returned as a timestamp */ get duration() { return this.stop.subtract(this.start); } } class v { id = crypto.randomUUID(); language = wt.en; groups = []; get text() { return this.groups.map(({ text: t }) => t).join(" "); } get words() { return this.groups.flatMap(({ words: t }) => t); } constructor(t = [], e = wt.en) { this.groups = t, this.language = e; } /** * Iterate over all words in groups */ *iter({ count: t, duration: e, length: s }) { for (const i of this.groups) { let n; for (const [o, a] of i.words.entries()) n && (t && n.words.length >= A(...t) ? (yield n, n = void 0) : e && n?.duration.seconds >= A(...e) ? (yield n, n = void 0) : s && n.text.length >= A(...s) && (yield n, n = void 0)), n ? n.words.push(a) : n = new Y([a]), o == i.words.length - 1 && (yield n); } } /** * This method will optimize the transcipt for display */ optimize() { const t = this.groups.flatMap((e) => e.words); for (let e = 0; e < t.length - 1; e++) { const s = t[e], i = t[e + 1]; i.start.millis - s.stop.millis < 0 ? i.start.millis = s.stop.millis + 1 : s.stop.millis = i.start.millis - 1; } return this; } /** * Convert the transcript into a SRT compatible * string and downloadable blob */ toSRT(t = {}) { let e = 1, s = ""; for (const i of this.iter(t)) { const n = Jt(i.start.seconds), o = Jt(i.stop.seconds); s += `${e} ` + Yt(n) + " --> " + Yt(o) + ` ${i.text} `, e += 1; } return { text: s, blob: new Blob([s], { type: "text/plain;charset=utf8" }) }; } toJSON() { return this.groups.map( (t) => t.words.map((e) => ({ token: e.text, start: e.start.millis, stop: e.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, e = !0) { let s = 0; const i = []; for (const n of this.groups) for (const o of n.words) if (i.length == 0 && e && (s = o.start.millis), i.push(new bt(o.text, o.start.millis - s, o.stop.millis - s)), i.length == t) return new v([new Y(i)]); return new v([new Y(i)]); } /** * Create a deep copy of the transcript * @returns A new Transcript instance */ copy() { return v.fromJSON(this.toJSON()); } static fromJSON(t) { const e = new v(); for (const s of t) { const i = new Y(); for (const n of s) i.words.push(new bt(n.token, n.start, n.stop)); e.groups.push(i); } return e; } /** * Fetch captions from an external resource and parse them. JSON needs * to be of the form `{ token: string; start: number; stop: number; }[][]` * @param input Location of the captions * @param init Additional fetch parameters such as method or headers * @returns A Transcript with processed captions */ static async from(t, e) { if (t instanceof File) return v.fromJSON(JSON.parse(await t.text())); const s = await fetch(t, e); if (!s.ok) throw new S({ code: "unexpectedIOError", message: "An unexpected error occurred while fetching the file" }); return v.fromJSON(await s.json()); } } class Z { /** * Unique identifier of the object */ id = crypto.randomUUID(); toJSON() { const t = {}; return (this.constructor.__serializableProperties || []).forEach(({ propertyKey: s, serializer: i }) => { const n = this[s]; i && n instanceof i ? t[s] = n.toJSON() : t[s] = n; }), t; } static fromJSON(t) { const e = new this(); return (this.__serializableProperties || []).forEach(({ propertyKey: i, serializer: n }) => { if (t.hasOwnProperty(i)) if (n) { const o = n.fromJSON(t[i]); e[i] = o; } else e[i] = t[i]; }), e; } } function d(r) { return function(t, e) { t.constructor.__serializableProperties || (t.constructor.__serializableProperties = []), t.constructor.__serializableProperties.push({ propertyKey: e, serializer: r }); }; } function T(r) { return class extends r { _handlers = {}; on(e, s) { if (typeof s != "function") throw new Error("The callback of an event listener needs to be a function."); const i = crypto.randomUUID(); return this._handlers[e] ? this._handlers[e][i] = s : this._handlers[e] = { [i]: s }, i; } off(e, ...s) { if (e) { if (e === "*") { this._handlers = {}; return; } for (const i of Object.values(this._handlers)) e in i && delete i[e]; for (const i of s) this.off(i); } } emit(e, s) { const i = new CustomEvent(e, { detail: s }); Object.defineProperty(i, "currentTarget", { writable: !1, value: this }); for (const n in this._handlers[e] ?? {}) this._handlers[e]?.[n](i); for (const n in this._handlers["*"] ?? {}) this._handlers["*"]?.[n](i); } bubble(e) { return this.on("*", (s) => { e.emit(s.type, s.detail); }); } resolve(e) { return (s, i) => { this.on("error", i), this.on(e, s); }; } }; } class ft extends T(Z) { _key; _value; _store; loaded = !1; constructor(t, e, s) { super(), this._store = t, this._key = e, this.initValue(s); } 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 ri { storageEngine; namespace; constructor(t, e = localStorage) { this.storageEngine = e, this.namespace = t; } define(t, e, s) { const i = this.get(t); return i === null ? (this.set(t, e), new ft(this, t, e)) : s && i != null ? new ft(this, t, s(i)) : new ft(this, t, i); } set(t, e) { this.storageEngine.setItem( this.getStorageId(t), JSON.stringify({ value: e }) ); } get(t) { const e = this.storageEngine.getItem(this.getStorageId(t)); return e ? JSON.parse(e).value : null; } remove(t) { this.storageEngine.removeItem(this.getStorageId(t)); } getStorageId(t) { return this.namespace ? `${this.namespace}.${t}` : t; } } class ni { worker; constructor(t) { this.worker = new t(), this.worker.onerror = console.error; } async run(t, e) { return this.worker.postMessage({ type: "init", ...t ?? {} }), await new Promise((s, i) => { this.worker.addEventListener("message", (n) => { e?.(n.data), n.data.type == "result" && (n.data.type = void 0, s(n.data)), n.data.type == "error" && i(n.data.message); }); }).then((s) => ({ result: s, error: void 0 })).catch((s) => ({ result: void 0, error: s })).finally(() => { this.worker.terminate(); }); } } function oi(r) { return async (t) => { try { await r(t); } catch (e) { self.postMessage({ type: "error", message: e?.message ?? "An unkown worker error occured" }); } }; } function Se() { return class extends T(class { }) { }; } function Fe(r) { const t = r.startsWith("#") ? r.slice(1) : r, e = parseInt(t, 16), s = e >> 16 & 255, i = e >> 8 & 255, n = e & 255; return { r: s, g: i, b: n }; } function Xe(r, t, e) { return `#${((1 << 24) + (Math.round(r) << 16) + (Math.round(t) << 8) + Math.round(e)).toString(16).slice(1)}`; } function vt(r, t) { switch (t) { case "ease-in": return r * r; case "ease-out": return r * (2 - r); case "ease-in-out": return r < 0.5 ? 2 * r * r : -1 + (4 - 2 * r) * r; case "ease-out-in": if (r < 0.5) { const e = r * 2; return e * (2 - e) / 2; } else { const e = (r - 0.5) * 2; return e * e / 2 + 0.5; } default: return r; } } function K(r, t, e) { return r + (t - r) * e; } function It(r) { const t = Fe(r); return Ge(t.r, t.g, t.b); } function Ge(r, t, e) { r /= 255, t /= 255, e /= 255; const s = Math.max(r, t, e), i = Math.min(r, t, e); let n = 0, o = 0; const a = (s + i) / 2; if (s !== i) { const l = s - i; switch (o = a > 0.5 ? l / (2 - s - i) : l / (s + i), s) { case r: n = (t - e) / l + (t < e ? 6 : 0); break; case t: n = (e - r) / l + 2; break; case e: n = (r - t) / l + 4; break; } n /= 6; } return { h: Math.round(n * 360), s: Math.round(o * 100), l: Math.round(a * 100) }; } function Te(r, t, e) { t /= 100, e /= 100, r = (r + 360) % 360; function s(c, u, h) { return h < 0 && (h += 1), h > 1 && (h -= 1), h < 1 / 6 ? c + (u - c) * 6 * h : h < 1 / 2 ? u : h < 2 / 3 ? c + (u - c) * (2 / 3 - h) * 6 : c; } const i = e < 0.5 ? e * (1 + t) : e + t - e * t, n = 2 * e - i, o = s(n, i, r / 360 + 1 / 3), a = s(n, i, r / 360), l = s(n, i, r / 360 - 1 / 3); return Xe( Math.round(o * 255), Math.round(a * 255), Math.round(l * 255) ); } function Ne(r, t) { const { frames: e, extrapolate: s = "clamp", easing: i } = r; if (t <= e[0].frame) return e[0].value; if (t >= e[e.length - 1].frame) return e[e.length - 1].value; let n, o; for (let g = 0; g < e.length - 1; g++) if (t >= e[g].frame && t <= e[g + 1].frame) { n = e[g], o = e[g + 1]; break; } if (!n || !o) throw new Error("Unexpected error in keyframe interpolation"); const a = (t - n.frame) / (o.frame - n.frame), l = vt(a, n.easing ?? i), c = It(n.value), u = It(o.value); let h = c.h, p = u.h; return Math.abs(p - h) > 180 && (h < p ? h += 360 : p += 360), Te( K(h, p, l), K(c.s, u.s, l), K(c.l, u.l, l) ); } function At(r, t) { const { frames: e, extrapolate: s = "clamp", easing: i } = r; if (t <= e[0].frame) return e[0].value; if (t >= e[e.length - 1].frame) return e[e.length - 1].value; let n, o; for (let c = 0; c < e.length - 1; c++) if (t >= e[c].frame && t <= e[c + 1].frame) { n = e[c], o = e[c + 1]; break; } if (!n || !o) throw new Error("Unexpected error in keyframe interpolation"); const a = (t - n.frame) / (o.frame - n.frame), l = vt(a, n.easing ?? i); return K(n.value, o.value, l); } function Ue(r, t) { const { frames: e, extrapolate: s = "clamp" } = r, i = e.map((a) => ({ value: parseFloat(a.value.replace("%", "")), // Remove '%' and convert to number frame: a.frame, easing: a.easing })), o = At({ key: "", extrapolate: s, frames: i }, t); return `${Math.round(o)}%`; } function Me(r, t) { const { frames: e, extrapolate: s = "clamp" } = r; if (t <= e[0].frame) return e[0].value; if (t >= e[e.length - 1].frame) return e[e.length - 1].value; let i, n; for (let h = 0; h < e.length - 1; h++) if (t >= e[h].frame && t <= e[h + 1].frame) { i = e[h], n = e[h + 1]; break; } if (!i || !n) throw new Error("Unexpected error in keyframe interpolation"); const o = (t - i.frame) / (n.frame - i.frame), a = vt(o, i.easing), l = n.value, c = l.length, u = Math.floor(a * c); return l.slice(0, u); } var Ee = Object.defineProperty, C = (r, t, e, s) => { for (var i = void 0, n = r.length - 1, o; n >= 0; n--) (o = r[n]) && (i = o(t, e, i) || i); return i && Ee(t, e, i), i; }; const N = class Kt extends T(Z) { _name; _delay = new f(); _duration = new f(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 with a * one-to-many (1:n) relationship */ 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 track */ track; /** * Human readable identifier ot the clip */ get name() { return this._name ?? this.source?.name; } set name(t) { this._name = t; } /** * 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(t = {}) { super(), Object.assign(this, t); } /** * Set the animation time of the clip * and interpolate the values * @param time the current absolute time to render */ animate(t) { const e = t.subtract(this.start).frames; for (const s of this.animations) { const i = s?.frames[0].value; typeof i == "number" ? this[s.key] = At(s, e) : typeof i == "string" && i.match(/^[0-9]{1,}%$/) ? this[s.key] = Ue(s, e) : typeof i == "string" && i.match(/^#[0-9abcdef]{3,8}$/i) ? this[s.key] = Ne(s, e) : typeof i == "string" && (this[s.key] = Me(s, e)); } return this; } /** * Method for connecting the track with the clip */ async connect(t) { this.track = t, this.emit("attach", void 0); } /** * Change clip's offset to zero in seconds. Can be negative */ set delay(t) { typeof t == "number" ? this._delay.frames = t : this._delay = t, this.emit("frame", this._delay.frames); } /** * Set the duration of the clip, needs to be positive */ set duration(t) { typeof t == "number" ? this._duration.frames = t : this._duration = t, this._duration.millis <= 0 && (this._duration.frames = 1), this.emit("frame", this._duration.frames); } /** * Offsets the clip by a given frame number */ offset(t) { return typeof t == "number" ? (this.delay.addFrames(t), this.emit("offset", f.fromFrames(t))) : (this.delay.addMillis(t.millis), this.emit("offset", t)), this.emit("frame", void 0), this; } /** * Triggered when the clip is * added to the composition */ async init() { } /** * Triggered when the clip enters the scene */ enter() { } /** * Triggered for each redraw of the scene. * Can return a promise which will be awaited * during export. * @param time the current time to render */ update(t, e, s = "pause", i = G) { } render(t, e) { } /** * Triggered when the clip exits the scene */ exit() { } /** * Remove the clip from the track */ detach() { return this.track?.remove(this), this; } /** * Trim the clip to the specified start and stop */ trim(t = this.start, e = this.stop) { return typeof e == "number" && (e = f.fromFrames(e)), typeof t == "number" && (t = f.fromFrames(t)), this.delay = t, this.duration = e.subtract(t), 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(t) { if (t || (t = this.track?.composition?.ticker.frame), typeof t == "number" && (t = f.fromFrames(t)), !t || t.millis <= this.start.millis || t.millis >= this.stop.millis) throw new w({ code: "splitOutOfRange", message: "Cannot split clip at the specified time" }); if (!this.track) throw new w({ code: "trackNotAttached", message: "Track must be attached to a track" }); const e = this.animate(t).copy(); this.duration = t.subtract(this.delay), e.trim(t.addMillis(1), e.stop), e.animations = []; const s = this.track.clips.findIndex((i) => i.id == this.id); return await this.track.add(e, s + 1), e; } /** * Create a copy of the clip */ copy() { return Kt.fromJSON(JSON.parse(JSON.stringify(this))); } }; C([ d() ], N.prototype, "_name"); C([ d(f) ], N.prototype, "_delay"); C([ d(f) ], N.prototype, "_duration"); C([ d() ], N.prototype, "data"); C([ d() ], N.prototype, "disabled"); C([ d() ], N.prototype, "animations"); let U = N; var Ce = Object.defineProperty, M = (r, t, e, s) => { for (var i = void 0, n = r.length - 1, o; n >= 0; n--) (o = r[n]) && (i = o(t, e, i) || i); return i && Ce(t, e, i), i; }; class V extends T(Z) { duration = new f(0, 16); /** * Indicates if the track is loading */ state = "IDLE"; data = {}; /** * Indicates whether the source is used inside the composition */ added = !1; type = "base"; name = ""; mimeType; externalURL; external = !1; /** * Access to the data of the source */ file; /** * Access to the element that is used to parse * the source */ element = new Image(); /** * The object url of the source */ get objectURL() { return this.element.src; } /** * Method for retrieving the file when * it has been loaded * @returns Loaded File */ async getFile() { if (!this.file && this.state == "LOADING" && await new Promise(this.resolve("load")), !this.file) throw new w({ code: "fileNotAccessible", message: "The desired file cannot be accessed" }); return this.file; } async loadElement() { this.state != "READY" && (this.element.setAttribute("src", URL.createObjectURL(await this.getFile())), await new Promise((t, e) => { this.element.onload = () => t(), this.element.onerror = () => e( new S({ code: "sourceNotProcessable", message: "An error occurred while processing the input medium." }) ); })); } async loadFile(t) { this.name = t.name, this.mimeType = kt(t.type), this.external = !1, this.file = t; } async loadUrl(t, e) { const s = await fetch(t, e); if (!s?.ok) throw new S({ code: "unexpectedIOError", message: "An unexpected error occurred while fetching the file" }); const i = await s.blob(); this.name = t.toString().split("/").at(-1) ?? "", this.external = !0, this.file = new File([i], this.name, { type: i.type }), this.externalURL = t, this.mimeType = kt(i.type); } async from(t, e) { try { this.state = "LOADING", t instanceof File ? await this.loadFile(t) : await this.loadUrl(t, e), await this.loadElement(), this.state = "READY", this.emit("load", void 0); } catch (s) { throw this.state = "ERROR", this.emit("error", new Error(String(s))), s; } return this; } async loaded() { if (this.state != "READY") { if (this.state == "ERROR") throw new S({ code: "sourceNotProcessable", message: "An error occurred while processing the input medium." }); this.state == "IDLE" && this.file ? await this.from(this.file) : await new Promise(this.resolve("load")); } } /** * Get the source as an array buffer */ async arrayBuffer() { return await (await this.getFile()).arrayBuffer(); } /** * Clean up the data associated with this object */ async remove() { this.state = "IDLE", this.element.removeAttribute("src"), this.externalURL = void 0, delete this.file; } /** * Downloads the file */ async download() { const t = await this.getFile(); Qt(t, this.name); } /** * Get a visulization of the source * as an html element */ async thumbnail() { return this.element; } /** * Create a new source for the specified input */ static async from(t, e, s = new this()) { return s.from(t, e); } } M([ d() ], V.prototype, "duration"); M([ d() ], V.prototype, "data"); M([ d() ], V.prototype, "type"); M([ d() ], V.prototype, "name"); M([ d() ], V.prototype, "mimeType"); M([ d() ], V.prototype, "externalURL"); M([ d() ], V.prototype, "external"); const zt = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3C/svg%3E"; function Oe(r) { const t = new TextEncoder().encode(r); let e = ""; const s = t.byteLength; for (let i = 0; i < s; i++) e += String.fromCharCode(t[i]); return btoa(e); } function Ye(r) { if (!r || !r.body) return zt; const t = r.body.scrollWidth, e = r.body.scrollHeight, s = r.cloneNode(!0), i = s.getElementsByTagName("style").item(0), n = s.getElementsByTagName("body").item(0); if (n?.setAttribute("xmlns", "http://www.w3.org/1999/xhtml"), !n) return zt; const o = new XMLSerializer(), a = i ? o.serializeToString(i) : "", l = o.serializeToString(n), c = ` <svg xmlns="http://www.w3.org/2000/svg" width="${t}" height="${e}"> body { padding: 0; } ${a} <foreignObject width="100%" height="100%"> ${l} </foreignObject> </svg>`; return "data:image/svg+xml;base64," + Oe(c); } function J(r) { class t extends r { /** * The height of the source */ get height() { return 1080; } /** * The width of the source */ get width() { return 1920; } /** * The aspect ratio of the source */ get aspectRatio() { return this.width / this.height; } } return t; } class Je extends J(V) { } class xt extends J(V) { type = "html"; /** * Access to the iframe that is required * for extracting the html's dimensions */ element = document.createElement("iframe"); constructor() { super(), 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); } get height() { return this.element.contentWindow?.document.body.scrollHeight ?? 0; } get width() { return this.element.contentWindow?.document.body.scrollWidth ?? 0; } /** * Access to the html document as loaded * within the iframe. Can be manipulated with * javascript */ get document() { return this.element.contentWindow?.document; } get imageUrl() { return Ye(this.document); } async thumbnail() { await this.loaded(); const t = new Image(); return t.className = "object-contain w-full aspect-video h-auto", t.src = this.imageUrl, t; } } function Ie(r, t = {}) { const { threshold: e = 0.02, hopSize: s = 1024, minDuration: i = 500 } = t, n = [], o = r.getChannelData(0), a = r.sampleRate, l = Math.floor(i / 1e3 * a); let c = null, u = 0; for (let h = 0; h < o.length; h += s) { let p = 0; const g = Math.min(h + s, o.length); for (let y = h; y < g; y++) p += o[y] * o[y]; p = Math.sqrt(p / (g - h)), p < e ? (u += s, c === null && (c = h)) : (c !== null && u >= l && n.push({ start: new f(0, c / a), stop: new f(0, h / a) }), c = null, u = 0); } return c !== null && u >= l && n.push({ start: new f(0, c / a), stop: new f(0, o.length / a) }), n; } const mt = 3e3; class X extends J(V) { decoding = !1; _silences; duration = new f(0, 0, 0, 1); type = "audio"; element = new Audio(); transcript; audioBuffer; async loadElement() { this.state != "READY" && (this.element.src = URL.createObjectURL(await this.getFile()), await new Promise((t, e) => { this.element.onloadedmetadata = () => { this.duration.seconds = this.element.duration, t(); }, this.element.onerror = () => e( new S({ code: "sourceNotProcessable", message: "An error occurred while processing the input medium." }) ); })); } async decode(t = 2, e = 48e3, s = !1) { if (this.decoding && s && (await new Promise(this.resolve("update")), this.audioBuffer)) return this.audioBuffer; this.decoding = !0; const i = await this.arrayBuffer(), o = await new OfflineAudioContext(t, 1, e).decodeAudioData(i); return this.duration.seconds = o.duration, s && (this.audioBuffer = o), this.decoding = !1, this.emit("update", void 0), o; } /** * 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: e = 0, stop: s, logarithmic: i = !1 } = {}) { typeof e == "object" && (e = e.millis), typeof s == "object" && (s = s.millis); const n = this.audioBuffer ?? await this.decode(1, mt, !0), o = n.getChannelData(0), a = Math.floor(Math.max(e * mt / 1e3, 0)), l = s ? Math.floor(Math.min(s * mt / 1e3, n.length)) : n.length, c = Math.floor((l - a) / t), u = new Float32Array(t); for (let h = 0; h < t; h++) { const p = a + h * c, g = p + c; let y = Number.NEGATIVE_INFINITY; for (let b = p; b < g; b++) { const Mt = o[b]; Mt > y && (y = Mt); } u[h] = i ? Math.log2(1 + y) : y; } return u; } async thumbnail(t = {}) { const e = await this.sample(t), s = document.createElement("div"); s.className = "flex flex-row absolute inset-0 audio-samples"; for (const i of e) { const n = document.createElement("div"); n.className = "audio-sample-item", n.style.height = `${i * 100}%`, s.appendChild(n); } return s; } /** * 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 = {}) { if (this._silences) return this._silences; const e = await this.arrayBuffer(), i = await new OfflineAudioContext(1, 1, 24e3).decodeAudioData(e); return this._silences = Ie(i, t), this._silences; } } class Zt extends J(V) { type = "image"; get height() { return this.element.naturalHeight; } get width() { return this.element.naturalWidth; } async thumbnail() { return this.element.className = "object-cover w-full aspect-video h-auto", this.element; } } class Vt extends J(X) { downloadInProgress = !0; type = "video"; element = document.createElement("video"); get height() { return this.element.videoHeight; } get width() { return this.element.videoWidth; } async loadElement() { this.state != "READY" && (this.element.controls = !1, this.element.playsInline = !0, this.element.style.display = "hidden", this.element.crossOrigin = "anonymous", this.element.src = this.external ? `${this.externalURL}` : URL.createObjectURL(await this.getFile()), await new Promise((t, e) => { this.element.onloadedmetadata = () => { this.duration.seconds = this.element.duration, t(); }, this.element.onerror = () => e( new S({ code: "sourceNotProcessable", message: "An error occurred while processing the input medium." }) ); })); } async loadUrl(t, e) { const s = await fetch(t, e); if (!s?.ok) throw new S({ code: "unexpectedIOError", message: "An unexpected error occurred while fetching the file" }); this.name = t.toString().split("/").at(-1) ?? "", this.external = !0, this.externalURL = t, this.mimeType = kt(s.headers.get("Content-type")), this.downloadInProgress = !0, s.blob().then((i) => { this.file = new File([i], this.name, { type: i.type }), this.emit("load", void 0); }).finally(() => { this.downloadInProgress = !1; }); } async getFile() { if (!this.file && this.downloadInProgress && await new Promise(this.resolve("load")), !this.file) throw new w({ code: "fileNotAccessible", message: "The desired file cannot be accessed" }); return this.file; } async thumbnail() { return this.element.className = "object-cover w-full aspect-video h-auto", this.element.controls = !1, this.element.addEventListener("mousemove", (t) => { const e = this.element.getBoundingClientRect(), s = t.clientX - (e?.left ?? 0), i = this.element.duration; i && e && e.width > 0 && (this.element.currentTime = Math.round(i * (s / e.width))); }), this.element; } } class ai { static fromType(t) { switch (t.type) { case "video": return new F(); case "audio": return new q(); case "html": return new tt(); case "image": return new $(); case "text": return new it(); default: return new U(); } } static fromSource(t) { if (t.type == "audio" && t instanceof X) return new q(t); if (t.type == "video" && t instanceof Vt) return new F(t); if (t.type == "image" && t instanceof Zt) return new $(t); if (t.type == "html" && t instanceof xt) return new tt(t); } } class ze { static fromJSON(t) { return [new f(t[0]), new f(t[1])]; } } var Le = Object.defineProperty, Be = Object.getOwnPropertyDescriptor, st = (r, t, e, s) => { for (var i = s > 1 ? void 0 : s ? Be(t, e) : t, n = r.length - 1, o; n >= 0; n--) (o = r[n]) && (i = (s ? o(t, e, i) : o(i)) || i); return s && i && Le(t, e, i), i; }; const I = class Dt extends U { source = new X(); /** * Is the media currently playing */ playing = !1; range = [new f(), new f()]; constructor(t = {}) { super(), Object.assign(this, t); } get transcript() { if (!this.source.transcript) return; if (!this.duration.millis) return this.source.transcript; const t = new v( this.source.transcript.groups.map((e) => { const s = new Y( e.words.filter((o) => o.stop.millis > this.range[0].millis && o.start.millis < this.range[1].millis).map((o) => new bt(o.text, o.start.millis, o.stop.millis)) ), i = s.words[0], n = s.words[s.words.length - 1]; return i && i.start.millis < this.range[0].millis && (i.start.millis = this.range[0].millis), n && n.stop.millis > this.range[1].millis && (n.stop.millis = this.range[1].millis), s; }).filter((e) => e.words.length > 0) ); return t.id = this.source.transcript.id, t; } set transcript(t) { this.source.transcript = t; } get start() { return this.range[0].add(this._delay); } get stop() { return this.range[1].add(this._delay); } get duration() { return this.range[1].subtract(this.range[0]); } set duration(t) { typeof t == "number" ? this.range[1] = this.range[0].copy().addFrames(t) : this.range[1] = this.range[0].add(t), this.range[1].millis <= this.range[0].millis && (this.range[1] = this.range[0].copy().addMillis(1)), this.range[1].millis > this.source.duration.millis && (this.range[1] = this.source.duration), this.emit("frame", this.stop.frames); } get muted() { return this.element?.muted ?? !1; } set muted(t) { this.element && (this.element.muted = t); } trim(t = this.start, e = this.stop) { return typeof t == "number" && (t = f.fromFrames(t)), typeof e == "number" && (e = f.fromFrames(e)), this.subclip(t.subtract(this.delay), e.subtract(this.delay)); } /** * Set the media playback to a given time */ seek(t) { return new Promise((e, s) => { if (!this.element) return s( new Et({ code: "elementNotDefined", message: "Cannot seek on undefined element" }) ); (t.millis < this.start.millis || t.millis > this.stop.millis) && (t = this.start), this.element.onerror = () => s(this.element?.error), this.element.pause(), this.element.currentTime = t.subtract(this._delay).seconds, this.element.onseeked = () => e(); }); } exit() { this.playing && this.element?.pause(); } /** * Returns a slice of a media clip with trimmed start and stop */ subclip(t, e) { if (t || (t = this.range[0]), e || (e = this.range[1]), typeof t == "number" && (t = f.fromFrames(t)), typeof e == "number" && (e = f.fromFrames(e)), t.millis >= e.millis) throw new w({ code: "invalidKeyframe", message: "Start can't lower than or equal the stop" }); return t.millis < 0 && (this.range[0].millis = 0, t = this.range[0]), e.millis > this.source.duration.millis && this.source.duration.millis && (e = this.source.duration), this.range = [t, e], this.emit("frame", void 0), this; } get volume() { return this.element?.volume ?? 1; } set volume(t) { this.element && (this.element.volume = t); } copy() { const t = Dt.fromJSON(JSON.parse(JSON.stringify(this))); return t.source = this.source, t; } async split(t) { if (t || (t = this.track?.composition?.ticker.frame), typeof t == "number" && (t = f.fromFrames(t)), !t || t.millis <= this.start.millis || t.millis >= this.stop.millis) throw new w({ code: "invalidKeyframe", message: "Cannot split clip at the specified time" }); if (!this.track) throw new Et({ code: "trackNotDefined", message: "Clip must be attached to a track" }); const e = t.subtract(this._delay), s = this.animate(t).copy(); this.range[1] = e.copy(), s.range[0] = e.copy().addMillis(1), s.animations = []; const i = this.track.clips.findIndex((n) => n.id == this.id); return await this.track.add(s, i + 1), s; } /** * Generates a new caption track for the current clip using the specified captioning strategy. * @param strategy An optional CaptionPresetStrategy to define how captions should be generated. */ async createCaptions(t) { if (!this.track?.composition) throw new w({ code: "compositionNotDefined", message: "Captions can only be generated after the clip has been added to the composition" }); return await this.track.composition.createTrack("caption").from(this).createCaptions(t); } /** * Remove silences from the clip * * @param options - Options for silence detection */ async removeSilences(t = {}) { await this.init(); const e = (await this.source.silences(t)).filter((n) => Lt(n, this.range)).sort((n, o) => n.start.millis - o.start.millis); if (e.length == 0) return [this]; const s = t.padding ?? 500, i = [this]; for (const n of e) { const o = i.at(-1); if (!o) break; if (!Lt(n, o.range)) continue; const a = new f( Math.min(n.start.millis + s, n.stop.millis) ); if (n.start.millis > o.range[0].millis && n.stop.millis < o.range[1].millis) { const l = o.copy(); o.range[1] = a, l.range[0] = n.stop, i.push(l); } else n.start.millis <= o.range[0].millis ? o.range[0] = n.stop : n.stop.millis >= o.range[1].millis && (o.range[1] = a); } return i; } }; st([ d(ze) ], I.prototype, "range", 2); st([ d(v) ], I.prototype, "transcript", 1); st([ d() ], I.prototype, "muted", 1); st([ d() ], I.prototype, "volume", 1); let z = I; function Lt(r, t) { return r.start.millis >= t[0].millis && r.start.millis <= t[1].millis || r.stop.millis <= t[1].millis && r.stop.millis >= t[0].millis; } function $t(r, t) { return t == "lower" ? r.toLocaleLowerCase() : t == "upper" ? r.toUpperCase() : r; } function pt(r = "#000000", t = 100) { return `${r}${Math.round(t / 100 * 255).toString(16)}`; } function m(r, t) { return typeof r == "number" ? r : Number.parseInt(r.replace("%", "")) * t / 100; } class St { /** * The canvas element */ canvas = document.createElement("canvas"); /** * The context of the canvas */ ctx = 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"; /** * The scale of the text */ textScale = 4; constructor(t = 1920, e = 1080, s = "#000000", i = 1) { this.canvas.style.background = s, this.ctx.imageSmoothingEnabled = !1, this.resolution = i, this.background = s, this.resize(t, e); } /** * Resize the canvas */ resize(t, e) { return this.width = Math.round(t), this.height = Math.round(e), this.canvas.width = Math.round(this.width * this.resolution), this.canvas.height = Math.round(this.height * this.resolution), this; } /** * Copy the renderer */ copy(t = this.resolution) { return new St(this.width, this.height, this.background, t); } clear(t) { let e = 0, s = 0, i = this.width * this.resolution, n = this.height * this.resolution; return this.ctx.fillStyle = this.background, t && (e = m(t.x ?? 0, this.width) * this.resolution, s = m(t.y ?? 0, this.height) * this.resolution, i = m(t.width, this.width) * this.resolution, n = m(t.height, this.height) * this.resolution), this.ctx.clearRect(e, s, i, n), this.ctx.fillRect(e, s, i, n), this; } rect(t) { return this.ctx.beginPath(), t.radius ? this.ctx.roundRect( m(t.x ?? 0, this.width) * this.resolution, m(t.y ?? 0, this.height) * this.resolution, m(t.width, this.width) * this.resolution, m(t.height, this.height) * this.resolution, t.radius * this.resolution ) : this.ctx.rect( m(t.x ?? 0, this.width) * this.resolution, m(t.y ?? 0, this.height) * this.resolution, m(t.width, this.width) * this.resolution, m(t.height, this.height) * this.resolution ), this.ctx.closePath(), this; } circle(t) { return this.ctx.beginPath(), this.ctx.arc( m(t.cx, this.width) * this.resolution, m(t.cy, this.height) * this.resolution, t.radius * this.resolution, 0, Math.PI * 2 ), this.ctx.closePath(), this; } image(t, e) { return e.width && e.height ? this.ctx.drawImage( t, m(e.x ?? 0, this.width) * this.resolution, m(e.y ?? 0, this.height) * this.resolution, m(e.width, this.width) * this.resolution, m(e.height, this.height) * this.resolution ) : this.ctx.drawIm