From 9c0c7c9489a8d8ebbfc4d5208866e14c38aa7117 Mon Sep 17 00:00:00 2001 From: sneak Date: Thu, 1 Oct 2026 19:14:59 +0000 Subject: [PATCH] Test the unset-clock EXIF date; drop the old dump layout from README Add a readPhotoExif test: EXIF whose DateTimeOriginal is 0000:00:00 00:00:00, as a camera with an unset clock writes it, gives no dateTimeOriginal while its other fields are still read. Remove the README sentence describing the backup's exif field in dumps from an earlier version. Model: opus-5-5 --- README.md | 10 ++++------ test/cli/metadata-exif.test.ts | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 3ab7ff2..66d60ea 100644 --- a/README.md +++ b/README.md @@ -500,12 +500,10 @@ its XMP metadata, its EXIF metadata and, for a JPEG, its dimensions. EXIF is read with [exifreader](https://github.com/mattiasw/ExifReader) from any image format it reads, JPEG, HEIC/HEIF, AVIF, PNG and WebP among them. The record's `exif` field is exifreader's EXIF tag output: each tag by name, with its -`value`, `description` and `computed` value. Dumps written before 2026-10-01 -hold `exif-reader`'s `Image`, `Photo` and `GPSInfo` groups there instead. An -EXIF block exifreader finds but reads no tag from is recorded, base64, as -`exifRaw`, with the reason in `exifError`. `collections`, `files`, `backup`, -`helper list-missing-thumbnails` and `helper fix-missing-thumbnails` take -`--json` for machine-readable output. +`value`, `description` and `computed` value. An EXIF block exifreader finds but +reads no tag from is recorded, base64, as `exifRaw`, with the reason in +`exifError`. `collections`, `files`, `backup`, `helper list-missing-thumbnails` +and `helper fix-missing-thumbnails` take `--json` for machine-readable output. `backup-metadata` fetches ML data in requests of up to 200 files. When a request fails, the error is logged, each of its files is written with the reason in an diff --git a/test/cli/metadata-exif.test.ts b/test/cli/metadata-exif.test.ts index dffe9a6..2deda57 100644 --- a/test/cli/metadata-exif.test.ts +++ b/test/cli/metadata-exif.test.ts @@ -27,6 +27,25 @@ const TIFF_ORIENTATION_6 = [ 0x00, 0x00, ]; +// A big-endian TIFF block holding Orientation 6 and DateTimeOriginal +// "0000:00:00 00:00:00", which a camera with an unset clock writes. +const TIFF_UNSET_DATE = [ + ...[0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08], // the first IFD at 8 + // The first IFD, at 8: two entries, then no next IFD. + ...[0x00, 0x02], + // Orientation (0x0112), SHORT, 6. + ...[0x01, 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00], + // The Exif IFD's offset (0x8769), LONG, 38. + ...[0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x26], + ...[0x00, 0x00, 0x00, 0x00], + // The Exif IFD, at 38: one entry, then no next IFD. + ...[0x00, 0x01], + // DateTimeOriginal (0x9003), 20 ASCII bytes at 56. + ...[0x90, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x38], + ...[0x00, 0x00, 0x00, 0x00], + ...new TextEncoder().encode("0000:00:00 00:00:00\0"), // at 56 +]; + // An APP1 segment whose length field matches its data. const app1 = (data: number[]): number[] => { const len = data.length + 2; @@ -44,6 +63,13 @@ describe("readPhotoExif", () => { }); }); + it("gives no dateTimeOriginal for a DateTimeOriginal of 0000:00:00 00:00:00", () => { + const data = [...EXIF_HEADER, ...TIFF_UNSET_DATE]; + expect(readPhotoExif(bytes(SOI, app1(data), SOS))).toStrictEqual({ + orientation: 6, + }); + }); + it.each([ [ "a file that is not an image",