Skip to content

Add a standalone Remove Duplicates button - #3

Merged
Patrick16 merged 1 commit into
masterfrom
add-dedup-button
Sep 11, 2026
Merged

Patrick16 merged 1 commit into
masterfrom
add-dedup-button

Conversation

@Patrick16

Copy link
Copy Markdown
Owner

Scans a folder for images with byte-identical content (exact SHA256 hash match) and removes every copy but one, independent of any download job. Keeper selection is deterministic: alphabetically-first filename in each duplicate group.

  • app/dedup.py: hash_file() and find_duplicate_groups() -- shares the IMAGE_EXTENSIONS set with caption-folder scanning, now moved to app/download.py so both modules import it from one place.
  • app/jobs.py: create_dedup_job()/run_dedup_job() reuse the existing JobState/WebSocket progress machinery. Removed duplicates (and any orphaned .txt caption for them) go to the OS Recycle Bin via send2trash, not a permanent delete -- this is a bulk action on a user's dataset, so it stays recoverable. The keeper's own caption is left untouched.
  • app/main.py: POST /api/dedup-folder endpoint.
  • static/index.html + app.js: new "Remove duplicate images" card with a folder picker, recursive toggle, and a confirm() dialog before running (explains the Recycle Bin recoverability) since this deletes across a whole folder at once.
  • README.md + docs/landscape-and-roadmap-notes.md: documented the new feature and marked the roadmap's exact-hash de-dup item as shipped (perceptual/similarity-based de-dup for near-duplicates remains open).

Live-verified beyond the 217 unit/integration tests (12 new for app/dedup.py, plus jobs/main coverage for the job kind and endpoint): downloaded real images from e621, manufactured file-level duplicates plus an orphaned caption, ran the actual /api/dedup-folder endpoint, and confirmed both the resulting stats/remaining files on disk AND that the removed files genuinely landed in the real Windows Recycle Bin (checked via the Shell.Application COM Recycle Bin namespace) -- then repeated the same scenario end-to-end through the real browser UI (form fill + button click, with window.confirm auto-accepted) to confirm the full client-to-server path, not just the API in isolation.

Scans a folder for images with byte-identical content (exact SHA256 hash
match) and removes every copy but one, independent of any download job.
Keeper selection is deterministic: alphabetically-first filename in each
duplicate group.

- app/dedup.py: hash_file() and find_duplicate_groups() -- shares the
  IMAGE_EXTENSIONS set with caption-folder scanning, now moved to
  app/download.py so both modules import it from one place.
- app/jobs.py: create_dedup_job()/run_dedup_job() reuse the existing
  JobState/WebSocket progress machinery. Removed duplicates (and any
  orphaned .txt caption for them) go to the OS Recycle Bin via
  send2trash, not a permanent delete -- this is a bulk action on a
  user's dataset, so it stays recoverable. The keeper's own caption is
  left untouched.
- app/main.py: POST /api/dedup-folder endpoint.
- static/index.html + app.js: new "Remove duplicate images" card with a
  folder picker, recursive toggle, and a confirm() dialog before running
  (explains the Recycle Bin recoverability) since this deletes across a
  whole folder at once.
- README.md + docs/landscape-and-roadmap-notes.md: documented the new
  feature and marked the roadmap's exact-hash de-dup item as shipped
  (perceptual/similarity-based de-dup for near-duplicates remains open).

Live-verified beyond the 217 unit/integration tests (12 new for
app/dedup.py, plus jobs/main coverage for the job kind and endpoint):
downloaded real images from e621, manufactured file-level duplicates
plus an orphaned caption, ran the actual /api/dedup-folder endpoint,
and confirmed both the resulting stats/remaining files on disk AND
that the removed files genuinely landed in the real Windows Recycle
Bin (checked via the Shell.Application COM Recycle Bin namespace) --
then repeated the same scenario end-to-end through the real browser
UI (form fill + button click, with window.confirm auto-accepted) to
confirm the full client-to-server path, not just the API in isolation.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
@Patrick16
Patrick16 merged commit 1e38a4e into master Sep 11, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant