diff --git a/desktop/src/main.ts b/desktop/src/main.ts index 242303ccf..0ff7664d1 100644 --- a/desktop/src/main.ts +++ b/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")); } }; diff --git a/desktop/src/main/ipc.ts b/desktop/src/main/ipc.ts index f59969202..1393f4bfd 100644 --- a/desktop/src/main/ipc.ts +++ b/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) => diff --git a/desktop/src/main/services/dir.ts b/desktop/src/main/services/dir.ts index d375648f6..d95c94f8e 100644 --- a/desktop/src/main/services/dir.ts +++ b/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 => { + // 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; +}; diff --git a/desktop/src/preload.ts b/desktop/src/preload.ts index 407e541ff..f9147e288 100644 --- a/desktop/src/preload.ts +++ b/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 diff --git a/web/apps/photos/src/components/ml/PeopleList.tsx b/web/apps/photos/src/components/ml/PeopleList.tsx index 8e6bc968f..09d9ebaaf 100644 --- a/web/apps/photos/src/components/ml/PeopleList.tsx +++ b/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 = ({ 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 = ({ 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 () => { diff --git a/web/packages/next/types/ipc.ts b/web/packages/next/types/ipc.ts index 4b05838fa..b4ef2b6b2 100644 --- a/web/packages/next/types/ipc.ts +++ b/web/packages/next/types/ipc.ts @@ -346,6 +346,28 @@ export interface Electron { */ faceEmbedding: (input: Float32Array) => Promise; + /** + * 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; + // - Watch /**