feat: update iPhone photo inbox scripts with environment file support and refactor batch handling

This commit is contained in:
2026-05-15 08:43:19 -06:00
parent 456a4c3381
commit 8950cfcdf0
6 changed files with 288 additions and 391 deletions

View File

@@ -1,61 +1,37 @@
# iPhone Photo Inbox
# Photo Inbox
Local HTTP receiver for sending JPEGs from iPhone Shortcuts into Mac inboxes.
The Shortcut sends a `profile`, and the Mac decides the destination folder and
clipboard behavior.
macOS HTTP receiver for sending JPEG uploads into a local photo inbox. Clients
can be iPhone Shortcuts, curl, another phone, a script, or any system that can
POST a JPEG file. The server currently supports macOS only because clipboard,
Finder reveal, and notifications use macOS APIs/tools.
## Profiles
By default, each upload is saved locally and the current batch is copied to the
macOS clipboard as native file URLs.
`opencode`
## Behavior
- Saves to `ai/inbox/photos/`
- Copies terminal-safe paths to the clipboard
- Best for pasting into OpenCode running in a terminal
- Saves photos to `~/Pictures/Photo Inbox` by default.
- Groups consecutive uploads into a batch.
- Every new photo extends the batch by `5s`.
- Every new photo immediately refreshes the clipboard with the full batch.
- When no new photo arrives before debounce expires, a summary notification is shown.
`mattermost`
- Saves to `~/Pictures/iPhone Inbox`
- Copies native macOS file URLs to the clipboard
- Best effort for pasting one or more files directly into Mattermost
`general`
- Saves to `~/Pictures/iPhone Inbox`
- Does not modify the clipboard
- Useful for plain capture
All profiles show a macOS notification by default.
## Batch debounce
Uploads are grouped by profile. Every new photo extends the active profile batch
by 10 seconds and immediately refreshes the clipboard with the full batch.
Example for `opencode`:
```text
photo 1 arrives -> clipboard has photo 1 path
photo 2 arrives within 10s -> clipboard has photo 1 + photo 2 paths
photo 3 arrives within 10s -> clipboard has photo 1 + photo 2 + photo 3 paths
10s pass with no new photo -> notification says the batch is ready
```
This keeps the Shortcut simple while still making the clipboard usable before
the final notification appears.
For `mattermost`, each upload rewrites the clipboard with the full batch using a
small Swift helper and `NSPasteboard.writeObjects`. This is closer to Finder's
file-copy behavior than the older AppleScript alias approach. If the native
helper fails, the receiver falls back to AppleScript and logs the fallback.
This uses a small Swift helper and `NSPasteboard.writeObjects`, which matches
Finder-style file clipboard behavior.
## Start the receiver
Recommended:
```bash
IPHONE_PHOTO_TOKEN="choose-a-token" python3 scripts/iphone-photo-inbox/receiver.py
cp scripts/iphone-photo-inbox/.env.example scripts/iphone-photo-inbox/.env
scripts/iphone-photo-inbox/run.sh
```
`.env` is loaded automatically and does not override variables already exported
in the shell. `run.sh` compiles the native pasteboard helper when it is missing
or older than the Swift source.
The receiver listens on:
```text
@@ -74,7 +50,19 @@ If that does not return an IP, use:
ifconfig
```
## Shortcut config
## Generic client contract
Send a JPEG request body:
```bash
curl --request POST \
--data-binary @photo.jpg \
"http://MAC_IP:8787/upload?token=choose-a-token"
```
Successful response body is the saved local file path.
## iPhone Shortcuts guide
Use a Dictionary near the top of the Shortcut:
@@ -82,29 +70,24 @@ Use a Dictionary near the top of the Shortcut:
mac_ip: 192.168.11.186
port: 8787
token: choose-a-token
profile: opencode
```
Build the URL from the dictionary:
```text
http://[mac_ip]:[port]/upload?token=[token]&profile=[profile]
http://[mac_ip]:[port]/upload?token=[token]
```
Use `profile: opencode` when the next paste target is OpenCode. Use
`profile: mattermost` when the next paste target is Mattermost.
## Camera shortcut
Camera Shortcut:
```text
Dictionary
mac_ip: 192.168.11.186
port: 8787
token: choose-a-token
profile: opencode
Text
http://[mac_ip]:[port]/upload?token=[token]&profile=[profile]
http://[mac_ip]:[port]/upload?token=[token]
Take Photo
Show Camera Preview: On
@@ -116,15 +99,13 @@ Get Contents of URL
File: Photo
Show Notification
Sent to [profile]
Sent to photo inbox
```
On the tested iPhone flow, `Take Photo` already produces a JPEG, so no
conversion step is needed.
## Existing photos shortcut
Use this when sending existing images from Photos:
Existing Photos Shortcut:
```text
Receive Images and Media from Share Sheet
@@ -133,87 +114,88 @@ Repeat with Each Item in Shortcut Input
Image: Repeat Item
Format: JPEG
Get Contents of URL
URL: http://[mac_ip]:[port]/upload?token=[token]&profile=[profile]
URL: http://[mac_ip]:[port]/upload?token=[token]
Method: POST
Request Body: File
File: Converted Image
End Repeat
Show Notification
Sent to [profile]
Sent to photo inbox
```
## Overrides
## Configuration
Profile folders:
Common `.env` values:
```bash
IPHONE_PHOTO_OPENCODE_DIR="/path/to/opencode/photos"
IPHONE_PHOTO_MATTERMOST_DIR="$HOME/Pictures/iPhone Inbox"
IPHONE_PHOTO_GENERAL_DIR="$HOME/Pictures/iPhone Inbox"
```
Global folder override for all profiles:
```bash
IPHONE_PHOTO_OUTPUT_DIR="$HOME/Pictures/iPhone Inbox" \
IPHONE_PHOTO_TOKEN="choose-a-token" \
python3 scripts/iphone-photo-inbox/receiver.py
```
Default profile when the URL does not include `profile=`:
```bash
IPHONE_PHOTO_PROFILE=mattermost \
IPHONE_PHOTO_TOKEN="choose-a-token" \
python3 scripts/iphone-photo-inbox/receiver.py
```
Clipboard override for all profiles:
```bash
IPHONE_PHOTO_CLIPBOARD=image
IPHONE_PHOTO_CLIPBOARD=files
IPHONE_PHOTO_CLIPBOARD=terminal-path
IPHONE_PHOTO_CLIPBOARD=path
IPHONE_PHOTO_CLIPBOARD=file
IPHONE_PHOTO_CLIPBOARD=none
```
Debounce override:
```bash
IPHONE_PHOTO_DEBOUNCE_SECONDS=5
PHOTO_INBOX_TOKEN=choose-a-token
PHOTO_INBOX_HOST=0.0.0.0
PHOTO_INBOX_PORT=8787
PHOTO_INBOX_DIR=/Users/david/Pictures/Photo Inbox
PHOTO_INBOX_DEBOUNCE_SECONDS=5
PHOTO_INBOX_CLIPBOARD=1
PHOTO_INBOX_NOTIFY=1
```
Other useful options:
```bash
python3 scripts/iphone-photo-inbox/receiver.py --no-notify
python3 scripts/iphone-photo-inbox/receiver.py --reveal
IPHONE_PHOTO_DEBUG=1 python3 scripts/iphone-photo-inbox/receiver.py
scripts/iphone-photo-inbox/run.sh --no-clipboard
scripts/iphone-photo-inbox/run.sh --no-notify
scripts/iphone-photo-inbox/run.sh --reveal
PHOTO_INBOX_DEBUG=1 scripts/iphone-photo-inbox/run.sh
```
## Project integration
Keep this utility as an isolated image mailbox. If a project wants easy access,
link the project inbox to the mailbox instead of making this utility know about
the project.
Example:
```bash
mkdir -p ai/inbox
ln -s "$HOME/Pictures/Photo Inbox" ai/inbox/photos
```
Or point the receiver at a project-owned folder from `.env`:
```bash
PHOTO_INBOX_DIR=/absolute/path/to/project/ai/inbox/photos
```
The symlink approach keeps this utility reusable across projects and devices.
## Troubleshooting
Startup should print each active profile:
Startup should print:
```text
profile opencode: dir=... clipboard=terminal-path notify=True reveal=False
profile mattermost: dir=... clipboard=files notify=True reveal=False
saving to: ...
debounce seconds: 5
clipboard: True
notify: True
```
After each upload, expect:
```text
clipboard mode applied: terminal-path profile=opencode count=2
saved ... profile=opencode batch_count=2
clipboard updated count=2
saved ... batch_count=2
```
After the debounce window closes, expect:
```text
batch notification sent profile=opencode count=2
batch finalized profile=opencode count=2 dir=...
batch notification sent count=2
batch finalized count=2 dir=...
```
With `PHOTO_INBOX_DEBUG=1`, a two-photo batch should report:
```text
pasteboard files=2 items=2
```
The native file clipboard helper lives at:
@@ -222,20 +204,8 @@ The native file clipboard helper lives at:
scripts/iphone-photo-inbox/copy_files_to_clipboard.swift
```
For faster multi-file clipboard updates during debounce, compile it once:
The compiled binary is ignored by git and generated by `run.sh`:
```bash
swiftc scripts/iphone-photo-inbox/copy_files_to_clipboard.swift \
-o scripts/iphone-photo-inbox/copy_files_to_clipboard
```text
scripts/iphone-photo-inbox/copy_files_to_clipboard
```
The compiled binary is ignored by git. If it is not present, the receiver can
run the Swift script directly, but that is slower on first use.
If files arrive but clipboard/notifications do not behave as expected, check:
- The Shortcut URL includes the intended `profile=`.
- The receiver log shows the expected profile.
- With `IPHONE_PHOTO_DEBUG=1`, the Mattermost profile should report `pasteboard files=2 items=2` after the second photo in a two-photo batch.
- macOS Focus/Do Not Disturb is not hiding notifications.
- Terminal/Codex has permission for AppleScript automation if macOS prompts.