This commit is contained in:
Ignacio Serantes
2026-04-08 15:47:29 +02:00
parent bff99226b0
commit 07afab6ca3
10 changed files with 336 additions and 113 deletions

View File

@@ -55,7 +55,7 @@ class DuplicateFileCounter(QThread):
def stop(self):
self._abort = True
self.wait() # Add this line
self.wait()
def run(self):
count = 0
@@ -68,7 +68,8 @@ class DuplicateFileCounter(QThread):
if self._abort:
break
abs_root = os.path.abspath(root)
dirs[:] = [d for d in dirs if os.path.join(abs_root, d) not in self.blacklist]
dirs[:] = [d for d in dirs
if os.path.join(abs_root, d) not in self.blacklist]
if abs_root in self.blacklist:
continue
for f in files:
@@ -422,7 +423,8 @@ class SettingsDialog(QDialog):
method_layout = QHBoxLayout()
method_label = QLabel(UITexts.SETTINGS_DUPLICATE_METHOD_LABEL)
self.duplicate_method_combo = QComboBox()
self.duplicate_method_combo.addItem(UITexts.METHOD_HISTOGRAM_HASHING, "histogram_hashing")
self.duplicate_method_combo.addItem(
UITexts.METHOD_HISTOGRAM_HASHING, "histogram_hashing")
self.duplicate_method_combo.addItem(UITexts.METHOD_RESNET, "resnet")
self.duplicate_method_combo.setEnabled(HAVE_IMAGEHASH)
@@ -437,7 +439,8 @@ class SettingsDialog(QDialog):
method_layout.addWidget(method_label)
method_layout.addWidget(self.duplicate_method_combo)
method_label.setToolTip(UITexts.SETTINGS_DUPLICATE_METHOD_TOOLTIP)
self.duplicate_method_combo.setToolTip(UITexts.SETTINGS_DUPLICATE_METHOD_TOOLTIP)
self.duplicate_method_combo.setToolTip(
UITexts.SETTINGS_DUPLICATE_METHOD_TOOLTIP)
duplicates_layout.addLayout(method_layout)
threshold_layout = QHBoxLayout()
@@ -454,7 +457,8 @@ class SettingsDialog(QDialog):
threshold_layout.addWidget(self.duplicate_threshold_value_label)
threshold_label.setToolTip(UITexts.SETTINGS_DUPLICATE_THRESHOLD_TOOLTIP)
self.duplicate_threshold_slider.setToolTip(UITexts.SETTINGS_DUPLICATE_THRESHOLD_TOOLTIP)
self.duplicate_threshold_slider.setToolTip(
UITexts.SETTINGS_DUPLICATE_THRESHOLD_TOOLTIP)
self.duplicate_threshold_slider.valueChanged.connect(
lambda v: self.duplicate_threshold_value_label.setText(f"{v}%"))
@@ -485,14 +489,16 @@ class SettingsDialog(QDialog):
# Whitelist
wl_cont, self.duplicate_whitelist_list, wl_add, wl_rem = create_path_list_ui(
UITexts.SETTINGS_DUPLICATE_WHITELIST_LABEL, UITexts.SETTINGS_DUPLICATE_WHITELIST_TOOLTIP)
UITexts.SETTINGS_DUPLICATE_WHITELIST_LABEL,
UITexts.SETTINGS_DUPLICATE_WHITELIST_TOOLTIP)
wl_add.clicked.connect(self.add_whitelist_path)
wl_rem.clicked.connect(self.remove_whitelist_path)
duplicates_layout.addWidget(wl_cont)
# Blacklist
bl_cont, self.duplicate_blacklist_list, bl_add, bl_rem = create_path_list_ui(
UITexts.SETTINGS_DUPLICATE_BLACKLIST_LABEL, UITexts.SETTINGS_DUPLICATE_BLACKLIST_TOOLTIP)
UITexts.SETTINGS_DUPLICATE_BLACKLIST_LABEL,
UITexts.SETTINGS_DUPLICATE_BLACKLIST_TOOLTIP)
bl_add.clicked.connect(self.add_blacklist_path)
bl_rem.clicked.connect(self.remove_blacklist_path)
duplicates_layout.addWidget(bl_cont)
@@ -500,7 +506,8 @@ class SettingsDialog(QDialog):
# Image Count Layout
count_layout = QHBoxLayout()
self.duplicate_scan_count_label = QLabel()
self.duplicate_scan_count_label.setStyleSheet("color: #3498db; font-weight: bold;")
self.duplicate_scan_count_label.setStyleSheet(
"color: #3498db; font-weight: bold;")
self.duplicate_scan_progress = QProgressBar()
self.duplicate_scan_progress.setRange(0, 0) # Indeterminate mode
self.duplicate_scan_progress.setFixedHeight(10)
@@ -517,20 +524,27 @@ class SettingsDialog(QDialog):
self.count_update_timer.setInterval(500)
self.count_update_timer.timeout.connect(self.update_duplicate_scan_count)
self.duplicate_whitelist_list.model().rowsInserted.connect(lambda *args: self.count_update_timer.start())
self.duplicate_whitelist_list.model().rowsRemoved.connect(lambda *args: self.count_update_timer.start())
self.duplicate_blacklist_list.model().rowsInserted.connect(lambda *args: self.count_update_timer.start())
self.duplicate_blacklist_list.model().rowsRemoved.connect(lambda *args: self.count_update_timer.start())
self.duplicate_whitelist_list.model().rowsInserted.connect(
lambda *args: self.count_update_timer.start())
self.duplicate_whitelist_list.model().rowsRemoved.connect(
lambda *args: self.count_update_timer.start())
self.duplicate_blacklist_list.model().rowsInserted.connect(
lambda *args: self.count_update_timer.start())
self.duplicate_blacklist_list.model().rowsRemoved.connect(
lambda *args: self.count_update_timer.start())
self.default_delete_to_trash_checkbox = QCheckBox(UITexts.SETTINGS_DEFAULT_DELETE_TO_TRASH_LABEL)
self.default_delete_to_trash_checkbox.setToolTip(UITexts.SETTINGS_DEFAULT_DELETE_TO_TRASH_TOOLTIP)
self.default_delete_to_trash_checkbox = QCheckBox(
UITexts.SETTINGS_DEFAULT_DELETE_TO_TRASH_LABEL)
self.default_delete_to_trash_checkbox.setToolTip(
UITexts.SETTINGS_DEFAULT_DELETE_TO_TRASH_TOOLTIP)
duplicates_layout.addWidget(self.default_delete_to_trash_checkbox)
duplicates_layout.addLayout(threshold_layout)
self.duplicate_confirm_delete_checkbox = QCheckBox(UITexts.SETTINGS_DUPLICATE_CONFIRM_DELETE_LABEL)
self.duplicate_confirm_delete_checkbox.setToolTip(UITexts.SETTINGS_DUPLICATE_CONFIRM_DELETE_TOOLTIP)
self.duplicate_confirm_delete_checkbox = QCheckBox(
UITexts.SETTINGS_DUPLICATE_CONFIRM_DELETE_LABEL)
self.duplicate_confirm_delete_checkbox.setToolTip(
UITexts.SETTINGS_DUPLICATE_CONFIRM_DELETE_TOOLTIP)
duplicates_layout.addWidget(self.duplicate_confirm_delete_checkbox)
duplicates_layout.addStretch()
@@ -945,10 +959,12 @@ class SettingsDialog(QDialog):
duplicate_confirm_delete = APP_CONFIG.get("duplicate_confirm_delete", True)
self.duplicate_confirm_delete_checkbox.setChecked(duplicate_confirm_delete)
duplicate_whitelist = APP_CONFIG.get("duplicate_whitelist", SCANNER_SETTINGS_DEFAULTS["duplicate_whitelist"])
duplicate_whitelist = APP_CONFIG.get(
"duplicate_whitelist", SCANNER_SETTINGS_DEFAULTS["duplicate_whitelist"])
for p in [x.strip() for x in duplicate_whitelist.split(",") if x.strip()]:
self._add_path_to_list(self.duplicate_whitelist_list, p)
duplicate_blacklist = APP_CONFIG.get("duplicate_blacklist", SCANNER_SETTINGS_DEFAULTS["duplicate_blacklist"])
duplicate_blacklist = APP_CONFIG.get(
"duplicate_blacklist", SCANNER_SETTINGS_DEFAULTS["duplicate_blacklist"])
for p in [x.strip() for x in duplicate_blacklist.split(",") if x.strip()]:
self._add_path_to_list(self.duplicate_blacklist_list, p)
@@ -1286,11 +1302,15 @@ class SettingsDialog(QDialog):
APP_CONFIG["viewer_wheel_speed"] = self.viewer_wheel_spin.value()
APP_CONFIG["duplicate_method"] = self.duplicate_method_combo.currentData()
APP_CONFIG["duplicate_threshold"] = self.duplicate_threshold_slider.value()
APP_CONFIG["default_delete_to_trash"] = self.default_delete_to_trash_checkbox.isChecked()
APP_CONFIG["duplicate_confirm_delete"] = self.duplicate_confirm_delete_checkbox.isChecked()
wl_paths = [self.duplicate_whitelist_list.item(i).text() for i in range(self.duplicate_whitelist_list.count())]
APP_CONFIG["default_delete_to_trash"] = \
self.default_delete_to_trash_checkbox.isChecked()
APP_CONFIG["duplicate_confirm_delete"] = \
self.duplicate_confirm_delete_checkbox.isChecked()
wl_paths = [self.duplicate_whitelist_list.item(i).text()
for i in range(self.duplicate_whitelist_list.count())]
APP_CONFIG["duplicate_whitelist"] = ",".join(wl_paths)
bl_paths = [self.duplicate_blacklist_list.item(i).text() for i in range(self.duplicate_blacklist_list.count())]
bl_paths = [self.duplicate_blacklist_list.item(i).text()
for i in range(self.duplicate_blacklist_list.count())]
APP_CONFIG["duplicate_blacklist"] = ",".join(bl_paths)
APP_CONFIG["viewer_auto_resize_window"] = \
@@ -1381,7 +1401,8 @@ class SettingsDialog(QDialog):
item = QListWidgetItem(path)
if not os.path.isdir(path):
item.setForeground(QColor("red"))
item.setToolTip(f"Warning: Path not found or is not a directory: {path}")
item.setToolTip(
UITexts.SETTINGS_PATH_NOT_FOUND_WARNING.format(path))
list_widget.addItem(item)
def add_whitelist_path(self):
@@ -1393,7 +1414,8 @@ class SettingsDialog(QDialog):
def remove_whitelist_path(self):
"""Removes the selected folders from the whitelist list."""
for item in self.duplicate_whitelist_list.selectedItems():
self.duplicate_whitelist_list.takeItem(self.duplicate_whitelist_list.row(item))
self.duplicate_whitelist_list.takeItem(
self.duplicate_whitelist_list.row(item))
def add_blacklist_path(self):
"""Opens a directory dialog to add a folder to the blacklist."""
@@ -1404,10 +1426,12 @@ class SettingsDialog(QDialog):
def remove_blacklist_path(self):
"""Removes the selected folders from the blacklist list."""
for item in self.duplicate_blacklist_list.selectedItems():
self.duplicate_blacklist_list.takeItem(self.duplicate_blacklist_list.row(item))
self.duplicate_blacklist_list.takeItem(
self.duplicate_blacklist_list.row(item))
def update_duplicate_scan_count(self):
"""Calculates and updates the count of images in whitelist/blacklist using a background thread."""
"""Calculates and updates the count of images in whitelist/blacklist
using a background thread."""
if self.counter_thread and self.counter_thread.isRunning():
self.counter_thread.stop()
self.counter_thread.wait()
@@ -1417,17 +1441,23 @@ class SettingsDialog(QDialog):
blacklist_paths = [self.duplicate_blacklist_list.item(i).text()
for i in range(self.duplicate_blacklist_list.count())]
whitelist = [os.path.abspath(os.path.expanduser(p.strip())) for p in whitelist_paths if p.strip()]
blacklist = {os.path.abspath(os.path.expanduser(p.strip())) for p in blacklist_paths if p.strip()}
whitelist = [os.path.abspath(os.path.expanduser(p.strip()))
for p in whitelist_paths if p.strip()]
blacklist = {os.path.abspath(os.path.expanduser(p.strip()))
for p in blacklist_paths if p.strip()}
if not whitelist:
self.duplicate_scan_count_label.setText(UITexts.SETTINGS_DUPLICATE_SCAN_COUNT_LABEL.format(0))
self.duplicate_scan_count_label.setText(
UITexts.SETTINGS_DUPLICATE_SCAN_COUNT_LABEL.format(0))
self.duplicate_scan_progress.hide()
return
self.duplicate_scan_progress.show()
self.counter_thread = DuplicateFileCounter(whitelist, blacklist, IMAGE_EXTENSIONS)
self.counter_thread = DuplicateFileCounter(
whitelist, blacklist, IMAGE_EXTENSIONS)
self.counter_thread.count_updated.connect(
lambda c: self.duplicate_scan_count_label.setText(UITexts.SETTINGS_DUPLICATE_SCAN_COUNT_LABEL.format(c)))
self.counter_thread.finished.connect(lambda: self.duplicate_scan_progress.hide())
lambda c: self.duplicate_scan_count_label.setText(
UITexts.SETTINGS_DUPLICATE_SCAN_COUNT_LABEL.format(c)))
self.counter_thread.finished.connect(
lambda: self.duplicate_scan_progress.hide())
self.counter_thread.start()