feat: better progress reporting for archives

This commit is contained in:
Maya 2025-11-22 18:39:13 +03:00
parent 3b443e16be
commit 255c17d3e0
No known key found for this signature in database
3 changed files with 68 additions and 23 deletions

View File

@ -185,7 +185,6 @@ class Files {
`extracted ${entries.length} files from zip (converters: ${converterCount}, compatible: ${canConvertAsOne})`,
);
// TODO: allow user to extract zip if they want to convert individuals (along with image sequence option) in dropdown
if (canConvertAsOne) {
// all files use same converter - add zip as a single VertFile file
const vf = new VertFile(file, ".zip");

View File

@ -122,38 +122,84 @@ export class VertFile {
private async convertZip(converter: Converter): Promise<VertFile> {
const { extractZip, createZip } = await import("$lib/util/zip");
const { default: PQueue } = await import("p-queue");
const entries = await extractZip(this.file);
const totalFiles = entries.length;
let processedFiles = 0;
const fileProgress: number[] = new Array(totalFiles).fill(0);
const convertedFiles: File[] = [];
const queue = new PQueue({ concurrency: navigator.hardwareConcurrency || 4 });
const updateProgress = () => {
const totalProgress = fileProgress.reduce((sum, p) => sum + p, 0);
this.progress = Math.round(totalProgress / totalFiles);
};
// convert all files in the zip
for (const { filename, data } of entries) {
if (this.cancelled) {
throw new Error("Conversion cancelled");
}
await queue.addAll(
entries.map(({ filename, data }, index) => async () => {
if (this.cancelled) {
throw new Error("Conversion cancelled");
}
const file = new File([new Uint8Array(data)], filename, {
type: "application/octet-stream",
});
const tempVFile = new VertFile(file, this.to);
tempVFile.converters = [converter];
const file = new File([new Uint8Array(data)], filename, {
type: "application/octet-stream",
});
const tempVFile = new VertFile(file, this.to);
tempVFile.converters = [converter];
const converted = await converter.convert(tempVFile, this.to);
if (converter.reportsProgress) {
// track progress of individual files
const progressInterval = setInterval(() => {
fileProgress[index] = tempVFile.progress;
updateProgress();
}, 100);
let outputExt = this.to;
if (!outputExt.startsWith(".")) outputExt = `.${outputExt}`;
const baseName = filename.replace(/\.[^/.]+$/, "");
const outputFilename = `${baseName}${outputExt}`;
try {
const converted = await converter.convert(
tempVFile,
this.to,
);
convertedFiles.push(
new File([await converted.file.arrayBuffer()], outputFilename),
);
let outputExt = this.to;
if (!outputExt.startsWith("."))
outputExt = `.${outputExt}`;
const baseName = filename.replace(/\.[^/.]+$/, "");
const outputFilename = `${baseName}${outputExt}`;
processedFiles++;
this.progress = Math.round((processedFiles / totalFiles) * 100); // TODO: show live progress between all files rather than per file
}
convertedFiles[index] = new File(
[await converted.file.arrayBuffer()],
outputFilename,
);
fileProgress[index] = 100;
updateProgress();
} finally {
clearInterval(progressInterval);
}
} else {
// else track progress via completions only
const converted = await converter.convert(
tempVFile,
this.to,
);
let outputExt = this.to;
if (!outputExt.startsWith(".")) outputExt = `.${outputExt}`;
const baseName = filename.replace(/\.[^/.]+$/, "");
const outputFilename = `${baseName}${outputExt}`;
convertedFiles[index] = new File(
[await converted.file.arrayBuffer()],
outputFilename,
);
fileProgress[index] = 100;
updateProgress();
}
}),
);
// return zip of converted files
const resultArray = await createZip(convertedFiles);

View File

@ -179,7 +179,7 @@
<ProgressBar
min={0}
max={100}
progress={currentConverter?.reportsProgress
progress={currentConverter?.reportsProgress || file.isZip()
? file.progress
: null}
/>