This commit is contained in:
Ignacio Serantes
2026-03-31 23:35:57 +02:00
parent ff7c1aa373
commit cb751b2970
14 changed files with 2431 additions and 119 deletions

View File

@@ -29,7 +29,7 @@ if FORCE_X11:
# --- CONFIGURATION ---
PROG_NAME = "Bagheera Image Viewer"
PROG_ID = "bagheeraview"
PROG_VERSION = "0.9.15"
PROG_VERSION = "0.9.16"
PROG_AUTHOR = "Ignacio Serantes"
# --- CACHE SETTINGS ---
@@ -60,13 +60,18 @@ CONFIG_FILE = f"{PROG_ID}rc"
CONFIG_LOCATION = '.config/iserantes'
CONFIG_DIR = os.path.join(os.path.expanduser("~"), CONFIG_LOCATION, PROG_ID)
CONFIG_PATH = os.path.join(CONFIG_DIR, CONFIG_FILE)
CACHE_PATH = os.path.join(CONFIG_DIR, "thumbnails_lmdb")
CACHE_PATH = os.path.join(CONFIG_DIR, "thumbnails")
HISTORY_FILE = "history.json"
HISTORY_PATH = os.path.join(CONFIG_DIR, HISTORY_FILE)
LAYOUTS_DIR = os.path.join(CONFIG_DIR, "layouts") # Layouts saving directory
FAVORITES_FILE = "favorites.json"
FAVORITES_PATH = os.path.join(CONFIG_DIR, FAVORITES_FILE)
FAVORITES_PATH = os.path.join(CONFIG_DIR, FAVORITES_FILE)
DUPLICATE_CACHE_PATH = os.path.join(CONFIG_DIR, "duplicates")
DUPLICATE_HASH_DB_NAME = b"hashes"
DUPLICATE_EXCEPTIONS_DB_NAME = b"exceptions"
DUPLICATE_PENDING_DB_NAME = b"pending"
def save_app_config():
@@ -76,9 +81,8 @@ def save_app_config():
with open(CONFIG_PATH, 'w', encoding='utf-8') as f:
# Use APP_CONFIG global
json.dump(APP_CONFIG, f, indent=4)
except OSError:
# Silently fail for now, but could log this
pass
except Exception as e:
print(f"CRITICAL: Failed to save configuration to {CONFIG_PATH}: {e}")
# --- CONFIGURATION LOADING ---
@@ -133,7 +137,13 @@ SCANNER_SETTINGS_DEFAULTS = {
"scan_full_on_start": True,
"person_tags": "",
"generation_threads": 4,
"search_engine": ""
"search_engine": "",
"duplicate_threshold": 90, # Similarity percentage (50-100)
"duplicate_method": "histogram_hashing",
"duplicate_confirm_delete": True,
"default_delete_to_trash": True,
"duplicate_whitelist": "",
"duplicate_blacklist": ""
}
# --- IMAGE VIEWER DEFAULTS ---
@@ -224,6 +234,16 @@ if HAVE_MEDIAPIPE:
DEFAULT_FACE_ENGINE = AVAILABLE_FACE_ENGINES[0] if AVAILABLE_FACE_ENGINES else None
DEFAULT_PET_ENGINE = AVAILABLE_PET_ENGINES[0] if AVAILABLE_PET_ENGINES else None
HAVE_IMAGEHASH = importlib.util.find_spec("imagehash") is not None
# --- DUPLICATE DETECTION ---
HAVE_DUPLICATE_RESNET_LIBS = all(
importlib.util.find_spec(lib) is not None
for lib in ["torch", "torchvision", "numpy", "sklearn"]
)
MAX_DHASH_DISTANCE = 64 # For 64-bit dHash
DEFAULT_FACE_BOX_COLOR = "#FFFFFF"
# Load preferred engine from config, or use the default.
FACE_DETECTION_ENGINE = APP_CONFIG.get("face_detection_engine",
@@ -379,6 +399,7 @@ _UI_TEXTS = {
"LOAD": "Load",
"SAVE": "Save",
"CREATE": "Create",
"CANCEL": "Cancel",
"RENAME": "Rename",
"COPY": "Copy",
"DELETE": "Delete",
@@ -489,6 +510,58 @@ _UI_TEXTS = {
"MENU_SHOW_LAYOUTS": "Show Layouts",
"MENU_SHOW_HISTORY": "Show History",
"MENU_SETTINGS": "Settings",
"SETTINGS_GROUP_DUPLICATES": "Duplicates",
"MENU_DUPLICATES": "Duplicates",
"MENU_DETECT_CURRENT_SEARCH": "Detect in current search",
"MENU_DETECT_ALL": "Detect all",
"MENU_FORCE_FULL_ANALYSIS": "Force full analysis",
"MENU_REVIEW_IGNORED": "Review ignored",
"MENU_CLEAN_UP_HASHES": "Clean up",
"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 the entire hash database?",
"CONFIRM_CLEAR_HASHES_INFO": "This will remove all calculated image hashes. They will be recalculated as you detect duplicates, which may be slow. This action cannot be undone.",
"SETTINGS_DUPLICATE_METHOD_LABEL": "Method:",
"SETTINGS_DUPLICATE_METHOD_TOOLTIP": "Select the method for duplicate detection.",
"METHOD_HISTOGRAM_HASHING": "Histogram + Hashing",
"METHOD_RESNET": "ResNet (AI Based)",
"SETTINGS_DUPLICATE_CONFIRM_DELETE_LABEL": "Confirm before deleting duplicates",
"SETTINGS_DUPLICATE_WHITELIST_LABEL": "Whitelist (folders to include):",
"SETTINGS_DUPLICATE_WHITELIST_TOOLTIP": "Comma-separated paths of folders to scan when using 'Detect all'.",
"SETTINGS_DUPLICATE_BLACKLIST_LABEL": "Blacklist (folders to exclude):",
"SETTINGS_DUPLICATE_BLACKLIST_TOOLTIP": "Comma-separated paths of folders to ignore during 'Detect all' scans.",
"SETTINGS_DUPLICATE_SCAN_COUNT_LABEL": "Images found for 'Detect all': {}",
"SETTINGS_DEFAULT_DELETE_TO_TRASH_LABEL": "Delete key sends to trash by default",
"SETTINGS_DEFAULT_DELETE_TO_TRASH_TOOLTIP": "If checked, pressing the Delete key will move files to trash. If unchecked, it will permanently delete them.",
"SETTINGS_DUPLICATE_CONFIRM_DELETE_TOOLTIP": "Show a confirmation dialog before moving a duplicate image to the trash.",
"SETTINGS_DUPLICATE_THRESHOLD_LABEL": "Similarity Threshold:",
"SETTINGS_DUPLICATE_THRESHOLD_TOOLTIP": "Set the similarity threshold (50-100%). Higher values mean images must be more similar to be considered duplicates.",
"SETTINGS_DUPLICATE_MISSING_LIBS": "The 'imagehash' library is required for duplicate detection but was not found. This feature is disabled.",
"MENU_DETECT_DUPLICATES": "Detect Duplicates",
"DUPLICATE_DETECTION_TITLE": "Duplicate Detection",
"DUPLICATE_ALREADY_RUNNING": "Duplicate detection is already in progress.",
"DUPLICATE_NO_IMAGES": "No images loaded to detect duplicates.",
"DUPLICATE_STARTING": "Starting duplicate detection...",
"DUPLICATE_PROGRESS": "Duplicate detection: {message} ({current}/{total})",
"DUPLICATE_NONE_FOUND": "No duplicates found.",
"DUPLICATE_FOUND_TITLE": "Duplicates Found",
"DUPLICATE_FOUND_MSG": "The following duplicates were found:\n",
"DUPLICATE_FOUND_MORE": "... and {count} more.",
"DUPLICATE_FINISHED": "Duplicate detection finished.",
"DUPLICATE_MSG_HASHING": "Hashing {filename}",
"DUPLICATE_MSG_ANALYZING": "Analyzing {filename}",
"DUPLICATE_MANAGER_TITLE": "Manage Duplicate Images",
"DUPLICATE_DELETE_LEFT": "Trash Left",
"DUPLICATE_DELETE_RIGHT": "Trash Right",
"CONFIRM_TRASH_TITLE": "Move to Trash",
"CONFIRM_TRASH_TEXT": "Do you want to move this image to the trash?",
"DUPLICATE_KEEP_BOTH": "Keep Both (Ignore)",
"DUPLICATE_SKIP": "Skip",
"DUPLICATE_REMOVE_IGNORED": "Remove from ignored",
"DUPLICATE_INFO_FORMAT": "{size} - {width}x{height}",
"VIEWER_MENU_LINK_PANES": "Link Panes",
"DUPLICATE_OPEN_COMPARISON": "Open Comparison",
"DUPLICATE_LIST_HEADER": "Duplicate Pairs",
"SETTINGS_GROUP_SCANNER": "Scanner",
"SETTINGS_GROUP_AREAS": "Areas",
"SETTINGS_GROUP_THUMBNAILS": "Thumbnails",
@@ -806,6 +879,7 @@ _UI_TEXTS = {
"CONTEXT_MENU_OPEN": "Open",
"CONTEXT_MENU_OPEN_SEARCH_LOCATION": "Open and search location",
"CONTEXT_MENU_OPEN_DEFAULT_APP": "Open location with default application",
"CONTEXT_MENU_OPEN_FULLSCREEN_VIEWER": "Open in Fullscreen Viewer",
"CONTEXT_MENU_MOVE_TO": "Move to...",
"CONTEXT_MENU_COPY_TO": "Copy to...",
"CONTEXT_MENU_ROTATE": "Rotate",
@@ -844,6 +918,7 @@ _UI_TEXTS = {
"LOAD": "Cargar",
"SAVE": "Guardar",
"CREATE": "Crear",
"CANCEL": "Cancelar",
"RENAME": "Renombrar",
"COPY": "Copiar",
"DELETE": "Eliminar",
@@ -954,6 +1029,58 @@ _UI_TEXTS = {
"MENU_SHOW_LAYOUTS": "Mostrar Diseños",
"MENU_SHOW_HISTORY": "Mostrar Historial",
"MENU_SETTINGS": "Opciones",
"SETTINGS_GROUP_DUPLICATES": "Duplicados",
"MENU_DUPLICATES": "Duplicados",
"MENU_DETECT_CURRENT_SEARCH": "Detectar en búsqueda actual",
"MENU_DETECT_ALL": "Detectar todos",
"MENU_FORCE_FULL_ANALYSIS": "Forzar análisis completo",
"MENU_REVIEW_IGNORED": "Revisar ignorados",
"MENU_CLEAN_UP_HASHES": "Limpiar",
"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 toda la base de datos de hashes?",
"CONFIRM_CLEAR_HASHES_INFO": "Esto eliminará todos los hashes de imágenes calculados. Se recalcularán a medida que detectes duplicados, lo que puede ser lento. Esta acción no se puede deshacer.",
"SETTINGS_DUPLICATE_METHOD_LABEL": "Método:",
"SETTINGS_DUPLICATE_METHOD_TOOLTIP": "Selecciona el método para la detección de duplicados.",
"METHOD_HISTOGRAM_HASHING": "Histograma + Hashing",
"METHOD_RESNET": "ResNet (Basado en IA)",
"SETTINGS_DUPLICATE_CONFIRM_DELETE_LABEL": "Confirmar antes de borrar duplicados",
"SETTINGS_DUPLICATE_WHITELIST_LABEL": "Lista blanca (carpetas a incluir):",
"SETTINGS_DUPLICATE_WHITELIST_TOOLTIP": "Rutas de carpetas separadas por comas para escanear al usar 'Detectar todos'.",
"SETTINGS_DUPLICATE_BLACKLIST_LABEL": "Lista negra (carpetas a excluir):",
"SETTINGS_DUPLICATE_BLACKLIST_TOOLTIP": "Rutas de carpetas separadas por comas para ignorar durante escaneos de 'Detectar todos'.",
"SETTINGS_DUPLICATE_SCAN_COUNT_LABEL": "Imágenes encontradas para 'Detectar todos': {}",
"SETTINGS_DEFAULT_DELETE_TO_TRASH_LABEL": "La tecla Supr envía a la papelera por defecto",
"SETTINGS_DEFAULT_DELETE_TO_TRASH_TOOLTIP": "Si está marcada, al pulsar la tecla Supr se moverán los archivos a la papelera. Si no, se eliminarán permanentemente.",
"SETTINGS_DUPLICATE_CONFIRM_DELETE_TOOLTIP": "Muestra un diálogo de confirmación antes de mover una imagen duplicada a la papelera.",
"SETTINGS_DUPLICATE_THRESHOLD_LABEL": "Umbral de Similitud:",
"SETTINGS_DUPLICATE_THRESHOLD_TOOLTIP": "Establece el umbral de similitud (50-100%). Valores más altos significan que las imágenes deben ser más parecidas para considerarse duplicadas.",
"SETTINGS_DUPLICATE_MISSING_LIBS": "La librería 'imagehash' es necesaria para la detección de duplicados pero no se ha encontrado. Esta función está desactivada.",
"MENU_DETECT_DUPLICATES": "Detectar Duplicados",
"DUPLICATE_DETECTION_TITLE": "Detección de Duplicados",
"DUPLICATE_ALREADY_RUNNING": "La detección de duplicados ya está en curso.",
"DUPLICATE_NO_IMAGES": "No hay imágenes cargadas para detectar duplicados.",
"DUPLICATE_STARTING": "Iniciando detección de duplicados...",
"DUPLICATE_PROGRESS": "Detección de duplicados: {message} ({current}/{total})",
"DUPLICATE_NONE_FOUND": "No se encontraron duplicados.",
"DUPLICATE_FOUND_TITLE": "Duplicados Encontrados",
"DUPLICATE_FOUND_MSG": "Se encontraron los siguientes duplicados:\n",
"DUPLICATE_FOUND_MORE": "... y {count} más.",
"DUPLICATE_FINISHED": "Detección de duplicados finalizada.",
"DUPLICATE_MSG_HASHING": "Procesando {filename}",
"DUPLICATE_MSG_ANALYZING": "Analizando {filename}",
"DUPLICATE_MANAGER_TITLE": "Gestionar Imágenes Duplicadas",
"DUPLICATE_DELETE_LEFT": "Papelera Izquierda",
"DUPLICATE_DELETE_RIGHT": "Papelera Derecha",
"CONFIRM_TRASH_TITLE": "Mover a la papelera",
"CONFIRM_TRASH_TEXT": "¿Deseas mover esta imagen a la papelera?",
"DUPLICATE_KEEP_BOTH": "Mantener Ambas (Ignorar)",
"DUPLICATE_SKIP": "Omitir",
"DUPLICATE_REMOVE_IGNORED": "Eliminar de ignorados",
"DUPLICATE_INFO_FORMAT": "{size} - {width}x{height}",
"VIEWER_MENU_LINK_PANES": "Vincular Paneles",
"DUPLICATE_OPEN_COMPARISON": "Abrir Comparación",
"DUPLICATE_LIST_HEADER": "Parejas Duplicadas",
"SETTINGS_GROUP_SCANNER": "Escáner",
"SETTINGS_GROUP_AREAS": "Áreas",
"SETTINGS_GROUP_THUMBNAILS": "Miniaturas",
@@ -1278,6 +1405,7 @@ _UI_TEXTS = {
"CONTEXT_MENU_OPEN": "Abrir",
"CONTEXT_MENU_OPEN_SEARCH_LOCATION": "Abrir y buscar en ubicación",
"CONTEXT_MENU_OPEN_DEFAULT_APP": "Abrir ubicación con aplicación por defecto",
"CONTEXT_MENU_OPEN_FULLSCREEN_VIEWER": "Abrir con Visor a Pantalla Completa",
"CONTEXT_MENU_MOVE_TO": "Mover a...",
"CONTEXT_MENU_COPY_TO": "Copiar a...",
"CONTEXT_MENU_ROTATE": "Girar",
@@ -1317,6 +1445,7 @@ _UI_TEXTS = {
"LOAD": "Cargar",
"SAVE": "Gardar",
"CREATE": "Crear",
"CANCEL": "Cancelar",
"RENAME": "Renomear",
"COPY": "Copiar",
"DELETE": "Eliminar",
@@ -1428,6 +1557,58 @@ _UI_TEXTS = {
"MENU_SHOW_LAYOUTS": "Amosar Deseños",
"MENU_SHOW_HISTORY": "Amosar Historial",
"MENU_SETTINGS": "Opcións",
"SETTINGS_GROUP_DUPLICATES": "Duplicados",
"MENU_DUPLICATES": "Duplicados",
"MENU_DETECT_CURRENT_SEARCH": "Detectar na busca actual",
"MENU_DETECT_ALL": "Detectar todos",
"MENU_FORCE_FULL_ANALYSIS": "Forzar análise completa",
"MENU_REVIEW_IGNORED": "Revisar ignorados",
"MENU_CLEAN_UP_HASHES": "Limpar",
"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 a base de datos de hashes?",
"CONFIRM_CLEAR_HASHES_INFO": "Isto eliminará todos os hashes de imaxes calculados. Rexeneraranse a medida que detectes duplicados, o que pode ser lento. Esta acción non se pode deshacer.",
"SETTINGS_DUPLICATE_METHOD_LABEL": "Método:",
"SETTINGS_DUPLICATE_METHOD_TOOLTIP": "Selecciona o método para a detección de duplicados.",
"METHOD_HISTOGRAM_HASHING": "Histograma + Hashing",
"METHOD_RESNET": "ResNet (Baseado en IA)",
"SETTINGS_DUPLICATE_CONFIRM_DELETE_LABEL": "Confirmar antes de borrar duplicados",
"SETTINGS_DUPLICATE_WHITELIST_LABEL": "Lista branca (cartafoles a incluír):",
"SETTINGS_DUPLICATE_WHITELIST_TOOLTIP": "Rutas de cartafoles separadas por comas para escanear ao usar 'Detectar todos'.",
"SETTINGS_DUPLICATE_BLACKLIST_LABEL": "Lista negra (cartafoles a excluír):",
"SETTINGS_DUPLICATE_BLACKLIST_TOOLTIP": "Rutas de cartafoles separadas por comas para ignorar durante escaneos de 'Detectar todos'.",
"SETTINGS_DUPLICATE_SCAN_COUNT_LABEL": "Imaxes atopadas para 'Detectar todos': {}",
"SETTINGS_DEFAULT_DELETE_TO_TRASH_LABEL": "A tecla Supr envía á papeleira por defecto",
"SETTINGS_DEFAULT_DELETE_TO_TRASH_TOOLTIP": "Se está marcada, ao premer a tecla Supr moveranse os ficheiros á papeleira. Se non, eliminaranse permanentemente.",
"SETTINGS_DUPLICATE_CONFIRM_DELETE_TOOLTIP": "Amosa un diálogo de confirmación antes de mover unha imaxe duplicada á papeleira.",
"SETTINGS_DUPLICATE_THRESHOLD_LABEL": "Umbral de Similitude:",
"SETTINGS_DUPLICATE_THRESHOLD_TOOLTIP": "Establece o umbral de similitude (50-100%). Valores máis altos significan que as imaxes deben ser máis parecidas para considerarse duplicadas.",
"SETTINGS_DUPLICATE_MISSING_LIBS": "A librería 'imagehash' é necesaria para a detección de duplicados pero non se atopou. Esta función está desactivada.",
"MENU_DETECT_DUPLICATES": "Detectar Duplicados",
"DUPLICATE_DETECTION_TITLE": "Detección de Duplicados",
"DUPLICATE_ALREADY_RUNNING": "A detección de duplicados xa está en curso.",
"DUPLICATE_NO_IMAGES": "Non hai imaxes cargadas para detectar duplicados.",
"DUPLICATE_STARTING": "Iniciando detección de duplicados...",
"DUPLICATE_PROGRESS": "Detección de duplicados: {message} ({current}/{total})",
"DUPLICATE_NONE_FOUND": "Non se atoparon duplicados.",
"DUPLICATE_FOUND_TITLE": "Duplicados Atopados",
"DUPLICATE_FOUND_MSG": "Atopáronse os seguintes duplicados:\n",
"DUPLICATE_FOUND_MORE": "... e {count} máis.",
"DUPLICATE_FINISHED": "Detección de duplicados finalizada.",
"DUPLICATE_MSG_HASHING": "Procesando {filename}",
"DUPLICATE_MSG_ANALYZING": "Analizando {filename}",
"DUPLICATE_MANAGER_TITLE": "Xestionar Imaxes Duplicadas",
"DUPLICATE_DELETE_LEFT": "Papeleira Esquerda",
"DUPLICATE_DELETE_RIGHT": "Papeleira Dereita",
"CONFIRM_TRASH_TITLE": "Mover á papeleira",
"CONFIRM_TRASH_TEXT": "Desexas mover esta imaxe á papeleira?",
"DUPLICATE_KEEP_BOTH": "Manter Ambas (Ignorar)",
"DUPLICATE_SKIP": "Omitir",
"DUPLICATE_REMOVE_IGNORED": "Eliminar de ignorados",
"DUPLICATE_INFO_FORMAT": "{size} - {width}x{height}",
"VIEWER_MENU_LINK_PANES": "Vincular Paneis",
"DUPLICATE_OPEN_COMPARISON": "Abrir Comparación",
"DUPLICATE_LIST_HEADER": "Parellas Duplicadas",
"SETTINGS_GROUP_SCANNER": "Escáner",
"SETTINGS_GROUP_AREAS": "Áreas",
"SETTINGS_GROUP_THUMBNAILS": "Miniaturas",
@@ -1762,6 +1943,7 @@ _UI_TEXTS = {
"CONTEXT_MENU_COPY_FILE": "Copiar URL do Ficheiro",
"CONTEXT_MENU_COPY_DIR": "Copiar Ruta do Directorio",
"CONTEXT_MENU_PROPERTIES": "Propiedades",
"CONTEXT_MENU_OPEN_FULLSCREEN_VIEWER": "Abrir con Visor a Pantalla Completa",
"CONTEXT_MENU_NO_APPS_FOUND": "Non se atoparon aplicacións",
"CONTEXT_MENU_REGENERATE": "Rexenerar Miniatura",
"CONTEXT_MENU_ERROR_LISTING_APPS": "Erro listando aplicacións",