mirror of https://github.com/VERT-sh/VERT.git
feat: imagemagick settings logic
this took a bit lol, restructured the conversion workers a little bit - i prob broke vertd and ffmpeg audio for now
This commit is contained in:
parent
33cd998ab8
commit
7fcbdcd73a
|
|
@ -85,6 +85,7 @@
|
||||||
"settings": "Settings",
|
"settings": "Settings",
|
||||||
"title": "File conversion settings",
|
"title": "File conversion settings",
|
||||||
"description": "Change the conversion settings for <b>{filename}</b>, which is using <b>{converter}</b>. These settings may not be available for all formats.",
|
"description": "Change the conversion settings for <b>{filename}</b>, which is using <b>{converter}</b>. These settings may not be available for all formats.",
|
||||||
|
"none": "No settings available for this format.",
|
||||||
"image": {
|
"image": {
|
||||||
"quality": "Quality",
|
"quality": "Quality",
|
||||||
"depth": "Color depth",
|
"depth": "Color depth",
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,11 @@
|
||||||
|
|
||||||
type Props = DialogType;
|
type Props = DialogType;
|
||||||
|
|
||||||
let { id, title, message, buttons, type, ...rest }: Props = $props();
|
let props: Props = $props();
|
||||||
let additional = $derived("additional" in rest ? rest.additional : undefined);
|
// svelte-ignore state_referenced_locally
|
||||||
|
const { id, title, message, buttons, type } = props;
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
|
const additional = "additional" in props ? props.additional : undefined;
|
||||||
|
|
||||||
const colors = {
|
const colors = {
|
||||||
success: "purple",
|
success: "purple",
|
||||||
|
|
@ -53,7 +56,9 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-1 w-full">
|
<div class="flex flex-col gap-1 w-full">
|
||||||
{#if typeof message === "string"}
|
{#if typeof message === "string"}
|
||||||
<p class="text-sm font-normal text-muted whitespace-pre-wrap">{message}</p>
|
<p class="text-sm font-normal text-muted whitespace-pre-wrap">
|
||||||
|
{message}
|
||||||
|
</p>
|
||||||
{:else}
|
{:else}
|
||||||
{@const MessageComponent = message}
|
{@const MessageComponent = message}
|
||||||
<div class="text-sm font-normal text-muted">
|
<div class="text-sm font-normal text-muted">
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@
|
||||||
import { m } from "$lib/paraglide/messages";
|
import { m } from "$lib/paraglide/messages";
|
||||||
import type { VertFile } from "$lib/types";
|
import type { VertFile } from "$lib/types";
|
||||||
import { sanitize } from "$lib/store/index.svelte";
|
import { sanitize } from "$lib/store/index.svelte";
|
||||||
|
import { log } from "$lib/util/logger";
|
||||||
|
import { type ConversionSettings } from "$lib/types/conversion-settings";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
file: VertFile | null;
|
file: VertFile | null;
|
||||||
|
|
@ -14,21 +16,32 @@
|
||||||
|
|
||||||
let { file, onclose }: Props = $props();
|
let { file, onclose }: Props = $props();
|
||||||
|
|
||||||
let settings = $state<Record<string, any>>({});
|
let settings = $state<ConversionSettings>({});
|
||||||
|
|
||||||
const handleSettingChange = (key: string, value: any) => {
|
const handleSettingChange = (key: string, value: any) => {
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
settings[key] = value;
|
settings[key] = value;
|
||||||
console.log(
|
|
||||||
`Changed settings for ${file.name}: ${JSON.stringify(settings, null, 2)}`,
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const applySettings = () => {
|
const applySettings = async () => {
|
||||||
onclose?.();
|
onclose?.();
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
file.conversionSettings = { ...file.conversionSettings, ...settings };
|
const converter = file.findConverter();
|
||||||
console.log(
|
if (!converter) {
|
||||||
|
log(
|
||||||
|
["settings", "modal"],
|
||||||
|
`No converter found for ${file.name}, cannot apply settings`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// apply defaults, then existing settings, then new settings on top
|
||||||
|
file.conversionSettings = {
|
||||||
|
...(await converter.getDefaultSettings()),
|
||||||
|
...file.conversionSettings,
|
||||||
|
...settings,
|
||||||
|
};
|
||||||
|
log(
|
||||||
|
["settings", "modal"],
|
||||||
`Applied settings for ${file.name}: ${JSON.stringify(file.conversionSettings, null, 2)}`,
|
`Applied settings for ${file.name}: ${JSON.stringify(file.conversionSettings, null, 2)}`,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
@ -58,7 +71,8 @@
|
||||||
<p class="text-base">
|
<p class="text-base">
|
||||||
{@html sanitize(
|
{@html sanitize(
|
||||||
m["convert.settings.description"]({
|
m["convert.settings.description"]({
|
||||||
converter: file.findConverter()?.name,
|
converter:
|
||||||
|
file.findConverter()?.name || "unknown",
|
||||||
filename: file.name,
|
filename: file.name,
|
||||||
}),
|
}),
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,18 @@
|
||||||
import type { ToastProps } from "$lib/util/toast.svelte";
|
import type { ToastProps } from "$lib/util/toast.svelte";
|
||||||
import type { SvelteComponent } from "svelte";
|
import type { SvelteComponent } from "svelte";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
import type { Toast as ToastType } from "$lib/util/toast.svelte";
|
||||||
|
|
||||||
const { id, type, message, durations, ...rest }: ToastProps = $props();
|
const props: {
|
||||||
|
toast: ToastType<unknown>;
|
||||||
|
} = $props();
|
||||||
|
|
||||||
const additional = $derived(
|
// svelte-ignore state_referenced_locally
|
||||||
"additional" in rest ? rest.additional : undefined,
|
const { id, type, message, durations } = props.toast;
|
||||||
);
|
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
|
const additional =
|
||||||
|
"additional" in props.toast ? props.toast.additional : {};
|
||||||
|
|
||||||
const colors = {
|
const colors = {
|
||||||
success: "purple",
|
success: "purple",
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ export class Converter {
|
||||||
public async convert(
|
public async convert(
|
||||||
input: VertFile,
|
input: VertFile,
|
||||||
to: string,
|
to: string,
|
||||||
settings?: ConversionSettings,
|
settings: ConversionSettings,
|
||||||
...args: any[]
|
...args: any[]
|
||||||
): Promise<VertFile> {
|
): Promise<VertFile> {
|
||||||
throw new Error("Not implemented");
|
throw new Error("Not implemented");
|
||||||
|
|
|
||||||
|
|
@ -118,12 +118,13 @@ export class MagickConverter extends Converter {
|
||||||
|
|
||||||
public async getAvailableSettings(): Promise<SettingDefinition[]> {
|
public async getAvailableSettings(): Promise<SettingDefinition[]> {
|
||||||
// images - quality/compression/quantize/interlace/depth-DPI, resize, crop, rotate, flip/flop, autoOrient?, color space/bit depth, transparency settings
|
// images - quality/compression/quantize/interlace/depth-DPI, resize, crop, rotate, flip/flop, autoOrient?, color space/bit depth, transparency settings
|
||||||
|
const global = Settings.instance.settings;
|
||||||
|
|
||||||
const quality: SettingDefinition = {
|
const quality: SettingDefinition = {
|
||||||
key: "quality",
|
key: "quality",
|
||||||
label: m["convert.settings.image.quality"](),
|
label: m["convert.settings.image.quality"](),
|
||||||
type: "number",
|
type: "number",
|
||||||
default: 100,
|
default: global.magickQuality ?? 100,
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 100,
|
max: 100,
|
||||||
};
|
};
|
||||||
|
|
@ -152,6 +153,7 @@ export class MagickConverter extends Converter {
|
||||||
// what are these even lmao
|
// what are these even lmao
|
||||||
{ value: "auto", label: "Auto" },
|
{ value: "auto", label: "Auto" },
|
||||||
{ value: "srgb", label: "sRGB" },
|
{ value: "srgb", label: "sRGB" },
|
||||||
|
{ value: "cmyk", label: "CMYK" },
|
||||||
{ value: "adobe98", label: "Adobe RGB" },
|
{ value: "adobe98", label: "Adobe RGB" },
|
||||||
{ value: "prophoto", label: "ProPhoto RGB" },
|
{ value: "prophoto", label: "ProPhoto RGB" },
|
||||||
{ value: "displayp3", label: "Display P3" },
|
{ value: "displayp3", label: "Display P3" },
|
||||||
|
|
@ -174,7 +176,7 @@ export class MagickConverter extends Converter {
|
||||||
key: "metadata",
|
key: "metadata",
|
||||||
label: m["convert.settings.common.metadata"](),
|
label: m["convert.settings.common.metadata"](),
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
default: true,
|
default: global.metadata ?? true,
|
||||||
};
|
};
|
||||||
|
|
||||||
// resize, crop, rotate - prob want a ui
|
// resize, crop, rotate - prob want a ui
|
||||||
|
|
@ -194,17 +196,10 @@ export class MagickConverter extends Converter {
|
||||||
public async convert(
|
public async convert(
|
||||||
input: VertFile,
|
input: VertFile,
|
||||||
to: string,
|
to: string,
|
||||||
|
settings: ConversionSettings,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
...args: any[]
|
...args: any[]
|
||||||
): Promise<VertFile> {
|
): Promise<VertFile> {
|
||||||
let compression: number | undefined = args.at(0);
|
|
||||||
if (!compression) {
|
|
||||||
compression = Settings.instance.settings.magickQuality ?? 100;
|
|
||||||
log(
|
|
||||||
["converters", this.name],
|
|
||||||
`using user setting for quality: ${compression}%`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
log(["converters", this.name], `converting ${input.name} to ${to}`);
|
log(["converters", this.name], `converting ${input.name} to ${to}`);
|
||||||
|
|
||||||
// handle converting from SVG manually because magick-wasm doesn't support it
|
// handle converting from SVG manually because magick-wasm doesn't support it
|
||||||
|
|
@ -216,7 +211,7 @@ export class MagickConverter extends Converter {
|
||||||
input.to,
|
input.to,
|
||||||
);
|
);
|
||||||
if (to === ".png") return pngFile; // if target is png, return it directly
|
if (to === ".png") return pngFile; // if target is png, return it directly
|
||||||
return await this.convert(pngFile, to, ...args); // otherwise, recursively convert png to user's target format
|
return await this.convert(pngFile, to, settings, ...args); // otherwise, recursively convert png to user's target format
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error(
|
error(
|
||||||
["converters", this.name],
|
["converters", this.name],
|
||||||
|
|
@ -270,9 +265,11 @@ export class MagickConverter extends Converter {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// every other format handled by magick worker
|
// every other format handled by magick worker
|
||||||
const keepMetadata: boolean =
|
const conversionSettings = JSON.stringify(
|
||||||
Settings.instance.settings.metadata ?? true;
|
Object.keys(settings).length > 0
|
||||||
log(["converters", this.name], `keep metadata: ${keepMetadata}`);
|
? settings // user-provided settings
|
||||||
|
: await this.getDefaultSettings(), // use defaults if not provided
|
||||||
|
);
|
||||||
const convertMsg: WorkerMessage = {
|
const convertMsg: WorkerMessage = {
|
||||||
type: "convert",
|
type: "convert",
|
||||||
id: input.id,
|
id: input.id,
|
||||||
|
|
@ -283,8 +280,7 @@ export class MagickConverter extends Converter {
|
||||||
to: input.to,
|
to: input.to,
|
||||||
},
|
},
|
||||||
to,
|
to,
|
||||||
compression,
|
conversionSettings,
|
||||||
keepMetadata,
|
|
||||||
};
|
};
|
||||||
worker.postMessage(convertMsg);
|
worker.postMessage(convertMsg);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -422,7 +422,7 @@ export class VertdConverter extends Converter {
|
||||||
return defaults;
|
return defaults;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async convert(input: VertFile, to: string): Promise<VertFile> {
|
public async convert(input: VertFile, to: string, settings: ConversionSettings): Promise<VertFile> {
|
||||||
if (to.startsWith(".")) to = to.slice(1);
|
if (to.startsWith(".")) to = to.slice(1);
|
||||||
|
|
||||||
let fileUpload = input;
|
let fileUpload = input;
|
||||||
|
|
@ -440,7 +440,7 @@ export class VertdConverter extends Converter {
|
||||||
fileUpload = await magickConverter.convert(
|
fileUpload = await magickConverter.convert(
|
||||||
input,
|
input,
|
||||||
".gif",
|
".gif",
|
||||||
input.conversionSettings,
|
settings,
|
||||||
100,
|
100,
|
||||||
);
|
);
|
||||||
this.log(`successfully converted webp to gif`);
|
this.log(`successfully converted webp to gif`);
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ export interface SettingDefinition {
|
||||||
min?: number;
|
min?: number;
|
||||||
max?: number;
|
max?: number;
|
||||||
step?: number;
|
step?: number;
|
||||||
options?: Array<{ value: string; label: string }>; // for select types
|
options?: Array<{ value: any; label: string }>; // for select types
|
||||||
description?: string;
|
description?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,7 @@ interface ConvertMessage {
|
||||||
to: string;
|
to: string;
|
||||||
} | VertFile;
|
} | VertFile;
|
||||||
to: string;
|
to: string;
|
||||||
compression: number | null;
|
conversionSettings: string; // JSON stringified ConversionSettings
|
||||||
keepMetadata?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FinishedMessage {
|
interface FinishedMessage {
|
||||||
|
|
|
||||||
|
|
@ -188,6 +188,7 @@ export class VertFile {
|
||||||
const converted = await converter.convert(
|
const converted = await converter.convert(
|
||||||
tempVFile,
|
tempVFile,
|
||||||
this.to,
|
this.to,
|
||||||
|
this.conversionSettings,
|
||||||
);
|
);
|
||||||
|
|
||||||
let outputExt = this.to;
|
let outputExt = this.to;
|
||||||
|
|
@ -209,6 +210,7 @@ export class VertFile {
|
||||||
const converted = await converter.convert(
|
const converted = await converter.convert(
|
||||||
tempVFile,
|
tempVFile,
|
||||||
this.to,
|
this.to,
|
||||||
|
this.conversionSettings,
|
||||||
);
|
);
|
||||||
|
|
||||||
let outputExt = this.to;
|
let outputExt = this.to;
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,20 @@
|
||||||
import {
|
import {
|
||||||
|
ColorSpace,
|
||||||
initializeImageMagick,
|
initializeImageMagick,
|
||||||
|
MagickColor,
|
||||||
MagickFormat,
|
MagickFormat,
|
||||||
MagickImage,
|
MagickImage,
|
||||||
MagickImageCollection,
|
MagickImageCollection,
|
||||||
MagickReadSettings,
|
MagickReadSettings,
|
||||||
|
AlphaAction,
|
||||||
type IMagickImage,
|
type IMagickImage,
|
||||||
} from "@imagemagick/magick-wasm";
|
} from "@imagemagick/magick-wasm";
|
||||||
import { makeZip } from "client-zip";
|
import { makeZip } from "client-zip";
|
||||||
import { parseAni } from "$lib/util/parse/ani";
|
import { parseAni } from "$lib/util/parse/ani";
|
||||||
import { parseIcns } from "vert-wasm";
|
import { parseIcns } from "vert-wasm";
|
||||||
import type { WorkerMessage } from "$lib/types";
|
import type { WorkerMessage } from "$lib/types";
|
||||||
|
import type { ConversionSettings } from "$lib/types/conversion-settings";
|
||||||
|
import { log } from "$lib/util/logger";
|
||||||
|
|
||||||
let magickInitialized = false;
|
let magickInitialized = false;
|
||||||
|
|
||||||
|
|
@ -44,9 +49,6 @@ const handleMessage = async (
|
||||||
return { type: "error", error: "magick-wasm not initialized" };
|
return { type: "error", error: "magick-wasm not initialized" };
|
||||||
}
|
}
|
||||||
|
|
||||||
const compression: number | undefined =
|
|
||||||
message.compression ?? undefined;
|
|
||||||
const keepMetadata: boolean = message.keepMetadata ?? true;
|
|
||||||
if (!message.to.startsWith(".")) message.to = `.${message.to}`;
|
if (!message.to.startsWith(".")) message.to = `.${message.to}`;
|
||||||
message.to = message.to.toLowerCase();
|
message.to = message.to.toLowerCase();
|
||||||
if (message.to === ".jfif") message.to = ".jpeg";
|
if (message.to === ".jfif") message.to = ".jpeg";
|
||||||
|
|
@ -55,6 +57,10 @@ const handleMessage = async (
|
||||||
if (from === ".jfif") from = ".jpeg";
|
if (from === ".jfif") from = ".jpeg";
|
||||||
if (from === ".fit") from = ".fits";
|
if (from === ".fit") from = ".fits";
|
||||||
|
|
||||||
|
console.log(JSON.stringify(message, null, 2));
|
||||||
|
const conversionSettings = JSON.parse(
|
||||||
|
message.conversionSettings || "{}",
|
||||||
|
) as ConversionSettings;
|
||||||
const buffer = await message.input.file.arrayBuffer();
|
const buffer = await message.input.file.arrayBuffer();
|
||||||
|
|
||||||
// special ico handling to split them all into separate images
|
// special ico handling to split them all into separate images
|
||||||
|
|
@ -90,8 +96,7 @@ const handleMessage = async (
|
||||||
const output = await magickConvert(
|
const output = await magickConvert(
|
||||||
img,
|
img,
|
||||||
message.to,
|
message.to,
|
||||||
keepMetadata,
|
conversionSettings,
|
||||||
compression,
|
|
||||||
);
|
);
|
||||||
convertedImgs[i] = output;
|
convertedImgs[i] = output;
|
||||||
}),
|
}),
|
||||||
|
|
@ -133,8 +138,7 @@ const handleMessage = async (
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
message.to,
|
message.to,
|
||||||
keepMetadata,
|
conversionSettings,
|
||||||
compression,
|
|
||||||
);
|
);
|
||||||
files.push(
|
files.push(
|
||||||
new File(
|
new File(
|
||||||
|
|
@ -184,8 +188,7 @@ const handleMessage = async (
|
||||||
const converted = await magickConvert(
|
const converted = await magickConvert(
|
||||||
img,
|
img,
|
||||||
message.to,
|
message.to,
|
||||||
keepMetadata,
|
conversionSettings,
|
||||||
compression,
|
|
||||||
);
|
);
|
||||||
outputs.push(converted);
|
outputs.push(converted);
|
||||||
break;
|
break;
|
||||||
|
|
@ -251,8 +254,7 @@ const handleMessage = async (
|
||||||
const converted = await magickConvert(
|
const converted = await magickConvert(
|
||||||
img,
|
img,
|
||||||
message.to,
|
message.to,
|
||||||
keepMetadata,
|
conversionSettings,
|
||||||
compression,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -287,8 +289,7 @@ const readToEnd = async (reader: ReadableStreamDefaultReader<Uint8Array>) => {
|
||||||
const magickConvert = async (
|
const magickConvert = async (
|
||||||
img: IMagickImage,
|
img: IMagickImage,
|
||||||
to: string,
|
to: string,
|
||||||
keepMetadata: boolean,
|
conversionSettings: ConversionSettings,
|
||||||
compression?: number,
|
|
||||||
) => {
|
) => {
|
||||||
let fmt = to.slice(1).toUpperCase();
|
let fmt = to.slice(1).toUpperCase();
|
||||||
if (fmt === "JFIF") fmt = "JPEG";
|
if (fmt === "JFIF") fmt = "JPEG";
|
||||||
|
|
@ -310,10 +311,56 @@ const magickConvert = async (
|
||||||
|
|
||||||
const result = await new Promise<Uint8Array>((resolve, reject) => {
|
const result = await new Promise<Uint8Array>((resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
// magick-wasm automatically clamps (https://github.com/dlemstra/magick-wasm/blob/76fc6f2b0c0497d2ddc251bbf6174b4dc92ac3ea/src/magick-image.ts#L2480)
|
// quality, depth, colorSpace, transparency, metadata
|
||||||
if (compression) img.quality = compression;
|
const quality = conversionSettings.quality as number;
|
||||||
if (!keepMetadata) img.strip();
|
const bitDepth = conversionSettings.depth as number;
|
||||||
|
const colorSpace = conversionSettings.colorSpace as string;
|
||||||
|
const transparency = conversionSettings.transparency as boolean;
|
||||||
|
const metadata = conversionSettings.metadata as boolean;
|
||||||
|
|
||||||
|
if (quality) img.quality = quality;
|
||||||
|
if (bitDepth) img.depth = bitDepth;
|
||||||
|
if (!metadata) img.strip();
|
||||||
|
if (colorSpace) {
|
||||||
|
switch (colorSpace) {
|
||||||
|
case "srgb":
|
||||||
|
img.colorSpace = ColorSpace.sRGB;
|
||||||
|
break;
|
||||||
|
case "cmyk":
|
||||||
|
img.colorSpace = ColorSpace.CMYK;
|
||||||
|
break;
|
||||||
|
case "adobe98":
|
||||||
|
img.colorSpace = ColorSpace.Adobe98;
|
||||||
|
break;
|
||||||
|
case "prophoto":
|
||||||
|
img.colorSpace = ColorSpace.ProPhoto;
|
||||||
|
break;
|
||||||
|
case "displayp3":
|
||||||
|
img.colorSpace = ColorSpace.DisplayP3;
|
||||||
|
break;
|
||||||
|
case "xyz":
|
||||||
|
img.colorSpace = ColorSpace.XYZ;
|
||||||
|
break;
|
||||||
|
case "lab":
|
||||||
|
img.colorSpace = ColorSpace.Lab;
|
||||||
|
break;
|
||||||
|
case "gray":
|
||||||
|
img.colorSpace = ColorSpace.Gray;
|
||||||
|
break;
|
||||||
|
// auto is default so do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!transparency) {
|
||||||
|
img.backgroundColor = new MagickColor(0, 0, 0, 255); // TODO: probably make it an option to set the bg colour
|
||||||
|
img.alpha(AlphaAction.Remove);
|
||||||
|
}
|
||||||
|
|
||||||
|
log(
|
||||||
|
["workers", "imagemagick"],
|
||||||
|
`Converting to ${fmt} with settings: ${JSON.stringify(conversionSettings)}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
// magick-wasm automatically clamps (https://github.com/dlemstra/magick-wasm/blob/76fc6f2b0c0497d2ddc251bbf6174b4dc92ac3ea/src/magick-image.ts#L2480)
|
||||||
img.write(fmt as unknown as MagickFormat, (o: Uint8Array) => {
|
img.write(fmt as unknown as MagickFormat, (o: Uint8Array) => {
|
||||||
resolve(structuredClone(o));
|
resolve(structuredClone(o));
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
const CACHE_NAME = "vert-wasm-cache-v2"; // updated when workers update
|
const CACHE_NAME = "vert-wasm-cache-v3"; // updated when workers update
|
||||||
|
|
||||||
const WASM_FILES = [
|
const WASM_FILES = [
|
||||||
"/pandoc.wasm",
|
"/pandoc.wasm", // from https://github.com/haskell-wasm/pandoc-wasm
|
||||||
"https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.10/dist/esm/ffmpeg-core.js",
|
"https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.10/dist/esm/ffmpeg-core.js",
|
||||||
"https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.10/dist/esm/ffmpeg-core.wasm",
|
"https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.10/dist/esm/ffmpeg-core.wasm",
|
||||||
];
|
];
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue