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": { "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": { "external_warning": {
"title": "External server 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?", "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}", "general": "Error converting {file}: {message}",
"cancel": "Error canceling conversion for {file}: {message}", "cancel": "Error canceling conversion for {file}: {message}",
"magick": "Error in Magick worker, image conversion may not work as expected.", "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.", "no_audio": "No audio stream found.",
"invalid_rate": "Invalid sample rate specified: {rate}Hz" "invalid_rate": "Invalid sample rate specified: {rate}Hz"
} }

View File

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