""" collectors/social_fallback_collector.py Resolves missing Instagram handles via the official MusicBrainz REST API. Extracts verified url-rel claims without scraping search engines. """ import re import time import requests from typing import Optional, Dict, List MB_BASE_URL = "https://musicbrainz.org/ws/2/artist/" HEADERS = { "User-Agent": "HIFFIArtistIntelligence/1.0 (sri@superlabs.ai)", "Accept": "application/json" } def resolve_musicbrainz_instagram(artist_name: str) -> Optional[str]: """ Searches MusicBrainz for the artist and inspects url-rels for Instagram profiles. """ search_url = f"{MB_BASE_URL}?query=artist:{requests.utils.quote(artist_name)}&fmt=json&limit=3" try: res = requests.get(search_url, headers=HEADERS, timeout=8) if res.status_code != 200: return None artists = res.json().get("artists", []) if not artists: return None # Check top matches for artist in artists: mbid = artist.get("id") if not mbid: continue rel_url = f"{MB_BASE_URL}{mbid}?inc=url-rels&fmt=json" time.sleep(1.0) # MusicBrainz rate limit compliance: 1 req/sec rel_res = requests.get(rel_url, headers=HEADERS, timeout=8) if rel_res.status_code == 200: relations = rel_res.json().get("relations", []) for rel in relations: target = rel.get("url", {}).get("resource", "") if "instagram.com" in target: match = re.search(r"instagram\.com/([A-Za-z0-9_.]+)", target) if match: handle = match.group(1).lower().rstrip("/.") if handle not in {"p", "reel", "explore"}: return handle except Exception: return None return None class SocialFallbackResolver: def resolve_handles(self, artist_names: List[str]) -> Dict[str, Optional[str]]: results = {} for name in artist_names: results[name] = resolve_musicbrainz_instagram(name) return results if __name__ == "__main__": resolver = SocialFallbackResolver() test_cohort = ["Rylo Rodriguez", "Q Da Fool", "Flatbush Zombies"] print("Testing MusicBrainz Entity Fallback Resolver:") for artist in test_cohort: h = resolve_musicbrainz_instagram(artist) print(f" {artist} -> @{h}")