Release v1.0.8: recursive folder scan and file type filters.
Adds subfolder inclusion and extension presets so the preview list can target nested media without loading everything. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
+45
-10
@@ -22,11 +22,14 @@ from PyQt6.QtWidgets import (
|
||||
QHeaderView,
|
||||
QAbstractItemView,
|
||||
QFrame,
|
||||
QCheckBox,
|
||||
QComboBox,
|
||||
)
|
||||
from PyQt6.QtCore import Qt, QDir, QItemSelectionModel
|
||||
from PyQt6.QtGui import QFont, QColor
|
||||
|
||||
from engine.pipeline import compute_preview, perform_renames, save_undo_log, load_undo_log, perform_undo
|
||||
from engine.file_scan import FILE_TYPE_PRESETS, list_files, parse_extension_filter, file_extension
|
||||
from engine.rules import Rule
|
||||
from .rule_widgets import (
|
||||
ReplaceRuleWidget,
|
||||
@@ -70,6 +73,22 @@ class MainWindow(QMainWindow):
|
||||
dir_layout.addWidget(browse_btn)
|
||||
layout.addLayout(dir_layout)
|
||||
|
||||
filter_layout = QHBoxLayout()
|
||||
self.recursive_cb = QCheckBox("Include subfolders")
|
||||
self.recursive_cb.toggled.connect(self._reload_files)
|
||||
filter_layout.addWidget(self.recursive_cb)
|
||||
filter_layout.addWidget(QLabel("File types:"))
|
||||
self.types_preset = QComboBox()
|
||||
self.types_preset.addItems(list(FILE_TYPE_PRESETS.keys()) + ["Custom"])
|
||||
self.types_preset.currentTextChanged.connect(self._on_types_preset_changed)
|
||||
filter_layout.addWidget(self.types_preset)
|
||||
self.types_edit = QLineEdit()
|
||||
self.types_edit.setPlaceholderText("e.g. .mkv,.mp4 or *.avi (empty = all types)")
|
||||
self.types_edit.setReadOnly(True)
|
||||
self.types_edit.editingFinished.connect(self._reload_files)
|
||||
filter_layout.addWidget(self.types_edit, 1)
|
||||
layout.addLayout(filter_layout)
|
||||
|
||||
# Split: left = rules, right = file list + preview
|
||||
split = QSplitter(Qt.Orientation.Horizontal)
|
||||
|
||||
@@ -160,6 +179,14 @@ class MainWindow(QMainWindow):
|
||||
if path:
|
||||
self.dir_edit.setText(path)
|
||||
|
||||
def _on_types_preset_changed(self, preset: str):
|
||||
if preset != "Custom":
|
||||
self.types_edit.setText(FILE_TYPE_PRESETS.get(preset, ""))
|
||||
self.types_edit.setReadOnly(True)
|
||||
else:
|
||||
self.types_edit.setReadOnly(False)
|
||||
self._reload_files()
|
||||
|
||||
def _on_dir_changed(self):
|
||||
path = self.dir_edit.text().strip()
|
||||
if not path or not os.path.isdir(path):
|
||||
@@ -167,10 +194,19 @@ class MainWindow(QMainWindow):
|
||||
self._file_names = []
|
||||
else:
|
||||
self._base_dir = path
|
||||
self._file_names = sorted(
|
||||
f for f in os.listdir(path)
|
||||
if os.path.isfile(os.path.join(path, f)) and not f.startswith(".")
|
||||
)
|
||||
self._reload_files()
|
||||
return
|
||||
self._refresh_preview()
|
||||
|
||||
def _reload_files(self):
|
||||
if not self._base_dir:
|
||||
return
|
||||
extensions = parse_extension_filter(self.types_edit.text())
|
||||
self._file_names = list_files(
|
||||
self._base_dir,
|
||||
recursive=self.recursive_cb.isChecked(),
|
||||
extensions=extensions,
|
||||
)
|
||||
self._refresh_preview()
|
||||
|
||||
def _on_rule_changed(self):
|
||||
@@ -189,9 +225,7 @@ class MainWindow(QMainWindow):
|
||||
|
||||
def _ext_for(self, filename: str) -> str:
|
||||
"""Return file extension with leading dot, or empty string if none."""
|
||||
if "." in filename and not filename.startswith("."):
|
||||
return "." + filename.rsplit(".", 1)[-1].lower()
|
||||
return ""
|
||||
return file_extension(filename)
|
||||
|
||||
def _on_preview_selection_changed(self):
|
||||
"""Show preview (new name) only in selected rows; others show original."""
|
||||
@@ -263,7 +297,8 @@ class MainWindow(QMainWindow):
|
||||
self.preview_status.setText("Warning: some new names are duplicated. Fix rules to avoid overwriting.")
|
||||
self.preview_status.setStyleSheet("color: #c00;")
|
||||
else:
|
||||
self.preview_status.setText(f"{len(preview)} file(s). Ready to rename.")
|
||||
scope = "including subfolders" if self.recursive_cb.isChecked() else "top folder only"
|
||||
self.preview_status.setText(f"{len(preview)} file(s) ({scope}). Ready to rename.")
|
||||
self.preview_status.setStyleSheet("")
|
||||
|
||||
def _apply_renames(self):
|
||||
@@ -317,7 +352,7 @@ class MainWindow(QMainWindow):
|
||||
else:
|
||||
save_undo_log(self._base_dir, renames)
|
||||
QMessageBox.information(self, "Done", f"Renamed {len(results)} file(s).")
|
||||
self._on_dir_changed()
|
||||
self._reload_files()
|
||||
|
||||
def _undo_renames(self):
|
||||
if not self._base_dir:
|
||||
@@ -349,4 +384,4 @@ class MainWindow(QMainWindow):
|
||||
QMessageBox.warning(self, "Undo errors", msg)
|
||||
else:
|
||||
QMessageBox.information(self, "Undone", f"Reverted {len(results)} file(s).")
|
||||
self._on_dir_changed()
|
||||
self._reload_files()
|
||||
|
||||
Reference in New Issue
Block a user