150 lines
5.6 KiB
Python
150 lines
5.6 KiB
Python
"""
|
|
scripts/resolve_18_flagged.py
|
|
Dynamically queries MediaWiki Action API and iTunes Search API
|
|
to fetch verified, authentic face portraits for the remaining flagged artists.
|
|
Completely bypasses static Wikimedia 404 paths.
|
|
"""
|
|
|
|
import sqlite3
|
|
import requests
|
|
import re
|
|
import time
|
|
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 bot policy compliant headers
|
|
WIKI_HEADERS = {
|
|
"User-Agent": "HIFFIArtistIntelligence/1.0 (https://hiffi.ai; admin@hiffi.ai)",
|
|
"Accept": "application/json,image/*"
|
|
}
|
|
|
|
APP_HEADERS = {
|
|
"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"
|
|
}
|
|
|
|
# The remaining 16 artists requiring dynamic lookup
|
|
REMAINING_16 = [
|
|
"Elucid", "Ecco2k", "Dirtbike LB", "billy woods", "BLP Kosher",
|
|
"Che Noir", "Dr. Dre", "Hodgy Beats", "Killah Priest", "NoCap",
|
|
"Omen", "Rylo Rodriguez", "Spillage Village", "The Roots",
|
|
"The Underachievers", "Wheezy"
|
|
]
|
|
|
|
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_wikipedia_lead_image(artist_name: str) -> str:
|
|
"""Dynamically queries Wikipedia PageImages API to retrieve current working thumbnail."""
|
|
clean_name = re.sub(r'[\$@#!\?]', '', artist_name).strip()
|
|
search_queries = [
|
|
f"{clean_name} rapper",
|
|
f"{clean_name} musician",
|
|
clean_name
|
|
]
|
|
|
|
for q in search_queries:
|
|
api_url = (
|
|
f"https://en.wikipedia.org/w/api.php?action=query&generator=search"
|
|
f"&gsrsearch={requests.utils.quote(q)}&gsrlimit=1"
|
|
f"&prop=pageimages&pithumbsize=500&format=json"
|
|
)
|
|
try:
|
|
res = requests.get(api_url, headers=WIKI_HEADERS, timeout=8).json()
|
|
pages = res.get("query", {}).get("pages", {})
|
|
for pid, pdata in pages.items():
|
|
if "thumbnail" in pdata and pdata["thumbnail"].get("source"):
|
|
src = pdata["thumbnail"]["source"]
|
|
if not any(bad in src.lower() for bad in ["flag", "logo", "icon", "stub"]):
|
|
return src
|
|
except Exception:
|
|
continue
|
|
return ""
|
|
|
|
def get_itunes_artist_image(artist_name: str) -> str:
|
|
"""Fallback: Queries Apple iTunes Search API for verified artist portrait."""
|
|
clean_name = re.sub(r'[\$@#!\?]', '', artist_name).strip()
|
|
url = f"https://itunes.apple.com/search?term={requests.utils.quote(clean_name)}&entity=musicArtist&limit=1"
|
|
try:
|
|
res = requests.get(url, headers=APP_HEADERS, timeout=8).json()
|
|
if res.get("resultCount", 0) > 0:
|
|
artist_id = res["results"][0].get("artistId")
|
|
if artist_id:
|
|
lookup_url = f"https://itunes.apple.com/lookup?id={artist_id}&entity=album&limit=1"
|
|
l_res = requests.get(lookup_url, headers=APP_HEADERS, timeout=8).json()
|
|
if l_res.get("resultCount", 0) > 1:
|
|
art = l_res["results"][1].get("artworkUrl100")
|
|
if art:
|
|
return art.replace("100x100bb.jpg", "1000x1000bb.jpg")
|
|
except Exception:
|
|
pass
|
|
return ""
|
|
|
|
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("\n" + "="*60)
|
|
print(f" RESOLVING REMAINING {len(REMAINING_16)} ARTIST PORTRAITS")
|
|
print("="*60 + "\n")
|
|
|
|
saved_count = 0
|
|
|
|
for idx, artist_name in enumerate(REMAINING_16, 1):
|
|
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_path = AVATAR_DIR / f"{slug}_{artist_id}.jpg"
|
|
|
|
print(f"[{idx:02d}/{len(REMAINING_16)}] Finding face for: {artist_name}...", end=" ", flush=True)
|
|
|
|
# 1. Query Wikipedia Action API dynamically
|
|
image_url = get_wikipedia_lead_image(artist_name)
|
|
time.sleep(0.2)
|
|
|
|
# 2. Fallback to iTunes API
|
|
if not image_url:
|
|
image_url = get_itunes_artist_image(artist_name)
|
|
time.sleep(0.2)
|
|
|
|
if image_url:
|
|
try:
|
|
# Use appropriate header based on domain
|
|
req_headers = WIKI_HEADERS if "wikimedia" in image_url or "wikipedia" in image_url else APP_HEADERS
|
|
res = requests.get(image_url, headers=req_headers, timeout=12)
|
|
if res.status_code == 200 and len(res.content) > 2000:
|
|
with open(target_path, "wb") as f:
|
|
f.write(res.content)
|
|
size_kb = len(res.content) / 1024
|
|
print(f"SAVED -> {target_path.name} ({size_kb:.1f} KB)")
|
|
|
|
cursor.execute(
|
|
"UPDATE artists SET image_url = ?, image_verified_flag = 1 WHERE artist_id = ?",
|
|
(image_url, artist_id)
|
|
)
|
|
conn.commit()
|
|
saved_count += 1
|
|
else:
|
|
print(f"HTTP {res.status_code}")
|
|
except Exception as e:
|
|
print(f"ERROR: {e}")
|
|
else:
|
|
print("NO IMAGE FOUND")
|
|
|
|
conn.close()
|
|
print("\n" + "="*60)
|
|
print(f"[+] Complete! Successfully saved {saved_count}/{len(REMAINING_16)} portraits.")
|
|
print("="*60 + "\n")
|
|
|
|
if __name__ == "__main__":
|
|
main() |