Improve stability issues

This commit is contained in:
Ignacio Serantes
2026-04-03 18:41:52 +02:00
parent ae00235db8
commit ca260d4219
4 changed files with 109 additions and 16 deletions

View File

@@ -32,7 +32,7 @@ from PySide6.QtCore import (
QObject, QThread, Signal, QMutex, QReadWriteLock, QSize, QSemaphore, QWaitCondition,
QByteArray, QBuffer, QIODevice, Qt, QTimer, QRunnable, QThreadPool, QFile
)
from PySide6.QtGui import QImage, QImageReader, QImageIOHandler
from PySide6.QtGui import QImage, QImageReader, QImageIOHandler, QIcon
from PySide6.QtWidgets import QApplication
from constants import (
@@ -134,11 +134,11 @@ class ScannerWorker(QRunnable):
sizes_to_check = self.target_sizes if self.target_sizes is not None \
else SCANNER_GENERATE_SIZES
fd = None
try:
if self._is_cancelled:
return
fd = None
# Optimize: Open file once to reuse FD for stat and xattrs
fd = os.open(self.path, os.O_RDONLY)
stat_res = os.fstat(fd)
@@ -196,8 +196,11 @@ class ScannerWorker(QRunnable):
tags, rating = res_meta.tags, res_meta.rating
self.result = (self.path, smallest_thumb_for_signal,
curr_mtime, tags, rating, curr_inode, curr_dev)
except (FileNotFoundError, PermissionError) as e:
logger.debug(f"Skipping {self.path} due to access issue: {e}")
self.result = None
except Exception as e:
logger.error(f"Error processing image {self.path}: {e}")
logger.warning(f"Unexpected error processing image {self.path}: {e}")
self.result = None
finally:
if fd is not None:
@@ -265,7 +268,7 @@ def generate_thumbnail(path, size, fd=None):
# better quality for upscaling.
return img.scaled(size, size, Qt.KeepAspectRatio, Qt.SmoothTransformation)
except Exception as e:
logger.error(f"Error generating thumbnail for {path}: {e}")
logger.debug(f"Could not generate thumbnail for {path}: {e}")
return None
@@ -523,10 +526,19 @@ class ThumbnailCache(QObject):
self._db_lock = QMutex() # Lock specifically for _db_handles access
self._db_handles = {} # Cache for LMDB database handles (dbi)
self._cancel_loading = False
self._broken_cache = {} # (dev, inode, size) -> (mtime, error_msg)
self._cache_bytes_size = 0
self._cache_writer = None
self._cache_loader = None
# Pre-generate broken images for standard tiers in the main thread
self._broken_images = {}
for size in THUMBNAIL_SIZES:
icon = QIcon.fromTheme("image-missing",
QIcon.fromTheme("broken-image",
QIcon.fromTheme("dialog-error")))
self._broken_images[size] = icon.pixmap(size, size).toImage()
self.lmdb_open()
def lmdb_open(self):
@@ -739,6 +751,21 @@ class ThumbnailCache(QObject):
return 256
return 512
def mark_broken(self, path, size, mtime, inode, dev_id, error_msg):
"""Marks a thumbnail load as failed with a message."""
key = (dev_id, struct.pack('Q', inode) if inode is not None else None, size)
with self._write_lock():
self._broken_cache[key] = (mtime, error_msg)
def get_broken_info(self, path, size, mtime, inode, dev_id):
"""Returns the error message if a thumbnail is known to have failed, else None."""
key = (dev_id, struct.pack('Q', inode) if inode is not None else None, size)
with self._read_lock():
info = self._broken_cache.get(key)
if info and info[0] == mtime:
return info[1]
return None
def _resolve_file_identity(self, path, curr_mtime, inode, device_id):
"""Helper to resolve file mtime, device, and inode."""
mtime = curr_mtime
@@ -859,6 +886,11 @@ class ThumbnailCache(QObject):
if mtime is None:
return EMPTY_THUMBNAIL
# Check if known to be broken
broken_msg = self.get_broken_info(path, target_tier, mtime, inode, device_id)
if broken_msg:
return ThumbnailResult(self._broken_images.get(target_tier), mtime, target_tier)
best_img, best_mtime, best_tier = None, 0, 0
with self._read_lock():
@@ -1455,13 +1487,14 @@ class ImageScanner(QThread):
more_files_available = Signal(int, int) # Last loaded index, remainder
def __init__(self, cache, paths, is_file_list=False, viewers=None,
thread_pool_manager=None):
thread_pool_manager=None, target_sizes=None):
# is_file_list is not used
if not paths or not isinstance(paths, (list, tuple)):
logger.warning("ImageScanner initialized with empty or invalid paths")
paths = []
super().__init__()
self.cache = cache
self.target_sizes = target_sizes
self.all_files = []
self.thread_pool_manager = thread_pool_manager
self._viewers = viewers
@@ -1818,7 +1851,7 @@ class ImageScanner(QThread):
return
for f_path, _ in tasks:
r = ScannerWorker(self.cache, f_path, semaphore=sem)
r = ScannerWorker(self.cache, f_path, semaphore=sem, target_sizes=self.target_sizes)
r.setAutoDelete(False)
runnables.append(r)
self._current_workers.append(r)