'I can't play song with Youtube URL on my Discord Bot

So I trying to create my own music bot for my own server with Python since Rhythm and Groovy unavailable, but I can't play it with YouTube URL. Do anyone have the solution?

    @commands.command()
    async def play(self,ctx,*,url):
        ctx.voice_client.stop()
        FFMPEG_OPTIONS = {'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', 'options': '-vn'}
        YDL_OPTIONS = {'format':"bestaudio"}
        vc = ctx.voice_client

        with youtube_dl.YoutubeDL(YDL_OPTIONS) as ydl:
         info = ydl.extract_info(url, download=False)
         if 'entries' in info:
           url2 = info["entries"][0]["formats"][0]['url']
         elif 'formats' in info:
           url2 = info["entries"][0]["formats"][0]['url']
         source = await discord.FFmpegOpusAudio.from_probe(url2, **FFMPEG_OPTIONS)
         vc.play(source)


Solution 1:[1]

import asyncio
import os
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=1.0):
       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)



bot = commands.Bot(command_prefix = '!')

@bot.event
async def on_ready():
  await bot.change_presence(activity = discord.Activity(type = 
  discord.ActivityType.listening, name = "your favourite song :)"), status = 
  discord.Status.idle)
  print('bot ready')

@bot.command(description="joins a voice channel")
async def join(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

async def on_ready(self):
  for guild in self.client.guilds:
    self.song_queue[guild.id] = []

async def check_queue(ctx):
  if len(ctx.song_queue[ctx.guild.id]) > 0:
    ctx.voice_client.stop()
    await ctx.play(ctx.song_queue[ctx.guild.id][0])  

@bot.command(description="streams music", aliases = ['pl','p'])
async def play(ctx,*, url):
  async with ctx.typing():
    player = await YTDLSource.from_url(url, loop= 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))
    song_queue = check_queue(ctx)
    song_queue[ctx.guild.id].append(player)
 
@bot.command(description="stops and disconnects the bot from voice")
async def leave(ctx):
  await ctx.voice_client.disconnect()


@play.before_invoke
async def ensure_voice(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()


bot.run('TOKEN')

Try this code; works only with url. Also, I have only the join, play and leave command, it's just a very basic music bot, and it does not queue either.

Solution 2:[2]

    @commands.command(name='play')
    async def _play(self, ctx: commands.Context, *, search: str):
        """Plays a song.
        If there are songs in the queue, this will be queued until the
        other songs finished playing.
        This command automatically searches from various sites if no URL is provided.
        
        """

        if not ctx.voice_state.voice:
            await ctx.invoke(self._join)

        async with ctx.typing():
            try:
                source = await YTDLSource.create_source(ctx, search, loop=self.bot.loop)
            except YTDLError as e:
                await ctx.send('An error occurred while processing this request: {}'.format(str(e)))
            else:
                song = Song(source)

                await ctx.voice_state.songs.put(song)
                await ctx.send('Enqueued {}'.format(str(source))) 

The above code has been taken from here. As this is a common question I thought that using pre-tested code would be the correct option.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 xeno
Solution 2