#!/usr/bin/env python3 """ MTGJSON v5 Data Downloader Downloads MTGJSON v5 data files and loads them into the database. Updated to use current MTGJSON API endpoints. Usage: python download_mtgjson_v5.py """ import sys from pathlib import Path # Add the app directory to Python path app_dir = Path(__file__).parent.parent sys.path.insert(0, str(app_dir)) import asyncio import json import gzip import logging from sqlalchemy import create_engine, text from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession import aiohttp from app.services.mtgjson_manager import MTGJSONManager from app.config import get_settings logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # MTGJSON v5 API base URL MTGJSON_API_BASE = "https://mtgjson.com/api/v5" async def download_file(session, url, dest_path): """Download a file from URL.""" logger.info(f"Downloading: {url}") try: async with session.get(url, timeout=aiohttp.ClientTimeout(total=600)) as response: if response.status == 200: dest_path.parent.mkdir(parents=True, exist_ok=True) with open(dest_path, 'wb') as f: async for chunk in response.content.iter_chunked(8192): f.write(chunk) logger.info(f"✓ Downloaded: {dest_path.name}") return True else: logger.error(f"✗ Failed to download {url}: HTTP {response.status}") return False except Exception as e: logger.error(f"✗ Error downloading {url}: {e}") return False def extract_gzip(gz_path, dest_path=None): """Extract a gzip file.""" if not dest_path: dest_path = gz_path.with_suffix('') logger.info(f"Extracting: {gz_path.name}") try: with gzip.open(gz_path, 'rt', encoding='utf-8') as f_in: with open(dest_path, 'w', encoding='utf-8') as f_out: f_out.write(f_in.read()) logger.info(f"✓ Extracted: {gz_path.name}") # Remove gz file after extraction gz_path.unlink() return True except Exception as e: logger.error(f"✗ Failed to extract {gz_path}: {e}") return False async def download_all_files(data_dir): """Download all MTGJSON v5 files.""" logger.info("=" * 60) logger.info("MTGJSON v5 Data Download") logger.info("=" * 60) files = [ "AllPrintings.json.gz", "AllSetFiles.json.gz", "AllIdentifiers.json.gz", "CardTypes.json.gz", "Keywords.json.gz", "MagicRoots.json.gz", "MagicSets.json.gz", "SetTranslations.json.gz" ] async with aiohttp.ClientSession() as session: for filename in files: url = f"{MTGJSON_API_BASE}/{filename}" dest_path = data_dir / filename await download_file(session, url, dest_path) await asyncio.sleep(1) # Be nice to the API async def load_data_to_database(data_dir): """Load downloaded MTGJSON data into PostgreSQL.""" logger.info("=" * 60) logger.info("Loading MTGJSON data into database") logger.info("=" * 60) manager = MTGJSONManager(data_dir) # Download files success = await manager.download_files() if not success: logger.error("✗ Download failed") return False # Unpack files success = manager.unpack_files() if not success: logger.error("✗ Unpack failed") return False # Upsert to database success = manager.upsert_to_database() if success: logger.info("✓ Data loaded successfully") else: logger.error("✗ Database upsert failed") return success async def main(): """Main entry point.""" settings = get_settings() data_dir = Path(settings.DATA_DIR) logger.info(f"Data directory: {data_dir}") # Clear corrupted data if data_dir.exists(): logger.info("Clearing corrupted data...") for f in data_dir.glob("*.json.gz"): f.unlink() logger.info(f" Removed: {f.name}") data_dir.mkdir(parents=True, exist_ok=True) # Download and load data await load_data_to_database(data_dir) if __name__ == "__main__": asyncio.run(main())