Files
LEDMatrix/src/spotify_client.py
2025-05-03 22:14:19 -05:00

132 lines
5.3 KiB
Python

import spotipy
from spotipy.oauth2 import SpotifyOAuth
import logging
import json
import os
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# Define paths relative to this file's location
CONFIG_DIR = os.path.join(os.path.dirname(__file__), '..', 'config')
SECRETS_PATH = os.path.join(CONFIG_DIR, 'config_secrets.json')
class SpotifyClient:
def __init__(self):
self.client_id = None
self.client_secret = None
self.redirect_uri = None
self.scope = "user-read-currently-playing user-read-playback-state"
self.sp = None
self.load_credentials()
if self.client_id and self.client_secret and self.redirect_uri:
self._authenticate()
else:
logging.warning("Spotify credentials not loaded. Cannot authenticate.")
def load_credentials(self):
if not os.path.exists(SECRETS_PATH):
logging.error(f"Secrets file not found at {SECRETS_PATH}")
return
try:
with open(SECRETS_PATH, 'r') as f:
secrets = json.load(f)
music_secrets = secrets.get("music", {})
self.client_id = music_secrets.get("SPOTIFY_CLIENT_ID")
self.client_secret = music_secrets.get("SPOTIFY_CLIENT_SECRET")
self.redirect_uri = music_secrets.get("SPOTIFY_REDIRECT_URI")
if not all([self.client_id, self.client_secret, self.redirect_uri]):
logging.warning("One or more Spotify credentials missing in config_secrets.json under the 'music' key.")
except json.JSONDecodeError:
logging.error(f"Error decoding JSON from {SECRETS_PATH}")
except Exception as e:
logging.error(f"Error loading Spotify credentials: {e}")
def _authenticate(self):
"""Handles the OAuth authentication flow."""
try:
# Spotipy handles token caching in .cache file by default
self.sp = spotipy.Spotify(auth_manager=SpotifyOAuth(
client_id=self.client_id,
client_secret=self.client_secret,
redirect_uri=self.redirect_uri,
scope=self.scope,
open_browser=False # Important for headless environments
))
# Try making a call to ensure authentication is working or trigger refresh
self.sp.current_user()
logging.info("Spotify authenticated successfully.")
except Exception as e:
logging.error(f"Spotify authentication failed: {e}")
self.sp = None # Ensure sp is None if auth fails
def is_authenticated(self):
"""Checks if the client is authenticated."""
# Check if sp object exists and try a lightweight API call
if not self.sp:
return False
try:
# A simple call to verify token validity
self.sp.current_user()
return True
except Exception as e:
# Log specific auth errors if needed
logging.warning(f"Spotify token validation failed: {e}")
return False
def get_auth_url(self):
"""Gets the authorization URL for the user."""
# Create a temporary auth manager just to get the URL
try:
auth_manager = SpotifyOAuth(
client_id=self.client_id,
client_secret=self.client_secret,
redirect_uri=self.redirect_uri,
scope=self.scope,
open_browser=False
)
return auth_manager.get_authorize_url()
except Exception as e:
logging.error(f"Could not get Spotify auth URL: {e}")
return None
def get_current_track(self):
"""Fetches the currently playing track from Spotify."""
if not self.is_authenticated():
logging.warning("Spotify not authenticated. Cannot fetch track.")
# Maybe try re-authenticating?
self._authenticate()
if not self.is_authenticated():
return None
try:
track_info = self.sp.current_playback()
if track_info and track_info['item']:
# Simplify structure slightly if needed, or return raw
return track_info
else:
return None # Nothing playing or unavailable
except Exception as e:
logging.error(f"Error fetching current track from Spotify: {e}")
# Check for specific errors like token expiration if spotipy doesn't handle it
if "expired" in str(e).lower():
logging.info("Spotify token might be expired, attempting refresh...")
self._authenticate() # Try to refresh/re-authenticate
return None
# Example Usage (for testing)
# if __name__ == '__main__':
# client = SpotifyClient()
# if client.is_authenticated():
# track = client.get_current_track()
# if track:
# print(json.dumps(track, indent=2))
# else:
# print("No track currently playing or error fetching.")
# else:
# auth_url = client.get_auth_url()
# if auth_url:
# print(f"Please authorize here: {auth_url}")
# else:
# print("Could not authenticate or get auth URL. Check credentials and config.")