diff --git a/booru_viewer/core/api/base.py b/booru_viewer/core/api/base.py index be3b0b4..8a5fa6b 100644 --- a/booru_viewer/core/api/base.py +++ b/booru_viewer/core/api/base.py @@ -25,6 +25,7 @@ class Post: source: str | None width: int = 0 height: int = 0 + created_at: str = "" # YYYY-MM-DD tag_categories: dict[str, list[str]] = field(default_factory=dict) @property @@ -32,6 +33,29 @@ class Post: return self.tags.split() +def _parse_date(raw) -> str: + """Normalize various booru date formats to YYYY-MM-DD.""" + if not raw: + return "" + if isinstance(raw, dict): + raw = raw.get("s", 0) + if isinstance(raw, (int, float)): + from datetime import datetime, timezone + return datetime.fromtimestamp(raw, tz=timezone.utc).strftime("%Y-%m-%d") + s = str(raw) + # ISO 8601 + if len(s) >= 10 and s[4] == '-' and s[7] == '-': + return s[:10] + # Gelbooru style: "Thu Jun 06 08:16:14 -0500 2024" + from datetime import datetime + for fmt in ("%a %b %d %H:%M:%S %z %Y",): + try: + return datetime.strptime(s, fmt).strftime("%Y-%m-%d") + except ValueError: + pass + return "" + + class BooruClient(ABC): """Base class for booru API clients.""" diff --git a/booru_viewer/core/api/danbooru.py b/booru_viewer/core/api/danbooru.py index 9a85a70..d6e15de 100644 --- a/booru_viewer/core/api/danbooru.py +++ b/booru_viewer/core/api/danbooru.py @@ -5,7 +5,7 @@ from __future__ import annotations import logging from ..config import DEFAULT_PAGE_SIZE -from .base import BooruClient, Post +from .base import BooruClient, Post, _parse_date log = logging.getLogger("booru") @@ -54,6 +54,7 @@ class DanbooruClient(BooruClient): source=item.get("source"), width=item.get("image_width", 0), height=item.get("image_height", 0), + created_at=_parse_date(item.get("created_at")), tag_categories=self._extract_tag_categories(item), ) ) @@ -85,6 +86,7 @@ class DanbooruClient(BooruClient): source=item.get("source"), width=item.get("image_width", 0), height=item.get("image_height", 0), + created_at=_parse_date(item.get("created_at")), ) async def autocomplete(self, query: str, limit: int = 10) -> list[str]: diff --git a/booru_viewer/core/api/e621.py b/booru_viewer/core/api/e621.py index c38f6bc..41f8d15 100644 --- a/booru_viewer/core/api/e621.py +++ b/booru_viewer/core/api/e621.py @@ -7,7 +7,7 @@ import logging import httpx from ..config import DEFAULT_PAGE_SIZE, USER_AGENT -from .base import BooruClient, Post +from .base import BooruClient, Post, _parse_date log = logging.getLogger("booru") @@ -74,6 +74,7 @@ class E621Client(BooruClient): source=self._get_source(item), width=self._get_nested(item, "file", "width") or 0, height=self._get_nested(item, "file", "height") or 0, + created_at=_parse_date(item.get("created_at")), tag_categories=self._extract_tag_categories(item), ) ) @@ -107,6 +108,7 @@ class E621Client(BooruClient): source=self._get_source(item), width=self._get_nested(item, "file", "width") or 0, height=self._get_nested(item, "file", "height") or 0, + created_at=_parse_date(item.get("created_at")), ) async def autocomplete(self, query: str, limit: int = 10) -> list[str]: diff --git a/booru_viewer/core/api/gelbooru.py b/booru_viewer/core/api/gelbooru.py index 3795135..f8eb0a9 100644 --- a/booru_viewer/core/api/gelbooru.py +++ b/booru_viewer/core/api/gelbooru.py @@ -5,7 +5,7 @@ from __future__ import annotations import logging from ..config import DEFAULT_PAGE_SIZE -from .base import BooruClient, Post +from .base import BooruClient, Post, _parse_date log = logging.getLogger("booru") @@ -72,6 +72,7 @@ class GelbooruClient(BooruClient): source=item.get("source"), width=item.get("width", 0), height=item.get("height", 0), + created_at=_parse_date(item.get("created_at")), ) ) return posts @@ -116,6 +117,7 @@ class GelbooruClient(BooruClient): source=item.get("source"), width=item.get("width", 0), height=item.get("height", 0), + created_at=_parse_date(item.get("created_at")), ) async def autocomplete(self, query: str, limit: int = 10) -> list[str]: diff --git a/booru_viewer/core/api/moebooru.py b/booru_viewer/core/api/moebooru.py index 98b0be4..ccfdd6f 100644 --- a/booru_viewer/core/api/moebooru.py +++ b/booru_viewer/core/api/moebooru.py @@ -5,7 +5,7 @@ from __future__ import annotations import logging from ..config import DEFAULT_PAGE_SIZE -from .base import BooruClient, Post +from .base import BooruClient, Post, _parse_date log = logging.getLogger("booru") @@ -48,6 +48,7 @@ class MoebooruClient(BooruClient): source=item.get("source"), width=item.get("width", 0), height=item.get("height", 0), + created_at=_parse_date(item.get("created_at")), ) ) return posts @@ -83,6 +84,7 @@ class MoebooruClient(BooruClient): source=item.get("source"), width=item.get("width", 0), height=item.get("height", 0), + created_at=_parse_date(item.get("created_at")), ) async def autocomplete(self, query: str, limit: int = 10) -> list[str]: diff --git a/booru_viewer/gui/app.py b/booru_viewer/gui/app.py index 4e98661..b2b513d 100644 --- a/booru_viewer/gui/app.py +++ b/booru_viewer/gui/app.py @@ -995,6 +995,7 @@ class BooruApp(QMainWindow): post = self._posts[index] self._status.showMessage( f"#{post.id} {post.width}x{post.height} score:{post.score} [{post.rating}] {Path(post.file_url.split('?')[0]).suffix.lstrip('.').upper() if post.file_url else ''}" + + (f" {post.created_at}" if post.created_at else "") ) if self._info_panel.isVisible(): self._info_panel.set_post(post) @@ -1015,7 +1016,8 @@ class BooruApp(QMainWindow): self._prefetch_pause.clear() # pause prefetch try: path = await download_image(post.file_url, progress_callback=_progress) - info = f"#{post.id} {post.width}x{post.height} score:{post.score} [{post.rating}] {Path(post.file_url.split('?')[0]).suffix.lstrip('.').upper() if post.file_url else ''}" + info = (f"#{post.id} {post.width}x{post.height} score:{post.score} [{post.rating}] {Path(post.file_url.split('?')[0]).suffix.lstrip('.').upper() if post.file_url else ''}" + + (f" {post.created_at}" if post.created_at else "")) self._signals.image_done.emit(str(path), info) except Exception as e: log.error(f"Image download failed: {e}") @@ -1217,7 +1219,7 @@ class BooruApp(QMainWindow): source=meta.get("source"), tag_categories=meta.get("tag_categories", {}), ) self._info_panel.set_post(p) - info = f"#{p.id} score:{p.score} [{p.rating}] {Path(path).suffix.lstrip('.').upper()}" + info = f"#{p.id} score:{p.score} [{p.rating}] {Path(path).suffix.lstrip('.').upper()}" + (f" {p.created_at}" if p.created_at else "") self._status.showMessage(info) def _on_library_selected(self, path: str) -> None: