node-ytdl-core icon indicating copy to clipboard operation
node-ytdl-core copied to clipboard

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

Open mattp94 opened this issue 3 years ago • 12 comments

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

mattp94 avatar Nov 08 '20 18:11 mattp94

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.

fent avatar Nov 08 '20 21:11 fent

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.

mattp94 avatar Nov 08 '20 23:11 mattp94

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'

fent avatar Nov 09 '20 06:11 fent

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 🙂

mattp94 avatar Nov 09 '20 18:11 mattp94

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

redbrain avatar Feb 20 '21 14:02 redbrain

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:

  • Download two separate buffers (audio/video) with the best track for each.
  • Convert them to files via fs.writeFile.
  • Run a child process with ffmpeg to combine both files (no encoding).
  • Then convert them back to Buffers (for my particular needs).

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.

fourjuaneight avatar Apr 25 '21 02:04 fourjuaneight

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

redbrain avatar Apr 25 '21 14:04 redbrain

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

fourjuaneight avatar Apr 26 '21 15:04 fourjuaneight

@redbrain that's a good work

Babailan avatar Mar 30 '22 15:03 Babailan

@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.

clipod avatar Oct 20 '22 01:10 clipod

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

Babailan avatar Oct 26 '22 05:10 Babailan

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

redbrain avatar Oct 31 '22 00:10 redbrain