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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +79 -77
app.py CHANGED
@@ -1,12 +1,12 @@
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)
@@ -24,11 +24,11 @@ from tempfile import NamedTemporaryFile
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
@@ -48,38 +48,38 @@ def get_spotify_token():
48
  csec = client_secrets[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}")
@@ -88,7 +88,7 @@ def make_spotify_request(url, params=None):
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("/")
@@ -106,113 +106,115 @@ 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(
 
1
+ # —————— PARCHE FASTAPI + GRADIO_CLIENT ——————
2
+ import fastapi.params
3
+ # Creamos un Schema dummy para que FastAPI no explote al hacer `class Param(Schema):`
4
+ fastapi.params.Schema = type("Schema", (object,), {})
5
 
 
6
  import gradio_client.utils as client_utils
7
  _original_get_type = client_utils.get_type
8
  def patched_get_type(schema):
9
+ # Si el schema no es dict (p. ej. un bool), devolvemos "Any" en lugar de crashear
10
  if not isinstance(schema, dict):
11
  return "Any"
12
  return _original_get_type(schema)
 
24
  # Verifica la versión de Gradio
25
  print("⚙️ Gradio version cargada:", gr.__version__)
26
 
27
+ # Configuración de credenciales Spotify
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("❌ Faltan SPOTIFY_CLIENT_IDS o SPOTIFY_CLIENT_SECRETS")
32
 
33
  # Rotación de clientes
34
  current_client_index = 0
 
48
  csec = client_secrets[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 al obtener token")
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}")
 
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("/")
 
106
  pid = extract_spotify_id(u, "playlist")
107
  if not pid: return []
108
  print(f"🎧 Playlist {pid}")
109
+ all_t = []
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)} items")
117
+ all_t += [it["track"] for it in items if it.get("track")]
118
+ url = d.get("next")
119
+ print(f"✅ {len(all_t)} tracks en la lista")
120
+ return all_t
121
 
122
  def get_album_tracks(u):
123
  aid = extract_spotify_id(u, "album")
124
  if not aid: return []
125
+ print(f"💿 Álbum {aid}")
126
+ all_t = []
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: all_t.append(td)
136
+ url = d.get("next")
137
+ print(f"✅ {len(all_t)} tracks en el álbum")
138
+ return all_t
139
 
140
  def process_tracks(urls):
141
+ print("🔍 process_tracks iniciado")
142
  all_t = []
143
  for u in urls:
144
+ print(f"➡️ Procesando {u}")
145
  try:
146
+ if "playlist" in u:
147
+ all_t += get_playlist_tracks(u)
148
+ elif "album" in u:
149
+ all_t += get_album_tracks(u)
150
  elif "track" in u:
151
+ tid = extract_spotify_id(u, "track")
152
  td = make_spotify_request(f"https://api.spotify.com/v1/tracks/{tid}")
153
  if td: all_t.append(td)
154
  except Exception as e:
155
  print(f"⚠️ Error en {u}: {e}")
156
+ print(f"🔍 process_tracks terminó con {len(all_t)} tracks")
157
  return all_t
158
 
159
  def create_final_dataframe(tracks, full=True):
160
+ print("📊 create_dataframe iniciado")
161
+ cache = {}
162
  def artist_details(aid):
163
  if aid not in cache:
164
+ print(f" artist {aid}")
165
  d = make_spotify_request(f"https://api.spotify.com/v1/artists/{aid}")
166
+ cache[aid] = {
167
+ "genres": d.get("genres",[]),
168
+ "followers": d.get("followers",{}).get("total",0),
169
+ "popularity": d.get("popularity",0)
170
  }
171
  return cache[aid]
172
+ rows = []
173
  for tr in tracks:
174
  if not tr: continue
175
+ ma = tr["artists"][0] if tr.get("artists") else {}
176
+ ad = artist_details(ma.get("id")) if ma.get("id") else {}
177
+ dur = f"{tr['duration_ms']//60000}:{(tr['duration_ms']%60000//1000):02d}"
178
+ base = {
179
+ "Artist": ma.get("name",""),
180
+ "Title": tr.get("name",""),
181
+ "Album": tr.get("album",{}).get("name",""),
182
+ "ISRC": tr.get("external_ids",{}).get("isrc",""),
183
+ "Popularity": tr.get("popularity",0),
184
+ "Duration": dur,
185
+ "Release Date": tr.get("album",{}).get("release_date",""),
186
+ "Spotify URI": tr.get("external_urls",{}).get("spotify","")
187
  }
188
  if full:
189
  base.update({
190
+ "Genres": ", ".join(ad.get("genres",[])),
191
+ "Artist Followers": ad.get("followers",0),
192
+ "Artist Popularity": ad.get("popularity",0),
193
+ "Explicit": "Yes" if tr.get("explicit") else "No",
194
+ "Track ID": tr.get("id"),
195
+ "Album ID": tr.get("album",{}).get("id")
196
  })
197
  rows.append(base)
198
+ df = pd.DataFrame(rows)
199
+ df["dup"] = df.apply(lambda x: x["ISRC"] or f"{x['Artist']}|{x['Title']}", axis=1)
200
+ df = df.drop_duplicates("dup").drop(columns="dup")
201
  print("📊 dataframe listo")
202
  return df
203
 
204
  def main_interface(name, urls_txt, full=True):
205
+ print(f"\n🎬 Inicio proyecto: {name}")
206
+ urls = [u.strip() for u in urls_txt.replace("\n",",").split(",") if u.strip()]
207
  if not urls:
208
+ return pd.DataFrame({"Error": ["❌ Ingresa al menos una URL"]}), None
209
+ tr = process_tracks(urls)
210
  if not tr:
211
+ return pd.DataFrame({"Error": ["❌ No se encontraron tracks"]}), None
212
+ df = create_final_dataframe(tr, full)
213
+ with NamedTemporaryFile(delete=False, suffix=".xlsx") as tmp:
214
  df.to_excel(tmp.name, index=False)
215
+ fname = f"{name or 'spotify_tracks'}.xlsx"
216
+ shutil.move(tmp.name, fname)
217
+ print("✅ Hecho:", fname)
218
  return df, fname
219
 
220
  iface = gr.Interface(