ytmusic-advanced
v2.1.0
Published
A powerful, type-safe YouTube Music API client with audio extraction, signature deciphering, and intelligent search parsing
Downloads
82
Maintainers
Readme
ytmusic-advanced
A powerful, type-safe YouTube Music API client with audio extraction, signature deciphering, and intelligent search parsing.
Current version: 2.1.0 — a complete TypeScript rewrite. See MIGRATION.md if you're upgrading from v1.x.
✨ Features
- 🎵 Search YouTube Music — songs, albums, artists, playlists, videos
- 🗂️ Core Entities — full metadata extraction for Albums, Artists, and Playlists
- 🌍 Discovery & Explore — fetch Trending Charts, Moods & Genres, and New Releases
- 📝 Track Details — fetch synchronized lyrics and related/Up Next tracks
- 👤 User Activity — retrieve Liked Songs and Listening History (requires cookies)
- 🎧 Direct audio extraction — get playable audio URLs without a browser
- 🔐 Signature deciphering — automatic YouTube signature decryption
- 🔄 Multi-client fallback —
android_test→tv→webstrategy - 📊 Type-safe — full TypeScript types and Zod runtime validation
- 🍪 Session management — automatic cookie handling for consistency
- ⚡ Smart retries — exponential backoff with jitter
- 🧱 Modular internals — clean, testable parser/request architecture
📦 Installation
npm install ytmusic-advancedRequirements: Node.js 16 or later.
🚀 Quick Start
CommonJS
const YTMusic = require("ytmusic-advanced");
const client = new YTMusic();
// Search for music
const results = await client.search("Bohemian Rhapsody");
console.log(results);
// Get a playable audio URL
const audioUrl = await client.getAudioURL("fJ9rUzIMcZQ");
console.log(audioUrl);ESM / TypeScript
import YTMusic, {
SearchResult,
SongDetailed,
AudioFormat,
VERSION,
} from "ytmusic-advanced";
const client = new YTMusic();
const results: SearchResult[] = await client.search("Queen");
for (const item of results) {
if (item.type === "SONG") {
// TypeScript narrows the union automatically
const song: SongDetailed = item;
console.log(`${song.name} — ${song.artist.name}`);
}
}📖 API Documentation
new YTMusic(options?)
| Option | Type | Default | Description |
| -------------- | --------- | ------- | ------------------------------------------ |
| mode | 'youtube' \| 'music' | 'youtube' | Initial request mode |
| language | string | 'en' | Preferred language for requests |
| cacheEnabled | boolean | true | Enable in-memory response caching |
| cacheTTL | number | 300000 | Cache TTL in ms (5 min) |
| timeout | number | 30000 | Per-request timeout in ms |
| retryAttempts| number | 3 | Max retry attempts |
| retryDelay | number | 1000 | Base delay for exponential backoff (ms) |
const client = new YTMusic({
mode: "music",
language: "en",
cacheEnabled: true,
cacheTTL: 5 * 60 * 1000,
});client.search(query, options?): Promise<SearchResult[]>
Search YouTube Music. Returns a discriminated union of SearchResult.
const results = await client.search("Imagine Dragons");
results.forEach((item) => {
switch (item.type) {
case "SONG":
console.log(`🎵 ${item.name} — ${item.artist.name}`);
break;
case "ALBUM":
console.log(`💿 ${item.name} — ${item.artist.name}`);
break;
case "ARTIST":
console.log(`👤 ${item.name}`);
break;
case "PLAYLIST":
console.log(`📜 ${item.name}`);
break;
case "VIDEO":
console.log(`🎬 ${item.name}`);
break;
}
});client.getAudioURL(videoId, options?): Promise<string>
Extract a direct, playable audio stream URL for a YouTube video.
const audioUrl = await client.getAudioURL("dQw4w9WgXcQ");
// Use with any HTML5 audio player
const audio = new Audio(audioUrl);
audio.play();Note: URLs typically expire after ~6 hours. Re-extract when needed.
client.getAudioFormats(videoId, clientType?): Promise<AudioFormat[]>
Get all available audio formats for a video. Useful for picking a specific bitrate/codec.
const formats = await client.getAudioFormats("dQw4w9WgXcQ");
formats.forEach((f) => {
console.log(`${f.mimeType} — ${f.bitrate} bps — ${f.quality}`);
});Core Entities & Discovery
| Method | Returns | Description |
| ------------------------------------ | ------------------------ | ------------------------------------------ |
| client.albums.getAlbum(id) | Promise<AlbumDetailed> | Fetch tracks and metadata for an Album |
| client.artists.getArtist(id) | Promise<ArtistFull> | Fetch top songs, albums, singles, videos |
| client.playlists.getPlaylist(id) | Promise<PlaylistFull> | Fetch tracks for a Playlist |
| client.getCharts() | Promise<HomeSection[]> | Fetch trending music charts |
| client.getNewReleases() | Promise<HomeSection[]> | Fetch new album & single releases |
| client.getGenres() | Promise<HomeSection[]> | Fetch music genres and moods |
| client.getLyrics(videoId) | Promise<string> | Fetch synchronized lyrics for a track |
| client.getRelatedTracks(videoId) | Promise<UpNextsDetails[]>| Fetch 'Up Next' or related tracks |
| client.library.getLikedSongs() | Promise<PlaylistFull> | Fetch liked songs (Requires cookies) |
| client.library.getListeningHistory()| Promise<HomeSection[]> | Fetch history (Requires cookies) |
Configuration Methods
| Method | Returns | Description |
| ---------------------------- | ------------------------ | ------------------------------------------ |
| client.init() | Promise<YTMusic> | Eagerly initialize the request layer |
| client.switchMode(mode) | YTMusic | Switch between 'youtube' and 'music' |
| client.setLanguage(lang) | YTMusic | Update preferred language |
| client.enableCache(bool?) | YTMusic | Toggle caching |
| client.setCacheTTL(ms) | YTMusic | Update cache TTL |
| client.clearCache() | Promise<void> | Drop all cached responses |
| client.getStatus() | object | Inspect cache + mode state |
🎯 TypeScript Types
SearchResult
A discriminated union returned by client.search().
type SearchResult =
| SongDetailed
| VideoDetailed
| AlbumDetailed
| ArtistDetailed
| PlaylistDetailed;SongDetailed
interface SongDetailed {
type: "SONG";
videoId: string;
name: string;
artist: { name: string; artistId: string | null };
album: { name: string; albumId: string } | null;
duration: number | null; // seconds
thumbnails: ThumbnailFull[];
}AlbumDetailed / ArtistDetailed / PlaylistDetailed / VideoDetailed
interface AlbumDetailed {
type: "ALBUM";
albumId: string;
playlistId: string;
name: string;
artist: { name: string; artistId: string | null };
year: number | null;
thumbnails: ThumbnailFull[];
}
interface ArtistDetailed {
type: "ARTIST";
artistId: string;
name: string;
thumbnails: ThumbnailFull[];
}
interface PlaylistDetailed {
type: "PLAYLIST";
playlistId: string;
name: string;
artist: { name: string; artistId: string | null };
thumbnails: ThumbnailFull[];
}
interface VideoDetailed {
type: "VIDEO";
videoId: string;
name: string;
artist: { name: string; artistId: string | null };
duration: number | null;
thumbnails: ThumbnailFull[];
}AudioFormat
interface AudioFormat {
itag: number;
url?: string; // may be undefined until deciphered
signatureCipher?: string;
mimeType: string; // e.g. "audio/webm; codecs=\"opus\""
bitrate: number; // bits per second
contentLength?: string;
quality?: string; // e.g. "tiny", "medium", "hd720"
qualityLabel?: string;
audioQuality?: string; // e.g. "AUDIO_QUALITY_MEDIUM"
approxDurationMs?: string;
audioSampleRate?: string;
audioChannels?: number;
}ExtractionOptions
interface ExtractionOptions {
quality?: "high" | "medium" | "low";
format?: "mp4" | "webm" | "any";
}🆚 Migration: v1 → v2
See MIGRATION.md for the full guide.
Quick highlights:
| v1.x | v2.0 |
| ------------------- | ----------------------- |
| result.type === 'song' | result.type === 'SONG' |
| result.title | result.name |
| result.artists[0] | result.artist.name |
| getAudioURLs(id) | getAudioURL(id) |
| No TypeScript types | Full type definitions |
| vm2 dependency | Native node:vm |
🐛 Troubleshooting
"All clients failed" when calling getAudioURL
The library tries android_test → tv → web in order. If all three fail:
- The video may be private, age-restricted, or region-locked
- The videoId may be invalid
- YouTube may be rate-limiting your IP — wait a few minutes
try {
const url = await client.getAudioURL("invalid_id");
} catch (err) {
if (err instanceof YTMusicError) {
console.error(err.message, err.context);
}
}Audio URL returns 403 when played
Stream URLs include a signature that expires (~6 hours). Re-extract if the cached URL is stale.
TypeScript can't find types
Ensure your tsconfig.json uses a modern moduleResolution:
{
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext"
}
}🔧 Advanced Usage
Error handling
import { YTMusicError, YTMusicAPIError } from "ytmusic-advanced";
try {
const url = await client.getAudioURL(videoId);
} catch (err) {
if (err instanceof YTMusicAPIError) {
console.error(`HTTP ${err.context.statusCode}`);
} else if (err instanceof YTMusicError) {
console.error(err.message);
}
}Custom retry policy
The library exposes the same retry helper it uses internally:
import { withRetry, DEFAULT_RETRY_OPTIONS } from "ytmusic-advanced";
const data = await withRetry(() => fetchSomething(), {
maxRetries: 5,
initialDelay: 500,
});Mode switching
import { YTMusic } from "ytmusic-advanced";
const client = new YTMusic({ mode: "music" });
// Later
client.switchMode("youtube");📄 License
MIT © Ganesh
⚠️ Disclaimer
This project is not affiliated with YouTube or Google. Use it responsibly and in accordance with YouTube's Terms of Service.
🙏 Acknowledgments
- ytmusicapi for parser inspiration
- Zod for runtime schema validation
- tough-cookie for session management
