Live photos are saved as a ZIP under the image's extension #107

Open
opened 2026-09-23 05:34:58 +02:00 by clawbot · 5 comments
Collaborator

Problem

A live photo is stored on Ente as one ZIP file holding the image and the video. This is from the upstream finding on #68 (comment). quak has no handling for this file type (livePhoto, src/model/decrypt.ts:32). It names the downloaded object after the title's extension:

  • the backup: originals/ID.HEIC (src/backup.ts:116-117);
  • the content cache: src/library/content.ts:213-214;
  • quak get without --out: IMG_1234.HEIC (src/cli-commands.ts:270).

In each case the file is really a ZIP, so photo viewers cannot open it and nothing tells the user why. Every live photo from an iPhone account is affected.

Definition of done

Pending the owner's choice of how a live photo is stored (see the comment below). Whichever way it goes:

  1. quak get, the backup tree and the content cache store live photos in the chosen form, and the album symlinks point at it.
  2. A test uses a ZIP fixture built by a helper in test/ holding a small image and a small video, and checks the stored result.
  3. The README "Backup layout" section describes the live photo case.
  4. make check green; TODO.md updated in the same commit.

Model: opus-5-5

## Problem A live photo is stored on Ente as one ZIP file holding the image and the video. This is from the upstream finding on https://git.eeqj.de/sneak/quak/issues/68#issuecomment-100442. quak has no handling for this file type (`livePhoto`, `src/model/decrypt.ts:32`). It names the downloaded object after the title's extension: - the backup: `originals/ID.HEIC` (`src/backup.ts:116-117`); - the content cache: `src/library/content.ts:213-214`; - `quak get` without `--out`: `IMG_1234.HEIC` (`src/cli-commands.ts:270`). In each case the file is really a ZIP, so photo viewers cannot open it and nothing tells the user why. Every live photo from an iPhone account is affected. ## Definition of done Pending the owner's choice of how a live photo is stored (see the comment below). Whichever way it goes: 1. `quak get`, the backup tree and the content cache store live photos in the chosen form, and the album symlinks point at it. 2. A test uses a ZIP fixture built by a helper in `test/` holding a small image and a small video, and checks the stored result. 3. The README "Backup layout" section describes the live photo case. 4. `make check` green; `TODO.md` updated in the same commit. Model: opus-5-5
clawbot self-assigned this 2026-09-23 05:34:58 +02:00
Author
Collaborator

Decision needed: how quak stores a live photo

What this is about: an iPhone live photo is a still image plus a short video. Ente stores the pair as one ZIP file. Today quak saves that ZIP as it is and names it after the image, for example originals/12345.HEIC in the backup and IMG_1234.HEIC from quak get. A photo viewer cannot open that file.

Keep the ZIP. Store the object unchanged, named ID.zip in the backup and IMG_1234.zip from quak get. The backup stays byte-for-byte what the server holds, and there is still one file per photo. The user has to unzip it to see the photo.

Unpack it. Write the image and the video as two files named after the entries inside the ZIP, for example originals/12345.HEIC and originals/12345.MOV, with both linked in each album. quak get writes both files. Anyone can open the result directly. This means a ZIP-reading library, which the content-hash work in #98 is adding anyway, and a "present means complete" check that covers two files.

Recommendation: Unpack it. The README describes a backup of photos a user can open, and Ente's own web client splits a live photo into its two files when downloading it. Keep the ZIP is the smaller change, if one file per photo matters more.

Reply with either label.

Model: opus-5-5

## Decision needed: how quak stores a live photo **What this is about:** an iPhone live photo is a still image plus a short video. Ente stores the pair as one ZIP file. Today quak saves that ZIP as it is and names it after the image, for example `originals/12345.HEIC` in the backup and `IMG_1234.HEIC` from `quak get`. A photo viewer cannot open that file. **Keep the ZIP.** Store the object unchanged, named `ID.zip` in the backup and `IMG_1234.zip` from `quak get`. The backup stays byte-for-byte what the server holds, and there is still one file per photo. The user has to unzip it to see the photo. **Unpack it.** Write the image and the video as two files named after the entries inside the ZIP, for example `originals/12345.HEIC` and `originals/12345.MOV`, with both linked in each album. `quak get` writes both files. Anyone can open the result directly. This means a ZIP-reading library, which the content-hash work in https://git.eeqj.de/sneak/quak/pulls/98 is adding anyway, and a "present means complete" check that covers two files. **Recommendation: Unpack it.** The README describes a backup of photos a user can open, and Ente's own web client splits a live photo into its two files when downloading it. Keep the ZIP is the smaller change, if one file per photo matters more. Reply with either label. Model: opus-5-5
clawbot removed their assignment 2026-09-23 05:35:10 +02:00
sneak was assigned by clawbot 2026-09-23 05:35:10 +02:00
Author
Collaborator

Note from the top-level manager, to keep the question exact. The README's backup layout (https://git.eeqj.de/sneak/quak/src/branch/next2/README.md) documents originals/<fileID>.<ext> as the actual file content, one per unique file, with one album symlink per file. Keep the ZIP fits that as written. Unpack it changes it: a live photo becomes two files and two album links, and that README section changes with it. The README describes downloading and decrypting images to disk, but it does not say whether a backed-up file must open directly in a photo viewer. Checked on next2: originalName in src/backup.ts takes the extension from the title, and src/model/decrypt.ts maps file type 2 to livePhoto.

Model: opus-5-5

Note from the top-level manager, to keep the question exact. The README's backup layout (https://git.eeqj.de/sneak/quak/src/branch/next2/README.md) documents `originals/<fileID>.<ext>` as the actual file content, one per unique file, with one album symlink per file. Keep the ZIP fits that as written. Unpack it changes it: a live photo becomes two files and two album links, and that README section changes with it. The README describes downloading and decrypting images to disk, but it does not say whether a backed-up file must open directly in a photo viewer. Checked on `next2`: `originalName` in `src/backup.ts` takes the extension from the title, and `src/model/decrypt.ts` maps file type 2 to `livePhoto`. Model: opus-5-5
Owner

no, unpack it. the spec is in flux and nothing is set in stone.

no, unpack it. the spec is in flux and nothing is set in stone.
Author
Collaborator

Ruling from sneak: unpack it. A live photo, which Ente stores as one ZIP, is saved as its still image and its video: two files a photo viewer opens. The README's backup layout is not fixed; its "one per unique file" line changes to say how live photos are stored.

Implementer brief (branch next2)

First, verify the ZIP layout from Ente's own source. Find how Ente's clients write and read a live photo's ZIP: the entry names, how the image and video entries are told apart, and where their extensions come from. Also find how Ente's web client names the two files it saves on download. Cite the repository, commit and file paths in the PR body. Earlier reading of that source (#68 (comment)) found entries named image.<ext> and video.<ext>; livePhotoHasher in src/download/index.ts already picks them out that way. Confirm or correct that before coding.

Where live photos are handled today: originalName in src/backup.ts:122 and src/library/content.ts:212, and originalName in src/cli-output.ts:38, used by quak get at src/cli-commands.ts:307. livePhotoHasher at src/download/index.ts:307 streams the decrypted ZIP through fflate's Unzip to check the content hash.

Definition of done

  1. One download writes both parts. For a live photo, the download writer writes the image entry and the video entry each to its own temp file as they decompress, so memory stays bounded as it does today. Both parts are checked against the recorded hash as today. Both are renamed into place only when both are complete and match. On any failure, neither is stored.
  2. Names. Each part keeps the name its non-live-photo counterpart would get, with that part's own extension from the ZIP. Extensions are limited to letters and digits, as today. The two names never collide.
    • Backup and content cache: originals/<fileID>.<imageExt> and originals/<fileID>.<videoExt>.
    • quak get without --out: the title's base name with each extension, following Ente's own download naming if it differs.
    • quak get --out PATH: the image is written to PATH as given, and the video beside it with PATH's base name and the video's extension.
  3. Present means complete, for both parts. The backup and the content cache count a live photo as stored only when both files are present and non-empty. The content cache evicts the pair together. The backup's failure record lists a live photo once, by file ID.
  4. Album folders link both parts. One symlink each, named with the collision rules from #103, so no name replaces another.
  5. Library API. The content result for a live photo gives the image's path plus the video's path in a plainly named field. This is documented in the README API reference.
  6. Old ZIPs are repaired. A live photo that an earlier version saved as a ZIP under the image's name, in the backup or the cache, is replaced by its two parts on the next run. The old ZIP and its links are removed.
  7. Other readers of a stored original use the image part. Any other code that reads a stored original, for example the EXIF extraction in backup-metadata, reads the image part of a live photo.
  8. Tests. A helper in test/ builds the ZIP with fflate from small image and video bytes. Tests check:
    • the stored pair and its links for the backup, the content cache and quak get with and without --out;
    • the repair of an old ZIP;
    • that a failed or mismatched live photo stores nothing.
  9. README. The backup layout says how live photos are stored, and the CLI surface says what quak get writes for one. Mark the README TODO item "Store live photos in a form a photo viewer can open" as done. TODO.md names the next open issue as the Next Step, or says none is open.
  10. TODO.md updated in the same commit.

Model: opus-5-5

Ruling from sneak: unpack it. A live photo, which Ente stores as one ZIP, is saved as its still image and its video: two files a photo viewer opens. The README's backup layout is not fixed; its "one per unique file" line changes to say how live photos are stored. ## Implementer brief (branch `next2`) **First, verify the ZIP layout from Ente's own source.** Find how Ente's clients write and read a live photo's ZIP: the entry names, how the image and video entries are told apart, and where their extensions come from. Also find how Ente's web client names the two files it saves on download. Cite the repository, commit and file paths in the PR body. Earlier reading of that source (https://git.eeqj.de/sneak/quak/issues/68#issuecomment-100442) found entries named `image.<ext>` and `video.<ext>`; `livePhotoHasher` in `src/download/index.ts` already picks them out that way. Confirm or correct that before coding. **Where live photos are handled today:** `originalName` in `src/backup.ts:122` and `src/library/content.ts:212`, and `originalName` in `src/cli-output.ts:38`, used by `quak get` at `src/cli-commands.ts:307`. `livePhotoHasher` at `src/download/index.ts:307` streams the decrypted ZIP through fflate's `Unzip` to check the content hash. ## Definition of done 1. **One download writes both parts.** For a live photo, the download writer writes the image entry and the video entry each to its own temp file as they decompress, so memory stays bounded as it does today. Both parts are checked against the recorded hash as today. Both are renamed into place only when both are complete and match. On any failure, neither is stored. 2. **Names.** Each part keeps the name its non-live-photo counterpart would get, with that part's own extension from the ZIP. Extensions are limited to letters and digits, as today. The two names never collide. - Backup and content cache: `originals/<fileID>.<imageExt>` and `originals/<fileID>.<videoExt>`. - `quak get` without `--out`: the title's base name with each extension, following Ente's own download naming if it differs. - `quak get --out PATH`: the image is written to `PATH` as given, and the video beside it with `PATH`'s base name and the video's extension. 3. **Present means complete, for both parts.** The backup and the content cache count a live photo as stored only when both files are present and non-empty. The content cache evicts the pair together. The backup's failure record lists a live photo once, by file ID. 4. **Album folders link both parts.** One symlink each, named with the collision rules from https://git.eeqj.de/sneak/quak/issues/103, so no name replaces another. 5. **Library API.** The content result for a live photo gives the image's path plus the video's path in a plainly named field. This is documented in the README API reference. 6. **Old ZIPs are repaired.** A live photo that an earlier version saved as a ZIP under the image's name, in the backup or the cache, is replaced by its two parts on the next run. The old ZIP and its links are removed. 7. **Other readers of a stored original use the image part.** Any other code that reads a stored original, for example the EXIF extraction in `backup-metadata`, reads the image part of a live photo. 8. **Tests.** A helper in `test/` builds the ZIP with fflate from small image and video bytes. Tests check: - the stored pair and its links for the backup, the content cache and `quak get` with and without `--out`; - the repair of an old ZIP; - that a failed or mismatched live photo stores nothing. 9. **README.** The backup layout says how live photos are stored, and the CLI surface says what `quak get` writes for one. Mark the README TODO item "Store live photos in a form a photo viewer can open" as done. `TODO.md` names the next open issue as the Next Step, or says none is open. 10. `TODO.md` updated in the same commit. Model: opus-5-5
sneak was unassigned by clawbot 2026-09-23 13:56:26 +02:00
clawbot self-assigned this 2026-09-23 13:56:26 +02:00
Author
Collaborator

Built in #128. A live photo is stored as its image and its video, beside <fileID>.livephoto.json, which names the two: their names come from inside the ZIP, so nothing else on disk says which is which. The PR lists the other judgement calls.

Model: opus-5-5

Built in https://git.eeqj.de/sneak/quak/pulls/128. A live photo is stored as its image and its video, beside `<fileID>.livephoto.json`, which names the two: their names come from inside the ZIP, so nothing else on disk says which is which. The PR lists the other judgement calls. Model: opus-5-5
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/quak#107