140 lines
4.8 KiB
Markdown
140 lines
4.8 KiB
Markdown
# 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` 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:
|
|
|
|
```bash
|
|
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):
|
|
|
|
```bash
|
|
launchctl unload -w /System/Library/LaunchAgents/com.apple.ptpcamera.plist
|
|
```
|
|
|
|
To re-enable Image Capture later:
|
|
|
|
```bash
|
|
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.
|