Rapptz / discord.py

An API wrapper for Discord written in Python.
http://discordpy.rtfd.org/en/latest
MIT License
14.88k stars 3.76k forks source link

VoiceClient.play() plays too fast in the first 5-10 seconds when streaming from youtube url #9120

Open Gabeperson opened 1 year ago

Gabeperson commented 1 year ago

Summary

If you stream any audio from a youtube url (not sure if the same applies for non-youtube urls), the first 5-10 seconds of audio are sped up by a significant amount. This does not happen when downloading the video and playing it locally using youtube-dl. I have also tried extracting the video url using PyTube to see if youtube-dl was the problem, but the problem still persists with it as well. Also, this does not happen all the time. Sometimes, the audio will play normally. Sometimes it will play quickly. I have replicated this with basic_voice.py.

Reproduction Steps

  1. Copy the code from examples/basic_voice.py into python file.
  2. Put discord token in.
  3. Start the bot.
  4. Join a voice channel.
  5. Type !yt (link)
  6. Listen.

Minimal Reproducible Code

No response

Expected Results

Audio starts playing at the same speed throughout the entire clip.

Actual Results

Audio starts playing quickly then slows down to the usual pace after around 5-10 seconds of audio.

Intents

message_content

System Information

Checklist

Additional Context

A question pertaining to the same (or a similar) bug was posted to StackOverflow around 1 year ago: https://stackoverflow.com/questions/70545716/discord-py-ffmpeg-song-plays-too-quickly-at-the-beginning The only "workaround" that is possible is to asyncio.sleep(), as the comment in the post suggests. However, this does not guarantee that this will happen. It merely decreases the chances. Due to the randomness and how asyncio.sleep() seems to somehow slightly alleviate this, I'm thinking it's some sort of network problem, but I could be wrong.

TimG233 commented 1 year ago

I got the same issue with the bot sometimes. I used extract_info rather than download for youtube_dl. I haven't found a solution yet. But seems like the situation is worse when testing locally than running the bot on a server (digital ocean). And the StackOverflow question is definitely not helping. (since asyncio.sleep(...) is not a proper way to solve this, and I mean like how long I should even sleep is something unknown and unsure)

blastbeng commented 1 year ago

+1 same problem here

I have a client server configuration. The bot is the client, and I have apis server side that do the processing work.

The Bot calls the API /youtube/get/myvideoid, then the server starts downloading audio and send it back to the client using Flask send_file. So all i receive from the Bot is a pretranscoded mp3 audio.

This is the code:

@client.tree.command()
@app_commands.rename(url='url')
@app_commands.describe(url="Youtube link (Must match https://www.youtube.com/watch?v=1abcd2efghi)")
async def youtube(interaction: discord.Interaction, url: str):
    """Play a youtube link"""
    try:
        if interaction.user.voice and interaction.user.voice.channel:
            voice_client = get_voice_client_by_guildid(client.voice_clients, interaction.guild.id)
            await connect_bot_by_voice_client(voice_client, interaction.user.voice.channel)

            if not hasattr(voice_client, 'play'):
                await interaction.response.send_message(utils.translate(get_current_guild_id(interaction.guild.id),"Retry in a moment, I'm initializing the voice connection..."), ephemeral = True)
            elif not voice_client.is_playing():

                if "watch?v=" in url:
                    currentguildid = get_current_guild_id(interaction.guild.id)
                    message = utils.translate(get_current_guild_id(interaction.guild.id),"I'm playing audio from") +' ' + url
                    voice_client.play(discord.FFmpegPCMAudio(os.environ.get("API_URL")+os.environ.get("API_PATH_MUSIC")+"youtube/get/"+(url.split("watch?v=",1)[1])+"/"+urllib.parse.quote(currentguildid)), after=lambda e: logging.info(message))
                    await interaction.response.send_message(message, ephemeral = True)
                else:
                    await interaction.response.send_message(utils.translate(get_current_guild_id(interaction.guild.id),"Retry in a moment or use /stop command"), ephemeral = True)
            else:
                await interaction.response.send_message(utils.translate(get_current_guild_id(interaction.guild.id),"URL must match something like https://www.youtube.com/watch?v=1abcd2efghi"), ephemeral = True)

        else:
            await interaction.response.send_message(utils.translate(get_current_guild_id(interaction.guild.id),"You must be connected to a voice channel to use this command"), ephemeral = True)
    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
        logging.error("%s %s %s", exc_type, fname, exc_tb.tb_lineno, exc_info=1)
        await interaction.response.send_message(utils.translate(get_current_guild_id(interaction.guild.id),"Error")+"!", ephemeral = True)

As you can see the important line is this one: voice_client.play(discord.FFmpegPCMAudio(os.environ.get("API_URL")+os.environ.get("API_PATH_MUSIC")+"youtube/get/"+(url.split("watch?v=",1)[1])+"/"+urllib.parse.quote(currentguildid)), after=lambda e: logging.info(message))

I directly play the generated audio calling my APIS, the resulting URL is something like: http://127.0.0.1/music/youtube/get/671b12ye6712/91236381278812821

Any thoughts?

hc20k commented 1 year ago

Has anyone found a reliable solution to this issue? I'm also having the same problem.