fent / node-ytdl-core

YouTube video downloader in javascript.
MIT License
4.48k stars 790 forks source link

Option quality with highest or lowest doesn't work anymore since v4 #770

Open mattp94 opened 3 years ago

mattp94 commented 3 years ago

Hey, thanks for this great package! πŸ™‚

It seems that the quality option with values like highest or lowest doesn't work anymore since v4. In this example, we have:

ytdl('aqz-KE-bpKQ', { quality: 'highest' }) // => itag 22 / 720p with v3.4.2
ytdl('aqz-KE-bpKQ', { quality: 'highest' }) // => itag 18 / 360p with v4.0.3

It seems that the sortFormats method has changed between v3 and v4 and no longer returns the same order: https://github.com/fent/node-ytdl-core/blob/4a017941c87d56181e0a510c1346dad3cc24a75e/lib/info.js#L285

[22, 18, 315, 308, 299, 303, 399, 298, 302, 136, 398, 247, 135, 244, 397, 134, 243, 396, 133, 242, 395, 160, 278, 394, 251, 140, 250, 249, 258, 256] // => with v3.4.2
[18, 315, 308, 299, 303, 399, 298, 302, 136, 398, 247, 135, 244, 397, 134, 243, 396, 133, 242, 395, 160, 278, 394, 258, 256, 251, 140, 250, 249, 22] // => with v4.0.3

But the indexes mapping didn't change between v3 and v4 which could explain the wrong match:

https://github.com/fent/node-ytdl-core/blob/3f57ce9845cb00502f68e251934c6317a3efad8f/lib/format-utils.js#L110-L116

fent commented 3 years ago

this is intentional, due to some formats, particularly itag 22 and dash mpd formats, being unreliable. the sorting was changed for these so that a download would have a greater chance of succeeding for the typical user.

https://github.com/fent/node-ytdl-core/blob/3f57ce9845cb00502f68e251934c6317a3efad8f/lib/format-utils.js#L65-L84

all the formats are still there for the power user if they'd like to get a higher quality video. note that itag 22, 720p, is usually not the highest available video quality. so if a user wanted the highest video quality, they could sort by highestvideo, which does not go through the regular "default" sorting.

mattp94 commented 3 years ago

Thanks for your reply @fent. But in my case, I wanted the best combination video + audio which was the itag 22 (720p), not the itag 18 (360p). Unfortunately, highestvideo doesn't contain audio track.

Moreover, when you choose lowest, you get the itag 22 which doesn't seem to be logic.

fent commented 3 years ago

Moreover, when you choose lowest, you get the itag 22 which doesn't seem to be logic.

that's true. maybe this default sorting can be kept as default, but renamed "best" or "recommended". it doesn't make sense that a highest quality format would be considered the format with both video and audio. it's subjective, for some, it could be the format with the highest quality video.

highestvideo, highestaudio, lowestvideo, lowestaudio work better as their sorting really is based on the quality of audio or video.

for your case, you could use filter: 'audioandvideo', quality: 'highestvideo'

mattp94 commented 3 years ago

That's a good idea, best / worst or recommended is less ambiguous than highest / lowest!

for your case, you could use filter: 'audioandvideo', quality: 'highestvideo'

Thanks, this works perfectly πŸ™‚

redbrain commented 3 years ago

It would be nice if a best/worst/reliable filter name scheme was implemented, however you should probably think more about it since it's quite a breaking change

fourjuaneight commented 3 years ago

So I noticed that using filter: 'audioandvideo', quality: 'highestvideo' still didn't yield the right results for me. Because of how YouTube creates various versions of a video for different quality playback, the highestvideo doesn't always come with the highestaudio. For example, a 720p video might have the best audio, but the 1080p is the one with the best video, but might not have an audio file. This is actually expected behavior and not an issue with ytdl-core necessarily.

How I solve this particular issue with youtube-dl, for example, is downloading 2 separate files (best audio and best video), then combine them into a single file. If I'm not mistaken, youtube-dl uses ffmpeg under the hood and it's how it accomplishes this in a single command:

youtube-dl -f 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/bestvideo+bestaudio:' --merge-output-format mp4 -o "%(title)s.%(ext)s" <URL>

So that's the approach I took to solve this issue with ytdl-core:

You can see this in practice here: https://github.com/fourjuaneight/archiver/blob/main/src/helpers/getYTVid.ts.

Anyways, thought I'd share my two cents. Maybe it'll help someone else. Goes without saying that this solution requires ffmpeg to be installed. So, your mileage may vary.

redbrain commented 3 years ago

I actually manage a npm package called ytdl-core-muxer that accomplishes this all in Streams, no temp files needed.

fourjuaneight commented 3 years ago

@redbrain Nice implemetnation! This will work nicely for me.

Babailan commented 2 years ago

@redbrain that's a good work

clipod commented 1 year ago

@redbrain It worked out good but the output is in mkv. I tried to change your source code where it says matroska to mp4, it throws error. How to create a MP4 file as the output.

Babailan commented 1 year ago

@clipod you can't they just check if the api render any type of videos

redbrain commented 1 year ago

It can be an mp4 only if the video source container is mp4 and the audio source container is m4a also, you'll need to add some extra flags to ffmpeg or else it will try to break the streaming interface sample code