cdnV2

git clone https://github.com/notamitgamer/cdnV2.git
· rebuilt hourlyOpen on GitHub
commit 9223d451e2839f3d7949ceb0fd7c2608c3a6a361
parent 215e9881058d37cf11c3f599c609ee4f55226d21
Author: Amit Dutta <mail@amit.is-a.dev>
Date:   Wed, 16 Sep 2026 22:12:42 +0530

fix#10

Diffstat:
Mapp/main.py | 67+++++++++++++++++++++++++++++++++++++++----------------------------
1 file changed, 39 insertions(+), 28 deletions(-)

diff --git a/app/main.py b/app/main.py @@ -308,49 +308,60 @@ async def url_ytmusic_page(request: Request): return templates.TemplateResponse(request, "ytmusic.html", ctx) # --------------------------------------------------------------------------------- -# YTMusic routes: Local extraction using yt-dlp (No Third-Party APIs) +# YTMusic routes: Cobalt API Proxy # --------------------------------------------------------------------------------- -import yt_dlp - class YtRequest(BaseModel): url: str @app.post("/api/yt/download") -async def yt_download_local(request: Request, body: YtRequest): +async def yt_download_cobalt(request: Request, body: YtRequest): url = body.url.strip() if not url: raise HTTPException(status_code=400, detail="Missing YouTube URL.") - # Configure yt-dlp to extract the best audio-only URL without downloading - ydl_opts = { - 'format': 'm4a/bestaudio/best', - 'quiet': True, - 'no_warnings': True, - 'skip_download': True, - 'noplaylist': True, + # Defaults to public instance. Can be overridden in Render Environment Variables later if needed. + cobalt_api = os.getenv("COBALT_API_URL", "https://api.cobalt.tools/api/json") + + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + # Spoofing a standard browser to ensure the public API accepts the request + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36" } - def _extract(): - with yt_dlp.YoutubeDL(ydl_opts) as ydl: - return ydl.extract_info(url, download=False) + # Cobalt v11 API Payload + payload = { + "url": url, + "isAudioOnly": True, + "audioFormat": "mp3" + } - try: - # Run in a thread pool so it doesn't freeze the FastAPI async event loop - info = await asyncio.to_thread(_extract) - - dl_url = info.get('url') - if not dl_url and 'requested_formats' in info: - dl_url = info['requested_formats'][0]['url'] + async with httpx.AsyncClient(timeout=60.0) as client: + try: + res = await client.post(cobalt_api, json=payload, headers=headers) + + if res.status_code != 200: + err_text = res.text + try: + err_text = res.json().get("text", err_text) + except Exception: + pass + raise HTTPException(status_code=400, detail=f"Cobalt API failed: {err_text}") - if not dl_url: - raise ValueError("No audio stream URL could be found in the video metadata.") + data = res.json() + status = data.get("status") - return {"url": dl_url, "title": info.get("title", "Audio")} + if status == "error": + raise HTTPException(status_code=400, detail=data.get("text", "Unknown Cobalt error")) + + dl_url = data.get("url") + if not dl_url: + raise HTTPException(status_code=500, detail="Cobalt succeeded but returned no download link.") + + return {"url": dl_url, "title": "Audio"} - except yt_dlp.utils.DownloadError as e: - raise HTTPException(status_code=400, detail=f"Extraction failed: {str(e)}") - except Exception as e: - raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}") + except httpx.RequestError as exc: + raise HTTPException(status_code=502, detail=f"Error connecting to Cobalt: {exc}") # --------------------------------------------------------------------------------- @app.get("/shorten")