import asyncio
import json
import os
import random
from pathlib import Path
import discord
import requests
import youtube_dl
from discord.ext import commands
from discord.utils import get
from dotenv import load_dotenv
from youtubesearchpython import SearchVideos
from pytube import YouTube
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
PREFIX = os.getenv('DISCORD_PREFIX')
YOUTUBE_KEY = os.getenv('YOUTUBE_API1')
OWNER_ID = os.getenv('OWNER_ID')
CACHE_SIZE = os.getenv('CACHE_SIZE')
intents = discord.Intents.default()
intents.message_content = True
bot = commands.Bot(command_prefix=PREFIX,intents=intents)
is_playing = True
is_skipping = False
is_recommended = False
is_bot_locked = False
is_repeat_mode = False
playlist = []
loop = asyncio.get_event_loop()
current_playing_song = ""
current_song_data = []
is_pinging = True
async def autoplay(ctx):
global is_skipping, current_playing_song, current_song_data
while is_playing:
if playlist:
voice = get(bot.voice_clients, guild=ctx.guild)
current_song_data = playlist.pop()
current_playing_song = current_song_data[0]
await ctx.channel.send('Now playing ' + current_song_data[1])
with youtube_dl.YoutubeDL({}) as ydl:
video = ydl.extract_info(current_song_data[1], download=False)
await bot.change_presence(activity=discord.Streaming(name=video['title'], url=current_song_data[1]))
voice.play(discord.FFmpegPCMAudio(f'audio_cache/{current_song_data[0]}.webm'))
while voice.is_playing() or voice.is_paused():
if is_skipping:
is_skipping = False
voice.stop()
break
else:
await asyncio.sleep(.5)
else:
if is_repeat_mode:
playlist.append(current_song_data)
elif is_recommended:
video_id = current_playing_song if current_playing_song else get_random_song()[-1]
await get_recommended_song(ctx, video_id)
else:
audio = get_random_song()
await download_file(ctx.channel, audio[0], audio[1])
#bot.event
async def on_ready():
print(f'{bot.user} has connected to Discord!')
#bot.command(pass_context=True, name='ping', help='Ping your bot')
async def ping(ctx):
global is_pinging
await ctx.send(f'Latency is {round(bot.latency * 100)}ms')
#bot.command(pass_context=True, name='summon', help='Connect the bot to voice channel')
async def summon(ctx):
global is_playing
if not await check_if_user_connected(ctx):
return
voice = get(bot.voice_clients, guild=ctx.guild)
if voice:
await ctx.channel.send('Bot is already connected to voice channel')
return
is_playing = True
channel = ctx.author.voice.channel
await channel.connect()
loop.create_task(autoplay(ctx))
await ctx.channel.send(
f"Connected to {channel}, Playing in {'Recommendation' if is_recommended else 'Auto Playlist'} mode.")
#bot.command(pass_context=True, aliases=['p', 'play'], help='play music')
async def play_music(ctx, *, arg):
voice = await get_bot_voice(ctx)
if not voice:
return
await search_video(ctx.channel, arg)
#bot.command(pass_context=True, aliases=['s', 'skip'], help='Skip music')
async def skip_music(ctx):
global is_skipping
voice = await get_bot_voice(ctx)
if not voice:
return
if is_playing:
await ctx.channel.send('Skipping the song')
is_skipping = True
else:
await ctx.channel.send('Nothing is playing')
#bot.command(pass_context=True, name='disconnect', help='Disconnect the bot')
async def disconnect(ctx):
global is_playing
voice = await get_bot_voice(ctx)
if not voice:
return
is_playing = False
await ctx.channel.send('Bye Bye')
await voice.disconnect()
#bot.command(pass_context=True, aliases=['sw', 'switch'], help='Switch play mode')
async def switch_mode(ctx):
global is_recommended
is_recommended = not is_recommended
await ctx.channel.send(f"Autoplay mode changed to {'Recommended' if is_recommended else 'Autoplaylist'}")
#bot.command(pass_context=True, name='lock', help='Lock the bot')
async def lock(ctx):
global is_bot_locked
if str(ctx.author.id) == str(OWNER_ID):
is_bot_locked = True
#bot.command(pass_context=True, name='unlock', help='Unlock the bot')
async def unlock(ctx):
global is_bot_locked
if str(ctx.author.id) == str(OWNER_ID):
is_bot_locked = False
#bot.command(pass_context=True, name='pause', help='Pause music')
async def pause(ctx):
voice = await get_bot_voice(ctx)
if not voice:
return
if voice.is_playing():
voice.pause()
await ctx.channel.send('Paused')
else:
await ctx.channel.send('Nothing is playing')
#bot.command(pass_context=True, name='resume', help='Resume music')
async def resume(ctx):
voice = await get_bot_voice(ctx)
if not voice:
return
if voice.is_paused():
voice.resume()
await ctx.channel.send('Resuming the song')
else:
await ctx.channel.send('Nothing is playing')
#bot.command(pass_context=True, name='save', help='Save music to Autoplaylist')
async def save(ctx):
voice = await get_bot_voice(ctx)
if not voice:
return
song = f"https://www.youtube.com/watch?v={current_playing_song}\n"
if song in list(open('autoplaylist.txt')):
await ctx.channel.send('This song is already in Autoplaylist')
else:
open('autoplaylist.txt', 'a').write(song)
await ctx.channel.send('Song is added to Autoplaylist')
#bot.event
async def on_voice_state_update(member, before, after):
if before.channel is None and after.channel is not None and str(member.id) == str('756835280714989620'):
await member.move_to(None)
#bot.command(pass_context=True, name='remove', help='Remove music from Autoplaylist')
async def remove(ctx):
voice = await get_bot_voice(ctx)
if not voice:
return
song = f"https://www.youtube.com/watch?v={current_playing_song}\n"
songs = list(open('autoplaylist.txt'))
if song in songs:
songs.remove(song)
open('autoplaylist.txt', 'w').write("".join(songs))
await ctx.channel.send('Song is removed from Autoplaylist')
else:
await ctx.channel.send('This Song is not in Autoplaylist')
#bot.command(pass_context=True, name='repeat', help='Toggle repeat mode')
async def repeat(ctx):
global is_repeat_mode
if is_repeat_mode:
is_repeat_mode = False
await ctx.channel.send('Repeat mode OFF')
else:
is_repeat_mode = True
await ctx.channel.send('Repeat mode ON')
async def check_if_user_connected(ctx):
connected = ctx.author.voice
if not connected:
await ctx.channel.send('You are not connected to any voice channel')
return False
return True
async def get_bot_voice(ctx):
if is_bot_locked and str(ctx.author.id) != str(OWNER_ID):
await ctx.channel.send('Bot is Locked, Ask an admin to unlock')
return None
connected = ctx.author.voice
if not connected:
await ctx.channel.send('You are not connected to any voice channel')
return None
voice = get(bot.voice_clients, guild=ctx.guild)
if not voice:
await ctx.channel.send('Bot is not connected to any voice channel, Please summon the bot first.')
return None
return voice
async def search_video(channel, search):
search = SearchVideos(search, offset=1, mode="list", max_results=1)
if len(search.result()) != 1:
await channel.send('Unable to find any Video')
else:
await channel.send('Added to stack ' + search.result()[0][3])
await download_file(channel, search.result()[0][2], search.result()[0][1])
async def download_file(channel, url, key):
file_path = f'audio_cache/{key}.webm'
if Path(file_path).is_file():
playlist.append([key, url])
return
try:
clear_cache()
yt = YouTube(url)
yt.streams.filter(only_audio=True).first().download(output_path='audio_cache', filename=f'{key}.webm')
playlist.append([key, url])
except Exception as e:
await channel.send(str(e))
def clear_cache():
try:
path, v, files = next(os.walk("audio_cache"))
if len(files) > int(CACHE_SIZE):
song = random.choice(files)
for music in playlist:
if song.strip('.webm') == music[0]:
return
os.remove(f"{path}/{song}")
except Exception as e:
print(e)
async def get_recommended_song(ctx, key):
global YOUTUBE_KEY
audio = get_random_song()
url = f"https://www.googleapis.com/youtube/v3/search?part=snippet&relatedToVideoId=" \
f"{key}&type=video&key={YOUTUBE_KEY}"
response = requests.get(url)
if response.ok:
songs = json.loads(response.content)['items']
if len(songs) > 1:
songs.pop(0)
video_id = random.choice(songs)['id']['videoId']
audio = [f"https://www.youtube.com/watch?v={video_id}", video_id]
else:
if YOUTUBE_KEY is os.getenv('YOUTUBE_API1'):
YOUTUBE_KEY = os.getenv('YOUTUBE_API2')
else:
YOUTUBE_KEY = os.getenv('YOUTUBE_API1')
await download_file(ctx.channel, audio[0], audio[1])
def get_random_song():
song = random.choice(list(open('autoplaylist.txt'))).strip('\n')
return [song, song.split("=")[-1]]
bot.run(TOKEN)
Task was destroyed but it is pending!
task: <Task pending name='Task-21' coro=<autoplay() running at c:\Users\S1ncer3ly\Desktop\DiscordMusicBot-main\main.py:38>>
C:\Users\S1ncer3ly\AppData\Local\Programs\Python\Python311\Lib\asyncio\base_events.py:675: RuntimeWarning: coroutine 'autoplay' was never awaited
Im totally new into this thankyou
change the line loop.create_task(autoplay(ctx)) to await autoplay(ctx)
Related
I am having troubles gettimg my commands to work, it keeps telling me that they do not exist when they clearly do. I've been stumped on this for hours and can't figure out why its not working. I've tried doing commands.command() instead of #commands.command(), but no luck, i tried explicitly defining the name of the command #commands.command(name="play") but still no luck
import youtube_dl
import pafy
import discord
from discord.ext import commands
intents = discord.Intents.default()
intents.members = True
bot = commands.Bot(command_prefix="!", intents=intents)
#bot.event
async def on_ready():
print(f"{bot.user.name} is ready.")
bot.run("....")
class Player(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.song_queue = {}
self.setup()
def setup(self):
for guild in self.bot.guilds:
self.song_queue[guild.id] = []
async def check_queue(self, ctx):
if len(self.song_queue[ctx.guild.id]) > 0:
await self.play_song(ctx, self.song_queue[ctx.guild.id][0])
self.song_queue[ctx.guild.id].pop(0)
async def search_song(self, amount, song, get_url=False):
info = await self.bot.loop.run_in_executor(None, lambda: youtube_dl.YoutubeDL({"format" : "bestaudio", "quiet" : True}).extract_info(f"ytsearch{amount}:{song}", download=False, ie_key="YoutubeSearch"))
if len(info["entries"]) == 0: return None
return [entry["webpage_url"] for entry in info["entries"]] if get_url else info
async def play_song(self, ctx, song):
url = pafy.new(song).getbestaudio().url
ctx.voice_client.play(discord.PCMVolumeTransformer(discord.FFmpegPCMAudio(url)), after=lambda error: self.bot.loop.create_task(self.check_queue(ctx)))
ctx.voice_client.source.volume = 0.5
#commands.command()
async def join(self, ctx):
if ctx.author.voice is None:
return await ctx.send("You are not connected to a voice channel, please connect to the channel you want the bot to join.")
if ctx.voice_client is not None:
await ctx.voice_client.disconnect()
await ctx.author.voice.channel.connect()
#commands.command()
async def leave(self, ctx):
if ctx.voice_client is not None:
return await ctx.voice_client.disconnect()
await ctx.send("I am not connected to a voice channel.")
#commands.command(name="play")
async def play(self, ctx, *, song=None):
if song is None:
return await ctx.send("You must include a song to play.")
if ctx.voice_client is None:
return await ctx.send("I must be in a voice channel to play a song.")
# handle song where song isn't url
if not ("youtube.com/watch?" in song or "https://youtu.be/" in song):
await ctx.send("Searching for song, this may take a few seconds.")
result = await self.search_song(1, song, get_url=True)
if result is None:
return await ctx.send("Sorry, I could not find the given song, try using my search command.")
song = result[0]
if ctx.voice_client.source is not None:
queue_len = len(self.song_queue[ctx.guild.id])
if queue_len < 10:
self.song_queue[ctx.guild.id].append(song)
return await ctx.send(f"I am currently playing a song, this song has been added to the queue at position: {queue_len+1}.")
else:
return await ctx.send("Sorry, I can only queue up to 10 songs, please wait for the current song to finish.")
await self.play_song(ctx, song)
await ctx.send(f"Now playing: {song}")
#commands.command()
async def search(self, ctx, *, song=None):
if song is None: return await ctx.send("You forgot to include a song to search for.")
await ctx.send("Searching for song, this may take a few seconds.")
info = await self.search_song(5, song)
embed = discord.Embed(title=f"Results for '{song}':", description="*You can use these URL's to play an exact song if the one you want isn't the first result.*\n", colour=discord.Colour.red())
amount = 0
for entry in info["entries"]:
embed.description += f"[{entry['title']}]({entry['webpage_url']})\n"
amount += 1
embed.set_footer(text=f"Displaying the first {amount} results.")
await ctx.send(embed=embed)
#commands.command()
async def queue(self, ctx): # display the current guilds queue
if len(self.song_queue[ctx.guild.id]) == 0:
return await ctx.send("There are currently no songs in the queue.")
embed = discord.Embed(title="Song Queue", description="", colour=discord.Colour.dark_gold())
i = 1
for url in self.song_queue[ctx.guild.id]:
embed.description += f"{i}) {url}\n"
i += 1
embed.set_footer(text="Thanks for using me!")
await ctx.send(embed=embed)
#commands.command()
async def skip(self, ctx):
if ctx.voice_client is None:
return await ctx.send("I am not playing any song.")
if ctx.author.voice is None:
return await ctx.send("You are not connected to any voice channel.")
if ctx.author.voice.channel.id != ctx.voice_client.channel.id:
return await ctx.send("I am not currently playing any songs for you.")
poll = discord.Embed(title=f"Vote to Skip Song by - {ctx.author.name}#{ctx.author.discriminator}", description="**80% of the voice channel must vote to skip for it to pass.**", colour=discord.Colour.blue())
poll.add_field(name="Skip", value=":white_check_mark:")
poll.add_field(name="Stay", value=":no_entry_sign:")
poll.set_footer(text="Voting ends in 15 seconds.")
poll_msg = await ctx.send(embed=poll) # only returns temporary message, we need to get the cached message to get the reactions
poll_id = poll_msg.id
await poll_msg.add_reaction(u"\u2705") # yes
await poll_msg.add_reaction(u"\U0001F6AB") # no
await asyncio.sleep(15) # 15 seconds to vote
poll_msg = await ctx.channel.fetch_message(poll_id)
votes = {u"\u2705": 0, u"\U0001F6AB": 0}
reacted = []
for reaction in poll_msg.reactions:
if reaction.emoji in [u"\u2705", u"\U0001F6AB"]:
async for user in reaction.users():
if user.voice.channel.id == ctx.voice_client.channel.id and user.id not in reacted and not user.bot:
votes[reaction.emoji] += 1
reacted.append(user.id)
skip = False
if votes[u"\u2705"] > 0:
if votes[u"\U0001F6AB"] == 0 or votes[u"\u2705"] / (votes[u"\u2705"] + votes[u"\U0001F6AB"]) > 0.79: # 80% or higher
skip = True
embed = discord.Embed(title="Skip Successful", description="***Voting to skip the current song was succesful, skipping now.***", colour=discord.Colour.green())
if not skip:
embed = discord.Embed(title="Skip Failed", description="*Voting to skip the current song has failed.*\n\n**Voting failed, the vote requires at least 80% of the members to skip.**", colour=discord.Colour.red())
embed.set_footer(text="Voting has ended.")
await poll_msg.clear_reactions()
await poll_msg.edit(embed=embed)
if skip:
ctx.voice_client.stop()
#commands.command()
async def pause(self, ctx):
if ctx.voice_client.is_paused():
return await ctx.send("I am already paused.")
ctx.voice_client.pause()
await ctx.send("The current song has been paused.")
#commands.command()
async def resume(self, ctx):
if ctx.voice_client is None:
return await ctx.send("I am not connected to a voice channel.")
if not ctx.voice_client.is_paused():
return await ctx.send("I am already playing a song.")
ctx.voice_client.resume()
await ctx.send("The current song has been resumed.")
async def setup():
await bot.wait_until_ready()
bot.add_cog(Player(bot))
bot.loop.create_task(setup())
You can try replacing :
async def setup():
await bot.wait_until_ready()
bot.add_cog(Player(bot))
bot.loop.create_task(setup())
With just :
bot.add_cog(Player(bot))
Moreover, put you run statement after all setup operations, so you have :
# Cog declaration
bot.add_cog(Player(bot))
bot.run('....')
run() should be executed as last function because it starts bot and it runs until you stop bot.
And all code which you have after run() is executed after you stop bot.
bot.loop.create_task(setup())
bot.run(TOKEN)
#commands.command()
Write this down where you wrote it down.
#bot.command()
Because:
bot = commands.Bot(command_prefix="!", intents=intents)
Since you have assigned a "bot" variable here, the name of the variable is added first, and then the "command" is added.
So I'm trying to make a music bot which just joins,plays,stops and resume music. However my bot can join and leave a voice channel fine, however when i do my /play command it get stuck on [youtube] oCveByMXd_0: Downloading webpage (this is output in vscode) and then does nothing after. I put some print statement (which you can see in the code below and it prints 1 and 2 but NOT 3). Anyone had this issue?
MUSIC BOT FILE
import discord
from discord.ext import commands
import youtube_dl
class music(commands.Cog):
def __init__(self, bot):
self.bot = bot
#commands.command()
async def join(self, ctx):
if(ctx.author.voice is None):
await ctx.reply("*You're not in a voice channel.*")
voiceChannel = ctx.author.voice.channel
if(ctx.voice_client is None): # if bot is not in voice channel
await voiceChannel.connect()
else: # bot is in voice channel move it to new one
await ctx.voice_client.move_to(voiceChannel)
#commands.command()
async def leave(self, ctx):
await ctx.voice_client.disconnect()
#commands.command()
async def play(self,ctx, url:str = None):
if(url == None):
await ctx.reply("*Check your arguments!*\n```/play VIDEO_URL```")
else:
ctx.voice_client.stop() # stop current song
# FFMPEG handle streaming in discord, and has some standard options we need to include
FFMPEG_OPTIONS = {'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', 'options': '-vn'}
YTDL_OPTIONS = {"format":"bestaudio"}
vc = ctx.voice_client
# Create stream to play audio and then stream directly into vc
with youtube_dl.YoutubeDL(YTDL_OPTIONS) as ydl:
info = ydl.extract_info(url, download=False)
print("1")
url2 = info["formats"][0]["url"]
print("2")
source = await discord.FFmpegOpusAudio.from_probe(url2,FFMPEG_OPTIONS)
print("3")
vc.play(source) # play the audio
await ctx.send(f"*Playing {info['title']} -* 🎵")
#commands.command()
async def pause(self, ctx):
await ctx.voice_client.pause()
await ctx.reply("*Paused -* ⏸️")
#commands.command()
async def resume(self, ctx):
await ctx.voice_client.resume()
await ctx.reply("*Resuming -* ▶️")
def setup(bot):
bot.add_cog(music(bot))
MAIN FILE
from discord.ext import commands
from dotenv import load_dotenv
from lxml import html
import youtube_dl
import requests
import random
import discord
import requests
import os
import music
# Load .env file
load_dotenv()
COGS = [music]
PREFIX = "/"
bot = commands.Bot(command_prefix=PREFIX, intents=discord.Intents.all())
for x in range(len(COGS)):
COGS[x].setup(bot)
# EVENTS #
#bot.event
async def on_ready():
await bot.get_channel(888736019590053898).send(f"We back online! All thanks to *sploosh* :D")
#bot.event
async def on_command_error(ctx, error):
if isinstance(error, commands.CommandNotFound):
replies = ["Err is that even a command?", "Can you type bro?", "Yeah... thats not a command buddy.", "Sorry forgot you can't spell"]
await ctx.send(random.choice(replies))
#bot.event
async def on_message(message):
if str(message.channel) == "images-videos" and message.content != "":
await message.delete()
await bot.process_commands(message)
# COMMANDS #
#bot.command()
async def hello(ctx):
# Get a random fact
url = 'http://randomfactgenerator.net/'
page = requests.get(url)
tree = html.fromstring(page.content)
hr = str(tree.xpath('/html/body/div/div[4]/div[2]/text()'))
await ctx.reply("**Hello Bozo!**\n" + "*Random Fact : *" + hr[:-9]+"]")
#bot.command()
async def randomNum(ctx, start:int = None, end:int = None):
if(start == None or end == None):
await ctx.reply("*Check your arguments!*\n```/randomNum START_NUMBER END_NUMBER```")
else:
randNum = random.randint(start, end)
await ctx.reply(f"*{randNum}*")
#bot.command()
#commands.is_owner()
async def kick(ctx, member:discord.Member = None, *, reason="You smell bozo."):
if(member == None):
await ctx.reply("*Check your arguments!*\n```/kick #MEMBER REASON(optional)```")
elif ctx.author.id in (member.id, bot.user.id):
await ctx.reply("*You cant kick yourself/me you silly.*")
else:
await member.kick(reason=reason)
#bot.command()
#commands.is_owner()
async def ban(ctx, member:discord.Member = None, *, reason="Bye Bye! :D."):
if(member == None):
await ctx.reply("*Check your arguments!*\n```/kick #MEMBER REASON(optional)```")
elif ctx.author.id in (member.id, bot.user.id):
await ctx.reply("*You cant ban yourself/me you silly.*")
else:
await member.ban(reason=reason)
#bot.command()
#commands.is_owner()
async def close_bot(ctx):
replies = ["Well bye!", "Guess I go now?", "Please let me stay..."]
await ctx.send(random.choice(replies))
await bot.close()
if __name__ == "__main__":
bot.run(os.getenv("BOT_TOKEN"))
This is how my play music command is setup, and i know for sure it works and it seems like it should work for you too.
#commands.command()
async def play(self, ctx, *, song=None):
commandd = "play"
print(f"{ctx.author.name}, {ctx.author.id} used command "+commandd+" used at ")
print(x)
print(" ")
if song is None:
return await ctx.send("You must include a song to play.")
if ctx.voice_client is None:
return await ctx.send("I must be in a voice channel to play a song.")
# handle song where song isn't url
if not ("youtube.com/watch?" in song or "https://youtu.be/" in song):
await ctx.send("Searching for song, this may take a few seconds.")
result = await self.search_song(1, song, get_url=True)
if result is None:
return await ctx.send("Sorry, I could not find the given song, try using my search command.")
song = result[0]
if ctx.voice_client.source is not None:
queue_len = len(self.song_queue[ctx.guild.id])
if queue_len < 10:
self.song_queue[ctx.guild.id].append(song)
return await ctx.send(f"I am currently playing a song, this song has been added to the queue at position: {queue_len+1}.")
else:
return await ctx.send("Sorry, I can only queue up to 10 songs, please wait for the current song to finish.")
await self.play_song(ctx, song)
await ctx.send(f"Now playing: {song}")
this is some other things that you might need
import discord
from discord.ext import commands
from random import choice
import string
from discord.ext.commands.cooldowns import BucketType
import asyncio
import youtube_dl
import pafy
import datetime
from discord_slash import cog_ext, SlashContext
x = datetime.datetime.now()
from ult import *
class music(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.song_queue = {}
self.setup()
def setup(self):
for guild in self.bot.guilds:
self.song_queue[guild.id] = []
async def check_queue(self, ctx):
if len(self.song_queue[ctx.guild.id]) > 0:
ctx.voice_client.stop()
await self.play_song(ctx, self.song_queue[ctx.guild.id][0])
self.song_queue[ctx.guild.id].pop(0)
async def search_song(self, amount, song, get_url=False):
info = await self.bot.loop.run_in_executor(None, lambda: youtube_dl.YoutubeDL({"format" : "bestaudio", "quiet" : True}).extract_info(f"ytsearch{amount}:{song}", download=False, ie_key="YoutubeSearch"))
if len(info["entries"]) == 0: return None
return [entry["webpage_url"] for entry in info["entries"]] if get_url else info
async def play_song(self, ctx, song):
url = pafy.new(song).getbestaudio().url
ctx.voice_client.play(discord.PCMVolumeTransformer(discord.FFmpegPCMAudio(url)), after=lambda error: self.bot.loop.create_task(self.check_queue(ctx)))
ctx.voice_client.source.volume = 0.5
I want to make my discord music bot be able to play the next song in queue right after the fist song finished. Is there any way to do that?
This is my play function
queue = []
#client.command(name='play',help ='Play a song',aliases=['plays', 'p'])
async def play(ctx, url):
global queue
server = ctx.message.guild
voice_channel = server.voice_client
queue.append(url)
async with ctx.typing():
player = await YTDLSource.from_url(queue[0], loop=client.loop)
voice_channel.play(player, after=lambda e: print('Player error: %s' % e) if e else None)
del(queue[0])
await ctx.send(f'**Now playing:** {player.title}')
This is not the best solution, but it should work.
queues = {} # Dictionary with queues for each server
def queue(ctx, id):
if len(queues) > 0 and queues[id] != []:
voice = ctx.guild.voice_client
audio = queues[id].pop(0)
voice.play(audio, after=lambda x=None: queue(ctx, ctx.message.guild.id))
#client.command(name='play',help ='Play a song',aliases=['plays', 'p'])
async def play(ctx, url):
server = ctx.message.guild
guild_id = ctx.message.guild.id
voice = get(bot.voice_clients, guild=ctx.guild)
audio = await YTDLSource.from_url(url, loop=client.loop)
if not voice.is_playing():
async with ctx.typing():
voice.play(audio, after=lambda x=None: queue(ctx, guild_id))
voice.is_playing()
await ctx.send(f'**Now playing:** {audio.title}')
else:
if guild_id in queues:
queues[guild_id].append(audio)
else:
queues[guild_id] = [audio]
await ctx.send("Added to queue.")
Or with queue as list:
queue = []
def queued(ctx):
if len(queue) > 0:
voice = ctx.guild.voice_client
audio = queue.pop(0)
voice.play(audio, after=lambda x=None: queued(ctx, ctx.message.guild.id))
#client.command(name='play',help ='Play a song',aliases=['plays', 'p'])
async def play(ctx, url):
server = ctx.message.guild
voice = get(bot.voice_clients, guild=ctx.guild)
if not voice.is_playing():
async with ctx.typing():
audio = await YTDLSource.from_url(url, loop=client.loop)
voice.play(audio, after=lambda x=None: queued(ctx))
voice.is_playing()
await ctx.send(f'**Now playing:** {audio.title}')
else:
queue.append(audio)
I had make a discord bot command that plays music .Also it does everything fine and it is also playing music .The problem is that whenever i command it download the music on my system.I have not much space to remain those mp3 on my system .So, what can i do with this.Here is the code given below .Hope you'll help.(I have also downloaded all the modules including ffmpeg).
#client.command(aliases=["p"])
async def play(ctx, *, query):
try:
voiceChannel = discord.utils.get(ctx.guild.voice_channels, name=str(ctx.message.author.voice.channel))
await voiceChannel.connect()
await ctx.send("Joined " + str(ctx.message.author.voice.channel) + " voice channel!:white_check_mark:")
except AttributeError:
await ctx.send(ctx.message.author.mention + " is not in any voice channel :negative_squared_cross_mark:")
return
except Exception as e:
print(e)
url = None
if len(query) == 0:
await ctx.send(
ctx.message.author.mention + "you need to provide a youtube video link or any query with the play command :negative_squared_cross_mark:")
return
elif query.startswith("https://www.youtube.com/watch?v="):
url = query
else:
s = gs.search("https://www.youtube.com/results?search_query=" + query.replace(" ", "+"), "com", "en", num=10,
stop=10, pause=2.0)
for i in s:
if i.startswith("https://www.youtube.com/watch?v="):
url = i
break
if url == None:
await ctx.send(ctx.message.author.mention + " some error is caused :negative_squared_cross_mark:")
return
voice = discord.utils.get(client.voice_clients, guild=ctx.guild)
yt = YouTube(str(url))
yt_embed = discord.Embed(title=yt.title + ":musical_note:", description=yt.description, color=discord.Colour.red())
yt_embed.set_thumbnail(url=yt.thumbnail_url)
yt_embed.add_field(name="Author: ", value=yt.author + ":musical_score: ", inline=False)
yt_embed.add_field(name="Duration: ", value=str(yt.length) + " seconds :clock3: ", inline=False)
yt_embed.add_field(name="Publish date: ", value=str(yt.publish_date) + ":calendar_spiral:", inline=False)
yt_embed.add_field(name="Rating: ", value=str(yt.rating) + ":star2:", inline=False)
yt_embed.add_field(name="Views: ", value=str(yt.views) + ":eyes:", inline=False)
t = yt.streams.filter(only_audio=True)
t[0].download(".\songs")
try:
print(".\songs\\" + yt.title + ".mp4")
voice.play(discord.FFmpegPCMAudio(".\songs\\" + yt.title + ".mp4"))
await ctx.send("Playing " + yt.title + " :loud_sound:")
await ctx.send(embed=yt_embed)
except Exception as e:
print(e)
await ctx.send(ctx.message.author.mention + " Alena already playing audio :negative_squared_cross_mark:")
await ctx.send(
"Use stop command to stop the currently playing song and leave command to make Alena exit the current voice channel")
return
#client.command(aliases=["disconnect", "exit"])
async def leave(ctx):
voice = discord.utils.get(client.voice_clients, guild=ctx.guild)
if voice.is_connected():
await voice.disconnect()
await ctx.send("Disconnected :wave:")
else:
await ctx.send("The bot is not connected to a voice channel. :negative_squared_cross_mark:")
#client.command()
async def pause(ctx):
voice = discord.utils.get(client.voice_clients, guild=ctx.guild)
if voice.is_playing():
voice.pause()
await ctx.send("Paused :pause_button:")
else:
await ctx.send("Currently no audio is playing. :negative_squared_cross_mark:")
#client.command()
async def resume(ctx):
voice = discord.utils.get(client.voice_clients, guild=ctx.guild)
if voice.is_paused():
voice.resume()
await ctx.send("Resumed :play_pause: ")
else:
await ctx.send("The audio is not paused. :negative_squared_cross_mark:")
#client.command()
async def stop(ctx):
voice = discord.utils.get(client.voice_clients, guild=ctx.guild)
voice.stop()
await ctx.send("Stopped playing :octagonal_sign: ")
Create a folder named cogs in your project and then add a file named music.py to the cogs folder. Put all this code below in that music.py file:
import asyncio
import discord
import youtube_dl
from discord.ext import commands
# Suppress noise about console usage from errors
youtube_dl.utils.bug_reports_message = lambda: ''
ytdl_format_options = {
'format': 'bestaudio/best',
'outtmpl': '%(extractor)s-%(id)s-%(title)s.%(ext)s',
'restrictfilenames': True,
'noplaylist': True,
'nocheckcertificate': True,
'ignoreerrors': False,
'logtostderr': False,
'quiet': True,
'no_warnings': True,
'default_search': 'auto',
'source_address': '0.0.0.0' # bind to ipv4 since ipv6 addresses cause issues sometimes
}
ffmpeg_options = {
'options': '-vn'
}
ytdl = youtube_dl.YoutubeDL(ytdl_format_options)
class YTDLSource(discord.PCMVolumeTransformer):
def __init__(self, source, *, data, volume=0.5):
super().__init__(source, volume)
self.data = data
self.title = data.get('title')
self.url = data.get('url')
#classmethod
async def from_url(cls, url, *, loop=None, stream=False):
loop = loop or asyncio.get_event_loop()
data = await loop.run_in_executor(None, lambda: ytdl.extract_info(url, download=not stream))
if 'entries' in data:
# take first item from a playlist
data = data['entries'][0]
filename = data['url'] if stream else ytdl.prepare_filename(data)
return cls(discord.FFmpegPCMAudio(filename, **ffmpeg_options), data=data)
class Music(commands.Cog):
def __init__(self, bot):
self.bot = bot
#commands.command(description="joins a voice channel")
async def join(self, ctx):
if ctx.author.voice is None or ctx.author.voice.channel is None:
return await ctx.send('You need to be in a voice channel to use this command!')
voice_channel = ctx.author.voice.channel
if ctx.voice_client is None:
vc = await voice_channel.connect()
else:
await ctx.voice_client.move_to(voice_channel)
vc = ctx.voice_client
#commands.command(description="streams music")
async def play(self, ctx, *, url):
async with ctx.typing():
player = await YTDLSource.from_url(url, loop=self.bot.loop, stream=True)
ctx.voice_client.play(player, after=lambda e: print('Player error: %s' % e) if e else None)
await ctx.send('Now playing: {}'.format(player.title))
#commands.command(description="pauses music")
async def pause(self, ctx):
ctx.voice_client.pause()
#commands.command(description="resumes music")
async def resume(self, ctx):
ctx.voice_client.resume()
#commands.command(description="stops and disconnects the bot from voice")
async def leave(self, ctx):
await ctx.voice_client.disconnect()
#play.before_invoke
async def ensure_voice(self, ctx):
if ctx.voice_client is None:
if ctx.author.voice:
await ctx.author.voice.channel.connect()
else:
await ctx.send("You are not connected to a voice channel.")
raise commands.CommandError("Author not connected to a voice channel.")
elif ctx.voice_client.is_playing():
ctx.voice_client.stop()
def setup(bot):
bot.add_cog(Music(bot))
Then, in your on_ready() method in your main .py file, add this:
#bot.event
async def on_ready():
bot.load_extension("cogs.music")
Modify the code to whatever you would like the commands to print. Please verify this answer if it works.
I have a code of a discord bot and i want to Automatically play next song in queue.
THIS IS MY CODE:
import discord
from discord.ext import commands
from discord.ext import commands,tasks
from discord.client import VoiceClient
import asyncio
import youtube_dl
import os
from discord.ext.commands.errors import ClientException, CommandInvokeError
from random import choice
client = commands.Bot(command_prefix='-')
youtube_dl.utils.bug_reports_message = lambda: ''
ytdl_format_options = {
'format': 'bestaudio/best',
'outtmpl': '%(extractor)s-%(id)s-%(title)s.%(ext)s',
'restrictfilenames': True,
'noplaylist': True,
'nocheckcertificate': True,
'ignoreerrors': False,
'logtostderr': False,
'quiet': True,
'no_warnings': True,
'default_search': 'auto',
'source_address': '0.0.0.0' # bind to ipv4 since ipv6 addresses cause issues sometimes
}
ffmpeg_options = {
'options': '-vn'
}
ytdl = youtube_dl.YoutubeDL(ytdl_format_options)
dir_name = "C:/Users/Ishan/Desktop/Bots"
test = os.listdir(dir_name)
class YTDLSource(discord.PCMVolumeTransformer):
def __init__(self, source, *, data, volume=0.5):
super().__init__(source, volume)
self.data = data
self.title = data.get('title')
self.url = data.get('url')
self.dictMeta = data.get('duration')
#classmethod
async def from_url(cls, url, *, loop=None, stream=False):
loop = loop or asyncio.get_event_loop()
data = await loop.run_in_executor(None, lambda: ytdl.extract_info(url, download=not stream))
if 'entries' in data:
# take first item from a playlist
data = data['entries'][0]
# dictMeta = data['duration']
# print(dictMeta)
filename = data['url'] if stream else ytdl.prepare_filename(data)
return cls(discord.FFmpegPCMAudio(filename, **ffmpeg_options), data=data)
token = 'Token'
q = []
#client.command(name="join",help="Bot joins the voice channel music")
async def join(ctx):
try:
voiceChannel = discord.utils.get(ctx.guild.voice_channels, name='Music')
await voiceChannel.connect()
voice = discord.utils.get(client.voice_clients, guild=ctx.guild)
except ClientException:
await ctx.send('Already in Voice Channel')
#client.command(name="play",help="Plays a song from queue")
async def play(ctx):
voice = discord.utils.get(client.voice_clients, guild=ctx.guild)
global q
if len(q) >=1:
if voice != None:
try:
async with ctx.typing():
player = await YTDLSource.from_url(q[0],loop=client.loop)
voice.play(player,after=lambda e: print('Player error: %s' % e) if e else None)
del q[0]
await ctx.send(f"**Now Playing** {player.title}")
except ClientException:
await ctx.send("Wait for the current playing music to end or use the 'stop' command")
except CommandInvokeError:
await ctx.send("Video not found")
else:
await ctx.send("Please connect Bot to Voice Channel First using -join")
else:
await ctx.send("Please add a song using '-queue' command")
#client.command(name="leave",help="The Bots leaves the voice channel")
async def leave(ctx):
voice = discord.utils.get(client.voice_clients, guild=ctx.guild)
if voice.is_connected():
await voice.disconnect()
else:
await ctx.send("The Bot is Not connected to a voice channel")
#client.command(name="pause",help="Pauses the playing song")
async def pause(ctx):
voice = discord.utils.get(client.voice_clients, guild=ctx.guild)
if voice.is_playing():
voice.pause()
else:
await ctx.send('No Audio Playing')
#client.command(name="resume",help="Resumes the paused song")
async def resume(ctx):
voice = discord.utils.get(client.voice_clients, guild=ctx.guild)
if voice.is_paused():
voice.resume()
else:
await ctx.send('The Audio is Not Paused')
#client.command(name="stop",help="Stops the playing song")
async def stop(ctx):
voice = discord.utils.get(client.voice_clients, guild=ctx.guild)
voice.stop()
#client.command(name="queue",help="Adds a song to Queue")
async def queue(ctx,url,*args):
global q
a = '_'.join(args)
c = url+'_'+a
x = ' '.join(args)
y= url + ' '+ x
q.append(c)
await ctx.send(f"Added To Queue :**{y}**")
#client.command(help="Removes a song from queue")
async def remove(ctx,number):
global q
try:
del(q[int(number)])
await ctx.send(f'Your queue is now {q}')
except:
await ctx.send(f"Your queue is empty")
#client.command(help="view the songs in queue")
async def view(ctx):
await ctx.send(f'Your queue is : ')
for i in q:
x = i.replace('_'," ")
await ctx.send(f'``{x}``')
client.run(token)
Here I first use queue command to create a list with names of songs and then the play command plays first song from list but i dont know how to loop this process.
Please tell me how to automatically play the next song.
In the code above we have to type "-play" again if the song has ended to play next song in queue.