diff --git a/web/packages/new/photos/services/ml/face.ts b/web/packages/new/photos/services/ml/face.ts index 7ecbf06002..891b605db2 100644 --- a/web/packages/new/photos/services/ml/face.ts +++ b/web/packages/new/photos/services/ml/face.ts @@ -139,6 +139,17 @@ export interface Face { * This ID is guaranteed to be unique for all the faces detected in all the * files for the user. In particular, each file can have multiple faces but * they all will get their own unique {@link faceID}. + * + * This ID is also meant to be stable across reindexing. That is, if the + * same algorithm and hyperparameters are used to reindex the file, then it + * should result in the same face IDs. This allows us leeway in letting + * unnecessary reindexing happen in rare cases without invalidating the + * clusters that rely on the presence of the given face ID. + * + * Finally, this face ID is not completely opaque. It consists of underscore + * separated components, the first of which is the ID of the + * {@link EnteFile} to which this face belongs. Client code can rely on this + * structure and can parse it if needed. */ faceID: string; /** diff --git a/web/packages/new/photos/services/ml/index.ts b/web/packages/new/photos/services/ml/index.ts index 03e39343fb..584c447b40 100644 --- a/web/packages/new/photos/services/ml/index.ts +++ b/web/packages/new/photos/services/ml/index.ts @@ -3,6 +3,7 @@ */ import { isDesktop } from "@/base/app"; +import { assertionFailed } from "@/base/assert"; import { blobCache } from "@/base/blob-cache"; import { ensureElectron } from "@/base/electron"; import { isDevBuild } from "@/base/env"; @@ -69,6 +70,11 @@ class MLState { * See {@link mlStatusSnapshot}. */ mlStatusSnapshot: MLStatus | undefined; + + /** + * IDs files for which we are currently regenerating face crops. + */ + inFlightFaceCropRegenFileIDs = new Set(); } /** State shared by the functions in this module. See {@link MLState}. */ @@ -481,6 +487,28 @@ export const unidentifiedFaceIDs = async ( return index?.faces.map((f) => f.faceID) ?? []; }; +/** + * Return the cached face crop for the given face, regenerating it if needed. + * + * @param faceID The id of the face whose face crop we want. + */ +export const faceCrop = (faceID: string) => { + const fileID = fileIDFromFaceID(faceID); +}; + +/** + * Extract the ID of the {@link EnteFile} to which this face belongs from the + * given {@link faceID}. + */ +const fileIDFromFaceID = (faceID: string) => { + const fileID = parseInt(faceID.split("_")[0] ?? ""); + if (isNaN(fileID)) { + assertionFailed(`Ignoring attempt to parse invalid faceID ${faceID}`); + return undefined; + } + return fileID; +}; + /** * Check to see if any of the faces in the given file do not have a face crop * present locally. If so, then regenerate the face crops for all the faces in