Ожидание загрузки с asyncio в Python - PullRequest
1 голос
/ 17 июня 2020

Я работаю над ботом Discord и пытаюсь реализовать проигрыватель musi c. Я использую пакеты Discord и youtube-dl. Это функция, которая обрабатывает команду воспроизведения (это все еще прототип):

@client.command(brief='Plays the song from the url.')
async def play(ctx, url):
    voice = get(client.voice_clients, guild=ctx.guild)
    if not voice.is_playing():
        try:
            if 'song.mp3' in os.listdir(curr_dir):
                os.remove(os.path.join(curr_dir, 'song.mp3'))
            await download_to_mp3(url)
            voice.play(discord.FFmpegPCMAudio(os.path.join(curr_dir, 'song.mp3')))
            voice.volume = 100
        except youtube_dl.DownloadError:
            await ctx.send('Invalid url.')

, а также функция download_to_mp3 ():

async def download_to_mp3(url):
    opts = {
        'outtmpl': os.path.join(curr_dir, 'song.webm'),
        'format': 'bestaudio/best',
        'postprocessors': [{
            'key': 'FFmpegExtractAudio',
            'preferredcodec': 'mp3',
            'preferredquality': '192',
        }],
    }
    with youtube_dl.YoutubeDL(opts) as ydl:
        ydl.download([url])

Я хотел, чтобы она работала в таком Таким образом, пока загрузка завершается, я все еще могу использовать другие функции бота. Насколько я понимаю, оператор await гласит: «Приостановите выполнение функции play (), сделайте что-нибудь еще, пока я жду. Когда download_to_mp3 завершится, продолжайте». Однако кажется, что он читает команды, выданные во время загрузки, но выполняет их только после завершения загрузки. Как заставить его выполнять команды во время загрузки?

1 Ответ

0 голосов
/ 19 июня 2020

Насколько я понимаю, в заявлении await говорится: «Приостановите выполнение функции play (), сделайте что-нибудь еще, пока я жду. Когда загрузка_to_mp3 завершится, продолжайте»

Именно так это работает, если вы следуете правилам asyn c, главное из которых: не блокировать во время asyn c. Поскольку YoutubeDL.download, по-видимому, является функцией блокировки (вы не ждете ее), выполнение download_to_mp3 останавливает все событие l oop. Сам факт отсутствия оператора await в download_to_mp3 подсказывает вам, что эта функция является asyn c только по названию.

Правильный способ исправить это - переключиться с YoutubeDL на асинхронный c загрузчик того же типа, если он существует. Если это не вариант или вам нужно быстрое исправление, вы можете использовать run_in_executor, который выполнит функцию блокировки в другом потоке и вернет ожидаемый объект, который приостанавливает ожидание до блокировки функция сделана. Например:

@client.command(brief='Plays the song from the url.')
async def play(ctx, url):
    voice = get(client.voice_clients, guild=ctx.guild)
    if not voice.is_playing():
        try:
            if 'song.mp3' in os.listdir(curr_dir):
                os.remove(os.path.join(curr_dir, 'song.mp3'))
            loop = asyncio.get_event_loop()
            await loop.run_in_executor(None, download_to_mp3, url)
            voice.play(discord.FFmpegPCMAudio(
                os.path.join(curr_dir, 'song.mp3')))
            voice.volume = 100
        except youtube_dl.DownloadError:
            await ctx.send('Invalid url.')

# note: download_to_mp3 is now an ordinary function, not an async one
def download_to_mp3(url):
    ... the same definition as before ...
...