""" collectors/sync_spotify_complete.py Spotify sync engine using credentials from .env. """ import os import re import time import base64 import sqlite3 import logging from pathlib import Path from datetime import datetime, timezone from typing import Dict, List, Optional import requests from dotenv import load_dotenv PROJECT_ROOT = Path(__file__).resolve().parent.parent load_dotenv(PROJECT_ROOT / ".env") logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s") logger = logging.getLogger("SpotifySync") DB_PATH = PROJECT_ROOT / "data" / "hiffi_catalog.db" class SpotifySyncEngine: def __init__(self, db_path: Path = DB_PATH): self.db_path = db_path self.client_id = os.getenv("SPOTIFY_CLIENT_ID", "").strip() self.client_secret = os.getenv("SPOTIFY_CLIENT_SECRET", "").strip() self.access_token = None def authenticate(self) -> bool: if not self.client_id or not self.client_secret: logger.warning("Spotify credentials missing in .env") return False url = "https://accounts.spotify.com/api/token" auth_bytes = f"{self.client_id}:{self.client_secret}".encode("utf-8") headers = { "Authorization": f"Basic {base64.b64encode(auth_bytes).decode('utf-8')}", "Content-Type": "application/x-www-form-urlencoded" } data = {"grant_type": "client_credentials"} try: resp = requests.post(url, headers=headers, data=data, timeout=10) if resp.status_code == 200: self.access_token = resp.json().get("access_token") return True except Exception as e: logger.error(f"Spotify auth error: {e}") return False def fetch_batch(self, sp_ids: List[str]) -> Dict[str, Dict]: results = {} if not self.access_token or not sp_ids: return results headers = {"Authorization": f"Bearer {self.access_token}"} for i in range(0, len(sp_ids), 50): chunk = sp_ids[i:i + 50] url = f"https://api.spotify.com/v1/artists?ids={','.join(chunk)}" try: resp = requests.get(url, headers=headers, timeout=10) if resp.status_code == 200: for art in resp.json().get("artists", []): if art: results[art["id"]] = { "followers": art.get("followers", {}).get("total", 0), "popularity": art.get("popularity", 0) } except Exception as e: logger.error(f"Batch fetch error: {e}") time.sleep(0.05) return results if __name__ == "__main__": engine = SpotifySyncEngine() if engine.authenticate(): logger.info("Spotify API credentials authenticated successfully.") else: logger.error("Authentication failed. Check SPOTIFY_CLIENT_ID and SPOTIFY_CLIENT_SECRET in .env.")