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 🙏

© 2024 – Pkg Stats / Ryan Hefner

m3u8-conver

v1.0.2

Published

Convert network or local m3u8 files into media files.将网络或者本地的m3u8文件转换成媒体文件

Downloads

22

Readme

English|简体中文

📖Introduction

Convert network or local m3u8 files to media files (such as mp4, avi, etc.).

  • [x] Decode AES-123-CBC
  • [x] Decode AES-192-CBC
  • [x] Decode AES-256-CBC
  • [X] parser m3u8 localfile
  • [X] custom save format.ext
  • [x] concurrence download
  • [x] custom m3u8 parser
  • [x] request optons

🚀Install

Make sureNodejs>=v16.13.0

If you do not install Nodejs, Please install Nodejs

# npm
npm i m3u8-conver -g

# pnpm
pnpm i m3u8-conver -g

🚗Use

1. Used on the command

# help
mconver -h
# Converts "https://www.test.com/test.m3u8" to the current directory "output. Mp4"
mconver -i "https://www.test.com/test.m3u8"
# or parser local file, and set output file, and set the number of concurrent downloads
mconver -i "./test.m3u8" -o "./output.mp4" -c 10

2. Used in the code

custom parser see Custom-parser

import mconver from "m3u8-conver"
// basic
const output = await mconver({
    input: "https://www.test.com",
})
console.log("convered path: ", output)

// other options
// More configuration see document #Options
await mconver({
    input: "https://www.test.com",
    name: "output.mp4",
    concurrency: 6,
    requestOptions: {
        method: "GET",
        headers: {
            "x-token": "your token",
            Cookie: "your cookie",
        },
    },
    parsered(fragments) {
        console.log(fragments, "m3u8 parsered!")
    },
    onchange(total, current, fragment) {
        console.log(`downloading... [${current + 1}/${total}]\r`)
    },
    parser(fragment, index) {
        // custom parser ...
        return fragment
    },
})

🔧Options

options

  • *input[String]: indicates the url or local file of the m3u8 file to be converted

  • concurrency[Number]: concurrency max number, default: 1

  • path[String]: save path after conversion. Default: Execution root path. process.cwd()

  • name[String]: indicates the converted file name (including the suffix). Default: "execute timestamp.mp4". new Date().getTime() + ".mp4"

  • tempDir[String]: indicates the temporary save path for ts chips. Default: m3u8-converproject root path. path.resolve(__dirname, "../", ".temp"),

  • encodeSuffix[String]: indicates the suffix of an undecrypted ts slice. The default is ".encode".

  • decodeSuffix[String]: decrypted or undecrypted ts slice suffix. Default: ".ts"

  • clear[Boolean]: Specifies whether to execute only the clear cache, default: false.

  • requestOptions[httpOption]: http[s] request options, default: {}. The following are common configurations. See more detailsgot-options.

    • method[String]: Request method, default: "GET"
    • headers[Object]: Request header information
    • timeout[Object]: Configure the request timeout. See detailsgot-timeout
    • body[String|Buffer|Stream|Generator|AsyncGenerator|FormData|undefined]: The request body is generally used with headers["Content-Type"]
  • parsered(fragments)[Function]: A callback function that is triggered when the solution is complete

    • fragments[Array]: indicates information about all fragments after resolution
  • onchange(total, current, fragment)[Function]: A callback function that is triggered when a fragment is downloaded

    • total[Number]: indicates the fragment total number
    • current[Number]: indicates the current index
    • fragment[Object]: indicates information about the current ts fragment
  • downloaded()[Function]: Callback after downloading all fragment information

  • parser(fragment, index)[Function]: custom parser, When this parameter is used, the internal parser will not execute, see [Custom parser](#Custom parser)

    • fragment[Object]: indicates information about the current ts fragment
    • index[Number]: indicates the current index

✏️Advanced

Custom-parser

In the vast majority of cases, you only need to use the standard m3u8 parser we provide without knowing its internal implementation.

In order to provide a more flexible way to use, we provide custom parsers to meet different needs.

Parser parameters

The parser must be a function whose 'this' points to an instance of Origin, and if you use an arrow function, then you won't be able to access its internal properties (if you don't need them, you can ignore them). It takes two arguments' fragment 'and' index '(the index of the currently executed fragment).

fragment

  • fragment[Object]: indicates all parsed ts fragments
    • duration[Number]: indicates the duration of the segment
    • uri[String]: uri link of the fragment
    • key[Object]: indicates an encryption parameter. If no, it indicates that no encryption is performed
      • key.method[String]: indicates the encryption method
      • key.uri[String]: uri link of the encrypted key
      • key.iv[ArrayBuffer]: indicates the encrypted iv
      • key.key[ArrayBuffer]: indicates the encrypted key content. The value is obtained by the uri
    • encryption[Boolean]: Specifies whether the segment is encrypted. The default value is false
    • timeline[Number]: Timeline

Parser return value

The parser must return a fragment object, and if there is an encryption argument (fragment.key), it must ensure its correctness.

Because when you use a custom parser, our parser will not execute, and the returned fragment will be used as the basis for subsequent decryption and download of ts fragment. If the encryption parameter of 'fragment' returned is incorrect, the m3u8 file cannot be successfully converted. If there is no encryption parameter, ignore it.

Example

The following example is a partial implementation of our parser, which you can use as a reference to implement your own parser.

import mconver from "m3u8-conver"
import got from "got" // or use other network request tools
import url from "url"
import { detectAesMode, isWebLink } from "m3u8-conver/dist/utilities.js"

await mconver({
    url: "https://www.test.com",
    parser
})

async function parser(fragment, index) {
    console.log("useing custom parser!")
    const uriIsWebLink = isWebLink(fragment.uri);
    if (this.model === 'Local' && !uriIsWebLink) {
        throw new Error("The download link is missing the host, please try using url mode!");
    }
    // fragment uri has portcol?
    fragment.uri = uriIsWebLink ? fragment.uri : url.resolve(this.options.input, fragment.uri);
    // if fragment not key, this not's encryption
    const key = Object.assign({}, fragment.key);
    if (!key || Object.keys(key).length === 0) {
        return fragment;
    }
    if (!key.uri || !key.iv) {
        throw new Error("The fragment encryption key or iv is missing the download link!");
    }
    // next all encryption is true
    key.uri = isWebLink(key.uri) ? key.uri : url.resolve(fragment.uri, key.uri);
    if (this.cache.get(key.uri)) {
        // Using the key to identify the real encryption mode
        // be confined to AES-128-CBC | AES-192-CBC | AES-256-CBC, default AES-128-CBC
        key.key = this.cache.get(key.uri);
    } else {
        const keyResponse = await got(key.uri, { ...this.options.requestOptions, responseType: "buffer" });
        const keyBuffer = Buffer.isBuffer(keyResponse.body.buffer) ? keyResponse.body.buffer : Buffer.from(keyResponse.body.buffer);
        this.cache.set(key.uri, keyBuffer);
        key.key = keyBuffer;
    }
    if (key.key) {
        key.method = detectAesMode(key.key);
    } else {
        key.method = "AES-128-CBC";
    }
    // reset fragment.key
    fragment.key = key;
    return fragment;
}