Spaces:
Runtime error
Runtime error
| import gradio as gr | |
| import requests | |
| import pandas as pd | |
| import time | |
| import shutil | |
| import numpy as np | |
| import os | |
| from tempfile import NamedTemporaryFile | |
| from openpyxl import Workbook | |
| # Spotify API credentials from environment variables | |
| client_ids = os.getenv("SPOTIFY_CLIENT_IDS").split(',') | |
| client_secrets = os.getenv("SPOTIFY_CLIENT_SECRETS").split(',') | |
| current_api_index = 0 | |
| # Request counters | |
| total_requests = 0 | |
| # Spotify Functions | |
| def get_token(client_id, client_secret): | |
| url = 'https://accounts.spotify.com/api/token' | |
| headers = {'Content-Type': 'application/x-www-form-urlencoded'} | |
| payload = {'grant_type': 'client_credentials'} | |
| response = requests.post(url, headers=headers, data=payload, auth=(client_id, client_secret)) | |
| global total_requests | |
| total_requests += 1 # Counting request | |
| if response.status_code == 200: | |
| return response.json().get('access_token') | |
| else: | |
| return None | |
| def handle_rate_limit(response, attempt): | |
| if response.status_code == 429: | |
| retry_after = int(response.headers.get('Retry-After', 1)) | |
| wait_time = retry_after + 10 * (2 ** attempt) # Exponential backoff | |
| time.sleep(wait_time) | |
| return True | |
| return False | |
| def make_request_with_retry(url, headers, params=None, max_retries=5): | |
| global total_requests | |
| for attempt in range(max_retries): | |
| response = requests.get(url, headers=headers, params=params) | |
| total_requests += 1 # Counting request | |
| if handle_rate_limit(response, attempt): | |
| continue | |
| if response.status_code == 200: | |
| return response | |
| else: | |
| break | |
| return None | |
| def get_audio_features(token, track_ids): | |
| audio_features = {} | |
| url = 'https://api.spotify.com/v1/audio-features' | |
| headers = {'Authorization': f'Bearer {token}'} | |
| for i in range(0, len(track_ids), 100): | |
| batch_ids = track_ids[i:i+100] | |
| params = {'ids': ','.join(batch_ids)} | |
| response = make_request_with_retry(url, headers, params) | |
| if response: | |
| for feature in response.json().get('audio_features', []): | |
| if feature: | |
| audio_features[feature['id']] = feature | |
| return audio_features | |
| def get_tracks_and_features(token, url): | |
| headers = {'Authorization': f'Bearer {token}'} | |
| if "track" in url: | |
| track_id = url.split("/")[-1].split("?")[0] | |
| track_ids = [track_id] | |
| elif "playlist" in url: | |
| playlist_id = url.split("/")[-1].split("?")[0] | |
| tracks_url = f'https://api.spotify.com/v1/playlists/{playlist_id}/tracks' | |
| response = make_request_with_retry(tracks_url, headers) | |
| if response: | |
| track_ids = [item['track']['id'] for item in response.json().get('items', []) if item['track']] | |
| else: | |
| return None, None | |
| audio_features = get_audio_features(token, track_ids) | |
| return track_ids, audio_features | |
| def find_similar_tracks(token, audio_features, n_songs=10): | |
| headers = {'Authorization': f'Bearer {token}'} | |
| seed_tracks = list(audio_features.keys())[:5] # Spotify allows up to 5 seed tracks | |
| params = { | |
| 'seed_tracks': ','.join(seed_tracks), | |
| 'limit': n_songs | |
| } | |
| url = 'https://api.spotify.com/v1/recommendations' | |
| response = make_request_with_retry(url, headers, params=params) | |
| if response: | |
| recommended_tracks = response.json().get('tracks', []) | |
| track_ids = [track['id'] for track in recommended_tracks] | |
| return get_track_information(token, track_ids) | |
| return [] | |
| def get_track_information(token, track_ids): | |
| tracks_info = [] | |
| audio_features = get_audio_features(token, track_ids) # Get audio features | |
| url = 'https://api.spotify.com/v1/tracks' | |
| headers = {'Authorization': f'Bearer {token}'} | |
| for i in range(0, len(track_ids), 50): | |
| batch_ids = track_ids[i:i+50] | |
| params = {'ids': ','.join(batch_ids)} | |
| response = make_request_with_retry(url, headers, params) | |
| if response: | |
| tracks = response.json().get('tracks', []) | |
| for track in tracks: | |
| features = audio_features.get(track['id'], {}) | |
| tracks_info.append({ | |
| 'artist': track['artists'][0]['name'] if track['artists'] else 'Unknown', | |
| 'title': track['name'], | |
| 'isrc': track['external_ids'].get('isrc', 'Not available'), | |
| 'popularity': track.get('popularity', 'Not available'), | |
| 'release_year': track.get('album', {}).get('release_date', 'Not available').split('-')[0] if track.get('album', {}).get('release_date') else 'Not available', | |
| 'duration': track.get('duration_ms', 'Not available'), | |
| 'danceability': features.get('danceability', 'Not available'), | |
| 'energy': features.get('energy', 'Not available'), | |
| 'tempo': features.get('tempo', 'Not available'), | |
| 'valence': features.get('valence', 'Not available'), | |
| 'url': track['external_urls']['spotify'] | |
| }) | |
| return tracks_info | |
| # Main Interface Function | |
| def interface(project_name, spotify_url, num_similar_songs=10): | |
| token_spotify = get_token(client_ids[current_api_index], client_secrets[current_api_index]) | |
| track_ids, audio_features = get_tracks_and_features(token_spotify, spotify_url) | |
| if not track_ids or not audio_features: | |
| return "Invalid URL or no songs found.", None | |
| similar_tracks_info = find_similar_tracks(token_spotify, audio_features, num_similar_songs) | |
| # Create DataFrame | |
| df = pd.DataFrame(similar_tracks_info) | |
| # Save DataFrame to an Excel file | |
| tmpfile = NamedTemporaryFile(delete=False, suffix='.xlsx') | |
| df.to_excel(tmpfile.name, index=False) | |
| # Rename the file with the project name | |
| project_file_name = f"{project_name}.xlsx" | |
| shutil.move(tmpfile.name, project_file_name) | |
| return df, project_file_name # Returns the DataFrame and the link to the Excel file | |
| # Gradio Interface Configuration | |
| iface = gr.Interface( | |
| fn=interface, | |
| inputs=[ | |
| gr.Textbox(label="Project Name"), | |
| gr.Textbox(label="Spotify URL (Track or Playlist)"), | |
| gr.Number(label="Number of Similar Songs", value=10, minimum=1, maximum=100) | |
| ], | |
| outputs=[gr.Dataframe(), gr.File(label="Download Excel")], | |
| title="Spotify Similar Track Finder", | |
| description="Enter a Spotify URL to find similar songs based on their features." | |
| ) | |
| iface.launch() | |