Files
SubMiner/vendor/hachidori/extension/capture-encoder-client.js
T
sudacode d9fdc7ef6d feat(dictionary): add Hachidori backend support
- Add backend selection, setup gating, Anki integration, and external host support
- Add launcher flags, documentation, packaging, and focused tests
- Open on-demand overlay modals on the first attempt
2026-09-22 00:21:19 -07:00

64 lines
2.2 KiB
JavaScript

// SPDX-License-Identifier: GPL-3.0-or-later
export const CAPTURE_ENCODING_TIMEOUT_MS = 30_000;
export function encodeCapturedAnimation(frames, options, {
WorkerClass = globalThis.Worker,
timeoutMs = CAPTURE_ENCODING_TIMEOUT_MS,
onProgress = () => {},
signal,
} = {}) {
if (typeof WorkerClass !== "function") return Promise.reject(new Error("Media encoder workers are unavailable."));
const worker = new WorkerClass(new URL("./capture-encoder-worker.js", import.meta.url), {
type: "module",
name: "hachidori-capture-encoder",
});
const id = crypto.randomUUID();
return new Promise((resolve, reject) => {
let settled = false;
let timer;
const finish = (callback, value) => {
if (settled) return;
settled = true;
clearTimeout(timer);
signal?.removeEventListener("abort", onAbort);
worker.terminate();
callback(value);
};
const onAbort = () => finish(reject, new Error("Media encoding was cancelled."));
timer = setTimeout(() => {
finish(reject, new Error("Media encoding exceeded the 30-second deadline."));
}, timeoutMs);
if (signal?.aborted) {
onAbort();
return;
}
signal?.addEventListener("abort", onAbort, { once: true });
worker.addEventListener("error", event => {
finish(reject, event.error ?? new Error(event.message || "The media encoder worker stopped."));
});
worker.addEventListener("message", event => {
const response = event.data;
if (response?.id !== id) return;
if (response.type === "progress") {
onProgress(response.completed, response.total, response.heapBytes);
return;
}
if (response.type !== "result") return;
if (!response.ok) {
finish(reject, new Error(response.error || "Media encoding failed."));
return;
}
finish(resolve, new Uint8Array(response.data));
});
const transferable = [];
const payloadFrames = frames.map(frame => {
const data = frame.data instanceof Uint8Array ? frame.data.slice()
: new Uint8Array(frame.data).slice();
transferable.push(data.buffer);
return { ...frame, data: data.buffer };
});
worker.postMessage({ type: "encode", id, frames: payloadFrames, ...options }, transferable);
});
}