From c25376749737e5c8376a124fff984d02632b4815 Mon Sep 17 00:00:00 2001 From: SimolZimol <70102430+SimolZimol@users.noreply.github.com> Date: Tue, 28 Oct 2025 01:49:21 +0100 Subject: [PATCH] modified: app.py --- app.py | 118 +++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 76 insertions(+), 42 deletions(-) diff --git a/app.py b/app.py index b54c7ad..12e9da8 100644 --- a/app.py +++ b/app.py @@ -374,14 +374,14 @@ except Exception as e: print(f"⚠️ Cookie configuration error: {e}") YTDL_OPTS = { - 'format': 'bestaudio/best', + 'format': 'bestaudio[ext=m4a]/bestaudio/best', 'noplaylist': True, 'quiet': True, 'default_search': 'ytsearch', 'skip_download': True, } -def get_ytdl_opts(client_hint: Optional[str] = None) -> Dict: +def get_ytdl_opts(client_hint: Optional[str] = None, use_cookies: bool = True) -> Dict: """Build yt-dlp options dynamically, injecting cookies and headers if configured. client_hint: preferred YouTube client (e.g., 'android', 'web', 'ios', 'tv'). """ @@ -400,10 +400,14 @@ def get_ytdl_opts(client_hint: Optional[str] = None) -> Dict: # player_client hint opts['extractor_args']['youtube']['player_client'] = [yt_client] # Use cookies if available - if YTDL_COOKIEFILE: - opts['cookiefile'] = YTDL_COOKIEFILE - elif YTDL_COOKIESFROMBROWSER: - opts['cookiesfrombrowser'] = YTDL_COOKIESFROMBROWSER + if use_cookies: + if YTDL_COOKIEFILE: + opts['cookiefile'] = YTDL_COOKIEFILE + elif YTDL_COOKIESFROMBROWSER: + opts['cookiesfrombrowser'] = YTDL_COOKIESFROMBROWSER + # Optional force IPv4 (set YTDL_FORCE_IPV4=1) + if os.getenv('YTDL_FORCE_IPV4'): + opts['force_ipv4'] = True return opts FFMPEG_BEFORE = "-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5" @@ -452,47 +456,77 @@ async def _ytdlp_extract(loop: asyncio.AbstractEventLoop, query: str) -> Optiona # Try multiple YouTube client profiles to bypass some restrictions env_client = os.getenv('YTDL_YT_CLIENT', 'android') - # Prefer android first in fallbacks to avoid SABR issues on web - fallback_clients = ['android', 'web', 'ios', 'tv'] - clients_to_try: List[str] = [] - for c in [env_client] + fallback_clients: - if c not in clients_to_try: - clients_to_try.append(c) + cookies_present = bool(YTDL_COOKIEFILE or YTDL_COOKIESFROMBROWSER) + + # Helper to try a sequence of clients with/without cookies + async def try_clients(clients: List[str], use_cookies: bool) -> Optional[Dict]: + nonlocal query + last_err: Optional[Exception] = None + for client in clients: + def _extract_with_client(): + with ytdlp.YoutubeDL(get_ytdl_opts(client, use_cookies=use_cookies)) as ytdl: + return ytdl.extract_info(query, download=False) + try: + info = await loop.run_in_executor(executor, _extract_with_client) + if info is None: + continue + if 'entries' in info: + info = info['entries'][0] + return info + except Exception as e: + last_err = e + msg = str(e) + print(f"⚠️ yt-dlp attempt failed (cookies={'on' if use_cookies else 'off'}) client='{client}': {msg[:200]}") + retriable_markers = ( + 'Sign in to confirm', 'Use --cookies', 'pass cookies', 'HTTP Error 403', + 'Signature extraction failed', 'Requested format is not available', + 'Only images are available', 'missing a url', 'SABR streaming' + ) + if any(m in msg for m in retriable_markers): + continue + else: + break + if last_err: + return None + return None + + # Build ordered lists + # When cookies are present, android/ios are skipped by yt-dlp; so try web/tv first with cookies. + # If that fails (e.g., SABR), try without cookies to enable android extraction. + cookie_clients: List[str] = [] + for c in [env_client, 'web', 'tv']: + if c in ('web', 'tv') and c not in cookie_clients: + cookie_clients.append(c) + nocookie_clients: List[str] = [] + for c in [env_client, 'android', 'ios', 'web', 'tv']: + if c not in nocookie_clients: + nocookie_clients.append(c) last_error: Optional[Exception] = None - for client in clients_to_try: - def _extract_with_client(): - with ytdlp.YoutubeDL(get_ytdl_opts(client)) as ytdl: - return ytdl.extract_info(query, download=False) - try: - info = await loop.run_in_executor(executor, _extract_with_client) - if info is None: - continue - if 'entries' in info: - info = info['entries'][0] + # Pass 1: with cookies (if provided) + if cookies_present: + info = await try_clients(cookie_clients, use_cookies=True) + if info: + return info + # Pass 2: without cookies to allow android/ios + info = await try_clients(nocookie_clients, use_cookies=False) + if info: + return info + else: + # No cookies: try standard nocookie list + info = await try_clients(nocookie_clients, use_cookies=False) + if info: return info - except Exception as e: - last_error = e - msg = str(e) - print(f"⚠️ yt-dlp attempt failed with client='{client}': {msg[:200]}") - # If it's clearly a bot-check/cookies or client format/signature issue, try next client - retriable_markers = ( - 'Sign in to confirm', 'Use --cookies', 'pass cookies', 'HTTP Error 403', - 'Signature extraction failed', 'Requested format is not available', - 'Only images are available', 'missing a url', 'SABR streaming' - ) - if any(m in msg for m in retriable_markers): - continue - # Otherwise, break early for other errors - break # All attempts failed - if last_error: - msg = str(last_error) - if 'Sign in to confirm' in msg or 'Use --cookies' in msg or 'pass cookies' in msg: - print("❌ yt-dlp error: YouTube requires cookies to proceed.") - print(" Provide YTDL_COOKIES_FILE or YTDL_COOKIES_B64 (Netscape cookies.txt).") - traceback.print_exc() + # At this point, all attempts failed; print last traceback if any + try: + if cookies_present: + print("❌ yt-dlp: All attempts failed with and without cookies. Consider trying another client or refreshing cookies.") + else: + print("❌ yt-dlp: All attempts failed without cookies. Consider providing cookies or changing YTDL_YT_CLIENT.") + except Exception: + pass return None async def _create_audio_source(loop: asyncio.AbstractEventLoop, search: str, volume: float):