How to make a Discord Bot play YouTube Audio

Allen Rowland picture Allen Rowland · May 9, 2019 · Viewed 10.2k times · Source

I'm a high school student making a Discord bot as a final project, and I'm having issues finding a working tutorial or baseline for playing YouTube audio in Discordpy Rewrite. If there's a tutorial I'm missing or basic code that can do this, I'd be extremely grateful for that.

I've already looked at some tutorials and tried them, but they don't seem to work for one reason or another. I've spent a lot of project time searching, but I can't seem to find what I'm looking for. Below, I found some code for making a bot play an MP3 file, but I'm unsure how to do it using a YouTube link.

@bot.command()
async def mp3play(context):
  user = context.message.author
  voice_channel = user.voice.voice_channel
  channel = None
  if voice_channel != None:
    channel=voice_channel.name
    vc = await channel.connect()
    audio = vc.play("holder.mp3", after=lambda: print("Complete."))
    vc.start()
    while vc.is_playing():
      await asyncio.sleep(1)
    vc.stop()
  else:
    await ctx.send("User must be in a voice channel.")

Answer

Vinicius Mesquita picture Vinicius Mesquita · Jun 21, 2019

Hello, some changes of methods have been made in this new version of discord.py 1.2.2, you can follow the migrations of methods in the documentation itself


first, you need to implement a class called YTDLSource to control yotube_dl

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

use this class in your music command, notice that my def play belongs to other class that i call Voice.

 @commands.command(pass_context=True)
    async def play(self, ctx, *, url):
        print(url)
        server = ctx.message.guild
        voice_channel = server.voice_client

        async with ctx.typing():
            player = await YTDLSource.from_url(url, loop=self.bot.loop)
            ctx.voice_channel.play(player, after=lambda e: print('Player error: %s' % e) if e else None)
        await ctx.send('Now playing: {}'.format(player.title))

before this, just add in your cog and enjoy!