From 3264286f7f360eae18f1d4ef01b1aa3302e36961 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 1 Jan 2026 13:21:07 +0700 Subject: [PATCH] Fix: Force IPv4 and add Retry logic for stream 403s --- backend/api/endpoints/stream.py | 36 +++++++++++++++++++++++++++++---- backend/services/youtube.py | 10 +++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/backend/api/endpoints/stream.py b/backend/api/endpoints/stream.py index 1da80be..86f8c5d 100644 --- a/backend/api/endpoints/stream.py +++ b/backend/api/endpoints/stream.py @@ -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" } - def iterfile(): - with requests.get(stream_url, headers=headers, stream=True, timeout=10) as r: + # Helper function to get stream + 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() + else: + raise e + + def iterfile(): + # Already opened request 'r' + try: for chunk in r.iter_content(chunk_size=64*1024): yield chunk + except Exception as e: + print(f"Chunk Error: {e}") + finally: + r.close() return StreamingResponse(iterfile(), media_type="audio/mpeg") except Exception as e: print(f"Stream Error: {e}") - # Return detail if it's a request error 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)) @router.get("/download") diff --git a/backend/services/youtube.py b/backend/services/youtube.py index b2186b8..252ac91 100644 --- a/backend/services/youtube.py +++ b/backend/services/youtube.py @@ -165,6 +165,7 @@ class YouTubeService: 'format': 'bestaudio[ext=m4a]/best[ext=mp4]/best', 'quiet': True, 'noplaylist': True, + 'force_ipv4': True, # Force IPv4 to avoid cloud range blocks } with yt_dlp.YoutubeDL(ydl_opts) as ydl: info = ydl.extract_info(url, download=False) @@ -183,6 +184,15 @@ class YouTubeService: except Exception as 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): if not seed_id: return [] cache_key = f"rec:{seed_id}"