mirror of https://github.com/VERT-sh/VERT.git
feat: add converter chaining for multi-step format conversions
This commit is contained in:
parent
916e67357d
commit
aaf667d5a7
|
|
@ -93,3 +93,61 @@ export class Converter {
|
|||
return this.supportedFormats.map((f) => f.name);
|
||||
}
|
||||
}
|
||||
|
||||
export interface ChainStep {
|
||||
converter: Converter;
|
||||
to: string; // intermediate format (last step uses the final target)
|
||||
}
|
||||
|
||||
export class ChainedConverter extends Converter {
|
||||
public override name: string;
|
||||
private steps: ChainStep[];
|
||||
|
||||
constructor(steps: ChainStep[]) {
|
||||
super(0);
|
||||
if (steps.length < 2) throw new Error("Chain requires at least 2 steps");
|
||||
this.steps = steps;
|
||||
this.name = steps.map((s) => s.converter.name).join("+");
|
||||
this.clearTimeout();
|
||||
this.status = this.deriveStatus();
|
||||
this.supportedFormats = [
|
||||
...steps[0].converter.supportedFormats.filter((f) => f.fromSupported),
|
||||
...steps[steps.length - 1].converter.supportedFormats.filter((f) => f.toSupported),
|
||||
];
|
||||
}
|
||||
|
||||
private deriveStatus(): WorkerStatus {
|
||||
const statuses = this.steps.map((s) => s.converter.status);
|
||||
if (statuses.some((s) => s === "error")) return "error";
|
||||
if (statuses.every((s) => s === "ready")) return "ready";
|
||||
if (statuses.some((s) => s === "downloading")) return "downloading";
|
||||
return "not-ready";
|
||||
}
|
||||
|
||||
public override async convert(input: VertFile, to: string): Promise<VertFile> {
|
||||
this.status = this.deriveStatus();
|
||||
const { VertFile: VF } = await import("$lib/types");
|
||||
let current: VertFile = input;
|
||||
|
||||
for (let i = 0; i < this.steps.length; i++) {
|
||||
const { converter, to: stepTo } = this.steps[i];
|
||||
const isLast = i === this.steps.length - 1;
|
||||
const target = isLast ? to : stepTo;
|
||||
const result = await converter.convert(current, target);
|
||||
if (!isLast) {
|
||||
current = new VF(
|
||||
new File([await result.file.arrayBuffer()], input.name.replace(/\.[^/.]+$/, target)),
|
||||
target,
|
||||
);
|
||||
} else {
|
||||
current = result;
|
||||
}
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
public override async cancel(input: VertFile): Promise<void> {
|
||||
await Promise.allSettled(this.steps.map((s) => s.converter.cancel(input)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { byNative, converters } from "$lib/converters";
|
||||
import type { Converter } from "$lib/converters/converter.svelte";
|
||||
import { ChainedConverter } from "$lib/converters/converter.svelte";
|
||||
import { m } from "$lib/paraglide/messages";
|
||||
import { ToastManager } from "$lib/util/toast.svelte";
|
||||
import type { Component } from "svelte";
|
||||
|
|
@ -65,7 +66,29 @@ export class VertFile {
|
|||
if (!theirFrom.isNative && !theirTo.isNative) return false;
|
||||
return true;
|
||||
});
|
||||
return converter;
|
||||
if (converter) return converter;
|
||||
|
||||
for (const a of this.converters) {
|
||||
for (const fmt of a.supportedFormats) {
|
||||
if (!fmt.toSupported) continue;
|
||||
const b = converters.find((c) => {
|
||||
const cFrom = c.supportedFormats.find(
|
||||
(f) => f.name === fmt.name && f.fromSupported,
|
||||
);
|
||||
const cTo = c.supportedFormats.find(
|
||||
(f) => f.name === this.to && f.toSupported,
|
||||
);
|
||||
return cFrom && cTo;
|
||||
});
|
||||
if (b)
|
||||
return new ChainedConverter([
|
||||
{ converter: a, to: fmt.name },
|
||||
{ converter: b, to: this.to },
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public isLarge(): boolean {
|
||||
|
|
|
|||
Loading…
Reference in New Issue