New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details
Socket
Book a DemoSign in
Socket

@shelby-protocol/media-prepare

Package Overview
Dependencies
Maintainers
1
Versions
2
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@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 b

latest
npmnpm
Version
0.0.1
Version published
Weekly downloads
6
500%
Maintainers
1
Weekly downloads
 
Created
Source

@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/core@0.12.9/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
EnvironmentRecommended SolutionPerformanceUse Case
Node.js ServerNative FFmpeg (args + spawn)⚡ ExcellentServer-side processing
Browser Client@ffmpeg/ffmpeg (WebAssembly)✅ GoodClient-side processing
Edge/ServerlessNative FFmpeg binary⚡ ExcellentServerless 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.

FAQs

Package last updated on 05 Nov 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts