diff --git a/src/components/ExportModal.tsx b/src/components/ExportModal.tsx
index 0450b43fe..ef336534d 100644
--- a/src/components/ExportModal.tsx
+++ b/src/components/ExportModal.tsx
@@ -70,14 +70,24 @@ export default function ExportModal(props: Props) {
if (!isElectron()) {
return;
}
- setExportFolder(getData(LS_KEYS.EXPORT)?.folder);
+ try {
+ setExportFolder(getData(LS_KEYS.EXPORT)?.folder);
- exportService.electronAPIs.registerStopExportListener(stopExport);
- exportService.electronAPIs.registerPauseExportListener(pauseExport);
- exportService.electronAPIs.registerResumeExportListener(resumeExport);
- exportService.electronAPIs.registerRetryFailedExportListener(
- retryFailedExport
- );
+ exportService.electronAPIs.registerStopExportListener(
+ stopExportHandler
+ );
+ exportService.electronAPIs.registerPauseExportListener(
+ pauseExportHandler
+ );
+ exportService.electronAPIs.registerResumeExportListener(
+ resumeExportHandler
+ );
+ exportService.electronAPIs.registerRetryFailedExportListener(
+ retryFailedExportHandler
+ );
+ } catch (e) {
+ logError(e, 'error in exportModal');
+ }
}, []);
useEffect(() => {
@@ -85,19 +95,25 @@ export default function ExportModal(props: Props) {
return;
}
const main = async () => {
- const exportInfo = await exportService.getExportRecord();
- setExportStage(exportInfo?.stage ?? ExportStage.INIT);
- setLastExportTime(exportInfo?.lastAttemptTimestamp);
- setExportProgress(exportInfo?.progress ?? { current: 0, total: 0 });
- setExportStats({
- success: exportInfo?.exportedFiles?.length ?? 0,
- failed: exportInfo?.failedFiles?.length ?? 0,
- });
- if (exportInfo?.stage === ExportStage.INPROGRESS) {
- resumeExport();
+ try {
+ const exportInfo = await exportService.getExportRecord();
+ setExportStage(exportInfo?.stage ?? ExportStage.INIT);
+ setLastExportTime(exportInfo?.lastAttemptTimestamp);
+ setExportProgress(
+ exportInfo?.progress ?? { current: 0, total: 0 }
+ );
+ setExportStats({
+ success: exportInfo?.exportedFiles?.length ?? 0,
+ failed: exportInfo?.failedFiles?.length ?? 0,
+ });
+ if (exportInfo?.stage === ExportStage.INPROGRESS) {
+ await resumeExport();
+ }
+ } catch (e) {
+ logError(e, 'error handling exportFolder change');
}
};
- main();
+ void main();
}, [exportFolder]);
useEffect(() => {
@@ -117,7 +133,7 @@ export default function ExportModal(props: Props) {
const failedFilesCnt = exportRecord.failedFiles?.length;
const syncedFilesCnt = userPersonalFiles.length;
if (syncedFilesCnt > exportedFileCnt + failedFilesCnt) {
- updateExportProgress({
+ await updateExportProgress({
current: exportedFileCnt + failedFilesCnt,
total: syncedFilesCnt,
});
@@ -131,11 +147,11 @@ export default function ExportModal(props: Props) {
getExportRecordFileUID(file)
)
);
- exportService.addFilesQueuedRecord(
+ await exportService.addFilesQueuedRecord(
exportFolder,
unExportedFiles
);
- updateExportStage(ExportStage.PAUSED);
+ await updateExportStage(ExportStage.PAUSED);
}
} catch (e) {
setExportStage(ExportStage.INIT);
@@ -143,7 +159,7 @@ export default function ExportModal(props: Props) {
}
}
};
- main();
+ void main();
}, [props.show]);
useEffect(() => {
@@ -158,19 +174,21 @@ export default function ExportModal(props: Props) {
setData(LS_KEYS.EXPORT, { folder: newFolder });
};
- const updateExportStage = (newStage: ExportStage) => {
+ const updateExportStage = async (newStage: ExportStage) => {
setExportStage(newStage);
- exportService.updateExportRecord({ stage: newStage });
+ await exportService.updateExportRecord({ stage: newStage });
};
- const updateExportTime = (newTime: number) => {
+ const updateExportTime = async (newTime: number) => {
setLastExportTime(newTime);
- exportService.updateExportRecord({ lastAttemptTimestamp: newTime });
+ await exportService.updateExportRecord({
+ lastAttemptTimestamp: newTime,
+ });
};
- const updateExportProgress = (newProgress: ExportProgress) => {
+ const updateExportProgress = async (newProgress: ExportProgress) => {
setExportProgress(newProgress);
- exportService.updateExportRecord({ progress: newProgress });
+ await exportService.updateExportRecord({ progress: newProgress });
};
// ======================
@@ -182,15 +200,15 @@ export default function ExportModal(props: Props) {
if (!exportFolder) {
await selectExportDirectory();
}
- updateExportStage(ExportStage.INPROGRESS);
+ await updateExportStage(ExportStage.INPROGRESS);
await sleep(100);
};
const postExportRun = async (exportResult?: { paused?: boolean }) => {
if (!exportResult?.paused) {
- updateExportStage(ExportStage.FINISHED);
+ await updateExportStage(ExportStage.FINISHED);
await sleep(100);
- updateExportTime(Date.now());
- syncExportStatsWithRecord();
+ await updateExportTime(Date.now());
+ await syncExportStatsWithRecord();
}
};
@@ -217,7 +235,7 @@ export default function ExportModal(props: Props) {
const startExport = async () => {
try {
await preExportRun();
- updateExportProgress({ current: 0, total: 0 });
+ await updateExportProgress({ current: 0, total: 0 });
const exportResult = await exportService.exportFiles(
updateExportProgress,
ExportType.NEW
@@ -233,7 +251,7 @@ export default function ExportModal(props: Props) {
const stopExport = async () => {
try {
exportService.stopRunningExport();
- postExportRun();
+ await postExportRun();
} catch (e) {
if (e.message !== CustomError.REQUEST_CANCELLED) {
logError(e, 'stopExport failed');
@@ -241,11 +259,11 @@ export default function ExportModal(props: Props) {
}
};
- const pauseExport = () => {
+ const pauseExport = async () => {
try {
- updateExportStage(ExportStage.PAUSED);
+ await updateExportStage(ExportStage.PAUSED);
exportService.pauseRunningExport();
- postExportRun({ paused: true });
+ await postExportRun({ paused: true });
} catch (e) {
if (e.message !== CustomError.REQUEST_CANCELLED) {
logError(e, 'pauseExport failed');
@@ -282,7 +300,10 @@ export default function ExportModal(props: Props) {
const retryFailedExport = async () => {
try {
await preExportRun();
- updateExportProgress({ current: 0, total: exportStats.failed });
+ await updateExportProgress({
+ current: 0,
+ total: exportStats.failed,
+ });
const exportResult = await exportService.exportFiles(
updateExportProgress,
@@ -296,10 +317,26 @@ export default function ExportModal(props: Props) {
}
};
+ const startExportHandler = () => {
+ void startExport();
+ };
+ const stopExportHandler = () => {
+ void stopExport();
+ };
+ const pauseExportHandler = () => {
+ void pauseExport();
+ };
+ const resumeExportHandler = () => {
+ void resumeExport();
+ };
+ const retryFailedExportHandler = () => {
+ void retryFailedExport();
+ };
+
const ExportDynamicContent = () => {
switch (exportStage) {
case ExportStage.INIT:
- return ;
+ return ;
case ExportStage.INPROGRESS:
case ExportStage.PAUSED:
@@ -307,9 +344,9 @@ export default function ExportModal(props: Props) {
);
case ExportStage.FINISHED:
@@ -318,8 +355,8 @@ export default function ExportModal(props: Props) {
onHide={props.onHide}
lastExportTime={lastExportTime}
exportStats={exportStats}
- exportFiles={startExport}
- retryFailed={retryFailedExport}
+ exportFiles={startExportHandler}
+ retryFailed={retryFailedExportHandler}
/>
);
diff --git a/src/services/exportService.ts b/src/services/exportService.ts
index 68a905dc6..87b6339a3 100644
--- a/src/services/exportService.ts
+++ b/src/services/exportService.ts
@@ -49,6 +49,7 @@ import { User } from 'types/user';
import { FILE_TYPE, TYPE_JPEG, TYPE_JPG } from 'constants/file';
import { ExportType, ExportNotification, RecordType } from 'constants/export';
import { ElectronAPIs } from 'types/electron';
+import { CustomError } from 'utils/error';
const LATEST_EXPORT_VERSION = 1;
const EXPORT_RECORD_FILE_NAME = 'export_status.json';
@@ -83,10 +84,11 @@ class ExportService {
this.pauseExport = true;
}
async exportFiles(
- updateProgress: (progress: ExportProgress) => void,
+ updateProgress: (progress: ExportProgress) => Promise,
exportType: ExportType
) {
try {
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
if (this.exportInProgress) {
this.electronAPIs.sendNotification(
ExportNotification.IN_PROGRESS
@@ -177,7 +179,7 @@ class ExportService {
newCollections: Collection[],
renamedCollections: Collection[],
collectionIDPathMap: CollectionIDPathMap,
- updateProgress: (progress: ExportProgress) => void,
+ updateProgress: (progress: ExportProgress) => Promise,
exportDir: string
): Promise<{ paused: boolean }> {
try {
@@ -212,7 +214,7 @@ class ExportService {
this.electronAPIs.showOnTray({
export_progress: `0 / ${files.length} files exported`,
});
- updateProgress({
+ await updateProgress({
current: 0,
total: files.length,
});
@@ -239,23 +241,28 @@ class ExportService {
RecordType.SUCCESS
);
} catch (e) {
+ logError(e, 'export failed for a file');
+ if (
+ e.message ===
+ CustomError.ADD_FILE_EXPORTED_RECORD_FAILED
+ ) {
+ throw e;
+ }
await this.addFileExportedRecord(
exportDir,
file,
RecordType.FAILED
);
-
- logError(
- e,
- 'download and save failed for file during export'
- );
}
this.electronAPIs.showOnTray({
export_progress: `${index + 1} / ${
files.length
} files exported`,
});
- updateProgress({ current: index + 1, total: files.length });
+ await updateProgress({
+ current: index + 1,
+ total: files.length,
+ });
}
if (this.stopExport) {
this.electronAPIs.sendNotification(ExportNotification.ABORT);
@@ -266,7 +273,7 @@ class ExportService {
} else if (failedFileCount > 0) {
this.electronAPIs.sendNotification(ExportNotification.FAILED);
this.electronAPIs.showOnTray({
- retry_export: `export failed - retry export`,
+ retry_export: `Retry failed exports`,
});
} else {
this.electronAPIs.sendNotification(ExportNotification.FINISH);
@@ -289,32 +296,37 @@ class ExportService {
file: EnteFile,
type: RecordType
) {
- const fileUID = getExportRecordFileUID(file);
- const exportRecord = await this.getExportRecord(folder);
- exportRecord.queuedFiles = exportRecord.queuedFiles.filter(
- (queuedFilesUID) => queuedFilesUID !== fileUID
- );
- if (type === RecordType.SUCCESS) {
- if (!exportRecord.exportedFiles) {
- exportRecord.exportedFiles = [];
- }
- exportRecord.exportedFiles.push(fileUID);
- exportRecord.failedFiles &&
- (exportRecord.failedFiles = exportRecord.failedFiles.filter(
- (FailedFileUID) => FailedFileUID !== fileUID
- ));
- } else {
- if (!exportRecord.failedFiles) {
- exportRecord.failedFiles = [];
- }
- if (!exportRecord.failedFiles.find((x) => x === fileUID)) {
- exportRecord.failedFiles.push(fileUID);
+ try {
+ const fileUID = getExportRecordFileUID(file);
+ const exportRecord = await this.getExportRecord(folder);
+ exportRecord.queuedFiles = exportRecord.queuedFiles.filter(
+ (queuedFilesUID) => queuedFilesUID !== fileUID
+ );
+ if (type === RecordType.SUCCESS) {
+ if (!exportRecord.exportedFiles) {
+ exportRecord.exportedFiles = [];
+ }
+ exportRecord.exportedFiles.push(fileUID);
+ exportRecord.failedFiles &&
+ (exportRecord.failedFiles = exportRecord.failedFiles.filter(
+ (FailedFileUID) => FailedFileUID !== fileUID
+ ));
+ } else {
+ if (!exportRecord.failedFiles) {
+ exportRecord.failedFiles = [];
+ }
+ if (!exportRecord.failedFiles.find((x) => x === fileUID)) {
+ exportRecord.failedFiles.push(fileUID);
+ }
}
+ exportRecord.exportedFiles = dedupe(exportRecord.exportedFiles);
+ exportRecord.queuedFiles = dedupe(exportRecord.queuedFiles);
+ exportRecord.failedFiles = dedupe(exportRecord.failedFiles);
+ await this.updateExportRecord(exportRecord, folder);
+ } catch (e) {
+ logError(e, 'addFileExportedRecord failed');
+ throw Error(CustomError.ADD_FILE_EXPORTED_RECORD_FAILED);
}
- exportRecord.exportedFiles = dedupe(exportRecord.exportedFiles);
- exportRecord.queuedFiles = dedupe(exportRecord.queuedFiles);
- exportRecord.failedFiles = dedupe(exportRecord.failedFiles);
- await this.updateExportRecord(exportRecord, folder);
}
async addCollectionExportedRecord(
@@ -354,6 +366,7 @@ class ExportService {
);
} catch (e) {
logError(e, 'error updating Export Record');
+ throw e;
}
}
@@ -372,6 +385,7 @@ class ExportService {
}
} catch (e) {
logError(e, 'export Record JSON parsing failed ');
+ throw e;
}
}
@@ -428,36 +442,45 @@ class ExportService {
}
async downloadAndSave(file: EnteFile, collectionPath: string) {
- file.metadata = mergeMetadata([file])[0].metadata;
- const fileSaveName = getUniqueFileSaveName(
- collectionPath,
- file.metadata.title,
- file.id
- );
- let fileStream = await retryAsyncFunction(() =>
- downloadManager.downloadFile(file)
- );
- const fileType = getFileExtension(file.metadata.title);
- if (
- file.pubMagicMetadata?.data.editedTime &&
- (fileType === TYPE_JPEG || fileType === TYPE_JPG)
- ) {
- const fileBlob = await new Response(fileStream).blob();
- if (!this.fileReader) {
- this.fileReader = new FileReader();
- }
- const updatedFileBlob = await updateFileCreationDateInEXIF(
- this.fileReader,
- fileBlob,
- new Date(file.pubMagicMetadata.data.editedTime / 1000)
+ try {
+ file.metadata = mergeMetadata([file])[0].metadata;
+ const fileSaveName = getUniqueFileSaveName(
+ collectionPath,
+ file.metadata.title,
+ file.id
);
- fileStream = updatedFileBlob.stream();
- }
- if (file.metadata.fileType === FILE_TYPE.LIVE_PHOTO) {
- await this.exportMotionPhoto(fileStream, file, collectionPath);
- } else {
- await this.saveMediaFile(collectionPath, fileSaveName, fileStream);
- await this.saveMetadataFile(collectionPath, fileSaveName, file);
+ let fileStream = await retryAsyncFunction(() =>
+ downloadManager.downloadFile(file)
+ );
+ const fileType = getFileExtension(file.metadata.title);
+ if (
+ file.pubMagicMetadata?.data.editedTime &&
+ (fileType === TYPE_JPEG || fileType === TYPE_JPG)
+ ) {
+ const fileBlob = await new Response(fileStream).blob();
+ if (!this.fileReader) {
+ this.fileReader = new FileReader();
+ }
+ const updatedFileBlob = await updateFileCreationDateInEXIF(
+ this.fileReader,
+ fileBlob,
+ new Date(file.pubMagicMetadata.data.editedTime / 1000)
+ );
+ fileStream = updatedFileBlob.stream();
+ }
+ if (file.metadata.fileType === FILE_TYPE.LIVE_PHOTO) {
+ await this.exportMotionPhoto(fileStream, file, collectionPath);
+ } else {
+ await this.saveMediaFile(
+ collectionPath,
+ fileSaveName,
+ fileStream
+ );
+ await this.saveMetadataFile(collectionPath, fileSaveName, file);
+ }
+ } catch (e) {
+ logError(e, 'download and save failed');
+ throw e;
}
}
diff --git a/src/utils/error/index.ts b/src/utils/error/index.ts
index 557d7c7f2..a4bf9a7c2 100644
--- a/src/utils/error/index.ts
+++ b/src/utils/error/index.ts
@@ -55,6 +55,7 @@ export const CustomError = {
'Windows native image processing is not supported',
NETWORK_ERROR: 'Network Error',
NOT_FILE_OWNER: 'not file owner',
+ ADD_FILE_EXPORTED_RECORD_FAILED: 'add file exported record failed',
};
export function parseUploadErrorCodes(error) {