import discord from discord.ext import commands import gradio as gr from dotenv import load_dotenv import os import threading import asyncio import yt_dlp # Load environment variables load_dotenv() # Check if cookies.txt exists if not os.path.exists('cookies.txt'): txt = os.getenv('COOKIES') with open('cookies.txt', 'w') as f: f.write(txt) # Bot configuration intents = discord.Intents.default() intents.message_content = True bot = commands.Bot(command_prefix='!', intents=intents) class MusicBot: def __init__(self): self.is_playing = False self.voice_client = None self.queue = [] self.ydl_opts = { 'format': 'bestaudio/best', 'cookiefile': 'cookies.txt', 'quiet': True, 'no_warnings': True, 'extract_flat': 'in_playlist' } async def join_voice(self, ctx): if ctx.author.voice: channel = ctx.author.voice.channel if self.voice_client is None: self.voice_client = await channel.connect() else: await self.voice_client.move_to(channel) return True return False async def play_next(self, ctx): if self.queue and not self.is_playing: self.is_playing = True url = self.queue.pop(0) try: ydl_opts = { 'format': 'bestaudio/best', 'cookiefile': 'cookies.txt', 'quiet': True, 'no_warnings': True, 'extract_flat': False, # Changed this to False for proper extraction 'postprocessors': [{ 'key': 'FFmpegExtractAudio', 'preferredcodec': 'opus', }] } with yt_dlp.YoutubeDL(ydl_opts) as ydl: info = ydl.extract_info(url, download=False) if 'entries' in info: # It's a playlist audio_url = info['entries'][0]['url'] title = info['entries'][0].get('title', 'Unknown title') else: # It's a single video audio_url = info['url'] title = info.get('title', 'Unknown title') # Updated FFmpeg options for better streaming ffmpeg_options = { 'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', 'options': '-vn -filter:a "volume=0.5"' } audio_source = discord.FFmpegPCMAudio(audio_url, **ffmpeg_options) def after_playing(error): self.is_playing = False if error: print(f"Playback error: {error}") asyncio.run_coroutine_threadsafe(self.play_next(ctx), bot.loop) self.voice_client.play(audio_source, after=after_playing) await ctx.send(f"Now playing: {title}") except yt_dlp.utils.DownloadError as e: print(f"YT-DLP Error for URL {url}: {e}") self.is_playing = False await ctx.send("Error playing the song: Could not extract audio stream") await self.play_next(ctx) except Exception as e: print(f"General Error playing URL {url}: {e}") self.is_playing = False await ctx.send("An unexpected error occurred while playing the song") await self.play_next(ctx) async def get_queue_info(self): if not self.queue: return "Queue is empty" try: queue_info = [] with yt_dlp.YoutubeDL(self.ydl_opts) as ydl: for i, url in enumerate(self.queue, 1): info = ydl.extract_info(url, download=False) title = info.get('title', 'Unknown title') queue_info.append(f"{i}. {title}") return "\n".join(queue_info) except Exception as e: return f"Error fetching queue info: {str(e)}" music_bot = MusicBot() @bot.event async def on_ready(): print(f'Bot is ready! Logged in as {bot.user}') print("Syncing commands...") try: await bot.tree.sync(guild=None) # Set to None for global sync print("Successfully synced commands globally!") except discord.app_commands.errors.CommandSyncFailure as e: print(f"Failed to sync commands: {e}") except Exception as e: print(f"An error occurred while syncing commands: {e}") @bot.tree.command(name="play", description="Play a YouTube URL") async def play(interaction: discord.Interaction, url: str): await interaction.response.defer() ctx = await commands.Context.from_interaction(interaction) if not await music_bot.join_voice(ctx): await interaction.followup.send("You need to be in a voice channel!") return try: with yt_dlp.YoutubeDL(music_bot.ydl_opts) as ydl: info = ydl.extract_info(url, download=False) title = info.get('title', 'Unknown title') music_bot.queue.append(url) await interaction.followup.send(f'Added to queue: {title}') if not music_bot.is_playing: await music_bot.play_next(ctx) except Exception as e: await interaction.followup.send(f'Error: {str(e)}') @bot.tree.command(name="skip", description="Skip the current song") async def skip(interaction: discord.Interaction): if music_bot.voice_client: music_bot.voice_client.stop() await interaction.response.send_message('Skipped current song!') else: await interaction.response.send_message('No song is currently playing!') @bot.tree.command(name="leave", description="Disconnect bot from voice channel") async def leave(interaction: discord.Interaction): if music_bot.voice_client: await music_bot.voice_client.disconnect() music_bot.voice_client = None music_bot.queue = [] music_bot.is_playing = False await interaction.response.send_message('Bot disconnected!') else: await interaction.response.send_message('Bot is not in a voice channel!') @bot.tree.command(name="queue", description="Show the current music queue") async def queue(interaction: discord.Interaction): if music_bot.is_playing: await interaction.response.defer() queue_info = await music_bot.get_queue_info() await interaction.followup.send(f"**Current Queue:**\n{queue_info}") else: await interaction.response.send_message("No music is currently playing!") def run_discord_bot(): bot.run(os.getenv('DISCORD_TOKEN')) # Create the Gradio interface with gr.Blocks() as iface: gr.Markdown("# Discord Music Bot Control Panel") gr.Markdown("Bot is running in background") if __name__ == "__main__": # Start the Discord bot in a separate thread bot_thread = threading.Thread(target=run_discord_bot, daemon=True) bot_thread.start() # Run Gradio interface in the main thread iface.launch(debug=True)