Fix: Force IPv4 and add Retry logic for stream 403s
This commit is contained in:
parent
20f48529ba
commit
3264286f7f
2 changed files with 42 additions and 4 deletions
|
|
@ -22,18 +22,46 @@ async def stream_audio(id: str, yt: YouTubeService = Depends(get_youtube_service
|
||||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
|
||||||
}
|
}
|
||||||
|
|
||||||
def iterfile():
|
# Helper function to get stream
|
||||||
with requests.get(stream_url, headers=headers, stream=True, timeout=10) as r:
|
def get_stream(url, headers):
|
||||||
|
return requests.get(url, headers=headers, stream=True, timeout=10)
|
||||||
|
|
||||||
|
try:
|
||||||
|
r = get_stream(stream_url, headers)
|
||||||
|
r.raise_for_status()
|
||||||
|
except requests.exceptions.HTTPError as e:
|
||||||
|
if e.response.status_code == 403:
|
||||||
|
print("Got 403 Forbidden. Invalidating cache and retrying...")
|
||||||
|
yt.invalidate_stream_cache(id)
|
||||||
|
# Fetch fresh
|
||||||
|
data = yt.get_stream_url(id)
|
||||||
|
if isinstance(data, dict):
|
||||||
|
stream_url = data.get("url")
|
||||||
|
headers = data.get("headers", {})
|
||||||
|
else:
|
||||||
|
stream_url = data
|
||||||
|
|
||||||
|
# Retry request
|
||||||
|
r = get_stream(stream_url, headers)
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
|
else:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
def iterfile():
|
||||||
|
# Already opened request 'r'
|
||||||
|
try:
|
||||||
for chunk in r.iter_content(chunk_size=64*1024):
|
for chunk in r.iter_content(chunk_size=64*1024):
|
||||||
yield chunk
|
yield chunk
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Chunk Error: {e}")
|
||||||
|
finally:
|
||||||
|
r.close()
|
||||||
|
|
||||||
return StreamingResponse(iterfile(), media_type="audio/mpeg")
|
return StreamingResponse(iterfile(), media_type="audio/mpeg")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Stream Error: {e}")
|
print(f"Stream Error: {e}")
|
||||||
# Return detail if it's a request error
|
|
||||||
if isinstance(e, requests.exceptions.HTTPError):
|
if isinstance(e, requests.exceptions.HTTPError):
|
||||||
print(f"Upstream Status: {e.response.status_code}")
|
print(f"Upstream Status: {e.response.status_code}")
|
||||||
raise HTTPException(status_code=500, detail=str(e))
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
@router.get("/download")
|
@router.get("/download")
|
||||||
|
|
|
||||||
|
|
@ -165,6 +165,7 @@ class YouTubeService:
|
||||||
'format': 'bestaudio[ext=m4a]/best[ext=mp4]/best',
|
'format': 'bestaudio[ext=m4a]/best[ext=mp4]/best',
|
||||||
'quiet': True,
|
'quiet': True,
|
||||||
'noplaylist': True,
|
'noplaylist': True,
|
||||||
|
'force_ipv4': True, # Force IPv4 to avoid cloud range blocks
|
||||||
}
|
}
|
||||||
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
||||||
info = ydl.extract_info(url, download=False)
|
info = ydl.extract_info(url, download=False)
|
||||||
|
|
@ -183,6 +184,15 @@ class YouTubeService:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ExternalAPIError(str(e))
|
raise ExternalAPIError(str(e))
|
||||||
|
|
||||||
|
def invalidate_stream_cache(self, id: str):
|
||||||
|
cache_key = f"stream:{id}"
|
||||||
|
path = self.cache._get_path(cache_key)
|
||||||
|
if path.exists():
|
||||||
|
try:
|
||||||
|
path.unlink()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
def get_recommendations(self, seed_id: str):
|
def get_recommendations(self, seed_id: str):
|
||||||
if not seed_id: return []
|
if not seed_id: return []
|
||||||
cache_key = f"rec:{seed_id}"
|
cache_key = f"rec:{seed_id}"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue