feat: more verbose worker status

This commit is contained in:
Maya 2025-09-05 23:02:17 +08:00
parent ce88e01a22
commit 5b0d0e2cc8
9 changed files with 77 additions and 33 deletions

View File

@ -31,7 +31,11 @@
"status": {
"text": "<b>Status:</b> {status}",
"ready": "ready",
"not_ready": "not ready"
"not_ready": "not ready",
"not_initialized": "not initialized",
"downloading": "downloading...",
"initializing": "initializing...",
"unknown": "unknown status"
},
"supported_formats": "Supported formats:"
},

View File

@ -1,5 +1,11 @@
import type { VertFile } from "$lib/types";
export type WorkerStatus =
| "not-ready"
| "downloading"
| "ready"
| "error";
export class FormatInfo {
public name: string;
@ -32,14 +38,15 @@ export class Converter {
* List of supported formats.
*/
public supportedFormats: FormatInfo[] = [];
public status: WorkerStatus = $state("not-ready");
public readonly reportsProgress: boolean = false;
/**
* Convert a file to a different format.
* @param input The input file.
* @param to The format to convert to. Includes the dot.
*/
public ready: boolean = $state(false);
public readonly reportsProgress: boolean = false;
public async convert(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
input: VertFile,

View File

@ -83,14 +83,19 @@ export class FFmpegConverter extends Converter {
(async () => {
const baseURL =
"https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.10/dist/esm";
this.status = "downloading";
await this.ffmpeg.load({
coreURL: `${baseURL}/ffmpeg-core.js`,
wasmURL: `${baseURL}/ffmpeg-core.wasm`,
});
this.ready = true;
this.status = "ready";
})();
} catch (err) {
error(["converters", this.name], `error loading ffmpeg: ${err}`);
this.status = "error";
addToast("error", m["workers.errors.ffmpeg"]());
}
}

View File

@ -86,19 +86,22 @@ export class MagickConverter extends Converter {
super();
log(["converters", this.name], `created converter`);
if (!browser) return;
this.status = "downloading";
log(["converters", this.name], `loading worker @ ${MagickWorker}`);
this.worker.onmessage = (e) => {
const message: WorkerMessage = e.data;
log(["converters", this.name], `received message ${message.type}`);
if (message.type === "loaded") {
this.ready = true;
this.status = "ready";
} else if (message.type === "error") {
error(
["converters", this.name],
`error in worker: ${message.error}`,
);
this.status = "error";
addToast("error", m["workers.errors.magick"]());
throw new Error(message.error);
}
};
}

View File

@ -2,6 +2,7 @@ import { VertFile } from "$lib/types";
import { Converter, FormatInfo } from "./converter.svelte";
import { browser } from "$app/environment";
import PandocWorker from "$lib/workers/pandoc?worker&url";
import { addToast } from "$lib/store/ToastProvider";
export class PandocConverter extends Converter {
public name = "pandoc";
@ -12,10 +13,17 @@ export class PandocConverter extends Converter {
super();
if (!browser) return;
(async () => {
this.wasm = await fetch("/pandoc.wasm").then((r) =>
r.arrayBuffer(),
);
this.ready = true;
try {
this.status = "downloading";
this.wasm = await fetch("/pandoc.wasm").then((r) =>
r.arrayBuffer(),
);
this.status = "ready";
} catch (err) {
this.status = "error";
addToast("error", `Failed to load Pandoc worker: ${err}`);
}
})();
}

View File

@ -237,7 +237,7 @@ export class VertdConverter extends Converter {
this.log = (msg) => log(["converters", this.name], msg);
this.log("created converter");
this.log("not rly sure how to implement this :P");
this.ready = true;
this.status = "ready";
}
public async convert(input: VertFile, to: string): Promise<VertFile> {

View File

@ -8,6 +8,7 @@
import { vertdLoaded } from "$lib/store/index.svelte";
import { m } from "$lib/paraglide/messages";
import { link } from "$lib/store/index.svelte";
//import { converters } from "$lib/converters";
let vertdCommit = $state<string | null>(null);
let abortController: AbortController | null = null;
@ -35,12 +36,16 @@
if (err.name !== "AbortError") {
vertdCommit = null;
vertdLoaded.set(false);
// const converter = converters.find((c) => c.name === "vertd");
// if (converter) converter.status = "not-ready";
}
});
} else {
if (abortController) abortController.abort();
vertdCommit = null;
vertdLoaded.set(false);
// const converter = converters.find((c) => c.name === "vertd");
// if (converter) converter.status = "not-ready";
}
return () => {

View File

@ -18,7 +18,7 @@ class Files {
public ready = $derived(
this.files.length === 0
? false
: this.requiredConverters.every((f) => f?.ready) &&
: this.requiredConverters.every((f) => f?.status === "ready") &&
this.files.every((f) => !f.processing),
);
public results = $derived(

View File

@ -10,6 +10,7 @@
import { browser } from "$app/environment";
import "overlayscrollbars/overlayscrollbars.css";
import { onMount } from "svelte";
import type { WorkerStatus } from "$lib/converters/converter.svelte";
const getSupportedFormats = (name: string) =>
converters
@ -20,41 +21,44 @@
)
.join(", ") || "none";
const status: {
const worker: {
[key: string]: {
ready: boolean;
formats: string;
icon: typeof Image;
title: string;
status: WorkerStatus;
};
} = $derived({
Images: {
ready:
converters.find((c) => c.name === "imagemagick")?.ready ||
false,
formats: getSupportedFormats("imagemagick"),
icon: Image,
title: m["upload.cards.images"](),
status:
converters.find((c) => c.name === "imagemagick")?.status ||
"not-ready",
},
Audio: {
ready: converters.find((c) => c.name === "ffmpeg")?.ready || false,
formats: getSupportedFormats("ffmpeg"),
icon: AudioLines,
title: m["upload.cards.audio"](),
status:
converters.find((c) => c.name === "ffmpeg")?.status ||
"not-ready",
},
Documents: {
ready: converters.find((c) => c.name === "pandoc")?.ready || false,
formats: getSupportedFormats("pandoc"),
icon: BookText,
title: m["upload.cards.documents"](),
status:
converters.find((c) => c.name === "pandoc")?.status ||
"not-ready",
},
Video: {
ready:
converters.find((c) => c.name === "vertd")?.ready ||
(false && $vertdLoaded),
formats: getSupportedFormats("vertd"),
icon: Film,
title: m["upload.cards.video"](),
status:
$vertdLoaded === true ? "ready" : "not-ready", // not using converter.status for this
},
});
@ -76,9 +80,21 @@
return "";
};
const getStatusText = (status: WorkerStatus) => {
switch (status) {
case "downloading":
return m["upload.cards.status.downloading"]();
case "ready":
return m["upload.cards.status.ready"]();
default:
// "not-ready", "error" and other statuses (somehow)
return m["upload.cards.status.not_ready"]();
}
};
let scrollContainers: HTMLElement[] = $state([]);
// svelte-ignore state_referenced_locally
let showBlur = $state(Array(Object.keys(status).length).fill(false));
let showBlur = $state(Array(Object.keys(worker).length).fill(false));
onMount(() => {
const handleResize = () => {
@ -129,7 +145,7 @@
<div class="flex gap-4 mt-8 md:flex-row flex-col">
{#if browser}
{#each Object.entries(status) as [key, s], i}
{#each Object.entries(worker) as [key, s], i}
{@const Icon = s.icon}
<div class="file-category-card w-full flex flex-col gap-4">
<div class="file-category-card-inner">
@ -201,16 +217,12 @@
{/if}
<p>
{@html m["upload.cards.status.text"]({
status: s.ready
? m[
"upload.cards.status.ready"
]()
: m[
"upload.cards.status.not_ready"
](),
status: getStatusText(s.status),
})}
</p>
<div class="flex flex-col items-center relative">
<div
class="flex flex-col items-center relative"
>
<b
>{m[
"upload.cards.supported_formats"