fix: add User-Agent header to all Scryfall HTTP requests (required, 400 without it)
This commit is contained in:
1 parent
47dde2c896
commit
2527b7cf98
1 file changed
+16
-4
@@ -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)
|
||||||
|
|||||||
Reference in new issue
Block a user