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 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,93 @@
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://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
- Install it via `npm` or `yarn`
79
+ It is pretty straight forward
11
80
 
12
81
  ```sh
13
- [sudo] npm install hlsdownloader --save
14
- # Or
15
- [sudo] yarn add hlsdownloader
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
- ## Configuration
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' //Using ES2015 module
28
- //var HLSDownloader = require('hlsdownloader').downloader; //using commonJS module
99
+ import HLSDownloader from 'hlsdownloader';
29
100
 
30
- const params = {
101
+ const options = {
31
102
  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)))
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
- `msg` is an object with following properties
111
+ > ℹ️ Check [example.js](example.js) for working example
39
112
 
40
113
  ```js
41
- //on success
114
+ // on success
42
115
  {
43
- message: 'Downloaded successfully',
44
- playlistURL: 'your playlist url'
116
+ total: <number>,
117
+ playlistURL: 'your playlist url'
118
+ message: 'Downloaded successfully',
45
119
  }
46
- //on partial download
120
+
121
+ // on partial download
47
122
  {
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
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
- `HLSDownloader` accepts all parameters supported by [request-promise][request-promise] except these following **options**
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
- - 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
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
- "name": "hlsdownloader",
3
- "version": "2.2.2",
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
+ "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/nmrony/hlsdownloader.git"
34
+ "type": "git+https",
35
+ "url": "git+https://github.com/nurrony/hlsdownloader.git"
39
36
  },
40
- "bugs": {
41
- "url": "https://github.com/nmrony/hlsdownloader/issues"
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
- "homepage": "https://nmrony.github.io/hlsdownloader",
44
- "husky": {
45
- "hooks": {
46
- "pre-commit": "npm run code:quality"
57
+ "config": {
58
+ "commitizen": {
59
+ "path": "cz-conventional-changelog"
47
60
  }
48
61
  },
49
- "dependencies": {
50
- "@babel/polyfill": "^7.4.3",
51
- "async": "^2.6.2",
52
- "minimist": "^1.2.0",
53
- "mkdirp": "^0.5.1",
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
- "@babel/cli": "^7.4.3",
60
- "@babel/core": "^7.4.3",
61
- "@babel/plugin-proposal-object-rest-spread": "^7.4.3",
62
- "@babel/preset-env": "^7.4.3",
63
- "@babel/register": "^7.4.0",
64
- "babel-eslint": "^10.0.1",
65
- "chai": "^4.2.0",
66
- "commitizen": "^3.1.1",
67
- "cz-conventional-changelog": "^2.1.0",
68
- "eslint": "^5.16.0",
69
- "eslint-config-standard": "^12.0.0",
70
- "eslint-plugin-import": "^2.17.2",
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
- "config": {
82
- "commitizen": {
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
- "snyk": true
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;