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
This commit is contained in:
pax 2026-04-05 16:37:12 -05:00
parent ea089075e6
commit 337d5d8087
3 changed files with 60 additions and 2 deletions

View File

@ -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:

View File

@ -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,

View File

@ -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("")