discord.py music: Queue doesn't update if the current song ends

Question:

I have been having issues with my queue feature as it would either delete two elements from the playlist dictionary at once when I skip a song using a command or it comes up with bugs when I let the songs finish- whenever I try to come up with a fix which would resolve both issues.

This is the relevant code –

  def queueCheck(self, ctx, id, vc):
    text = ""
    if queues[id] != []:       # if server player queue not empty
      stream = queues[id].pop(0)
      if self.skipping == True:
        current[id] = tracks[id].pop(0)
        if len(tracks[id]) != 0:
          text = "Now playing **" + tracks[ctx.message.guild.id][0] + "**"
      self.skipping = False
      vc.play(stream, after=lambda e: self.queueCheck(ctx, id, vc))
    else:
      text = "Queue has finished playing!"   
    coro = ctx.send(text)
    fut = asyncio.run_coroutine_threadsafe(coro, self.client.loop)
    try:
      fut.result()
    except:
      pass

  async def playSong(self, ctx, url):
    with youtube_dl.YoutubeDL(self.YDL_OPTIONS) as ydl:
      info = ydl.extract_info(url, download=False)
      if 'entries' in info:        # if no url is input
        url2 = info['entries'][0]['formats'][0]['url']
        if ctx.message.guild.id not in tracks.keys():
          tracks[ctx.message.guild.id] = [info['entries'][0]['title']]
        else:
          tracks[ctx.message.guild.id].append(info['entries'][0]['title'])
      elif 'formats' in info:      # if url is passed
        url2 = info['formats'][0]['url']
        if ctx.message.guild.id not in tracks.keys():
          tracks[ctx.message.guild.id] = [info['title']]
        else:
          tracks[ctx.message.guild.id].append(info['title'])
      #print(*(x for x in tracks[ctx.message.guild.id]), sep='n')
      stream = await discord.FFmpegOpusAudio.from_probe(url2, **self.FFMPEG_OPTIONS)
    return stream

  
  async def queue(self, ctx, url):
      stream = await self.playSong(ctx, url)
      guild = ctx.message.guild
      if guild.id in queues:
        queues[guild.id].append(stream)
      await ctx.send('Added track ' + '**' + str(tracks[guild.id][len(tracks[guild.id]) - 1]) + '**')

  @commands.command(name='play', help="Plays any song", aliases=['pl'])
  async def play(self, ctx, *, url):
    if ctx.author.voice is None:
      return await ctx.send('You are not in a voice channel!')
    if ctx.voice_client is None:
      await ctx.author.voice.channel.connect()
    
    vc = ctx.guild.voice_client
    guild = ctx.message.guild
    if not vc.is_playing() and not self.isPaused:# and not self.skipping:
      stream = await self.playSong(ctx, url)
      queues[guild.id] = [stream]
      vc.play(stream, after=lambda e: self.queueCheck(ctx, guild.id, vc))
      await ctx.send('Playing ' + '**' + str(tracks[ctx.message.guild.id][0]) + '**')
    else:
      await self.queue(ctx, url)

@commands.command(name='queue', help="Shows the current queue", aliases=['q','Q'])
  async def showQueue(self, ctx):
    if len(tracks[ctx.message.guild.id]) == 1:
      queueEmbed = discord.Embed(title="Queue", description = "Your queue seems to be a tad bit empty :(", color=discord.Colour.random())
      queueEmbed.add_field(name=':drum: Currently playing n' + tracks[ctx.message.guild.id][0], value='u200b', inline=False)
      await ctx.send(embed=queueEmbed)
    
    elif len(tracks[ctx.message.guild.id]) > 0 or current[ctx.message.guild.id] is not None:
      queueEmbed = discord.Embed(title="Queue", color=discord.Colour.random())
      queuee = ""
      if len(tracks[ctx.message.guild.id]) != 0:
        for i in range(1, len(tracks[ctx.message.guild.id])):
          queuee += ":white_small_square:" + str(i) + ". " + tracks[ctx.message.guild.id][i] + "n"
      else:
        queuee = "No songs here"
      queueEmbed.add_field(name='Coming up next', value='**' + queuee + '**', inline=False)
      queueEmbed.add_field(name=':drum: Currently playing n' + tracks[ctx.message.guild.id][0], value='u200b', inline=False)
      await ctx.send(embed=queueEmbed)
      
    else:
      await ctx.send('No music in queue')

  @commands.command(name='skip', help="Skips the current song")
  async def skip(self, ctx):
    if ctx.author.voice is None:
      return await ctx.send('You are not in a voice channel!')
    self.skipping = True
    ctx.voice_client.stop()
    self.isPaused = False

Cannot post the pic of the queue showing the wrong output but here is the output embed-

Queue
-Coming up next-
1. Song 2
2. Song 3
-:drum: Currently playing-
Song 1

Basically, if I keep skipping my way through all the songs before they end, the queue will update, otherwise if I wait for them to finish, the queue never updates. I am aware that this has to do with the conditional check for self.skipping which only lets me update the queue if I am skipping songs, but how can I pop off elements from track the moment the song ends at the same time?

Asked By: Virat Chauhan

||

Answers:

I’m not sure where you are getting the songs from as you haven’t shown that section of your code, however I am going to assume you are using YoutubeDL. In the case of YoutubeDL, what you can do is extract the duration of the video in seconds, and then have a while loop counting up from 0 every second, once the counter reaches the same value as the duration of the video, just remove the track from the queue and carry on. For example:

def player():
    with YoutubeDL(ydl_opts) as ydl:
        info = ydl.extract_info(queues[id][0], download=False)
    vduration = info['duration']
    link = info['url']
    voice.play(FFmpegPCMAudio(link, **ffmpeg_opts))
    voice.is_playing()

@commands.command()
asnyc def play(ctx, url)
   queues[id].append(url)
   #do everything you do here

   while True:
      if 0 <= 0 < len(queues[id]):
         player()
         counter = 0

         while not counter >= vduration: 
            await asyncio.sleep(1)
            counter += 1

         queues[id].pop(0)

This is just an extremely simple example because as I said before it’s hard to answer if you do not show all the relevant code, however it should apply regardless. Essentially all you need is to extract the duration of the video into a variable and then just count up 1 every second for as long as the video is playing, and then when variables match, move on to the next video.

Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.