Chrunos commited on
Commit
18310da
·
verified ·
1 Parent(s): 176be93

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +86 -23
app.py CHANGED
@@ -7,6 +7,11 @@ from bs4 import BeautifulSoup
7
  import os
8
  from datetime import datetime, timedelta
9
  import time
 
 
 
 
 
10
 
11
  # Configure logging
12
  logging.basicConfig(
@@ -104,28 +109,83 @@ def extract_track_id(track_url: str) -> str:
104
  logger.error(f"Failed to extract track ID from URL {track_url}: {str(e)}")
105
  raise HTTPException(status_code=400, detail="Invalid Spotify URL format")
106
 
107
- async def get_track_download_url(track_id: str) -> str:
108
- """Get download URL with fallback."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  apis = [
110
- f"https://downspotifyapis.vercel.app/api/v2/track/{track_id}",
111
- f"https://downspotifyapis.vercel.app/api/v1/track/{track_id}"
112
  ]
113
 
114
- for api_url in apis:
 
 
 
115
  try:
116
  logger.info(f"Attempting to get download URL from: {api_url}")
117
- response = requests.get(api_url, timeout=30)
 
 
 
 
 
 
 
118
  if response.status_code == 200:
119
  download_url = response.json().get("url")
120
  if download_url:
121
  logger.info(f"Successfully obtained download URL from {api_url}")
122
- return download_url
 
 
 
 
 
 
 
 
 
 
 
123
  except requests.exceptions.RequestException as e:
124
  logger.warning(f"Failed to get download URL from {api_url}: {str(e)}")
 
 
 
 
 
125
  continue
126
 
127
- logger.error(f"No download URL found for track {track_id}")
128
- raise HTTPException(status_code=404, detail="Download URL not found")
 
 
 
 
 
129
 
130
  @app.get("/get-track")
131
  async def get_track(
@@ -133,10 +193,7 @@ async def get_track(
133
  track_id: Optional[str] = None,
134
  track_url: Optional[str] = None
135
  ):
136
- """
137
- Get track information and download URL.
138
- Requires either track_id or track_url parameter.
139
- """
140
  request_id = datetime.now().strftime('%Y%m%d%H%M%S%f')
141
  logger.info(f"Request {request_id} started - Track ID: {track_id}, URL: {track_url}")
142
 
@@ -183,21 +240,26 @@ async def get_track(
183
 
184
  track_data = response.json()
185
 
186
- # Get download URL
187
- download_url = await get_track_download_url(track_id)
 
 
 
188
 
189
  # Prepare response
190
  result = {
191
  "track_id": track_id,
192
- "name": track_data["name"],
193
- "album": track_data["album"]["name"],
194
- "artist": track_data["artists"][0]["name"],
195
- "release_date": track_data["album"]["release_date"],
196
- "duration_ms": track_data["duration_ms"],
197
- "url": download_url
 
198
  }
199
 
200
- logger.info(f"Request {request_id} completed successfully in {time.time() - start_time:.2f}s")
 
201
  return result
202
 
203
  except HTTPException:
@@ -206,7 +268,8 @@ async def get_track(
206
  logger.error(f"Request {request_id} failed with error: {str(e)}")
207
  raise HTTPException(status_code=500, detail="Internal server error")
208
 
209
- @app.get("/health")
 
210
  async def health_check():
211
  """Health check endpoint."""
212
  try:
 
7
  import os
8
  from datetime import datetime, timedelta
9
  import time
10
+ import requests
11
+ from requests.adapters import HTTPAdapter
12
+ from urllib3.util.retry import Retry
13
+ import asyncio
14
+ from typing import Optional, Dict, Tuple
15
 
16
  # Configure logging
17
  logging.basicConfig(
 
109
  logger.error(f"Failed to extract track ID from URL {track_url}: {str(e)}")
110
  raise HTTPException(status_code=400, detail="Invalid Spotify URL format")
111
 
112
+ # Constants
113
+ DOWNLOAD_TIMEOUT = 30 # Increased timeout for download URLs
114
+ MAX_RETRIES = 3
115
+ BACKOFF_FACTOR = 0.5 # Will sleep for [0.5, 1.0, 2.0] seconds between retries
116
+
117
+ # Create a session with retry strategy
118
+ def create_request_session() -> requests.Session:
119
+ session = requests.Session()
120
+ retry_strategy = Retry(
121
+ total=MAX_RETRIES,
122
+ backoff_factor=BACKOFF_FACTOR,
123
+ status_forcelist=[408, 429, 500, 502, 503, 504],
124
+ )
125
+ adapter = HTTPAdapter(max_retries=retry_strategy)
126
+ session.mount("http://", adapter)
127
+ session.mount("https://", adapter)
128
+ return session
129
+
130
+ # Create a session to be reused
131
+ session = create_request_session()
132
+
133
+ async def get_track_download_url(track_id: str) -> Tuple[str, str]:
134
+ """
135
+ Get download URL with fallback and retry logic.
136
+ Returns tuple of (url, api_version) where api_version is 'v1' or 'v2'
137
+ """
138
  apis = [
139
+ ("https://downspotifyapis.vercel.app/api/v2/track/", "v2"),
140
+ ("https://downspotifyapis.vercel.app/api/v1/track/", "v1")
141
  ]
142
 
143
+ errors = []
144
+
145
+ for api_base, version in apis:
146
+ api_url = f"{api_base}{track_id}"
147
  try:
148
  logger.info(f"Attempting to get download URL from: {api_url}")
149
+
150
+ # Make the request with increased timeout and session
151
+ response = session.get(
152
+ api_url,
153
+ timeout=DOWNLOAD_TIMEOUT,
154
+ headers={'User-Agent': 'Mozilla/5.0'} # Adding user agent to prevent some blocks
155
+ )
156
+
157
  if response.status_code == 200:
158
  download_url = response.json().get("url")
159
  if download_url:
160
  logger.info(f"Successfully obtained download URL from {api_url}")
161
+ return download_url, version
162
+ else:
163
+ logger.warning(f"No URL in response from {api_url}")
164
+ errors.append(f"{version}: Empty URL in response")
165
+ else:
166
+ logger.warning(f"Failed response from {api_url}: {response.status_code}")
167
+ errors.append(f"{version}: Status {response.status_code}")
168
+
169
+ except requests.exceptions.Timeout as e:
170
+ logger.warning(f"Timeout for {api_url}: {str(e)}")
171
+ errors.append(f"{version}: Timeout after {DOWNLOAD_TIMEOUT}s")
172
+ continue
173
  except requests.exceptions.RequestException as e:
174
  logger.warning(f"Failed to get download URL from {api_url}: {str(e)}")
175
+ errors.append(f"{version}: {str(e)}")
176
+ continue
177
+ except Exception as e:
178
+ logger.error(f"Unexpected error for {api_url}: {str(e)}")
179
+ errors.append(f"{version}: Unexpected error - {str(e)}")
180
  continue
181
 
182
+ # If we get here, both APIs failed
183
+ error_msg = " | ".join(errors)
184
+ logger.error(f"All download URL attempts failed for track {track_id}: {error_msg}")
185
+ raise HTTPException(
186
+ status_code=404,
187
+ detail=f"Download URL not found. Errors: {error_msg}"
188
+ )
189
 
190
  @app.get("/get-track")
191
  async def get_track(
 
193
  track_id: Optional[str] = None,
194
  track_url: Optional[str] = None
195
  ):
196
+ """Get track information and download URL."""
 
 
 
197
  request_id = datetime.now().strftime('%Y%m%d%H%M%S%f')
198
  logger.info(f"Request {request_id} started - Track ID: {track_id}, URL: {track_url}")
199
 
 
240
 
241
  track_data = response.json()
242
 
243
+ # Get track metadata from Spotify
244
+ spotify_response = track_data
245
+
246
+ # Get download URL with version info
247
+ download_url, api_version = await get_track_download_url(track_id)
248
 
249
  # Prepare response
250
  result = {
251
  "track_id": track_id,
252
+ "name": spotify_response["name"],
253
+ "album": spotify_response["album"]["name"],
254
+ "artist": spotify_response["artists"][0]["name"],
255
+ "release_date": spotify_response["album"]["release_date"],
256
+ "duration_ms": spotify_response["duration_ms"],
257
+ "url": download_url,
258
+ "download_api_version": api_version
259
  }
260
 
261
+ duration = time.time() - start_time
262
+ logger.info(f"Request {request_id} completed successfully in {duration:.2f}s using API {api_version}")
263
  return result
264
 
265
  except HTTPException:
 
268
  logger.error(f"Request {request_id} failed with error: {str(e)}")
269
  raise HTTPException(status_code=500, detail="Internal server error")
270
 
271
+ # ... (rest of the code remains the same)
272
+ @app.get("/")
273
  async def health_check():
274
  """Health check endpoint."""
275
  try: