#!/usr/bin/env python3 """Pull a matched portrait cast from Unsplash for the AfriX concept avatars. Reads the access key from ./.unsplash-key (or $UNSPLASH_ACCESS_KEY) and never prints it. Writes img/p01.jpg ... and img/credits.json, then `python3 build.py` picks them up automatically — if no photos are present, the frames fall back to drawn portraits. python3 fetch_portraits.py """ import json, os, sys, time, urllib.parse, urllib.request HERE = os.path.dirname(os.path.abspath(__file__)) IMG = os.path.join(HERE, "img") WANT = 18 # portraits in the cast PER_QUERY = 6 # candidates pulled per search term # Spread across the regions the product actually serves, so the cast is not # one search term's idea of a continent. QUERIES = [ "nigerian man portrait", "kenyan woman portrait", "ghanaian portrait", "ethiopian woman portrait", "senegalese man portrait", "south african portrait", "moroccan man portrait", "tanzanian woman portrait", "african professional portrait", ] def key(): k = os.environ.get("UNSPLASH_ACCESS_KEY", "").strip() if not k: p = os.path.join(HERE, ".unsplash-key") if os.path.exists(p): k = open(p).read().strip() if not k: sys.exit("No Unsplash access key. Put one in concept-mobile/.unsplash-key " "or set UNSPLASH_ACCESS_KEY, then run this again.") return k def api(path, params, k): url = "https://api.unsplash.com" + path + "?" + urllib.parse.urlencode(params) req = urllib.request.Request(url, headers={ "Authorization": "Client-ID " + k, "Accept-Version": "v1", "User-Agent": "AfriX-concept/1.0", }) with urllib.request.urlopen(req, timeout=30) as r: return json.load(r) def main(): k = key() os.makedirs(IMG, exist_ok=True) seen, picked = set(), [] for q in QUERIES: if len(picked) >= WANT: break try: data = api("/search/photos", {"query": q, "per_page": PER_QUERY, "orientation": "squarish", "content_filter": "high"}, k) except Exception as e: print(" search failed for %r: %s" % (q, e)) continue for ph in data.get("results", []): if ph["id"] in seen or len(picked) >= WANT: continue seen.add(ph["id"]) picked.append(ph) time.sleep(0.4) # stay well inside the demo rate limit if not picked: sys.exit("Search returned nothing — check the key is an Access Key, not a Secret Key.") credits = [] for i, ph in enumerate(picked, 1): name = "p%02d.jpg" % i src = ph["urls"]["raw"] + "&w=200&h=200&fit=facearea&facepad=3&q=72&fm=jpg" try: with urllib.request.urlopen(src, timeout=30) as r, open(os.path.join(IMG, name), "wb") as f: f.write(r.read()) except Exception as e: print(" download failed for %s: %s" % (name, e)) continue # Unsplash API guidelines: register the download against the photo. try: api(urllib.parse.urlparse(ph["links"]["download_location"]).path, dict(urllib.parse.parse_qsl( urllib.parse.urlparse(ph["links"]["download_location"]).query)), k) except Exception: pass credits.append({"file": name, "photographer": ph["user"]["name"], "profile": ph["user"]["links"]["html"], "photo": ph["links"]["html"]}) print(" %s %s" % (name, ph["user"]["name"])) json.dump(credits, open(os.path.join(IMG, "credits.json"), "w"), indent=1) print("\n%d portraits in img/ — now run: python3 build.py" % len(credits)) if __name__ == "__main__": main()