ytdl-core-enhanced
v2.1.0
Published
Stable YouTube downloader using Android InnerTube client with automatic signature decoding. Features reliable format extraction, restricted video support with cookies, multi-threading, and production-ready 2025 compatibility.
Downloads
399
Maintainers
Readme
ytdl-core-enhanced
Stable YouTube downloader using Android InnerTube client with automatic signature decoding.
Features
- ✅ Android InnerTube Client - Reliable format extraction using YouTube's official Android API
- ✅ Automatic Signature Decoding - Handles encrypted format URLs transparently
- ✅ Restricted Videos - Download age-restricted and region-locked videos with cookies
- ✅ Multi-threading - Fast parallel downloads with multiple connections
- ✅ 100% API Compatible - Drop-in replacement for ytdl-core
- ✅ Production Ready - Stable and tested with YouTube 2025
Installation
npm install ytdl-core-enhancedQuick Start
Basic Download
const ytdl = require('ytdl-core-enhanced');
const fs = require('fs');
ytdl('https://www.youtube.com/watch?v=dQw4w9WgXcQ')
.pipe(fs.createWriteStream('video.mp4'));Download with Quality Selection
const ytdl = require('ytdl-core-enhanced');
const fs = require('fs');
// Highest quality
ytdl('VIDEO_URL', { quality: 'highest' })
.pipe(fs.createWriteStream('video.mp4'));
// Specific format (1080p video)
ytdl('VIDEO_URL', { quality: 137 })
.pipe(fs.createWriteStream('video-1080p.mp4'));
// Audio only
ytdl('VIDEO_URL', { filter: 'audioonly' })
.pipe(fs.createWriteStream('audio.m4a'));Get Video Info
const ytdl = require('ytdl-core-enhanced');
ytdl.getInfo('VIDEO_URL').then(info => {
console.log('Title:', info.videoDetails.title);
console.log('Author:', info.videoDetails.author.name);
console.log('Duration:', info.videoDetails.lengthSeconds, 'seconds');
console.log('Formats:', info.formats.length);
});Downloading Restricted Videos
For age-restricted, region-locked, or member-only videos, you need to provide YouTube cookies from a logged-in account.
Method 1: Using Cookie Editor Extension (Recommended)
Install Cookie-Editor extension for your browser:
Go to YouTube and log in to your account
Click the Cookie-Editor extension icon
Click "Export" → "Header String" (this copies cookie string to clipboard)
Use the cookie string in your code:
const ytdl = require('ytdl-core-enhanced');
const fs = require('fs');
const cookieString = 'PASTE_YOUR_COOKIE_STRING_HERE';
ytdl('VIDEO_URL', {
requestOptions: {
headers: {
'Cookie': cookieString
}
}
}).pipe(fs.createWriteStream('video.mp4'));Method 2: Save Cookie to File
const ytdl = require('ytdl-core-enhanced');
const fs = require('fs');
// Save your cookie string to a file
fs.writeFileSync('.youtube-cookies.txt', 'YOUR_COOKIE_STRING');
// Read and use it
const cookieString = fs.readFileSync('.youtube-cookies.txt', 'utf8');
ytdl('VIDEO_URL', {
requestOptions: {
headers: {
'Cookie': cookieString
}
}
}).pipe(fs.createWriteStream('video.mp4'));Important Cookie Notes
⚠️ Security Warning: Cookie strings contain your authentication tokens. Treat them like passwords:
- Never commit cookies to git repositories
- Add
.youtube-cookies.txtto your.gitignore - Regenerate cookies if accidentally exposed (logout and login again)
💡 Cookie Lifespan: YouTube cookies typically last 1-2 weeks. If downloads start failing with 403 errors, refresh your cookies.
API Reference
ytdl(url, [options])
Downloads a video from YouTube.
Parameters:
url(string): Video URL or video IDoptions(object): Download options
Options:
quality: Quality preference (default: 'highest')'highest'- Best quality'lowest'- Lowest quality'highestaudio'- Best audio quality'lowestaudio'- Lowest audio quality- Number (itag) - Specific format (e.g., 137 for 1080p)
filter: Format filter function or preset'audioonly'- Audio only'videoonly'- Video only (no audio)'audioandvideo'- Combined video+audio- Function - Custom filter
(format) => boolean
requestOptions: HTTP request optionsheaders: Custom headers (e.g., cookies)
Returns: ReadableStream
Example:
ytdl('dQw4w9WgXcQ', {
quality: 'highest',
filter: 'audioandvideo',
requestOptions: {
headers: {
'Cookie': cookieString
}
}
}).pipe(fs.createWriteStream('video.mp4'));ytdl.getInfo(url, [options])
Gets video information without downloading.
Parameters:
url(string): Video URL or video IDoptions(object): Options (same as ytdl)
Returns: Promise
Response Object:
{
videoDetails: {
title: string,
author: { name: string },
lengthSeconds: number,
viewCount: number,
...
},
formats: [
{
itag: number,
url: string,
qualityLabel: string,
container: string,
hasVideo: boolean,
hasAudio: boolean,
...
}
]
}Common Format ITAGs
Video + Audio (Progressive)
- 18: 360p MP4
- 22: 720p MP4 (not always available)
Video Only (Adaptive)
- 137: 1080p MP4
- 136: 720p MP4
- 135: 480p MP4
- 134: 360p MP4
Audio Only (Adaptive)
- 139: 48kbps M4A (lowest)
- 140: 128kbps M4A (medium)
- 251: 160kbps WEBM (highest)
Events
The download stream emits standard Node.js stream events:
const video = ytdl('VIDEO_URL');
video.on('info', (info, format) => {
console.log('Downloading:', info.videoDetails.title);
console.log('Format:', format.qualityLabel);
});
video.on('data', (chunk) => {
console.log('Received', chunk.length, 'bytes');
});
video.on('end', () => {
console.log('Download complete!');
});
video.on('error', (error) => {
console.error('Error:', error.message);
});
video.pipe(fs.createWriteStream('video.mp4'));Troubleshooting
403 Forbidden Error
Problem: Download fails with HTTP 403 error
Solutions:
- Add cookies from a logged-in YouTube account (see "Downloading Restricted Videos")
- Check if video is region-locked or requires login
- Refresh your cookies if they expired
Format Not Found
Problem: Requested format/quality not available
Solution: Check available formats first:
ytdl.getInfo('VIDEO_URL').then(info => {
console.log('Available formats:');
info.formats.forEach(format => {
console.log(`${format.itag}: ${format.qualityLabel} (${format.container})`);
});
});Video Unavailable
Problem: "Video is unavailable" error
Possible Reasons:
- Video is private or deleted
- Video is region-locked (try with cookies from matching region)
- Video is live stream that ended
- Video is members-only (requires membership cookies)
Advanced Usage
Custom Filter Function
ytdl('VIDEO_URL', {
filter: format => {
return format.container === 'mp4' &&
format.hasAudio &&
format.qualityLabel === '720p';
}
}).pipe(fs.createWriteStream('video.mp4'));Download Progress Tracking
const ytdl = require('ytdl-core-enhanced');
const fs = require('fs');
ytdl.getInfo('VIDEO_URL').then(info => {
const format = ytdl.chooseFormat(info.formats, { quality: 'highest' });
const video = ytdl.downloadFromInfo(info, { format });
let downloaded = 0;
const total = parseInt(format.contentLength);
video.on('data', chunk => {
downloaded += chunk.length;
const percent = ((downloaded / total) * 100).toFixed(2);
console.log(`Progress: ${percent}%`);
});
video.pipe(fs.createWriteStream('video.mp4'));
});Differences from ytdl-core
This package is designed as a drop-in replacement for ytdl-core with these improvements:
✅ API Compatible - Same API as ytdl-core, just change the require statement ✅ More Reliable - Uses Android InnerTube client which works consistently ✅ Simpler - Removed complex multi-client fallback logic ✅ Cleaner - No browser automation or anti-bot detection needed
Migration from ytdl-core
// Before
const ytdl = require('ytdl-core');
// After
const ytdl = require('ytdl-core-enhanced');
// All your existing code works unchanged!Contributing
Issues and pull requests are welcome at GitHub.
License
MIT
