突然出现 Spotipy 异常,HTTP 状态 403,原因:无

问题描述 投票:0回答:1

我正在使用 Flask 和 Spotipy 创建一个 Spotify 推荐系统。我有一个功能,系统可以根据用户以 HTML 形式输入的艺术家生成 20 首歌曲推荐。一周前一切都能正常运行,没有错误。然而,当我过去几天继续在这个系统上工作时,我一直收到 SpotifyException 并且无法克服它。自代码工作之日起,我没有对它进行任何更改。

我的浏览器中显示的内容:

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

我在控制台中收到此错误:

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

当我点击 API 调用链接时,我得到了这个:

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

我的 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'))

这是与音乐推荐有关的路线:

@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

回溯提到错误就在这里,尽管我对此不确定:

文件“C:\FYP\TuneTopia pp.py”,第 358 行,位于
new_user_recommendations

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

感谢所提供的任何帮助或指导。

编辑:我尝试删除该应用程序并在 Spotify Developers 上创建一个新应用程序,然后替换客户端 ID 和密钥,但这仍然不起作用。

python flask spotify recommendation-engine spotipy
1个回答
0
投票

Spotify 对其 API 进行了更改,并大部分关闭了这些功能以供公众使用:https://developer.spotify.com/blog/2024-11-27-changes-to-the-web-api

© www.soinside.com 2019 - 2024. All rights reserved.