feat: AIO archive conversion

This commit is contained in:
Maya 2025-11-21 21:48:41 +03:00
parent 3b15fb1d30
commit 0a51188982
No known key found for this signature in database
7 changed files with 53 additions and 12 deletions

View File

@ -47,8 +47,9 @@
}
},
"convert": {
"zip_file": {
"extracting": "Detected ZIP archive: {filename}",
"archive_file": {
"extract": "Extract archive",
"extracting": "Detected archive: {filename}",
"extracted": "Extracted {extract_count} files from {filename}. {ignore_count} items were ignored.",
"detected": "Detected {type} files in {filename}.",
"audio": "audio",

View File

@ -47,7 +47,7 @@
}
},
"convert": {
"zip_file": {
"archive_file": {
"extracting": "ZIP파일 감지됨: {filename}",
"extracted": "{filename}압축 파일에서 {extract_count}개의 파일을 풀었습니다. {ignore_count}개 항목은 무시되었습니다.",
"extract_error": "{filename}압축 파일 풀던 중 오류 발생: {error}"

View File

@ -47,7 +47,7 @@
}
},
"convert": {
"zip_file": {
"archive_file": {
"extracting": "检测到 ZIP 压缩包:{filename}",
"extracted": "从 {filename} 中提取了 {extract_count} 个文件。{ignore_count} 个项目被忽略。",
"extract_error": "提取 {filename} 时出错:{error}"

View File

@ -47,7 +47,7 @@
}
},
"convert": {
"zip_file": {
"archive_file": {
"extracting": "偵測到 ZIP 壓縮檔:{filename}",
"extracted": "從 {filename} 中提取了 {extract_count} 個檔案。{ignore_count} 個項目被忽略。",
"extract_error": "提取 {filename} 時出錯:{error}"

View File

@ -7,7 +7,8 @@
import { ChevronDown, SearchIcon } from "lucide-svelte";
import { onMount } from "svelte";
import { quintOut } from "svelte/easing";
import type { VertFile } from "$lib/types";
import { VertFile } from "$lib/types";
import { log, error } from "$lib/util/logger";
type Props = {
categories: Categories;
@ -276,6 +277,33 @@
}, 0); // let dropdown open first
};
const extract = async () => {
// extract all files in zip, then add all extracted files to files store
if (!file) return;
const { extractZip } = await import("$lib/util/zip");
const extractedFiles = await extractZip(file.file);
if (!Array.isArray(extractedFiles) || extractedFiles.length === 0)
return;
const newFiles = extractedFiles
.map(({ filename, data }) => {
try {
const f = new File([new Uint8Array(data)], filename, {
type: "application/octet-stream",
});
const ext = filename.split(".").pop() ?? "";
return new VertFile(f, ext);
} catch (err) {
return null;
}
})
.filter(Boolean);
files.files = files.files.filter((f) => f !== file);
newFiles.forEach((f) => files.add(f));
};
onMount(() => {
const handleClickOutside = (e: MouseEvent) => {
if (dropdown && !dropdown.contains(e.target as Node)) {
@ -439,6 +467,18 @@
</div>
{/if}
</div>
<!-- format options -->
<!-- TODO: extract zip, image sequence & fps -->
{#if file?.name.toLowerCase().endsWith(".zip")}
<div class="border-t border-separator text-base p-2">
<button
class="w-full p-2 text-center rounded-lg bg-accent text-black"
onclick={() => extract()}
>
{m["convert.archive_file.extract"]()}
</button>
</div>
{/if}
</div>
{/if}
</div>

View File

@ -146,7 +146,7 @@ class Files {
log(["files"], `extracting zip file: ${file.name}`);
ToastManager.add({
type: "info",
message: m["convert.zip_file.extracting"]({
message: m["convert.archive_file.extracting"]({
filename: file.name,
}),
});
@ -208,8 +208,8 @@ class Files {
ToastManager.add({
type: "success",
message: m["convert.zip_file.detected"]({
type: m[`convert.zip_file.${type}`](),
message: m["convert.archive_file.detected"]({
type: m[`convert.archive_file.${type}`](),
filename: file.name,
}),
});
@ -225,7 +225,7 @@ class Files {
ToastManager.add({
type: "success",
message: m["convert.zip_file.extracted"]({
message: m["convert.archive_file.extracted"]({
filename: file.name,
extract_count: entries.length,
ignore_count: 0,
@ -258,7 +258,7 @@ class Files {
error(["files"], `error extracting zip file: ${err}`);
ToastManager.add({
type: "error",
message: m["convert.zip_file.extract_error"]({
message: m["convert.archive_file.extract_error"]({
filename: file.name,
error: String(err),
}),

View File

@ -86,7 +86,7 @@ export class VertFile {
`${file.name.split(".").slice(0, -1).join(".")}.${ext?.toLowerCase()}`,
);
this.file = newFile;
this.to = to;
this.to = to.startsWith(".") ? to : `.${to}`;
this.converters = converters.filter((c) =>
c.formatStrings().includes(this.from),
);