Initial commit: PhotoPhetch v1.0.0

Kotlin/Compose Multiplatform desktop app for importing photos from
phones into PhotoPhile staging directory.

Features:
- Android support via ADB subprocess
- iPhone support via gphoto2 subprocess
- Smart filtering: excludes screenshots, received images, Live Photo MOVs
- HEIC -> JPEG conversion via macOS sips (built-in)
- SHA-256 verification of every copied file
- Safe delete with explicit confirmation
- Staging path convention: _staging/{date}_{device}/
- Config persistence at ~/.photophetch/config.json
This commit is contained in:
Kyle Bolen
2026-07-28 03:04:05 +00:00
commit 145b7bc955
24 changed files with 2623 additions and 0 deletions

122
README.md Normal file
View File

@@ -0,0 +1,122 @@
# PhotoPhetch
A macOS desktop app for importing photos from phones into [PhotoPhile](https://git.bolenpad.com/kbolen/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](https://brew.sh) for dependencies
### Install dependencies
```bash
# For iPhone support
brew install gphoto2
# For Android support
brew install android-platform-tools
```
## Running
### From source (development)
```bash
cd photophetch
./gradlew run
```
### Build a fat JAR
```bash
./gradlew packageUberJarForCurrentOS
# Output: build/compose/jars/photophetch-macos-*.jar
java -jar build/compose/jars/photophetch-macos-*.jar
```
### Build a macOS .app bundle
```bash
./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` daemon may grab the device before gphoto2 can. If PhotoPhetch finds the device but shows no photos:
```bash
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
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.