diff --git a/src/services/downloadManager.ts b/src/services/downloadManager.ts index 83da849f8..dfa650c61 100644 --- a/src/services/downloadManager.ts +++ b/src/services/downloadManager.ts @@ -5,6 +5,7 @@ import { fileIsHEIC, convertHEIC2JPEG, fileNameWithoutExtension, + generateStreamFromArrayBuffer, } from 'utils/file'; import HTTPService from './HTTPService'; import { File } from './fileService'; @@ -116,12 +117,7 @@ class DownloadManager { await worker.fromB64(file.file.decryptionHeader), file.key, ); - return new ReadableStream({ - async start(controller: ReadableStreamDefaultController) { - controller.enqueue(decrypted); - controller.close(); - }, - }); + return generateStreamFromArrayBuffer(decrypted); } const resp = await fetch(getFileUrl(file.id), { headers: { diff --git a/src/services/exportService.ts b/src/services/exportService.ts index 6f38f7134..b7aca3d83 100644 --- a/src/services/exportService.ts +++ b/src/services/exportService.ts @@ -1,10 +1,13 @@ +import { FILE_TYPE } from 'pages/gallery'; import { retryAsyncFunction, runningInBrowser } from 'utils/common'; import { getExportPendingFiles, getExportFailedFiles, getFilesUploadedAfterLastExport, getFileUID, dedupe, getGoogleLikeMetadataFile } from 'utils/export'; +import { fileNameWithoutExtension, generateStreamFromArrayBuffer } from 'utils/file'; import { logError } from 'utils/sentry'; import { getData, LS_KEYS } from 'utils/storage/localStorage'; import { Collection, getLocalCollections } from './collectionService'; import downloadManager from './downloadManager'; import { File, getLocalFiles } from './fileService'; +import { decodeMotionPhoto } from './motionPhotoService'; export interface ExportProgress { current: number; @@ -255,13 +258,35 @@ class ExportService { file.metadata.title, )}`; const fileStream = await retryAsyncFunction(()=>downloadManager.downloadFile(file)); - this.ElectronAPIs.saveStreamToDisk(`${collectionPath}/${uid}`, fileStream); + if (file.metadata.fileType===FILE_TYPE.LIVE_PHOTO) { + this.exportMotionPhoto(fileStream, file, collectionPath); + } else { + this.ElectronAPIs.saveStreamToDisk(`${collectionPath}/${uid}`, fileStream); + } this.ElectronAPIs.saveFileToDisk( `${collectionPath}/${MetadataFolderName}/${uid}.json`, getGoogleLikeMetadataFile(uid, file.metadata), ); } + private async exportMotionPhoto( + fileStream: ReadableStream, + file: File, + collectionPath: string, + ) { + const fileBlob = await new Response(fileStream).blob(); + const originalName = fileNameWithoutExtension(file.metadata.title); + const motionPhoto = await decodeMotionPhoto(fileBlob, originalName); + + const imageStream = generateStreamFromArrayBuffer(motionPhoto.image); + const imageSavePath=`${collectionPath}/${file.id}_${motionPhoto.imageNameTitle}`; + this.ElectronAPIs.saveStreamToDisk(imageSavePath, imageStream); + + const videoStream = generateStreamFromArrayBuffer(motionPhoto.video); + const videoSavePath=`${collectionPath}/${file.id}_${motionPhoto.videoNameTitle}`; + this.ElectronAPIs.saveStreamToDisk(videoSavePath, videoStream); + } + private sanitizeName(name) { return name.replaceAll('/', '_').replaceAll(' ', '_'); } diff --git a/src/utils/file/index.ts b/src/utils/file/index.ts index 2985ea36d..2d394e2c3 100644 --- a/src/utils/file/index.ts +++ b/src/utils/file/index.ts @@ -148,13 +148,22 @@ export function removeUnneccessaryFileProps(files:File[]):File[] { } export function fileNameWithoutExtension(filename) { - var lastDotPosition = filename.lastIndexOf("."); + const lastDotPosition = filename.lastIndexOf('.'); if (lastDotPosition === -1) return filename; else return filename.substr(0, lastDotPosition); } export function fileExtensionWithDot(filename) { - var lastDotPosition = filename.lastIndexOf("."); - if (lastDotPosition === -1) return ""; + const lastDotPosition = filename.lastIndexOf('.'); + if (lastDotPosition === -1) return ''; else return filename.substr(lastDotPosition); } + +export function generateStreamFromArrayBuffer(data: Uint8Array) { + return new ReadableStream({ + async start(controller: ReadableStreamDefaultController) { + controller.enqueue(data); + controller.close(); + }, + }); +}