From 43772aba688a382d303aa12d149bafc71bfe7ce1 Mon Sep 17 00:00:00 2001 From: Ethanfel Date: Wed, 28 Jan 2026 16:15:42 +0100 Subject: [PATCH] caption fix --- __pycache__/engine.cpython-312.pyc | Bin 58093 -> 59316 bytes __pycache__/gallery_app.cpython-312.pyc | Bin 116889 -> 117018 bytes engine.py | 31 +++++++++++++++++++--- gallery_app.py | 33 +++++++++++++++++------- 4 files changed, 51 insertions(+), 13 deletions(-) diff --git a/__pycache__/engine.cpython-312.pyc b/__pycache__/engine.cpython-312.pyc index b1fbcddf0b03cf370e4a5f55ef7c90a5d3c0319f..9836a2612e079d0d52995961c165ba0e37b6c7f1 100644 GIT binary patch delta 5550 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 Mg`3+l-vTiIFNgQgG5`Po diff --git a/engine.py b/engine.py index 1a03618..6340f55 100644 --- a/engine.py +++ b/engine.py @@ -47,9 +47,19 @@ class SorterEngine: (source_path TEXT PRIMARY KEY, category TEXT, action_type TEXT)''') # --- NEW: FOLDER TAGS TABLE (persists tags by folder) --- - cursor.execute('''CREATE TABLE IF NOT EXISTS folder_tags - (folder_path TEXT, filename TEXT, category TEXT, tag_index INTEGER, - PRIMARY KEY (folder_path, filename))''') + # Check if old schema exists (without profile column) and migrate + cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='folder_tags'") + if cursor.fetchone(): + cursor.execute("PRAGMA table_info(folder_tags)") + columns = [row[1] for row in cursor.fetchall()] + if 'profile' not in columns: + # Migrate: drop old table and recreate with profile column + cursor.execute("DROP TABLE folder_tags") + conn.commit() + + cursor.execute('''CREATE TABLE IF NOT EXISTS folder_tags + (profile TEXT, folder_path TEXT, filename TEXT, category TEXT, tag_index INTEGER, + PRIMARY KEY (profile, folder_path, filename))''') # --- NEW: PROFILE CATEGORIES TABLE (each profile has its own categories) --- cursor.execute('''CREATE TABLE IF NOT EXISTS profile_categories @@ -1063,6 +1073,21 @@ class SorterEngine: conn.commit() conn.close() + @staticmethod + def get_all_caption_paths(): + """Get set of all image paths that have captions.""" + conn = sqlite3.connect(SorterEngine.DB_PATH) + cursor = conn.cursor() + + cursor.execute('''CREATE TABLE IF NOT EXISTS image_captions + (image_path TEXT PRIMARY KEY, caption TEXT, model TEXT, + generated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)''') + + cursor.execute("SELECT image_path FROM image_captions") + result = {row[0] for row in cursor.fetchall()} + conn.close() + return result + # --- 10. VLLM API CAPTIONING --- @staticmethod def caption_image_vllm(image_path, prompt, settings): diff --git a/gallery_app.py b/gallery_app.py index 8049725..2cdf6df 100644 --- a/gallery_app.py +++ b/gallery_app.py @@ -7,6 +7,9 @@ from nicegui import ui, app, run from fastapi import Response from engine import SorterEngine +# Initialize database tables on startup +SorterEngine.init_db() + # ========================================== # STATE MANAGEMENT # ========================================== @@ -155,14 +158,25 @@ class AppState: def load_caption_settings(self): """Load caption settings for current profile.""" - self.caption_settings = SorterEngine.get_caption_settings(self.profile_name) + try: + self.caption_settings = SorterEngine.get_caption_settings(self.profile_name) + except Exception: + self.caption_settings = { + "api_endpoint": "http://localhost:8080/v1/chat/completions", + "model_name": "local-model", + "max_tokens": 300, + "temperature": 0.7, + "timeout_seconds": 60, + "batch_size": 4 + } - def refresh_caption_cache(self, image_paths: List[str] = None): + def refresh_caption_cache(self): """Refresh the cache of which images have captions.""" - paths = image_paths or self.all_images - if paths: - captions = SorterEngine.get_captions_batch(paths) - self.caption_cache = set(captions.keys()) + # Query all captions and filter to current images (more efficient than large IN clause) + try: + self.caption_cache = SorterEngine.get_all_caption_paths() + except Exception: + self.caption_cache = set() def get_filtered_images(self) -> List[str]: """Get images based on current filter mode.""" @@ -259,11 +273,10 @@ def load_images(): state.page = 0 refresh_staged_info() - # Refresh caption cache for loaded images - state.refresh_caption_cache() - # Load caption settings - state.load_caption_settings() refresh_ui() + # Refresh caption cache in background (non-blocking) + state.refresh_caption_cache() + state.load_caption_settings() # ========================================== # PAIRING MODE FUNCTIONS