I'm currently working on having my Discord music bot create a keyword search menu, similar to the one depicted in the image. I'm utilizing the ytsearch
from the ytdlp
library to retrieve the top five results for a keyword search. The search process often takes longer than 3 seconds, resulting in unsuccessful responses. I'm curious if any of you have effective strategies to reduce this search time.
I've attempted to reference this article for guidance. Although the code runs well, the issue of timeouts and failed responses persists, and I'm unsure how to address it.
Code snippet:
from discord.commands import slash_command
import yt_dlp as youtube_dl
youtube_dl.utils.bug_reports_message = lambda: ''
ytdlopts = {
'format': 'bestaudio/best',
'extractaudio': True,
'outtmpl': 'downloads/%(extractor)s-%(id)s-%(title)s.%(ext)s',
'restrictfilenames': True,
'noplaylist': True,
'nocheckcertificate': True,
'ignoreerrors': False,
'logtostderr': False,
'quiet': True,
'no_warnings': True,
'dump_single_json': True,
'default_search': 'auto',
'postprocessors': [{"key" : "FFmpegExtractAudio", "preferredcodec" : "mp3", "preferredquality" : "256"}],
'buffersize': 16777216,
'source_address': '0.0.0.0' # ipv6 addresses cause issues sometimes
}
ytdl = youtube_dl.YoutubeDL(ytdlopts)
async def song_search(self, ctx):
options = []
if ctx.value:
to_run = partial(ytdl.extract_info, f"ytsearch5:{ctx.value}", download=False)
info_dict = await asyncio.get_event_loop().run_in_executor(None, to_run)
if 'entries' in info_dict:
# Extract up to 5 items from the list of entries
entries = info_dict['entries'][:5]
for entry in entries:
if 'title' in entry:
options.append(entry['title'])
print(options)
return options
@slash_command(name="play", description="play")
@option("url",description="url", autocomplete = song_search)
async def play_(self, ctx, *, url: str):
await ctx.trigger_typing()
vc = ctx.voice_client
if not vc:
await self.join_channel(ctx)
await self.load_source_defer(ctx)
player = self.get_player(ctx)
source = await self.get_music_source(ctx, url)
await player.queue.put(source)
The error that appears:
['Create Your Own Discord Bot in Python 3.10 Tutorial (2022 Edition)', 'The EASIEST Discord Chat Bot Tutorial On The Internet (Python 3.10) 2023', 'Code a Discord Bot with Python - Host for Free in the Cloud', 'Making a Discord Bot In Python (Part 1: Setup)', 'All you need to know about Buttons in Discord.py & Pycord | Ultimate Python Guide']
Task exception was never retrieved
future: <Task finished name='Task-44' coro=<ApplicationCommandMixin.on_application_command_auto_complete.<locals>.callback() done, defined at C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\bot.py:853> exception=NotFound('404 Not Found (error code: 10062): Unknown interaction')>
Traceback (most recent call last):
File "C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\bot.py", line 856, in callback
return await command.invoke_autocomplete_callback(ctx)
File "C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\commands\core.py", line 1011, in invoke_autocomplete_callback
return await ctx.interaction.response.send_autocomplete_result(
File "C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\interactions.py", line 1017, in send_autocomplete_result
await self._locked_response(
File "C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\interactions.py", line 1090, in _locked_response
await coro
File "C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\webhook\async_.py", line 219, in request
raise NotFound(response, data)
discord.errors.NotFound: 404 Not Found (error code: 10062): Unknown interaction
Here's an example that I have below:
breed_types = ['German Shepherd', 'Bulldog', 'etc'] # list of breed types to autocomplete
dog_breeds = discord.Option(str, autocomplete=discord.utils.basic_autocomplete(breed_types),
required=False) # create options using autocomplete util
@bot.slash_command(name="dog", description="Random dog picture", )
async def dog(ctx, breed: dog_breeds):
await ctx.respond(breed)
This is what it outputs:
Here's what happens when you input something:
So here's how it works.
breed_types
is a list of different options. Technically, it is a global variable because it's where it's placed scope-wise, but I just put it there for explanation purposes (you could use a function to generate it). Then that gets put into dog_breeds
which is then put into autocomplete with the discord utils handler (I cover why I use this later). This allows for autocomplete input of anything in the list. However, it can let the user input anything outside of the list breed_types
which can lead to errors.
How to make the options non optional
If we edit the following lines:
def convert_list_to_options(input_list):
return [discord.OptionChoice(name=option) for option in input_list]
dog_breeds = discord.Option(str, choices=convert_list_to_options(breed_types),
required=False) # create options using helper function
I've created the above helper function. It converts a string list into discord.OptionChoice()
for the attribute choices
.
The list of available choices for this option. Can be a list of values or OptionChoice objects (which represent a name:value pair). If provided, the input from the user must match one of the choices in the list.
However, if you have a large number of options, the above method may not work. So, instead, you can add the following (to your slash command) and use the first method
if breed is not None:
if breed not in breed_types:
return await ctx.respond('Option does not exist', ephemeral=True)
else:
(insert code here)
Currently, the documentation says the following:
The autocomplete handler for the option. Accepts an iterable of str, a callable (sync or async) that takes a single argument of AutocompleteContext, or a coroutine. Must resolve to an iterable of str.
The: "Must resolve to an iterable of str." is not true. This is currently a code or documentation issue that hasn't been resolved yet. In the meanwhile, use the discord.utils
helper function provided above.
I've attempted to reference this article for guidance. Although the code runs well, the issue of timeouts and failed responses persists, and I'm unsure how to address it.
If your slash command itself is taking time you could use:
await ctx.defer()
Using the above at the beginning of a slash command will not time the command out.
This is typically used when the interaction is acknowledged and a secondary action will be done later.
However, if the slash option itself is timing out, you could try this in your song_search
function:
await ctx.interaction.response.defer()
Looking through the documentation, interaction.response
(looked into source too) is of class discord.InteractionResponse