from fastapi import APIRouter, HTTPException, Depends from fastapi.responses import StreamingResponse from backend.services.youtube import YouTubeService import requests router = APIRouter() def get_youtube_service(): return YouTubeService() @router.get("/stream") async def stream_audio(id: str, yt: YouTubeService = Depends(get_youtube_service)): try: data = yt.get_stream_url(id) if isinstance(data, dict): stream_url = data.get("url") headers = data.get("headers", {}) else: # Fallback for old cached string values stream_url = data headers = { "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" } # 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}") if isinstance(e, requests.exceptions.HTTPError): print(f"Upstream Status: {e.response.status_code}") raise HTTPException(status_code=500, detail=str(e)) @router.get("/download") async def download_audio(id: str, title: str = "audio", yt: YouTubeService = Depends(get_youtube_service)): try: data = yt.get_stream_url(id) if isinstance(data, dict): stream_url = data.get("url") headers = data.get("headers", {}) else: stream_url = data headers = { "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: r.raise_for_status() for chunk in r.iter_content(chunk_size=1024*1024): yield chunk safe_filename = "".join([c for c in title if c.isalnum() or c in (' ', '-', '_')]).strip() final_headers = { "Content-Disposition": f'attachment; filename="{safe_filename}.mp3"' } return StreamingResponse(iterfile(), media_type="audio/mpeg", headers=final_headers) except Exception as e: raise HTTPException(status_code=500, detail=str(e))