54 lines
1.5 KiB
Python
54 lines
1.5 KiB
Python
import asyncio
|
||
from typing import Any, Dict, List, Optional
|
||
|
||
import httpx
|
||
|
||
from rate_limiter import JIKAN_RATE_LIMITER
|
||
|
||
BASE_URL = "https://api.jikan.moe/v4"
|
||
CLIENT = httpx.AsyncClient(timeout=15.0)
|
||
|
||
|
||
async def _get(path: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
||
"""
|
||
Обёртка над GET с ретраями и rate-limit'ом.
|
||
"""
|
||
url = f"{BASE_URL}{path}"
|
||
last_exc: Exception | None = None
|
||
|
||
for i in range(5):
|
||
await JIKAN_RATE_LIMITER.acquire()
|
||
try:
|
||
r = await CLIENT.get(url, params=params)
|
||
r.raise_for_status()
|
||
return r.json()
|
||
except Exception as exc:
|
||
last_exc = exc
|
||
await asyncio.sleep(1 * 2**i)
|
||
|
||
raise RuntimeError(f"Jikan unreachable: {last_exc!r}")
|
||
|
||
|
||
async def search_producer(name: str, limit: int = 1) -> List[Dict[str, Any]]:
|
||
"""
|
||
Поиск студии/продюсера по имени.
|
||
|
||
Возвращает список элементов из data[]:
|
||
[{ "mal_id": int, "name": str, ... }, ...]
|
||
"""
|
||
if not name:
|
||
return []
|
||
|
||
data = await _get("/producers", {"q": name, "limit": limit})
|
||
return data.get("data") or []
|
||
|
||
|
||
async def fetch_producer_full(mal_id: int) -> Optional[Dict[str, Any]]:
|
||
"""
|
||
Полная инфа по producer'у (есть поле about).
|
||
"""
|
||
if not isinstance(mal_id, int):
|
||
return None
|
||
|
||
data = await _get(f"/producers/{mal_id}/full")
|
||
return data.get("data") or None
|