Files
Server-scripts/rename_episodes.py
T
2026-09-14 09:20:47 +02:00

181 lines
4.9 KiB
Python

#!/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()