#!/usr/bin/env python3 import argparse import re import os from pathlib import Path from serverLibary import safe_rename,safe_delete VIDEO_EXTENSIONS = {".mpg", ".avi", ".mkv", ".mp4", ".m4v", ".srt"} UNWANTED_PATTERNS_POST = [ r"TV\ Series", r"[a-z]264", r"10bit", r"\[.*?\]", r"320kbps", r"AC3", r"NF\.WEB", r"bdrip", r"RERIP", r"DUAL-", r"LostFilm", r"readnfo", r"BluRay", r"720p", r"1080p", r"WEB", r"AMZN", r"HDTV", r"DVD", r"BDRip", r"HDRip", r"REPACK", r"XviD", r"ETHEL", f"DL ", f"HEVC" ] UNWANTED_PATTERNS_PRE = [ r"www.UIndex.org", r"\[TorrentCounter\.to\]" ] GARBAGE_PATTERNS = [ r"www\.YTS.*", r".*\.nfo", r"RARBG.*", r".*YIFY.*\.jpg", f"Torrent Downloaded From.*txt", "Screens" ] DAMAGE_PATTERNS = [ (r"\.\.", "."), (r"__", "_"), (r"\s+", " "), ] def iter_media_files(base: Path): if base.is_file(): yield base for path in base.rglob("*"): if path.is_file() and path.suffix.lower() in VIDEO_EXTENSIONS: yield path def clean_name(name: Path, search=None, replace=None): if name.is_file(): base, ext = os.path.splitext(name.name) else: base = name.name ext = "" #base = base.replace(" ", ".") base = re.sub(r"s(\d{2})e", r"S\1E", base, flags=re.IGNORECASE) for pattern in UNWANTED_PATTERNS_PRE: base = re.sub(rf"{pattern}[^a-zA-Z0-9]*", "", base, flags=re.IGNORECASE) base_new = base for pattern in UNWANTED_PATTERNS_POST: base_new = re.sub(rf"{pattern}.*$", "", base_new, flags=re.IGNORECASE) if len(base_new) > 5: base = base_new if search and replace: base = re.sub(search, replace, base) # Remove all non-word characters at the end of the string base = re.sub(r'[^A-Za-z0-9)]+$', '', base) base = re.sub(r"\.+", ".", base) base = re.sub(r" +", " ", base) base = base.strip(".") return base + ext def capitalize_name(name: str): return re.sub( r"(^|[ ._\-])([a-z])", lambda m: m.group(1) + m.group(2).upper(), name.lower() ) # ----------------------------- # Operations # ----------------------------- def rename_files(base: Path, args): for file in iter_media_files(base): new_name = clean_name(file, args.search, args.replace) if args.capitalize: new_name = capitalize_name(new_name) new_path = file.with_name(new_name) safe_rename(file, new_path, args.dryrun, args.confirm) def rename_directories(base: Path, args): # reverse order → avoid renaming parent first for path in sorted(base.rglob("*"), reverse=True): if not path.is_dir(): continue new_name = clean_name(path, args.search, args.replace) if args.capitalize: new_name = capitalize_name(new_name) new_path = path.with_name(new_name) safe_rename(path, new_path, args.dryrun, args.confirm) def repair_names(base: Path, args): for file in iter_media_files(base): new_name = file.name for pattern, repl in DAMAGE_PATTERNS: new_name = re.sub(pattern, repl, new_name) new_path = file.with_name(new_name) safe_rename(file, new_path, args.dryrun, args.confirm) def delete_garbage(base: Path, args): for path in base.rglob("*"): for pattern in GARBAGE_PATTERNS: if re.match(pattern, path.name, re.IGNORECASE): print(f"Deleting: {path}") if not args.dryrun: safe_delete(path) # ----------------------------- # CLI # ----------------------------- def parse_args(): parser = argparse.ArgumentParser(description="Media renamer tool") parser.add_argument("-p","--path", default=".", help="Target directory") parser.add_argument("-rep","--repair", action="store_true", help="Fix filenames") parser.add_argument("-d","--delete", dest="delete", action="store_true", help="Delete junk files") parser.add_argument("-cap","--capitalize", dest="capitalize", action="store_true", help="Capitalize names") parser.add_argument("-r","--replace", nargs=2, metavar=("SEARCH", "REPLACE")) parser.add_argument("-dr","--dryrun", action="store_true", help="Dry run") parser.add_argument("-c","--confirm", action="store_true", help="Confirm each rename") return parser.parse_args() def main(): args = parse_args() if args.replace: args.search = args.replace else: args.search = args.replace = None base = Path(args.path).resolve() if not base.exists(): print("Invalid path") return # 1. fix broken names first (optional but recommended) if args.repair: repair_names(base, args) # 2. rename directories FIRST (important for path stability) rename_directories(base, args) # 3. rename files AFTER rename_files(base, args) # 4. cleanup if args.delete: delete_garbage(base, args) if __name__ == "__main__": main()