Skip to content

Commit

Permalink
[web] [desktop] Retain JPEG originals even on date modifications
Browse files Browse the repository at this point in the history
  • Loading branch information
mnvr committed Oct 30, 2024
1 parent ac65241 commit f979522
Show file tree
Hide file tree
Showing 9 changed files with 38 additions and 218 deletions.
1 change: 1 addition & 0 deletions desktop/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## v1.7.7 (Unreleased)

- Retain JPEG originals even on date modifications.
- .

## v1.7.6
Expand Down
39 changes: 32 additions & 7 deletions docs/docs/photos/faq/photo-dates.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,14 @@ videos that you imported. The modifications (e.g. date changes) you make within
Ente will be written into a separate metadata JSON file during export so as to
not modify the original.

> There is one exception to this. For JPEG files, the Exif DateTimeOriginal is
> changed during export from web or desktop apps. This was done on a customer
> request, but in hindsight this has been an incorrect move. We are going to
> deprecate this behavior, and will instead provide separate tools (or
> functionality within the app itself) for customers who intentionally wish to
> modify their originals to reflect the associated metadata JSON.
> [!WARNING]
>
> There used to be one exception to this - for JPEG files, the Exif
> DateTimeOriginal was changed during export from web or desktop apps. This was
> done on a customer request, but in hindsight this was an incorrect change.
>
> We have deprecated this behaviour, and the desktop version 1.7.6 is going to
> be the last version with this exception.
As an example: suppose you have `flower.png`. When you export your library, you
will end up with:
Expand All @@ -81,13 +83,36 @@ metadata/flower.png.json

Ente writes this JSON in the same format as Google Takeout so that if a tool
supports Google Takeout import, it should be able to read the JSON written by
Ente too
Ente too.

> One small difference is that, to avoid clutter, Ente puts the JSON in the
> `metadata/` subfolder, while Google puts it next to the file.<br>
>
> <br>Ente itself will read it from either place.
Here is a sample of how the JSON would look:

```json
{
"description": "This will be imported as the caption",
"creationTime": {
"timestamp": "1613532136",
"formatted": "17 Feb 2021, 03:22:16 UTC"
},
"modificationTime": {
"timestamp": "1640225957",
"formatted": "23 Dec 2021, 02:19:17 UTC"
},
"geoData": {
"latitude": 12.004170700000001,
"longitude": 79.8013945
}
}
```

`photoTakenTime` will be considered as an alias for `creationTime`, and
`geoDataExif` will be considered as a fallback for `geoData`.

### File creation time.

The photo's data will be preserved verbatim, however when it is written out to
Expand Down
1 change: 0 additions & 1 deletion web/apps/photos/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
"ml-matrix": "^6.11",
"p-debounce": "^4.0.0",
"photoswipe": "file:./thirdparty/photoswipe",
"piexifjs": "^1.0.6",
"pure-react-carousel": "^1.30.1",
"react-dropzone": "^14.2",
"react-select": "^5.8.0",
Expand Down
9 changes: 2 additions & 7 deletions web/apps/photos/src/services/export/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {
getCollectionUserFacingName,
} from "@/new/photos/services/collection";
import downloadManager from "@/new/photos/services/download";
import { updateExifIfNeededAndPossible } from "@/new/photos/services/exif-update";
import {
exportMetadataDirectoryName,
exportTrashDirectoryName,
Expand Down Expand Up @@ -939,16 +938,12 @@ class ExportService {
try {
const fileUID = getExportRecordFileUID(file);
const originalFileStream = await downloadManager.getFile(file);
const updatedFileStream = await updateExifIfNeededAndPossible(
file,
originalFileStream,
);
if (file.metadata.fileType === FileType.livePhoto) {
await this.exportLivePhoto(
exportDir,
fileUID,
collectionExportPath,
updatedFileStream,
originalFileStream,
file,
);
} else {
Expand All @@ -965,7 +960,7 @@ class ExportService {
await writeStream(
electron,
`${collectionExportPath}/${fileExportName}`,
updatedFileStream,
originalFileStream,
);
await this.addFileExportedRecord(
exportDir,
Expand Down
9 changes: 2 additions & 7 deletions web/apps/photos/src/utils/file/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import { ItemVisibility } from "@/media/file-metadata";
import { FileType } from "@/media/file-type";
import { decodeLivePhoto } from "@/media/live-photo";
import DownloadManager from "@/new/photos/services/download";
import { updateExifIfNeededAndPossible } from "@/new/photos/services/exif-update";
import {
isArchivedFile,
updateMagicMetadata,
Expand Down Expand Up @@ -79,9 +78,6 @@ export async function downloadFile(file: EnteFile) {
const fileType = await detectFileTypeInfo(
new File([fileBlob], file.metadata.title),
);
fileBlob = await new Response(
await updateExifIfNeededAndPossible(file, fileBlob.stream()),
).blob();
fileBlob = new Blob([fileBlob], { type: fileType.mimeType });
const tempURL = URL.createObjectURL(fileBlob);
downloadAndRevokeObjectURL(tempURL, file.metadata.title);
Expand Down Expand Up @@ -397,10 +393,9 @@ async function downloadFileDesktop(
const fs = electron.fs;

const stream = await DownloadManager.getFile(file);
const updatedStream = await updateExifIfNeededAndPossible(file, stream);

if (file.metadata.fileType === FileType.livePhoto) {
const fileBlob = await new Response(updatedStream).blob();
const fileBlob = await new Response(stream).blob();
const { imageFileName, imageData, videoFileName, videoData } =
await decodeLivePhoto(file.metadata.title, fileBlob);
const imageExportName = await safeFileName(
Expand Down Expand Up @@ -439,7 +434,7 @@ async function downloadFileDesktop(
await writeStream(
electron,
`${downloadDir}/${fileExportName}`,
updatedStream,
stream,
);
}
}
Expand Down
3 changes: 1 addition & 2 deletions web/docs/dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,7 @@ For more details, see [translations.md](translations.md).
## Media

- [ExifReader](https://github.com/mattiasw/ExifReader) is used for Exif
parsing. [piexifjs](https://github.com/hMatoba/piexifjs) is used for writing
back Exif (only supports JPEG).
parsing.

- [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
Expand Down
147 changes: 0 additions & 147 deletions web/packages/new/photos/services/exif-update.ts

This file was deleted.

42 changes: 0 additions & 42 deletions web/packages/new/photos/types/piexifjs.d.ts

This file was deleted.

5 changes: 0 additions & 5 deletions web/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3602,11 +3602,6 @@ picomatch@^2.3.1:
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==

piexifjs@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/piexifjs/-/piexifjs-1.0.6.tgz#883811d73f447218d0d06e9ed7866d04533e59e0"
integrity sha512-0wVyH0cKohzBQ5Gi2V1BuxYpxWfxF3cSqfFXfPIpl5tl9XLS5z4ogqhUCD20AbHi0h9aJkqXNJnkVev6gwh2ag==

pngjs@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-6.0.0.tgz#ca9e5d2aa48db0228a52c419c3308e87720da821"
Expand Down

0 comments on commit f979522

Please sign in to comment.