@transmitlive/m3u8-parser 4.7.2-beta.6 → 7.1.0-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -2
- package/dist/m3u8-parser.cjs.js +446 -302
- package/dist/m3u8-parser.es.js +443 -297
- package/dist/m3u8-parser.js +462 -351
- package/dist/m3u8-parser.min.js +2 -2
- package/package.json +6 -11
- package/src/parse-stream.js +93 -18
- package/src/parser.js +120 -9
- package/test/fixtures/integration/absoluteUris.js +1 -0
- package/test/fixtures/integration/allowCache.js +1 -0
- package/test/fixtures/integration/allowCacheInvalid.js +1 -0
- package/test/fixtures/integration/alternateAudio.js +1 -0
- package/test/fixtures/integration/alternateVideo.js +1 -0
- package/test/fixtures/integration/brightcove.js +1 -0
- package/test/fixtures/integration/byteRange.js +1 -0
- package/test/fixtures/integration/dateTime.js +3 -0
- package/test/fixtures/integration/diff-init-key.js +1 -0
- package/test/fixtures/integration/disallowCache.js +1 -0
- package/test/fixtures/integration/disc-sequence.js +9 -4
- package/test/fixtures/integration/discontinuity.js +19 -9
- package/test/fixtures/integration/domainUris.js +1 -0
- package/test/fixtures/integration/empty.js +1 -0
- package/test/fixtures/integration/emptyAllowCache.js +1 -0
- package/test/fixtures/integration/emptyMediaSequence.js +9 -4
- package/test/fixtures/integration/emptyPlaylistType.js +1 -0
- package/test/fixtures/integration/emptyTargetDuration.js +1 -0
- package/test/fixtures/integration/encrypted.js +1 -0
- package/test/fixtures/integration/event.js +1 -0
- package/test/fixtures/integration/extXPlaylistTypeInvalidPlaylist.js +3 -1
- package/test/fixtures/integration/extinf.js +3 -1
- package/test/fixtures/integration/fmp4.js +3 -1
- package/test/fixtures/integration/headerOnly.js +1 -0
- package/test/fixtures/integration/invalidAllowCache.js +1 -0
- package/test/fixtures/integration/invalidMediaSequence.js +9 -4
- package/test/fixtures/integration/invalidPlaylistType.js +1 -0
- package/test/fixtures/integration/invalidTargetDuration.js +1 -0
- package/test/fixtures/integration/liveMissingSegmentDuration.js +3 -1
- package/test/fixtures/integration/liveStart30sBefore.js +19 -9
- package/test/fixtures/integration/llhls-byte-range.js +1 -0
- package/test/fixtures/integration/llhls-delta-byte-range.js +1 -0
- package/test/fixtures/integration/llhls.js +8 -0
- package/test/fixtures/integration/llhlsDelta.js +5 -0
- package/test/fixtures/integration/manifestExtTTargetdurationNegative.js +1 -0
- package/test/fixtures/integration/manifestExtXEndlistEarly.js +1 -0
- package/test/fixtures/integration/manifestNoExtM3u.js +1 -0
- package/test/fixtures/integration/master-fmp4.js +27 -25
- package/test/fixtures/integration/master.js +1 -0
- package/test/fixtures/integration/media.js +1 -0
- package/test/fixtures/integration/mediaSequence.js +9 -4
- package/test/fixtures/integration/missingEndlist.js +1 -0
- package/test/fixtures/integration/missingExtinf.js +1 -0
- package/test/fixtures/integration/missingMediaSequence.js +9 -4
- package/test/fixtures/integration/missingSegmentDuration.js +3 -1
- package/test/fixtures/integration/multipleAudioGroups.js +1 -0
- package/test/fixtures/integration/multipleAudioGroupsCombinedMain.js +1 -0
- package/test/fixtures/integration/multipleTargetDurations.js +1 -0
- package/test/fixtures/integration/multipleVideo.js +1 -0
- package/test/fixtures/integration/negativeMediaSequence.js +9 -4
- package/test/fixtures/integration/playlist.js +1 -0
- package/test/fixtures/integration/playlistMediaSequenceHigher.js +3 -1
- package/test/fixtures/integration/start.js +1 -0
- package/test/fixtures/integration/streamInfInvalid.js +1 -0
- package/test/fixtures/integration/twoMediaSequences.js +9 -4
- package/test/fixtures/integration/versionInvalid.js +1 -0
- package/test/fixtures/integration/whiteSpace.js +1 -0
- package/test/fixtures/integration/zeroDuration.js +1 -0
- package/test/parse-stream.test.js +112 -16
- package/test/parser.test.js +392 -17
package/dist/m3u8-parser.min.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
/*! @name @transmitlive/m3u8-parser @version
|
|
2
|
-
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("global/window")):"function"==typeof define&&define.amd?define(["exports","global/window"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).m3u8Parser={},t.window)}(this,(function(t,e){"use strict";function i(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var r=i(e);var a=function(t,e){t.prototype=Object.create(e.prototype),t.prototype.constructor=t,t.__proto__=e},s=function(){function t(){this.listeners={}}var e=t.prototype;return e.on=function(t,e){this.listeners[t]||(this.listeners[t]=[]),this.listeners[t].push(e)},e.off=function(t,e){if(!this.listeners[t])return!1;var i=this.listeners[t].indexOf(e);return this.listeners[t]=this.listeners[t].slice(0),this.listeners[t].splice(i,1),i>-1},e.trigger=function(t){var e=this.listeners[t];if(e)if(2===arguments.length)for(var i=e.length,r=0;r<i;++r)e[r].call(this,arguments[1]);else for(var a=Array.prototype.slice.call(arguments,1),s=e.length,n=0;n<s;++n)e[n].apply(this,a)},e.dispose=function(){this.listeners={}},e.pipe=function(t){this.on("data",(function(e){t.push(e)}))},t}(),n=function(t){function e(){var e;return(e=t.call(this)||this).buffer="",e}return a(e,t),e.prototype.push=function(t){var e;for(this.buffer+=t,e=this.buffer.indexOf("\n");e>-1;e=this.buffer.indexOf("\n"))this.trigger("data",this.buffer.substring(0,e)),this.buffer=this.buffer.substring(e+1)},e}(s);var u=function(t,e,i){return t(i={path:e,exports:{},require:function(t,e){return function(){throw new Error("Dynamic requires are not currently supported by @rollup/plugin-commonjs")}(null==e&&i.path)}},i.exports),i.exports}((function(t){function e(){return t.exports=e=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var i=arguments[e];for(var r in i)Object.prototype.hasOwnProperty.call(i,r)&&(t[r]=i[r])}return t},e.apply(this,arguments)}t.exports=e})),o=String.fromCharCode(9),g=function(t){var e=/([0-9.]*)?@?([0-9.]*)?/.exec(t||""),i={};return e[1]&&(i.length=parseInt(e[1],10)),e[2]&&(i.offset=parseInt(e[2],10)),i},f=function(t){for(var e,i=t.split(new RegExp('(?:^|,)((?:[^=]*)=(?:"[^"]*"|[^,]*))')),r={},a=i.length;a--;)""!==i[a]&&((e=/([^=]*)=(.*)/.exec(i[a]).slice(1))[0]=e[0].replace(/^\s+|\s+$/g,""),e[1]=e[1].replace(/^\s+|\s+$/g,""),e[1]=e[1].replace(/^['"](.*)['"]$/g,"$1"),r[e[0]]=e[1]);return r},p=function(t){function e(){var e;return(e=t.call(this)||this).customParsers=[],e.tagMappers=[],e.lineNumber=0,e}a(e,t);var i=e.prototype;return i.push=function(t){var e,i,r=this;(this.lineNumber=this.lineNumber+1,0!==(t=t.trim()).length)&&("#"===t[0]?this.tagMappers.reduce((function(e,i){var r=i(t);return r===t?e:e.concat([r])}),[t]).forEach((function(t){for(var a=0;a<r.customParsers.length;a++)if(r.customParsers[a].call(r,t))return;if(0===t.indexOf("#EXT"))if(t=t.replace("\r",""),e=/^#EXTM3U/.exec(t))r.trigger("data",{type:"tag",tagType:"m3u"});else{if(e=/^#EXTINF:?([0-9\.]*)?,?(.*)?$/.exec(t))return i={type:"tag",tagType:"inf"},e[1]&&(i.duration=parseFloat(e[1])),e[2]&&(i.title=e[2]),void r.trigger("data",i);if(e=/^#EXT-X-TARGETDURATION:?([0-9.]*)?/.exec(t))return i={type:"tag",tagType:"targetduration"},e[1]&&(i.duration=parseInt(e[1],10)),void r.trigger("data",i);if(e=/^#EXT-X-VERSION:?([0-9.]*)?/.exec(t))return i={type:"tag",tagType:"version"},e[1]&&(i.version=parseInt(e[1],10)),void r.trigger("data",i);if(e=/^#EXT-X-MEDIA-SEQUENCE:?(\-?[0-9.]*)?/.exec(t))return i={type:"tag",tagType:"media-sequence"},e[1]&&(i.number=parseInt(e[1],10)),void r.trigger("data",i);if(e=/^#EXT-X-DISCONTINUITY-SEQUENCE:?(\-?[0-9.]*)?/.exec(t))return i={type:"tag",tagType:"discontinuity-sequence"},e[1]&&(i.number=parseInt(e[1],10)),void r.trigger("data",i);if(e=/^#EXT-X-PLAYLIST-TYPE:?(.*)?$/.exec(t))return i={type:"tag",tagType:"playlist-type"},e[1]&&(i.playlistType=e[1]),void r.trigger("data",i);if(e=/^#EXT-X-BYTERANGE:?(.*)?$/.exec(t))return i=u(g(e[1]),{type:"tag",tagType:"byterange"}),void r.trigger("data",i);if(e=/^#EXT-X-ALLOW-CACHE:?(YES|NO)?/.exec(t))return i={type:"tag",tagType:"allow-cache"},e[1]&&(i.allowed=!/NO/.test(e[1])),void r.trigger("data",i);if(e=/^#EXT-X-MAP:?(.*)$/.exec(t)){if(i={type:"tag",tagType:"map"},e[1]){var s=f(e[1]);s.URI&&(i.uri=s.URI),s.BYTERANGE&&(i.byterange=g(s.BYTERANGE))}r.trigger("data",i)}else if(e=/^#EXT-X-STREAM-INF:?(.*)$/.exec(t)){if(i={type:"tag",tagType:"stream-inf"},e[1]){if(i.attributes=f(e[1]),i.attributes.RESOLUTION){var n=i.attributes.RESOLUTION.split("x"),p={};n[0]&&(p.width=parseInt(n[0],10)),n[1]&&(p.height=parseInt(n[1],10)),i.attributes.RESOLUTION=p}i.attributes.BANDWIDTH&&(i.attributes.BANDWIDTH=parseInt(i.attributes.BANDWIDTH,10)),i.attributes["PROGRAM-ID"]&&(i.attributes["PROGRAM-ID"]=parseInt(i.attributes["PROGRAM-ID"],10))}r.trigger("data",i)}else{if(e=/^#EXT-X-MEDIA:?(.*)$/.exec(t))return i={type:"tag",tagType:"media"},e[1]&&(i.attributes=f(e[1])),void r.trigger("data",i);if(e=/^#EXT-X-ENDLIST/.exec(t))r.trigger("data",{type:"tag",tagType:"endlist"});else if(e=/^#EXT-X-DISCONTINUITY/.exec(t))r.trigger("data",{type:"tag",tagType:"discontinuity"});else{if(e=/^#EXT-X-PROGRAM-DATE-TIME:?(.*)$/.exec(t))return i={type:"tag",tagType:"program-date-time"},e[1]&&(i.dateTimeString=e[1],i.dateTimeObject=new Date(e[1])),void r.trigger("data",i);if(e=/^#EXT-X-KEY:?(.*)$/.exec(t))return i={type:"tag",tagType:"key"},e[1]&&(i.attributes=f(e[1]),i.attributes.IV&&("0x"===i.attributes.IV.substring(0,2).toLowerCase()&&(i.attributes.IV=i.attributes.IV.substring(2)),i.attributes.IV=i.attributes.IV.match(/.{8}/g),i.attributes.IV[0]=parseInt(i.attributes.IV[0],16),i.attributes.IV[1]=parseInt(i.attributes.IV[1],16),i.attributes.IV[2]=parseInt(i.attributes.IV[2],16),i.attributes.IV[3]=parseInt(i.attributes.IV[3],16),i.attributes.IV=new Uint32Array(i.attributes.IV))),void r.trigger("data",i);if(e=/^#EXT-X-START:?(.*)$/.exec(t))return i={type:"tag",tagType:"start"},e[1]&&(i.attributes=f(e[1]),i.attributes["TIME-OFFSET"]=parseFloat(i.attributes["TIME-OFFSET"]),i.attributes.PRECISE=/YES/.test(i.attributes.PRECISE)),void r.trigger("data",i);if(e=/^#EXT-X-CUE-OUT-CONT:?(.*)?$/.exec(t))return i={type:"tag",tagType:"cue-out-cont"},e[1]?i.data=e[1]:i.data="",void r.trigger("data",i);if(e=/^#EXT-X-CUE-OUT:?(.*)?$/.exec(t))return i={type:"tag",tagType:"cue-out"},e[1]?i.data=e[1]:i.data="",void r.trigger("data",i);if(e=/^#EXT-X-CUE-IN:?(.*)?$/.exec(t))return i={type:"tag",tagType:"cue-in"},e[1]?i.data=e[1]:i.data="",void r.trigger("data",i);if((e=/^#EXT-X-SKIP:(.*)$/.exec(t))&&e[1])return(i={type:"tag",tagType:"skip"}).attributes=f(e[1]),i.attributes.hasOwnProperty("SKIPPED-SEGMENTS")&&(i.attributes["SKIPPED-SEGMENTS"]=parseInt(i.attributes["SKIPPED-SEGMENTS"],10)),i.attributes.hasOwnProperty("RECENTLY-REMOVED-DATERANGES")&&(i.attributes["RECENTLY-REMOVED-DATERANGES"]=i.attributes["RECENTLY-REMOVED-DATERANGES"].split(o)),void r.trigger("data",i);if((e=/^#EXT-X-PART:(.*)$/.exec(t))&&e[1])return(i={type:"tag",tagType:"part"}).attributes=f(e[1]),["DURATION"].forEach((function(t){i.attributes.hasOwnProperty(t)&&(i.attributes[t]=parseFloat(i.attributes[t]))})),["INDEPENDENT","GAP"].forEach((function(t){i.attributes.hasOwnProperty(t)&&(i.attributes[t]=/YES/.test(i.attributes[t]))})),i.attributes.hasOwnProperty("BYTERANGE")&&(i.attributes.byterange=g(i.attributes.BYTERANGE)),void r.trigger("data",i);if((e=/^#EXT-X-SERVER-CONTROL:(.*)$/.exec(t))&&e[1])return(i={type:"tag",tagType:"server-control"}).attributes=f(e[1]),["CAN-SKIP-UNTIL","PART-HOLD-BACK","HOLD-BACK"].forEach((function(t){i.attributes.hasOwnProperty(t)&&(i.attributes[t]=parseFloat(i.attributes[t]))})),["CAN-SKIP-DATERANGES","CAN-BLOCK-RELOAD"].forEach((function(t){i.attributes.hasOwnProperty(t)&&(i.attributes[t]=/YES/.test(i.attributes[t]))})),void r.trigger("data",i);if((e=/^#EXT-X-PART-INF:(.*)$/.exec(t))&&e[1])return(i={type:"tag",tagType:"part-inf"}).attributes=f(e[1]),["PART-TARGET"].forEach((function(t){i.attributes.hasOwnProperty(t)&&(i.attributes[t]=parseFloat(i.attributes[t]))})),void r.trigger("data",i);if((e=/^#EXT-X-PRELOAD-HINT:(.*)$/.exec(t))&&e[1])return(i={type:"tag",tagType:"preload-hint"}).attributes=f(e[1]),["BYTERANGE-START","BYTERANGE-LENGTH"].forEach((function(t){if(i.attributes.hasOwnProperty(t)){i.attributes[t]=parseInt(i.attributes[t],10);var e="BYTERANGE-LENGTH"===t?"length":"offset";i.attributes.byterange=i.attributes.byterange||{},i.attributes.byterange[e]=i.attributes[t],delete i.attributes[t]}})),void r.trigger("data",i);if((e=/^#EXT-X-RENDITION-REPORT:(.*)$/.exec(t))&&e[1])return(i={type:"tag",tagType:"rendition-report"}).attributes=f(e[1]),["LAST-MSN","LAST-PART"].forEach((function(t){i.attributes.hasOwnProperty(t)&&(i.attributes[t]=parseInt(i.attributes[t],10))})),void r.trigger("data",i);r.trigger("data",{type:"tag",data:t.slice(4)})}}}else r.trigger("data",{type:"comment",text:t.slice(1)})})):this.trigger("data",{type:"uri",uri:t}))},i.addParser=function(t){var e=this,i=t.expression,r=t.customType,a=t.dataParser,s=t.segment;"function"!=typeof a&&(a=function(t){return t}),this.customParsers.push((function(t){if(i.exec(t))return e.trigger("data",{type:"custom",data:a(t),customType:r,segment:s}),!0}))},i.addTagMapper=function(t){var e=t.expression,i=t.map;this.tagMappers.push((function(t){return e.test(t)?i(t):t}))},e}(s);var c=function(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t};function d(t){for(var e,i=(e=t,r.default.atob?r.default.atob(e):Buffer.from(e,"base64").toString("binary")),a=new Uint8Array(i.length),s=0;s<i.length;s++)a[s]=i.charCodeAt(s);return a}var h=function(t){var e={};return Object.keys(t).forEach((function(i){var r;e[(r=i,r.toLowerCase().replace(/-(\w)/g,(function(t){return t[1].toUpperCase()})))]=t[i]})),e},l=function(t){var e=t.serverControl,i=t.targetDuration,r=t.partTargetDuration;if(e){var a="#EXT-X-SERVER-CONTROL",s="holdBack",n="partHoldBack",u=i&&3*i,o=r&&2*r;i&&!e.hasOwnProperty(s)&&(e[s]=u,this.trigger("info",{message:a+" defaulting HOLD-BACK to targetDuration * 3 ("+u+")."})),u&&e[s]<u&&(this.trigger("warn",{message:a+" clamping HOLD-BACK ("+e[s]+") to targetDuration * 3 ("+u+")"}),e[s]=u),r&&!e.hasOwnProperty(n)&&(e[n]=3*r,this.trigger("info",{message:a+" defaulting PART-HOLD-BACK to partTargetDuration * 3 ("+e[n]+")."})),r&&e[n]<o&&(this.trigger("warn",{message:a+" clamping PART-HOLD-BACK ("+e[n]+") to partTargetDuration * 2 ("+o+")."}),e[n]=o)}},b=function(t){function e(){var e;(e=t.call(this)||this).lineStream=new n,e.parseStream=new p,e.lineStream.pipe(e.parseStream);var i,r,a=c(e),s=[],o={},g=!1,f=function(){},b={AUDIO:{},VIDEO:{},"CLOSED-CAPTIONS":{},SUBTITLES:{}},E=0;e.manifest={allowCache:!0,discontinuityStarts:[],segments:[]};var m=0,T=0,y=0;return e.on("end",(function(){o.uri||!o.parts&&!o.preloadHints||(!o.map&&i&&(o.map=i),!o.key&&r&&(o.key=r),o.timeline||"number"!=typeof E||(o.timeline=E),e.manifest.preloadSegment=o)})),e.parseStream.on("data",(function(t){var e,n;Object.keys(o).length||(y=this.lineNumber),{tag:function(){({version:function(){t.version&&(this.manifest.version=t.version)},"allow-cache":function(){this.manifest.allowCache=t.allowed,"allowed"in t||(this.trigger("info",{message:"defaulting allowCache to YES"}),this.manifest.allowCache=!0)},byterange:function(){var e={};"length"in t&&(o.byterange=e,e.length=t.length,"offset"in t||(t.offset=m)),"offset"in t&&(o.byterange=e,e.offset=t.offset),m=e.offset+e.length},endlist:function(){this.manifest.endList=!0},inf:function(){"mediaSequence"in this.manifest||(this.manifest.mediaSequence=0,this.trigger("info",{message:"defaulting media sequence to zero"})),"discontinuitySequence"in this.manifest||(this.manifest.discontinuitySequence=0,this.trigger("info",{message:"defaulting discontinuity sequence to zero"})),t.duration>0&&(o.duration=t.duration),0===t.duration&&(o.duration=.01,this.trigger("info",{message:"updating zero segment duration to a small value"})),this.manifest.segments=s},key:function(){if(t.attributes)if("NONE"!==t.attributes.METHOD)if(t.attributes.URI){if("com.apple.streamingkeydelivery"===t.attributes.KEYFORMAT)return this.manifest.contentProtection=this.manifest.contentProtection||{},void(this.manifest.contentProtection["com.apple.fps.1_0"]={attributes:t.attributes});if("com.microsoft.playready"===t.attributes.KEYFORMAT)return this.manifest.contentProtection=this.manifest.contentProtection||{},void(this.manifest.contentProtection["com.microsoft.playready"]={uri:t.attributes.URI});if("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"===t.attributes.KEYFORMAT){return-1===["SAMPLE-AES","SAMPLE-AES-CTR","SAMPLE-AES-CENC"].indexOf(t.attributes.METHOD)?void this.trigger("warn",{message:"invalid key method provided for Widevine"}):("SAMPLE-AES-CENC"===t.attributes.METHOD&&this.trigger("warn",{message:"SAMPLE-AES-CENC is deprecated, please use SAMPLE-AES-CTR instead"}),"data:text/plain;base64,"!==t.attributes.URI.substring(0,23)?void this.trigger("warn",{message:"invalid key URI provided for Widevine"}):t.attributes.KEYID&&"0x"===t.attributes.KEYID.substring(0,2)?(this.manifest.contentProtection=this.manifest.contentProtection||{},void(this.manifest.contentProtection["com.widevine.alpha"]={attributes:{schemeIdUri:t.attributes.KEYFORMAT,keyId:t.attributes.KEYID.substring(2)},pssh:d(t.attributes.URI.split(",")[1])})):void this.trigger("warn",{message:"invalid key ID provided for Widevine"}))}t.attributes.METHOD||this.trigger("warn",{message:"defaulting key method to AES-128"}),r={method:t.attributes.METHOD||"AES-128",uri:t.attributes.URI},void 0!==t.attributes.IV&&(r.iv=t.attributes.IV)}else this.trigger("warn",{message:"ignoring key declaration without URI"});else r=null;else this.trigger("warn",{message:"ignoring key declaration without attribute list"})},"media-sequence":function(){isFinite(t.number)?this.manifest.mediaSequence=t.number:this.trigger("warn",{message:"ignoring invalid media sequence: "+t.number})},"discontinuity-sequence":function(){isFinite(t.number)?(this.manifest.discontinuitySequence=t.number,E=t.number):this.trigger("warn",{message:"ignoring invalid discontinuity sequence: "+t.number})},"playlist-type":function(){/VOD|EVENT/.test(t.playlistType)?this.manifest.playlistType=t.playlistType:this.trigger("warn",{message:"ignoring unknown playlist type: "+t.playlist})},map:function(){i={},t.uri&&(i.uri=t.uri),t.byterange&&(i.byterange=t.byterange),r&&(i.key=r)},"stream-inf":function(){this.manifest.playlists=s,this.manifest.mediaGroups=this.manifest.mediaGroups||b,t.attributes?(o.attributes||(o.attributes={}),u(o.attributes,t.attributes)):this.trigger("warn",{message:"ignoring empty stream-inf attributes"})},media:function(){if(this.manifest.mediaGroups=this.manifest.mediaGroups||b,t.attributes&&t.attributes.TYPE&&t.attributes["GROUP-ID"]&&t.attributes.NAME){var i=this.manifest.mediaGroups[t.attributes.TYPE];i[t.attributes["GROUP-ID"]]=i[t.attributes["GROUP-ID"]]||{},e=i[t.attributes["GROUP-ID"]],(n={default:/yes/i.test(t.attributes.DEFAULT)}).default?n.autoselect=!0:n.autoselect=/yes/i.test(t.attributes.AUTOSELECT),t.attributes.LANGUAGE&&(n.language=t.attributes.LANGUAGE),t.attributes.URI&&(n.uri=t.attributes.URI),t.attributes["INSTREAM-ID"]&&(n.instreamId=t.attributes["INSTREAM-ID"]),t.attributes.CHARACTERISTICS&&(n.characteristics=t.attributes.CHARACTERISTICS),t.attributes.FORCED&&(n.forced=/yes/i.test(t.attributes.FORCED)),e[t.attributes.NAME]=n}else this.trigger("warn",{message:"ignoring incomplete or missing media group"})},discontinuity:function(){E+=1,o.discontinuity=!0,this.manifest.discontinuityStarts.push(s.length)},"program-date-time":function(){void 0===this.manifest.dateTimeString&&(this.manifest.dateTimeString=t.dateTimeString,this.manifest.dateTimeObject=t.dateTimeObject),o.dateTimeString=t.dateTimeString,o.dateTimeObject=t.dateTimeObject},targetduration:function(){!isFinite(t.duration)||t.duration<0?this.trigger("warn",{message:"ignoring invalid target duration: "+t.duration}):(this.manifest.targetDuration=t.duration,l.call(this,this.manifest))},start:function(){t.attributes&&!isNaN(t.attributes["TIME-OFFSET"])?this.manifest.start={timeOffset:t.attributes["TIME-OFFSET"],precise:t.attributes.PRECISE}:this.trigger("warn",{message:"ignoring start declaration without appropriate attribute list"})},"cue-out":function(){o.cueOut=t.data},"cue-out-cont":function(){o.cueOutCont=t.data},"cue-in":function(){o.cueIn=t.data},skip:function(){this.manifest.skip=h(t.attributes),this.warnOnMissingAttributes_("#EXT-X-SKIP",t.attributes,["SKIPPED-SEGMENTS"])},part:function(){var e=this;g=!0;var i=this.manifest.segments.length,r=h(t.attributes);o.parts=o.parts||[],o.parts.push(r),r.byterange&&(r.byterange.hasOwnProperty("offset")||(r.byterange.offset=T),T=r.byterange.offset+r.byterange.length);var a=o.parts.length-1;this.warnOnMissingAttributes_("#EXT-X-PART #"+a+" for segment #"+i,t.attributes,["URI","DURATION"]),this.manifest.renditionReports&&this.manifest.renditionReports.forEach((function(t,i){t.hasOwnProperty("lastPart")||e.trigger("warn",{message:"#EXT-X-RENDITION-REPORT #"+i+" lacks required attribute(s): LAST-PART"})}))},"server-control":function(){var e=this.manifest.serverControl=h(t.attributes);e.hasOwnProperty("canBlockReload")||(e.canBlockReload=!1,this.trigger("info",{message:"#EXT-X-SERVER-CONTROL defaulting CAN-BLOCK-RELOAD to false"})),l.call(this,this.manifest),e.canSkipDateranges&&!e.hasOwnProperty("canSkipUntil")&&this.trigger("warn",{message:"#EXT-X-SERVER-CONTROL lacks required attribute CAN-SKIP-UNTIL which is required when CAN-SKIP-DATERANGES is set"})},"preload-hint":function(){var e=this.manifest.segments.length,i=h(t.attributes),r=i.type&&"PART"===i.type;o.preloadHints=o.preloadHints||[],o.preloadHints.push(i),i.byterange&&(i.byterange.hasOwnProperty("offset")||(i.byterange.offset=r?T:0,r&&(T=i.byterange.offset+i.byterange.length)));var a=o.preloadHints.length-1;if(this.warnOnMissingAttributes_("#EXT-X-PRELOAD-HINT #"+a+" for segment #"+e,t.attributes,["TYPE","URI"]),i.type)for(var s=0;s<o.preloadHints.length-1;s++){var n=o.preloadHints[s];n.type&&(n.type===i.type&&this.trigger("warn",{message:"#EXT-X-PRELOAD-HINT #"+a+" for segment #"+e+" has the same TYPE "+i.type+" as preload hint #"+s}))}},"rendition-report":function(){var e=h(t.attributes);this.manifest.renditionReports=this.manifest.renditionReports||[],this.manifest.renditionReports.push(e);var i=this.manifest.renditionReports.length-1,r=["LAST-MSN","URI"];g&&r.push("LAST-PART"),this.warnOnMissingAttributes_("#EXT-X-RENDITION-REPORT #"+i,t.attributes,r)},"part-inf":function(){this.manifest.partInf=h(t.attributes),this.warnOnMissingAttributes_("#EXT-X-PART-INF",t.attributes,["PART-TARGET"]),this.manifest.partInf.partTarget&&(this.manifest.partTargetDuration=this.manifest.partInf.partTarget),l.call(this,this.manifest)}}[t.tagType]||f).call(a)},uri:function(){o.uri=t.uri,o.lineNumberStart=y,o.lineNumberEnd=this.parseStream.lineNumber,s.push(o),this.manifest.targetDuration&&!("duration"in o)&&(this.trigger("warn",{message:"defaulting segment duration to the target duration"}),o.duration=this.manifest.targetDuration),r&&(o.key=r),o.timeline=E,i&&(o.map=i),T=0,o={}},comment:function(){},custom:function(){t.segment?(o.custom=o.custom||{},o.custom[t.customType]=t.data):(this.manifest.custom=this.manifest.custom||{},this.manifest.custom[t.customType]=t.data)}}[t.type].call(a)})),e}a(e,t);var i=e.prototype;return i.warnOnMissingAttributes_=function(t,e,i){var r=[];i.forEach((function(t){e.hasOwnProperty(t)||r.push(t)})),r.length&&this.trigger("warn",{message:t+" lacks required attribute(s): "+r.join(", ")})},i.push=function(t){this.lineStream.push(t)},i.end=function(){this.lineStream.push("\n"),this.trigger("end")},i.addParser=function(t){this.parseStream.addParser(t)},i.addTagMapper=function(t){this.parseStream.addTagMapper(t)},e}(s);t.LineStream=n,t.ParseStream=p,t.Parser=b,Object.defineProperty(t,"__esModule",{value:!0})}));
|
|
1
|
+
/*! @name @transmitlive/m3u8-parser @version 7.1.0-1 @license Apache-2.0 */
|
|
2
|
+
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).m3u8Parser={})}(this,(function(t){"use strict";var e=function(){function t(){this.listeners={}}var e=t.prototype;return e.on=function(t,e){this.listeners[t]||(this.listeners[t]=[]),this.listeners[t].push(e)},e.off=function(t,e){if(!this.listeners[t])return!1;var i=this.listeners[t].indexOf(e);return this.listeners[t]=this.listeners[t].slice(0),this.listeners[t].splice(i,1),i>-1},e.trigger=function(t){var e=this.listeners[t];if(e)if(2===arguments.length)for(var i=e.length,a=0;a<i;++a)e[a].call(this,arguments[1]);else for(var s=Array.prototype.slice.call(arguments,1),r=e.length,n=0;n<r;++n)e[n].apply(this,s)},e.dispose=function(){this.listeners={}},e.pipe=function(t){this.on("data",(function(e){t.push(e)}))},t}();class i extends e{constructor(){super(),this.buffer=""}push(t){let e;for(this.buffer+=t,e=this.buffer.indexOf("\n");e>-1;e=this.buffer.indexOf("\n"))this.trigger("data",this.buffer.substring(0,e)),this.buffer=this.buffer.substring(e+1)}}function a(){return s=a=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var i=arguments[e];for(var a in i)Object.prototype.hasOwnProperty.call(i,a)&&(t[a]=i[a])}return t},a.apply(this,arguments)}var s=a,r=s;const n=String.fromCharCode(9),u=function(t){const e=/([0-9.]*)?@?([0-9.]*)?/.exec(t||""),i={};return e[1]&&(i.length=parseInt(e[1],10)),e[2]&&(i.offset=parseInt(e[2],10)),i},o=function(t){const e={};if(!t)return e;const i=t.split(new RegExp('(?:^|,)((?:[^=]*)=(?:"[^"]*"|[^,]*))'));let a,s=i.length;for(;s--;)""!==i[s]&&(a=/([^=]*)=(.*)/.exec(i[s]).slice(1),a[0]=a[0].replace(/^\s+|\s+$/g,""),a[1]=a[1].replace(/^\s+|\s+$/g,""),a[1]=a[1].replace(/^['"](.*)['"]$/g,"$1"),e[a[0]]=a[1]);return e};class g extends e{constructor(){super(),this.customParsers=[],this.tagMappers=[],this.lineNumber=0}push(t){let e,i;if(this.lineNumber=this.lineNumber+1,0===(t=t.trim()).length)return;if("#"!==t[0])return void this.trigger("data",{type:"uri",uri:t});this.tagMappers.reduce(((e,i)=>{const a=i(t);return a===t?e:e.concat([a])}),[t]).forEach((t=>{for(let e=0;e<this.customParsers.length;e++)if(this.customParsers[e].call(this,t))return;if(0===t.indexOf("#EXT"))if(t=t.replace("\r",""),e=/^#EXTM3U/.exec(t),e)this.trigger("data",{type:"tag",tagType:"m3u"});else{if(e=/^#EXTINF:([0-9\.]*)?,?(.*)?$/.exec(t),e)return i={type:"tag",tagType:"inf"},e[1]&&(i.duration=parseFloat(e[1])),e[2]&&(i.title=e[2]),void this.trigger("data",i);if(e=/^#EXT-X-TARGETDURATION:([0-9.]*)?/.exec(t),e)return i={type:"tag",tagType:"targetduration"},e[1]&&(i.duration=parseInt(e[1],10)),void this.trigger("data",i);if(e=/^#EXT-X-VERSION:([0-9.]*)?/.exec(t),e)return i={type:"tag",tagType:"version"},e[1]&&(i.version=parseInt(e[1],10)),void this.trigger("data",i);if(e=/^#EXT-X-MEDIA-SEQUENCE:(\-?[0-9.]*)?/.exec(t),e)return i={type:"tag",tagType:"media-sequence"},e[1]&&(i.number=parseInt(e[1],10)),void this.trigger("data",i);if(e=/^#EXT-X-DISCONTINUITY-SEQUENCE:(\-?[0-9.]*)?/.exec(t),e)return i={type:"tag",tagType:"discontinuity-sequence"},e[1]&&(i.number=parseInt(e[1],10)),void this.trigger("data",i);if(e=/^#EXT-X-PLAYLIST-TYPE:(.*)?$/.exec(t),e)return i={type:"tag",tagType:"playlist-type"},e[1]&&(i.playlistType=e[1]),void this.trigger("data",i);if(e=/^#EXT-X-BYTERANGE:(.*)?$/.exec(t),e)return i=r(u(e[1]),{type:"tag",tagType:"byterange"}),void this.trigger("data",i);if(e=/^#EXT-X-ALLOW-CACHE:(YES|NO)?/.exec(t),e)return i={type:"tag",tagType:"allow-cache"},e[1]&&(i.allowed=!/NO/.test(e[1])),void this.trigger("data",i);if(e=/^#EXT-X-MAP:(.*)$/.exec(t),e){if(i={type:"tag",tagType:"map"},e[1]){const t=o(e[1]);t.URI&&(i.uri=t.URI),t.BYTERANGE&&(i.byterange=u(t.BYTERANGE))}this.trigger("data",i)}else if(e=/^#EXT-X-STREAM-INF:(.*)$/.exec(t),e){if(i={type:"tag",tagType:"stream-inf"},e[1]){if(i.attributes=o(e[1]),i.attributes.RESOLUTION){const t=i.attributes.RESOLUTION.split("x"),e={};t[0]&&(e.width=parseInt(t[0],10)),t[1]&&(e.height=parseInt(t[1],10)),i.attributes.RESOLUTION=e}i.attributes.BANDWIDTH&&(i.attributes.BANDWIDTH=parseInt(i.attributes.BANDWIDTH,10)),i.attributes["FRAME-RATE"]&&(i.attributes["FRAME-RATE"]=parseFloat(i.attributes["FRAME-RATE"])),i.attributes["PROGRAM-ID"]&&(i.attributes["PROGRAM-ID"]=parseInt(i.attributes["PROGRAM-ID"],10))}this.trigger("data",i)}else{if(e=/^#EXT-X-MEDIA:(.*)$/.exec(t),e)return i={type:"tag",tagType:"media"},e[1]&&(i.attributes=o(e[1])),void this.trigger("data",i);if(e=/^#EXT-X-ENDLIST/.exec(t),e)this.trigger("data",{type:"tag",tagType:"endlist"});else if(e=/^#EXT-X-DISCONTINUITY/.exec(t),e)this.trigger("data",{type:"tag",tagType:"discontinuity"});else{if(e=/^#EXT-X-PROGRAM-DATE-TIME:(.*)$/.exec(t),e)return i={type:"tag",tagType:"program-date-time"},e[1]&&(i.dateTimeString=e[1],i.dateTimeObject=new Date(e[1])),void this.trigger("data",i);if(e=/^#EXT-X-KEY:(.*)$/.exec(t),e)return i={type:"tag",tagType:"key"},e[1]&&(i.attributes=o(e[1]),i.attributes.IV&&("0x"===i.attributes.IV.substring(0,2).toLowerCase()&&(i.attributes.IV=i.attributes.IV.substring(2)),i.attributes.IV=i.attributes.IV.match(/.{8}/g),i.attributes.IV[0]=parseInt(i.attributes.IV[0],16),i.attributes.IV[1]=parseInt(i.attributes.IV[1],16),i.attributes.IV[2]=parseInt(i.attributes.IV[2],16),i.attributes.IV[3]=parseInt(i.attributes.IV[3],16),i.attributes.IV=new Uint32Array(i.attributes.IV))),void this.trigger("data",i);if(e=/^#EXT-X-START:(.*)$/.exec(t),e)return i={type:"tag",tagType:"start"},e[1]&&(i.attributes=o(e[1]),i.attributes["TIME-OFFSET"]=parseFloat(i.attributes["TIME-OFFSET"]),i.attributes.PRECISE=/YES/.test(i.attributes.PRECISE)),void this.trigger("data",i);if(e=/^#EXT-X-CUE-OUT-CONT:(.*)?$/.exec(t),e)return i={type:"tag",tagType:"cue-out-cont"},e[1]?i.data=e[1]:i.data="",void this.trigger("data",i);if(e=/^#EXT-X-CUE-OUT:(.*)?$/.exec(t),e)return i={type:"tag",tagType:"cue-out"},e[1]?i.data=e[1]:i.data="",void this.trigger("data",i);if(e=/^#EXT-X-CUE-IN:(.*)?$/.exec(t),e)return i={type:"tag",tagType:"cue-in"},e[1]?i.data=e[1]:i.data="",void this.trigger("data",i);if(e=/^#EXT-X-SKIP:(.*)$/.exec(t),e&&e[1])return i={type:"tag",tagType:"skip"},i.attributes=o(e[1]),i.attributes.hasOwnProperty("SKIPPED-SEGMENTS")&&(i.attributes["SKIPPED-SEGMENTS"]=parseInt(i.attributes["SKIPPED-SEGMENTS"],10)),i.attributes.hasOwnProperty("RECENTLY-REMOVED-DATERANGES")&&(i.attributes["RECENTLY-REMOVED-DATERANGES"]=i.attributes["RECENTLY-REMOVED-DATERANGES"].split(n)),void this.trigger("data",i);if(e=/^#EXT-X-PART:(.*)$/.exec(t),e&&e[1])return i={type:"tag",tagType:"part"},i.attributes=o(e[1]),["DURATION"].forEach((function(t){i.attributes.hasOwnProperty(t)&&(i.attributes[t]=parseFloat(i.attributes[t]))})),["INDEPENDENT","GAP"].forEach((function(t){i.attributes.hasOwnProperty(t)&&(i.attributes[t]=/YES/.test(i.attributes[t]))})),i.attributes.hasOwnProperty("BYTERANGE")&&(i.attributes.byterange=u(i.attributes.BYTERANGE)),void this.trigger("data",i);if(e=/^#EXT-X-SERVER-CONTROL:(.*)$/.exec(t),e&&e[1])return i={type:"tag",tagType:"server-control"},i.attributes=o(e[1]),["CAN-SKIP-UNTIL","PART-HOLD-BACK","HOLD-BACK"].forEach((function(t){i.attributes.hasOwnProperty(t)&&(i.attributes[t]=parseFloat(i.attributes[t]))})),["CAN-SKIP-DATERANGES","CAN-BLOCK-RELOAD"].forEach((function(t){i.attributes.hasOwnProperty(t)&&(i.attributes[t]=/YES/.test(i.attributes[t]))})),void this.trigger("data",i);if(e=/^#EXT-X-PART-INF:(.*)$/.exec(t),e&&e[1])return i={type:"tag",tagType:"part-inf"},i.attributes=o(e[1]),["PART-TARGET"].forEach((function(t){i.attributes.hasOwnProperty(t)&&(i.attributes[t]=parseFloat(i.attributes[t]))})),void this.trigger("data",i);if(e=/^#EXT-X-PRELOAD-HINT:(.*)$/.exec(t),e&&e[1])return i={type:"tag",tagType:"preload-hint"},i.attributes=o(e[1]),["BYTERANGE-START","BYTERANGE-LENGTH"].forEach((function(t){if(i.attributes.hasOwnProperty(t)){i.attributes[t]=parseInt(i.attributes[t],10);const e="BYTERANGE-LENGTH"===t?"length":"offset";i.attributes.byterange=i.attributes.byterange||{},i.attributes.byterange[e]=i.attributes[t],delete i.attributes[t]}})),void this.trigger("data",i);if(e=/^#EXT-X-RENDITION-REPORT:(.*)$/.exec(t),e&&e[1])return i={type:"tag",tagType:"rendition-report"},i.attributes=o(e[1]),["LAST-MSN","LAST-PART"].forEach((function(t){i.attributes.hasOwnProperty(t)&&(i.attributes[t]=parseInt(i.attributes[t],10))})),void this.trigger("data",i);if(e=/^#EXT-X-DATERANGE:(.*)$/.exec(t),e&&e[1]){i={type:"tag",tagType:"daterange"},i.attributes=o(e[1]),["ID","CLASS"].forEach((function(t){i.attributes.hasOwnProperty(t)&&(i.attributes[t]=String(i.attributes[t]))})),["START-DATE","END-DATE"].forEach((function(t){i.attributes.hasOwnProperty(t)&&(i.attributes[t]=new Date(i.attributes[t]))})),["DURATION","PLANNED-DURATION"].forEach((function(t){i.attributes.hasOwnProperty(t)&&(i.attributes[t]=parseFloat(i.attributes[t]))})),["END-ON-NEXT"].forEach((function(t){i.attributes.hasOwnProperty(t)&&(i.attributes[t]=/YES/i.test(i.attributes[t]))})),["SCTE35-CMD"," SCTE35-OUT","SCTE35-IN"].forEach((function(t){i.attributes.hasOwnProperty(t)&&(i.attributes[t]=i.attributes[t].toString(16))}));const t=/^X-([A-Z]+-)+[A-Z]+$/;for(const e in i.attributes){if(!t.test(e))continue;const a=/[0-9A-Fa-f]{6}/g.test(i.attributes[e]),s=/^\d+(\.\d+)?$/.test(i.attributes[e]);i.attributes[e]=a?i.attributes[e].toString(16):s?parseFloat(i.attributes[e]):String(i.attributes[e])}this.trigger("data",i)}else if(e=/^#EXT-X-INDEPENDENT-SEGMENTS/.exec(t),e)this.trigger("data",{type:"tag",tagType:"independent-segments"});else{if(e=/^#EXT-X-CONTENT-STEERING:(.*)$/.exec(t),e)return i={type:"tag",tagType:"content-steering"},i.attributes=o(e[1]),void this.trigger("data",i);this.trigger("data",{type:"tag",data:t.slice(4)})}}}}else this.trigger("data",{type:"comment",text:t.slice(1)})}))}addParser({expression:t,customType:e,dataParser:i,segment:a}){"function"!=typeof i&&(i=t=>t),this.customParsers.push((s=>{if(t.exec(s))return this.trigger("data",{type:"custom",data:i(s),customType:e,segment:a}),!0}))}addTagMapper({expression:t,map:e}){this.tagMappers.push((i=>t.test(i)?e(i):i))}}function h(t){for(var e,i=(e=t,window.atob?window.atob(e):Buffer.from(e,"base64").toString("binary")),a=new Uint8Array(i.length),s=0;s<i.length;s++)a[s]=i.charCodeAt(s);return a}const d=function(t){const e={};return Object.keys(t).forEach((function(i){var a;e[(a=i,a.toLowerCase().replace(/-(\w)/g,(t=>t[1].toUpperCase())))]=t[i]})),e},p=function(t){const{serverControl:e,targetDuration:i,partTargetDuration:a}=t;if(!e)return;const s="#EXT-X-SERVER-CONTROL",r="holdBack",n="partHoldBack",u=i&&3*i,o=a&&2*a;i&&!e.hasOwnProperty(r)&&(e[r]=u,this.trigger("info",{message:`${s} defaulting HOLD-BACK to targetDuration * 3 (${u}).`})),u&&e[r]<u&&(this.trigger("warn",{message:`${s} clamping HOLD-BACK (${e[r]}) to targetDuration * 3 (${u})`}),e[r]=u),a&&!e.hasOwnProperty(n)&&(e[n]=3*a,this.trigger("info",{message:`${s} defaulting PART-HOLD-BACK to partTargetDuration * 3 (${e[n]}).`})),a&&e[n]<o&&(this.trigger("warn",{message:`${s} clamping PART-HOLD-BACK (${e[n]}) to partTargetDuration * 2 (${o}).`}),e[n]=o)};t.LineStream=i,t.ParseStream=g,t.Parser=class extends e{constructor(){super(),this.lineStream=new i,this.parseStream=new g,this.lineStream.pipe(this.parseStream),this.lastProgramDateTime=null;const t=this,e=[];let a,s,n={},u=!1;const o=function(){},E={AUDIO:{},VIDEO:{},"CLOSED-CAPTIONS":{},SUBTITLES:{}};let f=0;this.manifest={allowCache:!0,discontinuityStarts:[],dateRanges:[],segments:[]};let T=0,c=0;const m={};let l=0;this.on("end",(()=>{n.uri||!n.parts&&!n.preloadHints||(!n.map&&a&&(n.map=a),!n.key&&s&&(n.key=s),n.timeline||"number"!=typeof f||(n.timeline=f),this.manifest.preloadSegment=n)})),this.parseStream.on("data",(function(i){let g,b;Object.keys(n).length||(l=this.lineNumber),{tag(){({version(){i.version&&(this.manifest.version=i.version)},"allow-cache"(){this.manifest.allowCache=i.allowed,"allowed"in i||(this.trigger("info",{message:"defaulting allowCache to YES"}),this.manifest.allowCache=!0)},byterange(){const t={};"length"in i&&(n.byterange=t,t.length=i.length,"offset"in i||(i.offset=T)),"offset"in i&&(n.byterange=t,t.offset=i.offset),T=t.offset+t.length},endlist(){this.manifest.endList=!0},inf(){"mediaSequence"in this.manifest||(this.manifest.mediaSequence=0,this.trigger("info",{message:"defaulting media sequence to zero"})),"discontinuitySequence"in this.manifest||(this.manifest.discontinuitySequence=0,this.trigger("info",{message:"defaulting discontinuity sequence to zero"})),i.title&&(n.title=i.title),i.duration>0&&(n.duration=i.duration),0===i.duration&&(n.duration=.01,this.trigger("info",{message:"updating zero segment duration to a small value"})),this.manifest.segments=e},key(){if(i.attributes)if("NONE"!==i.attributes.METHOD)if(i.attributes.URI){if("com.apple.streamingkeydelivery"===i.attributes.KEYFORMAT)return this.manifest.contentProtection=this.manifest.contentProtection||{},void(this.manifest.contentProtection["com.apple.fps.1_0"]={attributes:i.attributes});if("com.microsoft.playready"===i.attributes.KEYFORMAT)return this.manifest.contentProtection=this.manifest.contentProtection||{},void(this.manifest.contentProtection["com.microsoft.playready"]={uri:i.attributes.URI});if("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"===i.attributes.KEYFORMAT){return-1===["SAMPLE-AES","SAMPLE-AES-CTR","SAMPLE-AES-CENC"].indexOf(i.attributes.METHOD)?void this.trigger("warn",{message:"invalid key method provided for Widevine"}):("SAMPLE-AES-CENC"===i.attributes.METHOD&&this.trigger("warn",{message:"SAMPLE-AES-CENC is deprecated, please use SAMPLE-AES-CTR instead"}),"data:text/plain;base64,"!==i.attributes.URI.substring(0,23)?void this.trigger("warn",{message:"invalid key URI provided for Widevine"}):i.attributes.KEYID&&"0x"===i.attributes.KEYID.substring(0,2)?(this.manifest.contentProtection=this.manifest.contentProtection||{},void(this.manifest.contentProtection["com.widevine.alpha"]={attributes:{schemeIdUri:i.attributes.KEYFORMAT,keyId:i.attributes.KEYID.substring(2)},pssh:h(i.attributes.URI.split(",")[1])})):void this.trigger("warn",{message:"invalid key ID provided for Widevine"}))}i.attributes.METHOD||this.trigger("warn",{message:"defaulting key method to AES-128"}),s={method:i.attributes.METHOD||"AES-128",uri:i.attributes.URI},void 0!==i.attributes.IV&&(s.iv=i.attributes.IV)}else this.trigger("warn",{message:"ignoring key declaration without URI"});else s=null;else this.trigger("warn",{message:"ignoring key declaration without attribute list"})},"media-sequence"(){isFinite(i.number)?this.manifest.mediaSequence=i.number:this.trigger("warn",{message:"ignoring invalid media sequence: "+i.number})},"discontinuity-sequence"(){isFinite(i.number)?(this.manifest.discontinuitySequence=i.number,f=i.number):this.trigger("warn",{message:"ignoring invalid discontinuity sequence: "+i.number})},"playlist-type"(){/VOD|EVENT/.test(i.playlistType)?this.manifest.playlistType=i.playlistType:this.trigger("warn",{message:"ignoring unknown playlist type: "+i.playlist})},map(){a={},i.uri&&(a.uri=i.uri),i.byterange&&(a.byterange=i.byterange),s&&(a.key=s)},"stream-inf"(){this.manifest.playlists=e,this.manifest.mediaGroups=this.manifest.mediaGroups||E,i.attributes?(n.attributes||(n.attributes={}),r(n.attributes,i.attributes)):this.trigger("warn",{message:"ignoring empty stream-inf attributes"})},media(){if(this.manifest.mediaGroups=this.manifest.mediaGroups||E,!(i.attributes&&i.attributes.TYPE&&i.attributes["GROUP-ID"]&&i.attributes.NAME))return void this.trigger("warn",{message:"ignoring incomplete or missing media group"});const t=this.manifest.mediaGroups[i.attributes.TYPE];t[i.attributes["GROUP-ID"]]=t[i.attributes["GROUP-ID"]]||{},g=t[i.attributes["GROUP-ID"]],b={default:/yes/i.test(i.attributes.DEFAULT)},b.default?b.autoselect=!0:b.autoselect=/yes/i.test(i.attributes.AUTOSELECT),i.attributes.LANGUAGE&&(b.language=i.attributes.LANGUAGE),i.attributes.URI&&(b.uri=i.attributes.URI),i.attributes["INSTREAM-ID"]&&(b.instreamId=i.attributes["INSTREAM-ID"]),i.attributes.CHARACTERISTICS&&(b.characteristics=i.attributes.CHARACTERISTICS),i.attributes.FORCED&&(b.forced=/yes/i.test(i.attributes.FORCED)),g[i.attributes.NAME]=b},discontinuity(){f+=1,n.discontinuity=!0,this.manifest.discontinuityStarts.push(e.length)},"program-date-time"(){void 0===this.manifest.dateTimeString&&(this.manifest.dateTimeString=i.dateTimeString,this.manifest.dateTimeObject=i.dateTimeObject),n.dateTimeString=i.dateTimeString,n.dateTimeObject=i.dateTimeObject;const{lastProgramDateTime:t}=this;this.lastProgramDateTime=new Date(i.dateTimeString).getTime(),null===t&&this.manifest.segments.reduceRight(((t,e)=>(e.programDateTime=t-1e3*e.duration,e.programDateTime)),this.lastProgramDateTime)},targetduration(){!isFinite(i.duration)||i.duration<0?this.trigger("warn",{message:"ignoring invalid target duration: "+i.duration}):(this.manifest.targetDuration=i.duration,p.call(this,this.manifest))},start(){i.attributes&&!isNaN(i.attributes["TIME-OFFSET"])?this.manifest.start={timeOffset:i.attributes["TIME-OFFSET"],precise:i.attributes.PRECISE}:this.trigger("warn",{message:"ignoring start declaration without appropriate attribute list"})},"cue-out"(){n.cueOut=i.data},"cue-out-cont"(){n.cueOutCont=i.data},"cue-in"(){n.cueIn=i.data},skip(){this.manifest.skip=d(i.attributes),this.warnOnMissingAttributes_("#EXT-X-SKIP",i.attributes,["SKIPPED-SEGMENTS"])},part(){u=!0;const t=this.manifest.segments.length,e=d(i.attributes);n.parts=n.parts||[],n.parts.push(e),e.byterange&&(e.byterange.hasOwnProperty("offset")||(e.byterange.offset=c),c=e.byterange.offset+e.byterange.length);const a=n.parts.length-1;this.warnOnMissingAttributes_(`#EXT-X-PART #${a} for segment #${t}`,i.attributes,["URI","DURATION"]),this.manifest.renditionReports&&this.manifest.renditionReports.forEach(((t,e)=>{t.hasOwnProperty("lastPart")||this.trigger("warn",{message:`#EXT-X-RENDITION-REPORT #${e} lacks required attribute(s): LAST-PART`})}))},"server-control"(){const t=this.manifest.serverControl=d(i.attributes);t.hasOwnProperty("canBlockReload")||(t.canBlockReload=!1,this.trigger("info",{message:"#EXT-X-SERVER-CONTROL defaulting CAN-BLOCK-RELOAD to false"})),p.call(this,this.manifest),t.canSkipDateranges&&!t.hasOwnProperty("canSkipUntil")&&this.trigger("warn",{message:"#EXT-X-SERVER-CONTROL lacks required attribute CAN-SKIP-UNTIL which is required when CAN-SKIP-DATERANGES is set"})},"preload-hint"(){const t=this.manifest.segments.length,e=d(i.attributes),a=e.type&&"PART"===e.type;n.preloadHints=n.preloadHints||[],n.preloadHints.push(e),e.byterange&&(e.byterange.hasOwnProperty("offset")||(e.byterange.offset=a?c:0,a&&(c=e.byterange.offset+e.byterange.length)));const s=n.preloadHints.length-1;if(this.warnOnMissingAttributes_(`#EXT-X-PRELOAD-HINT #${s} for segment #${t}`,i.attributes,["TYPE","URI"]),e.type)for(let i=0;i<n.preloadHints.length-1;i++){const a=n.preloadHints[i];a.type&&(a.type===e.type&&this.trigger("warn",{message:`#EXT-X-PRELOAD-HINT #${s} for segment #${t} has the same TYPE ${e.type} as preload hint #${i}`}))}},"rendition-report"(){const t=d(i.attributes);this.manifest.renditionReports=this.manifest.renditionReports||[],this.manifest.renditionReports.push(t);const e=this.manifest.renditionReports.length-1,a=["LAST-MSN","URI"];u&&a.push("LAST-PART"),this.warnOnMissingAttributes_(`#EXT-X-RENDITION-REPORT #${e}`,i.attributes,a)},"part-inf"(){this.manifest.partInf=d(i.attributes),this.warnOnMissingAttributes_("#EXT-X-PART-INF",i.attributes,["PART-TARGET"]),this.manifest.partInf.partTarget&&(this.manifest.partTargetDuration=this.manifest.partInf.partTarget),p.call(this,this.manifest)},daterange(){this.manifest.dateRanges.push(d(i.attributes));const t=this.manifest.dateRanges.length-1;this.warnOnMissingAttributes_(`#EXT-X-DATERANGE #${t}`,i.attributes,["ID","START-DATE"]);const e=this.manifest.dateRanges[t];e.endDate&&e.startDate&&new Date(e.endDate)<new Date(e.startDate)&&this.trigger("warn",{message:"EXT-X-DATERANGE END-DATE must be equal to or later than the value of the START-DATE"}),e.duration&&e.duration<0&&this.trigger("warn",{message:"EXT-X-DATERANGE DURATION must not be negative"}),e.plannedDuration&&e.plannedDuration<0&&this.trigger("warn",{message:"EXT-X-DATERANGE PLANNED-DURATION must not be negative"});const a=!!e.endOnNext;if(a&&!e.class&&this.trigger("warn",{message:"EXT-X-DATERANGE with an END-ON-NEXT=YES attribute must have a CLASS attribute"}),a&&(e.duration||e.endDate)&&this.trigger("warn",{message:"EXT-X-DATERANGE with an END-ON-NEXT=YES attribute must not contain DURATION or END-DATE attributes"}),e.duration&&e.endDate){const i=e.startDate.getTime()+1e3*e.duration;this.manifest.dateRanges[t].endDate=new Date(i)}if(m[e.id]){for(const t in m[e.id])if(e[t]&&JSON.stringify(m[e.id][t])!==JSON.stringify(e[t])){this.trigger("warn",{message:"EXT-X-DATERANGE tags with the same ID in a playlist must have the same attributes values"});break}const t=this.manifest.dateRanges.findIndex((t=>t.id===e.id));this.manifest.dateRanges[t]=r(this.manifest.dateRanges[t],e),m[e.id]=r(m[e.id],e),this.manifest.dateRanges.pop()}else m[e.id]=e},"independent-segments"(){this.manifest.independentSegments=!0},"content-steering"(){this.manifest.contentSteering=d(i.attributes),this.warnOnMissingAttributes_("#EXT-X-CONTENT-STEERING",i.attributes,["SERVER-URI"])}}[i.tagType]||o).call(t)},uri(){n.uri=i.uri,n.lineNumberStart=l,n.lineNumberEnd=this.parseStream.lineNumber,e.push(n),this.manifest.targetDuration&&!("duration"in n)&&(this.trigger("warn",{message:"defaulting segment duration to the target duration"}),n.duration=this.manifest.targetDuration),s&&(n.key=s),n.timeline=f,a&&(n.map=a),c=0,null!==this.lastProgramDateTime&&(n.programDateTime=this.lastProgramDateTime,this.lastProgramDateTime+=1e3*n.duration),n={}},comment(){},custom(){i.segment?(n.custom=n.custom||{},n.custom[i.customType]=i.data):(this.manifest.custom=this.manifest.custom||{},this.manifest.custom[i.customType]=i.data)}}[i.type].call(t)}))}warnOnMissingAttributes_(t,e,i){const a=[];i.forEach((function(t){e.hasOwnProperty(t)||a.push(t)})),a.length&&this.trigger("warn",{message:`${t} lacks required attribute(s): ${a.join(", ")}`})}push(t){this.lineStream.push(t)}end(){this.lineStream.push("\n"),this.manifest.dateRanges.length&&null===this.lastProgramDateTime&&this.trigger("warn",{message:"A playlist with EXT-X-DATERANGE tag must contain atleast one EXT-X-PROGRAM-DATE-TIME tag"}),this.lastProgramDateTime=null,this.trigger("end")}addParser(t){this.parseStream.addParser(t)}addTagMapper(t){this.parseStream.addTagMapper(t)}},Object.defineProperty(t,"__esModule",{value:!0})}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@transmitlive/m3u8-parser",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.1.0-1",
|
|
4
4
|
"description": "m3u8 parser",
|
|
5
5
|
"main": "dist/m3u8-parser.cjs.js",
|
|
6
6
|
"module": "dist/m3u8-parser.es.js",
|
|
@@ -29,14 +29,13 @@
|
|
|
29
29
|
"build:js": "rollup -c scripts/rollup.config.js",
|
|
30
30
|
"clean": "shx rm -rf ./dist ./test/dist && shx mkdir -p ./dist ./test/dist",
|
|
31
31
|
"lint": "vjsstandard",
|
|
32
|
-
"prepublishOnly": "npm-run-all build-prod && vjsverify --verbose",
|
|
32
|
+
"prepublishOnly": "npm-run-all build-prod && vjsverify --verbose --skip-es-check",
|
|
33
33
|
"start": "npm-run-all -p server watch",
|
|
34
34
|
"server": "karma start scripts/karma.conf.js --singleRun=false --auto-watch",
|
|
35
35
|
"test": "npm-run-all lint build-test test:*",
|
|
36
36
|
"test:browser": "karma start scripts/karma.conf.js",
|
|
37
37
|
"test:node": "qunit test/dist/bundle.js",
|
|
38
38
|
"posttest": "shx cat test/dist/coverage/text.txt",
|
|
39
|
-
"preversion": "npm test",
|
|
40
39
|
"version": "is-prerelease || npm run update-changelog && git add CHANGELOG.md",
|
|
41
40
|
"update-changelog": "conventional-changelog -p videojs -i CHANGELOG.md -s",
|
|
42
41
|
"watch": "npm-run-all -p watch:*",
|
|
@@ -66,28 +65,24 @@
|
|
|
66
65
|
],
|
|
67
66
|
"dependencies": {
|
|
68
67
|
"@babel/runtime": "^7.12.5",
|
|
69
|
-
"@videojs/vhs-utils": "^3.0.
|
|
68
|
+
"@videojs/vhs-utils": "^3.0.5",
|
|
70
69
|
"global": "^4.4.0"
|
|
71
70
|
},
|
|
72
71
|
"devDependencies": {
|
|
73
72
|
"@rollup/plugin-replace": "^2.3.4",
|
|
74
73
|
"@videojs/generator-helpers": "~2.0.1",
|
|
75
74
|
"karma": "^5.2.3",
|
|
76
|
-
"rollup": "^2.
|
|
75
|
+
"rollup": "^2.38.0",
|
|
77
76
|
"rollup-plugin-data-files": "^0.1.0",
|
|
78
77
|
"sinon": "^9.2.3",
|
|
79
|
-
"videojs-generate-karma-config": "
|
|
80
|
-
"videojs-generate-rollup-config": "~
|
|
78
|
+
"videojs-generate-karma-config": "^8.0.1",
|
|
79
|
+
"videojs-generate-rollup-config": "~7.0.0",
|
|
81
80
|
"videojs-generator-verify": "~3.0.1",
|
|
82
81
|
"videojs-standard": "^8.0.4"
|
|
83
82
|
},
|
|
84
83
|
"generator-videojs-plugin": {
|
|
85
84
|
"version": "7.7.3"
|
|
86
85
|
},
|
|
87
|
-
"browserslist": [
|
|
88
|
-
"defaults",
|
|
89
|
-
"ie 11"
|
|
90
|
-
],
|
|
91
86
|
"husky": {
|
|
92
87
|
"hooks": {
|
|
93
88
|
"pre-commit": "lint-staged"
|
package/src/parse-stream.js
CHANGED
|
@@ -43,9 +43,14 @@ const attributeSeparator = function() {
|
|
|
43
43
|
* @param {string} attributes the attribute line to parse
|
|
44
44
|
*/
|
|
45
45
|
const parseAttributes = function(attributes) {
|
|
46
|
+
const result = {};
|
|
47
|
+
|
|
48
|
+
if (!attributes) {
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
|
|
46
52
|
// split the string using attributes as the separator
|
|
47
53
|
const attrs = attributes.split(attributeSeparator());
|
|
48
|
-
const result = {};
|
|
49
54
|
let i = attrs.length;
|
|
50
55
|
let attr;
|
|
51
56
|
|
|
@@ -166,7 +171,7 @@ export default class ParseStream extends Stream {
|
|
|
166
171
|
});
|
|
167
172
|
return;
|
|
168
173
|
}
|
|
169
|
-
match = (/^#EXTINF
|
|
174
|
+
match = (/^#EXTINF:([0-9\.]*)?,?(.*)?$/).exec(newLine);
|
|
170
175
|
if (match) {
|
|
171
176
|
event = {
|
|
172
177
|
type: 'tag',
|
|
@@ -181,7 +186,7 @@ export default class ParseStream extends Stream {
|
|
|
181
186
|
this.trigger('data', event);
|
|
182
187
|
return;
|
|
183
188
|
}
|
|
184
|
-
match = (/^#EXT-X-TARGETDURATION
|
|
189
|
+
match = (/^#EXT-X-TARGETDURATION:([0-9.]*)?/).exec(newLine);
|
|
185
190
|
if (match) {
|
|
186
191
|
event = {
|
|
187
192
|
type: 'tag',
|
|
@@ -193,7 +198,7 @@ export default class ParseStream extends Stream {
|
|
|
193
198
|
this.trigger('data', event);
|
|
194
199
|
return;
|
|
195
200
|
}
|
|
196
|
-
match = (/^#EXT-X-VERSION
|
|
201
|
+
match = (/^#EXT-X-VERSION:([0-9.]*)?/).exec(newLine);
|
|
197
202
|
if (match) {
|
|
198
203
|
event = {
|
|
199
204
|
type: 'tag',
|
|
@@ -205,7 +210,7 @@ export default class ParseStream extends Stream {
|
|
|
205
210
|
this.trigger('data', event);
|
|
206
211
|
return;
|
|
207
212
|
}
|
|
208
|
-
match = (/^#EXT-X-MEDIA-SEQUENCE
|
|
213
|
+
match = (/^#EXT-X-MEDIA-SEQUENCE:(\-?[0-9.]*)?/).exec(newLine);
|
|
209
214
|
if (match) {
|
|
210
215
|
event = {
|
|
211
216
|
type: 'tag',
|
|
@@ -217,7 +222,7 @@ export default class ParseStream extends Stream {
|
|
|
217
222
|
this.trigger('data', event);
|
|
218
223
|
return;
|
|
219
224
|
}
|
|
220
|
-
match = (/^#EXT-X-DISCONTINUITY-SEQUENCE
|
|
225
|
+
match = (/^#EXT-X-DISCONTINUITY-SEQUENCE:(\-?[0-9.]*)?/).exec(newLine);
|
|
221
226
|
if (match) {
|
|
222
227
|
event = {
|
|
223
228
|
type: 'tag',
|
|
@@ -229,7 +234,7 @@ export default class ParseStream extends Stream {
|
|
|
229
234
|
this.trigger('data', event);
|
|
230
235
|
return;
|
|
231
236
|
}
|
|
232
|
-
match = (/^#EXT-X-PLAYLIST-TYPE
|
|
237
|
+
match = (/^#EXT-X-PLAYLIST-TYPE:(.*)?$/).exec(newLine);
|
|
233
238
|
if (match) {
|
|
234
239
|
event = {
|
|
235
240
|
type: 'tag',
|
|
@@ -241,7 +246,7 @@ export default class ParseStream extends Stream {
|
|
|
241
246
|
this.trigger('data', event);
|
|
242
247
|
return;
|
|
243
248
|
}
|
|
244
|
-
match = (/^#EXT-X-BYTERANGE
|
|
249
|
+
match = (/^#EXT-X-BYTERANGE:(.*)?$/).exec(newLine);
|
|
245
250
|
if (match) {
|
|
246
251
|
event = Object.assign(parseByterange(match[1]), {
|
|
247
252
|
type: 'tag',
|
|
@@ -250,7 +255,7 @@ export default class ParseStream extends Stream {
|
|
|
250
255
|
this.trigger('data', event);
|
|
251
256
|
return;
|
|
252
257
|
}
|
|
253
|
-
match = (/^#EXT-X-ALLOW-CACHE
|
|
258
|
+
match = (/^#EXT-X-ALLOW-CACHE:(YES|NO)?/).exec(newLine);
|
|
254
259
|
if (match) {
|
|
255
260
|
event = {
|
|
256
261
|
type: 'tag',
|
|
@@ -262,7 +267,7 @@ export default class ParseStream extends Stream {
|
|
|
262
267
|
this.trigger('data', event);
|
|
263
268
|
return;
|
|
264
269
|
}
|
|
265
|
-
match = (/^#EXT-X-MAP
|
|
270
|
+
match = (/^#EXT-X-MAP:(.*)$/).exec(newLine);
|
|
266
271
|
if (match) {
|
|
267
272
|
event = {
|
|
268
273
|
type: 'tag',
|
|
@@ -283,7 +288,7 @@ export default class ParseStream extends Stream {
|
|
|
283
288
|
this.trigger('data', event);
|
|
284
289
|
return;
|
|
285
290
|
}
|
|
286
|
-
match = (/^#EXT-X-STREAM-INF
|
|
291
|
+
match = (/^#EXT-X-STREAM-INF:(.*)$/).exec(newLine);
|
|
287
292
|
if (match) {
|
|
288
293
|
event = {
|
|
289
294
|
type: 'tag',
|
|
@@ -307,6 +312,9 @@ export default class ParseStream extends Stream {
|
|
|
307
312
|
if (event.attributes.BANDWIDTH) {
|
|
308
313
|
event.attributes.BANDWIDTH = parseInt(event.attributes.BANDWIDTH, 10);
|
|
309
314
|
}
|
|
315
|
+
if (event.attributes['FRAME-RATE']) {
|
|
316
|
+
event.attributes['FRAME-RATE'] = parseFloat(event.attributes['FRAME-RATE']);
|
|
317
|
+
}
|
|
310
318
|
if (event.attributes['PROGRAM-ID']) {
|
|
311
319
|
event.attributes['PROGRAM-ID'] = parseInt(event.attributes['PROGRAM-ID'], 10);
|
|
312
320
|
}
|
|
@@ -314,7 +322,7 @@ export default class ParseStream extends Stream {
|
|
|
314
322
|
this.trigger('data', event);
|
|
315
323
|
return;
|
|
316
324
|
}
|
|
317
|
-
match = (/^#EXT-X-MEDIA
|
|
325
|
+
match = (/^#EXT-X-MEDIA:(.*)$/).exec(newLine);
|
|
318
326
|
if (match) {
|
|
319
327
|
event = {
|
|
320
328
|
type: 'tag',
|
|
@@ -342,7 +350,7 @@ export default class ParseStream extends Stream {
|
|
|
342
350
|
});
|
|
343
351
|
return;
|
|
344
352
|
}
|
|
345
|
-
match = (/^#EXT-X-PROGRAM-DATE-TIME
|
|
353
|
+
match = (/^#EXT-X-PROGRAM-DATE-TIME:(.*)$/).exec(newLine);
|
|
346
354
|
if (match) {
|
|
347
355
|
event = {
|
|
348
356
|
type: 'tag',
|
|
@@ -355,7 +363,7 @@ export default class ParseStream extends Stream {
|
|
|
355
363
|
this.trigger('data', event);
|
|
356
364
|
return;
|
|
357
365
|
}
|
|
358
|
-
match = (/^#EXT-X-KEY
|
|
366
|
+
match = (/^#EXT-X-KEY:(.*)$/).exec(newLine);
|
|
359
367
|
if (match) {
|
|
360
368
|
event = {
|
|
361
369
|
type: 'tag',
|
|
@@ -380,7 +388,7 @@ export default class ParseStream extends Stream {
|
|
|
380
388
|
this.trigger('data', event);
|
|
381
389
|
return;
|
|
382
390
|
}
|
|
383
|
-
match = (/^#EXT-X-START
|
|
391
|
+
match = (/^#EXT-X-START:(.*)$/).exec(newLine);
|
|
384
392
|
if (match) {
|
|
385
393
|
event = {
|
|
386
394
|
type: 'tag',
|
|
@@ -395,7 +403,7 @@ export default class ParseStream extends Stream {
|
|
|
395
403
|
this.trigger('data', event);
|
|
396
404
|
return;
|
|
397
405
|
}
|
|
398
|
-
match = (/^#EXT-X-CUE-OUT-CONT
|
|
406
|
+
match = (/^#EXT-X-CUE-OUT-CONT:(.*)?$/).exec(newLine);
|
|
399
407
|
if (match) {
|
|
400
408
|
event = {
|
|
401
409
|
type: 'tag',
|
|
@@ -409,7 +417,7 @@ export default class ParseStream extends Stream {
|
|
|
409
417
|
this.trigger('data', event);
|
|
410
418
|
return;
|
|
411
419
|
}
|
|
412
|
-
match = (/^#EXT-X-CUE-OUT
|
|
420
|
+
match = (/^#EXT-X-CUE-OUT:(.*)?$/).exec(newLine);
|
|
413
421
|
if (match) {
|
|
414
422
|
event = {
|
|
415
423
|
type: 'tag',
|
|
@@ -423,7 +431,7 @@ export default class ParseStream extends Stream {
|
|
|
423
431
|
this.trigger('data', event);
|
|
424
432
|
return;
|
|
425
433
|
}
|
|
426
|
-
match = (/^#EXT-X-CUE-IN
|
|
434
|
+
match = (/^#EXT-X-CUE-IN:(.*)?$/).exec(newLine);
|
|
427
435
|
if (match) {
|
|
428
436
|
event = {
|
|
429
437
|
type: 'tag',
|
|
@@ -561,6 +569,73 @@ export default class ParseStream extends Stream {
|
|
|
561
569
|
this.trigger('data', event);
|
|
562
570
|
return;
|
|
563
571
|
}
|
|
572
|
+
match = (/^#EXT-X-DATERANGE:(.*)$/).exec(newLine);
|
|
573
|
+
if (match && match[1]) {
|
|
574
|
+
event = {
|
|
575
|
+
type: 'tag',
|
|
576
|
+
tagType: 'daterange'
|
|
577
|
+
};
|
|
578
|
+
event.attributes = parseAttributes(match[1]);
|
|
579
|
+
['ID', 'CLASS'].forEach(function(key) {
|
|
580
|
+
if (event.attributes.hasOwnProperty(key)) {
|
|
581
|
+
event.attributes[key] = String(event.attributes[key]);
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
['START-DATE', 'END-DATE'].forEach(function(key) {
|
|
585
|
+
if (event.attributes.hasOwnProperty(key)) {
|
|
586
|
+
event.attributes[key] = new Date(event.attributes[key]);
|
|
587
|
+
}
|
|
588
|
+
});
|
|
589
|
+
['DURATION', 'PLANNED-DURATION'].forEach(function(key) {
|
|
590
|
+
if (event.attributes.hasOwnProperty(key)) {
|
|
591
|
+
event.attributes[key] = parseFloat(event.attributes[key]);
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
['END-ON-NEXT'].forEach(function(key) {
|
|
595
|
+
if (event.attributes.hasOwnProperty(key)) {
|
|
596
|
+
event.attributes[key] = (/YES/i).test(event.attributes[key]);
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
['SCTE35-CMD', ' SCTE35-OUT', 'SCTE35-IN'].forEach(function(key) {
|
|
600
|
+
if (event.attributes.hasOwnProperty(key)) {
|
|
601
|
+
event.attributes[key] = event.attributes[key].toString(16);
|
|
602
|
+
}
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
const clientAttributePattern = /^X-([A-Z]+-)+[A-Z]+$/;
|
|
606
|
+
|
|
607
|
+
for (const key in event.attributes) {
|
|
608
|
+
if (!clientAttributePattern.test(key)) {
|
|
609
|
+
continue;
|
|
610
|
+
}
|
|
611
|
+
const isHexaDecimal = (/[0-9A-Fa-f]{6}/g).test(event.attributes[key]);
|
|
612
|
+
const isDecimalFloating = (/^\d+(\.\d+)?$/).test(event.attributes[key]);
|
|
613
|
+
|
|
614
|
+
event.attributes[key] = isHexaDecimal ? event.attributes[key].toString(16) : isDecimalFloating ? parseFloat(event.attributes[key]) : String(event.attributes[key]);
|
|
615
|
+
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
this.trigger('data', event);
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
match = (/^#EXT-X-INDEPENDENT-SEGMENTS/).exec(newLine);
|
|
622
|
+
if (match) {
|
|
623
|
+
this.trigger('data', {
|
|
624
|
+
type: 'tag',
|
|
625
|
+
tagType: 'independent-segments'
|
|
626
|
+
});
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
match = (/^#EXT-X-CONTENT-STEERING:(.*)$/).exec(newLine);
|
|
630
|
+
if (match) {
|
|
631
|
+
event = {
|
|
632
|
+
type: 'tag',
|
|
633
|
+
tagType: 'content-steering'
|
|
634
|
+
};
|
|
635
|
+
event.attributes = parseAttributes(match[1]);
|
|
636
|
+
this.trigger('data', event);
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
564
639
|
|
|
565
640
|
// unknown tag type
|
|
566
641
|
this.trigger('data', {
|
package/src/parser.js
CHANGED
|
@@ -25,7 +25,7 @@ const camelCaseKeys = function(attributes) {
|
|
|
25
25
|
// partTargetDuration being set, but they may not be if SERVER-CONTROL appears before
|
|
26
26
|
// target durations are set.
|
|
27
27
|
const setHoldBack = function(manifest) {
|
|
28
|
-
const {
|
|
28
|
+
const {serverControl, targetDuration, partTargetDuration} = manifest;
|
|
29
29
|
|
|
30
30
|
if (!serverControl) {
|
|
31
31
|
return;
|
|
@@ -97,6 +97,8 @@ export default class Parser extends Stream {
|
|
|
97
97
|
this.parseStream = new ParseStream();
|
|
98
98
|
this.lineStream.pipe(this.parseStream);
|
|
99
99
|
|
|
100
|
+
this.lastProgramDateTime = null;
|
|
101
|
+
|
|
100
102
|
/* eslint-disable consistent-this */
|
|
101
103
|
const self = this;
|
|
102
104
|
/* eslint-enable consistent-this */
|
|
@@ -107,7 +109,7 @@ export default class Parser extends Stream {
|
|
|
107
109
|
// if specified, the active decryption key
|
|
108
110
|
let key;
|
|
109
111
|
let hasParts = false;
|
|
110
|
-
const noop = function() {
|
|
112
|
+
const noop = function() {};
|
|
111
113
|
const defaultMediaGroups = {
|
|
112
114
|
'AUDIO': {},
|
|
113
115
|
'VIDEO': {},
|
|
@@ -124,6 +126,7 @@ export default class Parser extends Stream {
|
|
|
124
126
|
this.manifest = {
|
|
125
127
|
allowCache: true,
|
|
126
128
|
discontinuityStarts: [],
|
|
129
|
+
dateRanges: [],
|
|
127
130
|
segments: []
|
|
128
131
|
};
|
|
129
132
|
// keep track of the last seen segment's byte range end, as segments are not required
|
|
@@ -132,6 +135,7 @@ export default class Parser extends Stream {
|
|
|
132
135
|
let lastByterangeEnd = 0;
|
|
133
136
|
// keep track of the last seen part's byte range end.
|
|
134
137
|
let lastPartByterangeEnd = 0;
|
|
138
|
+
const dateRangeTags = {};
|
|
135
139
|
|
|
136
140
|
// track where next segment starts
|
|
137
141
|
let nextSegmentLineNumberStart = 0;
|
|
@@ -228,6 +232,11 @@ export default class Parser extends Stream {
|
|
|
228
232
|
message: 'defaulting discontinuity sequence to zero'
|
|
229
233
|
});
|
|
230
234
|
}
|
|
235
|
+
|
|
236
|
+
if (entry.title) {
|
|
237
|
+
currentUri.title = entry.title;
|
|
238
|
+
}
|
|
239
|
+
|
|
231
240
|
if (entry.duration > 0) {
|
|
232
241
|
currentUri.duration = entry.duration;
|
|
233
242
|
}
|
|
@@ -408,9 +417,9 @@ export default class Parser extends Stream {
|
|
|
408
417
|
this.manifest.mediaGroups || defaultMediaGroups;
|
|
409
418
|
|
|
410
419
|
if (!(entry.attributes &&
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
420
|
+
entry.attributes.TYPE &&
|
|
421
|
+
entry.attributes['GROUP-ID'] &&
|
|
422
|
+
entry.attributes.NAME)) {
|
|
414
423
|
this.trigger('warn', {
|
|
415
424
|
message: 'ignoring incomplete or missing media group'
|
|
416
425
|
});
|
|
@@ -466,9 +475,24 @@ export default class Parser extends Stream {
|
|
|
466
475
|
this.manifest.dateTimeString = entry.dateTimeString;
|
|
467
476
|
this.manifest.dateTimeObject = entry.dateTimeObject;
|
|
468
477
|
}
|
|
469
|
-
|
|
470
478
|
currentUri.dateTimeString = entry.dateTimeString;
|
|
471
479
|
currentUri.dateTimeObject = entry.dateTimeObject;
|
|
480
|
+
|
|
481
|
+
const { lastProgramDateTime } = this;
|
|
482
|
+
|
|
483
|
+
this.lastProgramDateTime = new Date(entry.dateTimeString).getTime();
|
|
484
|
+
|
|
485
|
+
// We should extrapolate Program Date Time backward only during first program date time occurrence.
|
|
486
|
+
// Once we have at least one program date time point, we can always extrapolate it forward using lastProgramDateTime reference.
|
|
487
|
+
if (lastProgramDateTime === null) {
|
|
488
|
+
// Extrapolate Program Date Time backward
|
|
489
|
+
// Since it is first program date time occurrence we're assuming that
|
|
490
|
+
// all this.manifest.segments have no program date time info
|
|
491
|
+
this.manifest.segments.reduceRight((programDateTime, segment) => {
|
|
492
|
+
segment.programDateTime = programDateTime - (segment.duration * 1000);
|
|
493
|
+
return segment.programDateTime;
|
|
494
|
+
}, this.lastProgramDateTime);
|
|
495
|
+
}
|
|
472
496
|
},
|
|
473
497
|
targetduration() {
|
|
474
498
|
if (!isFinite(entry.duration) || entry.duration < 0) {
|
|
@@ -639,6 +663,81 @@ export default class Parser extends Stream {
|
|
|
639
663
|
}
|
|
640
664
|
|
|
641
665
|
setHoldBack.call(this, this.manifest);
|
|
666
|
+
},
|
|
667
|
+
'daterange'() {
|
|
668
|
+
this.manifest.dateRanges.push(camelCaseKeys(entry.attributes));
|
|
669
|
+
const index = this.manifest.dateRanges.length - 1;
|
|
670
|
+
|
|
671
|
+
this.warnOnMissingAttributes_(
|
|
672
|
+
`#EXT-X-DATERANGE #${index}`,
|
|
673
|
+
entry.attributes,
|
|
674
|
+
['ID', 'START-DATE']
|
|
675
|
+
);
|
|
676
|
+
const dateRange = this.manifest.dateRanges[index];
|
|
677
|
+
|
|
678
|
+
if (dateRange.endDate && dateRange.startDate && new Date(dateRange.endDate) < new Date(dateRange.startDate)) {
|
|
679
|
+
this.trigger('warn', {
|
|
680
|
+
message: 'EXT-X-DATERANGE END-DATE must be equal to or later than the value of the START-DATE'
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
if (dateRange.duration && dateRange.duration < 0) {
|
|
684
|
+
this.trigger('warn', {
|
|
685
|
+
message: 'EXT-X-DATERANGE DURATION must not be negative'
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
if (dateRange.plannedDuration && dateRange.plannedDuration < 0) {
|
|
689
|
+
this.trigger('warn', {
|
|
690
|
+
message: 'EXT-X-DATERANGE PLANNED-DURATION must not be negative'
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
const endOnNextYes = !!dateRange.endOnNext;
|
|
694
|
+
|
|
695
|
+
if (endOnNextYes && !dateRange.class) {
|
|
696
|
+
this.trigger('warn', {
|
|
697
|
+
message: 'EXT-X-DATERANGE with an END-ON-NEXT=YES attribute must have a CLASS attribute'
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
if (endOnNextYes && (dateRange.duration || dateRange.endDate)) {
|
|
701
|
+
this.trigger('warn', {
|
|
702
|
+
message: 'EXT-X-DATERANGE with an END-ON-NEXT=YES attribute must not contain DURATION or END-DATE attributes'
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
if (dateRange.duration && dateRange.endDate) {
|
|
706
|
+
const startDate = dateRange.startDate;
|
|
707
|
+
const newDateInSeconds = startDate.getTime() + (dateRange.duration * 1000);
|
|
708
|
+
|
|
709
|
+
this.manifest.dateRanges[index].endDate = new Date(newDateInSeconds);
|
|
710
|
+
}
|
|
711
|
+
if (!dateRangeTags[dateRange.id]) {
|
|
712
|
+
dateRangeTags[dateRange.id] = dateRange;
|
|
713
|
+
} else {
|
|
714
|
+
for (const attribute in dateRangeTags[dateRange.id]) {
|
|
715
|
+
if (!!dateRange[attribute] && JSON.stringify(dateRangeTags[dateRange.id][attribute]) !== JSON.stringify(dateRange[attribute])) {
|
|
716
|
+
this.trigger('warn', {
|
|
717
|
+
message: 'EXT-X-DATERANGE tags with the same ID in a playlist must have the same attributes values'
|
|
718
|
+
});
|
|
719
|
+
break;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
// if tags with the same ID do not have conflicting attributes, merge them
|
|
723
|
+
const dateRangeWithSameId = this.manifest.dateRanges.findIndex((dateRangeToFind) => dateRangeToFind.id === dateRange.id);
|
|
724
|
+
|
|
725
|
+
this.manifest.dateRanges[dateRangeWithSameId] = Object.assign(this.manifest.dateRanges[dateRangeWithSameId], dateRange);
|
|
726
|
+
dateRangeTags[dateRange.id] = Object.assign(dateRangeTags[dateRange.id], dateRange);
|
|
727
|
+
// after merging, delete the duplicate dateRange that was added last
|
|
728
|
+
this.manifest.dateRanges.pop();
|
|
729
|
+
}
|
|
730
|
+
},
|
|
731
|
+
'independent-segments'() {
|
|
732
|
+
this.manifest.independentSegments = true;
|
|
733
|
+
},
|
|
734
|
+
'content-steering'() {
|
|
735
|
+
this.manifest.contentSteering = camelCaseKeys(entry.attributes);
|
|
736
|
+
this.warnOnMissingAttributes_(
|
|
737
|
+
'#EXT-X-CONTENT-STEERING',
|
|
738
|
+
entry.attributes,
|
|
739
|
+
['SERVER-URI']
|
|
740
|
+
);
|
|
642
741
|
}
|
|
643
742
|
})[entry.tagType] || noop).call(self);
|
|
644
743
|
},
|
|
@@ -668,6 +767,12 @@ export default class Parser extends Stream {
|
|
|
668
767
|
// reset the last byterange end as it needs to be 0 between parts
|
|
669
768
|
lastPartByterangeEnd = 0;
|
|
670
769
|
|
|
770
|
+
// Once we have at least one program date time we can always extrapolate it forward
|
|
771
|
+
if (this.lastProgramDateTime !== null) {
|
|
772
|
+
currentUri.programDateTime = this.lastProgramDateTime;
|
|
773
|
+
this.lastProgramDateTime += currentUri.duration * 1000;
|
|
774
|
+
}
|
|
775
|
+
|
|
671
776
|
// prepare for the next URI
|
|
672
777
|
currentUri = {};
|
|
673
778
|
},
|
|
@@ -679,7 +784,7 @@ export default class Parser extends Stream {
|
|
|
679
784
|
if (entry.segment) {
|
|
680
785
|
currentUri.custom = currentUri.custom || {};
|
|
681
786
|
currentUri.custom[entry.customType] = entry.data;
|
|
682
|
-
|
|
787
|
+
// if this is manifest-level data attach to the top level manifest object
|
|
683
788
|
} else {
|
|
684
789
|
this.manifest.custom = this.manifest.custom || {};
|
|
685
790
|
this.manifest.custom[entry.customType] = entry.data;
|
|
@@ -699,7 +804,7 @@ export default class Parser extends Stream {
|
|
|
699
804
|
});
|
|
700
805
|
|
|
701
806
|
if (missing.length) {
|
|
702
|
-
this.trigger('warn', {
|
|
807
|
+
this.trigger('warn', {message: `${identifier} lacks required attribute(s): ${missing.join(', ')}`});
|
|
703
808
|
}
|
|
704
809
|
}
|
|
705
810
|
|
|
@@ -720,7 +825,13 @@ export default class Parser extends Stream {
|
|
|
720
825
|
end() {
|
|
721
826
|
// flush any buffered input
|
|
722
827
|
this.lineStream.push('\n');
|
|
828
|
+
if (this.manifest.dateRanges.length && this.lastProgramDateTime === null) {
|
|
829
|
+
this.trigger('warn', {
|
|
830
|
+
message: 'A playlist with EXT-X-DATERANGE tag must contain atleast one EXT-X-PROGRAM-DATE-TIME tag'
|
|
831
|
+
});
|
|
832
|
+
}
|
|
723
833
|
|
|
834
|
+
this.lastProgramDateTime = null;
|
|
724
835
|
this.trigger('end');
|
|
725
836
|
}
|
|
726
837
|
/**
|
|
@@ -728,7 +839,7 @@ export default class Parser extends Stream {
|
|
|
728
839
|
*
|
|
729
840
|
* @param {Object} options a map of options for the added parser
|
|
730
841
|
* @param {RegExp} options.expression a regular expression to match the custom header
|
|
731
|
-
* @param {string} options.
|
|
842
|
+
* @param {string} options.customType the custom type to register to the output
|
|
732
843
|
* @param {Function} [options.dataParser] function to parse the line into an object
|
|
733
844
|
* @param {boolean} [options.segment] should tag data be attached to the segment object
|
|
734
845
|
*/
|