AI-powered media folder analyzer and pruner using local Vision models
Project description
ShutterSort
AI-powered media folder analyzer and pruner. Scan your photo libraries, get intelligent scene analysis from a local Vision model, detect duplicates, and clean up with confidence.
pip install shuttersort
shuttersort
What It Does
| Feature | Description |
|---|---|
| AI Scene Analysis | Classifies folders as landscape, portrait, event, junk, etc. using llama3.2-vision |
| Quality Scoring | Rates each folder 1-10 based on composition, lighting, and content value |
| People Detection | Counts people and describes appearances, emotions, and context |
| Duplicate Detection | Finds duplicate files across folders using content hashing (first 1MB + file size) |
| Interactive Cleanup | Review each folder in a Rich table, then Keep, Delete (to Trash), Open, or Skip |
| RAW Support | Extracts previews from Sony ARW files using rawpy |
| Video Support | Extracts 3 representative frames from MP4s using OpenCV |
| 100% Local | All AI runs locally via Ollama — no cloud, no uploads, no API keys |
Quick Install
Prerequisites
-
Python 3.10+
python3 --version # Must be 3.10 or higher
-
Ollama installed and running
# Install Ollama (macOS) brew install ollama # Start the Ollama service ollama serve # Pull the vision model (required) ollama pull llama3.2-vision
-
macOS Full Disk Access (required for scanning Desktop, Downloads, Documents)
- Open System Settings → Privacy & Security → Full Disk Access
- Click the + button and add your terminal app:
- Terminal.app:
/System/Applications/Utilities/Terminal.app - iTerm2:
/Applications/iTerm.app - VS Code Terminal:
/Applications/Visual Studio Code.app
- Terminal.app:
- Restart your terminal after granting access
Without Full Disk Access, macOS will silently return empty results when scanning protected folders.
Install ShutterSort
pip install shuttersort
Or from source:
git clone https://github.com/camiloavilacm/ShutterSort.git
cd ShutterSort
pip install -e .
Usage
Basic Scan (Default Paths)
Scans ~/Desktop, ~/Downloads, and ~/Documents:
shuttersort
Custom Paths
# Single path
shuttersort --path ~/Photos
# Multiple paths
shuttersort --path ~/Photos ~/Pictures ~/ExternalDrive
# Shorthand
shuttersort -p ~/Photos
Different Model
shuttersort --model llava
shuttersort -m llava
Dry Run (Preview Only)
See what would be deleted without actually deleting anything:
shuttersort --dry-run
Verbose Output
Show detailed debug logging:
shuttersort --verbose
shuttersort -v
Non-Interactive Mode
Just show the summary table without the interactive review prompts:
shuttersort --no-interactive
How It Works
ShutterSort uses a three-agent architecture:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ LibrarianAgent │────>│ CuratorAgent │────>│ DecisionAgent │
│ │ │ │ │ │
│ • Walks folders │ │ • Calls Ollama │ │ • Rich table │
│ • Finds media │ │ • Analyzes imgs │ │ • [K/D/O/S] loop│
│ • Extracts ARW │ │ • Scores 1-10 │ │ • AppleScript │
│ • Finds dupes │ │ • Detects people│ │ • Trash to Finder│
└─────────────────┘ └─────────────────┘ └─────────────────┘
- LibrarianAgent walks your folders, finds all media files (JPG, PNG, ARW, MP4), extracts previews from RAW files, and detects duplicates.
- CuratorAgent sends up to 5 representative images per folder to
llama3.2-visionand returns a structured analysis (scene type, score, people count, emotions). - DecisionAgent presents everything in a color-coded Rich table and walks you through each folder with an interactive
[K]eep / [D]elete / [O]pen / [S]kiploop.
Duplicate Detection
Files are matched by a composite key: MD5 hash of the first 1MB + file size. When duplicates are found across folders, ShutterSort suggests keeping the copy in the folder with the higher AI score.
Delete Behavior (Trash vs Permanent)
When you choose Delete, ShutterSort uses AppleScript to move files to the macOS Trash:
tell application "Finder" to delete POSIX file "/path/to/file"
This is equivalent to right-clicking a file and selecting "Move to Trash." Files can be recovered from the Trash until you empty it. ShutterSort never permanently deletes files.
Temporary File Handling
All extracted previews (from ARW files) and video frames (from MP4s) are saved to temporary files using Python's tempfile module. These are cleaned up automatically after analysis. The gc.collect() call after ARW processing ensures native C memory from rawpy is released promptly, keeping RAM usage low on 16GB machines.
Output Example
ShutterSort — Folder Analysis Summary
┏━━━┳━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━┓
┃ # ┃ Score ┃ Scene ┃ People ┃ Folder ┃ Summary ┃ Size ┃ Pic% ┃ Vid% ┃ Dupes ┃
┡━━━╇━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━┩
│ 1 │ 8/10 │ landscape │ 2 │ .../vacation │ Beautiful beach photos from vac… │ 245.30 MB│ 100% │ 0% │ No │
│ 2 │ 2/10 │ junk │ 0 │ .../screenshots │ Screenshots of documents and re… │ 12.50 MB │ 100% │ 0% │ Yes │
│ 3 │ 9/10 │ event │ 6 │ .../family │ Birthday party with family memb… │ 512.00 MB│ 80% │ 20% │ No │
└───┴───────┴───────────┴────────┴──────────────────┴───────────────────────────────────┴──────────┴────────┴────────┴───────┘
Troubleshooting
| Problem | Solution |
|---|---|
| "No media folders found" | Check Full Disk Access for your terminal app (see Prerequisites above) |
| Ollama connection refused | Run ollama serve in another terminal tab |
| Model not found | Run ollama pull llama3.2-vision |
| ARW files fail to process | Ensure rawpy is installed: pip install rawpy |
| Slow analysis | Large folders take longer; use --verbose to see progress |
| JSON parse errors | The retry loop handles this automatically (up to 3 retries) |
Contributing
We welcome contributions! See CONTRIBUTING.md for the full guide.
Quick start for contributors:
git clone https://github.com/camiloavilacm/ShutterSort.git
cd ShutterSort
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest -m "not integration"
Branching Model
main— Production (auto-publishes to PyPI)develop— Staging (auto-publishes to TestPyPI)feature/*— Feature branches (PR → develop)
CI/CD
Every PR runs lint (ruff), type checks (mypy), and tests (pytest) on Python 3.10–3.13.
License
MIT — see LICENSE for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file shuttersort-0.1.0.tar.gz.
File metadata
- Download URL: shuttersort-0.1.0.tar.gz
- Upload date:
- Size: 37.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cf8b8b0e26645473ed4a522e995ddde648b8187387bc7b96a2361467e1312a24
|
|
| MD5 |
00e58d90034a803e95b31363655549ae
|
|
| BLAKE2b-256 |
bd1d516a0ca15c8069a3f0831ce216badfa209eacd6414cd7a48ff1ce13fb914
|
File details
Details for the file shuttersort-0.1.0-py3-none-any.whl.
File metadata
- Download URL: shuttersort-0.1.0-py3-none-any.whl
- Upload date:
- Size: 29.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fe17e70106099bfbc3c3cf379ff8f9bd3959405e0b0203316d1f3e6850188434
|
|
| MD5 |
c93e5efb94cc3358a0457dd61e6d3025
|
|
| BLAKE2b-256 |
0dd0551eca0330f65c0ec48506a4a41ecc7b773a55dbe0efb447d24c8e76e287
|