ef7e4d92a9
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
105 lines
2.9 KiB
Python
105 lines
2.9 KiB
Python
"""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,
|
|
)
|