Give every file the name of the folder it lives in.
A small local tool for anyone whose exports come out as DSC_4471.raw and need to
come out as Iceland 2024 DSC_4471.raw. Point it at a root folder, and every file
in every subfolder inherits its folder's name as a prefix.
NameSake walks a root folder recursively. In each subfolder it finds, every file is
renamed to <folder name> <original name>:
2024 Shoots/ 2024 Shoots/
├── Iceland 2024/ ├── Iceland 2024/
│ ├── DSC_4471.raw → │ ├── Iceland 2024 DSC_4471.raw
│ └── DSC_4472.raw │ └── Iceland 2024 DSC_4472.raw
└── Studio Portraits/ └── Studio Portraits/
└── headshot.jpg └── Studio Portraits headshot.jpg
Files that already carry the prefix are left alone, so a second run is a no-op —
you never get Iceland 2024 Iceland 2024 DSC_4471.raw.
Double-click Launcher.command. It creates the virtual environment on first
run, installs Flask, starts the server on 127.0.0.1:5000 and opens your browser.
Or start it by hand:
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
python app.pyThen open http://127.0.0.1:5000.
The same rename, without the interface:
python rename_files.py "/Users/you/Pictures/2024 Shoots"Renaming in bulk is irreversible, so NameSake is deliberately conservative:
- Files directly in the root are skipped. Only files inside subfolders are renamed — the root folder's own name is never applied.
- Dotfiles are skipped, so
.DS_Storeand friends stay as they are. - Existing files are never overwritten. If the target name is already taken,
the file is reported as an error and left untouched. (
os.renamewould silently replace it on macOS and Linux; NameSake checks first.) - Nothing leaves your machine. The server binds to
127.0.0.1and makes no network requests — no fonts, no analytics, no CDN.
There is no undo. The transform is invertible by hand — strip the
<folder name> prefix — but check the folder you picked before you run it.
- Pick a folder with the system dialog, or paste a path directly.
- ⏎ runs, ⌘O opens the folder picker.
- The counters double as log filters — click Errors to see only what failed.
- Copy puts the whole log on the clipboard as plain text.
macOS, Python 3.9+, and Flask. The folder picker uses AppleScript, so that part is macOS-only; the CLI script runs anywhere.
