Update engine.py
This commit is contained in:
46
engine.py
46
engine.py
@@ -1,10 +1,7 @@
|
||||
import os
|
||||
import shutil
|
||||
import sqlite3
|
||||
import threading
|
||||
import hashlib
|
||||
from contextlib import contextmanager
|
||||
from functools import lru_cache
|
||||
from PIL import Image
|
||||
from io import BytesIO
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
@@ -285,33 +282,12 @@ class SorterEngine:
|
||||
return fid
|
||||
|
||||
# ==========================================
|
||||
# IMAGE COMPRESSION (OPTIMIZED WITH CACHING)
|
||||
# IMAGE COMPRESSION
|
||||
# ==========================================
|
||||
|
||||
# Simple in-memory cache for thumbnails
|
||||
_thumbnail_cache: Dict[str, bytes] = {}
|
||||
_cache_max_items = 500
|
||||
_cache_lock = threading.Lock()
|
||||
|
||||
@classmethod
|
||||
def _get_cache_key(cls, path: str, quality: int, target_size: Optional[int]) -> str:
|
||||
"""Generate cache key including file modification time."""
|
||||
try:
|
||||
mtime = os.path.getmtime(path)
|
||||
except OSError:
|
||||
mtime = 0
|
||||
return hashlib.md5(f"{path}:{quality}:{target_size}:{mtime}".encode()).hexdigest()
|
||||
|
||||
@classmethod
|
||||
def compress_for_web(cls, path: str, quality: int, target_size: Optional[int] = None) -> Optional[bytes]:
|
||||
"""Loads image, resizes smart, and saves as WebP with caching."""
|
||||
cache_key = cls._get_cache_key(path, quality, target_size)
|
||||
|
||||
# Check cache first
|
||||
with cls._cache_lock:
|
||||
if cache_key in cls._thumbnail_cache:
|
||||
return cls._thumbnail_cache[cache_key]
|
||||
|
||||
"""Loads image, resizes smart, and saves as WebP."""
|
||||
try:
|
||||
with Image.open(path) as img:
|
||||
# Keep RGBA for WebP support, only convert unusual modes
|
||||
@@ -326,19 +302,8 @@ class SorterEngine:
|
||||
|
||||
# Save as WebP
|
||||
buf = BytesIO()
|
||||
img.save(buf, format="WEBP", quality=quality, method=4) # method=4 is faster
|
||||
result = buf.getvalue()
|
||||
|
||||
# Cache the result
|
||||
with cls._cache_lock:
|
||||
if len(cls._thumbnail_cache) >= cls._cache_max_items:
|
||||
# Simple eviction: remove oldest 20%
|
||||
keys_to_remove = list(cls._thumbnail_cache.keys())[:cls._cache_max_items // 5]
|
||||
for k in keys_to_remove:
|
||||
del cls._thumbnail_cache[k]
|
||||
cls._thumbnail_cache[cache_key] = result
|
||||
|
||||
return result
|
||||
img.save(buf, format="WEBP", quality=quality)
|
||||
return buf.getvalue()
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
@@ -350,7 +315,8 @@ class SorterEngine:
|
||||
def process_one(path):
|
||||
return path, SorterEngine.compress_for_web(path, quality)
|
||||
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=8) as executor:
|
||||
# Reduced from 8 to 2 workers to prevent memory issues
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
|
||||
future_to_path = {executor.submit(process_one, p): p for p in image_paths}
|
||||
for future in concurrent.futures.as_completed(future_to_path):
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user