MediaCleanup is a Python 3.11+ CLI that reorganizes media folders in place, records everything it would do in action logs, and can run in a safe dry-run mode before touching any files.
- Python 3.11 or newer (tomllib is required and bundled in 3.11+)
- Git checkout of this repository (see
make install/make uninstall) ghCLI authenticated when you want to create releases
Use the bundled CLI directly from the repo:
./mediacleanup.py --dry-run
./mediacleanup.py --apply
./mediacleanup.py --log-level DEBUG
./mediacleanup.py --output-style pro
./mediacleanup.py --version
make update-config--dry-run(default) /--apply: choose simulation or actual filesystem changes. These options are mutually exclusive.--log-level LEVEL: control verbosity (ERROR,WARN,INFO,DEBUG).--verboseis shorthand for--log-level DEBUG.--no-virtual: prevent building the in-memory model, useful for very large trees where you only want the action log and counters.--config PATH: point to a custom TOML configuration file (defaults to~/.mediacleanup.toml).--output-style STYLE: override the theme (minimal,vibrant,pro) set in the config, valid for a single run.--version: print the packaged version number (2.1.0) and exit immediately.
Copy mediacleanup.toml.sample to ~/.mediacleanup.toml and edit it:
output_style = "vibrant"
media_dirs = [
"${HOME}/Media/TV Shows",
"${HOME}/Media/Movies",
]
allowed_file_ext = ["mp4", "mkv", "avi", "srt"]media_dirsmust contain absolute paths; the CLI rejects relative roots or/,.formats.allowed_file_extentries normalize to lowercase and drop leading dots; invalid strings are skipped with a warning.output_stylemust match one of the supported themes;make update-config(backed byscripts/update_config_defaults.py) safely inserts newly required keys without overwriting your values.- Legacy
.confor shell-style configs are rejected with a clear migration message.
- Reads every configured media root and allowed extension list before processing.
- Builds a virtual state (with
--dry-runand no--no-virtual) to ensure deterministic simulations. - Moves files from nested directories into the root so that they can be normalized/organized consistently.
- Normalizes filenames by stripping punctuation, capitalizing segments, and trimming numeric suffixes when appropriate.
- Detects TV episodes, creates
Series.Name/Sxx/directories, and uses.tvshowmarkers so reruns recognize the series root. - Detects movie series by shared prefixes, moves them under
Series.Name/roots, and creates.movieseriesmarkers to avoid reprocessing. - Removes files that do not match the allowed extensions list (except for
.DS_Store, which is also cleaned if present). - Removes empty directories after cleaning (and deletes stray
.DS_Storemarkers). - All changes are tracked in
/tmp/mediacleanup/action-list-<timestamp>.txtwith tab-delimitedACTION<TAB>source<TAB>destentries.
- Operates only under the configured absolute media roots; anything outside is untouched.
- Every move/rename/delete/rmdir/mkdir/touch is logged both on the console and in the action list file.
- Repeated runs converge: once files are normalized and organized, rerunning in dry-run will quickly report “skipped” for everything else.
You can read the summary at the end of the CLI output. A typical dry-run looks like:
────────────────────────────────────────────────────────────────────── MEDIA CLEANUP
▶ Run started at 2026-03-05 15:49:54 (dry-run)
Mode: DRY-RUN | Virtual model: on | Log level: INFO | Theme: minimal
──────────────────────────────────────────────────────────────────────
▸ /private/tmp/mediacleanup-sample/TV
› Moving files from nested dirs to root
· Simulating move
from Season-1/Sample.Show.S01E01.mp4
to Sample.Show.S01E01.mp4
› Normalizing filenames
· Simulating rename
from Sample.Show.S01E01.mp4
to Sample.Show.S01e01.mp4
› Organizing episode files
· Simulating mkdir: Sample.Show
· Simulating marker: Sample.Show/.tvshow
· Simulating mkdir: Sample.Show/S01
· Simulating move
from Sample.Show.S01e01.mp4
to Sample.Show/S01/Sample.Show.S01E01.mp4
› Organizing movie series
› Removing unwanted files
› Removing empty subdirectories
· Simulating rmdir: Season-1
──────────────────────────────────────────────────────────────────────
▸ /private/tmp/mediacleanup-sample/Movies
› Moving files from nested dirs to root
› Normalizing filenames
› Organizing episode files
› Organizing movie series
› Removing unwanted files
› Removing empty subdirectories
› Cleanup complete in 0s
› Action list recorded at /tmp/mediacleanup/action-list-20260305154954.txt
· Run ended at 2026-03-05 15:49:54
────────────────────────────────────────────────────────────────────── RUN SUMMARY
# Summary at 2026-03-05 15:49:54
- Actions
Moves 2
Renames 1
Deletes 0
Rmdirs 1
Mkdirs 2
Touches 1
- Outcome
Simulated 7
Skipped 0
› Dry-run: no changes made.
› To apply these changes, run again with --apply.
Action list files remain in /tmp/mediacleanup, and only the five most recent files are kept automatically.
make check-prereq
make lint
make test
make install
make uninstall