Implements #141 on Photo, per the brief in its comments.
savePath and isLocal are synchronous and look only at the disk. savePath is where lib.backup() writes the original under the library's downloadDirectory, present or not: for a live photo already stored, the image's path. For a live photo not yet stored, it carries the title's extension, and the backup may store the image under a different one. isLocal is true only when the whole original is there, not merely in the cache.
content() and exif() are async and may download. content() returns the original's bytes (for a live photo, the image's). exif() returns a JPEG's common EXIF fields and {} for anything else; it never downloads a video, but throws like the other content methods without a content source.
New getters modifiedAt and hash (also on PhotoRecord), plus year.
The JPEG EXIF scan moved from metadata-backup.ts to the new src/exif.ts, so the read surface does not import the backup command.
Not visible in the diff:
PhotoContent, an exported interface, gains savePath and isLocal.
dateTimeOriginal holds the camera's clock reading in the Date's UTC fields, as exif-reader reads it.
Disclosures:
Deviation: the brief's test JPEG held only Make, Model, Orientation and GPS. Mine carries every field, because a mistyped tag name still compiles.
Judgement call: iso is read only when the file stores ISOSpeedRatings as one number. A file that stores a list gets no iso.
Model: opus-5-5
Implements https://git.eeqj.de/sneak/quak/issues/141 on `Photo`, per the brief in its comments.
- `savePath` and `isLocal` are synchronous and look only at the disk. `savePath` is where `lib.backup()` writes the original under the library's `downloadDirectory`, present or not: for a live photo already stored, the image's path. For a live photo not yet stored, it carries the title's extension, and the backup may store the image under a different one. `isLocal` is true only when the whole original is there, not merely in the cache.
- `content()` and `exif()` are async and may download. `content()` returns the original's bytes (for a live photo, the image's). `exif()` returns a JPEG's common EXIF fields and `{}` for anything else; it never downloads a video, but throws like the other content methods without a content source.
- New getters `modifiedAt` and `hash` (also on `PhotoRecord`), plus `year`.
The JPEG EXIF scan moved from `metadata-backup.ts` to the new `src/exif.ts`, so the read surface does not import the backup command.
Not visible in the diff:
- `PhotoContent`, an exported interface, gains `savePath` and `isLocal`.
- `dateTimeOriginal` holds the camera's clock reading in the `Date`'s UTC fields, as exif-reader reads it.
Disclosures:
- Deviation: the brief's test JPEG held only Make, Model, Orientation and GPS. Mine carries every field, because a mistyped tag name still compiles.
- Judgement call: `iso` is read only when the file stores `ISOSpeedRatings` as one number. A file that stores a list gets no `iso`.
Model: opus-5-5
A Photo now has savePath and isLocal, which look only at the disk;
content() and exif(), which may download the original; and modifiedAt,
hash, fileSize and year. PhotoRecord gains modifiedAt, hash and fileSize.
The JPEG EXIF scan moves from metadata-backup.ts to the new src/exif.ts,
so the read surface does not import the backup command.
Model: opus-5-5
fileSize is described wrongly. README.md:744 and src/library/records.ts:48 call it the original's size in bytes, but the server's info.fileSize is the size of the encrypted file it stores. That is the original plus 17 bytes for every 4 MiB, and for a live photo it covers the image and the video together in one ZIP. It never equals the length of content() or the size of the file at savePath. Acceptable: describe it as the size of the encrypted file on the server and name those two differences, or derive the original's size from it.
Two sentences about the four async methods are not true of all four. README.md:725 says they read from the backup when the bytes are there, but thumbnail() never reads the backup. README.md:727 and src/library/read.ts:17 say they throw when the library has no content source, but exif() on a video returns {} instead. Acceptable: say which methods each sentence covers, or have exif() check for the content cache before it checks for a video, so that it throws like the others.
No test covers exif() on a JPEG whose EXIF block cannot be parsed (the try/catch at src/exif.ts:112). The brief at #141 (comment) requires {} there, not an error. Acceptable: a test in test/library/content-library.test.ts that serves a JPEG with a malformed EXIF block and expects {}.
Model: opus-5-5
Review: fail.
1. `fileSize` is described wrongly. README.md:744 and src/library/records.ts:48 call it the original's size in bytes, but the server's `info.fileSize` is the size of the encrypted file it stores. That is the original plus 17 bytes for every 4 MiB, and for a live photo it covers the image and the video together in one ZIP. It never equals the length of `content()` or the size of the file at `savePath`. Acceptable: describe it as the size of the encrypted file on the server and name those two differences, or derive the original's size from it.
2. Two sentences about the four async methods are not true of all four. README.md:725 says they read from the backup when the bytes are there, but `thumbnail()` never reads the backup. README.md:727 and src/library/read.ts:17 say they throw when the library has no content source, but `exif()` on a video returns `{}` instead. Acceptable: say which methods each sentence covers, or have `exif()` check for the content cache before it checks for a video, so that it throws like the others.
3. No test covers `exif()` on a JPEG whose EXIF block cannot be parsed (the `try`/`catch` at src/exif.ts:112). The brief at https://git.eeqj.de/sneak/quak/issues/141#issuecomment-107492 requires `{}` there, not an error. Acceptable: a test in test/library/content-library.test.ts that serves a JPEG with a malformed EXIF block and expects `{}`.
Model: opus-5-5
Remove fileSize from Photo, PhotoRecord and README. The server's value is the size of the encrypted file, not the original's, so it does not belong among these getters. It was my addition to the brief, not the owner's request.
exif() checks for the content cache first, so it throws like the other three methods when the cache is absent; only after that does a video return {}. Change README.md:725 so it names the methods that read from the backup.
Add the test the finding asks for: a JPEG with an EXIF block that cannot be parsed returns {}.
Model: opus-5-5
Rework, for the review above:
1. Remove `fileSize` from `Photo`, `PhotoRecord` and README. The server's value is the size of the encrypted file, not the original's, so it does not belong among these getters. It was my addition to the brief, not the owner's request.
2. `exif()` checks for the content cache first, so it throws like the other three methods when the cache is absent; only after that does a video return `{}`. Change README.md:725 so it names the methods that read from the backup.
3. Add the test the finding asks for: a JPEG with an EXIF block that cannot be parsed returns `{}`.
Model: opus-5-5
Remove fileSize from Photo, PhotoRecord, README and TODO.md: the server's
value is the size of the encrypted file, not the original's.
exif() now checks for the content cache before it returns {} for a video,
so it throws like original(), thumbnail() and content() when the library
has no content source. README names the methods that also serve an
original from the backup, since thumbnail() does not.
New tests: exif() on a JPEG whose EXIF block cannot be parsed returns {},
and exif() on a video throws without a content source.
Model: opus-5-5
savePath is documented as the backup's path even for a live photo the backup has not stored yet, which is not true. src/library/read.ts:92-93 says it is where lib.backup() writes the original whether or not it is there yet, "for a live photo, its image"; README.md:700-702 makes the same claim before limiting the image's path to a live photo already stored. Until the backup stores a live photo, savePath is the file ID with the title's extension, and the backup then writes the image under the extension found inside the live photo, so the path named beforehand never holds it (the PR's own test at test/library/content-library.test.ts:384 sees 1.jpg before the backup and 1.heic after). Acceptable: both places say plainly that for a live photo not yet stored, savePath carries the title's extension and the image may be stored under a different one, as the comment on ContentCache.savePath and that test already do.
Model: opus-5-5
Review: fail.
1. `savePath` is documented as the backup's path even for a live photo the backup has not stored yet, which is not true. src/library/read.ts:92-93 says it is where `lib.backup()` writes the original whether or not it is there yet, "for a live photo, its image"; README.md:700-702 makes the same claim before limiting the image's path to a live photo already stored. Until the backup stores a live photo, `savePath` is the file ID with the title's extension, and the backup then writes the image under the extension found inside the live photo, so the path named beforehand never holds it (the PR's own test at test/library/content-library.test.ts:384 sees `1.jpg` before the backup and `1.heic` after). Acceptable: both places say plainly that for a live photo not yet stored, `savePath` carries the title's extension and the image may be stored under a different one, as the comment on `ContentCache.savePath` and that test already do.
Model: opus-5-5
For a live photo the backup has not stored, savePath carries the
title's extension, and the backup may store the image under the one
found inside the live photo. The comment on Photo.savePath and the
README now say so, as the comment on ContentCache.savePath does.
Model: opus-5-5
src/library/content.ts:440-442: the comment on ContentCache.savePath still says it is where a backup stores the original "whether or not it is there yet", setting apart only a live photo already stored. For a live photo not yet stored, the method returns the file ID with the title's extension, and the backup stores the image under the extension found inside the live photo, so that path may never hold it. This is the wording the last review failed in read.ts and README. It remains here, although commit 824b2db says this comment already carries the caveat. Acceptable: the same caveat as src/library/read.ts:92-97.
The PR body runs to about 270 words, over the limit of about 250. Acceptable: trim it to about 250, for example by shortening "Not visible in the diff".
Model: opus-5-5
Review: fail.
1. src/library/content.ts:440-442: the comment on `ContentCache.savePath` still says it is where a backup stores the original "whether or not it is there yet", setting apart only a live photo already stored. For a live photo not yet stored, the method returns the file ID with the title's extension, and the backup stores the image under the extension found inside the live photo, so that path may never hold it. This is the wording the last review failed in read.ts and README. It remains here, although commit 824b2db says this comment already carries the caveat. Acceptable: the same caveat as src/library/read.ts:92-97.
2. The PR body runs to about 270 words, over the limit of about 250. Acceptable: trim it to about 250, for example by shortening "Not visible in the diff".
Model: opus-5-5
The comment on ContentCache.savePath, a new comment on
PhotoContent.savePath, and the TODO.md entry now say that for a live
photo not yet stored, savePath carries the title's extension, and the
backup may store the image under a different one.
Model: opus-5-5
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Implements #141 on
Photo, per the brief in its comments.savePathandisLocalare synchronous and look only at the disk.savePathis wherelib.backup()writes the original under the library'sdownloadDirectory, present or not: for a live photo already stored, the image's path. For a live photo not yet stored, it carries the title's extension, and the backup may store the image under a different one.isLocalis true only when the whole original is there, not merely in the cache.content()andexif()are async and may download.content()returns the original's bytes (for a live photo, the image's).exif()returns a JPEG's common EXIF fields and{}for anything else; it never downloads a video, but throws like the other content methods without a content source.modifiedAtandhash(also onPhotoRecord), plusyear.The JPEG EXIF scan moved from
metadata-backup.tsto the newsrc/exif.ts, so the read surface does not import the backup command.Not visible in the diff:
PhotoContent, an exported interface, gainssavePathandisLocal.dateTimeOriginalholds the camera's clock reading in theDate's UTC fields, as exif-reader reads it.Disclosures:
isois read only when the file storesISOSpeedRatingsas one number. A file that stores a list gets noiso.Model: opus-5-5
Review: fail.
fileSizeis described wrongly. README.md:744 and src/library/records.ts:48 call it the original's size in bytes, but the server'sinfo.fileSizeis the size of the encrypted file it stores. That is the original plus 17 bytes for every 4 MiB, and for a live photo it covers the image and the video together in one ZIP. It never equals the length ofcontent()or the size of the file atsavePath. Acceptable: describe it as the size of the encrypted file on the server and name those two differences, or derive the original's size from it.Two sentences about the four async methods are not true of all four. README.md:725 says they read from the backup when the bytes are there, but
thumbnail()never reads the backup. README.md:727 and src/library/read.ts:17 say they throw when the library has no content source, butexif()on a video returns{}instead. Acceptable: say which methods each sentence covers, or haveexif()check for the content cache before it checks for a video, so that it throws like the others.No test covers
exif()on a JPEG whose EXIF block cannot be parsed (thetry/catchat src/exif.ts:112). The brief at #141 (comment) requires{}there, not an error. Acceptable: a test in test/library/content-library.test.ts that serves a JPEG with a malformed EXIF block and expects{}.Model: opus-5-5
Rework, for the review above:
fileSizefromPhoto,PhotoRecordand README. The server's value is the size of the encrypted file, not the original's, so it does not belong among these getters. It was my addition to the brief, not the owner's request.exif()checks for the content cache first, so it throws like the other three methods when the cache is absent; only after that does a video return{}. Change README.md:725 so it names the methods that read from the backup.{}.Model: opus-5-5
Remove fileSize from Photo, PhotoRecord, README and TODO.md: the server's value is the size of the encrypted file, not the original's. exif() now checks for the content cache before it returns {} for a video, so it throws like original(), thumbnail() and content() when the library has no content source. README names the methods that also serve an original from the backup, since thumbnail() does not. New tests: exif() on a JPEG whose EXIF block cannot be parsed returns {}, and exif() on a video throws without a content source. Model: opus-5-5Review: fail.
savePathis documented as the backup's path even for a live photo the backup has not stored yet, which is not true. src/library/read.ts:92-93 says it is wherelib.backup()writes the original whether or not it is there yet, "for a live photo, its image"; README.md:700-702 makes the same claim before limiting the image's path to a live photo already stored. Until the backup stores a live photo,savePathis the file ID with the title's extension, and the backup then writes the image under the extension found inside the live photo, so the path named beforehand never holds it (the PR's own test at test/library/content-library.test.ts:384 sees1.jpgbefore the backup and1.heicafter). Acceptable: both places say plainly that for a live photo not yet stored,savePathcarries the title's extension and the image may be stored under a different one, as the comment onContentCache.savePathand that test already do.Model: opus-5-5
Review: fail.
ContentCache.savePathstill says it is where a backup stores the original "whether or not it is there yet", setting apart only a live photo already stored. For a live photo not yet stored, the method returns the file ID with the title's extension, and the backup stores the image under the extension found inside the live photo, so that path may never hold it. This is the wording the last review failed in read.ts and README. It remains here, although commit824b2dbsays this comment already carries the caveat. Acceptable: the same caveat as src/library/read.ts:92-97.Model: opus-5-5
Review: pass.
Model: opus-5-5