hlsdownloader 2.2.2 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +5 -18
- package/README.md +167 -56
- package/build/index.js +1 -0
- package/package.json +72 -69
- package/.snyk +0 -10
- package/index.js +0 -1
package/LICENSE
CHANGED
|
@@ -1,22 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright
|
|
3
|
+
Copyright 2023 Nur Rony
|
|
4
4
|
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
11
6
|
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
22
8
|
|
|
9
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,21 +1,93 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
Downloads
|
|
1
|
+
<div align="center">
|
|
2
|
+
<a href="https://nurrony.github.io/hlsdownloader/" alt="HLSDownloader Logo"><img alt="HLSDownloader" style="height: 200px; width: 200px;border-radius: 5px;" src="./assets/logo.png" /></a><br />
|
|
3
|
+
</div>
|
|
4
|
+
|
|
5
|
+
<p align="center" style="font-size: 18px;">
|
|
6
|
+
Downloads HLS Playlist file and TS chunks. You can use it for content pre-fetching from CDN to Edge Server for your end viewers.
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<p align="center" style="font-size: 18px;">
|
|
10
|
+
<a href="https://www.npmjs.com/package/hlsdownloader"><b>NPM</b></a> • <a href="https://nurrony.github.io/hlsdownloader/"><b>Documentation</b></a> • <a href="https://github.com/nurrony/hlsdownloader"><b>GitHub</b></a>
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
<div align="center">
|
|
14
|
+
<a href="https://www.npmjs.com/package/hlsdownloader" target="_blank">
|
|
15
|
+
<img alt="Version" src="https://img.shields.io/npm/v/hlsdownloader.svg?style=flat-square">
|
|
16
|
+
</a>
|
|
17
|
+
<a href="https://www.npmjs.com/package/hlsdownloader" target="_blank">
|
|
18
|
+
<img src="https://img.shields.io/badge/node-%3E%3D18-blue.svg?style=flat-square" />
|
|
19
|
+
</a>
|
|
20
|
+
<a href="https://nurrony.github.io/hlsdownloader" target="_blank">
|
|
21
|
+
<img alt="Documentation" src="https://img.shields.io/badge/documentation-yes-brightgreen.svg?style=flat-square" />
|
|
22
|
+
</a>
|
|
23
|
+
<a href="https://codecov.io/gh/nurrony/hlsdownloader" >
|
|
24
|
+
<img src="https://codecov.io/gh/nurrony/hlsdownloader/graph/badge.svg?token=er50RqLH6T?style=flat-square"/>
|
|
25
|
+
</a>
|
|
26
|
+
<a href="https://github.com/nurrony/hlsdownloader/graphs/commit-activity" target="_blank">
|
|
27
|
+
<img alt="Maintenance" src="https://img.shields.io/badge/Maintained%3F-yes-green.svg?style=flat-square" />
|
|
28
|
+
</a>
|
|
29
|
+
<a href="https://github.com/nurrony/hlsdownloader/blob/master/LICENSE" target="_blank">
|
|
30
|
+
<img alt="License: MIT" src="https://img.shields.io/github/license/nurrony/hlsdownloader?style=flat-square" />
|
|
31
|
+
</a>
|
|
32
|
+
<a href="https://npmjs.com/package/hlsdownloader" target="_blank">
|
|
33
|
+
<img alt="Semver: Badge" src="https://img.shields.io/badge/%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079?style=flat-square" />
|
|
34
|
+
</a>
|
|
35
|
+
<a href="https://npm-stat.com/charts.html?package=hlsdownloader" target="_blank">
|
|
36
|
+
<img alt="Downloads: HLSDownloader" src="https://img.shields.io/npm/dm/hlsdownloader.svg?style=flat-square" />
|
|
37
|
+
</a>
|
|
38
|
+
<br /> <br />
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
> ⚠️
|
|
42
|
+
> <strong>This package is native [ESM](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) and no longer provides a CommonJS export. If your project uses CommonJS, you will have to [convert to ESM](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c). Please don't open issues for questions regarding CommonJS / ESM.</strong>
|
|
43
|
+
|
|
44
|
+
> ⚠️
|
|
45
|
+
> <strong>HLSDownloader `v2.x.x` is no longer maintained and we will not accept any backport requests.</strong>
|
|
46
|
+
|
|
47
|
+
## Table of Contents
|
|
48
|
+
|
|
49
|
+
- [Features](#features)
|
|
50
|
+
- [Prerequisites](#prerequisites)
|
|
51
|
+
- [Installation](#installation)
|
|
52
|
+
- [How to use](#how-to-use)
|
|
53
|
+
- [Advance Usage](#advance-usage)
|
|
54
|
+
- [Running Tests](#running-tests)
|
|
55
|
+
- [Generate Documentations](#generate-documentations)
|
|
56
|
+
- [Authors](#authors)
|
|
57
|
+
- [Contributing](#contributing)
|
|
58
|
+
- [Show your support](#show-your-support)
|
|
59
|
+
- [Special Thanks to](#special-thanks-to)
|
|
60
|
+
- [License](#license)
|
|
61
|
+
|
|
62
|
+
## Features
|
|
63
|
+
|
|
64
|
+
- Retryable
|
|
65
|
+
- Promise Based
|
|
66
|
+
- Support for HTTP/2
|
|
67
|
+
- Overwrite protection
|
|
68
|
+
- Support for custom HTTP Headers
|
|
69
|
+
- Support for custom HTTP Client
|
|
70
|
+
- Bring your own progress bar during download
|
|
71
|
+
- Concurrent download segments with multiple http connections
|
|
72
|
+
|
|
73
|
+
## Prerequisites
|
|
74
|
+
|
|
75
|
+
- node >=18.x.x
|
|
7
76
|
|
|
8
77
|
## Installation
|
|
9
78
|
|
|
10
|
-
|
|
79
|
+
It is pretty straight forward
|
|
11
80
|
|
|
12
81
|
```sh
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
82
|
+
# using npm
|
|
83
|
+
npm install --save hlsdownloader
|
|
84
|
+
# or with yarn
|
|
85
|
+
yarn add hlsdownloader
|
|
86
|
+
# or pnpm
|
|
87
|
+
pnpm install hlsdownloader
|
|
16
88
|
```
|
|
17
89
|
|
|
18
|
-
##
|
|
90
|
+
## How to use
|
|
19
91
|
|
|
20
92
|
`destination` field is optional. If `destination` is not provided it just fetches the content from origin.
|
|
21
93
|
It can also be useful if you want to do content pre-fetching from CDN for your end viewers. If any `TS` or `m3u8`
|
|
@@ -24,67 +96,106 @@ variant download is failed it continues downloading others and reports after fin
|
|
|
24
96
|
It's simple as below.
|
|
25
97
|
|
|
26
98
|
```js
|
|
27
|
-
import HLSDownloader from 'hlsdownloader'
|
|
28
|
-
//var HLSDownloader = require('hlsdownloader').downloader; //using commonJS module
|
|
99
|
+
import HLSDownloader from 'hlsdownloader';
|
|
29
100
|
|
|
30
|
-
const
|
|
101
|
+
const options = {
|
|
31
102
|
playlistURL: 'http://example.com/path/to/your/playlist.m3u8', // change it
|
|
32
|
-
destination: '/tmp' // change it (optional
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
103
|
+
destination: '/tmp', // change it (optional: default '')
|
|
104
|
+
concurrency: 10, // change it (optional: default = 1),
|
|
105
|
+
overwrite: true, // change it (optional: default = false)
|
|
106
|
+
};
|
|
107
|
+
const downloader = new HLSDownloader(options);
|
|
108
|
+
downloader.startDownload().then(response => console.log(response));
|
|
36
109
|
```
|
|
37
110
|
|
|
38
|
-
|
|
111
|
+
> ℹ️ Check [example.js](example.js) for working example
|
|
39
112
|
|
|
40
113
|
```js
|
|
41
|
-
//on success
|
|
114
|
+
// on success
|
|
42
115
|
{
|
|
43
|
-
|
|
44
|
-
playlistURL: 'your playlist url'
|
|
116
|
+
total: <number>,
|
|
117
|
+
playlistURL: 'your playlist url'
|
|
118
|
+
message: 'Downloaded successfully',
|
|
45
119
|
}
|
|
46
|
-
|
|
120
|
+
|
|
121
|
+
// on partial download
|
|
47
122
|
{
|
|
48
|
-
|
|
49
|
-
playlistURL: 'your playlist url',
|
|
50
|
-
|
|
123
|
+
total: <number>,
|
|
124
|
+
playlistURL: 'your playlist url',
|
|
125
|
+
message: 'Download done with some errors',
|
|
126
|
+
errors: [
|
|
127
|
+
{
|
|
128
|
+
name: 'InvalidPlaylist',
|
|
129
|
+
message: 'Playlist parsing is not successful'
|
|
130
|
+
url: 'https://cnd.hls-server.test/playlist.m3u8'
|
|
131
|
+
}
|
|
132
|
+
] // items url that is skipped or could not downloaded for error
|
|
51
133
|
}
|
|
52
134
|
```
|
|
53
135
|
|
|
54
136
|
## Advance Usage
|
|
55
137
|
|
|
56
|
-
|
|
138
|
+
HLSDownloader supports all [Ky API](https://github.com/sindresorhus/ky?tab=readme-ov-file#api) except these options given below
|
|
57
139
|
|
|
58
|
-
- method
|
|
59
140
|
- uri
|
|
60
141
|
- url
|
|
61
|
-
- transform
|
|
62
|
-
- resolveWithFullResponse
|
|
63
|
-
- baseUrl
|
|
64
142
|
- json
|
|
65
143
|
- form
|
|
66
|
-
-
|
|
67
|
-
-
|
|
68
|
-
-
|
|
69
|
-
-
|
|
70
|
-
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
144
|
+
- body
|
|
145
|
+
- method
|
|
146
|
+
- setHost
|
|
147
|
+
- isStream
|
|
148
|
+
- parseJson
|
|
149
|
+
- prefixUrl
|
|
150
|
+
- cookieJar
|
|
151
|
+
- playlistURL
|
|
152
|
+
- concurrency
|
|
153
|
+
- allowGetBody
|
|
154
|
+
- stringifyJson
|
|
155
|
+
- methodRewriting
|
|
156
|
+
|
|
157
|
+
It also disable retry failed request that you can easily override
|
|
158
|
+
|
|
159
|
+
## Running Tests
|
|
160
|
+
|
|
161
|
+
```sh
|
|
162
|
+
npm test
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
To run it on watch mode
|
|
166
|
+
|
|
167
|
+
```sh
|
|
168
|
+
npm run test:watch
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Generate Documentations
|
|
172
|
+
|
|
173
|
+
```sh
|
|
174
|
+
npm docs:gen
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Authors
|
|
178
|
+
|
|
179
|
+
👤 **Nur Rony**
|
|
180
|
+
|
|
181
|
+
- Website: [nurrony.github.io](https://nurrony.github.io)
|
|
182
|
+
- Twitter: [@nmrony](https://twitter.com/nmrony)
|
|
183
|
+
- Github: [@nurrony](https://github.com/nurrony)
|
|
184
|
+
- LinkedIn: [@nmrony](https://linkedin.com/in/nmrony)
|
|
185
|
+
|
|
186
|
+
## Contributing
|
|
187
|
+
|
|
188
|
+
Contributions, issues and feature requests are welcome!<br />Feel free to check [issues page](https://github.com/nurrony/hlsdownloader/issues). You can also take a look at the [contributing guide](https://github.com/nurrony/hlsdownloader/blob/master/CONTRIBUTING.md).
|
|
189
|
+
|
|
190
|
+
## Show your support
|
|
191
|
+
|
|
192
|
+
Give a ⭐️ if this project helped you!. I will be grateful if you all help me to improve this package by giving your suggestions, feature request and pull requests. I am all ears!!
|
|
193
|
+
|
|
194
|
+
## Special Thanks to
|
|
195
|
+
|
|
196
|
+
- [Ky Team](https://www.npmjs.com/package/ky)
|
|
197
|
+
|
|
198
|
+
## License
|
|
199
|
+
|
|
200
|
+
Copyright © 2023 [Nur Rony](https://github.com/nurrony).<br />
|
|
201
|
+
This project is [MIT](https://github.com/nurrony/hlsdownloader/blob/master/LICENSE) licensed.
|
package/build/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var R=Object.defineProperty;var a=(e,t)=>R(e,"name",{value:t,configurable:!0});import{createWriteStream as k}from"fs";import{access as x,constants as D,mkdir as E,unlink as v}from"fs/promises";import u from"ky";import U from"p-limit";import{dirname as I,join as f}from"path";import{Readable as C}from"stream";import{URL as T}from"url";var m=class extends Error{static{a(this,"InvalidPlaylist")}constructor(t){super(t),this.name=this.constructor.name,Error.captureStackTrace(this,this.constructor)}},p=m;var d=class extends Error{static{a(this,"ProtocolNotSupported")}constructor(t){super(t),this.name=this.constructor.name,Error.captureStackTrace(this,this.constructor)}},y=d;var P=a((e,t=["http:","https:","ftp:","sftp:"])=>{try{let{protocol:s}=new URL(e);if(s&&!t.includes(`${s}`))throw new y(`${s} is not supported. Supported protocols are ${t.join(", ")}`);return!0}catch(s){throw s}},"isValidUrl"),g=a(e=>e.substring(0,1).replace("/","")+e.substring(1),"stripFirstSlash"),b=a(e=>e.match(/^#EXTM3U/im)!==null,"isValidPlaylist"),O=a(e=>new URL(e),"parseUrl"),S=a((e,...t)=>{let s=new Set(t.flat());return Object.fromEntries(Object.entries(e).filter(([r])=>!s.has(r)))},"omit");var l={isValidPlaylist:b,isValidUrl:P,omit:S,parseUrl:O,stripFirstSlash:g};var V=".m3u8",w=class e{static{a(this,"Downloader")}static defaultKyOptions={retry:{limit:0}};pool=U(1);overwrite=!1;static unSupportedOptions=["uri","url","json","form","body","method","setHost","isStream","parseJson","prefixUrl","cookieJar","playlistURL","concurrency","allowGetBody","stringifyJson","methodRewriting"];items=[];errors=[];concurrency=1;kyOptions={};playlistURL="";destination="";constructor({playlistURL:t,destination:s,concurrency:r=1,overwrite:i,...n}={options:{},playlistURL:"",concurrency:1,destination:"",overwrite:!1}){try{l.isValidUrl(t),this.items=[t],this.playlistURL=t,this.concurrency=r,this.overwrite=i??!1,this.destination=s??"",this.pool=U(r??1),this.kyOptions=this.mergeOptions(n),this.fetchItems=this.fetchItems.bind(this),this.downloadItem=this.downloadItem.bind(this),this.mergeOptions=this.mergeOptions.bind(this),this.fetchPlaylist=this.fetchPlaylist.bind(this),this.startDownload=this.startDownload.bind(this),this.downloadItems=this.downloadItems.bind(this),this.shouldOverwrite=this.shouldOverwrite.bind(this),this.createDirectory=this.createDirectory.bind(this),this.parsePlaylist=this.parsePlaylist.bind(this),this.processPlaylistItems=this.processPlaylistItems.bind(this),this.formatPlaylistContent=this.formatPlaylistContent.bind(this)}catch(h){throw h}}async startDownload(){let{url:t,body:s}=await this.fetchPlaylist(this.playlistURL);if(this.errors.length>0)return{errors:this.errors,message:"Unsuccessful download"};let r=this.parsePlaylist(t,s);this.items=[...this.items,...r];let i=r.filter(o=>o.toLowerCase().endsWith(V)),n=await Promise.allSettled(i.map(o=>this.fetchPlaylist(o)));return r=this.formatPlaylistContent(n).map(o=>this.parsePlaylist(o?.url,o?.body)).flat(),this.items=[...this.items,...r],await this.processPlaylistItems(),this.errors.length>0?{errors:this.errors,total:this.items.length,message:"Download ended with some errors"}:{total:this.items.length,playlistURL:this.playlistURL,message:"Downloaded successfully"}}mergeOptions(t){return Object.assign(e.defaultKyOptions,l.omit(t,...e.unSupportedOptions))}parsePlaylist(t,s){return s.replace(/^#[\s\S].*/gim,"").split(/\r?\n/).reduce((r,i)=>{if(i!==""){let n=new T(i,t).href;r.push(n)}return r},[])}async fetchPlaylist(t){try{let s=await u.get(t,{...this.kyOptions}).text();if(!l.isValidPlaylist(s)){let{name:r,message:i}=new p("Invalid playlist");return this.errors.push({url:t,name:r,message:i}),{url:"",body:""}}return{url:t,body:s}}catch({name:s,message:r}){return this.errors.push({url:t,name:s,message:r}),{url:"",body:""}}}formatPlaylistContent(t){return t.reduce((s,{status:r,value:i})=>(r.toLowerCase()==="fulfilled"&&i&&s.push(i),s),[])}async processPlaylistItems(){return this.destination&&this.downloadItems()||this.fetchItems()}async downloadItem(t){try{let s=await u.get(t,{...this.kyOptions}),r=await this.createDirectory(t),i=C.fromWeb(s.body);return new Promise((n,h)=>{let o=k(r);i.pipe(o),i.on("error",c=>{i.destroy(),o.destroy(),v(r),h(c)}),o.on("finish",()=>{o.close(),n("success")}),o.on("error",c=>{o.destroy(),i.destroy(),h(c)})})}catch({name:s,message:r}){this.errors.push({name:s,message:r,url:t})}}async downloadItems(){try{if(!await this.shouldOverwrite(this.playlistURL)){let s=new Error("directory already exists");throw s.name="EEXIST",s}await this.createDirectory(this.playlistURL);let t=this.items.map(s=>this.pool(this.downloadItem,s));return Promise.allSettled(t)}catch(t){this.errors.push({url:this.playlistURL,name:t.name,message:t.message})}}async fetchItems(){return Promise.allSettled(this.items.map(t=>this.pool(async()=>{try{return await u.get(t,{...this.kyOptions})}catch({name:s,message:r}){this.errors.push({url:t,name:s,message:r})}})))}async createDirectory(t){let{pathname:s}=l.parseUrl(t),r=f(this.destination,I(s));return await E(r,{recursive:!0}),f(this.destination,l.stripFirstSlash(s))}async shouldOverwrite(t){try{let{pathname:s}=l.parseUrl(t),r=f(this.destination,I(s));return await x(r,D.F_OK),this.overwrite}catch(s){if(s.code==="ENOENT")return!0;throw s}}},L=w;var at=L;export{at as default};
|
package/package.json
CHANGED
|
@@ -1,87 +1,90 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
|
|
2
|
+
"author": {
|
|
3
|
+
"name": "Nur Rony",
|
|
4
|
+
"email": "pro.nmrony@gmail.com",
|
|
5
|
+
"url": "https://nurrony.github.io"
|
|
6
|
+
},
|
|
7
|
+
"bugs": {
|
|
8
|
+
"url": "https://github.com/nurrony/hlsdownloader/issues"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
4
11
|
"description": "Downloads HLS Playlist file and TS chunks",
|
|
5
|
-
"main": "index.js",
|
|
6
12
|
"engines": {
|
|
7
|
-
"node": ">=
|
|
8
|
-
|
|
9
|
-
"scripts": {
|
|
10
|
-
"prepublishOnly": "npm run build",
|
|
11
|
-
"build": "npm run compile && npm run uglify && npm run clean",
|
|
12
|
-
"clean": "rm -f hlsdownloader.es5.js",
|
|
13
|
-
"commit": "git-cz",
|
|
14
|
-
"compile": "babel hlsdownloader.js --out-file hlsdownloader.es5.js",
|
|
15
|
-
"example": "npm run build && node example",
|
|
16
|
-
"lint": "eslint . && echo 'No linting error has found cool all fine 😎'",
|
|
17
|
-
"lint:fix": "eslint 'hlsdownloader.js' --fix",
|
|
18
|
-
"test": "mocha --require @babel/register --recursive",
|
|
19
|
-
"code:quality": "npm run lint && npm run lint:fix && npm run test",
|
|
20
|
-
"coverage": "NODE_ENV=test nyc -x .babelrc.js -x test --reporter=lcov --reporter=text npm test",
|
|
21
|
-
"uglify": "uglifyjs --compress --mangle -o index.js -- hlsdownloader.es5.js",
|
|
22
|
-
"travis-deploy-once": "travis-deploy-once",
|
|
23
|
-
"semantic-release": "semantic-release",
|
|
24
|
-
"snyk-protect": "snyk protect",
|
|
25
|
-
"prepare": "npm run snyk-protect"
|
|
13
|
+
"node": ">=18",
|
|
14
|
+
"npm": ">=9"
|
|
26
15
|
},
|
|
16
|
+
"homepage": "https://nurrony.github.io/hlsdownloader",
|
|
27
17
|
"keywords": [
|
|
28
|
-
"hlsdownloader",
|
|
29
|
-
"m3u8downloader",
|
|
30
|
-
"m3u8",
|
|
31
18
|
"HLS",
|
|
32
|
-
"
|
|
19
|
+
"m3u8",
|
|
20
|
+
"playlist",
|
|
21
|
+
"downloader",
|
|
22
|
+
"hlsdownloader",
|
|
23
|
+
"m3u8downloader"
|
|
24
|
+
],
|
|
25
|
+
"files": [
|
|
26
|
+
"build"
|
|
33
27
|
],
|
|
34
|
-
"
|
|
28
|
+
"exports": {
|
|
29
|
+
"default": "./build/index.js"
|
|
30
|
+
},
|
|
35
31
|
"license": "MIT",
|
|
32
|
+
"name": "hlsdownloader",
|
|
36
33
|
"repository": {
|
|
37
|
-
"type": "git",
|
|
38
|
-
"url": "https://github.com/
|
|
34
|
+
"type": "git+https",
|
|
35
|
+
"url": "git+https://github.com/nurrony/hlsdownloader.git"
|
|
39
36
|
},
|
|
40
|
-
"
|
|
41
|
-
"
|
|
37
|
+
"scripts": {
|
|
38
|
+
"semantic-release": "semantic-release",
|
|
39
|
+
"prepublishOnly": "npm run build",
|
|
40
|
+
"build": "npm run lint:fix && npm run lint && npm test && npm run build:clean && npm run compile && echo '📦 Build artifact has been generated successfully.'",
|
|
41
|
+
"coverage": "NODE_OPTIONS=--experimental-vm-modules npx jest --coverage",
|
|
42
|
+
"prepare": "husky install",
|
|
43
|
+
"build:clean": "rimraf -fr build && echo '🧹 Build artifacts has been cleaned.'",
|
|
44
|
+
"compile": "npx esbuild --outdir=build --platform=node --format=esm --target=node18 --packages=external --bundle --minify --tree-shaking=true --keep-names src/index.js",
|
|
45
|
+
"docs:gen": "npm run docs:clean && NODE_OPTIONS=--experimental-vm-modules jsdoc -c jsdoc.json && echo '📄 Docs has been generated successfully.'",
|
|
46
|
+
"docs:clean": "rimraf -fr ./docs && echo '🧹 All docs has been cleaned.'",
|
|
47
|
+
"lint": "eslint . --ext .js && echo '💄 Coding style guideline has been followed properly.'",
|
|
48
|
+
"lint:fix": "eslint . --ext .js --fix && echo '🔧 Coding style has been fixed as per guideline.'",
|
|
49
|
+
"prod:start": "node index.js",
|
|
50
|
+
"test:coverage:clean": "rimraf -fr ./coverage && echo '🧹 All test coverage reports has been cleaned.'",
|
|
51
|
+
"test": "npm run test:coverage:clean && NODE_OPTIONS=--experimental-vm-modules npx jest",
|
|
52
|
+
"example": "NODE_OPTIONS=--experimental-vm-modules node example.mjs",
|
|
53
|
+
"test:watch": "NODE_OPTIONS=--experimental-vm-modules npx jest --no-cache --watch",
|
|
54
|
+
"version": "echo $npm_package_version",
|
|
55
|
+
"commitlint": "NODE_OPTIONS=--experimental-vm-modules npx commitlint --edit && echo '🔖 Commit message guidelines are followed properly.'"
|
|
42
56
|
},
|
|
43
|
-
"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"pre-commit": "npm run code:quality"
|
|
57
|
+
"config": {
|
|
58
|
+
"commitizen": {
|
|
59
|
+
"path": "cz-conventional-changelog"
|
|
47
60
|
}
|
|
48
61
|
},
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
"request": "^2.88.0",
|
|
55
|
-
"request-promise": "^4.2.4",
|
|
56
|
-
"snyk": "^1.189.0"
|
|
62
|
+
"release": {
|
|
63
|
+
"debug": true,
|
|
64
|
+
"branches": [
|
|
65
|
+
"master"
|
|
66
|
+
]
|
|
57
67
|
},
|
|
68
|
+
"version": "3.0.0",
|
|
58
69
|
"devDependencies": {
|
|
59
|
-
"@
|
|
60
|
-
"@
|
|
61
|
-
"@
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"eslint-plugin-node": "^8.0.1",
|
|
72
|
-
"eslint-plugin-promise": "^4.1.1",
|
|
73
|
-
"eslint-plugin-standard": "^4.0.0",
|
|
74
|
-
"husky": "^1.3.1",
|
|
75
|
-
"mocha": "^6.1.4",
|
|
76
|
-
"nyc": "^14.0.0",
|
|
77
|
-
"semantic-release": "^15.13.12",
|
|
78
|
-
"travis-deploy-once": "^5.0.11",
|
|
79
|
-
"uglify-es": "^3.3.9"
|
|
70
|
+
"@commitlint/cli": "^18.4.3",
|
|
71
|
+
"@commitlint/config-conventional": "^18.4.3",
|
|
72
|
+
"@types/jest": "^29.5.11",
|
|
73
|
+
"clean-jsdoc-theme": "^4.2.17",
|
|
74
|
+
"esbuild": "^0.19.9",
|
|
75
|
+
"eslint": "^8.56.0",
|
|
76
|
+
"husky": "^8.0.0",
|
|
77
|
+
"jest": "^29.7.0",
|
|
78
|
+
"jsdoc": "^4.0.2",
|
|
79
|
+
"prettier": "^3.1.1",
|
|
80
|
+
"rimraf": "^5.0.5",
|
|
81
|
+
"semantic-release": "22.0.12"
|
|
80
82
|
},
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
"path": "./node_modules/cz-conventional-changelog"
|
|
84
|
-
}
|
|
83
|
+
"hooks": {
|
|
84
|
+
"pre-commit": "npm run commitlint ${1} && npm run lint:fix && npm run lint && npm test"
|
|
85
85
|
},
|
|
86
|
-
"
|
|
86
|
+
"dependencies": {
|
|
87
|
+
"ky": "^1.1.3",
|
|
88
|
+
"p-limit": "^5.0.0"
|
|
89
|
+
}
|
|
87
90
|
}
|
package/.snyk
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
|
|
2
|
-
version: v1.13.5
|
|
3
|
-
ignore: {}
|
|
4
|
-
# patches apply the minimum changes required to fix a vulnerability
|
|
5
|
-
patch:
|
|
6
|
-
SNYK-JS-LODASH-450202:
|
|
7
|
-
- async > lodash:
|
|
8
|
-
patched: '2019-07-03T23:05:13.467Z'
|
|
9
|
-
- request-promise > request-promise-core > lodash:
|
|
10
|
-
patched: '2019-07-03T23:05:13.467Z'
|
package/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=exports.downloader=void 0,require("core-js/modules/es6.object.to-string");var _each=_interopRequireDefault(require("async/each")),_fs=_interopRequireDefault(require("fs")),_mkdirp=_interopRequireDefault(require("mkdirp")),_path=require("path"),_requestPromise=_interopRequireDefault(require("request-promise")),_url=require("url");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function _objectSpread(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{},s=Object.keys(r);"function"==typeof Object.getOwnPropertySymbols&&(s=s.concat(Object.getOwnPropertySymbols(r).filter(function(e){return Object.getOwnPropertyDescriptor(r,e).enumerable}))),s.forEach(function(t){_defineProperty(e,t,r[t])})}return e}function _defineProperty(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function _objectWithoutProperties(e,t){if(null==e)return{};var r,s,i=_objectWithoutPropertiesLoose(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(s=0;s<o.length;s++)r=o[s],t.indexOf(r)>=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(i[r]=e[r])}return i}function _objectWithoutPropertiesLoose(e,t){if(null==e)return{};var r,s,i={},o=Object.keys(e);for(s=0;s<o.length;s++)r=o[s],t.indexOf(r)>=0||(i[r]=e[r]);return i}function isValidPlaylist(e){return null!==e.match(/^#EXTM3U/im)}function validateURL(e){return new RegExp("^(?:(?:https?)://)(?:\\S+(?::\\S*)?@)?(?:(?!(?:10|127)(?:\\.\\d{1,3}){3})(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))(?::\\d{2,5})?(?:/\\S*)?$","i").test(e)}function stripFirstSlash(e){return e.substr(0,1).replace("/","")+e.substring(1)}class HLSDownloader{constructor(e={}){let{playlistURL:t="",destination:r=null,method:s,uri:i,url:o,transform:a,resolveWithFullResponse:l,baseUrl:n,form:u,formData:d,preambleCRLF:p,postambleCRLF:h,json:f,jsonReviver:c,jsonReplacer:m}=e,y=_objectWithoutProperties(e,["playlistURL","destination","method","uri","url","transform","resolveWithFullResponse","baseUrl","form","formData","preambleCRLF","postambleCRLF","json","jsonReviver","jsonReplacer"]);if(!validateURL(t)){const e=new Error;throw e.message="playListURL is required or check if your URL is valid or not!!",e.name="ERR_VALIDATION",e}this.playlistURL=t,this.destination=r,this.options=y,this.hostName=(0,_url.parse)(t,!0,!0).hostname,this.items=[],this.errors=[]}startDownload(e){return this.getPlaylist(e)}getPlaylist(e){const t=_objectSpread({},this.options,{method:"GET",uri:this.playlistURL});(0,_requestPromise.default)(t).then(t=>{if(!isValidPlaylist(t))return e(new Error("This playlist isn't a valid m3u8 playlist"));this.items.push(this.playlistURL),this.parseMasterPlaylist(t,e)}).catch(t=>{if(t){const r=new Error("VariantDownloadError");return r.statusCode=t.statusCode,r.uri=t.options.uri,e(r)}})}parseMasterPlaylist(e,t){if(e.match(/^#EXT-X-TARGETDURATION:\d+/im))this.parseVariantPlaylist(e),this.downloadItems(t);else try{const r=e.replace(/^#[\s\S].*/gim,"").split("\n").filter(e=>""!==e);let s=0;const i=r.length;(0,_each.default)(r,(e,t)=>{const r=(0,_url.resolve)(this.playlistURL,e),o=_objectSpread({},this.options,{method:"GET",uri:r});(0,_requestPromise.default)(o).then(e=>{if(isValidPlaylist(e))return this.items.push(r),this.parseVariantPlaylist(e),t(null)}).catch(e=>(this.errors.push(e.options.uri),e&&++s===i?t(!0):t(null)))},e=>e?t({playlistURL:this.playlistURL,message:"No valid Downloadable variant exists in master playlist"}):this.downloadItems(t))}catch(e){return t(e)}}parseVariantPlaylist(e){const t=e.replace(/^#[\s\S].*/gim,"").split("\n").filter(e=>""!==e).map(e=>(0,_url.resolve)(this.playlistURL,e));this.items=this.items.concat(t)}downloadItems(e){(0,_each.default)(this.items,(e,t)=>{const r=_objectSpread({},this.options,{method:"GET",uri:e});(0,_requestPromise.default)(r).then(r=>null!==this.destination&&""!==this.destination&&"undefined"!==this.destination?this.createItems(e,r,t):(r=null,t())).catch(e=>(this.errors.push(e.options.uri),t(null)))},t=>t?e({playlistURL:this.playlistURL,message:"Internal Server Error from remote"}):this.errors.length>0?e(null,{message:"Download done with some errors",playlistURL:this.playlistURL,errors:this.errors}):e(null,{message:"Downloaded successfully",playlistURL:this.playlistURL}))}createItems(e,t,r){const s=(0,_url.parse)(e).pathname,i=this.destination+(0,_path.dirname)(s),o=this.destination+"/"+stripFirstSlash(s);(0,_mkdirp.default)(i,e=>e?r(e):_fs.default.writeFile(o,t,r))}}const downloader=HLSDownloader;exports.downloader=downloader;var _default=HLSDownloader;exports.default=_default;
|