YouTube video downloader in pure JavaScript with streaming interface and comprehensive metadata extraction.
Advanced format selection utilities for choosing optimal video and audio formats based on quality, codec, and container preferences. Provides both automated selection and manual filtering capabilities.
Automatically selects the best format from available options based on specified criteria.
/**
* Chooses the best format from available formats based on criteria
* @param {videoFormat | videoFormat[]} formats - Available formats or single format
* @param {chooseFormatOptions} options - Selection criteria
* @returns {videoFormat} - Selected format
* @throws {Error} - If no matching format found
*/
function chooseFormat(formats, options);
interface chooseFormatOptions {
quality?: string | number | string[] | number[]; // Quality preference
filter?: string | function; // Format filter
format?: videoFormat; // Specific format to use
}Usage Examples:
const info = await ytdl.getInfo(videoUrl);
// Choose highest quality format (default)
const format = ytdl.chooseFormat(info.formats, { quality: 'highest' });
// Choose by quality string
const audioFormat = ytdl.chooseFormat(info.formats, { quality: 'highestaudio' });
const videoFormat = ytdl.chooseFormat(info.formats, { quality: 'highestvideo' });
// Choose by itag number
const specificFormat = ytdl.chooseFormat(info.formats, { quality: 140 });
// Choose from multiple quality options (first available wins)
const format = ytdl.chooseFormat(info.formats, {
quality: ['highestvideo', 'highest', 140]
});
// Choose with filter
const mp4Format = ytdl.chooseFormat(info.formats, {
quality: 'highest',
filter: 'audioandvideo'
});
// Use custom filter function
const customFormat = ytdl.chooseFormat(info.formats, {
filter: format => format.container === 'mp4' && format.hasAudio
});Filters available formats based on specific criteria, useful for examining available options before selection.
/**
* Filters formats based on criteria
* @param {videoFormat | videoFormat[]} formats - Formats to filter
* @param {string | function} filter - Filter criteria or custom function
* @returns {videoFormat[]} - Filtered formats array
*/
function filterFormats(formats, filter);Usage Examples:
const info = await ytdl.getInfo(videoUrl);
// Filter for audio-only formats
const audioFormats = ytdl.filterFormats(info.formats, 'audioonly');
console.log(`Audio formats: ${audioFormats.length}`);
// Filter for video-only formats
const videoFormats = ytdl.filterFormats(info.formats, 'videoonly');
// Filter for formats with both audio and video
const combinedFormats = ytdl.filterFormats(info.formats, 'audioandvideo');
// Custom filter function
const mp4Formats = ytdl.filterFormats(info.formats, format => {
return format.container === 'mp4' && format.bitrate > 1000000;
});
// Filter for HD formats
const hdFormats = ytdl.filterFormats(info.formats, format => {
return format.height && format.height >= 720;
});type ChooseFormatQuality =
| 'lowest' // Lowest quality format
| 'highest' // Highest quality format (default)
| 'highestaudio' // Best audio quality (minimizes video bitrate)
| 'lowestaudio' // Lowest audio quality
| 'highestvideo' // Best video quality (minimizes audio bitrate)
| 'lowestvideo'; // Lowest video quality
type VideoFormatQuality =
| 'tiny' | 'small' | 'medium' | 'large'
| 'hd720' | 'hd1080' | 'hd1440' | 'hd2160' | 'highres';
// Utility type to extend string types while preserving auto-completion
type ExtendString<T extends string> = T | Omit<string, T>;Quality Selection Behavior:
// 'highest' - Prefers formats with both audio and video, highest bitrate
const format = ytdl.chooseFormat(formats, { quality: 'highest' });
// 'highestaudio' - Best audio quality, worst video quality for that audio
const format = ytdl.chooseFormat(formats, { quality: 'highestaudio' });
// 'highestvideo' - Best video quality, worst audio quality for that video
const format = ytdl.chooseFormat(formats, { quality: 'highestvideo' });YouTube formats are identified by itag numbers. You can specify exact formats:
// Common itag examples:
// 18 - 360p MP4 with audio (avc1.42001E, mp4a.40.2)
// 140 - Audio-only M4A (mp4a.40.2) ~128kbps
// 137 - 1080p MP4 video-only (avc1.640028)
// 248 - 1080p WebM video-only (vp9)
const format = ytdl.chooseFormat(formats, { quality: 140 }); // Audio-only M4A
const format = ytdl.chooseFormat(formats, { quality: [137, 248, 'highest'] }); // Prefer 1080p, fallbacktype Filter =
| 'audioandvideo' // Formats with both audio and video
| 'videoandaudio' // Same as audioandvideo
| 'video' // Formats containing video (may also have audio)
| 'videoonly' // Formats with video but no audio
| 'audio' // Formats containing audio (may also have video)
| 'audioonly' // Formats with audio but no video
| function; // Custom filter functionFilter Examples:
// Get only formats with both audio and video
const combined = ytdl.filterFormats(formats, 'audioandvideo');
// Get video-only formats (for merging with separate audio)
const videoOnly = ytdl.filterFormats(formats, 'videoonly');
// Get audio-only formats
const audioOnly = ytdl.filterFormats(formats, 'audioonly');
// Custom filter for specific containers
const webmFormats = ytdl.filterFormats(formats, format => format.container === 'webm');Filter functions receive a format object and should return boolean:
/**
* Custom filter function
* @param {videoFormat} format - Format to evaluate
* @param {number} index - Index in formats array
* @param {videoFormat[]} formats - Full formats array
* @returns {boolean} - Whether to include this format
*/
function customFilter(format, index, formats);Custom Filter Examples:
// Filter for high-quality MP4 formats
const hqMp4 = ytdl.filterFormats(formats, format => {
return format.container === 'mp4' &&
format.height >= 720 &&
format.hasAudio;
});
// Filter for formats with specific codecs
const h264Formats = ytdl.filterFormats(formats, format => {
return format.videoCodec && format.videoCodec.includes('avc1');
});
// Filter for live stream formats
const liveFormats = ytdl.filterFormats(formats, format => format.isLive);
// Filter by file size (approximate)
const smallFormats = ytdl.filterFormats(formats, format => {
const sizeEstimate = parseInt(format.contentLength) || 0;
return sizeEstimate < 50 * 1024 * 1024; // Under 50MB
});Formats are automatically sorted by ytdl-core using these criteria (highest priority first):
For highest quality, download separate audio and video streams and merge with ffmpeg:
const info = await ytdl.getInfo(videoUrl);
// Get best video-only format
const videoFormat = ytdl.chooseFormat(info.formats, {
quality: 'highestvideo',
filter: 'videoonly'
});
// Get best audio-only format
const audioFormat = ytdl.chooseFormat(info.formats, {
quality: 'highestaudio',
filter: 'audioonly'
});
// Download both streams
const videoStream = ytdl.downloadFromInfo(info, { format: videoFormat });
const audioStream = ytdl.downloadFromInfo(info, { format: audioFormat });
// Merge with ffmpeg (external process)Choose formats based on available bandwidth or device capabilities:
function chooseAdaptiveFormat(formats, maxBitrate) {
const suitableFormats = ytdl.filterFormats(formats, format => {
return format.hasAudio && format.hasVideo &&
(format.bitrate || 0) <= maxBitrate;
});
return ytdl.chooseFormat(suitableFormats, { quality: 'highest' });
}
// For mobile devices or slow connections
const mobileFormat = chooseAdaptiveFormat(info.formats, 1000000); // 1Mbps maxChoose formats based on container requirements:
// Prefer MP4 for better compatibility
const mp4Format = ytdl.chooseFormat(info.formats, {
quality: 'highest',
filter: format => format.container === 'mp4'
});
// WebM for web streaming
const webmFormat = ytdl.chooseFormat(info.formats, {
quality: 'highest',
filter: format => format.container === 'webm'
});Live streams require special format handling:
const info = await ytdl.getInfo(liveVideoUrl);
// Filter for HLS formats (required for live streams)
const liveFormats = ytdl.filterFormats(info.formats, format => format.isHLS);
if (liveFormats.length > 0) {
const format = ytdl.chooseFormat(liveFormats, { quality: 'highest' });
const stream = ytdl.downloadFromInfo(info, {
format,
begin: Date.now(), // Start from current time
liveBuffer: 30000 // 30 second buffer
});
}Common format selection errors and solutions:
try {
const format = ytdl.chooseFormat(info.formats, options);
} catch (error) {
if (error.message.includes('No such format found')) {
console.log('Requested format not available');
// Fallback to any available format
const fallbackFormat = ytdl.chooseFormat(info.formats, { quality: 'highest' });
// Or list available formats
console.log('Available formats:');
info.formats.forEach(f => {
console.log(`${f.itag}: ${f.qualityLabel} ${f.container}`);
});
}
}Install with Tessl CLI
npx tessl i tessl/npm-ytdl-core