Merge branch 'main' into watch

This commit is contained in:
Rushikesh Tote 2022-07-27 18:45:49 +05:30
commit 1527f9fbad
31 changed files with 247 additions and 191 deletions

View file

@ -1,6 +1,6 @@
import * as Sentry from '@sentry/nextjs'; import * as Sentry from '@sentry/nextjs';
import { getSentryTunnelURL } from 'utils/common/apiUtil'; import { getSentryTunnelURL } from 'utils/common/apiUtil';
import { getUserAnonymizedID } from 'utils/user'; import { getSentryUserID } from 'utils/user';
import { import {
getSentryDSN, getSentryDSN,
getSentryENV, getSentryENV,
@ -13,7 +13,7 @@ const SENTRY_ENV = getSentryENV();
const SENTRY_RELEASE = getSentryRelease(); const SENTRY_RELEASE = getSentryRelease();
const IS_ENABLED = getIsSentryEnabled(); const IS_ENABLED = getIsSentryEnabled();
Sentry.setUser({ id: getUserAnonymizedID() }); Sentry.setUser({ id: getSentryUserID() });
Sentry.init({ Sentry.init({
dsn: SENTRY_DSN, dsn: SENTRY_DSN,
enabled: IS_ENABLED, enabled: IS_ENABLED,

View file

@ -1,7 +1,7 @@
import { Button, DialogActions, DialogContent, Stack } from '@mui/material'; import { Button, DialogActions, DialogContent, Stack } from '@mui/material';
import React from 'react'; import React from 'react';
import { ExportStats } from 'types/export'; import { ExportStats } from 'types/export';
import { formatDateTime } from 'utils/file'; import { formatDateTime } from 'utils/time';
import constants from 'utils/strings/constants'; import constants from 'utils/strings/constants';
import { FlexWrapper, Label, Value } from './Container'; import { FlexWrapper, Label, Value } from './Container';
import { ComfySpan } from './ExportInProgress'; import { ComfySpan } from './ExportInProgress';

View file

@ -29,7 +29,7 @@ import DialogTitleWithCloseButton from './DialogBox/TitleWithCloseButton';
import MoreHoriz from '@mui/icons-material/MoreHoriz'; import MoreHoriz from '@mui/icons-material/MoreHoriz';
import OverflowMenu from './OverflowMenu/menu'; import OverflowMenu from './OverflowMenu/menu';
import { OverflowMenuOption } from './OverflowMenu/option'; import { OverflowMenuOption } from './OverflowMenu/option';
import { convertBytesToHumanReadable } from 'utils/billing'; import { convertBytesToHumanReadable } from 'utils/file/size';
import { CustomError } from 'utils/error'; import { CustomError } from 'utils/error';
import { getLocalUserDetails } from 'utils/user'; import { getLocalUserDetails } from 'utils/user';

View file

@ -16,7 +16,7 @@ import constants from 'utils/strings/constants';
import { PublicCollectionGalleryContext } from 'utils/publicCollectionGallery'; import { PublicCollectionGalleryContext } from 'utils/publicCollectionGallery';
import { ENTE_WEBSITE_LINK } from 'constants/urls'; import { ENTE_WEBSITE_LINK } from 'constants/urls';
import { getVariantColor, ButtonVariant } from './pages/gallery/LinkButton'; import { getVariantColor, ButtonVariant } from './pages/gallery/LinkButton';
import { convertBytesToHumanReadable } from 'utils/billing'; import { convertBytesToHumanReadable } from 'utils/file/size';
import { DeduplicateContext } from 'pages/deduplicate'; import { DeduplicateContext } from 'pages/deduplicate';
import { FlexWrapper } from './Container'; import { FlexWrapper } from './Container';
import { Typography } from '@mui/material'; import { Typography } from '@mui/material';

View file

@ -4,9 +4,9 @@ import { EnteFile } from 'types/file';
import constants from 'utils/strings/constants'; import constants from 'utils/strings/constants';
import { import {
changeFileCreationTime, changeFileCreationTime,
formatDateTime,
updateExistingFilePubMetadata, updateExistingFilePubMetadata,
} from 'utils/file'; } from 'utils/file';
import { formatDateTime } from 'utils/time';
import EditIcon from '@mui/icons-material/Edit'; import EditIcon from '@mui/icons-material/Edit';
import { Label, Row, Value } from 'components/Container'; import { Label, Row, Value } from 'components/Container';
import { logError } from 'utils/sentry'; import { logError } from 'utils/sentry';

View file

@ -1,6 +1,6 @@
import React, { useContext } from 'react'; import React, { useContext, useEffect, useState } from 'react';
import constants from 'utils/strings/constants'; import constants from 'utils/strings/constants';
import { formatDateTime } from 'utils/file'; import { formatDateTime } from 'utils/time';
import { RenderFileName } from './RenderFileName'; import { RenderFileName } from './RenderFileName';
import { ExifData } from './ExifData'; import { ExifData } from './ExifData';
import { RenderCreationTime } from './RenderCreationTime'; import { RenderCreationTime } from './RenderCreationTime';
@ -8,6 +8,9 @@ import { RenderInfoItem } from './RenderInfoItem';
import DialogTitleWithCloseButton from 'components/DialogBox/TitleWithCloseButton'; import DialogTitleWithCloseButton from 'components/DialogBox/TitleWithCloseButton';
import { Dialog, DialogContent, Link, styled, Typography } from '@mui/material'; import { Dialog, DialogContent, Link, styled, Typography } from '@mui/material';
import { AppContext } from 'pages/_app'; import { AppContext } from 'pages/_app';
import { Location, Metadata } from 'types/upload';
import Photoswipe from 'photoswipe';
import { getEXIFLocation } from 'services/upload/exifService';
const FileInfoDialog = styled(Dialog)(({ theme }) => ({ const FileInfoDialog = styled(Dialog)(({ theme }) => ({
zIndex: 1501, zIndex: 1501,
@ -19,6 +22,17 @@ const FileInfoDialog = styled(Dialog)(({ theme }) => ({
}, },
})); }));
interface Iprops {
shouldDisableEdits: boolean;
showInfo: boolean;
handleCloseInfo: () => void;
items: any[];
photoSwipe: Photoswipe<Photoswipe.Options>;
metadata: Metadata;
exif: any;
scheduleUpdate: () => void;
}
export function FileInfo({ export function FileInfo({
shouldDisableEdits, shouldDisableEdits,
showInfo, showInfo,
@ -28,8 +42,30 @@ export function FileInfo({
metadata, metadata,
exif, exif,
scheduleUpdate, scheduleUpdate,
}) { }: Iprops) {
const appContext = useContext(AppContext); const appContext = useContext(AppContext);
const [location, setLocation] = useState<Location>(null);
useEffect(() => {
if (!location && metadata) {
if (metadata.longitude || metadata.longitude === 0) {
setLocation({
latitude: metadata.latitude,
longitude: metadata.longitude,
});
}
}
}, [metadata]);
useEffect(() => {
if (!location && exif) {
const exifLocation = getEXIFLocation(exif);
if (exifLocation.latitude || exifLocation.latitude === 0) {
setLocation(exifLocation);
}
}
}, [exif]);
return ( return (
<FileInfoDialog <FileInfoDialog
open={showInfo} open={showInfo}
@ -66,8 +102,7 @@ export function FileInfo({
constants.UPDATED_ON, constants.UPDATED_ON,
formatDateTime(metadata.modificationTime / 1000) formatDateTime(metadata.modificationTime / 1000)
)} )}
{metadata?.longitude > 0 && {location &&
metadata?.longitude > 0 &&
RenderInfoItem( RenderInfoItem(
constants.LOCATION, constants.LOCATION,
<Link <Link

View file

@ -2,7 +2,7 @@ import { AppContext } from 'pages/_app';
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import { downloadAsFile } from 'utils/file'; import { downloadAsFile } from 'utils/file';
import constants from 'utils/strings/constants'; import constants from 'utils/strings/constants';
import { logUploadInfo, getUploadLogs } from 'utils/upload'; import { addLogLine, getDebugLogs } from 'utils/logging';
import SidebarButton from './Button'; import SidebarButton from './Button';
export default function DebugLogs() { export default function DebugLogs() {
@ -14,18 +14,18 @@ export default function DebugLogs() {
proceed: { proceed: {
text: constants.DOWNLOAD, text: constants.DOWNLOAD,
variant: 'accent', variant: 'accent',
action: downloadUploadLogs, action: downloadDebugLogs,
}, },
close: { close: {
text: constants.CANCEL, text: constants.CANCEL,
}, },
}); });
const downloadUploadLogs = () => { const downloadDebugLogs = () => {
logUploadInfo('exporting logs'); addLogLine('exporting logs');
const logs = getUploadLogs(); const logs = getDebugLogs();
const logString = logs.join('\n'); const logString = logs.join('\n');
downloadAsFile(`upload_logs_${Date.now()}.txt`, logString); downloadAsFile(`debug_logs_${Date.now()}.txt`, logString);
}; };
return ( return (

View file

@ -3,17 +3,18 @@ import { EnteFile } from 'types/file';
import { styled } from '@mui/material'; import { styled } from '@mui/material';
import PlayCircleOutlineOutlinedIcon from '@mui/icons-material/PlayCircleOutlineOutlined'; import PlayCircleOutlineOutlinedIcon from '@mui/icons-material/PlayCircleOutlineOutlined';
import DownloadManager from 'services/downloadManager'; import DownloadManager from 'services/downloadManager';
import useLongPress from 'utils/common/useLongPress'; import useLongPress from 'hooks/useLongPress';
import { GalleryContext } from 'pages/gallery'; import { GalleryContext } from 'pages/gallery';
import { GAP_BTW_TILES, IMAGE_CONTAINER_MAX_WIDTH } from 'constants/gallery'; import { GAP_BTW_TILES, IMAGE_CONTAINER_MAX_WIDTH } from 'constants/gallery';
import { PublicCollectionGalleryContext } from 'utils/publicCollectionGallery'; import { PublicCollectionGalleryContext } from 'utils/publicCollectionGallery';
import PublicCollectionDownloadManager from 'services/publicCollectionDownloadManager'; import PublicCollectionDownloadManager from 'services/publicCollectionDownloadManager';
import LivePhotoIcon from '@mui/icons-material/LightMode'; import LivePhotoIcon from '@mui/icons-material/LightMode';
import { formatDateRelative, isLivePhoto } from 'utils/file'; import { isLivePhoto } from 'utils/file';
import { DeduplicateContext } from 'pages/deduplicate'; import { DeduplicateContext } from 'pages/deduplicate';
import { logError } from 'utils/sentry'; import { logError } from 'utils/sentry';
import { Overlay } from 'components/Container'; import { Overlay } from 'components/Container';
import { TRASH_SECTION } from 'constants/collection'; import { TRASH_SECTION } from 'constants/collection';
import { formatDateRelative } from 'utils/time';
interface IProps { interface IProps {
file: EnteFile; file: EnteFile;

View file

@ -13,7 +13,7 @@ import { getData, LS_KEYS } from 'utils/storage/localStorage';
import HTTPService from 'services/HTTPService'; import HTTPService from 'services/HTTPService';
import FlashMessageBar from 'components/FlashMessageBar'; import FlashMessageBar from 'components/FlashMessageBar';
import Head from 'next/head'; import Head from 'next/head';
import { logUploadInfo } from 'utils/upload'; import { addLogLine } from 'utils/logging';
import LoadingBar from 'react-top-loading-bar'; import LoadingBar from 'react-top-loading-bar';
import DialogBox from 'components/DialogBox'; import DialogBox from 'components/DialogBox';
import { styled, ThemeProvider } from '@mui/material/styles'; import { styled, ThemeProvider } from '@mui/material/styles';
@ -27,6 +27,7 @@ import {
getRoadmapRedirectURL, getRoadmapRedirectURL,
} from 'services/userService'; } from 'services/userService';
import { CustomError } from 'utils/error'; import { CustomError } from 'utils/error';
import { getSentryUserID } from 'utils/user';
export const MessageContainer = styled('div')` export const MessageContainer = styled('div')`
background-color: #111; background-color: #111;
@ -206,10 +207,11 @@ export default function App({ Component, err }) {
}, [redirectName]); }, [redirectName]);
useEffect(() => { useEffect(() => {
logUploadInfo(`app started`); addLogLine(`app started`);
logUploadInfo( addLogLine(
`latest commit id :${process.env.NEXT_PUBLIC_LATEST_COMMIT_HASH}` `latest commit id :${process.env.NEXT_PUBLIC_LATEST_COMMIT_HASH}`
); );
addLogLine(`user sentry id ${getSentryUserID()}`);
}, []); }, []);
useEffect(() => setMessageDialogView(true), [dialogMessage]); useEffect(() => setMessageDialogView(true), [dialogMessage]);

View file

@ -99,6 +99,8 @@ import { NotificationAttributes } from 'types/Notification';
import { ITEM_TYPE, TimeStampListItem } from 'components/PhotoList'; import { ITEM_TYPE, TimeStampListItem } from 'components/PhotoList';
import UploadInputs from 'components/UploadSelectorInputs'; import UploadInputs from 'components/UploadSelectorInputs';
import useFileInput from 'hooks/useFileInput'; import useFileInput from 'hooks/useFileInput';
import { User } from 'types/user';
import { getData, LS_KEYS } from 'utils/storage/localStorage';
export const DeadCenter = styled('div')` export const DeadCenter = styled('div')`
flex: 1; flex: 1;
@ -132,6 +134,7 @@ export const GalleryContext = createContext<GalleryContextType>(
export default function Gallery() { export default function Gallery() {
const router = useRouter(); const router = useRouter();
const [user, setUser] = useState(null);
const [collections, setCollections] = useState<Collection[]>(null); const [collections, setCollections] = useState<Collection[]>(null);
const [files, setFiles] = useState<EnteFile[]>(null); const [files, setFiles] = useState<EnteFile[]>(null);
@ -245,10 +248,12 @@ export default function Gallery() {
setPlanModalView(true); setPlanModalView(true);
} }
setIsFirstLogin(false); setIsFirstLogin(false);
const user = getData(LS_KEYS.USER);
const files = mergeMetadata(await getLocalFiles()); const files = mergeMetadata(await getLocalFiles());
const collections = await getLocalCollections(); const collections = await getLocalCollections();
const trash = await getLocalTrash(); const trash = await getLocalTrash();
files.push(...getTrashedFiles(trash)); files.push(...getTrashedFiles(trash));
setUser(user);
setFiles(sortFiles(files)); setFiles(sortFiles(files));
setCollections(collections); setCollections(collections);
await syncWithRemote(true); await syncWithRemote(true);
@ -261,7 +266,10 @@ export default function Gallery() {
}, []); }, []);
useEffect(() => { useEffect(() => {
setDerivativeState(collections, files); if (!user || !files || !collections) {
return;
}
setDerivativeState(user, collections, files);
}, [collections, files]); }, [collections, files]);
useEffect( useEffect(
@ -378,18 +386,17 @@ export default function Gallery() {
}; };
const setDerivativeState = async ( const setDerivativeState = async (
user: User,
collections: Collection[], collections: Collection[],
files: EnteFile[] files: EnteFile[]
) => { ) => {
if (!collections || !files) {
return;
}
const favItemIds = await getFavItemIds(files); const favItemIds = await getFavItemIds(files);
setFavItemIds(favItemIds); setFavItemIds(favItemIds);
const archivedCollections = getArchivedCollections(collections); const archivedCollections = getArchivedCollections(collections);
setArchivedCollections(archivedCollections); setArchivedCollections(archivedCollections);
const collectionSummaries = getCollectionSummaries( const collectionSummaries = getCollectionSummaries(
user,
collections, collections,
files, files,
archivedCollections archivedCollections

View file

@ -770,6 +770,7 @@ function compareCollectionsLatestFile(first: EnteFile, second: EnteFile) {
} }
export function getCollectionSummaries( export function getCollectionSummaries(
user: User,
collections: Collection[], collections: Collection[],
files: EnteFile[], files: EnteFile[],
archivedCollections: Set<number> archivedCollections: Set<number>
@ -781,7 +782,6 @@ export function getCollectionSummaries(
); );
const collectionFilesCount = getCollectionsFileCount(files); const collectionFilesCount = getCollectionsFileCount(files);
const uniqueFileCount = new Set(files.map((file) => file.id)).size; const uniqueFileCount = new Set(files.map((file) => file.id)).size;
const user: User = getData(LS_KEYS.USER);
for (const collection of collections) { for (const collection of collections) {
if (collectionFilesCount.get(collection.id)) { if (collectionFilesCount.get(collection.id)) {

View file

@ -17,7 +17,7 @@ import { EnteFile, TrashRequest } from 'types/file';
import { SetFiles } from 'types/gallery'; import { SetFiles } from 'types/gallery';
import { MAX_TRASH_BATCH_SIZE } from 'constants/file'; import { MAX_TRASH_BATCH_SIZE } from 'constants/file';
import { BulkUpdateMagicMetadataRequest } from 'types/magicMetadata'; import { BulkUpdateMagicMetadataRequest } from 'types/magicMetadata';
import { logUploadInfo } from 'utils/upload'; import { addLogLine } from 'utils/logging';
const ENDPOINT = getEndpoint(); const ENDPOINT = getEndpoint();
const FILES_TABLE = 'files'; const FILES_TABLE = 'files';
@ -37,9 +37,7 @@ export const setLocalFiles = async (files: EnteFile[]) => {
logError(e1, 'failed to save files to indexedDB', { logError(e1, 'failed to save files to indexedDB', {
storageEstimate, storageEstimate,
}); });
logUploadInfo( addLogLine(`storage estimate ${JSON.stringify(storageEstimate)}`);
`storage estimate ${JSON.stringify(storageEstimate)}`
);
} catch (e2) { } catch (e2) {
logError(e1, 'failed to save files to indexedDB'); logError(e1, 'failed to save files to indexedDB');
logError(e2, 'failed to get storage stats'); logError(e2, 'failed to get storage stats');

View file

@ -3,7 +3,7 @@ import { CustomError } from 'utils/error';
import { createNewConvertWorker } from 'utils/heicConverter'; import { createNewConvertWorker } from 'utils/heicConverter';
import { retryAsyncFunction } from 'utils/network'; import { retryAsyncFunction } from 'utils/network';
import { logError } from 'utils/sentry'; import { logError } from 'utils/sentry';
import { logUploadInfo } from 'utils/upload'; import { addLogLine } from 'utils/logging';
const WORKER_POOL_SIZE = 2; const WORKER_POOL_SIZE = 2;
const MAX_CONVERSION_IN_PARALLEL = 1; const MAX_CONVERSION_IN_PARALLEL = 1;
@ -63,7 +63,7 @@ class HEICConverter {
this.workerPool.push({ comlink, worker }); this.workerPool.push({ comlink, worker });
return convertedHEIC; return convertedHEIC;
} catch (e) { } catch (e) {
logUploadInfo('heic conversion failed-' + e.message); addLogLine('heic conversion failed-' + e.message);
logError(e, 'heic conversion failed'); logError(e, 'heic conversion failed');
worker.terminate(); worker.terminate();
this.workerPool.push(await createNewConvertWorker()); this.workerPool.push(await createNewConvertWorker());

View file

@ -47,7 +47,7 @@ export async function getFileType(
} catch (e) { } catch (e) {
const fileFormat = getFileExtension(receivedFile.name); const fileFormat = getFileExtension(receivedFile.name);
const formatMissedByTypeDetection = FORMAT_MISSED_BY_FILE_TYPE_LIB.find( const formatMissedByTypeDetection = FORMAT_MISSED_BY_FILE_TYPE_LIB.find(
(a) => a.exactType === fileFormat.toLocaleLowerCase() (a) => a.exactType === fileFormat
); );
if (formatMissedByTypeDetection) { if (formatMissedByTypeDetection) {
return formatMissedByTypeDetection; return formatMissedByTypeDetection;
@ -64,22 +64,30 @@ export async function getFileType(
} }
async function extractFileType(file: File) { async function extractFileType(file: File) {
const fileChunkBlob = file.slice(0, CHUNK_SIZE_FOR_TYPE_DETECTION); const fileBlobChunk = file.slice(0, CHUNK_SIZE_FOR_TYPE_DETECTION);
return getFileTypeFromBlob(fileChunkBlob); const fileDataChunk = await getUint8ArrayView(fileBlobChunk);
return getFileTypeFromBuffer(fileDataChunk);
} }
async function extractElectronFileType(file: ElectronFile) { async function extractElectronFileType(file: ElectronFile) {
const stream = await file.stream(); const stream = await file.stream();
const reader = stream.getReader(); const reader = stream.getReader();
const { value } = await reader.read(); const { value: fileDataChunk } = await reader.read();
const fileTypeResult = await FileType.fromBuffer(value); return getFileTypeFromBuffer(fileDataChunk);
return fileTypeResult;
} }
async function getFileTypeFromBlob(fileBlob: Blob) { async function getFileTypeFromBuffer(buffer: Uint8Array) {
try { try {
const initialFiledata = await getUint8ArrayView(fileBlob); const result = await FileType.fromBuffer(buffer);
return await FileType.fromBuffer(initialFiledata); if (!result.mime) {
logError(
Error('mimetype missing from file type result'),
CustomError.TYPE_DETECTION_FAILED,
{ result }
);
throw Error(CustomError.TYPE_DETECTION_FAILED);
}
return result;
} catch (e) { } catch (e) {
throw Error(CustomError.TYPE_DETECTION_FAILED); throw Error(CustomError.TYPE_DETECTION_FAILED);
} }

View file

@ -131,7 +131,7 @@ export async function getRawExif(
return exifData; return exifData;
} }
function getEXIFLocation(exifData): Location { export function getEXIFLocation(exifData): Location {
if (!exifData.latitude || !exifData.longitude) { if (!exifData.latitude || !exifData.longitude) {
return NULL_LOCATION; return NULL_LOCATION;
} }

View file

@ -13,7 +13,7 @@ import {
} from 'types/upload'; } from 'types/upload';
import { splitFilenameAndExtension } from 'utils/file'; import { splitFilenameAndExtension } from 'utils/file';
import { logError } from 'utils/sentry'; import { logError } from 'utils/sentry';
import { getFileNameSize, logUploadInfo } from 'utils/upload'; import { getFileNameSize, addLogLine } from 'utils/logging';
import { encryptFiledata } from './encryptionService'; import { encryptFiledata } from './encryptionService';
import { extractMetadata, getMetadataJSONMapKey } from './metadataService'; import { extractMetadata, getMetadataJSONMapKey } from './metadataService';
import { import {
@ -41,7 +41,7 @@ export async function readFile(
rawFile, rawFile,
fileTypeInfo fileTypeInfo
); );
logUploadInfo(`reading file data ${getFileNameSize(rawFile)} `); addLogLine(`reading file data ${getFileNameSize(rawFile)} `);
let filedata: Uint8Array | DataStream; let filedata: Uint8Array | DataStream;
if (!(rawFile instanceof File)) { if (!(rawFile instanceof File)) {
if (rawFile.size > MULTIPART_PART_SIZE) { if (rawFile.size > MULTIPART_PART_SIZE) {
@ -58,7 +58,7 @@ export async function readFile(
filedata = await getUint8ArrayView(rawFile); filedata = await getUint8ArrayView(rawFile);
} }
logUploadInfo(`read file data successfully ${getFileNameSize(rawFile)} `); addLogLine(`read file data successfully ${getFileNameSize(rawFile)} `);
return { return {
filedata, filedata,

View file

@ -3,12 +3,12 @@ import { CustomError, errorWithContext } from 'utils/error';
import { logError } from 'utils/sentry'; import { logError } from 'utils/sentry';
import { BLACK_THUMBNAIL_BASE64 } from 'constants/upload'; import { BLACK_THUMBNAIL_BASE64 } from 'constants/upload';
import FFmpegService from 'services/ffmpeg/ffmpegService'; import FFmpegService from 'services/ffmpeg/ffmpegService';
import { convertBytesToHumanReadable } from 'utils/billing'; import { convertBytesToHumanReadable } from 'utils/file/size';
import { isFileHEIC } from 'utils/file'; import { isFileHEIC } from 'utils/file';
import { ElectronFile, FileTypeInfo } from 'types/upload'; import { ElectronFile, FileTypeInfo } from 'types/upload';
import { getUint8ArrayView } from '../readerService'; import { getUint8ArrayView } from '../readerService';
import HEICConverter from 'services/heicConverter/heicConverterService'; import HEICConverter from 'services/heicConverter/heicConverterService';
import { getFileNameSize, logUploadInfo } from 'utils/upload'; import { getFileNameSize, addLogLine } from 'utils/logging';
const MAX_THUMBNAIL_DIMENSION = 720; const MAX_THUMBNAIL_DIMENSION = 720;
const MIN_COMPRESSION_PERCENTAGE_SIZE_DIFF = 10; const MIN_COMPRESSION_PERCENTAGE_SIZE_DIFF = 10;
@ -28,7 +28,7 @@ export async function generateThumbnail(
fileTypeInfo: FileTypeInfo fileTypeInfo: FileTypeInfo
): Promise<{ thumbnail: Uint8Array; hasStaticThumbnail: boolean }> { ): Promise<{ thumbnail: Uint8Array; hasStaticThumbnail: boolean }> {
try { try {
logUploadInfo(`generating thumbnail for ${getFileNameSize(file)}`); addLogLine(`generating thumbnail for ${getFileNameSize(file)}`);
let hasStaticThumbnail = false; let hasStaticThumbnail = false;
let canvas = document.createElement('canvas'); let canvas = document.createElement('canvas');
let thumbnail: Uint8Array; let thumbnail: Uint8Array;
@ -41,14 +41,14 @@ export async function generateThumbnail(
canvas = await generateImageThumbnail(file, isHEIC); canvas = await generateImageThumbnail(file, isHEIC);
} else { } else {
try { try {
logUploadInfo( addLogLine(
`ffmpeg generateThumbnail called for ${getFileNameSize( `ffmpeg generateThumbnail called for ${getFileNameSize(
file file
)}` )}`
); );
const thumb = await FFmpegService.generateThumbnail(file); const thumb = await FFmpegService.generateThumbnail(file);
logUploadInfo( addLogLine(
`ffmpeg thumbnail successfully generated ${getFileNameSize( `ffmpeg thumbnail successfully generated ${getFileNameSize(
file file
)}` )}`
@ -59,7 +59,7 @@ export async function generateThumbnail(
false false
); );
} catch (e) { } catch (e) {
logUploadInfo( addLogLine(
`ffmpeg thumbnail generated failed ${getFileNameSize( `ffmpeg thumbnail generated failed ${getFileNameSize(
file file
)} error: ${e.message}` )} error: ${e.message}`
@ -75,14 +75,14 @@ export async function generateThumbnail(
if (thumbnail.length === 0) { if (thumbnail.length === 0) {
throw Error('EMPTY THUMBNAIL'); throw Error('EMPTY THUMBNAIL');
} }
logUploadInfo( addLogLine(
`thumbnail successfully generated ${getFileNameSize(file)}` `thumbnail successfully generated ${getFileNameSize(file)}`
); );
} catch (e) { } catch (e) {
logError(e, 'uploading static thumbnail', { logError(e, 'uploading static thumbnail', {
fileFormat: fileTypeInfo.exactType, fileFormat: fileTypeInfo.exactType,
}); });
logUploadInfo( addLogLine(
`thumbnail generation failed ${getFileNameSize(file)} error: ${ `thumbnail generation failed ${getFileNameSize(file)} error: ${
e.message e.message
}` }`
@ -107,9 +107,9 @@ export async function generateImageThumbnail(file: File, isHEIC: boolean) {
let timeout = null; let timeout = null;
if (isHEIC) { if (isHEIC) {
logUploadInfo(`HEICConverter called for ${getFileNameSize(file)}`); addLogLine(`HEICConverter called for ${getFileNameSize(file)}`);
file = new File([await HEICConverter.convert(file)], file.name); file = new File([await HEICConverter.convert(file)], file.name);
logUploadInfo(`${getFileNameSize(file)} successfully converted`); addLogLine(`${getFileNameSize(file)} successfully converted`);
} }
let image = new Image(); let image = new Image();
imageURL = URL.createObjectURL(file); imageURL = URL.createObjectURL(file);

View file

@ -16,7 +16,6 @@ import { logError } from 'utils/sentry';
import { getMetadataJSONMapKey, parseMetadataJSON } from './metadataService'; import { getMetadataJSONMapKey, parseMetadataJSON } from './metadataService';
import { import {
areFileWithCollectionsSame, areFileWithCollectionsSame,
getFileNameSize,
segregateMetadataAndMediaFiles, segregateMetadataAndMediaFiles,
} from 'utils/upload'; } from 'utils/upload';
import uploader from './uploader'; import uploader from './uploader';
@ -28,6 +27,7 @@ import { EnteFile } from 'types/file';
import { import {
ElectronFile, ElectronFile,
FileWithCollection, FileWithCollection,
Metadata,
MetadataAndFileTypeInfo, MetadataAndFileTypeInfo,
MetadataAndFileTypeInfoMap, MetadataAndFileTypeInfoMap,
ParsedMetadataJSON, ParsedMetadataJSON,
@ -41,7 +41,7 @@ import {
import { ComlinkWorker } from 'utils/comlink'; import { ComlinkWorker } from 'utils/comlink';
import { FILE_TYPE } from 'constants/file'; import { FILE_TYPE } from 'constants/file';
import uiService from './uiService'; import uiService from './uiService';
import { logUploadInfo } from 'utils/upload'; import { addLogLine, getFileNameSize } from 'utils/logging';
import isElectron from 'is-electron'; import isElectron from 'is-electron';
import ImportService from 'services/importService'; import ImportService from 'services/importService';
import watchFolderService from 'services/watchFolder/watchFolderService'; import watchFolderService from 'services/watchFolder/watchFolderService';
@ -95,15 +95,13 @@ class UploadManager {
) { ) {
try { try {
await this.init(collections); await this.init(collections);
logUploadInfo( addLogLine(
`received ${fileWithCollectionToBeUploaded.length} files to upload` `received ${fileWithCollectionToBeUploaded.length} files to upload`
); );
const { metadataJSONFiles, mediaFiles } = const { metadataJSONFiles, mediaFiles } =
segregateMetadataAndMediaFiles(fileWithCollectionToBeUploaded); segregateMetadataAndMediaFiles(fileWithCollectionToBeUploaded);
logUploadInfo( addLogLine(`has ${metadataJSONFiles.length} metadata json files`);
`has ${metadataJSONFiles.length} metadata json files` addLogLine(`has ${mediaFiles.length} media files`);
);
logUploadInfo(`has ${mediaFiles.length} media files`);
if (metadataJSONFiles.length) { if (metadataJSONFiles.length) {
UIService.setUploadStage( UIService.setUploadStage(
UPLOAD_STAGES.READING_GOOGLE_METADATA_FILES UPLOAD_STAGES.READING_GOOGLE_METADATA_FILES
@ -129,7 +127,7 @@ class UploadManager {
); );
UIService.setUploadStage(UPLOAD_STAGES.START); UIService.setUploadStage(UPLOAD_STAGES.START);
logUploadInfo(`clusterLivePhotoFiles called`); addLogLine(`clusterLivePhotoFiles called`);
// filter out files whose metadata detection failed or those that have been skipped because the files are too large, // filter out files whose metadata detection failed or those that have been skipped because the files are too large,
// as they will be rejected during upload and are not valid upload files which we need to clustering // as they will be rejected during upload and are not valid upload files which we need to clustering
@ -172,7 +170,7 @@ class UploadManager {
UIService.setHasLivePhoto( UIService.setHasLivePhoto(
mediaFiles.length !== allFiles.length mediaFiles.length !== allFiles.length
); );
logUploadInfo( addLogLine(
`got live photos: ${mediaFiles.length !== allFiles.length}` `got live photos: ${mediaFiles.length !== allFiles.length}`
); );
@ -186,7 +184,7 @@ class UploadManager {
UIService.setPercentComplete(FILE_UPLOAD_COMPLETED); UIService.setPercentComplete(FILE_UPLOAD_COMPLETED);
} catch (e) { } catch (e) {
logError(e, 'uploading failed with error'); logError(e, 'uploading failed with error');
logUploadInfo( addLogLine(
`uploading failed with error -> ${e.message} `uploading failed with error -> ${e.message}
${(e as Error).stack}` ${(e as Error).stack}`
); );
@ -200,7 +198,7 @@ class UploadManager {
private async parseMetadataJSONFiles(metadataFiles: FileWithCollection[]) { private async parseMetadataJSONFiles(metadataFiles: FileWithCollection[]) {
try { try {
logUploadInfo(`parseMetadataJSONFiles function executed `); addLogLine(`parseMetadataJSONFiles function executed `);
UIService.reset(metadataFiles.length); UIService.reset(metadataFiles.length);
@ -210,7 +208,7 @@ class UploadManager {
return; return;
} }
logUploadInfo( addLogLine(
`parsing metadata json file ${getFileNameSize(file)}` `parsing metadata json file ${getFileNameSize(file)}`
); );
@ -226,14 +224,14 @@ class UploadManager {
); );
UIService.increaseFileUploaded(); UIService.increaseFileUploaded();
} }
logUploadInfo( addLogLine(
`successfully parsed metadata json file ${getFileNameSize( `successfully parsed metadata json file ${getFileNameSize(
file file
)}` )}`
); );
} catch (e) { } catch (e) {
logError(e, 'parsing failed for a file'); logError(e, 'parsing failed for a file');
logUploadInfo( addLogLine(
`failed to parse metadata json file ${getFileNameSize( `failed to parse metadata json file ${getFileNameSize(
file file
)} error: ${e.message}` )} error: ${e.message}`
@ -248,7 +246,7 @@ class UploadManager {
private async extractMetadataFromFiles(mediaFiles: FileWithCollection[]) { private async extractMetadataFromFiles(mediaFiles: FileWithCollection[]) {
try { try {
logUploadInfo(`extractMetadataFromFiles executed`); addLogLine(`extractMetadataFromFiles executed`);
UIService.reset(mediaFiles.length); UIService.reset(mediaFiles.length);
for (const { file, localID, collectionID } of mediaFiles) { for (const { file, localID, collectionID } of mediaFiles) {
if (this.isUploadPausing) { if (this.isUploadPausing) {
@ -258,7 +256,7 @@ class UploadManager {
let metadata = null; let metadata = null;
let filePath = null; let filePath = null;
try { try {
logUploadInfo( addLogLine(
`metadata extraction started ${getFileNameSize(file)} ` `metadata extraction started ${getFileNameSize(file)} `
); );
const result = await this.extractFileTypeAndMetadata( const result = await this.extractFileTypeAndMetadata(
@ -268,14 +266,14 @@ class UploadManager {
fileTypeInfo = result.fileTypeInfo; fileTypeInfo = result.fileTypeInfo;
metadata = result.metadata; metadata = result.metadata;
filePath = result.filePath; filePath = result.filePath;
logUploadInfo( addLogLine(
`metadata extraction successful${getFileNameSize( `metadata extraction successful${getFileNameSize(
file file
)} ` )} `
); );
} catch (e) { } catch (e) {
logError(e, 'metadata extraction failed for a file'); logError(e, 'extractFileTypeAndMetadata failed');
logUploadInfo( addLogLine(
`metadata extraction failed ${getFileNameSize( `metadata extraction failed ${getFileNameSize(
file file
)} error: ${e.message}` )} error: ${e.message}`
@ -299,7 +297,7 @@ class UploadManager {
collectionID: number collectionID: number
) { ) {
if (file.size >= MAX_FILE_SIZE_SUPPORTED) { if (file.size >= MAX_FILE_SIZE_SUPPORTED) {
logUploadInfo( addLogLine(
`${getFileNameSize(file)} rejected because of large size` `${getFileNameSize(file)} rejected because of large size`
); );
@ -307,15 +305,17 @@ class UploadManager {
} }
const fileTypeInfo = await UploadService.getFileType(file); const fileTypeInfo = await UploadService.getFileType(file);
if (fileTypeInfo.fileType === FILE_TYPE.OTHERS) { if (fileTypeInfo.fileType === FILE_TYPE.OTHERS) {
logUploadInfo( addLogLine(
`${getFileNameSize( `${getFileNameSize(
file file
)} rejected because of unknown file format` )} rejected because of unknown file format`
); );
return { fileTypeInfo, metadata: null }; return { fileTypeInfo, metadata: null };
} }
logUploadInfo(` extracting ${getFileNameSize(file)} metadata`); addLogLine(` extracting ${getFileNameSize(file)} metadata`);
const metadata = let metadata: Metadata;
try {
metadata =
(await UploadService.extractFileMetadata( (await UploadService.extractFileMetadata(
file, file,
collectionID, collectionID,
@ -323,10 +323,15 @@ class UploadManager {
)) || null; )) || null;
const filePath = (file as any).path as string; const filePath = (file as any).path as string;
return { fileTypeInfo, metadata, filePath }; return { fileTypeInfo, metadata, filePath };
} catch (e) {
logError(e, 'failed to extract file metadata');
return { fileTypeInfo, metadata: null };
}
return { fileTypeInfo, metadata };
} }
private async uploadMediaFiles(mediaFiles: FileWithCollection[]) { private async uploadMediaFiles(mediaFiles: FileWithCollection[]) {
logUploadInfo(`uploadMediaFiles called`); addLogLine(`uploadMediaFiles called`);
this.filesToBeUploaded.push(...mediaFiles); this.filesToBeUploaded.push(...mediaFiles);
if (isElectron()) { if (isElectron()) {
@ -398,7 +403,7 @@ class UploadManager {
) { ) {
try { try {
let decryptedFile: EnteFile; let decryptedFile: EnteFile;
logUploadInfo(`uploadedFile ${JSON.stringify(uploadedFile)}`); addLogLine(`uploadedFile ${JSON.stringify(uploadedFile)}`);
this.updateElectronRemainingFiles(fileWithCollection); this.updateElectronRemainingFiles(fileWithCollection);
switch (fileUploadResult) { switch (fileUploadResult) {
case UPLOAD_RESULT.FAILED: case UPLOAD_RESULT.FAILED:
@ -442,7 +447,7 @@ class UploadManager {
return fileUploadResult; return fileUploadResult;
} catch (e) { } catch (e) {
logError(e, 'failed to do post file upload action'); logError(e, 'failed to do post file upload action');
logUploadInfo( addLogLine(
`failed to do post file upload action -> ${e.message} `failed to do post file upload action -> ${e.message}
${(e as Error).stack}` ${(e as Error).stack}`
); );

View file

@ -12,8 +12,8 @@ import UploadService from './uploadService';
import { FILE_TYPE } from 'constants/file'; import { FILE_TYPE } from 'constants/file';
import { UPLOAD_RESULT, MAX_FILE_SIZE_SUPPORTED } from 'constants/upload'; import { UPLOAD_RESULT, MAX_FILE_SIZE_SUPPORTED } from 'constants/upload';
import { FileWithCollection, BackupedFile, UploadFile } from 'types/upload'; import { FileWithCollection, BackupedFile, UploadFile } from 'types/upload';
import { logUploadInfo } from 'utils/upload'; import { addLogLine } from 'utils/logging';
import { convertBytesToHumanReadable } from 'utils/billing'; import { convertBytesToHumanReadable } from 'utils/file/size';
import { sleep } from 'utils/common'; import { sleep } from 'utils/common';
import { addToCollection } from 'services/collectionService'; import { addToCollection } from 'services/collectionService';
@ -33,7 +33,7 @@ export default async function uploader(
fileWithCollection fileWithCollection
)}_${convertBytesToHumanReadable(UploadService.getAssetSize(uploadAsset))}`; )}_${convertBytesToHumanReadable(UploadService.getAssetSize(uploadAsset))}`;
logUploadInfo(`uploader called for ${fileNameSize}`); addLogLine(`uploader called for ${fileNameSize}`);
UIService.setFileProgress(localID, 0); UIService.setFileProgress(localID, 0);
await sleep(0); await sleep(0);
const { fileTypeInfo, metadata } = const { fileTypeInfo, metadata } =
@ -55,7 +55,7 @@ export default async function uploader(
metadata metadata
); );
if (sameFileInSameCollection) { if (sameFileInSameCollection) {
logUploadInfo(`skipped upload for ${fileNameSize}`); addLogLine(`skipped upload for ${fileNameSize}`);
return { return {
fileUploadResult: UPLOAD_RESULT.ALREADY_UPLOADED, fileUploadResult: UPLOAD_RESULT.ALREADY_UPLOADED,
uploadedFile: sameFileInSameCollection, uploadedFile: sameFileInSameCollection,
@ -68,7 +68,7 @@ export default async function uploader(
); );
if (sameFileInOtherCollection) { if (sameFileInOtherCollection) {
logUploadInfo( addLogLine(
`same file in other collection found for ${fileNameSize}` `same file in other collection found for ${fileNameSize}`
); );
const resultFile = Object.assign({}, sameFileInOtherCollection); const resultFile = Object.assign({}, sameFileInOtherCollection);
@ -84,7 +84,7 @@ export default async function uploader(
// This change allow users to export by albums, upload to ente. And export all photos -> upload files which are not already uploaded // This change allow users to export by albums, upload to ente. And export all photos -> upload files which are not already uploaded
// as part of the albums // as part of the albums
if (shouldDedupeAcrossCollection(fileWithCollection.collection.name)) { if (shouldDedupeAcrossCollection(fileWithCollection.collection.name)) {
logUploadInfo(`deduped upload for ${fileNameSize}`); addLogLine(`deduped upload for ${fileNameSize}`);
const sameFileInOtherCollection = findSameFileInCollection( const sameFileInOtherCollection = findSameFileInCollection(
existingFiles, existingFiles,
metadata metadata
@ -96,7 +96,7 @@ export default async function uploader(
}; };
} }
} }
logUploadInfo(`reading asset ${fileNameSize}`); addLogLine(`reading asset ${fileNameSize}`);
const file = await UploadService.readAsset(fileTypeInfo, uploadAsset); const file = await UploadService.readAsset(fileTypeInfo, uploadAsset);
@ -110,14 +110,14 @@ export default async function uploader(
metadata, metadata,
}; };
logUploadInfo(`encryptAsset ${fileNameSize}`); addLogLine(`encryptAsset ${fileNameSize}`);
const encryptedFile = await UploadService.encryptAsset( const encryptedFile = await UploadService.encryptAsset(
worker, worker,
fileWithMetadata, fileWithMetadata,
collection.key collection.key
); );
logUploadInfo(`uploadToBucket ${fileNameSize}`); addLogLine(`uploadToBucket ${fileNameSize}`);
const backupedFile: BackupedFile = await UploadService.uploadToBucket( const backupedFile: BackupedFile = await UploadService.uploadToBucket(
encryptedFile.file encryptedFile.file
@ -128,12 +128,12 @@ export default async function uploader(
backupedFile, backupedFile,
encryptedFile.fileKey encryptedFile.fileKey
); );
logUploadInfo(`uploadFile ${fileNameSize}`); addLogLine(`uploadFile ${fileNameSize}`);
const uploadedFile = await UploadHttpClient.uploadFile(uploadFile); const uploadedFile = await UploadHttpClient.uploadFile(uploadFile);
UIService.increaseFileUploaded(); UIService.increaseFileUploaded();
logUploadInfo(`${fileNameSize} successfully uploaded`); addLogLine(`${fileNameSize} successfully uploaded`);
return { return {
fileUploadResult: metadata.hasStaticThumbnail fileUploadResult: metadata.hasStaticThumbnail
@ -142,9 +142,7 @@ export default async function uploader(
uploadedFile: uploadedFile, uploadedFile: uploadedFile,
}; };
} catch (e) { } catch (e) {
logUploadInfo( addLogLine(`upload failed for ${fileNameSize} ,error: ${e.message}`);
`upload failed for ${fileNameSize} ,error: ${e.message}`
);
logError(e, 'file upload failed', { logError(e, 'file upload failed', {
fileFormat: fileTypeInfo?.exactType, fileFormat: fileTypeInfo?.exactType,

View file

@ -2,28 +2,28 @@ import { NULL_EXTRACTED_METADATA } from 'constants/upload';
import ffmpegService from 'services/ffmpeg/ffmpegService'; import ffmpegService from 'services/ffmpeg/ffmpegService';
import { ElectronFile } from 'types/upload'; import { ElectronFile } from 'types/upload';
import { logError } from 'utils/sentry'; import { logError } from 'utils/sentry';
import { getFileNameSize, logUploadInfo } from 'utils/upload'; import { getFileNameSize, addLogLine } from 'utils/logging';
export async function getVideoMetadata(file: File | ElectronFile) { export async function getVideoMetadata(file: File | ElectronFile) {
let videoMetadata = NULL_EXTRACTED_METADATA; let videoMetadata = NULL_EXTRACTED_METADATA;
try { try {
logUploadInfo(`getVideoMetadata called for ${getFileNameSize(file)}`); addLogLine(`getVideoMetadata called for ${getFileNameSize(file)}`);
if (!(file instanceof File)) { if (!(file instanceof File)) {
logUploadInfo('get file blob for video metadata extraction'); addLogLine('get file blob for video metadata extraction');
file = new File([await file.blob()], file.name, { file = new File([await file.blob()], file.name, {
lastModified: file.lastModified, lastModified: file.lastModified,
}); });
logUploadInfo( addLogLine(
'get file blob for video metadata extraction successfully' 'get file blob for video metadata extraction successfully'
); );
} }
videoMetadata = await ffmpegService.extractMetadata(file); videoMetadata = await ffmpegService.extractMetadata(file);
logUploadInfo( addLogLine(
`videoMetadata successfully extracted ${getFileNameSize(file)}` `videoMetadata successfully extracted ${getFileNameSize(file)}`
); );
} catch (e) { } catch (e) {
logError(e, 'failed to get video metadata'); logError(e, 'failed to get video metadata');
logUploadInfo( addLogLine(
`videoMetadata extracted failed ${getFileNameSize(file)} ,${ `videoMetadata extracted failed ${getFileNameSize(file)} ,${
e.message e.message
} ` } `

View file

@ -38,19 +38,6 @@ export function convertBytesToGBs(bytes: number, precision = 0): string {
return (bytes / (1024 * 1024 * 1024)).toFixed(precision); return (bytes / (1024 * 1024 * 1024)).toFixed(precision);
} }
export function convertBytesToHumanReadable(
bytes: number,
precision = 2
): string {
if (bytes === 0) {
return '0 MB';
}
const i = Math.floor(Math.log(bytes) / Math.log(1024));
const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
return (bytes / Math.pow(1024, i)).toFixed(precision) + ' ' + sizes[i];
}
export function makeHumanReadableStorage( export function makeHumanReadableStorage(
bytes: number, bytes: number,
round: 'round-up' | 'round-down' = 'round-down' round: 'round-up' | 'round-down' = 'round-down'

View file

@ -195,44 +195,6 @@ export function formatDate(date: number | Date) {
return dateTimeFormat.format(date); return dateTimeFormat.format(date);
} }
export function formatDateTime(date: number | Date) {
const dateTimeFormat = new Intl.DateTimeFormat('en-IN', {
weekday: 'short',
year: 'numeric',
month: 'long',
day: 'numeric',
});
const timeFormat = new Intl.DateTimeFormat('en-IN', {
timeStyle: 'short',
});
return `${dateTimeFormat.format(date)} ${timeFormat.format(date)}`;
}
export function formatDateRelative(date: number) {
const units = {
year: 24 * 60 * 60 * 1000 * 365,
month: (24 * 60 * 60 * 1000 * 365) / 12,
day: 24 * 60 * 60 * 1000,
hour: 60 * 60 * 1000,
minute: 60 * 1000,
second: 1000,
};
const relativeDateFormat = new Intl.RelativeTimeFormat('en-IN', {
localeMatcher: 'best fit',
numeric: 'always',
style: 'long',
});
const elapsed = date - Date.now();
// "Math.abs" accounts for both "past" & "future" scenarios
for (const u in units)
if (Math.abs(elapsed) > units[u] || u === 'second')
return relativeDateFormat.format(
Math.round(elapsed / units[u]),
u as Intl.RelativeTimeFormatUnit
);
}
export function sortFiles(files: EnteFile[]) { export function sortFiles(files: EnteFile[]) {
// sort according to modification time first // sort according to modification time first
files = files.sort((a, b) => { files = files.sort((a, b) => {
@ -352,8 +314,8 @@ export function splitFilenameAndExtension(filename: string): [string, string] {
]; ];
} }
export function getFileExtension(filename) { export function getFileExtension(filename: string) {
return splitFilenameAndExtension(filename)[1]; return splitFilenameAndExtension(filename)[1]?.toLocaleLowerCase();
} }
export function generateStreamFromArrayBuffer(data: Uint8Array) { export function generateStreamFromArrayBuffer(data: Uint8Array) {

12
src/utils/file/size.ts Normal file
View file

@ -0,0 +1,12 @@
export function convertBytesToHumanReadable(
bytes: number,
precision = 2
): string {
if (bytes === 0) {
return '0 MB';
}
const i = Math.floor(Math.log(bytes) / Math.log(1024));
const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
return (bytes / Math.pow(1024, i)).toFixed(precision) + ' ' + sizes[i];
}

View file

@ -0,0 +1,21 @@
import { ElectronFile } from 'types/upload';
import { convertBytesToHumanReadable } from 'utils/file/size';
import { formatDateTime } from 'utils/time';
import { saveLogLine, getLogs } from 'utils/storage';
export function addLogLine(log: string) {
saveLogLine({
timestamp: Date.now(),
logLine: log,
});
}
export function getDebugLogs() {
return getLogs().map(
(log) => `[${formatDateTime(log.timestamp)}] ${log.logLine}`
);
}
export function getFileNameSize(file: File | ElectronFile) {
return `${file.name}_${convertBytesToHumanReadable(file.size)}`;
}

View file

@ -1,5 +1,6 @@
import * as Sentry from '@sentry/nextjs'; import * as Sentry from '@sentry/nextjs';
import { getUserAnonymizedID } from 'utils/user'; import { addLogLine } from 'utils/logging';
import { getSentryUserID } from 'utils/user';
export const logError = ( export const logError = (
error: any, error: any,
@ -10,12 +11,17 @@ export const logError = (
return; return;
} }
const err = errorWithContext(error, msg); const err = errorWithContext(error, msg);
addLogLine(
`error: ${error?.name} ${error?.message} ${
error?.stack
} msg: ${msg} info: ${JSON.stringify(info)}`
);
if (!process.env.NEXT_PUBLIC_SENTRY_ENV) { if (!process.env.NEXT_PUBLIC_SENTRY_ENV) {
console.log(error, { msg, info }); console.log(error, { msg, info });
} }
Sentry.captureException(err, { Sentry.captureException(err, {
level: Sentry.Severity.Info, level: Sentry.Severity.Info,
user: { id: getUserAnonymizedID() }, user: { id: getSentryUserID() },
contexts: { contexts: {
...(info && { ...(info && {
info: info, info: info,

View file

@ -1,7 +1,6 @@
import { getData, LS_KEYS, setData } from './localStorage'; import { getData, LS_KEYS, setData } from './localStorage';
export interface Log { export interface Log {
type: string;
timestamp: number; timestamp: number;
logLine: string; logLine: string;
} }

View file

@ -12,7 +12,7 @@ export enum LS_KEYS {
JUST_SIGNED_UP = 'justSignedUp', JUST_SIGNED_UP = 'justSignedUp',
SHOW_BACK_BUTTON = 'showBackButton', SHOW_BACK_BUTTON = 'showBackButton',
EXPORT = 'export', EXPORT = 'export',
AnonymizeUserID = 'anonymizedUserID', AnonymizedUserID = 'anonymizedUserID',
THUMBNAIL_FIX_STATE = 'thumbnailFixState', THUMBNAIL_FIX_STATE = 'thumbnailFixState',
LIVE_PHOTO_INFO_SHOWN_COUNT = 'livePhotoInfoShownCount', LIVE_PHOTO_INFO_SHOWN_COUNT = 'livePhotoInfoShownCount',
LOGS = 'logs', LOGS = 'logs',

View file

@ -163,3 +163,39 @@ function getDateFromComponents(dateComponent: DateComponent<string>) {
? new Date(year, month, day, hour, minute, second) ? new Date(year, month, day, hour, minute, second)
: new Date(year, month, day); : new Date(year, month, day);
} }
export function formatDateTime(date: number | Date) {
const dateTimeFormat = new Intl.DateTimeFormat('en-IN', {
weekday: 'short',
year: 'numeric',
month: 'long',
day: 'numeric',
});
const timeFormat = new Intl.DateTimeFormat('en-IN', {
timeStyle: 'short',
});
return `${dateTimeFormat.format(date)} ${timeFormat.format(date)}`;
}
export function formatDateRelative(date: number) {
const units = {
year: 24 * 60 * 60 * 1000 * 365,
month: (24 * 60 * 60 * 1000 * 365) / 12,
day: 24 * 60 * 60 * 1000,
hour: 60 * 60 * 1000,
minute: 60 * 1000,
second: 1000,
};
const relativeDateFormat = new Intl.RelativeTimeFormat('en-IN', {
localeMatcher: 'best fit',
numeric: 'always',
style: 'long',
});
const elapsed = date - Date.now(); // "Math.abs" accounts for both "past" & "future" scenarios
for (const u in units)
if (Math.abs(elapsed) > units[u] || u === 'second')
return relativeDateFormat.format(
Math.round(elapsed / units[u]),
u as Intl.RelativeTimeFormatUnit
);
}

View file

@ -1,8 +1,5 @@
import { ElectronFile, FileWithCollection, Metadata } from 'types/upload'; import { FileWithCollection, Metadata } from 'types/upload';
import { EnteFile } from 'types/file'; import { EnteFile } from 'types/file';
import { convertBytesToHumanReadable } from 'utils/billing';
import { formatDateTime } from 'utils/file';
import { getLogs, saveLogLine } from 'utils/storage';
import { A_SEC_IN_MICROSECONDS } from 'constants/upload'; import { A_SEC_IN_MICROSECONDS } from 'constants/upload';
import { FILE_TYPE } from 'constants/file'; import { FILE_TYPE } from 'constants/file';
@ -117,24 +114,6 @@ export function segregateMetadataAndMediaFiles(
return { mediaFiles, metadataJSONFiles }; return { mediaFiles, metadataJSONFiles };
} }
export function logUploadInfo(log: string) {
saveLogLine({
type: 'upload',
timestamp: Date.now(),
logLine: log,
});
}
export function getUploadLogs() {
return getLogs()
.filter((log) => log.type === 'upload')
.map((log) => `[${formatDateTime(log.timestamp)}] ${log.logLine}`);
}
export function getFileNameSize(file: File | ElectronFile) {
return `${file.name}_${convertBytesToHumanReadable(file.size)}`;
}
export function areFileWithCollectionsSame( export function areFileWithCollectionsSame(
firstFile: FileWithCollection, firstFile: FileWithCollection,
secondFile: FileWithCollection secondFile: FileWithCollection

View file

@ -14,11 +14,11 @@ export function makeID(length) {
return result; return result;
} }
export function getUserAnonymizedID() { export function getSentryUserID() {
let anonymizeUserID = getData(LS_KEYS.AnonymizeUserID)?.id; let anonymizeUserID = getData(LS_KEYS.AnonymizedUserID)?.id;
if (!anonymizeUserID) { if (!anonymizeUserID) {
anonymizeUserID = makeID(6); anonymizeUserID = makeID(6);
setData(LS_KEYS.AnonymizeUserID, { id: anonymizeUserID }); setData(LS_KEYS.AnonymizedUserID, { id: anonymizeUserID });
} }
return anonymizeUserID; return anonymizeUserID;
} }