Mthrfkr commited on
Commit
e6d5dd6
·
verified ·
1 Parent(s): 67c4abd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +111 -121
app.py CHANGED
@@ -1,13 +1,15 @@
1
- # —————— PARCHE GRADIO_CLIENT BOOLEANS ——————
2
- import gradio_client.utils as client_utils
 
 
3
 
 
 
4
  _original_get_type = client_utils.get_type
5
  def patched_get_type(schema):
6
- # Si el schema no es dict (e.g. un bool), devolvemos Any pa' que no truene
7
  if not isinstance(schema, dict):
8
  return "Any"
9
  return _original_get_type(schema)
10
-
11
  client_utils.get_type = patched_get_type
12
  # ————————————————————————————————
13
 
@@ -19,27 +21,25 @@ import shutil
19
  import os
20
  from tempfile import NamedTemporaryFile
21
 
22
- # Imprime versión pa' checar que ya cargue Gradio correcto
23
- import gradio as _gr
24
- print("⚙️ Gradio version cargada:", _gr.__version__)
25
 
26
  # Configuración de Credenciales
27
  client_ids = os.getenv("SPOTIFY_CLIENT_IDS", "").split(',')
28
  client_secrets = os.getenv("SPOTIFY_CLIENT_SECRETS", "").split(',')
29
-
30
  if not client_ids or not client_secrets:
31
  raise ValueError("❌ Missing environment variables SPOTIFY_CLIENT_IDS or SPOTIFY_CLIENT_SECRETS")
32
 
33
- # Gestión de Rotación de Clientes
34
  current_client_index = 0
35
  request_counter = 0
36
- MAX_REQUESTS_PER_CLIENT = 95 # Rotar antes de llegar al límite de 100
37
 
38
  def rotate_client():
39
  global current_client_index, request_counter
40
  current_client_index = (current_client_index + 1) % len(client_ids)
41
  request_counter = 0
42
- print(f"🔄 Rotated to Spotify client #{current_client_index + 1}")
43
 
44
  def get_spotify_token():
45
  global current_client_index
@@ -49,182 +49,172 @@ def get_spotify_token():
49
  print(f"🔑 Intentando token con cliente #{current_client_index+1}")
50
  resp = requests.post(
51
  'https://accounts.spotify.com/api/token',
52
- headers={'Content-Type': 'application/x-www-form-urlencoded'},
53
- data={'grant_type': 'client_credentials'},
54
  auth=(cid, csec)
55
  )
56
  print(f"➡️ get_spotify_token status={resp.status_code}")
57
  if resp.status_code == 200:
58
- token = resp.json().get('access_token')
59
- print(f"✅ Token OK (cliente #{current_client_index+1}): {token[:10]}…")
60
- return token
61
- else:
62
- print(f"❌ Error token cliente #{current_client_index+1}: {resp.text}")
63
- rotate_client()
64
- raise Exception("🚨 All clients failed to obtain token")
65
 
66
  def make_spotify_request(url, params=None):
67
  global request_counter
68
  token = get_spotify_token()
69
- print(f"🔑 Usando token: {token[:10]}…")
70
- print(f"🌐 Llamando a URL: {url}")
71
- if params:
72
- print(f" con params: {params}")
73
- headers = {'Authorization': f'Bearer {token}'}
74
- for intento in range(3):
75
- print(f"🔁 intento #{intento+1}")
76
  r = requests.get(url, headers=headers, params=params)
77
- print(f"➡️ Status code: {r.status_code}")
78
  request_counter += 1
79
  if r.status_code == 429:
80
- retry = int(r.headers.get('Retry-After', 10))
81
- print(f"⚠️ Rate limit, espero {retry}s y rotamos cliente")
82
  rotate_client()
83
- time.sleep(retry)
84
  continue
85
  if r.status_code == 200:
86
- if request_counter >= MAX_REQUESTS_PER_CLIENT:
87
- print("🔄 Límite alcanzado, roto cliente")
88
  rotate_client()
89
  return r.json()
90
  print(f"❌ Error {r.status_code}: {r.text}")
91
  time.sleep(2)
92
- print("🚫 make_spotify_request: fallaron los 3 intentos")
93
  return None
94
 
95
- def extract_spotify_id(url, resource_type):
96
- clean = url.strip().split('?')[0].replace("https://open.spotify.com/", "")
97
  if "/intl-" in clean:
98
  return clean.split("/")[-1]
99
  parts = clean.split("/")
100
  try:
101
- i = parts.index(resource_type)
102
- return parts[i+1]
103
  except ValueError:
104
  for p in parts:
105
- if len(p)==22 and resource_type in url:
106
  return p
107
- print(f"❌ extract_spotify_id no pudo extraer de {url}")
108
  return None
109
 
110
- def get_playlist_tracks(playlist_url):
111
- pid = extract_spotify_id(playlist_url, "playlist")
112
  if not pid: return []
113
- print(f"🎧 Getting playlist: {pid}")
114
- tracks = []
115
  url = f"https://api.spotify.com/v1/playlists/{pid}/tracks"
116
- params = {'market':'US','limit':100}
117
  while url:
118
- data = make_spotify_request(url, params)
119
- if not data: break
120
- items = data.get('items',[])
121
- print(f" Recibí {len(items)} items")
122
- tracks += [it['track'] for it in items if it.get('track')]
123
- url = data.get('next')
124
- print(f"✅ Found {len(tracks)} tracks")
125
- return tracks
126
-
127
- def get_album_tracks(album_url):
128
- aid = extract_spotify_id(album_url, "album")
129
  if not aid: return []
130
- print(f"💿 Getting album: {aid}")
131
- all_t = []
132
  url = f"https://api.spotify.com/v1/albums/{aid}/tracks"
133
- params = {'market':'US','limit':50}
134
  while url:
135
- d = make_spotify_request(url, params)
136
  if not d: break
137
  for it in d.get('items',[]):
138
- print(f" Detail track {it['id']}")
139
  td = make_spotify_request(f"https://api.spotify.com/v1/tracks/{it['id']}")
140
- if td: all_t.append(td)
141
  url = d.get('next')
142
- print(f"✅ Found {len(all_t)} tracks")
143
- return all_t
144
 
145
  def process_tracks(urls):
146
- print("🔍 process_tracks iniciado")
147
  all_t = []
148
  for u in urls:
149
- print(f"➡️ Procesando {u}")
150
  try:
151
- if "playlist" in u:
152
- all_t += get_playlist_tracks(u)
153
- elif "album" in u:
154
- all_t += get_album_tracks(u)
155
  elif "track" in u:
156
- tid = extract_spotify_id(u, "track")
157
  td = make_spotify_request(f"https://api.spotify.com/v1/tracks/{tid}")
158
  if td: all_t.append(td)
159
  except Exception as e:
160
  print(f"⚠️ Error en {u}: {e}")
161
- print(f"🔍 process_tracks terminó con {len(all_t)} tracks")
162
  return all_t
163
 
164
- def create_final_dataframe(tracks, include_all_info=True):
165
- print("📊 create_final_dataframe iniciado")
166
- cache = {}
167
- def get_artist(aid):
168
  if aid not in cache:
169
- print(f" Artist details {aid}")
170
  d = make_spotify_request(f"https://api.spotify.com/v1/artists/{aid}")
171
- cache[aid] = {
172
- 'genres':d.get('genres',[]) if d else [],
173
- 'followers':d.get('followers',{}).get('total',0) if d else 0,
174
- 'popularity':d.get('popularity',0) if d else 0
175
  }
176
  return cache[aid]
177
- rows = []
178
  for tr in tracks:
179
  if not tr: continue
180
- ma = tr['artists'][0] if tr.get('artists') else {}
181
- ai = get_artist(ma.get('id')) if ma.get('id') else {}
182
- dur = f"{tr['duration_ms']//60000}:{str(tr['duration_ms']%60000//1000).zfill(2)}"
183
- base = {
184
- 'Artist': ma.get('name','Unknown'),
185
- 'Title': tr.get('name','No title'),
186
- 'Album': tr.get('album',{}).get('name',''),
187
- 'ISRC': tr.get('external_ids',{}).get('isrc',''),
188
- 'Popularity': tr.get('popularity',0),
189
- 'Duration': dur,
190
- 'Release Date': tr.get('album',{}).get('release_date',''),
191
- 'Spotify URI': tr.get('external_urls',{}).get('spotify','')
192
  }
193
- if include_all_info:
194
  base.update({
195
- 'Genres': ', '.join(ai.get('genres',[])),
196
- 'Artist Followers': ai.get('followers',0),
197
- 'Artist Popularity': ai.get('popularity',0),
198
- 'Explicit': 'Yes' if tr.get('explicit') else 'No',
199
- 'Track ID': tr.get('id'),
200
- 'Album ID': tr.get('album',{}).get('id')
201
  })
202
  rows.append(base)
203
- df = pd.DataFrame(rows)
204
- df['dup_key'] = df.apply(lambda x: x['ISRC'] or f"{x['Artist']}|{x['Title']}", axis=1)
205
- df = df.drop_duplicates('dup_key').drop(columns='dup_key')
206
- print("📊 create_final_dataframe completado")
207
  return df
208
 
209
- def main_interface(project_name, spotify_urls, include_all_info=True):
210
- print(f"\n🎬 Inicio interface proyecto: {project_name}")
211
- urls = [u.strip() for u in spotify_urls.replace('\n',',').split(',') if u.strip()]
212
  if not urls:
213
- print("❌ No hay URLs válidas")
214
- return pd.DataFrame({"Error":["❌ Ingresa al menos una URL válida"]}), None
215
- tracks = process_tracks(urls)
216
- if not tracks:
217
- print("❌ No se encontraron tracks")
218
  return pd.DataFrame({"Error":["❌ No se encontraron tracks"]}), None
219
- df = create_final_dataframe(tracks, include_all_info)
220
  with NamedTemporaryFile(delete=False, suffix='.xlsx') as tmp:
221
  df.to_excel(tmp.name, index=False)
222
- fname = f"{project_name or 'spotify_tracks'}.xlsx"
223
- shutil.move(tmp.name, fname)
224
- print(f"✅ Proceso completado. Archivo: {fname}\n")
225
  return df, fname
226
 
227
- # Configuración de la interfaz
228
  iface = gr.Interface(
229
  fn=main_interface,
230
  inputs=[
@@ -237,10 +227,10 @@ iface = gr.Interface(
237
  gr.File(label="Descargar Excel")
238
  ],
239
  title="🎵 Spotify Track Collector PRO",
240
- description="Extrae info detallada de múltiples URLs de Spotify y genera un archivo Excel unificado",
241
  examples=[
242
- ["Classic Rock", "https://open.spotify.com/playlist/37i9dQZF1DXcBWIGoYBM5M\nhttps://open.spotify.com/album/1R5BORZZxNUg8QMgbqt0nd", True],
243
- ["Current Hits", "https://open.spotify.com/playlist/37i9dQZEtDXc8QFRQ4qegR", False]
244
  ],
245
  allow_flagging="never",
246
  theme=gr.themes.Soft()
 
1
+ # —————— PARCHES PARA SPACES ——————
2
+ # 1) Alias de pydantic.Schema para evitar el ImportError en FastAPI
3
+ import pydantic
4
+ pydantic.Schema = pydantic.Field
5
 
6
+ # 2) Monkey-patch para el bug de gradio_client.utils.get_type
7
+ import gradio_client.utils as client_utils
8
  _original_get_type = client_utils.get_type
9
  def patched_get_type(schema):
 
10
  if not isinstance(schema, dict):
11
  return "Any"
12
  return _original_get_type(schema)
 
13
  client_utils.get_type = patched_get_type
14
  # ————————————————————————————————
15
 
 
21
  import os
22
  from tempfile import NamedTemporaryFile
23
 
24
+ # Verifica la versión de Gradio
25
+ print("⚙️ Gradio version cargada:", gr.__version__)
 
26
 
27
  # Configuración de Credenciales
28
  client_ids = os.getenv("SPOTIFY_CLIENT_IDS", "").split(',')
29
  client_secrets = os.getenv("SPOTIFY_CLIENT_SECRETS", "").split(',')
 
30
  if not client_ids or not client_secrets:
31
  raise ValueError("❌ Missing environment variables SPOTIFY_CLIENT_IDS or SPOTIFY_CLIENT_SECRETS")
32
 
33
+ # Rotación de clientes
34
  current_client_index = 0
35
  request_counter = 0
36
+ MAX_REQUESTS_PER_CLIENT = 95
37
 
38
  def rotate_client():
39
  global current_client_index, request_counter
40
  current_client_index = (current_client_index + 1) % len(client_ids)
41
  request_counter = 0
42
+ print(f"🔄 Rotated to Spotify client #{current_client_index+1}")
43
 
44
  def get_spotify_token():
45
  global current_client_index
 
49
  print(f"🔑 Intentando token con cliente #{current_client_index+1}")
50
  resp = requests.post(
51
  'https://accounts.spotify.com/api/token',
52
+ headers={'Content-Type':'application/x-www-form-urlencoded'},
53
+ data={'grant_type':'client_credentials'},
54
  auth=(cid, csec)
55
  )
56
  print(f"➡️ get_spotify_token status={resp.status_code}")
57
  if resp.status_code == 200:
58
+ tok = resp.json().get('access_token')
59
+ print(f"✅ Token OK: {tok[:10]}…")
60
+ return tok
61
+ print(f"❌ Error token: {resp.text}")
62
+ rotate_client()
63
+ raise Exception("🚨 Todos los clientes fallaron")
 
64
 
65
  def make_spotify_request(url, params=None):
66
  global request_counter
67
  token = get_spotify_token()
68
+ print(f"🌐 GET {url} params={params} (token {token[:10]}…)")
69
+ headers = {'Authorization':f'Bearer {token}'}
70
+ for i in range(3):
71
+ print(f"🔁 intento #{i+1}")
 
 
 
72
  r = requests.get(url, headers=headers, params=params)
73
+ print(f"➡️ status {r.status_code}")
74
  request_counter += 1
75
  if r.status_code == 429:
76
+ wait = int(r.headers.get('Retry-After',10))
77
+ print(f"⚠️ Rate limit, espero {wait}s")
78
  rotate_client()
79
+ time.sleep(wait)
80
  continue
81
  if r.status_code == 200:
82
+ if request_counter>=MAX_REQUESTS_PER_CLIENT:
 
83
  rotate_client()
84
  return r.json()
85
  print(f"❌ Error {r.status_code}: {r.text}")
86
  time.sleep(2)
87
+ print("🚫 3 intentos fallidos")
88
  return None
89
 
90
+ def extract_spotify_id(url, resource):
91
+ clean = url.split('?')[0].replace("https://open.spotify.com/","")
92
  if "/intl-" in clean:
93
  return clean.split("/")[-1]
94
  parts = clean.split("/")
95
  try:
96
+ idx = parts.index(resource)
97
+ return parts[idx+1]
98
  except ValueError:
99
  for p in parts:
100
+ if len(p)==22 and resource in url:
101
  return p
102
+ print(f"❌ No pude extraer ID de {url}")
103
  return None
104
 
105
+ def get_playlist_tracks(u):
106
+ pid = extract_spotify_id(u, "playlist")
107
  if not pid: return []
108
+ print(f"🎧 Playlist {pid}")
109
+ al = []
110
  url = f"https://api.spotify.com/v1/playlists/{pid}/tracks"
111
+ p = {'market':'US','limit':100}
112
  while url:
113
+ d = make_spotify_request(url, p)
114
+ if not d: break
115
+ items = d.get('items',[])
116
+ print(f" recibí {len(items)} ítems")
117
+ al += [it['track'] for it in items if it.get('track')]
118
+ url = d.get('next')
119
+ print(f"✅ {len(al)} tracks en lista")
120
+ return al
121
+
122
+ def get_album_tracks(u):
123
+ aid = extract_spotify_id(u, "album")
124
  if not aid: return []
125
+ print(f"💿 Album {aid}")
126
+ al = []
127
  url = f"https://api.spotify.com/v1/albums/{aid}/tracks"
128
+ p = {'market':'US','limit':50}
129
  while url:
130
+ d = make_spotify_request(url, p)
131
  if not d: break
132
  for it in d.get('items',[]):
133
+ print(f" detalle track {it['id']}")
134
  td = make_spotify_request(f"https://api.spotify.com/v1/tracks/{it['id']}")
135
+ if td: al.append(td)
136
  url = d.get('next')
137
+ print(f"✅ {len(al)} tracks en álbum")
138
+ return al
139
 
140
  def process_tracks(urls):
141
+ print("🔍 process_tracks")
142
  all_t = []
143
  for u in urls:
144
+ print("➡️",u)
145
  try:
146
+ if "playlist" in u: all_t += get_playlist_tracks(u)
147
+ elif "album" in u: all_t += get_album_tracks(u)
 
 
148
  elif "track" in u:
149
+ tid = extract_spotify_id(u,"track")
150
  td = make_spotify_request(f"https://api.spotify.com/v1/tracks/{tid}")
151
  if td: all_t.append(td)
152
  except Exception as e:
153
  print(f"⚠️ Error en {u}: {e}")
154
+ print(f"🔍 total tracks: {len(all_t)}")
155
  return all_t
156
 
157
+ def create_final_dataframe(tracks, full=True):
158
+ print("📊 create_dataframe")
159
+ cache={}
160
+ def artist_details(aid):
161
  if aid not in cache:
162
+ print(" artist",aid)
163
  d = make_spotify_request(f"https://api.spotify.com/v1/artists/{aid}")
164
+ cache[aid]={
165
+ 'genres':d.get('genres',[]),
166
+ 'followers':d.get('followers',{}).get('total',0),
167
+ 'popularity':d.get('popularity',0)
168
  }
169
  return cache[aid]
170
+ rows=[]
171
  for tr in tracks:
172
  if not tr: continue
173
+ ma=tr['artists'][0] if tr.get('artists') else {}
174
+ ad=artist_details(ma.get('id')) if ma.get('id') else {}
175
+ dur=f"{tr['duration_ms']//60000}:{(tr['duration_ms']%60000//1000):02d}"
176
+ base={
177
+ 'Artist':ma.get('name',''),
178
+ 'Title':tr.get('name',''),
179
+ 'Album':tr.get('album',{}).get('name',''),
180
+ 'ISRC':tr.get('external_ids',{}).get('isrc',''),
181
+ 'Popularity':tr.get('popularity',0),
182
+ 'Duration':dur,
183
+ 'Release Date':tr.get('album',{}).get('release_date',''),
184
+ 'Spotify URI':tr.get('external_urls',{}).get('spotify','')
185
  }
186
+ if full:
187
  base.update({
188
+ 'Genres':', '.join(ad.get('genres',[])),
189
+ 'Artist Followers':ad.get('followers',0),
190
+ 'Artist Popularity':ad.get('popularity',0),
191
+ 'Explicit':'Yes' if tr.get('explicit') else 'No',
192
+ 'Track ID':tr.get('id'),
193
+ 'Album ID':tr.get('album',{}).get('id')
194
  })
195
  rows.append(base)
196
+ df=pd.DataFrame(rows)
197
+ df['dup'] = df.apply(lambda x: x['ISRC'] or f"{x['Artist']}|{x['Title']}",axis=1)
198
+ df=df.drop_duplicates('dup').drop(columns='dup')
199
+ print("📊 dataframe listo")
200
  return df
201
 
202
+ def main_interface(name, urls_txt, full=True):
203
+ print(f"\n🎬 Inicio: {name}")
204
+ urls=[u.strip() for u in urls_txt.replace('\n',',').split(',') if u.strip()]
205
  if not urls:
206
+ return pd.DataFrame({"Error":["❌ Mete al menos una URL"]}), None
207
+ tr=process_tracks(urls)
208
+ if not tr:
 
 
209
  return pd.DataFrame({"Error":["❌ No se encontraron tracks"]}), None
210
+ df=create_final_dataframe(tr, full)
211
  with NamedTemporaryFile(delete=False, suffix='.xlsx') as tmp:
212
  df.to_excel(tmp.name, index=False)
213
+ fname=f"{name or 'spotify_tracks'}.xlsx"
214
+ shutil.move(tmp.name,fname)
215
+ print("✅ Hecho:",fname)
216
  return df, fname
217
 
 
218
  iface = gr.Interface(
219
  fn=main_interface,
220
  inputs=[
 
227
  gr.File(label="Descargar Excel")
228
  ],
229
  title="🎵 Spotify Track Collector PRO",
230
+ description="Extrae info de varias URLs de Spotify y genera un Excel unificado",
231
  examples=[
232
+ ["Classic Rock", "https://open.spotify.com/playlist/37i9dQZF1DXcBWIGoYBM5M", True],
233
+ ["Current Hits", "https://open.spotify.com/playlist/37i9dQZEtDXc8QFRQ4qegR", False],
234
  ],
235
  allow_flagging="never",
236
  theme=gr.themes.Soft()