diff --git a/src/lib/converters/ffmpeg.svelte.ts b/src/lib/converters/ffmpeg.svelte.ts index cf2b9ec..119d96d 100644 --- a/src/lib/converters/ffmpeg.svelte.ts +++ b/src/lib/converters/ffmpeg.svelte.ts @@ -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); diff --git a/src/lib/converters/vertd.svelte.ts b/src/lib/converters/vertd.svelte.ts index 78504b3..7debb63 100644 --- a/src/lib/converters/vertd.svelte.ts +++ b/src/lib/converters/vertd.svelte.ts @@ -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; } diff --git a/src/lib/converters/vips.svelte.ts b/src/lib/converters/vips.svelte.ts index 24a8283..47af61e 100644 --- a/src/lib/converters/vips.svelte.ts +++ b/src/lib/converters/vips.svelte.ts @@ -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), diff --git a/src/lib/store/index.svelte.ts b/src/lib/store/index.svelte.ts index dc4e63b..67682f5 100644 --- a/src/lib/store/index.svelte.ts +++ b/src/lib/store/index.svelte.ts @@ -9,7 +9,7 @@ class Files { public files = $state([]); 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; }, diff --git a/src/lib/types/file.svelte.ts b/src/lib/types/file.svelte.ts index 7efa889..f6ac1f3 100644 --- a/src/lib/types/file.svelte.ts +++ b/src/lib/types/file.svelte.ts @@ -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; diff --git a/src/routes/convert/+page.svelte b/src/routes/convert/+page.svelte index 3b3d6d6..c97dc04 100644 --- a/src/routes/convert/+page.svelte +++ b/src/routes/convert/+page.svelte @@ -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 @@ {#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)}
- {#if !file.converter} + {#if !converters.length} {:else if isAudio} @@ -90,7 +101,7 @@ @@ -110,7 +121,7 @@
- {#if !file.converter} + {#if !currentConverter} {#if file.name.startsWith("vertd")}
- 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)} />