"""Download and manage PokeDB pokemon sprites.""" from __future__ import annotations import sys import urllib.request from pathlib import Path from typing import Any from .mappings import PokemonMapper def download_sprites( pokemon_mapper: PokemonMapper, encountered_form_ids: set[str], sprites_dir: Path, ) -> dict[str, str]: """Download sprites for all encountered pokemon forms. Returns a mapping of pokemon_form_identifier → local sprite filename. Skips already-downloaded sprites. """ sprites_dir.mkdir(parents=True, exist_ok=True) to_download: list[tuple[str, str, Path]] = [] # (form_id, url, dest) result: dict[str, str] = {} for form_id in sorted(encountered_form_ids): info = pokemon_mapper.lookup(form_id) if info is None: continue pokeapi_id, _ = info sprite_url = pokemon_mapper.get_sprite_url(form_id) if not sprite_url: continue filename = f"{pokeapi_id}.webp" dest = sprites_dir / filename result[form_id] = filename if not dest.exists(): to_download.append((form_id, sprite_url, dest)) if not to_download: print(f" Sprites: {len(result)} already cached") return result print(f" Downloading {len(to_download)} sprites ({len(result) - len(to_download)} cached)...") failed = 0 for i, (form_id, url, dest) in enumerate(to_download, 1): try: urllib.request.urlretrieve(url, dest) except Exception as e: print(f" Warning: Failed to download sprite for {form_id}: {e}", file=sys.stderr) failed += 1 # Remove the failed entry from results result.pop(form_id, None) # Progress every 100 if i % 100 == 0: print(f" {i}/{len(to_download)}...") if failed: print(f" Sprites: {len(result)} downloaded, {failed} failed") else: print(f" Sprites: {len(result)} total ({len(to_download)} new)") return result def download_all_sprites( pokemon_mapper: PokemonMapper, sprites_dir: Path, ) -> set[int]: """Download sprites for all known pokemon (not just encountered ones). Returns a set of pokeapi_ids that have sprites downloaded. """ sprites_dir.mkdir(parents=True, exist_ok=True) to_download: list[tuple[int, str, Path]] = [] have_sprites: set[int] = set() for pokeapi_id, (_ndex, _name) in pokemon_mapper.all_pokemon(): if pokemon_mapper.has_sprite_for_id(pokeapi_id): filename = f"{pokeapi_id}.webp" dest = sprites_dir / filename have_sprites.add(pokeapi_id) if not dest.exists(): # Get the sprite URL via the mapper url = pokemon_mapper.get_sprite_url_for_id(pokeapi_id) if url: to_download.append((pokeapi_id, url, dest)) if not to_download: print(f" All sprites: {len(have_sprites)} already cached") return have_sprites print(f" Downloading {len(to_download)} additional sprites ({len(have_sprites) - len(to_download)} cached)...") failed = 0 for i, (pid, url, dest) in enumerate(to_download, 1): try: urllib.request.urlretrieve(url, dest) except Exception as e: print(f" Warning: Failed to download sprite for pokemon {pid}: {e}", file=sys.stderr) failed += 1 have_sprites.discard(pid) if i % 100 == 0: print(f" {i}/{len(to_download)}...") if failed: print(f" All sprites: {len(have_sprites)} downloaded, {failed} failed") else: print(f" All sprites: {len(have_sprites)} total ({len(to_download)} new)") return have_sprites def sprite_path_for_pokemon(pokeapi_id: int) -> str: """Generate the sprite URL path for use in pokemon.json. Returns an absolute path like "/sprites/25.webp" for the frontend (files in frontend/public/ are served at the root). """ return f"/sprites/{pokeapi_id}.webp"