fix: fix file name conflicts

pressing "download all" with multiple files of same name gets overwritten (e.g. when pasting screenshots/from clipboard)
This commit is contained in:
Maya 2026-02-11 18:41:28 +03:00
parent 9332c95721
commit 167991081a
No known key found for this signature in database
2 changed files with 58 additions and 13 deletions

View File

@ -265,7 +265,9 @@ export class VertdConverter extends Converter {
new FormatInfo("mov", true, true), new FormatInfo("mov", true, true),
new FormatInfo("gif", true, true), new FormatInfo("gif", true, true),
new FormatInfo("apng", true, true), new FormatInfo("apng", true, true),
new FormatInfo("webp", false, true), // for some odd reasons, ffmpeg only supports encoding webp and not decoding -- https://trac.ffmpeg.org/ticket/4907 // for some odd reasons, ffmpeg only supports encoding webp and not decoding -- https://trac.ffmpeg.org/ticket/4907
// honestly, we could probably convert webp to apng, then apng to (x) video format
new FormatInfo("webp", false, true),
new FormatInfo("mts", true, true), new FormatInfo("mts", true, true),
new FormatInfo("ts", true, true), new FormatInfo("ts", true, true),
new FormatInfo("m2ts", true, true), new FormatInfo("m2ts", true, true),

View File

@ -126,9 +126,11 @@ class Files {
// check if completely transparent // check if completely transparent
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const isTransparent = Array.from(imageData.data).every((value, index) => { const isTransparent = Array.from(imageData.data).every(
return (index + 1) % 4 !== 0 || value === 0; (value, index) => {
}); return (index + 1) % 4 !== 0 || value === 0;
},
);
if (isTransparent) { if (isTransparent) {
canvas.remove(); canvas.remove();
return undefined; return undefined;
@ -271,7 +273,9 @@ class Files {
} }
const converter = converters const converter = converters
.sort(byNative(format)) .sort(byNative(format))
.find((converter) => converter.formatStrings().includes(format)); .find((converter) =>
converter.formatStrings().includes(format),
);
if (!converter) { if (!converter) {
log(["files"], `no converter found for ${file.name}`); log(["files"], `no converter found for ${file.name}`);
this.files.push(new VertFile(file, format)); this.files.push(new VertFile(file, format));
@ -312,7 +316,10 @@ class Files {
action: () => { action: () => {
this.files = [ this.files = [
...this.files.filter( ...this.files.filter(
(f) => !f.converters.map((c) => c.name).includes("vertd"), (f) =>
!f.converters
.map((c) => c.name)
.includes("vertd"),
), ),
]; ];
this._warningShown = false; this._warningShown = false;
@ -321,7 +328,10 @@ class Files {
{ {
text: m["convert.external_warning.yes"](), text: m["convert.external_warning.yes"](),
action: () => { action: () => {
localStorage.setItem("acceptedExternalWarning", "true"); localStorage.setItem(
"acceptedExternalWarning",
"true",
);
this._warningShown = false; this._warningShown = false;
}, },
}, },
@ -337,7 +347,14 @@ class Files {
public add(file: VertFile[] | null | undefined): void; public add(file: VertFile[] | null | undefined): void;
public add(file: FileList | null | undefined): void; public add(file: FileList | null | undefined): void;
public add( public add(
file: VertFile | File | VertFile[] | File[] | FileList | null | undefined, file:
| VertFile
| File
| VertFile[]
| File[]
| FileList
| null
| undefined,
) { ) {
if (!file) return; if (!file) return;
if (Array.isArray(file) || file instanceof FileList) { if (Array.isArray(file) || file instanceof FileList) {
@ -357,11 +374,13 @@ class Files {
} }
public async downloadAll() { public async downloadAll() {
if (files.files.length === 0) return; if (this.files.length === 0) return;
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
const dlFiles: any[] = []; const dlFiles: any[] = [];
for (let i = 0; i < files.files.length; i++) { const fileNames: string[] = [];
const file = files.files[i];
for (let i = 0; i < this.files.length; i++) {
const file = this.files[i];
const result = file.result; const result = file.result;
if (!result) { if (!result) {
@ -372,12 +391,33 @@ class Files {
let to = result.to; let to = result.to;
if (!to.startsWith(".")) to = `.${to}`; if (!to.startsWith(".")) to = `.${to}`;
fileNames.push(file.file.name.replace(/\.[^/.]+$/, "") + to);
}
for (let i = 0; i < this.files.length; i++) {
const file = this.files[i];
const result = file.result;
if (!result) continue;
let fileName = fileNames[i];
// check if this filename appears more than once
const isDuplicate = fileNames.filter((name) => name === fileName).length > 1;
if (isDuplicate) {
const nameParts = fileName.lastIndexOf(".");
const nameWithoutExt = fileName.substring(0, nameParts);
const ext = fileName.substring(nameParts);
fileName = `${nameWithoutExt} (${i + 1})${ext}`;
}
dlFiles.push({ dlFiles.push({
name: file.file.name.replace(/\.[^/.]+$/, "") + to, name: fileName,
lastModified: Date.now(), lastModified: Date.now(),
input: await result.file.arrayBuffer(), input: await result.file.arrayBuffer(),
}); });
} }
const { downloadZip } = await import("client-zip"); const { downloadZip } = await import("client-zip");
const blob = await downloadZip(dlFiles, "converted.zip").blob(); const blob = await downloadZip(dlFiles, "converted.zip").blob();
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
@ -551,7 +591,10 @@ export const getMaxArrayBufferSize = (): number => {
const cached = localStorage.getItem("maxArrayBufferSize"); const cached = localStorage.getItem("maxArrayBufferSize");
if (cached) { if (cached) {
const parsed = Number(cached); const parsed = Number(cached);
log(["converters"], `using cached max ArrayBuffer size: ${parsed} bytes`); log(
["converters"],
`using cached max ArrayBuffer size: ${parsed} bytes`,
);
if (!isNaN(parsed) && parsed > 0) return parsed; if (!isNaN(parsed) && parsed > 0) return parsed;
} }