import logging import time import spotipy from spotipy.oauth2 import SpotifyClientCredentials logging.basicConfig( level=logging.INFO, format="{asctime} - {levelname} - [{funcName}:{lineno}] - {message}", style="{", datefmt="%Y-%m-%d %H:%M", ) def init_spotify_client(): """ Initialize and return a Spotify client. Requires SPOTIPY_CLIENT_ID and SPOTIPY_CLIENT_SECRET environment variables. """ return spotipy.Spotify(client_credentials_manager=SpotifyClientCredentials()) def search_track(spotify, artist, title): """ Search for a track on Spotify. Args: spotify: Spotify client instance artist: Artist name (string) title: Track title (string) Returns: dict with track data if found, None otherwise. Structure: { 'found': True, 'artist': str, 'album': str, 'album_id': str, 'release_date': str, 'release_date_precision': str, 'total_tracks': int, 'track_number': int, 'popularity': int, 'image_url': str, 'genres': list, 'label': str, 'versions_count': int } """ try: querystring = "artist:{0} track:{1}".format(artist.split("\00")[0], title) logging.info("Searching Spotify for track with query: " + querystring) results = spotify.search(q=querystring, type='track') if len(results['tracks']['items']) > 0: logging.info("Track found on Spotify!") track = results['tracks']['items'][0] album = track["album"] # Get artist genres artist_search = spotify.artist(track['artists'][0]['external_urls']['spotify']) genres = artist_search.get('genres', []) # Build response data = { 'found': True, 'artist': album["artists"][0]["name"], 'album': album["name"], 'album_id': album["id"], 'release_date': album["release_date"], 'release_date_precision': album.get("release_date_precision", "day"), 'total_tracks': album["total_tracks"], 'track_number': track["track_number"], 'popularity': track["popularity"], 'image_url': album["images"][0]["url"] if album["images"] else None, 'genres': genres, 'label': album.get("label", ""), 'versions_count': len(results["tracks"]["items"]) } logging.info(f"Found: {data['artist']} - {data['album']}") return data else: logging.info("No track found on Spotify") return None except Exception as err: logging.error(f"Error searching for track on Spotify: {err}") return None def search_album(spotify, artist, album_name): """ Search for an album on Spotify. Args: spotify: Spotify client instance artist: Artist name (string) album_name: Album name (string) Returns: dict with album data if found, None otherwise. Structure: { 'found': True, 'artist': str, 'album': str, 'album_id': str, 'release_date': str, 'release_date_precision': str, 'total_tracks': int, 'image_url': str, 'genres': list, 'versions_count': int } """ try: querystring = "artist:{0} album:{1}".format(artist, album_name) logging.info("Searching Spotify for album with query: " + querystring) tries = 0 found = False results = None while tries < 5 and not found: try: results = spotify.search(q=querystring, type='album') found = True except Exception as err: logging.error(f"Could not search on Spotify: {err}") logging.info("Waiting 30 seconds before trying again") time.sleep(30) tries += 1 if not found or not results: logging.error("Could not search on Spotify after 5 tries") return None if len(results["albums"]["items"]) > 0: logging.info("Album found on Spotify!") album = results["albums"]["items"][0] # Get artist genres artist_search = spotify.artist(album['artists'][0]['external_urls']['spotify']) genres = artist_search.get('genres', []) # Build response data = { 'found': True, 'artist': album["artists"][0]["name"], 'album': album["name"], 'album_id': album["id"], 'release_date': album["release_date"], 'release_date_precision': album.get("release_date_precision", "day"), 'total_tracks': album["total_tracks"], 'image_url': album["images"][0]["url"] if album["images"] else None, 'genres': genres, 'versions_count': len(results["albums"]["items"]) } logging.info(f"Found: {data['artist']} - {data['album']}") return data else: logging.info("No album found on Spotify") return None except Exception as err: logging.error(f"Error searching for album on Spotify: {err}") return None def format_genres(genres): """ Format a list of genres into a comma-separated string. Args: genres: list of genre strings Returns: Comma-separated string of genres, or empty string if no genres """ if not genres or len(genres) == 0: return "" elif len(genres) == 1: return str(genres[0]) else: return ",".join(genres)