feat: conversion resolver improvements

This commit is contained in:
not-nullptr 2025-04-14 20:06:57 +01:00
parent 61c548eed9
commit b89e55c997
12 changed files with 125 additions and 90 deletions

View File

@ -69,7 +69,7 @@
{#if files.requiredConverters.length === 1}
<!-- cannot convert to svg or heif -->
{@const supported = files.files[0]?.converters
.flatMap((c) => c.supportedFormats)
.flatMap((c) => c.formatStrings((f) => f.toSupported))
?.filter(
(format) => format !== ".svg" && format !== ".heif",
)}

View File

@ -62,8 +62,8 @@
: 'justify-center'} overflow-hidden relative cursor-pointer {settingsStyle
? 'px-4'
: 'px-3'} py-3.5 bg-button {disabled
? 'opacity-50'
: ''} flex items-center {settingsStyle
? 'opacity-50 cursor-auto'
: 'cursor-pointer'} flex items-center {settingsStyle
? 'rounded-xl'
: 'rounded-full'} focus:!outline-none"
onclick={toggle}

View File

@ -31,7 +31,7 @@
)
).filter((c) => typeof c !== "undefined");
acceptedTypes = filteredConverters
.map((c) => c.supportedFormats.join(","))
.map((c) => c.formatStrings((f) => f.fromSupported).join(","))
.join(",");
};

View File

@ -1,5 +1,24 @@
import type { VertFile } from "$lib/types";
export class FormatInfo {
public name: string;
constructor(
name: string,
public fromSupported: boolean,
public toSupported: boolean,
) {
this.name = name;
if (!this.name.startsWith(".")) {
this.name = `.${this.name}`;
}
if (!this.fromSupported && !this.toSupported) {
throw new Error("Format must support at least one direction");
}
}
}
/**
* Base class for all converters.
*/
@ -11,7 +30,7 @@ export class Converter {
/**
* List of supported formats.
*/
public supportedFormats: string[] = [];
public supportedFormats: FormatInfo[] = [];
/**
* Convert a file to a different format.
* @param input The input file.
@ -34,4 +53,11 @@ export class Converter {
public async valid(): Promise<boolean> {
return true;
}
public formatStrings(predicate?: (f: FormatInfo) => boolean) {
if (predicate) {
return this.supportedFormats.filter(predicate).map((f) => f.name);
}
return this.supportedFormats.map((f) => f.name);
}
}

View File

@ -1,5 +1,5 @@
import { VertFile } from "$lib/types";
import { Converter } from "./converter.svelte";
import { Converter, FormatInfo } from "./converter.svelte";
import { FFmpeg } from "@ffmpeg/ffmpeg";
import { browser } from "$app/environment";
import { error, log } from "$lib/logger";
@ -11,17 +11,17 @@ export class FFmpegConverter extends Converter {
public ready = $state(false);
public supportedFormats = [
".mp3",
".wav",
".flac",
".ogg",
".aac",
".m4a",
".wma",
".amr",
".ac3",
".alac",
".aiff",
new FormatInfo("mp3", true, true),
new FormatInfo("wav", true, true),
new FormatInfo("flac", true, true),
new FormatInfo("ogg", true, true),
new FormatInfo("aac", true, true),
new FormatInfo("m4a", true, true),
new FormatInfo("wma", true, true),
new FormatInfo("amr", true, true),
new FormatInfo("ac3", true, true),
new FormatInfo("alac", true, true),
new FormatInfo("aiff", true, true),
];
public readonly reportsProgress = true;

View File

@ -1,5 +1,5 @@
import { VertFile } from "$lib/types";
import { Converter } from "./converter.svelte";
import { Converter, FormatInfo } from "./converter.svelte";
import { browser } from "$app/environment";
import PandocWorker from "$lib/workers/pandoc?worker";
@ -61,23 +61,19 @@ export class PandocConverter extends Converter {
);
}
// public name = "pandoc";
// public ready = $state(false);
// public wasm: ArrayBuffer = null!;
public supportedFormats = [
".docx",
".doc",
".md",
".html",
".rtf",
".csv",
".tsv",
".json",
".rst",
".epub",
".odt",
".docbook",
new FormatInfo("docx", true, true),
new FormatInfo("doc", true, true),
new FormatInfo("md", true, true),
new FormatInfo("html", true, true),
new FormatInfo("rtf", true, true),
new FormatInfo("csv", true, true),
new FormatInfo("tsv", true, true),
new FormatInfo("json", true, true),
new FormatInfo("rst", true, true),
new FormatInfo("epub", true, true),
new FormatInfo("odt", true, true),
new FormatInfo("docbook", true, true),
];
}

View File

@ -1,7 +1,7 @@
import { log } from "$lib/logger";
import { Settings } from "$lib/sections/settings/index.svelte";
import { VertFile } from "$lib/types";
import { Converter } from "./converter.svelte";
import { Converter, FormatInfo } from "./converter.svelte";
interface VertdError {
type: "error";
@ -200,16 +200,18 @@ export class VertdConverter extends Converter {
public name = "vertd";
public ready = $state(false);
public reportsProgress = true;
public supportedFormats = [
".mkv",
".mp4",
".webm",
".avi",
".wmv",
".mov",
".gif",
".mts",
new FormatInfo("mkv", true, true),
new FormatInfo("mp4", true, true),
new FormatInfo("webm", true, true),
new FormatInfo("avi", true, true),
new FormatInfo("wmv", true, true),
new FormatInfo("mov", true, true),
new FormatInfo("gif", true, true),
new FormatInfo("mts", true, true),
];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private log: (...msg: any[]) => void = () => {};

View File

@ -4,7 +4,7 @@ import { addToast } from "$lib/store/ToastProvider";
import type { OmitBetterStrict, WorkerMessage } from "$lib/types";
import { VertFile } from "$lib/types";
import VipsWorker from "$lib/workers/vips?worker&url";
import { Converter } from "./converter.svelte";
import { Converter, FormatInfo } from "./converter.svelte";
export class VipsConverter extends Converter {
private worker: Worker = browser
@ -15,34 +15,32 @@ export class VipsConverter extends Converter {
private id = 0;
public name = "libvips";
public ready = $state(false);
public static supportedFormatsStatic = [
...new Set([
".png",
".jpeg",
".jpg",
".webp",
".gif",
".ico",
".cur",
".ani",
".heic",
".hdr",
".jpe",
".dng",
".mat",
".pbm",
".pfm",
".pgm",
".pnm",
".ppm",
".raw",
".tif",
".tiff",
".jfif",
]),
];
public supportedFormats = VipsConverter.supportedFormatsStatic;
public supportedFormats = [
new FormatInfo("png", true, true),
new FormatInfo("jpeg", true, true),
new FormatInfo("jpg", true, true),
new FormatInfo("webp", true, true),
new FormatInfo("gif", true, true),
new FormatInfo("ico", true, false),
new FormatInfo("cur", true, false),
new FormatInfo("ani", true, false),
new FormatInfo("heic", true, false),
new FormatInfo("hdr", true, true),
new FormatInfo("jpe", true, true),
new FormatInfo("dng", true, false),
new FormatInfo("mat", true, true),
new FormatInfo("pbm", true, true),
new FormatInfo("pfm", true, true),
new FormatInfo("pgm", true, true),
new FormatInfo("pnm", true, true),
new FormatInfo("ppm", true, true),
new FormatInfo("raw", false, true),
new FormatInfo("tif", true, true),
new FormatInfo("tiff", true, true),
new FormatInfo("jfif", true, true),
new FormatInfo("avif", true, true),
];
public readonly reportsProgress = false;

View File

@ -32,10 +32,12 @@ class Files {
this.thumbnailQueue.add(async () => {
const isAudio = converters
.find((c) => c.name === "ffmpeg")
?.supportedFormats?.includes(file.from.toLowerCase());
?.formatStrings()
?.includes(file.from.toLowerCase());
const isVideo = converters
.find((c) => c.name === "vertd")
?.supportedFormats?.includes(file.from.toLowerCase());
?.formatStrings()
?.includes(file.from.toLowerCase());
try {
if (isAudio) {
@ -119,16 +121,16 @@ class Files {
return;
}
const converter = converters.find((c) =>
c.supportedFormats.includes(
format || ".somenonexistentextension",
),
c
.formatStrings()
.includes(format || ".somenonexistentextension"),
);
if (!converter) {
log(["files"], `no converter found for ${file.name}`);
this.files.push(new VertFile(file, format));
return;
}
const to = converter.supportedFormats.find((f) => f !== format);
const to = converter.formatStrings().find((f) => f !== format);
if (!to) {
log(["files"], `no output format found for ${file.name}`);
return;

View File

@ -28,7 +28,7 @@ export class VertFile {
public findConverters(supportedFormats: string[] = [this.from]) {
const converter = this.converters.filter((converter) =>
converter.supportedFormats.map((f) => supportedFormats.includes(f)),
converter.formatStrings().map((f) => supportedFormats.includes(f)),
);
return converter;
}
@ -36,8 +36,8 @@ export class VertFile {
public findConverter() {
const converter = this.converters.find(
(converter) =>
converter.supportedFormats.includes(this.from) &&
converter.supportedFormats.includes(this.to),
converter.formatStrings().includes(this.from) &&
converter.formatStrings().includes(this.to),
);
return converter;
}
@ -51,7 +51,7 @@ export class VertFile {
this.file = newFile;
this.to = to;
this.converters = converters.filter((c) =>
c.supportedFormats.includes(this.from),
c.formatStrings().includes(this.from),
);
this.convert = this.convert.bind(this);
this.download = this.download.bind(this);

View File

@ -8,8 +8,13 @@
const { data } = $props();
const getSupportedFormats = (name: string) =>
converters.find((c) => c.name === name)?.supportedFormats.join(", ") ||
"none";
converters
.find((c) => c.name === name)
?.supportedFormats.map(
(f) =>
`${f.name}${f.fromSupported && f.toSupported ? "" : "*"}`,
)
.join(", ") || "none";
const status: {
[key: string]: {

View File

@ -89,21 +89,25 @@
{@const availableConverters = file.findConverters()}
{@const currentConverter = converters.find(
(c) =>
c.supportedFormats.includes(file.from) &&
c.supportedFormats.includes(file.to),
c.formatStrings((f) => f.fromSupported).includes(file.from) &&
c.formatStrings((f) => f.toSupported).includes(file.to),
)}
{@const isAudio = converters
.find((c) => c.name === "ffmpeg")
?.supportedFormats.includes(file.from)}
?.formatStrings((f) => f.fromSupported)
.includes(file.from)}
{@const isVideo = converters
.find((c) => c.name === "vertd")
?.supportedFormats.includes(file.from)}
?.formatStrings((f) => f.fromSupported)
.includes(file.from)}
{@const isImage = converters
.find((c) => c.name === "libvips")
?.supportedFormats.includes(file.from)}
?.formatStrings((f) => f.fromSupported)
.includes(file.from)}
{@const isDocument = converters
.find((c) => c.name === "pandoc")
?.supportedFormats.includes(file.from)}
?.formatStrings((f) => f.fromSupported)
.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 !converters.length}
@ -222,7 +226,9 @@
<!-- cannot convert to svg or heif -->
<Dropdown
options={availableConverters
.flatMap((c) => c.supportedFormats)
.flatMap((c) =>
c.formatStrings((f) => f.toSupported),
)
.filter(
(format) =>
format !== ".svg" && format !== ".heif",