fent / node-ytdl-core

YouTube video downloader in javascript.
MIT License
4.53k stars 802 forks source link

Stream is terminated 10 - 15 seconds before the end of the video #402

Closed TotomInc closed 4 years ago

TotomInc commented 5 years ago

It's been 1 month since users have been reporting that YouTube videos are not played entirely.

The bot often stops 10 - 15 seconds before the end of a video. I don't have any particular error thrown by ytdl nor by discord.js.

When I log the reason of a voice-connection ending is Stream is not generating quickly enough.. I really doubt this is a low bandwidth issue since the bot is hosted on a Google Cloud Platform VM with 400 mbps up/down.

What I'm doing with YTDL:

export function getReadableStream(track: models.Track): Readable {
  return YTDL(track.streamURL, { filter: 'audioonly' });
}

From this code snippet, I store the result of the function and play the stream with discord.js:

guildVoiceConnection.playStream(stream, streamOptions)
    .on('end', (reason) => _onTrackEnd(reason, message));

The bot is open-source if you want to dig-up more code.

I've also opened an issue on the repo of the bot.

EDIT : see @COPS2000 answer for a working fix

sharplol commented 5 years ago

Can confirm, tried the master discord.js build, as someone told me it has fully rewritten voice functions. Still no luck. Get same reason: "Stream is not generating quickly enough".

TotomInc commented 5 years ago

I would like to add I haven't tried to fetch and store the video audio into a .mp3 file and see if it contains the full audio of the video. I really doubt this is an issue from discord.js as it works fine with every SoundCloud tracks.

I also don't know if this happens randomly or everytime on a specific video.

sasjafor commented 5 years ago

I can confirm, I'm also experiencing this issue with my own bot. From what I can tell, is that this started happening without updating anything. So I would suspect that something changed with YouTube and that broke something with ytdl-core. I don't think the problem lies with youtube-dl itself, as many more people would be complaining about that and I also don't think the issue lies with discord.js.

fent commented 5 years ago

One thing I can suggest for a quick remedy is to try not using filter: 'audioonly'. Audio only formats have previously been heavily throttled. Maybe this would issue would happen less with other formats.

I would like to add I haven't tried to fetch and store the video audio into a .mp3 file and see if it contains the full audio of the video.

I would try this, see if the bot works by downloading the video to a file and then reading from the filesystem and feeding that to discord. I just tried myself downloading a random music video and it downloaded the whole thing.

sharplol commented 5 years ago

I would try this, see if the bot works by downloading the video to a file and then reading from the filesystem and feeding that to discord

Does work, it downloads the whole thing, and discord plays it all aswell. Removing filter, or changing it to a different one doesn't solve the issue with streaming on the fly. Still ends 15-30 secs before the end.

TotomInc commented 5 years ago

Does work, it downloads the whole thing, and discord plays it all aswell. Removing filter, or changing it to a different one doesn't solve the issue with streaming on the fly. Still ends 15-30 secs before the end.

So the quick-fix here is to stop in-memory streaming and instead stream from a file (which have been freshly downloaded using ytdl), which I really don't want to do for performance and storage reasons.

sharplol commented 5 years ago

So the quick-fix here is to stop in-memory streaming and instead stream from a file (which have been freshly downloaded using ytdl), which I really don't want to do for performance and storage reasons.

You can just delete the file after it has been played. It's a fair quick-fix for now, unless you're using the bot in a lot of guilds, this shouldn't impact performance as much.

fent commented 5 years ago

So the quick-fix here is to stop in-memory streaming and instead stream from a file (which have been freshly downloaded using ytdl), which I really don't want to do for performance and storage reasons.

Another thing you could try is increasing the highWaterMark option. The default is 16kb, you can try increasing it to 1<<25 (32mb) for example. With that option, it'll keep at most 32mb of a song in memory, which is more than enough for most songs.

Normally, discord will throttle the download to go as fast as it's being played. Which may not play well with youtube, it may not be as responsive to continuing a download or to reconnects if the stream is not actively consumed.

sasjafor commented 5 years ago

Another thing you could try is increasing the highWaterMark option. The default is 16kb, you can try increasing it to 1<<25 (32mb) for example. With that option, it'll keep at most 32mb of a song in memory, which is more than enough for most songs.

Normally, discord will throttle the download to go as fast as it's being played. Which may not play well with youtube, it may not be as responsive to continuing a download or to reconnects if the stream is not actively consumed.

From some quick testing this definitely helps, but it has the disadvantage that volume changes only really apply between songs. But as a temporary fix this seems great!

fent commented 5 years ago

but it has the disadvantage that volume changes only really apply between songs

How does this work? Are volume changes applied on the ytdl stream, or the discord voice connection? Is this a setting a user can change or does the bot change it?

Could also try lowering the highWaterMark a bit, try 1mb or a couple, that could contain just a few seconds of music, in which after you can apply the volume change?

sasjafor commented 5 years ago

How does this work? Are volume changes applied on the ytdl stream, or the discord voice connection? Is this a setting a user can change or does the bot change it?

Could also try lowering the highWaterMark a bit, try 1mb or a couple, that could contain just a few seconds of music, in which after you can apply the volume change?

The volume change is a feature of discord.js. I'm not completely aware of their source code anymore, but I think it is applied directly to the stream that is supplied to a method that plays the stream to a Discord voice channel. The volume option is not very important as every Discord user can just change the volume of the bot for themselves in the Discord client. I will try lower values for highWaterMark and report back if it still works without stopping before the end.

sasjafor commented 5 years ago

Sorry, I got confused because discord.js also has a highWaterMark option that you can pass and instead of increasing the option with ytdl, all along I was increasing it with discord.js.

The volume option of discord.js is completely unaffected by changing highWaterMark in ytdl, which makes sense actually.

Now, I will have to test more with actually changing the right option.

Mstrodl commented 5 years ago

Can also repro, even downloading it directly through ffmpeg (using ytdl.getInfo() and .formats[0].url) doesn't seem to work. It doesn't always stop at the end depending on the video. ytdl seems to stop emitting data some time shortly before playback stops. It usually stops 10-30s before the end of the video but it varies. It doesn't always happen either, shorter videos don't seem to have the issue (or if they do I haven't been able to reproduce it with them). Interestingly enough, it only seems to happen on one IP but not the other (I tested at two places, same ISP).

TotomInc commented 5 years ago

I agree with @Mstrodl, I can't reproduce this on shorter videos (max. 30-45 seconds length).

About the IP problem, my bot is currently hosted on a Compute Engine VM in Belgium. I should try to migrate the bot on other places like London, USA, Australia, ...

Mstrodl commented 5 years ago

If it's of any help, both the IPs tested were ordinary, residential IPs from the same ISP within a small geographical area in the USA. Youtube web player works as expected as well. Something interesting I noticed when looking in the network inspector in devtools was that the web player seems to make multiple different videoplayback requests every ~10-15s (this could just be something weird like HLS, but that doesn't seem likely). Each videoplayback request had different rn, range, and rbuf parameters.

HcgRandon commented 5 years ago

This is not a ytdl-core issue. It's also extremely easy to fix.

FireController1847 commented 5 years ago

@HcgRandon Okay, please elaborate.

TotomInc commented 5 years ago

It's also extremely easy to fix.

@HcgRandon comments like this don't add anything useful to the conversation. This is OSS software, everyone is trying to find how to fix this bug and collaborate with everyone.

HcgRandon commented 5 years ago

Fine,

The issue is caused by Youtube closing the connection before you can finish reading it. This can be solved by simply reopening the connection and using a Range header to resume where u left off.

With ffmpeg based connections this can be done by including -reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 4 in the ffmpeg arguments. Which you may notice that these are missing from discord.js's BasePlayer https://github.com/discordjs/discord.js/blob/master/src/client/voice/player/BasePlayer.js#L8

For opus re-streams something like node-miniget will solve the problem.

Enjoy.

fent commented 5 years ago

For opus re-streams something like node-miniget will solve the problem.

But ytdl-core uses miniget to download videos, with maxReconnect: 5. Which should be enough, but you can try to increase this number.

Can also repro, even downloading it directly through ffmpeg (using ytdl.getInfo() and .formats[0].url) doesn't seem to work.

If you're downloading from the format url directly, well you're on your own there. ytdl-core tries to help with downloading by using miniget, because we've gotten several problems in the past from reconnect and redirects. You can try the ffmpeg options that @HcgRandon suggested if you really have to do it that way.

And I'd also like to know if the people having issues have tried downloading videos and saving them onto a file. If that works fine for you, then ytdl-core is working as intended. If discord.js requires a long persistent connection that is downloaded at a slow rate, that's where the highWaterMark option can help you.

TotomInc commented 5 years ago

I agree for the highWaterMark option which fixes the issue for long streams with discord.js. No more bug since I passed this parameter.

We just need feedback when using ytdl-core to download to a file.

HcgRandon commented 5 years ago

@fent a lot of people still use ffmpeg for volume control reasons

Mstrodl commented 5 years ago

@fent Downloading directly from the URL with ffmpeg was just to try and figure out if it was ytdl-core causing it

rozivector commented 5 years ago

How about expand the duration time? Well, i don't think this is legit.

fent commented 5 years ago

Some people are reporting that using this new module fixes this issue for them

https://www.npmjs.com/package/ytdl-core-discord

sasjafor commented 5 years ago

I think that is probably because it always tries to use opus streams when possible. Have we even tried to compare opus vs non-opus/ffmpeg processed streams? I always used ffmpeg in my case (passing no type option to discord.js play method)

@HcgRandon said that miniget should solve the problem with opus streams. So if your module uses miniget, then only streams that are processed by ffmpeg would have this problem and the missing options in discord.js BasePlayer are the issue there.

For my use case I need to use ffmpeg as I'm not aware of a way to seek in pure opus streams without ffmpeg.

SirDeadlystrike commented 5 years ago

From my testing this issue is not present on 0.24.0 but present on 0.29.x

ewang2002 commented 5 years ago

From my testing this issue is not present on 0.24.0 but present on 0.29.x

I downgraded to 0.24.0 and noticed that the issue is no longer present; however, I will be doing more testing to make sure this is the case.

fent commented 5 years ago

From my testing this issue is not present on 0.24.0 but present on 0.29.x

Is this easily reproducible for you? Could you run git bisect so we can find the exact commit that's causing this?

ewang2002 commented 5 years ago

I did more extensive testing with ytdl-core 0.24.0 (bot play music in multiple servers instead of one server; bot played music for a longer duration of time) and noticed that the stream termination happens (10-15 seconds before the end of the video) again, although not as prevalent.

@SirDeadlystrike Can you describe your testing method? (i.e. how did you determine 0.24.0 was the ideal version?)

TotomInc commented 5 years ago

Did someone here tried the ytdl-core-discord from the discord.js team? I see the package is internally using the ytdl-core@0.29.1.

ewang2002 commented 5 years ago

Some people are reporting that using this new module fixes this issue for them

https://www.npmjs.com/package/ytdl-core-discord

The above quote came from fent.

For me personally, I'm probably not going to switch to ytdl-core-discord unless I continue to receive issues regarding music. I'm currently using ytdl-core 0.24.0 with the highWaterMark option set to fent's recommendation.

I know a friend personally that used ytdl-core-discord module and he said it seems to fix the stream termination issue.

Ensjam commented 5 years ago

switching to ytdl-core-discord did not solve the issue !

SirDeadlystrike commented 5 years ago

So a bit more info.

@ewang20027 0.24 was the version one of my bots with audio capabilities was running for quiet a few months and was considered mostly stable, not perfect, but stable enough.

Upon upgrading to the latest at the time 0.29.1 within an hour multiple reports of audio pausing came in and I was able to observe the affect myself after running tracks for a bit and patiently waiting.

I downgraded back to 0.24 and things seemed back to normal.

this issue is definitely elusive per @fent request to find the troublesome commit I recruited a few people to help and over the course of a evening we listened to over 10 hours of combined music spending roughly 1-2hrs on each commit (0.29.1, 0.29.0, 0.27, 0.24 and a few others in between). Not a single instance of it ending early was observed.

Later that day it happened twice within 20 minutes on 0.29.1 on songs that I have listened to plenty times before with zero issues. I can not find a common denominator or a way to reproduce this accurately.

My previous statement about the issue not existing on 0.24 may not be correct afterall, it may simply be not as prevalent per https://github.com/fent/node-ytdl-core/issues/402#issuecomment-469055578

ewang2002 commented 5 years ago

@Ensjam Interesting. I suspected that maybe ytdl-core-discord would work for some people. Assuming it isn't because of a CPU or internet issue, did you try fent's suggestion of increasing your highWaterMark option? Something like this:

const stream = ytdl(this.queue[0].getLink(), {
    filter: "audioonly",
    highWaterMark: 1<<25
});

Thanks for the clarification, @SirDeadlystrike.

SirDeadlystrike commented 5 years ago

My best guess with ytdl-core-discord fixing it for some people is that when available the use of the prism encoder for opus streams does not suffer from the same issues as ytdl-core does with using ffmpeg. And while not all yt videos can benefit from prisim, enough do to make the issue seem muted.

ray-1337 commented 5 years ago

My best guess with ytdl-core-discord fixing it for some people is that when available the use of the prism encoder for opus streams does not suffer from the same issues as ytdl-core does with using ffmpeg. And while not all yt videos can benefit from prisim, enough do to make the issue seem muted.

sadly cant adjust the volume.

amishshah commented 5 years ago

Hi, I'm the author of ytdl-core-discord.

To try and fix these issues, I've bypassed downloading the videos with ytdl-core itself and instead obtaining the m3u8 stream URL and providing that to FFmpeg instead. However, this will only affect a very small number of videos, as any WebM Opus videos are downloaded with ytdl-core.

If the issue persists, I will consider doing the same for WebM Opus streams to try and track down where the issue is actually occurring.

Edit: reading above comments, looks like this won't fix the issue 😕

TotomInc commented 5 years ago

Well is this bug still happening? My Discord bot is not online since a few months, so I haven't been able to test if this issue is still relevant.

amishshah commented 5 years ago

I assume it's still occurring

Raserhead commented 5 years ago

It is still occurring. Using it with Discord.js 12.0.0, stream will just stop without sending an end event. Tried some of the "workarounds" in this thread but they seem to create abnormalities and are somewhat inconsistent.

TotomInc commented 5 years ago

@Raserhead have you tried to increase the highWaterMark in your YTDL options? This worked well for my bot, however I haven't really tried on long videos (more than 1 hour long).

You can see this commit as a reference.

Raserhead commented 5 years ago

@TotomInc I have, Initially 1<<25 created some interruptions in the stream, to the point of being unbearable. Tried lowering to 1<<15 and the issue reappeared. I'm currently testing back at 1<<25 after increasing the discord channel bitrate, since I know the VPS I use should have more than sufficient bandwidth. Funny enough long videos (1-2hours) had the least issues and it was the average length (2-3mins) that it would reoccur, but that would usually only be during a queue (after several other average length songs had been played).

Discord.js also has it's own highWaterMark option for the dispatcher, using that one created a weird situation where a song played 2-3 songs ago would suddenly start replaying, not really relevant just something I found while fiddling. At least by using YTDL's option the volume can still be changed in realtime.

fent commented 5 years ago

please test this on ytdl-core version v0.29.3, just released. it uses an updated version of miniget that handles reconnects a little better.

majko96 commented 5 years ago

still the same problem, the stream ends in 3/4 of the video

NuwanDhanushka commented 5 years ago

is there any solution for this?

Raserhead commented 5 years ago

is there any solution for this?

using the highWaterMark option 1<<25 or higher on the ytdl stream that has solved it terminating early, comes with the occasional weirdness or stuttering if the connection can't keep up. I think an "official" fix is still in the works.

Mstrodl commented 5 years ago

Possibly relevant? discordjs/discord.js#3362 Has anyone tried downgrading below Node 10?

galnir commented 5 years ago

I'm not getting this issue on my bot(both on Windows and Debian). I'm using Node 10.11.0, ytdl-core 0.29.5 and discord.js master and these are my options: quality: 'highestaudio', highWaterMark: 1024 * 1024 * 10

TotomInc commented 5 years ago

@galnir you don't seem to have the issue due to your highWaterMark value being around the recommended value for the fix: we recommend setting it to 1<<25 (which is equal to 33554432) but yours is equal to 10485760 which is nearly the same.

The default value from the Node Stream class is very small, it's 16384 or around 16kb.