import os
import re
import shutil
from pathlib import Path
from datetime import datetime
# ============================================================
# UTILITY: List Files in Folder
# ============================================================
def list_files(folder, extension_filter=None):
files = []
for f in sorted(Path(folder).iterdir()):
if f.is_file():
if extension_filter:
if f.suffix.lower() == extension_filter.lower():
files.append(f)
else:
files.append(f)
return files
# ============================================================
# MODE 1: Add Prefix / Suffix
# ============================================================
def rename_prefix_suffix(files, prefix="", suffix=""):
renamed = []
for f in files:
stem = f.stem # filename without extension
ext = f.suffix # .jpg, .txt etc.
new_name = f"{prefix}{stem}{suffix}{ext}"
new_path = f.parent / new_name
renamed.append((f, new_path))
return renamed
# ============================================================
# MODE 2: Sequential Numbering
# ============================================================
def rename_sequential(files, base_name="file", start=1, padding=3):
renamed = []
for i, f in enumerate(files, start=start):
ext = f.suffix
number = str(i).zfill(padding)
new_name = f"{base_name}_{number}{ext}"
new_path = f.parent / new_name
renamed.append((f, new_path))
return renamed
# ============================================================
# MODE 3: Find & Replace in Filename
# ============================================================
def rename_find_replace(files, find_text, replace_text):
renamed = []
for f in files:
new_name = f.name.replace(find_text, replace_text)
new_path = f.parent / new_name
renamed.append((f, new_path))
return renamed
# ============================================================
# MODE 4: Regex-Based Rename
# ============================================================
def rename_regex(files, pattern, replacement):
renamed = []
for f in files:
try:
new_name = re.sub(pattern, replacement, f.name)
new_path = f.parent / new_name
renamed.append((f, new_path))
except re.error as e:
print(f" ⚠ Regex error for {f.name}: {e}")
return renamed
# ============================================================
# MODE 5: Add Date Stamp
# ============================================================
def rename_add_date(files, position="prefix"):
today = datetime.today().strftime("%Y%m%d")
renamed = []
for f in files:
stem = f.stem
ext = f.suffix
if position == "prefix":
new_name = f"{today}_{stem}{ext}"
else:
new_name = f"{stem}_{today}{ext}"
new_path = f.parent / new_name
renamed.append((f, new_path))
return renamed
# ============================================================
# PREVIEW & CONFIRM
# ============================================================
def preview_changes(renamed):
print("\n" + "-"*60)
print(f"{'ORIGINAL':<30} {'NEW NAME':<30}")
print("-"*60)
for old, new in renamed:
print(f"{old.name:<30} {new.name:<30}")
print("-"*60)
def apply_rename(renamed, dry_run=False):
success = 0
skipped = 0
for old, new in renamed:
if old == new:
skipped += 1
continue
if new.exists():
print(f" ⚠ Skipped (already exists): {new.name}")
skipped += 1
continue
if not dry_run:
old.rename(new)
success += 1
if dry_run:
print(f"\n Dry Run Complete: {success} files would be renamed, {skipped} skipped.")
else:
print(f"\n Done! {success} files renamed, {skipped} skipped.")
# ============================================================
# BACKUP (Optional)
# ============================================================
def backup_folder(folder):
backup_path = str(folder) + "_backup_" + datetime.today().strftime("%Y%m%d_%H%M%S")
shutil.copytree(folder, backup_path)
print(f" Backup created: {backup_path}")
# ============================================================
# MAIN MENU
# ============================================================
def main():
print("\n" + "="*55)
print(" BULK FILE RENAMER")
print("="*55)
# --- Folder Path ---
folder = input("\nEnter folder path: ").strip()
if not os.path.isdir(folder):
print(" Invalid folder path!")
return
# --- Optional Extension Filter ---
ext_filter = input("Filter by extension? (e.g. .jpg) or press Enter for all: ").strip()
if not ext_filter:
ext_filter = None
files = list_files(folder, ext_filter)
if not files:
print(" No files found!")
return
print(f"\n Found {len(files)} file(s) in '{folder}'")
# --- Optional Backup ---
backup = input("\nCreate backup before renaming? (y/n): ").strip().lower()
if backup == "y":
backup_folder(folder)
# --- Choose Mode ---
print("\nChoose Rename Mode:")
print(" 1. Add Prefix / Suffix")
print(" 2. Sequential Numbering")
print(" 3. Find & Replace")
print(" 4. Regex Pattern")
print(" 5. Add Date Stamp")
choice = input("\nEnter choice (1-5): ").strip()
renamed = []
if choice == "1":
prefix = input("Enter prefix (or leave blank): ").strip()
suffix = input("Enter suffix (or leave blank): ").strip()
renamed = rename_prefix_suffix(files, prefix, suffix)
elif choice == "2":
base = input("Enter base name (e.g. photo): ").strip()
start = int(input("Start number (default 1): ").strip() or 1)
padding = int(input("Number padding digits (default 3): ").strip() or 3)
renamed = rename_sequential(files, base, start, padding)
elif choice == "3":
find = input("Find text: ").strip()
replace = input("Replace with: ").strip()
renamed = rename_find_replace(files, find, replace)
elif choice == "4":
print(" Example pattern: (\\d+) → matches numbers")
pattern = input("Regex pattern: ").strip()
replacement = input("Replacement: ").strip()
renamed = rename_regex(files, pattern, replacement)
elif choice == "5":
pos = input("Add date as prefix or suffix? (prefix/suffix): ").strip().lower()
if pos not in ["prefix", "suffix"]:
pos = "prefix"
renamed = rename_add_date(files, pos)
else:
print(" Invalid choice.")
return
if not renamed:
print(" No files to rename.")
return
# --- Preview ---
preview_changes(renamed)
# --- Dry Run or Apply ---
mode = input("\nChoose action:\n 1. Apply rename\n 2. Dry run (preview only)\nChoice: ").strip()
if mode == "1":
apply_rename(renamed, dry_run=False)
else:
apply_rename(renamed, dry_run=True)
# ============================================================
# RUN
# ============================================================
if __name__ == "__main__":
main()
No comments:
Post a Comment