Search code examples
pythonffmpegdiscord.pyyoutube-dlpytube

Small discord bot with a set of working music commands, about 6 days ago the play function completely stopped functioning (more below)


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')

Solution

  • 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.