feat: enhance iPhone photo inbox receiver with notification, reveal, and clipboard copy options

This commit is contained in:
2026-05-11 11:27:42 -06:00
parent e01b59c065
commit 97ef0be216
2 changed files with 116 additions and 0 deletions

View File

@@ -5,7 +5,9 @@ from __future__ import annotations
import argparse
import datetime as dt
import json
import os
import subprocess
from http import HTTPStatus
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
@@ -33,6 +35,44 @@ def looks_like_jpeg(data: bytes) -> bool:
return data.startswith(b"\xff\xd8") and data.endswith(b"\xff\xd9")
def env_flag(name: str) -> bool:
return os.getenv(name, "").strip().lower() in {"1", "true", "yes", "on"}
def run_macos_action(command: list[str]) -> bool:
try:
result = subprocess.run(command, check=False, capture_output=True, text=True)
except OSError as error:
print(f"macOS action failed: {error}", flush=True)
return False
if result.returncode != 0:
error = result.stderr.strip() or result.stdout.strip() or f"exit {result.returncode}"
print(f"macOS action failed: {error}", flush=True)
return False
return True
def notify(title: str, message: str) -> bool:
script = f"display notification {json.dumps(message)} with title {json.dumps(title)}"
return run_macos_action(["osascript", "-e", script])
def reveal_in_finder(path: Path) -> bool:
return run_macos_action(["open", "-R", str(path)])
def copy_file_to_clipboard(path: Path) -> bool:
script = f"""
set theFile to POSIX file {json.dumps(str(path))} as alias
tell application "Finder"
set the clipboard to {{theFile}}
end tell
"""
return run_macos_action(["osascript", "-e", script])
class UploadHandler(BaseHTTPRequestHandler):
server_version = "iPhonePhotoInbox/1.0"
@@ -81,6 +121,16 @@ class UploadHandler(BaseHTTPRequestHandler):
temp_path = Path(tmp.name)
temp_path.replace(target)
if self.server.notify_on_upload:
if notify("iPhone Photo Inbox", target.name):
print("notification sent", flush=True)
if self.server.reveal_on_upload:
if reveal_in_finder(target):
print("revealed in Finder", flush=True)
if self.server.copy_on_upload:
if copy_file_to_clipboard(target):
print("copied file to clipboard", flush=True)
self.send_text(HTTPStatus.CREATED, f"{target}\n")
print(f"saved {target}", flush=True)
@@ -104,11 +154,17 @@ class UploadServer(ThreadingHTTPServer):
output_dir: Path,
upload_token: str,
max_bytes: int,
notify_on_upload: bool,
reveal_on_upload: bool,
copy_on_upload: bool,
) -> None:
super().__init__(server_address, handler_class)
self.output_dir = output_dir
self.upload_token = upload_token
self.max_bytes = max_bytes
self.notify_on_upload = notify_on_upload
self.reveal_on_upload = reveal_on_upload
self.copy_on_upload = copy_on_upload
def parse_args() -> argparse.Namespace:
@@ -118,6 +174,9 @@ def parse_args() -> argparse.Namespace:
parser.add_argument("--output-dir", type=Path, default=Path(os.getenv("IPHONE_PHOTO_OUTPUT_DIR", DEFAULT_OUTPUT_DIR)))
parser.add_argument("--token", default=os.getenv("IPHONE_PHOTO_TOKEN", ""))
parser.add_argument("--max-mb", type=int, default=int(os.getenv("IPHONE_PHOTO_MAX_MB", "30")))
parser.add_argument("--notify", action="store_true", default=env_flag("IPHONE_PHOTO_NOTIFY"))
parser.add_argument("--reveal", action="store_true", default=env_flag("IPHONE_PHOTO_REVEAL"))
parser.add_argument("--copy", action="store_true", default=env_flag("IPHONE_PHOTO_COPY"))
return parser.parse_args()
@@ -129,9 +188,18 @@ def main() -> None:
args.output_dir.expanduser().resolve(),
args.token,
args.max_mb * 1024 * 1024,
args.notify,
args.reveal,
args.copy,
)
print(f"listening on http://{args.host}:{args.port}/upload", flush=True)
print(f"saving JPEGs to {server.output_dir}", flush=True)
if args.notify:
print("notifications enabled", flush=True)
if args.reveal:
print("Finder reveal enabled", flush=True)
if args.copy:
print("clipboard copy enabled", flush=True)
if not args.token:
print("warning: no token configured; anyone on this network can upload", flush=True)
server.serve_forever()