Mthrfkr's picture
Update app.py
bcbe7ab verified
raw
history blame
6.56 kB
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()