""" scripts/force_replace_silhouettes.py Direct local portrait generator for $uicideboy$, 100k Track, and Aminé. Uses Wikimedia REST endpoints and guaranteed local binary writing. Zero network dependencies on blocked CDNs. """ import sqlite3 import base64 import requests import re from pathlib import Path BASE_DIR = Path(__file__).resolve().parent.parent DB_PATH = BASE_DIR / "data" / "hiffi_catalog.db" AVATAR_DIR = BASE_DIR / "data" / "artist_avatars" AVATAR_DIR.mkdir(parents=True, exist_ok=True) # Wikimedia REST API lead image endpoints (official, completely unblocked) TARGET_CONFIG = { "$uicideboy$": { "wiki_title": "Suicideboys", "fallback_deezer_id": "8482488" }, "100k Track": { "wiki_title": "100k_Track", "fallback_deezer_id": "131441852" }, "Aminé": { "wiki_title": "Aminé_(rapper)", "fallback_deezer_id": "10486256" } } HEADERS = { "User-Agent": "HIFFIArtistApp/1.0 (contact@hiffi.ai)", "Accept": "application/json,image/*" } def clean_file_slug(name: str) -> str: cleaned = re.sub(r'[^a-zA-Z0-9_-]', '_', name.strip().lower()) return re.sub(r'_+', '_', cleaned).strip('_') def get_verified_image_bytes(artist_name: str, cfg: dict): """Fetches real image bytes using unblocked Wikipedia REST API or Deezer direct entity.""" # 1. Try Wikipedia REST summary API title = cfg["wiki_title"] wiki_url = f"https://en.wikipedia.org/api/rest_v1/page/summary/{title}" try: r = requests.get(wiki_url, headers=HEADERS, timeout=10) if r.status_code == 200: data = r.json() if "thumbnail" in data and "source" in data["thumbnail"]: img_url = data["thumbnail"]["source"] img_res = requests.get(img_url, headers=HEADERS, timeout=10) if img_res.status_code == 200 and len(img_res.content) > 3000: return img_res.content, img_url except Exception: pass # 2. Try Deezer public API entity endpoint deezer_id = cfg["fallback_deezer_id"] dz_url = f"https://api.deezer.com/artist/{deezer_id}" try: r = requests.get(dz_url, headers=HEADERS, timeout=10) if r.status_code == 200: data = r.json() img_url = data.get("picture_xl") or data.get("picture_big") or data.get("picture_medium") if img_url and "artist//500x500" not in img_url: img_res = requests.get(img_url, headers=HEADERS, timeout=10) if img_res.status_code == 200 and len(img_res.content) > 3000: return img_res.content, img_url except Exception: pass return None, None def main(): if not DB_PATH.exists(): print(f"[-] Database not found at {DB_PATH}") return conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() print("[*] Fetching verified unblocked face portraits...\n") for artist_name, cfg in TARGET_CONFIG.items(): cursor.execute("SELECT artist_id FROM artists WHERE artist_name = ? COLLATE NOCASE", (artist_name,)) row = cursor.fetchone() artist_id = str(row[0]) if row else "0" slug = clean_file_slug(artist_name) target_file = AVATAR_DIR / f"{slug}_{artist_id}.jpg" print(f" [>] Processing {artist_name} (ID: {artist_id})...", flush=True) img_bytes, img_url = get_verified_image_bytes(artist_name, cfg) if img_bytes: with open(target_file, "wb") as f: f.write(img_bytes) size_kb = len(img_bytes) / 1024 print(f" [+] Successfully written: {target_file.name} ({size_kb:.1f} KB)", flush=True) cursor.execute( "UPDATE artists SET image_url = ?, image_verified_flag = 1 WHERE artist_id = ?", (img_url, artist_id) ) conn.commit() else: print(f" [-] Failed to retrieve image for {artist_name}", flush=True) conn.close() print("\n[+] Direct write complete.") if __name__ == "__main__": main()