Package Exports
- @irithell-js/yt-play
Readme
@irithell-js/yt-play
High-performance YouTube audio/video download engine with intelligent caching, built-in yt-dlp and aria2c binaries for blazing fast downloads.
Features
- Bundled Binaries - yt-dlp and aria2c included (no system dependencies)
- Auto-Update System - yt-dlp updates automatically when needed
- Ultra Fast Downloads - aria2c acceleration (up to 5x faster)
- Playlist Support - Search, extract, and resolve direct media URLs from entire playlists in batches
- Shorts Support - Download and convertion for more compatibility
- Intelligent Caching - TTL-based cache with automatic cleanup
- Smart Quality - Auto-reduces quality for long videos (>1h)
- Container Ready - Works in Docker/isolated environments with cookie support
- Auto-Configuration - Creates yt-dlp.conf with optimal settings
- Cross-Platform - Linux (x64/arm64), macOS, Windows
- Zero Config - Auto-detects binaries and optimizes settings
- TypeScript - Full type definitions included
- Dual Format - ESM and CommonJS support
Installation
npm install @irithell-js/yt-playBinaries (yt-dlp + aria2c) are automatically downloaded during installation (this may take a few seconds during the first use).
Quick Start
Basic Usage (ESM)
import { PlayEngine } from "@irithell-js/yt-play";
const engine = new PlayEngine();
// Search and download
const metadata = await engine.search("linkin park numb");
if (!metadata) throw new Error("Not found");
const requestId = engine.generateRequestId();
await engine.preload(metadata, requestId);
// Get audio file
const { file: audioFile } = await engine.getOrDownload(requestId, "audio");
console.log("Audio:", audioFile.path);
// Get video file
const { file: videoFile } = await engine.getOrDownload(requestId, "video");
console.log("Video:", videoFile.path);
// Cleanup cache
engine.cleanup(requestId);Basic Usage (CommonJS)
const { PlayEngine } = require("@irithell-js/yt-play");
const engine = new PlayEngine();
async function download() {
const metadata = await engine.search("song name");
const requestId = engine.generateRequestId();
await engine.preload(metadata, requestId);
const { file } = await engine.getOrDownload(requestId, "audio");
console.log("Downloaded:", file.path);
engine.cleanup(requestId);
}
download();Direct URL Support
// Search by name
const metadata1 = await engine.search("never gonna give you up");
// Or use direct YouTube URL
const metadata2 = await engine.search(
"https://www.youtube.com/watch?v=dQw4w9WgXcQ",
);
// Both return the same metadata format
console.log(metadata2.title); // "Rick Astley - Never Gonna Give You Up"Playlist Support
You can search and extract direct media URLs from playlists with automatic batch processing:
import { YtDlpClient, searchPlaylistBest } from "@irithell-js/yt-play";
// 1. Find a playlist (by search term or direct URL)
const metadata = await searchPlaylistBest("Lofi hip hop");
if (metadata) {
const ytdlp = new YtDlpClient();
// 2. Extract and resolve direct streaming URLs
const playlistInfo = await ytdlp.getPlaylistInfo(metadata.url, {
limit: 5, // Get exactly 5 valid tracks
resolveLinks: true, // Automatically resolve direct media URLs
type: "audio", // "audio", "video", or "both"
batchSize: 5, // Process 5 tracks concurrently for maximum speed
});
playlistInfo.entries.forEach((track: any) => {
console.log(`Title: ${track.title}`);
console.log(`Direct URL: ${track.directUrl}\n`);
});
}Example Output:
{
"id": "PLjNlQ2vXx1xbt30X8TcUfNzw_akVISXEu",
"title": "BEST Anime Openings and Endings Songs *Playlist*",
"uploader": "by - AnimeEnfermos -",
"entries": [
{
"id": "jIfogFtgV-o",
"title": "Noragami / Opening 2",
"url": "https://www.youtube.com/watch?v=jIfogFtgV-o",
"duration": 103,
"uploader": "reeaaas",
"directUrl": "https://rr2---sn-103oxu-bg0l.go..."
},
{
"id": "0YF8vecQWYs",
"title": "Crying for Rain - 美波 (Minami) MV",
"url": "https://www.youtube.com/watch?v=0YF8vecQWYs",
"duration": 254,
"uploader": "Minami",
"directUrl": "https://rr1---sn-103oxu-bg0l.go..."
},
{
"id": "pmanD_s7G3U",
"title": "Demon Slayer | OP | Gurenge by LiSA HD",
"url": "https://www.youtube.com/watch?v=pmanD_s7G3U",
"duration": 90,
"uploader": "animelab",
"directUrl": "https://rr1---sn-103oxu-bg0l.go"
},
{
"id": "atxYe-nOa9w",
"title": "One Punch Man - Official Opening - The Hero!! Set Fire to the Furious Fist",
"url": "https://www.youtube.com/watch?v=atxYe-nOa9w",
"duration": 90,
"uploader": "animelab",
"directUrl": "https://rr1---sn-103oxu-bg0l.go..."
},
{
"id": "792vg0amsuQ",
"title": "Steins;Gate 0 - op pt-br legendado",
"url": "https://www.youtube.com/watch?v=792vg0amsuQ",
"duration": 243,
"uploader": "Animes Nii-san",
"directUrl": "https://rr1---sn-103oxu-bg0l.go..."
}
]
}Configuration
Constructor Options
const engine = new PlayEngine({
// Cache settings
cacheDir: "./cache", // Cache directory (default: OS temp)
ttlMs: 5 * 60_000, // Cache TTL in ms (default: 3min)
cleanupIntervalMs: 30_000, // Cleanup interval (default: 30s)
preloadBuffer: true, // Load files into RAM (default: true)
// Quality settings
preferredAudioKbps: 128, // Audio quality: 320|256|192|128|96|64
preferredVideoP: 720, // Video quality: 1080|720|480|360
maxPreloadDurationSeconds: 1200, // Max duration for preload (default: 20min)
// Performance settings (auto-optimized)
useAria2c: true, // Use aria2c for downloads (default: auto)
concurrentFragments: 8, // Parallel fragments (default: 5)
ytdlpTimeoutMs: 300_000, // yt-dlp timeout (default: 5min)
// Binary paths (optional - auto-detected)
ytdlpBinaryPath: "./bin/yt-dlp",
aria2cPath: "./bin/aria2c",
ffmpegPath: "/usr/bin/ffmpeg", // Optional
// Cookies (required for VPS/Docker environments)
cookiesPath: "./cookies.txt", // Path to Netscape cookies file
// OR
cookiesFromBrowser: "firefox", // Extract from browser: chrome, firefox, edge, safari
// Logging
logger: console, // Logger instance (optional)
});Cookie Configuration (VPS/Docker)
For headless environments (VPS, Docker), YouTube authentication is required:
Option 1: Export cookies from browser
- Install browser extension: "Get cookies.txt LOCALLY"
- Visit youtube.com and login
- Export cookies to
cookies.txt - Use in PlayEngine:
const engine = new PlayEngine({
cookiesPath: "/path/to/cookies.txt",
});Option 2: Extract directly from browser (requires browser installed)
const engine = new PlayEngine({
cookiesFromBrowser: "firefox", // chrome, firefox, edge, safari
});The engine automatically creates yt-dlp.conf with:
- Node.js runtime detection
- EJS remote components
- Cookie authentication
- Optimal download settings
Quality Presets
// High quality (larger files)
const hq = new PlayEngine({
preferredAudioKbps: 320,
preferredVideoP: 1080,
});
// Balanced (recommended)
const balanced = new PlayEngine({
preferredAudioKbps: 128,
preferredVideoP: 720,
});
// Low quality (faster, smaller)
const lq = new PlayEngine({
preferredAudioKbps: 96,
preferredVideoP: 480,
});API Reference
Exported Functions
searchBest(query: string): Promise<PlayMetadata | null>
Search for a video on YouTube or get metadata from a URL.
searchPlaylistBest(query: string): Promise<PlayMetadata | null>
Search for a playlist on YouTube or get metadata from a direct playlist URL. Returns the list ID and base URL.
PlayEngine Methods
search(query: string): Promise<PlayMetadata | null>
Search for a video on YouTube or get metadata from URL.
// Search by name
const metadata = await engine.search("artist - song name");
// Or use direct URL
const metadata = await engine.search(
"https://www.youtube.com/watch?v=VIDEO_ID",
);
// Returns: { title, author, duration, durationSeconds, thumb, videoId, url }generateRequestId(prefix?: string): string
Generate unique request ID for caching.
const requestId = engine.generateRequestId("audio"); // "audio_1234567890_abc123"preload(metadata: PlayMetadata, requestId: string): Promise<void>
Pre-download audio and video in parallel (cached for TTL duration).
await engine.preload(metadata, requestId);
// Downloads audio + video if <1h, only audio if >1h (96kbps)getOrDownload(requestId: string, type: 'audio' | 'video'): Promise<Result>
Get file from cache or download directly.
const result = await engine.getOrDownload(requestId, "audio");
// Returns: { metadata, file: { path, size, info, buffer? }, direct: boolean }
console.log(result.file.path); // "/tmp/cache/audio_xxx.m4a"
console.log(result.file.size); // 8457234 (bytes)
console.log(result.file.info.quality); // "128kbps m4a"
console.log(result.direct); // false if from cache, true if direct downloadwaitCache(requestId: string, type: 'audio' | 'video', timeoutMs?: number, intervalMs?: number): Promise<CachedFile | null>
Wait for cache to be ready (useful for checking preload status).
const cached = await engine.waitCache(requestId, "audio", 8000, 500);
if (cached) {
console.log("Cache ready:", cached.path);
} else {
console.log("Timeout - falling back to direct download");
}cleanup(requestId: string): void
Remove cached files for a request.
engine.cleanup(requestId); // Deletes audio + video from cachegetFromCache(requestId: string): CacheEntry | undefined
Get cache entry metadata (without downloading).
const entry = engine.getFromCache(requestId);
if (entry) {
console.log(entry.metadata.title);
console.log(entry.audio?.path);
console.log(entry.loading); // true if preload in progress
}YtDlpClient Methods
getPlaylistInfo(url: string, opts?: YtDlpPlaylistOptions): Promise<YtDlpPlaylistInfo>
Fetches all entries from a playlist efficiently. If resolveLinks: true is provided in options, it will batch-resolve the direct playable streaming URLs for each valid track up to the provided limit.
Auto-Update System
The engine includes an intelligent auto-update system for yt-dlp:
How it works
- Constant Background Checks - Checks GitHub for new yt-dlp releases
- Instant Updates on Failure - If a download fails, immediately checks and updates yt-dlp
- Automatic Retry - After updating, automatically retries the failed download
- Version Detection - Compares installed binary version with latest GitHub release
- Zero Downtime - Updates happen in background without blocking your application
Update Behavior
const engine = new PlayEngine();
// ✓ Checks for updates in background
// If download fails
await engine.getOrDownload(requestId, "audio");
// <!> Download failed. Forcing yt-dlp update check...
// >> Updating yt-dlp to 2026.02.04...
// ✓ Updated to 2026.02.04
// >> Retrying download after update...
// ✓ SuccessManual Update Check
To force an update check, simply trigger a download. The system will automatically update if needed.
Advanced Usage
Handle Long Videos (>1h)
const metadata = await engine.search("2h music mix");
// Automatically uses:
// - Audio: 96kbps (reduced quality)
// - Video: skipped (audio only)
await engine.preload(metadata, requestId);
const { file } = await engine.getOrDownload(requestId, "audio");
// Fast download with reduced qualityCustom Cache Directory
import path from "path";
const engine = new PlayEngine({
cacheDir: path.join(process.cwd(), "downloads"),
ttlMs: 10 * 60_000, // 10 minutes
});Performance Monitoring
const startTime = Date.now();
await engine.preload(metadata, requestId);
const preloadTime = Date.now() - startTime;
console.log(`Preload took ${(preloadTime / 1000).toFixed(2)}s`);Error Handling
try {
const metadata = await engine.search("non-existent-video");
if (!metadata) {
console.error("Video not found");
return;
}
await engine.preload(metadata, requestId);
const { file } = await engine.getOrDownload(requestId, "audio");
console.log("Success:", file.path);
} catch (error) {
console.error("Download failed:", error.message);
// Engine automatically tries to update yt-dlp and retry
}Performance
With aria2c enabled (default):
| Video Length | Audio Download | Video Download | Total Time |
|---|---|---|---|
| 5 min | ~3-5s | ~6-8s | ~8s |
| 1 hour | ~15-20s | Audio only | ~20s |
| 2 hours | ~25-30s | Audio only | ~30s |
Times may vary based on network speed and YouTube throttling
The values are based on local tests with optimized caching, for downloading long videos use direct download
File Formats
- Audio: M4A (native format, no conversion needed)
- Video: MP4 (with audio merged)
M4A provides better quality-to-size ratio and downloads 2-5x faster (no re-encoding).
Requirements
- Node.js >= 18.0.0
- ~50MB disk space for binaries (auto-downloaded)
- Optional: ffmpeg for advanced features
Binaries
The package automatically downloads and manages:
- yt-dlp (auto-updates to latest version) (~35 MB)
- aria2c v1.37.0 (12 MB)
Binaries are platform-specific and downloaded on first npm install.
Auto-Update Schedule
- Background Check: Constant version check
- On-Demand: Immediately when download fails
- Version Source: GitHub Releases API
Supported Platforms
- Linux x64 / arm64
- macOS x64 / arm64 (Apple Silicon)
- Windows x64
Manual Binary Paths
const engine = new PlayEngine({
ytdlpBinaryPath: "/custom/path/yt-dlp",
aria2cPath: "/custom/path/aria2c",
});Troubleshooting
Slow Downloads
// Enable aria2c explicitly
const engine = new PlayEngine({
useAria2c: true,
concurrentFragments: 10, // Increase parallelism
});Cache Issues
// Clear cache directory manually
import fs from "fs";
fs.rmSync("./cache", { recursive: true, force: true });Binary Not Found
Binaries are auto-downloaded to node_modules/@irithell-js/yt-play/bin/. If missing:
npm rebuild @irithell-js/yt-playCookie Authentication Issues
If downloads fail with "Sign in to confirm you're not a bot":
- Export fresh cookies from your browser
- Ensure cookies.txt is in Netscape format
- Verify cookie file path is correct
- Try
cookiesFromBrowseroption if browser is installed
Update Check Failures
If auto-update fails:
# Manually update binaries
cd node_modules/@irithell-js/yt-play
node scripts/setup-binaries.mjsLicense
MIT
Contributing
Issues and PRs welcome!
Changelog
Deprecated versions have been removed to prevent errors during use.
0.3.2 (Latest)
- Added full support for YouTube Playlists (search by name or URL via
searchPlaylistBest) - Added fast batch processing to resolve direct media URLs from playlists (
resolveLinks) with auto-replenishment - Fixed race conditions in the
PlayEngineauto-update system during concurrent downloads - Fixed binary setup scripts to use atomic downloads (temp files), preventing missing or corrupted binaries on connection drops
- Added automatic cleanup of leftover
.partand.ytdltemporary files on download failure or timeout
0.2.8
- Fixed Codec for shorts to work in all plataforms
0.2.7
- Fixed parse for shorts
0.2.6
- Added auto-update system for yt-dlp
- Direct YouTube URL support in search()
- Auto-configuration system (creates yt-dlp.conf)
- Cookie authentication support (cookiesPath/cookiesFromBrowser)
- Fixed URL search returning wrong video
- Improved error handling with automatic retry after update
0.2.5
- Added support to direct cookies extraction in pre-built browsers
0.2.4
- Added support to cookies.txt
0.2.3
- Updated documentation
- Improved error messages
0.2.2
- Many syntax errors fixed
0.2.1
- Added auto-detection for yt-dlp and aria2c binaries
- Fixed CommonJS compatibility
- Improved error handling for long videos
0.2.0
- Initial release with bundled binaries
- aria2c acceleration support
- Intelligent caching system