Mthrfkr commited on
Commit
22b0b45
·
verified ·
1 Parent(s): f2d2da0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +32 -235
app.py CHANGED
@@ -1,242 +1,39 @@
1
- # —————— PARCHE Pydantic para FastAPI ——————
2
- import pydantic
3
- # Alias Schema a Field para que fastapi.params no trunque al importar
4
- pydantic.Schema = pydantic.Field
5
-
6
- # —————— PARCHE GRADIO_CLIENT BOOLEANS ——————
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
-
16
  import gradio as gr
17
  import requests
18
- import pandas as pd
19
- import time
20
- import shutil
21
- import os
22
- from tempfile import NamedTemporaryFile
23
-
24
- # Verifica la versión de Gradio cargada
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
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
46
- for _ in range(len(client_ids)):
47
- cid = client_ids[current_client_index]
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}")
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
- 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(
221
- fn=main_interface,
222
- inputs=[
223
- gr.Textbox(label="Nombre del proyecto"),
224
- gr.Textbox(label="URLs de Spotify", lines=5),
225
- gr.Checkbox(label="Incluir información completa", value=True)
226
- ],
227
- outputs=[
228
- gr.Dataframe(label="Resultados"),
229
- gr.File(label="Descargar Excel")
230
- ],
231
- title="🎵 Spotify Track Collector PRO",
232
- description="Extrae info de varias URLs de Spotify y genera un Excel unificado",
233
- examples=[
234
- ["Classic Rock", "https://open.spotify.com/playlist/37i9dQZF1DXcBWIGoYBM5M", True],
235
- ["Current Hits", "https://open.spotify.com/playlist/37i9dQZEtDXc8QFRQ4qegR", False],
236
- ],
237
- allow_flagging="never",
238
- theme=gr.themes.Soft()
239
  )
240
 
241
  if __name__ == "__main__":
242
- iface.launch(server_name="0.0.0.0", server_port=7860)
 
1
+ import os
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  import gradio as gr
3
  import requests
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
+ # 1️⃣ LEE TUS CREDENCIALES DE ENV
6
+ SPOTIFY_CLIENT_ID = os.getenv("SPOTIFY_CLIENT_IDS", "").split(',')[0]
7
+ SPOTIFY_CLIENT_SECRET = os.getenv("SPOTIFY_CLIENT_SECRETS", "").split(',')[0]
8
+
9
+ def test_spotify_api():
10
+ """
11
+ Pide un token a Spotify y regresa 'OK' o el error que truene.
12
+ """
13
+ if not SPOTIFY_CLIENT_ID or not SPOTIFY_CLIENT_SECRET:
14
+ return "❌ No encontré SPOTIFY_CLIENT_IDS o SPOTIFY_CLIENT_SECRETS"
15
+
16
+ # 2️⃣ Solicita token
17
+ resp = requests.post(
18
+ "https://accounts.spotify.com/api/token",
19
+ headers={"Content-Type": "application/x-www-form-urlencoded"},
20
+ data={"grant_type": "client_credentials"},
21
+ auth=(SPOTIFY_CLIENT_ID, SPOTIFY_CLIENT_SECRET)
22
+ )
23
+ if resp.status_code == 200:
24
+ token = resp.json().get("access_token")
25
+ return f"✅ Token OK: {token[:20]}…"
26
+ else:
27
+ return f"❌ Status {resp.status_code}: {resp.text}"
28
+
29
+ # 3️⃣ Monta la interfaz Gradio
30
  iface = gr.Interface(
31
+ fn=test_spotify_api,
32
+ inputs=[],
33
+ outputs="text",
34
+ title="🛠️ Test Spotify API",
35
+ description="Pulsa el botón y checa si obtenemos token de Spotify"
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  )
37
 
38
  if __name__ == "__main__":
39
+ iface.launch()