feat: zip archive toasts

also localizes pandoc error toast

this zip archive feature could serve as a base to handle #75 and #81
This commit is contained in:
Maya 2025-10-25 11:24:58 +03:00
parent abbfbacf0e
commit 256876181e
No known key found for this signature in database
3 changed files with 49 additions and 9 deletions

View File

@ -47,6 +47,11 @@
}
},
"convert": {
"zip_file": {
"extracting": "Detected ZIP archive: {filename}",
"extracted": "Extracted {extract_count} files from {filename}. {ignore_count} items were ignored.",
"extract_error": "Error extracting {filename}: {error}"
},
"external_warning": {
"title": "External server warning",
"text": "If you choose to convert into a video format, those files will be uploaded to an external server to be converted. Do you want to continue?",
@ -247,7 +252,8 @@
"general": "Error converting {file}: {message}",
"cancel": "Error canceling conversion for {file}: {message}",
"magick": "Error in Magick worker, image conversion may not work as expected.",
"ffmpeg": "Error loading ffmpeg, some features may not work.",
"ffmpeg": "Error loading FFmpeg, some features may not work as expected.",
"pandoc": "Error loading Pandoc worker, document conversion may not work as expected.",
"no_audio": "No audio stream found.",
"invalid_rate": "Invalid sample rate specified: {rate}Hz"
}

View File

@ -26,9 +26,13 @@ export class PandocConverter extends Converter {
this.status = "ready";
} catch (err) {
this.status = "error";
error(
["converters", this.name],
`Failed to load Pandoc worker: ${err}`,
);
ToastManager.add({
type: "error",
message: `Failed to load Pandoc worker: ${err}`, // TODO: i18n
message: m["workers.errors.pandoc"](),
});
}
})();

View File

@ -10,6 +10,7 @@ import { getLocale, setLocale } from "$lib/paraglide/runtime";
import { m } from "$lib/paraglide/messages";
import sanitizeHtml from "sanitize-html";
import { unzip } from "fflate";
import { ToastManager } from "$lib/toast/index.svelte";
class Files {
public files = $state<VertFile[]>([]);
@ -144,6 +145,12 @@ class Files {
private async _handleZipFile(file: File): Promise<void> {
try {
log(["files"], `extracting zip file: ${file.name}`);
ToastManager.add({
type: "info",
message: m["convert.zip_file.extracting"]({
filename: file.name,
}),
});
const arrayBuffer = await file.arrayBuffer();
const uint8Array = new Uint8Array(arrayBuffer);
@ -158,18 +165,20 @@ class Files {
return;
}
log(
["files"],
`extracted ${Object.keys(unzipped).length} files from zip`,
);
const itemCount = Object.keys(unzipped).length;
let ignoreCount = 0;
log(["files"], `extracted ${itemCount} files from zip`);
for (const [filename, data] of Object.entries(unzipped)) {
if (
filename.startsWith(".") ||
filename.includes("/__MACOSX/") ||
filename.endsWith("/")
)
) {
ignoreCount++;
continue;
}
const buffer = Array.from(data);
const extractedFile = new File(
@ -180,6 +189,15 @@ class Files {
this._add(extractedFile);
}
ToastManager.add({
type: "success",
message: m["convert.zip_file.extracted"]({
filename: file.name,
extract_count: itemCount - ignoreCount,
ignore_count: ignoreCount,
}),
});
resolve();
});
});
@ -200,8 +218,20 @@ class Files {
file.type === "application/x-zip-compressed";
if (isZip) {
await this._handleZipFile(file);
return;
try {
await this._handleZipFile(file);
return;
} catch (err) {
error(["files"], `error extracting zip file: ${err}`);
ToastManager.add({
type: "error",
message: m["convert.zip_file.extract_error"]({
filename: file.name,
error: String(err),
}),
});
return;
}
}
// regular files