Запустите асинхронную функцию, когда youtube-dl закончит загрузку (python). - PullRequest
0 голосов
/ 17 сентября 2018

Я пишу музыкального бота для python, используя перезапись discord.py. Он загружает видео через YouTube и воспроизводит их в голосовом чате. Я усердно работал над расширением музыки и недавно понял, что что-то упустил из виду. Параметр progress hooks для youtube-dl является синхронным, а discord.py - асинхронным. youtube-dl порождает подпроцесс при загрузке видео, а не при запуске его в текущем потоке, поэтому программа не зависает. Функция, которую мне нужно запустить после завершения загрузки, является сопрограммой, так как она является частью discord.py

TL; DR Мне нужно запустить сопрограмму после завершения загрузки youtube-dl

Я знаю, что это возможно, я уже видел это, но не совсем понимаю.

Вот что у меня есть:

def log(text):
  print(Style.BRIGHT + Fore.WHITE + '[' + Fore.RED + 'Music' + Fore.WHITE + '] ' + Style.RESET_ALL + text)
def sync_config():
  raw_config.seek(0)
  raw_config.write(json.dumps(config))
  raw_config.truncate()
lookup_opts = {
    "simulate": True,
    "quiet" : True, #TODO: make this part of config.json
}

if not os.path.exists("plugins/music"):
  log("Config does not exist! Creating it for you..")
  os.makedirs("plugins/music")
if not os.path.exists("plugins/music/cache"):
  os.makedirs("plugins/music/cache")
if not os.path.exists("plugins/music/config.json"):
    with open('plugins/music/config.json', 'w+') as f:
      f.write('{}')
      log('Created config.json')
raw_config = open('plugins/music/config.json', 'r+')
config = json.load(raw_config)

class Music:
  def __init__(self, bot):
      self.bot = bot
  @commands.command(hidden=True)
  async def clearcache(self, ctx):
    if ctx.author.id in ctx.bot.config["admins"]:
      log("Cache cleared!")
      await ctx.message.add_reaction("✅")
      shutil.rmtree("plugins/music/cache")
      os.makedirs("plugins/music/cache")
    else:
      await ctx.send(ctx.bot.denied())
  @commands.command()
  async def play(self, ctx, url):
    """Download and play a link from youtube"""
    message = await ctx.send(f"Downloading <{url}>..")
    with youtube_dl.YoutubeDL(lookup_opts) as ydl:
      try:
        info = ydl.extract_info(url)
        await message.edit(content=f"Downloading {info['title']}..")
      except:
        await ctx.send("An error occured downloading that video! Are you sure that URL is correct?")
      def callback(d):
        if d['status'] == 'finished':
              loop = asyncio.new_event_loop()
              asyncio.set_event_loop(loop)
              loop.run_until_complete(ctx.send("Done!"))
              print("Done!")
      download_opts = {
        "format": 'm4a/bestaudio/best',
        "quiet" : True, #TODO: make this part of config.json
        'progress_hooks': [callback],
      }
      with youtube_dl.YoutubeDL(download_opts) as ydl:
          ydl.download([url])

1 Ответ

0 голосов
/ 20 сентября 2018

Самый простой способ запланировать асинхронное выполнение сопрограммы из кода блокировки - loop.create_task.Так как callback наследует область действия метода play, мы можем напрямую использовать self.bot.loop:

  def callback(d):
    if d['status'] == 'finished':
          self.bot.loop.create_task(ctx.send("Done!"))
          print("Done!")
...