feat: add cached thumb quaity and resolution settings (#1101)

This commit is contained in:
Travis Abendshien
2025-09-06 13:31:49 -07:00
committed by GitHub
parent 7a8d34e190
commit fff967617b
4 changed files with 41 additions and 12 deletions

View File

@@ -24,6 +24,10 @@ DEFAULT_GLOBAL_SETTINGS_PATH = (
DEFAULT_THUMB_CACHE_SIZE = 500 # Number in MiB
MIN_THUMB_CACHE_SIZE = 10 # Number in MiB
# See: https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#webp-saving
DEFAULT_CACHED_IMAGE_QUALITY = 80
DEFAULT_CACHED_IMAGE_RES = 256
class Theme(IntEnum):
DARK = 0
@@ -56,6 +60,8 @@ class GlobalSettings(BaseModel):
open_last_loaded_on_startup: bool = Field(default=True)
generate_thumbs: bool = Field(default=True)
thumb_cache_size: float = Field(default=DEFAULT_THUMB_CACHE_SIZE)
cached_thumb_quality: int = Field(default=DEFAULT_CACHED_IMAGE_QUALITY)
cached_thumb_resolution: int = Field(default=DEFAULT_CACHED_IMAGE_RES)
autoplay: bool = Field(default=True)
loop: bool = Field(default=True)
show_filenames_in_grid: bool = Field(default=True)

View File

@@ -12,7 +12,7 @@ import structlog
from PIL import Image
from tagstudio.core.constants import THUMB_CACHE_NAME, TS_FOLDER_NAME
from tagstudio.core.global_settings import DEFAULT_THUMB_CACHE_SIZE
from tagstudio.core.global_settings import DEFAULT_CACHED_IMAGE_QUALITY, DEFAULT_THUMB_CACHE_SIZE
logger = structlog.get_logger(__name__)
@@ -24,19 +24,21 @@ class CacheFolder:
class CacheManager:
MAX_FOLDER_SIZE = 10 # Number in MiB
MAX_FOLDER_SIZE = 10 # Absolute maximum size of a folder, number in MiB
STAT_MULTIPLIER = 1_000_000 # Multiplier to apply to file stats (bytes) to get user units (MiB)
def __init__(
self,
library_dir: Path,
max_size: int | float = DEFAULT_THUMB_CACHE_SIZE,
img_quality: int = DEFAULT_CACHED_IMAGE_QUALITY,
):
"""A class for managing frontend caches, such as for file thumbnails.
Args:
library_dir(Path): The path of the folder containing the .TagStudio library folder.
max_size: (int | float) The maximum size of the cache, in MiB.
img_quality: (int) The image quality value to save PIL images (0-100, default=80)
"""
self._lock = RLock()
self.cache_path = library_dir / TS_FOLDER_NAME / THUMB_CACHE_NAME
@@ -44,6 +46,9 @@ class CacheManager:
math.floor(max_size * CacheManager.STAT_MULTIPLIER),
math.floor(CacheManager.MAX_FOLDER_SIZE * CacheManager.STAT_MULTIPLIER),
)
self.img_quality = (
img_quality if img_quality >= 0 and img_quality <= 100 else DEFAULT_CACHED_IMAGE_QUALITY
)
self.folders: list[CacheFolder] = []
self.current_size = 0
@@ -127,12 +132,20 @@ class CacheManager:
with self._lock as _lock:
cache_folder: CacheFolder = self._get_current_folder()
file_path = cache_folder.path / file_name
image.save(file_path, mode=mode)
try:
image.save(file_path, mode=mode, quality=self.img_quality)
size = file_path.stat().st_size
cache_folder.size += size
self.current_size += size
self._cull_folders()
size = file_path.stat().st_size
cache_folder.size += size
self.current_size += size
self._cull_folders()
except FileNotFoundError:
logger.warn(
"[CacheManager] Failed to save cached image, was the folder deleted on disk?",
folder=file_path,
)
if not cache_folder.path.exists():
self.folders.remove(cache_folder)
def _create_folder(self) -> CacheFolder:
with self._lock as _lock:

View File

@@ -1697,7 +1697,11 @@ class QtDriver(DriverMixin, QObject):
open_status = LibraryStatus(
success=False, library_path=path, message=type(e).__name__, msg_description=str(e)
)
self.cache_manager = CacheManager(path, max_size=self.settings.thumb_cache_size)
self.cache_manager = CacheManager(
path,
max_size=self.settings.thumb_cache_size,
img_quality=self.settings.cached_thumb_quality,
)
logger.info(
f"[Config] Thumbnail Cache Size: {format_size(self.settings.thumb_cache_size)}",
)

View File

@@ -55,6 +55,7 @@ from tagstudio.core.constants import (
FONT_SAMPLE_TEXT,
)
from tagstudio.core.exceptions import NoRendererError
from tagstudio.core.global_settings import DEFAULT_CACHED_IMAGE_RES
from tagstudio.core.library.ignore import Ignore
from tagstudio.core.media_types import MediaCategories, MediaType
from tagstudio.core.palette import UI_COLORS, ColorType, UiColor, get_ui_color
@@ -94,9 +95,7 @@ class ThumbRenderer(QObject):
rm: ResourceManager = ResourceManager()
updated = Signal(float, QPixmap, QSize, Path)
updated_ratio = Signal(float)
cached_img_res: int = 256 # TODO: Pull this from config
cached_img_ext: str = ".webp" # TODO: Pull this from config
cached_img_ext: str = ".webp"
def __init__(self, driver: "QtDriver", library: "Library") -> None:
"""Initialize the class."""
@@ -104,6 +103,13 @@ class ThumbRenderer(QObject):
self.driver = driver
self.lib = library
settings_res = self.driver.settings.cached_thumb_resolution
self.cached_img_res = (
settings_res
if settings_res >= 16 and settings_res <= 2048
else DEFAULT_CACHED_IMAGE_RES
)
# Cached thumbnail elements.
# Key: Size + Pixel Ratio Tuple + Radius Scale
# (Ex. (512, 512, 1.25, 4))
@@ -1404,7 +1410,7 @@ class ThumbRenderer(QObject):
image = self._render(
timestamp,
filepath,
(ThumbRenderer.cached_img_res, ThumbRenderer.cached_img_res),
(self.cached_img_res, self.cached_img_res),
1,
is_grid_thumb,
save_to_file=file_name,