diff --git a/web/apps/photos/src/services/export/index.ts b/web/apps/photos/src/services/export/index.ts index fbc4e98741..d1b614dd17 100644 --- a/web/apps/photos/src/services/export/index.ts +++ b/web/apps/photos/src/services/export/index.ts @@ -485,6 +485,7 @@ class ExportService { renamedCollections: Collection[], isCanceled: CancellationStatus, ) { + const fs = ensureElectron().fs; try { for (const collection of renamedCollections) { try { @@ -498,6 +499,7 @@ class ExportService { const newCollectionExportName = await safeDirectoryName( exportFolder, getCollectionUserFacingName(collection), + fs.exists, ); log.info( `renaming collection with id ${collection.id} from ${oldCollectionExportName} to ${newCollectionExportName}`, @@ -513,7 +515,7 @@ class ExportService { newCollectionExportName, ); try { - await ensureElectron().fs.rename( + await fs.rename( oldCollectionExportPath, newCollectionExportPath, ); @@ -1017,15 +1019,17 @@ class ExportService { collectionID: number, collectionIDNameMap: Map, ) { + const electron = ensureElectron(); await this.verifyExportFolderExists(exportFolder); const collectionName = collectionIDNameMap.get(collectionID); const collectionExportName = await safeDirectoryName( exportFolder, collectionName, + electron.fs.exists, ); const collectionExportPath = `${exportFolder}/${collectionExportName}`; - await ensureElectron().checkExistsAndCreateDir(collectionExportPath); - await ensureElectron().checkExistsAndCreateDir( + await electron.checkExistsAndCreateDir(collectionExportPath); + await electron.checkExistsAndCreateDir( getMetadataFolderExportPath(collectionExportPath), ); @@ -1037,6 +1041,7 @@ class ExportService { collectionExportPath: string, file: EnteFile, ): Promise { + const electron = ensureElectron(); try { const fileUID = getExportRecordFileUID(file); const originalFileStream = await downloadManager.getFile(file); @@ -1060,6 +1065,7 @@ class ExportService { const fileExportName = await safeFileName( collectionExportPath, file.metadata.title, + electron.fs.exists, ); await this.addFileExportedRecord( exportDir, @@ -1072,7 +1078,7 @@ class ExportService { fileExportName, file, ); - await ensureElectron().saveStreamToDisk( + await electron.saveStreamToDisk( `${collectionExportPath}/${fileExportName}`, updatedFileStream, ); @@ -1094,15 +1100,18 @@ class ExportService { fileStream: ReadableStream, file: EnteFile, ) { + const electron = ensureElectron(); const fileBlob = await new Response(fileStream).blob(); const livePhoto = await decodeLivePhoto(file, fileBlob); const imageExportName = await safeFileName( collectionExportPath, livePhoto.imageNameTitle, + electron.fs.exists, ); const videoExportName = await safeFileName( collectionExportPath, livePhoto.videoNameTitle, + electron.fs.exists, ); const livePhotoExportName = getLivePhotoExportName( imageExportName, @@ -1120,7 +1129,7 @@ class ExportService { imageExportName, file, ); - await ensureElectron().saveStreamToDisk( + await electron.saveStreamToDisk( `${collectionExportPath}/${imageExportName}`, imageStream, ); diff --git a/web/apps/photos/src/services/export/migration.ts b/web/apps/photos/src/services/export/migration.ts index 50e4218da0..886c516147 100644 --- a/web/apps/photos/src/services/export/migration.ts +++ b/web/apps/photos/src/services/export/migration.ts @@ -208,6 +208,7 @@ async function migrateCollectionFolders( const newCollectionExportPath = await safeDirectoryName( exportDir, collection.name, + fs.exists, ); collectionIDPathMap.set(collection.id, newCollectionExportPath); if (!(await fs.exists(oldCollectionExportPath))) continue; @@ -228,6 +229,7 @@ async function migrateFiles( files: EnteFile[], collectionIDPathMap: Map, ) { + const fs = ensureElectron().fs; for (const file of files) { const collectionPath = collectionIDPathMap.get(file.collectionID); const metadataPath = `${collectionPath}/${exportMetadataDirectoryName}`; @@ -239,6 +241,7 @@ async function migrateFiles( const newFileName = await safeFileName( collectionPath, file.metadata.title, + fs.exists, ); const newFilePath = `${collectionPath}/${newFileName}`; const newFileMetadataPath = `${metadataPath}/${newFileName}.json`; diff --git a/web/apps/photos/src/utils/collection/index.ts b/web/apps/photos/src/utils/collection/index.ts index b57f9799f3..be72a7d0d2 100644 --- a/web/apps/photos/src/utils/collection/index.ts +++ b/web/apps/photos/src/utils/collection/index.ts @@ -1,3 +1,4 @@ +import { ensureElectron } from "@/next/electron"; import log from "@/next/log"; import { CustomError } from "@ente/shared/error"; import { getAlbumsURL } from "@ente/shared/network/api"; @@ -172,6 +173,7 @@ async function createCollectionDownloadFolder( const collectionDownloadName = await safeDirectoryName( downloadDirPath, collectionName, + ensureElectron().fs.exists, ); const collectionDownloadPath = `${downloadDirPath}/${collectionDownloadName}`; await exportService.checkExistsAndCreateDir(collectionDownloadPath); diff --git a/web/apps/photos/src/utils/file/index.ts b/web/apps/photos/src/utils/file/index.ts index 42b859772c..2a3b156f2f 100644 --- a/web/apps/photos/src/utils/file/index.ts +++ b/web/apps/photos/src/utils/file/index.ts @@ -815,6 +815,7 @@ async function downloadFileDesktop( const imageExportName = await safeFileName( downloadPath, livePhoto.imageNameTitle, + electron.fs.exists, ); const imageStream = generateStreamFromArrayBuffer(livePhoto.image); await electron.saveStreamToDisk( @@ -825,6 +826,7 @@ async function downloadFileDesktop( const videoExportName = await safeFileName( downloadPath, livePhoto.videoNameTitle, + electron.fs.exists, ); const videoStream = generateStreamFromArrayBuffer(livePhoto.video); await electron.saveStreamToDisk( @@ -839,6 +841,7 @@ async function downloadFileDesktop( const fileExportName = await safeFileName( downloadPath, file.metadata.title, + electron.fs.exists, ); await electron.saveStreamToDisk( `${downloadPath}/${fileExportName}`, diff --git a/web/apps/photos/src/utils/native-fs.ts b/web/apps/photos/src/utils/native-fs.ts index 01b7b18d57..2ef8963022 100644 --- a/web/apps/photos/src/utils/native-fs.ts +++ b/web/apps/photos/src/utils/native-fs.ts @@ -1,12 +1,10 @@ /** - * @file Native filesystem access using custom Node.js functionality provided by - * our desktop app. + * @file Utilities for native filesystem access. * - * Precondition: Unless mentioned otherwise, the functions in these file only - * work when we are running in our desktop app. + * While they don't have any direct dependencies to our desktop app, they were + * written for use by the code that runs in our desktop app. */ -import { ensureElectron } from "@/next/electron"; import { nameAndExtension } from "@/next/file"; import sanitize from "sanitize-filename"; import { @@ -31,15 +29,15 @@ export const sanitizeFilename = (s: string) => * We also ensure we don't return names which might collide with our own special * directories. * - * This function only works when we are running inside an electron app (since it - * requires permissionless access to the native filesystem to find a new - * filename that doesn't conflict with any existing items). + * @param exists A function to check if an item already exists at the given + * path. Usually, you'd pass `fs.exists` from {@link Electron}. * - * See also: {@link safeDirectoryName} + * See also: {@link safeFileame} */ export const safeDirectoryName = async ( directoryPath: string, name: string, + exists: (path: string) => Promise, ): Promise => { const specialDirectoryNames = [ exportTrashDirectoryName, @@ -62,10 +60,13 @@ export const safeDirectoryName = async ( * Return a new sanitized and unique file name based on {@link name} that is not * the same as any existing item in the given {@link directoryPath}. * - * This function only works when we are running inside an electron app. - * @see {@link safeDirectoryName}. + * This is a sibling of {@link safeDirectoryName} for use with file names. */ -export const safeFileName = async (directoryPath: string, name: string) => { +export const safeFileName = async ( + directoryPath: string, + name: string, + exists: (path: string) => Promise, +) => { let result = sanitizeFilename(name); let count = 1; while (await exists(`${directoryPath}/${result}`)) { @@ -76,9 +77,3 @@ export const safeFileName = async (directoryPath: string, name: string) => { } return result; }; - -/** - * Return true if an item exists an the given {@link path} on the user's local - * filesystem. - */ -const exists = (path: string) => ensureElectron().fs.exists(path);