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
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
Adds a synchronous, key-free snapshot() returning LibrarySnapshot (one PhotoRecord per fileID, deduped, newest first) with edited-name/time precedence (pubMagicMetadata over basic metadata) in milliseconds, plus AlbumRecord (favorites identified by type). subscribe({onChange}) delivers LibraryChange (changed/removed albums and files, refreshedAt) only when a refresh changes something; unsubscribe stops delivery. Built on the existing refresh loop; served from RAM, safe to send over IPC.
Model: opus-4-8