From 4461775283921f5cd2e82206884ed662df4ed35d Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Mon, 22 Apr 2024 16:32:04 +0530 Subject: [PATCH] Desktop side --- desktop/src/main/ipc.ts | 5 ++--- desktop/src/main/services/convert.ts | 4 ++-- desktop/src/main/services/ffmpeg.ts | 13 ++++++------- desktop/src/main/services/ml-clip.ts | 2 +- desktop/src/main/utils-temp.ts | 13 +++++++------ desktop/src/preload.ts | 11 ++--------- web/apps/photos/src/services/ffmpeg.ts | 27 ++++++++++++-------------- web/packages/next/types/ipc.ts | 25 ++++++++++-------------- 8 files changed, 42 insertions(+), 58 deletions(-) diff --git a/desktop/src/main/ipc.ts b/desktop/src/main/ipc.ts index 9ea4d802f..ed6ea48cb 100644 --- a/desktop/src/main/ipc.ts +++ b/desktop/src/main/ipc.ts @@ -156,10 +156,9 @@ export const attachIPCHandlers = () => { ( _, command: string[], - inputDataOrPath: Uint8Array | string, - outputFileName: string, + dataOrPath: Uint8Array | string, timeoutMS: number, - ) => ffmpegExec(command, inputDataOrPath, outputFileName, timeoutMS), + ) => ffmpegExec(command, dataOrPath, timeoutMS), ); // - ML diff --git a/desktop/src/main/services/convert.ts b/desktop/src/main/services/convert.ts index 7f38a86ea..71e8f419e 100644 --- a/desktop/src/main/services/convert.ts +++ b/desktop/src/main/services/convert.ts @@ -14,7 +14,7 @@ export const convertToJPEG = async ( imageData: Uint8Array, ): Promise => { const inputFilePath = await makeTempFilePath(fileName); - const outputFilePath = await makeTempFilePath("output.jpeg"); + const outputFilePath = await makeTempFilePath(".jpeg"); // Construct the command first, it may throw on NotAvailable on win32. const command = convertToJPEGCommand(inputFilePath, outputFilePath); @@ -150,7 +150,7 @@ async function generateImageThumbnail_( let tempOutputFilePath: string; let quality = MAX_QUALITY; try { - tempOutputFilePath = await makeTempFilePath("thumb.jpeg"); + tempOutputFilePath = await makeTempFilePath(".jpeg"); let thumbnail: Uint8Array; do { await execAsync( diff --git a/desktop/src/main/services/ffmpeg.ts b/desktop/src/main/services/ffmpeg.ts index 5547cf834..251f71f6d 100644 --- a/desktop/src/main/services/ffmpeg.ts +++ b/desktop/src/main/services/ffmpeg.ts @@ -37,8 +37,7 @@ const outputPathPlaceholder = "OUTPUT"; */ export const ffmpegExec = async ( command: string[], - inputDataOrPath: Uint8Array | string, - outputFileName: string, + dataOrPath: Uint8Array | string, timeoutMS: number, ): Promise => { // TODO (MR): This currently copies files for both input and output. This @@ -47,18 +46,18 @@ export const ffmpegExec = async ( let inputFilePath: string; let isInputFileTemporary: boolean; - if (typeof inputDataOrPath == "string") { - inputFilePath = inputDataOrPath; + if (typeof dataOrPath == "string") { + inputFilePath = dataOrPath; isInputFileTemporary = false; } else { - inputFilePath = await makeTempFilePath("input" /* arbitrary */); + inputFilePath = await makeTempFilePath(".in"); isInputFileTemporary = true; - await fs.writeFile(inputFilePath, inputDataOrPath); + await fs.writeFile(inputFilePath, dataOrPath); } let outputFilePath: string | undefined; try { - outputFilePath = await makeTempFilePath(outputFileName); + outputFilePath = await makeTempFilePath(".out"); const cmd = substitutePlaceholders( command, diff --git a/desktop/src/main/services/ml-clip.ts b/desktop/src/main/services/ml-clip.ts index 0c466b9f6..a5f407f9e 100644 --- a/desktop/src/main/services/ml-clip.ts +++ b/desktop/src/main/services/ml-clip.ts @@ -20,7 +20,7 @@ const cachedCLIPImageSession = makeCachedInferenceSession( ); export const clipImageEmbedding = async (jpegImageData: Uint8Array) => { - const tempFilePath = await makeTempFilePath(""); + const tempFilePath = await makeTempFilePath(); const imageStream = new Response(jpegImageData.buffer).body; await writeStream(tempFilePath, imageStream); try { diff --git a/desktop/src/main/utils-temp.ts b/desktop/src/main/utils-temp.ts index e9c4c1773..f48b2c388 100644 --- a/desktop/src/main/utils-temp.ts +++ b/desktop/src/main/utils-temp.ts @@ -25,20 +25,21 @@ const randomPrefix = () => { }; /** - * Return the path to a temporary file with the given {@link formatSuffix}. + * Return the path to a temporary file with the given {@link suffix}. * * The function returns the path to a file in the system temp directory (in an - * Ente specific folder therin) with a random prefix and the given - * {@link formatSuffix}. It ensures that there is no existing file with the same - * name already. + * Ente specific folder therin) with a random prefix and an (optional) + * {@link suffix}. + * + * It ensures that there is no existing file with the same name already. * * Use {@link deleteTempFile} to remove this file when you're done. */ -export const makeTempFilePath = async (formatSuffix: string) => { +export const makeTempFilePath = async (suffix?: string) => { const tempDir = await enteTempDirPath(); let result: string; do { - result = path.join(tempDir, randomPrefix() + "-" + formatSuffix); + result = path.join(tempDir, `${randomPrefix()}${suffix ?? ""}`); } while (existsSync(result)); return result; }; diff --git a/desktop/src/preload.ts b/desktop/src/preload.ts index c3f964e17..c2af31abc 100644 --- a/desktop/src/preload.ts +++ b/desktop/src/preload.ts @@ -144,17 +144,10 @@ const generateImageThumbnail = ( const ffmpegExec = ( command: string[], - inputDataOrPath: Uint8Array | string, - outputFileName: string, + dataOrPath: Uint8Array | string, timeoutMS: number, ): Promise => - ipcRenderer.invoke( - "ffmpegExec", - command, - inputDataOrPath, - outputFileName, - timeoutMS, - ); + ipcRenderer.invoke("ffmpegExec", command, dataOrPath, timeoutMS); // - ML diff --git a/web/apps/photos/src/services/ffmpeg.ts b/web/apps/photos/src/services/ffmpeg.ts index 5dc40b0e5..3c3f58a5f 100644 --- a/web/apps/photos/src/services/ffmpeg.ts +++ b/web/apps/photos/src/services/ffmpeg.ts @@ -36,7 +36,6 @@ export const generateVideoThumbnail = async (blob: Blob) => { outputPathPlaceholder, ], blob, - "thumb.jpeg", ); try { @@ -161,27 +160,25 @@ export async function convertToMP4(file: File) { * Run the given FFmpeg command. * * If we're running in the context of our desktop app, use the FFmpeg binary we - * bundle with our desktop app to run the command. Otherwise fallback to using - * the wasm FFmpeg we link to from our web app in a web worker. + * bundle with our desktop app to run the command. Otherwise fallback to using a + * wasm FFmpeg in a web worker. * - * As a rough ballpark, the native FFmpeg integration in the desktop app is - * 10-20x faster than the wasm one currently. See: [Note: FFmpeg in Electron]. + * As a rough ballpark, currently the native FFmpeg integration in the desktop + * app is 10-20x faster than the wasm one. See: [Note: FFmpeg in Electron]. */ const ffmpegExec = async ( command: string[], blob: Blob, - outputFileName: string, timeoutMs: number = 0, -): Promise => { +) => { const electron = globalThis.electron; - if (electron) - return electron.ffmpegExec(command, blob, outputFileName, timeoutMs); - else - return workerFactory - .lazy() - .then((worker) => - worker.exec(command, blob, outputFileName, timeoutMs), - ); + if (electron) { + const data = new Uint8Array(await blob.arrayBuffer()); + return await electron.ffmpegExec(command, data, timeoutMs); + } else { + const worker = await workerFactory.lazy() + return await worker.exec(command, blob, timeoutMs); + } }; const ffmpegExec2 = async ( diff --git a/web/packages/next/types/ipc.ts b/web/packages/next/types/ipc.ts index d87b8e830..ef10a43fe 100644 --- a/web/packages/next/types/ipc.ts +++ b/web/packages/next/types/ipc.ts @@ -237,11 +237,11 @@ export interface Electron { ) => Promise; /** - * Execute a ffmpeg {@link command}. + * Execute a FFmpeg {@link command} on the given {@link dataOrPath}. * - * This executes the command using the ffmpeg executable we bundle with our - * desktop app. There is also a ffmpeg wasm implementation that we use when - * running on the web, it also has a sibling function with the same + * This executes the command using a FFmpeg executable we bundle with our + * desktop app. We also have a wasm FFmpeg wasm implementation that we use + * when running on the web, which has a sibling function with the same * parameters. See [Note: ffmpeg in Electron]. * * @param command An array of strings, each representing one positional @@ -250,25 +250,20 @@ export interface Electron { * (respectively {@link inputPathPlaceholder}, * {@link outputPathPlaceholder}, {@link ffmpegPathPlaceholder}). * - * @param inputDataOrPath The bytes of the input file, or the path to the - * input file on the user's local disk. In both cases, the data gets - * serialized to a temporary file, and then that path gets substituted in - * the ffmpeg {@link command} by {@link inputPathPlaceholder}. - * - * @param outputFileName The name of the file we instruct ffmpeg to produce - * when giving it the given {@link command}. The contents of this file get - * returned as the result. + * @param dataOrPath The bytes of the input file, or the path to the input + * file on the user's local disk. In both cases, the data gets serialized to + * a temporary file, and then that path gets substituted in the FFmpeg + * {@link command} in lieu of {@link inputPathPlaceholder}. * * @param timeoutMS If non-zero, then abort and throw a timeout error if the * ffmpeg command takes more than the given number of milliseconds. * * @returns The contents of the output file produced by the ffmpeg command - * at {@link outputFileName}. + * (specified as {@link outputPathPlaceholder} in {@link command}). */ ffmpegExec: ( command: string[], - inputDataOrPath: Uint8Array | string, - outputFileName: string, + dataOrPath: Uint8Array | string, timeoutMS: number, ) => Promise;