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

expo-ytdlp

v1.0.1

Published

A native Android Expo module providing a TypeScript API around yt-dlp.

Readme

expo-ytdlp

A native Android Expo module providing a modern TypeScript API around yt-dlp. It embeds the Python runtime, yt-dlp and all site extractors through the yt-dlp-android library — no Python, yt-dlp, Chaquopy, FFmpeg or Termux setup is required in your app.

import YtDlp from 'expo-ytdlp';

const info = await YtDlp.extractInfo('https://www.youtube.com/watch?v=...');
console.log(info.title);
console.log(info.formats);

const task = await YtDlp.download({
  url: 'https://www.youtube.com/watch?v=...',
  format: 'bestvideo+bestaudio',
  output: { directory: 'Movies' },
});

task.addListener('progress', (progress) => {
  console.log(progress.percent);
});

await task.cancel();

Requirements

  • Platform: Android only. Importing on iOS/web throws YtDlpError with code UNSUPPORTED_PLATFORM.

  • Expo SDK: 57 (tested against Expo 57 / React Native 0.86).

  • Minimum Android: API 24.

  • Native build required. This is a custom native module. It does not work inside standard Expo Go — Expo Go cannot load arbitrary custom native modules. Use a development build:

    npx expo prebuild
    npx expo run:android

Installation

npx expo install expo-ytdlp

If expo install does not resolve the package (e.g. before it is indexed), fall back to:

npm install expo-ytdlp

Usage

Extract media information

import YtDlp from 'expo-ytdlp';

const info = await YtDlp.extractInfo(url);
console.log(info.title);   // string | undefined
console.log(info.duration);
console.log(info.thumbnail);
console.log(info.formats);

extractInfo never downloads media. Fields that a site does not provide are undefined — no field is guaranteed for every site.

List formats

const formats = await YtDlp.getFormats(url);

getFormats reuses the extraction result, so it does not extract twice.

Download

const task = await YtDlp.download({
  url,
  format: 'bestvideo+bestaudio', // raw yt-dlp format expression
  output: {
    directory: 'Movies',
    filename: '%(title)s.%(ext)s',
  },
});

task.addListener('progress', (progress) => {
  console.log(progress.percent, progress.speedBytesPerSecond, progress.etaSeconds);
});

task.addListener('completed', (result) => {
  console.log(result.path);
});

task.addListener('error', (error) => {
  console.log(error.code, error.message);
});

download resolves as soon as the task is registered; progress and the final result arrive through the task's listeners. Multiple downloads can run at the same time — every event carries a taskId.

Cancel

await task.cancel();

Cancellation calls the native cancellation path and aborts the underlying yt-dlp download. To cancel a task after your JS task object is gone:

await YtDlp.cancel(taskId);

Version

const version = await YtDlp.getVersion();
// { ytDlp: '2026.xx.xx', library: '0.1.0' }

The embedded yt-dlp version and the npm package version are independent.

Supported download options

| Option | Description | | --- | --- | | url | Required. Any URL supported by yt-dlp. | | format | Raw yt-dlp format expression, e.g. best, bestaudio, best[height<=720]. | | output.directory | Subdirectory under the app's yt-dlp folder. Sanitized. | | output.filename | yt-dlp output template, e.g. %(title)s.%(ext)s. Sanitized. | | headers | Extra HTTP headers, e.g. { Referer: '...' }. | | userAgent | Custom User-Agent. | | referer | Custom Referer. | | proxy | Proxy URL. | | cookies.path | Path to a Netscape-format cookies file. | | playlist.enabled | Default false — a playlist URL downloads only the first item unless enabled. | | playlist.start / playlist.end | Playlist item range (1-based). | | subtitles.enabled | Write subtitles. | | subtitles.languages | e.g. ['en', 'bn']. | | subtitles.autoGenerated | Also write auto-generated subtitles. | | network.timeout | Socket timeout in seconds. | | network.retries | Number of retries. |

Not supported (yet)

The bundled yt-dlp-android build ships no FFmpeg, so the following are rejected up front with PROCESSING_FAILED rather than silently ignored:

  • merge: true (e.g. bestvideo+bestaudio will fail because merging requires FFmpeg)
  • audio.* (audio extraction / re-encoding)
  • metadata (embedding)
  • thumbnail (embedding)

Use format: 'best' or format: 'bestaudio' and download a single stream that requires no post-processing.

Download task

A DownloadTask exposes:

  • id: string
  • cancel(): Promise<void>
  • getStatus(): Promise<DownloadStatus>
  • getProgress(): Promise<DownloadProgress | null>
  • addListener(event, listener): Subscription

Statuses: queued | extracting | downloading | processing | completed | cancelled | failed.

Events:

  • progressDownloadProgress (percent, downloadedBytes, totalBytes, speedBytesPerSecond, etaSeconds, filename, phase)
  • state{ taskId, status }
  • completedDownloadResult (taskId, path, filename, size)
  • errorYtDlpError

Progress events are throttled to ~200 ms and numeric fields are undefined when the value is unknown (never NaN).

Errors

All failures normalize to YtDlpError with a code:

INVALID_URL, EXTRACTION_FAILED, DOWNLOAD_FAILED, CANCELLED, FORMAT_UNAVAILABLE, NETWORK_ERROR, AUTHENTICATION_REQUIRED, GEO_RESTRICTED, PRIVATE_CONTENT, AGE_RESTRICTED, PROCESSING_FAILED, STORAGE_ERROR, INIT_FAILED, UNSUPPORTED_PLATFORM, UNKNOWN.

import { YtDlpError } from 'expo-ytdlp';

try {
  await YtDlp.extractInfo(url);
} catch (error) {
  if (error instanceof YtDlpError) {
    console.log(error.code, error.message);
  }
}

Raw native stack traces are never surfaced to the user.

Storage

Files are written to app-specific external storage:

Android/data/<your-package>/files/yt-dlp/<output.directory>/...

Filenames and directory segments are sanitized against illegal characters, path traversal, excessive length and empty names. This avoids dangerous permissions like MANAGE_EXTERNAL_STORAGE. The returned DownloadResult.path is an absolute path inside your app's own storage.

Limitations

  • Android only.
  • No FFmpeg-based post-processing (see "Not supported" above).
  • No background/foreground service: downloads pause if the JS/native runtime is destroyed. Persisting task IDs lets you re-issue cancellation later.
  • yt-dlp site support changes frequently. Not every website works forever, and not every site provides every field.
  • This package does not bundle or provide a way to update the embedded yt-dlp at runtime.
  • Playlists are opt-in via playlist.enabled to avoid accidental bulk downloads.

Legal & responsible use

expo-ytdlp is a technical wrapper around yt-dlp. It does not circumvent DRM (Widevine, FairPlay, PlayReady, ...), bypass authentication, or access private or unauthorized content — content that requires DRM or authentication will fail with an error.

You are responsible for complying with:

  • website terms of service
  • copyright law and content licenses
  • authentication rules
  • platform policies

Do not use this library to download content you do not have the right to download.

Third-party licenses

| Component | License | | --- | --- | | expo-ytdlp (this package) | MIT | | yt-dlp-android (Maven dev.ffmpegkit-maintained:yt-dlp-android) | MIT | | yt-dlp | Unlicense | | Chaquopy | BSD-style (per the embedded distribution) |

Re-verify third-party licenses at release time.