Uses AWT FileDialog with apple.awt.fileDialogForDirectories=true to open the native macOS folder chooser. Falls back gracefully (dialog cancelled) with no change to the text field.
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
- Launch the app
- Go to Settings and set your staging root (e.g.
/Volumes/delphi/_staging) - Make sure your NAS is mounted at that path
- Connect your phone via USB
- Click Scan for Devices on the Connect screen
iPhone notes
When you plug in an iPhone, macOS's built-in PTPCamera daemon may grab the device before gphoto2 can. If PhotoPhetch finds the device but shows no photos:
killall PTPCamera
Then rescan in the app.
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.