0
votes

The bot has play command that does 3 things it joins the voice channel if it isn't in the voice channel already, it plays songs(obviously) and it stores the songs in a dict called queues if the bot is already playing a song to then play it when the song ends. To do something like this you need the after statment on the voiceClients play command and it seems to be behaving quit weirdly. I've put some print commands to test the bot and it seems that the function that I put on the after statment is firing right away rather then when the song ends, I don't know if this is a bug or if I'm doing something wrong... anyway here's the code:

this is the function that plays after the play command

def continue(ctx):

   queues[ctx.guild.id].pop(0)

   print(f'function continue: {queues}')
    
   if len(queues[ctx.guild.id]) == 0:
       return

   Songs.play(ctx=ctx ,song=queues[ctx.guild.id][0])

and here is the play command

class Songs(commands.Cog):

   def __init__(self, bot):
       self.client = client


   @commands.command()
   async def play(self, ctx, *, song):

       if ctx.voice_client is None:
           await ctx.message.author.voice.channel.connect()
       else:
           if ctx.voice_client.channel != ctx.message.author.voice.channel:
               await ctx.send('You are not in the same channel as the bot!')
               return
    
       if ctx.guild.id in queues:
           queues[ctx.guild.id].append(song)
       else: 
           queues[ctx.guild.id] = [song]
    
       print(f'function play: {queues}')

       if ctx.voice_client.is_playing():
           await ctx.send('Song added to queue! :thumbsup:')
           return

       ydl_opts = {'format': 'bestaudio'}
       with youtube_dl.YoutubeDL(ydl_opts) as ydl:
           if musica.startswith('https'):
               info = ydl.extract_info(song, download=False)
               URL = info['formats'][0]['url']
           else:
               info = ydl.extract_info(f'ytsearch:{song}', download=False)
               URL = info['entries'][0]['formats'][0]['url']
    

       FFMPEG_OPTIONS = {'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', 'options': '-vn'}
       ctx.message.guild.voice_client.play(discord.FFmpegPCMAudio(URL, **FFMPEG_OPTIONS), after=continue(ctx))
2

2 Answers

1
votes

Ah, you have committed one of the classic Python blunders. Look at this line:

       ctx.message.guild.voice_client.play(discord.FFmpegPCMAudio(URL, **FFMPEG_OPTIONS), after=continue(ctx))

When that line runs, the first thing it's going to do is call your function continue. It will then pass the RESULT of that function call to the play function. You don't want to CALL the function, you want to pass the function object:

       ctx.message.guild.voice_client.play(discord.FFmpegPCMAudio(URL, **FFMPEG_OPTIONS), after=continue)

If you really need the context in there, you'll have to use a lambda:

       ctx.message.guild.voice_client.play(discord.FFmpegPCMAudio(URL, **FFMPEG_OPTIONS), after=lambda: continue(ctx))
0
votes

In the final line of your snippet:

ctx.message.guild.voice_client.play(discord.FFmpegPCMAudio(URL, **FFMPEG_OPTIONS), after=continue(ctx))

The after keyword-parameter expects a callable. You are not giving it that. You are invoking a function continue (not a great name for a function, by the way, because you shadow the builtin keyword), and bind the return value to after. Since your callable must accept a parameter ctx, the quick fix would be to pass in a lambda:

after=lambda: continue(ctx)