Initial commit: Bulk Renamer with AppImage build and Gitea push notes
Made-with: Cursor
This commit is contained in:
@@ -0,0 +1,327 @@
|
||||
"""
|
||||
Rule configuration widgets: each rule type has a small form that updates the Rule model.
|
||||
"""
|
||||
from PyQt6.QtWidgets import (
|
||||
QWidget,
|
||||
QVBoxLayout,
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
QLineEdit,
|
||||
QSpinBox,
|
||||
QComboBox,
|
||||
QCheckBox,
|
||||
QGroupBox,
|
||||
QFormLayout,
|
||||
QStackedWidget,
|
||||
)
|
||||
from PyQt6.QtCore import pyqtSignal
|
||||
|
||||
from engine.rules import (
|
||||
ReplaceRule,
|
||||
InsertRule,
|
||||
RemoveRule,
|
||||
CaseRule,
|
||||
NumberingRule,
|
||||
EpisodeRenumberRule,
|
||||
RegexRule,
|
||||
PrefixSuffixRule,
|
||||
)
|
||||
|
||||
|
||||
class ReplaceRuleWidget(QWidget):
|
||||
ruleChanged = pyqtSignal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
layout = QFormLayout(self)
|
||||
self.enabled_cb = QCheckBox("Use this rule")
|
||||
self.enabled_cb.setChecked(False)
|
||||
self.enabled_cb.toggled.connect(self._emit)
|
||||
layout.addRow(self.enabled_cb)
|
||||
self.find = QLineEdit()
|
||||
self.find.setPlaceholderText("Text to find")
|
||||
self.find.textChanged.connect(self._emit)
|
||||
self.replace = QLineEdit()
|
||||
self.replace.setPlaceholderText("Replace with")
|
||||
self.replace.textChanged.connect(self._emit)
|
||||
self.caseSensitive = QCheckBox("Case sensitive")
|
||||
self.caseSensitive.toggled.connect(self._emit)
|
||||
self.wholeWord = QCheckBox("Whole word only")
|
||||
self.wholeWord.toggled.connect(self._emit)
|
||||
layout.addRow("Find:", self.find)
|
||||
layout.addRow("Replace with:", self.replace)
|
||||
layout.addRow(self.caseSensitive)
|
||||
layout.addRow(self.wholeWord)
|
||||
|
||||
def _emit(self):
|
||||
self.ruleChanged.emit()
|
||||
|
||||
def getRule(self) -> ReplaceRule:
|
||||
r = ReplaceRule(
|
||||
find=self.find.text(),
|
||||
replace=self.replace.text(),
|
||||
case_sensitive=self.caseSensitive.isChecked(),
|
||||
whole_word=self.wholeWord.isChecked(),
|
||||
)
|
||||
r.enabled = self.enabled_cb.isChecked()
|
||||
return r
|
||||
|
||||
|
||||
class RegexRuleWidget(QWidget):
|
||||
ruleChanged = pyqtSignal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
layout = QFormLayout(self)
|
||||
self.enabled_cb = QCheckBox("Use this rule")
|
||||
self.enabled_cb.setChecked(False)
|
||||
self.enabled_cb.toggled.connect(self._emit)
|
||||
layout.addRow(self.enabled_cb)
|
||||
self.pattern = QLineEdit()
|
||||
self.pattern.setPlaceholderText(r"e.g. S(\d+)E(\d+)")
|
||||
self.pattern.textChanged.connect(self._emit)
|
||||
self.replacement = QLineEdit()
|
||||
self.replacement.setPlaceholderText(r"e.g. S\1E\2")
|
||||
self.replacement.textChanged.connect(self._emit)
|
||||
layout.addRow("Regex pattern:", self.pattern)
|
||||
layout.addRow("Replacement:", self.replacement)
|
||||
|
||||
def _emit(self):
|
||||
self.ruleChanged.emit()
|
||||
|
||||
def getRule(self) -> RegexRule:
|
||||
r = RegexRule(pattern=self.pattern.text(), replacement=self.replacement.text())
|
||||
r.enabled = self.enabled_cb.isChecked()
|
||||
return r
|
||||
|
||||
|
||||
class InsertRuleWidget(QWidget):
|
||||
ruleChanged = pyqtSignal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
layout = QFormLayout(self)
|
||||
self.enabled_cb = QCheckBox("Use this rule")
|
||||
self.enabled_cb.setChecked(False)
|
||||
self.enabled_cb.toggled.connect(self._emit)
|
||||
layout.addRow(self.enabled_cb)
|
||||
self.text = QLineEdit()
|
||||
self.text.setPlaceholderText("Text to insert")
|
||||
self.text.textChanged.connect(self._emit)
|
||||
self.position = QComboBox()
|
||||
self.position.addItems(["At start", "At end", "At position..."])
|
||||
self.position.setCurrentIndex(0)
|
||||
self.position.currentIndexChanged.connect(self._emit)
|
||||
self.positionSpin = QSpinBox()
|
||||
self.positionSpin.setMinimum(0)
|
||||
self.positionSpin.setMaximum(9999)
|
||||
self.positionSpin.valueChanged.connect(self._emit)
|
||||
layout.addRow("Text:", self.text)
|
||||
layout.addRow("Position:", self.position)
|
||||
layout.addRow("Index:", self.positionSpin)
|
||||
|
||||
def _emit(self):
|
||||
self.ruleChanged.emit()
|
||||
|
||||
def getRule(self) -> InsertRule:
|
||||
idx = self.position.currentIndex()
|
||||
if idx == 0:
|
||||
pos = 0
|
||||
elif idx == 1:
|
||||
pos = -1
|
||||
else:
|
||||
pos = self.positionSpin.value()
|
||||
r = InsertRule(text=self.text.text(), position=pos)
|
||||
r.enabled = self.enabled_cb.isChecked()
|
||||
return r
|
||||
|
||||
|
||||
class RemoveRuleWidget(QWidget):
|
||||
ruleChanged = pyqtSignal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
layout = QFormLayout(self)
|
||||
self.enabled_cb = QCheckBox("Use this rule")
|
||||
self.enabled_cb.setChecked(False)
|
||||
self.enabled_cb.toggled.connect(self._emit)
|
||||
layout.addRow(self.enabled_cb)
|
||||
self.removeType = QComboBox()
|
||||
self.removeType.addItems([
|
||||
"Remove text",
|
||||
"Remove all digits",
|
||||
"Remove first N characters",
|
||||
"Remove last N characters",
|
||||
])
|
||||
self.removeType.currentIndexChanged.connect(self._emit)
|
||||
self.value = QLineEdit()
|
||||
self.value.setPlaceholderText("Text or number")
|
||||
self.value.textChanged.connect(self._emit)
|
||||
layout.addRow("Type:", self.removeType)
|
||||
layout.addRow("Value:", self.value)
|
||||
|
||||
def _emit(self):
|
||||
self.ruleChanged.emit()
|
||||
|
||||
def getRule(self) -> RemoveRule:
|
||||
idx = self.removeType.currentIndex()
|
||||
types = ["chars", "digits", "first_n", "last_n"]
|
||||
r = RemoveRule(remove_type=types[idx], value=self.value.text())
|
||||
r.enabled = self.enabled_cb.isChecked()
|
||||
return r
|
||||
|
||||
|
||||
class CaseRuleWidget(QWidget):
|
||||
ruleChanged = pyqtSignal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
layout = QFormLayout(self)
|
||||
self.enabled_cb = QCheckBox("Use this rule")
|
||||
self.enabled_cb.setChecked(False)
|
||||
self.enabled_cb.toggled.connect(self._emit)
|
||||
layout.addRow(self.enabled_cb)
|
||||
self.caseType = QComboBox()
|
||||
self.caseType.addItems(["Title Case", "UPPER", "lower", "Sentence case"])
|
||||
self.caseType.currentIndexChanged.connect(self._emit)
|
||||
layout.addRow("Case:", self.caseType)
|
||||
|
||||
def _emit(self):
|
||||
self.ruleChanged.emit()
|
||||
|
||||
def getRule(self) -> CaseRule:
|
||||
types = ["title", "upper", "lower", "sentence"]
|
||||
r = CaseRule(case_type=types[self.caseType.currentIndex()])
|
||||
r.enabled = self.enabled_cb.isChecked()
|
||||
return r
|
||||
|
||||
|
||||
class NumberingRuleWidget(QWidget):
|
||||
ruleChanged = pyqtSignal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
layout = QFormLayout(self)
|
||||
self.enabled_cb = QCheckBox("Use this rule")
|
||||
self.enabled_cb.setChecked(False)
|
||||
self.enabled_cb.toggled.connect(self._emit)
|
||||
layout.addRow(self.enabled_cb)
|
||||
self.start = QSpinBox()
|
||||
self.start.setMinimum(-9999)
|
||||
self.start.setMaximum(99999)
|
||||
self.start.setValue(1)
|
||||
self.start.valueChanged.connect(self._emit)
|
||||
self.step = QSpinBox()
|
||||
self.step.setMinimum(1)
|
||||
self.step.setMaximum(100)
|
||||
self.step.setValue(1)
|
||||
self.step.valueChanged.connect(self._emit)
|
||||
self.padding = QSpinBox()
|
||||
self.padding.setMinimum(1)
|
||||
self.padding.setMaximum(10)
|
||||
self.padding.setValue(2)
|
||||
self.padding.valueChanged.connect(self._emit)
|
||||
self.where = QComboBox()
|
||||
self.where.addItems(["Prefix", "Suffix", "Insert at index"])
|
||||
self.where.currentIndexChanged.connect(self._emit)
|
||||
self.separator = QLineEdit()
|
||||
self.separator.setText(" ")
|
||||
self.separator.textChanged.connect(self._emit)
|
||||
self.insertAt = QSpinBox()
|
||||
self.insertAt.setMinimum(0)
|
||||
self.insertAt.setMaximum(9999)
|
||||
self.insertAt.valueChanged.connect(self._emit)
|
||||
layout.addRow("Start:", self.start)
|
||||
layout.addRow("Step:", self.step)
|
||||
layout.addRow("Padding:", self.padding)
|
||||
layout.addRow("Where:", self.where)
|
||||
layout.addRow("Separator:", self.separator)
|
||||
layout.addRow("Insert at index:", self.insertAt)
|
||||
|
||||
def _emit(self):
|
||||
self.ruleChanged.emit()
|
||||
|
||||
def getRule(self) -> NumberingRule:
|
||||
where_map = ["prefix", "suffix", "insert_at"]
|
||||
r = NumberingRule(
|
||||
start=self.start.value(),
|
||||
step=self.step.value(),
|
||||
padding=self.padding.value(),
|
||||
where=where_map[self.where.currentIndex()],
|
||||
insert_at=self.insertAt.value(),
|
||||
separator=self.separator.text() or " ",
|
||||
)
|
||||
r.enabled = self.enabled_cb.isChecked()
|
||||
return r
|
||||
|
||||
|
||||
class EpisodeRenumberRuleWidget(QWidget):
|
||||
ruleChanged = pyqtSignal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
layout = QFormLayout(self)
|
||||
self.enabled_cb = QCheckBox("Use this rule")
|
||||
self.enabled_cb.setChecked(False)
|
||||
self.enabled_cb.toggled.connect(self._emit)
|
||||
layout.addRow(self.enabled_cb)
|
||||
self.start = QSpinBox()
|
||||
self.start.setMinimum(1)
|
||||
self.start.setMaximum(9999)
|
||||
self.start.setValue(1)
|
||||
self.start.valueChanged.connect(self._emit)
|
||||
self.step = QSpinBox()
|
||||
self.step.setMinimum(1)
|
||||
self.step.setValue(1)
|
||||
self.step.valueChanged.connect(self._emit)
|
||||
self.padding = QSpinBox()
|
||||
self.padding.setMinimum(1)
|
||||
self.padding.setMaximum(3)
|
||||
self.padding.setValue(2)
|
||||
self.padding.valueChanged.connect(self._emit)
|
||||
layout.addRow("First episode number:", self.start)
|
||||
layout.addRow("Step:", self.step)
|
||||
layout.addRow("Zero-pad width:", self.padding)
|
||||
info = QLabel("Matches patterns like S01E05 - Title or Show 1x03 - Title. Episode number is replaced; title is kept.")
|
||||
info.setWordWrap(True)
|
||||
layout.addRow(info)
|
||||
|
||||
def _emit(self):
|
||||
self.ruleChanged.emit()
|
||||
|
||||
def getRule(self) -> EpisodeRenumberRule:
|
||||
r = EpisodeRenumberRule(
|
||||
start=self.start.value(),
|
||||
step=self.step.value(),
|
||||
padding=self.padding.value(),
|
||||
)
|
||||
r.enabled = self.enabled_cb.isChecked()
|
||||
return r
|
||||
|
||||
|
||||
class PrefixSuffixRuleWidget(QWidget):
|
||||
ruleChanged = pyqtSignal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
layout = QFormLayout(self)
|
||||
self.enabled_cb = QCheckBox("Use this rule")
|
||||
self.enabled_cb.setChecked(False)
|
||||
self.enabled_cb.toggled.connect(self._emit)
|
||||
layout.addRow(self.enabled_cb)
|
||||
self.prefix = QLineEdit()
|
||||
self.prefix.setPlaceholderText("Prefix")
|
||||
self.prefix.textChanged.connect(self._emit)
|
||||
self.suffix = QLineEdit()
|
||||
self.suffix.setPlaceholderText("Suffix")
|
||||
self.suffix.textChanged.connect(self._emit)
|
||||
layout.addRow("Prefix:", self.prefix)
|
||||
layout.addRow("Suffix:", self.suffix)
|
||||
|
||||
def _emit(self):
|
||||
self.ruleChanged.emit()
|
||||
|
||||
def getRule(self) -> PrefixSuffixRule:
|
||||
r = PrefixSuffixRule(prefix=self.prefix.text(), suffix=self.suffix.text())
|
||||
r.enabled = self.enabled_cb.isChecked()
|
||||
return r
|
||||
Reference in New Issue
Block a user