It is a small discord python bot for my server with various features, which had included music commands until they stopped out of the blue without showing any error for the problem. It uses FFMpeg, and youtubeDl along with pytube to gather the song and store it locally to play it, I have pip updated all of these and they are definitely on the current versions as I have made sure of this online. Any help or insight anyone could provide would be greatly appreciated. I'm sorry if the code is convoluted in the way it's written I'm still pretty new to coding and this is one of my first proper larger projects.
If you need any information I'm happy to give what I can to help.
Here is the code for the play command:
@client.command()
async def play(ctx, *args):
global queu
#global autom
if not args:
voice = get(client.voice_clients, guild=ctx.guild)
if not voice.is_playing():
server = ctx.message.guild
voice_channel = server.voice_client
if queu:
async with ctx.typing():
player = await YTDLSource.from_url(queu[0], loop=client.loop)
voice_channel.play(player, after=lambda e: print('Player error: %s' % e) if e else None)
await ctx.send('**Now playing:** {}'.format(player.title))
del(queu[0])
# while autom == True:
# try:
# a = client.get_command('auto')
# await ctx.invoke(a)
# except:
# print('')
elif not queu:
await ctx.send("You can't play if there isn't anything in the queue\nIf auto mode was on it has now been disabled, to use it gain please add to the queue and run ``;auto on``")
autom = False
if args:
global gueu
search_keywords = ""
print(args)
for word in args:
search_keywords += word
search_keywords += '+'
link = "https://www.youtube.com/results?search_query="
link += search_keywords
#print(link)
html = urllib.request.urlopen(link)
video_ids = re.findall(r"watch\?v=(\S{11})", html.read().decode())
url = ("https://www.youtube.com/watch?v=" + video_ids[0])
#print(url)
queu.append(url)
#print(queu)
await ctx.send("``{}`` added to queue!\n If the song doesn't start please either let the current song end and run ``;play``/``;next`` again or run ``;next`` to play now".format(url))
try:
p = client.get_command('play')
await ctx.invoke(p)
except:
print('failed')
I know that this code won't solve your particular problem, but I have some python code for a music bot that will, instead of downloading the mp3 file on your device, just stream the youtube song you're looking to play - sort of like the Groovy and Rhythm bot. It's a lot quicker and the code works perfectly fine for me. Here you go:
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 = {
"before_options": "-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5",
'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, client):
self.client = client
@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.client.loop, stream=True)
ctx.voice_client.play(player, after=lambda e: print('Player error: %s' % e) if e else None)
embed = discord.Embed(title="Now playing", description=f"[{player.title}]({player.url}) [{ctx.author.mention}]")
await ctx.send(embed=embed)
def setup(bot):
bot.add_cog(Music(bot))
Firstly, make sure you have ffmpeg
and youtube_dl
downloaded and updated to their latest versions. Also, note that this code was written in a cog
file, so if you decide to format your project how I did, then create a folder named cogs
in your project directory, add a new file into the folder (you can name it whatever you want but it needs to end in .py
), and copy-paste the code above into this file. Then, in your main .py
file, add this code to load the cog:
# loading all cogs
for filename in os.listdir('./cogs'):
if filename.endswith('.py'):
bot.load_extension(f'cogs.{filename[:-3]}')
Or, instead of creating a cogs
folder, you can choose to simply add the music code into your main .py
file, in which case you won't be needing the code above.