m3u8-conver v1.0.2
English|简体中文
📖Introduction
Convert network or local m3u8
files to media files (such as mp4
, avi
, etc.).
- Decode AES-123-CBC
- Decode AES-192-CBC
- Decode AES-256-CBC
- parser m3u8 localfile
- custom save format.ext
- concurrence download
- custom m3u8 parser
- 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 convertedconcurrency
Number: concurrency max number, default: 1path
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-conver
project 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: https request options, default: {}. The following are common configurations. See more detailsgot-options.method
String: Request method, default: "GET"headers
Object: Request header informationtimeout
Object: Configure the request timeout. See detailsgot-timeoutbody
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 completefragments
Array: indicates information about all fragments after resolution
onchange(total, current, fragment)
Function: A callback function that is triggered when a fragment is downloadeddownloaded()
Function: Callback after downloading all fragment informationparser(fragment, index)
Function: custom parser, When this parameter is used, the internal parser will not execute, see Custom parser(#Custom parser)
✏️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 fragmentsduration
Number: indicates the duration of the segmenturi
String: uri link of the fragmentkey
Object: indicates an encryption parameter. If no, it indicates that no encryption is performedkey.method
String: indicates the encryption methodkey.uri
String: uri link of the encrypted keykey.iv
ArrayBuffer: indicates the encrypted ivkey.key
ArrayBuffer: indicates the encrypted key content. The value is obtained by the uri
- encryptionBoolean: Specifies whether the segment is encrypted. The default value is false
- timelineNumber: 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;
}