From 85a3a2f2ea49fb920b55b342261f8b026f6c681e Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Fri, 4 Jul 2025 15:40:06 +0530 Subject: [PATCH] Update --- web/packages/gallery/services/save.ts | 85 +++++++++++---------- web/packages/gallery/utils/native-stream.ts | 2 +- 2 files changed, 47 insertions(+), 40 deletions(-) diff --git a/web/packages/gallery/services/save.ts b/web/packages/gallery/services/save.ts index f081a870af..a3c59e4635 100644 --- a/web/packages/gallery/services/save.ts +++ b/web/packages/gallery/services/save.ts @@ -26,6 +26,15 @@ import { } from "ente-new/photos/utils/native-fs"; import { wait } from "ente-utils/promise"; +/** + * An object that keeps track of progress of the download of a set of files. + * + * This "download" is distinct from the downloads the app does from remote. What + * we're doing here is perhaps more accurately but too verbosely described as "a + * user initiated download of files to the user's device", aka "saving them". In + * contrast, the app does the "download the file from remote" internal action, + * e.g. for showing to the user or performing some indexing etc. + */ export interface FilesDownloadProgressAttributes { id: number; success: number; @@ -215,7 +224,7 @@ async function downloadFilesDesktop( if (progressBarUpdater?.isCancelled()) { return; } - await downloadFileDesktop(electron, file, downloadPath); + await saveFileDesktop(electron, file, downloadPath); progressBarUpdater?.increaseSuccess(); } catch (e) { log.error("download fail for file", e); @@ -224,60 +233,58 @@ async function downloadFilesDesktop( } } -async function downloadFileDesktop( +/** + * Save a file to the given {@link directoryPath} using native filesystem APIs. + * + * This is a sibling of {@link saveAsFile} for use when we are running in the + * context of our desktop app. Unlike the browser, the desktop app can use + * native file system APIs to efficiently write the files on disk without + * needing to prompt the user for each write. + * + * @param electron An {@link Electron} instance, a witness to the fact that + * we're running in the desktop app. + * + * @param file The {@link EnteFile} whose contents we want to save to the user's + * file system. + * + * @param directoryPath The file system directory in which to save the file. + */ +const saveFileDesktop = async ( electron: Electron, file: EnteFile, - downloadDir: string, -) { + directoryPath: string, +) => { const fs = electron.fs; + const createSafeName = (fileName: string) => + safeFileName(directoryPath, fileName, fs.exists); + + const writeStreamToFile = ( + fileName: string, + stream: ReadableStream | null, + ) => writeStream(electron, joinPath(directoryPath, fileName), stream); + const stream = await downloadManager.fileStream(file); const fileName = fileFileName(file); if (file.metadata.fileType == FileType.livePhoto) { - const fileBlob = await new Response(stream).blob(); const { imageFileName, imageData, videoFileName, videoData } = - await decodeLivePhoto(fileName, fileBlob); - const imageExportName = await safeFileName( - downloadDir, - imageFileName, - fs.exists, - ); - const imageStream = new Response(imageData).body; - await writeStream( - electron, - joinPath(downloadDir, imageExportName), - imageStream, - ); + await decodeLivePhoto(fileName, await new Response(stream).blob()); + const imageExportName = await createSafeName(imageFileName); + await writeStreamToFile(imageExportName, new Response(imageData).body); try { - const videoExportName = await safeFileName( - downloadDir, - videoFileName, - fs.exists, - ); - const videoStream = new Response(videoData).body; - await writeStream( - electron, - joinPath(downloadDir, videoExportName), - videoStream, + await writeStreamToFile( + await createSafeName(videoFileName), + new Response(videoData).body, ); } catch (e) { - await fs.rm(joinPath(downloadDir, imageExportName)); + await fs.rm(joinPath(directoryPath, imageExportName)); throw e; } } else { - const fileExportName = await safeFileName( - downloadDir, - fileName, - fs.exists, - ); - await writeStream( - electron, - joinPath(downloadDir, fileExportName), - stream, - ); + await writeStreamToFile(await createSafeName(fileName), stream); } -} +}; export async function downloadCollectionHelper( collectionID: number, diff --git a/web/packages/gallery/utils/native-stream.ts b/web/packages/gallery/utils/native-stream.ts index e5a7160595..f52aa7319d 100644 --- a/web/packages/gallery/utils/native-stream.ts +++ b/web/packages/gallery/utils/native-stream.ts @@ -91,7 +91,7 @@ const readNumericHeader = (res: Response, key: string) => { export const writeStream = async ( _: Electron, path: string, - stream: ReadableStream, + stream: ReadableStream | null, ) => { const params = new URLSearchParams({ path }); const url = new URL(`stream://write?${params.toString()}`);