CLI tool
Last-Order/Minyami avatar
Last-Order/Minyami

Minyami pulls HLS playlists into files, or into your own Node code

A lovely video downloader for HLS videos

648 stars42 forksTypeScriptGPL-3.0

At a glance

What is it?
Minyami is a GPL-3.0 HLS downloader and TypeScript library with a CLI for playlists, live streams, time slices, proxies and explicit decryption keys, plus a programmatic API. It is currently published as a 6.0.0 beta, and encrypted fMP4 streams need Bento4 on your PATH.
Who is it for?
Minyami fits a developer who already knows the stream is theirs to fetch, whether that means archiving an event replay, moving a video onto a device you control, or wiring HLS retrieval into a Node service through the exported downloader objects. It does not fit a team that wants a stable release, since the published version is a 6.0.0 beta, and it does not fit a machine without Bento4 if the playlists are encrypted fMP4 with SAMPLE-AES cbcs.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A downloader for HLS, not a general purpose video grabber

Minyami is described simply as a video downloader for HLS videos, and that scoping is the thing to hold on to. It works on m3u8 playlists, the segmented manifest format used by HTTP live streaming, and it is built in TypeScript with Node.js 24 or newer as the stated requirement, with an active LTS release recommended. There is no page scraper, no site-specific extractor list and no promise to find a video somewhere. What it gives you is a reliable way to take a playlist URL and end up with a file. The package is published under the name minyami on npm, the version currently listed is 6.0.0-beta.8, the author is Eridanus Sora, and the licence is GPLv3, with the README stating GPLv3 for 2018-2025. The project is on its sixth major version and still shipping beta tags, which tells you the API surface is still moving.

Installing the CLI and the two external tools it may call

Installation is a global npm install, and the README also documents the Yarn equivalent:

shell
npm install --global minyami
shell
yarn global add minyami

Two external dependencies matter. mkvmerge or ffmpeg is optional, needed only if you want to mux separate audio and video tracks, and it has to be added to your PATH. Bento4 mp4decrypt is a harder requirement, and it must be available in PATH to decrypt fMP4 and CMAF SAMPLE-AES cbcs playlists. That distinction matters when you plan a deployment: the muxing tool is a convenience you can skip, whereas a stream encrypted with cbcs will simply fail without mp4decrypt present. For containerised use the repository ships a Dockerfile that builds the package with tsdown on node:24-alpine, packs it, and installs it globally in a runtime image that also installs dumb-init and mkvtoolnix, with the container entrypoint defaulting to minyami --help.

Downloading a playlist, a slice or a live stream

The command surface is a single download subcommand with a URL or a local playlist path as its argument:

shell
minyami "https://example.com/video.m3u8" --output "./video.ts"

Several options cover the common cases. Concurrency is set with --threads and defaults to 5. A time range is requested with --slice in a start-end form, and live playlists are followed with --live until the command is stopped. Proxies are supported over HTTP, HTTPS and SOCKS5 with --proxy, and --no-proxy ignores proxy environment variables and system proxy settings. Custom headers and cookies go out with --headers and --cookies, which matters for any endpoint that expects a session. The option table also covers --retries for both source requests and download tasks, defaulting to 5, a --temp-dir for the parent directory of temporary files, --no-merge to keep downloaded chunks separate, --keep to retain temporary files and --keep-encrypted-chunks to keep encrypted chunks after decryption, which is used together with --keep. The download command requires a URL or local playlist path, and missing input shows an error and the help text.

Decryption keys come from you, not from the stream

The --key option takes an explicit HLS decryption key in one of two forms. A single key applies to every AES-128 key URI in the playlist. When several keys are given, they map to distinct URIs in first-seen order within each Media Playlist, and those assignments are retained across live refreshes, which is the detail that makes multi-key live streams work rather than scramble. fMP4 SAMPLE-AES needs the kid:key form, since the key has to be selected by key ID rather than by position. The library API expresses the same idea differently, where callers pass parsed explicit keys as HLSExplicitKey objects such as explicitKeys: [{ key }] or explicitKeys: [{ kid, key }]. Putting key handling in the caller's hands is a deliberate design choice rather than a gap: Minyami does not go looking for keys, and mp4decrypt handles the cbcs case separately as an external tool.

A configuration file that is quietly forgiving

Defaults can come from a JSON object placed in ~/.minyami/.minyamirc, ~/.minyami/.minyamirc.json or ~/.minyami/minyami.config.json, with the first readable JSON object in that order supplying defaults. Option names are the CLI names in camelCase, so noProxy, tempDir and keepEncryptedChunks are the file spellings:

json
{
    "threads": 8,
    "noProxy": true,
    "verbose": true
}

The parsing behaviour is unusually forgiving in the places where it does not matter and strict in the place where it does. Unreadable files, malformed JSON and non-object roots are all silently skipped, and if no candidate is usable the command simply proceeds without file defaults. Explicit CLI values override file defaults before validation happens. Invalid merged option types stop the command with an error and exit code 1. Types matter: JSON booleans for flags, positive integers for threads and retries, strings for other values, and a string or string array for headers and key.

Using Minyami as a library through the exported downloader objects

The package is distributed as ESM and must be loaded with import rather than CommonJS require, which is the first thing that breaks when you paste an example into a CommonJS project. The exported surface is two factory functions, createArchiveDownloader for a finished playlist and createLiveDownloader for one that keeps updating. The archive case looks like this:

TypeScript
import { createArchiveDownloader } from "minyami";

const downloader = createArchiveDownloader("https://example.com/archive.m3u8", {
    output: "./archive.ts",
    threads: 8,
});

downloader.on("chunk-downloaded", (chunk) => {
    console.log(chunk);
});

await downloader.download();

The downloader is an event emitter, so chunk-downloaded gives you progress per segment rather than a single final callback. For a live playlist you call stop() when you want to finish, which is the difference between the two factories. When a streamSelector is omitted the library selects all tracks from the highest-bandwidth option of a master playlist, and when you supply one you return the tracks you want from the catalog options, or return undefined to cancel the download.

Where Minyami sits against ffmpeg and the generic grabbers

The realistic alternative is to hand the same playlist to ffmpeg with its HLS demuxer, or to reach for a general purpose media downloader that inspects web pages and figures out the stream itself. The difference in approach is scope and control. ffmpeg is a media processing tool that happens to read HLS, so it brings a much larger surface and a different command language, and it will not hand you the segment-level events or the explicit key mapping that Minyami exposes. A generic grabber goes in the opposite direction, deciding for you which URLs to fetch, which is helpful when you do not know the manifest and unhelpful when you do, for instance when the playlist requires cookies and specific headers. Minyami sits between them: you supply the playlist and the credentials, it handles the manifest, the segment concurrency, the retries and the decryption hand-off.

Editorial conclusion

Minyami fits a developer who already knows the stream is theirs to fetch, whether that means archiving an event replay, moving a video onto a device you control, or wiring HLS retrieval into a Node service through the exported downloader objects. It does not fit a team that wants a stable release, since the published version is a 6.0.0 beta, and it does not fit a machine without Bento4 if the playlists are encrypted fMP4 with SAMPLE-AES cbcs. Verify first that the playlist you want is a standard HLS manifest rather than a DRM scheme Minyami does not implement, that your Node version is 24 or newer, and that mkvmerge or ffmpeg is on your PATH if you need separate audio and video muxed.

Frequently asked questions

How do I install Minyami and what else does it need?

Install it globally with npm install --global minyami, or yarn global add minyami, and use Node.js 24 or newer. mkvmerge or ffmpeg is optional and only needed to mux separate audio and video, while Bento4 mp4decrypt must be on your PATH to decrypt fMP4 and CMAF SAMPLE-AES cbcs playlists.

How do I download only part of an HLS stream with Minyami?

Pass a time range to --slice, for example minyami -d "https://example.com/video.m3u8" --slice "45:00-53:00". The option requires a string value, and invalid values are reported and omitted while the other valid options stay active.

Can I use Minyami as a Node library instead of a command line tool?

Yes. The package is distributed as ESM and must be imported with import rather than require. You create a downloader with createArchiveDownloader or createLiveDownloader, listen for chunk-downloaded events, and await the download method, calling stop on a live downloader when you want to finish.

How does Minyami pick tracks from a master playlist?

When no streamSelector is given, the library selects all tracks from the highest-bandwidth option. Supplying a streamSelector lets you return the tracks you want from the catalog options, or return undefined to cancel the download.

Official sources

  1. Issues
  2. Last-Order/Minyami on GitHub
  3. License: GPL-3.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/last-order-minyami.svg)](https://hysenlabs.com/projects/last-order-minyami)

Was this useful?