""" scripts/fix_real_artist_images.py Replaces placeholder star images and broken/stub avatars using: 1. MediaWiki Search & PageImages API (auto-resolves redirects and searches) 2. Deezer Public Artist API (official high-res portraits, picture_xl) """ from pathlib import Path import sys import pandas as pd import requests ROOT_DIR = Path(__file__).resolve().parent.parent if str(ROOT_DIR) not in sys.path: sys.path.insert(0, str(ROOT_DIR)) from core.database import get_connection LASTFM_STAR_HASH = "2a96cbd8b46e442fc41c2b86b821562f" HEADERS = {"User-Agent": "HIFFIArtistIntelligence/1.0 (contact@hiffi.com)"} def get_wikimedia_image(name: str): """Searches Wikipedia and retrieves the top matched artist page's thumbnail.""" url = "https://en.wikipedia.org/w/api.php" params = { "action": "query", "format": "json", "generator": "search", "gsrsearch": f"{name} rapper musician", "gsrlimit": 1, "prop": "pageimages", "piprop": "thumbnail", "pithumbsize": 600 } try: r = requests.get(url, params=params, headers=HEADERS, timeout=8) if r.status_code == 200: pages = r.json().get("query", {}).get("pages", {}) for _, page in pages.items(): thumb = page.get("thumbnail", {}).get("source") if thumb: return thumb except Exception: pass return None def get_deezer_image(name: str): """Queries Deezer API for verified editorial artist portraits.""" url = "https://api.deezer.com/search/artist" params = {"q": name, "limit": 1} try: r = requests.get(url, params=params, timeout=8) if r.status_code == 200: data = r.json().get("data", []) if data: artist = data[0] img = artist.get("picture_xl") or artist.get("picture_big") or artist.get("picture_medium") if img and "artist-default" not in img: return img except Exception: pass return None def main(): print("=" * 65) print("HIFFI INTELLIGENCE: HIGH-RES PORTRAIT ENRICHMENT ENGINE") print("=" * 65) csv_path = ROOT_DIR / "data" / "hiffi_multiplatform_500.csv" if not csv_path.exists(): csv_path = ROOT_DIR / "cleaned_artists_inventory.csv" df_csv = pd.read_csv(csv_path) name_col = "name" if "name" in df_csv.columns else "artist_name" cohort = [str(n).strip() for n in df_csv[name_col].dropna()] conn = get_connection() cur = conn.cursor() placeholders = ",".join(["?"] * len(cohort)) query = f""" SELECT artist_id, artist_name, image_url FROM artists WHERE artist_name IN ({placeholders}) """ cur.execute(query, cohort) rows = cur.fetchall() flagged = [] for artist_id, name, img in rows: # Flag any artist using the Last.fm star placeholder or an empty image if not img or LASTFM_STAR_HASH in img: flagged.append((artist_id, name)) print(f"Targeting {len(flagged)} artists needing verified portraits...\n") resolved = 0 for idx, (artist_id, name) in enumerate(flagged, start=1): # 1. Try Wikimedia action search img_url = get_wikimedia_image(name) source = "Wikimedia" # 2. Fall back to Deezer if Wikimedia has no thumbnail if not img_url: img_url = get_deezer_image(name) source = "Deezer" if img_url: cur.execute(""" UPDATE artists SET image_url = ?, image_verified_flag = 1 WHERE artist_id = ? """, (img_url, artist_id)) resolved += 1 print(f"[{idx:>3}/{len(flagged)}] [OK] ({source}) Saved portrait for {name}") else: print(f"[{idx:>3}/{len(flagged)}] [FAIL] Could not locate image for {name}") conn.commit() conn.close() print("\n" + "=" * 65) print(f"Successfully resolved high-resolution portraits for {resolved} / {len(flagged)} artists.") print("=" * 65) if __name__ == "__main__": main()