dasha 3.0.4 → 3.1.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 +21 -661
- package/README.md +6 -10
- package/dist/dasha.js +6 -0
- package/dist/dasha.mjs +6 -0
- package/package.json +49 -32
- package/types/dasha.d.ts +62 -12
- package/dasha.js +0 -26
- package/lib/audio.js +0 -128
- package/lib/dash.js +0 -430
- package/lib/hls.js +0 -206
- package/lib/subtitle.js +0 -120
- package/lib/track.js +0 -107
- package/lib/util.js +0 -97
- package/lib/video.js +0 -158
- package/lib/xml.js +0 -296
package/README.md
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
# dasha
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/dasha)
|
|
4
|
+
[](https://www.npmjs.com/package/dasha)
|
|
5
|
+
[](https://www.npmjs.com/package/dasha)
|
|
6
6
|
|
|
7
|
-
Library for parsing MPEG-DASH and HLS manifests. Made with the purpose of obtaining a simplified representation convenient for further downloading of segments.
|
|
8
|
-
|
|
9
|
-
<div align="left">
|
|
10
|
-
<span>English</span> •
|
|
11
|
-
<a href="https://github.com/vitalygashkov/dasha/tree/main/docs/README.ru.md">Pусский</a>
|
|
12
|
-
</div>
|
|
7
|
+
Library for parsing MPEG-DASH (.mpd) and HLS (.m3u8) manifests. Made with the purpose of obtaining a simplified representation convenient for further downloading of segments.
|
|
13
8
|
|
|
14
9
|
## Install
|
|
15
10
|
|
|
@@ -23,7 +18,8 @@ npm i dasha
|
|
|
23
18
|
import fs from 'node:fs/promises';
|
|
24
19
|
import { parse } from 'dasha';
|
|
25
20
|
|
|
26
|
-
const url =
|
|
21
|
+
const url =
|
|
22
|
+
'https://dash.akamaized.net/dash264/TestCases/1a/sony/SNE_DASH_SD_CASE1A_REVISED.mpd';
|
|
27
23
|
const body = await fetch(url).then((res) => res.text());
|
|
28
24
|
const manifest = await parse(body, url);
|
|
29
25
|
|
package/dist/dasha.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";var R=(e,r)=>()=>(r||e((r={exports:{}}).exports,r),r.exports);var ee=R((Tr,Z)=>{"use strict";function we(e,r={}){var t=r.pos||0,s=!!r.keepComments,n=!!r.keepWhitespace,i="<",o=60,a=">",c=62,l=45,h=47,d=33,u=39,p=34,C=91,S=93;function v(g){for(var f=[];e[t];)if(e.charCodeAt(t)==o){if(e.charCodeAt(t+1)===h){var A=t+2;t=e.indexOf(a,t);var B=e.substring(A,t);if(B.indexOf(g)==-1){let b=e.substring(0,t).split(`
|
|
2
|
+
`);throw new Error(`Unexpected close tag
|
|
3
|
+
Line: `+(b.length-1)+`
|
|
4
|
+
Column: `+(b[b.length-1].length+1)+`
|
|
5
|
+
Char: `+e[t])}return t+1&&(t+=1),f}else if(e.charCodeAt(t+1)===d){if(e.charCodeAt(t+2)==l){let b=t;for(;t!==-1&&!(e.charCodeAt(t)===c&&e.charCodeAt(t-1)==l&&e.charCodeAt(t-2)==l&&t!=-1);)t=e.indexOf(a,t+1);t===-1&&(t=e.length),s&&f.push(e.substring(b,t+1))}else if(e.charCodeAt(t+2)===C&&e.charCodeAt(t+8)===C&&e.substr(t+3,5).toLowerCase()==="cdata"){var I=e.indexOf("]]>",t);I==-1?(f.push(e.substr(t+9)),t=e.length):(f.push(e.substring(t+9,I)),t=I+3);continue}else{let b=t+1;t+=2;for(var m=!1;(e.charCodeAt(t)!==c||m===!0)&&e[t];)e.charCodeAt(t)===C?m=!0:m===!0&&e.charCodeAt(t)===S&&(m=!1),t++;f.push(e.substring(b,t))}t++;continue}var N=L();f.push(N),N.tagName[0]==="?"&&(f.push(...N.children),N.children=[])}else{let b=O();if(n)b.length>0&&f.push(b);else{var _=b.trim();_.length>0&&f.push(_)}t++}return f}function O(){var g=t;return t=e.indexOf(i,t)-1,t===-2&&(t=e.length),e.slice(g,t+1)}var U=`\r
|
|
6
|
+
>/= `;function V(){for(var g=t;U.indexOf(e[t])===-1&&e[t];)t++;return e.slice(g,t)}var G=r.noChildNodes||["img","br","input","meta","link","hr"];function L(){t++;let g=V(),f={},A=[];for(;e.charCodeAt(t)!==c&&e[t];){var B=e.charCodeAt(t);if(B>64&&B<91||B>96&&B<123){for(var I=V(),m=e.charCodeAt(t);m&&m!==u&&m!==p&&!(m>64&&m<91||m>96&&m<123)&&m!==c;)t++,m=e.charCodeAt(t);if(m===u||m===p){var N=T();if(t===-1)return{tagName:g,attributes:f,children:A}}else N=null,t--;f[I]=N}t++}if(e.charCodeAt(t-1)!==h)if(g=="script"){let _=t+1;t=e.indexOf("</script>",t),A=[e.slice(_,t)],t+=9}else if(g=="style"){let _=t+1;t=e.indexOf("</style>",t),A=[e.slice(_,t)],t+=8}else G.indexOf(g)===-1?(t++,A=v(g)):t++;else t++;return{tagName:g,attributes:f,children:A}}function T(){var g=e[t],f=t+1;return t=e.indexOf(g,f),e.slice(f,t)}function $(){var g=new RegExp("\\s"+r.attrName+`\\s*=['"]`+r.attrValue+`['"]`).exec(e);return g?g.index:-1}let y=null;if(r.attrValue!==void 0)for(r.attrName=r.attrName||"id",y=[];(t=$())!==-1;)t=e.lastIndexOf("<",t),t!==-1&&y.push(L()),e=e.substr(t),t=0;else r.parseNode?y=L():y=v("");return r.filter&&(y=W(y,r.filter)),r.setPos&&(y.pos=t),y}function W(e,r,t=0,s=""){var n=[];return e.forEach(function(i,o){if(typeof i=="object"&&r(i,o,t,s)&&n.push(i),i.children){var a=W(i.children,r,t+1,(s?s+".":"")+o+"."+i.tagName);n=n.concat(a)}}),n}Z.exports={parse:we,filter:W}});var D=R((yr,ne)=>{"use strict";var te=(e,r=["Bytes","KB","MB","GB","TB"])=>{if(e==0)return`0 ${r[0]}`;let t=parseInt(Math.floor(Math.log(e)/Math.log(1024)));return t==0?e+" "+r[t]:(e/Math.pow(1024,t)).toFixed(1)+" "+r[t]},Te=(e,r)=>({b:e*r,kb:e/1e3*r,mb:e/8e6*r,gb:e/8e9*r,toString(){return te(e*r)}}),ye=e=>({bps:e,kbps:e/1e3,mbps:e/8e6,gbps:e/8e9,toString(){return te(e,["bps","Kbps","Mbps","Gbps"])}}),re=[{width:7680,height:4320},{width:3840,height:2160},{width:2560,height:1440},{width:1920,height:1080},{width:1280,height:720},{width:1024,height:576},{width:854,height:480},{width:640,height:360},{width:426,height:240},{width:256,height:144}],Ne=e=>re.find(r=>r.height===e)?.width,se=e=>re.find(r=>r.width===e)?.height,Be=e=>`${se(e.width)||e.height}p`,Ue=e=>{let r=Math.max(...e.map(t=>t.bitrate.bps));return e.find(t=>t.bitrate.bps===r)},_e=e=>{let a=/P(?:(\d*)Y)?(?:(\d*)M)?(?:(\d*)D)?(?:T(?:(\d*)H)?(?:(\d*)M)?(?:([\d.]*)S)?)?/.exec(e);if(!a)return 0;let[c,l,h,d,u,p]=a.slice(1);return parseFloat(c||0)*31536e3+parseFloat(l||0)*2592e3+parseFloat(h||0)*86400+parseFloat(d||0)*3600+parseFloat(u||0)*60+parseFloat(p||0)},Re=e=>{try{return Intl.getCanonicalLocales(e),!0}catch{return}};ne.exports={parseSize:Te,parseBitrate:ye,getWidth:Ne,getHeight:se,getQualityLabel:Be,getBestTrack:Ue,parseDuration:_e,isLanguageTagValid:Re}});var Y=R((Nr,ie)=>{"use strict";var{parseBitrate:Ee,getQualityLabel:Le,parseSize:Ie}=D(),F={avc:"H.264",hevc:"H.265",vc1:"VC-1",vp8:"VP8",vp9:"VP9",av1:"AV1"},P={sdr:"SDR",hlg:"HLG",hdr10:"HDR10",hdr10p:"HDR10+",dv:"DV"},Q={Unspecified:0,BT_709:1,BT_601_625:5,BT_601_525:6,BT_2020_and_2100:9,SMPTE_ST_2113_and_EG_4321:12},q={Unspecified:0,BT_709:1,BT_601:6,BT_2020:14,BT_2100:15,BT_2100_PQ:16,BT_2100_HLG:18},De={RGB:0,YCbCr_BT_709:1,YCbCr_BT_601_625:5,YCbCr_BT_601_525:6,YCbCr_BT_2020_and_2100:9,ICtCp_BT_2100:14},Fe=e=>{let r=e.toLowerCase().trim().split(".")[0],t=["avc1","avc2","avc3","dva1","dvav"],s=["hev1","hev2","hev3","hvc1","hvc2","hvc3","dvh1","dvhe","lhv1","lhe1"],n=["vc-1"],i=["vp08","vp8"],o=["vp09","vp9"],a=["av01"];if(t.includes(r))return F.avc;if(s.includes(r)||n.includes(r))return F.hevc;if(i.includes(r))return F.vp8;if(o.includes(r))return F.vp9;if(a.includes(r))return F.av1;throw new Error(`The MIME ${e} is not supported as video codec`)},Me=(e,r,t)=>(r==5&&(r=q.BT_601),e==Q.Unspecified&&r==q.Unspecified&&t==De.RGB||[Q.BT_601_625,Q.BT_601_525].includes(e)?P.sdr:q.BT_2100_PQ===r?P.hdr10:q.BT_2100_HLG===r?P.hlg:P.sdr),Oe=({id:e,label:r,type:t,codec:s,dynamicRange:n,contentProtection:i,bitrate:o,duration:a,width:c,height:l,fps:h,language:d,segments:u})=>{let p=Ee(Number(o)),C=Number(c),S=Number(l),v=a?Ie(Number(o),Number(a)):void 0;return{id:e,label:r,type:t,codec:s,bitrate:p,size:v,protection:i,segments:u,dynamicRange:n,language:d,width:C,height:S,fps:Number(h),quality:Le({width:C,height:S}),toString(){return["VIDEO",`[${s}, ${n}]`,d,`${c}x${l} @ ${p.kbps} kb/s, ${h} FPS`].join(" | ")}}},Pe=e=>{for(let r of e.toLowerCase().split(",")){let t=r.trim().split(".")[0];try{return Fe(t)}catch{continue}}throw new Error(`No MIME types matched any supported Video Codecs in ${e}`)},ke=(e,r=[],t=[])=>{if(["dva1","dvav","dvhe","dvh1"].some(u=>e.startsWith(u)))return P.dv;let n="urn:mpeg:mpegB:cicp:ColourPrimaries",i="urn:mpeg:mpegB:cicp:TransferCharacteristics",o="urn:mpeg:mpegB:cicp:MatrixCoefficients",a=[...t,...r],c=u=>a.filter(p=>p.attributes.schemeIdUri===u).map(p=>parseInt(p.attributes.value)),l=c(n).reduce((u,p)=>u+p,0),h=c(i).reduce((u,p)=>u+p,0),d=c(o).reduce((u,p)=>u+p,0);return Me(l,h,d)};ie.exports={parseVideoCodec:Pe,parseDynamicRange:ke,createVideoTrack:Oe,VIDEO_CODECS:F}});var k=R((Br,de)=>{"use strict";var{getBestTrack:oe}=D(),Ve=e=>e.toLowerCase().split(",").map(r=>r.trim().split(".")[0]),ae=e=>({width:r,height:t})=>e.filter(s=>(!r||s.width===r)&&(!t||s.height===t)),z=e=>r=>{if(!r?.length)return e;let t=e.filter(s=>r.includes(s.codec));return t.sort((s,n)=>n.bitrate.bps-s.bitrate.bps),t},$e=e=>z(e),ce=e=>r=>{if(!r)return[oe(e)];let t=String(r).includes("p")?r:`${r}p`,s=e.filter(n=>n.quality===t);return s.sort((n,i)=>i.bitrate.bps-n.bitrate.bps),s.length?s:[oe(e)]},Ge=e=>z(e),ue=e=>(r=[],t)=>{if(!r.length)for(let o of e)r.includes(o.language)||r.push(o.language);let s=[];for(let o of r){let a=e.filter(c=>c.language?.startsWith(o));s.push(...a)}let n=[],i=[...new Set(s.map(o=>o.language))];for(let o of i){let a=s.filter(c=>c.language===o).slice(0,t);n.push(...a)}return n},le=e=>r=>{if(!r)return e;let t=typeof r=="string"?parseFloat(r):r;return e.filter(s=>s.channels===t)},qe=e=>r=>r.length?e.filter(t=>r.some(s=>t.language?.startsWith(s)||t.label?.startsWith(s))):e,He=(e,r)=>ae(e)(r),We=(e,r)=>ce(e)(r),Qe=(e,r)=>z(e)(r),Ye=(e,r,t)=>ue(e)(r,t),ze=(e,r)=>le(e)(r);de.exports={parseMimes:Ve,createResolutionFilter:ae,createVideoCodecFilter:$e,createVideoQualityFilter:ce,createAudioCodecFilter:Ge,createAudioLanguageFilter:ue,createAudioChannelsFilter:le,createSubtitleLanguageFilter:qe,filterByResolution:He,filterByQuality:We,filterByCodecs:Qe,filterByLanguages:Ye,filterByChannels:ze}});var j=R((Ur,pe)=>{"use strict";var{parseMimes:je}=k(),{parseBitrate:Ke,parseSize:Xe}=D(),E={AAC:"AAC",AC3:"DD",EC3:"DD+",OPUS:"OPUS",OGG:"VORB",DTS:"DTS",ALAC:"ALAC",FLAC:"FLAC"},xe=e=>{switch(e.toLowerCase().trim().split(".")[0]){case"mp4a":return E.AAC;case"ac-3":return E.AC3;case"ec-3":return E.EC3;case"opus":return E.OPUS;case"dtsc":return E.DTS;case"alac":return E.ALAC;case"flac":return E.FLAC;default:throw new Error(`The MIME ${e} is not supported as audio codec`)}},Je=e=>{let r=je(e);for(let t of r)try{return xe(t)}catch{continue}throw new Error(`No MIME types matched any supported Audio Codecs in ${e}`)},Ze=(e=[])=>{let r="tag:dolby.com,2018:dash:EC3_ExtensionComplexityIndex:2018";for(let t of e)if(t.attributes.schemeIdUri===r)return parseInt(t.attributes.value)},et=(e=[])=>{for(let r of e){let{schemeIdUri:t,value:s}=r.attributes,n=t=="urn:mpeg:dash:role:2011"&&s==="descriptive",i=t=="urn:tva:metadata:cs:AudioPurposeCS:2007"&&s==="1";if(n||i)return!0}return!1},tt=e=>{let r=t=>t>="0"&&t<="9";if(typeof e=="string"){if(e.toUpperCase()=="A000")return 2;if(e.toUpperCase()=="F801")return 5.1;if(r(e.replace("ch","").replace(".","")[0]))return parseFloat(e.replace("ch",""));throw new Error(`Unsupported audio channels value, '${e}'`)}return parseFloat(e)},rt=({id:e,label:r,type:t,codec:s,channels:n,bitrate:i,duration:o,jointObjectCoding:a=0,isDescriptive:c=!1,language:l,segments:h})=>{let d=Ke(Number(i)),u=tt(n),p=o?Xe(Number(i),Number(o)):void 0;return{id:e,label:r,type:t,codec:s,bitrate:d,size:p,language:l,segments:h,channels:u,jointObjectCoding:a,isDescriptive:c,toString(){return["AUDIO",`[${s}]`,`${u||"?"}`+(a?` (JOC ${a})`:""),`${d.kbps} kb/s`,l].join(" | ")}}};pe.exports={AUDIO_CODECS:E,parseAudioCodec:Je,createAudioTrack:rt,getDolbyDigitalPlusComplexityIndex:Ze,checkIsDescriptive:et}});var ge=R((_r,he)=>{"use strict";var{parseMimes:st}=k(),{parseBitrate:nt,parseSize:it}=D(),M={SubRip:"SRT",SubStationAlpha:"SSA",SubStationAlphav4:"ASS",TimedTextMarkupLang:"TTML",WebVTT:"VTT",fTTML:"STPP",fVTT:"WVTT"},ot=e=>{switch(e.toLowerCase().trim().split(".")[0]){case"srt":case"x-subrip":return M.SubRip;case"ssa":return M.SubStationAlpha;case"ass":return M.SubStationAlphav4;case"ttml":return M.TimedTextMarkupLang;case"vtt":return M.WebVTT;case"stpp":return M.fTTML;case"wvtt":return M.fVTT;default:throw new Error(`The MIME ${e} is not supported as subtitle codec`)}},at=e=>{let r=st(e);for(let t of r)try{return ot(t)}catch{continue}throw new Error(`No MIME types matched any supported Subtitle Codecs in ${e}`)},ct=(e=[])=>{for(let r of e)if(r.attributes.schemeIdUri==="urn:mpeg:dash:role:2011"&&r.attributes.value==="caption")return!0;return!1},ut=(e=[])=>{for(let r of e){let{schemeIdUri:t,value:s}=r.attributes;if(t==="urn:tva:metadata:cs:AudioPurposeCS:2007"&&s==="2")return!0}return!1},lt=(e=[])=>{for(let r of e)if(r.attributes.schemeIdUri==="urn:mpeg:dash:role:2011"&&(r.attributes.value==="forced-subtitle"||r.attributes.value==="forced_subtitle"))return!0;return!1},dt=({id:e,label:r,bitrate:t,duration:s,type:n,codec:i,isClosedCaption:o,isSdh:a,isForced:c,language:l,segments:h})=>{let d=nt(Number(t)),u=s?it(Number(t),Number(s)):void 0;return{id:e,label:r,bitrate:d,size:u,type:n,codec:i,isClosedCaption:o,isSdh:a,isForced:c,segments:h,language:l,toString(){return["SUBTITLE",`[${i}]`,l].join(" | ")}}};he.exports={parseSubtitleCodec:at,checkIsClosedCaption:ct,checkIsSdh:ut,checkIsForced:lt,createSubtitleTrack:dt}});var me=R((Rr,fe)=>{"use strict";var pt=ee(),{parseDuration:ht,isLanguageTagValid:gt}=D(),{parseVideoCodec:ft,parseDynamicRange:mt,createVideoTrack:Ct}=Y(),{parseAudioCodec:bt,createAudioTrack:St,getDolbyDigitalPlusComplexityIndex:vt,checkIsDescriptive:At}=j(),{parseSubtitleCodec:wt,checkIsClosedCaption:Tt,checkIsSdh:yt,checkIsForced:Nt,createSubtitleTrack:Bt}=ge(),{createResolutionFilter:Ut,createVideoQualityFilter:_t,createAudioLanguageFilter:Rt,createSubtitleLanguageFilter:Et,createVideoCodecFilter:Lt,createAudioCodecFilter:It,createAudioChannelsFilter:Dt}=k(),H=e=>e&&(Array.isArray(e)?e.get=r=>H(e.find(t=>t.tagName===r)):(e.getAttr=r=>e.attributes[r],e.getChild=r=>{let t=e.children.find(n=>n.tagName===r);return!r&&typeof e.children?.[0]=="string"?e.children[0]:H(t)},e.set=(r,t)=>e.attributes[r]=t,e.get=r=>e.getAttr(r)||e.getChild(r),e.getNumber=r=>Number(e.find(r)),e.getAll=r=>e.children.filter(t=>t.tagName===r).map(H),e.getBaseUrls=()=>e.getAll("BaseURL").map(r=>r.children[0]),e.getBaseUrl=()=>e.getBaseUrls()[0]),e),Ft=(e,r)=>{let t=e.get,s=e.getAll,n=o=>t(o)||r.get(o),i=o=>[...s(o),...r.getAll(o)].filter(Boolean);return e.get=n,e.getAll=i,{get:n,getAll:i}},Mt=(e,r,t,s)=>{let n=r.getBaseUrl();return n?n.startsWith("https://")||(n=new URL(n,e).toString()):n=e,(t.getBaseUrl()||n)&&(n=new URL(t.getBaseUrl()||"",n).toString()),new URL(s.getBaseUrl()||"",n).toString()},Ot=e=>{let r=e.get("mimeType"),t=e.get("contentType")||r?.split("/")[0];if(!t&&!r)throw new Error("Unable to determine the format of a Representation, cannot continue...");return{contentType:t,mimeType:r}},Pt=(e,r,t)=>r==="text"&&!t.includes("mp4")?t.split("/")[1]:e.get("codecs"),kt=(e,r,t)=>{let s="",n=[],i=e.get("lang"),o=e.get("id");if(e&&(n.push(i),o)){let a=o.match(/\w+_(\w+)=\d+/);a&&a[1]&&n.push(a[1])}n.push(r.get("lang")),t&&n.push(t);for(let a of n){let c=(a||"").trim();!gt(c)||c.startsWith("und")||(s=c)}return s},K=(e,r)=>{let t=e;for(let[s,n]of Object.entries(r))t=t.replace("$"+s+"$",n);return t},Vt=(e,r,t)=>{for(let s of["initialization","media"]){let n=e.get(s);if(n){if(!n.startsWith("https://")){if(!r)throw new Error("Resolved Segment URL is not absolute, and no Base URL is available.");n=new URL(n,r).toString()}if(!new URL(n).search){let i=new URL(t).search;i&&(n+=`?${i}`)}e.set(s,n)}}},$t=(e,r,t,s)=>{let n=[],i=0;for(let c of e.getAll("S")){let l=Number(c.get("t")),h=Number(c.get("r")||0),d=Number(c.get("d"));l&&(i=l);for(let u=0;u<h+1;u++)n.push(i),i+=d}let o=[],a=[...Array(n.length).keys()].map(c=>c+s);for(let c=0;c<n.length;c++){let l=n[c],h=a[c],d=K(r.get("media"),{Bandwidth:t.get("bandwidth"),RepresentationID:t.get("id"),Number:h,Time:l});o.push({url:d})}return o},Gt=(e,r,t,s,n)=>{let i=Number(e.get("startNumber")||1),o=e.get("SegmentTimeline");Vt(e,r,t);let a=parseFloat(e.get("duration")),c=parseFloat(e.get("timescale")||1),h=s?Math.ceil(s/(a/c)):35,d=n.get("bandwidth"),u=n.get("id"),p=[];if(o)p.push(...$t(o,e,n,i));else for(let S=i;S<i+h;S++){let v=K(e.get("media"),{Bandwidth:d,RepresentationID:u,Number:S,Time:S});p.push({url:v})}let C=e.get("initialization");if(C){let S=K(C,{Bandwidth:d,RepresentationID:u});p.unshift({url:S,init:!0})}return p},qt=(e,r)=>{let t=e.get("SegmentURL"),s=[];for(let n of t){let i=n.get("media");i?i.startsWith("https://")||(i=new URL(i,r).toString()):i=r,s.push({url:i,range:n.get("mediaRange")})}return s},Ht=async(e,r)=>{let t=e.get("Initialization");return{url:r,range:""}},Wt=e=>{for(let r of e)if(r.url.includes("&")){let s=new URL(r.url),n=new URLSearchParams(s.searchParams.toString()).entries();for(let[i,o]of n)s.searchParams.delete(i),s.searchParams.append(i.replaceAll("amp;",""),o);r.url=s.toString()}},Qt={"urn:mpeg:dash:mp4protection:2011":"common","urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95":"playready","urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed":"widevine"},Yt=e=>{let r={};for(let t of e){let s=t.get("schemeIdUri")?.toLowerCase(),n=t.get("value"),i=t.get("cenc:pssh")?.get(),o=t.get("cenc:default_KID"),a={id:s,value:n,pssh:i,defaultKeyId:o};r[Qt[s]]=a}return r},zt=async(e,r,t)=>{let s=H(pt.parse(e)).get("MPD"),n=s.get("Period"),i=n.get("duration")||s.get("mediaPresentationDuration"),o=ht(i),a=[],c=[],l=[];for(let h of n.getAll("AdaptationSet"))for(let d of h.getAll("Representation")){let{get:u,getAll:p}=Ft(d,h),{contentType:C,mimeType:S}=Ot(d),v=Pt(d,C,S),O=kt(d,h,t),U=Mt(r,s,n,d),V=u("SegmentTemplate"),G=u("SegmentList"),L=u("SegmentBase"),T=[];if(V){let w=Gt(V,U,r,o,d);T.push(...w)}else if(G){let w=qt(G,U);T.push(...w)}else if(L){let w=await Ht(L,U);T.push(w)}else if(U)T.push({url:U});else throw new Error("Could not find a way to get segments from this MPD manifest.");Wt(T);let $=u("label"),y=u("frameRate")??L?.attributes.timescale,g=u("width")??0,f=u("height")??0,A=u("bandwidth"),B=p("SupplementalProperty"),I=p("EssentialProperty"),m=h.getAll("Accessibility"),N=h.getAll("Role"),_=p("ContentProtection"),b=[new URL(U).hostname,C,v,A,O,s.get("id"),n.get("id"),u("id"),u("audioTrackId")].filter(Boolean).join("-").replaceAll("/","-");switch(C){case"video":{let w=Ct({id:b,label:$,type:C,codec:ft(v),dynamicRange:mt(v,B,I),contentProtection:Yt(_),bitrate:A,duration:o,width:g,height:f,fps:y,language:O,segments:T});a.push(w);break}case"audio":{let w=St({id:b,label:$,type:C,codec:bt(v),channels:u("AudioChannelConfiguration")?.get("value"),jointObjectCoding:vt(B),isDescriptive:At(m),bitrate:A,duration:o,language:O,segments:T});c.push(w);break}case"text":{let w=Bt({id:b,label:$,type:C,codec:wt(v||"vtt"),isClosedCaption:Tt(N),isSdh:yt(m),isForced:Nt(N),bitrate:A,duration:o,language:O,segments:T});l.push(w);break}case"image":break;default:throw new Error(`Unknown content type: ${C}`)}}return a.sort((h,d)=>d.bitrate.bps-h.bitrate.bps),{duration:o,tracks:{all:a.concat(c).concat(l),videos:a,audios:c,subtitles:l,withResolution:Ut(a),withVideoCodecs:Lt(a),withVideoQuality:_t(a),withAudioCodecs:It(c),withAudioLanguages:Rt(c),withAudioChannels:Dt(c),withSubtitleLanguages:Et(l)}}};fe.exports={parseManifest:zt}});var Ae=R((Er,ve)=>{"use strict";var{dirname:jt,basename:Ce}=require("path"),Kt=require("m3u8-parser"),{parseBitrate:Xt,getQualityLabel:xt}=D(),{createResolutionFilter:Jt,createVideoQualityFilter:Zt,createAudioLanguageFilter:er,createSubtitleLanguageFilter:tr,createVideoCodecFilter:rr,createAudioCodecFilter:sr,createAudioChannelsFilter:nr}=k(),{createAudioTrack:ir}=j(),{createVideoTrack:or}=Y(),be=e=>{let r=new Kt.Parser;return r.push(e),r.end(),r.manifest},ar=async e=>{let r=await fetch(e);if(!r.ok)throw new Error(`Failed to fetch playlist (${r.status}): ${e}`);let t=await r.text();return be(t)},J=(e,r)=>{let t=e;return t.startsWith("https://")||(t=new URL(t,r).toString()+new URL(r).search),t},cr=(e,r)=>new URL(e).pathname===new URL(r).pathname,Se=(e,r)=>{let t=[];if(!e)return t;for(let[s,n]of Object.entries(e))for(let[i,o]of Object.entries(n)){let a=J(o.uri,r);t.find(l=>cr(l.url,a))||t.push({groupId:s,id:o.uri.replace("/",""),type:s,label:i,language:o.language,url:a,default:o.default})}return t},ur=(e,r)=>e.mediaGroups?Se(e.mediaGroups.AUDIO,r):[],lr=(e,r)=>e.mediaGroups?Se(e.mediaGroups.SUBTITLES,r):[],dr=(e,r)=>e.playlists?e.playlists.map(t=>{let s=t.attributes?.BANDWIDTH,n=t.resolvedUri||J(t.uri,r),i={id:t.uri.replace("/",""),bitrate:Xt(s),url:n};return i.type="video",t.attributes.RESOLUTION&&(i.resolution=t.attributes.RESOLUTION,i.quality=xt(i.resolution)),t.attributes["VIDEO-RANGE"]&&(i.dynamicRange=t.attributes["VIDEO-RANGE"]),t.attributes.CODECS&&(i.codecs=t.attributes.CODECS),t.attributes["FRAME-RATE"]&&(i.frameRate=t.attributes["FRAME-RATE"]),i}):[],pr=(e=[],r)=>{let t=i=>{let o=i.resolvedUri||i.uri;if(!o.startsWith("https://")&&r.url){let a=jt(r.url)+"/";o=new URL(o,a).toString()}return{url:o,duration:i.duration,number:i.number,presentationTime:i.presentationTime}},s=e.map(t),n=e[0].map;if(e.length&&(n?.resolvedUri||n?.uri)){let i=n?.resolvedUri||J(n.uri,r.url);s.unshift({url:i,init:!0,duration:0,number:0,presentationTime:0})}return s},x=(e,r)=>{if(r.segments=pr(e.segments,r),e.contentProtection){r.protection={};let t=e.contentProtection["com.apple.fps.1_0"];t&&(r.protection.fairplay={keyFormat:t.attributes.KEYFORMAT,uri:t.attributes.URI,method:t.attributes.METHOD});let s=e.contentProtection["com.widevine.alpha"];s&&(r.protection.widevine={pssh:s.pssh,uri:s.attributes.schemeIdUri,keyId:s.attributes.keyId})}},X=e=>Promise.all(e.map(async r=>{let t=await ar(r.url);x(t,r)})),hr=async(e,r)=>{let t=be(e),s=dr(t,r),n=ur(t,r),i=lr(t,r);if(!t.playlists&&t.segments){let{pathname:a}=new URL(r);if(a.includes(".m4a")||a.includes(".mp3")||a.includes(".opus")){let l=ir({id:"audio"+Ce(a),label:"audio",type:"audio",codec:"",channels:2,jointObjectCoding:"",isDescriptive:!1,bitrate:NaN,duration:NaN,language:""});x(t,l),n.push(l)}else{let l=or({id:"video"+Ce(a),label:"video",type:"video",codec:"",dynamicRange:"",contentProtection:"",bitrate:NaN,duration:NaN,width:NaN,height:NaN,fps:NaN,language:""});x(t,l),s.push(l)}}else await Promise.all([X(s),X(n),X(i)]);return{tracks:{all:s.concat(n).concat(i),videos:s,audios:n,subtitles:i,withResolution:Jt(s),withVideoCodecs:rr(s),withVideoQuality:Zt(s),withAudioCodecs:sr(n),withAudioLanguages:er(n),withAudioChannels:nr(n),withSubtitleLanguages:tr(i)}}};ve.exports={parseManifest:hr}});var gr=me(),fr=Ae(),{filterByResolution:mr,filterByQuality:Cr,filterByCodecs:br,filterByLanguages:Sr,filterByChannels:vr}=k(),Ar=(e,r,t)=>{if(e.includes("<MPD"))return gr.parseManifest(e,r,t);if(e.includes("#EXTM3U"))return fr.parseManifest(e,r);throw new Error("Invalid manifest")};module.exports={parse:Ar,filterByResolution:mr,filterByQuality:Cr,filterByCodecs:br,filterByLanguages:Sr,filterByChannels:vr};
|
package/dist/dasha.mjs
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
var Z=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(r,t)=>(typeof require<"u"?require:r)[t]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var U=(e,r)=>()=>(r||e((r={exports:{}}).exports,r),r.exports);var te=U((Br,ee)=>{"use strict";function ye(e,r={}){var t=r.pos||0,s=!!r.keepComments,n=!!r.keepWhitespace,i="<",o=60,a=">",c=62,l=45,h=47,d=33,u=39,p=34,C=91,S=93;function v(g){for(var f=[];e[t];)if(e.charCodeAt(t)==o){if(e.charCodeAt(t+1)===h){var A=t+2;t=e.indexOf(a,t);var B=e.substring(A,t);if(B.indexOf(g)==-1){let b=e.substring(0,t).split(`
|
|
2
|
+
`);throw new Error(`Unexpected close tag
|
|
3
|
+
Line: `+(b.length-1)+`
|
|
4
|
+
Column: `+(b[b.length-1].length+1)+`
|
|
5
|
+
Char: `+e[t])}return t+1&&(t+=1),f}else if(e.charCodeAt(t+1)===d){if(e.charCodeAt(t+2)==l){let b=t;for(;t!==-1&&!(e.charCodeAt(t)===c&&e.charCodeAt(t-1)==l&&e.charCodeAt(t-2)==l&&t!=-1);)t=e.indexOf(a,t+1);t===-1&&(t=e.length),s&&f.push(e.substring(b,t+1))}else if(e.charCodeAt(t+2)===C&&e.charCodeAt(t+8)===C&&e.substr(t+3,5).toLowerCase()==="cdata"){var I=e.indexOf("]]>",t);I==-1?(f.push(e.substr(t+9)),t=e.length):(f.push(e.substring(t+9,I)),t=I+3);continue}else{let b=t+1;t+=2;for(var m=!1;(e.charCodeAt(t)!==c||m===!0)&&e[t];)e.charCodeAt(t)===C?m=!0:m===!0&&e.charCodeAt(t)===S&&(m=!1),t++;f.push(e.substring(b,t))}t++;continue}var N=L();f.push(N),N.tagName[0]==="?"&&(f.push(...N.children),N.children=[])}else{let b=O();if(n)b.length>0&&f.push(b);else{var R=b.trim();R.length>0&&f.push(R)}t++}return f}function O(){var g=t;return t=e.indexOf(i,t)-1,t===-2&&(t=e.length),e.slice(g,t+1)}var _=`\r
|
|
6
|
+
>/= `;function V(){for(var g=t;_.indexOf(e[t])===-1&&e[t];)t++;return e.slice(g,t)}var G=r.noChildNodes||["img","br","input","meta","link","hr"];function L(){t++;let g=V(),f={},A=[];for(;e.charCodeAt(t)!==c&&e[t];){var B=e.charCodeAt(t);if(B>64&&B<91||B>96&&B<123){for(var I=V(),m=e.charCodeAt(t);m&&m!==u&&m!==p&&!(m>64&&m<91||m>96&&m<123)&&m!==c;)t++,m=e.charCodeAt(t);if(m===u||m===p){var N=T();if(t===-1)return{tagName:g,attributes:f,children:A}}else N=null,t--;f[I]=N}t++}if(e.charCodeAt(t-1)!==h)if(g=="script"){let R=t+1;t=e.indexOf("</script>",t),A=[e.slice(R,t)],t+=9}else if(g=="style"){let R=t+1;t=e.indexOf("</style>",t),A=[e.slice(R,t)],t+=8}else G.indexOf(g)===-1?(t++,A=v(g)):t++;else t++;return{tagName:g,attributes:f,children:A}}function T(){var g=e[t],f=t+1;return t=e.indexOf(g,f),e.slice(f,t)}function $(){var g=new RegExp("\\s"+r.attrName+`\\s*=['"]`+r.attrValue+`['"]`).exec(e);return g?g.index:-1}let y=null;if(r.attrValue!==void 0)for(r.attrName=r.attrName||"id",y=[];(t=$())!==-1;)t=e.lastIndexOf("<",t),t!==-1&&y.push(L()),e=e.substr(t),t=0;else r.parseNode?y=L():y=v("");return r.filter&&(y=W(y,r.filter)),r.setPos&&(y.pos=t),y}function W(e,r,t=0,s=""){var n=[];return e.forEach(function(i,o){if(typeof i=="object"&&r(i,o,t,s)&&n.push(i),i.children){var a=W(i.children,r,t+1,(s?s+".":"")+o+"."+i.tagName);n=n.concat(a)}}),n}ee.exports={parse:ye,filter:W}});var D=U((Ur,ie)=>{"use strict";var re=(e,r=["Bytes","KB","MB","GB","TB"])=>{if(e==0)return`0 ${r[0]}`;let t=parseInt(Math.floor(Math.log(e)/Math.log(1024)));return t==0?e+" "+r[t]:(e/Math.pow(1024,t)).toFixed(1)+" "+r[t]},Ne=(e,r)=>({b:e*r,kb:e/1e3*r,mb:e/8e6*r,gb:e/8e9*r,toString(){return re(e*r)}}),Be=e=>({bps:e,kbps:e/1e3,mbps:e/8e6,gbps:e/8e9,toString(){return re(e,["bps","Kbps","Mbps","Gbps"])}}),se=[{width:7680,height:4320},{width:3840,height:2160},{width:2560,height:1440},{width:1920,height:1080},{width:1280,height:720},{width:1024,height:576},{width:854,height:480},{width:640,height:360},{width:426,height:240},{width:256,height:144}],Ue=e=>se.find(r=>r.height===e)?.width,ne=e=>se.find(r=>r.width===e)?.height,_e=e=>`${ne(e.width)||e.height}p`,Re=e=>{let r=Math.max(...e.map(t=>t.bitrate.bps));return e.find(t=>t.bitrate.bps===r)},Ee=e=>{let a=/P(?:(\d*)Y)?(?:(\d*)M)?(?:(\d*)D)?(?:T(?:(\d*)H)?(?:(\d*)M)?(?:([\d.]*)S)?)?/.exec(e);if(!a)return 0;let[c,l,h,d,u,p]=a.slice(1);return parseFloat(c||0)*31536e3+parseFloat(l||0)*2592e3+parseFloat(h||0)*86400+parseFloat(d||0)*3600+parseFloat(u||0)*60+parseFloat(p||0)},Le=e=>{try{return Intl.getCanonicalLocales(e),!0}catch{return}};ie.exports={parseSize:Ne,parseBitrate:Be,getWidth:Ue,getHeight:ne,getQualityLabel:_e,getBestTrack:Re,parseDuration:Ee,isLanguageTagValid:Le}});var Y=U((_r,oe)=>{"use strict";var{parseBitrate:Ie,getQualityLabel:De,parseSize:Fe}=D(),F={avc:"H.264",hevc:"H.265",vc1:"VC-1",vp8:"VP8",vp9:"VP9",av1:"AV1"},P={sdr:"SDR",hlg:"HLG",hdr10:"HDR10",hdr10p:"HDR10+",dv:"DV"},Q={Unspecified:0,BT_709:1,BT_601_625:5,BT_601_525:6,BT_2020_and_2100:9,SMPTE_ST_2113_and_EG_4321:12},q={Unspecified:0,BT_709:1,BT_601:6,BT_2020:14,BT_2100:15,BT_2100_PQ:16,BT_2100_HLG:18},Me={RGB:0,YCbCr_BT_709:1,YCbCr_BT_601_625:5,YCbCr_BT_601_525:6,YCbCr_BT_2020_and_2100:9,ICtCp_BT_2100:14},Oe=e=>{let r=e.toLowerCase().trim().split(".")[0],t=["avc1","avc2","avc3","dva1","dvav"],s=["hev1","hev2","hev3","hvc1","hvc2","hvc3","dvh1","dvhe","lhv1","lhe1"],n=["vc-1"],i=["vp08","vp8"],o=["vp09","vp9"],a=["av01"];if(t.includes(r))return F.avc;if(s.includes(r)||n.includes(r))return F.hevc;if(i.includes(r))return F.vp8;if(o.includes(r))return F.vp9;if(a.includes(r))return F.av1;throw new Error(`The MIME ${e} is not supported as video codec`)},Pe=(e,r,t)=>(r==5&&(r=q.BT_601),e==Q.Unspecified&&r==q.Unspecified&&t==Me.RGB||[Q.BT_601_625,Q.BT_601_525].includes(e)?P.sdr:q.BT_2100_PQ===r?P.hdr10:q.BT_2100_HLG===r?P.hlg:P.sdr),ke=({id:e,label:r,type:t,codec:s,dynamicRange:n,contentProtection:i,bitrate:o,duration:a,width:c,height:l,fps:h,language:d,segments:u})=>{let p=Ie(Number(o)),C=Number(c),S=Number(l),v=a?Fe(Number(o),Number(a)):void 0;return{id:e,label:r,type:t,codec:s,bitrate:p,size:v,protection:i,segments:u,dynamicRange:n,language:d,width:C,height:S,fps:Number(h),quality:De({width:C,height:S}),toString(){return["VIDEO",`[${s}, ${n}]`,d,`${c}x${l} @ ${p.kbps} kb/s, ${h} FPS`].join(" | ")}}},Ve=e=>{for(let r of e.toLowerCase().split(",")){let t=r.trim().split(".")[0];try{return Oe(t)}catch{continue}}throw new Error(`No MIME types matched any supported Video Codecs in ${e}`)},$e=(e,r=[],t=[])=>{if(["dva1","dvav","dvhe","dvh1"].some(u=>e.startsWith(u)))return P.dv;let n="urn:mpeg:mpegB:cicp:ColourPrimaries",i="urn:mpeg:mpegB:cicp:TransferCharacteristics",o="urn:mpeg:mpegB:cicp:MatrixCoefficients",a=[...t,...r],c=u=>a.filter(p=>p.attributes.schemeIdUri===u).map(p=>parseInt(p.attributes.value)),l=c(n).reduce((u,p)=>u+p,0),h=c(i).reduce((u,p)=>u+p,0),d=c(o).reduce((u,p)=>u+p,0);return Pe(l,h,d)};oe.exports={parseVideoCodec:Ve,parseDynamicRange:$e,createVideoTrack:ke,VIDEO_CODECS:F}});var k=U((Rr,pe)=>{"use strict";var{getBestTrack:ae}=D(),Ge=e=>e.toLowerCase().split(",").map(r=>r.trim().split(".")[0]),ce=e=>({width:r,height:t})=>e.filter(s=>(!r||s.width===r)&&(!t||s.height===t)),z=e=>r=>{if(!r?.length)return e;let t=e.filter(s=>r.includes(s.codec));return t.sort((s,n)=>n.bitrate.bps-s.bitrate.bps),t},qe=e=>z(e),ue=e=>r=>{if(!r)return[ae(e)];let t=String(r).includes("p")?r:`${r}p`,s=e.filter(n=>n.quality===t);return s.sort((n,i)=>i.bitrate.bps-n.bitrate.bps),s.length?s:[ae(e)]},He=e=>z(e),le=e=>(r=[],t)=>{if(!r.length)for(let o of e)r.includes(o.language)||r.push(o.language);let s=[];for(let o of r){let a=e.filter(c=>c.language?.startsWith(o));s.push(...a)}let n=[],i=[...new Set(s.map(o=>o.language))];for(let o of i){let a=s.filter(c=>c.language===o).slice(0,t);n.push(...a)}return n},de=e=>r=>{if(!r)return e;let t=typeof r=="string"?parseFloat(r):r;return e.filter(s=>s.channels===t)},We=e=>r=>r.length?e.filter(t=>r.some(s=>t.language?.startsWith(s)||t.label?.startsWith(s))):e,Qe=(e,r)=>ce(e)(r),Ye=(e,r)=>ue(e)(r),ze=(e,r)=>z(e)(r),je=(e,r,t)=>le(e)(r,t),Ke=(e,r)=>de(e)(r);pe.exports={parseMimes:Ge,createResolutionFilter:ce,createVideoCodecFilter:qe,createVideoQualityFilter:ue,createAudioCodecFilter:He,createAudioLanguageFilter:le,createAudioChannelsFilter:de,createSubtitleLanguageFilter:We,filterByResolution:Qe,filterByQuality:Ye,filterByCodecs:ze,filterByLanguages:je,filterByChannels:Ke}});var j=U((Er,he)=>{"use strict";var{parseMimes:Xe}=k(),{parseBitrate:xe,parseSize:Je}=D(),E={AAC:"AAC",AC3:"DD",EC3:"DD+",OPUS:"OPUS",OGG:"VORB",DTS:"DTS",ALAC:"ALAC",FLAC:"FLAC"},Ze=e=>{switch(e.toLowerCase().trim().split(".")[0]){case"mp4a":return E.AAC;case"ac-3":return E.AC3;case"ec-3":return E.EC3;case"opus":return E.OPUS;case"dtsc":return E.DTS;case"alac":return E.ALAC;case"flac":return E.FLAC;default:throw new Error(`The MIME ${e} is not supported as audio codec`)}},et=e=>{let r=Xe(e);for(let t of r)try{return Ze(t)}catch{continue}throw new Error(`No MIME types matched any supported Audio Codecs in ${e}`)},tt=(e=[])=>{let r="tag:dolby.com,2018:dash:EC3_ExtensionComplexityIndex:2018";for(let t of e)if(t.attributes.schemeIdUri===r)return parseInt(t.attributes.value)},rt=(e=[])=>{for(let r of e){let{schemeIdUri:t,value:s}=r.attributes,n=t=="urn:mpeg:dash:role:2011"&&s==="descriptive",i=t=="urn:tva:metadata:cs:AudioPurposeCS:2007"&&s==="1";if(n||i)return!0}return!1},st=e=>{let r=t=>t>="0"&&t<="9";if(typeof e=="string"){if(e.toUpperCase()=="A000")return 2;if(e.toUpperCase()=="F801")return 5.1;if(r(e.replace("ch","").replace(".","")[0]))return parseFloat(e.replace("ch",""));throw new Error(`Unsupported audio channels value, '${e}'`)}return parseFloat(e)},nt=({id:e,label:r,type:t,codec:s,channels:n,bitrate:i,duration:o,jointObjectCoding:a=0,isDescriptive:c=!1,language:l,segments:h})=>{let d=xe(Number(i)),u=st(n),p=o?Je(Number(i),Number(o)):void 0;return{id:e,label:r,type:t,codec:s,bitrate:d,size:p,language:l,segments:h,channels:u,jointObjectCoding:a,isDescriptive:c,toString(){return["AUDIO",`[${s}]`,`${u||"?"}`+(a?` (JOC ${a})`:""),`${d.kbps} kb/s`,l].join(" | ")}}};he.exports={AUDIO_CODECS:E,parseAudioCodec:et,createAudioTrack:nt,getDolbyDigitalPlusComplexityIndex:tt,checkIsDescriptive:rt}});var fe=U((Lr,ge)=>{"use strict";var{parseMimes:it}=k(),{parseBitrate:ot,parseSize:at}=D(),M={SubRip:"SRT",SubStationAlpha:"SSA",SubStationAlphav4:"ASS",TimedTextMarkupLang:"TTML",WebVTT:"VTT",fTTML:"STPP",fVTT:"WVTT"},ct=e=>{switch(e.toLowerCase().trim().split(".")[0]){case"srt":case"x-subrip":return M.SubRip;case"ssa":return M.SubStationAlpha;case"ass":return M.SubStationAlphav4;case"ttml":return M.TimedTextMarkupLang;case"vtt":return M.WebVTT;case"stpp":return M.fTTML;case"wvtt":return M.fVTT;default:throw new Error(`The MIME ${e} is not supported as subtitle codec`)}},ut=e=>{let r=it(e);for(let t of r)try{return ct(t)}catch{continue}throw new Error(`No MIME types matched any supported Subtitle Codecs in ${e}`)},lt=(e=[])=>{for(let r of e)if(r.attributes.schemeIdUri==="urn:mpeg:dash:role:2011"&&r.attributes.value==="caption")return!0;return!1},dt=(e=[])=>{for(let r of e){let{schemeIdUri:t,value:s}=r.attributes;if(t==="urn:tva:metadata:cs:AudioPurposeCS:2007"&&s==="2")return!0}return!1},pt=(e=[])=>{for(let r of e)if(r.attributes.schemeIdUri==="urn:mpeg:dash:role:2011"&&(r.attributes.value==="forced-subtitle"||r.attributes.value==="forced_subtitle"))return!0;return!1},ht=({id:e,label:r,bitrate:t,duration:s,type:n,codec:i,isClosedCaption:o,isSdh:a,isForced:c,language:l,segments:h})=>{let d=ot(Number(t)),u=s?at(Number(t),Number(s)):void 0;return{id:e,label:r,bitrate:d,size:u,type:n,codec:i,isClosedCaption:o,isSdh:a,isForced:c,segments:h,language:l,toString(){return["SUBTITLE",`[${i}]`,l].join(" | ")}}};ge.exports={parseSubtitleCodec:ut,checkIsClosedCaption:lt,checkIsSdh:dt,checkIsForced:pt,createSubtitleTrack:ht}});var Ce=U((Ir,me)=>{"use strict";var gt=te(),{parseDuration:ft,isLanguageTagValid:mt}=D(),{parseVideoCodec:Ct,parseDynamicRange:bt,createVideoTrack:St}=Y(),{parseAudioCodec:vt,createAudioTrack:At,getDolbyDigitalPlusComplexityIndex:wt,checkIsDescriptive:Tt}=j(),{parseSubtitleCodec:yt,checkIsClosedCaption:Nt,checkIsSdh:Bt,checkIsForced:Ut,createSubtitleTrack:_t}=fe(),{createResolutionFilter:Rt,createVideoQualityFilter:Et,createAudioLanguageFilter:Lt,createSubtitleLanguageFilter:It,createVideoCodecFilter:Dt,createAudioCodecFilter:Ft,createAudioChannelsFilter:Mt}=k(),H=e=>e&&(Array.isArray(e)?e.get=r=>H(e.find(t=>t.tagName===r)):(e.getAttr=r=>e.attributes[r],e.getChild=r=>{let t=e.children.find(n=>n.tagName===r);return!r&&typeof e.children?.[0]=="string"?e.children[0]:H(t)},e.set=(r,t)=>e.attributes[r]=t,e.get=r=>e.getAttr(r)||e.getChild(r),e.getNumber=r=>Number(e.find(r)),e.getAll=r=>e.children.filter(t=>t.tagName===r).map(H),e.getBaseUrls=()=>e.getAll("BaseURL").map(r=>r.children[0]),e.getBaseUrl=()=>e.getBaseUrls()[0]),e),Ot=(e,r)=>{let t=e.get,s=e.getAll,n=o=>t(o)||r.get(o),i=o=>[...s(o),...r.getAll(o)].filter(Boolean);return e.get=n,e.getAll=i,{get:n,getAll:i}},Pt=(e,r,t,s)=>{let n=r.getBaseUrl();return n?n.startsWith("https://")||(n=new URL(n,e).toString()):n=e,(t.getBaseUrl()||n)&&(n=new URL(t.getBaseUrl()||"",n).toString()),new URL(s.getBaseUrl()||"",n).toString()},kt=e=>{let r=e.get("mimeType"),t=e.get("contentType")||r?.split("/")[0];if(!t&&!r)throw new Error("Unable to determine the format of a Representation, cannot continue...");return{contentType:t,mimeType:r}},Vt=(e,r,t)=>r==="text"&&!t.includes("mp4")?t.split("/")[1]:e.get("codecs"),$t=(e,r,t)=>{let s="",n=[],i=e.get("lang"),o=e.get("id");if(e&&(n.push(i),o)){let a=o.match(/\w+_(\w+)=\d+/);a&&a[1]&&n.push(a[1])}n.push(r.get("lang")),t&&n.push(t);for(let a of n){let c=(a||"").trim();!mt(c)||c.startsWith("und")||(s=c)}return s},K=(e,r)=>{let t=e;for(let[s,n]of Object.entries(r))t=t.replace("$"+s+"$",n);return t},Gt=(e,r,t)=>{for(let s of["initialization","media"]){let n=e.get(s);if(n){if(!n.startsWith("https://")){if(!r)throw new Error("Resolved Segment URL is not absolute, and no Base URL is available.");n=new URL(n,r).toString()}if(!new URL(n).search){let i=new URL(t).search;i&&(n+=`?${i}`)}e.set(s,n)}}},qt=(e,r,t,s)=>{let n=[],i=0;for(let c of e.getAll("S")){let l=Number(c.get("t")),h=Number(c.get("r")||0),d=Number(c.get("d"));l&&(i=l);for(let u=0;u<h+1;u++)n.push(i),i+=d}let o=[],a=[...Array(n.length).keys()].map(c=>c+s);for(let c=0;c<n.length;c++){let l=n[c],h=a[c],d=K(r.get("media"),{Bandwidth:t.get("bandwidth"),RepresentationID:t.get("id"),Number:h,Time:l});o.push({url:d})}return o},Ht=(e,r,t,s,n)=>{let i=Number(e.get("startNumber")||1),o=e.get("SegmentTimeline");Gt(e,r,t);let a=parseFloat(e.get("duration")),c=parseFloat(e.get("timescale")||1),h=s?Math.ceil(s/(a/c)):35,d=n.get("bandwidth"),u=n.get("id"),p=[];if(o)p.push(...qt(o,e,n,i));else for(let S=i;S<i+h;S++){let v=K(e.get("media"),{Bandwidth:d,RepresentationID:u,Number:S,Time:S});p.push({url:v})}let C=e.get("initialization");if(C){let S=K(C,{Bandwidth:d,RepresentationID:u});p.unshift({url:S,init:!0})}return p},Wt=(e,r)=>{let t=e.get("SegmentURL"),s=[];for(let n of t){let i=n.get("media");i?i.startsWith("https://")||(i=new URL(i,r).toString()):i=r,s.push({url:i,range:n.get("mediaRange")})}return s},Qt=async(e,r)=>{let t=e.get("Initialization");return{url:r,range:""}},Yt=e=>{for(let r of e)if(r.url.includes("&")){let s=new URL(r.url),n=new URLSearchParams(s.searchParams.toString()).entries();for(let[i,o]of n)s.searchParams.delete(i),s.searchParams.append(i.replaceAll("amp;",""),o);r.url=s.toString()}},zt={"urn:mpeg:dash:mp4protection:2011":"common","urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95":"playready","urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed":"widevine"},jt=e=>{let r={};for(let t of e){let s=t.get("schemeIdUri")?.toLowerCase(),n=t.get("value"),i=t.get("cenc:pssh")?.get(),o=t.get("cenc:default_KID"),a={id:s,value:n,pssh:i,defaultKeyId:o};r[zt[s]]=a}return r},Kt=async(e,r,t)=>{let s=H(gt.parse(e)).get("MPD"),n=s.get("Period"),i=n.get("duration")||s.get("mediaPresentationDuration"),o=ft(i),a=[],c=[],l=[];for(let h of n.getAll("AdaptationSet"))for(let d of h.getAll("Representation")){let{get:u,getAll:p}=Ot(d,h),{contentType:C,mimeType:S}=kt(d),v=Vt(d,C,S),O=$t(d,h,t),_=Pt(r,s,n,d),V=u("SegmentTemplate"),G=u("SegmentList"),L=u("SegmentBase"),T=[];if(V){let w=Ht(V,_,r,o,d);T.push(...w)}else if(G){let w=Wt(G,_);T.push(...w)}else if(L){let w=await Qt(L,_);T.push(w)}else if(_)T.push({url:_});else throw new Error("Could not find a way to get segments from this MPD manifest.");Yt(T);let $=u("label"),y=u("frameRate")??L?.attributes.timescale,g=u("width")??0,f=u("height")??0,A=u("bandwidth"),B=p("SupplementalProperty"),I=p("EssentialProperty"),m=h.getAll("Accessibility"),N=h.getAll("Role"),R=p("ContentProtection"),b=[new URL(_).hostname,C,v,A,O,s.get("id"),n.get("id"),u("id"),u("audioTrackId")].filter(Boolean).join("-").replaceAll("/","-");switch(C){case"video":{let w=St({id:b,label:$,type:C,codec:Ct(v),dynamicRange:bt(v,B,I),contentProtection:jt(R),bitrate:A,duration:o,width:g,height:f,fps:y,language:O,segments:T});a.push(w);break}case"audio":{let w=At({id:b,label:$,type:C,codec:vt(v),channels:u("AudioChannelConfiguration")?.get("value"),jointObjectCoding:wt(B),isDescriptive:Tt(m),bitrate:A,duration:o,language:O,segments:T});c.push(w);break}case"text":{let w=_t({id:b,label:$,type:C,codec:yt(v||"vtt"),isClosedCaption:Nt(N),isSdh:Bt(m),isForced:Ut(N),bitrate:A,duration:o,language:O,segments:T});l.push(w);break}case"image":break;default:throw new Error(`Unknown content type: ${C}`)}}return a.sort((h,d)=>d.bitrate.bps-h.bitrate.bps),{duration:o,tracks:{all:a.concat(c).concat(l),videos:a,audios:c,subtitles:l,withResolution:Rt(a),withVideoCodecs:Dt(a),withVideoQuality:Et(a),withAudioCodecs:Ft(c),withAudioLanguages:Lt(c),withAudioChannels:Mt(c),withSubtitleLanguages:It(l)}}};me.exports={parseManifest:Kt}});var we=U((Dr,Ae)=>{"use strict";var{dirname:Xt,basename:be}=Z("node:path"),xt=Z("m3u8-parser"),{parseBitrate:Jt,getQualityLabel:Zt}=D(),{createResolutionFilter:er,createVideoQualityFilter:tr,createAudioLanguageFilter:rr,createSubtitleLanguageFilter:sr,createVideoCodecFilter:nr,createAudioCodecFilter:ir,createAudioChannelsFilter:or}=k(),{createAudioTrack:ar}=j(),{createVideoTrack:cr}=Y(),Se=e=>{let r=new xt.Parser;return r.push(e),r.end(),r.manifest},ur=async e=>{let r=await fetch(e);if(!r.ok)throw new Error(`Failed to fetch playlist (${r.status}): ${e}`);let t=await r.text();return Se(t)},J=(e,r)=>{let t=e;return t.startsWith("https://")||(t=new URL(t,r).toString()+new URL(r).search),t},lr=(e,r)=>new URL(e).pathname===new URL(r).pathname,ve=(e,r)=>{let t=[];if(!e)return t;for(let[s,n]of Object.entries(e))for(let[i,o]of Object.entries(n)){let a=J(o.uri,r);t.find(l=>lr(l.url,a))||t.push({groupId:s,id:o.uri.replace("/",""),type:s,label:i,language:o.language,url:a,default:o.default})}return t},dr=(e,r)=>e.mediaGroups?ve(e.mediaGroups.AUDIO,r):[],pr=(e,r)=>e.mediaGroups?ve(e.mediaGroups.SUBTITLES,r):[],hr=(e,r)=>e.playlists?e.playlists.map(t=>{let s=t.attributes?.BANDWIDTH,n=t.resolvedUri||J(t.uri,r),i={id:t.uri.replace("/",""),bitrate:Jt(s),url:n};return i.type="video",t.attributes.RESOLUTION&&(i.resolution=t.attributes.RESOLUTION,i.quality=Zt(i.resolution)),t.attributes["VIDEO-RANGE"]&&(i.dynamicRange=t.attributes["VIDEO-RANGE"]),t.attributes.CODECS&&(i.codecs=t.attributes.CODECS),t.attributes["FRAME-RATE"]&&(i.frameRate=t.attributes["FRAME-RATE"]),i}):[],gr=(e=[],r)=>{let t=i=>{let o=i.resolvedUri||i.uri;if(!o.startsWith("https://")&&r.url){let a=Xt(r.url)+"/";o=new URL(o,a).toString()}return{url:o,duration:i.duration,number:i.number,presentationTime:i.presentationTime}},s=e.map(t),n=e[0].map;if(e.length&&(n?.resolvedUri||n?.uri)){let i=n?.resolvedUri||J(n.uri,r.url);s.unshift({url:i,init:!0,duration:0,number:0,presentationTime:0})}return s},x=(e,r)=>{if(r.segments=gr(e.segments,r),e.contentProtection){r.protection={};let t=e.contentProtection["com.apple.fps.1_0"];t&&(r.protection.fairplay={keyFormat:t.attributes.KEYFORMAT,uri:t.attributes.URI,method:t.attributes.METHOD});let s=e.contentProtection["com.widevine.alpha"];s&&(r.protection.widevine={pssh:s.pssh,uri:s.attributes.schemeIdUri,keyId:s.attributes.keyId})}},X=e=>Promise.all(e.map(async r=>{let t=await ur(r.url);x(t,r)})),fr=async(e,r)=>{let t=Se(e),s=hr(t,r),n=dr(t,r),i=pr(t,r);if(!t.playlists&&t.segments){let{pathname:a}=new URL(r);if(a.includes(".m4a")||a.includes(".mp3")||a.includes(".opus")){let l=ar({id:"audio"+be(a),label:"audio",type:"audio",codec:"",channels:2,jointObjectCoding:"",isDescriptive:!1,bitrate:NaN,duration:NaN,language:""});x(t,l),n.push(l)}else{let l=cr({id:"video"+be(a),label:"video",type:"video",codec:"",dynamicRange:"",contentProtection:"",bitrate:NaN,duration:NaN,width:NaN,height:NaN,fps:NaN,language:""});x(t,l),s.push(l)}}else await Promise.all([X(s),X(n),X(i)]);return{tracks:{all:s.concat(n).concat(i),videos:s,audios:n,subtitles:i,withResolution:er(s),withVideoCodecs:nr(s),withVideoQuality:tr(s),withAudioCodecs:ir(n),withAudioLanguages:rr(n),withAudioChannels:or(n),withSubtitleLanguages:sr(i)}}};Ae.exports={parseManifest:fr}});var yr=U((Fr,Te)=>{var mr=Ce(),Cr=we(),{filterByResolution:br,filterByQuality:Sr,filterByCodecs:vr,filterByLanguages:Ar,filterByChannels:wr}=k(),Tr=(e,r,t)=>{if(e.includes("<MPD"))return mr.parseManifest(e,r,t);if(e.includes("#EXTM3U"))return Cr.parseManifest(e,r);throw new Error("Invalid manifest")};Te.exports={parse:Tr,filterByResolution:br,filterByQuality:Sr,filterByCodecs:vr,filterByLanguages:Ar,filterByChannels:wr}});export default yr();
|
package/package.json
CHANGED
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dasha",
|
|
3
|
-
"version": "3.0
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "3.1.0",
|
|
4
|
+
"description": "Streaming manifest parser",
|
|
5
|
+
"files": [
|
|
6
|
+
"dist",
|
|
7
|
+
"types"
|
|
8
|
+
],
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "npm run lint && npm run types && node --test",
|
|
11
|
+
"types": "tsc -p tsconfig.json",
|
|
12
|
+
"lint": "eslint . && prettier --check .",
|
|
13
|
+
"fix": "eslint . --fix && prettier --write .",
|
|
14
|
+
"build": "tsup dasha.js --format esm,cjs --minify",
|
|
15
|
+
"prepublishOnly": "npm run build"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/vitalygashkov/dasha"
|
|
20
|
+
},
|
|
7
21
|
"keywords": [
|
|
8
22
|
"mpeg",
|
|
9
23
|
"dash",
|
|
@@ -11,50 +25,53 @@
|
|
|
11
25
|
"adaptive",
|
|
12
26
|
"mpd",
|
|
13
27
|
"m3u8",
|
|
14
|
-
"manifest"
|
|
28
|
+
"manifest",
|
|
29
|
+
"playlist"
|
|
15
30
|
],
|
|
31
|
+
"author": "Vitaly Gashkov <vitalygashkov@vk.com>",
|
|
32
|
+
"license": "MIT",
|
|
16
33
|
"readmeFilename": "README.md",
|
|
17
|
-
"repository": {
|
|
18
|
-
"type": "git",
|
|
19
|
-
"url": "https://github.com/vitalygashkov/dasha"
|
|
20
|
-
},
|
|
21
34
|
"bugs": {
|
|
22
35
|
"url": "https://github.com/vitalygashkov/dasha/issues",
|
|
23
36
|
"email": "vitalygashkov@vk.com"
|
|
24
37
|
},
|
|
25
|
-
"main": "dasha.js",
|
|
26
|
-
"types": "types/dasha.d.ts",
|
|
27
38
|
"funding": [
|
|
28
39
|
{
|
|
29
40
|
"type": "individual",
|
|
30
|
-
"url": "https://boosty.to/vitalygashkov
|
|
41
|
+
"url": "https://boosty.to/vitalygashkov"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"type": "patreon",
|
|
45
|
+
"url": "https://www.patreon.com/vitalygashkov"
|
|
31
46
|
}
|
|
32
47
|
],
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
48
|
+
"exports": {
|
|
49
|
+
".": {
|
|
50
|
+
"require": {
|
|
51
|
+
"types": "./types/dasha.d.ts",
|
|
52
|
+
"default": "./dist/dasha.js"
|
|
53
|
+
},
|
|
54
|
+
"import": {
|
|
55
|
+
"types": "./types/dasha.d.ts",
|
|
56
|
+
"default": "./dist/dasha.mjs"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"engines": {
|
|
61
|
+
"node": "20 || 21 || 22"
|
|
46
62
|
},
|
|
47
63
|
"dependencies": {
|
|
48
|
-
"m3u8-parser": "^7.
|
|
64
|
+
"m3u8-parser": "^7.2.0"
|
|
49
65
|
},
|
|
50
66
|
"devDependencies": {
|
|
51
|
-
"@
|
|
52
|
-
"
|
|
67
|
+
"@eslint/js": "^9.13.0",
|
|
68
|
+
"@types/node": "^22.7.9",
|
|
69
|
+
"eslint": "^9.13.0",
|
|
53
70
|
"eslint-config-prettier": "^9.1.0",
|
|
54
|
-
"eslint-plugin-
|
|
55
|
-
"
|
|
71
|
+
"eslint-plugin-prettier": "^5.2.1",
|
|
72
|
+
"globals": "^15.11.0",
|
|
56
73
|
"prettier": "^3.3.3",
|
|
57
|
-
"tsup": "^8.
|
|
58
|
-
"typescript": "^5.
|
|
74
|
+
"tsup": "^8.3.0",
|
|
75
|
+
"typescript": "^5.6.3"
|
|
59
76
|
}
|
|
60
77
|
}
|
package/types/dasha.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
export function parse(
|
|
1
|
+
export function parse(
|
|
2
|
+
text: string,
|
|
3
|
+
url?: string,
|
|
4
|
+
fallbackLanguage?: string,
|
|
5
|
+
): Promise<Manifest>;
|
|
2
6
|
|
|
3
7
|
export interface Manifest {
|
|
4
8
|
duration?: number;
|
|
@@ -7,25 +11,46 @@ export interface Manifest {
|
|
|
7
11
|
videos: VideoTrack[];
|
|
8
12
|
audios: AudioTrack[];
|
|
9
13
|
subtitles: SubtitleTrack[];
|
|
10
|
-
withResolution(resolution: {
|
|
14
|
+
withResolution(resolution: {
|
|
15
|
+
width?: string;
|
|
16
|
+
height?: string;
|
|
17
|
+
}): VideoTrack[];
|
|
11
18
|
withVideoCodecs(codecs: VideoCodec[]): VideoTrack[];
|
|
12
19
|
withVideoQuality(quality: number | string): VideoTrack[];
|
|
13
20
|
withAudioCodecs(codecs: AudioCodec[]): AudioTrack[];
|
|
14
|
-
withAudioLanguages(
|
|
21
|
+
withAudioLanguages(
|
|
22
|
+
languages: string[],
|
|
23
|
+
maxTracksPerLanguage?: number,
|
|
24
|
+
): AudioTrack[];
|
|
15
25
|
withSubtitleLanguages(languages: string[]): SubtitleTrack[];
|
|
16
26
|
};
|
|
17
27
|
}
|
|
18
28
|
|
|
19
|
-
export function filterByResolution(resolution: {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
29
|
+
export function filterByResolution(resolution: {
|
|
30
|
+
width?: string;
|
|
31
|
+
height?: string;
|
|
32
|
+
}): VideoTrack[];
|
|
33
|
+
export function filterByCodecs(
|
|
34
|
+
tracks: VideoTrack[],
|
|
35
|
+
codecs: VideoCodec[],
|
|
36
|
+
): VideoTrack[];
|
|
37
|
+
export function filterByCodecs(
|
|
38
|
+
tracks: AudioTrack[],
|
|
39
|
+
codecs: AudioCodec[],
|
|
40
|
+
): AudioTrack[];
|
|
41
|
+
export function filterByQuality(
|
|
42
|
+
tracks: VideoTrack[],
|
|
43
|
+
quality: number | string,
|
|
44
|
+
): VideoTrack[];
|
|
23
45
|
export function filterByLanguages(
|
|
24
46
|
tracks: AudioTrack[],
|
|
25
47
|
languages: string[],
|
|
26
|
-
maxTracksPerLanguage?: number
|
|
48
|
+
maxTracksPerLanguage?: number,
|
|
49
|
+
): AudioTrack[];
|
|
50
|
+
export function filterByChannels(
|
|
51
|
+
tracks: AudioTrack[],
|
|
52
|
+
channels: number | string,
|
|
27
53
|
): AudioTrack[];
|
|
28
|
-
export function filterByChannels(tracks: AudioTrack[], channels: number | string): AudioTrack[];
|
|
29
54
|
|
|
30
55
|
export interface Track {
|
|
31
56
|
id: string;
|
|
@@ -78,13 +103,30 @@ export interface VideoTrack extends Track {
|
|
|
78
103
|
bitrate: Bitrate;
|
|
79
104
|
width: number;
|
|
80
105
|
height: number;
|
|
81
|
-
quality:
|
|
106
|
+
quality:
|
|
107
|
+
| '144p'
|
|
108
|
+
| '240p'
|
|
109
|
+
| '360p'
|
|
110
|
+
| '480p'
|
|
111
|
+
| '720p'
|
|
112
|
+
| '1080p'
|
|
113
|
+
| '2160p'
|
|
114
|
+
| '4320p'
|
|
115
|
+
| string;
|
|
82
116
|
dynamicRange: DynamicRange;
|
|
83
117
|
fps?: string;
|
|
84
118
|
language?: string;
|
|
85
119
|
}
|
|
86
120
|
|
|
87
|
-
export type AudioCodec =
|
|
121
|
+
export type AudioCodec =
|
|
122
|
+
| 'AAC'
|
|
123
|
+
| 'DD'
|
|
124
|
+
| 'DD+'
|
|
125
|
+
| 'OPUS'
|
|
126
|
+
| 'VORB'
|
|
127
|
+
| 'DTS'
|
|
128
|
+
| 'ALAC'
|
|
129
|
+
| 'FLAC';
|
|
88
130
|
|
|
89
131
|
export interface AudioTrack extends Track {
|
|
90
132
|
type: 'audio';
|
|
@@ -96,7 +138,15 @@ export interface AudioTrack extends Track {
|
|
|
96
138
|
isDescriptive?: boolean;
|
|
97
139
|
}
|
|
98
140
|
|
|
99
|
-
export type SubtitleCodec =
|
|
141
|
+
export type SubtitleCodec =
|
|
142
|
+
| 'SRT'
|
|
143
|
+
| 'SSA'
|
|
144
|
+
| 'ASS'
|
|
145
|
+
| 'TTML'
|
|
146
|
+
| 'VTT'
|
|
147
|
+
| 'STPP'
|
|
148
|
+
| 'fTTML'
|
|
149
|
+
| 'fVTT';
|
|
100
150
|
|
|
101
151
|
export interface SubtitleTrack extends Track {
|
|
102
152
|
type: 'text';
|
package/dasha.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const dash = require('./lib/dash');
|
|
4
|
-
const hls = require('./lib/hls');
|
|
5
|
-
const {
|
|
6
|
-
filterByResolution,
|
|
7
|
-
filterByQuality,
|
|
8
|
-
filterByCodecs,
|
|
9
|
-
filterByLanguages,
|
|
10
|
-
filterByChannels,
|
|
11
|
-
} = require('./lib/track');
|
|
12
|
-
|
|
13
|
-
const parse = (text, url, fallbackLanguage) => {
|
|
14
|
-
if (text.includes('<MPD')) return dash.parseManifest(text, url, fallbackLanguage);
|
|
15
|
-
else if (text.includes('#EXTM3U')) return hls.parseManifest(text, url);
|
|
16
|
-
else throw new Error('Invalid manifest');
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
module.exports = {
|
|
20
|
-
parse,
|
|
21
|
-
filterByResolution,
|
|
22
|
-
filterByQuality,
|
|
23
|
-
filterByCodecs,
|
|
24
|
-
filterByLanguages,
|
|
25
|
-
filterByChannels,
|
|
26
|
-
};
|
package/lib/audio.js
DELETED
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { parseMimes } = require('./track');
|
|
4
|
-
const { parseBitrate, parseSize } = require('./util');
|
|
5
|
-
|
|
6
|
-
const AUDIO_CODECS = {
|
|
7
|
-
AAC: 'AAC', // https://wikipedia.org/wiki/Advanced_Audio_Coding
|
|
8
|
-
AC3: 'DD', // https://wikipedia.org/wiki/Dolby_Digital
|
|
9
|
-
EC3: 'DD+', // https://wikipedia.org/wiki/Dolby_Digital_Plus
|
|
10
|
-
OPUS: 'OPUS', // https://wikipedia.org/wiki/Opus_(audio_format)
|
|
11
|
-
OGG: 'VORB', // https://wikipedia.org/wiki/Vorbis
|
|
12
|
-
DTS: 'DTS', // https://en.wikipedia.org/wiki/DTS_(company)#DTS_Digital_Surround
|
|
13
|
-
ALAC: 'ALAC', // https://en.wikipedia.org/wiki/Apple_Lossless_Audio_Codec
|
|
14
|
-
FLAC: 'FLAC', // https://en.wikipedia.org/wiki/FLAC
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
const parseAudioCodecFromMime = (mime) => {
|
|
18
|
-
const target = mime.toLowerCase().trim().split('.')[0];
|
|
19
|
-
switch (target) {
|
|
20
|
-
case 'mp4a':
|
|
21
|
-
return AUDIO_CODECS.AAC;
|
|
22
|
-
case 'ac-3':
|
|
23
|
-
return AUDIO_CODECS.AC3;
|
|
24
|
-
case 'ec-3':
|
|
25
|
-
return AUDIO_CODECS.EC3;
|
|
26
|
-
case 'opus':
|
|
27
|
-
return AUDIO_CODECS.OPUS;
|
|
28
|
-
case 'dtsc':
|
|
29
|
-
return AUDIO_CODECS.DTS;
|
|
30
|
-
case 'alac':
|
|
31
|
-
return AUDIO_CODECS.ALAC;
|
|
32
|
-
case 'flac':
|
|
33
|
-
return AUDIO_CODECS.FLAC;
|
|
34
|
-
default:
|
|
35
|
-
throw new Error(`The MIME ${mime} is not supported as audio codec`);
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const parseAudioCodec = (codecs) => {
|
|
40
|
-
const mimes = parseMimes(codecs);
|
|
41
|
-
for (const mime of mimes) {
|
|
42
|
-
try {
|
|
43
|
-
return parseAudioCodecFromMime(mime);
|
|
44
|
-
} catch (e) {
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
throw new Error(`No MIME types matched any supported Audio Codecs in ${codecs}`);
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
// https://professionalsupport.dolby.com/s/article/What-is-Dolby-Digital-Plus-JOC-Joint-Object-Coding?language=en_US
|
|
52
|
-
const getDolbyDigitalPlusComplexityIndex = (supplementalProps = []) => {
|
|
53
|
-
const targetScheme = 'tag:dolby.com,2018:dash:EC3_ExtensionComplexityIndex:2018';
|
|
54
|
-
for (const prop of supplementalProps)
|
|
55
|
-
if (prop.attributes.schemeIdUri === targetScheme) return parseInt(prop.attributes.value);
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
const checkIsDescriptive = (accessibilities = []) => {
|
|
59
|
-
for (const accessibility of accessibilities) {
|
|
60
|
-
const { schemeIdUri, value } = accessibility.attributes;
|
|
61
|
-
const firstMatch = schemeIdUri == 'urn:mpeg:dash:role:2011' && value === 'descriptive';
|
|
62
|
-
const secondMatch = schemeIdUri == 'urn:tva:metadata:cs:AudioPurposeCS:2007' && value === '1';
|
|
63
|
-
const isDescriptive = firstMatch || secondMatch;
|
|
64
|
-
if (isDescriptive) return true;
|
|
65
|
-
}
|
|
66
|
-
return false;
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
const parseChannels = (channels) => {
|
|
70
|
-
const isDigit = (char) => char >= '0' && char <= '9';
|
|
71
|
-
if (typeof channels === 'string') {
|
|
72
|
-
if (channels.toUpperCase() == 'A000') return 2.0;
|
|
73
|
-
else if (channels.toUpperCase() == 'F801') return 5.1;
|
|
74
|
-
else if (isDigit(channels.replace('ch', '').replace('.', '')[0]))
|
|
75
|
-
// e.g., '2ch', '2', '2.0', '5.1ch', '5.1'
|
|
76
|
-
return parseFloat(channels.replace('ch', ''));
|
|
77
|
-
throw new Error(`Unsupported audio channels value, '${channels}'`);
|
|
78
|
-
}
|
|
79
|
-
return parseFloat(channels);
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
const createAudioTrack = ({
|
|
83
|
-
id,
|
|
84
|
-
label,
|
|
85
|
-
type,
|
|
86
|
-
codec,
|
|
87
|
-
channels,
|
|
88
|
-
bitrate,
|
|
89
|
-
duration,
|
|
90
|
-
jointObjectCoding = 0,
|
|
91
|
-
isDescriptive = false,
|
|
92
|
-
language,
|
|
93
|
-
segments,
|
|
94
|
-
}) => {
|
|
95
|
-
const parsedBitrate = parseBitrate(Number(bitrate));
|
|
96
|
-
const parsedChannels = parseChannels(channels);
|
|
97
|
-
const size = duration ? parseSize(Number(bitrate), Number(duration)) : undefined;
|
|
98
|
-
return {
|
|
99
|
-
id,
|
|
100
|
-
label,
|
|
101
|
-
type,
|
|
102
|
-
codec,
|
|
103
|
-
bitrate: parsedBitrate,
|
|
104
|
-
size,
|
|
105
|
-
language,
|
|
106
|
-
segments,
|
|
107
|
-
channels: parsedChannels,
|
|
108
|
-
jointObjectCoding,
|
|
109
|
-
isDescriptive,
|
|
110
|
-
toString() {
|
|
111
|
-
return [
|
|
112
|
-
'AUDIO',
|
|
113
|
-
`[${codec}]`,
|
|
114
|
-
`${parsedChannels || '?'}` + (jointObjectCoding ? ` (JOC ${jointObjectCoding})` : ''),
|
|
115
|
-
`${parsedBitrate.kbps} kb/s`,
|
|
116
|
-
language,
|
|
117
|
-
].join(' | ');
|
|
118
|
-
},
|
|
119
|
-
};
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
module.exports = {
|
|
123
|
-
AUDIO_CODECS,
|
|
124
|
-
parseAudioCodec,
|
|
125
|
-
createAudioTrack,
|
|
126
|
-
getDolbyDigitalPlusComplexityIndex,
|
|
127
|
-
checkIsDescriptive,
|
|
128
|
-
};
|