Add CSV mapping rule and Undo last rename feature
Made-with: Cursor
This commit is contained in:
+57
-1
@@ -1,12 +1,16 @@
|
||||
"""
|
||||
Apply a list of rules to a list of filenames; produce preview and perform renames.
|
||||
Undo: save a log after renames; undo reverses renames from that log.
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
|
||||
from .rules import Rule
|
||||
|
||||
UNDO_FILENAME = ".bulk-renamer-undo.json"
|
||||
|
||||
|
||||
def _split_name(name: str) -> tuple[str, str]:
|
||||
if "." in name and not name.startswith("."):
|
||||
@@ -26,7 +30,7 @@ def apply_pipeline(
|
||||
for r in rules:
|
||||
if not r.enabled:
|
||||
continue
|
||||
stem, ext = r.apply(stem, ext, index, total)
|
||||
stem, ext = r.apply(stem, ext, index, total, original_name=name)
|
||||
return stem + ext
|
||||
|
||||
|
||||
@@ -104,3 +108,55 @@ def perform_renames(
|
||||
pass
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def undo_log_path(base_dir: str) -> Path:
|
||||
return Path(base_dir) / UNDO_FILENAME
|
||||
|
||||
|
||||
def save_undo_log(base_dir: str, renames: List[tuple[str, str]]) -> None:
|
||||
"""Save (old_name, new_name) list so undo can reverse it. Overwrites any existing log for this folder."""
|
||||
path = undo_log_path(base_dir)
|
||||
data = {"renames": renames}
|
||||
try:
|
||||
path.write_text(json.dumps(data, indent=0), encoding="utf-8")
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
def load_undo_log(base_dir: str) -> Optional[List[tuple[str, str]]]:
|
||||
"""Load the last rename log for this folder. Returns list of (old_name, new_name) or None."""
|
||||
path = undo_log_path(base_dir)
|
||||
if not path.is_file():
|
||||
return None
|
||||
try:
|
||||
data = json.loads(path.read_text(encoding="utf-8"))
|
||||
return data.get("renames")
|
||||
except (OSError, json.JSONDecodeError, TypeError):
|
||||
return None
|
||||
|
||||
|
||||
def remove_undo_log(base_dir: str) -> None:
|
||||
"""Remove the undo log file for this folder."""
|
||||
path = undo_log_path(base_dir)
|
||||
try:
|
||||
path.unlink()
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
def perform_undo(base_dir: str) -> List[tuple[str, str, Optional[str]]]:
|
||||
"""
|
||||
Undo the last rename in base_dir using the saved log.
|
||||
Returns same format as perform_renames. Does not remove the log on failure.
|
||||
"""
|
||||
renames = load_undo_log(base_dir)
|
||||
if not renames:
|
||||
return []
|
||||
# Reverse: rename current (new) back to original (old). Order must be reversed for chains.
|
||||
undo_list = [(new_name, old_name) for (old_name, new_name) in renames]
|
||||
undo_list.reverse()
|
||||
results = perform_renames(base_dir, undo_list, dry_run=False)
|
||||
if not any(r[2] for r in results):
|
||||
remove_undo_log(base_dir)
|
||||
return results
|
||||
|
||||
Reference in New Issue
Block a user