hlsdownloader 3.3.3 → 4.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/README.md +1 -1
- package/build/index.js +1 -1
- package/package.json +11 -11
package/README.md
CHANGED
package/build/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var L=Object.defineProperty;var n=(i,t)=>L(i,"name",{value:t,configurable:!0});import{createWriteStream as R}from"fs";import{access as k,constants as x,mkdir as T,unlink as v}from"fs/promises";import y from"ky";import I from"p-limit";import{dirname as O,join as f}from"path";import{Readable as C}from"stream";import{URL as F}from"url";var m=class extends Error{static{n(this,"InvalidPlaylist")}constructor(t){super(t),this.name=this.constructor.name,Error.captureStackTrace(this,this.constructor)}},p=m;var u=class extends Error{static{n(this,"ProtocolNotSupported")}constructor(t){super(t),this.name=this.constructor.name,Error.captureStackTrace(this,this.constructor)}},d=u;var g=n((i,t=["http:","https:","ftp:","sftp:"])=>{try{let{protocol:s}=new URL(i);if(s&&!t.includes(`${s}`))throw new d(`${s} is not supported. Supported protocols are ${t.join(", ")}`);return!0}catch(s){throw s}},"isValidUrl"),E=n(i=>i.substring(0,1).replace("/","")+i.substring(1),"stripFirstSlash"),P=n(i=>i.match(/^#EXTM3U/im)!==null,"isValidPlaylist"),b=n(i=>new URL(i),"parseUrl"),U=n((i,...t)=>{let s=new Set(t.flat());return Object.fromEntries(Object.entries(i).filter(([r])=>!s.has(r)))},"omit"),D=n(i=>typeof i!="function","isNotFunction");var l={isValidPlaylist:P,isValidUrl:g,omit:U,parseUrl:b,stripFirstSlash:E,isNotFunction:D};var N=".m3u8",w=class i{static{n(this,"Downloader")}static defaultKyOptions={retry:{limit:0}};pool=I(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="";onData=null;onError=null;constructor({playlistURL:t,destination:s,concurrency:r=1,overwrite:e=!1,onData:h=null,onError:c=null,...o}={concurrency:1,destination:"",playlistURL:"",onData:null,onError:null,overwrite:!1,options:{}}){try{if(this.items=[t],this.playlistURL=t,this.concurrency=r,this.overwrite=e??!1,this.destination=s??"",this.pool=I(r??1),this.kyOptions=this.mergeOptions(o),this.onData=h,this.onError=c,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),l.isValidUrl(t),this.onData!==null&&l.isNotFunction(this.onData))throw TypeError("The `onData` must be a function");if(this.onError!==null&&l.isNotFunction(this.onError))throw TypeError("The `onError` must be a function")}catch(a){throw a}}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 e=r.filter(o=>o.toLowerCase().endsWith(N)),h=await Promise.allSettled(e.map(this.fetchPlaylist));return r=this.formatPlaylistContent(h).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(i.defaultKyOptions,l.omit(t,...i.unSupportedOptions))}parsePlaylist(t,s){return s.replace(/^#[\s\S].*/gim,"").split(/\r?\n/).reduce((r,e)=>{if(e!==""){let h=new F(e,t).href;r.push(h)}return r},[])}async fetchPlaylist(t){try{let s=await y.get(t,{...this.kyOptions}).text();if(!l.isValidPlaylist(s)){let{name:r,message:e}=new p("Invalid playlist");return this.errors.push({url:t,name:r,message:e}),{url:"",body:""}}return{url:t,body:s}}catch({name:s,message:r}){return this.errors.push({url:t,name:s,message:r}),this.onError&&this.onError({name:s,message:r,url:t}),{url:"",body:""}}}formatPlaylistContent(t){return t.reduce((s,{status:r,value:e})=>(r.toLowerCase()==="fulfilled"&&e&&s.push(e),s),[])}async processPlaylistItems(){return this.destination&&this.downloadItems()||this.fetchItems()}async downloadItem(t){try{let s=await y.get(t,{...this.kyOptions}),r=await this.createDirectory(t),e=C.fromWeb(s.body);return new Promise((h,c)=>{let o=R(r);e.pipe(o),e.on("error",a=>{e.destroy(),o.destroy(),v(r),this.onError&&this.onError({url:t,name:a.name,message:a.message}),c(a)}),o.on("finish",()=>{o.close(),this.onData&&this.onData({url:t,totalItems:this.items.length,path:r}),h("success")}),o.on("error",a=>{o.destroy(),e.destroy(),this.onError&&this.onError({url:t,name:a.name,message:a.message}),c(a)})})}catch({name:s,message:r}){this.errors.push({name:s,message:r,url:t}),this.onError&&this.onError({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}),this.onError&&this.onError({url:this.playlistURL,name:t.name,message:t.message})}}async fetchItems(){return Promise.allSettled(this.items.map(t=>this.pool(async()=>{try{let s=await y.get(t,{...this.kyOptions});return this.onData&&this.onData({url:t,totalItems:this.items.length,path:null}),s}catch({name:s,message:r}){this.errors.push({url:t,name:s,message:r}),this.onError&&this.onError({url:t,name:s,message:r})}})))}async createDirectory(t){let{pathname:s}=l.parseUrl(t),r=f(this.destination,O(s));return await T(r,{recursive:!0}),f(this.destination,l.stripFirstSlash(s))}async shouldOverwrite(t){try{let{pathname:s}=l.parseUrl(t),r=f(this.destination,O(s));return await k(r,x.F_OK),this.overwrite}catch(s){if(s.code==="ENOENT")return!0;throw s}}},S=w;var at=S;export{at as default};
|
|
1
|
+
var L=Object.defineProperty;var n=(i,t)=>L(i,"name",{value:t,configurable:!0});import{createWriteStream as R}from"fs";import{access as k,constants as x,mkdir as T,unlink as v}from"fs/promises";import y from"ky";import I from"p-limit";import{dirname as O,join as f}from"path";import{Readable as C}from"stream";import{URL as F}from"url";var m=class extends Error{static{n(this,"InvalidPlaylist")}constructor(t){super(t),this.name=this.constructor.name,Error.captureStackTrace(this,this.constructor)}},p=m;var u=class extends Error{static{n(this,"ProtocolNotSupported")}constructor(t){super(t),this.name=this.constructor.name,Error.captureStackTrace(this,this.constructor)}},d=u;var g=n((i,t=["http:","https:","ftp:","sftp:"])=>{try{let{protocol:s}=new URL(i);if(s&&!t.includes(`${s}`))throw new d(`${s} is not supported. Supported protocols are ${t.join(", ")}`);return!0}catch(s){throw s}},"isValidUrl"),E=n(i=>i.substring(0,1).replace("/","")+i.substring(1),"stripFirstSlash"),P=n(i=>i.match(/^#EXTM3U/im)!==null,"isValidPlaylist"),b=n(i=>new URL(i),"parseUrl"),U=n((i,...t)=>{let s=new Set(t.flat());return Object.fromEntries(Object.entries(i).filter(([r])=>!s.has(r)))},"omit"),D=n(i=>typeof i!="function","isNotFunction");var l={isValidPlaylist:P,isValidUrl:g,omit:U,parseUrl:b,stripFirstSlash:E,isNotFunction:D};var N=".m3u8",w=class i{static{n(this,"Downloader")}static defaultKyOptions={retry:{limit:0}};pool=I(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="";onData=null;onError=null;constructor({playlistURL:t,destination:s,concurrency:r=1,overwrite:e=!1,onData:h=null,onError:c=null,...o}={concurrency:1,destination:"",playlistURL:"",onData:null,onError:null,overwrite:!1,options:{}}){try{if(this.items=[t],this.playlistURL=t,this.concurrency=r,this.overwrite=e??!1,this.destination=s??"",this.pool=I(r??1),this.kyOptions=this.mergeOptions(o),this.onData=h,this.onError=c,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),l.isValidUrl(t),this.onData!==null&&l.isNotFunction(this.onData))throw TypeError("The `onData` must be a function");if(this.onError!==null&&l.isNotFunction(this.onError))throw TypeError("The `onError` must be a function")}catch(a){throw a}}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 e=r.filter(o=>o.toLowerCase().endsWith(N)),h=await Promise.allSettled(e.map(this.fetchPlaylist));return r=this.formatPlaylistContent(h).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({},i.defaultKyOptions,l.omit(t,...i.unSupportedOptions))}parsePlaylist(t,s){return s.replace(/^#[\s\S].*/gim,"").split(/\r?\n/).reduce((r,e)=>{if(e!==""){let h=new F(e,t).href;r.push(h)}return r},[])}async fetchPlaylist(t){try{let s=await y.get(t,{...this.kyOptions}).text();if(!l.isValidPlaylist(s)){let{name:r,message:e}=new p("Invalid playlist");return this.errors.push({url:t,name:r,message:e}),{url:"",body:""}}return{url:t,body:s}}catch({name:s,message:r}){return this.errors.push({url:t,name:s,message:r}),this.onError&&this.onError({name:s,message:r,url:t}),{url:"",body:""}}}formatPlaylistContent(t){return t.reduce((s,{status:r,value:e})=>(r.toLowerCase()==="fulfilled"&&e&&s.push(e),s),[])}async processPlaylistItems(){return this.destination&&this.downloadItems()||this.fetchItems()}async downloadItem(t){try{let s=await y.get(t,{...this.kyOptions}),r=await this.createDirectory(t),e=C.fromWeb(s.body);return new Promise((h,c)=>{let o=R(r);e.pipe(o),e.on("error",a=>{e.destroy(),o.destroy(),v(r),this.onError&&this.onError({url:t,name:a.name,message:a.message}),c(a)}),o.on("finish",()=>{o.close(),this.onData&&this.onData({url:t,totalItems:this.items.length,path:r}),h("success")}),o.on("error",a=>{o.destroy(),e.destroy(),this.onError&&this.onError({url:t,name:a.name,message:a.message}),c(a)})})}catch({name:s,message:r}){this.errors.push({name:s,message:r,url:t}),this.onError&&this.onError({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}),this.onError&&this.onError({url:this.playlistURL,name:t.name,message:t.message})}}async fetchItems(){return Promise.allSettled(this.items.map(t=>this.pool(async()=>{try{let s=await y.get(t,{...this.kyOptions});return this.onData&&this.onData({url:t,totalItems:this.items.length,path:null}),s}catch({name:s,message:r}){this.errors.push({url:t,name:s,message:r}),this.onError&&this.onError({url:t,name:s,message:r})}})))}async createDirectory(t){let{pathname:s}=l.parseUrl(t),r=f(this.destination,O(s));return await T(r,{recursive:!0}),f(this.destination,l.stripFirstSlash(s))}async shouldOverwrite(t){try{let{pathname:s}=l.parseUrl(t),r=f(this.destination,O(s));return await k(r,x.F_OK),this.overwrite}catch(s){if(s.code==="ENOENT")return!0;throw s}}},S=w;var at=S;export{at as default};
|
package/package.json
CHANGED
|
@@ -13,27 +13,27 @@
|
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"ky": "^1.
|
|
17
|
-
"p-limit": "^
|
|
16
|
+
"ky": "^1.10.0",
|
|
17
|
+
"p-limit": "^7.1.1"
|
|
18
18
|
},
|
|
19
19
|
"description": "Downloads HLS Playlist file and TS chunks",
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@commitlint/cli": "^19.8.1",
|
|
22
22
|
"@commitlint/config-conventional": "^19.8.1",
|
|
23
|
-
"@types/jest": "^
|
|
23
|
+
"@types/jest": "^30.0.0",
|
|
24
24
|
"clean-jsdoc-theme": "^4.3.0",
|
|
25
|
-
"esbuild": "^0.25.
|
|
26
|
-
"eslint": "^9.
|
|
25
|
+
"esbuild": "^0.25.10",
|
|
26
|
+
"eslint": "^9.36.0",
|
|
27
27
|
"husky": "^9.1.7",
|
|
28
|
-
"jest": "^
|
|
28
|
+
"jest": "^30.1.3",
|
|
29
29
|
"jsdoc": "^4.0.4",
|
|
30
|
-
"lint-staged": "^16.
|
|
31
|
-
"prettier": "^3.
|
|
30
|
+
"lint-staged": "^16.2.0",
|
|
31
|
+
"prettier": "^3.6.2",
|
|
32
32
|
"rimraf": "^6.0.1",
|
|
33
|
-
"semantic-release": "24.2.
|
|
33
|
+
"semantic-release": "24.2.9"
|
|
34
34
|
},
|
|
35
35
|
"engines": {
|
|
36
|
-
"node": ">=
|
|
36
|
+
"node": ">=20",
|
|
37
37
|
"npm": ">=9"
|
|
38
38
|
},
|
|
39
39
|
"exports": {
|
|
@@ -97,5 +97,5 @@
|
|
|
97
97
|
},
|
|
98
98
|
"snyk": true,
|
|
99
99
|
"type": "module",
|
|
100
|
-
"version": "
|
|
100
|
+
"version": "4.0.0"
|
|
101
101
|
}
|