"""Bandcamp discovery using their public APIs (no scraping).""" import httpx HEADERS = { "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", } DIG_DEEPER_URL = "https://bandcamp.com/api/hub/2/dig_deeper" async def discover_by_tag( tags: list[str], sort: str = "new", page: int = 1, ) -> list[dict]: """Discover new music on Bandcamp by tag using their public API. Args: tags: List of genre/tag strings (e.g. ["indie-rock", "shoegaze"]) sort: "new", "rec", or "pop" (new releases, recommended, popular) page: Page number for pagination Returns list of releases with: title, artist, art_url, bandcamp_url, genre, item_type """ async with httpx.AsyncClient(timeout=15, headers=HEADERS) as client: resp = await client.post( DIG_DEEPER_URL, json={ "filters": { "format": "all", "location": 0, "sort": sort, "tags": tags, }, "page": page, }, ) if resp.status_code != 200: return [] data = resp.json() results = [] for item in data.get("items", []): art_id = item.get("art_id") art_url = f"https://f4.bcbits.com/img/a{art_id}_16.jpg" if art_id else None tralbum_type = item.get("tralbum_type", "a") type_path = "album" if tralbum_type == "a" else "track" item_url = item.get("tralbum_url", "") results.append({ "title": item.get("title", ""), "artist": item.get("artist", ""), "art_url": art_url, "bandcamp_url": item_url, "genre": ", ".join(tags), "item_type": type_path, }) return results async def get_trending_tags() -> list[str]: """Return common Bandcamp genre tags for discovery.""" return [ "indie-rock", "electronic", "hip-hop-rap", "ambient", "punk", "experimental", "folk", "jazz", "metal", "pop", "r-b-soul", "shoegaze", "post-punk", "synthwave", "lo-fi", "dream-pop", "indie-pop", "psychedelic", "garage-rock", "emo", ]