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

hls-grab

v1.0.0

Published

Zero-dependency Node.js CLI to download an HLS (.m3u8) stream from a CDN and mux it into a single video file.

Readme

hls-grab

A small, zero-dependency Node.js CLI that downloads an HLS stream from a CDN — given the .m3u8 URL — and saves it as a single video file.

It parses the master playlist, picks a variant (quality), downloads every media segment in parallel, decrypts AES-128 segments if needed, and remuxes the result into a clean .mp4 with ffmpeg (stream copy — no re-encoding).

Requirements

  • Node.js ≥ 18 (uses the built-in fetch — no npm install needed)
  • ffmpeg on your PATH (optional but recommended; used to remux to .mp4 and to merge a separate audio track). Without it, the raw concatenated stream is kept instead.

Install

Run it directly from the repo:

node index.js <master.m3u8 URL> [options]

Or install it globally to get an hls-grab command anywhere:

npm i -g .       # from inside the repo
hls-grab <master.m3u8 URL> [options]

Usage

node index.js <master.m3u8 URL> [options]

Options

| Option | Description | | --- | --- | | -o, --output <file> | Output path (default: ./<name>.mp4) | | -q, --quality <best\|worst> | Variant to pick from a master playlist (default: best) | | -r, --resolution <px> | Pick a variant by height, e.g. 720 or 1080 (overrides --quality) | | -c, --concurrency <n> | Parallel segment downloads (default: 6) | | -H, --header <"K: V"> | Extra request header, repeatable (cookies, auth, referer) | | --no-ffmpeg | Skip remux; keep the raw concatenated .ts/.mp4 stream | | --keep-temp | Keep the temp segment directory (debugging) | | -l, --list | List the variants in a master playlist and exit | | -h, --help | Show help |

Examples

# Best quality, default output name
node index.js 'https://cdn.example.com/video/master.m3u8'

# Inspect what qualities are available
node index.js 'https://cdn.example.com/master.m3u8' --list

# Pick 720p, custom output, more parallelism
node index.js 'https://cdn.example.com/master.m3u8' -r 720 -o movie.mp4 -c 10

# Pass auth/cookies for a protected CDN
node index.js 'https://cdn.example.com/master.m3u8' \
  -H 'Cookie: session=abc123' \
  -H 'Referer: https://example.com/'

What it handles

  • Master playlists (variant selection by best / worst / target resolution)
  • Media playlists (plain MPEG-TS and fragmented-MP4 / CMAF via EXT-X-MAP)
  • AES-128 encrypted segments (EXT-X-KEY), including key fetching/caching and both explicit and sequence-derived IVs
  • Byte-range segments (EXT-X-BYTERANGE)
  • A separate audio rendition (EXT-X-MEDIA TYPE=AUDIO) — downloaded and merged with the video by ffmpeg
  • Relative segment/key URIs (resolved against the playlist URL)
  • A browser-like User-Agent by default (overridable with -H)
  • Retries with exponential backoff, and resume of partially-downloaded runs
  • Live playlists: downloads the current segment window (no EXT-X-ENDLIST) and warns

Not supported

  • SAMPLE-AES / FairPlay / Widevine DRM (these are protected by design)
  • Continuously following a live stream until it ends

Working with signed CDN URLs

Many CDNs hand out signed, time-limited playlist URLs (with a hash/token in the path or query). A few things that save headache:

  • Always wrap the URL in single quotes. Signed URLs are full of =, &, , characters. Double quotes (or no quotes) let the shell mangle them — a stray backslash before = is the most common cause of a sudden 403. (The tool now strips stray backslashes defensively and warns, but single-quoting is the fix.)
  • Copy the URL from the same machine/network you'll download on. Tokens are often bound to your IP, so a link grabbed elsewhere will be rejected.
  • Use it promptly. Once past the token's expiry, only a fresh URL works.
  • Grab it from the browser's Network tab (filter m3u8), or use Copy → Copy as cURL to also capture the Cookie/Referer headers, then pass them with -H.

Common errors

| Status | Meaning | Fix | | --- | --- | --- | | 403 Forbidden | Bad/expired/missing signature, wrong IP, or a shell-mangled URL | Single-quote the URL (no backslashes); re-copy a fresh link from the same machine | | 470 (non-standard) | CDN rejected the request: token required / segments unsigned | Feed the signed playlist the browser actually loads, and pass its Cookie/Referer via -H |

Layout

index.js        # CLI parsing + orchestration
lib/parser.js   # m3u8 parsing (master + media), variant selection
lib/download.js # fetch + retries, concurrency pool, AES-128, assembly
lib/mux.js      # ffmpeg remux / audio+video merge

Note

Only download streams you own or are authorized to access. Respect the terms of service of the site you're downloading from and applicable copyright law. This tool does not bypass DRM.

License

MIT © Hossein Seifi