db: add is_post_in_library / get_saved_post_ids helpers

The pre-template world used find_library_files(post_id) — a
filesystem walk matching files whose stem equals str(post_id) — for
"is this post saved?" checks across the bookmark dot indicator,
browse dot indicator, Unsave menu visibility, etc. With templated
filenames (e.g. 12345_hatsune_miku.jpg) the stem no longer equals
the post id and the dots silently stop lighting up.

Two new helpers, both indexed:
- is_post_in_library(post_id) -> bool   single check, SELECT 1
- get_saved_post_ids() -> set[int]      batch fetch for grid scans

Both go through library_meta which is keyed by post_id, so they're
format-agnostic — they don't care whether the on-disk filename is
12345.jpg, mon3tr_(arknights).jpg, or anything else, as long as the
save flow wrote a meta row. Every save site does this since the
unified save_post_file refactor landed.
This commit is contained in:
pax 2026-04-09 17:29:10 -05:00
parent f0b1fc9052
commit 28348fa9ab

View File

@ -583,6 +583,38 @@ class Database:
datetime.now(timezone.utc).isoformat(), filename), datetime.now(timezone.utc).isoformat(), filename),
) )
def is_post_in_library(self, post_id: int) -> bool:
"""True iff a `library_meta` row exists for `post_id`.
Cheap, indexed lookup. Use this instead of walking the
filesystem when you only need a yes/no for a single post
e.g. the bookmark context-menu's "Unsave from Library"
visibility check, or the bookmarklibrary copy's existence
guard. Replaces digit-stem matching, which can't see
templated filenames.
"""
row = self.conn.execute(
"SELECT 1 FROM library_meta WHERE post_id = ? LIMIT 1",
(post_id,),
).fetchone()
return row is not None
def get_saved_post_ids(self) -> set[int]:
"""Return every post_id that has a library_meta row.
Used for batch saved-locally dot population on grids load
the set once, do per-thumb membership checks against it.
Single SELECT, much cheaper than per-post DB lookups or
per-grid filesystem walks. Format-agnostic: handles both
templated and digit-stem filenames as long as the file's
save flow wrote a meta row (every save site does after the
unified save_post_file refactor).
"""
rows = self.conn.execute(
"SELECT post_id FROM library_meta"
).fetchall()
return {r["post_id"] for r in rows}
def get_library_post_id_by_filename(self, filename: str) -> int | None: def get_library_post_id_by_filename(self, filename: str) -> int | None:
"""Look up which post a saved-library file belongs to, by basename. """Look up which post a saved-library file belongs to, by basename.