#!/usr/bin/env python3 """Fetch comprehensive D&D 5e data from Open5e v1 API and PF2e from GitHub""" import json, time, os, sys, requests from urllib.parse import urljoin OUT_DIR = os.path.join(os.path.dirname(__file__), "src", "assets", "srd") BASE_V1 = "https://api.open5e.com/v1/" def fetch_paginated(endpoint, max_pages=None): """Fetch all pages from an Open5e v1 endpoint.""" results = [] url = urljoin(BASE_V1, f"{endpoint}/?format=json&limit=500") page = 0 while url: page += 1 print(f" Fetching {endpoint} page {page}... ({len(results)} items so far)") resp = requests.get(url, headers={"User-Agent": "TTRPG-Manager/1.0"}, timeout=30) if resp.status_code != 200: print(f" Error: {resp.status_code}") break data = resp.json() results.extend(data.get("results", [])) url = data.get("next") if max_pages and page >= max_pages: break time.sleep(0.3) return results def fetch_all(): os.makedirs(OUT_DIR, exist_ok=True) # Only fetch SRD/OGL content (document__slug = 'wotc-srd' or 'srd') endpoints = { "monsters": "monsters", "spells": "spells", "magicitems": "magicitems", "weapons": "weapons", } for name, endpoint in endpoints.items(): print(f"\n=== Fetching {name} ===") items = fetch_paginated(endpoint) if not items: print(f" No items found for {name}, trying alternate...") continue # Filter to SRD content only srd_items = [] for item in items: doc = item.get("document__slug", "") if doc in ("wotc-srd", "srd", "wotc", "5e-srd", "o5e"): srd_items.append(item) print(f" Total: {len(items)}, SRD: {len(srd_items)}") # Save full file outpath = os.path.join(OUT_DIR, f"{name}-full.json") with open(outpath, "w") as f: json.dump(items, f, indent=2) print(f" Saved {len(items)} to {outpath}") # Save SRD-only file if srd_items and len(srd_items) < len(items): outpath_srd = os.path.join(OUT_DIR, f"{name}-srd.json") with open(outpath_srd, "w") as f: json.dump(srd_items, f, indent=2) print(f" Saved {len(srd_items)} SRD to {outpath_srd}") if __name__ == "__main__": fetch_all()