From 9c7db068c051dece9bc683c4efba18bcad8f60d7 Mon Sep 17 00:00:00 2001 From: SimolZimol <70102430+SimolZimol@users.noreply.github.com> Date: Tue, 28 Oct 2025 01:32:16 +0100 Subject: [PATCH] modified: app.py --- app.py | 64 ++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/app.py b/app.py index 1c3543f..30a70dd 100644 --- a/app.py +++ b/app.py @@ -381,20 +381,23 @@ YTDL_OPTS = { 'skip_download': True, } -def get_ytdl_opts() -> Dict: - """Build yt-dlp options dynamically, injecting cookies and headers if configured.""" +def get_ytdl_opts(client_hint: Optional[str] = None) -> Dict: + """Build yt-dlp options dynamically, injecting cookies and headers if configured. + client_hint: preferred YouTube client (e.g., 'android', 'web', 'ios', 'tv'). + """ opts = dict(YTDL_OPTS) # UA and extractor tweaks ua = os.getenv('YTDL_UA') or ( 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ' '(KHTML, like Gecko) Chrome/118.0 Safari/537.36' ) - yt_client = os.getenv('YTDL_YT_CLIENT', 'android') + yt_client_env = os.getenv('YTDL_YT_CLIENT', 'android') + yt_client = client_hint or yt_client_env opts['http_headers'] = {'User-Agent': ua} # Extractor args can help avoid some player config checks opts.setdefault('extractor_args', {}) opts['extractor_args'].setdefault('youtube', {}) - # player_client hint (e.g., android) + # player_client hint opts['extractor_args']['youtube']['player_client'] = [yt_client] # Use cookies if available if YTDL_COOKIEFILE: @@ -446,24 +449,45 @@ async def _ensure_connected(ctx: commands.Context) -> Optional[discord.VoiceClie async def _ytdlp_extract(loop: asyncio.AbstractEventLoop, query: str) -> Optional[Dict]: if not ytdlp: return None - def _extract(): - with ytdlp.YoutubeDL(get_ytdl_opts()) as ytdl: - return ytdl.extract_info(query, download=False) - try: - info = await loop.run_in_executor(executor, _extract) - if info is None: - return None - if 'entries' in info: - info = info['entries'][0] - return info - except Exception as e: - # Make YouTube cookie issues clearer in logs - msg = str(e) - if 'Sign in to confirm you’re not a bot' in msg or 'Use --cookies' in msg or 'pass cookies' in msg: + + # Try multiple YouTube client profiles to bypass some restrictions + env_client = os.getenv('YTDL_YT_CLIENT', 'android') + fallback_clients = ['web', 'android', '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) + + 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] + 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 issue, try next client + if ('Sign in to confirm' in msg) or ('Use --cookies' in msg) or ('pass cookies' in msg) or ('HTTP Error 403' in msg): + 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() - return None + return None async def _create_audio_source(loop: asyncio.AbstractEventLoop, search: str, volume: float): # Accept either URL or search text; prepend ytsearch1: if not a URL @@ -559,7 +583,7 @@ async def play(ctx: commands.Context, *, query: str): item = await _create_audio_source(loop, query, state['volume']) if not item: # Give a hint if cookies likely required - hint = "If this is YouTube, the server IP may be challenged. Provide YTDL_COOKIES_FILE or YTDL_COOKIES_B64." + hint = "If this is YouTube, try setting YTDL_YT_CLIENT=web and/or provide cookies via YTDL_COOKIES_FILE or YTDL_COOKIES_B64." await ctx.reply(f"❌ Couldn't get audio from that query.\n{hint}") return state['queue'].append(item)