NoteStack-Test / backend /app /services /firecrawl.py
Arslan1997's picture
LETS GOOOOOOOOOOOOOOOOOOOOOOOOO
0c11338
Raw History Blame Contribute Delete
1.39 kB
"""Firecrawl: renders JavaScript sites, so posts on sites without a feed (Next.js, Framer, Webflow) still come in."""
import httpx
from app.config import settings
class FirecrawlError(RuntimeError):
pass
def enabled() -> bool:
return bool(settings.firecrawl_api_key)
def _post(path: str, body: dict, timeout: float = 90) -> dict:
resp = httpx.post(
f"{settings.firecrawl_url.rstrip('/')}{path}",
json=body,
headers={"Authorization": f"Bearer {settings.firecrawl_api_key}"},
timeout=timeout,
)
if resp.status_code >= 400:
raise FirecrawlError(f"Firecrawl returned {resp.status_code}: {resp.text[:200]}")
data = resp.json()
if data.get("success") is False:
raise FirecrawlError(str(data.get("error") or "Firecrawl request failed")[:200])
return data
def map_site(url: str, limit: int) -> list[str]:
"""Every URL Firecrawl can find on the site (sitemap plus links on rendered pages)."""
data = _post("/v2/map", {"url": url, "limit": limit, "sitemap": "include"})
return [link if isinstance(link, str) else link.get("url", "") for link in data.get("links") or []]
def scrape(url: str) -> dict:
"""Main content of one page as markdown, plus page metadata."""
data = _post("/v2/scrape", {"url": url, "formats": ["markdown"], "onlyMainContent": True})
return data.get("data") or {}