Merge branch 'main' into image-exif-aspect-ratio

This commit is contained in:
Abhinav 2023-06-05 23:42:49 +05:30
commit c94bf73ebf
27 changed files with 130 additions and 57 deletions

View file

@ -340,6 +340,7 @@
"CAPTION_CHARACTER_LIMIT": "",
"DATE_TIME_ORIGINAL": "",
"DATE_TIME_DIGITIZED": "",
"METADATA_DATE": "",
"CUSTOM_TIME": "",
"REOPEN_PLAN_SELECTOR_MODAL": "",
"OPEN_PLAN_SELECTOR_MODAL_FAILED": "",

View file

@ -340,6 +340,7 @@
"CAPTION_CHARACTER_LIMIT": "5000 characters max",
"DATE_TIME_ORIGINAL": "EXIF:DateTimeOriginal",
"DATE_TIME_DIGITIZED": "EXIF:DateTimeDigitized",
"METADATA_DATE":"EXIF:MetadataDate",
"CUSTOM_TIME": "Custom time",
"REOPEN_PLAN_SELECTOR_MODAL": "Re-open plans",
"OPEN_PLAN_SELECTOR_MODAL_FAILED": "Failed to open plans",

View file

@ -340,6 +340,7 @@
"CAPTION_CHARACTER_LIMIT": "Máximo 5000 caracteres",
"DATE_TIME_ORIGINAL": "EXIF: Fecha original",
"DATE_TIME_DIGITIZED": "EXIF: Fecha Digitalizado",
"METADATA_DATE": "",
"CUSTOM_TIME": "Hora personalizada",
"REOPEN_PLAN_SELECTOR_MODAL": "Reabrir planes",
"OPEN_PLAN_SELECTOR_MODAL_FAILED": "Error al abrir los planes",

View file

@ -340,6 +340,7 @@
"CAPTION_CHARACTER_LIMIT": "",
"DATE_TIME_ORIGINAL": "",
"DATE_TIME_DIGITIZED": "",
"METADATA_DATE": "",
"CUSTOM_TIME": "",
"REOPEN_PLAN_SELECTOR_MODAL": "",
"OPEN_PLAN_SELECTOR_MODAL_FAILED": "",

View file

@ -340,6 +340,7 @@
"CAPTION_CHARACTER_LIMIT": "",
"DATE_TIME_ORIGINAL": "",
"DATE_TIME_DIGITIZED": "",
"METADATA_DATE": "",
"CUSTOM_TIME": "",
"REOPEN_PLAN_SELECTOR_MODAL": "",
"OPEN_PLAN_SELECTOR_MODAL_FAILED": "",

View file

@ -340,6 +340,7 @@
"CAPTION_CHARACTER_LIMIT": "5000 caractères max",
"DATE_TIME_ORIGINAL": "EXIF:DateTimeOriginal",
"DATE_TIME_DIGITIZED": "EXIF:DateTimeDigitized",
"METADATA_DATE": "",
"CUSTOM_TIME": "Heure personnalisée",
"REOPEN_PLAN_SELECTOR_MODAL": "Rouvrir les plans",
"OPEN_PLAN_SELECTOR_MODAL_FAILED": "Échec pour rouvrir les plans",

View file

@ -340,6 +340,7 @@
"CAPTION_CHARACTER_LIMIT": "",
"DATE_TIME_ORIGINAL": "",
"DATE_TIME_DIGITIZED": "",
"METADATA_DATE": "",
"CUSTOM_TIME": "",
"REOPEN_PLAN_SELECTOR_MODAL": "",
"OPEN_PLAN_SELECTOR_MODAL_FAILED": "",

View file

@ -340,6 +340,7 @@
"CAPTION_CHARACTER_LIMIT": "5000 tekens max",
"DATE_TIME_ORIGINAL": "EXIF:DatumTijdOrigineel",
"DATE_TIME_DIGITIZED": "EXIF:DatumTijdDigitaliseerd",
"METADATA_DATE": "",
"CUSTOM_TIME": "Aangepaste tijd",
"REOPEN_PLAN_SELECTOR_MODAL": "Abonnementen heropenen",
"OPEN_PLAN_SELECTOR_MODAL_FAILED": "Kon abonnementen niet openen",

View file

@ -340,6 +340,7 @@
"CAPTION_CHARACTER_LIMIT": "",
"DATE_TIME_ORIGINAL": "",
"DATE_TIME_DIGITIZED": "",
"METADATA_DATE": "",
"CUSTOM_TIME": "",
"REOPEN_PLAN_SELECTOR_MODAL": "",
"OPEN_PLAN_SELECTOR_MODAL_FAILED": "",

View file

@ -340,6 +340,7 @@
"CAPTION_CHARACTER_LIMIT": "",
"DATE_TIME_ORIGINAL": "",
"DATE_TIME_DIGITIZED": "",
"METADATA_DATE": "",
"CUSTOM_TIME": "",
"REOPEN_PLAN_SELECTOR_MODAL": "",
"OPEN_PLAN_SELECTOR_MODAL_FAILED": "",

View file

@ -340,6 +340,7 @@
"CAPTION_CHARACTER_LIMIT": "",
"DATE_TIME_ORIGINAL": "",
"DATE_TIME_DIGITIZED": "",
"METADATA_DATE": "",
"CUSTOM_TIME": "",
"REOPEN_PLAN_SELECTOR_MODAL": "",
"OPEN_PLAN_SELECTOR_MODAL_FAILED": "",

View file

@ -340,6 +340,7 @@
"CAPTION_CHARACTER_LIMIT": "5000个字符上限",
"DATE_TIME_ORIGINAL": "EXIF日期 时间 原始文件",
"DATE_TIME_DIGITIZED": "EXIF:日期 时间 数字化",
"METADATA_DATE": "",
"CUSTOM_TIME": "自定义时间",
"REOPEN_PLAN_SELECTOR_MODAL": "重新启动计划",
"OPEN_PLAN_SELECTOR_MODAL_FAILED": "未能打开计划",

View file

@ -29,6 +29,7 @@ export enum FIX_STATE {
export enum FIX_OPTIONS {
DATE_TIME_ORIGINAL,
DATE_TIME_DIGITIZED,
METADATA_DATE,
CUSTOM_TIME,
}

View file

@ -56,6 +56,14 @@ export default function FixCreationTimeOptions({ handleChange, values }) {
selected={Number(values.option)}
/>
</Row>
<Row style={{ margin: '0' }}>
<Option
value={FIX_OPTIONS.METADATA_DATE}
onChange={handleChange('option')}
label={t('METADATA_DATE')}
selected={Number(values.option)}
/>
</Row>
<Row style={{ margin: '0' }}>
<Value width="50%">
<Option

View file

@ -428,7 +428,10 @@ const PhotoFrame = ({
`[${item.id}] gallery context cache miss, calling downloadManager to get file`
);
appContext.startLoading();
let downloadedURL;
let downloadedURL: {
original: string[];
converted: string[];
};
if (publicCollectionGalleryContext.accessedThroughSharedURL) {
downloadedURL =
await PublicCollectionDownloadManager.getFile(

View file

@ -179,7 +179,8 @@ function PhotoViewer(props: Iprops) {
useEffect(() => {
if (!isOpen) return;
const item = items[photoSwipe?.getCurrentIndex()];
if (item && item.metadata.fileType === FILE_TYPE.LIVE_PHOTO) {
if (!item) return;
if (item.metadata.fileType === FILE_TYPE.LIVE_PHOTO) {
const getVideoAndImage = () => {
const video = document.getElementById(
`live-photo-video-${item.id}`
@ -213,32 +214,25 @@ function PhotoViewer(props: Iprops) {
loading: true,
});
}
}
const downloadLivePhotoBtn = document.getElementById(
`download-btn-${item.id}`
) as HTMLButtonElement;
if (downloadLivePhotoBtn) {
const downloadLivePhoto = () => {
const downloadFile = () => {
downloadFileHelper(photoSwipe.currItem);
};
downloadLivePhotoBtn.addEventListener(
'click',
downloadLivePhoto
);
return () => {
downloadLivePhotoBtn.removeEventListener(
'click',
downloadLivePhoto
);
setLivePhotoBtnOptions(defaultLivePhotoDefaultOptions);
};
if (downloadLivePhotoBtn) {
downloadLivePhotoBtn.addEventListener('click', downloadFile);
}
return () => {
if (downloadLivePhotoBtn) {
downloadLivePhotoBtn.removeEventListener('click', downloadFile);
}
setLivePhotoBtnOptions(defaultLivePhotoDefaultOptions);
};
}
}, [photoSwipe?.currItem, isOpen, isSourceLoaded]);
useEffect(() => {

View file

@ -13,13 +13,15 @@ export class ElectronFFmpeg implements IFFmpeg {
async run(
cmd: string[],
inputFile: ElectronFile | File,
outputFilename: string
outputFilename: string,
dontTimeout?: boolean
) {
if (this.electronAPIs?.runFFmpegCmd) {
return this.electronAPIs.runFFmpegCmd(
cmd,
inputFile,
outputFilename
outputFilename,
dontTimeout
);
}
}

View file

@ -7,7 +7,8 @@ export interface IFFmpeg {
run: (
cmd: string[],
inputFile: File | ElectronFile,
outputFilename: string
outputFilename: string,
dontTimeout?: boolean
) => Promise<File | ElectronFile>;
}

View file

@ -90,7 +90,8 @@ export async function convertToMP4(file: File | ElectronFile) {
OUTPUT_PATH_PLACEHOLDER,
],
file,
'output.mp4'
'output.mp4',
true
);
} catch (e) {
logError(e, 'ffmpeg convertToMP4 failed');

View file

@ -55,10 +55,16 @@ export async function updateCreationTimeWithExif(
correctCreationTime = getUnixTimeInMicroSeconds(
exifData?.DateTimeOriginal ?? exifData?.DateCreated
);
} else {
} else if (fixOption === FIX_OPTIONS.DATE_TIME_DIGITIZED) {
correctCreationTime = getUnixTimeInMicroSeconds(
exifData?.CreateDate
);
} else if (fixOption === FIX_OPTIONS.METADATA_DATE) {
correctCreationTime = getUnixTimeInMicroSeconds(
exifData?.MetadataDate
);
} else {
throw new Error('Invalid fix option');
}
}
if (

View file

@ -15,6 +15,7 @@ type ParsedEXIFData = Record<string, any> &
CreateDate: Date;
ModifyDate: Date;
DateCreated: Date;
MetadataDate: Date;
latitude: number;
longitude: number;
imageWidth: number;
@ -27,6 +28,7 @@ type RawEXIFData = Record<string, any> &
CreateDate: string;
ModifyDate: string;
DateCreated: string;
MetadataDate: string;
GPSLatitude: number[];
GPSLongitude: number[];
GPSLatitudeRef: string;
@ -91,6 +93,7 @@ function parseExifData(exifData: RawEXIFData): ParsedEXIFData {
ExifImageWidth,
PixelXDimension,
PixelYDimension,
MetadataDate,
...rest
} = exifData;
const parsedExif: ParsedEXIFData = { ...rest };
@ -106,6 +109,9 @@ function parseExifData(exifData: RawEXIFData): ParsedEXIFData {
if (DateCreated) {
parsedExif.DateCreated = parseEXIFDate(exifData.DateCreated);
}
if (MetadataDate) {
parsedExif.MetadataDate = parseEXIFDate(exifData.MetadataDate);
}
if (exifData.GPSLatitude && exifData.GPSLongitude) {
const parsedLocation = parseEXIFLocation(
exifData.GPSLatitude,
@ -294,6 +300,7 @@ export function getEXIFTime(exifData: ParsedEXIFData): number {
exifData.DateTimeOriginal ??
exifData.DateCreated ??
exifData.CreateDate ??
exifData.MetadataDate ??
exifData.ModifyDate;
if (!dateTime) {
return null;

View file

@ -48,6 +48,7 @@ const EXIF_TAGS_NEEDED = [
'ImageHeight',
'PixelXDimension',
'PixelYDimension',
'MetadataDate',
];
export async function extractMetadata(

View file

@ -32,13 +32,22 @@ export class WasmFFmpeg {
}
}
async run(cmd: string[], inputFile: File, outputFileName: string) {
const response = this.ffmpegTaskQueue.queueUpRequest(() =>
promiseWithTimeout<File>(
async run(
cmd: string[],
inputFile: File,
outputFileName: string,
dontTimeout = false
) {
const response = this.ffmpegTaskQueue.queueUpRequest(() => {
if (dontTimeout) {
return this.execute(cmd, inputFile, outputFileName);
} else {
return promiseWithTimeout<File>(
this.execute(cmd, inputFile, outputFileName),
FFMPEG_EXECUTION_WAIT_TIME
)
);
}
});
try {
return await response.promise;
} catch (e) {

View file

@ -74,7 +74,8 @@ export interface ElectronAPIs {
runFFmpegCmd: (
cmd: string[],
inputFile: File | ElectronFile,
outputFileName: string
outputFileName: string,
dontTimeout?: boolean
) => Promise<File>;
muteUpdateNotification: (version: string) => void;
generateImageThumbnail: (

View file

@ -39,6 +39,7 @@ import {
updateFileMagicMetadata,
updateFilePublicMagicMetadata,
} from 'services/fileService';
import { isPlaybackPossible } from 'utils/photoFrame';
const WAIT_TIME_IMAGE_CONVERSION = 30 * 1000;
@ -293,6 +294,16 @@ export async function getRenderableFileURL(file: EnteFile, fileBlob: Blob) {
original: [URL.createObjectURL(fileBlob)],
};
}
case FILE_TYPE.VIDEO: {
const convertedBlob = await getPlayableVideo(
file.metadata.title,
new Uint8Array(await fileBlob.arrayBuffer())
);
return {
converted: [URL.createObjectURL(convertedBlob)],
original: [URL.createObjectURL(fileBlob)],
};
}
default: {
const previewURL = await createTypedObjectURL(
fileBlob,
@ -318,12 +329,28 @@ async function getRenderableLivePhoto(
]);
}
async function getPlayableVideo(videoNameTitle: string, video: Uint8Array) {
export async function getPlayableVideo(
videoNameTitle: string,
video: Uint8Array
) {
try {
const isPlayable = await isPlaybackPossible(
URL.createObjectURL(new Blob([video]))
);
if (isPlayable) {
return new Blob([video.buffer]);
} else {
addLogLine('video format not supported, converting it');
const mp4ConvertedVideo = await ffmpegService.convertToMP4(
new File([video], videoNameTitle)
);
return new Blob([await mp4ConvertedVideo.arrayBuffer()]);
}
} catch (e) {
logError(e, 'video conversion failed');
return new Blob([video.buffer]);
}
}
export async function getRenderableImage(fileName: string, imageBlob: Blob) {
if (await isFileHEIC(imageBlob, fileName)) {

View file

@ -1,8 +1,8 @@
import { FILE_TYPE } from 'constants/file';
import { t } from 'i18next';
import { EnteFile } from 'types/file';
import { MergedSourceURL } from 'types/gallery';
import { logError } from 'utils/sentry';
import { t } from 'i18next';
const WAIT_FOR_VIDEO_PLAYBACK = 1 * 1000;
@ -119,9 +119,9 @@ export async function updateFileSrcProps(
<img src="${file.msrc}" onContextMenu="return false;"/>
<div class="download-banner">
${t('VIDEO_PLAYBACK_FAILED_DOWNLOAD_INSTEAD')}
<a class="btn btn-outline-success" href=${convertedVideoURL} download="${
file.metadata.title
}"">Download</a>
<button class = "btn btn-outline-success" id = "download-btn-${
file.id
}">${t('DOWNLOAD')}</button>
</div>
</div>
`;

View file

@ -7,8 +7,8 @@ export class DedicatedFFmpegWorker {
this.wasmFFmpeg = new WasmFFmpeg();
}
run(cmd, inputFile, outputFileName) {
return this.wasmFFmpeg.run(cmd, inputFile, outputFileName);
run(cmd, inputFile, outputFileName, dontTimeout) {
return this.wasmFFmpeg.run(cmd, inputFile, outputFileName, dontTimeout);
}
}