Add ml search config to enable or disable ml search

Show ml search ui elements when enabled
This commit is contained in:
Shailesh Pandit 2022-01-24 19:11:40 +05:30
parent b21e5608bd
commit 8c0c3c5c94
10 changed files with 159 additions and 43 deletions

View file

@ -1,4 +1,4 @@
import React, { useEffect, useRef, useState } from 'react';
import React, { useContext, useEffect, useRef, useState } from 'react';
import Photoswipe from 'photoswipe';
import PhotoswipeUIDefault from 'photoswipe/dist/photoswipe-ui-default';
import classnames from 'classnames';
@ -49,6 +49,7 @@ import { Formik } from 'formik';
import * as Yup from 'yup';
import EnteSpinner from 'components/EnteSpinner';
import EnteDateTimePicker from 'components/EnteDateTimePicker';
import { AppContext } from 'pages/_app';
interface Iprops {
isOpen: boolean;
@ -397,6 +398,7 @@ function InfoModal({
exif,
scheduleUpdate,
}) {
const appContext = useContext(AppContext);
return (
<Modal show={showInfo} onHide={handleCloseInfo}>
<Modal.Header closeButton>
@ -438,16 +440,22 @@ function InfoModal({
{constants.SHOW_MAP}
</a>
)}
<div>
<Legend>{constants.PEOPLE}</Legend>
</div>
<PhotoPeopleList file={items[photoSwipe?.getCurrentIndex()]} />
<div>
<Legend>{constants.UNIDENTIFIED_FACES}</Legend>
</div>
<UnidentifiedFaces
file={items[photoSwipe?.getCurrentIndex()]}
/>
{appContext.mlSearchEnabled && (
<>
<div>
<Legend>{constants.PEOPLE}</Legend>
</div>
<PhotoPeopleList
file={items[photoSwipe?.getCurrentIndex()]}
/>
<div>
<Legend>{constants.UNIDENTIFIED_FACES}</Legend>
</div>
<UnidentifiedFaces
file={items[photoSwipe?.getCurrentIndex()]}
/>
</>
)}
{exif && (
<>
<ExifData exif={exif} />

View file

@ -1,5 +1,5 @@
import { Search, SearchStats } from 'pages/gallery';
import React, { useEffect, useRef, useState } from 'react';
import React, { useContext, useEffect, useRef, useState } from 'react';
import styled from 'styled-components';
import AsyncSelect from 'react-select/async';
import { components } from 'react-select';
@ -28,6 +28,7 @@ import VideoIcon from './icons/VideoIcon';
import { IconButton } from './Container';
import { Person } from 'types/machineLearning';
import { PeopleList } from './MachineLearning/PeopleList';
import { AppContext } from 'pages/_app';
const Wrapper = styled.div<{ isDisabled: boolean; isOpen: boolean }>`
position: fixed;
@ -118,6 +119,7 @@ interface Props {
export default function SearchBar(props: Props) {
const selectRef = useRef(null);
const [value, setValue] = useState<Suggestion>(null);
const appContext = useContext(AppContext);
const handleChange = (value) => {
setValue(value);
@ -129,12 +131,14 @@ export default function SearchBar(props: Props) {
// Functionality
// = =========================
const getAutoCompleteSuggestions = async (searchPhrase: string) => {
const options = await getAllPeopleSuggestion();
// const options = [];
const options = [];
searchPhrase = searchPhrase.trim().toLowerCase();
if (!searchPhrase?.length) {
return [];
}
if (appContext.mlSearchEnabled) {
options.push(...(await getAllPeopleSuggestion()));
}
options.push(...getHolidaySuggestion(searchPhrase));
options.push(...getYearSuggestion(searchPhrase));

View file

@ -37,6 +37,7 @@ import {
TRASH_SECTION,
} from 'components/pages/gallery/Collections';
import FixLargeThumbnails from './FixLargeThumbnail';
import { AppContext } from 'pages/_app';
interface Props {
collections: Collection[];
setDialogMessage: SetDialogMessage;
@ -56,6 +57,7 @@ export default function Sidebar(props: Props) {
const [exportModalView, setExportModalView] = useState(false);
const [fixLargeThumbsView, setFixLargeThumbsView] = useState(false);
const galleryContext = useContext(GalleryContext);
const appContext = useContext(AppContext);
useEffect(() => {
const main = async () => {
if (!isOpen) {
@ -297,6 +299,17 @@ export default function Sidebar(props: Props) {
onClick={openFeedbackURL}>
{constants.REQUEST_FEATURE}
</LinkButton>
<LinkButton
style={{ marginTop: '30px' }}
onClick={() => {
appContext.updateMlSearchEnabled(
!appContext.mlSearchEnabled
);
}}>
{appContext.mlSearchEnabled
? constants.DISABLE_ML_SEARCH
: constants.ENABLE_ML_SEARCH}
</LinkButton>
<LinkButton
style={{ marginTop: '30px' }}
onClick={() => {

View file

@ -1,5 +1,5 @@
import { JobConfig } from 'types/common/job';
import { MLSyncConfig } from 'types/machineLearning';
import { MLSearchConfig, MLSyncConfig } from 'types/machineLearning';
export const DEFAULT_ML_SYNC_JOB_CONFIG: JobConfig = {
intervalSec: 30,
@ -53,6 +53,10 @@ export const DEFAULT_ML_SYNC_CONFIG: MLSyncConfig = {
mlVersion: 2,
};
export const DEFAULT_ML_SEARCH_CONFIG: MLSearchConfig = {
enabled: false,
};
export const ML_SYNC_DOWNLOAD_TIMEOUT_MS = 300000;
export const MAX_FACE_DISTANCE_PERCENT = Math.sqrt(2) / 100;

View file

@ -16,6 +16,10 @@ import FlashMessageBar from 'components/FlashMessageBar';
import Head from 'next/head';
import { eventBus, Events } from 'services/events';
import mlWorkManager from 'services/machineLearning/mlWorkManager';
import {
getMLSearchConfig,
updateMLSearchConfig,
} from 'utils/machineLearning/config';
const GlobalStyles = createGlobalStyle`
/* ubuntu-regular - latin */
@ -483,6 +487,8 @@ type AppContextType = {
setDisappearingFlashMessage: (message: FlashMessage) => void;
redirectUrl: string;
setRedirectUrl: (url: string) => void;
mlSearchEnabled: boolean;
updateMlSearchEnabled: (enabled: boolean) => void;
};
export enum FLASH_MESSAGE_TYPE {
@ -513,6 +519,7 @@ export default function App({ Component, err }) {
const [redirectName, setRedirectName] = useState<string>(null);
const [flashMessage, setFlashMessage] = useState<FlashMessage>(null);
const [redirectUrl, setRedirectUrl] = useState(null);
const [mlSearchEnabled, setMlSearchEnabled] = useState(false);
useEffect(() => {
if (
!('serviceWorker' in navigator) ||
@ -550,12 +557,18 @@ export default function App({ Component, err }) {
}, []);
useEffect(() => {
try {
mlWorkManager;
eventBus.emit(Events.APP_START);
} catch (e) {
logError(e, 'Error in appStart handlers');
}
const loadMlSearchState = async () => {
try {
const mlSearchConfig = await getMLSearchConfig();
setMlSearchEnabled(mlSearchConfig.enabled);
mlWorkManager.setMlSearchEnabled(mlSearchConfig.enabled);
eventBus.emit(Events.APP_START);
} catch (e) {
logError(e, 'Error in appStart handlers');
}
};
loadMlSearchState();
}, []);
const setUserOnline = () => setOffline(false);
@ -612,6 +625,13 @@ export default function App({ Component, err }) {
setFlashMessage(flashMessages);
setTimeout(() => setFlashMessage(null), 5000);
};
const updateMlSearchEnabled = async (enabled: boolean) => {
const mlSearchConfig = await getMLSearchConfig();
mlSearchConfig.enabled = enabled;
await updateMLSearchConfig(mlSearchConfig);
setMlSearchEnabled(enabled);
mlWorkManager.setMlSearchEnabled(enabled);
};
// ho ja yaar
return (
<>
@ -657,6 +677,8 @@ export default function App({ Component, err }) {
setDisappearingFlashMessage,
redirectUrl,
setRedirectUrl,
mlSearchEnabled,
updateMlSearchEnabled,
}}>
{loading ? (
<Container>

View file

@ -19,8 +19,12 @@ class MLWorkManager {
private mlSyncJob: MLSyncJob;
private syncJobWorker: MLWorkerWithProxy;
private debouncedLiveSyncIdle: () => void;
private debouncedFilesUpdated: () => void;
private liveSyncQueue: PQueue;
private liveSyncWorker: MLWorkerWithProxy;
private mlSearchEnabled: boolean;
constructor() {
this.liveSyncQueue = new PQueue({
@ -29,31 +33,58 @@ class MLWorkManager {
timeout: LIVE_SYNC_QUEUE_TIMEOUT_SEC * 1000,
throwOnTimeout: true,
});
logQueueStats(this.liveSyncQueue, 'livesync');
this.mlSearchEnabled = false;
const debouncedLiveSyncIdle = debounce(
eventBus.on(Events.LOGOUT, this.logoutHandler, this);
this.debouncedLiveSyncIdle = debounce(
() => this.onLiveSyncIdle(),
LIVE_SYNC_IDLE_DEBOUNCE_SEC * 1000
);
this.liveSyncQueue.on('idle', () => debouncedLiveSyncIdle(), this);
eventBus.on(Events.APP_START, this.appStartHandler, this);
eventBus.on(Events.LOGIN, this.startSyncJob, this);
eventBus.on(Events.LOGOUT, this.logoutHandler, this);
eventBus.on(Events.FILE_UPLOADED, this.fileUploadedHandler, this);
const debouncedFilesUpdated = debounce(
() => this.localFilesUpdatedHandler(),
this.debouncedFilesUpdated = debounce(
() => this.mlSearchEnabled && this.localFilesUpdatedHandler(),
LOCAL_FILES_UPDATED_DEBOUNCE_SEC * 1000
);
eventBus.on(
Events.LOCAL_FILES_UPDATED,
() => debouncedFilesUpdated(),
this
);
}
public async setMlSearchEnabled(enabled: boolean) {
if (!this.mlSearchEnabled && enabled) {
console.log('Enabling MLWorkManager');
this.mlSearchEnabled = true;
logQueueStats(this.liveSyncQueue, 'livesync');
this.liveSyncQueue.on('idle', this.debouncedLiveSyncIdle, this);
// eventBus.on(Events.APP_START, this.appStartHandler, this);
// eventBus.on(Events.LOGIN, this.startSyncJob, this);
eventBus.on(Events.FILE_UPLOADED, this.fileUploadedHandler, this);
eventBus.on(
Events.LOCAL_FILES_UPDATED,
this.debouncedFilesUpdated,
this
);
await this.startSyncJob();
} else if (this.mlSearchEnabled && !enabled) {
console.log('Disabling MLWorkManager');
this.mlSearchEnabled = false;
this.liveSyncQueue.removeAllListeners();
// eventBus.removeListener(Events.APP_START, this.appStartHandler, this);
// eventBus.removeListener(Events.LOGIN, this.startSyncJob, this);
eventBus.removeListener(
Events.FILE_UPLOADED,
this.fileUploadedHandler,
this
);
eventBus.removeListener(
Events.LOCAL_FILES_UPDATED,
this.debouncedFilesUpdated,
this
);
await this.stopSyncJob();
}
}
// Handlers
@ -83,6 +114,9 @@ class MLWorkManager {
enteFile: File;
localFile: globalThis.File;
}) {
if (!this.mlSearchEnabled) {
return;
}
console.log('fileUploadedHandler: ', arg.enteFile.id);
if (arg.enteFile.metadata.fileType !== FILE_TYPE.IMAGE) {
console.log('Skipping non image file for local file processing');
@ -131,7 +165,7 @@ class MLWorkManager {
private async onLiveSyncIdle() {
console.log('Live sync idle');
await this.terminateLiveSyncWorker();
this.startSyncJob();
this.mlSearchEnabled && this.startSyncJob();
}
public async syncLocalFile(enteFile: File, localFile: globalThis.File) {
@ -185,6 +219,10 @@ class MLWorkManager {
public async startSyncJob() {
try {
console.log('MLWorkManager.startSyncJob');
if (!this.mlSearchEnabled) {
console.log('ML Search disabled, not starting ml sync job');
return;
}
if (!getToken()) {
console.log('User not logged in, not starting ml sync job');
return;

View file

@ -244,6 +244,10 @@ export interface MLSyncConfig extends Config {
mlVersion: number;
}
export interface MLSearchConfig extends Config {
enabled: boolean;
}
export interface MLSyncContext {
token: string;
config: MLSyncConfig;

View file

@ -1,10 +1,12 @@
import {
DEFAULT_ML_SEARCH_CONFIG,
DEFAULT_ML_SYNC_CONFIG,
DEFAULT_ML_SYNC_JOB_CONFIG,
} from 'constants/machineLearning/config';
import { JobConfig } from 'types/common/job';
import { MLSyncConfig } from 'types/machineLearning';
import { MLSearchConfig, MLSyncConfig } from 'types/machineLearning';
import mlIDbStorage, {
ML_SEARCH_CONFIG_NAME,
ML_SYNC_CONFIG_NAME,
ML_SYNC_JOB_CONFIG_NAME,
} from 'utils/storage/mlIDbStorage';
@ -20,6 +22,13 @@ export async function getMLSyncConfig() {
return mlIDbStorage.getConfig(ML_SYNC_CONFIG_NAME, DEFAULT_ML_SYNC_CONFIG);
}
export async function getMLSearchConfig() {
return mlIDbStorage.getConfig(
ML_SEARCH_CONFIG_NAME,
DEFAULT_ML_SEARCH_CONFIG
);
}
export async function updateMLSyncJobConfig(newConfig: JobConfig) {
return mlIDbStorage.putConfig(ML_SYNC_JOB_CONFIG_NAME, newConfig);
}
@ -27,3 +36,7 @@ export async function updateMLSyncJobConfig(newConfig: JobConfig) {
export async function updateMLSyncConfig(newConfig: MLSyncConfig) {
return mlIDbStorage.putConfig(ML_SYNC_CONFIG_NAME, newConfig);
}
export async function updateMLSearchConfig(newConfig: MLSearchConfig) {
return mlIDbStorage.putConfig(ML_SEARCH_CONFIG_NAME, newConfig);
}

View file

@ -1,4 +1,5 @@
import {
DEFAULT_ML_SEARCH_CONFIG,
DEFAULT_ML_SYNC_CONFIG,
DEFAULT_ML_SYNC_JOB_CONFIG,
} from 'constants/machineLearning/config';
@ -16,6 +17,7 @@ import { runningInBrowser } from 'utils/common';
export const ML_SYNC_JOB_CONFIG_NAME = 'ml-sync-job';
export const ML_SYNC_CONFIG_NAME = 'ml-sync';
export const ML_SEARCH_CONFIG_NAME = 'ml-search';
const MLDATA_DB_NAME = 'mldata';
interface MLDb extends DBSchema {
@ -50,7 +52,7 @@ class MLIDbStorage {
return;
}
this.db = openDB<MLDb>(MLDATA_DB_NAME, 2, {
this.db = openDB<MLDb>(MLDATA_DB_NAME, 3, {
upgrade(db, oldVersion, newVersion, tx) {
if (oldVersion < 1) {
const filesStore = db.createObjectStore('files', {
@ -82,6 +84,12 @@ class MLIDbStorage {
ML_SYNC_CONFIG_NAME
);
}
if (oldVersion < 3) {
tx.objectStore('configs').add(
DEFAULT_ML_SEARCH_CONFIG,
ML_SEARCH_CONFIG_NAME
);
}
},
});
}

View file

@ -204,6 +204,8 @@ const englishConstants = {
),
CONTACT_SUPPORT: 'contact support',
REQUEST_FEATURE: 'request feature',
ENABLE_ML_SEARCH: 'enable ML Search beta',
DISABLE_ML_SEARCH: 'disable ML Search beta',
ML_DEBUG: 'ML Debug',
SUPPORT: 'support',
CONFIRM: 'confirm',