fix: add User-Agent header to all Scryfall HTTP requests (required, 400 without it)

This commit is contained in:
KoalaTrapLord committed 2026-04-01 13:46:08 -04:00
1 parent 47dde2c896
commit 2527b7cf98
1 file changed
+16 -4
+16 -4
View File
@@ -35,16 +35,25 @@ log = logging.getLogger("download_card_images")
SCRYFALL_BULK_API = "https://api.scryfall.com/bulk-data/default-cards" SCRYFALL_BULK_API = "https://api.scryfall.com/bulk-data/default-cards"
DEFAULT_IMAGE_SIZE = "normal" # 488x680px — ideal for Unity card rendering DEFAULT_IMAGE_SIZE = "normal" # 488x680px — ideal for Unity card rendering
DEFAULT_WORKERS = 6 DEFAULT_WORKERS = 6
PLACEHOLDER_URL = "https://cards.scryfall.io/normal/front/0/0/placeholder.jpg"
# Scryfall requires a descriptive User-Agent — returns HTTP 400 without it.
# See: https://scryfall.com/docs/api#making-requests
USER_AGENT = "CommanderAILab/1.0 (https://github.com/KoalaTrapLord/commander-ai-lab)"
PROJECT_ROOT = Path(__file__).parent.parent PROJECT_ROOT = Path(__file__).parent.parent
OUTPUT_DIR = PROJECT_ROOT / "static" / "card-images" OUTPUT_DIR = PROJECT_ROOT / "static" / "card-images"
def _open(url: str, timeout: int = 30):
"""urllib.request.urlopen with required Scryfall User-Agent header."""
req = urllib.request.Request(url, headers={"User-Agent": USER_AGENT})
return urllib.request.urlopen(req, timeout=timeout)
def fetch_bulk_download_url(image_size: str) -> str: def fetch_bulk_download_url(image_size: str) -> str:
"""Get the Scryfall bulk data download URL for default_cards.""" """Get the Scryfall bulk data download URL for default_cards."""
log.info("Fetching Scryfall bulk data manifest...") log.info("Fetching Scryfall bulk data manifest...")
with urllib.request.urlopen(SCRYFALL_BULK_API, timeout=30) as resp: with _open(SCRYFALL_BULK_API, timeout=30) as resp:
meta = json.loads(resp.read()) meta = json.loads(resp.read())
return meta["download_uri"] return meta["download_uri"]
@@ -52,7 +61,7 @@ def fetch_bulk_download_url(image_size: str) -> str:
def load_bulk_data(download_url: str) -> list[dict]: def load_bulk_data(download_url: str) -> list[dict]:
"""Stream-download and parse the bulk JSON (can be ~100MB).""" """Stream-download and parse the bulk JSON (can be ~100MB)."""
log.info("Downloading bulk card data from %s ...", download_url) log.info("Downloading bulk card data from %s ...", download_url)
with urllib.request.urlopen(download_url, timeout=120) as resp: with _open(download_url, timeout=120) as resp:
cards = json.loads(resp.read()) cards = json.loads(resp.read())
log.info("Loaded %d card entries.", len(cards)) log.info("Loaded %d card entries.", len(cards))
return cards return cards
@@ -98,7 +107,10 @@ def get_image_jobs(cards: list[dict], image_size: str, resume: bool) -> list[tup
def download_image(url: str, dest: str) -> tuple[bool, str]: def download_image(url: str, dest: str) -> tuple[bool, str]:
"""Download a single image. Returns (success, dest).""" """Download a single image. Returns (success, dest)."""
try: try:
urllib.request.urlretrieve(url, dest) req = urllib.request.Request(url, headers={"User-Agent": USER_AGENT})
with urllib.request.urlopen(req, timeout=30) as resp:
with open(dest, "wb") as f:
f.write(resp.read())
return True, dest return True, dest
except Exception as exc: except Exception as exc:
log.warning("Failed to download %s -> %s: %s", url, dest, exc) log.warning("Failed to download %s -> %s: %s", url, dest, exc)