No description
  • Python 97%
  • Shell 1.7%
  • PowerShell 1.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Claude a2942e20cc
All checks were successful
CI / windows-exe (push) Successful in 11m58s
CI / lint (push) Successful in 18s
CI / test (push) Successful in 6m30s
CI / build-windows-exe (push) Successful in 0s
CI / appimage (push) Successful in 17m16s
CI / build-appimage (push) Successful in 0s
Fix network-cache resurrection on delete and Tag Filter reset race (0.9.4) (#189)
## Summary

- **Issue #185** — Close Directory and Delete Cache (and app exit with "Always delete cache when closing workspace or exiting") no longer sync the local cache back to the network share before deleting it. Both paths now remove the share's colocated cache copy instead, so a later open of the same share doesn't hydrate the discarded tags/thumbnails back in. Directory switch and exit/close *without* delete-cache are unchanged and still sync.
- **Issue #186** — Tag Filter now reliably resets to Settings → Tag Filter defaults on every Open Directory, including auto-open of the last directory on startup. `_apply_tag_filter_defaults()` now forces an immediate view refresh, and session restore takes a `directory_open_pending` flag (computed in `main.py`) so it skips restoring the persisted Tag Filter whenever an auto-open is about to run right after — removing a race against `open_directory()`'s own reset.

## Changes

- `src/bulk_image_organizer/ui/main_window.py`: `_delete_share_cache_files()`, `_finalize_workspace_close(sync_to_share=...)`, `_on_close_directory_and_cache()`, `closeEvent()`, `_apply_tag_filter_defaults()`, `restore_session_if_available()` / `_restore_session_state()`.
- `src/bulk_image_organizer/main.py`: compute and pass `directory_open_pending`.
- Docs: `CHANGELOG.md` (0.9.4), `docs/DECISIONS.md` (DR-015, DR-021), `docs/DESIGN.md`, `resources/help/USER_GUIDE.md`.
- `VERSION` bumped 0.9.3 → 0.9.4 (compiled inputs changed).

## Test plan

- [x] `ruff check .` / `ruff format --check .`
- [x] Full `pytest` suite (799 passed, 1 skipped)
- [x] New/updated tests: `tests/test_pr13_config.py` (close-directory/app-exit network-sync coverage, session-restore-pending coverage), `tests/test_tag_filter_enhancements.py::test_open_directory_resets_tag_filter_to_defaults`
- [x] `python -m pip install -e .` + `scripts/get-version.py --canonical` → `0.9.4`

Fixes #185, fixes #186.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

https://claude.ai/code/session_01TrNqVfQQpmRfwhoUkKkkiJ
Reviewed-on: #189
2026-08-28 20:22:19 +00:00
.forgejo/ISSUE_TEMPLATE Update .forgejo/ISSUE_TEMPLATE/feature.yaml 2026-06-09 05:29:45 +00:00
.github/workflows Expand documented-feature test coverage and drop leftover APIs (0.5.0) (#161) 2026-08-13 06:45:01 +00:00
.grok/agents docs(agents): codify disciplined development practices for agents 2026-05-31 22:35:05 -07:00
docs Fix network-cache resurrection on delete and Tag Filter reset race (0.9.4) (#189) 2026-08-28 20:22:19 +00:00
resources Fix network-cache resurrection on delete and Tag Filter reset race (0.9.4) (#189) 2026-08-28 20:22:19 +00:00
scripts Add command-line arguments to the executable (0.8.0) (#175) 2026-08-23 02:33:54 +00:00
src/bulk_image_organizer Fix network-cache resurrection on delete and Tag Filter reset race (0.9.4) (#189) 2026-08-28 20:22:19 +00:00
tests Fix network-cache resurrection on delete and Tag Filter reset race (0.9.4) (#189) 2026-08-28 20:22:19 +00:00
.gitignore Expand documented-feature test coverage and drop leftover APIs (0.5.0) (#161) 2026-08-13 06:45:01 +00:00
AGENTS.md Make adjacent duplicate-set switching near-instant (0.9.3, #182) (#188) 2026-08-28 19:35:19 +00:00
CHANGELOG.md Fix network-cache resurrection on delete and Tag Filter reset race (0.9.4) (#189) 2026-08-28 20:22:19 +00:00
CLAUDE.md docs: Align product documentation with the shipped application (0.4.3) (#159) 2026-08-13 02:35:29 +00:00
DEVELOPMENT.md ci: Run Linux test/AppImage on python-3.12 instead of ubuntu-full (issue #156) (#157) 2026-08-12 22:07:42 +00:00
LICENSE docs: Polish README and design docs; switch to MIT-only license 2026-06-08 18:32:05 -07:00
pyproject.toml feat(settings): configurable image types filter (issue #28 / DR-019) (#42) 2026-06-11 17:50:28 +00:00
README.md Add command-line arguments to the executable (0.8.0) (#175) 2026-08-23 02:33:54 +00:00
VERSION Fix network-cache resurrection on delete and Tag Filter reset race (0.9.4) (#189) 2026-08-28 20:22:19 +00:00

Bulk Image Organizer

A fast, keyboard-centric desktop application for power users who need to review, tag, deduplicate, and organize large batches of images—1,000 to 10,000 or more—in a single directory tree.

Everything stays on your machine. Tags and staging for deletion replace destructive edits until you explicitly confirm. Duplicate detection is ad-hoc—you run it when you need it. The filesystem is always the source of truth; a portable cache database beside your images keeps thumbnails and metadata fast.

Supported formats: JPEG, PNG, WEBP, TIFF, GIF, BMP (HEIC/HEIF not supported). Subsets can be enabled/disabled globally in Settings → Image Types; disabled formats are ignored by scans.

Platforms: Windows 11 and Linux (tested on Linux Mint)


Why Bulk Image Organizer?

Large photo dumps, wallpaper collections, and download folders are painful to triage in a file manager. Bulk Image Organizer is built for that workflow:

  • Non-destructive by default — Tag images or stage them for deletion; nothing hits the recycle bin until you confirm.
  • Responsive at scale — Background scanning and lazy thumbnails keep the UI usable while tens of thousands of files are indexed.
  • Tag-driven organization — Filter, sort, bulk-assign, and sort into folders by tag.
  • Ad-hoc duplicate tools — Exact and perceptual duplicate scans on demand.
  • Keyboard-first — Customizable shortcuts across grid, viewer, duplicates, and trash workbenches.

Features

Grid workbench

  • Recursive directory scan with progressive thumbnail loading
  • Adjustable thumbnail size (display scale and cache resolution in Settings)
  • Multi-select, sorting (filename, date, size, resolution, tag priority), text search, and tag filtering (any selected tag, untagged-only, hide staged deletions, horizontal/vertical orientation)
  • Tag badges on thumbnails; prominent red overlay on images staged for deletion
  • Session undo/redo for tag assignment, deletion staging, and crop (Ctrl+Z / Ctrl+Y)

Image viewer

  • Full-resolution zoom, pan, and fit
  • Carousel and filmstrip navigation (global preferences)
  • Rotation saved to the image file after a short debounce
  • From Grid: horizontal mirror and interactive crop (aspect presets, undoable; originals backed up under .originals/)
  • Metadata bar for filename, resolution, file size, and tags; compare coloring in duplicate Single mode
  • Background preloading of adjacent images for smooth prev/next

Tagging

  • Unlimited custom tags per workspace, with fourteen preset colors (including black and white) or a custom picker
  • New Tag, Bulk Create, Export Tags, and Create from Sub-directories
  • Single-click toggles tags on the current selection; creation requires an open directory
  • Tag toolbox: middle-click excludes a tag from the Tag Filter, right-click filters to that tag only and jumps to the first match, Ctrl-click assigns a 09 hotkey
  • Stage images for deletion to review them later in the Trash workbench (Delete or keypad .)
  • Sort into Folders… moves singly tagged images into subfolders named after that tag; optional multi-tag folder names

Duplicates workbench

Duplicate detection is ad-hoc. When you run a scan:

  • Exact duplicates — Full-file cryptographic hash (F-DUPE_# tags)
  • Visual duplicates — Perceptual hash with configurable similarity (V-DUPE_# tags)

Review groups in Dupe Grid, a comparison Multi view, or Single viewer. Keep stages lower-quality copies for deletion using a chosen criterion. Duplicate membership is ordinary tags—you can rename, recolor, or remove them like any other tag.

Trash workbench

  • Review everything staged for deletion without grid overlays
  • Confirm Delete sends displayed rows to the system recycle bin via send2trash
  • Filter-scoped Clear Deleted Flag and Unset Applied Tags

Settings & help

  • Dark and light themes
  • Customizable global keyboard shortcuts
  • Startup, tag-filter defaults, thumbnail cache, duplicate defaults, viewer modes, and advanced tunables
  • Bundled user guide and About dialog inside the app

Data & privacy

  • All processing is local; no network features beyond optional workspace-cache sync for network folders
  • Local folders: .bulk_image_organizer.db lives beside your images and travels with the folder
  • Network folders: Thumbnails stay on your computer; the full cache can optionally sync to the share when you close the workspace

Quick start

Run a packaged build

Linux — Extract a release tarball, then run the bulk_image_organizer binary inside.

Windows — Run bulk_image_organizer.exe. On first launch, Nuitka unpacks into %LOCALAPPDATA%\ValleyGeek\Bulk Image Organizer\. Launching a newer build removes unpack folders from older versions/builds under that path, so only the currently running build's files are kept there.

Run from source

git clone <repository-url>
cd bulk-image-organizer

python -m venv .venv
source .venv/bin/activate   # Linux/macOS
# .venv\Scripts\activate    # Windows

pip install -e ".[dev]"
python -m bulk_image_organizer

On Linux, ./scripts/launch-linux.sh sets up the virtual environment, installs dependencies if needed, and configures Qt platform plugins.

Command-line options

Both python -m bulk_image_organizer and the packaged executables accept command-line options — an optional directory to open immediately, --version, --theme, --no-auto-scan, --no-restore-session, --log-level, --debug, and --config-dir/--portable to relocate the global settings database. Run with --help for the full list. On Windows, run the .exe from an existing Command Prompt or PowerShell window to see this output — a normal double-click launch has no console attached by design.


Basic workflow

  1. Open a directory (Ctrl+O) — The app scans in the background and fills the grid as thumbnails are ready.
  2. Tag and filter — Create tags with New Tag, Bulk Create, or Create from Sub-directories in the right column; use Tag Filter on the sub-toolbar to narrow the grid.
  3. Review in the viewer — Double-click or press Enter; use arrow keys, carousel, or filmstrip to move through images.
  4. Find duplicates (optional) — Switch to Duplicates, run an exact or visual scan, review groups, and use Keep.
  5. Clean up — Stage rejects for deletion, review them in Trash, then confirm delete to send files to the recycle bin.
  6. Organize — Use Sort into Folders… for tagged images.

Default shortcuts and full workbench details are in the in-app Help guide (resources/help/USER_GUIDE.md).


Building releases

Linux — AppImage (BulkImageOrganizer_{version}.AppImage) or standalone folder under build/linux/:

./scripts/build-linux-appimage.sh
./scripts/build-linux.sh

Windows — single executable in an output directory (BulkImageOrganizer_{version}.exe):

scripts\manual-build-windows.ps1 -OutputPath C:\builds

CI runs lint and tests first, then parallel Linux AppImage and Windows release jobs. main publishes versioned Forgejo releases (v{version}) with both .AppImage and .exe artifacts; development branches publish dev-{branch-slug} releases with branch-named artifacts that are pruned when the branch is deleted. Versioning policy: docs/VERSIONING.md. See DEVELOPMENT.md for local validation, test layout, and contributor workflow.

Security scanning

Nuitka-compiled binaries (especially Windows .exe files) are sometimes flagged by antivirus software as false positives due to how compiled Python code is structured. Both release CI jobs scan their build artifact before publishing it, so you can verify a release independently of that risk:

  • ClamAV scans the artifact after it's built; a detection fails the build, so a published release was never flagged by ClamAV's signature database at build time.
  • Static analysis runs checksec/readelf/file on the Linux AppImage and an Authenticode signature check + PE section/entropy dump (via pefile) on the Windows executable. This is informational only and never fails the build.
  • VirusTotal (optional — only runs when the VT_API_KEY secret is configured) uploads the artifact for a multi-engine scan and links the report. Also informational only.

Each job's summary (visible on the workflow run page) includes the artifact's SHA256 hash and the scan results — compare the hash against what you downloaded, and check the summary before trusting a build if you're unsure. The scanning logic lives in scripts/security-scan-artifact.py and scripts/virustotal-scan.py.


Development

ruff check .
ruff format --check .
pytest

Architecture, design decisions, and the implementation history live in docs/DESIGN.md, docs/TECHNICAL_OUTLINE.md, and docs/DECISIONS.md.


License

MIT License. You may use, modify, and distribute this software freely. Redistributions and derivative works must include the original copyright notice and license text, attributing Bulk Image Organizer to its authors.