Neeraj Gupta il y a 1 an
Parent
commit
ac12ce7a19

+ 0 - 1367
apps/cast/src/services/collectionService.ts

@@ -1,1367 +0,0 @@
-import { getActualKey } from '@ente/shared/user';
-import { batch } from '@ente/shared/batch';
-import { EnteFile } from 'types/file';
-import {
-    sortFiles,
-    groupFilesBasedOnCollectionID,
-    getUniqueFiles,
-} from 'utils/file';
-import {
-    Collection,
-    CollectionToFileMap,
-    AddToCollectionRequest,
-    MoveToCollectionRequest,
-    EncryptedFileKey,
-    CreatePublicAccessTokenRequest,
-    PublicURL,
-    UpdatePublicURL,
-    CollectionSummaries,
-    CollectionSummary,
-    CollectionFilesCount,
-    EncryptedCollection,
-    CollectionMagicMetadata,
-    CollectionMagicMetadataProps,
-    CollectionPublicMagicMetadata,
-    RemoveFromCollectionRequest,
-    CollectionShareeMagicMetadata,
-} from 'types/collection';
-import {
-    COLLECTION_LIST_SORT_BY,
-    CollectionType,
-    ARCHIVE_SECTION,
-    TRASH_SECTION,
-    COLLECTION_SORT_ORDER,
-    ALL_SECTION,
-    CollectionSummaryType,
-    DUMMY_UNCATEGORIZED_COLLECTION,
-    HIDDEN_ITEMS_SECTION,
-} from 'constants/collection';
-import { SUB_TYPE, UpdateMagicMetadataRequest } from 'types/magicMetadata';
-import {
-    isArchivedCollection,
-    isArchivedFile,
-    isPinnedCollection,
-    updateMagicMetadata,
-} from 'utils/magicMetadata';
-import {
-    isQuickLinkCollection,
-    isOutgoingShare,
-    isSharedOnlyViaLink,
-    isValidMoveTarget,
-    isHiddenCollection,
-    getNonHiddenCollections,
-    changeCollectionSubType,
-    isIncomingCollabShare,
-    isIncomingShare,
-    isDefaultHiddenCollection,
-    getHiddenCollections,
-} from 'utils/collection';
-import ComlinkCryptoWorker from 'utils/comlink/ComlinkCryptoWorker';
-import { getLocalFiles } from './fileService';
-import { REQUEST_BATCH_SIZE } from 'constants/api';
-import { t } from 'i18next';
-import { EncryptedMagicMetadata } from 'types/magicMetadata';
-import { VISIBILITY_STATE } from 'types/magicMetadata';
-import { getEndpoint } from '@ente/shared/network/api';
-import { getToken } from '@ente/shared/storage/localStorage/helpers';
-import HTTPService from '@ente/shared/network/HTTPService';
-import { logError } from '@ente/shared/sentry';
-import { LS_KEYS, getData } from '@ente/shared/storage/localStorage';
-import localForage from '@ente/shared/storage/localForage';
-import { User } from '@ente/shared/user/types';
-
-const ENDPOINT = getEndpoint();
-const COLLECTION_TABLE = 'collections';
-const COLLECTION_UPDATION_TIME = 'collection-updation-time';
-const HIDDEN_COLLECTION_IDS = 'hidden-collection-ids';
-
-const UNCATEGORIZED_COLLECTION_NAME = 'Uncategorized';
-export const HIDDEN_COLLECTION_NAME = '.hidden';
-
-export const getCollectionLastSyncTime = async (collection: Collection) =>
-    (await localForage.getItem<number>(`${collection.id}-time`)) ?? 0;
-
-export const setCollectionLastSyncTime = async (
-    collection: Collection,
-    time: number
-) => await localForage.setItem<number>(`${collection.id}-time`, time);
-
-export const removeCollectionLastSyncTime = async (collection: Collection) =>
-    await localForage.removeItem(`${collection.id}-time`);
-
-const getCollectionWithSecrets = async (
-    collection: EncryptedCollection,
-    key: string,
-    type: 'master' | 'collection' = 'master'
-): Promise<Collection> => {
-    const cryptoWorker = await ComlinkCryptoWorker.getInstance();
-    const userID = getData(LS_KEYS.USER).id;
-    let collectionKey: string;
-    if (type === 'master') {
-        if (collection.owner.id === userID) {
-            collectionKey = await cryptoWorker.decryptB64(
-                collection.encryptedKey,
-                collection.keyDecryptionNonce,
-                key
-            );
-        } else {
-            const keyAttributes = getData(LS_KEYS.KEY_ATTRIBUTES);
-            const secretKey = await cryptoWorker.decryptB64(
-                keyAttributes.encryptedSecretKey,
-                keyAttributes.secretKeyDecryptionNonce,
-                key
-            );
-            collectionKey = await cryptoWorker.boxSealOpen(
-                collection.encryptedKey,
-                keyAttributes.publicKey,
-                secretKey
-            );
-        }
-    } else {
-        collectionKey = key;
-    }
-    const collectionName =
-        collection.name ||
-        (await cryptoWorker.decryptToUTF8(
-            collection.encryptedName,
-            collection.nameDecryptionNonce,
-            collectionKey
-        ));
-
-    let collectionMagicMetadata: CollectionMagicMetadata;
-    if (collection.magicMetadata?.data) {
-        collectionMagicMetadata = {
-            ...collection.magicMetadata,
-            data: await cryptoWorker.decryptMetadata(
-                collection.magicMetadata.data,
-                collection.magicMetadata.header,
-                collectionKey
-            ),
-        };
-    }
-    let collectionPublicMagicMetadata: CollectionPublicMagicMetadata;
-    if (collection.pubMagicMetadata?.data) {
-        collectionPublicMagicMetadata = {
-            ...collection.pubMagicMetadata,
-            data: await cryptoWorker.decryptMetadata(
-                collection.pubMagicMetadata.data,
-                collection.pubMagicMetadata.header,
-                collectionKey
-            ),
-        };
-    }
-
-    let collectionShareeMagicMetadata: CollectionShareeMagicMetadata;
-    if (collection.sharedMagicMetadata?.data) {
-        collectionShareeMagicMetadata = {
-            ...collection.sharedMagicMetadata,
-            data: await cryptoWorker.decryptMetadata(
-                collection.sharedMagicMetadata.data,
-                collection.sharedMagicMetadata.header,
-                collectionKey
-            ),
-        };
-    }
-
-    return {
-        ...collection,
-        name: collectionName,
-        key: collectionKey,
-        magicMetadata: collectionMagicMetadata,
-        pubMagicMetadata: collectionPublicMagicMetadata,
-        sharedMagicMetadata: collectionShareeMagicMetadata,
-    };
-};
-
-const getCollections = async (
-    token: string,
-    sinceTime: number,
-    key: string
-): Promise<Collection[]> => {
-    try {
-        const resp = await HTTPService.get(
-            `${ENDPOINT}/collections/v2`,
-            {
-                sinceTime,
-            },
-            { 'X-Auth-Token': token }
-        );
-        const decryptedCollections: Collection[] = await Promise.all(
-            resp.data.collections.map(
-                async (collection: EncryptedCollection) => {
-                    if (collection.isDeleted) {
-                        return collection;
-                    }
-                    try {
-                        return await getCollectionWithSecrets(collection, key);
-                    } catch (e) {
-                        logError(e, `decryption failed for collection`, {
-                            collectionID: collection.id,
-                        });
-                        return collection;
-                    }
-                }
-            )
-        );
-        // only allow deleted or collection with key, filtering out collection whose decryption failed
-        const collections = decryptedCollections.filter(
-            (collection) => collection.isDeleted || collection.key
-        );
-        return collections;
-    } catch (e) {
-        logError(e, 'getCollections failed');
-        throw e;
-    }
-};
-
-export const getLocalCollections = async (
-    type: 'normal' | 'hidden' = 'normal'
-): Promise<Collection[]> => {
-    const collections = await getAllLocalCollections();
-    return type === 'normal'
-        ? getNonHiddenCollections(collections)
-        : getHiddenCollections(collections);
-};
-
-export const getAllLocalCollections = async (): Promise<Collection[]> => {
-    const collections: Collection[] =
-        (await localForage.getItem(COLLECTION_TABLE)) ?? [];
-    return collections;
-};
-
-export const getCollectionUpdationTime = async (): Promise<number> =>
-    (await localForage.getItem<number>(COLLECTION_UPDATION_TIME)) ?? 0;
-
-export const getHiddenCollectionIDs = async (): Promise<number[]> =>
-    (await localForage.getItem<number[]>(HIDDEN_COLLECTION_IDS)) ?? [];
-
-export const getLatestCollections = async (
-    type: 'normal' | 'hidden' = 'normal'
-): Promise<Collection[]> => {
-    const collections = await getAllLatestCollections();
-    return type === 'normal'
-        ? getNonHiddenCollections(collections)
-        : getHiddenCollections(collections);
-};
-export const getAllLatestCollections = async (): Promise<Collection[]> => {
-    const collections = await syncCollections();
-    return collections;
-};
-
-export const syncCollections = async () => {
-    const localCollections = await getAllLocalCollections();
-    let lastCollectionUpdationTime = await getCollectionUpdationTime();
-    const hiddenCollectionIDs = await getHiddenCollectionIDs();
-    const token = getToken();
-    const key = await getActualKey();
-    const updatedCollections =
-        (await getCollections(token, lastCollectionUpdationTime, key)) ?? [];
-    if (updatedCollections.length === 0) {
-        return localCollections;
-    }
-    const allCollectionsInstances = [
-        ...localCollections,
-        ...updatedCollections,
-    ];
-    const latestCollectionsInstances = new Map<number, Collection>();
-    allCollectionsInstances.forEach((collection) => {
-        if (
-            !latestCollectionsInstances.has(collection.id) ||
-            latestCollectionsInstances.get(collection.id).updationTime <
-                collection.updationTime
-        ) {
-            latestCollectionsInstances.set(collection.id, collection);
-        }
-    });
-
-    const collections: Collection[] = [];
-    // eslint-disable-next-line @typescript-eslint/no-unused-vars
-    for (const [_, collection] of latestCollectionsInstances) {
-        const isDeletedCollection = collection.isDeleted;
-        const isNewlyHiddenCollection =
-            isHiddenCollection(collection) &&
-            !hiddenCollectionIDs.includes(collection.id);
-        const isNewlyUnHiddenCollection =
-            !isHiddenCollection(collection) &&
-            hiddenCollectionIDs.includes(collection.id);
-        if (
-            isDeletedCollection ||
-            isNewlyHiddenCollection ||
-            isNewlyUnHiddenCollection
-        ) {
-            removeCollectionLastSyncTime(collection);
-        }
-        if (isDeletedCollection) {
-            continue;
-        }
-        collections.push(collection);
-        lastCollectionUpdationTime = Math.max(
-            lastCollectionUpdationTime,
-            collection.updationTime
-        );
-    }
-
-    const updatedHiddenCollectionIDs = collections
-        .filter((collection) => isHiddenCollection(collection))
-        .map((collection) => collection.id);
-
-    await localForage.setItem(COLLECTION_TABLE, collections);
-    await localForage.setItem(
-        COLLECTION_UPDATION_TIME,
-        lastCollectionUpdationTime
-    );
-    await localForage.setItem(
-        HIDDEN_COLLECTION_IDS,
-        updatedHiddenCollectionIDs
-    );
-    return collections;
-};
-
-export const getCollection = async (
-    collectionID: number
-): Promise<Collection> => {
-    try {
-        const token = getToken();
-        if (!token) {
-            return;
-        }
-        const resp = await HTTPService.get(
-            `${ENDPOINT}/collections/${collectionID}`,
-            null,
-            { 'X-Auth-Token': token }
-        );
-        const key = await getActualKey();
-        const collectionWithSecrets = await getCollectionWithSecrets(
-            resp.data?.collection,
-            key
-        );
-        return collectionWithSecrets;
-    } catch (e) {
-        logError(e, 'failed to get collection');
-        throw e;
-    }
-};
-
-export const getCollectionWithKey = async (
-    collectionID: number,
-    key: string,
-    tokenType: 'user' | 'cast' = 'user',
-    castToken?: string
-): Promise<Collection> => {
-    try {
-        let token;
-        if (tokenType === 'user') {
-            token = getToken();
-        } else {
-            token = castToken!;
-        }
-        if (!token) {
-            return;
-        }
-        const resp = await HTTPService.get(
-            `${ENDPOINT}/collections/${collectionID}`,
-            null,
-            { 'X-Auth-Token': token }
-        );
-        const collectionWithSecrets = await getCollectionWithSecrets(
-            resp.data?.collection,
-            key,
-            'collection'
-        );
-        return collectionWithSecrets;
-    } catch (e) {
-        logError(e, 'failed to get collection');
-        throw e;
-    }
-};
-
-export const getCollectionLatestFiles = (
-    files: EnteFile[]
-): CollectionToFileMap => {
-    const latestFiles = new Map<number, EnteFile>();
-
-    files.forEach((file) => {
-        if (!latestFiles.has(file.collectionID)) {
-            latestFiles.set(file.collectionID, file);
-        }
-    });
-    return latestFiles;
-};
-
-export const getCollectionCoverFiles = (
-    files: EnteFile[],
-    collections: Collection[]
-): CollectionToFileMap => {
-    const collectionIDToFileMap = groupFilesBasedOnCollectionID(files);
-
-    const coverFiles = new Map<number, EnteFile>();
-
-    collections.forEach((collection) => {
-        const collectionFiles = collectionIDToFileMap.get(collection.id);
-        if (!collectionFiles || collectionFiles.length === 0) {
-            return;
-        }
-        const coverID = collection.pubMagicMetadata?.data?.coverID;
-        if (typeof coverID === 'number' && coverID > 0) {
-            const coverFile = collectionFiles.find(
-                (file) => file.id === coverID
-            );
-            if (coverFile) {
-                coverFiles.set(collection.id, coverFile);
-                return;
-            }
-        }
-        if (collection.pubMagicMetadata?.data?.asc) {
-            coverFiles.set(
-                collection.id,
-                collectionFiles[collectionFiles.length - 1]
-            );
-        } else {
-            coverFiles.set(collection.id, collectionFiles[0]);
-        }
-    });
-    return coverFiles;
-};
-
-export const getFavItemIds = async (
-    files: EnteFile[]
-): Promise<Set<number>> => {
-    const favCollection = await getFavCollection();
-    if (!favCollection) return new Set();
-
-    return new Set(
-        files
-            .filter((file) => file.collectionID === favCollection.id)
-            .map((file): number => file.id)
-    );
-};
-
-export const createAlbum = (albumName: string) => {
-    return createCollection(albumName, CollectionType.album);
-};
-
-const createCollection = async (
-    collectionName: string,
-    type: CollectionType,
-    magicMetadataProps?: CollectionMagicMetadataProps
-): Promise<Collection> => {
-    try {
-        const cryptoWorker = await ComlinkCryptoWorker.getInstance();
-        const encryptionKey = await getActualKey();
-        const token = getToken();
-        const collectionKey = await cryptoWorker.generateEncryptionKey();
-        const { encryptedData: encryptedKey, nonce: keyDecryptionNonce } =
-            await cryptoWorker.encryptToB64(collectionKey, encryptionKey);
-        const { encryptedData: encryptedName, nonce: nameDecryptionNonce } =
-            await cryptoWorker.encryptUTF8(collectionName, collectionKey);
-        let encryptedMagicMetadata: EncryptedMagicMetadata;
-        if (magicMetadataProps) {
-            const magicMetadata = await updateMagicMetadata(magicMetadataProps);
-            const { file: encryptedMagicMetadataProps } =
-                await cryptoWorker.encryptMetadata(
-                    magicMetadataProps,
-                    collectionKey
-                );
-
-            encryptedMagicMetadata = {
-                ...magicMetadata,
-                data: encryptedMagicMetadataProps.encryptedData,
-                header: encryptedMagicMetadataProps.decryptionHeader,
-            };
-        }
-        const newCollection: EncryptedCollection = {
-            id: null,
-            owner: null,
-            encryptedKey,
-            keyDecryptionNonce,
-            encryptedName,
-            nameDecryptionNonce,
-            type,
-            attributes: {},
-            sharees: null,
-            updationTime: null,
-            isDeleted: false,
-            magicMetadata: encryptedMagicMetadata,
-            pubMagicMetadata: null,
-            sharedMagicMetadata: null,
-        };
-        const createdCollection = await postCollection(newCollection, token);
-        const decryptedCreatedCollection = await getCollectionWithSecrets(
-            createdCollection,
-            encryptionKey
-        );
-        return decryptedCreatedCollection;
-    } catch (e) {
-        logError(e, 'create collection failed');
-        throw e;
-    }
-};
-
-const postCollection = async (
-    collectionData: EncryptedCollection,
-    token: string
-): Promise<EncryptedCollection> => {
-    try {
-        const response = await HTTPService.post(
-            `${ENDPOINT}/collections`,
-            collectionData,
-            null,
-            { 'X-Auth-Token': token }
-        );
-        return response.data.collection;
-    } catch (e) {
-        logError(e, 'post Collection failed ');
-    }
-};
-
-export const addToCollection = async (
-    collection: Collection,
-    files: EnteFile[]
-) => {
-    try {
-        const token = getToken();
-        const batchedFiles = batch(files, REQUEST_BATCH_SIZE);
-        for (const batch of batchedFiles) {
-            const fileKeysEncryptedWithNewCollection =
-                await encryptWithNewCollectionKey(collection, batch);
-
-            const requestBody: AddToCollectionRequest = {
-                collectionID: collection.id,
-                files: fileKeysEncryptedWithNewCollection,
-            };
-            await HTTPService.post(
-                `${ENDPOINT}/collections/add-files`,
-                requestBody,
-                null,
-                {
-                    'X-Auth-Token': token,
-                }
-            );
-        }
-    } catch (e) {
-        logError(e, 'Add to collection Failed ');
-        throw e;
-    }
-};
-
-export const restoreToCollection = async (
-    collection: Collection,
-    files: EnteFile[]
-) => {
-    try {
-        const token = getToken();
-        const batchedFiles = batch(files, REQUEST_BATCH_SIZE);
-        for (const batch of batchedFiles) {
-            const fileKeysEncryptedWithNewCollection =
-                await encryptWithNewCollectionKey(collection, batch);
-
-            const requestBody: AddToCollectionRequest = {
-                collectionID: collection.id,
-                files: fileKeysEncryptedWithNewCollection,
-            };
-            await HTTPService.post(
-                `${ENDPOINT}/collections/restore-files`,
-                requestBody,
-                null,
-                {
-                    'X-Auth-Token': token,
-                }
-            );
-        }
-    } catch (e) {
-        logError(e, 'restore to collection Failed ');
-        throw e;
-    }
-};
-export const moveToCollection = async (
-    fromCollectionID: number,
-    toCollection: Collection,
-    files: EnteFile[]
-) => {
-    try {
-        const token = getToken();
-        const batchedFiles = batch(files, REQUEST_BATCH_SIZE);
-        for (const batch of batchedFiles) {
-            const fileKeysEncryptedWithNewCollection =
-                await encryptWithNewCollectionKey(toCollection, batch);
-
-            const requestBody: MoveToCollectionRequest = {
-                fromCollectionID: fromCollectionID,
-                toCollectionID: toCollection.id,
-                files: fileKeysEncryptedWithNewCollection,
-            };
-            await HTTPService.post(
-                `${ENDPOINT}/collections/move-files`,
-                requestBody,
-                null,
-                {
-                    'X-Auth-Token': token,
-                }
-            );
-        }
-    } catch (e) {
-        logError(e, 'move to collection Failed ');
-        throw e;
-    }
-};
-
-const encryptWithNewCollectionKey = async (
-    newCollection: Collection,
-    files: EnteFile[]
-): Promise<EncryptedFileKey[]> => {
-    const fileKeysEncryptedWithNewCollection: EncryptedFileKey[] = [];
-    const cryptoWorker = await ComlinkCryptoWorker.getInstance();
-    for (const file of files) {
-        const newEncryptedKey = await cryptoWorker.encryptToB64(
-            file.key,
-            newCollection.key
-        );
-        const encryptedKey = newEncryptedKey.encryptedData;
-        const keyDecryptionNonce = newEncryptedKey.nonce;
-
-        fileKeysEncryptedWithNewCollection.push({
-            id: file.id,
-            encryptedKey,
-            keyDecryptionNonce,
-        });
-    }
-    return fileKeysEncryptedWithNewCollection;
-};
-export const removeFromCollection = async (
-    collectionID: number,
-    toRemoveFiles: EnteFile[],
-    allFiles?: EnteFile[]
-) => {
-    try {
-        const user: User = getData(LS_KEYS.USER);
-        const nonUserFiles = [];
-        const userFiles = [];
-        for (const file of toRemoveFiles) {
-            if (file.ownerID === user.id) {
-                userFiles.push(file);
-            } else {
-                nonUserFiles.push(file);
-            }
-        }
-
-        if (nonUserFiles.length > 0) {
-            await removeNonUserFiles(collectionID, nonUserFiles);
-        }
-        if (userFiles.length > 0) {
-            await removeUserFiles(collectionID, userFiles, allFiles);
-        }
-    } catch (e) {
-        logError(e, 'remove from collection failed ');
-        throw e;
-    }
-};
-
-export const removeUserFiles = async (
-    sourceCollectionID: number,
-    toRemoveFiles: EnteFile[],
-    allFiles?: EnteFile[]
-) => {
-    try {
-        if (!allFiles) {
-            allFiles = await getLocalFiles();
-        }
-        const toRemoveFilesIds = new Set(toRemoveFiles.map((f) => f.id));
-        const toRemoveFilesCopiesInOtherCollections = allFiles.filter((f) => {
-            return toRemoveFilesIds.has(f.id);
-        });
-        const groupiedFiles = groupFilesBasedOnCollectionID(
-            toRemoveFilesCopiesInOtherCollections
-        );
-
-        const collections = await getLocalCollections();
-        const collectionsMap = new Map(collections.map((c) => [c.id, c]));
-        const user: User = getData(LS_KEYS.USER);
-
-        for (const [targetCollectionID, files] of groupiedFiles.entries()) {
-            const targetCollection = collectionsMap.get(targetCollectionID);
-            if (
-                !isValidMoveTarget(sourceCollectionID, targetCollection, user)
-            ) {
-                continue;
-            }
-            const toMoveFiles = files.filter((f) => {
-                if (toRemoveFilesIds.has(f.id)) {
-                    toRemoveFilesIds.delete(f.id);
-                    return true;
-                }
-                return false;
-            });
-            if (toMoveFiles.length === 0) {
-                continue;
-            }
-            await moveToCollection(
-                sourceCollectionID,
-                targetCollection,
-                toMoveFiles
-            );
-        }
-        const leftFiles = toRemoveFiles.filter((f) =>
-            toRemoveFilesIds.has(f.id)
-        );
-
-        if (leftFiles.length === 0) {
-            return;
-        }
-        let uncategorizedCollection = await getUncategorizedCollection();
-        if (!uncategorizedCollection) {
-            uncategorizedCollection = await createUnCategorizedCollection();
-        }
-        await moveToCollection(
-            sourceCollectionID,
-            uncategorizedCollection,
-            leftFiles
-        );
-    } catch (e) {
-        logError(e, 'remove user files failed ');
-        throw e;
-    }
-};
-
-export const removeNonUserFiles = async (
-    collectionID: number,
-    nonUserFiles: EnteFile[]
-) => {
-    try {
-        const fileIDs = nonUserFiles.map((f) => f.id);
-        const token = getToken();
-        const batchedFileIDs = batch(fileIDs, REQUEST_BATCH_SIZE);
-        for (const batch of batchedFileIDs) {
-            const request: RemoveFromCollectionRequest = {
-                collectionID,
-                fileIDs: batch,
-            };
-
-            await HTTPService.post(
-                `${ENDPOINT}/collections/v3/remove-files`,
-                request,
-                null,
-                { 'X-Auth-Token': token }
-            );
-        }
-    } catch (e) {
-        logError(e, 'remove non user files failed ');
-        throw e;
-    }
-};
-
-export const deleteCollection = async (
-    collectionID: number,
-    keepFiles: boolean
-) => {
-    try {
-        if (keepFiles) {
-            const allFiles = await getLocalFiles();
-            const collectionFiles = allFiles.filter((file) => {
-                return file.collectionID === collectionID;
-            });
-            await removeFromCollection(collectionID, collectionFiles, allFiles);
-        }
-        const token = getToken();
-
-        await HTTPService.delete(
-            `${ENDPOINT}/collections/v3/${collectionID}`,
-            null,
-            { collectionID, keepFiles },
-            { 'X-Auth-Token': token }
-        );
-    } catch (e) {
-        logError(e, 'delete collection failed ');
-        throw e;
-    }
-};
-
-export const leaveSharedAlbum = async (collectionID: number) => {
-    try {
-        const token = getToken();
-
-        await HTTPService.post(
-            `${ENDPOINT}/collections/leave/${collectionID}`,
-            null,
-            null,
-            { 'X-Auth-Token': token }
-        );
-    } catch (e) {
-        logError(e, 'leave shared album failed ');
-        throw e;
-    }
-};
-
-export const updateCollectionMagicMetadata = async (
-    collection: Collection,
-    updatedMagicMetadata: CollectionMagicMetadata
-) => {
-    const token = getToken();
-    if (!token) {
-        return;
-    }
-
-    const cryptoWorker = await ComlinkCryptoWorker.getInstance();
-
-    const { file: encryptedMagicMetadata } = await cryptoWorker.encryptMetadata(
-        updatedMagicMetadata.data,
-        collection.key
-    );
-
-    const reqBody: UpdateMagicMetadataRequest = {
-        id: collection.id,
-        magicMetadata: {
-            version: updatedMagicMetadata.version,
-            count: updatedMagicMetadata.count,
-            data: encryptedMagicMetadata.encryptedData,
-            header: encryptedMagicMetadata.decryptionHeader,
-        },
-    };
-
-    await HTTPService.put(
-        `${ENDPOINT}/collections/magic-metadata`,
-        reqBody,
-        null,
-        {
-            'X-Auth-Token': token,
-        }
-    );
-    const updatedCollection: Collection = {
-        ...collection,
-        magicMetadata: {
-            ...updatedMagicMetadata,
-            version: updatedMagicMetadata.version + 1,
-        },
-    };
-    return updatedCollection;
-};
-
-export const updateSharedCollectionMagicMetadata = async (
-    collection: Collection,
-    updatedMagicMetadata: CollectionMagicMetadata
-) => {
-    const token = getToken();
-    if (!token) {
-        return;
-    }
-
-    const cryptoWorker = await ComlinkCryptoWorker.getInstance();
-
-    const { file: encryptedMagicMetadata } = await cryptoWorker.encryptMetadata(
-        updatedMagicMetadata.data,
-        collection.key
-    );
-
-    const reqBody: UpdateMagicMetadataRequest = {
-        id: collection.id,
-        magicMetadata: {
-            version: updatedMagicMetadata.version,
-            count: updatedMagicMetadata.count,
-            data: encryptedMagicMetadata.encryptedData,
-            header: encryptedMagicMetadata.decryptionHeader,
-        },
-    };
-
-    await HTTPService.put(
-        `${ENDPOINT}/collections/sharee-magic-metadata`,
-        reqBody,
-        null,
-        {
-            'X-Auth-Token': token,
-        }
-    );
-    const updatedCollection: Collection = {
-        ...collection,
-        magicMetadata: {
-            ...updatedMagicMetadata,
-            version: updatedMagicMetadata.version + 1,
-        },
-    };
-    return updatedCollection;
-};
-
-export const updatePublicCollectionMagicMetadata = async (
-    collection: Collection,
-    updatedPublicMagicMetadata: CollectionPublicMagicMetadata
-) => {
-    const token = getToken();
-    if (!token) {
-        return;
-    }
-
-    const cryptoWorker = await ComlinkCryptoWorker.getInstance();
-
-    const { file: encryptedMagicMetadata } = await cryptoWorker.encryptMetadata(
-        updatedPublicMagicMetadata.data,
-        collection.key
-    );
-
-    const reqBody: UpdateMagicMetadataRequest = {
-        id: collection.id,
-        magicMetadata: {
-            version: updatedPublicMagicMetadata.version,
-            count: updatedPublicMagicMetadata.count,
-            data: encryptedMagicMetadata.encryptedData,
-            header: encryptedMagicMetadata.decryptionHeader,
-        },
-    };
-
-    await HTTPService.put(
-        `${ENDPOINT}/collections/public-magic-metadata`,
-        reqBody,
-        null,
-        {
-            'X-Auth-Token': token,
-        }
-    );
-    const updatedCollection: Collection = {
-        ...collection,
-        pubMagicMetadata: {
-            ...updatedPublicMagicMetadata,
-            version: updatedPublicMagicMetadata.version + 1,
-        },
-    };
-    return updatedCollection;
-};
-
-export const renameCollection = async (
-    collection: Collection,
-    newCollectionName: string
-) => {
-    if (isQuickLinkCollection(collection)) {
-        // Convert quick link collection to normal collection on rename
-        await changeCollectionSubType(collection, SUB_TYPE.DEFAULT);
-    }
-    const token = getToken();
-    const cryptoWorker = await ComlinkCryptoWorker.getInstance();
-    const { encryptedData: encryptedName, nonce: nameDecryptionNonce } =
-        await cryptoWorker.encryptUTF8(newCollectionName, collection.key);
-    const collectionRenameRequest = {
-        collectionID: collection.id,
-        encryptedName,
-        nameDecryptionNonce,
-    };
-    await HTTPService.post(
-        `${ENDPOINT}/collections/rename`,
-        collectionRenameRequest,
-        null,
-        {
-            'X-Auth-Token': token,
-        }
-    );
-};
-
-export const unshareCollection = async (
-    collection: Collection,
-    withUserEmail: string
-) => {
-    try {
-        const token = getToken();
-        const shareCollectionRequest = {
-            collectionID: collection.id,
-            email: withUserEmail,
-        };
-        await HTTPService.post(
-            `${ENDPOINT}/collections/unshare`,
-            shareCollectionRequest,
-            null,
-            {
-                'X-Auth-Token': token,
-            }
-        );
-    } catch (e) {
-        logError(e, 'unshare collection failed ');
-    }
-};
-
-export const createShareableURL = async (collection: Collection) => {
-    try {
-        const token = getToken();
-        if (!token) {
-            return null;
-        }
-        const createPublicAccessTokenRequest: CreatePublicAccessTokenRequest = {
-            collectionID: collection.id,
-        };
-        const resp = await HTTPService.post(
-            `${ENDPOINT}/collections/share-url`,
-            createPublicAccessTokenRequest,
-            null,
-            {
-                'X-Auth-Token': token,
-            }
-        );
-        return resp.data.result as PublicURL;
-    } catch (e) {
-        logError(e, 'createShareableURL failed ');
-        throw e;
-    }
-};
-
-export const deleteShareableURL = async (collection: Collection) => {
-    try {
-        const token = getToken();
-        if (!token) {
-            return null;
-        }
-        await HTTPService.delete(
-            `${ENDPOINT}/collections/share-url/${collection.id}`,
-            null,
-            null,
-            {
-                'X-Auth-Token': token,
-            }
-        );
-    } catch (e) {
-        logError(e, 'deleteShareableURL failed ');
-        throw e;
-    }
-};
-
-export const updateShareableURL = async (
-    request: UpdatePublicURL
-): Promise<PublicURL> => {
-    try {
-        const token = getToken();
-        if (!token) {
-            return null;
-        }
-        const res = await HTTPService.put(
-            `${ENDPOINT}/collections/share-url`,
-            request,
-            null,
-            {
-                'X-Auth-Token': token,
-            }
-        );
-        return res.data.result as PublicURL;
-    } catch (e) {
-        logError(e, 'updateShareableURL failed ');
-        throw e;
-    }
-};
-
-export const getFavCollection = async () => {
-    const collections = await getLocalCollections();
-    for (const collection of collections) {
-        if (collection.type === CollectionType.favorites) {
-            return collection;
-        }
-    }
-};
-
-export const getNonEmptyCollections = (
-    collections: Collection[],
-    files: EnteFile[]
-) => {
-    const nonEmptyCollectionsIds = new Set<number>();
-    for (const file of files) {
-        nonEmptyCollectionsIds.add(file.collectionID);
-    }
-    return collections.filter((collection) =>
-        nonEmptyCollectionsIds.has(collection.id)
-    );
-};
-
-export function sortCollectionSummaries(
-    collectionSummaries: CollectionSummary[],
-    sortBy: COLLECTION_LIST_SORT_BY
-) {
-    return collectionSummaries
-        .sort((a, b) => {
-            switch (sortBy) {
-                case COLLECTION_LIST_SORT_BY.CREATION_TIME_ASCENDING:
-                    return (
-                        -1 *
-                        compareCollectionsLatestFile(b.latestFile, a.latestFile)
-                    );
-                case COLLECTION_LIST_SORT_BY.UPDATION_TIME_DESCENDING:
-                    return b.updationTime - a.updationTime;
-                case COLLECTION_LIST_SORT_BY.NAME:
-                    return a.name.localeCompare(b.name);
-            }
-        })
-        .sort((a, b) => b.order ?? 0 - a.order ?? 0)
-        .sort(
-            (a, b) =>
-                COLLECTION_SORT_ORDER.get(a.type) -
-                COLLECTION_SORT_ORDER.get(b.type)
-        );
-}
-
-function compareCollectionsLatestFile(first: EnteFile, second: EnteFile) {
-    if (!first) {
-        return 1;
-    } else if (!second) {
-        return -1;
-    } else {
-        const sortedFiles = sortFiles([first, second]);
-        if (sortedFiles[0].id !== first.id) {
-            return 1;
-        } else {
-            return -1;
-        }
-    }
-}
-
-export function getCollectionSummaries(
-    user: User,
-    collections: Collection[],
-    files: EnteFile[]
-): CollectionSummaries {
-    const collectionSummaries: CollectionSummaries = new Map();
-    const collectionLatestFiles = getCollectionLatestFiles(files);
-    const collectionCoverFiles = getCollectionCoverFiles(files, collections);
-    const collectionFilesCount = getCollectionsFileCount(files);
-
-    let hasUncategorizedCollection = false;
-    for (const collection of collections) {
-        if (
-            !hasUncategorizedCollection &&
-            collection.type === CollectionType.uncategorized
-        ) {
-            hasUncategorizedCollection = true;
-        }
-        if (
-            collectionFilesCount.get(collection.id) ||
-            collection.type === CollectionType.uncategorized
-        ) {
-            let type: CollectionSummaryType;
-            if (isIncomingShare(collection, user)) {
-                if (isIncomingCollabShare(collection, user)) {
-                    type = CollectionSummaryType.incomingShareCollaborator;
-                } else {
-                    type = CollectionSummaryType.incomingShareViewer;
-                }
-            } else if (isOutgoingShare(collection, user)) {
-                type = CollectionSummaryType.outgoingShare;
-            } else if (isSharedOnlyViaLink(collection)) {
-                type = CollectionSummaryType.sharedOnlyViaLink;
-            } else if (isArchivedCollection(collection)) {
-                type = CollectionSummaryType.archived;
-            } else if (isDefaultHiddenCollection(collection)) {
-                type = CollectionSummaryType.defaultHidden;
-            } else if (isPinnedCollection(collection)) {
-                type = CollectionSummaryType.pinned;
-            } else {
-                type = CollectionSummaryType[collection.type];
-            }
-
-            let CollectionSummaryItemName: string;
-            if (type === CollectionSummaryType.uncategorized) {
-                CollectionSummaryItemName = t('UNCATEGORIZED');
-            } else if (type === CollectionSummaryType.favorites) {
-                CollectionSummaryItemName = t('FAVORITES');
-            } else {
-                CollectionSummaryItemName = collection.name;
-            }
-
-            collectionSummaries.set(collection.id, {
-                id: collection.id,
-                name: CollectionSummaryItemName,
-                latestFile: collectionLatestFiles.get(collection.id),
-                coverFile: collectionCoverFiles.get(collection.id),
-                fileCount: collectionFilesCount.get(collection.id) ?? 0,
-                updationTime: collection.updationTime,
-                type: type,
-                order: collection.magicMetadata?.data?.order ?? 0,
-            });
-        }
-    }
-    if (!hasUncategorizedCollection) {
-        collectionSummaries.set(
-            DUMMY_UNCATEGORIZED_COLLECTION,
-            getDummyUncategorizedCollectionSummary()
-        );
-    }
-
-    return collectionSummaries;
-}
-
-function getCollectionsFileCount(files: EnteFile[]): CollectionFilesCount {
-    const collectionIDToFileMap = groupFilesBasedOnCollectionID(files);
-    const collectionFilesCount = new Map<number, number>();
-    for (const [id, files] of collectionIDToFileMap) {
-        collectionFilesCount.set(id, files.length);
-    }
-    return collectionFilesCount;
-}
-
-export function getSectionSummaries(
-    files: EnteFile[],
-    trashedFiles: EnteFile[],
-    archivedCollections: Set<number>
-): CollectionSummaries {
-    const collectionSummaries: CollectionSummaries = new Map();
-    collectionSummaries.set(
-        ALL_SECTION,
-        getAllSectionSummary(files, archivedCollections)
-    );
-    collectionSummaries.set(
-        TRASH_SECTION,
-        getTrashedCollectionSummary(trashedFiles)
-    );
-    collectionSummaries.set(ARCHIVE_SECTION, getArchivedSectionSummary(files));
-
-    return collectionSummaries;
-}
-
-function getAllSectionSummary(
-    files: EnteFile[],
-    archivedCollections: Set<number>
-): CollectionSummary {
-    const allSectionFiles = getAllSectionVisibleFiles(
-        files,
-        archivedCollections
-    );
-    return {
-        id: ALL_SECTION,
-        name: t('ALL_SECTION_NAME'),
-        type: CollectionSummaryType.all,
-        coverFile: allSectionFiles?.[0],
-        latestFile: allSectionFiles?.[0],
-        fileCount: allSectionFiles?.length || 0,
-        updationTime: allSectionFiles?.[0]?.updationTime,
-    };
-}
-
-function getAllSectionVisibleFiles(
-    files: EnteFile[],
-    archivedCollections: Set<number>
-): EnteFile[] {
-    const allSectionVisibleFiles = getUniqueFiles(
-        files.filter((file) => {
-            if (
-                isArchivedFile(file) ||
-                archivedCollections.has(file.collectionID)
-            ) {
-                return false;
-            }
-            return true;
-        })
-    );
-    return allSectionVisibleFiles;
-}
-
-export function getDummyUncategorizedCollectionSummary(): CollectionSummary {
-    return {
-        id: DUMMY_UNCATEGORIZED_COLLECTION,
-        name: t('UNCATEGORIZED'),
-        type: CollectionSummaryType.uncategorized,
-        latestFile: null,
-        coverFile: null,
-        fileCount: 0,
-        updationTime: 0,
-    };
-}
-
-export function getArchivedSectionSummary(
-    files: EnteFile[]
-): CollectionSummary {
-    const archivedFiles = getUniqueFiles(
-        files.filter((file) => isArchivedFile(file))
-    );
-    return {
-        id: ARCHIVE_SECTION,
-        name: t('ARCHIVE_SECTION_NAME'),
-        type: CollectionSummaryType.archive,
-        coverFile: null,
-        latestFile: archivedFiles?.[0],
-        fileCount: archivedFiles?.length,
-        updationTime: archivedFiles?.[0]?.updationTime,
-    };
-}
-
-export function getHiddenItemsSummary(
-    hiddenFiles: EnteFile[],
-    hiddenCollections: Collection[]
-): CollectionSummary {
-    const defaultHiddenCollectionIds = new Set(
-        hiddenCollections
-            .filter((collection) => isDefaultHiddenCollection(collection))
-            .map((collection) => collection.id)
-    );
-    const hiddenItems = getUniqueFiles(
-        hiddenFiles.filter((file) =>
-            defaultHiddenCollectionIds.has(file.collectionID)
-        )
-    );
-    return {
-        id: HIDDEN_ITEMS_SECTION,
-        name: t('HIDDEN_ITEMS'),
-        type: CollectionSummaryType.hiddenItems,
-        coverFile: hiddenItems?.[0],
-        latestFile: hiddenItems?.[0],
-        fileCount: hiddenItems?.length,
-        updationTime: hiddenItems?.[0]?.updationTime,
-    };
-}
-
-export function getTrashedCollectionSummary(
-    trashedFiles: EnteFile[]
-): CollectionSummary {
-    return {
-        id: TRASH_SECTION,
-        name: t('TRASH'),
-        type: CollectionSummaryType.trash,
-        coverFile: null,
-        latestFile: trashedFiles?.[0],
-        fileCount: trashedFiles?.length,
-        updationTime: trashedFiles?.[0]?.updationTime,
-    };
-}
-
-export async function getUncategorizedCollection(
-    collections?: Collection[]
-): Promise<Collection> {
-    if (!collections) {
-        collections = await getLocalCollections();
-    }
-    const uncategorizedCollection = collections.find(
-        (collection) => collection.type === CollectionType.uncategorized
-    );
-
-    return uncategorizedCollection;
-}
-
-export function createUnCategorizedCollection() {
-    return createCollection(
-        UNCATEGORIZED_COLLECTION_NAME,
-        CollectionType.uncategorized
-    );
-}
-
-export async function getDefaultHiddenCollection(): Promise<Collection> {
-    const collections = await getLocalCollections('hidden');
-    const hiddenCollection = collections.find((collection) =>
-        isDefaultHiddenCollection(collection)
-    );
-
-    return hiddenCollection;
-}
-
-export function createHiddenCollection() {
-    return createCollection(HIDDEN_COLLECTION_NAME, CollectionType.album, {
-        subType: SUB_TYPE.DEFAULT_HIDDEN,
-        visibility: VISIBILITY_STATE.HIDDEN,
-    });
-}
-
-export async function moveToHiddenCollection(files: EnteFile[]) {
-    try {
-        let hiddenCollection = await getDefaultHiddenCollection();
-        if (!hiddenCollection) {
-            hiddenCollection = await createHiddenCollection();
-        }
-        const groupiedFiles = groupFilesBasedOnCollectionID(files);
-        // eslint-disable-next-line @typescript-eslint/no-unused-vars
-        for (const [collectionID, files] of groupiedFiles.entries()) {
-            if (collectionID === hiddenCollection.id) {
-                continue;
-            }
-            await moveToCollection(collectionID, hiddenCollection, files);
-        }
-    } catch (e) {
-        logError(e, 'move to hidden collection failed ');
-        throw e;
-    }
-}

+ 0 - 415
apps/cast/src/services/downloadManager.ts

@@ -1,415 +0,0 @@
-import {
-    generateStreamFromArrayBuffer,
-    getRenderableFileURL,
-    createTypedObjectURL,
-} from 'utils/file';
-import { EnteFile } from 'types/file';
-
-import { FILE_TYPE } from 'constants/file';
-import { CustomError } from '@ente/shared/error';
-import ComlinkCryptoWorker from 'utils/comlink/ComlinkCryptoWorker';
-import { CacheStorageService } from './cache/cacheStorageService';
-import { CACHES } from 'constants/cache';
-import { Remote } from 'comlink';
-import { DedicatedCryptoWorker } from 'worker/crypto.worker';
-import { LimitedCache } from 'types/cache';
-import { retryAsyncFunction } from 'utils/network';
-import { getToken } from '@ente/shared/storage/localStorage/helpers';
-import { getFileURL, getThumbnailURL } from '@ente/shared/network/api';
-import HTTPService from '@ente/shared/network/HTTPService';
-import { logError } from '@ente/shared/sentry';
-import { addLogLine } from '@ente/shared/logging';
-
-class DownloadManager {
-    private fileObjectURLPromise = new Map<
-        string,
-        Promise<{ original: string[]; converted: string[] }>
-    >();
-    private thumbnailObjectURLPromise = new Map<number, Promise<string>>();
-
-    private fileDownloadProgress = new Map<number, number>();
-
-    private progressUpdater: (value: Map<number, number>) => void = () => {};
-
-    private thumbnailCache: LimitedCache;
-
-    setProgressUpdater(progressUpdater: (value: Map<number, number>) => void) {
-        this.progressUpdater = progressUpdater;
-    }
-
-    private async getThumbnailCache() {
-        try {
-            if (!this.thumbnailCache) {
-                this.thumbnailCache = await CacheStorageService.open(
-                    CACHES.THUMBS
-                );
-            }
-            return this.thumbnailCache;
-        } catch (e) {
-            return null;
-            // ignore
-        }
-    }
-
-    public async getCachedThumbnail(file: EnteFile) {
-        try {
-            const thumbnailCache = await this.getThumbnailCache();
-            const cacheResp: Response = await thumbnailCache?.match(
-                file.id.toString()
-            );
-
-            if (cacheResp) {
-                return URL.createObjectURL(await cacheResp.blob());
-            }
-            return null;
-        } catch (e) {
-            logError(e, 'failed to get cached thumbnail');
-            throw e;
-        }
-    }
-
-    public async getThumbnail(
-        file: EnteFile,
-        tokenOverride?: string,
-        usingWorker?: Remote<DedicatedCryptoWorker>,
-        timeout?: number
-    ) {
-        try {
-            const token = tokenOverride || getToken();
-            if (!token) {
-                return null;
-            }
-            if (!this.thumbnailObjectURLPromise.has(file.id)) {
-                const downloadPromise = async () => {
-                    const thumbnailCache = await this.getThumbnailCache();
-                    const cachedThumb = await this.getCachedThumbnail(file);
-                    if (cachedThumb) {
-                        return cachedThumb;
-                    }
-                    const thumb = await this.downloadThumb(
-                        token,
-                        file,
-                        usingWorker,
-                        timeout
-                    );
-                    const thumbBlob = new Blob([thumb]);
-
-                    thumbnailCache
-                        ?.put(file.id.toString(), new Response(thumbBlob))
-                        .catch((e) => {
-                            logError(e, 'cache put failed');
-                            // TODO: handle storage full exception.
-                        });
-                    return URL.createObjectURL(thumbBlob);
-                };
-                this.thumbnailObjectURLPromise.set(file.id, downloadPromise());
-            }
-
-            return await this.thumbnailObjectURLPromise.get(file.id);
-        } catch (e) {
-            this.thumbnailObjectURLPromise.delete(file.id);
-            logError(e, 'get DownloadManager preview Failed');
-            throw e;
-        }
-    }
-
-    downloadThumb = async (
-        token: string,
-        file: EnteFile,
-        usingWorker?: Remote<DedicatedCryptoWorker>,
-        timeout?: number
-    ) => {
-        const resp = await HTTPService.get(
-            getThumbnailURL(file.id),
-            null,
-            { 'X-Auth-Token': token },
-            { responseType: 'arraybuffer', timeout }
-        );
-        if (typeof resp.data === 'undefined') {
-            throw Error(CustomError.REQUEST_FAILED);
-        }
-        const cryptoWorker =
-            usingWorker || (await ComlinkCryptoWorker.getInstance());
-        const decrypted = await cryptoWorker.decryptThumbnail(
-            new Uint8Array(resp.data),
-            await cryptoWorker.fromB64(file.thumbnail.decryptionHeader),
-            file.key
-        );
-        return decrypted;
-    };
-
-    getFile = async (file: EnteFile, forPreview = false) => {
-        const fileKey = forPreview ? `${file.id}_preview` : `${file.id}`;
-        try {
-            const getFilePromise = async () => {
-                const fileStream = await this.downloadFile(file);
-                const fileBlob = await new Response(fileStream).blob();
-                if (forPreview) {
-                    return await getRenderableFileURL(file, fileBlob);
-                } else {
-                    const fileURL = await createTypedObjectURL(
-                        fileBlob,
-                        file.metadata.title
-                    );
-                    return { converted: [fileURL], original: [fileURL] };
-                }
-            };
-            if (!this.fileObjectURLPromise.get(fileKey)) {
-                this.fileObjectURLPromise.set(fileKey, getFilePromise());
-            }
-            const fileURLs = await this.fileObjectURLPromise.get(fileKey);
-            return fileURLs;
-        } catch (e) {
-            this.fileObjectURLPromise.delete(fileKey);
-            logError(e, 'download manager Failed to get File');
-            throw e;
-        }
-    };
-
-    public async getCachedOriginalFile(file: EnteFile) {
-        return (await this.fileObjectURLPromise.get(file.id.toString()))
-            ?.original;
-    }
-
-    async downloadFile(
-        file: EnteFile,
-        tokenOverride?: string,
-        usingWorker?: Remote<DedicatedCryptoWorker>,
-        timeout?: number
-    ) {
-        try {
-            const cryptoWorker =
-                usingWorker || (await ComlinkCryptoWorker.getInstance());
-            const token = tokenOverride || getToken();
-            if (!token) {
-                return null;
-            }
-            const onDownloadProgress = this.trackDownloadProgress(
-                file.id,
-                file.info?.fileSize
-            );
-            if (
-                file.metadata.fileType === FILE_TYPE.IMAGE ||
-                file.metadata.fileType === FILE_TYPE.LIVE_PHOTO
-            ) {
-                const resp = await retryAsyncFunction(() =>
-                    HTTPService.get(
-                        getFileURL(file.id),
-                        null,
-                        { 'X-Auth-Token': token },
-                        {
-                            responseType: 'arraybuffer',
-                            timeout,
-                            onDownloadProgress,
-                        }
-                    )
-                );
-                this.clearDownloadProgress(file.id);
-                if (typeof resp.data === 'undefined') {
-                    throw Error(CustomError.REQUEST_FAILED);
-                }
-                try {
-                    const decrypted = await cryptoWorker.decryptFile(
-                        new Uint8Array(resp.data),
-                        await cryptoWorker.fromB64(file.file.decryptionHeader),
-                        file.key
-                    );
-                    return generateStreamFromArrayBuffer(decrypted);
-                } catch (e) {
-                    if (e.message === CustomError.PROCESSING_FAILED) {
-                        logError(e, 'Failed to process file', {
-                            fileID: file.id,
-                            fromMobile:
-                                !!file.metadata.localID ||
-                                !!file.metadata.deviceFolder ||
-                                !!file.metadata.version,
-                        });
-                        addLogLine(
-                            `Failed to process file with fileID:${file.id}, localID: ${file.metadata.localID}, version: ${file.metadata.version}, deviceFolder:${file.metadata.deviceFolder} with error: ${e.message}`
-                        );
-                    }
-                    throw e;
-                }
-            }
-            const resp = await retryAsyncFunction(() =>
-                fetch(getFileURL(file.id), {
-                    headers: {
-                        'X-Auth-Token': token,
-                    },
-                })
-            );
-            const reader = resp.body.getReader();
-
-            const contentLength = +resp.headers.get('Content-Length') ?? 0;
-            let downloadedBytes = 0;
-
-            const stream = new ReadableStream({
-                async start(controller) {
-                    try {
-                        const decryptionHeader = await cryptoWorker.fromB64(
-                            file.file.decryptionHeader
-                        );
-                        const fileKey = await cryptoWorker.fromB64(file.key);
-                        const { pullState, decryptionChunkSize } =
-                            await cryptoWorker.initChunkDecryption(
-                                decryptionHeader,
-                                fileKey
-                            );
-                        let data = new Uint8Array();
-                        // The following function handles each data chunk
-                        const push = () => {
-                            // "done" is a Boolean and value a "Uint8Array"
-                            reader.read().then(async ({ done, value }) => {
-                                try {
-                                    // Is there more data to read?
-                                    if (!done) {
-                                        downloadedBytes += value.byteLength;
-                                        onDownloadProgress({
-                                            loaded: downloadedBytes,
-                                            total: contentLength,
-                                        });
-                                        const buffer = new Uint8Array(
-                                            data.byteLength + value.byteLength
-                                        );
-                                        buffer.set(new Uint8Array(data), 0);
-                                        buffer.set(
-                                            new Uint8Array(value),
-                                            data.byteLength
-                                        );
-                                        if (
-                                            buffer.length > decryptionChunkSize
-                                        ) {
-                                            const fileData = buffer.slice(
-                                                0,
-                                                decryptionChunkSize
-                                            );
-                                            try {
-                                                const { decryptedData } =
-                                                    await cryptoWorker.decryptFileChunk(
-                                                        fileData,
-                                                        pullState
-                                                    );
-                                                controller.enqueue(
-                                                    decryptedData
-                                                );
-                                                data =
-                                                    buffer.slice(
-                                                        decryptionChunkSize
-                                                    );
-                                            } catch (e) {
-                                                if (
-                                                    e.message ===
-                                                    CustomError.PROCESSING_FAILED
-                                                ) {
-                                                    logError(
-                                                        e,
-                                                        'Failed to process file',
-                                                        {
-                                                            fileID: file.id,
-                                                            fromMobile:
-                                                                !!file.metadata
-                                                                    .localID ||
-                                                                !!file.metadata
-                                                                    .deviceFolder ||
-                                                                !!file.metadata
-                                                                    .version,
-                                                        }
-                                                    );
-                                                    addLogLine(
-                                                        `Failed to process file ${file.id} from localID: ${file.metadata.localID} version: ${file.metadata.version} deviceFolder:${file.metadata.deviceFolder} with error: ${e.message}`
-                                                    );
-                                                }
-                                                throw e;
-                                            }
-                                        } else {
-                                            data = buffer;
-                                        }
-                                        push();
-                                    } else {
-                                        if (data) {
-                                            try {
-                                                const { decryptedData } =
-                                                    await cryptoWorker.decryptFileChunk(
-                                                        data,
-                                                        pullState
-                                                    );
-                                                controller.enqueue(
-                                                    decryptedData
-                                                );
-                                                data = null;
-                                            } catch (e) {
-                                                if (
-                                                    e.message ===
-                                                    CustomError.PROCESSING_FAILED
-                                                ) {
-                                                    logError(
-                                                        e,
-                                                        'Failed to process file',
-                                                        {
-                                                            fileID: file.id,
-                                                            fromMobile:
-                                                                !!file.metadata
-                                                                    .localID ||
-                                                                !!file.metadata
-                                                                    .deviceFolder ||
-                                                                !!file.metadata
-                                                                    .version,
-                                                        }
-                                                    );
-                                                    addLogLine(
-                                                        `Failed to process file ${file.id} from localID: ${file.metadata.localID} version: ${file.metadata.version} deviceFolder:${file.metadata.deviceFolder} with error: ${e.message}`
-                                                    );
-                                                }
-                                                throw e;
-                                            }
-                                        }
-                                        controller.close();
-                                    }
-                                } catch (e) {
-                                    logError(e, 'Failed to process file chunk');
-                                    controller.error(e);
-                                }
-                            });
-                        };
-
-                        push();
-                    } catch (e) {
-                        logError(e, 'Failed to process file stream');
-                        controller.error(e);
-                    }
-                },
-            });
-            return stream;
-        } catch (e) {
-            logError(e, 'Failed to download file');
-            throw e;
-        }
-    }
-
-    trackDownloadProgress = (fileID: number, fileSize: number) => {
-        return (event: { loaded: number; total: number }) => {
-            if (isNaN(event.total) || event.total === 0) {
-                if (!fileSize) {
-                    return;
-                }
-                event.total = fileSize;
-            }
-            if (event.loaded === event.total) {
-                this.fileDownloadProgress.delete(fileID);
-            } else {
-                this.fileDownloadProgress.set(
-                    fileID,
-                    Math.round((event.loaded * 100) / event.total)
-                );
-            }
-            this.progressUpdater(new Map(this.fileDownloadProgress));
-        };
-    };
-
-    clearDownloadProgress = (fileID: number) => {
-        this.fileDownloadProgress.delete(fileID);
-        this.progressUpdater(new Map(this.fileDownloadProgress));
-    };
-}
-
-export default new DownloadManager();

+ 0 - 312
apps/cast/src/services/fileService.ts

@@ -1,312 +0,0 @@
-import { getEndpoint } from '@ente/shared/network/api';
-
-import { getToken } from '@ente/shared/storage/localStorage/helpers';
-import { Collection } from 'types/collection';
-
-import {
-    decryptFile,
-    getLatestVersionFiles,
-    mergeMetadata,
-    sortFiles,
-} from 'utils/file';
-import { eventBus, Events } from './events';
-import {
-    EnteFile,
-    EncryptedEnteFile,
-    TrashRequest,
-    FileWithUpdatedMagicMetadata,
-    FileWithUpdatedPublicMagicMetadata,
-} from 'types/file';
-import { SetFiles } from 'types/gallery';
-import { BulkUpdateMagicMetadataRequest } from 'types/magicMetadata';
-import ComlinkCryptoWorker from 'utils/comlink/ComlinkCryptoWorker';
-import {
-    getCollectionLastSyncTime,
-    setCollectionLastSyncTime,
-} from './collectionService';
-import { REQUEST_BATCH_SIZE } from 'constants/api';
-import { batch } from '@ente/shared/batch';
-import HTTPService from '@ente/shared/network/HTTPService';
-import localForage from '@ente/shared/storage/localForage';
-import { logError } from '@ente/shared/sentry';
-import { addLogLine } from '@ente/shared/logging';
-
-const ENDPOINT = getEndpoint();
-const FILES_TABLE = 'files';
-const HIDDEN_FILES_TABLE = 'hidden-files';
-
-export const getLocalFiles = async (type: 'normal' | 'hidden' = 'normal') => {
-    const tableName = type === 'normal' ? FILES_TABLE : HIDDEN_FILES_TABLE;
-    const files: Array<EnteFile> =
-        (await localForage.getItem<EnteFile[]>(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) {
-            logError(e, 'Error in localFileUpdated handlers');
-        }
-    } catch (e1) {
-        try {
-            const storageEstimate = await navigator.storage.estimate();
-            logError(e1, 'failed to save files to indexedDB', {
-                storageEstimate,
-            });
-            addLogLine(`storage estimate ${JSON.stringify(storageEstimate)}`);
-        } catch (e2) {
-            logError(e1, 'failed to save files to indexedDB');
-            logError(e2, 'failed to get storage stats');
-        }
-        throw e1;
-    }
-};
-
-export const getAllLocalFiles = async () => {
-    const normalFiles = await getLocalFiles('normal');
-    const hiddenFiles = await getLocalFiles('hidden');
-    return [...normalFiles, ...hiddenFiles];
-};
-
-export const syncFiles = async (
-    type: 'normal' | 'hidden',
-    collections: Collection[],
-    setFiles: SetFiles
-) => {
-    const localFiles = await getLocalFiles(type);
-    let files = await removeDeletedCollectionFiles(collections, localFiles);
-    if (files.length !== localFiles.length) {
-        await setLocalFiles(type, files);
-        setFiles(sortFiles(mergeMetadata(files)));
-    }
-    for (const collection of collections) {
-        if (!getToken()) {
-            continue;
-        }
-        const lastSyncTime = await getCollectionLastSyncTime(collection);
-        if (collection.updationTime === lastSyncTime) {
-            continue;
-        }
-
-        const newFiles = await getFiles(collection, lastSyncTime, setFiles);
-        files = getLatestVersionFiles([...files, ...newFiles]);
-        await setLocalFiles(type, files);
-        setCollectionLastSyncTime(collection, collection.updationTime);
-    }
-    return files;
-};
-
-const getFiles = async (
-    collection: Collection,
-    sinceTime: number,
-    setFiles: SetFiles
-): Promise<EnteFile[]> => {
-    try {
-        let decryptedFiles: EnteFile[] = [];
-        let time = sinceTime;
-        let resp;
-        do {
-            const token = getToken();
-            if (!token) {
-                break;
-            }
-            resp = await HTTPService.get(
-                `${ENDPOINT}/collections/v2/diff`,
-                {
-                    collectionID: collection.id,
-                    sinceTime: time,
-                },
-                {
-                    'X-Auth-Token': token,
-                }
-            );
-
-            const newDecryptedFilesBatch = await Promise.all(
-                resp.data.diff.map(async (file: EncryptedEnteFile) => {
-                    if (!file.isDeleted) {
-                        return await decryptFile(file, collection.key);
-                    } else {
-                        return file;
-                    }
-                }) as Promise<EnteFile>[]
-            );
-            decryptedFiles = [...decryptedFiles, ...newDecryptedFilesBatch];
-
-            setFiles((files) =>
-                sortFiles(
-                    mergeMetadata(
-                        getLatestVersionFiles([
-                            ...(files || []),
-                            ...decryptedFiles,
-                        ])
-                    )
-                )
-            );
-            if (resp.data.diff.length) {
-                time = resp.data.diff.slice(-1)[0].updationTime;
-            }
-        } while (resp.data.hasMore);
-        return decryptedFiles;
-    } catch (e) {
-        logError(e, 'Get files failed');
-        throw e;
-    }
-};
-
-const removeDeletedCollectionFiles = async (
-    collections: Collection[],
-    files: EnteFile[]
-) => {
-    const syncedCollectionIds = new Set<number>();
-    for (const collection of collections) {
-        syncedCollectionIds.add(collection.id);
-    }
-    files = files.filter((file) => syncedCollectionIds.has(file.collectionID));
-    return files;
-};
-
-export const trashFiles = async (filesToTrash: EnteFile[]) => {
-    try {
-        const token = getToken();
-        if (!token) {
-            return;
-        }
-        const batchedFilesToTrash = batch(filesToTrash, REQUEST_BATCH_SIZE);
-        for (const batch of batchedFilesToTrash) {
-            const trashRequest: TrashRequest = {
-                items: batch.map((file) => ({
-                    fileID: file.id,
-                    collectionID: file.collectionID,
-                })),
-            };
-            await HTTPService.post(
-                `${ENDPOINT}/files/trash`,
-                trashRequest,
-                null,
-                {
-                    'X-Auth-Token': token,
-                }
-            );
-        }
-    } catch (e) {
-        logError(e, 'trash file failed');
-        throw e;
-    }
-};
-
-export const deleteFromTrash = async (filesToDelete: number[]) => {
-    try {
-        const token = getToken();
-        if (!token) {
-            return;
-        }
-        const batchedFilesToDelete = batch(filesToDelete, REQUEST_BATCH_SIZE);
-
-        for (const batch of batchedFilesToDelete) {
-            await HTTPService.post(
-                `${ENDPOINT}/trash/delete`,
-                { fileIDs: batch },
-                null,
-                {
-                    'X-Auth-Token': token,
-                }
-            );
-        }
-    } catch (e) {
-        logError(e, 'deleteFromTrash failed');
-        throw e;
-    }
-};
-
-export const updateFileMagicMetadata = async (
-    fileWithUpdatedMagicMetadataList: FileWithUpdatedMagicMetadata[]
-) => {
-    const token = getToken();
-    if (!token) {
-        return;
-    }
-    const reqBody: BulkUpdateMagicMetadataRequest = { metadataList: [] };
-    const cryptoWorker = await ComlinkCryptoWorker.getInstance();
-    for (const {
-        file,
-        updatedMagicMetadata,
-    } of fileWithUpdatedMagicMetadataList) {
-        const { file: encryptedMagicMetadata } =
-            await cryptoWorker.encryptMetadata(
-                updatedMagicMetadata.data,
-                file.key
-            );
-        reqBody.metadataList.push({
-            id: file.id,
-            magicMetadata: {
-                version: updatedMagicMetadata.version,
-                count: updatedMagicMetadata.count,
-                data: encryptedMagicMetadata.encryptedData,
-                header: encryptedMagicMetadata.decryptionHeader,
-            },
-        });
-    }
-    await HTTPService.put(`${ENDPOINT}/files/magic-metadata`, reqBody, null, {
-        'X-Auth-Token': token,
-    });
-    return fileWithUpdatedMagicMetadataList.map(
-        ({ file, updatedMagicMetadata }): EnteFile => ({
-            ...file,
-            magicMetadata: {
-                ...updatedMagicMetadata,
-                version: updatedMagicMetadata.version + 1,
-            },
-        })
-    );
-};
-
-export const updateFilePublicMagicMetadata = async (
-    fileWithUpdatedPublicMagicMetadataList: FileWithUpdatedPublicMagicMetadata[]
-): Promise<EnteFile[]> => {
-    const token = getToken();
-    if (!token) {
-        return;
-    }
-    const reqBody: BulkUpdateMagicMetadataRequest = { metadataList: [] };
-    const cryptoWorker = await ComlinkCryptoWorker.getInstance();
-    for (const {
-        file,
-        updatedPublicMagicMetadata: updatePublicMagicMetadata,
-    } of fileWithUpdatedPublicMagicMetadataList) {
-        const { file: encryptedPubMagicMetadata } =
-            await cryptoWorker.encryptMetadata(
-                updatePublicMagicMetadata.data,
-                file.key
-            );
-        reqBody.metadataList.push({
-            id: file.id,
-            magicMetadata: {
-                version: updatePublicMagicMetadata.version,
-                count: updatePublicMagicMetadata.count,
-                data: encryptedPubMagicMetadata.encryptedData,
-                header: encryptedPubMagicMetadata.decryptionHeader,
-            },
-        });
-    }
-    await HTTPService.put(
-        `${ENDPOINT}/files/public-magic-metadata`,
-        reqBody,
-        null,
-        {
-            'X-Auth-Token': token,
-        }
-    );
-    return fileWithUpdatedPublicMagicMetadataList.map(
-        ({ file, updatedPublicMagicMetadata }): EnteFile => ({
-            ...file,
-            pubMagicMetadata: {
-                ...updatedPublicMagicMetadata,
-                version: updatedPublicMagicMetadata.version + 1,
-            },
-        })
-    );
-};

+ 0 - 373
apps/cast/src/services/upload/exifService.ts

@@ -1,373 +0,0 @@
-import { EXIFLESS_FORMATS, NULL_LOCATION } from 'constants/upload';
-import { Location } from 'types/upload';
-import exifr from 'exifr';
-import piexif from 'piexifjs';
-import { FileTypeInfo } from 'types/upload';
-import { logError } from '@ente/shared/sentry';
-import { validateAndGetCreationUnixTimeInMicroSeconds } from 'utils/time';
-import { CustomError } from '@ente/shared/error';
-
-const EXIFR_UNSUPPORTED_FILE_FORMAT_MESSAGE = 'Unknown file format';
-
-type ParsedEXIFData = Record<string, any> &
-    Partial<{
-        DateTimeOriginal: Date;
-        CreateDate: Date;
-        ModifyDate: Date;
-        DateCreated: Date;
-        MetadataDate: Date;
-        latitude: number;
-        longitude: number;
-        imageWidth: number;
-        imageHeight: number;
-    }>;
-
-type RawEXIFData = Record<string, any> &
-    Partial<{
-        DateTimeOriginal: string;
-        CreateDate: string;
-        ModifyDate: string;
-        DateCreated: string;
-        MetadataDate: string;
-        GPSLatitude: number[];
-        GPSLongitude: number[];
-        GPSLatitudeRef: string;
-        GPSLongitudeRef: string;
-        ImageWidth: number;
-        ImageHeight: number;
-    }>;
-
-export async function getParsedExifData(
-    receivedFile: File,
-    fileTypeInfo: FileTypeInfo,
-    tags?: string[]
-): Promise<ParsedEXIFData> {
-    try {
-        if (EXIFLESS_FORMATS.includes(fileTypeInfo.exactType)) {
-            return null;
-        }
-        const exifData: RawEXIFData = await exifr.parse(receivedFile, {
-            reviveValues: false,
-            tiff: true,
-            xmp: true,
-            icc: true,
-            iptc: true,
-            jfif: true,
-            ihdr: true,
-        });
-        if (!exifData) {
-            return null;
-        }
-        const filteredExifData = tags
-            ? Object.fromEntries(
-                  Object.entries(exifData).filter(([key]) => tags.includes(key))
-              )
-            : exifData;
-        return parseExifData(filteredExifData);
-    } catch (e) {
-        if (e.message === EXIFR_UNSUPPORTED_FILE_FORMAT_MESSAGE) {
-            logError(e, 'exif library unsupported format', {
-                fileType: fileTypeInfo.exactType,
-            });
-        } else {
-            logError(e, 'get parsed exif data failed', {
-                fileType: fileTypeInfo.exactType,
-            });
-            throw e;
-        }
-    }
-}
-
-function parseExifData(exifData: RawEXIFData): ParsedEXIFData {
-    if (!exifData) {
-        return null;
-    }
-    const {
-        DateTimeOriginal,
-        CreateDate,
-        ModifyDate,
-        DateCreated,
-        ImageHeight,
-        ImageWidth,
-        ExifImageHeight,
-        ExifImageWidth,
-        PixelXDimension,
-        PixelYDimension,
-        MetadataDate,
-        ...rest
-    } = exifData;
-    const parsedExif: ParsedEXIFData = { ...rest };
-    if (DateTimeOriginal) {
-        parsedExif.DateTimeOriginal = parseEXIFDate(exifData.DateTimeOriginal);
-    }
-    if (CreateDate) {
-        parsedExif.CreateDate = parseEXIFDate(exifData.CreateDate);
-    }
-    if (ModifyDate) {
-        parsedExif.ModifyDate = parseEXIFDate(exifData.ModifyDate);
-    }
-    if (DateCreated) {
-        parsedExif.DateCreated = parseEXIFDate(exifData.DateCreated);
-    }
-    if (MetadataDate) {
-        parsedExif.MetadataDate = parseEXIFDate(exifData.MetadataDate);
-    }
-    if (exifData.GPSLatitude && exifData.GPSLongitude) {
-        const parsedLocation = parseEXIFLocation(
-            exifData.GPSLatitude,
-            exifData.GPSLatitudeRef,
-            exifData.GPSLongitude,
-            exifData.GPSLongitudeRef
-        );
-        parsedExif.latitude = parsedLocation.latitude;
-        parsedExif.longitude = parsedLocation.longitude;
-    }
-    if (ImageWidth && ImageHeight) {
-        if (typeof ImageWidth === 'number' && typeof ImageHeight === 'number') {
-            parsedExif.imageWidth = ImageWidth;
-            parsedExif.imageHeight = ImageHeight;
-        } else {
-            logError(
-                new Error('ImageWidth or ImageHeight is not a number'),
-                'Image dimension parsing failed',
-                {
-                    ImageWidth,
-                    ImageHeight,
-                }
-            );
-        }
-    } else if (ExifImageWidth && ExifImageHeight) {
-        if (
-            typeof ExifImageWidth === 'number' &&
-            typeof ExifImageHeight === 'number'
-        ) {
-            parsedExif.imageWidth = ExifImageWidth;
-            parsedExif.imageHeight = ExifImageHeight;
-        } else {
-            logError(
-                new Error('ExifImageWidth or ExifImageHeight is not a number'),
-                'Image dimension parsing failed',
-                {
-                    ExifImageWidth,
-                    ExifImageHeight,
-                }
-            );
-        }
-    } else if (PixelXDimension && PixelYDimension) {
-        if (
-            typeof PixelXDimension === 'number' &&
-            typeof PixelYDimension === 'number'
-        ) {
-            parsedExif.imageWidth = PixelXDimension;
-            parsedExif.imageHeight = PixelYDimension;
-        } else {
-            logError(
-                new Error('PixelXDimension or PixelYDimension is not a number'),
-                'Image dimension parsing failed',
-                {
-                    PixelXDimension,
-                    PixelYDimension,
-                }
-            );
-        }
-    }
-    return parsedExif;
-}
-
-function parseEXIFDate(dateTimeString: string) {
-    try {
-        if (typeof dateTimeString !== 'string' || dateTimeString === '') {
-            throw Error(CustomError.NOT_A_DATE);
-        }
-
-        // Check and parse date in the format YYYYMMDD
-        if (dateTimeString.length === 8) {
-            const year = Number(dateTimeString.slice(0, 4));
-            const month = Number(dateTimeString.slice(4, 6));
-            const day = Number(dateTimeString.slice(6, 8));
-            if (
-                !Number.isNaN(year) &&
-                !Number.isNaN(month) &&
-                !Number.isNaN(day)
-            ) {
-                const date = new Date(year, month - 1, day);
-                if (!Number.isNaN(+date)) {
-                    return date;
-                }
-            }
-        }
-        const [year, month, day, hour, minute, second] = dateTimeString
-            .match(/\d+/g)
-            .map(Number);
-
-        if (
-            typeof year === 'undefined' ||
-            Number.isNaN(year) ||
-            typeof month === 'undefined' ||
-            Number.isNaN(month) ||
-            typeof day === 'undefined' ||
-            Number.isNaN(day)
-        ) {
-            throw Error(CustomError.NOT_A_DATE);
-        }
-        let date: Date;
-        if (
-            typeof hour === 'undefined' ||
-            Number.isNaN(hour) ||
-            typeof minute === 'undefined' ||
-            Number.isNaN(minute) ||
-            typeof second === 'undefined' ||
-            Number.isNaN(second)
-        ) {
-            date = new Date(year, month - 1, day);
-        } else {
-            date = new Date(year, month - 1, day, hour, minute, second);
-        }
-        if (Number.isNaN(+date)) {
-            throw Error(CustomError.NOT_A_DATE);
-        }
-        return date;
-    } catch (e) {
-        logError(e, 'parseEXIFDate failed', {
-            dateTimeString,
-        });
-        return null;
-    }
-}
-
-export function parseEXIFLocation(
-    gpsLatitude: number[],
-    gpsLatitudeRef: string,
-    gpsLongitude: number[],
-    gpsLongitudeRef: string
-) {
-    try {
-        if (
-            !Array.isArray(gpsLatitude) ||
-            !Array.isArray(gpsLongitude) ||
-            gpsLatitude.length !== 3 ||
-            gpsLongitude.length !== 3
-        ) {
-            throw Error(CustomError.NOT_A_LOCATION);
-        }
-        const latitude = convertDMSToDD(
-            gpsLatitude[0],
-            gpsLatitude[1],
-            gpsLatitude[2],
-            gpsLatitudeRef
-        );
-        const longitude = convertDMSToDD(
-            gpsLongitude[0],
-            gpsLongitude[1],
-            gpsLongitude[2],
-            gpsLongitudeRef
-        );
-        return { latitude, longitude };
-    } catch (e) {
-        logError(e, 'parseEXIFLocation failed', {
-            gpsLatitude,
-            gpsLatitudeRef,
-            gpsLongitude,
-            gpsLongitudeRef,
-        });
-        return NULL_LOCATION;
-    }
-}
-
-function convertDMSToDD(
-    degrees: number,
-    minutes: number,
-    seconds: number,
-    direction: string
-) {
-    let dd = degrees + minutes / 60 + seconds / (60 * 60);
-    if (direction === 'S' || direction === 'W') dd *= -1;
-    return dd;
-}
-
-export function getEXIFLocation(exifData: ParsedEXIFData): Location {
-    if (!exifData || (!exifData.latitude && exifData.latitude !== 0)) {
-        return NULL_LOCATION;
-    }
-    return { latitude: exifData.latitude, longitude: exifData.longitude };
-}
-
-export function getEXIFTime(exifData: ParsedEXIFData): number {
-    if (!exifData) {
-        return null;
-    }
-    const dateTime =
-        exifData.DateTimeOriginal ??
-        exifData.DateCreated ??
-        exifData.CreateDate ??
-        exifData.MetadataDate ??
-        exifData.ModifyDate;
-    if (!dateTime) {
-        return null;
-    }
-    return validateAndGetCreationUnixTimeInMicroSeconds(dateTime);
-}
-
-export async function updateFileCreationDateInEXIF(
-    reader: FileReader,
-    fileBlob: Blob,
-    updatedDate: Date
-) {
-    try {
-        let imageDataURL = await convertImageToDataURL(reader, fileBlob);
-        imageDataURL =
-            'data:image/jpeg;base64' +
-            imageDataURL.slice(imageDataURL.indexOf(','));
-        const exifObj = piexif.load(imageDataURL);
-        if (!exifObj['Exif']) {
-            exifObj['Exif'] = {};
-        }
-        exifObj['Exif'][piexif.ExifIFD.DateTimeOriginal] =
-            convertToExifDateFormat(updatedDate);
-
-        const exifBytes = piexif.dump(exifObj);
-        const exifInsertedFile = piexif.insert(exifBytes, imageDataURL);
-        return dataURIToBlob(exifInsertedFile);
-    } catch (e) {
-        logError(e, 'updateFileModifyDateInEXIF failed');
-        return fileBlob;
-    }
-}
-
-async function convertImageToDataURL(reader: FileReader, blob: Blob) {
-    const dataURL = await new Promise<string>((resolve) => {
-        reader.onload = () => resolve(reader.result as string);
-        reader.readAsDataURL(blob);
-    });
-    return dataURL;
-}
-
-function dataURIToBlob(dataURI: string) {
-    // convert base64 to raw binary data held in a string
-    // doesn't handle URLEncoded DataURIs - see SO answer #6850276 for code that does this
-    const byteString = atob(dataURI.split(',')[1]);
-
-    // separate out the mime component
-    const mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
-
-    // write the bytes of the string to an ArrayBuffer
-    const ab = new ArrayBuffer(byteString.length);
-
-    // create a view into the buffer
-    const ia = new Uint8Array(ab);
-
-    // set the bytes of the buffer to the correct values
-    for (let i = 0; i < byteString.length; i++) {
-        ia[i] = byteString.charCodeAt(i);
-    }
-
-    // write the ArrayBuffer to a blob, and you're done
-    const blob = new Blob([ab], { type: mimeString });
-    return blob;
-}
-
-function convertToExifDateFormat(date: Date) {
-    return `${date.getFullYear()}:${
-        date.getMonth() + 1
-    }:${date.getDate()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`;
-}

+ 1 - 143
apps/cast/src/utils/collection/index.ts

@@ -1,6 +1,5 @@
 import {
     getNonEmptyCollections,
-    updateCollectionMagicMetadata,
     updatePublicCollectionMagicMetadata,
 } from 'services/collectionService';
 import { EnteFile } from 'types/file';
@@ -8,25 +7,16 @@ import { logError } from '@ente/shared/sentry';
 import {
     COLLECTION_ROLE,
     Collection,
-    CollectionMagicMetadataProps,
     CollectionPublicMagicMetadataProps,
-    CollectionSummaries,
 } from 'types/collection';
 import {
     CollectionSummaryType,
     CollectionType,
     HIDE_FROM_COLLECTION_BAR_TYPES,
     OPTIONS_NOT_HAVING_COLLECTION_TYPES,
-    SYSTEM_COLLECTION_TYPES,
-    MOVE_TO_NOT_ALLOWED_COLLECTION,
-    ADD_TO_NOT_ALLOWED_COLLECTION,
 } from 'constants/collection';
-import { getUnixTimeInMicroSecondsWithDelta } from 'utils/time';
 import { SUB_TYPE, VISIBILITY_STATE } from 'types/magicMetadata';
-import { isArchivedCollection, updateMagicMetadata } from 'utils/magicMetadata';
-import bs58 from 'bs58';
-import { t } from 'i18next';
-import { getAlbumsURL } from '@ente/shared/network/api';
+import { updateMagicMetadata } from 'utils/magicMetadata';
 import { User } from '@ente/shared/user/types';
 import { getData, LS_KEYS } from '@ente/shared/storage/localStorage';
 
@@ -45,59 +35,6 @@ export function getSelectedCollection(
     return collections.find((collection) => collection.id === collectionID);
 }
 
-export function appendCollectionKeyToShareURL(
-    url: string,
-    collectionKey: string
-) {
-    if (!url) {
-        return null;
-    }
-
-    const sharableURL = new URL(url);
-    const albumsURL = new URL(getAlbumsURL());
-
-    sharableURL.protocol = albumsURL.protocol;
-    sharableURL.host = albumsURL.host;
-    sharableURL.pathname = albumsURL.pathname;
-
-    const bytes = Buffer.from(collectionKey, 'base64');
-    sharableURL.hash = bs58.encode(bytes);
-    return sharableURL.href;
-}
-
-const _intSelectOption = (i: number) => {
-    const label = i === 0 ? t('NO_DEVICE_LIMIT') : i.toString();
-    return { label, value: i };
-};
-
-export function getDeviceLimitOptions() {
-    return [0, 2, 5, 10, 25, 50].map((i) => _intSelectOption(i));
-}
-
-export const shareExpiryOptions = () => [
-    { label: t('NEVER'), value: () => 0 },
-    {
-        label: t('AFTER_TIME.HOUR'),
-        value: () => getUnixTimeInMicroSecondsWithDelta({ hours: 1 }),
-    },
-    {
-        label: t('AFTER_TIME.DAY'),
-        value: () => getUnixTimeInMicroSecondsWithDelta({ days: 1 }),
-    },
-    {
-        label: t('AFTER_TIME.WEEK'),
-        value: () => getUnixTimeInMicroSecondsWithDelta({ days: 7 }),
-    },
-    {
-        label: t('AFTER_TIME.MONTH'),
-        value: () => getUnixTimeInMicroSecondsWithDelta({ months: 1 }),
-    },
-    {
-        label: t('AFTER_TIME.YEAR'),
-        value: () => getUnixTimeInMicroSecondsWithDelta({ years: 1 }),
-    },
-];
-
 export const changeCollectionSortOrder = async (
     collection: Collection,
     asc: boolean
@@ -123,85 +60,6 @@ export const changeCollectionSortOrder = async (
     }
 };
 
-export const changeCollectionOrder = async (
-    collection: Collection,
-    order: number
-) => {
-    try {
-        const updatedMagicMetadataProps: CollectionMagicMetadataProps = {
-            order,
-        };
-
-        const updatedMagicMetadata = await updateMagicMetadata(
-            updatedMagicMetadataProps,
-            collection.magicMetadata,
-            collection.key
-        );
-
-        await updateCollectionMagicMetadata(collection, updatedMagicMetadata);
-    } catch (e) {
-        logError(e, 'change collection order failed');
-    }
-};
-
-export const changeCollectionSubType = async (
-    collection: Collection,
-    subType: SUB_TYPE
-) => {
-    try {
-        const updatedMagicMetadataProps: CollectionMagicMetadataProps = {
-            subType: subType,
-        };
-
-        const updatedMagicMetadata = await updateMagicMetadata(
-            updatedMagicMetadataProps,
-            collection.magicMetadata,
-            collection.key
-        );
-        await updateCollectionMagicMetadata(collection, updatedMagicMetadata);
-    } catch (e) {
-        logError(e, 'change collection subType failed');
-        throw e;
-    }
-};
-
-export const getArchivedCollections = (collections: Collection[]) => {
-    return new Set<number>(
-        collections
-            .filter(isArchivedCollection)
-            .map((collection) => collection.id)
-    );
-};
-
-export const getDefaultHiddenCollectionIDs = (collections: Collection[]) => {
-    return new Set<number>(
-        collections
-            .filter(isDefaultHiddenCollection)
-            .map((collection) => collection.id)
-    );
-};
-
-export const hasNonSystemCollections = (
-    collectionSummaries: CollectionSummaries
-) => {
-    for (const collectionSummary of collectionSummaries.values()) {
-        if (!isSystemCollection(collectionSummary.type)) return true;
-    }
-    return false;
-};
-
-export const isMoveToAllowedCollection = (type: CollectionSummaryType) => {
-    return !MOVE_TO_NOT_ALLOWED_COLLECTION.has(type);
-};
-
-export const isAddToAllowedCollection = (type: CollectionSummaryType) => {
-    return !ADD_TO_NOT_ALLOWED_COLLECTION.has(type);
-};
-
-export const isSystemCollection = (type: CollectionSummaryType) => {
-    return SYSTEM_COLLECTION_TYPES.has(type);
-};
-
 export const shouldShowOptions = (type: CollectionSummaryType) => {
     return !OPTIONS_NOT_HAVING_COLLECTION_TYPES.has(type);
 };

+ 0 - 77
apps/cast/src/utils/ffmpeg/index.ts

@@ -1,77 +0,0 @@
-import { NULL_LOCATION } from 'constants/upload';
-import { ParsedExtractedMetadata } from 'types/upload';
-import { validateAndGetCreationUnixTimeInMicroSeconds } from 'utils/time';
-
-enum MetadataTags {
-    CREATION_TIME = 'creation_time',
-    APPLE_CONTENT_IDENTIFIER = 'com.apple.quicktime.content.identifier',
-    APPLE_LIVE_PHOTO_IDENTIFIER = 'com.apple.quicktime.live-photo.auto',
-    APPLE_CREATION_DATE = 'com.apple.quicktime.creationdate',
-    APPLE_LOCATION_ISO = 'com.apple.quicktime.location.ISO6709',
-    LOCATION = 'location',
-}
-
-export function parseFFmpegExtractedMetadata(encodedMetadata: Uint8Array) {
-    const metadataString = new TextDecoder().decode(encodedMetadata);
-    const metadataPropertyArray = metadataString.split('\n');
-    const metadataKeyValueArray = metadataPropertyArray.map((property) =>
-        property.split('=')
-    );
-    const validKeyValuePairs = metadataKeyValueArray.filter(
-        (keyValueArray) => keyValueArray.length === 2
-    ) as Array<[string, string]>;
-
-    const metadataMap = Object.fromEntries(validKeyValuePairs);
-
-    const location = parseAppleISOLocation(
-        metadataMap[MetadataTags.APPLE_LOCATION_ISO] ??
-            metadataMap[MetadataTags.LOCATION]
-    );
-
-    const creationTime = parseCreationTime(
-        metadataMap[MetadataTags.APPLE_CREATION_DATE] ??
-            metadataMap[MetadataTags.CREATION_TIME]
-    );
-    const parsedMetadata: ParsedExtractedMetadata = {
-        creationTime,
-        location: {
-            latitude: location.latitude,
-            longitude: location.longitude,
-        },
-        width: null,
-        height: null,
-    };
-    return parsedMetadata;
-}
-
-function parseAppleISOLocation(isoLocation: string) {
-    let location = NULL_LOCATION;
-    if (isoLocation) {
-        const [latitude, longitude] = isoLocation
-            .match(/(\+|-)\d+\.*\d+/g)
-            .map((x) => parseFloat(x));
-
-        location = { latitude, longitude };
-    }
-    return location;
-}
-
-function parseCreationTime(creationTime: string) {
-    let dateTime = null;
-    if (creationTime) {
-        dateTime = validateAndGetCreationUnixTimeInMicroSeconds(
-            new Date(creationTime)
-        );
-    }
-    return dateTime;
-}
-
-export function splitFilenameAndExtension(filename: string): [string, string] {
-    const lastDotPosition = filename.lastIndexOf('.');
-    if (lastDotPosition === -1) return [filename, null];
-    else
-        return [
-            filename.slice(0, lastDotPosition),
-            filename.slice(lastDotPosition + 1),
-        ];
-}

+ 1 - 268
apps/cast/src/utils/file/index.ts

@@ -10,7 +10,6 @@ import {
 import { decodeLivePhoto } from 'services/livePhotoService';
 import { getFileType } from 'services/typeDetectionService';
 import { logError } from '@ente/shared/sentry';
-import { updateFileCreationDateInEXIF } from 'services/upload/exifService';
 import {
     TYPE_JPEG,
     TYPE_JPG,
@@ -28,15 +27,10 @@ import { isArchivedFile, updateMagicMetadata } from 'utils/magicMetadata';
 
 import { CustomError } from '@ente/shared/error';
 import ComlinkCryptoWorker from 'utils/comlink/ComlinkCryptoWorker';
-import {
-    deleteFromTrash,
-    trashFiles,
-    updateFileMagicMetadata,
-} from 'services/fileService';
+import { updateFileMagicMetadata } from 'services/fileService';
 import isElectron from 'is-electron';
 import { isPlaybackPossible } from 'utils/photoFrame';
 import { FileTypeInfo } from 'types/upload';
-import { moveToHiddenCollection } from 'services/collectionService';
 import { getData, LS_KEYS } from '@ente/shared/storage/localStorage';
 import { User } from '@ente/shared/user/types';
 import { addLogLine, addLocalLog } from '@ente/shared/logging';
@@ -85,75 +79,6 @@ export async function getUpdatedEXIFFileForDownload(
     }
 }
 
-export async function downloadFile(
-    file: EnteFile,
-    token?: string,
-    passwordToken?: string
-) {
-    try {
-        let fileBlob: Blob;
-        const fileReader = new FileReader();
-        const fileURL = await CastDownloadManager.getCachedOriginalFile(
-            file
-        )[0];
-        if (!fileURL) {
-            fileBlob = await new Response(
-                await CastDownloadManager.downloadFile(
-                    token,
-                    passwordToken,
-                    file
-                )
-            ).blob();
-        } else {
-            fileBlob = await (await fetch(fileURL)).blob();
-        }
-
-        if (file.metadata.fileType === FILE_TYPE.LIVE_PHOTO) {
-            const livePhoto = await decodeLivePhoto(file, fileBlob);
-            const image = new File([livePhoto.image], livePhoto.imageNameTitle);
-            const imageType = await getFileType(image);
-            const tempImageURL = URL.createObjectURL(
-                new Blob([livePhoto.image], { type: imageType.mimeType })
-            );
-            const video = new File([livePhoto.video], livePhoto.videoNameTitle);
-            const videoType = await getFileType(video);
-            const tempVideoURL = URL.createObjectURL(
-                new Blob([livePhoto.video], { type: videoType.mimeType })
-            );
-            downloadUsingAnchor(tempImageURL, livePhoto.imageNameTitle);
-            downloadUsingAnchor(tempVideoURL, livePhoto.videoNameTitle);
-        } else {
-            const fileType = await getFileType(
-                new File([fileBlob], file.metadata.title)
-            );
-            fileBlob = await new Response(
-                await getUpdatedEXIFFileForDownload(
-                    fileReader,
-                    file,
-                    fileBlob.stream()
-                )
-            ).blob();
-            fileBlob = new Blob([fileBlob], { type: fileType.mimeType });
-            const tempURL = URL.createObjectURL(fileBlob);
-            downloadUsingAnchor(tempURL, file.metadata.title);
-        }
-    } catch (e) {
-        logError(e, 'failed to download file');
-        throw e;
-    }
-}
-
-export function downloadUsingAnchor(link: string, name: string) {
-    const a = document.createElement('a');
-    a.style.display = 'none';
-    a.href = link;
-    a.download = name;
-    document.body.appendChild(a);
-    a.click();
-    URL.revokeObjectURL(link);
-    a.remove();
-}
-
 export function groupFilesBasedOnCollectionID(files: EnteFile[]) {
     const collectionWiseFiles = new Map<number, EnteFile[]>();
     for (const file of files) {
@@ -544,106 +469,6 @@ export function getUniqueFiles(files: EnteFile[]) {
     return uniqueFiles;
 }
 
-export async function downloadFiles(
-    files: EnteFile[],
-    progressBarUpdater?: {
-        increaseSuccess: () => void;
-        increaseFailed: () => void;
-        isCancelled: () => boolean;
-    }
-) {
-    for (const file of files) {
-        try {
-            if (progressBarUpdater?.isCancelled()) {
-                return;
-            }
-            await downloadFile(file, false);
-            progressBarUpdater?.increaseSuccess();
-        } catch (e) {
-            logError(e, 'download fail for file');
-            progressBarUpdater?.increaseFailed();
-        }
-    }
-}
-
-export async function downloadFilesDesktop(
-    files: EnteFile[],
-    progressBarUpdater: {
-        increaseSuccess: () => void;
-        increaseFailed: () => void;
-        isCancelled: () => boolean;
-    },
-    downloadPath: string
-) {
-    const fileReader = new FileReader();
-    for (const file of files) {
-        try {
-            if (progressBarUpdater?.isCancelled()) {
-                return;
-            }
-            await downloadFileDesktop(fileReader, file, downloadPath);
-            progressBarUpdater?.increaseSuccess();
-        } catch (e) {
-            logError(e, 'download fail for file');
-            progressBarUpdater?.increaseFailed();
-        }
-    }
-}
-
-export async function downloadFileDesktop(
-    fileReader: FileReader,
-    file: EnteFile,
-    downloadPath: string
-) {
-    console.log(fileReader, file, downloadPath);
-    // let fileStream: ReadableStream<Uint8Array>;
-    // const fileURL = await DownloadManager.getCachedOriginalFile(file)[0];
-    // if (!fileURL) {
-    //     fileStream = await DownloadManager.downloadFile(file);
-    // } else {
-    //     fileStream = await fetch(fileURL).then((res) => res.body);
-    // }
-
-    // if (file.metadata.fileType === FILE_TYPE.LIVE_PHOTO) {
-    //     const fileBlob = await new Response(updatedFileStream).blob();
-    //     const livePhoto = await decodeLivePhoto(file, fileBlob);
-    //     const imageExportName = getUniqueFileExportName(
-    //         downloadPath,
-    //         livePhoto.imageNameTitle
-    //     );
-    //     const imageStream = generateStreamFromArrayBuffer(livePhoto.image);
-    //     await ElectronFSService.saveMediaFile(
-    //         getFileExportPath(downloadPath, imageExportName),
-    //         imageStream
-    //     );
-    //     try {
-    //         const videoExportName = getUniqueFileExportName(
-    //             downloadPath,
-    //             livePhoto.videoNameTitle
-    //         );
-    //         const videoStream = generateStreamFromArrayBuffer(livePhoto.video);
-    //         await ElectronFSService.saveMediaFile(
-    //             getFileExportPath(downloadPath, videoExportName),
-    //             videoStream
-    //         );
-    //     } catch (e) {
-    //         ElectronFSService.deleteFile(
-    //             getFileExportPath(downloadPath, imageExportName)
-    //         );
-    //         throw e;
-    //     }
-    // } else {
-    //     const fileExportName = getUniqueFileExportName(
-    //         downloadPath,
-    //         file.metadata.title
-    //     );
-    //     await ElectronFSService.saveMediaFile(
-    //         getFileExportPath(downloadPath, fileExportName),
-    //         updatedFileStream
-    //     );
-    // }
-}
-
 export const isImageOrVideo = (fileType: FILE_TYPE) =>
     [FILE_TYPE.IMAGE, FILE_TYPE.VIDEO].includes(fileType);
 
@@ -764,71 +589,6 @@ export const shouldShowAvatar = (file: EnteFile, user: User) => {
     }
 };
 
-export const handleFileOps = async (
-    ops: FILE_OPS_TYPE,
-    files: EnteFile[],
-    setDeletedFileIds: (
-        deletedFileIds: Set<number> | ((prev: Set<number>) => Set<number>)
-    ) => void,
-    setHiddenFileIds: (
-        hiddenFileIds: Set<number> | ((prev: Set<number>) => Set<number>)
-    ) => void,
-    setFixCreationTimeAttributes: (
-        fixCreationTimeAttributes:
-            | {
-                  files: EnteFile[];
-              }
-            | ((prev: { files: EnteFile[] }) => { files: EnteFile[] })
-    ) => void
-) => {
-    switch (ops) {
-        case FILE_OPS_TYPE.TRASH:
-            await deleteFileHelper(files, false, setDeletedFileIds);
-            break;
-        case FILE_OPS_TYPE.DELETE_PERMANENTLY:
-            await deleteFileHelper(files, true, setDeletedFileIds);
-            break;
-        case FILE_OPS_TYPE.HIDE:
-            await hideFilesHelper(files, setHiddenFileIds);
-            break;
-        case FILE_OPS_TYPE.DOWNLOAD:
-            await downloadFiles(files);
-            break;
-        case FILE_OPS_TYPE.FIX_TIME:
-            fixTimeHelper(files, setFixCreationTimeAttributes);
-            break;
-        case FILE_OPS_TYPE.ARCHIVE:
-            await changeFilesVisibility(files, VISIBILITY_STATE.ARCHIVED);
-            break;
-        case FILE_OPS_TYPE.UNARCHIVE:
-            await changeFilesVisibility(files, VISIBILITY_STATE.VISIBLE);
-            break;
-    }
-};
-
-const deleteFileHelper = async (
-    selectedFiles: EnteFile[],
-    permanent: boolean,
-    setDeletedFileIds: (
-        deletedFileIds: Set<number> | ((prev: Set<number>) => Set<number>)
-    ) => void
-) => {
-    try {
-        setDeletedFileIds((deletedFileIds) => {
-            selectedFiles.forEach((file) => deletedFileIds.add(file.id));
-            return new Set(deletedFileIds);
-        });
-        if (permanent) {
-            await deleteFromTrash(selectedFiles.map((file) => file.id));
-        } else {
-            await trashFiles(selectedFiles);
-        }
-    } catch (e) {
-        setDeletedFileIds(new Set());
-        throw e;
-    }
-};
-
 export const downloadFileAsBlob = async (
     file: EnteFile,
     castToken: string
@@ -855,33 +615,6 @@ export const downloadFileAsBlob = async (
     }
 };
 
-const hideFilesHelper = async (
-    selectedFiles: EnteFile[],
-    setHiddenFileIds: (
-        hiddenFileIds: Set<number> | ((prev: Set<number>) => Set<number>)
-    ) => void
-) => {
-    try {
-        setHiddenFileIds((hiddenFileIds) => {
-            selectedFiles.forEach((file) => hiddenFileIds.add(file.id));
-            return new Set(hiddenFileIds);
-        });
-        await moveToHiddenCollection(selectedFiles);
-    } catch (e) {
-        setHiddenFileIds(new Set());
-        throw e;
-    }
-};
-
-const fixTimeHelper = async (
-    selectedFiles: EnteFile[],
-    setFixCreationTimeAttributes: (fixCreationTimeAttributes: {
-        files: EnteFile[];
-    }) => void
-) => {
-    setFixCreationTimeAttributes({ files: selectedFiles });
-};
-
 const getFileObjectURLs = (originalBlob: Blob, convertedBlob: Blob) => {
     const originalURL = URL.createObjectURL(originalBlob);
     const convertedURL = convertedBlob

+ 0 - 41
apps/cast/src/utils/time/index.ts

@@ -14,23 +14,6 @@ interface DateComponent<T = number> {
     second: T;
 }
 
-export function getUnixTimeInMicroSecondsWithDelta(delta: TimeDelta): number {
-    let currentDate = new Date();
-    if (delta?.hours) {
-        currentDate = _addHours(currentDate, delta.hours);
-    }
-    if (delta?.days) {
-        currentDate = _addDays(currentDate, delta.days);
-    }
-    if (delta?.months) {
-        currentDate = _addMonth(currentDate, delta.months);
-    }
-    if (delta?.years) {
-        currentDate = _addYears(currentDate, delta.years);
-    }
-    return currentDate.getTime() * 1000;
-}
-
 export function validateAndGetCreationUnixTimeInMicroSeconds(dateTime: Date) {
     if (!dateTime || isNaN(dateTime.getTime())) {
         return null;
@@ -46,30 +29,6 @@ export function validateAndGetCreationUnixTimeInMicroSeconds(dateTime: Date) {
     }
 }
 
-function _addDays(date: Date, days: number): Date {
-    const result = new Date(date);
-    result.setDate(date.getDate() + days);
-    return result;
-}
-
-function _addHours(date: Date, hours: number): Date {
-    const result = new Date(date);
-    result.setHours(date.getHours() + hours);
-    return result;
-}
-
-function _addMonth(date: Date, months: number) {
-    const result = new Date(date);
-    result.setMonth(date.getMonth() + months);
-    return result;
-}
-
-function _addYears(date: Date, years: number) {
-    const result = new Date(date);
-    result.setFullYear(date.getFullYear() + years);
-    return result;
-}
-
 /*
 generates data component for date in format YYYYMMDD-HHMMSS
  */