diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fb10c6d3..5351924f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -109,6 +109,9 @@ importers: '@vitejs/plugin-basic-ssl': specifier: ^1.1.0 version: 1.1.0(vite@5.3.5(@types/node@20.14.14)) + mime: + specifier: ^4.0.4 + version: 4.0.4 sveltekit-i18n: specifier: ^2.4.2 version: 2.4.2(svelte@4.2.18) @@ -1591,6 +1594,11 @@ packages: engines: {node: '>=4'} hasBin: true + mime@4.0.4: + resolution: {integrity: sha512-v8yqInVjhXyqP6+Kw4fV3ZzeMRqEW6FotRsKXjRS5VMTNIuXsdRoAvklpoRgSqXm6o9VNH4/C0mgedko9DdLsQ==} + engines: {node: '>=16'} + hasBin: true + mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -3583,6 +3591,8 @@ snapshots: mime@1.6.0: {} + mime@4.0.4: {} + mimic-fn@2.1.0: {} min-indent@1.0.1: {} diff --git a/web/package.json b/web/package.json index 9722e8d7..97650ff2 100644 --- a/web/package.json +++ b/web/package.json @@ -51,6 +51,7 @@ "@imput/version-info": "workspace:^", "@tabler/icons-svelte": "3.6.0", "@vitejs/plugin-basic-ssl": "^1.1.0", + "mime": "^4.0.4", "sveltekit-i18n": "^2.4.2", "ts-deepmerge": "^7.0.0" } diff --git a/web/src/lib/ffmpeg.ts b/web/src/lib/ffmpeg.ts index c2f8899d..c5536629 100644 --- a/web/src/lib/ffmpeg.ts +++ b/web/src/lib/ffmpeg.ts @@ -1,19 +1,20 @@ +import { FFmpeg } from "@imput/ffmpeg.wasm"; import ffmpegCore from "@imput/ffmpeg-core?url"; import ffmpegCoreWASM from "@imput/ffmpeg-core/wasm?url"; -import { FFmpeg } from "@imput/ffmpeg.wasm"; -import { fetchFile } from "@imput/ffmpeg-util"; +import mime from "mime"; -type renderParams = { - url: string, - input: { - type: string, - format: string, - }, - output: { - type: string, - format: string, - }, +type InputFileKind = "video" | "audio"; + +type FileInfo = { + type?: string | null, + kind: InputFileKind, + extension: string, +} + +type RenderParams = { + file: File, + output?: FileInfo, args: string[], } @@ -51,26 +52,41 @@ export default class FFmpegWrapper { return this.ffmpeg.terminate(); } - async renderFile({ url, input, output, args }: renderParams) { - const inputFile = `input.${input.format}`; + async renderFile({ file, output, args }: RenderParams) { + const inputKind = file.type.split("/")[0]; + const inputExtension = mime.getExtension(file.type); + if (inputKind !== "video" && inputKind !== "audio") return; + if (!inputExtension) return; + + const input: FileInfo = { + kind: inputKind, + extension: inputExtension, + } + + if (!output) output = input; + + output.type = mime.getType(output.extension); + if (!output.type) return; + + const buffer = new Uint8Array(await file.arrayBuffer()); await this.ffmpeg.writeFile( - inputFile, - await fetchFile(url) - ) + 'input', + buffer + ); await this.ffmpeg.exec([ '-threads', this.concurrency.toString(), - '-i', inputFile, + '-i', 'input', ...args, - `output.${output.format}` + `output.${output.extension}` ]); - const data = await this.ffmpeg.readFile(`output.${output.format}`); - const finalBlob = URL.createObjectURL( - new Blob([data], { type: `${output.type}/${output.format}` }) + const renderBlob = new Blob( + [await this.ffmpeg.readFile(`output.${output.extension}`)], + { type: output.type } ); - return finalBlob + return renderBlob; } }