This commit is contained in:
Ignacio Serantes
2026-05-03 13:31:48 +02:00
parent 28b120c9e9
commit 8025bef8d3
6 changed files with 654 additions and 227 deletions

View File

@@ -14,7 +14,7 @@ Classes:
MainWindow: The main application window containing the thumbnail grid and docks.
"""
__appname__ = "BagheeraView"
__version__ = "0.9.25"
__version__ = "0.9.26"
__author__ = "Ignacio Serantes"
__email__ = "kde@aynoa.net"
__license__ = "LGPL"
@@ -1768,13 +1768,18 @@ class MainWindow(QMainWindow):
UITexts.MENU_DETECT_CURRENT_SEARCH)
detect_current_action.triggered.connect(self.start_duplicate_detection)
detect_all_action = duplicates_menu.addAction(UITexts.MENU_DETECT_ALL)
detect_all_action.triggered.connect(self.detect_all_duplicates)
force_full_action = duplicates_menu.addAction(UITexts.MENU_FORCE_FULL_ANALYSIS)
force_full_action.triggered.connect(
lambda: self.start_duplicate_detection(force_full=True))
detect_all_action = duplicates_menu.addAction(UITexts.MENU_DETECT_ALL)
detect_all_action.triggered.connect(self.detect_all_duplicates)
force_full_all_action = duplicates_menu.addAction(
UITexts.MENU_FORCE_FULL_ALL_ANALYSIS)
force_full_all_action.triggered.connect(
lambda: self.detect_all_duplicates(force_full=True))
review_ignored_action = duplicates_menu.addAction(UITexts.MENU_REVIEW_IGNORED)
review_ignored_action.triggered.connect(self.review_ignored_duplicates)
@@ -1784,6 +1789,13 @@ class MainWindow(QMainWindow):
QIcon.fromTheme("edit-clear-all"), UITexts.MENU_CLEAN_UP_HASHES)
clean_hashes_action.triggered.connect(self.clean_duplicate_hashes)
repair_index_action = duplicates_menu.addAction(UITexts.MENU_REPAIR_DATABASE)
repair_index_action.triggered.connect(self.repair_duplicate_index)
clear_exceptions_action = duplicates_menu.addAction(
UITexts.MENU_CLEAR_EXCEPTIONS)
clear_exceptions_action.triggered.connect(self.clear_ignored_duplicates)
if self.duplicate_cache:
count, size_bytes = self.duplicate_cache.get_hash_stats()
size_mb = size_bytes / (1024 * 1024)
@@ -1828,7 +1840,7 @@ class MainWindow(QMainWindow):
menu.exec(self.menu_btn.mapToGlobal(QPoint(0, self.menu_btn.height())))
def detect_all_duplicates(self):
def detect_all_duplicates(self, force_full=False):
"""Gathers files from whitelist (respecting blacklist) and runs detector."""
QApplication.setOverrideCursor(Qt.WaitCursor)
try:
@@ -1849,7 +1861,7 @@ class MainWindow(QMainWindow):
# By default, we use optimized (incremental) mode to avoid repeating
# comparisons.
self.start_duplicate_detection(force_full=False, custom_paths=paths)
self.start_duplicate_detection(force_full=force_full, custom_paths=paths)
def _gather_files_for_duplicates(self):
"""Helper to collect image paths based on whitelist and blacklist settings."""
@@ -1892,6 +1904,40 @@ class MainWindow(QMainWindow):
count = self.duplicate_cache.clean_stale_hashes()
self.status_lbl.setText(f"Cleaned up {count} stale hash entries.")
def repair_duplicate_index(self):
"""Regenerates the BK-Tree and reverse index."""
if not self.duplicate_cache:
return
if self.duplicate_detector and self.duplicate_detector.isRunning():
QMessageBox.information(self, UITexts.DUPLICATE_DETECTION_TITLE,
UITexts.DUPLICATE_ALREADY_RUNNING)
return
self.status_lbl.setText(UITexts.REPAIRING_DATABASE)
QApplication.setOverrideCursor(Qt.WaitCursor)
try:
self.duplicate_cache.regenerate_bktree()
self.status_lbl.setText(UITexts.READY)
finally:
QApplication.restoreOverrideCursor()
def clear_ignored_duplicates(self):
"""Clears the ignored pairs database after user confirmation."""
if not self.duplicate_cache:
return
confirm = QMessageBox(self)
confirm.setIcon(QMessageBox.Question)
confirm.setWindowTitle(UITexts.CONFIRM_CLEAR_EXCEPTIONS_TITLE)
confirm.setText(UITexts.CONFIRM_CLEAR_EXCEPTIONS_TEXT)
confirm.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
confirm.setDefaultButton(QMessageBox.No)
if confirm.exec() == QMessageBox.Yes:
self.duplicate_cache.clear_exceptions()
self.status_lbl.setText(UITexts.READY)
def clear_duplicate_hashes(self):
if not self.duplicate_cache:
return

View File

@@ -29,7 +29,7 @@ if FORCE_X11:
# --- CONFIGURATION ---
PROG_NAME = "Bagheera Image Viewer"
PROG_ID = "bagheeraview"
PROG_VERSION = "0.9.25"
PROG_VERSION = "0.9.26"
PROG_AUTHOR = "Ignacio Serantes"
# --- CACHE SETTINGS ---
@@ -75,6 +75,8 @@ DUPLICATE_CACHE_PATH = os.path.join(APP_DATA_DIR, "duplicates")
DUPLICATE_HASH_DB_NAME = b"hashes"
DUPLICATE_EXCEPTIONS_DB_NAME = b"exceptions"
DUPLICATE_PENDING_DB_NAME = b"pending"
DUPLICATE_BKTREE_DB_NAME = b"bktree"
DUPLICATE_HASH_TO_FILES_DB_NAME = b"hash_to_files"
def save_app_config():
@@ -523,9 +525,16 @@ _UI_TEXTS = {
"MENU_DUPLICATES": "Duplicates",
"MENU_DETECT_CURRENT_SEARCH": "Detect in current search",
"MENU_DETECT_ALL": "Detect all",
"MENU_FORCE_FULL_ALL_ANALYSIS": "Force full all analysis",
"MENU_FORCE_FULL_ANALYSIS": "Force full analysis",
"MENU_REVIEW_IGNORED": "Review ignored",
"MENU_CLEAN_UP_HASHES": "Clean up",
"MENU_REPAIR_DATABASE": "Repair index",
"MENU_CLEAR_EXCEPTIONS": "Clear ignored pairs",
"CONFIRM_CLEAR_EXCEPTIONS_TITLE": "Confirm Clear Ignored Pairs",
"CONFIRM_CLEAR_EXCEPTIONS_TEXT": "Are you sure you want to clear all "
"ignored duplicate pairs? They will be detected again in the next scan.",
"REPAIRING_DATABASE": "Repairing duplicate index...",
"MENU_CLEAR_HASHES": "Clear hashes ({} items, {:.1f} MB on disk)",
"CONFIRM_CLEAR_HASHES_TITLE": "Confirm Clear Hashes",
"CONFIRM_CLEAR_HASHES_TEXT": "Are you sure you want to permanently delete "
@@ -786,7 +795,8 @@ _UI_TEXTS = {
"RENAME_ERROR_EXISTS": "File '{}' already exists.",
"FILE_RENAMED": "File renamed to {}",
"ERROR_RENAME": "Could not rename file: {}",
"ERROR_JPEG_METADATA_LIMIT": "Metadata size limit exceeded for '{}'. This JPEG file has too much existing metadata (XMP) to save more.",
"ERROR_JPEG_METADATA_LIMIT": "Metadata size limit exceeded for '{}'. This "
"JPEG file has too much existing metadata (XMP) to save more.",
"MAIN_DOCK_TITLE": "",
"LAYOUTS_TAB": "Layouts",
"LAYOUTS_TABLE_HEADER": ["Name", "Last Modified"],
@@ -1067,9 +1077,16 @@ _UI_TEXTS = {
"MENU_DUPLICATES": "Duplicados",
"MENU_DETECT_CURRENT_SEARCH": "Detectar en búsqueda actual",
"MENU_DETECT_ALL": "Detectar todos",
"MENU_FORCE_FULL_ALL_ANALYSIS": "Forzar análisis completo de todo",
"MENU_FORCE_FULL_ANALYSIS": "Forzar análisis completo",
"MENU_REVIEW_IGNORED": "Revisar ignorados",
"MENU_CLEAN_UP_HASHES": "Limpiar",
"MENU_REPAIR_DATABASE": "Reparar índice",
"MENU_CLEAR_EXCEPTIONS": "Limpiar parejas ignoradas",
"CONFIRM_CLEAR_EXCEPTIONS_TITLE": "Confirmar Limpieza de Ignorados",
"CONFIRM_CLEAR_EXCEPTIONS_TEXT": "¿Seguro que quieres borrar todas las parejas "
"de duplicados ignoradas? Se volverán a detectar en el próximo escaneo.",
"REPAIRING_DATABASE": "Reparando índice de duplicados...",
"MENU_CLEAR_HASHES": "Limpiar hashes ({} ítems, {:.1f} MB en disco)",
"CONFIRM_CLEAR_HASHES_TITLE": "Confirmar Limpieza de Hashes",
"CONFIRM_CLEAR_HASHES_TEXT": "¿Seguro que quieres eliminar permanentemente "
@@ -1340,7 +1357,8 @@ _UI_TEXTS = {
"RENAME_ERROR_EXISTS": "El archivo '{}' ya existe.",
"FILE_RENAMED": "Archivo renombrado a {}",
"ERROR_RENAME": "No se pudo renombrar el archivo: {}",
"ERROR_JPEG_METADATA_LIMIT": "Límite de metadatos excedido para '{}'. Este archivo JPEG ya tiene demasiados metadatos (XMP) para guardar más.",
"ERROR_JPEG_METADATA_LIMIT": "Límite de metadatos excedido para '{}'. Este "
"archivo JPEG ya tiene demasiados metadatos (XMP) para guardar más.",
"MAIN_DOCK_TITLE": "Panel principal",
"LAYOUTS_TAB": "Diseños",
"LAYOUTS_TABLE_HEADER": ["Nombre", "Última Modificación"],
@@ -1625,9 +1643,16 @@ _UI_TEXTS = {
"MENU_DUPLICATES": "Duplicados",
"MENU_DETECT_CURRENT_SEARCH": "Detectar na busca actual",
"MENU_DETECT_ALL": "Detectar todos",
"MENU_FORCE_FULL_ALL_ANALYSIS": "Forzar análise completa de todo",
"MENU_FORCE_FULL_ANALYSIS": "Forzar análise completa",
"MENU_REVIEW_IGNORED": "Revisar ignorados",
"MENU_CLEAN_UP_HASHES": "Limpar",
"MENU_REPAIR_DATABASE": "Reparar índice",
"MENU_CLEAR_EXCEPTIONS": "Limpar parellas ignoradas",
"CONFIRM_CLEAR_EXCEPTIONS_TITLE": "Confirmar Limpeza de Ignorados",
"CONFIRM_CLEAR_EXCEPTIONS_TEXT": "Seguro que queres borrar todas as parellas "
"de duplicados ignoradas? Volveranse detectar no vindeiro escaneo.",
"REPAIRING_DATABASE": "Reparando índice de duplicados...",
"MENU_CLEAR_HASHES": "Limpar hashes ({} elementos, {:.1f} MB en disco)",
"CONFIRM_CLEAR_HASHES_TITLE": "Confirmar Limpeza de Hashes",
"CONFIRM_CLEAR_HASHES_TEXT": "Seguro que queres eliminar permanentemente toda "
@@ -1896,7 +1921,8 @@ _UI_TEXTS = {
"RENAME_ERROR_EXISTS": "O ficheiro '{}' xa existe.",
"FILE_RENAMED": "Ficheiro renomeado a {}",
"ERROR_RENAME": "Non se puido renomear o ficheiro: {}",
"ERROR_JPEG_METADATA_LIMIT": "Límite de metadatos excedido para '{}'. Este ficheiro JPEG xa ten demasiados metadatos (XMP) para gardar máis.",
"ERROR_JPEG_METADATA_LIMIT": "Límite de metadatos excedido para '{}'. Este "
"ficheiro JPEG xa ten demasiados metadatos (XMP) para gardar máis.",
"MAIN_DOCK_TITLE": "Panel principal",
"LAYOUTS_TAB": "Deseños",
"LAYOUTS_TABLE_HEADER": ["Nome", "Última Modificación"],

File diff suppressed because it is too large Load Diff

View File

@@ -20,7 +20,8 @@ except ImportError:
from utils import preserve_mtime
from constants import RATING_XATTR_NAME, XATTR_NAME
from constants import RATING_XATTR_NAME, XATTR_NAME, UITexts
logger = logging.getLogger(__name__)

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "bagheeraview"
version = "0.9.25"
version = "0.9.26"
authors = [
{ name = "Ignacio Serantes" }
]

View File

@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
setup(
name="bagheeraview",
version="0.9.25",
version="0.9.26",
author="Ignacio Serantes",
description="Bagheera Image Viewer - An image viewer for KDE with Baloo in mind",
long_description="A fast image viewer built with PySide6, featuring search and "