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
This commit was merged in pull request #142.
This commit is contained in:
2026-10-01 17:58:23 +02:00
parent e6825abcdb
commit 2b598d3622
12 changed files with 566 additions and 86 deletions
+23
View File
@@ -209,6 +209,29 @@ describe("lib.photos", () => {
expect("key" in photo.record()).toBe(false);
});
it("byID exposes modifiedAt, hash and the year taken", () => {
// Mid-July, so the year is 2021 in every time zone.
const takenAt = Date.UTC(2021, 6, 15, 12);
const records = deriveRecords(
[collection(1)],
[
file(1001, 1, {
metadata: {
title: "IMG.jpg",
fileType: "image",
creationTime: micros(takenAt),
modificationTime: micros(1_700_000_123_456),
hash: "aGFzaA==",
},
}),
],
);
const photo = apis(records).photos.byID({ fileID: 1001 })!;
expect(photo.modifiedAt).toBe(ms(1_700_000_123_456));
expect(photo.hash).toBe("aGFzaA==");
expect(photo.year).toBe(2021);
});
it("byID returns undefined for an unknown file id", () => {
const records = deriveRecords([collection(1)], [file(1, 1)]);
expect(apis(records).photos.byID({ fileID: 999 })).toBeUndefined();