# lastfm_helpers.py import pylast from config import ( LASTFM_API_KEY, LASTFM_API_SECRET, LASTFM_USERNAME, MIN_PLAYS, RECENT_MONTHS ) import logging from datetime import datetime, timedelta from collections import defaultdict log = logging.getLogger("LastFmHelpers") LASTFM_NETWORK = None try: LASTFM_NETWORK = pylast.LastFMNetwork( api_key=LASTFM_API_KEY, api_secret=LASTFM_API_SECRET, username=LASTFM_USERNAME ) log.info("✅ Connected to Last.fm API.") except Exception as e: log.error(f"❌ Failed to initialize Last.fm network. Check API keys and username: {e}") def recent_artists(): """Fetches recently played artists from Last.fm, filtered by play count and recency.""" if not LASTFM_NETWORK: log.error("Last.fm network not initialized. Cannot fetch recent artists.") return [] user = LASTFM_NETWORK.get_user(LASTFM_USERNAME) since_timestamp = int((datetime.utcnow() - timedelta(days=30 * RECENT_MONTHS)).timestamp()) counts = defaultdict(int) log.info(f"Fetching recent tracks for {LASTFM_USERNAME} since {datetime.fromtimestamp(since_timestamp).strftime('%Y-%m-%d %H:%M:%S')}") try: for track in user.get_recent_tracks(time_from=since_timestamp, limit=None): artist = track.artist counts[(artist.name, artist.mbid)] += 1 except pylast.NetworkError as ne: log.error(f"Network error fetching recent tracks: {ne}") return [] except pylast.WSError as wse: log.error(f"Last.fm API error fetching recent tracks: {wse}") return [] except Exception as e: log.error(f"Unexpected error fetching recent tracks: {e}") return [] return [(name, mbid) for (name, mbid), count in counts.items() if count >= MIN_PLAYS] def get_similar_artists_pylast(artist_mbid, limit): """Fetches artists similar to a given artist from Last.fm.""" if not LASTFM_NETWORK: log.error("Last.fm network not initialized. Cannot fetch similar artists.") return [] try: artist = LASTFM_NETWORK.get_artist_by_mbid(artist_mbid) similar_artists_tuples = artist.get_similar(limit=limit) return similar_artists_tuples except pylast.WSError as wse: log.warning(f"Last.fm API error fetching similar artists for MBID {artist_mbid}: {wse}. Check MBID.") return [] except pylast.NetworkError as ne: log.warning(f"Network error fetching similar artists for MBID {artist_mbid}: {ne}") return [] except Exception as e: log.warning(f"Could not fetch similar artists for MBID {artist_mbid}: {e}") return [] def get_artist_top_tracks(artist_name, limit=5): """Fetches top tracks for a specific artist from Last.fm.""" if not LASTFM_NETWORK: log.error("Last.fm network not initialized. Cannot fetch top tracks.") return [] try: artist = LASTFM_NETWORK.get_artist(artist_name) top_tracks = artist.get_top_tracks(limit=limit) return [{"artist": track.item.artist.name, "title": track.item.title} for track in top_tracks] except pylast.WSError as wse: log.warning(f"Last.fm API error fetching top tracks for '{artist_name}': {wse}. Check artist name.") return [] except pylast.NetworkError as ne: log.warning(f"Network error fetching top tracks for '{artist_name}': {ne}") return [] except Exception as e: log.warning(f"Unexpected error fetching top tracks for '{artist_name}': {e}") return []