This commit is contained in:
Manav Rathi 2024-04-24 10:19:39 +05:30
parent 9103dadc6f
commit 2bee444078
No known key found for this signature in database
2 changed files with 72 additions and 96 deletions

View file

@ -1,4 +1,4 @@
import { getFileNameSize } from "@/next/file";
import { getFileNameSize, nameAndExtension } from "@/next/file";
import log from "@/next/log";
import { ElectronFile } from "@/next/types/file";
import { DedicatedCryptoWorker } from "@ente/shared/crypto/internal/crypto.worker";
@ -188,13 +188,6 @@ async function getVideoMetadata(file: File | ElectronFile) {
return videoMetadata;
}
interface LivePhotoIdentifier {
collectionID: number;
fileType: FILE_TYPE;
name: string;
size: number;
}
const UNDERSCORE_THREE = "_3";
// Note: The icloud-photos-downloader library appends _HVEC to the end of the filename in case of live photos
// https://github.com/icloud-photos-downloader/icloud_photos_downloader
@ -312,74 +305,64 @@ export function getLivePhotoSize(livePhotoAssets: LivePhotoAssets) {
return livePhotoAssets.image.size + livePhotoAssets.video.size;
}
export async function clusterLivePhotoFiles(mediaFiles: FileWithCollection2[]) {
/**
* Go through the given files, combining any sibling image + video assets into a
* single live photo when appropriate.
*/
export const clusterLivePhotos = (mediaFiles: FileWithCollection2[]) => {
try {
const analysedMediaFiles: FileWithCollection2[] = [];
const result: FileWithCollection2[] = [];
mediaFiles
.sort((firstMediaFile, secondMediaFile) =>
splitFilenameAndExtension(
getFileName(firstMediaFile.file),
)[0].localeCompare(
splitFilenameAndExtension(
getFileName(secondMediaFile.file),
)[0],
.sort((f, g) =>
nameAndExtension(getFileName(f.file))[0].localeCompare(
nameAndExtension(getFileName(g.file))[0],
),
)
.sort(
(firstMediaFile, secondMediaFile) =>
firstMediaFile.collectionID - secondMediaFile.collectionID,
);
.sort((f, g) => f.collectionID - g.collectionID);
let index = 0;
while (index < mediaFiles.length - 1) {
if (uploadCancelService.isUploadCancelationRequested()) {
throw Error(CustomError.UPLOAD_CANCELLED);
}
const firstMediaFile = mediaFiles[index];
const secondMediaFile = mediaFiles[index + 1];
const firstFileType =
getFileTypeFromExtensionForLivePhotoClustering(
getFileName(firstMediaFile.file),
);
const secondFileType =
getFileTypeFromExtensionForLivePhotoClustering(
getFileName(secondMediaFile.file),
);
const firstFileIdentifier: LivePhotoIdentifier = {
collectionID: firstMediaFile.collectionID,
fileType: firstFileType,
name: getFileName(firstMediaFile.file),
const f = mediaFiles[index];
const g = mediaFiles[index + 1];
const fFileType = getFileTypeFromExtensionForLivePhotoClustering(
getFileName(f.file),
);
const gFileType = getFileTypeFromExtensionForLivePhotoClustering(
getFileName(g.file),
);
const fFileIdentifier: LivePhotoIdentifier = {
collectionID: f.collectionID,
fileType: fFileType,
name: getFileName(f.file),
/* TODO(MR): ElectronFile changes */
size: (firstMediaFile as FileWithCollection).file.size,
size: (f as FileWithCollection).file.size,
};
const secondFileIdentifier: LivePhotoIdentifier = {
collectionID: secondMediaFile.collectionID,
fileType: secondFileType,
name: getFileName(secondMediaFile.file),
const gFileIdentifier: LivePhotoIdentifier = {
collectionID: g.collectionID,
fileType: gFileType,
name: getFileName(g.file),
/* TODO(MR): ElectronFile changes */
size: (secondMediaFile as FileWithCollection).file.size,
size: (g as FileWithCollection).file.size,
};
if (
areFilesLivePhotoAssets(
firstFileIdentifier,
secondFileIdentifier,
)
) {
if (areLivePhotoAssets(fFileIdentifier, gFileIdentifier)) {
let imageFile: File | ElectronFile | string;
let videoFile: File | ElectronFile | string;
if (
firstFileType === FILE_TYPE.IMAGE &&
secondFileType === FILE_TYPE.VIDEO
fFileType === FILE_TYPE.IMAGE &&
gFileType === FILE_TYPE.VIDEO
) {
imageFile = firstMediaFile.file;
videoFile = secondMediaFile.file;
imageFile = f.file;
videoFile = g.file;
} else {
videoFile = firstMediaFile.file;
imageFile = secondMediaFile.file;
videoFile = f.file;
imageFile = g.file;
}
const livePhotoLocalID = firstMediaFile.localID;
analysedMediaFiles.push({
const livePhotoLocalID = f.localID;
result.push({
localID: livePhotoLocalID,
collectionID: firstMediaFile.collectionID,
collectionID: f.collectionID,
isLivePhoto: true,
livePhotoAssets: {
image: imageFile,
@ -388,20 +371,20 @@ export async function clusterLivePhotoFiles(mediaFiles: FileWithCollection2[]) {
});
index += 2;
} else {
analysedMediaFiles.push({
...firstMediaFile,
result.push({
...f,
isLivePhoto: false,
});
index += 1;
}
}
if (index === mediaFiles.length - 1) {
analysedMediaFiles.push({
result.push({
...mediaFiles[index],
isLivePhoto: false,
});
}
return analysedMediaFiles;
return result;
} catch (e) {
if (e.message === CustomError.UPLOAD_CANCELLED) {
throw e;
@ -410,42 +393,44 @@ export async function clusterLivePhotoFiles(mediaFiles: FileWithCollection2[]) {
throw e;
}
}
};
interface LivePhotoIdentifier {
collectionID: number;
fileType: FILE_TYPE;
name: string;
size: number;
}
function areFilesLivePhotoAssets(
firstFileIdentifier: LivePhotoIdentifier,
secondFileIdentifier: LivePhotoIdentifier,
) {
const haveSameCollectionID =
firstFileIdentifier.collectionID === secondFileIdentifier.collectionID;
const areNotSameFileType =
firstFileIdentifier.fileType !== secondFileIdentifier.fileType;
const areLivePhotoAssets = (f: LivePhotoIdentifier, g: LivePhotoIdentifier) => {
const haveSameCollectionID = f.collectionID === g.collectionID;
const areNotSameFileType = f.fileType !== g.fileType;
let firstFileNameWithoutSuffix: string;
let secondFileNameWithoutSuffix: string;
if (firstFileIdentifier.fileType === FILE_TYPE.IMAGE) {
if (f.fileType === FILE_TYPE.IMAGE) {
firstFileNameWithoutSuffix = removePotentialLivePhotoSuffix(
getFileNameWithoutExtension(firstFileIdentifier.name),
getFileNameWithoutExtension(f.name),
// Note: The Google Live Photo image file can have video extension appended as suffix, passing that to removePotentialLivePhotoSuffix to remove it
// Example: IMG_20210630_0001.mp4.jpg (Google Live Photo image file)
getFileExtensionWithDot(secondFileIdentifier.name),
getFileExtensionWithDot(g.name),
);
secondFileNameWithoutSuffix = removePotentialLivePhotoSuffix(
getFileNameWithoutExtension(secondFileIdentifier.name),
getFileNameWithoutExtension(g.name),
);
} else {
firstFileNameWithoutSuffix = removePotentialLivePhotoSuffix(
getFileNameWithoutExtension(firstFileIdentifier.name),
getFileNameWithoutExtension(f.name),
);
secondFileNameWithoutSuffix = removePotentialLivePhotoSuffix(
getFileNameWithoutExtension(secondFileIdentifier.name),
getFileExtensionWithDot(firstFileIdentifier.name),
getFileNameWithoutExtension(g.name),
getFileExtensionWithDot(f.name),
);
}
if (
haveSameCollectionID &&
isImageOrVideo(firstFileIdentifier.fileType) &&
isImageOrVideo(secondFileIdentifier.fileType) &&
isImageOrVideo(f.fileType) &&
isImageOrVideo(g.fileType) &&
areNotSameFileType &&
firstFileNameWithoutSuffix === secondFileNameWithoutSuffix
) {
@ -455,23 +440,20 @@ function areFilesLivePhotoAssets(
// I did that based on the assumption that live photo assets ideally would not be larger than LIVE_PHOTO_ASSET_SIZE_LIMIT
// also zipping library doesn't support stream as a input
if (
firstFileIdentifier.size <= LIVE_PHOTO_ASSET_SIZE_LIMIT &&
secondFileIdentifier.size <= LIVE_PHOTO_ASSET_SIZE_LIMIT
f.size <= LIVE_PHOTO_ASSET_SIZE_LIMIT &&
g.size <= LIVE_PHOTO_ASSET_SIZE_LIMIT
) {
return true;
} else {
log.error(
`${CustomError.TOO_LARGE_LIVE_PHOTO_ASSETS} - ${JSON.stringify({
fileSizes: [
firstFileIdentifier.size,
secondFileIdentifier.size,
],
fileSizes: [f.size, g.size],
})}`,
);
}
}
return false;
}
};
function removePotentialLivePhotoSuffix(
filenameWithoutExtension: string,

View file

@ -367,19 +367,13 @@ class UploadManager {
);
await this.parseMetadataJSONFiles(metadataJSONFiles);
}
if (mediaFiles.length) {
log.info(`clusterLivePhotoFiles started`);
const analysedMediaFiles =
await clusterLivePhotoFiles(mediaFiles);
log.info(`clusterLivePhotoFiles ended`);
log.info(
`got live photos: ${
mediaFiles.length !== analysedMediaFiles.length
}`,
);
const clusteredMediaFiles = await clusterLivePhotos(mediaFiles);
this.uiService.setFilenames(
new Map<number, string>(
analysedMediaFiles.map((mediaFile) => [
clusteredMediaFiles.map((mediaFile) => [
mediaFile.localID,
assetName(mediaFile),
]),
@ -387,10 +381,10 @@ class UploadManager {
);
this.uiService.setHasLivePhoto(
mediaFiles.length !== analysedMediaFiles.length,
mediaFiles.length !== clusteredMediaFiles.length,
);
await this.uploadMediaFiles(analysedMediaFiles);
await this.uploadMediaFiles(clusteredMediaFiles);
}
} catch (e) {
if (e.message === CustomError.UPLOAD_CANCELLED) {