A desktop and command-line app that reads images from a folder, sends them in batches to the OpenAI Vision model to pick the best photos (sharpness, focus, lighting, composition, no duplicates, best aesthetic), then moves the selected photos to a destination folder and can send an SMS when the job is complete.
Contents: User interface · Requirements · Install · Configuration · Usage · Project structure
The Photo Selector app (Tauri Mac app or browser UI) presents a single screen with:
| Element | Description |
|---|---|
| Title | Photo Selector — Pick best photos with AI at the top of the window. |
| OpenAI API Key | A required field (masked) for your OpenAI API key. Placeholder: sk-.... |
| Folder with photos (source) | Full path to the folder that contains your images. Hint: Full path to the folder containing your images. |
| Folder for selected photos (destination) | Full path where the 20 selected photos will be moved. Hint: Full path where the 20 selected photos will be moved. |
| Phone number to notify when done | Optional. E.g. +15551234567. Hint: Optional. Set Twilio credentials in .env or leave blank. |
| Run photo selection | A single blue button that starts the AI photo selection. While running, a progress line and bar show batch progress and how many photos were selected. |
| Status / log area | A large area below the button that shows status messages, progress (e.g. photos in folder, batches, batch N of M, photos selected), and any errors or logs from the Python script. |
After you click Run photo selection, the app scans the source folder, runs the AI in batches, and moves the chosen photos to the destination folder. Use a full path for both folders (e.g. /Users/you/Pictures/Photos).
- Python 3.10+
- OpenAI API key
- (Optional) Twilio account for SMS notifications
From the project folder (e.g. the app folder you copied this into):
# Create a virtual environment (recommended)
python3 -m venv venv
source venv/bin/activate # On macOS/Linux
# Install packages
pip install -r requirements.txt-
Copy the sample environment file and add your keys:
cp .env.example .env
-
Edit
.envand set at least:OPENAI_API_KEY– required for AI selection- Optionally:
SOURCE_FOLDER(default source folder) - Optionally: Twilio variables for SMS (
TWILIO_ACCOUNT_SID,TWILIO_AUTH_TOKEN,TWILIO_FROM_NUMBER,TWILIO_TO_NUMBER)
Setting the key (Mac / server only):
- Recommended: Use a
.envfile (as above). The app loads it automatically; noexportneeded. - Shell (temporary):
export OPENAI_API_KEY=your_actual_api_key_here
(No spaces around=, no quotes unless the value contains spaces.)
See .env.example for the full list and format.
Do not put OPENAI_API_KEY inside an iPhone (or any mobile) app. Keys in the app can be extracted from the binary. Use one of these patterns instead:
- Backend proxy (recommended for iPhone): Run a small server (e.g. the
api_server.pyin this repo) on your Mac or a cloud host. StoreOPENAI_API_KEYonly in that server’s environment or.env. The iPhone app calls your server over HTTPS; the server calls OpenAI with the key. The key never leaves the server. - Mac-only workflow: If the script runs only on your Mac (e.g. from Terminal or Shortcuts), keep the key in
.envon the Mac. The iPhone never needs the key.
You can build a standalone Mac app (.app bundle) with Tauri. The app window has the same form (API key, source path, destination path, phone number) and runs the Python script under the hood.
Requirements: Node.js (npm), Rust (rustup), Python 3 with dependencies installed (pip install -r requirements.txt), and system python3 on your PATH.
# Install Node deps and Tauri CLI
npm install
# Development: opens the app window (uses dev server for frontend)
npm run dev
# Build the Mac .app bundle
npm run buildThe built app is in src-tauri/target/release/bundle/macos/Photo Selector.app. You can drag it to Applications and run it like any Mac app. The first time you run the built app, ensure Python 3 is installed and the packages from requirements.txt are available to that Python (e.g. install them in the user environment or in a venv that’s on PATH).
To add or change the app icon, run: npx tauri icon path/to/your/icon.png (creates/updates src-tauri/icons/), then run npm run build again.
If the app icon doesn't update (you still see the old or generic icon):
- Use the new build – Run the app from
src-tauri/target/release/bundle/macos/Photo Selector.app, or drag that.appinto Applications and replace the old one when prompted. - Quit Photo Selector completely (Cmd+Q), then remove it from the Dock (right-click → Options → Remove from Dock).
- Refresh macOS icon cache – In Terminal run:
killall Dock. If the icon still doesn't change, run:sudo rm -rf /Library/Caches/com.apple.iconservices.storethenkillall Dock. Open Photo Selector again from the new build and add it back to the Dock.
Option A — Browser UI (no extra install)
Works with any Python that has Flask (already in requirements):
source venv/bin/activate
python main_web.pyThen open http://127.0.0.1:5000 in your browser. Enter API key, source folder path, destination folder path, and optional phone number, then click Run photo selection.
Option B — Desktop window (Tkinter)
If your Python has Tkinter (e.g. python.org installer), you can run:
python main_gui.pyIf you get ModuleNotFoundError: No module named '_tkinter' (common with Homebrew Python), either install Tk with brew install [email protected] (match your Python version) or use the browser UI above.
In either UI you can set:
- OpenAI API key (required)
- Folder with photos (source path)
- Folder for selected photos (destination path)
- Phone number for “job complete” SMS (optional; Twilio credentials in
.envfor SMS)
# Use default source folder (from config / SOURCE_FOLDER env)
python main.py
# Or specify folder path
python main.py /path/to/your/photosThe script will:
- Read images from the given folder
- Send them to the OpenAI Vision model in batches of 10
- Ask the AI to return only the best photo filenames in JSON:
{"selected": ["IMG_1234.jpg", ...]} - Append those filenames to
selected_photos.txtin the same folder - Move selected photos into a subfolder named
Selected - Send an SMS when the job is complete (if Twilio is configured)
The app sends a prompt like this (plus the images):
You are a professional photo curator. Look at the images provided.
For each image you receive, consider:
- Sharpness: Is the image sharp and clear (not blurry)?
- Focus: Is the subject in focus?
- Lighting: Is the lighting flattering and well-balanced?
- Composition: Is the framing and composition strong (rule of thirds, balance, no cut-off subjects)?
- No duplicates: If two images are nearly identical, pick only the better one.
- Best overall aesthetic: Which images would you keep in a final album?
Your task: From the images in this batch, select ONLY the best photos. Return the filenames of selected photos only.
You MUST respond with valid JSON only, no other text. Use this exact format:
{
"selected": ["filename1.jpg", "filename2.jpg"]
}
...
Return ONLY the JSON object. No markdown, no explanation, no code block.
main.py– Entry point; runs the full workflowai_selector.py– Calls OpenAI Vision and returns selected filenames (JSON)file_handler.py–get_images,batch_images,move_selected_images, append toselected_photos.txtnotifier.py–send_smsvia Twilio (env-based)config.py– Paths, batch size, model namerequirements.txt– Dependencies.env.example– Sample environment variablesapi_server.py– Optional backend for iPhone app (keeps API key on server)main_gui.py– Desktop GUI (requires Tkinter)main_web.py– Browser GUI (no Tkinter; run then open http://127.0.0.1:5000)- Tauri app –
package.json,index.html,src/main.js,vite.config.js,src-tauri/(Rust + config). Runnpm run devornpm run buildfor a native Mac .app.
To call the photo-selection logic from an iPhone app without embedding the API key:
- On your Mac (or a server), set
OPENAI_API_KEYin.envor the environment. - Start the API server:
python api_server.py
- From the iPhone app, send
POST /selectwith a JSON body:{"images": [{"filename": "a.jpg", "base64": "<base64 data>"}, ...]}.
Response:{"selected": ["a.jpg", ...]}.
The API key stays on the machine running api_server.py; the iPhone app never sees it.
- Uses pathlib for paths and shutil for moving files.
- API calls use retry logic (see
config.OPENAI_MAX_RETRIES). - If Twilio is not configured, the app still runs and only skips the SMS.
- Supported image extensions:
.jpg,.jpeg,.png,.gif,.webp,.heic,.bmp,.tiff,.tif.