downloads-cleanup
v1.0.0
Published
Review your Downloads folder in the browser and clear out the clutter — duplicates, junk, old files, installers, and folders — straight to the Trash.
Maintainers
Readme
downloads-cleanup
My Downloads folder had twenty-thousand-something files in it and I had no idea what most of them were. Deleting things one by one in Finder wasn't going to happen, so I wrote this: a little local web app that looks through the folder, guesses what's junk, and lets me throw the obvious stuff in the Trash without worrying I'll nuke something I actually need.
It runs entirely on your machine. Nothing gets uploaded anywhere, and "delete" means "move to the Trash", so you can always fish a file back out.
Running it
Nothing to install — you need Node 20+, then:
npx downloads-cleanupThat spins up a local server and opens the dashboard in your browser, pointed at
~/Downloads. Tick what you don't want, hit the Trash button, done. It keeps
running until you stop it with Ctrl+C.
If you reach for it often, install it once:
npm install -g downloads-cleanup
downloads-cleanupSensible to try it on a throwaway folder first:
npx downloads-cleanup --root /some/test/folderRun npx downloads-cleanup --help for the full list of flags (--root, --port,
--age, --no-open).
Working on it
If you've cloned the repo instead, npm install builds it and npm start runs
your local copy.
It shows the top level of your Downloads, the way Finder does: loose files plus
folders, with each folder as one row showing its total size and how many items
it holds. So you can throw out a whole extracted-app folder in one go instead of
picking through it. (If you'd rather see every nested file too, set
recursive: true in config.json.)
What it flags
Every item gets a verdict: keep, review, or remove. The "remove" ones are
where I was comfortable being opinionated:
- Duplicates — same bytes, verified with a SHA-256 hash. It keeps one copy
and flags the others. Things like
invoice (1).pdfget caught here. - Junk —
.DS_Store,.localized, half-finished.crdownload/.partdownloads, virus-scan logs, empty files, empty folders. - Installers —
.dmg,.pkg,.exe, and archives that already have a matching extracted folder sitting next to them.
"Review" is the softer bucket: things you haven't touched in a while (180 days by default), and loose archives. Those I'd rather look at before deleting, so they don't get the red label.
None of this happens automatically. You pick what goes; the app just does the sorting and the nervous double-checking. When you clear out a big batch, a progress bar tracks the files heading to the Trash.
Configuration
Defaults live in config.json. You can edit that, or override per run with an
env var or a flag:
| What | config.json | Override |
| --- | --- | --- |
| Folder to scan | root | DOWNLOADS_ROOT=… or --root … |
| Port | port | PORT=… or --port … |
| "Old" cutoff in days | ageThresholdDays | AGE_DAYS=… or --age … |
NO_OPEN=1 stops it from opening a browser tab on start, which is handy if
you're poking at it from the command line.
A couple of things worth knowing
It handles big folders fine. The table only ever keeps the rows you can actually see in the DOM (a few dozen), so scrolling and filtering through 20k+ files stays snappy instead of freezing the tab. The duplicate check only hashes files that share an exact size, so the initial scan doesn't read every byte on disk.
The search box takes wildcards if you want them: *.pdf for everything ending
in .pdf, IMG_* for camera dumps. Plain text still does a normal substring
match.
Selecting is the usual deal: click a row to toggle it, or click one and shift+click another to grab everything in between. Handy for clearing out a long run of junk in one go.
The one thing I was careful about: anything the server is asked to open, preview,
or trash gets checked against the scan root first (assertWithinRoot in
src/safety.ts). A weird request can't talk it into touching /etc/passwd or
anything else outside your Downloads folder.
How it's put together
TypeScript front to back. The server compiles to dist/, the browser code to
public/app.js (both via npm run build, which npm start runs for you).
src/
scanner.ts walk the folder, collect file metadata
rules.ts the junk/installer/old heuristics
duplicates.ts size-bucket then SHA-256 to find real duplicates
analyzer.ts turn signals into a verdict + summary
safety.ts the path guard
trash.ts thin wrapper over the `trash` package
server.ts express: the API + serves the page
client/app.ts the dashboard (virtualized table, filters, selection)
public/
index.html, styles.css + the compiled app.jsUseful scripts: npm run typecheck (no emit), npm run build, and
npm run serve if you've already built and just want to start the server.
