139 lines
5.3 KiB
Python
139 lines
5.3 KiB
Python
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()
|