Erfan commited on
Commit ·
0e4bd6e
1
Parent(s): ad6c7a2
fix: multiple format attempts per yt-dlp client + ytdl.ga API fallback
Browse files- Fix 'Requested format not available' by trying 3 format strings per client
- Use bv*+ba/b (best video+audio merge) as primary format
- Add ytdl.ga and najemi.cz PHP API endpoints as fallback
- Prioritize yt-dlp (with cookies) then ytdl.ga then rest
- bot/youtube.py +68 -23
bot/youtube.py
CHANGED
|
@@ -170,35 +170,76 @@ def _has_cookies() -> bool:
|
|
| 170 |
|
| 171 |
async def _try_ytdlp(url: str, quality: str = "audio"):
|
| 172 |
if quality == "audio":
|
| 173 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 174 |
else:
|
| 175 |
-
|
| 176 |
clients = ["android_embedded", "android", "web", "tv", "ios", "web_creator", "music"]
|
| 177 |
cookies = _has_cookies()
|
| 178 |
for client in clients:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 179 |
try:
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
"
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
if
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
|
|
|
|
|
|
|
|
|
| 200 |
except Exception as e:
|
| 201 |
-
_log(f"
|
| 202 |
return None, None
|
| 203 |
|
| 204 |
|
|
@@ -229,6 +270,10 @@ async def download_yt(url: str, quality: str = "audio"):
|
|
| 229 |
if result[0]:
|
| 230 |
return result
|
| 231 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 232 |
result = await _try_piped(url, quality)
|
| 233 |
if result[0]:
|
| 234 |
return result
|
|
|
|
| 170 |
|
| 171 |
async def _try_ytdlp(url: str, quality: str = "audio"):
|
| 172 |
if quality == "audio":
|
| 173 |
+
formats = ["bestaudio/best", "bestaudio", "worstaudio"]
|
| 174 |
+
elif quality == "1080p":
|
| 175 |
+
formats = ["bv*[height<=1080]+ba/b[height<=1080]", "bestvideo[height<=1080]+bestaudio/best[height<=1080]", "best[height<=1080]"]
|
| 176 |
+
elif quality == "720p":
|
| 177 |
+
formats = ["bv*[height<=720]+ba/b[height<=720]", "bestvideo[height<=720]+bestaudio/best[height<=720]", "best[height<=720]"]
|
| 178 |
else:
|
| 179 |
+
formats = ["bv*+ba/b", "bestvideo+bestaudio/best", "best"]
|
| 180 |
clients = ["android_embedded", "android", "web", "tv", "ios", "web_creator", "music"]
|
| 181 |
cookies = _has_cookies()
|
| 182 |
for client in clients:
|
| 183 |
+
for fmt in formats:
|
| 184 |
+
try:
|
| 185 |
+
_log(f"Trying yt-dlp client={client} fmt={fmt}{' (with cookies)' if cookies else ''}")
|
| 186 |
+
opts = {
|
| 187 |
+
"format": fmt,
|
| 188 |
+
"outtmpl": os.path.join(DOWNLOAD_DIR, f"{uuid.uuid4()}.%(ext)s"),
|
| 189 |
+
"quiet": True,
|
| 190 |
+
"no_warnings": True,
|
| 191 |
+
"extractor_args": {"youtube": {"player_client": [client]}},
|
| 192 |
+
}
|
| 193 |
+
if cookies:
|
| 194 |
+
opts["cookiefile"] = COOKIES_FILE
|
| 195 |
+
with yt_dlp.YoutubeDL(opts) as ydl:
|
| 196 |
+
info = ydl.extract_info(url, download=True)
|
| 197 |
+
file_path = ydl.prepare_filename(info)
|
| 198 |
+
if os.path.exists(file_path):
|
| 199 |
+
_log(f"SUCCESS via yt-dlp client={client} fmt={fmt}")
|
| 200 |
+
return file_path, info.get("title", "video")
|
| 201 |
+
found = _find_downloaded_file()
|
| 202 |
+
if found:
|
| 203 |
+
_log(f"SUCCESS via yt-dlp client={client} fmt={fmt}")
|
| 204 |
+
return found, info.get("title", "video")
|
| 205 |
+
except Exception as e:
|
| 206 |
+
_log(f"yt-dlp client={client} fmt={fmt} failed: {e}")
|
| 207 |
+
return None, None
|
| 208 |
+
|
| 209 |
+
|
| 210 |
+
async def _try_ytdlp_ga(url: str, quality: str = "audio"):
|
| 211 |
+
_log("Trying ytdl.ga API...")
|
| 212 |
+
endpoints = [
|
| 213 |
+
"https://ytdl.ga/handler.php",
|
| 214 |
+
"https://najemi.cz/ytdl/handler.php",
|
| 215 |
+
]
|
| 216 |
+
for endpoint in endpoints:
|
| 217 |
try:
|
| 218 |
+
async with httpx.AsyncClient(timeout=30, follow_redirects=True) as client:
|
| 219 |
+
r = await client.get(endpoint, params={"url": url})
|
| 220 |
+
if r.status_code != 200:
|
| 221 |
+
_log(f"ytdl.ga {endpoint} returned {r.status_code}")
|
| 222 |
+
continue
|
| 223 |
+
data = r.json() if r.headers.get("content-type", "").startswith("application/json") else r.text
|
| 224 |
+
if isinstance(data, dict):
|
| 225 |
+
dl_url = data.get("url") or data.get("download") or data.get("link")
|
| 226 |
+
else:
|
| 227 |
+
dl_url = str(data).strip()
|
| 228 |
+
if not dl_url or dl_url == url:
|
| 229 |
+
continue
|
| 230 |
+
ext = ".mp4"
|
| 231 |
+
if quality == "audio":
|
| 232 |
+
ext = ".mp3"
|
| 233 |
+
filename = f"{uuid.uuid4()}{ext}"
|
| 234 |
+
filepath = os.path.join(DOWNLOAD_DIR, filename)
|
| 235 |
+
r2 = await client.get(dl_url, follow_redirects=True, timeout=120)
|
| 236 |
+
if r2.status_code == 200:
|
| 237 |
+
with open(filepath, "wb") as f:
|
| 238 |
+
f.write(r2.content)
|
| 239 |
+
_log(f"SUCCESS via ytdl.ga ({endpoint})")
|
| 240 |
+
return filepath, "YouTube Video"
|
| 241 |
except Exception as e:
|
| 242 |
+
_log(f"ytdl.ga {endpoint} failed: {e}")
|
| 243 |
return None, None
|
| 244 |
|
| 245 |
|
|
|
|
| 270 |
if result[0]:
|
| 271 |
return result
|
| 272 |
|
| 273 |
+
result = await _try_ytdlp_ga(url, quality)
|
| 274 |
+
if result[0]:
|
| 275 |
+
return result
|
| 276 |
+
|
| 277 |
result = await _try_piped(url, quality)
|
| 278 |
if result[0]:
|
| 279 |
return result
|