Ver Fonte

Serve legacy face crops

Manav Rathi há 1 ano atrás
pai
commit
333f364d38

+ 20 - 6
desktop/src/main.ts

@@ -287,13 +287,27 @@ const setupTrayItem = (mainWindow: BrowserWindow) => {
 
 /**
  * Older versions of our app used to maintain a cache dir using the main
- * process. This has been deprecated in favor of using a normal web cache.
+ * process. This has been removed in favor of cache on the web layer.
  *
- * Delete the old cache dir if it exists. This code was added March 2024, and
- * can be removed after some time once most people have upgraded to newer
- * versions.
+ * Delete the old cache dir if it exists.
+ *
+ * This will happen in two phases. The cache had three subdirectories:
+ *
+ * - Two of them, "thumbs" and "files", will be removed now (v1.7.0, May 2024).
+ *
+ * - The third one, "face-crops" will be removed once we finish the face search
+ *   changes. See: [Note: Legacy face crops].
+ *
+ * This migration code can be removed after some time once most people have
+ * upgraded to newer versions.
  */
 const deleteLegacyDiskCacheDirIfExists = async () => {
+    const removeIfExists = async (dirPath: string) => {
+        log.info(`Removing legacy disk cache from ${dirPath}`);
+        await fs.rm(dirPath, { recursive: true });
+    };
+    // [Note: Getting the cache path]
+    //
     // The existing code was passing "cache" as a parameter to getPath.
     //
     // However, "cache" is not a valid parameter to getPath. It works! (for
@@ -309,8 +323,8 @@ const deleteLegacyDiskCacheDirIfExists = async () => {
     // @ts-expect-error "cache" works but is not part of the public API.
     const cacheDir = path.join(app.getPath("cache"), "ente");
     if (existsSync(cacheDir)) {
-        log.info(`Removing legacy disk cache from ${cacheDir}`);
-        await fs.rm(cacheDir, { recursive: true });
+        await removeIfExists(path.join(cacheDir, "thumbs"));
+        await removeIfExists(path.join(cacheDir, "files"));
     }
 };
 

+ 5 - 0
desktop/src/main/ipc.ts

@@ -24,6 +24,7 @@ import {
     updateOnNextRestart,
 } from "./services/app-update";
 import {
+    legacyFaceCrop,
     openDirectory,
     openLogDirectory,
     selectDirectory,
@@ -198,6 +199,10 @@ export const attachIPCHandlers = () => {
         faceEmbedding(input),
     );
 
+    ipcMain.handle("legacyFaceCrop", (_, faceID: string) =>
+        legacyFaceCrop(faceID),
+    );
+
     // - Upload
 
     ipcMain.handle("listZipItems", (_, zipPath: string) =>

+ 15 - 0
desktop/src/main/services/dir.ts

@@ -1,5 +1,7 @@
 import { shell } from "electron/common";
 import { app, dialog } from "electron/main";
+import { existsSync } from "fs";
+import fs from "node:fs/promises";
 import path from "node:path";
 import { posixPath } from "../utils/electron";
 
@@ -49,3 +51,16 @@ export const openLogDirectory = () => openDirectory(logDirectoryPath());
  *
  */
 const logDirectoryPath = () => app.getPath("logs");
+
+/**
+ * See: [Note: Legacy face crops]
+ */
+export const legacyFaceCrop = async (
+    faceID: string,
+): Promise<Uint8Array | undefined> => {
+    // See: [Note: Getting the cache path]
+    // @ts-expect-error "cache" works but is not part of the public API.
+    const cacheDir = path.join(app.getPath("cache"), "ente");
+    const filePath = path.join(cacheDir, "face-crops", faceID);
+    return existsSync(filePath) ? await fs.readFile(filePath) : undefined;
+};

+ 4 - 0
desktop/src/preload.ts

@@ -164,6 +164,9 @@ const detectFaces = (input: Float32Array) =>
 const faceEmbedding = (input: Float32Array) =>
     ipcRenderer.invoke("faceEmbedding", input);
 
+const legacyFaceCrop = (faceID: string) =>
+    ipcRenderer.invoke("legacyFaceCrop", faceID);
+
 // - Watch
 
 const watchGet = () => ipcRenderer.invoke("watchGet");
@@ -341,6 +344,7 @@ contextBridge.exposeInMainWorld("electron", {
     clipTextEmbeddingIfAvailable,
     detectFaces,
     faceEmbedding,
+    legacyFaceCrop,
 
     // - Watch
 

+ 10 - 7
web/apps/photos/src/components/ml/PeopleList.tsx

@@ -1,11 +1,8 @@
-import { cachedOrNew } from "@/next/blob-cache";
-import { ensureLocalUser } from "@/next/local-user";
 import log from "@/next/log";
 import { Skeleton, styled } from "@mui/material";
 import { Legend } from "components/PhotoViewer/styledComponents/Legend";
 import { t } from "i18next";
 import React, { useEffect, useState } from "react";
-import machineLearningService from "services/machineLearning/machineLearningService";
 import { EnteFile } from "types/file";
 import { Face, Person } from "types/machineLearning";
 import { getPeopleList, getUnidentifiedFaces } from "utils/machineLearning";
@@ -163,8 +160,12 @@ const FaceCropImageView: React.FC<FaceCropImageViewProps> = ({
 
     useEffect(() => {
         let didCancel = false;
+        const electron = globalThis.electron;
 
-        if (cacheKey) {
+        if (cacheKey && electron) {
+            electron
+                .legacyFaceCrop(cacheKey)
+                /*
             cachedOrNew("face-crops", cacheKey, async () => {
                 const user = await ensureLocalUser();
                 return machineLearningService.regenerateFaceCrop(
@@ -172,9 +173,11 @@ const FaceCropImageView: React.FC<FaceCropImageViewProps> = ({
                     user.id,
                     faceId,
                 );
-            }).then((blob) => {
-                if (!didCancel) setObjectURL(URL.createObjectURL(blob));
-            });
+            })*/
+                .then((data) => {
+                    const blob = new Blob([data]);
+                    if (!didCancel) setObjectURL(URL.createObjectURL(blob));
+                });
         } else setObjectURL(undefined);
 
         return () => {

+ 22 - 0
web/packages/next/types/ipc.ts

@@ -346,6 +346,28 @@ export interface Electron {
      */
     faceEmbedding: (input: Float32Array) => Promise<Float32Array>;
 
+    /**
+     * Return a face crop stored by a previous version of ML.
+     *
+     * [Note: Legacy face crops]
+     *
+     * Older versions of ML generated and stored face crops in a "face-crops"
+     * cache directory on the Electron side. For the time being, we have
+     * disabled the face search whilst we put finishing touches to it. However,
+     * it'll be nice to still show the existing faces that have been clustered
+     * for people who opted in to the older beta.
+     *
+     * So we retain the older "face-crops" disk cache, and use this method to
+     * serve faces from it when needed.
+     *
+     * @param faceID An identifier corresponding to which the face crop had been
+     * stored by the older version of our app.
+     *
+     * @returns the JPEG data of the face crop if a file is found for the given
+     * {@link faceID}, otherwise undefined.
+     */
+    legacyFaceCrop: (faceID: string) => Promise<Uint8Array | undefined>;
+
     // - Watch
 
     /**