242 lines
5.7 KiB
Markdown
242 lines
5.7 KiB
Markdown
# iPhone 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.
|
|
|
|
## Profiles
|
|
|
|
`opencode`
|
|
|
|
- Saves to `ai/inbox/photos/`
|
|
- Copies terminal-safe paths to the clipboard
|
|
- Best for pasting into OpenCode running in a terminal
|
|
|
|
`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.
|
|
|
|
## Start the receiver
|
|
|
|
Recommended:
|
|
|
|
```bash
|
|
IPHONE_PHOTO_TOKEN="choose-a-token" python3 scripts/iphone-photo-inbox/receiver.py
|
|
```
|
|
|
|
The receiver listens on:
|
|
|
|
```text
|
|
http://MAC_IP:8787/upload
|
|
```
|
|
|
|
Find the Mac IP address on the current network:
|
|
|
|
```bash
|
|
ipconfig getifaddr en0
|
|
```
|
|
|
|
If that does not return an IP, use:
|
|
|
|
```bash
|
|
ifconfig
|
|
```
|
|
|
|
## Shortcut config
|
|
|
|
Use a Dictionary near the top of the Shortcut:
|
|
|
|
```text
|
|
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]
|
|
```
|
|
|
|
Use `profile: opencode` when the next paste target is OpenCode. Use
|
|
`profile: mattermost` when the next paste target is Mattermost.
|
|
|
|
## 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]
|
|
|
|
Take Photo
|
|
Show Camera Preview: On
|
|
|
|
Get Contents of URL
|
|
URL: Text
|
|
Method: POST
|
|
Request Body: File
|
|
File: Photo
|
|
|
|
Show Notification
|
|
Sent to [profile]
|
|
```
|
|
|
|
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:
|
|
|
|
```text
|
|
Receive Images and Media from Share Sheet
|
|
Repeat with Each Item in Shortcut Input
|
|
Convert Image
|
|
Image: Repeat Item
|
|
Format: JPEG
|
|
Get Contents of URL
|
|
URL: http://[mac_ip]:[port]/upload?token=[token]&profile=[profile]
|
|
Method: POST
|
|
Request Body: File
|
|
File: Converted Image
|
|
End Repeat
|
|
Show Notification
|
|
Sent to [profile]
|
|
```
|
|
|
|
## Overrides
|
|
|
|
Profile folders:
|
|
|
|
```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
|
|
```
|
|
|
|
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
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
Startup should print each active profile:
|
|
|
|
```text
|
|
profile opencode: dir=... clipboard=terminal-path notify=True reveal=False
|
|
profile mattermost: dir=... clipboard=files notify=True reveal=False
|
|
```
|
|
|
|
After each upload, expect:
|
|
|
|
```text
|
|
clipboard mode applied: terminal-path profile=opencode count=2
|
|
saved ... profile=opencode batch_count=2
|
|
```
|
|
|
|
After the debounce window closes, expect:
|
|
|
|
```text
|
|
batch notification sent profile=opencode count=2
|
|
batch finalized profile=opencode count=2 dir=...
|
|
```
|
|
|
|
The native file clipboard helper lives at:
|
|
|
|
```text
|
|
scripts/iphone-photo-inbox/copy_files_to_clipboard.swift
|
|
```
|
|
|
|
For faster multi-file clipboard updates during debounce, compile it once:
|
|
|
|
```bash
|
|
swiftc scripts/iphone-photo-inbox/copy_files_to_clipboard.swift \
|
|
-o 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.
|