From 337d5d80875ea4a6199d64ebcfefcec8923420f1 Mon Sep 17 00:00:00 2001 From: pax Date: Sun, 5 Apr 2026 16:37:12 -0500 Subject: [PATCH] Library metadata: store tags on save, search by tag in Library - library_meta table stores tags, score, rating, source per post - Metadata saved automatically when saving from Browse - Search box in Library tab filters by tags via DB lookup - Works with all file types --- booru_viewer/core/db.py | 38 +++++++++++++++++++++++++++++++++++++ booru_viewer/gui/app.py | 8 +++++++- booru_viewer/gui/library.py | 16 +++++++++++++++- 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/booru_viewer/core/db.py b/booru_viewer/core/db.py index 52b9cb8..e456862 100644 --- a/booru_viewer/core/db.py +++ b/booru_viewer/core/db.py @@ -59,6 +59,16 @@ CREATE TABLE IF NOT EXISTS blacklisted_posts ( url TEXT NOT NULL UNIQUE ); +CREATE TABLE IF NOT EXISTS library_meta ( + post_id INTEGER PRIMARY KEY, + tags TEXT NOT NULL DEFAULT '', + score INTEGER DEFAULT 0, + rating TEXT, + source TEXT, + file_url TEXT, + saved_at TEXT +); + CREATE TABLE IF NOT EXISTS settings ( key TEXT PRIMARY KEY, value TEXT NOT NULL @@ -439,6 +449,34 @@ class Database: rows = self.conn.execute("SELECT url FROM blacklisted_posts").fetchall() return {r["url"] for r in rows} + # -- Library Metadata -- + + def save_library_meta(self, post_id: int, tags: str = "", score: int = 0, + rating: str = None, source: str = None, file_url: str = None) -> None: + from datetime import datetime, timezone + self.conn.execute( + "INSERT OR REPLACE INTO library_meta (post_id, tags, score, rating, source, file_url, saved_at) " + "VALUES (?, ?, ?, ?, ?, ?, ?)", + (post_id, tags, score, rating, source, file_url, datetime.now(timezone.utc).isoformat()), + ) + self.conn.commit() + + def get_library_meta(self, post_id: int) -> dict | None: + row = self.conn.execute("SELECT * FROM library_meta WHERE post_id = ?", (post_id,)).fetchone() + return dict(row) if row else None + + def search_library_meta(self, query: str) -> set[int]: + """Search library metadata by tags. Returns matching post IDs.""" + rows = self.conn.execute( + "SELECT post_id FROM library_meta WHERE tags LIKE ?", + (f"%{query}%",), + ).fetchall() + return {r["post_id"] for r in rows} + + def remove_library_meta(self, post_id: int) -> None: + self.conn.execute("DELETE FROM library_meta WHERE post_id = ?", (post_id,)) + self.conn.commit() + # -- Settings -- def get_setting(self, key: str) -> str: diff --git a/booru_viewer/gui/app.py b/booru_viewer/gui/app.py index 9d56efc..fb572cd 100644 --- a/booru_viewer/gui/app.py +++ b/booru_viewer/gui/app.py @@ -360,7 +360,7 @@ class BooruApp(QMainWindow): self._bookmarks_view.bookmark_activated.connect(self._on_bookmark_activated) self._stack.addWidget(self._bookmarks_view) - self._library_view = LibraryView() + self._library_view = LibraryView(db=self._db) self._library_view.file_selected.connect(self._on_library_selected) self._library_view.file_activated.connect(self._on_library_activated) self._stack.addWidget(self._library_view) @@ -1781,6 +1781,12 @@ class BooruApp(QMainWindow): import shutil as _sh _sh.copy2(thumb_src, lib_thumb) + # Store metadata for library search + self._db.save_library_meta( + post_id=post.id, tags=post.tags, score=post.score, + rating=post.rating, source=post.source, file_url=post.file_url, + ) + where = folder or "Unsorted" self._signals.bookmark_done.emit( self._grid.selected_index, diff --git a/booru_viewer/gui/library.py b/booru_viewer/gui/library.py index bc37f44..47b2d7c 100644 --- a/booru_viewer/gui/library.py +++ b/booru_viewer/gui/library.py @@ -15,6 +15,7 @@ from PySide6.QtWidgets import ( QHBoxLayout, QPushButton, QLabel, + QLineEdit, QComboBox, QMenu, QMessageBox, @@ -75,7 +76,11 @@ class LibraryView(QWidget): refresh_btn.clicked.connect(self.refresh) top.addWidget(refresh_btn) - top.addStretch(1) + self._search_input = QLineEdit() + self._search_input.setPlaceholderText("Search tags...") + self._search_input.returnPressed.connect(self.refresh) + top.addWidget(self._search_input, stretch=1) + layout.addLayout(top) # --- Count label --- @@ -107,6 +112,15 @@ class LibraryView(QWidget): self._files = self._scan_files() self._sort_files() + # Filter by tag search if query entered + query = self._search_input.text().strip() + if query and self._db: + matching_ids = self._db.search_library_meta(query) + if matching_ids: + self._files = [f for f in self._files if f.stem.isdigit() and int(f.stem) in matching_ids] + else: + self._files = [] + if self._files: self._count_label.setText(f"{len(self._files)} files") self._count_label.setStyleSheet("")