wip checkpoint

This commit is contained in:
Manav Rathi
2024-09-18 15:16:40 +05:30
parent 63138a539a
commit 6254fe032b
7 changed files with 87 additions and 113 deletions

View File

@@ -17,7 +17,7 @@ import {
import { wipHasSwitchedOnceCmpAndSet } from "@/new/photos/services/ml";
import {
filterSearchableFiles,
setSearchableCollectionsAndFiles,
setSearchCollectionsAndFiles,
} from "@/new/photos/services/search";
import type { SearchOption } from "@/new/photos/services/search/types";
import { EnteFile } from "@/new/photos/types/file";
@@ -411,7 +411,7 @@ export default function Gallery() {
useEffect(
() =>
setSearchableCollectionsAndFiles({
setSearchCollectionsAndFiles({
collections: collections ?? [],
files: getUniqueFiles(files ?? []),
}),

View File

@@ -1,6 +1,6 @@
import { useIsMobileWidth } from "@/base/hooks";
import type { Person } from "@/new/photos/services/ml";
import { faceCrop, unidentifiedFaceIDs } from "@/new/photos/services/ml";
import type { Person } from "@/new/photos/services/ml/cgroups";
import type { EnteFile } from "@/new/photos/types/file";
import { Skeleton, Typography, styled } from "@mui/material";
import { t } from "i18next";

View File

@@ -2,7 +2,6 @@ import { masterKeyFromSession } from "@/base/session-store";
import { fileIDFromFaceID, wipClusterEnable } from ".";
import type { EnteFile } from "../../types/file";
import { getLocalFiles } from "../files";
import { setCGroups } from "../search";
import { pullCGroups } from "../user-entity";
import type { FaceCluster } from "./cluster";
import { getClusterGroups, getFaceIndexes } from "./db";
@@ -78,6 +77,47 @@ export interface CGroup {
avatarFaceID: string | undefined;
}
/**
* A massaged version of {@link CGroup} suitable for being shown in the UI.
*
* The cgroups synced with remote do not directly correspond to "people".
* CGroups represent both positive and negative feedback, where the negations
* are specifically feedback meant so that we do not show the corresponding
* cluster in the UI.
*
* So while each person has an underlying cgroups, not all cgroups have a
* corresponding person.
*
* Beyond this semantic difference, there is also data massaging: a
* {@link Person} has data converted into a format that the UI can directly and
* efficiently use, as compared to a {@link CGroup}, which is tailored for
* transmission and storage.
*/
export interface Person {
/**
* Nanoid of the underlying {@link CGroup}.
*/
id: string;
/**
* The name of the person.
*/
name: string;
/**
* IDs of the (unique) files in which this face occurs.
*/
fileIDs: number[];
/**
* The face that should be used as the "cover" face to represent this
* {@link Person} in the UI.
*/
displayFaceID: string;
/**
* The {@link EnteFile} which contains the display face.
*/
displayFaceFile: EnteFile;
}
// TODO-Cluster remove me
/**
* A {@link CGroup} annotated with various in-memory state to make it easier for
* the upper layers of our code to directly use it.
@@ -110,10 +150,13 @@ export const syncCGroups = async () => {
* This function is meant to run after files, cgroups and faces have been synced
* with remote. It then uses all the information in the local DBs to construct
* an in-memory list of {@link Person}s on which the UI will operate.
*
* @return A list of {@link Person}s, sorted by the number of files that they
* reference.
*/
export const updatePeople = async () => {
if (!process.env.NEXT_PUBLIC_ENTE_WIP_CL) return;
if (!(await wipClusterEnable())) return;
export const updatedPeople = async () => {
if (!process.env.NEXT_PUBLIC_ENTE_WIP_CL) return [];
if (!(await wipClusterEnable())) return [];
// Ignore faces belonging to deleted (incl Trash) and hidden files.
//
@@ -144,7 +187,7 @@ export const updatePeople = async () => {
// Convert cgroups to people.
const cgroups = await getClusterGroups();
const people = cgroups
return cgroups
.map((cgroup) => {
// Hidden cgroups are clusters specifically marked so as to not be shown
// in the UI.
@@ -196,15 +239,6 @@ export const updatePeople = async () => {
return { id, name, fileIDs, displayFaceID, displayFaceFile };
})
.filter((c) => !!c);
// Read all the latest cluster groups, locally present files, local faces
// Re-read the cgroups across which we should search from the local ML DB.
//
// This DB read can happen in the search worker too, but that would cause
// another handle to the DB to be opened (from the search worker's context).
// Making the DB call here avoids that (not that we noticed any issues, but
// preemptively trying not to poke IndexedDB too much lest it be flaky).
await setCGroups();
.filter((c) => !!c)
.sort((a, b) => b.fileIDs.length - a.fileIDs.length);
};

View File

@@ -596,46 +596,6 @@ const setInterimScheduledStatus = () => {
const workerDidProcessFileOrIdle = throttled(updateMLStatusSnapshot, 2000);
/**
* A massaged version of {@link CGroup} suitable for being shown in the UI.
*
* The cgroups synced with remote do not directly correspond to "people".
* CGroups represent both positive and negative feedback, where the negations
* are specifically feedback meant so that we do not show the corresponding
* cluster in the UI.
*
* So while each person has an underlying cgroups, not all cgroups have a
* corresponding person.
*
* Beyond this semantic difference, there is also data massaging: a
* {@link Person} has data converted into a format that the UI can directly and
* efficiently use, as compared to a {@link CGroup}, which is tailored for
* transmission and storage.
*/
export interface Person {
/**
* Nanoid of the underlying {@link CGroup}.
*/
id: string;
/**
* The name of the person.
*/
name: string;
/**
* IDs of the (unique) files in which this face occurs.
*/
fileIDs: number[];
/**
* The face that should be used as the "cover" face to represent this
* {@link Person} in the UI.
*/
displayFaceID: string;
/**
* The {@link EnteFile} which contains the display face.
*/
displayFaceFile: EnteFile;
}
/**
* A function that can be used to subscribe to updates to {@link Person}s.
*

View File

@@ -4,12 +4,12 @@ import { ComlinkWorker } from "@/base/worker/comlink-worker";
import { FileType } from "@/media/file-type";
import i18n, { t } from "i18next";
import { clipMatches, isMLEnabled, isMLSupported } from "../ml";
import type { CGroup } from "../ml/cgroups";
import type { Person } from "../ml/cgroups";
import type {
LabelledFileType,
LabelledSearchDateComponents,
LocalizedSearchData,
SearchableCollectionsAndFiles,
SearchCollectionsAndFiles,
SearchSuggestion,
} from "./types";
import type { SearchWorker } from "./worker";
@@ -54,15 +54,14 @@ export const searchDataSync = () =>
/**
* Set the collections and files over which we should search.
*/
export const setSearchableCollectionsAndFiles = (
data: SearchableCollectionsAndFiles,
) => void worker().then((w) => w.setSearchableCollectionsAndFiles(data));
export const setSearchCollectionsAndFiles = (cf: SearchCollectionsAndFiles) =>
void worker().then((w) => w.setCollectionsAndFiles(cf));
/**
* Set the cgroups that we should search across.
* Set the people that we should search across.
*/
export const setCGroups = (data: CGroup[]) =>
worker().then((w) => w.setCGroups(data));
export const setPeople = (people: Person[]) =>
void worker().then((w) => w.setPeople(people));
/**
* Convert a search string into (annotated) suggestions that can be shown in the
@@ -121,7 +120,7 @@ const suggestionsToOptions = (suggestions: SearchSuggestion[]) =>
/**
* Return the list of {@link EnteFile}s (from amongst the previously set
* {@link SearchableCollectionsAndFiles}) that match the given search {@link suggestion}.
* {@link SearchCollectionsAndFiles}) that match the given search {@link suggestion}.
*/
export const filterSearchableFiles = async (suggestion: SearchSuggestion) =>
worker().then((w) => w.filterSearchableFiles(suggestion));

View File

@@ -6,7 +6,7 @@
import type { Location } from "@/base/types";
import type { Collection } from "@/media/collection";
import { FileType } from "@/media/file-type";
import type { Person } from "@/new/photos/services/ml";
import type { Person } from "@/new/photos/services/ml/cgroups";
import type { EnteFile } from "@/new/photos/types/file";
import type { LocationTag } from "../user-entity";
@@ -45,9 +45,9 @@ export interface SearchOption {
}
/**
* The collections and files which we should search.
* The collections and files over which we should search.
*/
export interface SearchableCollectionsAndFiles {
export interface SearchCollectionsAndFiles {
collections: Collection[];
files: EnteFile[];
}

View File

@@ -1,9 +1,8 @@
import { HTTPError } from "@/base/http";
import log from "@/base/log";
import type { Location } from "@/base/types";
import type { Collection } from "@/media/collection";
import { fileCreationPhotoDate, fileLocation } from "@/media/file-metadata";
import type { CGroup } from "@/new/photos/services/ml/cgroups";
import type { Person } from "@/new/photos/services/ml/cgroups";
import type { EnteFile } from "@/new/photos/types/file";
import { ensure } from "@/utils/ensure";
import { nullToUndefined } from "@/utils/transform";
@@ -23,13 +22,11 @@ import type {
LabelledSearchDateComponents,
LocalizedSearchData,
Searchable,
SearchableCollectionsAndFiles,
SearchCollectionsAndFiles,
SearchDateComponents,
SearchSuggestion,
} from "./types";
type SearchableCGroup = Searchable<Omit<CGroup, "name"> & { name: string }>;
/**
* A web worker that runs the search asynchronously so that the main thread
* remains responsive.
@@ -37,11 +34,11 @@ type SearchableCGroup = Searchable<Omit<CGroup, "name"> & { name: string }>;
export class SearchWorker {
private locationTags: Searchable<LocationTag>[] = [];
private cities: Searchable<City>[] = [];
private searchableCollectionsAndFiles: SearchableCollectionsAndFiles = {
private collectionsAndFiles: SearchCollectionsAndFiles = {
collections: [],
files: [],
};
private searchableCGroups: SearchableCGroup[] = [];
private searchablePeople: Searchable<Person>[] = [];
/**
* Fetch any state we might need when the actual search happens.
@@ -71,23 +68,17 @@ export class SearchWorker {
/**
* Set the collections and files that we should search across.
*/
setSearchableCollectionsAndFiles(data: SearchableCollectionsAndFiles) {
this.searchableCollectionsAndFiles = data;
setCollectionsAndFiles(cf: SearchCollectionsAndFiles) {
this.collectionsAndFiles = cf;
}
/**
* Set the cgroups that we should search across.
* Set the people that we should search across.
*/
setCGroups(cgroups: CGroup[]) {
this.searchableCGroups = cgroups
.map((cgroup) => {
const name = cgroup.name;
if (!name) return undefined;
if (cgroup.isHidden) return undefined;
return { ...cgroup, name, lowercasedName: name.toLowerCase() };
})
.filter((c) => !!c);
log.debug(() => ["searchableCGroups", this.searchableCGroups]);
setPeople(people: Person[]) {
this.searchablePeople = people.map((person) => {
return { ...person, lowercasedName: person.name.toLowerCase() };
});
}
/**
@@ -101,8 +92,8 @@ export class SearchWorker {
return suggestionsForString(
s,
searchString,
this.searchableCollectionsAndFiles,
this.searchableCGroups,
this.collectionsAndFiles,
this.searchablePeople,
localizedSearchData,
this.locationTags,
this.cities,
@@ -114,7 +105,7 @@ export class SearchWorker {
*/
filterSearchableFiles(suggestion: SearchSuggestion) {
return filterSearchableFiles(
this.searchableCollectionsAndFiles.files,
this.collectionsAndFiles.files,
suggestion,
);
}
@@ -123,7 +114,7 @@ export class SearchWorker {
* Batched variant of {@link filterSearchableFiles}.
*/
filterSearchableFilesMulti(suggestions: SearchSuggestion[]) {
const files = this.searchableCollectionsAndFiles.files;
const files = this.collectionsAndFiles.files;
return suggestions
.map((sg) => [filterSearchableFiles(files, sg), sg] as const)
.filter(([files]) => files.length);
@@ -139,15 +130,15 @@ expose(SearchWorker);
const suggestionsForString = (
s: string,
searchString: string,
{ collections, files }: SearchableCollectionsAndFiles,
searchableCGroups: SearchableCGroup[],
{ collections, files }: SearchCollectionsAndFiles,
searchablePeople: Searchable<Person>[],
{ locale, holidays, labelledFileTypes }: LocalizedSearchData,
locationTags: Searchable<LocationTag>[],
cities: Searchable<City>[],
): SearchSuggestion[] =>
[
// . <-- clip suggestions will be inserted here by our caller.
peopleSuggestions(s, searchableCGroups),
peopleSuggestions(s, searchablePeople),
fileTypeSuggestions(s, labelledFileTypes),
dateSuggestions(s, locale, holidays),
locationSuggestions(s, locationTags, cities),
@@ -216,21 +207,11 @@ const fileCaptionSuggestion = (
const peopleSuggestions = (
s: string,
searchableCGroups: SearchableCGroup[],
): SearchSuggestion[] => {
// Suppress the unused warning during WIP
if (process.env.NEXT_PUBLIC_ENTE_WIP_CL) {
console.log({ s, searchableCGroups });
}
return [];
// searchableCGroups
// .filter(({ lowercasedName }) => lowercasedName.startsWith(s))
// .map((scgroup) => ({
// type: "person",
// person: scgroup,
// label: scgroup.name,
// }));
};
searchablePeople: Searchable<Person>[],
): SearchSuggestion[] =>
searchablePeople
.filter(({ lowercasedName }) => lowercasedName.startsWith(s))
.map((person) => ({ type: "person", person, label: person.name }));
const dateSuggestions = (
s: string,