Development Blog on software and related things

The Next Chapter in File Organization: Introducing Reny

Remember our previous look into organizing files? Back then, the work was done by renamer, a component buried deep inside the batchmp multimedia suite. It was useful for tidying up messy download folders, generating virtual views, and wrangling chronological chaos.

But times change, and tools evolve.

batchmp remains a powerhouse for media processing, yet the core file management logic inside renamer proved far too universally useful to stay locked inside a heavy multimedia package. So it was spun off into a dedicated, standalone tool.

Behold reny!

reny takes the familiar renamer functionality and repackages it as a lightweight CLI application. Free from the ffmpeg dependencies of its predecessor, it is easier to install and far better suited to everyday file management.

The original feature set is all still there, along with a number of additions: global and local configs, contextual ignore files that cut down on long strings of command line options, Git integration, and a handful of quality-of-life touches earned from daily use.

Here is a brief tour of what the standalone tool brings to the table.

Files Visualization

Before renaming or moving anything, it helps to see exactly what you are dealing with. Run reny with no arguments and it prints the current directory:

$ reny
~/Dev/reny/demo_data
  |-/downloads
  |-/episodes
  |-/nested
  |-/photos
0 files, 4 folders

Recursion Control

reny stays at the top level unless told otherwise. -r displays recursively, -el (end level) sets how deep to descend, and -sl (start level) skips levels at the top:

$ reny -el 1
~/Dev/reny/demo_data
  |->/downloads
    |- receipt.pdf
    |- screen_recording.mov
    |- screenshot.png
    |- tax_return.pdf
    |- vacation_movie.mp4
  |->/episodes
    |- ep1.mp4
    |- ep10.mp4
    |- ep2.mp4
  |->/nested
    |-/folder1
    |-/folder2
  |->/photos
    |- IMG_2023.jpg
    |- IMG_2024.jpg
10 files, 6 folders

Sizes & Sorting

-ss adds sizes, aggregated per folder and totalled at the end, and -s picks the sort order: na / nd by name, sa / sd by size:

$ reny -el 1 -ss -s sd
~/Dev/reny
  |-  9KB README.md
  |-  1KB pyproject.toml
  |-  1KB LICENSE
  |->/ 19.3MB tests
    |-  0KB conftest.py
    |-  0KB __init__.py
    |-/ 19.2MB fs
    |-/ 45KB commons
    |-/ 20KB base
    |-/ 1KB __pycache__
  |->/ 688KB reny
    |-  0KB __init__.py
    |-/ 363KB fstools
    |-/ 192KB cli
    |-/ 126KB commons
    |-/ 0KB __pycache__
  |->/ 97KB dist
    |-  53KB reny-1.0.10-py3-none-any.whl
    |-  44KB reny-1.0.10.tar.gz
14 files, 17 folders
Total selected entries size: 20.1MB

Because folder sizes roll up, a single glance is usually enough to find whatever is quietly eating your disk.

Filtering

Filters are what turn a wall of output into something readable. -in and -ex take Unix-style name patterns separated by ;:

$ reny -d reny -el 2 -in '*.py'
~/Dev/reny/reny
  |- __init__.py
  |../cli
    |- __init__.py
  |../commons
    |- __init__.py
    |- chainedhandler.py
    |- descriptors.py
    |- progressbar.py
    |- taskprocessor.py
    |- utils.py
  |../fstools
    |- __init__.py
    |- dirtools.py
    |- fsutils.py
    |- rename.py
    |- virtual_organizer.py
    |- walker.py
14 files, 0 folders

For media libraries, -ft filters by media class rather than by name — image, video, audio, media, nonmedia, playable, nonplayable, or any:

$ reny -d demo_data -el 1 -ft image -ad
~/Dev/reny/demo_data
  |->/downloads
    |- screenshot.png
  |->/episodes
  |->/nested
    |-/folder1
    |-/folder2
  |->/photos
    |- IMG_2023.jpg
    |- IMG_2024.jpg
3 files, 6 folders

Finally, -ig reads patterns from an ignore file and appends them to the exclusions, so a repository can keep its noise permanently out of the way:

$ reny -el 2 -ig .renyignore

Any .gitignore-style file works here. reny also picks up ./.renyignore from the target directory automatically, falling back to ~/.renyignore.

Virtual Views

Organizing usually starts with a question: what would this folder look like if it were tidy? A virtual view answers it by rendering the reorganized structure — here by type, sorted by size descending (-s sd, though sorting by date using da or dd is also supported), with sizes shown (-ss) — without changing anything on disk:

$ reny -b type -s sd -ss
Virtual view by type:
~/Downloads
  |->/ 1.3GB mp4
    |-  531.0MB movie_part1.mp4
    |-  442.0MB movie_part2.mp4
    |-  357.0MB presentation.mp4
  |->/ 200.0MB dmg
    |-  200.0MB installer.dmg
  |->/ 150.0MB pdf
    |-  150.0MB documentation.pdf
  |->/ 32.0MB xlsx
    |-  32.0MB spreadsheet.xlsx
  |->/ 5.0MB jpg
    |-  4.0MB high_res_photo.jpg
    |-  1.0MB thumbnail.jpg
  |->/ 2.0MB png
    |-  2.0MB screenshot.png
9 files, 6 folders
Total selected entries size: 1.7GB

Sorting by size makes it obvious which file types are eating up the most space. Once the preview looks right, the very same flags handed to organize turn it into reality.

File Organization

Organize by File Type

To commit the view above and actually sort a mixed directory by type:

$ reny organize -b type

reny groups files by extension, creating one subdirectory per type and dropping anything extensionless into unknown. As always, it shows the changes it intends to make and asks for confirmation first.

Organize by Date

Chronological sorting works the same way:

$ reny organize -b date --date-format "%Y/%m"

This relies on standard Python strftime formatting to build nested year/month directories.

Batch Renaming

With everything in its place, the names themselves are usually next. reny keeps all the classic batch renaming commands from the original tool, so bulk modifications stay straightforward. Every command is a dry run by default: reny visualizes the targeted changes and asks for confirmation before touching anything. Adding -dc (display current) puts the current names into that confirmation prompt too, giving you a before-and-after comparison.

Regex Replace

When filenames are inconsistent across directories, regular expressions clean them up in one pass:

$ reny replace -fs 'test' -rs 'tst' -dc

This finds every occurrence of ‘test’ in filenames and replaces it with ‘tst’.

Padding Numbers

When files sort awkwardly (file_1.txt, file_10.txt, file_2.txt), the pad command restores the expected order by padding numeric sequences with leading zeros:

$ reny pad -md 2 -dc

This forces every number to at least two digits (file_01.txt, file_02.txt, and so on), fixing file explorer sorting.

Indexing

To add sequential numbers to a batch of files:

$ reny index -dc

This prefixes each file with a sequential index, which comes in handy for photo albums and structured datasets. Indexing is multi-level by default, restarting the count inside each directory; -sq numbers files sequentially across the whole tree instead.

Bulk Deletion

Sometimes the best way to organize is just to throw things out. reny delete safely removes targeted files. When combined with filters, it’s perfect for safely cleaning up a messy downloads folder (reny -in "*.tmp" delete), or sweeping away build artifacts to prepare for a clean build. Pair it with the -gi flag to instantly target and delete all Git-ignored files (__pycache__, dist/, .egg-info):

$ reny -gi -ex .venv delete -id
The following files / folders will be deleted
/reny
  |-  6KB .DS_Store
  |->/ 3.4MB .mypy_cache
    |-  0KB .gitignore
    |-  0KB CACHEDIR.TAG
    |-/ 3.4MB 3.14
  |->/ 5KB .pytest_cache
    |-  0KB .gitignore
    |-  0KB CACHEDIR.TAG
    |-  0KB README.md
  |->/ 103KB dist
    |-  56KB reny-1.0.12-py3-none-any.whl
    |-  47KB reny-1.0.12.tar.gz
  |->/ 13KB reny.egg-info
    |-  0KB dependency_links.txt
    |-  0KB entry_points.txt
    |-  11KB PKG-INFO
    |-  0KB requires.txt
    |-  1KB SOURCES.txt
    |-  0KB top_level.txt
14 files, 5 folders
Total selected entries size: 3.5MB

Like everything else, it previews the deletion tree and waits for a firm confirmation before dropping anything into the void.

Developers’ Quality-of-Life Features

As a standalone CLI tool, reny slots neatly into everyday developer workflows.

Git Integration

Inside a Git repository, -g annotates the view with each entry’s status and bubbles modifications up to their parent directories, so it is immediately clear where the changes live:

$ reny -el 1 -ig .renyignore -g
~/Dev/reny
  |- LICENSE
  |- pyproject.toml
  |- README.md [ M]
  |->/demo_data [??]
    |-/downloads
    |-/episodes
    |-/nested
    |-/photos
  |->/reny [* ]
    |-/cli [* ]
    |-/commons
    |-/fstools [* ]
  |->/tests
    |- conftest.py
    |-/base
    |-/commons
    |-/fs
4 files, 13 folders

The markers will look familiar: [ M] for modified, [??] for untracked, and [* ] for a directory holding changes somewhere below it. -go goes further and drops everything unmodified, which is ideal for scoping a rename to work in progress:

$ reny -el 2 -ig .renyignore -go
  |- README.md [ M]
  |->/demo_data [??]
  |->/reny [* ]
    |->/cli [* ]
      |-/base [* ]
    |->/fstools [* ]
      |- dirtools.py [ M]
2 files, 5 folders

Similarly, -gt or --git-tracked filters the view to show only files already tracked by Git, completely ignoring untracked clutter. If you want the exact opposite, --not-git-tracked (-ngt) surfaces only the untracked files. Lastly, --git-ignored (-gi) gives you a crystal-clear view of everything Git explicitly ignores—like build artifacts and .DS_Stores—temporarily bypassing .renyignore so nothing slips by.

Configuration & Exclusions

Rather than retyping the same flags, generate a local config file in the current directory:

$ reny config -l

This creates a fully commented .reny.toml and opens it in your default editor, where you can set defaults for recursion depth, filtering, sizes, sort order, and more. Without -l, the same command manages the global configuration at ~/.config/reny/config.toml. A local config takes precedence over the global one, and command line flags override both.

To exclude certain files or directories:

$ reny ignore

This creates a local .renyignore that behaves like a .gitignore; reny ignore -gl does the same globally at ~/.renyignore. Add patterns such as node_modules/ or *.tmp, and reny will skip them from then on.

Getting Started

By shedding its multimedia dependencies, reny has become a fast, focused tool for terminal-based file organization.

Ready to get organized? reny is available via Homebrew:

$ brew tap akpw/tap
$ brew install reny

Or from PyPI:

$ pip install reny