Просмотр исходного кода

input filename is not needed

tested with sips
Manav Rathi 1 год назад
Родитель
Сommit
05cd0bcd2c

+ 8 - 4
desktop/src/main/ipc.ts

@@ -141,14 +141,18 @@ export const attachIPCHandlers = () => {
 
 
     // - Conversion
     // - Conversion
 
 
-    ipcMain.handle("convertToJPEG", (_, fileName, imageData) =>
-        convertToJPEG(fileName, imageData),
+    ipcMain.handle("convertToJPEG", (_, imageData: Uint8Array) =>
+        convertToJPEG(imageData),
     );
     );
 
 
     ipcMain.handle(
     ipcMain.handle(
         "generateImageThumbnail",
         "generateImageThumbnail",
-        (_, inputFile, maxDimension, maxSize) =>
-            generateImageThumbnail(inputFile, maxDimension, maxSize),
+        (
+            _,
+            dataOrPath: Uint8Array | string,
+            maxDimension: number,
+            maxSize: number,
+        ) => generateImageThumbnail(dataOrPath, maxDimension, maxSize),
     );
     );
 
 
     ipcMain.handle(
     ipcMain.handle(

+ 23 - 8
desktop/src/main/services/convert.ts

@@ -3,20 +3,17 @@
 import { existsSync } from "fs";
 import { existsSync } from "fs";
 import fs from "node:fs/promises";
 import fs from "node:fs/promises";
 import path from "path";
 import path from "path";
-import { CustomErrorMessage, ElectronFile } from "../../types/ipc";
+import { CustomErrorMessage } from "../../types/ipc";
 import log from "../log";
 import log from "../log";
 import { writeStream } from "../stream";
 import { writeStream } from "../stream";
 import { execAsync, isDev } from "../utils-electron";
 import { execAsync, isDev } from "../utils-electron";
 import { deleteTempFile, makeTempFilePath } from "../utils-temp";
 import { deleteTempFile, makeTempFilePath } from "../utils-temp";
 
 
-export const convertToJPEG = async (
-    fileName: string,
-    imageData: Uint8Array,
-): Promise<Uint8Array> => {
-    const inputFilePath = await makeTempFilePath(fileName);
+export const convertToJPEG = async (imageData: Uint8Array) => {
+    const inputFilePath = await makeTempFilePath();
     const outputFilePath = await makeTempFilePath(".jpeg");
     const outputFilePath = await makeTempFilePath(".jpeg");
 
 
-    // Construct the command first, it may throw on NotAvailable on win32.
+    // Construct the command first, it may throw NotAvailable on win32.
     const command = convertToJPEGCommand(inputFilePath, outputFilePath);
     const command = convertToJPEGCommand(inputFilePath, outputFilePath);
 
 
     try {
     try {
@@ -106,10 +103,28 @@ const IMAGE_MAGICK_THUMBNAIL_GENERATE_COMMAND_TEMPLATE = [
 ];
 ];
 
 
 export async function generateImageThumbnail(
 export async function generateImageThumbnail(
-    inputFile: File | ElectronFile,
+    dataOrPath: Uint8Array | string,
     maxDimension: number,
     maxDimension: number,
     maxSize: number,
     maxSize: number,
 ): Promise<Uint8Array> {
 ): Promise<Uint8Array> {
+    const inputFilePath = await makeTempFilePath(fileName);
+    const outputFilePath = await makeTempFilePath(".jpeg");
+
+    // Construct the command first, it may throw NotAvailable on win32.
+    const command = convertToJPEGCommand(inputFilePath, outputFilePath);
+
+    try {
+        await fs.writeFile(inputFilePath, imageData);
+        await execAsync(command);
+        return new Uint8Array(await fs.readFile(outputFilePath));
+    } finally {
+        try {
+            deleteTempFile(outputFilePath);
+            deleteTempFile(inputFilePath);
+        } catch (e) {
+            log.error("Ignoring error when cleaning up temp files", e);
+        }
+    }
     let inputFilePath = null;
     let inputFilePath = null;
     let createdTempInputFile = null;
     let createdTempInputFile = null;
     try {
     try {

+ 4 - 7
desktop/src/preload.ts

@@ -124,20 +124,17 @@ const fsIsDir = (dirPath: string): Promise<boolean> =>
 
 
 // - Conversion
 // - Conversion
 
 
-const convertToJPEG = (
-    fileName: string,
-    imageData: Uint8Array,
-): Promise<Uint8Array> =>
-    ipcRenderer.invoke("convertToJPEG", fileName, imageData);
+const convertToJPEG = (imageData: Uint8Array): Promise<Uint8Array> =>
+    ipcRenderer.invoke("convertToJPEG", imageData);
 
 
 const generateImageThumbnail = (
 const generateImageThumbnail = (
-    inputFile: File | ElectronFile,
+    dataOrPath: Uint8Array | string,
     maxDimension: number,
     maxDimension: number,
     maxSize: number,
     maxSize: number,
 ): Promise<Uint8Array> =>
 ): Promise<Uint8Array> =>
     ipcRenderer.invoke(
     ipcRenderer.invoke(
         "generateImageThumbnail",
         "generateImageThumbnail",
-        inputFile,
+        dataOrPath,
         maxDimension,
         maxDimension,
         maxSize,
         maxSize,
     );
     );

+ 2 - 5
web/apps/photos/src/services/upload/thumbnail.ts

@@ -59,15 +59,12 @@ export const generateThumbnail = async (
         const thumbnail =
         const thumbnail =
             fileTypeInfo.fileType === FILE_TYPE.IMAGE
             fileTypeInfo.fileType === FILE_TYPE.IMAGE
                 ? await generateImageThumbnail(blob, fileTypeInfo)
                 ? await generateImageThumbnail(blob, fileTypeInfo)
-                : await generateVideoThumbnail(blob, fileTypeInfo);
+                : await generateVideoThumbnail(blob);
 
 
         if (thumbnail.length == 0) throw new Error("Empty thumbnail");
         if (thumbnail.length == 0) throw new Error("Empty thumbnail");
         return { thumbnail, hasStaticThumbnail: false };
         return { thumbnail, hasStaticThumbnail: false };
     } catch (e) {
     } catch (e) {
-        log.error(
-            `Failed to generate thumbnail for format ${fileTypeInfo.exactType}`,
-            e,
-        );
+        log.error(`Failed to generate ${fileTypeInfo.exactType} thumbnail`, e);
         return { thumbnail: fallbackThumbnail(), hasStaticThumbnail: true };
         return { thumbnail: fallbackThumbnail(), hasStaticThumbnail: true };
     }
     }
 };
 };

+ 7 - 9
web/apps/photos/src/utils/file/index.ts

@@ -292,14 +292,12 @@ export const getRenderableImage = async (fileName: string, imageBlob: Blob) => {
             return imageBlob;
             return imageBlob;
         }
         }
 
 
-        let jpegBlob: Blob | undefined;
-
         const available = !moduleState.isNativeJPEGConversionNotAvailable;
         const available = !moduleState.isNativeJPEGConversionNotAvailable;
         if (isElectron() && available && isSupportedRawFormat(exactType)) {
         if (isElectron() && available && isSupportedRawFormat(exactType)) {
             // If we're running in our desktop app, see if our Node.js layer can
             // If we're running in our desktop app, see if our Node.js layer can
             // convert this into a JPEG using native tools for us.
             // convert this into a JPEG using native tools for us.
             try {
             try {
-                jpegBlob = await nativeConvertToJPEG(fileName, imageBlob);
+                return await nativeConvertToJPEG(imageBlob);
             } catch (e) {
             } catch (e) {
                 if (e.message == CustomErrorMessage.NotAvailable) {
                 if (e.message == CustomErrorMessage.NotAvailable) {
                     moduleState.isNativeJPEGConversionNotAvailable = true;
                     moduleState.isNativeJPEGConversionNotAvailable = true;
@@ -309,12 +307,12 @@ export const getRenderableImage = async (fileName: string, imageBlob: Blob) => {
             }
             }
         }
         }
 
 
-        if (!jpegBlob && isFileHEIC(exactType)) {
+        if (!isFileHEIC(exactType)) {
             // If it is an HEIC file, use our web HEIC converter.
             // If it is an HEIC file, use our web HEIC converter.
-            jpegBlob = await heicToJPEG(imageBlob);
+            return await heicToJPEG(imageBlob);
         }
         }
 
 
-        return jpegBlob;
+        return undefined;
     } catch (e) {
     } catch (e) {
         log.error(
         log.error(
             `Failed to get renderable image for ${JSON.stringify(fileTypeInfo ?? fileName)}`,
             `Failed to get renderable image for ${JSON.stringify(fileTypeInfo ?? fileName)}`,
@@ -324,7 +322,7 @@ export const getRenderableImage = async (fileName: string, imageBlob: Blob) => {
     }
     }
 };
 };
 
 
-const nativeConvertToJPEG = async (fileName: string, imageBlob: Blob) => {
+const nativeConvertToJPEG = async (imageBlob: Blob) => {
     const startTime = Date.now();
     const startTime = Date.now();
     const imageData = new Uint8Array(await imageBlob.arrayBuffer());
     const imageData = new Uint8Array(await imageBlob.arrayBuffer());
     const electron = globalThis.electron;
     const electron = globalThis.electron;
@@ -332,8 +330,8 @@ const nativeConvertToJPEG = async (fileName: string, imageBlob: Blob) => {
     // the main thread since workers don't have access to the `window` (and
     // the main thread since workers don't have access to the `window` (and
     // thus, to the `window.electron`) object.
     // thus, to the `window.electron`) object.
     const jpegData = electron
     const jpegData = electron
-        ? await electron.convertToJPEG(fileName, imageData)
-        : await workerBridge.convertToJPEG(fileName, imageData);
+        ? await electron.convertToJPEG(imageData)
+        : await workerBridge.convertToJPEG(imageData);
     log.debug(() => `Native JPEG conversion took ${Date.now() - startTime} ms`);
     log.debug(() => `Native JPEG conversion took ${Date.now() - startTime} ms`);
     return new Blob([jpegData]);
     return new Blob([jpegData]);
 };
 };

+ 5 - 7
web/packages/next/types/ipc.ts

@@ -204,14 +204,10 @@ export interface Electron {
      * yet possible, this function will throw an error with the
      * yet possible, this function will throw an error with the
      * {@link CustomErrorMessage.NotAvailable} message.
      * {@link CustomErrorMessage.NotAvailable} message.
      *
      *
-     * @param fileName The name of the file whose data we're being given.
      * @param imageData The raw image data (the contents of the image file).
      * @param imageData The raw image data (the contents of the image file).
      * @returns JPEG data of the converted image.
      * @returns JPEG data of the converted image.
      */
      */
-    convertToJPEG: (
-        fileName: string,
-        imageData: Uint8Array,
-    ) => Promise<Uint8Array>;
+    convertToJPEG: (imageData: Uint8Array) => Promise<Uint8Array>;
 
 
     /**
     /**
      * Generate a JPEG thumbnail for the given image.
      * Generate a JPEG thumbnail for the given image.
@@ -224,14 +220,16 @@ export interface Electron {
      * not yet possible, this function will throw an error with the
      * not yet possible, this function will throw an error with the
      * {@link CustomErrorMessage.NotAvailable} message.
      * {@link CustomErrorMessage.NotAvailable} message.
      *
      *
-     * @param inputFile The file whose thumbnail we want.
+     * @param dataOrPath The data-of or path-to the image whose thumbnail we
+     * want.
      * @param maxDimension The maximum width or height of the generated
      * @param maxDimension The maximum width or height of the generated
      * thumbnail.
      * thumbnail.
      * @param maxSize Maximum size (in bytes) of the generated thumbnail.
      * @param maxSize Maximum size (in bytes) of the generated thumbnail.
+     *
      * @returns JPEG data of the generated thumbnail.
      * @returns JPEG data of the generated thumbnail.
      */
      */
     generateImageThumbnail: (
     generateImageThumbnail: (
-        inputFile: File | ElectronFile,
+        dataOrPath: Uint8Array | string,
         maxDimension: number,
         maxDimension: number,
         maxSize: number,
         maxSize: number,
     ) => Promise<Uint8Array>;
     ) => Promise<Uint8Array>;

+ 2 - 2
web/packages/next/worker/comlink-worker.ts

@@ -44,8 +44,8 @@ const workerBridge = {
     logToDisk,
     logToDisk,
     // Needed by ML worker
     // Needed by ML worker
     getAuthToken: () => ensureLocalUser().then((user) => user.token),
     getAuthToken: () => ensureLocalUser().then((user) => user.token),
-    convertToJPEG: (fileName: string, imageData: Uint8Array) =>
-        ensureElectron().convertToJPEG(fileName, imageData),
+    convertToJPEG: (imageData: Uint8Array) =>
+        ensureElectron().convertToJPEG(imageData),
     detectFaces: (input: Float32Array) => ensureElectron().detectFaces(input),
     detectFaces: (input: Float32Array) => ensureElectron().detectFaces(input),
     faceEmbedding: (input: Float32Array) =>
     faceEmbedding: (input: Float32Array) =>
         ensureElectron().faceEmbedding(input),
         ensureElectron().faceEmbedding(input),