Commit Graph
5 Commits
Author SHA1 Message Date
clawbot e50d2a78c8 exif() returns every EXIF tag in the file (closes #156)
check / check (push) Failing after 59s
`photo.exif()` now returns every EXIF tag in the file, as the owner ruled, typed `ExifTags`: each tag keyed by name with `exifreader`'s `id`, `value`, `description` and `computed`. A tag with no name is keyed `undefined-` plus its number, and the embedded thumbnail's tags sit under `Thumbnail`. The thirteen typed methods stay, with the same names and types; each now picks its field from `exif()`'s tags. GPS latitude and longitude are worked out from the GPS tags and their reference tags, and a position with no reference tags gives neither. `backup-metadata --exif` output is unchanged.

Model: opus-5-5
Co-authored-by: clawbot <sneak+clawbot@sneak.cloud>
2026-10-02 05:30:05 +02:00
clawbot 67d554fb46 exif(): read HEIF/HEIC originals with exifreader (closes #145)
check / check (push) Successful in 2m10s
`photo.exif()` and `quak backup-metadata --exif` now read EXIF through `exifreader`. HEIC/HEIF originals get EXIF, including a live photo's image, as do the other formats `exifreader` reads. It replaces `exif-reader` and the hand-written JPEG scan. `PhotoExif` is unchanged.

The `backup-metadata` dump now holds `exifreader`'s tag output, with unnamed tags keyed `undefined-` plus their number. GPS altitude without a reference counts as above sea level. A latitude or longitude without its hemisphere tag, an unreadable text tag, and a date the parser rejects each give no field.

Licence: `exifreader` is MPL-2.0, used unmodified.

Model: opus-5-5
2026-10-01 22:34:09 +02:00
clawbot 2b598d3622 Photo: save path, is-local, content bytes, metadata and EXIF getters (closes #141)
check / check (push) Successful in 1m24s
`Photo` gains:

- `savePath` and `isLocal`: synchronous, disk only. Where `lib.backup()` writes the original under the library's `downloadDirectory`, and whether all of it is there; a copy only in the cache does not count.
- `content()` and `exif()`: async, may download. `exif()` reads the common EXIF fields of a JPEG and returns `{}` for anything else.
- The getters `modifiedAt` and `hash`, also on `PhotoRecord`, and `year`.

For a live photo the backup has not stored yet, `savePath` carries the title's extension, and the image may be stored under a different one. The JPEG EXIF scan moved to `src/exif.ts`. The exported `PhotoContent` interface gains `savePath` and `isLocal`.

Judgement call: `iso` is read only when the file stores it as a single number.

Model: opus-5-5
2026-10-01 17:58:23 +02:00
clawbot c75c4f987c Pin three untested guards: download timer, URL fragment, short APP1 (closes #89)
check / check (push) Successful in 24s
The download idle deadline's timer is unref'd so it never holds the
process open, and a test checks no timer is left after a download
completes or fails. A test covers the rejection of "#" in a request
path. The EXIF scan accepts an APP1 segment only when its length is at
least 8, since a shorter one cannot hold the six-byte Exif header;
tests cover lengths 7 and 8.

Model: opus-5-5
2026-09-23 03:44:45 +02:00
clawbot 52f58f5d2b Harden the JPEG EXIF scan against malformed input (closes #11)
check / check (push) Successful in 29s
The segment scan behind `backup-metadata --exif` now checks every
segment length against the bytes that remain and stops on lengths
under 2, so a truncated or corrupt original can neither throw nor loop.
A malformed or unparseable EXIF segment is recorded as
`imageMetadata.exifError`, and a failure to read the original as
`imageMetadataError` in the per-file JSON, instead of the field being
silently left out. Tests use short hand-built byte arrays.

Model: opus-5-5
2026-09-23 02:28:15 +02:00