import discord from discord.ext import commands import asyncio import os from dotenv import load_dotenv import yt_dlp from ytmusicapi import YTMusic load_dotenv() songs_dir = os.getenv("SONGS_DIR") intents = discord.Intents.default() intents.message_content = True intents.voice_states = True bot = commands.Bot(command_prefix='!', intents=intents) queued_songs = [] voice_client = None def search_song(query): ytmusic = YTMusic() search_results = ytmusic.search(query, filter='songs', limit=1) if search_results: song_id = search_results[0]['videoId'] return song_id else: return None def search_video(query): ydl_opts = { 'default_search': 'auto', 'format': 'best', 'quiet': True } with yt_dlp.YoutubeDL(ydl_opts) as ydl: try: result = ydl.extract_info(query, download=False) if 'entries' in result: # Take the first result video_id = result['entries'][0]['id'] return video_id else: return None except yt_dlp.DownloadError: return None def download_song(query): song_id = search_video(query) ydl_opts = { 'format': 'bestaudio/best', 'outtmpl': f'{songs_dir}/%(title)s.%(ext)s', 'postprocessors': [{ 'key': 'FFmpegExtractAudio', 'preferredcodec': 'mp3', 'preferredquality': '192', }], } if not os.path.exists(songs_dir): os.makedirs(songs_dir) with yt_dlp.YoutubeDL(ydl_opts) as ydl: info = ydl.extract_info(f'https://music.youtube.com/watch?v={song_id}', download=True) filename = ydl.prepare_filename(info) filename = os.path.splitext(filename)[0] + '.mp3' return filename @bot.command() async def play(ctx, *, query): global voice_client if voice_client and voice_client.is_connected(): await add_to_queue(ctx, query) else: await connect_and_play(ctx, query) @bot.command() async def skip(ctx): global voice_client if voice_client and voice_client.is_playing(): voice_client.stop() @bot.command() async def stop(ctx): global voice_client for file in os.listdir(songs_dir): file_path = os.path.join(songs_dir, file) if os.path.isfile(file_path): os.unlink(file_path) if voice_client and voice_client.is_connected(): queued_songs.clear() if voice_client.is_playing(): voice_client.stop() await voice_client.disconnect() voice_client = None await ctx.send("Stopped playback and cleared queue.") async def add_to_queue(ctx, query): global voice_client song_path = download_song(query) print(song_path) if song_path: song_name = os.path.relpath(song_path, start=songs_dir) # Get the song name relative to songs_dir queued_songs.append((song_path, song_name)) await ctx.send(f"**Added to queue:** `{song_name}`") else: await ctx.send("Can't find a match for that song.") async def connect_and_play(ctx, query): global voice_client voice_channel = ctx.author.voice.channel if voice_channel: song_path = download_song(query) print(song_path) if song_path: song_name = os.path.relpath(song_path, start=songs_dir) # Get the song name relative to songs_dir queued_songs.append((song_path, song_name)) voice_client = await play_queued_songs(ctx, voice_channel) else: await ctx.send("Can't find a match for that song.") async def play_queued_songs(ctx, voice_channel): global voice_client voice_client = await voice_channel.connect() while queued_songs: song_path, song_name = queued_songs.pop(0) # Get the first song in the queue await ctx.send(f"**Now playing:** `{song_name}`") voice_client.play(discord.FFmpegPCMAudio(song_path)) while voice_client and voice_client.is_playing(): # Check if voice_client is not None await asyncio.sleep(1) if voice_client: for file in os.listdir(songs_dir): file_path = os.path.join(songs_dir, file) if os.path.isfile(file_path): os.unlink(file_path) await voice_client.disconnect() bot.run(os.getenv("DISCORD_TOKEN"))