[Python] Suddenly got Spotipy exception, HTTP status 403, reason: None

Discussão em 'Python' iniciado por Stack, Dezembro 1, 2024.

  1. Stack

    Stack Membro Participativo

    I'm creating a Spotify recommendation system using Flask and Spotipy. I have a feature where the system can generate 20 song recommendations based on the artists that the user has inputted in an HTML form. Everything was able to run a week ago without errors. However, as I continued to work on this system these past few days, I've been getting the SpotifyException and wasn't able to get past it. I have NOT made any changes to my code since the day it was working.

    What appears in my browser:

    spotipy.exceptions.SpotifyException: http status: 403, code:-1 - https://api.spotify.com/v1/audio-features/?ids=1BxfuPKGuaTgP7aM0Bbdwr,2OzhQlSqBEmt7hmkYxfT6m,4q5YezDOIPcoLr8R81x9qy,3hUxzQpSfdDqwM3ZTFQY0K,4R2kfaDFhslZEMJqAFNpdd,1dGr1c8CrMLDpV6mPbImSI,1R0a2iXumgCiFb7HEZ7gUE,0V3wPSX9ygBnCm8psDIegu,1u8c2t2Cy7UBoG4ArRcF5g,0W0iAC1VGlB82PI6elxFYf: None, reason: None

    I got this error in the console:

    HTTP Error for GET to https://api.spotify.com/v1/audio-features/?ids=1BxfuPKGuaTgP7aM0Bbdwr,2OzhQlSqBEmt7hmkYxfT6m,4q5YezDOIPcoLr8R81x9qy,3hUxzQpSfdDqwM3ZTFQY0K,4R2kfaDFhslZEMJqAFNpdd,1dGr1c8CrMLDpV6mPbImSI,1R0a2iXumgCiFb7HEZ7gUE,0V3wPSX9ygBnCm8psDIegu,1u8c2t2Cy7UBoG4ArRcF5g,0W0iAC1VGlB82PI6elxFYf with Params: {} returned 403 due to None

    I got this when I clicked on the API call link:

    {
    "error": {
    "status": 401,
    "message": "No token provided"
    }
    }


    What's in my app.py:

    app = Flask(__name__)
    app.jinja_env.globals.update(zip=zip)
    app.secret_key = 'secret-key'

    CLIENT_ID = 'client-id'
    CLIENT_SECRET = 'client-secret'
    REDIRECT_URI = 'http://localhost:5000/callback'

    AUTH_URL = 'https://accounts.spotify.com/authorize'
    TOKEN_URL = 'https://accounts.spotify.com/api/token'
    API_BASE_URL = 'https://api.spotify.com/v1/'

    sp = spotipy.Spotify(auth_manager=SpotifyClientCredentials(client_id=CLIENT_ID, client_secret=CLIENT_SECRET))

    ...

    @app.route('/login')
    def login():
    scope = 'user-read-private user-read-email user-library-read user-top-read'
    params = {
    'client_id': CLIENT_ID,
    'response_type': 'code',
    'scope': scope,
    'redirect_uri': REDIRECT_URI,
    'show_dialog': True # set to False after testing
    }

    auth_url = f"{AUTH_URL}?{urllib.parse.urlencode(params)}"
    return redirect(auth_url)

    @app.route('/callback')
    def callback():
    if 'error' in request.args:
    return jsonify({"error": request.args['error']})

    # Login successful
    if 'code' in request.args:
    # Build request body with data to be sent to Spotify to get access token
    req_body = {
    'code': request.args['code'],
    'grant_type': 'authorization_code',
    'redirect_uri': REDIRECT_URI,
    'client_id': CLIENT_ID,
    'client_secret': CLIENT_SECRET
    }

    response = requests.post(TOKEN_URL, data=req_body)
    token_info = response.json()

    session['access_token'] = token_info['access_token']
    session['refresh_token'] = token_info['refresh_token']
    session['expires_at'] = datetime.now().timestamp() + token_info['expires_in']
    # Access token only lasts for 1 day

    # Fetch user profile
    user_profile = requests.get(API_BASE_URL + 'me', headers={'Authorization': f"Bearer {token_info['access_token']}"})
    user_info = user_profile.json()

    # Store additional user profile data in session
    session['user_name'] = user_info['display_name']
    session['user_id'] = user_info['id']
    session['user_email'] = user_info['email']
    session['user_uri'] = user_info['uri']
    session['user_link'] = user_info['external_urls']['spotify']
    session['user_image'] = user_info['images'][0]['url'] if user_info.get('images') else None

    return redirect(url_for('home'))

    @app.route('/refresh-token')
    def refresh_token():
    # Check if refresh token is NOT in the session, redirect to login
    if 'refresh_token' not in session:
    return redirect(url_for('login'))

    # If access token is expired, make a request to get a fresh one
    if datetime.now().timestamp() > session['expires_at']:
    req_body = {
    'grant_type': 'refresh_token',
    'refresh_token': session['refresh_token'],
    'client_id': CLIENT_ID,
    'client_secret': CLIENT_SECRET
    }

    response = requests.post(TOKEN_URL, data=req_body)
    new_token_info = response.json()

    # Override the access token we had before
    session['access_token'] = new_token_info['access_token']
    session['expires_at'] = datetime.now().timestamp() + new_token_info['expires_in']

    return redirect(url_for('recommend'))


    This is the route that has to do with the music recommendation:

    @app.route('/new_user_recommendations', methods=['POST'])
    def new_user_recommendations():
    # Ensure the user is logged in
    if 'access_token' not in session:
    return jsonify({"error": "Please log in to use this feature"}), 401

    # Retrieve artist names from the form
    artist_names = [request.form.get(f'artist{i}') for i in range(1, 4)]

    # Key mapping for human-readable keys
    key_mapping = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']

    # Validate and fetch artist IDs
    seed_artists = []
    input_audio_features = []
    for artist_name in artist_names:
    if not artist_name:
    return jsonify({"error": f"Missing artist name for input {artist_names.index(artist_name) + 1}"}), 400

    search_result = sp.search(f'artist:"{artist_name}"', type='artist', limit=1)
    if search_result['artists']['items']:
    artist_id = search_result['artists']['items'][0]['id']
    seed_artists.append(artist_id)

    # Fetch top tracks and audio features for the artist
    top_tracks = sp.artist_top_tracks(artist_id)['tracks']
    track_ids = [track['id'] for track in top_tracks]
    audio_features = sp.audio_features(track_ids)

    # Average the features for visualization
    normalized_features = {
    'tempo': np.mean([feat['tempo'] for feat in audio_features if feat]),
    'energy': np.mean([feat['energy'] for feat in audio_features if feat]),
    'danceability': np.mean([feat['danceability'] for feat in audio_features if feat]),
    'valence': np.mean([feat['valence'] for feat in audio_features if feat])
    }
    input_audio_features.append(normalized_features)
    else:
    return jsonify({"error": f"Artist '{artist_name}' not found"}), 404

    # Validate that we have enough artists
    if len(seed_artists) < 1:
    return jsonify({"error": "No valid artists found for recommendations"}), 400

    # Construct the API request
    headers = {
    'Authorization': f"Bearer {session['access_token']}"
    }
    params = {
    'seed_artists': ','.join(seed_artists),
    'limit': 20
    }
    # Add dynamic target features as needed, e.g., tempo, danceability
    # params['target_tempo'] = 120

    url = f'https://api.spotify.com/v1/recommendations?{urllib.parse.urlencode(params)}'

    try:
    response = requests.get(url, headers=headers)
    response.raise_for_status()
    recommendations = response.json()

    # Process recommendations
    recommended_tracks = []
    for track in recommendations.get('tracks', []):
    # Fetch the genres for each artist in the track
    track_genres = []
    for artist in track['artists']:
    artist_id = artist['id']
    artist_info = sp.artist(artist_id)
    artist_genres = artist_info.get('genres', [])
    track_genres.extend(artist_genres)

    recommended_tracks.append({
    'track_name': track['name'],
    'artists': ', '.join(artist['name'] for artist in track['artists']),
    'album_name': track['album']['name'],
    'popularity': track['popularity'],
    'preview_url': track['preview_url'],
    'genres': track_genres, # Add genres to the track
    'id': track.get('id')
    })

    # Fetch audio features for recommended tracks
    recommendation_ids = [track['id'] for track in recommended_tracks if track['id']]
    rec_audio_features = sp.audio_features(recommendation_ids)
    recommended_audio_features = [
    {
    'tempo': round(feat['tempo']),
    'key': key_mapping[feat['key']],
    'energy': feat['energy'],
    'danceability': feat['danceability'],
    'valence': feat['valence']
    }
    for feat in rec_audio_features if feat
    ]

    if not recommended_tracks:
    return jsonify({"error": "No recommendations found. Try with different artists"}), 404

    # Render the template
    return render_template(
    'new_user.html',
    recommendations=recommended_tracks,
    input_features=input_audio_features,
    recommendation_features=recommended_audio_features
    )

    except requests.exceptions.RequestException as e:
    return jsonify({"error": f"Error fetching recommendations: {str(e)}"}), 500


    The traceback mentioned that the error lies here though I'm unsure about this:

    File "C:\FYP\TuneTopia\app.py", line 358, in new_user_recommendations


    audio_features = sp.audio_features(track_ids)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^


    Appreciate any assistance or guidance provided.

    Continue reading...

Compartilhe esta Página