Handle new motion photo import image name format (#1399)

This commit is contained in:
Abhinav Kumar 2023-10-19 08:10:28 +00:00 committed by GitHub
commit 618cf574cc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 70 additions and 25 deletions

View file

@ -1,6 +1,9 @@
import JSZip from 'jszip';
import { EnteFile } from 'types/file';
import { fileExtensionWithDot, fileNameWithoutExtension } from 'utils/file';
import {
getFileExtensionWithDot,
getFileNameWithoutExtension,
} from 'utils/file';
class LivePhoto {
image: Uint8Array;
@ -10,18 +13,18 @@ class LivePhoto {
}
export const decodeLivePhoto = async (file: EnteFile, zipBlob: Blob) => {
const originalName = fileNameWithoutExtension(file.metadata.title);
const originalName = getFileNameWithoutExtension(file.metadata.title);
const zip = await JSZip.loadAsync(zipBlob, { createFolders: true });
const livePhoto = new LivePhoto();
for (const zipFilename in zip.files) {
if (zipFilename.startsWith('image')) {
livePhoto.imageNameTitle =
originalName + fileExtensionWithDot(zipFilename);
originalName + getFileExtensionWithDot(zipFilename);
livePhoto.image = await zip.files[zipFilename].async('uint8array');
} else if (zipFilename.startsWith('video')) {
livePhoto.videoNameTitle =
originalName + fileExtensionWithDot(zipFilename);
originalName + getFileExtensionWithDot(zipFilename);
livePhoto.video = await zip.files[zipFilename].async('uint8array');
}
}
@ -31,11 +34,11 @@ export const decodeLivePhoto = async (file: EnteFile, zipBlob: Blob) => {
export const encodeLivePhoto = async (livePhoto: LivePhoto) => {
const zip = new JSZip();
zip.file(
'image' + fileExtensionWithDot(livePhoto.imageNameTitle),
'image' + getFileExtensionWithDot(livePhoto.imageNameTitle),
livePhoto.image
);
zip.file(
'video' + fileExtensionWithDot(livePhoto.videoNameTitle),
'video' + getFileExtensionWithDot(livePhoto.videoNameTitle),
livePhoto.video
);
return await zip.generateAsync({ type: 'uint8array' });

View file

@ -12,7 +12,12 @@ import {
} from 'types/upload';
import { CustomError } from 'utils/error';
import { getFileTypeFromExtensionForLivePhotoClustering } from 'utils/file/livePhoto';
import { splitFilenameAndExtension, isImageOrVideo } from 'utils/file';
import {
splitFilenameAndExtension,
isImageOrVideo,
getFileExtensionWithDot,
getFileNameWithoutExtension,
} from 'utils/file';
import { logError } from 'utils/sentry';
import { getUint8ArrayView } from '../readerService';
import { extractFileMetadata } from './fileService';
@ -33,7 +38,6 @@ 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
const UNDERSCORE_HEVC = '_HVEC';
const UNDERSCORE = '_';
export async function getLivePhotoFileType(
livePhotoAssets: LivePhotoAssets
@ -219,18 +223,38 @@ function areFilesLivePhotoAssets(
firstFileIdentifier: LivePhotoIdentifier,
secondFileIdentifier: LivePhotoIdentifier
) {
const haveSameCollectionID =
firstFileIdentifier.collectionID === secondFileIdentifier.collectionID;
const areNotSameFileType =
firstFileIdentifier.fileType !== secondFileIdentifier.fileType;
let firstFileNameWithoutSuffix: string;
let secondFileNameWithoutSuffix: string;
if (firstFileIdentifier.fileType === FILE_TYPE.IMAGE) {
firstFileNameWithoutSuffix = removePotentialLivePhotoSuffix(
getFileNameWithoutExtension(firstFileIdentifier.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)
);
secondFileNameWithoutSuffix = removePotentialLivePhotoSuffix(
getFileNameWithoutExtension(secondFileIdentifier.name)
);
} else {
firstFileNameWithoutSuffix = removePotentialLivePhotoSuffix(
getFileNameWithoutExtension(firstFileIdentifier.name)
);
secondFileNameWithoutSuffix = removePotentialLivePhotoSuffix(
getFileNameWithoutExtension(secondFileIdentifier.name),
getFileExtensionWithDot(firstFileIdentifier.name)
);
}
if (
firstFileIdentifier.collectionID ===
secondFileIdentifier.collectionID &&
firstFileIdentifier.fileType !== secondFileIdentifier.fileType &&
haveSameCollectionID &&
isImageOrVideo(firstFileIdentifier.fileType) &&
isImageOrVideo(secondFileIdentifier.fileType) &&
removePotentailLivePhotoSuffix(
splitFilenameAndExtension(firstFileIdentifier.name)[0]
) ===
removePotentailLivePhotoSuffix(
splitFilenameAndExtension(secondFileIdentifier.name)[0]
)
areNotSameFileType &&
firstFileNameWithoutSuffix === secondFileNameWithoutSuffix
) {
// checks size of live Photo assets are less than allowed limit
// I did that based on the assumption that live photo assets ideally would not be larger than LIVE_PHOTO_ASSET_SIZE_LIMIT
@ -256,11 +280,29 @@ function areFilesLivePhotoAssets(
return false;
}
function removePotentailLivePhotoSuffix(filename: string) {
if (filename.endsWith(UNDERSCORE_THREE)) {
return filename.slice(0, filename.lastIndexOf(UNDERSCORE));
} else if (filename.toLowerCase().endsWith(UNDERSCORE_HEVC)) {
return filename.slice(0, filename.lastIndexOf(UNDERSCORE_HEVC));
function removePotentialLivePhotoSuffix(
filenameWithoutExtension: string,
suffix?: string
) {
let presentSuffix: string;
if (filenameWithoutExtension.endsWith(UNDERSCORE_THREE)) {
presentSuffix = UNDERSCORE_THREE;
} else if (filenameWithoutExtension.endsWith(UNDERSCORE_HEVC)) {
presentSuffix = UNDERSCORE_HEVC;
} else if (
filenameWithoutExtension.endsWith(UNDERSCORE_HEVC.toLowerCase())
) {
presentSuffix = UNDERSCORE_HEVC.toLowerCase();
} else if (suffix) {
if (filenameWithoutExtension.endsWith(suffix)) {
presentSuffix = suffix;
} else if (filenameWithoutExtension.endsWith(suffix.toLowerCase())) {
presentSuffix = suffix.toLowerCase();
}
}
if (presentSuffix) {
return filenameWithoutExtension.slice(0, presentSuffix.length * -1);
} else {
return filenameWithoutExtension;
}
return filename;
}

View file

@ -292,13 +292,13 @@ export async function decryptFile(
}
}
export function fileNameWithoutExtension(filename: string) {
export function getFileNameWithoutExtension(filename: string) {
const lastDotPosition = filename.lastIndexOf('.');
if (lastDotPosition === -1) return filename;
else return filename.slice(0, lastDotPosition);
}
export function fileExtensionWithDot(filename: string) {
export function getFileExtensionWithDot(filename: string) {
const lastDotPosition = filename.lastIndexOf('.');
if (lastDotPosition === -1) return '';
else return filename.slice(lastDotPosition);