This commit is contained in:
Manav Rathi 2024-04-17 12:23:50 +05:30
parent 652be207be
commit 184ba91a2d
No known key found for this signature in database
7 changed files with 81 additions and 58 deletions

View file

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

View file

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

View file

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

View file

@ -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 {
filedata: await encodeLivePhoto({
image,
video,
imageNameTitle: livePhotoAssets.image.name,
videoNameTitle: livePhotoAssets.video.name,
imageFileName: livePhotoAssets.image.name,
imageData,
videoFileName: livePhotoAssets.video.name,
videoData,
}),
thumbnail,
hasStaticThumbnail,

View file

@ -97,22 +97,20 @@ export async function downloadFile(file: EnteFile) {
await DownloadManager.getFile(file),
).blob();
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 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 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 {
const fileType = await getFileType(
new File([fileBlob], file.metadata.title),
@ -350,9 +348,9 @@ async function getRenderableLivePhotoURL(
const getRenderableLivePhotoImageURL = async () => {
try {
const imageBlob = new Blob([livePhoto.image]);
const imageBlob = new Blob([livePhoto.imageData]);
const convertedImageBlob = await getRenderableImage(
livePhoto.imageNameTitle,
livePhoto.imageFileName,
imageBlob,
);
@ -365,10 +363,9 @@ async function getRenderableLivePhotoURL(
const getRenderableLivePhotoVideoURL = async () => {
try {
const videoBlob = new Blob([livePhoto.video]);
const videoBlob = new Blob([livePhoto.videoData]);
const convertedVideoBlob = await getPlayableVideo(
livePhoto.videoNameTitle,
livePhoto.videoFileName,
videoBlob,
forceConvert,
true,

View file

@ -134,11 +134,11 @@ async function getOriginalConvertedFile(file: EnteFile, queue?: PQueue) {
if (file.metadata.fileType === FILE_TYPE.IMAGE) {
return await getRenderableImage(file.metadata.title, fileBlob);
} 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]));
}
}

View file

@ -1,11 +1,14 @@
import { fileNameFromComponents, nameAndExtension } from "@/next/file";
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
* 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 zip = await JSZip.loadAsync(zipBlob, { createFolders: true });
const livePhoto = new LivePhoto();
for (const zipFileName in zip.files) {
if (zipFileName.startsWith("image")) {
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")) {
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.
*/
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();
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" });
};