Я разрабатываю бота Discord, используя discord.py
, и в рамках этого бота мне поручено создавать музыкальный функционал. Я действительно это сделал, и бот будет воспроизводить песни с помощью модуля pytube
. Для этого он загружает лучший источник звука, преобразует его в .wav
(чтобы Discord мог его понять), затем открывает этот файл и воспроизводит его через голосовой клиент, чтобы его могли услышать все участники голосового вызова. Проблема в том, что он работает очень медленно из-за необходимости загрузки полного аудио перед началом воспроизведения. Это код в его нынешнем виде:
# Plays a Youtube object in the voice call.
async def play_youtube_object(self, yt:YouTube, interaction:discord.Interaction, voice_client):
try:
# Extract audio stream with highest audio bitrate
audio_stream = yt.streams.filter(file_extension = "webm").order_by('abr').desc().first()
audio_filename = os.path.abspath(rf"./data/music cache/{yt.author}{hex(random.randint(1,999999))[2:]}")
audio_stream.download(filename=audio_filename+".webm")
# Convert from .webm to .wav
audio_f = moviepy.AudioFileClip(audio_filename+".webm")
audio_f.write_audiofile(audio_filename+".wav", fps=48000) # Discord requires 48kHz, 44.1kHz causes it to speed up by 8.125%.
remove_file(audio_filename + ".webm") # Remove the .webm since we're now done with it
# Open the downloaded audio file in binary mode and create a PCMAudio object which can be interpreted by discord
f = open(audio_filename+".wav", "rb")
source = PCMAudio(f)
# Play the audio
voice_client.play(source)
# Wait until the song is finished before ending the function
while voice_client.is_playing() or voice_client.is_paused():
await asyncio.sleep(1)
f.close()
except ...
Мое потенциальное решение этой проблемы (если это возможно) — транслировать аудио прямо с YouTube, конвертировать его в источник PCMAudio
и передавать в Discord Live. Проблема в том, что я понятия не имею, с чего начать реализацию этого. Является ли это возможным? Если да, то где мне следует начать исследования для этого? Есть ли модуль, который может помочь?
По сути, цель состоит в том, чтобы получить близкую к мгновенной обратную связь: объект pytube
YouTube
анализируется в подпрограмме play_youtube_object
, и звук начинает воспроизводиться для пользователя в Discord сразу после этого (или, по крайней мере, не с задержкой до 20 секунд, как сейчас). .
Любая помощь или подсказки приветствуются, заранее спасибо! :D
В Python также есть модуль ffmpeg-python, который может быть полезен.
Решено! Благодаря некоторой помощи, указывающей мне в правильном направлении из комментариев. Использование yt-dlp
и использование ffmpeg
по конвейеру сработало блестяще и устранило эту неприятную задержку (теперь примерно 3 секунды, что совсем неплохо).
Это рабочий код, в котором на данный момент немного беспорядок, но я решил закончить этот пост, прежде чем продолжить работу над ним:
async def play_youtube_url(self, url, interaction:discord.Interaction, voice_client):
ydl_opts = {
'format': 'bestaudio/best',
'quiet': True,
'noplaylist': True
}
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info_dict = ydl.extract_info(url, download=False)
audio_url = info_dict['url']
ffmpeg_options = {
'options': '-vn'
}
source = FFmpegPCMAudio(audio_url, **ffmpeg_options)
voice_client.play(source)
while voice_client.is_playing() or voice_client.is_paused():
await asyncio.sleep(1)
except Exception as e:
await interaction.followup.send(f"An error occured: {e}")
Благодаря yt-dlp
программа сможет обрабатывать гораздо больше аудиоисточников, но не тех, которые имеют защиту DRM (например, Spotify), поэтому мне, возможно, придется посмотреть, смогу ли я найти способ обойти это, или, если кто-нибудь что-нибудь знает, пожалуйста, прокомментируйте: ).
Я не знаю, поддерживает ли pytube потоковую передачу, но у вас
yt-dlp
есть возможность перенаправления в другую программу, аffmpeg
(который также используется вmoviepy
) должна быть возможность читать из другой программы, а также перенаправляться в другую программу - используяpipe
- вbash
в Linux он может выглядеть похоже наyt-dlp -o - ... | ffmpeg .... | vlc -
, ноsubprocess
вpython
также может его перенаправить, но, возможно, над этим придется поработать.