Screenshot Sorter
Screenshots that file themselves. Take a screenshot, and an AI gives it a title, a category and tags, then saves it into the right folder. Later you can find it by category or tag instead of scrolling through a pile of Screenshot_2026….png files.
Built for the Invisible AI hackathon. The AI is not a chatbot or a prompt box. It is the sorting step you would never do by hand, and it happens in the background.
How it works
Print Screen / button → drag to select → saved to Screenshots/_pending/
→ Gemini reads the image → title, category, tags
→ copied to Screenshots/<Category>/ → recorded in SQLite
→ gallery refreshes → search by category and tags (no AI call)
| Part | File | What it does |
|---|---|---|
| Entry point and UI | main.py |
Window, status line, hotkey, background worker thread |
| Capture | capture.py |
Drag-to-select overlay, saves a PNG (uses mss, Pillow, Tkinter) |
| AI analysis | ai_analyses.py |
Sends the image to Gemini and returns a validated title, category, tags and description |
| Categories | categories.py |
The one list of categories, shared by the AI and storage |
| Storage | storage.py |
Creates or reuses the category folder, copies the file without overwriting, writes the metadata to SQLite |
| Gallery and search | image_browser.py |
Category dropdown and tag filters, reading only saved data |
| Database schema | image_repository.sql |
Tables: categories, tags, images, image_tags (see databaseStructure.md) |
Requirements
- Python 3.10 or newer (tested on 3.12)
- A Google Gemini API key (free tier works): create one at https://aistudio.google.com
- Windows is the main target. The app also runs elsewhere, but the Print Screen hotkey may need extra permissions (see Troubleshooting).
Setup
pip install -r requirements.txt
Give the app your API key in one of two ways. Never commit the key.
- Create a file named
.envnext tomain.pycontaining one line (.envis already in.gitignore):GEMINI_API_KEY=your-key-here - Or set an environment variable. On Windows PowerShell:
$env:GEMINI_API_KEY="your-key-here"
| Variable | Required | Purpose |
|---|---|---|
GEMINI_API_KEY |
Yes | Your Gemini API key |
GEMINI_MODEL |
No | Overrides the model name (default is set in ai_analyses.py). Use this if you get a "model not found" error. |
AI_MOCK |
No | Set to 1 to use fake metadata with no API call. For testing only; the app shows a warning banner when it is on. |
Run
python main.py
Using it
- Click Take screenshot or press Print Screen.
- Drag over the area you want. Press Esc or right-click to cancel.
- The status line shows Analysing…, then Saved to 'Code': </em>.</li> <li>The screenshot appears in the list, and the file is in <code>Screenshots/<Category>/</code>.</li> <li>Filter by <strong>Category</strong> and/or select <strong>tags</strong> (Ctrl+click for several; a screenshot must have all selected tags), then click <strong>Search</strong>.</li> </ol> <p>You can take another screenshot while earlier ones are still being analysed.</p> <h2 id="what-happens-when-something-goes-wrong">What happens when something goes wrong</h2> <ul> <li><strong>No API key, no internet, or the AI fails:</strong> the screenshot is still saved, under the category <strong>Other</strong>, and the status line says the AI analysis failed.</li> <li><strong>Filing fails</strong> (for example a disk or database error): the original stays in <code>Screenshots/_pending/</code> and the status line shows where.</li> <li><strong>Name clashes:</strong> a file is never overwritten; a numeric suffix is added instead.</li> <li><strong>Closing the window while screenshots are processing:</strong> the app asks first. Anything unfinished stays in <code>Screenshots/_pending/</code>.</li> </ul> <h2 id="files-the-app-creates">Files the app creates</h2> <ul> <li><code>Screenshots/<Category>/…png</code>: your sorted screenshots</li> <li><code>Screenshots/_pending/</code>: screenshots waiting to be sorted, or kept after a failure</li> <li><code>screenshot_library.db</code>: the SQLite database (created on first run from <code>image_repository.sql</code>)</li> </ul> <p><code>image_repository.db</code> is the original sample database (made-up categories and images). The app does not use it.</p> <p>To start a demo with an empty library, close the app and run <code>python clear_db.py</code> (it asks before deleting). It empties the database only; delete the files in <code>Screenshots/</code> yourself if you want those gone too.</p> <h2 id="privacy">Privacy</h2> <p>Each screenshot is sent to the Google Gemini API for analysis (downscaled so its longest side is at most 1600 px). Do not capture anything you would not want sent there. A local, on-device model is on the roadmap (below).</p> <h2 id="tests">Tests</h2> <pre class="language-bash"><code>python -m unittest tests.test_storage -v </code></pre> <p>These cover storage (filing, no overwrite, rollback on failure, concurrent saves). They need no display or API key. <code>tests/test_gui_xvfb.py</code> is a Linux-only headless test of the whole app (it needs <code>xvfb</code> and <code>xdotool</code>), and it is not needed on Windows.</p> <h2 id="known-limitations">Known limitations</h2> <ul> <li>Search is by category and exact tags only. It does not match meaning, so "tree" will not find a screenshot tagged only "BST".</li> <li>Categories are a fixed list in <code>categories.py</code>, plus <strong>Other</strong> as the fallback.</li> <li>Gemini's one-sentence description is generated but not stored, because the database has no column for it.</li> <li>The gallery is a list of titles and file paths, with no thumbnails.</li> <li>Needs an internet connection to analyse screenshots.</li> <li>While the app is running, it captures the Print Screen key, so other tools will not receive it.</li> </ul> <h2 id="roadmap">Roadmap</h2> <ul> <li>Meaning-based search</li> <li>The AI proposes categories from your own screenshots, so no presets are needed</li> <li>An optional on-device model for privacy and offline use (the AI sits behind one function, <code>analyse_screenshot()</code>, so swapping the model touches one file)</li> <li>Beyond screenshots: photos, scans and PDFs, screen recordings and phone galleries, using a folder watcher in place of the capture overlay</li> </ul> <h2 id="troubleshooting">Troubleshooting</h2> <ul> <li><strong>"Hotkey unavailable":</strong> the <code>keyboard</code> library could not start. On Linux it needs root; on macOS it may not work at all. The <strong>Take screenshot</strong> button still works.</li> <li><strong>"model not found" or the AI always fails:</strong> check <code>GEMINI_API_KEY</code>, and set <code>GEMINI_MODEL</code> to a current model name from AI Studio. The exact reason appears in the status line.</li> <li><strong>Screenshot looks the wrong size or in the wrong place on a high-DPI screen:</strong> make sure you start the app with <code>python main.py</code> (it enables Windows DPI awareness on startup).</li> </ul> <h2 id="credits-and-disclosure">Credits and disclosure</h2> <p>Built during the hackathon with Python, Tkinter, <a href="https://pypi.org/project/mss/">mss</a>, <a href="https://pypi.org/project/pillow/">Pillow</a>, <a href="https://pypi.org/project/keyboard/">keyboard</a>, SQLite, <a href="https://pypi.org/project/python-dotenv/">python-dotenv</a> and the <a href="https://ai.google.dev/">Google Gemini API</a> via the <code>google-genai</code> library.</p>
Log in or sign up for Devpost to join the conversation.