v0.9.26
This commit is contained in:
@@ -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
|
||||
|
||||
34
constants.py
34
constants.py
@@ -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
@@ -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__)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "bagheeraview"
|
||||
version = "0.9.25"
|
||||
version = "0.9.26"
|
||||
authors = [
|
||||
{ name = "Ignacio Serantes" }
|
||||
]
|
||||
|
||||
2
setup.py
2
setup.py
@@ -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 "
|
||||
|
||||
Reference in New Issue
Block a user