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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@shelby-protocol/media-prepare

v0.0.1

Published

FFmpeg presets and a declarative builder for CMAF + HLS: multi‑rung ladders, multi‑audio, trickplay, safe segment sizing, and clean `var_stream_map`. The library produces FFmpeg argument arrays only — no manual folder or manifest creation — and works in b

Readme

@shelby-protocol/media-prepare

FFmpeg presets and a declarative builder for CMAF + HLS: multi‑rung ladders, multi‑audio, trickplay, safe segment sizing, and clean var_stream_map. The library produces FFmpeg argument arrays only — no manual folder or manifest creation — and works in both Node (native FFmpeg) and the browser (FFmpeg.wasm).

We use CMAF + HLS for broad device/DRM support: HLS provides the playlists and CMAF provides fragmented MP4 media segments (.m4s).

Usage

Subpath exports

  • @shelby-protocol/media-prepare/core — platform-agnostic utilities and Zod schemas
  • @shelby-protocol/media-prepare/node — Node helpers (ffmpeg executor, system checks, media probe)
  • @shelby-protocol/media-prepare/browser — Browser helpers (FFmpeg.wasm executor, probe)

Key points

  • Declarative: Build a typed plan, then render FFmpeg args.
  • Zod validated: Inputs and the IR are runtime-validated (with helpful messages) and fully typed via z.infer.
  • No file IO helpers: You execute FFmpeg with the args; it writes playlists and segments.
  • Ergonomic builder: withLadder, withAudio, withTrickplay, withSegments.

Build & Scripts

This package uses tsup for builds and Biome for lint/format.

Development

# Install dependencies
pnpm install

# Build the package
pnpm run build

# Run linting
pnpm run lint

# Auto-fix linting issues
pnpm run fmt

# Run tests (core + browser shim)
pnpm test

# Run local system tests (requires FFmpeg installed)
pnpm test:local

# Run browser tests with Playwright  
pnpm test:browser

Testing

  • pnpm test — Fast unit tests (core logic, HLS+CMAF builder), CI‑safe
  • pnpm test:browser — Browser tests using Playwright; runs FFmpeg.wasm in real browser
  • pnpm test:local — System integration tests that execute your local FFmpeg (requires FFmpeg installed)

Local tests expect FFmpeg v7+ with encoders like libx264/libx265/libaom; they're excluded from CI.

The browser tests validate that the generated args are compatible with FFmpeg.wasm and can produce actual HLS+CMAF output.

Quick Start

Node (native FFmpeg)

import { planHlsCmaf, x264, aac, presets } from "@shelby-protocol/media-prepare/core/hls-cmaf";
import { execFfmpeg } from "@shelby-protocol/media-prepare/node";
import * as fs from "node:fs/promises";

const plan = planHlsCmaf()
  .input("input.mp4")
  .outputDir("out")
  .withLadder(presets.vodHd_1080p)
  .withVideoEncoder(x264())
  .withAudio(aac(), { language: "eng", bitrateBps: 128_000, default: true })
  .withSegments({ mode: "auto", maxBlobBytes: 10 * 1024 * 1024, safety: 0.9, minSeconds: 1, align: 1 })
  .hlsCmaf()
  .render.ffmpegArgs();

// Execute ffmpeg
await fs.rm(plan.outputDir, { recursive: true, force: true });
await execFfmpeg(plan.args, { precreate: plan.variantNames.map(name => `${plan.outputDir}/${name}`) });

Browser (FFmpeg.wasm)

import { planHlsCmaf } from "@shelby-protocol/media-prepare/core/hls-cmaf";
import { FFmpeg } from "@ffmpeg/ffmpeg";
import { toBlobURL, fetchFile } from "@ffmpeg/util";

const plan = planHlsCmaf()
  .input("input.mp4")
  .outputDir("/out")
  .withLadder([{ width: 854, height: 480, bitrateBps: 1_000_000, name: "480p" }])
  .withVideoEncoder({ kind: "copy" }) // copy‑mode recommended in wasm
  .withSegments({ mode: "fixed", segmentSeconds: 4 })
  .hlsCmaf()
  .render.ffmpegArgs();

const ffmpeg = new FFmpeg();
const baseURL = "https://unpkg.com/@ffmpeg/[email protected]/dist/esm";
await ffmpeg.load({
  coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, "text/javascript"),
  wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, "application/wasm"),
});
await ffmpeg.writeFile("input.mp4", await fetchFile(file));

// Create output directories
for (const variant of plan.variantNames) {
  await ffmpeg.createDir(`/out/${variant}`);
}

await ffmpeg.exec(plan.args);

System Requirements

For Node.js usage, ensure you have:

  • FFmpeg >= 7.0 with required codecs (libx264, libx265, libvpx, libaom, libfdk_aac)
  • FFprobe (usually bundled with FFmpeg)
  • Shaka Packager (optional)

Install on macOS:

brew install ffmpeg

For Shaka Packager, download from https://github.com/shaka-project/shaka-packager. Get it from the Releases page https://github.com/shaka-project/shaka-packager/releases

The SystemChecker utility will validate your system setup and provide installation guidance for your platform.

WebAssembly vs Native FFmpeg: Understanding the Differences

Why FFmpeg.wasm Packages Don't Work in Node.js

Common Misconception: "WebAssembly runs everywhere, so @ffmpeg/ffmpeg should work in Node.js"

Reality: While WebAssembly itself can run in Node.js, the @ffmpeg/ffmpeg and @ffmpeg/core packages are specifically designed for browser environments and will fail in Node.js.

@ffmpeg/ffmpeg Limitations in Node.js

// ❌ This fails in Node.js
import { FFmpeg } from '@ffmpeg/ffmpeg';
const ffmpeg = new FFmpeg();
await ffmpeg.load(); // Error: "does not support nodejs"

Why it fails:

  • Built around Web Workers for non-blocking browser processing
  • Uses browser-specific APIs like SharedArrayBuffer and MessagePort
  • Intentionally blocks Node.js execution with runtime checks

@ffmpeg/core Limitations in Node.js

// ❌ This also fails in Node.js
import createFFmpegCore from '@ffmpeg/core';
const Module = await createFFmpegCore({}); // ReferenceError: self is not defined

Why it fails:

  • References browser globals like self, window, document
  • Expects browser's fetch API and URL handling
  • Designed for browser's WebAssembly loading mechanisms

Recommended Approaches by Environment

| Environment | Recommended Solution | Performance | Use Case | |-------------|---------------------|-------------|-----------| | Node.js Server | Native FFmpeg (args + spawn) | ⚡ Excellent | Server-side processing | | Browser Client | @ffmpeg/ffmpeg (WebAssembly) | ✅ Good | Client-side processing | | Edge/Serverless | Native FFmpeg binary | ⚡ Excellent | Serverless functions |

Node.js: Native FFmpeg

import { planHlsCmaf, x264, aac } from "@shelby-protocol/media-prepare/core/hls-cmaf";
import { execFfmpeg } from "@shelby-protocol/media-prepare/node";
import * as fs from "node:fs/promises";

const plan = planHlsCmaf()
  .input("/path/to/video.mp4")
  .outputDir("/path/to/output")
  .withLadder([
    { width: 1920, height: 1080, bitrateBps: 5_000_000, name: "1080p" },
    { width: 1280, height: 720, bitrateBps: 3_000_000, name: "720p" },
  ])
  .withVideoEncoder(x264())
  .withAudio(aac(), { language: "eng", bitrateBps: 128_000, default: true })
  .withSegments({ mode: "auto", maxBlobBytes: 10 * 1024 * 1024, safety: 0.9, minSeconds: 1, align: 1 })
  .hlsCmaf()
  .render.ffmpegArgs();

await fs.rm(plan.outputDir, { recursive: true, force: true });
await execFfmpeg(plan.args, { precreate: plan.variantNames.map(name => `${plan.outputDir}/${name}`) });

Advantages:

  • Performance: Native binaries are 3-10x faster than WebAssembly
  • 🛠️ Full Feature Set: Access to all FFmpeg codecs and filters
  • 💾 Memory Efficiency: Better memory management for large files
  • 🔧 System Integration: Hardware acceleration support

Browser: WebAssembly FFmpeg

import { planHlsCmaf } from "@shelby-protocol/media-prepare/core/hls-cmaf";
import { FFmpeg } from "@ffmpeg/ffmpeg"; // peer dependency
import { fetchFile } from "@ffmpeg/util";

const plan = planHlsCmaf()
  .input("input.mp4")
  .outputDir("/out")
  .withLadder([{ width: 854, height: 480, bitrateBps: 1_000_000, name: "480p" }])
  .withVideoEncoder({ kind: "copy" }) // copy mode recommended in wasm
  .withSegments({ mode: "fixed", segmentSeconds: 4 })
  .hlsCmaf()
  .render.ffmpegArgs();

const ffmpeg = new FFmpeg();
await ffmpeg.load();
await ffmpeg.writeFile("input.mp4", await fetchFile(file));
await ffmpeg.exec(plan.args);

Advantages:

  • 🌐 Client-Side: No server upload required
  • 🔒 Privacy: Files never leave user's device
  • 📱 Universal: Works in any modern browser
  • Scalable: Offloads processing from servers

Limitations:

  • 🐌 Performance: Slower than native FFmpeg
  • 💾 Memory: Limited by browser WebAssembly constraints
  • 🎛️ Codecs: Subset of full FFmpeg codec support
  • 📁 File Size: Best for smaller files (<100MB)

Peer Dependencies

For browser WebAssembly integration, install the optional peer dependencies:

# Browser WebAssembly FFmpeg
npm install @ffmpeg/ffmpeg @ffmpeg/util @ffmpeg/core

Note: These are optional peer dependencies since Node.js applications use native FFmpeg binaries instead.