Kyle Bolen ec114aa7ce Fix Android file listing: replace stat with ls -la for compatibility
Samsung (and other Android vendors using toybox) do not support GNU
stat -c flag syntax. Switch to find for file discovery + ls -la per
file for size/date metadata, which works on all Android versions.
2026-07-28 04:36:40 +00:00
2026-07-28 03:04:05 +00:00
2026-07-28 03:09:28 +00:00

PhotoPhetch

A macOS desktop app for importing photos from phones into PhotoPhile's staging directory.

Solves the "messy phone import" problem: only grabs camera-roll originals, filters out screenshots and received images, optionally converts HEIC → JPEG, and SHA-256 verifies every file before optionally deleting it from the phone.

Features

  • Android support via ADB (adb pull)
  • iPhone support via gphoto2 (PTP over USB)
  • Smart filtering: excludes screenshots, WhatsApp/Telegram received images, and Live Photo motion clips
  • HEIC → JPEG conversion via macOS sips (built-in, no install needed)
  • SHA-256 verification of every copied file
  • Safe delete: explicit confirmation screen before removing files from the phone
  • Staging path convention: writes to _staging/<YYYY-MM-DD>_<DeviceName>/
  • Persists settings to ~/.photophetch/config.json

Requirements

  • macOS (primary target)
  • Java 21+
  • Homebrew for dependencies

Install dependencies

# For iPhone support
brew install gphoto2

# For Android support
brew install android-platform-tools

Running

From source (development)

cd photophetch
./gradlew run

Build a fat JAR

./gradlew packageUberJarForCurrentOS
# Output: build/compose/jars/photophetch-macos-*.jar
java -jar build/compose/jars/photophetch-macos-*.jar

Build a macOS .app bundle

./gradlew packageDmg
# Output: build/compose/binaries/main/dmg/PhotoPhetch-1.0.0.dmg

First-time setup

  1. Launch the app
  2. Go to Settings and set your staging root (e.g. /Volumes/delphi/_staging)
  3. Make sure your NAS is mounted at that path
  4. Connect your phone via USB
  5. Click Scan for Devices on the Connect screen

iPhone notes

When you plug in an iPhone, macOS's built-in PTPCamera and AMPDeviceDiscoveryAgent daemons grab the device before gphoto2 or ADB can. Symptoms: "Could not connect to device" errors, Image Capture showing the phone under DEVICES with nothing listed, or LOC:something entries cycling in Image Capture.

Kill them before scanning:

killall PTPCamera
killall AMPDeviceDiscoveryAgent

Close Image Capture first if it's open. Then rescan in PhotoPhetch.

To stop them from re-grabbing the device on every USB connect (recommended if you use PhotoPhetch regularly):

launchctl unload -w /System/Library/LaunchAgents/com.apple.ptpcamera.plist

To re-enable Image Capture later:

launchctl load -w /System/Library/LaunchAgents/com.apple.ptpcamera.plist

HEIC settings

If your iPhone is shooting in HEIC (the default), PhotoPhetch will warn you and convert to JPEG during import. To stop shooting HEIC:

Settings → Camera → Formats → Most Compatible

Android notes

Enable USB debugging:

Settings → About Phone → tap Build Number 7 times → Developer Options → USB Debugging

When you plug in the USB cable, a prompt will appear on the phone asking for the connection mode. Choose "Transferring files / Android Auto" (also called "File Transfer" on some phones). Do not choose "Transferring images" (PTP) — it restricts file access and breaks ADB browsing.

Accept the "Trust this computer?" prompt on the phone when connecting.

Project structure

src/main/kotlin/com/bolenpad/photophetch/
├── Main.kt                    # Entry point
├── device/
│   ├── DeviceConnector.kt     # Interface
│   ├── AndroidConnector.kt    # ADB implementation
│   └── IosConnector.kt        # gphoto2 implementation
├── model/
│   ├── PhonePhoto.kt          # Photo metadata
│   ├── ImportJob.kt           # Import batch + results
│   ├── DeviceInfo.kt          # Connected device info
│   └── AppConfig.kt           # Persisted preferences
├── transfer/
│   └── Copier.kt              # Copy + SHA-256 verify + HEIC conversion
├── ui/
│   ├── App.kt                 # Root composable + navigation
│   ├── AppState.kt            # StateFlow ViewModel
│   ├── ConnectScreen.kt       # Device detection
│   ├── BrowseScreen.kt        # Photo grid + filters
│   ├── ImportScreen.kt        # Import configuration
│   ├── VerifyScreen.kt        # Progress + results + delete confirmation
│   └── SettingsScreen.kt      # Staging path + preferences
└── util/
    ├── ConfigStore.kt          # Config persistence
    ├── ExifFilter.kt           # Screenshot/noise filtering
    └── StagingPath.kt          # Staging directory path resolution

PhotoPhile integration

PhotoPhetch writes directly to the mounted NAS path — no API calls. After import, PhotoPhile picks up the new folder in _staging/ and runs it through its normal pipeline: configure → normalize → publish.

Description
Desktop app for importing photos from phones into PhotoPhile staging
Readme 796 KiB
2026-08-02 11:32:00 -07:00
Languages
Kotlin 88.1%
Python 7%
Shell 4.9%