Skip to content

feat: add photo organizing, a new category - #10

Merged
vamgan merged 2 commits into
mainfrom
feat/photos
Aug 25, 2026
Merged

vamgan merged 2 commits into
mainfrom
feat/photos

Conversation

@vamgan

@vamgan vamgan commented Aug 25, 2026

Copy link
Copy Markdown
Owner

Photos were the highest-value candidate from the category brainstorm and the one I set aside, because near-duplicate detection needs image decoding and that breaks the no-dependency rule.

Most of the value turns out not to need it.

Reading photos without decoding them

scripts/photos.py pulls EXIF dates, camera make and model, and image dimensions straight from file headers. Stdlib only, same behaviour on every platform.

That buys the thing sorting usually gets wrong:

A file's modified date is not when the photo was taken.

Copying, syncing, restoring a backup, or receiving an image through a messaging app all rewrite it. Sort by file date and a 2019 holiday lands in whatever month the phone was last restored.

So the skill leads with the count and offers the rest separately:

2,847 photos
  2,610 carry the date they were taken
    237 do not, mostly saved images and things sent by other people

I can group the 2,610 accurately. For the other 237 the only date
available is when the file was last written, which is often wrong.
Group those separately, or leave them where they are?

Bursts are not duplicates

Detected by Treated as
Duplicates byte-for-byte identical safe to propose collapsing
Bursts timestamps 3s apart, same camera shown, never resolved

Forty near-identical frames are a person holding the shutter down. Only they can see which one has everyone's eyes open, so the skill never picks the keeper.

Nothing in this skill deletes a photo. Not to the trash, not "safely", not even exact duplicates without asking. The most it does unprompted is move files.

The EXIF parser is validated against an independent reader

Testing a parser against fixtures written by my own generator proves nothing — both can be wrong the same way. So I spliced the generated APP1 segment into a real JPEG and read it back with macOS sips:

sips  -> make: Apple   model: iPhone 15 Pro   4096x2160
mine  -> make: Apple   model: iPhone 15 Pro   4096x2160

A different implementation confirms the layout.

Also

  • media is a new category, so the set is now one skill per category across four categories
  • Apple Photos libraries are explicitly not supported and the skill stops rather than reaching into the package and corrupting it
  • Photo libraries inside cloud drives are checked for placeholder files first

152 tests, 25 of them new.

vamgan added 2 commits August 25, 2026 10:13
Photos are the largest clutter surface on a normal machine and the one
people most want sorted. The blocker was that near-duplicate detection
needs image decoding, which would break the no-dependency rule. Most of
the value turns out not to need it.

scripts/photos.py reads EXIF dates, camera make and model, and image
dimensions straight from file headers. No decoding, no dependencies, same
behaviour on every platform.

That buys the thing sorting usually gets wrong. A file's modified date is
not when the photo was taken; copying, syncing, restoring a backup, or
receiving an image through a messaging app all rewrite it. Sort by file
date and a 2019 holiday lands in whatever month the phone was last
restored. The skill leads with how many photos carry a real date and
offers the rest separately rather than silently guessing.

Bursts are detected by timestamps three seconds apart on the same camera,
not by comparing pixels, and are deliberately kept distinct from exact
duplicates. Forty near-identical frames are a person holding the shutter
down, and only they can see which one has everyone's eyes open.

Nothing in this skill deletes a photo. The most it does unprompted is
move files into a clearer structure.

The EXIF parser was validated against an independent reader rather than
against its own test fixtures: splicing the generated APP1 segment into a
real JPEG and reading it back with macOS sips returns the same make and
model this parser reports.
@vamgan
vamgan merged commit 279de97 into main Aug 25, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant