hlsdownloader 2.2.3 → 3.0.1

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 CHANGED
@@ -1,22 +1,9 @@
1
- The MIT License (MIT)
1
+ MIT License
2
2
 
3
- Copyright (c) 2018 Nur Rony
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,102 @@
1
- # HLSDownloader
2
-
3
- [![Greenkeeper badge](https://badges.greenkeeper.io/nmrony/hlsdownloader.svg)](https://greenkeeper.io/)
4
- [![version][npm-version]][npm-url] [![coding style: standard][standard-svg]][standard-site] [![dependencies][npm-dependencies]][dep-status] [![devDependencies][npm-dev-dependencies]][devdep-status] [![Downloads][npm-total-downloads]][npm-url] [![Travis branch][travis-badge]][travis-url] [![semantic-release][semvarbadge]][npm-url]
5
-
6
- Downloads `m3u8` playlist and `TS` chunks for a given playlist URL.
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://github.com/nurrony/hlsdownloader/actions/workflows/test.yaml" target="_blank">
21
+ <img src=https://github.com/nurrony/hlsdownloader/actions/workflows/test.yaml/badge.svg?style=flat-square" />
22
+ </a>
23
+ <a href="https://nurrony.github.io/hlsdownloader" target="_blank">
24
+ <img alt="Documentation" src="https://img.shields.io/badge/documentation-yes-brightgreen.svg?style=flat-square" />
25
+ </a>
26
+ <a href="https://codecov.io/gh/nurrony/hlsdownloader" >
27
+ <img src="https://codecov.io/gh/nurrony/hlsdownloader/graph/badge.svg?token=er50RqLH6T?style=flat-square"/>
28
+ </a>
29
+ <a href="https://github.com/nurrony/hlsdownloader/graphs/commit-activity" target="_blank">
30
+ <img alt="Maintenance" src="https://img.shields.io/badge/Maintained%3F-yes-green.svg?style=flat-square" />
31
+ </a>
32
+ <a href="https://github.com/nurrony/hlsdownloader/blob/master/LICENSE" target="_blank">
33
+ <img alt="License: MIT" src="https://img.shields.io/github/license/nurrony/hlsdownloader?style=flat-square" />
34
+ </a>
35
+ <a href="https://npmjs.com/package/hlsdownloader" target="_blank">
36
+ <img alt="Semver: Badge" src="https://img.shields.io/badge/%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079?style=flat-square" />
37
+ </a>
38
+ <a href="https://npm-stat.com/charts.html?package=hlsdownloader" target="_blank">
39
+ <img alt="Downloads: HLSDownloader" src="https://img.shields.io/npm/dm/hlsdownloader.svg?style=flat-square" />
40
+ </a>
41
+ <a href="https://bundlephobia.com/package/hlsdownloader@latest" target="_blank">
42
+ <img alt="Min Bundle Size: HLSDownloader" src="https://img.shields.io/bundlephobia/minzip/hlsdownloader?style=flat-square" />
43
+ </a>
44
+ <a href="https://bundlephobia.com/package/hlsdownloader@latest" target="_blank">
45
+ <img alt="Min Bundle Size: HLSDownloader" src="https://img.shields.io/badge/dynamic/json?url=https://packagephobia.com/v2/api.json?p=hlsdownloader&query=$.install.pretty&label=install%20size&style=flat-square" />
46
+ </a>
47
+ <br /> <br />
48
+ </div>
49
+
50
+ > ⚠️
51
+ > <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>
52
+
53
+ > ⚠️
54
+ > <strong>HLSDownloader `v2.x.x` is no longer maintained and we will not accept any backport requests.</strong>
55
+
56
+ ## Table of Contents
57
+
58
+ - [Features](#features)
59
+ - [Prerequisites](#prerequisites)
60
+ - [Installation](#installation)
61
+ - [How to use](#how-to-use)
62
+ - [Advance Usage](#advance-usage)
63
+ - [Running Tests](#running-tests)
64
+ - [Generate Documentations](#generate-documentations)
65
+ - [Authors](#authors)
66
+ - [Contributing](#contributing)
67
+ - [Show your support](#show-your-support)
68
+ - [Special Thanks to](#special-thanks-to)
69
+ - [License](#license)
70
+
71
+ ## Features
72
+
73
+ - Retryable
74
+ - Promise Based
75
+ - Support for HTTP/2
76
+ - Overwrite protection
77
+ - Support for custom HTTP Headers
78
+ - Support for custom HTTP Client
79
+ - Bring your own progress bar during download
80
+ - Concurrent download segments with multiple http connections
81
+
82
+ ## Prerequisites
83
+
84
+ - node >=18.x.x
7
85
 
8
86
  ## Installation
9
87
 
10
- Install it via `npm` or `yarn`
88
+ It is pretty straight forward
11
89
 
12
90
  ```sh
13
- [sudo] npm install hlsdownloader --save
14
- # Or
15
- [sudo] yarn add hlsdownloader
91
+ # using npm
92
+ npm install --save hlsdownloader
93
+ # or with yarn
94
+ yarn add hlsdownloader
95
+ # or pnpm
96
+ pnpm install hlsdownloader
16
97
  ```
17
98
 
18
- ## Configuration
99
+ ## How to use
19
100
 
20
101
  `destination` field is optional. If `destination` is not provided it just fetches the content from origin.
21
102
  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 +105,106 @@ variant download is failed it continues downloading others and reports after fin
24
105
  It's simple as below.
25
106
 
26
107
  ```js
27
- import HLSDownloader from 'hlsdownloader' //Using ES2015 module
28
- //var HLSDownloader = require('hlsdownloader').downloader; //using commonJS module
108
+ import HLSDownloader from 'hlsdownloader';
29
109
 
30
- const params = {
110
+ const options = {
31
111
  playlistURL: 'http://example.com/path/to/your/playlist.m3u8', // change it
32
- destination: '/tmp' // change it (optional field)
33
- }
34
- const downloader = new HLSDownloader(params)
35
- downloader.startDownload((err, msg) => (err ? console.log(err) : console.log(msg)))
112
+ destination: '/tmp', // change it (optional: default '')
113
+ concurrency: 10, // change it (optional: default = 1),
114
+ overwrite: true, // change it (optional: default = false)
115
+ };
116
+ const downloader = new HLSDownloader(options);
117
+ downloader.startDownload().then(response => console.log(response));
36
118
  ```
37
119
 
38
- `msg` is an object with following properties
120
+ > ℹ️ Check [example.js](example.js) for working example
39
121
 
40
122
  ```js
41
- //on success
123
+ // on success
42
124
  {
43
- message: 'Downloaded successfully',
44
- playlistURL: 'your playlist url'
125
+ total: <number>,
126
+ playlistURL: 'your playlist url'
127
+ message: 'Downloaded successfully',
45
128
  }
46
- //on partial download
129
+
130
+ // on partial download
47
131
  {
48
- message: 'Download done with some errors',
49
- playlistURL: 'your playlist url',
50
- errors: [] // items url that is skipped or could not downloaded for error
132
+ total: <number>,
133
+ playlistURL: 'your playlist url',
134
+ message: 'Download done with some errors',
135
+ errors: [
136
+ {
137
+ name: 'InvalidPlaylist',
138
+ message: 'Playlist parsing is not successful'
139
+ url: 'https://cnd.hls-server.test/playlist.m3u8'
140
+ }
141
+ ] // items url that is skipped or could not downloaded for error
51
142
  }
52
143
  ```
53
144
 
54
145
  ## Advance Usage
55
146
 
56
- `HLSDownloader` accepts all parameters supported by [request-promise][request-promise] except these following **options**
147
+ HLSDownloader supports all [Ky API](https://github.com/sindresorhus/ky?tab=readme-ov-file#api) except these options given below
57
148
 
58
- - method
59
149
  - uri
60
150
  - url
61
- - transform
62
- - resolveWithFullResponse
63
- - baseUrl
64
151
  - json
65
152
  - form
66
- - formData
67
- - preambleCRLF
68
- - postambleCRLF
69
- - jsonReviver
70
- - jsonReplacer
71
-
72
- It helps you to do `Auth`, limit `concurrency` of download and other various tasks without changing your code and workflow.
73
-
74
- I will be grateful if you all help me to improve this package by giving your suggestions, feature request and
75
- pull requests. I am all ears!!
76
-
77
- [npm-badge]: https://nodei.co/npm/hlsdownloader.png?compact=true
78
- [npm-version]: https://img.shields.io/npm/v/hlsdownloader.svg?style=flat-square
79
- [npm-dependencies]: https://img.shields.io/david/nmrony/hlsdownloader.svg?style=flat-square
80
- [npm-dev-dependencies]: https://img.shields.io/david/dev/nmrony/hlsdownloader.svg?style=flat-square
81
- [npm-total-downloads]: https://img.shields.io/npm/dm/hlsdownloader.svg?style=flat-square
82
- [npm-url]: https://www.npmjs.com/package/hlsdownloader
83
- [dep-status]: https://david-dm.org/nmrony/hlsdownloader#info=dependencies&view=table
84
- [devdep-status]: https://david-dm.org/nmrony/hlsdownloader#info=devDependencies&view=table
85
- [standard-svg]: https://img.shields.io/badge/code%20style-standard-brightgreen.svg
86
- [standard-site]: http://standardjs.com
87
- [request-promise]: https://github.com/request/request-promise
88
- [travis-badge]: https://img.shields.io/travis/nmrony/hlsdownloader/master.svg?style=flat-square
89
- [travis-url]: https://travis-ci.org/nmrony/hlsdownloader
90
- [semvarbadge]: https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg
153
+ - body
154
+ - method
155
+ - setHost
156
+ - isStream
157
+ - parseJson
158
+ - prefixUrl
159
+ - cookieJar
160
+ - playlistURL
161
+ - concurrency
162
+ - allowGetBody
163
+ - stringifyJson
164
+ - methodRewriting
165
+
166
+ It also disable retry failed request that you can easily override
167
+
168
+ ## Running Tests
169
+
170
+ ```sh
171
+ npm test
172
+ ```
173
+
174
+ To run it on watch mode
175
+
176
+ ```sh
177
+ npm run test:watch
178
+ ```
179
+
180
+ ## Generate Documentations
181
+
182
+ ```sh
183
+ npm docs:gen
184
+ ```
185
+
186
+ ## Authors
187
+
188
+ 👤 **Nur Rony**
189
+
190
+ - Website: [nurrony.github.io](https://nurrony.github.io)
191
+ - Twitter: [@nmrony](https://twitter.com/nmrony)
192
+ - Github: [@nurrony](https://github.com/nurrony)
193
+ - LinkedIn: [@nmrony](https://linkedin.com/in/nmrony)
194
+
195
+ ## Contributing
196
+
197
+ 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).
198
+
199
+ ## Show your support
200
+
201
+ 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!!
202
+
203
+ ## Special Thanks to
204
+
205
+ - [Ky Team](https://www.npmjs.com/package/ky)
206
+
207
+ ## License
208
+
209
+ Copyright © 2023 [Nur Rony](https://github.com/nurrony).<br />
210
+ 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,88 +1,91 @@
1
1
  {
2
- "name": "hlsdownloader",
3
- "version": "2.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": ">=6.10"
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
- "playlist"
19
+ "m3u8",
20
+ "playlist",
21
+ "downloader",
22
+ "hlsdownloader",
23
+ "m3u8downloader"
24
+ ],
25
+ "files": [
26
+ "build"
33
27
  ],
34
- "author": "Nur Rony<pro.nmrony@gmail.com>",
28
+ "main": "./build/index.js",
29
+ "exports": {
30
+ "default": "./build/index.js"
31
+ },
35
32
  "license": "MIT",
33
+ "name": "hlsdownloader",
36
34
  "repository": {
37
- "type": "git",
38
- "url": "https://github.com/nmrony/hlsdownloader.git"
35
+ "type": "git+https",
36
+ "url": "git+https://github.com/nurrony/hlsdownloader.git"
39
37
  },
40
- "bugs": {
41
- "url": "https://github.com/nmrony/hlsdownloader/issues"
38
+ "scripts": {
39
+ "semantic-release": "semantic-release",
40
+ "prepublishOnly": "npm run build",
41
+ "build": "npm run lint:fix && npm run lint && npm test && npm run build:clean && npm run compile && echo '📦 Build artifact has been generated successfully.'",
42
+ "coverage": "NODE_OPTIONS=--experimental-vm-modules npx jest --coverage",
43
+ "prepare": "husky install",
44
+ "build:clean": "rimraf -fr build && echo '🧹 Build artifacts has been cleaned.'",
45
+ "compile": "npx esbuild --outdir=build --platform=node --format=esm --target=node18 --packages=external --bundle --minify --tree-shaking=true --keep-names src/index.js",
46
+ "docs:gen": "npm run docs:clean && NODE_OPTIONS=--experimental-vm-modules jsdoc -c jsdoc.json && echo '📄 Docs has been generated successfully.'",
47
+ "docs:clean": "rimraf -fr ./docs && echo '🧹 All docs has been cleaned.'",
48
+ "lint": "eslint . --ext .js && echo '💄 Coding style guideline has been followed properly.'",
49
+ "lint:fix": "eslint . --ext .js --fix && echo '🔧 Coding style has been fixed as per guideline.'",
50
+ "prod:start": "node index.js",
51
+ "test:coverage:clean": "rimraf -fr ./coverage && echo '🧹 All test coverage reports has been cleaned.'",
52
+ "test": "npm run test:coverage:clean && NODE_OPTIONS=--experimental-vm-modules npx jest",
53
+ "example": "NODE_OPTIONS=--experimental-vm-modules node example.mjs",
54
+ "test:watch": "NODE_OPTIONS=--experimental-vm-modules npx jest --no-cache --watch",
55
+ "version": "echo $npm_package_version",
56
+ "commitlint": "NODE_OPTIONS=--experimental-vm-modules npx commitlint --edit && echo '🔖 Commit message guidelines are followed properly.'"
42
57
  },
43
- "homepage": "https://nmrony.github.io/hlsdownloader",
44
- "husky": {
45
- "hooks": {
46
- "pre-commit": "npm run code:quality"
58
+ "config": {
59
+ "commitizen": {
60
+ "path": "cz-conventional-changelog"
47
61
  }
48
62
  },
49
- "dependencies": {
50
- "@babel/polyfill": "^7.6.0",
51
- "async": "^3.1.0",
52
- "minimist": "^1.2.0",
53
- "mkdirp": "^0.5.1",
54
- "request": "^2.88.0",
55
- "request-promise": "^4.2.4",
56
- "snyk": "^1.239.0"
63
+ "release": {
64
+ "debug": true,
65
+ "branches": [
66
+ "master"
67
+ ]
57
68
  },
69
+ "version": "3.0.1",
58
70
  "devDependencies": {
59
- "@babel/cli": "^7.6.4",
60
- "@babel/core": "^7.6.4",
61
- "@babel/plugin-proposal-object-rest-spread": "^7.6.2",
62
- "@babel/preset-env": "^7.6.3",
63
- "@babel/register": "^7.6.2",
64
- "babel-eslint": "^10.0.3",
65
- "chai": "^4.2.0",
66
- "commitizen": "^4.0.3",
67
- "core-js": "^3.3.4",
68
- "cz-conventional-changelog": "^3.0.2",
69
- "eslint": "^6.6.0",
70
- "eslint-config-standard": "^14.1.0",
71
- "eslint-plugin-import": "^2.18.2",
72
- "eslint-plugin-node": "^10.0.0",
73
- "eslint-plugin-promise": "^4.2.1",
74
- "eslint-plugin-standard": "^4.0.1",
75
- "husky": "^3.0.9",
76
- "mocha": "^6.2.2",
77
- "nyc": "^14.1.1",
78
- "semantic-release": "^15.13.28",
79
- "travis-deploy-once": "^5.0.11",
80
- "uglify-es": "^3.3.9"
71
+ "@commitlint/cli": "^18.4.3",
72
+ "@commitlint/config-conventional": "^18.4.3",
73
+ "@types/jest": "^29.5.11",
74
+ "clean-jsdoc-theme": "^4.2.17",
75
+ "esbuild": "^0.19.9",
76
+ "eslint": "^8.56.0",
77
+ "husky": "^8.0.0",
78
+ "jest": "^29.7.0",
79
+ "jsdoc": "^4.0.2",
80
+ "prettier": "^3.1.1",
81
+ "rimraf": "^5.0.5",
82
+ "semantic-release": "22.0.12"
81
83
  },
82
- "config": {
83
- "commitizen": {
84
- "path": "./node_modules/cz-conventional-changelog"
85
- }
84
+ "hooks": {
85
+ "pre-commit": "npm run commitlint ${1} && npm run lint:fix && npm run lint && npm test"
86
86
  },
87
- "snyk": true
87
+ "dependencies": {
88
+ "ky": "^1.1.3",
89
+ "p-limit": "^5.0.0"
90
+ }
88
91
  }
package/.snyk DELETED
@@ -1,33 +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'
11
- - snyk > lodash:
12
- patched: '2019-10-26T22:55:18.122Z'
13
- - snyk > snyk-config > lodash:
14
- patched: '2019-10-26T22:55:18.122Z'
15
- - snyk > inquirer > lodash:
16
- patched: '2019-10-26T22:55:18.122Z'
17
- - snyk > @snyk/dep-graph > graphlib > lodash:
18
- patched: '2019-10-26T22:55:18.122Z'
19
- - snyk > @snyk/dep-graph > lodash:
20
- patched: '2019-10-26T22:55:18.122Z'
21
- - snyk > snyk-nodejs-lockfile-parser > lodash:
22
- patched: '2019-10-26T22:55:18.122Z'
23
- - snyk > snyk-go-plugin > graphlib > lodash:
24
- patched: '2019-10-26T22:55:18.122Z'
25
- - snyk > snyk-nodejs-lockfile-parser > graphlib > lodash:
26
- patched: '2019-10-26T22:55:18.122Z'
27
- - snyk > snyk-nuget-plugin > lodash:
28
- patched: '2019-10-26T22:55:18.122Z'
29
- SNYK-JS-HTTPSPROXYAGENT-469131:
30
- - snyk > proxy-agent > https-proxy-agent:
31
- patched: '2019-10-26T22:55:18.122Z'
32
- - snyk > proxy-agent > pac-proxy-agent > https-proxy-agent:
33
- patched: '2019-10-26T22:55:18.122Z'
package/index.js DELETED
@@ -1 +0,0 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=exports.downloader=void 0;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 ownKeys(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(e);t&&(s=s.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,s)}return r}function _objectSpread(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?ownKeys(r,!0).forEach(function(t){_defineProperty(e,t,r[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):ownKeys(r).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(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:p,preambleCRLF:d,postambleCRLF:f,json:h,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;