@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.
Files changed (68) hide show
  1. package/README.md +7 -2
  2. package/dist/m3u8-parser.cjs.js +446 -302
  3. package/dist/m3u8-parser.es.js +443 -297
  4. package/dist/m3u8-parser.js +462 -351
  5. package/dist/m3u8-parser.min.js +2 -2
  6. package/package.json +6 -11
  7. package/src/parse-stream.js +93 -18
  8. package/src/parser.js +120 -9
  9. package/test/fixtures/integration/absoluteUris.js +1 -0
  10. package/test/fixtures/integration/allowCache.js +1 -0
  11. package/test/fixtures/integration/allowCacheInvalid.js +1 -0
  12. package/test/fixtures/integration/alternateAudio.js +1 -0
  13. package/test/fixtures/integration/alternateVideo.js +1 -0
  14. package/test/fixtures/integration/brightcove.js +1 -0
  15. package/test/fixtures/integration/byteRange.js +1 -0
  16. package/test/fixtures/integration/dateTime.js +3 -0
  17. package/test/fixtures/integration/diff-init-key.js +1 -0
  18. package/test/fixtures/integration/disallowCache.js +1 -0
  19. package/test/fixtures/integration/disc-sequence.js +9 -4
  20. package/test/fixtures/integration/discontinuity.js +19 -9
  21. package/test/fixtures/integration/domainUris.js +1 -0
  22. package/test/fixtures/integration/empty.js +1 -0
  23. package/test/fixtures/integration/emptyAllowCache.js +1 -0
  24. package/test/fixtures/integration/emptyMediaSequence.js +9 -4
  25. package/test/fixtures/integration/emptyPlaylistType.js +1 -0
  26. package/test/fixtures/integration/emptyTargetDuration.js +1 -0
  27. package/test/fixtures/integration/encrypted.js +1 -0
  28. package/test/fixtures/integration/event.js +1 -0
  29. package/test/fixtures/integration/extXPlaylistTypeInvalidPlaylist.js +3 -1
  30. package/test/fixtures/integration/extinf.js +3 -1
  31. package/test/fixtures/integration/fmp4.js +3 -1
  32. package/test/fixtures/integration/headerOnly.js +1 -0
  33. package/test/fixtures/integration/invalidAllowCache.js +1 -0
  34. package/test/fixtures/integration/invalidMediaSequence.js +9 -4
  35. package/test/fixtures/integration/invalidPlaylistType.js +1 -0
  36. package/test/fixtures/integration/invalidTargetDuration.js +1 -0
  37. package/test/fixtures/integration/liveMissingSegmentDuration.js +3 -1
  38. package/test/fixtures/integration/liveStart30sBefore.js +19 -9
  39. package/test/fixtures/integration/llhls-byte-range.js +1 -0
  40. package/test/fixtures/integration/llhls-delta-byte-range.js +1 -0
  41. package/test/fixtures/integration/llhls.js +8 -0
  42. package/test/fixtures/integration/llhlsDelta.js +5 -0
  43. package/test/fixtures/integration/manifestExtTTargetdurationNegative.js +1 -0
  44. package/test/fixtures/integration/manifestExtXEndlistEarly.js +1 -0
  45. package/test/fixtures/integration/manifestNoExtM3u.js +1 -0
  46. package/test/fixtures/integration/master-fmp4.js +27 -25
  47. package/test/fixtures/integration/master.js +1 -0
  48. package/test/fixtures/integration/media.js +1 -0
  49. package/test/fixtures/integration/mediaSequence.js +9 -4
  50. package/test/fixtures/integration/missingEndlist.js +1 -0
  51. package/test/fixtures/integration/missingExtinf.js +1 -0
  52. package/test/fixtures/integration/missingMediaSequence.js +9 -4
  53. package/test/fixtures/integration/missingSegmentDuration.js +3 -1
  54. package/test/fixtures/integration/multipleAudioGroups.js +1 -0
  55. package/test/fixtures/integration/multipleAudioGroupsCombinedMain.js +1 -0
  56. package/test/fixtures/integration/multipleTargetDurations.js +1 -0
  57. package/test/fixtures/integration/multipleVideo.js +1 -0
  58. package/test/fixtures/integration/negativeMediaSequence.js +9 -4
  59. package/test/fixtures/integration/playlist.js +1 -0
  60. package/test/fixtures/integration/playlistMediaSequenceHigher.js +3 -1
  61. package/test/fixtures/integration/start.js +1 -0
  62. package/test/fixtures/integration/streamInfInvalid.js +1 -0
  63. package/test/fixtures/integration/twoMediaSequences.js +9 -4
  64. package/test/fixtures/integration/versionInvalid.js +1 -0
  65. package/test/fixtures/integration/whiteSpace.js +1 -0
  66. package/test/fixtures/integration/zeroDuration.js +1 -0
  67. package/test/parse-stream.test.js +112 -16
  68. package/test/parser.test.js +392 -17
@@ -1,2 +1,2 @@
1
- /*! @name @transmitlive/m3u8-parser @version 4.7.2-beta.6 @license Apache-2.0 */
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": "4.7.2-beta.6",
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.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.37.1",
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": "~7.1.0",
80
- "videojs-generate-rollup-config": "~6.2.0",
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"
@@ -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:?([0-9\.]*)?,?(.*)?$/).exec(newLine);
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:?([0-9.]*)?/).exec(newLine);
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:?([0-9.]*)?/).exec(newLine);
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:?(\-?[0-9.]*)?/).exec(newLine);
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:?(\-?[0-9.]*)?/).exec(newLine);
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:?(.*)?$/).exec(newLine);
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:?(.*)?$/).exec(newLine);
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:?(YES|NO)?/).exec(newLine);
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:?(.*)$/).exec(newLine);
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:?(.*)$/).exec(newLine);
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:?(.*)$/).exec(newLine);
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:?(.*)$/).exec(newLine);
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:?(.*)$/).exec(newLine);
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:?(.*)$/).exec(newLine);
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:?(.*)?$/).exec(newLine);
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:?(.*)?$/).exec(newLine);
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:?(.*)?$/).exec(newLine);
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 { serverControl, targetDuration, partTargetDuration } = manifest;
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
- entry.attributes.TYPE &&
412
- entry.attributes['GROUP-ID'] &&
413
- entry.attributes.NAME)) {
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
- // if this is manifest-level data attach to the top level manifest object
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', { message: `${identifier} lacks required attribute(s): ${missing.join(', ')}` });
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.type the type to register to the output
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
  */
@@ -1,5 +1,6 @@
1
1
  module.exports = {
2
2
  allowCache: true,
3
+ dateRanges: [],
3
4
  mediaSequence: 0,
4
5
  playlistType: 'VOD',
5
6
  segments: [