@snack-dev/browser-studio
Version:
Build bleeding edge video processing applications
1,718 lines (1,717 loc) • 141 kB
JavaScript
import { Container as I, Filter as Fe, Sprite as Y, Texture as ot, TextStyle as Ue, Text as st, Color as bt, CanvasTextMetrics as Ge, Graphics as ne, autoDetectRenderer as Xe } from "pixi.js";
import { GlowFilter as ke } from "pixi-filters";
import { ArrayBufferTarget as oe, FileSystemWritableFileStreamTarget as Ne, Muxer as re } from "mp4-muxer";
const G = 30;
class X extends Error {
message;
code;
constructor({ message: t = "", code: e = "" }, ...i) {
super(t, ...i), console.error(t), this.code = e, this.message = t;
}
}
class v extends X {
}
class g extends X {
}
class ii extends X {
}
class k extends X {
}
class Lt extends X {
}
function Ce(o, t = G) {
if (t < 1) throw new g({
code: "invalidArgument",
message: "FPS must be greater or equal to 1"
});
return Math.round(o * t);
}
function ni(o, t = G) {
if (t < 1) throw new g({
code: "invalidArgument",
message: "FPS must be greater or equal to 1"
});
return Math.round(o / t * 1e3) / 1e3;
}
function E(o, t = G) {
if (t < 1) throw new g({
code: "invalidArgument",
message: "FPS must be greater or equal to 1"
});
return Math.round(o / t * 1e3);
}
class u {
/**
* Time state in **milliseconds**
*/
time;
/**
* Create a new timestamp from **milliseconds**
*/
constructor(t = 0) {
this.time = Math.round(t);
}
/**
* 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 Ce(this.millis / 1e3);
}
set frames(t) {
this.millis = E(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 = E(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 u(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 u(this.millis - t.millis);
}
/**
* Create a new timestamp from seconds
*/
static fromSeconds(t) {
const e = new u();
return e.millis = t * 1e3, e;
}
/**
* Create a new timestamp from frames
*/
static fromFrames(t, e) {
const i = new u();
return i.millis = E(t, e), i;
}
/**
* get a copy of the object
*/
copy() {
return new u(this.millis);
}
toJSON() {
return this.millis;
}
static fromJSON(t) {
return new u(t);
}
}
function oi(o) {
return Math.floor(o * 255).toString(16).padStart(2, "0").toUpperCase();
}
function ri(o, t) {
return o.reduce(
(e, i) => {
const s = i[t];
return e[s] || (e[s] = []), e[s].push(i), e;
},
// @ts-ignore
{}
);
}
function Yt(o, t) {
return [o.slice(0, t), o.slice(t)].filter((e) => e.length > 0);
}
function it(o, t) {
return t ? Math.floor(Math.random() * (t - o + 1) + o) : o;
}
async function ai(o) {
o <= 0 || await new Promise((t) => setTimeout(t, o));
}
function li(o) {
if (!o)
throw "Assertion failed!";
}
function ci(o, t = 300) {
let e;
return (...i) => {
clearTimeout(e), e = setTimeout(() => {
o.apply(o, i);
}, t);
};
}
function Oe(o, t, e) {
e < 0 && (e = 0);
const i = o[t];
o.splice(t, 1), o.splice(e, 0, i);
}
function hi() {
return crypto.randomUUID().split("-").at(0);
}
function Bt(o) {
return typeof o != "function" ? !1 : /^class\s/.test(Function.prototype.toString.call(o));
}
function di(o) {
return o.charAt(0).toUpperCase() + o.slice(1);
}
function Te(o) {
if (o.numberOfChannels === 1)
return o.getChannelData(0);
const t = [];
for (let r = 0; r < o.numberOfChannels; r++)
t.push(o.getChannelData(r));
const e = Math.max(...t.map((r) => r.length)), i = new Float32Array(e * o.numberOfChannels);
let s = 0, n = 0;
for (; n < e; )
t.forEach((r) => {
i[s++] = r[n] !== void 0 ? r[n] : 0;
}), n++;
return i;
}
function $(o, t, e) {
for (let i = 0; i < e.length; i++)
o.setUint8(t + i, e.charCodeAt(i));
}
function Ee(o, t, e) {
for (let i = 0; i < t.length; i++, e += 2) {
const s = Math.max(-1, Math.min(1, t[i]));
o.setInt16(e, s < 0 ? s * 32768 : s * 32767, !0);
}
return o;
}
function _e(o, t, e) {
const n = t * 2, r = 8, a = 36, c = o.length * 2, h = a + c, p = new ArrayBuffer(r + h), d = new DataView(p);
return $(d, 0, "RIFF"), d.setUint32(4, h, !0), $(d, 8, "WAVE"), $(d, 12, "fmt "), d.setUint32(16, 16, !0), d.setUint16(20, 1, !0), d.setUint16(22, t, !0), d.setUint32(24, e, !0), d.setUint32(28, e * n, !0), d.setUint16(32, n, !0), d.setUint16(34, 16, !0), $(d, 36, "data"), d.setUint32(40, c, !0), Ee(d, o, r + a);
}
function ui(o, t = "audio/wav") {
const e = Te(o), i = _e(e, o.numberOfChannels, o.sampleRate);
return new Blob([i], { type: t });
}
function Me(o) {
const t = new Float32Array(o.length * o.numberOfChannels);
let e = 0;
for (let i = 0; i < o.numberOfChannels; i++) {
const s = o.getChannelData(i);
t.set(s, e), e += s.length;
}
return t;
}
function ae(o) {
const t = o.numberOfChannels, e = o.length, i = new Int16Array(e * t);
for (let s = 0; s < e; s++)
for (let n = 0; n < t; n++) {
let r = o.getChannelData(n)[s] * 32767;
r > 32767 && (r = 32767), r < -32767 && (r = -32767), i[s * t + n] = r;
}
return i;
}
async function pi(o, t = 22050, e = Math.sqrt(2)) {
const i = await o.arrayBuffer(), s = new OfflineAudioContext({ sampleRate: t, length: 1 }), n = await s.decodeAudioData(i), r = s.createBuffer(1, n.length, t);
if (n.numberOfChannels >= 2) {
const a = n.getChannelData(0), c = n.getChannelData(1), h = r.getChannelData(0);
for (let p = 0; p < n.length; ++p)
h[p] = e * (a[p] + c[p]) / 2;
return r;
}
return n;
}
function Je(o, t = 44100, e = 2) {
if (o.sampleRate == t && o.numberOfChannels == e)
return o;
const i = Math.floor(o.duration * t), n = new OfflineAudioContext(e, 1, t).createBuffer(e, i, t);
for (let r = 0; r < o.numberOfChannels; r++) {
const a = o.getChannelData(r), c = n.getChannelData(r), h = o.sampleRate / t;
for (let p = 0; p < c.length; p++) {
const d = p * h, f = Math.floor(d), m = Math.ceil(d);
if (m >= a.length)
c[p] = a[f];
else {
const y = d - f;
c[p] = a[f] * (1 - y) + a[m] * y;
}
}
}
return n;
}
async function le(o) {
const { fps: t, height: e, width: i, bitrate: s } = o, 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',
], r = ["prefer-hardware", "prefer-software"], a = [];
for (const h of n)
for (const p of r)
a.push({
codec: h,
hardwareAcceleration: p,
width: i,
height: e,
bitrate: s,
framerate: t
});
const c = [];
if (!("VideoEncoder" in window))
return c;
for (const h of a) {
const p = await VideoEncoder.isConfigSupported(h);
p.supported && c.push(p.config ?? h);
}
return c.sort(Be);
}
async function Le(o) {
const { sampleRate: t, numberOfChannels: e, bitrate: i } = o, s = ["mp4a.40.2", "opus"], n = [];
if (!("AudioEncoder" in window))
return n;
for (const r of s) {
const a = await AudioEncoder.isConfigSupported({
codec: r,
numberOfChannels: e,
bitrate: i,
sampleRate: t
});
a.supported && n.push(a.config);
}
return n;
}
async function Ye(o) {
const t = await Le(o.audio), e = await le(o.video);
if (!e.length)
throw new k({
message: "Encoder can't be configured with any of the tested codecs",
code: "codecsNotSupported"
});
return [e[0], t[0]];
}
function Be(o, t) {
const e = o.hardwareAcceleration ?? "", i = t.hardwareAcceleration ?? "";
return e < i ? -1 : e > i ? 1 : 0;
}
async function ce(o, t = "untitled") {
const e = document.createElement("a");
if (document.head.appendChild(e), e.download = t, typeof o == "string" && o.startsWith("data:image/svg+xml;base64,")) {
const i = o.split(",")[1], s = atob(i), n = new Array(s.length);
for (let c = 0; c < s.length; c++)
n[c] = s.charCodeAt(c);
const r = new Uint8Array(n), a = new Blob([r], { type: "image/svg+xml" });
e.href = URL.createObjectURL(a), e.download = t.split(".")[0] + ".svg";
} else typeof o == "string" ? e.href = o : e.href = URL.createObjectURL(o);
e.click(), e.remove();
}
async function fi(o, t = !0) {
return new Promise((e) => {
const i = document.createElement("input");
i.type = "file", i.accept = o, i.multiple = t, i.onchange = (s) => {
const n = Array.from(s.target?.files ?? []);
e(n);
}, i.click();
});
}
function It(o) {
return `${o.hours.toString().padStart(2, "0")}:${o.minutes.toString().padStart(2, "0")}:${o.seconds.toString().padStart(2, "0")},` + o.milliseconds.toString().padStart(3, "0");
}
function zt(o) {
const t = new Date(1970, 0, 1);
return t.setSeconds(o), t.setMilliseconds(Math.round(o % 1 * 1e3)), {
hours: t.getHours(),
minutes: t.getMinutes(),
seconds: t.getSeconds(),
milliseconds: t.getMilliseconds()
};
}
class q {
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 u();
}
get stop() {
return this.words.at(-1)?.stop ?? new u();
}
}
var St = /* @__PURE__ */ ((o) => (o.en = "en", o.de = "de", o))(St || {});
class jt {
/**
* 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, i, s) {
this.text = t, this.start = new u(e), this.stop = new u(i), this.confidence = s;
}
/**
* Defines the time between start
* and stop returned as a timestamp
*/
get duration() {
return this.stop.subtract(this.start);
}
}
class U {
id = crypto.randomUUID();
language = St.en;
groups = [];
get text() {
return this.groups.map(({ text: t }) => t).join(" ");
}
get words() {
return this.groups.flatMap(({ words: t }) => t);
}
constructor(t = [], e = St.en) {
this.groups = t, this.language = e;
}
/**
* Iterate over all words in groups
*/
*iter({ count: t, duration: e, length: i }) {
for (const s of this.groups) {
let n;
for (const [r, a] of s.words.entries())
n && (t && n.words.length >= it(...t) ? (yield n, n = void 0) : e && n?.duration.seconds >= it(...e) ? (yield n, n = void 0) : i && n.text.length >= it(...i) && (yield n, n = void 0)), n ? n.words.push(a) : n = new q([a]), r == s.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 i = t[e], s = t[e + 1];
s.start.millis - i.stop.millis < 0 ? s.start.millis = i.stop.millis + 1 : i.stop.millis = s.start.millis - 1;
}
return this;
}
/**
* Convert the transcript into a SRT compatible
* string and downloadable blob
*/
toSRT(t = {}) {
let e = 1, i = "";
for (const s of this.iter(t)) {
const n = zt(s.start.seconds), r = zt(s.stop.seconds);
i += `${e}
` + It(n) + " --> " + It(r) + `
${s.text}
`, e += 1;
}
return {
text: i,
blob: new Blob([i], { 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 i = 0;
const s = [];
for (const n of this.groups)
for (const r of n.words)
if (s.length == 0 && e && (i = r.start.millis), s.push(new jt(r.text, r.start.millis - i, r.stop.millis - i)), s.length == t)
return new U([new q(s)]);
return new U([new q(s)]);
}
/**
* Create a deep copy of the transcript
* @returns A new Transcript instance
*/
copy() {
return U.fromJSON(this.toJSON());
}
static fromJSON(t) {
const e = new U();
for (const i of t) {
const s = new q();
for (const n of i)
s.words.push(new jt(n.token, n.start, n.stop));
e.groups.push(s);
}
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 url 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) {
const i = await fetch(t, e);
if (!i.ok)
throw new v({
code: "unexpectedIOError",
message: "An unexpected error occurred while fetching the file"
});
return U.fromJSON(await i.json());
}
}
function nt(o, t, e) {
return o + (t - o) * e;
}
function Ie(o, t, e) {
const i = Number.parseInt(o.slice(1), 16), s = Number.parseInt(t.slice(1), 16), n = i >> 16 & 255, r = i >> 8 & 255, a = i & 255, c = s >> 16 & 255, h = s >> 8 & 255, p = s & 255, d = Math.round(nt(n, c, e)), f = Math.round(nt(r, h, e)), m = Math.round(nt(a, p, e));
return `#${((1 << 24) + (d << 16) + (f << 8) + m).toString(16).slice(1)}`;
}
const ze = {
linear: (o) => o,
easeIn: (o) => o * o,
easeOut: (o) => o * (2 - o),
easeInOut: (o) => o < 0.5 ? 2 * o * o : -1 + (4 - 2 * o) * o
};
class R {
/**
* Defines the range of the input values
* in milliseconds
*/
input;
/**
* Defines the range of the output values
*/
output;
/**
* Defines the required options that
* control the behaviour of the keyframe
*/
options;
/**
* Constructs a Keyframe object.
* @param inputRange - The range of input values (e.g., frame numbers).
* @param outputRange - The range of output values (e.g., opacity, degrees, colors).
* @param options - Additional options for extrapolation, type, and easing.
*/
constructor(t, e, i = {}) {
if (t.length !== e.length)
throw new g({
code: "invalidKeyframes",
message: "inputRange and outputRange must have the same length"
});
this.input = t.map((s) => E(s)), this.output = e, this.options = {
extrapolate: "clamp",
easing: "linear",
type: "number",
...JSON.parse(JSON.stringify(i))
};
}
/**
* Normalizes the frame number to a value between 0 and 1 based on the input range.
* @param frame - The current frame number.
* @returns The normalized value.
*/
normalize(t) {
const { input: e } = this;
if (t < e[0])
return this.options.extrapolate === "clamp" ? { t: 0, segment: 0 } : { t: (t - e[0]) / (e[1] - e[0]), segment: 0 };
if (t > e[e.length - 1])
return this.options.extrapolate === "clamp" ? { t: 1, segment: e.length - 2 } : { t: (t - e[e.length - 2]) / (e[e.length - 1] - e[e.length - 2]), segment: e.length - 2 };
for (let i = 0; i < e.length - 1; i++) {
const s = e[i], n = e[i + 1];
if (t >= s && t <= n)
return { t: (t - s) / (n - s), segment: i };
}
return { t: 0, segment: 0 };
}
/**
* Interpolates the output value based on the normalized frame value.
* @param t - The normalized frame value (between 0 and 1).
* @param segment - The current segment index.
* @returns The interpolated output value.
*/
interpolate(t, e) {
const i = this.output[e], s = this.output[e + 1], n = ze[this.options.easing](t);
if (typeof i == "number" && typeof s == "number")
return nt(i, s, n);
if (typeof i == "string" && typeof s == "string")
return Ie(i, s, n);
if (this.output.length == 1)
return this.output[0];
throw new g({
code: "invalidKeyframes",
message: "Unsupported output range types"
});
}
/**
* Evaluates the interpolated value for a given milliseconds number.
* @param time - The current time in milliseconds or as a timestamp
* @returns The interpolated output value.
*/
value(t) {
const { t: e, segment: i } = this.normalize(typeof t == "number" ? t : t.millis);
return this.interpolate(e, i);
}
/**
* Add a new keyframe to the animation
* @param frame time of the keyframe
* @param value value of the keyframe
*/
push(t, e) {
return this.input.push(E(t)), this.output.push(e), this;
}
toJSON() {
return this;
}
static fromJSON(t) {
const e = new R([], []);
return Object.assign(e, t), e;
}
}
class Z {
/**
* Unique identifier of the object
*/
id = crypto.randomUUID();
toJSON() {
const t = {};
return (this.constructor.__serializableProperties || []).forEach(({ propertyKey: i, serializer: s }) => {
const n = this[i];
s && n instanceof s ? t[i] = n.toJSON() : t[i] = n;
}), t;
}
static fromJSON(t) {
const e = new this();
return (this.__serializableProperties || []).forEach(({ propertyKey: s, serializer: n }) => {
if (t.hasOwnProperty(s))
if (n) {
const r = n.fromJSON(t[s]);
e[s] = r;
} else
e[s] = t[s];
}), e;
}
}
function l(o) {
return function(t, e) {
t.constructor.__serializableProperties || (t.constructor.__serializableProperties = []), t.constructor.__serializableProperties.push({
propertyKey: e,
serializer: o
});
};
}
function N(o) {
return class extends o {
_handlers = {};
on(e, i) {
if (typeof i != "function")
throw new Error("The callback of an event listener needs to be a function.");
const s = crypto.randomUUID();
return this._handlers[e] ? this._handlers[e][s] = i : this._handlers[e] = { [s]: i }, s;
}
off(e, ...i) {
if (e) {
if (e === "*") {
this._handlers = {};
return;
}
for (const s of Object.values(this._handlers))
e in s && delete s[e];
for (const s of i)
this.off(s);
}
}
trigger(e, i) {
const s = new CustomEvent(e, {
detail: i
});
Object.defineProperty(s, "currentTarget", { writable: !1, value: this });
for (const n in this._handlers[e] ?? {})
this._handlers[e]?.[n](s);
for (const n in this._handlers["*"] ?? {})
this._handlers["*"]?.[n](s);
}
bubble(e) {
return this.on("*", (i) => {
e.trigger(i.type, i.detail);
});
}
resolve(e) {
return (i, s) => {
this.on("error", s), this.on(e, i);
};
}
};
}
class xt extends N(Z) {
_key;
_value;
_store;
loaded = !1;
constructor(t, e, i) {
super(), this._store = t, this._key = e, 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.trigger("update", void 0);
}
async initValue(t) {
t instanceof Promise ? this._value = await t : this._value = t, this.loaded = !0, this.trigger("update", void 0);
}
}
class gi {
storageEngine;
namespace;
constructor(t, e = localStorage) {
this.storageEngine = e, this.namespace = t;
}
define(t, e, i) {
const s = this.get(t);
return s === null ? (this.set(t, e), new xt(this, t, e)) : i && s != null ? new xt(this, t, i(s)) : new xt(this, t, s);
}
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 yi {
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((i, s) => {
this.worker.addEventListener("message", (n) => {
e?.(n.data), n.data.type == "result" && (n.data.type = void 0, i(n.data)), n.data.type == "error" && s(n.data.message);
});
}).then((i) => ({ result: i, error: void 0 })).catch((i) => ({ result: void 0, error: i })).finally(() => {
this.worker.terminate();
});
}
}
function wi(o) {
return async (t) => {
try {
await o(t);
} catch (e) {
self.postMessage({
type: "error",
message: e?.message ?? "An unkown worker error occured"
});
}
};
}
function je() {
return class extends N(class {
}) {
};
}
function Ot(o, t, e = 0) {
if (!(o instanceof I || o instanceof Fe || e == 3))
for (const i in o) {
const s = o[i];
i && (s instanceof R && (o[i] = s.value(t)), s != null && typeof s == "object" && Object.keys(s).length && Ot(s, t, e + 1));
}
}
var Pe = Object.defineProperty, at = (o, t, e, i) => {
for (var s = void 0, n = o.length - 1, r; n >= 0; n--)
(r = o[n]) && (s = r(t, e, s) || s);
return s && Pe(t, e, s), s;
};
const z = class he extends N(Z) {
_name;
_start = new u();
_stop = u.fromSeconds(16);
/**
* Defines the type of the clip
*/
type = "base";
/**
* Defines the source of the clip with a
* one-to-many (1:n) relationship
*/
source;
/**
* The view that contains the render related information
*/
view = new I();
/**
* Timestamp when the clip has been created
*/
createdAt = /* @__PURE__ */ new Date();
disabled = !1;
/**
* Track is ready to be rendered
*/
state = "IDLE";
/**
* 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._start;
}
/**
* Get the last visible frame
*/
get stop() {
return this._stop;
}
constructor(t = {}) {
super(), Object.assign(this, t);
}
/**
* Method for connecting the track with the clip
*/
async connect(t) {
this.state = "ATTACHED", this.track = t, this.trigger("attach", void 0);
}
/**
* Change clip's offset to zero in seconds. Can be negative
*/
set start(t) {
typeof t == "number" ? this.start.frames = t : this._start = t, this.start.millis >= this.stop.millis && (this.stop.millis = this.start.millis + 1), this.trigger("frame", this.start.frames);
}
/**
* Set the last visible time that the
* clip is visible
*/
set stop(t) {
typeof t == "number" ? this.stop.frames = t : this._stop = t, this.stop.millis <= this.start.millis && (this.start.millis = this.stop.millis - 1), this.trigger("frame", this.stop.frames);
}
/**
* Offsets the clip by a given frame number
*/
offsetBy(t) {
return typeof t == "number" ? (this.start.addFrames(t), this.stop.addFrames(t), this.trigger("offsetBy", u.fromFrames(t))) : (this.start.addMillis(t.millis), this.stop.addMillis(t.millis), this.trigger("offsetBy", t)), this.trigger("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) {
}
/**
* Triggered when the clip exits the scene
*/
exit() {
}
/**
* Remove the clip from the track
*/
detach() {
return this.track?.remove(this), 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?.frame), typeof t == "number" && (t = u.fromFrames(t)), !t || t.millis <= this.start.millis || t.millis >= this.stop.millis)
throw new g({
code: "splitOutOfRange",
message: "Cannot split clip at the specified time"
});
if (!this.track)
throw new g({
code: "trackNotAttached",
message: "Track must be attached to a track"
});
const e = this.copy();
this.stop = t.copy(), e.start = t.copy().addMillis(1), Ot(e, e.start.subtract(this.start));
const i = this.track.clips.findIndex((s) => s.id == this.id);
return await this.track.add(e, i + 1), e;
}
/**
* Create a copy of the clip
*/
copy() {
return he.fromJSON(JSON.parse(JSON.stringify(this)));
}
/**
* Modify the properties of the clip and
* trigger an update afterwards
*/
set(t) {
return t && Object.assign(this, t), this.trigger("update", void 0), this;
}
};
at([
l()
], z.prototype, "_name");
at([
l(u)
], z.prototype, "_start");
at([
l(u)
], z.prototype, "_stop");
at([
l()
], z.prototype, "disabled");
let _ = z;
var He = Object.defineProperty, C = (o, t, e, i) => {
for (var s = void 0, n = o.length - 1, r; n >= 0; n--)
(r = o[n]) && (s = r(t, e, s) || s);
return s && He(t, e, s), s;
};
class W extends N(Z) {
/**
* Indicates if the track is loading
*/
state = "IDLE";
/**
* Metadata associated with the source
*/
metadata;
objectURL;
duration = u.fromSeconds(16);
/**
* 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;
/**
* By default this is a URL.createObjectURL proxy
*/
async createObjectURL() {
return this.objectURL ? this.objectURL : (this.objectURL = URL.createObjectURL(await this.getFile()), this.objectURL);
}
/**
* Method for retrieving the file when
* it has been loaded
* @returns The loaded file
*/
async getFile() {
if (!this.file && this.state == "LOADING" && await new Promise(this.resolve("load")), !this.file)
throw new g({
code: "fileNotAccessible",
message: "The desired file cannot be accessed"
});
return this.file;
}
async loadFile(t) {
this.name = t.name, this.mimeType = Nt(t.type), this.external = !1, this.file = t;
}
async loadUrl(t, e) {
const i = await fetch(t, e);
if (!i?.ok) throw new v({
code: "unexpectedIOError",
message: "An unexpected error occurred while fetching the file"
});
const s = await i.blob();
this.name = t.toString().split("/").at(-1) ?? "", this.external = !0, this.file = new File([s], this.name, { type: s.type }), this.externalURL = t, this.mimeType = Nt(s.type);
}
async from(t, e) {
try {
this.state = "LOADING", t instanceof File ? await this.loadFile(t) : await this.loadUrl(t, e), this.state = "READY", this.trigger("load", void 0);
} catch (i) {
throw this.state = "ERROR", this.trigger("error", new Error(String(i))), i;
}
return this;
}
/**
* 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.objectURL && (URL.revokeObjectURL(this.objectURL), this.objectURL = void 0), delete this.file;
}
/**
* Downloads the file
*/
async download() {
const t = await this.getFile();
ce(t, this.name);
}
/**
* Get a visulization of the source
* as an html element
*/
async thumbnail() {
return document.createElement("div");
}
/**
* Create a new source for the specified input
*/
static async from(t, e, i = new this()) {
return i.from(t, e);
}
}
C([
l()
], W.prototype, "objectURL");
C([
l()
], W.prototype, "duration");
C([
l()
], W.prototype, "type");
C([
l()
], W.prototype, "name");
C([
l()
], W.prototype, "mimeType");
C([
l()
], W.prototype, "externalURL");
C([
l()
], W.prototype, "external");
const Pt = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3C/svg%3E";
function Ae(o) {
const t = new TextEncoder().encode(o);
let e = "";
const i = t.byteLength;
for (let s = 0; s < i; s++)
e += String.fromCharCode(t[s]);
return btoa(e);
}
function Ht(o) {
if (!o || !o.body) return Pt;
const t = o.body.scrollWidth, e = o.body.scrollHeight, i = o.cloneNode(!0), s = i.getElementsByTagName("style").item(0), n = i.getElementsByTagName("body").item(0);
if (n?.setAttribute("xmlns", "http://www.w3.org/1999/xhtml"), !n) return Pt;
const r = new XMLSerializer(), a = s ? r.serializeToString(s) : "", c = r.serializeToString(n), h = `
<svg xmlns="http://www.w3.org/2000/svg" width="${t}" height="${e}">
body { padding: 0; }
${a}
<foreignObject width="100%" height="100%">
${c}
</foreignObject>
</svg>`;
return "data:image/svg+xml;base64," + Ae(h);
}
class Ft extends W {
type = "html";
/**
* Access to the iframe that is required
* for extracting the html's dimensions
*/
iframe;
constructor() {
super();
const t = document.createElement("iframe");
t.style.position = "absolute", t.style.width = "0", t.style.height = "0", t.style.border = "0", t.style.visibility = "hidden", document.body.appendChild(t), this.iframe = t;
}
/**
* Access to the html document as loaded
* within the iframe. Can be manipulated with
* javascript
*/
get document() {
return this.iframe.contentWindow?.document;
}
async createObjectURL() {
return !this.file && this.state == "LOADING" && await new Promise(this.resolve("load")), this.objectURL ? this.objectURL : (this.objectURL = Ht(this.document), this.objectURL);
}
async loadUrl(t, e) {
await super.loadUrl(t, e), this.iframe.setAttribute("src", URL.createObjectURL(this.file)), await new Promise((i, s) => {
this.iframe.onload = () => i(), this.iframe.onerror = (n) => s(n);
});
}
async loadFile(t) {
await super.loadFile(t), this.iframe.setAttribute("src", URL.createObjectURL(this.file)), await new Promise((e, i) => {
this.iframe.onload = () => e(), this.iframe.onerror = (s) => i(s);
});
}
/**
* Update the object url using the current
* contents of the iframe document
*/
update() {
this.objectURL && (this.objectURL = Ht(this.document));
}
async thumbnail() {
const t = new Image();
return t.src = await this.createObjectURL(), t.className = "object-contain w-full aspect-video h-auto", t;
}
}
function De(o, t = {}) {
const { threshold: e = 0.02, hopSize: i = 1024, minDuration: s = 500 } = t, n = [], r = o.getChannelData(0), a = o.sampleRate, c = Math.floor(s / 1e3 * a);
let h = null, p = 0;
for (let d = 0; d < r.length; d += i) {
let f = 0;
const m = Math.min(d + i, r.length);
for (let y = d; y < m; y++)
f += r[y] * r[y];
f = Math.sqrt(f / (m - d)), f < e ? (p += i, h === null && (h = d)) : (h !== null && p >= c && n.push({
start: u.fromSeconds(h / a),
stop: u.fromSeconds(d / a)
}), h = null, p = 0);
}
return h !== null && p >= c && n.push({
start: u.fromSeconds(h / a),
stop: u.fromSeconds(r.length / a)
}), n;
}
const Zt = 3e3;
class B extends W {
type = "audio";
decoding = !1;
_silences;
transcript;
audioBuffer;
async decode(t = 2, e = 48e3, i = !1) {
if (this.decoding && i && (await new Promise(this.resolve("update")), this.audioBuffer))
return this.audioBuffer;
this.decoding = !0;
const s = await this.arrayBuffer(), r = await new OfflineAudioContext(t, 1, e).decodeAudioData(s);
return this.duration.seconds = r.duration, i && (this.audioBuffer = r), this.decoding = !1, this.trigger("update", void 0), r;
}
/**
* @deprecated Use fastsampler instead.
*/
async samples(t = 60, e = 50, i = 0) {
const s = this.audioBuffer ?? await this.decode(1, 3e3, !0), n = Math.round(s.sampleRate / e), r = s.sampleRate * s.duration - n, a = Math.ceil(r / t), c = s.getChannelData(0), h = [];
for (let p = 0; p < r; p += a) {
let d = 0;
for (let f = p; f < p + n; f++)
d += Math.abs(c[p]);
h.push(Math.log1p(d / n * 100));
}
return h.map((p) => Math.round(p / Math.max(...h) * (100 - i)) + i);
}
/**
* Fast 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 fastsampler({
length: t = 60,
start: e = 0,
stop: i,
logarithmic: s = !1
} = {}) {
typeof e == "object" && (e = e.millis), typeof i == "object" && (i = i.millis);
const n = this.audioBuffer ?? await this.decode(1, Zt, !0), r = n.getChannelData(0), a = Math.floor(Math.max(e * Zt / 1e3, 0)), c = i ? Math.floor(Math.min(i * Zt / 1e3, n.length)) : n.length, h = Math.floor((c - a) / t), p = new Float32Array(t);
for (let d = 0; d < t; d++) {
const f = a + d * h, m = f + h;
let y = -1 / 0;
for (let w = f; w < m; w++) {
const Jt = r[w];
Jt > y && (y = Jt);
}
p[d] = s ? Math.log2(1 + y) : y;
}
return p;
}
async thumbnail(...t) {
const e = await this.samples(...t), i = document.createElement("div");
i.className = "flex flex-row absolute space-between inset-0 audio-samples";
for (const s of e) {
const n = document.createElement("div");
n.className = "audio-sample-item", n.style.height = `${s}%`, i.appendChild(n);
}
return 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 = {}) {
if (this._silences) return this._silences;
const e = await this.arrayBuffer(), i = new AudioContext(), s = await i.decodeAudioData(e);
return this._silences = De(s, t), i.close(), this._silences;
}
}
class Ut extends W {
type = "image";
async thumbnail() {
const t = new Image();
return t.src = await this.createObjectURL(), t.className = "object-cover w-full aspect-video h-auto", t;
}
}
class Gt extends B {
type = "video";
downloadInProgress = !0;
async loadUrl(t, e) {
const i = await fetch(t, e);
if (!i?.ok) throw new v({
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.objectURL = String(t), this.mimeType = Nt(i.headers.get("Content-type")), this.getBlob(i);
}
async getFile() {
if (!this.file && this.downloadInProgress && await new Promise(this.resolve("load")), !this.file)
throw new g({
code: "fileNotAccessible",
message: "The desired file cannot be accessed"
});
return this.file;
}
async thumbnail() {
const t = document.createElement("video");
return t.className = "object-cover w-full aspect-video h-auto", t.controls = !1, t.addEventListener("loadedmetadata", () => {
this.duration.seconds = t.duration, this.trigger("update", void 0);
}), t.addEventListener("mousemove", (e) => {
const i = e.currentTarget, s = i?.getBoundingClientRect(), n = e.clientX - (s?.left ?? 0), r = i?.duration;
r && s && s.width > 0 && (i.currentTime = Math.round(r * (n / s.width)));
}), t.src = await this.createObjectURL(), t;
}
async getBlob(t) {
try {
this.downloadInProgress = !0;
const e = await t.blob();
this.file = new File([e], this.name, { type: e.type }), this.trigger("load", void 0);
} catch (e) {
this.state = "ERROR", this.trigger("error", new Error(String(e)));
} finally {
this.downloadInProgress = !1;
}
}
}
class bi {
static fromType(t) {
switch (t.type) {
case "video":
return new kt();
case "audio":
return new rt();
case "html":
return new $t();
case "image":
return new Dt();
case "text":
return new P();
case "complex_text":
return new H();
default:
return new _();
}
}
static fromSource(t) {
if (t.type == "audio" && t instanceof B)
return new rt(t);
if (t.type == "video" && t instanceof Gt)
return new kt(t);
if (t.type == "image" && t instanceof Ut)
return new Dt(t);
if (t.type == "html" && t instanceof Ft)
return new $t(t);
}
}
class Qe {
static fromJSON(t) {
return [new u(t[0]), new u(t[1])];
}
}
var Ke = Object.defineProperty, $e = Object.getOwnPropertyDescriptor, M = (o, t, e, i) => {
for (var s = i > 1 ? void 0 : i ? $e(t, e) : t, n = o.length - 1, r; n >= 0; n--)
(r = o[n]) && (s = (i ? r(t, e, s) : r(s)) || s);
return i && s && Ke(t, e, s), s;
};
const O = class de extends _ {
source = new B();
_offset = new u();
/**
* Is the media currently playing
*/
playing = !1;
duration = new u();
range = [new u(), this.duration];
constructor(t = {}) {
super(), Object.assign(this, t);
}
get transcript() {
return this.source.transcript;
}
set transcript(t) {
this.source.transcript = t;
}
get start() {
return this.range[0].add(this.offset);
}
get stop() {
return this.range[1].add(this.offset);
}
set start(t) {
typeof t == "number" && (t = u.fromFrames(t));
const e = t.subtract(this.offset);
e.millis >= 0 && e.millis < this.range[1].millis ? this.range[0].millis = e.millis : e.millis < 0 ? this.range[0].millis = 0 : this.range[0].millis = this.range[1].millis - 1, this.trigger("frame", void 0);
}
set stop(t) {
typeof t == "number" && (t = u.fromFrames(t));
const e = t.subtract(this.offset);
e.millis > this.range[0].millis && e.millis <= this.duration.millis ? this.range[1] = e : e.millis > this.duration.millis ? this.range[1] = this.duration : this.range[1].millis = this.range[0].millis + 1, this.trigger("frame", void 0);
}
/**
* Offest from frame 0 of the composition
*/
get offset() {
return this._offset;
}
/**
* Change clip's offset to zero in frames. Can be negative
*/
set offset(t) {
typeof t == "number" ? this._offset.frames = t : this._offset = t, this.trigger("frame", this.offset.frames);
}
/**
* Offsets the clip by a given frame number
*/
offsetBy(t) {
return typeof t == "number" ? (this.offset.addFrames(t), this.trigger("offsetBy", u.fromFrames(t))) : (this.offset.addMillis(t.millis), this.trigger("offsetBy", t)), this.trigger("frame", void 0), this;
}
get muted() {
return this.element?.muted ?? !1;
}
set muted(t) {
this.element && (this.element.muted = t);
}
/**
* Set the media playback to a given time
*/
seek(t) {
return new Promise((e, i) => {
if (!this.element)
return i(
new Lt({
code: "elementNotDefined",
message: "Cannot seek on undefined element"
})
);
(t.millis < this.start.millis || t.millis > this.stop.millis) && (t = this.start), this.element.onerror = () => i(this.element?.error), this.element.pause(), this.element.currentTime = t.subtract(this.offset).seconds, this.element.onseeked = () => e();
});
}
/**
* 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 = u.fromFrames(t)), typeof e == "number" && (e = u.fromFrames(e)), t.millis >= e.millis)
throw new g({
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.duration.millis && this.duration.millis && (e = this.duration), this.range = [t, e], this.trigger("frame", void 0), this;
}
get volume() {
return this.element?.volume ?? 1;
}
set volume(t) {
this.element && (this.element.volume = t);
}
copy() {
const t = de.fromJSON(JSON.parse(JSON.stringify(this)));
return t.source = this.source, t;
}
async split(t) {
if (t || (t = this.track?.composition?.frame), typeof t == "number" && (t = u.fromFrames(t)), !t || t.millis <= this.start.millis || t.millis >= this.stop.millis)
throw new g({
code: "invalidKeyframe",
message: "Cannot split clip at the specified time"
});
if (!this.track)
throw new Lt({
code: "trackNotDefined",
message: "Clip must be attached to a track"
});
t = t.subtract(this.offset);
const e = this.copy();
this.range[1] = t.copy(), e.range[0] = t.copy().addMillis(1), Ot(e, e.start.subtract(this.start));
const i = this.track.clips.findIndex((s) => s.id == this.id);
return await this.track.add(e, i + 1), e;
}
/**
* 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 addCaptions(t) {
if (!this.track?.composition)
throw new g({
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).generate(t);
}
set(t) {
return super.set(t);
}
/**
* @deprecated use `addCaptions` instead
*/
async generateCaptions(t) {
return this.addCaptions(t);
}
/**
* Remove silences from the clip
*
* @param options - Options for silence detection
*/
async removeSilences(t = {}) {
["READY", "ATTACHED"].includes(this.state) || await this.init();
const e = (await this.source.silences(t)).filter((n) => At(n, this.range)).sort((n, r) => n.start.millis - r.start.millis);
if (e.length == 0)
return [this];
const i = t.padding ?? 500, s = [this];
for (const n of e) {
const r = s.at(-1);
if (!r) break;
if (!At(n, r.range)) continue;
const a = new u(
Math.min(n.start.millis + i, n.stop.millis)
);
if (n.start.millis > r.range[0].millis && n.stop.millis < r.range[1].millis) {
const c = r.copy();
r.range[1] = a, c.range[0] = n.stop, s.push(c);
} else n.start.millis <= r.range[0].millis ? r.range[0] = n.stop : n.stop.millis >= r.range[1].millis && (r.range[1] = a);
}
return s;
}
};
M([
l(u)
], O.prototype, "_offset", 2);
M([
l(u)
], O.prototype, "duration", 2);
M([
l(Qe)
], O.prototype, "range", 2);
M([
l(U)
], O.prototype, "transcript", 1);
M([
l()
], O.prototype, "muted", 1);
M([
l()
], O.prototype, "volume", 1);
let lt = O;
function At(o, t) {
return o.start.millis >= t[0].millis && o.start.millis <= t[1].millis || o.stop.millis <= t[1].millis && o.stop.millis >= t[0].millis;
}
class tt {
static fromJSON(t) {
return typeof t == "object" ? R.fromJSON(t) : t;
}
}
class Rt {
static fromJSON(t) {
return typeof t.x == "object" && (t.x = R.fromJSON(t.x)), typeof t.y == "object" && (t.y = R.fromJSON(t.y)), t;
}
}
let qe = class {
target;
animation;
constructor(t) {
this.target = t;
}
init(t, e, i = 0, s) {
if (!(t in this.target))
throw new Error(`Property [${String(t)}] cannot be assigned`);
const n = [i], r = [e];
typeof this.target[t] == typeof e && i != 0 && (n.unshift(0), r.unshift(this.target[t])), this.target[t] = this.animation = new R(n, r, { easing: s });
}
};
function ts(o) {
const t = new Proxy(o, {
get(e, i) {
return i == "to" ? (s, n) => {
if (!e.animation)
throw new g({
code: "undefinedKeyframe",
message: "Cannot use 'to() before selecting a property"
});
const a = new u(e.animation.input.at(-1)).frames + n;
return e.animation.push(a, s), t;
} : (s, n, r) => (e.init(i, s, n, r), t);
}
});
return t;
}
class es extends qe {
}
var ss = Object.defineProperty, is = Object.getOwnPropertyDescriptor, F = (o, t, e, i) => {
for (var s = i > 1 ? void 0 : i ? is(t, e) : t, n = o.length - 1, r; n >= 0; n--)
(r = o[n]) && (s = (i ? r(t, e, s) : r(s)) || s);
return i && s && ss(t, e, s), s;
};
function ct(o) {
class t extends o {
/**
* Apply one or more `Pixi.js` filters to the clip.
* @example
* clip.filters = [new BlurFilter()];
*/
filters;
_height;
_width;
_position = {
x: this.view.position.x,
y: this.view.position.y
};
_scale;
rotation = this.view.angle;
alpha = 1;
translate = { x: 0, y: 0 };
/**
* The coordinate of the object relative to the local coordinates of the parent.
* @default { x: 0, y: 0 }
*/
get position() {
return this._position;
}
set position(i) {
typeof i == "string" ? (this._position = { x: "50%", y: "50%" }, this.anchor = { x: 0.5, y: 0.5 }) : this._position = i;
}
/**
* The scale factors of this object along the local coordinate axes.
* Will be added to the scale applied by setting height and/or width
* @default { x: 1, y: 1 }
*/
get scale() {
return this._scale ?? {
x: this.view.scale.x,
y: this.view.scale.y
};
}
set scale(i) {
typeof i == "number" || i instanceof R || typeof i == "function" ? this._scale = { x: i, y: i } : this._scale = i;
}
/**
* The position of the clip on the x axis relative.
* An alias to position.x
* @default 0
*/
get x() {
return this._position.x;
}
set x(i) {
this._position.x = i;
}
/**
* The position of the clip on the y axis. An alias to position.y
* @default 0
*/
get y() {
return this._position.y;
}
set y(i) {
this._position.y = i;
}
/**
* Offset relative to the x position
* @default 0
*/
get translateX() {
return this.translate.x;
}
set translateX(i) {
this.translate.x = i;
}
/**
* Offset relative to the y position
* @default 0
*/
get translateY() {
return this.translate.y;
}
set translateY(i) {
this.translate.y = i;
}
/**
* The height of the clip/container
*/
get height() {
return this._height ?? this.view.height;
}
set height(i) {
this._height = i;
}
/**
* The width of the clip/container
*/
get width() {
return this._width ?? this.view.width;
}
set width(i) {
this._width = i;
}
/**
* The mask to apply to the clip
*/
get mask() {
return this.view.mask;
}
set mask(i) {
this.view.mask = i ?? null;
}
get anchor() {
return this.view.children[0] instanceof Y ? {
x: this.view.children[0].anchor.x,
y: this.view.children[0].anchor.y
} : { x: 0, y: 0 };
}
set anchor(i) {
const s = typeof i == "number" ? { x: i, y: i } : i;
for (const n of this.view.children)
n instanceof Y && n.anchor.set(s.x, s.y);
}
enter() {
this.filters && !this.view.filters && (this.view.filters = this.filters);
}
exit() {
this.filters && this.view.filters && (this.view.filters = null);
}
animate() {
return ts(
new es(this)
);
}
}
return F([
l(tt)
], t.prototype, "_height", 2), F([
l(tt)
], t.prototype, "_width", 2), F([
l(Rt)
], t.prototype, "_position", 2), F([
l(Rt)
], t.prototype, "_scale", 2), F([
l(tt)
], t.prototype, "rotation", 2), F([
l(tt)
], t.prototype, "alpha", 2), F([
l(Rt)
], t.prototype, "translate", 2), F([
l()
], t.prototype, "anchor", 1), t;
}
function ht(o, t, e) {
const i = e.value;
return e.value = function(...s) {
const n = s[0].subtract(this.start), r = {
width: this.track?.composition?.width ?? 1920,
height: this.track?.composition?.height ?? 1080
};
let a;
typeof this.translate.x == "number" ? a = this.translate.x : typeof this.translate.x == "function" ? a = this.translate.x.bind(this)(n) : a = this.translate.x.value(n);
let c;
typeof this.translate.y == "number" ? c = this.translate.y : typeof this.translate.y == "function" ? c = this.translate.y.bind(this)(n) : c = t