Manav Rathi 1 年之前
父节点
当前提交
184ba91a2d

+ 2 - 2
web/apps/cast/src/utils/file/index.ts

@@ -137,11 +137,11 @@ export const getPreviewableImage = async (
             await CastDownloadManager.downloadFile(castToken, file),
             await CastDownloadManager.downloadFile(castToken, file),
         ).blob();
         ).blob();
         if (file.metadata.fileType === FILE_TYPE.LIVE_PHOTO) {
         if (file.metadata.fileType === FILE_TYPE.LIVE_PHOTO) {
-            const livePhoto = await decodeLivePhoto(
+            const { imageData } = await decodeLivePhoto(
                 file.metadata.title,
                 file.metadata.title,
                 fileBlob,
                 fileBlob,
             );
             );
-            fileBlob = new Blob([livePhoto.image]);
+            fileBlob = new Blob([imageData]);
         }
         }
         const fileType = await getFileType(
         const fileType = await getFileType(
             new File([fileBlob], file.metadata.title),
             new File([fileBlob], file.metadata.title),

+ 13 - 11
web/apps/photos/src/services/export/index.ts

@@ -1,3 +1,4 @@
+import { decodeLivePhoto } from "@/media/live-photo";
 import { ensureElectron } from "@/next/electron";
 import { ensureElectron } from "@/next/electron";
 import log from "@/next/log";
 import log from "@/next/log";
 import { CustomError } from "@ente/shared/error";
 import { CustomError } from "@ente/shared/error";
@@ -38,7 +39,6 @@ import { writeStream } from "utils/native-stream";
 import { getAllLocalCollections } from "../collectionService";
 import { getAllLocalCollections } from "../collectionService";
 import downloadManager from "../download";
 import downloadManager from "../download";
 import { getAllLocalFiles } from "../fileService";
 import { getAllLocalFiles } from "../fileService";
-import { decodeLivePhoto } from "@/media/live-photo";
 import { migrateExport } from "./migration";
 import { migrateExport } from "./migration";
 
 
 /** Name of the JSON file in which we keep the state of the export. */
 /** Name of the JSON file in which we keep the state of the export. */
@@ -1015,18 +1015,18 @@ class ExportService {
         fileStream: ReadableStream<any>,
         fileStream: ReadableStream<any>,
         file: EnteFile,
         file: EnteFile,
     ) {
     ) {
-        const electron = ensureElectron();
+        const fs = ensureElectron().fs;
         const fileBlob = await new Response(fileStream).blob();
         const fileBlob = await new Response(fileStream).blob();
         const livePhoto = await decodeLivePhoto(file.metadata.title, fileBlob);
         const livePhoto = await decodeLivePhoto(file.metadata.title, fileBlob);
         const imageExportName = await safeFileName(
         const imageExportName = await safeFileName(
             collectionExportPath,
             collectionExportPath,
-            livePhoto.imageNameTitle,
-            electron.fs.exists,
+            livePhoto.imageFileName,
+            fs.exists,
         );
         );
         const videoExportName = await safeFileName(
         const videoExportName = await safeFileName(
             collectionExportPath,
             collectionExportPath,
-            livePhoto.videoNameTitle,
-            electron.fs.exists,
+            livePhoto.videoFileName,
+            fs.exists,
         );
         );
         const livePhotoExportName = getLivePhotoExportName(
         const livePhotoExportName = getLivePhotoExportName(
             imageExportName,
             imageExportName,
@@ -1038,7 +1038,9 @@ class ExportService {
             livePhotoExportName,
             livePhotoExportName,
         );
         );
         try {
         try {
-            const imageStream = generateStreamFromArrayBuffer(livePhoto.image);
+            const imageStream = generateStreamFromArrayBuffer(
+                livePhoto.imageData,
+            );
             await this.saveMetadataFile(
             await this.saveMetadataFile(
                 collectionExportPath,
                 collectionExportPath,
                 imageExportName,
                 imageExportName,
@@ -1049,7 +1051,9 @@ class ExportService {
                 imageStream,
                 imageStream,
             );
             );
 
 
-            const videoStream = generateStreamFromArrayBuffer(livePhoto.video);
+            const videoStream = generateStreamFromArrayBuffer(
+                livePhoto.videoData,
+            );
             await this.saveMetadataFile(
             await this.saveMetadataFile(
                 collectionExportPath,
                 collectionExportPath,
                 videoExportName,
                 videoExportName,
@@ -1061,9 +1065,7 @@ class ExportService {
                     videoStream,
                     videoStream,
                 );
                 );
             } catch (e) {
             } catch (e) {
-                await electron.fs.rm(
-                    `${collectionExportPath}/${imageExportName}`,
-                );
+                await fs.rm(`${collectionExportPath}/${imageExportName}`);
                 throw e;
                 throw e;
             }
             }
         } catch (e) {
         } catch (e) {

+ 3 - 3
web/apps/photos/src/services/export/migration.ts

@@ -318,18 +318,18 @@ async function getFileExportNamesFromExportedFiles(
         if (file.metadata.fileType === FILE_TYPE.LIVE_PHOTO) {
         if (file.metadata.fileType === FILE_TYPE.LIVE_PHOTO) {
             const fileStream = await downloadManager.getFile(file);
             const fileStream = await downloadManager.getFile(file);
             const fileBlob = await new Response(fileStream).blob();
             const fileBlob = await new Response(fileStream).blob();
-            const livePhoto = await decodeLivePhoto(
+            const { imageFileName, videoFileName } = await decodeLivePhoto(
                 file.metadata.title,
                 file.metadata.title,
                 fileBlob,
                 fileBlob,
             );
             );
             const imageExportName = getUniqueFileExportNameForMigration(
             const imageExportName = getUniqueFileExportNameForMigration(
                 collectionPath,
                 collectionPath,
-                livePhoto.imageNameTitle,
+                imageFileName,
                 usedFilePaths,
                 usedFilePaths,
             );
             );
             const videoExportName = getUniqueFileExportNameForMigration(
             const videoExportName = getUniqueFileExportNameForMigration(
                 collectionPath,
                 collectionPath,
-                livePhoto.videoNameTitle,
+                videoFileName,
                 usedFilePaths,
                 usedFilePaths,
             );
             );
             fileExportName = getLivePhotoExportName(
             fileExportName = getLivePhotoExportName(

+ 6 - 6
web/apps/photos/src/services/upload/livePhotoService.ts

@@ -101,16 +101,16 @@ export async function readLivePhoto(
         },
         },
     );
     );
 
 
-    const image = await getUint8ArrayView(livePhotoAssets.image);
+    const imageData = await getUint8ArrayView(livePhotoAssets.image);
 
 
-    const video = await getUint8ArrayView(livePhotoAssets.video);
+    const videoData = await getUint8ArrayView(livePhotoAssets.video);
 
 
     return {
     return {
         filedata: await encodeLivePhoto({
         filedata: await encodeLivePhoto({
-            image,
-            video,
-            imageNameTitle: livePhotoAssets.image.name,
-            videoNameTitle: livePhotoAssets.video.name,
+            imageFileName: livePhotoAssets.image.name,
+            imageData,
+            videoFileName: livePhotoAssets.video.name,
+            videoData,
         }),
         }),
         thumbnail,
         thumbnail,
         hasStaticThumbnail,
         hasStaticThumbnail,

+ 12 - 15
web/apps/photos/src/utils/file/index.ts

@@ -97,22 +97,20 @@ export async function downloadFile(file: EnteFile) {
             await DownloadManager.getFile(file),
             await DownloadManager.getFile(file),
         ).blob();
         ).blob();
         if (file.metadata.fileType === FILE_TYPE.LIVE_PHOTO) {
         if (file.metadata.fileType === FILE_TYPE.LIVE_PHOTO) {
-            const livePhoto = await decodeLivePhoto(
-                file.metadata.title,
-                fileBlob,
-            );
-            const image = new File([livePhoto.image], livePhoto.imageNameTitle);
+            const { imageFileName, imageData, videoFileName, videoData } =
+                await decodeLivePhoto(file.metadata.title, fileBlob);
+            const image = new File([imageData], imageFileName);
             const imageType = await getFileType(image);
             const imageType = await getFileType(image);
             const tempImageURL = URL.createObjectURL(
             const tempImageURL = URL.createObjectURL(
-                new Blob([livePhoto.image], { type: imageType.mimeType }),
+                new Blob([imageData], { type: imageType.mimeType }),
             );
             );
-            const video = new File([livePhoto.video], livePhoto.videoNameTitle);
+            const video = new File([videoData], videoFileName);
             const videoType = await getFileType(video);
             const videoType = await getFileType(video);
             const tempVideoURL = URL.createObjectURL(
             const tempVideoURL = URL.createObjectURL(
-                new Blob([livePhoto.video], { type: videoType.mimeType }),
+                new Blob([videoData], { type: videoType.mimeType }),
             );
             );
-            downloadUsingAnchor(tempImageURL, livePhoto.imageNameTitle);
-            downloadUsingAnchor(tempVideoURL, livePhoto.videoNameTitle);
+            downloadUsingAnchor(tempImageURL, imageFileName);
+            downloadUsingAnchor(tempVideoURL, videoFileName);
         } else {
         } else {
             const fileType = await getFileType(
             const fileType = await getFileType(
                 new File([fileBlob], file.metadata.title),
                 new File([fileBlob], file.metadata.title),
@@ -350,9 +348,9 @@ async function getRenderableLivePhotoURL(
 
 
     const getRenderableLivePhotoImageURL = async () => {
     const getRenderableLivePhotoImageURL = async () => {
         try {
         try {
-            const imageBlob = new Blob([livePhoto.image]);
+            const imageBlob = new Blob([livePhoto.imageData]);
             const convertedImageBlob = await getRenderableImage(
             const convertedImageBlob = await getRenderableImage(
-                livePhoto.imageNameTitle,
+                livePhoto.imageFileName,
                 imageBlob,
                 imageBlob,
             );
             );
 
 
@@ -365,10 +363,9 @@ async function getRenderableLivePhotoURL(
 
 
     const getRenderableLivePhotoVideoURL = async () => {
     const getRenderableLivePhotoVideoURL = async () => {
         try {
         try {
-            const videoBlob = new Blob([livePhoto.video]);
-
+            const videoBlob = new Blob([livePhoto.videoData]);
             const convertedVideoBlob = await getPlayableVideo(
             const convertedVideoBlob = await getPlayableVideo(
-                livePhoto.videoNameTitle,
+                livePhoto.videoFileName,
                 videoBlob,
                 videoBlob,
                 forceConvert,
                 forceConvert,
                 true,
                 true,

+ 4 - 4
web/apps/photos/src/utils/machineLearning/index.ts

@@ -134,11 +134,11 @@ async function getOriginalConvertedFile(file: EnteFile, queue?: PQueue) {
     if (file.metadata.fileType === FILE_TYPE.IMAGE) {
     if (file.metadata.fileType === FILE_TYPE.IMAGE) {
         return await getRenderableImage(file.metadata.title, fileBlob);
         return await getRenderableImage(file.metadata.title, fileBlob);
     } else {
     } else {
-        const livePhoto = await decodeLivePhoto(file.metadata.title, fileBlob);
-        return await getRenderableImage(
-            livePhoto.imageNameTitle,
-            new Blob([livePhoto.image]),
+        const { imageFileName, imageData } = await decodeLivePhoto(
+            file.metadata.title,
+            fileBlob,
         );
         );
+        return await getRenderableImage(imageFileName, new Blob([imageData]));
     }
     }
 }
 }
 
 

+ 41 - 17
web/packages/media/live-photo.ts

@@ -1,11 +1,14 @@
 import { fileNameFromComponents, nameAndExtension } from "@/next/file";
 import { fileNameFromComponents, nameAndExtension } from "@/next/file";
 import JSZip from "jszip";
 import JSZip from "jszip";
 
 
-class LivePhoto {
-    image: Uint8Array;
-    video: Uint8Array;
-    imageNameTitle: string;
-    videoNameTitle: string;
+/**
+ * An in-memory representation of a live photo.
+ */
+interface LivePhoto {
+    imageFileName: string;
+    imageData: Uint8Array;
+    videoFileName: string;
+    videoData: Uint8Array;
 }
 }
 
 
 /**
 /**
@@ -23,23 +26,39 @@ class LivePhoto {
  * @param zipBlob A blob contained the zipped data (i.e. the binary serialized
  * @param zipBlob A blob contained the zipped data (i.e. the binary serialized
  * live photo).
  * live photo).
  */
  */
-export const decodeLivePhoto = async (fileName: string, zipBlob: Blob) => {
+export const decodeLivePhoto = async (
+    fileName: string,
+    zipBlob: Blob,
+): Promise<LivePhoto> => {
+    let imageFileName, videoFileName: string | undefined;
+    let imageData, videoData: Uint8Array | undefined;
+
     const [name] = nameAndExtension(fileName);
     const [name] = nameAndExtension(fileName);
     const zip = await JSZip.loadAsync(zipBlob, { createFolders: true });
     const zip = await JSZip.loadAsync(zipBlob, { createFolders: true });
 
 
-    const livePhoto = new LivePhoto();
     for (const zipFileName in zip.files) {
     for (const zipFileName in zip.files) {
         if (zipFileName.startsWith("image")) {
         if (zipFileName.startsWith("image")) {
             const [, imageExt] = nameAndExtension(zipFileName);
             const [, imageExt] = nameAndExtension(zipFileName);
-            livePhoto.imageNameTitle = fileNameFromComponents([name, imageExt]);
-            livePhoto.image = await zip.files[zipFileName].async("uint8array");
+            imageFileName = fileNameFromComponents([name, imageExt]);
+            imageData = await zip.files[zipFileName]?.async("uint8array");
         } else if (zipFileName.startsWith("video")) {
         } else if (zipFileName.startsWith("video")) {
             const [, videoExt] = nameAndExtension(zipFileName);
             const [, videoExt] = nameAndExtension(zipFileName);
-            livePhoto.videoNameTitle = fileNameFromComponents([name, videoExt]);
-            livePhoto.video = await zip.files[zipFileName].async("uint8array");
+            videoFileName = fileNameFromComponents([name, videoExt]);
+            videoData = await zip.files[zipFileName]?.async("uint8array");
         }
         }
     }
     }
-    return livePhoto;
+
+    if (!imageFileName || !imageData)
+        throw new Error(
+            `Decoded live photo ${fileName} does not have an image`,
+        );
+
+    if (!videoFileName || !videoData)
+        throw new Error(
+            `Decoded live photo ${fileName} does not have an image`,
+        );
+
+    return { imageFileName, imageData, videoFileName, videoData };
 };
 };
 
 
 /**
 /**
@@ -52,12 +71,17 @@ export const decodeLivePhoto = async (fileName: string, zipBlob: Blob) => {
  *
  *
  * @param livePhoto The in-mem photo to serialized.
  * @param livePhoto The in-mem photo to serialized.
  */
  */
-export const encodeLivePhoto = async (livePhoto: LivePhoto) => {
-    const [, imageExt] = nameAndExtension(livePhoto.imageNameTitle);
-    const [, videoExt] = nameAndExtension(livePhoto.videoNameTitle);
+export const encodeLivePhoto = async ({
+    imageFileName,
+    imageData,
+    videoFileName,
+    videoData,
+}: LivePhoto) => {
+    const [, imageExt] = nameAndExtension(imageFileName);
+    const [, videoExt] = nameAndExtension(videoFileName);
 
 
     const zip = new JSZip();
     const zip = new JSZip();
-    zip.file(fileNameFromComponents(["image", imageExt]), livePhoto.image);
-    zip.file(fileNameFromComponents(["video", videoExt]), livePhoto.video);
+    zip.file(fileNameFromComponents(["image", imageExt]), imageData);
+    zip.file(fileNameFromComponents(["video", videoExt]), videoData);
     return await zip.generateAsync({ type: "uint8array" });
     return await zip.generateAsync({ type: "uint8array" });
 };
 };