mirror of
https://github.com/ente-io/ente.git
synced 2025-08-09 07:48:52 +00:00
Move
This commit is contained in:
parent
d59e50ff93
commit
67d1d6c597
@ -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()}`;
|
|
||||||
}
|
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
64
web/packages/new/photos/services/exif-update.ts
Normal file
64
web/packages/new/photos/services/exif-update.ts
Normal 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()}`;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user