Add CSV mapping rule and Undo last rename feature

Made-with: Cursor
This commit is contained in:
Bulk Renamer
2026-03-03 22:42:57 -06:00
parent 94fa790d48
commit d1a13115a4
6 changed files with 285 additions and 14 deletions
+44 -2
View File
@@ -26,7 +26,7 @@ from PyQt6.QtWidgets import (
from PyQt6.QtCore import Qt, QDir, QItemSelectionModel
from PyQt6.QtGui import QFont, QColor
from engine.pipeline import compute_preview, perform_renames
from engine.pipeline import compute_preview, perform_renames, save_undo_log, load_undo_log, perform_undo
from engine.rules import Rule
from .rule_widgets import (
ReplaceRuleWidget,
@@ -37,6 +37,7 @@ from .rule_widgets import (
NumberingRuleWidget,
EpisodeRenumberRuleWidget,
PrefixSuffixRuleWidget,
CsvMappingRuleWidget,
)
@@ -85,6 +86,7 @@ class MainWindow(QMainWindow):
NumberingRuleWidget(),
EpisodeRenumberRuleWidget(),
PrefixSuffixRuleWidget(),
CsvMappingRuleWidget(),
]
rule_titles = [
"1. Replace",
@@ -95,6 +97,7 @@ class MainWindow(QMainWindow):
"6. Numbering",
"7. Episode renumber",
"8. Prefix / Suffix",
"9. CSV mapping",
]
for title, w in zip(rule_titles, self._rule_widgets):
w.enabled_cb.toggled.connect(self._refresh_preview) # always refresh when checkbox toggled
@@ -130,10 +133,16 @@ class MainWindow(QMainWindow):
self.preview_status = QLabel("Add a folder to see files.")
right_layout.addWidget(self.preview_status)
btn_layout = QHBoxLayout()
apply_btn = QPushButton("Apply renames")
apply_btn.setMinimumHeight(36)
apply_btn.clicked.connect(self._apply_renames)
right_layout.addWidget(apply_btn)
undo_btn = QPushButton("Undo last rename")
undo_btn.setMinimumHeight(36)
undo_btn.clicked.connect(self._undo_renames)
btn_layout.addWidget(apply_btn)
btn_layout.addWidget(undo_btn)
right_layout.addLayout(btn_layout)
split.addWidget(right)
split.setSizes([320, 680])
@@ -289,5 +298,38 @@ class MainWindow(QMainWindow):
msg += f"\n... and {len(errors) - 10} more."
QMessageBox.warning(self, "Rename errors", msg)
else:
save_undo_log(self._base_dir, renames)
QMessageBox.information(self, "Done", f"Renamed {len(results)} file(s).")
self._on_dir_changed()
def _undo_renames(self):
if not self._base_dir:
QMessageBox.warning(self, "No folder", "Select a folder first.")
return
renames = load_undo_log(self._base_dir)
if not renames:
QMessageBox.information(
self,
"No undo data",
f"No undo data for this folder.\n\nUndo is available after you run \"Apply renames\" here.",
)
return
ok = QMessageBox.question(
self,
"Undo last rename",
f"Revert {len(renames)} file(s) in\n{self._base_dir}\nback to their previous names?",
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
QMessageBox.StandardButton.No,
)
if ok != QMessageBox.StandardButton.Yes:
return
results = perform_undo(self._base_dir)
errors = [r for r in results if r[2]]
if errors:
msg = "\n".join(f"{r[0]}: {r[2]}" for r in errors[:10])
if len(errors) > 10:
msg += f"\n... and {len(errors) - 10} more."
QMessageBox.warning(self, "Undo errors", msg)
else:
QMessageBox.information(self, "Undone", f"Reverted {len(results)} file(s).")
self._on_dir_changed()