mirror of https://github.com/VERT-sh/VERT.git
feat: new* conversion resolver, vertd gif support
This commit is contained in:
parent
0e62d79a23
commit
c00dac9207
|
@ -81,7 +81,7 @@ export class FFmpegConverter extends Converter {
|
|||
window.plausible("convert", {
|
||||
props: {
|
||||
type: "audio",
|
||||
}
|
||||
},
|
||||
});
|
||||
ffmpeg.terminate();
|
||||
return new VertFile(new File([output], input.name), to);
|
||||
|
|
|
@ -193,7 +193,15 @@ export class VertdConverter extends Converter {
|
|||
public name = "vertd";
|
||||
public ready = $state(false);
|
||||
public reportsProgress = true;
|
||||
public supportedFormats = [".mkv", ".mp4", ".webm", ".avi", ".wmv", ".mov"];
|
||||
public supportedFormats = [
|
||||
".mkv",
|
||||
".mp4",
|
||||
".webm",
|
||||
".avi",
|
||||
".wmv",
|
||||
".mov",
|
||||
".gif",
|
||||
];
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private log: (...msg: any[]) => void = () => {};
|
||||
|
||||
|
@ -260,14 +268,7 @@ export class VertdConverter extends Converter {
|
|||
});
|
||||
// const res = await fetch(url).then((res) => res.blob());
|
||||
const res = await downloadFile(url, input);
|
||||
resolve(
|
||||
new VertFile(
|
||||
new File([res], input.name),
|
||||
to,
|
||||
this,
|
||||
undefined,
|
||||
),
|
||||
);
|
||||
resolve(new VertFile(new File([res], input.name), to));
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ export class VipsConverter extends Converter {
|
|||
".heif", // HEIF files that are encoded like HEIC files (and HEIC files in general) aren't supported due to https://github.com/kleisauke/wasm-vips/issues/3
|
||||
".avif",
|
||||
".jxl",
|
||||
".svg"
|
||||
".svg",
|
||||
];
|
||||
|
||||
public readonly reportsProgress = false;
|
||||
|
@ -49,11 +49,17 @@ export class VipsConverter extends Converter {
|
|||
this.worker.onmessage = (e) => {
|
||||
const message: WorkerMessage = e.data;
|
||||
log(["converters", this.name], `received message ${message.type}`);
|
||||
if (message.type === "loaded") {
|
||||
this.ready = true;
|
||||
} else if (message.type === "error") {
|
||||
error(["converters", this.name], `error in worker: ${message.error}`);
|
||||
addToast("error", `Error in VIPS worker, some features may not work.`);
|
||||
if (message.type === "loaded") {
|
||||
this.ready = true;
|
||||
} else if (message.type === "error") {
|
||||
error(
|
||||
["converters", this.name],
|
||||
`error in worker: ${message.error}`,
|
||||
);
|
||||
addToast(
|
||||
"error",
|
||||
`Error in VIPS worker, some features may not work.`,
|
||||
);
|
||||
throw new Error(message.error);
|
||||
}
|
||||
};
|
||||
|
@ -78,7 +84,7 @@ export class VipsConverter extends Converter {
|
|||
window.plausible("convert", {
|
||||
props: {
|
||||
type: "image",
|
||||
}
|
||||
},
|
||||
});
|
||||
return new VertFile(
|
||||
new File([res.output as unknown as BlobPart], input.name),
|
||||
|
|
|
@ -9,7 +9,7 @@ class Files {
|
|||
public files = $state<VertFile[]>([]);
|
||||
|
||||
public requiredConverters = $derived(
|
||||
Array.from(new Set(files.files.map((f) => f.converter))),
|
||||
Array.from(new Set(files.files.map((f) => f.converters).flat())),
|
||||
);
|
||||
|
||||
public ready = $derived(
|
||||
|
@ -33,15 +33,14 @@ class Files {
|
|||
try {
|
||||
if (isAudio) {
|
||||
// try to get the thumbnail from the audio via music-metadata
|
||||
const {common} = await parseBlob(file.file, {skipPostHeaders: true});
|
||||
const { common } = await parseBlob(file.file, {
|
||||
skipPostHeaders: true,
|
||||
});
|
||||
const cover = selectCover(common.picture);
|
||||
if (cover) {
|
||||
const blob = new Blob(
|
||||
[cover.data],
|
||||
{
|
||||
type: cover.format,
|
||||
},
|
||||
);
|
||||
const blob = new Blob([cover.data], {
|
||||
type: cover.format,
|
||||
});
|
||||
file.blobUrl = URL.createObjectURL(blob);
|
||||
}
|
||||
} else if (isVideo) {
|
||||
|
@ -118,7 +117,7 @@ class Files {
|
|||
);
|
||||
if (!converter) {
|
||||
log(["files"], `no converter found for ${file.name}`);
|
||||
this.files.push(new VertFile(file, format, null));
|
||||
this.files.push(new VertFile(file, format));
|
||||
return;
|
||||
}
|
||||
const to = converter.supportedFormats.find((f) => f !== format);
|
||||
|
@ -126,7 +125,7 @@ class Files {
|
|||
log(["files"], `no output format found for ${file.name}`);
|
||||
return;
|
||||
}
|
||||
const vf = new VertFile(file, to, converter);
|
||||
const vf = new VertFile(file, to);
|
||||
this.files.push(vf);
|
||||
this._addThumbnail(vf);
|
||||
|
||||
|
@ -136,13 +135,15 @@ class Files {
|
|||
if (isVideo && !acceptedExternalWarning && !this._warningShown) {
|
||||
this._warningShown = true;
|
||||
const message =
|
||||
"Some of your files will be uploaded to an external server to be converted. Do you want to continue?";
|
||||
"If you choose to convert into a video format, some of your files will be uploaded to an external server to be converted. Do you want to continue?";
|
||||
const buttons = [
|
||||
{
|
||||
text: "No",
|
||||
action: () => {
|
||||
this.files = this.files.filter(
|
||||
(f) => f.converter?.name !== "vertd",
|
||||
this.files = this.files.filter((f) =>
|
||||
f.converters
|
||||
.map((c) => c.name)
|
||||
.includes("vertd"),
|
||||
);
|
||||
this._warningShown = false;
|
||||
},
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { converters } from "$lib/converters";
|
||||
import type { Converter } from "$lib/converters/converter.svelte";
|
||||
import { error } from "$lib/logger";
|
||||
import { addToast } from "$lib/store/ToastProvider";
|
||||
|
@ -22,29 +23,49 @@ export class VertFile {
|
|||
|
||||
public processing = $state(false);
|
||||
|
||||
public converter: Converter | null = null;
|
||||
public converters: Converter[] = [];
|
||||
|
||||
public findConverters(supportedFormats: string[] = [this.from]) {
|
||||
const converter = this.converters.filter((converter) =>
|
||||
converter.supportedFormats.map((f) => supportedFormats.includes(f)),
|
||||
);
|
||||
console.log(this.converters, supportedFormats);
|
||||
return converter;
|
||||
}
|
||||
|
||||
public findConverter() {
|
||||
const converter = this.converters.find(
|
||||
(converter) =>
|
||||
converter.supportedFormats.includes(this.from) &&
|
||||
converter.supportedFormats.includes(this.to),
|
||||
);
|
||||
return converter;
|
||||
}
|
||||
|
||||
constructor(
|
||||
public readonly file: File,
|
||||
to: string,
|
||||
converter?: Converter | null,
|
||||
blobUrl?: string,
|
||||
) {
|
||||
this.to = to;
|
||||
this.converter = converter ?? null;
|
||||
this.converters = converters.filter((c) =>
|
||||
c.supportedFormats.includes(this.from),
|
||||
);
|
||||
this.convert = this.convert.bind(this);
|
||||
this.download = this.download.bind(this);
|
||||
this.blobUrl = blobUrl;
|
||||
}
|
||||
|
||||
public async convert() {
|
||||
if (!this.converter) throw new Error("No converter found");
|
||||
if (!this.converters.length) throw new Error("No converters found");
|
||||
const converter = this.findConverter();
|
||||
if (!converter) throw new Error("No converter found");
|
||||
this.result = null;
|
||||
this.progress = 0;
|
||||
this.processing = true;
|
||||
let res;
|
||||
try {
|
||||
res = await this.converter.convert(this, this.to);
|
||||
res = await converter.convert(this, this.to);
|
||||
this.result = res;
|
||||
} catch (err) {
|
||||
const castedErr = err as Error;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
import Panel from "$lib/components/visual/Panel.svelte";
|
||||
import ProgressBar from "$lib/components/visual/ProgressBar.svelte";
|
||||
import Tooltip from "$lib/components/visual/Tooltip.svelte";
|
||||
import { converters } from "$lib/converters";
|
||||
import {
|
||||
effects,
|
||||
files,
|
||||
|
@ -43,15 +44,15 @@
|
|||
// Set gradient color depending on the file types
|
||||
// TODO: if more file types added, add a "fileType" property to the file object
|
||||
const allAudio = files.files.every(
|
||||
(file) => file.converter?.name === "ffmpeg",
|
||||
(file) => file.findConverter()?.name === "ffmpeg",
|
||||
);
|
||||
const allImages = files.files.every(
|
||||
(file) =>
|
||||
file.converter?.name !== "ffmpeg" &&
|
||||
file.converter?.name !== "vertd",
|
||||
file.findConverter()?.name !== "ffmpeg" &&
|
||||
file.findConverter()?.name !== "vertd",
|
||||
);
|
||||
const allVideos = files.files.every(
|
||||
(file) => file.converter?.name === "vertd",
|
||||
(file) => file.findConverter()?.name === "vertd",
|
||||
);
|
||||
|
||||
if (files.files.length === 1 && files.files[0].blobUrl && !allVideos) {
|
||||
|
@ -72,11 +73,21 @@
|
|||
</script>
|
||||
|
||||
{#snippet fileItem(file: VertFile, index: number)}
|
||||
{@const isAudio = file.converter?.name === "ffmpeg"}
|
||||
{@const isVideo = file.converter?.name === "vertd"}
|
||||
{@const availableConverters = file.findConverters()}
|
||||
{@const currentConverter = converters.find(
|
||||
(c) =>
|
||||
c.supportedFormats.includes(file.from) &&
|
||||
c.supportedFormats.includes(file.to),
|
||||
)}
|
||||
{@const isAudio = converters
|
||||
.find((c) => c.name === "ffmpeg")
|
||||
?.supportedFormats.includes(file.from)}
|
||||
{@const isVideo = converters
|
||||
.find((c) => c.name === "vertd")
|
||||
?.supportedFormats.includes(file.from)}
|
||||
<Panel class="p-5 flex flex-col min-w-0 gap-4 relative">
|
||||
<div class="flex-shrink-0 h-8 w-full flex items-center gap-2">
|
||||
{#if !file.converter}
|
||||
{#if !converters.length}
|
||||
<FileQuestionIcon size="24" class="flex-shrink-0" />
|
||||
{:else if isAudio}
|
||||
<AudioLines size="24" class="flex-shrink-0" />
|
||||
|
@ -90,7 +101,7 @@
|
|||
<ProgressBar
|
||||
min={0}
|
||||
max={100}
|
||||
progress={file.converter?.reportsProgress
|
||||
progress={currentConverter?.reportsProgress
|
||||
? file.progress
|
||||
: null}
|
||||
/>
|
||||
|
@ -110,7 +121,7 @@
|
|||
<XIcon size="24" class="text-muted" />
|
||||
</button>
|
||||
</div>
|
||||
{#if !file.converter}
|
||||
{#if !currentConverter}
|
||||
{#if file.name.startsWith("vertd")}
|
||||
<div
|
||||
class="h-full flex flex-col text-center justify-center text-failure"
|
||||
|
@ -185,10 +196,12 @@
|
|||
>
|
||||
<!-- cannot convert to svg or heif -->
|
||||
<Dropdown
|
||||
options={file.converter?.supportedFormats?.filter(
|
||||
(format) =>
|
||||
format !== ".svg" && format !== ".heif",
|
||||
) || []}
|
||||
options={availableConverters
|
||||
.flatMap((c) => c.supportedFormats)
|
||||
.filter(
|
||||
(format) =>
|
||||
format !== ".svg" && format !== ".heif",
|
||||
) || []}
|
||||
bind:selected={file.to}
|
||||
onselect={(option) => handleSelect(option, file)}
|
||||
/>
|
||||
|
|
Loading…
Reference in New Issue