Lewis H
Lewis H

Reputation: 33

Discord.py rewrite - what is the source for YoutubeDL to play music?

As mentioned in the docs here, I need to use a source to play music using the play() command, I am trying to use YoutubeDL but I can't figure it out.

I have checked the rapptz discord.py basic voice example, but since I'm not using object-oriented programming its confusing me quite alot. Everywhere I have looked, their example is using the v0.16 discord.py, and I can't work out how to convert this player = await voice_client.create_ytdl_player(url) into the rewrite.

My play function at the moment looks like this:

async def play(ctx, url = None):
...
player = await YTDLSource(url) 
    await ctx.voice_client.play(player)
    await ctx.send("Now playing: " + player.title())

"YTDLSource" being a placeholder for the source.

Any help greatly appreciated, thanks.

Upvotes: 3

Views: 23968

Answers (2)

Themis
Themis

Reputation: 569

The discord docs now have a full example on how to make a voice bot that implements ytdl!

Check out the yt method in https://github.com/Rapptz/discord.py/blob/master/examples/basic_voice.py :

@commands.command()
async def yt(self, ctx, *, url):
    """Plays from a url (almost anything youtube_dl supports)"""

    async with ctx.typing():
        player = await YTDLSource.from_url(url, loop=self.bot.loop)
        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))

And the YTDLSource class it depends on:

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)

Change player = await YTDLSource.from_url(url, loop=self.bot.loop) to player = await YTDLSource.from_url(url, loop=self.bot.loop, stream=True) if you'd rather stream audio from youtube instead of predownloading it

pastebin archive: https://pastebin.com/nEiJ5YrD

Upvotes: 2

Liam McCullough
Liam McCullough

Reputation: 69

I am sure there are better ways of doing this with the rewrite, but I am in the same boat as you. I could not figure it out for the longest time.

After looking through youtube-dl documents and the rewrite documents this is the best I could come up with. Keep in mind I do not know if this will work with a queue system (probably not). Also I don't know if it's a bug or something I'm doing wrong when the bot joins and then you use the play command it does not output the music, but if the bot leaves then joins again the music will play. To fix I made my join command join, leave, and join.

Join command:

@bot.command(pass_context=True, brief="Makes the bot join your channel", aliases=['j', 'jo'])
async def join(ctx):
    channel = ctx.message.author.voice.channel
    if not channel:
        await ctx.send("You are not connected to a voice channel")
        return
    voice = get(bot.voice_clients, guild=ctx.guild)
    if voice and voice.is_connected():
        await voice.move_to(channel)
    else:
        voice = await channel.connect()
    await voice.disconnect()
    if voice and voice.is_connected():
        await voice.move_to(channel)
    else:
        voice = await channel.connect()
    await ctx.send(f"Joined {channel}")

play command:

@bot.command(pass_context=True, brief="This will play a song 'play [url]'", aliases=['pl'])
async def play(ctx, url: str):
    song_there = os.path.isfile("song.mp3")
    try:
        if song_there:
            os.remove("song.mp3")
    except PermissionError:
        await ctx.send("Wait for the current playing music end or use the 'stop' command")
        return
    await ctx.send("Getting everything ready, playing audio soon")
    print("Someone wants to play music let me get that ready for them...")
    voice = get(bot.voice_clients, guild=ctx.guild)
    ydl_opts = {
        'format': 'bestaudio/best',
        'postprocessors': [{
            'key': 'FFmpegExtractAudio',
            'preferredcodec': 'mp3',
            'preferredquality': '192',
        }],
    }
    with youtube_dl.YoutubeDL(ydl_opts) as ydl:
        ydl.download([url])
    for file in os.listdir("./"):
        if file.endswith(".mp3"):
            os.rename(file, 'song.mp3')
    voice.play(discord.FFmpegPCMAudio("song.mp3"))
    voice.volume = 100
    voice.is_playing()

Leave command:

@bot.command(pass_context=True, brief="Makes the bot leave your channel", aliases=['l', 'le', 'lea'])
async def leave(ctx):
    channel = ctx.message.author.voice.channel
    voice = get(bot.voice_clients, guild=ctx.guild)
    if voice and voice.is_connected():
        await voice.disconnect()
        await ctx.send(f"Left {channel}")
    else:
        await ctx.send("Don't think I am in a voice channel")

All that needs to be imported (I think):

import discord
import youtube_dl
import os
from discord.ext import commands
from discord.utils import get
from discord import FFmpegPCMAudio
from os import system

you also might need to download ffmpeg off their website (there are youtube tutorials on how to do so and install it)

With the Play command post with a youtube url ('/play www.youtube.com') it will first look for a 'song.mp3' and delete it if there is one, download the new song rename it to 'song.mp3' then plays the mp3 file. The mp3 file will be put in them same directory as your bot.py

Like I said before there is probably a batter way to do this allowing a queue command, but I don't know that way as of now.

hope this helps!

Upvotes: 5

Related Questions