diff --git a/web/apps/photos/src/services/wasm/ffmpeg.ts b/web/apps/photos/src/services/wasm/ffmpeg.ts deleted file mode 100644 index 33dc929cf..000000000 --- a/web/apps/photos/src/services/wasm/ffmpeg.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { nameAndExtension } from "@/next/file"; -import log from "@/next/log"; -import { withTimeout } from "@ente/shared/utils"; -import QueueProcessor from "@ente/shared/utils/queueProcessor"; -import { generateTempName } from "@ente/shared/utils/temp"; -import { - FFMPEG_PLACEHOLDER, - INPUT_PATH_PLACEHOLDER, - OUTPUT_PATH_PLACEHOLDER, -} from "constants/ffmpeg"; -import { FFmpeg, createFFmpeg } from "ffmpeg-wasm"; -import { getUint8ArrayView } from "services/readerService"; - -const FFMPEG_EXECUTION_WAIT_TIME = 30 * 1000; - -export class WasmFFmpeg { - private ffmpeg: FFmpeg; - private ready: Promise = null; - private ffmpegTaskQueue = new QueueProcessor(); - - constructor() { - this.ffmpeg = createFFmpeg({ - corePath: "/js/ffmpeg/ffmpeg-core.js", - mt: false, - }); - - this.ready = this.init(); - } - - private async init() { - if (!this.ffmpeg.isLoaded()) { - await this.ffmpeg.load(); - } - } - - async run( - cmd: string[], - inputFile: File, - outputFileName: string, - dontTimeout = false, - ) { - const response = this.ffmpegTaskQueue.queueUpRequest(() => { - if (dontTimeout) { - return this.execute(cmd, inputFile, outputFileName); - } else { - return withTimeout( - this.execute(cmd, inputFile, outputFileName), - FFMPEG_EXECUTION_WAIT_TIME, - ); - } - }); - try { - return await response.promise; - } catch (e) { - log.error("ffmpeg run failed", e); - throw e; - } - } - - private async execute( - cmd: string[], - inputFile: File, - outputFileName: string, - ) { - let tempInputFilePath: string; - let tempOutputFilePath: string; - try { - await this.ready; - const [, extension] = nameAndExtension(inputFile.name); - const tempNameSuffix = extension ? `input.${extension}` : "input"; - tempInputFilePath = `${generateTempName(10, tempNameSuffix)}`; - this.ffmpeg.FS( - "writeFile", - tempInputFilePath, - await getUint8ArrayView(inputFile), - ); - tempOutputFilePath = `${generateTempName(10, outputFileName)}`; - - cmd = cmd.map((cmdPart) => { - if (cmdPart === FFMPEG_PLACEHOLDER) { - return ""; - } else if (cmdPart === INPUT_PATH_PLACEHOLDER) { - return tempInputFilePath; - } else if (cmdPart === OUTPUT_PATH_PLACEHOLDER) { - return tempOutputFilePath; - } else { - return cmdPart; - } - }); - log.info(`${cmd}`); - await this.ffmpeg.run(...cmd); - return new File( - [this.ffmpeg.FS("readFile", tempOutputFilePath)], - outputFileName, - ); - } finally { - try { - this.ffmpeg.FS("unlink", tempInputFilePath); - } catch (e) { - log.error("unlink input file failed", e); - } - try { - this.ffmpeg.FS("unlink", tempOutputFilePath); - } catch (e) { - log.error("unlink output file failed", e); - } - } - } -} diff --git a/web/apps/photos/src/worker/ffmpeg.worker.ts b/web/apps/photos/src/worker/ffmpeg.worker.ts index d3f503abb..7d75db58e 100644 --- a/web/apps/photos/src/worker/ffmpeg.worker.ts +++ b/web/apps/photos/src/worker/ffmpeg.worker.ts @@ -1,5 +1,16 @@ +import { nameAndExtension } from "@/next/file"; +import log from "@/next/log"; +import { withTimeout } from "@ente/shared/utils"; +import QueueProcessor from "@ente/shared/utils/queueProcessor"; +import { generateTempName } from "@ente/shared/utils/temp"; import * as Comlink from "comlink"; -import { WasmFFmpeg } from "services/wasm/ffmpeg"; +import { + FFMPEG_PLACEHOLDER, + INPUT_PATH_PLACEHOLDER, + OUTPUT_PATH_PLACEHOLDER, +} from "constants/ffmpeg"; +import { FFmpeg, createFFmpeg } from "ffmpeg-wasm"; +import { getUint8ArrayView } from "services/readerService"; export class DedicatedFFmpegWorker { wasmFFmpeg: WasmFFmpeg; @@ -13,3 +24,100 @@ export class DedicatedFFmpegWorker { } Comlink.expose(DedicatedFFmpegWorker, self); + +const FFMPEG_EXECUTION_WAIT_TIME = 30 * 1000; + +export class WasmFFmpeg { + private ffmpeg: FFmpeg; + private ready: Promise = null; + private ffmpegTaskQueue = new QueueProcessor(); + + constructor() { + this.ffmpeg = createFFmpeg({ + corePath: "/js/ffmpeg/ffmpeg-core.js", + mt: false, + }); + + this.ready = this.init(); + } + + private async init() { + if (!this.ffmpeg.isLoaded()) { + await this.ffmpeg.load(); + } + } + + async run( + cmd: string[], + inputFile: File, + outputFileName: string, + dontTimeout = false, + ) { + const response = this.ffmpegTaskQueue.queueUpRequest(() => { + if (dontTimeout) { + return this.execute(cmd, inputFile, outputFileName); + } else { + return withTimeout( + this.execute(cmd, inputFile, outputFileName), + FFMPEG_EXECUTION_WAIT_TIME, + ); + } + }); + try { + return await response.promise; + } catch (e) { + log.error("ffmpeg run failed", e); + throw e; + } + } + + private async execute( + cmd: string[], + inputFile: File, + outputFileName: string, + ) { + let tempInputFilePath: string; + let tempOutputFilePath: string; + try { + await this.ready; + const [, extension] = nameAndExtension(inputFile.name); + const tempNameSuffix = extension ? `input.${extension}` : "input"; + tempInputFilePath = `${generateTempName(10, tempNameSuffix)}`; + this.ffmpeg.FS( + "writeFile", + tempInputFilePath, + await getUint8ArrayView(inputFile), + ); + tempOutputFilePath = `${generateTempName(10, outputFileName)}`; + + cmd = cmd.map((cmdPart) => { + if (cmdPart === FFMPEG_PLACEHOLDER) { + return ""; + } else if (cmdPart === INPUT_PATH_PLACEHOLDER) { + return tempInputFilePath; + } else if (cmdPart === OUTPUT_PATH_PLACEHOLDER) { + return tempOutputFilePath; + } else { + return cmdPart; + } + }); + log.info(`${cmd}`); + await this.ffmpeg.run(...cmd); + return new File( + [this.ffmpeg.FS("readFile", tempOutputFilePath)], + outputFileName, + ); + } finally { + try { + this.ffmpeg.FS("unlink", tempInputFilePath); + } catch (e) { + log.error("unlink input file failed", e); + } + try { + this.ffmpeg.FS("unlink", tempOutputFilePath); + } catch (e) { + log.error("unlink output file failed", e); + } + } + } +}