Commit Graph

13 Commits

Author SHA1 Message Date
Kyle Bolen
558a7add89 HEIC thumbnails: pull full file (partial reads unreliable for sips conversion)
1MB partial HEIC reads are not sufficient for sips to convert to JPEG.
Revert to full file pull for HEIC thumbnails. Files are cached permanently
so each HEIC is only transferred once — subsequent launches skip already-cached files.
2026-07-30 03:57:44 +00:00
Kyle Bolen
30b600e2a4 HEIC thumbnails: extract embedded thumbnail instead of full file pull
For HEIC photos: read first 1MB (thumbnail track is near file start),
then use sips -getProperty thumbnail to extract the embedded JPEG
thumbnail without decoding the full image. Falls back to full resample
if thumbnail extraction fails.

For JPEG/PNG: unchanged, 512KB partial read + resample.

This avoids transferring 3-5MB per HEIC photo just for a thumbnail.
2026-07-30 03:54:40 +00:00
Kyle Bolen
6ed4362856 Fix HEIC thumbnails: pull full file + convert to JPEG via sips
HEIC is not front-loaded — partial 512KB reads produce broken output.
batch-thumbs now pulls full file for HEIC/HEIF, partial for JPEG/PNG.

sips: add --setProperty format jpeg to all thumbnail conversions so
HEIC files are always converted to JPEG. SkiaImage on JVM desktop
cannot decode HEIC natively — JPEG is required.
2026-07-30 03:53:24 +00:00
Kyle Bolen
b12cb4dd1e iOS thumbnails: add batch-thumbs command for future optimization
batch-thumbs pulls multiple files in one Python process session,
amortising the pymobiledevice3 startup cost. Not yet wired into the
app — thumbnails load lazily per-cell and cache permanently.

Known limitation: first load of iPhone thumbnails is slow (~2-3s each)
because each pull spawns a new Python process. After first view they
are instant from the on-disk cache.
2026-07-28 08:44:54 +00:00
Kyle Bolen
66faf4c7e5 iOS thumbnails: use partial 512KB read for faster thumbnail generation
Add pull-thumb command to helper script that uses afc.fread() to read
only the first 512KB of each photo. EXIF thumbnails are in the first
few KB so sips can generate a resized thumbnail from the partial file.
Falls back to full afc.pull() if fread is unavailable.

IosConnector.pullToFile() uses pull-thumb for thumbnail requests.
Full afc.pull() still used for actual photo import.
2026-07-28 08:37:57 +00:00
Kyle Bolen
f739114f7d Fix iOS date: st_mtime is datetime.datetime object, use isoformat() directly 2026-07-28 08:33:36 +00:00
Kyle Bolen
fa2e95bb45 Simplify iOS date parsing: use datetime tuple, confirmed working format 2026-07-28 08:32:33 +00:00
Kyle Bolen
884dc7ec30 Fix iOS date: use 'datetime' tuple key from AFC stat
pymobiledevice3 AfcService.stat() returns 'datetime' as a tuple
(year, month, day, hour, minute, second, microsecond) not 'st_mtime'.
Parse it directly into a datetime object and emit as ISO string.
2026-07-28 08:31:56 +00:00
Kyle Bolen
6f7dad3296 Add debug logging to diagnose iOS stat date issue
iOS helper: log raw stat keys+values for first file to stderr.
IosConnector: already logs stderr at WARN level.
logback.xml: print DEBUG+ for photophetch package to console so
stat output is visible when running ./gradlew run.
2026-07-28 08:29:37 +00:00
Kyle Bolen
fecdef3acf Fix date parsing: handle both nanosecond and second AFC timestamps
Threshold: >1e15 = nanoseconds, >1e9 = seconds. Previous threshold of
1e18 was too high for nanosecond timestamps in the year 2025 range
(which are ~1.75e18, so actually fine). Also revert pull to use
afc.pull() since it exists in this pymobiledevice3 version.
2026-07-28 08:26:30 +00:00
Kyle Bolen
d947018f42 Fix iOS helper: correct date parsing and pull method
Date: AFC st_mtime is nanoseconds since epoch. Fix threshold check:
values > 1e18 are nanoseconds, > 1e9 are seconds. Dec 31 bug was
caused by mtime being treated as seconds when it was nanoseconds,
giving a timestamp near epoch (1970 = Dec 31 local time).

Pull: use get_file_contents() which is the correct async AFC method
(not pull/afc.open which don't exist in this pymobiledevice3 version).
2026-07-28 08:17:39 +00:00
Kyle Bolen
7f11a4b7d6 Fix iOS helper: async pymobiledevice3 API + auto-discover pipx Python
The helper script now uses asyncio.run() for all pymobiledevice3 calls
since create_using_usbmux() and AfcService methods are async in newer
versions.

IosConnector: auto-discovers the pipx venv Python at
~/Library/Application Support/pipx/venvs/pymobiledevice3/bin/python3
instead of using system python3 (which doesn't have the module).
Falls back to ~/.local/pipx/... paths too.

AppConfig python3Path acts as an override if auto-discovery fails.
2026-07-28 08:06:13 +00:00
Kyle Bolen
50d6f66116 Replace gphoto2 with pymobiledevice3 for iOS connectivity
gphoto2 stopped working on iOS 17+ (Apple tightened PTP access).
New approach uses pymobiledevice3 via AFC protocol — the same protocol
Finder uses to browse the iPhone.

- photophetch-ios-helper.py: bundled Python script (in JAR resources)
  that calls pymobiledevice3 AFC to list-devices, list-photos, pull, delete
- IosConnector: rewritten to shell out to the helper script instead of
  gphoto2. Extracts the script from JAR to a temp file on first use.
- AppConfig: gphoto2Path → python3Path
- AppState: IosConnector now receives python3Path

Prerequisites: brew install pipx && pipx install pymobiledevice3
2026-07-28 07:47:16 +00:00