import os import re import requests import time from concurrent.futures import ThreadPoolExecutor # Configuration DOCS_DIR = r"..\chrono.docs" CLOUD_CARDS_DIR = r"Cloud\wwwroot\cards" STANDALONE_CARDS_DIR = r"Standalone\wwwroot\cards" USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36" def get_card_names(): card_names = [] # Search for all .md files in chrono.docs for root, dirs, files in os.walk(DOCS_DIR): for file in files: if file.endswith(".md"): # Remove .md extension card_names.append(file[:-3]) return card_names def slugify(card_name): # Manual overrides for slugs that don't follow the standard pattern overrides = { "APEX Starcruise": "apex-starcruiser", "Consummate Conspirator": "consomme-conspirator", "Librarian's Assistant": "librarian-assistant", "Violet Inquisitioner": "violet-inquisitor", "Breakdown": "break-down", "Da'Kad, Heretic Crusher": "da-kad-heretic-crusher", "Shae'Fan, Remembered": "shae-fan-remembered", "Possessed Prawn": "possessed-prawn-card", "P.O.G.O": "p-o-g-o", "A'kon, Starry Diviner": "a-kon-starry-diviner", "B.O.O.F.": "b-o-o-f", "Raiz, Pacifist's Conclusion": "raiz-pacifists-conclusion", "Spirit's Lament": "spirits-lament", "Overmind's Guilt": "overminds-guilt", "Entropy's End": "entropys-end", "Vor’kon, Eternal Source": "vor-kon-eternal-source", "Ta'kan the Tattle": "ta-kan-the-tattle", } # Fix potential encoding issues in card_name (e.g. smart quotes) # If the script is run in an environment where the filenames were read as mangled # we want to ensure we use the correct name for the filename and slug lookup. # Note: '’' is the UTF-8 bytes for '’' interpreted as Windows-1252 if isinstance(card_name, str): card_name = card_name.replace("’", "'").replace("’", "'") if card_name in overrides: return overrides[card_name] # Convert to lowercase, replace spaces/special chars with hyphens slug = card_name.lower() slug = re.sub(r'[^a-z0-9]+', '-', slug) slug = slug.strip('-') return slug def download_full_art(card_name): # Fix potential encoding issues in card_name (e.g. smart quotes) if isinstance(card_name, str): card_name = card_name.replace("’", "'").replace("’", "'") slug = slugify(card_name) url = f"https://www.playchrono.com/card/{slug}" print(f"Processing {card_name} ({url})...") try: response = requests.get(url, headers={"User-Agent": USER_AGENT}, timeout=10) if response.status_code != 200: print(f" [ERROR] Failed to fetch page for {card_name}: {response.status_code}") return # Look for all data-lightbox-src and data-lightbox-alt pairs # We want the ones that end with "Full Art" and match our card name pattern = r'data-lightbox-src="([^"]+)"\s+data-lightbox-alt="([^"]+)"' matches = re.findall(pattern, response.text) full_art_url = None for src, alt in matches: if "Full Art" in alt and (card_name.lower() in alt.lower()): full_art_url = src break # Fallback to the first "Full Art" if no exact match (sometimes names might slightly differ) if not full_art_url: for src, alt in matches: if "Full Art" in alt: full_art_url = src break # Absolute fallback to any data-lightbox-src with the pattern if still nothing if not full_art_url: match = re.search(r'data-lightbox-src="(https://cdn\.playchrono\.com/latest/en_us/img/cards/set/1/[^"]+\.png)"', response.text) if match: full_art_url = match.group(1) if not full_art_url: print(f" [INFO] No full art found for {card_name}") return print(f" [FOUND] {full_art_url} for {card_name}") # Download image img_response = requests.get(full_art_url, headers={"User-Agent": USER_AGENT}, timeout=10) if img_response.status_code == 200: filename = f"fa{card_name}.png" # Save to Cloud os.makedirs(CLOUD_CARDS_DIR, exist_ok=True) with open(os.path.join(CLOUD_CARDS_DIR, filename), "wb") as f: f.write(img_response.content) # Save to Standalone os.makedirs(STANDALONE_CARDS_DIR, exist_ok=True) with open(os.path.join(STANDALONE_CARDS_DIR, filename), "wb") as f: f.write(img_response.content) print(f" [SUCCESS] Saved {filename}") else: print(f" [ERROR] Failed to download image for {card_name}: {img_response.status_code}") except Exception as e: print(f" [ERROR] Exception for {card_name}: {e}") def main(): card_names = get_card_names() print(f"Found {len(card_names)} cards to process.") # Use ThreadPoolExecutor for faster downloads with ThreadPoolExecutor(max_workers=5) as executor: executor.map(download_full_art, card_names) if __name__ == "__main__": main()