node-ytdl-core
node-ytdl-core copied to clipboard
Option quality with highest or lowest doesn't work anymore since v4
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
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.
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.
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'
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 🙂
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
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.
I actually manage a npm package called ytdl-core-muxer that accomplishes this all in Streams, no temp files needed.
@redbrain Nice implemetnation! This will work nicely for me.
@redbrain that's a good work
@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 you can't they just check if the api render any type of videos
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