import asyncio import logging import httpx logger = logging.getLogger(__name__) ITUNES_SEARCH_URL = "https://itunes.apple.com/search" # Rate limit: ~20 req/min for iTunes _last_request_time = 0.0 _min_interval = 3.0 # 3s between requests async def _rate_limit(): global _last_request_time now = asyncio.get_event_loop().time() elapsed = now - _last_request_time if elapsed < _min_interval: await asyncio.sleep(_min_interval - elapsed) _last_request_time = asyncio.get_event_loop().time() async def search_track(artist: str, title: str) -> dict | None: """Search iTunes for a track and return preview info, or None if not found.""" await _rate_limit() query = f"{artist} {title}" async with httpx.AsyncClient(timeout=10) as client: resp = await client.get( ITUNES_SEARCH_URL, params={ "term": query, "media": "music", "entity": "song", "limit": 5, }, ) resp.raise_for_status() data = resp.json() results = data.get("results", []) if not results: return None # Find best match (simple: first result with a preview URL) for r in results: if r.get("previewUrl"): return { "track_id": r["trackId"], "preview_url": r["previewUrl"], "apple_music_id": str(r.get("trackId", "")), "duration_ms": r.get("trackTimeMillis"), "genre": r.get("primaryGenreName"), } return None