Skip to main content

What is yt-dlp-ejs?

yt-dlp-ejs is a Python package that provides external JavaScript support for yt-dlp. It solves YouTube player challenges (n-parameter and signature decryption) by bundling JavaScript solvers that work across multiple runtimes.
Use Case: This library is primarily used by yt-dlp to decrypt YouTube video URLs by solving JavaScript challenges embedded in YouTube’s player code.

Quick Example

Here’s a complete example showing how to use yt-dlp-ejs to access the JavaScript solver bundles:
1

Import the module

import yt_dlp_ejs.yt.solver
2

Access the solver bundles

The library provides two main functions to access JavaScript solver code:
# Get the core solver bundle
core_bundle = yt_dlp_ejs.yt.solver.core()

# Get the library solver bundle
lib_bundle = yt_dlp_ejs.yt.solver.lib()

print(f"Core bundle loaded: {len(core_bundle)} characters")
print(f"Lib bundle loaded: {len(lib_bundle)} characters")
Both functions return the minified JavaScript code as a string.
3

Understanding the solver functions

The solver bundles are used to:
  • Preprocess YouTube player code - Parse and prepare player JavaScript
  • Solve ‘n’ challenges - Decrypt the n-parameter throttling parameter
  • Solve ‘sig’ challenges - Decrypt video signatures
import importlib.resources
import yt_dlp_ejs.yt.solver

def core() -> str:
    """
    Read the contents of the JavaScript core solver bundle as string.
    """
    return (importlib.resources.files(yt_dlp_ejs.yt.solver) / "core.min.js").read_text(
        encoding="utf-8"
    )

def lib() -> str:
    """
    Read the contents of the JavaScript library solver bundle as string.
    """
    return (importlib.resources.files(yt_dlp_ejs.yt.solver) / "lib.min.js").read_text(
        encoding="utf-8"
    )

How It Works

The JavaScript solver processes YouTube player code to extract decryption functions:
1

Input Processing

The solver accepts input with:
  • Player code (raw YouTube player JavaScript) or preprocessed player
  • Request type (n for throttling parameter, sig for signature)
  • Challenges (encrypted strings to decrypt)
type Input = {
  type: "player";
  player: string;                    // Raw player JavaScript
  requests: Request[];               // Array of challenge requests
  output_preprocessed: boolean;      // Whether to return preprocessed player
} | {
  type: "preprocessed";
  preprocessed_player: string;       // Already preprocessed player
  requests: Request[];               // Array of challenge requests
}
2

Solver Execution

The solver:
  1. Preprocesses the player code (if needed)
  2. Extracts the relevant decryption functions
  3. Applies them to each challenge
  4. Returns the decrypted results
type Request = {
  type: "n" | "sig";      // Challenge type
  challenges: string[];    // Strings to decrypt
}
3

Output Format

The solver returns results for each challenge:
type Output = {
  type: "result";
  preprocessed_player?: string;      // Optional preprocessed player
  responses: Response[];             // Array of responses
} | {
  type: "error";
  error: string;                     // Error message
}

type Response = {
  type: "result";
  data: Record<string, string>;      // Challenge -> decrypted result
} | {
  type: "error";
  error: string;                     // Error message
}

Complete Working Example

Here’s a practical example that demonstrates accessing the solver bundles:
import yt_dlp_ejs.yt.solver
from yt_dlp_ejs import version

def main():
    print(f"yt-dlp-ejs version: {version}")
    print()
    
    # Access the core solver bundle
    core = yt_dlp_ejs.yt.solver.core()
    print(f"✓ Core solver loaded: {len(core):,} bytes")
    
    # Access the library solver bundle
    lib = yt_dlp_ejs.yt.solver.lib()
    print(f"✓ Library solver loaded: {len(lib):,} bytes")
    
    # Both bundles are minified JavaScript code
    print()
    print("Core bundle preview:")
    print(core[:100] + "...")
    
    print()
    print("Lib bundle preview:")
    print(lib[:100] + "...")

if __name__ == "__main__":
    main()
The solver bundles are typically used by JavaScript runtime environments (Deno, Bun, Node.js) to execute the solver logic. yt-dlp handles this integration automatically.

Integration with yt-dlp

While yt-dlp-ejs can be used standalone, it’s designed to work seamlessly with yt-dlp:
yt-dlp automatically detects and uses yt-dlp-ejs when it’s installed in the same environment. No additional configuration is needed.
When yt-dlp needs to decrypt YouTube URLs, it:
  1. Calls yt_dlp_ejs.yt.solver.core() or yt_dlp_ejs.yt.solver.lib() to get the solver JavaScript
  2. Executes the solver in a JavaScript runtime (Deno, Bun, or Node.js)
  3. Passes the YouTube player code and challenges to the solver
  4. Receives decrypted parameters back from the solver
  5. Uses the decrypted parameters to construct working video URLs

Supported Runtimes

The JavaScript solvers support multiple runtimes:

Deno

Modern TypeScript runtime with built-in security

Bun

Fast all-in-one JavaScript runtime and toolkit

Node.js

Traditional JavaScript runtime (requires version 22.6+)
The JavaScript runtime must be available in your system PATH for yt-dlp to execute the solver bundles.

Next Steps

API Reference

Explore the complete API documentation

Architecture

Learn how the solver works internally

Build docs developers (and LLMs) love