Mthrfkr commited on
Commit
bcbe7ab
verified
1 Parent(s): 2889cf8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +53 -62
app.py CHANGED
@@ -4,54 +4,45 @@ import pandas as pd
4
  import time
5
  import shutil
6
  import numpy as np
 
7
  from tempfile import NamedTemporaryFile
8
  from openpyxl import Workbook
9
 
10
- # Lista de credenciales de API de Spotify
11
- client_ids = [
12
- '9dfc90506fa04938b05d7913f8b13bad',
13
- '0ef8df0b57864accb36251fb0b741935',
14
- '066bb9a3e0ac40aba89732f9a97249bf',
15
- 'cfbe3754c86048d1a82542a5ab432b9a'
16
- ]
17
- client_secrets = [
18
- '62a96c883b564f1e8c9f3af935f9f88e',
19
- '8a1bb6f0f8f14feb9be2dff4b603bb5f',
20
- '9cdae5b56ec24aed91bb3958823ff39e',
21
- 'c55801afd5c24df3b8673cc07468c7b6'
22
- ]
23
  current_api_index = 0
24
 
25
- # Contadores de solicitudes
26
- solicitudes_totales = 0
27
 
28
- # Funciones para Spotify
29
- def obtener_token(client_id, client_secret):
30
  url = 'https://accounts.spotify.com/api/token'
31
  headers = {'Content-Type': 'application/x-www-form-urlencoded'}
32
  payload = {'grant_type': 'client_credentials'}
33
  response = requests.post(url, headers=headers, data=payload, auth=(client_id, client_secret))
34
- global solicitudes_totales
35
- solicitudes_totales += 1 # Contando solicitud
36
  if response.status_code == 200:
37
  return response.json().get('access_token')
38
  else:
39
  return None
40
 
41
- def manejar_rate_limit(response, intento):
42
  if response.status_code == 429:
43
  retry_after = int(response.headers.get('Retry-After', 1))
44
- wait_time = retry_after + 10 * (2 ** intento) # Retroceso exponencial
45
  time.sleep(wait_time)
46
  return True
47
  return False
48
 
49
- def hacer_request_con_reintento(url, headers, params=None, max_retries=5):
50
- global solicitudes_totales
51
- for intento in range(max_retries):
52
  response = requests.get(url, headers=headers, params=params)
53
- solicitudes_totales += 1 # Contando solicitud
54
- if manejar_rate_limit(response, intento):
55
  continue
56
  if response.status_code == 200:
57
  return response
@@ -59,7 +50,7 @@ def hacer_request_con_reintento(url, headers, params=None, max_retries=5):
59
  break
60
  return None
61
 
62
- def obtener_caracteristicas_audio(token, track_ids):
63
  audio_features = {}
64
  url = 'https://api.spotify.com/v1/audio-features'
65
  headers = {'Authorization': f'Bearer {token}'}
@@ -67,14 +58,14 @@ def obtener_caracteristicas_audio(token, track_ids):
67
  for i in range(0, len(track_ids), 100):
68
  batch_ids = track_ids[i:i+100]
69
  params = {'ids': ','.join(batch_ids)}
70
- response = hacer_request_con_reintento(url, headers, params)
71
  if response:
72
  for feature in response.json().get('audio_features', []):
73
  if feature:
74
  audio_features[feature['id']] = feature
75
  return audio_features
76
 
77
- def obtener_tracks_y_caracteristicas(token, url):
78
  headers = {'Authorization': f'Bearer {token}'}
79
  if "track" in url:
80
  track_id = url.split("/")[-1].split("?")[0]
@@ -82,18 +73,18 @@ def obtener_tracks_y_caracteristicas(token, url):
82
  elif "playlist" in url:
83
  playlist_id = url.split("/")[-1].split("?")[0]
84
  tracks_url = f'https://api.spotify.com/v1/playlists/{playlist_id}/tracks'
85
- response = hacer_request_con_reintento(tracks_url, headers)
86
  if response:
87
  track_ids = [item['track']['id'] for item in response.json().get('items', []) if item['track']]
88
  else:
89
  return None, None
90
 
91
- audio_features = obtener_caracteristicas_audio(token, track_ids)
92
  return track_ids, audio_features
93
 
94
- def buscar_canciones_similares(token, audio_features, n_songs=10):
95
  headers = {'Authorization': f'Bearer {token}'}
96
- seed_tracks = list(audio_features.keys())[:5] # Spotify solo permite hasta 5 canciones semilla
97
 
98
  params = {
99
  'seed_tracks': ','.join(seed_tracks),
@@ -101,76 +92,76 @@ def buscar_canciones_similares(token, audio_features, n_songs=10):
101
  }
102
 
103
  url = 'https://api.spotify.com/v1/recommendations'
104
- response = hacer_request_con_reintento(url, headers, params=params)
105
 
106
  if response:
107
  recommended_tracks = response.json().get('tracks', [])
108
  track_ids = [track['id'] for track in recommended_tracks]
109
- return obtener_informacion_canciones(token, track_ids)
110
 
111
  return []
112
 
113
- def obtener_informacion_canciones(token, track_ids):
114
  tracks_info = []
115
- audio_features = obtener_caracteristicas_audio(token, track_ids) # Obtener caracter铆sticas de audio
116
  url = 'https://api.spotify.com/v1/tracks'
117
  headers = {'Authorization': f'Bearer {token}'}
118
 
119
  for i in range(0, len(track_ids), 50):
120
  batch_ids = track_ids[i:i+50]
121
  params = {'ids': ','.join(batch_ids)}
122
- response = hacer_request_con_reintento(url, headers, params)
123
  if response:
124
  tracks = response.json().get('tracks', [])
125
  for track in tracks:
126
  features = audio_features.get(track['id'], {})
127
  tracks_info.append({
128
- 'artista': track['artists'][0]['name'] if track['artists'] else 'Desconocido',
129
- 'titulo': track['name'],
130
- 'isrc': track['external_ids'].get('isrc', 'No disponible'),
131
- 'popularity': track.get('popularity', 'No disponible'),
132
- 'release_year': track.get('album', {}).get('release_date', 'No disponible').split('-')[0] if track.get('album', {}).get('release_date') else 'No disponible',
133
- 'duration': track.get('duration_ms', 'No disponible'),
134
- 'danceability': features.get('danceability', 'No disponible'),
135
- 'energy': features.get('energy', 'No disponible'),
136
- 'tempo': features.get('tempo', 'No disponible'),
137
- 'valence': features.get('valence', 'No disponible'),
138
  'url': track['external_urls']['spotify']
139
  })
140
  return tracks_info
141
 
142
- # Funci贸n principal de la interfaz
143
  def interface(project_name, spotify_url, num_similar_songs=10):
144
- token_spotify = obtener_token(client_ids[current_api_index], client_secrets[current_api_index])
145
- track_ids, audio_features = obtener_tracks_y_caracteristicas(token_spotify, spotify_url)
146
  if not track_ids or not audio_features:
147
- return "URL no v谩lida o no se encontraron canciones.", None
148
 
149
- similar_tracks_info = buscar_canciones_similares(token_spotify, audio_features, num_similar_songs)
150
 
151
- # Crear DataFrame
152
  df = pd.DataFrame(similar_tracks_info)
153
 
154
- # Guardar DataFrame en un archivo Excel
155
  tmpfile = NamedTemporaryFile(delete=False, suffix='.xlsx')
156
  df.to_excel(tmpfile.name, index=False)
157
 
158
- # Renombrar el archivo con el nombre del proyecto
159
  project_file_name = f"{project_name}.xlsx"
160
  shutil.move(tmpfile.name, project_file_name)
161
 
162
- return df, project_file_name # Devuelve el DataFrame y el enlace al archivo Excel
163
 
164
- # Configuraci贸n de Gradio
165
  iface = gr.Interface(
166
  fn=interface,
167
  inputs=[
168
- gr.Textbox(label="Nombre del Proyecto"),
169
- gr.Textbox(label="Spotify URL (Track o Playlist)"),
170
- gr.Number(label="N煤mero de Canciones Similares", value=10, minimum=1, maximum=100)
171
  ],
172
  outputs=[gr.Dataframe(), gr.File(label="Download Excel")],
173
  title="Spotify Similar Track Finder",
174
- description="Ingresa una URL de Spotify para encontrar canciones similares basadas en sus caracter铆sticas."
175
  )
176
  iface.launch()
 
4
  import time
5
  import shutil
6
  import numpy as np
7
+ import os
8
  from tempfile import NamedTemporaryFile
9
  from openpyxl import Workbook
10
 
11
+ # Spotify API credentials from environment variables
12
+ client_ids = os.getenv("SPOTIFY_CLIENT_IDS").split(',')
13
+ client_secrets = os.getenv("SPOTIFY_CLIENT_SECRETS").split(',')
 
 
 
 
 
 
 
 
 
 
14
  current_api_index = 0
15
 
16
+ # Request counters
17
+ total_requests = 0
18
 
19
+ # Spotify Functions
20
+ def get_token(client_id, client_secret):
21
  url = 'https://accounts.spotify.com/api/token'
22
  headers = {'Content-Type': 'application/x-www-form-urlencoded'}
23
  payload = {'grant_type': 'client_credentials'}
24
  response = requests.post(url, headers=headers, data=payload, auth=(client_id, client_secret))
25
+ global total_requests
26
+ total_requests += 1 # Counting request
27
  if response.status_code == 200:
28
  return response.json().get('access_token')
29
  else:
30
  return None
31
 
32
+ def handle_rate_limit(response, attempt):
33
  if response.status_code == 429:
34
  retry_after = int(response.headers.get('Retry-After', 1))
35
+ wait_time = retry_after + 10 * (2 ** attempt) # Exponential backoff
36
  time.sleep(wait_time)
37
  return True
38
  return False
39
 
40
+ def make_request_with_retry(url, headers, params=None, max_retries=5):
41
+ global total_requests
42
+ for attempt in range(max_retries):
43
  response = requests.get(url, headers=headers, params=params)
44
+ total_requests += 1 # Counting request
45
+ if handle_rate_limit(response, attempt):
46
  continue
47
  if response.status_code == 200:
48
  return response
 
50
  break
51
  return None
52
 
53
+ def get_audio_features(token, track_ids):
54
  audio_features = {}
55
  url = 'https://api.spotify.com/v1/audio-features'
56
  headers = {'Authorization': f'Bearer {token}'}
 
58
  for i in range(0, len(track_ids), 100):
59
  batch_ids = track_ids[i:i+100]
60
  params = {'ids': ','.join(batch_ids)}
61
+ response = make_request_with_retry(url, headers, params)
62
  if response:
63
  for feature in response.json().get('audio_features', []):
64
  if feature:
65
  audio_features[feature['id']] = feature
66
  return audio_features
67
 
68
+ def get_tracks_and_features(token, url):
69
  headers = {'Authorization': f'Bearer {token}'}
70
  if "track" in url:
71
  track_id = url.split("/")[-1].split("?")[0]
 
73
  elif "playlist" in url:
74
  playlist_id = url.split("/")[-1].split("?")[0]
75
  tracks_url = f'https://api.spotify.com/v1/playlists/{playlist_id}/tracks'
76
+ response = make_request_with_retry(tracks_url, headers)
77
  if response:
78
  track_ids = [item['track']['id'] for item in response.json().get('items', []) if item['track']]
79
  else:
80
  return None, None
81
 
82
+ audio_features = get_audio_features(token, track_ids)
83
  return track_ids, audio_features
84
 
85
+ def find_similar_tracks(token, audio_features, n_songs=10):
86
  headers = {'Authorization': f'Bearer {token}'}
87
+ seed_tracks = list(audio_features.keys())[:5] # Spotify allows up to 5 seed tracks
88
 
89
  params = {
90
  'seed_tracks': ','.join(seed_tracks),
 
92
  }
93
 
94
  url = 'https://api.spotify.com/v1/recommendations'
95
+ response = make_request_with_retry(url, headers, params=params)
96
 
97
  if response:
98
  recommended_tracks = response.json().get('tracks', [])
99
  track_ids = [track['id'] for track in recommended_tracks]
100
+ return get_track_information(token, track_ids)
101
 
102
  return []
103
 
104
+ def get_track_information(token, track_ids):
105
  tracks_info = []
106
+ audio_features = get_audio_features(token, track_ids) # Get audio features
107
  url = 'https://api.spotify.com/v1/tracks'
108
  headers = {'Authorization': f'Bearer {token}'}
109
 
110
  for i in range(0, len(track_ids), 50):
111
  batch_ids = track_ids[i:i+50]
112
  params = {'ids': ','.join(batch_ids)}
113
+ response = make_request_with_retry(url, headers, params)
114
  if response:
115
  tracks = response.json().get('tracks', [])
116
  for track in tracks:
117
  features = audio_features.get(track['id'], {})
118
  tracks_info.append({
119
+ 'artist': track['artists'][0]['name'] if track['artists'] else 'Unknown',
120
+ 'title': track['name'],
121
+ 'isrc': track['external_ids'].get('isrc', 'Not available'),
122
+ 'popularity': track.get('popularity', 'Not available'),
123
+ 'release_year': track.get('album', {}).get('release_date', 'Not available').split('-')[0] if track.get('album', {}).get('release_date') else 'Not available',
124
+ 'duration': track.get('duration_ms', 'Not available'),
125
+ 'danceability': features.get('danceability', 'Not available'),
126
+ 'energy': features.get('energy', 'Not available'),
127
+ 'tempo': features.get('tempo', 'Not available'),
128
+ 'valence': features.get('valence', 'Not available'),
129
  'url': track['external_urls']['spotify']
130
  })
131
  return tracks_info
132
 
133
+ # Main Interface Function
134
  def interface(project_name, spotify_url, num_similar_songs=10):
135
+ token_spotify = get_token(client_ids[current_api_index], client_secrets[current_api_index])
136
+ track_ids, audio_features = get_tracks_and_features(token_spotify, spotify_url)
137
  if not track_ids or not audio_features:
138
+ return "Invalid URL or no songs found.", None
139
 
140
+ similar_tracks_info = find_similar_tracks(token_spotify, audio_features, num_similar_songs)
141
 
142
+ # Create DataFrame
143
  df = pd.DataFrame(similar_tracks_info)
144
 
145
+ # Save DataFrame to an Excel file
146
  tmpfile = NamedTemporaryFile(delete=False, suffix='.xlsx')
147
  df.to_excel(tmpfile.name, index=False)
148
 
149
+ # Rename the file with the project name
150
  project_file_name = f"{project_name}.xlsx"
151
  shutil.move(tmpfile.name, project_file_name)
152
 
153
+ return df, project_file_name # Returns the DataFrame and the link to the Excel file
154
 
155
+ # Gradio Interface Configuration
156
  iface = gr.Interface(
157
  fn=interface,
158
  inputs=[
159
+ gr.Textbox(label="Project Name"),
160
+ gr.Textbox(label="Spotify URL (Track or Playlist)"),
161
+ gr.Number(label="Number of Similar Songs", value=10, minimum=1, maximum=100)
162
  ],
163
  outputs=[gr.Dataframe(), gr.File(label="Download Excel")],
164
  title="Spotify Similar Track Finder",
165
+ description="Enter a Spotify URL to find similar songs based on their features."
166
  )
167
  iface.launch()