Discord.py: How to implement ytsearch on the play command?

Viewed 1707

Hi guys I created this music bot and I want to implement the ytsearch on the play button like the bot "Rythm". For the moment it can play music only with an url but I want my bot to be able to play from url and from keywords (like !play never gonna give you up) with the same command, but I have no idea how to do this. Can you guys help me?

musics= {}
ytdl = youtube_dl.YoutubeDL()

class Video:
    def __init__(self, link):
        video = ytdl.extract_info(link, download = False)
        video_format = video["formats"][0]
        self.url =video["webpage_url"]
        self.stream_url =video_format["url"]

def play_song(client, queue, song):
    source= discord.PCMVolumeTransformer(discord.FFmpegPCMAudio(song.stream_url
    , before_options = "-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5"))
    
    def next(_):
        if len(queue)>0:
            new_song= queue[0]
            del queue[0]
            play_song(client, queue, new_song)
        else:
            asyncio.run_coroutine_threadsafe(client.disconnect(), bot.loop)
    
    
    client.play(source, after=next)


@bot.command()
async def play (ctx, url):
    client = ctx.guild.voice_client
    if client and client.channel:
        video = Video(url)
        musics[ctx.guild].append(video)
    else:
        channel = ctx.author.voice.channel
        video = Video(url)
        musics[ctx.guild] = []
        client = await channel.connect()
        await ctx.send(f"Playing : {video.url}")
        play_song(client, musics[ctx.guild], video)

Edit: So after triying to add the ytdl_options it gives me this error, even without the --noplaylist, it still gives me the same error (the music that I'm triying to play is blinding lights).

ytdl_options = {
    'noplaylist': True,
    'default_search': 'auto'
}


ytdl = youtube_dl.YoutubeDL(ytdl_options)
[download] Downloading playlist: blinding
[youtube:search] query "blinding": Downloading page 1
[youtube:search] playlist blinding: Downloading 1 videos
[download] Downloading video 1 of 1
[youtube] 4NRXx6U8ABQ: Downloading webpage
[youtube] Downloading just video 4NRXx6U8ABQ because of --no-playlist
[download] Finished downloading playlist: blinding
Ignoring exception in command play:
Traceback (most recent call last):
  File "C:\Users\pietr\AppData\Local\Programs\Python\Python39\lib\site-packages\discord\ext\commands\core.py", line 85, in wrapped
    ret = await coro(*args, **kwargs)
  File "c:\Users\pietr\Desktop\Discord bot\Discord bot", line 152, in play
    video = Video(url)
  File "c:\Users\pietr\Desktop\Discord bot\Discord bot", line 93, in __init__
    video_format = video["formats"][0]
KeyError: 'formats'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "C:\Users\pietr\AppData\Local\Programs\Python\Python39\lib\site-packages\discord\ext\commands\bot.py", line 902, in invoke
    await ctx.command.invoke(ctx)
  File "C:\Users\pietr\AppData\Local\Programs\Python\Python39\lib\site-packages\discord\ext\commands\core.py", line 864, in invoke
    await injected(*ctx.args, **ctx.kwargs)
  File "C:\Users\pietr\AppData\Local\Programs\Python\Python39\lib\site-packages\discord\ext\commands\core.py", line 94, in wrapped
    raise CommandInvokeError(exc) from exc
discord.ext.commands.errors.CommandInvokeError: Command raised an exception: KeyError: 'formats'
2 Answers

When you create the YoutubeDL object, you can specify its default search behaviour by setting the default_search option.

Quoted from here (emphasis mine):

Use this prefix for unqualified URLs. For example "gvsearch2:" downloads two videos from google videos for youtube-dl "large apple". Use the value "auto" to let youtube-dl guess ("auto_warning" to emit a warning when guessing). "error" just throws an error. The default value "fixup_error" repairs broken URLs, but emits an error if this is not possible instead of searching.

ytdl_options = {'default_search': 'auto'}
ytdl = youtube_dl.YoutubeDL(ytdl_options)

You need to handle cases where a search is done, as the returned format is different to when a valid URL is supplied.

import youtube_dl

ytdl_options = {
    'noplaylist': True,
    'default_search': 'auto'
}

ytdl = youtube_dl.YoutubeDL(ytdl_options)

link = 'test search'

video = ytdl.extract_info(link, download = False)

if 'entries' in video:
    video_format = video['entries'][0]["formats"][0]
elif 'formats' in video:
    video_format = video["formats"][0]

url = video["webpage_url"]
stream_url = video_format["url"]

Probably would like to look at the Youtube API. Change the resource to search and then click on list (by keyword). You'll get sample code on it.

You will first need to Obtain authorization credentials in order to get it to work properly.

Related