gelbooru: implement _post_view_url + _tag_api_url + prefetch wiring

Overrides both URL methods from the base class:
  _post_view_url(post) -> /index.php?page=post&s=view&id={id}
    Universal HTML scrape path — works on Gelbooru proper, Rule34,
    Safebooru.org without auth.
  _tag_api_url() -> {base_url}/index.php
    Batch tag DAPI fast path. The CategoryFetcher's probe-and-cache
    determines at runtime whether the endpoint actually honors
    names=. Gelbooru proper: probe succeeds. Rule34: probe fails
    (garbage response), falls back to HTML. Safebooru.org: no auth,
    dispatch skips batch entirely.

search() and get_post() now call
    await self.category_fetcher.prefetch_batch(posts)
after building the post list, when a fetcher is attached. The
prefetch is fire-and-forget — search returns immediately and the
background tasks fill categories as the user reads. When no
fetcher is attached (Test Connection dialog, scripts), this is a
no-op and behavior is unchanged.
This commit is contained in:
pax 2026-04-09 19:15:02 -05:00
parent 5ba0441be7
commit 7f897df4b2

View File

@ -13,6 +13,12 @@ log = logging.getLogger("booru")
class GelbooruClient(BooruClient): class GelbooruClient(BooruClient):
api_type = "gelbooru" api_type = "gelbooru"
def _post_view_url(self, post: Post) -> str:
return f"{self.base_url}/index.php?page=post&s=view&id={post.id}"
def _tag_api_url(self) -> str:
return f"{self.base_url}/index.php"
async def search( async def search(
self, tags: str = "", page: int = 1, limit: int = DEFAULT_PAGE_SIZE self, tags: str = "", page: int = 1, limit: int = DEFAULT_PAGE_SIZE
) -> list[Post]: ) -> list[Post]:
@ -75,6 +81,8 @@ class GelbooruClient(BooruClient):
created_at=_parse_date(item.get("created_at")), created_at=_parse_date(item.get("created_at")),
) )
) )
if self.category_fetcher is not None:
await self.category_fetcher.prefetch_batch(posts)
return posts return posts
@staticmethod @staticmethod
@ -107,7 +115,7 @@ class GelbooruClient(BooruClient):
file_url = item.get("file_url", "") file_url = item.get("file_url", "")
if not file_url: if not file_url:
return None return None
return Post( post = Post(
id=item["id"], id=item["id"],
file_url=file_url, file_url=file_url,
preview_url=item.get("preview_url"), preview_url=item.get("preview_url"),
@ -119,6 +127,9 @@ class GelbooruClient(BooruClient):
height=item.get("height", 0), height=item.get("height", 0),
created_at=_parse_date(item.get("created_at")), created_at=_parse_date(item.get("created_at")),
) )
if self.category_fetcher is not None:
await self.category_fetcher.prefetch_batch([post])
return post
async def autocomplete(self, query: str, limit: int = 10) -> list[str]: async def autocomplete(self, query: str, limit: int = 10) -> list[str]:
try: try: