From 3d91e1c4818e01576abc7ef62ba914fa0885a21c Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 26 Jun 2024 10:57:03 +0530 Subject: [PATCH 1/8] doc --- web/packages/next/local-user.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/packages/next/local-user.ts b/web/packages/next/local-user.ts index d657264287..8e9fd05f62 100644 --- a/web/packages/next/local-user.ts +++ b/web/packages/next/local-user.ts @@ -9,7 +9,8 @@ const LocalUser = z.object({ /** * The user's (plaintext) auth token. * - * It is used for making API calls on their behalf. + * It is used for making API calls on their behalf, by passing this token as + * the value of the X-Auth-Token header in the HTTP request. */ token: z.string(), }); From a5a7c4fcb2948e1d5a4289bf5ed32515175a79fc Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 26 Jun 2024 11:22:36 +0530 Subject: [PATCH 2/8] Sketch --- .../photos/src/services/embeddingService.ts | 2 +- web/apps/photos/src/types/embedding.tsx | 9 +--- web/packages/new/photos/services/embedding.ts | 46 +++++++++++++++++++ 3 files changed, 48 insertions(+), 9 deletions(-) create mode 100644 web/packages/new/photos/services/embedding.ts diff --git a/web/apps/photos/src/services/embeddingService.ts b/web/apps/photos/src/services/embeddingService.ts index 04c5089f9e..f78d410042 100644 --- a/web/apps/photos/src/services/embeddingService.ts +++ b/web/apps/photos/src/services/embeddingService.ts @@ -1,3 +1,4 @@ +import type { EmbeddingModel } from "@/new/photos/services/embedding"; import { inWorker } from "@/next/env"; import log from "@/next/log"; import { apiOrigin } from "@/next/origins"; @@ -9,7 +10,6 @@ import localForage from "@ente/shared/storage/localForage"; import { getToken } from "@ente/shared/storage/localStorage/helpers"; import type { Embedding, - EmbeddingModel, EncryptedEmbedding, GetEmbeddingDiffResponse, PutEmbeddingRequest, diff --git a/web/apps/photos/src/types/embedding.tsx b/web/apps/photos/src/types/embedding.tsx index 161244c159..c14b4a137d 100644 --- a/web/apps/photos/src/types/embedding.tsx +++ b/web/apps/photos/src/types/embedding.tsx @@ -1,11 +1,4 @@ -/** - * The embeddings that we (the current client) knows how to handle. - * - * This is an exhaustive set of values we pass when PUT-ting encrypted - * embeddings on the server. However, we should be prepared to receive an - * {@link EncryptedEmbedding} with a model value different from these. - */ -export type EmbeddingModel = "onnx-clip" | "file-ml-clip-face"; +import type { EmbeddingModel } from "@/new/photos/services/embedding"; export interface EncryptedEmbedding { fileID: number; diff --git a/web/packages/new/photos/services/embedding.ts b/web/packages/new/photos/services/embedding.ts new file mode 100644 index 0000000000..9acd7ccd20 --- /dev/null +++ b/web/packages/new/photos/services/embedding.ts @@ -0,0 +1,46 @@ +/** + * The embeddings that we (the current client) knows how to handle. + * + * It is vernacularly called a model, but strictly speaking this is not the + * model, but the embedding produced by a particular model with a particular set + * of pre-/post- processing steps and related hyperparameters. It is better + * thought of as an "type" of embedding produced or consumed by the client. + * + * The embeddings themselves have a version included in them, so it is possible + * for us to make backward compatible updates to the indexing process on newer + * clients. Alternatively, if the changes are not backward compatible and can + * only be consumed by clients with the relevant scaffolding, then we change + * this "model" (i.e "type") field to create a new universe of embeddings. + * + * This is an exhaustive set of values we pass when GET-ing or PUT-ting + * encrypted embeddings from remote. However, we should be prepared to receive + * an {@link EncryptedEmbedding} with a model value different from these. + */ +export type EmbeddingModel = "onnx-clip" | "file-ml-clip-face"; + +/** + * Ask remote for what all changes have happened to the face embeddings that it + * knows about since the last time we synced. Then update our local state to + * reflect those changes. + * + * It takes no parameters since it saves the last sync time in local storage. + */ +export const syncRemoteFaceEmbeddings = async () => { + return 0; +}; + +// const getFaceEmbeddings = async () => { + +// } + +/** + * GET /embeddings/diff for the given model and changes {@link sinceTime}. + * + * @param model The {@link EmbeddingModel} whose diff we wish for. + * + * @param sinceTime The last time we synced (epoch ms). + */ +// const getEmbeddingsDiff = async (model: EmbeddingModel, sinceTime: number) => { +// // const params = new URLSearchParams() + +// } From 7cac870a68362f4d9943c50a5bf82eaed85c0040 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 26 Jun 2024 11:31:46 +0530 Subject: [PATCH 3/8] API --- web/packages/new/photos/services/embedding.ts | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/web/packages/new/photos/services/embedding.ts b/web/packages/new/photos/services/embedding.ts index 9acd7ccd20..8699c08ffe 100644 --- a/web/packages/new/photos/services/embedding.ts +++ b/web/packages/new/photos/services/embedding.ts @@ -1,3 +1,6 @@ +import { authenticatedRequestHeaders } from "@/next/http"; +import { apiOrigin } from "@/next/origins"; + /** * The embeddings that we (the current client) knows how to handle. * @@ -33,14 +36,23 @@ export const syncRemoteFaceEmbeddings = async () => { // } +/** The maximum number of items to fetch in a single GET /embeddings/diff */ +const diffLimit = 500; + /** * GET /embeddings/diff for the given model and changes {@link sinceTime}. * * @param model The {@link EmbeddingModel} whose diff we wish for. * - * @param sinceTime The last time we synced (epoch ms). + * @param sinceTime The last time we synced (epoch ms). Pass 0 to fetch + * everything from the beginning. */ -// const getEmbeddingsDiff = async (model: EmbeddingModel, sinceTime: number) => { -// // const params = new URLSearchParams() +const getEmbeddingsDiff = async (model: EmbeddingModel, sinceTime: number) => { + const params = new URLSearchParams({model, sinceTime: `${sinceTime}`, limit: `${diffLimit}`}) + const url = `${apiOrigin()}/embeddings/diff`; + const res = await fetch(`${url}?${params.toString()}`, { + headers: authenticatedRequestHeaders(), + }) + if (!res.ok) throw new Error(`Failed to fetch ${url}: HTTP ${res.status}`); +} -// } From 4839aaaf6df2e51e4c3300cdf8d438a4219b39e1 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 26 Jun 2024 11:56:42 +0530 Subject: [PATCH 4/8] types --- web/packages/new/photos/services/embedding.ts | 67 ++++++++++++++++--- 1 file changed, 57 insertions(+), 10 deletions(-) diff --git a/web/packages/new/photos/services/embedding.ts b/web/packages/new/photos/services/embedding.ts index 8699c08ffe..506fb3f05c 100644 --- a/web/packages/new/photos/services/embedding.ts +++ b/web/packages/new/photos/services/embedding.ts @@ -1,25 +1,75 @@ import { authenticatedRequestHeaders } from "@/next/http"; import { apiOrigin } from "@/next/origins"; +import { nullToUndefined } from "@/utils/transform"; +import { z } from "zod"; /** * The embeddings that we (the current client) knows how to handle. * + * This is an exhaustive set of values we pass when GET-ing or PUT-ting + * encrypted embeddings from remote. However, we should be prepared to receive a + * {@link RemoteEmbedding} with a model value different from these. + * * It is vernacularly called a model, but strictly speaking this is not the * model, but the embedding produced by a particular model with a particular set * of pre-/post- processing steps and related hyperparameters. It is better * thought of as an "type" of embedding produced or consumed by the client. * + * [Note: Handling versioning of embeddings] + * * The embeddings themselves have a version included in them, so it is possible * for us to make backward compatible updates to the indexing process on newer - * clients. Alternatively, if the changes are not backward compatible and can - * only be consumed by clients with the relevant scaffolding, then we change - * this "model" (i.e "type") field to create a new universe of embeddings. + * clients. * - * This is an exhaustive set of values we pass when GET-ing or PUT-ting - * encrypted embeddings from remote. However, we should be prepared to receive - * an {@link EncryptedEmbedding} with a model value different from these. + * If we bump the version of same model (say when indexing on a newer client), + * the assumption will be that older client will be able to consume the + * response. Say if we improve blur detection, older client should just consume + * the newer version and not try to index the file locally. + * + * If you get version that is older, client should discard and try to index + * locally (if needed) and also put the newer version it has on remote. + * + * In the case where the changes are not backward compatible and can only be + * consumed by clients with the relevant scaffolding, then we change this + * "model" (i.e "type") field to create a new universe of embeddings. */ -export type EmbeddingModel = "onnx-clip" | "file-ml-clip-face"; +export type EmbeddingModel = + | "onnx-clip" /* CLIP (text) embeddings */ + | "file-ml-clip-face" /* Face embeddings */; + +/** The maximum number of items to fetch in a single GET /embeddings/diff */ +const diffLimit = 500; + +const RemoteEmbedding = z.object({ + /** The ID of the file whose embedding this is. */ + fileID: z.number(), + /** + * The embedding "type". + * + * This can be an arbitrary string since there might be models the current + * client does not know about; we limit our interactions to values that are + * one of {@link EmbeddingModel}. + */ + model: z.string(), + /** + * Base64 representation of the encrypted (model specific) embedding JSON. + */ + encryptedEmbedding: z.string(), + /** + * Base64 representation of the header that should be passed when decrypting + * {@link encryptedEmbedding}. See the {@link decryptMetadata} function in + * the crypto layer. + */ + decryptionHeader: z.string(), + /** Last time (epoch ms) this embedding was updated. */ + updatedAt: z.number(), + /** + * The version for the embedding. Optional. + * + * See: [Note: Handling versioning of embeddings] + */ + version: z.number().nullish().transform(nullToUndefined), +}); /** * Ask remote for what all changes have happened to the face embeddings that it @@ -36,9 +86,6 @@ export const syncRemoteFaceEmbeddings = async () => { // } -/** The maximum number of items to fetch in a single GET /embeddings/diff */ -const diffLimit = 500; - /** * GET /embeddings/diff for the given model and changes {@link sinceTime}. * From 4c8e6853be1c8b0e90d9207944ec62bb8b9c4140 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 26 Jun 2024 12:43:20 +0530 Subject: [PATCH 5/8] sync time --- web/packages/new/photos/services/embedding.ts | 53 +++++++++++++++---- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/web/packages/new/photos/services/embedding.ts b/web/packages/new/photos/services/embedding.ts index 506fb3f05c..614e9aece4 100644 --- a/web/packages/new/photos/services/embedding.ts +++ b/web/packages/new/photos/services/embedding.ts @@ -37,9 +37,6 @@ export type EmbeddingModel = | "onnx-clip" /* CLIP (text) embeddings */ | "file-ml-clip-face" /* Face embeddings */; -/** The maximum number of items to fetch in a single GET /embeddings/diff */ -const diffLimit = 500; - const RemoteEmbedding = z.object({ /** The ID of the file whose embedding this is. */ fileID: z.number(), @@ -71,6 +68,8 @@ const RemoteEmbedding = z.object({ version: z.number().nullish().transform(nullToUndefined), }); +type RemoteEmbedding = z.infer; + /** * Ask remote for what all changes have happened to the face embeddings that it * knows about since the last time we synced. Then update our local state to @@ -79,27 +78,59 @@ const RemoteEmbedding = z.object({ * It takes no parameters since it saves the last sync time in local storage. */ export const syncRemoteFaceEmbeddings = async () => { + const sinceTime = faceEmbeddingSyncTime(); + saveFaceEmbeddingSyncTime(sinceTime); return 0; }; +/** + * The updatedAt of the most recent face {@link RemoteEmbedding} we've retrieved + * and saved from remote, or 0. + * + * This value is persisted to local storage. To update it, use + * {@link saveFaceEmbeddingSyncMarker}. + */ +const faceEmbeddingSyncTime = () => + parseInt(localStorage.getItem("faceEmbeddingSyncTime") ?? "0"); + +/** Sibling of {@link faceEmbeddingSyncMarker}. */ +const saveFaceEmbeddingSyncTime = (t: number) => + localStorage.setItem("faceEmbeddingSyncTime", `${t}`); + // const getFaceEmbeddings = async () => { // } +/** The maximum number of items to fetch in a single GET /embeddings/diff */ +const diffLimit = 500; + /** - * GET /embeddings/diff for the given model and changes {@link sinceTime}. + * GET embeddings for the given model that have been updated {@link sinceTime}. + * + * This fetches the next {@link diffLimit} embeddings whose {@link updatedAt} is + * greater than the given {@link sinceTime} (non-inclusive). * * @param model The {@link EmbeddingModel} whose diff we wish for. * - * @param sinceTime The last time we synced (epoch ms). Pass 0 to fetch - * everything from the beginning. + * @param sinceTime The updatedAt of the last embedding we've synced (epoch ms). + * Pass 0 to fetch everything from the beginning. + * + * @returns an array of {@link RemoteEmbedding}. The returned array is limited + * to a maximum count of {@link diffLimit}. */ -const getEmbeddingsDiff = async (model: EmbeddingModel, sinceTime: number) => { - const params = new URLSearchParams({model, sinceTime: `${sinceTime}`, limit: `${diffLimit}`}) +const getEmbeddingsDiff = async ( + model: EmbeddingModel, + sinceTime: number, +): Promise => { + const params = new URLSearchParams({ + model, + sinceTime: `${sinceTime}`, + limit: `${diffLimit}`, + }); const url = `${apiOrigin()}/embeddings/diff`; const res = await fetch(`${url}?${params.toString()}`, { headers: authenticatedRequestHeaders(), - }) + }); if (!res.ok) throw new Error(`Failed to fetch ${url}: HTTP ${res.status}`); -} - + return z.array(RemoteEmbedding).parse(await res.json()); +}; From 1d0cbc527afc322eda3583fc2c3d23f678523df6 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 26 Jun 2024 12:54:39 +0530 Subject: [PATCH 6/8] loop --- web/packages/new/photos/services/embedding.ts | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/web/packages/new/photos/services/embedding.ts b/web/packages/new/photos/services/embedding.ts index 614e9aece4..b6bf93b690 100644 --- a/web/packages/new/photos/services/embedding.ts +++ b/web/packages/new/photos/services/embedding.ts @@ -78,9 +78,24 @@ type RemoteEmbedding = z.infer; * It takes no parameters since it saves the last sync time in local storage. */ export const syncRemoteFaceEmbeddings = async () => { - const sinceTime = faceEmbeddingSyncTime(); - saveFaceEmbeddingSyncTime(sinceTime); - return 0; + let sinceTime = faceEmbeddingSyncTime(); + // TODO: eslint has fixed this spurious warning, but we're not on the latest + // version yet, so add a disable. + // https://github.com/eslint/eslint/pull/18286 + /* eslint-disable no-constant-condition */ + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + while (true) { + const embeddings = await getEmbeddingsDiff( + "file-ml-clip-face", + sinceTime, + ); + if (embeddings.length == 0) break; + sinceTime = embeddings.reduce( + (max, { updatedAt }) => Math.max(max, updatedAt), + sinceTime, + ); + saveFaceEmbeddingSyncTime(sinceTime); + } }; /** From 2c3bd39ab64b7bafc1dc09f2d7a2e62b218c56b7 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 26 Jun 2024 13:28:26 +0530 Subject: [PATCH 7/8] read files --- web/apps/cast/src/services/render.ts | 12 ++-- web/apps/cast/src/types/file/index.ts | 2 +- .../components/Collections/CollectionCard.tsx | 2 +- .../Collections/CollectionOptions/index.tsx | 2 +- .../photos/src/components/ExportFinished.tsx | 2 +- .../photos/src/components/ExportModal.tsx | 2 +- .../src/components/ExportPendingList.tsx | 2 +- .../photos/src/components/FixCreationTime.tsx | 2 +- web/apps/photos/src/components/PhotoFrame.tsx | 8 +-- .../src/components/PhotoList/dedupe.tsx | 2 +- .../photos/src/components/PhotoList/index.tsx | 2 +- .../PhotoViewer/FileInfo/RenderCaption.tsx | 2 +- .../FileInfo/RenderCreationTime.tsx | 2 +- .../PhotoViewer/FileInfo/RenderFileName.tsx | 2 +- .../components/PhotoViewer/FileInfo/index.tsx | 2 +- .../PhotoViewer/ImageEditorOverlay/index.tsx | 2 +- .../src/components/PhotoViewer/index.tsx | 5 +- .../src/components/Search/SearchBar/index.tsx | 2 +- .../Search/SearchBar/searchInput/index.tsx | 2 +- .../photos/src/components/ml/PeopleList.tsx | 2 +- .../src/components/pages/gallery/Avatar.tsx | 2 +- .../src/components/pages/gallery/Navbar.tsx | 2 +- .../components/pages/gallery/PreviewCard.tsx | 2 +- .../photos/src/pages/deduplicate/index.tsx | 6 +- web/apps/photos/src/pages/gallery/index.tsx | 5 +- .../photos/src/pages/shared-albums/index.tsx | 2 +- web/apps/photos/src/services/clip-service.ts | 4 +- .../photos/src/services/collectionService.ts | 18 ++--- .../src/services/deduplicationService.ts | 2 +- .../src/services/download/clients/photos.ts | 2 +- .../services/download/clients/publicAlbums.ts | 2 +- .../photos/src/services/download/index.ts | 33 ++-------- .../photos/src/services/embeddingService.ts | 4 +- web/apps/photos/src/services/export/index.ts | 4 +- .../photos/src/services/export/migration.ts | 4 +- web/apps/photos/src/services/face/f-index.ts | 2 +- .../photos/src/services/face/face.worker.ts | 2 +- web/apps/photos/src/services/face/indexer.ts | 4 +- .../src/services/face/indexer.worker.ts | 2 +- .../photos/src/services/face/mlWorkManager.ts | 2 +- web/apps/photos/src/services/face/remote.ts | 2 +- web/apps/photos/src/services/fileService.ts | 65 +++---------------- web/apps/photos/src/services/fix-exif.ts | 2 +- .../machineLearning/machineLearningService.ts | 2 +- .../src/services/publicCollectionService.ts | 2 +- web/apps/photos/src/services/searchService.ts | 2 +- web/apps/photos/src/services/trashService.ts | 2 +- .../services/upload/publicUploadHttpClient.ts | 2 +- .../src/services/upload/uploadHttpClient.ts | 2 +- .../src/services/upload/uploadManager.ts | 4 +- .../src/services/upload/uploadService.ts | 18 ++--- web/apps/photos/src/services/watch.ts | 4 +- web/apps/photos/src/types/collection/index.ts | 6 +- web/apps/photos/src/types/export/index.ts | 2 +- web/apps/photos/src/types/gallery/index.ts | 2 +- .../src/types/publicCollection/index.ts | 2 +- web/apps/photos/src/types/search/index.ts | 2 +- web/apps/photos/src/types/trash/index.ts | 2 +- web/apps/photos/src/utils/collection/index.ts | 6 +- web/apps/photos/src/utils/file/index.ts | 20 +++--- .../photos/src/utils/magicMetadata/index.ts | 7 +- web/apps/photos/src/utils/photoFrame/index.ts | 4 +- web/apps/photos/src/worker/search.worker.ts | 2 +- web/apps/photos/tests/upload.test.ts | 2 +- web/apps/photos/tsconfig.json | 4 +- web/packages/new/photos/services/embedding.ts | 30 ++++++++- web/packages/new/photos/services/files.ts | 38 +++++++++++ .../new/photos/types/file.ts} | 34 ++++++++-- .../new/photos/types/magicMetadata.ts} | 0 69 files changed, 228 insertions(+), 205 deletions(-) create mode 100644 web/packages/new/photos/services/files.ts rename web/{apps/photos/src/types/file/index.ts => packages/new/photos/types/file.ts} (74%) rename web/{apps/photos/src/types/magicMetadata/index.ts => packages/new/photos/types/magicMetadata.ts} (100%) diff --git a/web/apps/cast/src/services/render.ts b/web/apps/cast/src/services/render.ts index 18c4fa928f..672f95f269 100644 --- a/web/apps/cast/src/services/render.ts +++ b/web/apps/cast/src/services/render.ts @@ -9,6 +9,12 @@ import { FILE_TYPE } from "@/media/file-type"; import { isHEICExtension, isNonWebImageFileExtension } from "@/media/formats"; import { heicToJPEG } from "@/media/heic-convert"; import { decodeLivePhoto } from "@/media/live-photo"; +import type { + EncryptedEnteFile, + EnteFile, + FileMagicMetadata, + FilePublicMagicMetadata, +} from "@/new/photos/types/file"; import { nameAndExtension } from "@/next/file"; import log from "@/next/log"; import { apiOrigin, customAPIOrigin } from "@/next/origins"; @@ -21,12 +27,6 @@ import HTTPService from "@ente/shared/network/HTTPService"; import type { AxiosResponse } from "axios"; import type { CastData } from "services/cast-data"; import { detectMediaMIMEType } from "services/detect-type"; -import type { - EncryptedEnteFile, - EnteFile, - FileMagicMetadata, - FilePublicMagicMetadata, -} from "types/file"; import { isChromecast } from "./chromecast"; /** diff --git a/web/apps/cast/src/types/file/index.ts b/web/apps/cast/src/types/file/index.ts index f294a54a18..c1bb5304a8 100644 --- a/web/apps/cast/src/types/file/index.ts +++ b/web/apps/cast/src/types/file/index.ts @@ -3,7 +3,7 @@ import type { EncryptedMagicMetadata, MagicMetadataCore, VISIBILITY_STATE, -} from "types/magicMetadata"; +} from "@/new/photos/types/magicMetadata"; export interface MetadataFileAttributes { encryptedData: string; diff --git a/web/apps/photos/src/components/Collections/CollectionCard.tsx b/web/apps/photos/src/components/Collections/CollectionCard.tsx index 9217f077fd..5bf247020e 100644 --- a/web/apps/photos/src/components/Collections/CollectionCard.tsx +++ b/web/apps/photos/src/components/Collections/CollectionCard.tsx @@ -1,10 +1,10 @@ +import { EnteFile } from "@/new/photos/types/file"; import { LoadingThumbnail, StaticThumbnail, } from "components/PlaceholderThumbnails"; import { useEffect, useState } from "react"; import downloadManager from "services/download"; -import { EnteFile } from "types/file"; export default function CollectionCard(props: { children?: any; diff --git a/web/apps/photos/src/components/Collections/CollectionOptions/index.tsx b/web/apps/photos/src/components/Collections/CollectionOptions/index.tsx index 61dcbc9e7e..5d03331918 100644 --- a/web/apps/photos/src/components/Collections/CollectionOptions/index.tsx +++ b/web/apps/photos/src/components/Collections/CollectionOptions/index.tsx @@ -1,3 +1,4 @@ +import { VISIBILITY_STATE } from "@/new/photos/types/magicMetadata"; import log from "@/next/log"; import { HorizontalFlex } from "@ente/shared/components/Container"; import OverflowMenu from "@ente/shared/components/OverflowMenu/menu"; @@ -18,7 +19,6 @@ import * as CollectionAPI from "services/collectionService"; import * as TrashService from "services/trashService"; import { Collection } from "types/collection"; import { SetFilesDownloadProgressAttributesCreator } from "types/gallery"; -import { VISIBILITY_STATE } from "types/magicMetadata"; import { changeCollectionOrder, changeCollectionSortOrder, diff --git a/web/apps/photos/src/components/ExportFinished.tsx b/web/apps/photos/src/components/ExportFinished.tsx index 43ba027578..24c142d75a 100644 --- a/web/apps/photos/src/components/ExportFinished.tsx +++ b/web/apps/photos/src/components/ExportFinished.tsx @@ -1,3 +1,4 @@ +import { EnteFile } from "@/new/photos/types/file"; import { SpaceBetweenFlex } from "@ente/shared/components/Container"; import { formatDateTime } from "@ente/shared/time/format"; import { @@ -9,7 +10,6 @@ import { } from "@mui/material"; import { t } from "i18next"; import { useState } from "react"; -import { EnteFile } from "types/file"; import { formatNumber } from "utils/number/format"; import ExportPendingList from "./ExportPendingList"; import LinkButton from "./pages/gallery/LinkButton"; diff --git a/web/apps/photos/src/components/ExportModal.tsx b/web/apps/photos/src/components/ExportModal.tsx index 0dcefbda6b..012244f734 100644 --- a/web/apps/photos/src/components/ExportModal.tsx +++ b/web/apps/photos/src/components/ExportModal.tsx @@ -1,3 +1,4 @@ +import { EnteFile } from "@/new/photos/types/file"; import log from "@/next/log"; import ChangeDirectoryOption from "@ente/shared/components/ChangeDirectoryOption"; import { @@ -24,7 +25,6 @@ import exportService, { selectAndPrepareExportDirectory, } from "services/export"; import { ExportProgress, ExportSettings } from "types/export"; -import { EnteFile } from "types/file"; import { getExportDirectoryDoesNotExistMessage } from "utils/ui"; import { DirectoryPath } from "./Directory"; import ExportFinished from "./ExportFinished"; diff --git a/web/apps/photos/src/components/ExportPendingList.tsx b/web/apps/photos/src/components/ExportPendingList.tsx index 93aa850787..88abf66b22 100644 --- a/web/apps/photos/src/components/ExportPendingList.tsx +++ b/web/apps/photos/src/components/ExportPendingList.tsx @@ -1,9 +1,9 @@ +import { EnteFile } from "@/new/photos/types/file"; import { FlexWrapper } from "@ente/shared/components/Container"; import DialogBoxV2 from "@ente/shared/components/DialogBoxV2"; import { Box, styled } from "@mui/material"; import ItemList from "components/ItemList"; import { t } from "i18next"; -import { EnteFile } from "types/file"; import CollectionCard from "./Collections/CollectionCard"; import { ResultPreviewTile } from "./Collections/styledComponents"; diff --git a/web/apps/photos/src/components/FixCreationTime.tsx b/web/apps/photos/src/components/FixCreationTime.tsx index 757ca27376..1c060250ff 100644 --- a/web/apps/photos/src/components/FixCreationTime.tsx +++ b/web/apps/photos/src/components/FixCreationTime.tsx @@ -1,3 +1,4 @@ +import { EnteFile } from "@/new/photos/types/file"; import DialogBox from "@ente/shared/components/DialogBox/"; import { Button, @@ -14,7 +15,6 @@ import { t } from "i18next"; import { GalleryContext } from "pages/gallery"; import React, { useContext, useEffect, useState } from "react"; import { updateCreationTimeWithExif } from "services/fix-exif"; -import { EnteFile } from "types/file"; import EnteDateTimePicker from "./EnteDateTimePicker"; export interface FixCreationTimeAttributes { diff --git a/web/apps/photos/src/components/PhotoFrame.tsx b/web/apps/photos/src/components/PhotoFrame.tsx index 89f1ce887e..6095a31765 100644 --- a/web/apps/photos/src/components/PhotoFrame.tsx +++ b/web/apps/photos/src/components/PhotoFrame.tsx @@ -1,4 +1,6 @@ import { FILE_TYPE } from "@/media/file-type"; +import type { LivePhotoSourceURL, SourceURLs } from "@/new/photos/types/file"; +import { EnteFile } from "@/new/photos/types/file"; import log from "@/next/log"; import { PHOTOS_PAGES } from "@ente/shared/constants/pages"; import { CustomError } from "@ente/shared/error"; @@ -12,11 +14,7 @@ import PhotoSwipe from "photoswipe"; import { useContext, useEffect, useState } from "react"; import AutoSizer from "react-virtualized-auto-sizer"; import { Duplicate } from "services/deduplicationService"; -import DownloadManager, { - LivePhotoSourceURL, - SourceURLs, -} from "services/download"; -import { EnteFile } from "types/file"; +import DownloadManager from "services/download"; import { SelectedState, SetFilesDownloadProgressAttributesCreator, diff --git a/web/apps/photos/src/components/PhotoList/dedupe.tsx b/web/apps/photos/src/components/PhotoList/dedupe.tsx index 61b9958ef0..56a9638e3f 100644 --- a/web/apps/photos/src/components/PhotoList/dedupe.tsx +++ b/web/apps/photos/src/components/PhotoList/dedupe.tsx @@ -1,3 +1,4 @@ +import { EnteFile } from "@/new/photos/types/file"; import { FlexWrapper } from "@ente/shared/components/Container"; import { Box, styled } from "@mui/material"; import { @@ -18,7 +19,6 @@ import { areEqual, } from "react-window"; import { Duplicate } from "services/deduplicationService"; -import { EnteFile } from "types/file"; import { formattedByteSize } from "utils/units"; export enum ITEM_TYPE { diff --git a/web/apps/photos/src/components/PhotoList/index.tsx b/web/apps/photos/src/components/PhotoList/index.tsx index bfc06d8c78..85c6db57cb 100644 --- a/web/apps/photos/src/components/PhotoList/index.tsx +++ b/web/apps/photos/src/components/PhotoList/index.tsx @@ -1,3 +1,4 @@ +import { EnteFile } from "@/new/photos/types/file"; import { FlexWrapper } from "@ente/shared/components/Container"; import { formatDate } from "@ente/shared/time/format"; import { Box, Checkbox, Link, Typography, styled } from "@mui/material"; @@ -21,7 +22,6 @@ import { ListChildComponentProps, areEqual, } from "react-window"; -import { EnteFile } from "types/file"; import { handleSelectCreator } from "utils/photoFrame"; import { PublicCollectionGalleryContext } from "utils/publicCollectionGallery"; import { formattedByteSize } from "utils/units"; diff --git a/web/apps/photos/src/components/PhotoViewer/FileInfo/RenderCaption.tsx b/web/apps/photos/src/components/PhotoViewer/FileInfo/RenderCaption.tsx index 3a5dbb6bc2..2eddc5fd33 100644 --- a/web/apps/photos/src/components/PhotoViewer/FileInfo/RenderCaption.tsx +++ b/web/apps/photos/src/components/PhotoViewer/FileInfo/RenderCaption.tsx @@ -1,3 +1,4 @@ +import { EnteFile } from "@/new/photos/types/file"; import log from "@/next/log"; import { FlexWrapper } from "@ente/shared/components/Container"; import Close from "@mui/icons-material/Close"; @@ -6,7 +7,6 @@ import { Box, IconButton, TextField } from "@mui/material"; import { Formik } from "formik"; import { t } from "i18next"; import { useState } from "react"; -import { EnteFile } from "types/file"; import { changeCaption, updateExistingFilePubMetadata } from "utils/file"; import * as Yup from "yup"; import { SmallLoadingSpinner } from "../styledComponents/SmallLoadingSpinner"; diff --git a/web/apps/photos/src/components/PhotoViewer/FileInfo/RenderCreationTime.tsx b/web/apps/photos/src/components/PhotoViewer/FileInfo/RenderCreationTime.tsx index 9d5cad467c..85be6a3c1a 100644 --- a/web/apps/photos/src/components/PhotoViewer/FileInfo/RenderCreationTime.tsx +++ b/web/apps/photos/src/components/PhotoViewer/FileInfo/RenderCreationTime.tsx @@ -1,10 +1,10 @@ +import { EnteFile } from "@/new/photos/types/file"; import log from "@/next/log"; import { FlexWrapper } from "@ente/shared/components/Container"; import { formatDate, formatTime } from "@ente/shared/time/format"; import CalendarTodayIcon from "@mui/icons-material/CalendarToday"; import EnteDateTimePicker from "components/EnteDateTimePicker"; import { useState } from "react"; -import { EnteFile } from "types/file"; import { changeFileCreationTime, updateExistingFilePubMetadata, diff --git a/web/apps/photos/src/components/PhotoViewer/FileInfo/RenderFileName.tsx b/web/apps/photos/src/components/PhotoViewer/FileInfo/RenderFileName.tsx index e9e27d55e8..caa9be2de4 100644 --- a/web/apps/photos/src/components/PhotoViewer/FileInfo/RenderFileName.tsx +++ b/web/apps/photos/src/components/PhotoViewer/FileInfo/RenderFileName.tsx @@ -1,4 +1,5 @@ import { FILE_TYPE } from "@/media/file-type"; +import { EnteFile } from "@/new/photos/types/file"; import { nameAndExtension } from "@/next/file"; import log from "@/next/log"; import { FlexWrapper } from "@ente/shared/components/Container"; @@ -6,7 +7,6 @@ import PhotoOutlined from "@mui/icons-material/PhotoOutlined"; import VideocamOutlined from "@mui/icons-material/VideocamOutlined"; import Box from "@mui/material/Box"; import { useEffect, useState } from "react"; -import { EnteFile } from "types/file"; import { changeFileName, updateExistingFilePubMetadata } from "utils/file"; import { formattedByteSize } from "utils/units"; import { FileNameEditDialog } from "./FileNameEditDialog"; diff --git a/web/apps/photos/src/components/PhotoViewer/FileInfo/index.tsx b/web/apps/photos/src/components/PhotoViewer/FileInfo/index.tsx index 5a17f43d17..6fab590737 100644 --- a/web/apps/photos/src/components/PhotoViewer/FileInfo/index.tsx +++ b/web/apps/photos/src/components/PhotoViewer/FileInfo/index.tsx @@ -1,3 +1,4 @@ +import { EnteFile } from "@/new/photos/types/file"; import CopyButton from "@ente/shared/components/CodeBlock/CopyButton"; import { FlexWrapper } from "@ente/shared/components/Container"; import EnteSpinner from "@ente/shared/components/EnteSpinner"; @@ -18,7 +19,6 @@ import { AppContext } from "pages/_app"; import { GalleryContext } from "pages/gallery"; import { useContext, useEffect, useMemo, useState } from "react"; import { getEXIFLocation } from "services/exif"; -import { EnteFile } from "types/file"; import { PublicCollectionGalleryContext } from "utils/publicCollectionGallery"; import { getMapDisableConfirmationDialog, diff --git a/web/apps/photos/src/components/PhotoViewer/ImageEditorOverlay/index.tsx b/web/apps/photos/src/components/PhotoViewer/ImageEditorOverlay/index.tsx index 9bb27b4b2a..0d41aba55d 100644 --- a/web/apps/photos/src/components/PhotoViewer/ImageEditorOverlay/index.tsx +++ b/web/apps/photos/src/components/PhotoViewer/ImageEditorOverlay/index.tsx @@ -1,3 +1,4 @@ +import { EnteFile } from "@/new/photos/types/file"; import { nameAndExtension } from "@/next/file"; import log from "@/next/log"; import { ensure } from "@/utils/ensure"; @@ -36,7 +37,6 @@ import { createContext, useContext, useEffect, useRef, useState } from "react"; import { getLocalCollections } from "services/collectionService"; import downloadManager from "services/download"; import uploadManager from "services/upload/uploadManager"; -import { EnteFile } from "types/file"; import { getEditorCloseConfirmationMessage } from "utils/ui"; import ColoursMenu from "./ColoursMenu"; import CropMenu, { cropRegionOfCanvas, getCropRegionArgs } from "./CropMenu"; diff --git a/web/apps/photos/src/components/PhotoViewer/index.tsx b/web/apps/photos/src/components/PhotoViewer/index.tsx index c7383efb13..db3d11b5c2 100644 --- a/web/apps/photos/src/components/PhotoViewer/index.tsx +++ b/web/apps/photos/src/components/PhotoViewer/index.tsx @@ -1,3 +1,4 @@ +import { EnteFile } from "@/new/photos/types/file"; import log from "@/next/log"; import Photoswipe from "photoswipe"; import PhotoswipeUIDefault from "photoswipe/dist/photoswipe-ui-default"; @@ -6,7 +7,6 @@ import { addToFavorites, removeFromFavorites, } from "services/collectionService"; -import { EnteFile } from "types/file"; import { copyFileToClipboard, downloadSingleFile, @@ -16,6 +16,7 @@ import { import { FILE_TYPE } from "@/media/file-type"; import { isNonWebImageFileExtension } from "@/media/formats"; +import type { LoadedLivePhotoSourceURL } from "@/new/photos/types/file"; import { lowercaseExtension } from "@/next/file"; import { FlexWrapper } from "@ente/shared/components/Container"; import EnteSpinner from "@ente/shared/components/EnteSpinner"; @@ -44,7 +45,7 @@ import isElectron from "is-electron"; import { AppContext } from "pages/_app"; import { GalleryContext } from "pages/gallery"; import { detectFileTypeInfo } from "services/detect-type"; -import downloadManager, { LoadedLivePhotoSourceURL } from "services/download"; +import downloadManager from "services/download"; import { getParsedExifData } from "services/exif"; import { trashFiles } from "services/fileService"; import { SetFilesDownloadProgressAttributesCreator } from "types/gallery"; diff --git a/web/apps/photos/src/components/Search/SearchBar/index.tsx b/web/apps/photos/src/components/Search/SearchBar/index.tsx index 5ba0462107..8d7612d4b5 100644 --- a/web/apps/photos/src/components/Search/SearchBar/index.tsx +++ b/web/apps/photos/src/components/Search/SearchBar/index.tsx @@ -1,7 +1,7 @@ import { Collection } from "types/collection"; import { SearchBarMobile } from "./searchBarMobile"; -import { EnteFile } from "types/file"; +import { EnteFile } from "@/new/photos/types/file"; import { UpdateSearch } from "types/search"; import SearchInput from "./searchInput"; import { SearchBarWrapper } from "./styledComponents"; diff --git a/web/apps/photos/src/components/Search/SearchBar/searchInput/index.tsx b/web/apps/photos/src/components/Search/SearchBar/searchInput/index.tsx index 62d4a1f434..d2fc70ee30 100644 --- a/web/apps/photos/src/components/Search/SearchBar/searchInput/index.tsx +++ b/web/apps/photos/src/components/Search/SearchBar/searchInput/index.tsx @@ -1,4 +1,5 @@ import { FILE_TYPE } from "@/media/file-type"; +import { EnteFile } from "@/new/photos/types/file"; import CloseIcon from "@mui/icons-material/Close"; import { IconButton } from "@mui/material"; import { t } from "i18next"; @@ -17,7 +18,6 @@ import { } from "services/searchService"; import { Collection } from "types/collection"; import { LocationTagData } from "types/entity"; -import { EnteFile } from "types/file"; import { ClipSearchScores, DateValue, diff --git a/web/apps/photos/src/components/ml/PeopleList.tsx b/web/apps/photos/src/components/ml/PeopleList.tsx index 38e70c3a8d..4e59588379 100644 --- a/web/apps/photos/src/components/ml/PeopleList.tsx +++ b/web/apps/photos/src/components/ml/PeopleList.tsx @@ -1,3 +1,4 @@ +import { EnteFile } from "@/new/photos/types/file"; import { blobCache } from "@/next/blob-cache"; import { Skeleton, styled } from "@mui/material"; import { Legend } from "components/PhotoViewer/styledComponents/Legend"; @@ -5,7 +6,6 @@ import { t } from "i18next"; import React, { useEffect, useState } from "react"; import { unidentifiedFaceIDs } from "services/face/indexer"; import type { Person } from "services/face/people"; -import { EnteFile } from "types/file"; const FaceChipContainer = styled("div")` display: flex; diff --git a/web/apps/photos/src/components/pages/gallery/Avatar.tsx b/web/apps/photos/src/components/pages/gallery/Avatar.tsx index caa9b8b782..12d61a34df 100644 --- a/web/apps/photos/src/components/pages/gallery/Avatar.tsx +++ b/web/apps/photos/src/components/pages/gallery/Avatar.tsx @@ -1,9 +1,9 @@ +import { EnteFile } from "@/new/photos/types/file"; import log from "@/next/log"; import { styled } from "@mui/material"; import { useTheme } from "@mui/material/styles"; import { GalleryContext } from "pages/gallery"; import React, { useContext, useLayoutEffect, useState } from "react"; -import { EnteFile } from "types/file"; interface AvatarProps { file?: EnteFile; diff --git a/web/apps/photos/src/components/pages/gallery/Navbar.tsx b/web/apps/photos/src/components/pages/gallery/Navbar.tsx index 4a710faa81..08a52cf628 100644 --- a/web/apps/photos/src/components/pages/gallery/Navbar.tsx +++ b/web/apps/photos/src/components/pages/gallery/Navbar.tsx @@ -1,3 +1,4 @@ +import { EnteFile } from "@/new/photos/types/file"; import { FlexWrapper, HorizontalFlex } from "@ente/shared/components/Container"; import SidebarToggler from "@ente/shared/components/Navbar/SidebarToggler"; import NavbarBase from "@ente/shared/components/Navbar/base"; @@ -9,7 +10,6 @@ import { t } from "i18next"; import { AppContext } from "pages/_app"; import React from "react"; import { Collection } from "types/collection"; -import { EnteFile } from "types/file"; import { UpdateSearch } from "types/search"; interface Iprops { diff --git a/web/apps/photos/src/components/pages/gallery/PreviewCard.tsx b/web/apps/photos/src/components/pages/gallery/PreviewCard.tsx index 8369b1e860..0b4c386c4b 100644 --- a/web/apps/photos/src/components/pages/gallery/PreviewCard.tsx +++ b/web/apps/photos/src/components/pages/gallery/PreviewCard.tsx @@ -1,4 +1,5 @@ import { FILE_TYPE } from "@/media/file-type"; +import { EnteFile } from "@/new/photos/types/file"; import log from "@/next/log"; import { Overlay } from "@ente/shared/components/Container"; import { CustomError } from "@ente/shared/error"; @@ -17,7 +18,6 @@ import { DeduplicateContext } from "pages/deduplicate"; import { GalleryContext } from "pages/gallery"; import React, { useContext, useEffect, useRef, useState } from "react"; import DownloadManager from "services/download"; -import { EnteFile } from "types/file"; import { shouldShowAvatar } from "utils/file"; import Avatar from "./Avatar"; diff --git a/web/apps/photos/src/pages/deduplicate/index.tsx b/web/apps/photos/src/pages/deduplicate/index.tsx index cbd297f78c..588df0a3dd 100644 --- a/web/apps/photos/src/pages/deduplicate/index.tsx +++ b/web/apps/photos/src/pages/deduplicate/index.tsx @@ -1,11 +1,11 @@ -import { t } from "i18next"; - +import { getLocalFiles } from "@/new/photos/services/files"; import PhotoFrame from "components/PhotoFrame"; import { ALL_SECTION } from "constants/collection"; +import { t } from "i18next"; import { AppContext } from "pages/_app"; import { createContext, useContext, useEffect, useState } from "react"; import { Duplicate, getDuplicates } from "services/deduplicationService"; -import { getLocalFiles, syncFiles, trashFiles } from "services/fileService"; +import { syncFiles, trashFiles } from "services/fileService"; import { SelectedState } from "types/gallery"; import { VerticallyCentered } from "@ente/shared/components/Container"; diff --git a/web/apps/photos/src/pages/gallery/index.tsx b/web/apps/photos/src/pages/gallery/index.tsx index 2b7a180137..360100c01c 100644 --- a/web/apps/photos/src/pages/gallery/index.tsx +++ b/web/apps/photos/src/pages/gallery/index.tsx @@ -1,6 +1,8 @@ import { WhatsNew } from "@/new/photos/components/WhatsNew"; import { shouldShowWhatsNew } from "@/new/photos/services/changelog"; import { fetchAndSaveFeatureFlagsIfNeeded } from "@/new/photos/services/feature-flags"; +import { getLocalFiles } from "@/new/photos/services/files"; +import { EnteFile } from "@/new/photos/types/file"; import log from "@/next/log"; import { CenteredFlex } from "@ente/shared/components/Container"; import EnteSpinner from "@ente/shared/components/EnteSpinner"; @@ -90,13 +92,12 @@ import { import downloadManager from "services/download"; import { syncCLIPEmbeddings } from "services/embeddingService"; import { syncEntities } from "services/entityService"; -import { getLocalFiles, syncFiles } from "services/fileService"; +import { syncFiles } from "services/fileService"; import locationSearchService from "services/locationSearchService"; import { getLocalTrashedFiles, syncTrash } from "services/trashService"; import uploadManager from "services/upload/uploadManager"; import { isTokenValid, syncMapEnabled } from "services/userService"; import { Collection, CollectionSummaries } from "types/collection"; -import { EnteFile } from "types/file"; import { GalleryContextType, SelectedState, diff --git a/web/apps/photos/src/pages/shared-albums/index.tsx b/web/apps/photos/src/pages/shared-albums/index.tsx index 6962d7c450..2976ed1e88 100644 --- a/web/apps/photos/src/pages/shared-albums/index.tsx +++ b/web/apps/photos/src/pages/shared-albums/index.tsx @@ -1,3 +1,4 @@ +import { EnteFile } from "@/new/photos/types/file"; import log from "@/next/log"; import { CenteredFlex, @@ -58,7 +59,6 @@ import { verifyPublicCollectionPassword, } from "services/publicCollectionService"; import { Collection } from "types/collection"; -import { EnteFile } from "types/file"; import { SelectedState, SetFilesDownloadProgressAttributes, diff --git a/web/apps/photos/src/services/clip-service.ts b/web/apps/photos/src/services/clip-service.ts index 915f9ae03e..f0de7c3127 100644 --- a/web/apps/photos/src/services/clip-service.ts +++ b/web/apps/photos/src/services/clip-service.ts @@ -1,4 +1,6 @@ import { FILE_TYPE } from "@/media/file-type"; +import { getAllLocalFiles, getLocalFiles } from "@/new/photos/services/files"; +import { EnteFile } from "@/new/photos/types/file"; import { ensureElectron } from "@/next/electron"; import log from "@/next/log"; import ComlinkCryptoWorker from "@ente/shared/crypto"; @@ -8,11 +10,9 @@ import { LS_KEYS, getData } from "@ente/shared/storage/localStorage"; import isElectron from "is-electron"; import PQueue from "p-queue"; import { Embedding } from "types/embedding"; -import { EnteFile } from "types/file"; import { getPersonalFiles } from "utils/file"; import downloadManager from "./download"; import { localCLIPEmbeddings, putEmbedding } from "./embeddingService"; -import { getAllLocalFiles, getLocalFiles } from "./fileService"; /** Status of CLIP indexing on the images in the user's local library. */ export interface CLIPIndexingStatus { diff --git a/web/apps/photos/src/services/collectionService.ts b/web/apps/photos/src/services/collectionService.ts index 485ea7cb76..17b9da68dc 100644 --- a/web/apps/photos/src/services/collectionService.ts +++ b/web/apps/photos/src/services/collectionService.ts @@ -1,10 +1,18 @@ +import { getLocalFiles } from "@/new/photos/services/files"; +import { EnteFile } from "@/new/photos/types/file"; +import { + EncryptedMagicMetadata, + SUB_TYPE, + UpdateMagicMetadataRequest, + VISIBILITY_STATE, +} from "@/new/photos/types/magicMetadata"; import log from "@/next/log"; import { apiOrigin } from "@/next/origins"; import ComlinkCryptoWorker from "@ente/shared/crypto"; import { CustomError } from "@ente/shared/error"; import HTTPService from "@ente/shared/network/HTTPService"; import localForage from "@ente/shared/storage/localForage"; -import { getData, LS_KEYS } from "@ente/shared/storage/localStorage"; +import { LS_KEYS, getData } from "@ente/shared/storage/localStorage"; import { getToken } from "@ente/shared/storage/localStorage/helpers"; import { getActualKey } from "@ente/shared/user"; import type { User } from "@ente/shared/user/types"; @@ -40,13 +48,6 @@ import { RemoveFromCollectionRequest, UpdatePublicURL, } from "types/collection"; -import { EnteFile } from "types/file"; -import { - EncryptedMagicMetadata, - SUB_TYPE, - UpdateMagicMetadataRequest, - VISIBILITY_STATE, -} from "types/magicMetadata"; import { FamilyData } from "types/user"; import { changeCollectionSubType, @@ -73,7 +74,6 @@ import { isPinnedCollection, updateMagicMetadata, } from "utils/magicMetadata"; -import { getLocalFiles } from "./fileService"; import { getPublicKey } from "./userService"; const COLLECTION_TABLE = "collections"; diff --git a/web/apps/photos/src/services/deduplicationService.ts b/web/apps/photos/src/services/deduplicationService.ts index b17d9f4f0b..d1edc4f642 100644 --- a/web/apps/photos/src/services/deduplicationService.ts +++ b/web/apps/photos/src/services/deduplicationService.ts @@ -1,11 +1,11 @@ import { hasFileHash } from "@/media/file"; import { FILE_TYPE } from "@/media/file-type"; import type { Metadata } from "@/media/types/file"; +import { EnteFile } from "@/new/photos/types/file"; import log from "@/next/log"; import { apiOrigin } from "@/next/origins"; import HTTPService from "@ente/shared/network/HTTPService"; import { getToken } from "@ente/shared/storage/localStorage/helpers"; -import { EnteFile } from "types/file"; interface DuplicatesResponse { duplicates: Array<{ diff --git a/web/apps/photos/src/services/download/clients/photos.ts b/web/apps/photos/src/services/download/clients/photos.ts index ac88fa0324..3740061805 100644 --- a/web/apps/photos/src/services/download/clients/photos.ts +++ b/web/apps/photos/src/services/download/clients/photos.ts @@ -1,9 +1,9 @@ +import { EnteFile } from "@/new/photos/types/file"; import { customAPIOrigin } from "@/next/origins"; import { CustomError } from "@ente/shared/error"; import HTTPService from "@ente/shared/network/HTTPService"; import { retryAsyncFunction } from "@ente/shared/utils"; import { DownloadClient } from "services/download"; -import { EnteFile } from "types/file"; export class PhotosDownloadClient implements DownloadClient { constructor( diff --git a/web/apps/photos/src/services/download/clients/publicAlbums.ts b/web/apps/photos/src/services/download/clients/publicAlbums.ts index 4a15407e59..9875c8d0fc 100644 --- a/web/apps/photos/src/services/download/clients/publicAlbums.ts +++ b/web/apps/photos/src/services/download/clients/publicAlbums.ts @@ -1,9 +1,9 @@ +import { EnteFile } from "@/new/photos/types/file"; import { customAPIOrigin } from "@/next/origins"; import { CustomError } from "@ente/shared/error"; import HTTPService from "@ente/shared/network/HTTPService"; import { retryAsyncFunction } from "@ente/shared/utils"; import { DownloadClient } from "services/download"; -import { EnteFile } from "types/file"; export class PublicAlbumsDownloadClient implements DownloadClient { private token: string; diff --git a/web/apps/photos/src/services/download/index.ts b/web/apps/photos/src/services/download/index.ts index a080acd92e..179aa7cbc8 100644 --- a/web/apps/photos/src/services/download/index.ts +++ b/web/apps/photos/src/services/download/index.ts @@ -1,5 +1,10 @@ import { FILE_TYPE } from "@/media/file-type"; import { decodeLivePhoto } from "@/media/live-photo"; +import { + EnteFile, + type LivePhotoSourceURL, + type SourceURLs, +} from "@/new/photos/types/file"; import { blobCache, type BlobCache } from "@/next/blob-cache"; import log from "@/next/log"; import ComlinkCryptoWorker from "@ente/shared/crypto"; @@ -9,38 +14,10 @@ import { isPlaybackPossible } from "@ente/shared/media/video-playback"; import type { Remote } from "comlink"; import isElectron from "is-electron"; import * as ffmpeg from "services/ffmpeg"; -import { EnteFile } from "types/file"; import { getRenderableImage } from "utils/file"; import { PhotosDownloadClient } from "./clients/photos"; import { PublicAlbumsDownloadClient } from "./clients/publicAlbums"; -export type LivePhotoSourceURL = { - image: () => Promise; - video: () => Promise; -}; - -export type LoadedLivePhotoSourceURL = { - image: string; - video: string; -}; - -export type SourceURLs = { - url: string | LivePhotoSourceURL | LoadedLivePhotoSourceURL; - isOriginal: boolean; - isRenderable: boolean; - type: "normal" | "livePhoto"; - /** - * Best effort attempt at obtaining the MIME type. - * - * Known cases where it is missing: - * - * - Live photos (these have a different code path for obtaining the URL). - * - A video that is passes the isPlayable test in the browser. - * - */ - mimeType?: string; -}; - export type OnDownloadProgress = (event: { loaded: number; total: number; diff --git a/web/apps/photos/src/services/embeddingService.ts b/web/apps/photos/src/services/embeddingService.ts index f78d410042..2c91ca01c9 100644 --- a/web/apps/photos/src/services/embeddingService.ts +++ b/web/apps/photos/src/services/embeddingService.ts @@ -1,4 +1,6 @@ import type { EmbeddingModel } from "@/new/photos/services/embedding"; +import { getAllLocalFiles } from "@/new/photos/services/files"; +import { EnteFile } from "@/new/photos/types/file"; import { inWorker } from "@/next/env"; import log from "@/next/log"; import { apiOrigin } from "@/next/origins"; @@ -14,10 +16,8 @@ import type { GetEmbeddingDiffResponse, PutEmbeddingRequest, } from "types/embedding"; -import { EnteFile } from "types/file"; import { getLocalCollections } from "./collectionService"; import type { FaceIndex } from "./face/types"; -import { getAllLocalFiles } from "./fileService"; import { getLocalTrashedFiles } from "./trashService"; type FileML = FaceIndex & { diff --git a/web/apps/photos/src/services/export/index.ts b/web/apps/photos/src/services/export/index.ts index d54ae205ec..6acae01e28 100644 --- a/web/apps/photos/src/services/export/index.ts +++ b/web/apps/photos/src/services/export/index.ts @@ -1,6 +1,8 @@ import { FILE_TYPE } from "@/media/file-type"; import { decodeLivePhoto } from "@/media/live-photo"; import type { Metadata } from "@/media/types/file"; +import { getAllLocalFiles } from "@/new/photos/services/files"; +import { EnteFile } from "@/new/photos/types/file"; import { ensureElectron } from "@/next/electron"; import log from "@/next/log"; import { wait } from "@/utils/promise"; @@ -22,7 +24,6 @@ import { ExportUIUpdaters, FileExportNames, } from "types/export"; -import { EnteFile } from "types/file"; import { constructCollectionNameMap, getCollectionUserFacingName, @@ -37,7 +38,6 @@ import { safeDirectoryName, safeFileName } from "utils/native-fs"; import { writeStream } from "utils/native-stream"; import { getAllLocalCollections } from "../collectionService"; import downloadManager from "../download"; -import { getAllLocalFiles } from "../fileService"; import { migrateExport } from "./migration"; /** Name of the JSON file in which we keep the state of the export. */ diff --git a/web/apps/photos/src/services/export/migration.ts b/web/apps/photos/src/services/export/migration.ts index 234427de83..f452a52642 100644 --- a/web/apps/photos/src/services/export/migration.ts +++ b/web/apps/photos/src/services/export/migration.ts @@ -1,5 +1,7 @@ import { FILE_TYPE } from "@/media/file-type"; import { decodeLivePhoto } from "@/media/live-photo"; +import { getAllLocalFiles } from "@/new/photos/services/files"; +import { EnteFile } from "@/new/photos/types/file"; import { ensureElectron } from "@/next/electron"; import { nameAndExtension } from "@/next/file"; import log from "@/next/log"; @@ -8,7 +10,6 @@ import { LS_KEYS, getData } from "@ente/shared/storage/localStorage"; import type { User } from "@ente/shared/user/types"; import { getLocalCollections } from "services/collectionService"; import downloadManager from "services/download"; -import { getAllLocalFiles } from "services/fileService"; import { Collection } from "types/collection"; import { CollectionExportNames, @@ -20,7 +21,6 @@ import { ExportedCollectionPaths, FileExportNames, } from "types/export"; -import { EnteFile } from "types/file"; import { getNonEmptyPersonalCollections } from "utils/collection"; import { getIDBasedSortedFiles, diff --git a/web/apps/photos/src/services/face/f-index.ts b/web/apps/photos/src/services/face/f-index.ts index cdcc4fe919..584b733148 100644 --- a/web/apps/photos/src/services/face/f-index.ts +++ b/web/apps/photos/src/services/face/f-index.ts @@ -1,5 +1,6 @@ import { FILE_TYPE } from "@/media/file-type"; import { decodeLivePhoto } from "@/media/live-photo"; +import type { EnteFile } from "@/new/photos/types/file"; import log from "@/next/log"; import { workerBridge } from "@/next/worker/worker-bridge"; import { Matrix } from "ml-matrix"; @@ -12,7 +13,6 @@ import { scale, translate, } from "transformation-matrix"; -import type { EnteFile } from "types/file"; import { getRenderableImage } from "utils/file"; import { saveFaceCrop } from "./crop"; import { diff --git a/web/apps/photos/src/services/face/face.worker.ts b/web/apps/photos/src/services/face/face.worker.ts index af0995951f..d74c235cc0 100644 --- a/web/apps/photos/src/services/face/face.worker.ts +++ b/web/apps/photos/src/services/face/face.worker.ts @@ -1,7 +1,7 @@ +import { EnteFile } from "@/new/photos/types/file"; import { expose } from "comlink"; import downloadManager from "services/download"; import mlService from "services/machineLearning/machineLearningService"; -import { EnteFile } from "types/file"; export class DedicatedMLWorker { public async closeLocalSyncContext() { diff --git a/web/apps/photos/src/services/face/indexer.ts b/web/apps/photos/src/services/face/indexer.ts index 5314a15e0b..c6cdc3295a 100644 --- a/web/apps/photos/src/services/face/indexer.ts +++ b/web/apps/photos/src/services/face/indexer.ts @@ -2,11 +2,11 @@ import { isBetaUser, isInternalUser, } from "@/new/photos/services/feature-flags"; +import { getAllLocalFiles } from "@/new/photos/services/files"; +import type { EnteFile } from "@/new/photos/types/file"; import { ComlinkWorker } from "@/next/worker/comlink-worker"; import { ensure } from "@/utils/ensure"; import type { Remote } from "comlink"; -import { getAllLocalFiles } from "services/fileService"; -import type { EnteFile } from "types/file"; import { faceIndex, indexableFileIDs, diff --git a/web/apps/photos/src/services/face/indexer.worker.ts b/web/apps/photos/src/services/face/indexer.worker.ts index 6cf1e5008f..8de8b61d16 100644 --- a/web/apps/photos/src/services/face/indexer.worker.ts +++ b/web/apps/photos/src/services/face/indexer.worker.ts @@ -1,5 +1,5 @@ +import type { EnteFile } from "@/new/photos/types/file"; import log from "@/next/log"; -import type { EnteFile } from "types/file"; import { fileLogID } from "utils/file"; import { closeFaceDBConnectionsIfNeeded, diff --git a/web/apps/photos/src/services/face/mlWorkManager.ts b/web/apps/photos/src/services/face/mlWorkManager.ts index 1a89dc1575..d1d1dcbad8 100644 --- a/web/apps/photos/src/services/face/mlWorkManager.ts +++ b/web/apps/photos/src/services/face/mlWorkManager.ts @@ -1,4 +1,5 @@ import { FILE_TYPE } from "@/media/file-type"; +import { EnteFile } from "@/new/photos/types/file"; import { ensureElectron } from "@/next/electron"; import log from "@/next/log"; import { clientPackageNamePhotosDesktop } from "@/next/types/app"; @@ -8,7 +9,6 @@ import { getToken, getUserID } from "@ente/shared/storage/localStorage/helpers"; import debounce from "debounce"; import PQueue from "p-queue"; import type { DedicatedMLWorker } from "services/face/face.worker"; -import { EnteFile } from "types/file"; export type JobState = "Scheduled" | "Running" | "NotScheduled"; diff --git a/web/apps/photos/src/services/face/remote.ts b/web/apps/photos/src/services/face/remote.ts index 2fd50024da..2c4209ac28 100644 --- a/web/apps/photos/src/services/face/remote.ts +++ b/web/apps/photos/src/services/face/remote.ts @@ -1,7 +1,7 @@ +import type { EnteFile } from "@/new/photos/types/file"; import log from "@/next/log"; import ComlinkCryptoWorker from "@ente/shared/crypto"; import { putEmbedding } from "services/embeddingService"; -import type { EnteFile } from "types/file"; import type { FaceIndex } from "./types"; export const putFaceIndex = async ( diff --git a/web/apps/photos/src/services/fileService.ts b/web/apps/photos/src/services/fileService.ts index ebabc9dbfc..2e8621d693 100644 --- a/web/apps/photos/src/services/fileService.ts +++ b/web/apps/photos/src/services/fileService.ts @@ -1,21 +1,20 @@ +import { getLocalFiles, setLocalFiles } from "@/new/photos/services/files"; +import { + EncryptedEnteFile, + EnteFile, + FileWithUpdatedMagicMetadata, + FileWithUpdatedPublicMagicMetadata, + TrashRequest, +} from "@/new/photos/types/file"; +import { BulkUpdateMagicMetadataRequest } from "@/new/photos/types/magicMetadata"; import log from "@/next/log"; import { apiOrigin } from "@/next/origins"; import ComlinkCryptoWorker from "@ente/shared/crypto"; -import { Events, eventBus } from "@ente/shared/events"; import HTTPService from "@ente/shared/network/HTTPService"; -import localForage from "@ente/shared/storage/localForage"; import { getToken } from "@ente/shared/storage/localStorage/helpers"; import { REQUEST_BATCH_SIZE } from "constants/api"; import { Collection } from "types/collection"; -import { - EncryptedEnteFile, - EnteFile, - FileWithUpdatedMagicMetadata, - FileWithUpdatedPublicMagicMetadata, - TrashRequest, -} from "types/file"; import { SetFiles } from "types/gallery"; -import { BulkUpdateMagicMetadataRequest } from "types/magicMetadata"; import { batch } from "utils/common"; import { decryptFile, @@ -28,52 +27,6 @@ import { setCollectionLastSyncTime, } from "./collectionService"; -const FILES_TABLE = "files"; -const HIDDEN_FILES_TABLE = "hidden-files"; - -/** - * Return all files that we know about locally, both "normal" and "hidden". - */ -export const getAllLocalFiles = async () => - [].concat(await getLocalFiles("normal"), await getLocalFiles("hidden")); - -/** - * Return all files that we know about locally. By default it returns only - * "normal" (i.e. non-"hidden") files, but it can be passed the {@link type} - * "hidden" to get it to instead return hidden files that we know about locally. - */ -export const getLocalFiles = async (type: "normal" | "hidden" = "normal") => { - const tableName = type === "normal" ? FILES_TABLE : HIDDEN_FILES_TABLE; - const files: Array = - (await localForage.getItem(tableName)) || []; - return files; -}; - -const setLocalFiles = async (type: "normal" | "hidden", files: EnteFile[]) => { - try { - const tableName = type === "normal" ? FILES_TABLE : HIDDEN_FILES_TABLE; - await localForage.setItem(tableName, files); - try { - eventBus.emit(Events.LOCAL_FILES_UPDATED); - } catch (e) { - log.error("Error in localFileUpdated handlers", e); - } - } catch (e1) { - try { - const storageEstimate = await navigator.storage.estimate(); - log.error( - `failed to save files to indexedDB (storageEstimate was ${storageEstimate}`, - e1, - ); - log.info(`storage estimate ${JSON.stringify(storageEstimate)}`); - } catch (e2) { - log.error("failed to save files to indexedDB", e1); - log.error("failed to get storage stats", e2); - } - throw e1; - } -}; - export const syncFiles = async ( type: "normal" | "hidden", collections: Collection[], diff --git a/web/apps/photos/src/services/fix-exif.ts b/web/apps/photos/src/services/fix-exif.ts index f47e4c5ed0..a695f8d3a0 100644 --- a/web/apps/photos/src/services/fix-exif.ts +++ b/web/apps/photos/src/services/fix-exif.ts @@ -1,9 +1,9 @@ import { FILE_TYPE } from "@/media/file-type"; +import { EnteFile } from "@/new/photos/types/file"; import log from "@/next/log"; import { validateAndGetCreationUnixTimeInMicroSeconds } from "@ente/shared/time"; import type { FixOption } from "components/FixCreationTime"; import { detectFileTypeInfo } from "services/detect-type"; -import { EnteFile } from "types/file"; import { changeFileCreationTime, updateExistingFilePubMetadata, diff --git a/web/apps/photos/src/services/machineLearning/machineLearningService.ts b/web/apps/photos/src/services/machineLearning/machineLearningService.ts index f28efe8b47..7dbadf9186 100644 --- a/web/apps/photos/src/services/machineLearning/machineLearningService.ts +++ b/web/apps/photos/src/services/machineLearning/machineLearningService.ts @@ -1,9 +1,9 @@ +import { EnteFile } from "@/new/photos/types/file"; import log from "@/next/log"; import { CustomError, parseUploadErrorCodes } from "@ente/shared/error"; import PQueue from "p-queue"; import { syncAndGetFilesToIndex } from "services/face/indexer"; import { FaceIndexerWorker } from "services/face/indexer.worker"; -import { EnteFile } from "types/file"; const batchSize = 200; diff --git a/web/apps/photos/src/services/publicCollectionService.ts b/web/apps/photos/src/services/publicCollectionService.ts index 9dced45610..d33d89fc41 100644 --- a/web/apps/photos/src/services/publicCollectionService.ts +++ b/web/apps/photos/src/services/publicCollectionService.ts @@ -1,3 +1,4 @@ +import { EncryptedEnteFile, EnteFile } from "@/new/photos/types/file"; import log from "@/next/log"; import { apiOrigin } from "@/next/origins"; import ComlinkCryptoWorker from "@ente/shared/crypto"; @@ -5,7 +6,6 @@ import { CustomError, parseSharingErrorCodes } from "@ente/shared/error"; import HTTPService from "@ente/shared/network/HTTPService"; import localForage from "@ente/shared/storage/localForage"; import { Collection, CollectionPublicMagicMetadata } from "types/collection"; -import { EncryptedEnteFile, EnteFile } from "types/file"; import { LocalSavedPublicCollectionFiles } from "types/publicCollection"; import { decryptFile, mergeMetadata, sortFiles } from "utils/file"; diff --git a/web/apps/photos/src/services/searchService.ts b/web/apps/photos/src/services/searchService.ts index 27786a2d3a..9fe1580e55 100644 --- a/web/apps/photos/src/services/searchService.ts +++ b/web/apps/photos/src/services/searchService.ts @@ -1,11 +1,11 @@ import { FILE_TYPE } from "@/media/file-type"; +import { EnteFile } from "@/new/photos/types/file"; import log from "@/next/log"; import * as chrono from "chrono-node"; import { t } from "i18next"; import type { Person } from "services/face/people"; import { Collection } from "types/collection"; import { EntityType, LocationTag, LocationTagData } from "types/entity"; -import { EnteFile } from "types/file"; import { ClipSearchScores, DateValue, diff --git a/web/apps/photos/src/services/trashService.ts b/web/apps/photos/src/services/trashService.ts index f367304550..cd5b2aa9b9 100644 --- a/web/apps/photos/src/services/trashService.ts +++ b/web/apps/photos/src/services/trashService.ts @@ -1,10 +1,10 @@ +import { EnteFile } from "@/new/photos/types/file"; import log from "@/next/log"; import { apiOrigin } from "@/next/origins"; import HTTPService from "@ente/shared/network/HTTPService"; import localForage from "@ente/shared/storage/localForage"; import { getToken } from "@ente/shared/storage/localStorage/helpers"; import { Collection } from "types/collection"; -import { EnteFile } from "types/file"; import { SetFiles } from "types/gallery"; import { EncryptedTrashItem, Trash } from "types/trash"; import { decryptFile, mergeMetadata, sortTrashFiles } from "utils/file"; diff --git a/web/apps/photos/src/services/upload/publicUploadHttpClient.ts b/web/apps/photos/src/services/upload/publicUploadHttpClient.ts index 22ed45e2f3..e0f0bac3ea 100644 --- a/web/apps/photos/src/services/upload/publicUploadHttpClient.ts +++ b/web/apps/photos/src/services/upload/publicUploadHttpClient.ts @@ -1,8 +1,8 @@ +import { EnteFile } from "@/new/photos/types/file"; import log from "@/next/log"; import { apiOrigin } from "@/next/origins"; import { CustomError, handleUploadError } from "@ente/shared/error"; import HTTPService from "@ente/shared/network/HTTPService"; -import { EnteFile } from "types/file"; import { retryHTTPCall } from "./uploadHttpClient"; import { MultipartUploadURLs, UploadFile, UploadURL } from "./uploadService"; diff --git a/web/apps/photos/src/services/upload/uploadHttpClient.ts b/web/apps/photos/src/services/upload/uploadHttpClient.ts index 6841c0c1e8..67e52c2143 100644 --- a/web/apps/photos/src/services/upload/uploadHttpClient.ts +++ b/web/apps/photos/src/services/upload/uploadHttpClient.ts @@ -1,10 +1,10 @@ +import { EnteFile } from "@/new/photos/types/file"; import log from "@/next/log"; import { apiOrigin, uploaderOrigin } from "@/next/origins"; import { wait } from "@/utils/promise"; import { CustomError, handleUploadError } from "@ente/shared/error"; import HTTPService from "@ente/shared/network/HTTPService"; import { getToken } from "@ente/shared/storage/localStorage/helpers"; -import { EnteFile } from "types/file"; import { MultipartUploadURLs, UploadFile, UploadURL } from "./uploadService"; const MAX_URL_REQUESTS = 50; diff --git a/web/apps/photos/src/services/upload/uploadManager.ts b/web/apps/photos/src/services/upload/uploadManager.ts index 150b85ba69..0560fe8010 100644 --- a/web/apps/photos/src/services/upload/uploadManager.ts +++ b/web/apps/photos/src/services/upload/uploadManager.ts @@ -1,5 +1,7 @@ import { FILE_TYPE } from "@/media/file-type"; import { potentialFileTypeFromExtension } from "@/media/live-photo"; +import { getLocalFiles } from "@/new/photos/services/files"; +import { EncryptedEnteFile, EnteFile } from "@/new/photos/types/file"; import { ensureElectron } from "@/next/electron"; import { lowercaseExtension, nameAndExtension } from "@/next/file"; import log from "@/next/log"; @@ -26,10 +28,8 @@ import { import { getDisableCFUploadProxyFlag } from "services/userService"; import watcher from "services/watch"; import { Collection } from "types/collection"; -import { EncryptedEnteFile, EnteFile } from "types/file"; import { SetFiles } from "types/gallery"; import { decryptFile, getUserOwnedFiles, sortFiles } from "utils/file"; -import { getLocalFiles } from "../fileService"; import { getMetadataJSONMapKeyForJSON, tryParseTakeoutMetadataJSON, diff --git a/web/apps/photos/src/services/upload/uploadService.ts b/web/apps/photos/src/services/upload/uploadService.ts index 7161eb530c..66c0dcf8da 100644 --- a/web/apps/photos/src/services/upload/uploadService.ts +++ b/web/apps/photos/src/services/upload/uploadService.ts @@ -2,6 +2,15 @@ import { hasFileHash } from "@/media/file"; import { FILE_TYPE, type FileTypeInfo } from "@/media/file-type"; import { encodeLivePhoto } from "@/media/live-photo"; import type { Metadata } from "@/media/types/file"; +import { + EnteFile, + MetadataFileAttributes, + S3FileAttributes, + type EncryptedEnteFile, + type FilePublicMagicMetadata, + type FilePublicMagicMetadataProps, +} from "@/new/photos/types/file"; +import { EncryptedMagicMetadata } from "@/new/photos/types/magicMetadata"; import { ensureElectron } from "@/next/electron"; import { basename } from "@/next/file"; import log from "@/next/log"; @@ -24,15 +33,6 @@ import { PublicUploadProps, type LivePhotoAssets, } from "services/upload/uploadManager"; -import { - EnteFile, - MetadataFileAttributes, - S3FileAttributes, - type EncryptedEnteFile, - type FilePublicMagicMetadata, - type FilePublicMagicMetadataProps, -} from "types/file"; -import { EncryptedMagicMetadata } from "types/magicMetadata"; import type { ParsedExtractedMetadata } from "types/metadata"; import { getNonEmptyMagicMetadataProps, diff --git a/web/apps/photos/src/services/watch.ts b/web/apps/photos/src/services/watch.ts index 42ce245fa4..e9f7f33005 100644 --- a/web/apps/photos/src/services/watch.ts +++ b/web/apps/photos/src/services/watch.ts @@ -3,6 +3,8 @@ * watch folders functionality. */ +import { getLocalFiles } from "@/new/photos/services/files"; +import { EncryptedEnteFile } from "@/new/photos/types/file"; import { ensureElectron } from "@/next/electron"; import { basename, dirname } from "@/next/file"; import log from "@/next/log"; @@ -18,10 +20,8 @@ import uploadManager, { type UploadItemWithCollection, } from "services/upload/uploadManager"; import { Collection } from "types/collection"; -import { EncryptedEnteFile } from "types/file"; import { groupFilesBasedOnCollectionID } from "utils/file"; import { removeFromCollection } from "./collectionService"; -import { getLocalFiles } from "./fileService"; /** * Watch for file system folders and automatically update the corresponding Ente diff --git a/web/apps/photos/src/types/collection/index.ts b/web/apps/photos/src/types/collection/index.ts index 91b96281c7..1321451b2e 100644 --- a/web/apps/photos/src/types/collection/index.ts +++ b/web/apps/photos/src/types/collection/index.ts @@ -1,11 +1,11 @@ -import { CollectionSummaryType, CollectionType } from "constants/collection"; -import { EnteFile } from "types/file"; +import { EnteFile } from "@/new/photos/types/file"; import { EncryptedMagicMetadata, MagicMetadataCore, SUB_TYPE, VISIBILITY_STATE, -} from "types/magicMetadata"; +} from "@/new/photos/types/magicMetadata"; +import { CollectionSummaryType, CollectionType } from "constants/collection"; export enum COLLECTION_ROLE { VIEWER = "VIEWER", diff --git a/web/apps/photos/src/types/export/index.ts b/web/apps/photos/src/types/export/index.ts index 64ef249eda..6767c3e869 100644 --- a/web/apps/photos/src/types/export/index.ts +++ b/web/apps/photos/src/types/export/index.ts @@ -1,5 +1,5 @@ +import { EnteFile } from "@/new/photos/types/file"; import type { ExportStage } from "services/export"; -import { EnteFile } from "types/file"; export interface ExportProgress { success: number; diff --git a/web/apps/photos/src/types/gallery/index.ts b/web/apps/photos/src/types/gallery/index.ts index f69b6c24b6..1f95e78a19 100644 --- a/web/apps/photos/src/types/gallery/index.ts +++ b/web/apps/photos/src/types/gallery/index.ts @@ -1,9 +1,9 @@ +import { EnteFile } from "@/new/photos/types/file"; import type { User } from "@ente/shared/user/types"; import { CollectionSelectorAttributes } from "components/Collections/CollectionSelector"; import { FilesDownloadProgressAttributes } from "components/FilesDownloadProgress"; import { TimeStampListItem } from "components/PhotoList"; import { Collection } from "types/collection"; -import { EnteFile } from "types/file"; export type SelectedState = { [k: number]: boolean; diff --git a/web/apps/photos/src/types/publicCollection/index.ts b/web/apps/photos/src/types/publicCollection/index.ts index 11ee4a2d12..95c5607584 100644 --- a/web/apps/photos/src/types/publicCollection/index.ts +++ b/web/apps/photos/src/types/publicCollection/index.ts @@ -1,6 +1,6 @@ +import { EnteFile } from "@/new/photos/types/file"; import { TimeStampListItem } from "components/PhotoList"; import { PublicURL } from "types/collection"; -import { EnteFile } from "types/file"; export interface PublicCollectionGalleryContextType { token: string; diff --git a/web/apps/photos/src/types/search/index.ts b/web/apps/photos/src/types/search/index.ts index adeb03d3aa..0e18d787ab 100644 --- a/web/apps/photos/src/types/search/index.ts +++ b/web/apps/photos/src/types/search/index.ts @@ -1,9 +1,9 @@ import { FILE_TYPE } from "@/media/file-type"; +import { EnteFile } from "@/new/photos/types/file"; import type { FaceIndexingStatus } from "services/face/indexer"; import type { Person } from "services/face/people"; import { City } from "services/locationSearchService"; import { LocationTagData } from "types/entity"; -import { EnteFile } from "types/file"; export enum SuggestionType { DATE = "DATE", diff --git a/web/apps/photos/src/types/trash/index.ts b/web/apps/photos/src/types/trash/index.ts index d7e231c1a9..df786029a1 100644 --- a/web/apps/photos/src/types/trash/index.ts +++ b/web/apps/photos/src/types/trash/index.ts @@ -1,4 +1,4 @@ -import { EncryptedEnteFile, EnteFile } from "types/file"; +import { EncryptedEnteFile, EnteFile } from "@/new/photos/types/file"; export interface TrashItem extends Omit { file: EnteFile; diff --git a/web/apps/photos/src/utils/collection/index.ts b/web/apps/photos/src/utils/collection/index.ts index 212cff7b1c..12fffe6bfa 100644 --- a/web/apps/photos/src/utils/collection/index.ts +++ b/web/apps/photos/src/utils/collection/index.ts @@ -1,3 +1,6 @@ +import { getAllLocalFiles, getLocalFiles } from "@/new/photos/services/files"; +import { EnteFile } from "@/new/photos/types/file"; +import { SUB_TYPE, VISIBILITY_STATE } from "@/new/photos/types/magicMetadata"; import { ensureElectron } from "@/next/electron"; import log from "@/next/log"; import { CustomError } from "@ente/shared/error"; @@ -30,7 +33,6 @@ import { updatePublicCollectionMagicMetadata, updateSharedCollectionMagicMetadata, } from "services/collectionService"; -import { getAllLocalFiles, getLocalFiles } from "services/fileService"; import { COLLECTION_ROLE, Collection, @@ -38,9 +40,7 @@ import { CollectionPublicMagicMetadataProps, CollectionSummaries, } from "types/collection"; -import { EnteFile } from "types/file"; import { SetFilesDownloadProgressAttributes } from "types/gallery"; -import { SUB_TYPE, VISIBILITY_STATE } from "types/magicMetadata"; import { downloadFilesWithProgress } from "utils/file"; import { isArchivedCollection, updateMagicMetadata } from "utils/magicMetadata"; import { safeDirectoryName } from "utils/native-fs"; diff --git a/web/apps/photos/src/utils/file/index.ts b/web/apps/photos/src/utils/file/index.ts index 5e2c60cee2..a6793a3386 100644 --- a/web/apps/photos/src/utils/file/index.ts +++ b/web/apps/photos/src/utils/file/index.ts @@ -2,6 +2,16 @@ import { FILE_TYPE } from "@/media/file-type"; import { isNonWebImageFileExtension } from "@/media/formats"; import { heicToJPEG } from "@/media/heic-convert"; import { decodeLivePhoto } from "@/media/live-photo"; +import { + EncryptedEnteFile, + EnteFile, + FileMagicMetadata, + FileMagicMetadataProps, + FilePublicMagicMetadata, + FilePublicMagicMetadataProps, + FileWithUpdatedMagicMetadata, +} from "@/new/photos/types/file"; +import { VISIBILITY_STATE } from "@/new/photos/types/magicMetadata"; import { lowercaseExtension } from "@/next/file"; import log from "@/next/log"; import { CustomErrorMessage, type Electron } from "@/next/types/ipc"; @@ -23,21 +33,11 @@ import { updateFileMagicMetadata, updateFilePublicMagicMetadata, } from "services/fileService"; -import { - EncryptedEnteFile, - EnteFile, - FileMagicMetadata, - FileMagicMetadataProps, - FilePublicMagicMetadata, - FilePublicMagicMetadataProps, - FileWithUpdatedMagicMetadata, -} from "types/file"; import { SelectedState, SetFilesDownloadProgressAttributes, SetFilesDownloadProgressAttributesCreator, } from "types/gallery"; -import { VISIBILITY_STATE } from "types/magicMetadata"; import { isArchivedFile, updateMagicMetadata } from "utils/magicMetadata"; import { safeFileName } from "utils/native-fs"; import { writeStream } from "utils/native-stream"; diff --git a/web/apps/photos/src/utils/magicMetadata/index.ts b/web/apps/photos/src/utils/magicMetadata/index.ts index cce41791e2..8d94a574f9 100644 --- a/web/apps/photos/src/utils/magicMetadata/index.ts +++ b/web/apps/photos/src/utils/magicMetadata/index.ts @@ -1,7 +1,10 @@ +import { EnteFile } from "@/new/photos/types/file"; +import { + MagicMetadataCore, + VISIBILITY_STATE, +} from "@/new/photos/types/magicMetadata"; import ComlinkCryptoWorker from "@ente/shared/crypto"; import { Collection } from "types/collection"; -import { EnteFile } from "types/file"; -import { MagicMetadataCore, VISIBILITY_STATE } from "types/magicMetadata"; export function isArchivedFile(item: EnteFile): boolean { if (!item || !item.magicMetadata || !item.magicMetadata.data) { diff --git a/web/apps/photos/src/utils/photoFrame/index.ts b/web/apps/photos/src/utils/photoFrame/index.ts index 93b680149f..8049d8ba01 100644 --- a/web/apps/photos/src/utils/photoFrame/index.ts +++ b/web/apps/photos/src/utils/photoFrame/index.ts @@ -1,7 +1,7 @@ import { FILE_TYPE } from "@/media/file-type"; +import type { LivePhotoSourceURL, SourceURLs } from "@/new/photos/types/file"; +import { EnteFile } from "@/new/photos/types/file"; import log from "@/next/log"; -import { LivePhotoSourceURL, SourceURLs } from "services/download"; -import { EnteFile } from "types/file"; import { SetSelectedState } from "types/gallery"; export async function playVideo(livePhotoVideo, livePhotoImage) { diff --git a/web/apps/photos/src/worker/search.worker.ts b/web/apps/photos/src/worker/search.worker.ts index 2667c0c850..f81aecfb96 100644 --- a/web/apps/photos/src/worker/search.worker.ts +++ b/web/apps/photos/src/worker/search.worker.ts @@ -1,9 +1,9 @@ +import { EnteFile } from "@/new/photos/types/file"; import * as Comlink from "comlink"; import { isInsideCity, isInsideLocationTag, } from "services/locationSearchService"; -import { EnteFile } from "types/file"; import { Search } from "types/search"; import { isSameDayAnyYear } from "utils/search"; diff --git a/web/apps/photos/tests/upload.test.ts b/web/apps/photos/tests/upload.test.ts index c4d76d5240..d839af637e 100644 --- a/web/apps/photos/tests/upload.test.ts +++ b/web/apps/photos/tests/upload.test.ts @@ -1,6 +1,6 @@ import { FILE_TYPE } from "@/media/file-type"; +import { getLocalFiles } from "@/new/photos/services/files"; import { getLocalCollections } from "services/collectionService"; -import { getLocalFiles } from "services/fileService"; import { tryToParseDateTime } from "services/upload/date"; import { MAX_FILE_NAME_LENGTH_GOOGLE_EXPORT, diff --git a/web/apps/photos/tsconfig.json b/web/apps/photos/tsconfig.json index cfe85e6d70..779ac7aa6c 100644 --- a/web/apps/photos/tsconfig.json +++ b/web/apps/photos/tsconfig.json @@ -22,7 +22,9 @@ "**/*.tsx", "**/*.js", "../../packages/shared/themes/mui-theme.d.ts", - "../../packages/next/global-electron.d.ts" + "../../packages/next/global-electron.d.ts", + "../../packages/new/photos/types/file.ts", + "../../packages/new/photos/types/magicMetadata.ts" ], "exclude": ["node_modules", "out", ".next", "thirdparty"] } diff --git a/web/packages/new/photos/services/embedding.ts b/web/packages/new/photos/services/embedding.ts index b6bf93b690..e20515e0a9 100644 --- a/web/packages/new/photos/services/embedding.ts +++ b/web/packages/new/photos/services/embedding.ts @@ -1,7 +1,9 @@ import { authenticatedRequestHeaders } from "@/next/http"; import { apiOrigin } from "@/next/origins"; import { nullToUndefined } from "@/utils/transform"; +// import ComlinkCryptoWorker from "@ente/shared/crypto"; import { z } from "zod"; +// import { getAllLocalFiles } from "./files"; /** * The embeddings that we (the current client) knows how to handle. @@ -79,18 +81,24 @@ type RemoteEmbedding = z.infer; */ export const syncRemoteFaceEmbeddings = async () => { let sinceTime = faceEmbeddingSyncTime(); + // const cryptoWorker = await ComlinkCryptoWorker.getInstance(); + // const files = await getAllLocalFiles(); + // TODO: eslint has fixed this spurious warning, but we're not on the latest // version yet, so add a disable. // https://github.com/eslint/eslint/pull/18286 /* eslint-disable no-constant-condition */ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition while (true) { - const embeddings = await getEmbeddingsDiff( + const remoteEmbeddings = await getEmbeddingsDiff( "file-ml-clip-face", sinceTime, ); - if (embeddings.length == 0) break; - sinceTime = embeddings.reduce( + if (remoteEmbeddings.length == 0) break; + // const _embeddings = Promise.all( + // remoteEmbeddings.map(decryptFaceEmbedding), + // ); + sinceTime = remoteEmbeddings.reduce( (max, { updatedAt }) => Math.max(max, updatedAt), sinceTime, ); @@ -98,6 +106,22 @@ export const syncRemoteFaceEmbeddings = async () => { } }; +// const decryptFaceEmbedding = async (remoteEmbedding: RemoteEmbedding) => { +// const fileKey = fileIdToKeyMap.get(embedding.fileID); +// if (!fileKey) { +// throw Error(CustomError.FILE_NOT_FOUND); +// } +// const decryptedData = await worker.decryptMetadata( +// embedding.encryptedEmbedding, +// embedding.decryptionHeader, +// fileIdToKeyMap.get(embedding.fileID), +// ); +// return { +// ...decryptedData, +// updatedAt: embedding.updatedAt, +// } as unknown as FileML; +// }; + /** * The updatedAt of the most recent face {@link RemoteEmbedding} we've retrieved * and saved from remote, or 0. diff --git a/web/packages/new/photos/services/files.ts b/web/packages/new/photos/services/files.ts new file mode 100644 index 0000000000..d5a17b4e72 --- /dev/null +++ b/web/packages/new/photos/services/files.ts @@ -0,0 +1,38 @@ +import { type EnteFile } from "@/new/photos/types/file"; +import log from "@/next/log"; +import { Events, eventBus } from "@ente/shared/events"; +import localForage from "@ente/shared/storage/localForage"; + +const FILES_TABLE = "files"; +const HIDDEN_FILES_TABLE = "hidden-files"; + +/** + * Return all files that we know about locally, both "normal" and "hidden". + */ +export const getAllLocalFiles = async () => + (await getLocalFiles("normal")).concat(await getLocalFiles("hidden")); + +/** + * Return all files that we know about locally. By default it returns only + * "normal" (i.e. non-"hidden") files, but it can be passed the {@link type} + * "hidden" to get it to instead return hidden files that we know about locally. + */ +export const getLocalFiles = async (type: "normal" | "hidden" = "normal") => { + const tableName = type === "normal" ? FILES_TABLE : HIDDEN_FILES_TABLE; + const files: EnteFile[] = + (await localForage.getItem(tableName)) ?? []; + return files; +}; + +export const setLocalFiles = async ( + type: "normal" | "hidden", + files: EnteFile[], +) => { + const tableName = type === "normal" ? FILES_TABLE : HIDDEN_FILES_TABLE; + await localForage.setItem(tableName, files); + try { + eventBus.emit(Events.LOCAL_FILES_UPDATED); + } catch (e) { + log.error("Failed to save files", e); + } +}; diff --git a/web/apps/photos/src/types/file/index.ts b/web/packages/new/photos/types/file.ts similarity index 74% rename from web/apps/photos/src/types/file/index.ts rename to web/packages/new/photos/types/file.ts index c3d4cca440..1993ff9e8b 100644 --- a/web/apps/photos/src/types/file/index.ts +++ b/web/packages/new/photos/types/file.ts @@ -1,10 +1,9 @@ import type { Metadata } from "@/media/types/file"; -import { SourceURLs } from "services/download"; import { - EncryptedMagicMetadata, - MagicMetadataCore, VISIBILITY_STATE, -} from "types/magicMetadata"; + type EncryptedMagicMetadata, + type MagicMetadataCore, +} from "./magicMetadata"; export interface MetadataFileAttributes { encryptedData: string; @@ -63,6 +62,33 @@ export interface EnteFile isConverted?: boolean; } +export interface LivePhotoSourceURL { + image: () => Promise; + video: () => Promise; +} + +export interface LoadedLivePhotoSourceURL { + image: string; + video: string; +} + +export interface SourceURLs { + url: string | LivePhotoSourceURL | LoadedLivePhotoSourceURL; + isOriginal: boolean; + isRenderable: boolean; + type: "normal" | "livePhoto"; + /** + * Best effort attempt at obtaining the MIME type. + * + * Known cases where it is missing: + * + * - Live photos (these have a different code path for obtaining the URL). + * - A video that is passes the isPlayable test in the browser. + * + */ + mimeType?: string; +} + export interface TrashRequest { items: TrashRequestItems[]; } diff --git a/web/apps/photos/src/types/magicMetadata/index.ts b/web/packages/new/photos/types/magicMetadata.ts similarity index 100% rename from web/apps/photos/src/types/magicMetadata/index.ts rename to web/packages/new/photos/types/magicMetadata.ts From bfa4806d47278c6aa86e9e315482e6ea3e916a50 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 26 Jun 2024 15:08:37 +0530 Subject: [PATCH 8/8] Remove unnecessary automatically made edits --- web/apps/photos/tsconfig.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/web/apps/photos/tsconfig.json b/web/apps/photos/tsconfig.json index 779ac7aa6c..cfe85e6d70 100644 --- a/web/apps/photos/tsconfig.json +++ b/web/apps/photos/tsconfig.json @@ -22,9 +22,7 @@ "**/*.tsx", "**/*.js", "../../packages/shared/themes/mui-theme.d.ts", - "../../packages/next/global-electron.d.ts", - "../../packages/new/photos/types/file.ts", - "../../packages/new/photos/types/magicMetadata.ts" + "../../packages/next/global-electron.d.ts" ], "exclude": ["node_modules", "out", ".next", "thirdparty"] }