YouTube video downloader in pure JavaScript with streaming interface and comprehensive metadata extraction.
Comprehensive metadata extraction including video details, available formats, thumbnails, captions, and related videos. Provides both basic and full information retrieval with caching support.
Retrieves complete video metadata including all formats with deciphered download URLs.
/**
* Gets complete video metadata including deciphered formats
* @param {string} url - YouTube video URL or video ID
* @param {getInfoOptions} options - Request options
* @returns {Promise<videoInfo>} - Complete video information
*/
async function getInfo(url, options);
interface getInfoOptions {
lang?: string; // Language code for metadata (default: 'en')
requestCallback?: function; // Callback for request streams
requestOptions?: object; // Options passed to miniget requests
}Usage Examples:
// Basic info retrieval
const info = await ytdl.getInfo('https://www.youtube.com/watch?v=aqz-KE-bpKQ');
console.log(info.videoDetails.title);
console.log(`Available formats: ${info.formats.length}`);
// With language preference
const info = await ytdl.getInfo(videoUrl, { lang: 'es' });
// With request options
const info = await ytdl.getInfo(videoUrl, {
requestOptions: {
headers: {
'User-Agent': 'Custom User Agent'
}
}
});
// With request callback for monitoring
const info = await ytdl.getInfo(videoUrl, {
requestCallback: (req) => {
console.log(`Making request to: ${req.url}`);
}
});Retrieves basic video metadata without deciphering format URLs (faster but limited).
/**
* Gets basic video metadata without deciphered URLs (faster)
* @param {string} url - YouTube video URL or video ID
* @param {getInfoOptions} options - Request options
* @returns {Promise<videoInfo>} - Basic video information (full: false)
*/
async function getBasicInfo(url, options);Usage Examples:
// Quick metadata retrieval
const basicInfo = await ytdl.getBasicInfo(videoUrl);
console.log(basicInfo.videoDetails.title);
console.log(basicInfo.videoDetails.lengthSeconds);
// Note: Cannot use with downloadFromInfo()
// This will throw an error:
// ytdl.downloadFromInfo(basicInfo, options); // ERROR!interface videoInfo {
videoDetails: VideoDetails; // Core video metadata
formats: videoFormat[]; // Available download formats
player_response: object; // Raw YouTube player response
related_videos: relatedVideo[]; // Related video suggestions
full: boolean; // true for getInfo(), false for getBasicInfo()
// Additional metadata from YouTube's response
iv_load_policy?: string;
iv_allow_in_place_switch?: string;
fade_in_start_milliseconds?: string;
fade_in_duration_milliseconds?: string;
fade_out_start_milliseconds?: string;
fade_out_duration_milliseconds?: string;
// ... many more YouTube-specific fields
}Core video metadata and information.
interface VideoDetails {
videoId: string; // 11-character YouTube video ID
title: string; // Video title
shortDescription: string; // Video description
lengthSeconds: string; // Duration in seconds (as string)
keywords?: string[]; // Video tags/keywords
channelId: string; // Channel ID
isOwnerViewing: boolean; // Whether owner is viewing
isCrawlable: boolean; // Whether video is crawlable
thumbnails: thumbnail[]; // Video thumbnails
averageRating: number; // Average rating (deprecated by YouTube)
allowRatings: boolean; // Whether ratings are enabled
viewCount: string; // View count (as string)
author: string; // Channel name
isPrivate: boolean; // Whether video is private
isUnpluggedCorpus: boolean; // YouTube Premium content flag
isLiveContent: boolean; // Whether content is/was live
}Extended video information available in the info object.
interface MoreVideoDetails {
// Extends VideoDetails with additional fields
published: number; // Publication timestamp
video_url: string; // Full YouTube URL
age_restricted: boolean; // Age restriction status
likes: number | null; // Like count (may be null if hidden)
dislikes: number | null; // Dislike count (deprecated by YouTube)
media: Media; // Media/music information
author: Author; // Extended author information
thumbnails: thumbnail[]; // Video thumbnails
storyboards: storyboard[]; // Video preview thumbnails
chapters: Chapter[]; // Video chapters
description: string | null; // Full description
}
interface storyboard {
templateUrl: string;
thumbnailWidth: number;
thumbnailHeight: number;
thumbnailCount: number;
interval: number;
columns: number;
rows: number;
storyboardCount: number;
}
interface Chapter {
title: string;
start_time: number;
}Detailed format specifications for each available download option.
interface videoFormat {
itag: number; // Format identifier
url: string; // Download URL (deciphered in getInfo())
mimeType?: string; // MIME type (e.g., 'video/mp4; codecs="avc1.640028"')
bitrate?: number; // Video bitrate
audioBitrate?: number; // Audio bitrate
width?: number; // Video width in pixels
height?: number; // Video height in pixels
initRange?: { start: string; end: string }; // Initialization range
indexRange?: { start: string; end: string }; // Index range
lastModified: string; // Last modified timestamp
contentLength: string; // Content length in bytes
quality: string; // Quality descriptor
qualityLabel: string; // Human-readable quality (e.g., '720p')
projectionType?: string; // Video projection type
fps?: number; // Frames per second
averageBitrate?: number; // Average bitrate
audioQuality?: string; // Audio quality descriptor
colorInfo?: object; // Color space information
approxDurationMs?: string; // Approximate duration
targetDurationSec?: number; // Target duration for segments
maxDvrDurationSec?: number; // Max DVR duration for live
audioSampleRate?: string; // Audio sample rate
audioChannels?: number; // Number of audio channels
// Added by ytdl-core
container: string; // Container format ('mp4', 'webm', etc.)
hasVideo: boolean; // Whether format includes video
hasAudio: boolean; // Whether format includes audio
codecs: string; // Codec information
videoCodec?: string; // Video codec
audioCodec?: string; // Audio codec
isLive: boolean; // Whether format is for live content
isHLS: boolean; // Whether format uses HLS
isDashMPD: boolean; // Whether format uses DASH
}Video thumbnail information in various sizes.
interface thumbnail {
url: string; // Thumbnail image URL
width: number; // Width in pixels
height: number; // Height in pixels
}Information about related/suggested videos.
interface relatedVideo {
id?: string; // Video ID
title?: string; // Video title
published?: string; // Publication date
author: Author | string; // Channel information
short_view_count_text?: string; // Abbreviated view count
view_count?: string; // Full view count
length_seconds?: number; // Duration in seconds
thumbnails: thumbnail[]; // Video thumbnails
richThumbnails: thumbnail[]; // Rich thumbnails
isLive: boolean; // Whether video is live
}Extended channel/author metadata.
interface Author {
id: string; // Channel ID
name: string; // Channel name
avatar: string; // Avatar URL (deprecated)
thumbnails?: thumbnail[]; // Channel thumbnails
verified: boolean; // Verification status
user?: string; // Username
channel_url: string; // Channel URL
external_channel_url?: string; // External channel URL
user_url?: string; // User URL
subscriber_count?: number; // Subscriber count
}Available captions and subtitle tracks.
interface captionTrack {
baseUrl: string; // Caption file URL
name: { simpleText: string }; // Language name
vssId: string; // Voice search service ID
languageCode: string; // Language code (e.g., 'en', 'es')
kind: string; // Caption kind
rtl?: boolean; // Right-to-left text
isTranslatable: boolean; // Whether translatable
}const info = await ytdl.getInfo(videoUrl);
// Basic video information
console.log(`Title: ${info.videoDetails.title}`);
console.log(`Duration: ${info.videoDetails.lengthSeconds} seconds`);
console.log(`Views: ${info.videoDetails.viewCount}`);
console.log(`Author: ${info.videoDetails.author}`);
// Extended information
console.log(`Published: ${new Date(info.videoDetails.published * 1000)}`);
console.log(`Likes: ${info.videoDetails.likes}`);
console.log(`Age Restricted: ${info.videoDetails.age_restricted}`);const info = await ytdl.getInfo(videoUrl);
// List all available formats
info.formats.forEach(format => {
console.log(`${format.itag}: ${format.qualityLabel} ${format.container} ` +
`(${format.hasVideo ? 'V' : ''}${format.hasAudio ? 'A' : ''})`);
});
// Find best quality format with both audio and video
const bestFormat = info.formats
.filter(f => f.hasVideo && f.hasAudio)
.sort((a, b) => b.bitrate - a.bitrate)[0];const info = await ytdl.getInfo(videoUrl);
if (info.player_response.captions) {
const captionTracks = info.player_response.captions.playerCaptionsTracklistRenderer.captionTracks;
// List available caption languages
captionTracks.forEach(track => {
console.log(`${track.name.simpleText}: ${track.languageCode}`);
});
// Get English captions URL
const englishTrack = captionTracks.find(track => track.languageCode === 'en');
if (englishTrack) {
console.log(`English captions: ${englishTrack.baseUrl}`);
}
}// Use getBasicInfo() when you only need metadata
const basicInfo = await ytdl.getBasicInfo(videoUrl);
if (basicInfo.videoDetails.isPrivate) {
console.log('Video is private, cannot download');
return;
}
// Only call getInfo() when you need formats or plan to download
const fullInfo = await ytdl.getInfo(videoUrl);
const stream = ytdl.downloadFromInfo(fullInfo, options);Install with Tessl CLI
npx tessl i tessl/npm-ytdl-core