diff --git a/desktop/src/api/imageProcessor.ts b/desktop/src/api/imageProcessor.ts deleted file mode 100644 index b97bd7f7bd..0000000000 --- a/desktop/src/api/imageProcessor.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { ipcRenderer } from "electron/renderer"; -import { existsSync } from "fs"; -import { CustomErrors } from "../constants/errors"; -import { writeStream } from "../services/fs"; -import { logError } from "../main/log"; -import { ElectronFile } from "../types"; -import { isPlatform } from "../utils/common/platform"; - -export async function convertToJPEG( - fileData: Uint8Array, - filename: string, -): Promise { - if (isPlatform("windows")) { - throw Error(CustomErrors.WINDOWS_NATIVE_IMAGE_PROCESSING_NOT_SUPPORTED); - } - const convertedFileData = await ipcRenderer.invoke( - "convert-to-jpeg", - fileData, - filename, - ); - return convertedFileData; -} - -export async function generateImageThumbnail( - inputFile: File | ElectronFile, - maxDimension: number, - maxSize: number, -): Promise { - let inputFilePath = null; - let createdTempInputFile = null; - try { - if (isPlatform("windows")) { - throw Error( - CustomErrors.WINDOWS_NATIVE_IMAGE_PROCESSING_NOT_SUPPORTED, - ); - } - if (!existsSync(inputFile.path)) { - const tempFilePath = await ipcRenderer.invoke( - "get-temp-file-path", - inputFile.name, - ); - await writeStream(tempFilePath, await inputFile.stream()); - inputFilePath = tempFilePath; - createdTempInputFile = true; - } else { - inputFilePath = inputFile.path; - } - const thumbnail = await ipcRenderer.invoke( - "generate-image-thumbnail", - inputFilePath, - maxDimension, - maxSize, - ); - return thumbnail; - } finally { - if (createdTempInputFile) { - try { - await ipcRenderer.invoke("remove-temp-file", inputFilePath); - } catch (e) { - logError(e, "failed to deleteTempFile"); - } - } - } -} diff --git a/desktop/src/main/ipc.ts b/desktop/src/main/ipc.ts index 6f56fb6209..89698ae4aa 100644 --- a/desktop/src/main/ipc.ts +++ b/desktop/src/main/ipc.ts @@ -8,7 +8,6 @@ import { ipcMain } from "electron/main"; import { clearElectronStore } from "../api/electronStore"; -import { runFFmpegCmd } from "../api/ffmpeg"; import { getEncryptionKey, setEncryptionKey } from "../api/safeStorage"; import { appVersion, @@ -20,7 +19,12 @@ import { computeImageEmbedding, computeTextEmbedding, } from "../services/clipService"; -import type { Model } from "../types"; +import { runFFmpegCmd } from "../services/ffmpeg"; +import { + convertToJPEG, + generateImageThumbnail, +} from "../services/imageProcessor"; +import type { ElectronFile, Model } from "../types"; import { checkExistsAndCreateDir, fsExists } from "./fs"; import { openDirectory, openLogDirectory } from "./general"; import { logToDisk } from "./log"; @@ -71,6 +75,16 @@ export const attachIPCHandlers = () => { muteUpdateNotification(version), ); + ipcMain.handle("convertToJPEG", (_, fileData, filename) => + convertToJPEG(fileData, filename), + ); + + ipcMain.handle( + "generateImageThumbnail", + (_, inputFile, maxDimension, maxSize) => + generateImageThumbnail(inputFile, maxDimension, maxSize), + ); + ipcMain.handle( "runFFmpegCmd", ( diff --git a/desktop/src/preload.ts b/desktop/src/preload.ts index 04e476c37d..3c1d2057d2 100644 --- a/desktop/src/preload.ts +++ b/desktop/src/preload.ts @@ -32,7 +32,6 @@ import * as fs from "node:fs/promises"; import { Readable } from "node:stream"; import path from "path"; import { getDirFiles } from "./api/fs"; -import { convertToJPEG, generateImageThumbnail } from "./api/imageProcessor"; import { getElectronFilesFromGoogleZip, getPendingUploads, @@ -51,6 +50,7 @@ import { updateWatchMappingSyncedFiles, } from "./api/watch"; import { logErrorSentry, setupLogging } from "./main/log"; +import type { ElectronFile } from "types"; setupLogging(); @@ -142,6 +142,24 @@ const muteUpdateNotification = (version: string) => { // - Conversion +const convertToJPEG = ( + fileData: Uint8Array, + filename: string, +): Promise => + ipcRenderer.invoke("convertToJPEG", fileData, filename); + +const generateImageThumbnail = ( + inputFile: File | ElectronFile, + maxDimension: number, + maxSize: number, +): Promise => + ipcRenderer.invoke( + "generateImageThumbnail", + inputFile, + maxDimension, + maxSize, + ); + const runFFmpegCmd = ( cmd: string[], inputFile: File | ElectronFile, @@ -398,6 +416,8 @@ contextBridge.exposeInMainWorld("ElectronAPIs", { registerUpdateEventListener, // - Conversion + convertToJPEG, + generateImageThumbnail, runFFmpegCmd, // - ML @@ -434,9 +454,6 @@ contextBridge.exposeInMainWorld("ElectronAPIs", { isFolder, updateWatchMappingSyncedFiles, updateWatchMappingIgnoredFiles, - convertToJPEG, - - generateImageThumbnail, moveFile, deleteFolder, rename, diff --git a/desktop/src/services/imageProcessor.ts b/desktop/src/services/imageProcessor.ts index c0b8e3123e..46e546bc06 100644 --- a/desktop/src/services/imageProcessor.ts +++ b/desktop/src/services/imageProcessor.ts @@ -1,14 +1,18 @@ import { exec } from "child_process"; -import util from "util"; - import log from "electron-log"; +import { existsSync } from "fs"; import * as fs from "node:fs/promises"; import path from "path"; +import util from "util"; import { CustomErrors } from "../constants/errors"; import { isDev } from "../main/general"; +import { logError, logErrorSentry } from "../main/log"; +import { writeStream } from "../services/fs"; +import { ElectronFile } from "../types"; import { isPlatform } from "../utils/common/platform"; import { generateTempFilePath } from "../utils/temp"; -import { logErrorSentry } from "../main/log"; +import { deleteTempFile } from "./ffmpeg"; + const shellescape = require("any-shell-escape"); const asyncExec = util.promisify(exec); @@ -80,6 +84,17 @@ function getImageMagickStaticPath() { export async function convertToJPEG( fileData: Uint8Array, filename: string, +): Promise { + if (isPlatform("windows")) { + throw Error(CustomErrors.WINDOWS_NATIVE_IMAGE_PROCESSING_NOT_SUPPORTED); + } + const convertedFileData = await convertToJPEG_(fileData, filename); + return convertedFileData; +} + +async function convertToJPEG_( + fileData: Uint8Array, + filename: string, ): Promise { let tempInputFilePath: string; let tempOutputFilePath: string; @@ -159,6 +174,44 @@ function constructConvertCommand( } export async function generateImageThumbnail( + inputFile: File | ElectronFile, + maxDimension: number, + maxSize: number, +): Promise { + let inputFilePath = null; + let createdTempInputFile = null; + try { + if (isPlatform("windows")) { + throw Error( + CustomErrors.WINDOWS_NATIVE_IMAGE_PROCESSING_NOT_SUPPORTED, + ); + } + if (!existsSync(inputFile.path)) { + const tempFilePath = await generateTempFilePath(inputFile.name); + await writeStream(tempFilePath, await inputFile.stream()); + inputFilePath = tempFilePath; + createdTempInputFile = true; + } else { + inputFilePath = inputFile.path; + } + const thumbnail = await generateImageThumbnail_( + inputFilePath, + maxDimension, + maxSize, + ); + return thumbnail; + } finally { + if (createdTempInputFile) { + try { + await deleteTempFile(inputFilePath); + } catch (e) { + logError(e, "failed to deleteTempFile"); + } + } + } +} + +async function generateImageThumbnail_( inputFilePath: string, width: number, maxSize: number, diff --git a/desktop/src/utils/ipcComms.ts b/desktop/src/utils/ipcComms.ts index a6a1adfabd..d203c56f25 100644 --- a/desktop/src/utils/ipcComms.ts +++ b/desktop/src/utils/ipcComms.ts @@ -2,17 +2,7 @@ import chokidar from "chokidar"; import { BrowserWindow, dialog, ipcMain, Tray } from "electron"; import path from "path"; import { attachIPCHandlers } from "../main/ipc"; -import { - computeImageEmbedding, - computeTextEmbedding, -} from "../services/clipService"; -import { deleteTempFile } from "../services/ffmpeg"; import { getDirFilePaths } from "../services/fs"; -import { - convertToJPEG, - generateImageThumbnail, -} from "../services/imageProcessor"; -import { generateTempFilePath } from "./temp"; export default function setupIpcComs( tray: Tray, @@ -65,29 +55,4 @@ export default function setupIpcComs( ipcMain.handle("remove-watcher", async (_, args: { dir: string }) => { watcher.unwatch(args.dir); }); - - ipcMain.handle("convert-to-jpeg", (_, fileData, filename) => { - return convertToJPEG(fileData, filename); - }); - - ipcMain.handle("get-temp-file-path", (_, formatSuffix) => { - return generateTempFilePath(formatSuffix); - }); - ipcMain.handle("remove-temp-file", (_, tempFilePath: string) => { - return deleteTempFile(tempFilePath); - }); - - ipcMain.handle( - "generate-image-thumbnail", - (_, fileData, maxDimension, maxSize) => { - return generateImageThumbnail(fileData, maxDimension, maxSize); - }, - ); - - ipcMain.handle("compute-image-embedding", (_, model, inputFilePath) => { - return computeImageEmbedding(model, inputFilePath); - }); - ipcMain.handle("compute-text-embedding", (_, model, text) => { - return computeTextEmbedding(model, text); - }); } diff --git a/web/packages/shared/electron/types.ts b/web/packages/shared/electron/types.ts index d469746dab..03ceaf94b8 100644 --- a/web/packages/shared/electron/types.ts +++ b/web/packages/shared/electron/types.ts @@ -79,16 +79,23 @@ export interface ElectronAPIsType { }; /** TODO: AUDIT below this */ + // - General + registerForegroundEventListener: (onForeground: () => void) => void; + clearElectronStore: () => void; setEncryptionKey: (encryptionKey: string) => Promise; + getEncryptionKey: () => Promise; // - App update + updateAndRestart: () => void; + skipAppUpdate: (version: string) => void; + muteUpdateNotification: (version: string) => void; registerUpdateEventListener: ( @@ -97,6 +104,17 @@ export interface ElectronAPIsType { // - Conversion + convertToJPEG: ( + fileData: Uint8Array, + filename: string, + ) => Promise; + + generateImageThumbnail: ( + inputFile: File | ElectronFile, + maxDimension: number, + maxSize: number, + ) => Promise; + runFFmpegCmd: ( cmd: string[], inputFile: File | ElectronFile, @@ -160,16 +178,6 @@ export interface ElectronAPIsType { removeFolder: (folderPath: string) => Promise, ) => void; isFolder: (dirPath: string) => Promise; - convertToJPEG: ( - fileData: Uint8Array, - filename: string, - ) => Promise; - - generateImageThumbnail: ( - inputFile: File | ElectronFile, - maxDimension: number, - maxSize: number, - ) => Promise; moveFile: (oldPath: string, newPath: string) => Promise; deleteFolder: (path: string) => Promise; deleteFile: (path: string) => Promise;