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": { "status": {
"text": "<b>Status:</b> {status}", "text": "<b>Status:</b> {status}",
"ready": "ready", "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:" "supported_formats": "Supported formats:"
}, },

View File

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

View File

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

View File

@ -86,19 +86,22 @@ export class MagickConverter extends Converter {
super(); super();
log(["converters", this.name], `created converter`); log(["converters", this.name], `created converter`);
if (!browser) return; if (!browser) return;
this.status = "downloading";
log(["converters", this.name], `loading worker @ ${MagickWorker}`); log(["converters", this.name], `loading worker @ ${MagickWorker}`);
this.worker.onmessage = (e) => { this.worker.onmessage = (e) => {
const message: WorkerMessage = e.data; const message: WorkerMessage = e.data;
log(["converters", this.name], `received message ${message.type}`); log(["converters", this.name], `received message ${message.type}`);
if (message.type === "loaded") { if (message.type === "loaded") {
this.ready = true; this.status = "ready";
} else if (message.type === "error") { } else if (message.type === "error") {
error( error(
["converters", this.name], ["converters", this.name],
`error in worker: ${message.error}`, `error in worker: ${message.error}`,
); );
this.status = "error";
addToast("error", m["workers.errors.magick"]()); 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 { Converter, FormatInfo } from "./converter.svelte";
import { browser } from "$app/environment"; import { browser } from "$app/environment";
import PandocWorker from "$lib/workers/pandoc?worker&url"; import PandocWorker from "$lib/workers/pandoc?worker&url";
import { addToast } from "$lib/store/ToastProvider";
export class PandocConverter extends Converter { export class PandocConverter extends Converter {
public name = "pandoc"; public name = "pandoc";
@ -12,10 +13,17 @@ export class PandocConverter extends Converter {
super(); super();
if (!browser) return; if (!browser) return;
(async () => { (async () => {
this.wasm = await fetch("/pandoc.wasm").then((r) => try {
r.arrayBuffer(), this.status = "downloading";
); this.wasm = await fetch("/pandoc.wasm").then((r) =>
this.ready = true; 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 = (msg) => log(["converters", this.name], msg);
this.log("created converter"); this.log("created converter");
this.log("not rly sure how to implement this :P"); 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> { public async convert(input: VertFile, to: string): Promise<VertFile> {

View File

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

View File

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

View File

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