npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mks2508/bundlp

v0.1.36

Published

Bun-native YouTube video/music resolver library

Readme

bundlp

Bun-native TypeScript library for extracting YouTube video and audio streams

E2E Tests TypeScript Bun License

Features

  • Complete Extraction: Video, audio, and combined formats with full metadata
  • Multi-Client Support: ANDROID_SDKLESS, TV, WEB, IOS with automatic fallback
  • PO Token System: Superior implementation with SQLite caching (12h TTL)
  • AST-Based Cipher: Signature decryption using meriyah parser
  • Result Pattern: Type-safe error handling with Result<T, E>
  • ArkType Validation: Runtime type validation for API responses
  • HLS/DASH Support: Manifest parsing for streaming formats
  • Zero Python: Pure TypeScript, optimized for Bun runtime

Installation

bun add bundlp

Quick Start

import { YouTubeExtractor, isOk } from 'bundlp';

const extractor = new YouTubeExtractor();

// Extract from URL
const result = await extractor.extract('https://youtube.com/watch?v=dQw4w9WgXcQ');

if (isOk(result)) {
  const video = result.value;

  console.log('Title:', video.title);
  console.log('Duration:', video.duration, 'seconds');
  console.log('Channel:', video.channel.name);

  // Get best audio
  const bestAudio = video.formats.audio[0];
  console.log('Best Audio URL:', bestAudio.url);
  console.log('Audio Quality:', bestAudio.audioQuality);
  console.log('Sample Rate:', bestAudio.audioSampleRate);

  // Get best video
  const bestVideo = video.formats.video[0];
  console.log('Best Video URL:', bestVideo.url);
  console.log('Resolution:', bestVideo.qualityLabel);
}

API Reference

YouTubeExtractor

Main class for extracting video information.

import { YouTubeExtractor } from 'bundlp';

const extractor = new YouTubeExtractor({
  cacheDir: '.cache',                  // Optional: cache directory for player.js
  preferredClient: 'ANDROID_SDKLESS',  // Optional: preferred InnerTube client
  poToken: 'your-token'                // Optional: static PO token
});

Methods

extract(url: string): Promise<Result<VideoInfo, BundlpError>>

Extracts complete video information from a YouTube URL or video ID.

Supported URL formats:

  • https://youtube.com/watch?v=VIDEO_ID
  • https://youtu.be/VIDEO_ID
  • https://youtube.com/shorts/VIDEO_ID
  • https://youtube.com/embed/VIDEO_ID
  • https://youtube.com/live/VIDEO_ID
  • https://music.youtube.com/watch?v=VIDEO_ID
  • Direct video ID: dQw4w9WgXcQ

VideoInfo

Complete video information returned by extract().

interface VideoInfo {
  id: string;                    // Video ID
  title: string;                 // Video title
  description: string;           // Video description
  duration: number;              // Duration in seconds
  uploadDate?: string;           // ISO date string
  channel: ChannelInfo;          // Channel information
  viewCount: number;             // View count
  thumbnails: Thumbnail[];       // Available thumbnails
  formats: FormatCollection;     // All available formats
  subtitles: Map<string, Subtitle[]>;  // Subtitles by language
  isLive: boolean;               // Is live stream
  isPrivate: boolean;            // Is private video
}

FormatCollection

Categorized formats for easy access.

interface FormatCollection {
  combined: Format[];    // Video+Audio (progressive downloads)
  video: Format[];       // Video-only (adaptive streaming)
  audio: Format[];       // Audio-only (adaptive streaming)
  hls?: HlsInfo;         // HLS manifest info
  dash?: DashInfo;       // DASH manifest info
}

Format

Individual format details.

interface Format {
  itag: number;              // YouTube format identifier
  url: string;               // Direct playback URL
  mimeType: string;          // MIME type (e.g., 'audio/webm')
  codecs: string[];          // Codec list ['opus']
  bitrate?: number;          // Bitrate in bps

  // Video properties
  width?: number;            // Video width
  height?: number;           // Video height
  fps?: number;              // Frames per second
  qualityLabel?: string;     // e.g., '1080p60'

  // Audio properties
  audioQuality?: string;     // 'AUDIO_QUALITY_LOW/MEDIUM/HIGH'
  audioSampleRate?: number;  // Sample rate in Hz
  audioChannels?: number;    // Number of audio channels

  // Metadata
  contentLength?: number;    // File size in bytes
  approxDurationMs?: number; // Duration in milliseconds
  hasDrm: boolean;           // Has DRM protection
  isAdaptive: boolean;       // Is adaptive format
}

Usage Examples

Get Best Audio URL

import { YouTubeExtractor, isOk } from 'bundlp';

async function getBestAudioUrl(videoUrl: string): Promise<string | null> {
  const extractor = new YouTubeExtractor();
  const result = await extractor.extract(videoUrl);

  if (!isOk(result)) {
    console.error('Extraction failed:', result.error.message);
    return null;
  }

  const { audio } = result.value.formats;

  if (audio.length === 0) {
    console.error('No audio formats available');
    return null;
  }

  // Formats are sorted by quality (best first)
  return audio[0].url;
}

const audioUrl = await getBestAudioUrl('https://youtube.com/watch?v=dQw4w9WgXcQ');
console.log('Audio URL:', audioUrl);

Get All Audio Formats with Details

import { YouTubeExtractor, isOk } from 'bundlp';

async function getAudioFormats(videoUrl: string) {
  const extractor = new YouTubeExtractor();
  const result = await extractor.extract(videoUrl);

  if (!isOk(result)) return [];

  return result.value.formats.audio.map(format => ({
    itag: format.itag,
    url: format.url,
    mimeType: format.mimeType,
    codecs: format.codecs.join(', '),
    bitrate: format.bitrate ? `${Math.round(format.bitrate / 1000)} kbps` : 'unknown',
    sampleRate: format.audioSampleRate ? `${format.audioSampleRate} Hz` : 'unknown',
    channels: format.audioChannels || 2,
    quality: format.audioQuality || 'unknown',
    size: format.contentLength
      ? `${(format.contentLength / 1024 / 1024).toFixed(2)} MB`
      : 'unknown'
  }));
}

const formats = await getAudioFormats('https://youtube.com/watch?v=dQw4w9WgXcQ');
console.table(formats);

Get Video Metadata

import { YouTubeExtractor, isOk } from 'bundlp';

async function getVideoMetadata(videoUrl: string) {
  const extractor = new YouTubeExtractor();
  const result = await extractor.extract(videoUrl);

  if (!isOk(result)) {
    throw new Error(result.error.message);
  }

  const video = result.value;

  return {
    id: video.id,
    title: video.title,
    description: video.description,
    duration: {
      seconds: video.duration,
      formatted: formatDuration(video.duration)
    },
    channel: {
      name: video.channel.name,
      id: video.channel.id,
      url: video.channel.url
    },
    statistics: {
      views: video.viewCount,
      viewsFormatted: formatNumber(video.viewCount)
    },
    thumbnails: video.thumbnails.map(t => ({
      url: t.url,
      resolution: `${t.width}x${t.height}`
    })),
    uploadDate: video.uploadDate,
    isLive: video.isLive,
    availableFormats: {
      video: video.formats.video.length,
      audio: video.formats.audio.length,
      combined: video.formats.combined.length
    }
  };
}

function formatDuration(seconds: number): string {
  const h = Math.floor(seconds / 3600);
  const m = Math.floor((seconds % 3600) / 60);
  const s = seconds % 60;
  if (h > 0) return `${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;
  return `${m}:${s.toString().padStart(2, '0')}`;
}

function formatNumber(num: number): string {
  if (num >= 1e9) return `${(num / 1e9).toFixed(1)}B`;
  if (num >= 1e6) return `${(num / 1e6).toFixed(1)}M`;
  if (num >= 1e3) return `${(num / 1e3).toFixed(1)}K`;
  return num.toString();
}

const metadata = await getVideoMetadata('https://youtube.com/watch?v=dQw4w9WgXcQ');
console.log(JSON.stringify(metadata, null, 2));

Download Best Quality Audio

import { YouTubeExtractor, isOk } from 'bundlp';

async function downloadAudio(videoUrl: string, outputPath: string) {
  const extractor = new YouTubeExtractor();
  const result = await extractor.extract(videoUrl);

  if (!isOk(result)) {
    throw new Error(`Extraction failed: ${result.error.message}`);
  }

  const bestAudio = result.value.formats.audio[0];

  if (!bestAudio) {
    throw new Error('No audio formats available');
  }

  console.log(`Downloading: ${result.value.title}`);
  console.log(`Format: ${bestAudio.mimeType} (${bestAudio.codecs.join(', ')})`);
  console.log(`Bitrate: ${Math.round((bestAudio.bitrate || 0) / 1000)} kbps`);

  const response = await fetch(bestAudio.url);
  const buffer = await response.arrayBuffer();

  await Bun.write(outputPath, buffer);
  console.log(`Saved to: ${outputPath}`);
}

await downloadAudio(
  'https://youtube.com/watch?v=dQw4w9WgXcQ',
  'audio.webm'
);

Get HLS/DASH Streaming URLs

import { YouTubeExtractor, isOk } from 'bundlp';

async function getStreamingManifests(videoUrl: string) {
  const extractor = new YouTubeExtractor();
  const result = await extractor.extract(videoUrl);

  if (!isOk(result)) return null;

  const { hls, dash } = result.value.formats;

  return {
    hls: hls ? {
      manifestUrl: hls.manifestUrl,
      variants: hls.variants.map(v => ({
        url: v.url,
        bandwidth: `${Math.round(v.bandwidth / 1000)} kbps`,
        resolution: v.resolution,
        codecs: v.codecs
      }))
    } : null,
    dash: dash ? {
      manifestUrl: dash.manifestUrl,
      duration: dash.duration
    } : null
  };
}

const manifests = await getStreamingManifests('https://youtube.com/watch?v=dQw4w9WgXcQ');
console.log(JSON.stringify(manifests, null, 2));

Filter Formats by Criteria

import { YouTubeExtractor, isOk, type Format } from 'bundlp';

async function getFormatsFiltered(videoUrl: string, options: {
  maxBitrate?: number;
  codec?: string;
  minQuality?: string;
}) {
  const extractor = new YouTubeExtractor();
  const result = await extractor.extract(videoUrl);

  if (!isOk(result)) return { audio: [], video: [] };

  const { audio, video } = result.value.formats;

  const filterFormat = (f: Format) => {
    if (options.maxBitrate && f.bitrate && f.bitrate > options.maxBitrate) return false;
    if (options.codec && !f.codecs.some(c => c.includes(options.codec!))) return false;
    return true;
  };

  return {
    audio: audio.filter(filterFormat),
    video: video.filter(filterFormat)
  };
}

// Get only opus audio under 128kbps
const formats = await getFormatsFiltered('https://youtube.com/watch?v=dQw4w9WgXcQ', {
  codec: 'opus',
  maxBitrate: 128000
});

Result Pattern

bundlp uses the Result pattern for type-safe error handling.

import { isOk, isErr, match, unwrapOr } from 'bundlp';

// Check result type
if (isOk(result)) {
  console.log(result.value);
} else {
  console.error(result.error);
}

// Pattern matching
match(result, {
  ok: (video) => console.log('Success:', video.title),
  err: (error) => console.error('Error:', error.message)
});

// Default value on error
const video = unwrapOr(result, defaultVideoInfo);

Error Codes

| Code | Description | |------|-------------| | INVALID_URL | Could not parse video ID from URL | | VIDEO_UNAVAILABLE | Video is unavailable or deleted | | NETWORK_ERROR | Network request failed | | PARSE_ERROR | Failed to parse response | | CIPHER_ERROR | Signature decryption failed | | ALL_CLIENTS_FAILED | All InnerTube clients failed |

CLI

bundlp includes a CLI for testing and debugging.

# Extract video info
bun run cli extract https://youtube.com/watch?v=dQw4w9WgXcQ

# List formats
bun run cli formats https://youtube.com/watch?v=dQw4w9WgXcQ

# Debug extraction
bun run cli debug https://youtube.com/watch?v=dQw4w9WgXcQ

# Run benchmarks (bundlp only)
bun run cli benchmark

Benchmarking

Compare bundlp performance against other libraries (yt-dlp, YouTube.js).

Compare Libraries

# Compare all three libraries on a single video
bun run cli benchmark compare <url>

# Compare specific libraries
bun run cli benchmark compare <url> -l bundlp,yt-dlp

# Specify output format
bun run cli benchmark compare <url> -o json -f results.json

# With warmup and multiple iterations
bun run cli benchmark compare <url> -w 2 -i 3

Options:

  • -l, --libraries <libs> - Libraries to compare (default: bundlp,yt-dlp,YouTube.js)
  • -w, --warmup <count> - Warmup iterations (default: 1)
  • -i, --iterations <count> - Measurement iterations (default: 1)
  • --no-validate - Skip URL playability validation
  • -o, --output <format> - Output format: console|json|all (default: console)
  • -f, --file <path> - Output file path for JSON

Export JSON

Export video information in yt-dlp compatible JSON format.

# Export to JSON with auto-generated filename
bun run cli benchmark export-json <url>

# Specify output file
bun run cli benchmark export-json <url> -o my-video.json

The JSON includes:

  • Basic video info (id, title, duration, view count)
  • Format count and breakdown (video/audio/combined)
  • Highest quality available
  • URL playability rate
  • bundlp metadata (version, extraction time, comparison results)

Performance

| Operation | Time (cached) | Time (fresh) | |-----------|---------------|--------------| | Full Extraction | ~130ms | ~5s | | Player.js Parse | ~20ms | ~5s | | PO Token | ~10ms | ~800ms | | InnerTube Request | ~120ms | ~120ms |

Architecture

src/
├── core/           # YouTubeExtractor
├── innertube/      # InnerTube API client
├── player/         # Player.js + AST cipher extraction
├── streaming/      # HLS/DASH processing
├── po-token/       # PO Token system
├── http/           # HTTP client with cookies
├── result/         # Result<T,E> pattern
├── types/          # TypeScript types
├── validation/     # ArkType schemas
└── utils/          # Constants, parsers

Documentation

Development

# Install dependencies
bun install

# Run E2E tests
bun test:e2e

# Run unit tests
bun test

# Type check
bunx tsc --noEmit

# Lint
bun run lint

# CLI
bun run cli

License

MIT