"""Minimal gallery-dl sidecar parsing (one-time filesystem-import aid). No per-platform branching: a small common key set with fallbacks; the full JSON is kept in raw so anything unmapped is recoverable later. """ from dataclasses import dataclass from datetime import UTC, datetime from pathlib import Path @dataclass(frozen=True) class SidecarData: platform: str | None external_post_id: str | None post_url: str | None post_title: str | None description: str | None attachment_count: int | None post_date: datetime | None raw: dict def find_sidecar(media: Path) -> Path | None: for cand in (media.with_suffix(".json"), Path(str(media) + ".json")): if cand.is_file(): return cand return None def _first_str(data: dict, keys: tuple[str, ...]) -> str | None: for k in keys: v = data.get(k) if isinstance(v, str) and v.strip(): return v.strip() return None def _parse_date(v) -> datetime | None: if isinstance(v, bool): return None if isinstance(v, (int, float)): try: return datetime.fromtimestamp(v, tz=UTC) except (OverflowError, OSError, ValueError): return None if isinstance(v, str): s = v.strip() if not s: return None if s.endswith("Z"): s = s[:-1] + "+00:00" try: dt = datetime.fromisoformat(s) except ValueError: return None return dt if dt.tzinfo else dt.replace(tzinfo=UTC) return None def parse_sidecar(data: dict) -> SidecarData: if not isinstance(data, dict): data = {} cat = data.get("category") platform = cat if isinstance(cat, str) and cat.strip() else None external_post_id = None for k in ("id", "post_id", "index", "message_id"): v = data.get(k) if isinstance(v, bool): continue if isinstance(v, (str, int)) and str(v).strip(): external_post_id = str(v) break pc = data.get("page_count") if isinstance(pc, bool): attachment_count = None elif isinstance(pc, int) and pc > 0: attachment_count = pc elif isinstance(data.get("images"), list): attachment_count = len(data["images"]) else: attachment_count = None post_date = None for k in ("published_at", "created_at", "date", "create_date", "timestamp"): if k in data: post_date = _parse_date(data[k]) if post_date is not None: break return SidecarData( platform=platform, external_post_id=external_post_id, post_url=_first_str(data, ("url", "post_url")), post_title=_first_str(data, ("title",)), description=_first_str(data, ("content", "description", "caption")), attachment_count=attachment_count, post_date=post_date, raw=data, )