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.
npm install ytdl-core-enhancedStable YouTube downloader using Android InnerTube client with automatic signature decoding.
- ✅ 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
``bash`
npm install ytdl-core-enhanced
`javascript
const ytdl = require('ytdl-core-enhanced');
const fs = require('fs');
ytdl('https://www.youtube.com/watch?v=dQw4w9WgXcQ')
.pipe(fs.createWriteStream('video.mp4'));
`
`javascript
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'));
`
`javascript
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);
});
`
For age-restricted, region-locked, or member-only videos, you need to provide YouTube cookies from a logged-in account.
1. Install Cookie-Editor extension for your browser:
- Chrome/Edge
- Firefox
2. Go to YouTube and log in to your account
3. Click the Cookie-Editor extension icon
4. Click "Export" → "Header String" (this copies cookie string to clipboard)
5. Use the cookie string in your code:
`javascript
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'));
`
`javascript
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'));
`
⚠️ Security Warning: Cookie strings contain your authentication tokens. Treat them like passwords:
- Never commit cookies to git repositories
- Add .youtube-cookies.txt to 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.
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(format) => boolean
- Function - Custom filter
- requestOptions: HTTP request optionsheaders
- : Custom headers (e.g., cookies)
Returns: ReadableStream
Example:
`javascript`
ytdl('dQw4w9WgXcQ', {
quality: 'highest',
filter: 'audioandvideo',
requestOptions: {
headers: {
'Cookie': cookieString
}
}
}).pipe(fs.createWriteStream('video.mp4'));
Gets video information without downloading.
Parameters:
- url (string): Video URL or video IDoptions
- (object): Options (same as ytdl)
Returns: Promise
Response Object:
`javascript`
{
videoDetails: {
title: string,
author: { name: string },
lengthSeconds: number,
viewCount: number,
...
},
formats: [
{
itag: number,
url: string,
qualityLabel: string,
container: string,
hasVideo: boolean,
hasAudio: boolean,
...
}
]
}
The download stream emits standard Node.js stream events:
`javascript
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'));
`
Problem: Download fails with HTTP 403 error
Solutions:
1. Add cookies from a logged-in YouTube account (see "Downloading Restricted Videos")
2. Check if video is region-locked or requires login
3. Refresh your cookies if they expired
Problem: Requested format/quality not available
Solution: Check available formats first:
`javascript${format.itag}: ${format.qualityLabel} (${format.container})
ytdl.getInfo('VIDEO_URL').then(info => {
console.log('Available formats:');
info.formats.forEach(format => {
console.log();`
});
});
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)
`javascript`
ytdl('VIDEO_URL', {
filter: format => {
return format.container === 'mp4' &&
format.hasAudio &&
format.qualityLabel === '720p';
}
}).pipe(fs.createWriteStream('video.mp4'));
`javascript
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'));
});
`
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
`javascript
// Before
const ytdl = require('ytdl-core');
// After
const ytdl = require('ytdl-core-enhanced');
// All your existing code works unchanged!
``
Issues and pull requests are welcome at GitHub.
MIT
- Original ytdl-core by fent
- Android InnerTube client implementation by Satoru FX
- Maintained by Satoru FX