CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-ytdl-core

YouTube video downloader in pure JavaScript with streaming interface and comprehensive metadata extraction.

Overview
Eval results
Files

format-selection.mddocs/

Format Selection

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.

Capabilities

Choose Format

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
});

Filter Formats

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;
});

Quality Options

Predefined Quality Strings

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' });

Itag-based Selection

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, fallback

Filter Options

Predefined Filter Strings

type 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 function

Filter 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');

Custom Filter Functions

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
});

Format Sorting and Prioritization

Formats are automatically sorted by ytdl-core using these criteria (highest priority first):

  1. Video Encoding Quality: H.264 > VP9 > VP8 > older codecs
  2. Video Bitrate: Higher bitrate preferred
  3. Audio Encoding Quality: FLAC > Opus > AAC > Vorbis > MP3 > older codecs
  4. Audio Bitrate: Higher bitrate preferred
  5. Resolution: Higher resolution preferred

Advanced Selection Patterns

Separate Audio and Video Streams

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)

Adaptive Quality Selection

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 max

Container-specific Selection

Choose 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 Stream Handling

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
  });
}

Error Handling

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

docs

format-selection.md

index.md

url-processing.md

video-downloading.md

video-information.md

tile.json