This commit is contained in:
Manav Rathi 2024-07-22 13:57:59 +05:30
parent d59e50ff93
commit 67d1d6c597
No known key found for this signature in database
4 changed files with 67 additions and 65 deletions

View File

@ -7,7 +7,6 @@ import type {
} from "@/new/photos/types/metadata"; } from "@/new/photos/types/metadata";
import { validateAndGetCreationUnixTimeInMicroSeconds } from "@ente/shared/time"; import { validateAndGetCreationUnixTimeInMicroSeconds } from "@ente/shared/time";
import exifr from "exifr"; import exifr from "exifr";
import piexif from "piexifjs";
type ParsedEXIFData = Record<string, any> & type ParsedEXIFData = Record<string, any> &
Partial<{ Partial<{
@ -332,65 +331,3 @@ export function getEXIFTime(exifData: ParsedEXIFData): number {
} }
return validateAndGetCreationUnixTimeInMicroSeconds(dateTime); return validateAndGetCreationUnixTimeInMicroSeconds(dateTime);
} }
export async function updateFileCreationDateInEXIF(
fileBlob: Blob,
updatedDate: Date,
) {
try {
let imageDataURL = await blobToDataURL(fileBlob);
// Since we pass a Blob without an associated type, we get back a
// generic data URL like "data:application/octet-stream;base64,...".
// Modify it to have a `image/jpeg` MIME type.
imageDataURL =
"data:image/jpeg;base64" +
imageDataURL.slice(imageDataURL.indexOf(","));
const exifObj = piexif.load(imageDataURL);
if (!exifObj["Exif"]) {
exifObj["Exif"] = {};
}
exifObj["Exif"][piexif.ExifIFD.DateTimeOriginal] =
convertToExifDateFormat(updatedDate);
log.debug(() => [
"updateFileCreationDateInEXIF",
{ updatedDate, exifObj },
]);
const exifBytes = piexif.dump(exifObj);
const exifInsertedFile = piexif.insert(exifBytes, imageDataURL);
return dataURLToBlob(exifInsertedFile);
} catch (e) {
log.error("updateFileModifyDateInEXIF failed", e);
return fileBlob;
}
}
/**
* Convert a blob to a `data:` URL.
*/
const blobToDataURL = (blob: Blob) =>
new Promise<string>((resolve) => {
const reader = new FileReader();
// We need to cast to a string here. This should be safe since MDN says:
//
// > the result attribute contains the data as a data: URL representing
// > the file's data as a base64 encoded string.
// >
// > https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL
reader.onload = () => resolve(reader.result as string);
reader.readAsDataURL(blob);
});
/**
* Convert a `data:` URL to a blob.
*
* Requires `connect-src data:` in the CSP (since it internally uses `fetch` to
* perform the conversion).
*/
const dataURLToBlob = (dataURI: string) =>
fetch(dataURI).then((res) => res.blob());
function convertToExifDateFormat(date: Date) {
return `${date.getFullYear()}:${
date.getMonth() + 1
}:${date.getDate()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`;
}

View File

@ -25,7 +25,7 @@ import type { User } from "@ente/shared/user/types";
import { downloadUsingAnchor } from "@ente/shared/utils"; import { downloadUsingAnchor } from "@ente/shared/utils";
import { t } from "i18next"; import { t } from "i18next";
import { moveToHiddenCollection } from "services/collectionService"; import { moveToHiddenCollection } from "services/collectionService";
import { updateFileCreationDateInEXIF } from "services/exif"; import { updateFileCreationDateInEXIF } from "@/new/photos/services/exif";
import { import {
deleteFromTrash, deleteFromTrash,
trashFiles, trashFiles,

View File

@ -192,7 +192,8 @@ For more details, see [translations.md](translations.md).
## Media ## Media
- [ExifReader](https://github.com/mattiasw/ExifReader) is used for Exif - [ExifReader](https://github.com/mattiasw/ExifReader) is used for Exif
parsing. parsing. [piexifjs](https://github.com/hMatoba/piexifjs) is used for writing
back Exif (only supports JPEG).
- [jszip](https://github.com/Stuk/jszip) is used for reading zip files in the - [jszip](https://github.com/Stuk/jszip) is used for reading zip files in the
web code (Live photos are zip files under the hood). Note that the desktop web code (Live photos are zip files under the hood). Note that the desktop

View File

@ -0,0 +1,64 @@
import log from "@/base/log";
import piexif from "piexifjs";
export const updateFileCreationDateInEXIF = async (
fileBlob: Blob,
updatedDate: Date,
) => {
try {
let imageDataURL = await blobToDataURL(fileBlob);
// Since we pass a Blob without an associated type, we get back a
// generic data URL like "data:application/octet-stream;base64,...".
// Modify it to have a `image/jpeg` MIME type.
imageDataURL =
"data:image/jpeg;base64" +
imageDataURL.slice(imageDataURL.indexOf(","));
const exifObj = piexif.load(imageDataURL);
if (!exifObj["Exif"]) {
exifObj["Exif"] = {};
}
exifObj["Exif"][piexif.ExifIFD.DateTimeOriginal] =
convertToExifDateFormat(updatedDate);
log.debug(() => [
"updateFileCreationDateInEXIF",
{ updatedDate, exifObj },
]);
const exifBytes = piexif.dump(exifObj);
const exifInsertedFile = piexif.insert(exifBytes, imageDataURL);
return dataURLToBlob(exifInsertedFile);
} catch (e) {
log.error("updateFileModifyDateInEXIF failed", e);
return fileBlob;
}
};
/**
* Convert a blob to a `data:` URL.
*/
const blobToDataURL = (blob: Blob) =>
new Promise<string>((resolve) => {
const reader = new FileReader();
// We need to cast to a string here. This should be safe since MDN says:
//
// > the result attribute contains the data as a data: URL representing
// > the file's data as a base64 encoded string.
// >
// > https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL
reader.onload = () => resolve(reader.result as string);
reader.readAsDataURL(blob);
});
/**
* Convert a `data:` URL to a blob.
*
* Requires `connect-src data:` in the CSP (since it internally uses `fetch` to
* perform the conversion).
*/
const dataURLToBlob = (dataURI: string) =>
fetch(dataURI).then((res) => res.blob());
function convertToExifDateFormat(date: Date) {
return `${date.getFullYear()}:${
date.getMonth() + 1
}:${date.getDate()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`;
}