move move old scripts
This commit is contained in:
1 parent
93b262977b
commit
208fbd96b4
27 files changed
+346
No files matched your search
Executable
+227
@@ -0,0 +1,227 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from serverlibary import safe_rename,safe_delete
|
||||
|
||||
VIDEO_EXTENSIONS = {".mpg", ".avi", ".mkv", ".mp4", ".m4v", ".srt"}
|
||||
|
||||
UNWANTED_PATTERNS_POST = [
|
||||
r"\d{2,3}k", r"MULTi", 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\]",
|
||||
r"www.Torrenting.org", r"www.Torrenting.com"
|
||||
]
|
||||
|
||||
GARBAGE_PATTERNS = [
|
||||
r"Downloaded From.*", r"www\.YTS.*", r"YTS.*txt", r"YTS.*jp*g" , r".*\.nfo", r"YTSY.*", 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 = 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
|
||||
paths = [base] + list(base.rglob("*"))
|
||||
new_base = base
|
||||
if base.is_dir():
|
||||
new_base = clean_name(base, args.search, args.replace)
|
||||
if args.capitalize:
|
||||
new_base = capitalize_name(new_base)
|
||||
new_base = base.with_name(new_base)
|
||||
|
||||
for path in sorted(paths, 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)
|
||||
return new_base
|
||||
|
||||
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
|
||||
|
||||
home = Path.home()
|
||||
|
||||
if base == home or home in base.parents:
|
||||
answer = input(
|
||||
f"You are about to run on your home directory:\n"
|
||||
f" {base}\n\n"
|
||||
"This may process many files. Continue? [y/N]: "
|
||||
).strip().lower()
|
||||
|
||||
if answer not in ("y", "yes"):
|
||||
print("Aborted.")
|
||||
sys.exit(1)
|
||||
|
||||
#video_files = []
|
||||
#for file in iter_media_files(base):
|
||||
# print(file)
|
||||
# video_files.append(file)
|
||||
#
|
||||
#if len(video_files) == 0:
|
||||
# print(f"Error: No media files found in {base}")
|
||||
# sys.exit(1)
|
||||
#
|
||||
#for f in video_files:
|
||||
# p = Path(f).resolve()
|
||||
#
|
||||
# # skip files not inside base
|
||||
# try:
|
||||
# rel = p.relative_to(base)
|
||||
# except ValueError:
|
||||
# continue
|
||||
#
|
||||
# # rel.parts includes directories + filename
|
||||
# # depth 0: (file directly in base) -> len(parts) == 1
|
||||
# # depth 1: base/sub/file -> len(parts) == 2
|
||||
# if len(rel.parts) <= 2:
|
||||
# raise ValueError(f"Invalid file location: {p} (inside base or depth 1 subdir)")
|
||||
#
|
||||
# 1. fix broken names first (optional but recommended)
|
||||
if args.repair:
|
||||
print(f"Repair names")
|
||||
repair_names(base, args)
|
||||
|
||||
# 2. rename directories FIRST (important for path stability)
|
||||
print(f"Rename directories: {base}")
|
||||
base = rename_directories(base, args)
|
||||
|
||||
# 3. rename files AFTER
|
||||
print(f"Rename files {base}")
|
||||
rename_files(base, args)
|
||||
|
||||
# 4. cleanup
|
||||
if args.delete:
|
||||
delete_garbage(base, args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user