diff --git a/app.py b/app.py index 30a70dd..b54c7ad 100644 --- a/app.py +++ b/app.py @@ -452,7 +452,8 @@ 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') - fallback_clients = ['web', 'android', 'ios', 'tv'] + # 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: @@ -474,8 +475,13 @@ async def _ytdlp_extract(loop: asyncio.AbstractEventLoop, query: str) -> Optiona 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): + # 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 @@ -582,8 +588,11 @@ async def play(ctx: commands.Context, *, query: str): await ctx.reply("🔎 Searching…") item = await _create_audio_source(loop, query, state['volume']) if not item: - # Give a hint if cookies likely required - hint = "If this is YouTube, try setting YTDL_YT_CLIENT=web and/or provide cookies via YTDL_COOKIES_FILE or YTDL_COOKIES_B64." + # Give a hint if cookies or client change likely required + hint = ( + "If this is YouTube, try YTDL_YT_CLIENT=android (or 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) @@ -1812,11 +1821,23 @@ async def on_command_error(ctx, error): import traceback traceback.print_exc() - # Send detailed error to user if owner - if ctx.author.id == OWNER_ID: - await ctx.send(f"❌ **Error Details (Owner only):**\n```python\n{type(error).__name__}: {str(error)[:1800]}\n```") - else: - await ctx.send("❌ An unknown error occurred!") + # Send detailed error to user if owner; be safe for slash interactions + try: + content_owner = f"❌ **Error Details (Owner only):**\n```python\n{type(error).__name__}: {str(error)[:1800]}\n```" + content_user = "❌ An unknown error occurred!" + if hasattr(ctx, 'interaction') and ctx.interaction is not None: + if not ctx.interaction.response.is_done(): + await ctx.interaction.response.send_message(content_owner if ctx.author.id == OWNER_ID else content_user, ephemeral=ctx.author.id != OWNER_ID) + else: + await ctx.interaction.followup.send(content_owner if ctx.author.id == OWNER_ID else content_user, ephemeral=ctx.author.id != OWNER_ID) + else: + await ctx.send(content_owner if ctx.author.id == OWNER_ID else content_user) + except Exception as send_err: + # Final fallback: try reply, then ignore + try: + await ctx.reply("❌ Error occurred, and sending details failed.") + except Exception: + pass async def main(): """Main function to start the bot"""