/* * @File : hls.js.js * @Author : jade * @Date : 2024/2/5 16:07 * @Email : jadehh@1ive.com * @Software : Samples * @Desc : */ let t = {}; function e(e) { if (t.strictMode) throw e; t.silent || console.error(e.message) } function s(t, ...s) { for (const [i, n] of s.entries()) n || e(new Error(`${t} : Failed at [${i}]`)) } function i(...t) { for (const [s, [i, n]] of t.entries()) i && (n || e(new Error(`Conditional Assert : Failed at [${s}]`))) } function n(...t) { for (const [s, i] of t.entries()) void 0 === i && e(new Error(`Param Check : Failed at [${s}]`)) } function a(...t) { for (const [s, [i, n]] of t.entries()) i && void 0 === n && e(new Error(`Conditional Param Check : Failed at [${s}]`)) } function r(t) { e(new Error(`Invalid Playlist : ${t}`)) } function o(t, e = 10) { if ("number" == typeof t) return t; const s = 10 === e ? Number.parseFloat(t) : Number.parseInt(t, e); return Number.isNaN(s) ? 0 : s } function E(t) { (t.startsWith("0x") || t.startsWith("0X")) && (t = t.slice(2)); const e = new Uint8Array(t.length / 2); for (let s = 0; s < t.length; s += 2) e[s / 2] = o(t.slice(s, s + 2), 16); return e } function T(t, s = 0, i = t.length) { i <= s && e(new Error(`end must be larger than start : start=${s}, end=${i}`)); const n = []; for (let e = s; e < i; e++) n.push(`0${(255 & t[e]).toString(16).toUpperCase()}`.slice(-2)); return `0x${n.join("")}` } function u(t, e, s = 0) { let i = -1; for (let n = 0, a = 0; n < t.length; n++) if (t[n] === e) { if (a++ === s) return [t.slice(0, n), t.slice(n + 1)]; i = n } return -1 !== i ? [t.slice(0, i), t.slice(i + 1)] : [t] } function c(t) { const e = []; let s = !1; for (const i of t) "-" !== i && "_" !== i ? s ? (e.push(i.toUpperCase()), s = !1) : e.push(i.toLowerCase()) : s = !0; return e.join("") } function l(t) { return `${t.getUTCFullYear()}-${("0" + (t.getUTCMonth() + 1)).slice(-2)}-${("0" + t.getUTCDate()).slice(-2)}T${("0" + t.getUTCHours()).slice(-2)}:${("0" + t.getUTCMinutes()).slice(-2)}:${("0" + t.getUTCSeconds()).slice(-2)}.${("00" + t.getUTCMilliseconds()).slice(-3)}Z` } function h(e = {}) { t = Object.assign(t, e) } function X() { return Object.assign({}, t) } function p(t, e) { e = Math.trunc(e) || 0; const s = t.length >>> 0; if (e < 0 && (e = s + e), !(e < 0 || e >= s)) return t[e] } class I { constructor({ type: t, uri: e, groupId: s, language: a, assocLanguage: r, name: o, isDefault: E, autoselect: T, forced: u, instreamId: c, characteristics: l, channels: h }) { n(t, s, o), i(["SUBTITLES" === t, e], ["CLOSED-CAPTIONS" === t, c], ["CLOSED-CAPTIONS" === t, !e], [u, "SUBTITLES" === t]), this.type = t, this.uri = e, this.groupId = s, this.language = a, this.assocLanguage = r, this.name = o, this.isDefault = E, this.autoselect = T, this.forced = u, this.instreamId = c, this.characteristics = l, this.channels = h } } class N { constructor({ uri: t, isIFrameOnly: e = !1, bandwidth: s, averageBandwidth: i, score: a, codecs: r, resolution: o, frameRate: E, hdcpLevel: T, allowedCpc: u, videoRange: c, stableVariantId: l, programId: h, audio: X = [], video: p = [], subtitles: I = [], closedCaptions: N = [], currentRenditions: d = {audio: 0, video: 0, subtitles: 0, closedCaptions: 0} }) { n(t, s), this.uri = t, this.isIFrameOnly = e, this.bandwidth = s, this.averageBandwidth = i, this.score = a, this.codecs = r, this.resolution = o, this.frameRate = E, this.hdcpLevel = T, this.allowedCpc = u, this.videoRange = c, this.stableVariantId = l, this.programId = h, this.audio = X, this.video = p, this.subtitles = I, this.closedCaptions = N, this.currentRenditions = d } } class d { constructor({id: t, value: e, uri: i, language: a}) { n(t, e || i), s("SessionData cannot have both value and uri, shoud be either.", !(e && i)), this.id = t, this.value = e, this.uri = i, this.language = a } } class A { constructor({method: t, uri: e, iv: s, format: r, formatVersion: o}) { n(t), a(["NONE" !== t, e]), i(["NONE" === t, !(e || s || r || o)]), this.method = t, this.uri = e, this.iv = s, this.format = r, this.formatVersion = o } } class f { constructor({hint: t = !1, uri: e, mimeType: s, byterange: i}) { n(e), this.hint = t, this.uri = e, this.mimeType = s, this.byterange = i } } class S { constructor({ id: t, classId: e, start: s, end: r, duration: o, plannedDuration: E, endOnNext: T, attributes: u = {} }) { n(t), a([!0 === T, e]), i([r, s], [r, s <= r], [o, o >= 0], [E, E >= 0]), this.id = t, this.classId = e, this.start = s, this.end = r, this.duration = o, this.plannedDuration = E, this.endOnNext = T, this.attributes = u } } class R { constructor({type: t, duration: e, tagName: s, value: i}) { n(t), a(["OUT" === t, e]), a(["RAW" === t, s]), this.type = t, this.duration = e, this.tagName = s, this.value = i } } class m { constructor(t) { n(t), this.type = t } } class g extends m { constructor({isMasterPlaylist: t, uri: e, version: s, independentSegments: i = !1, start: a, source: r}) { super("playlist"), n(t), this.isMasterPlaylist = t, this.uri = e, this.version = s, this.independentSegments = i, this.start = a, this.source = r } } class O extends g { constructor(t = {}) { super(Object.assign(Object.assign({}, t), {isMasterPlaylist: !0})); const {variants: e = [], currentVariant: s, sessionDataList: i = [], sessionKeyList: n = []} = t; this.variants = e, this.currentVariant = s, this.sessionDataList = i, this.sessionKeyList = n } } class D extends g { constructor(t = {}) { super(Object.assign(Object.assign({}, t), {isMasterPlaylist: !1})); const { targetDuration: e, mediaSequenceBase: s = 0, discontinuitySequenceBase: i = 0, endlist: n = !1, playlistType: a, isIFrame: r, segments: o = [], prefetchSegments: E = [], lowLatencyCompatibility: T, partTargetDuration: u, renditionReports: c = [], skip: l = 0, hash: h } = t; this.targetDuration = e, this.mediaSequenceBase = s, this.discontinuitySequenceBase = i, this.endlist = n, this.playlistType = a, this.isIFrame = r, this.segments = o, this.prefetchSegments = E, this.lowLatencyCompatibility = T, this.partTargetDuration = u, this.renditionReports = c, this.skip = l, this.hash = h } } class P extends m { constructor({ uri: t, mimeType: e, data: s, duration: i, title: n, byterange: a, discontinuity: r, mediaSequenceNumber: o = 0, discontinuitySequence: E = 0, key: T, map: u, programDateTime: c, dateRange: l, markers: h = [], parts: X = [] }) { super("segment"), this.uri = t, this.mimeType = e, this.data = s, this.duration = i, this.title = n, this.byterange = a, this.discontinuity = r, this.mediaSequenceNumber = o, this.discontinuitySequence = E, this.key = T, this.map = u, this.programDateTime = c, this.dateRange = l, this.markers = h, this.parts = X } } class y extends m { constructor({hint: t = !1, uri: e, duration: s, independent: i, byterange: a, gap: r}) { super("part"), n(e), this.hint = t, this.uri = e, this.duration = s, this.independent = i, this.duration = s, this.byterange = a, this.gap = r } } class C extends m { constructor({uri: t, discontinuity: e, mediaSequenceNumber: s = 0, discontinuitySequence: i = 0, key: a}) { super("prefetch"), n(t), this.uri = t, this.discontinuity = e, this.mediaSequenceNumber = s, this.discontinuitySequence = i, this.key = a } } class U { constructor({uri: t, lastMSN: e, lastPart: s}) { n(t), this.uri = t, this.lastMSN = e, this.lastPart = s } } var M = Object.freeze({ __proto__: null, Rendition: I, Variant: N, SessionData: d, Key: A, MediaInitializationSection: f, DateRange: S, SpliceInfo: R, Playlist: g, MasterPlaylist: O, MediaPlaylist: D, Segment: P, PartialSegment: y, PrefetchSegment: C, RenditionReport: U }); function b(t) { return function (t, e = " ") { return t ? (t = t.trim(), " " === e || (t.startsWith(e) && (t = t.slice(1)), t.endsWith(e) && (t = t.slice(0, -1))), t) : t }(t, '"') } function L(t) { const e = u(t, ","); return {duration: o(e[0]), title: decodeURIComponent(escape(e[1]))} } function v(t) { const e = u(t, "@"); return {length: o(e[0]), offset: e[1] ? o(e[1]) : -1} } function $(t) { const e = u(t, "x"); return {width: o(e[0]), height: o(e[1])} } function Y(t) { const e = "ALLOWED-CPC: Each entry must consit of KEYFORMAT and Content Protection Configuration", s = t.split(","); 0 === s.length && r(e); const i = []; for (const t of s) { const [s, n] = u(t, ":"); s && n ? i.push({format: s, cpcList: n.split("/")}) : r(e) } return i } function F(t) { const e = E(t); return 16 !== e.length && r("IV must be a 128-bit unsigned integer"), e } function G(t, e) { e.IV && t.compatibleVersion < 2 && (t.compatibleVersion = 2), (e.KEYFORMAT || e.KEYFORMATVERSIONS) && t.compatibleVersion < 5 && (t.compatibleVersion = 5) } function V(t) { const e = {}; for (const i of function (t) { const e = []; let s = !0, i = 0; const n = []; for (let a = 0; a < t.length; a++) { const r = t[a]; s && "," === r ? (e.push(t.slice(i, a).trim()), i = a + 1) : '"' !== r && "'" !== r || (s ? (n.push(r), s = !1) : r === p(n, -1) ? (n.pop(), s = !0) : n.push(r)) } return e.push(t.slice(i).trim()), e }(t)) { const [t, n] = u(i, "="), a = b(n); switch (t) { case"URI": e[t] = a; break; case"START-DATE": case"END-DATE": e[t] = new Date(a); break; case"IV": e[t] = F(a); break; case"BYTERANGE": e[t] = v(a); break; case"RESOLUTION": e[t] = $(a); break; case"ALLOWED-CPC": e[t] = Y(a); break; case"END-ON-NEXT": case"DEFAULT": case"AUTOSELECT": case"FORCED": case"PRECISE": case"CAN-BLOCK-RELOAD": case"INDEPENDENT": case"GAP": e[t] = "YES" === a; break; case"DURATION": case"PLANNED-DURATION": case"BANDWIDTH": case"AVERAGE-BANDWIDTH": case"FRAME-RATE": case"TIME-OFFSET": case"CAN-SKIP-UNTIL": case"HOLD-BACK": case"PART-HOLD-BACK": case"PART-TARGET": case"BYTERANGE-START": case"BYTERANGE-LENGTH": case"LAST-MSN": case"LAST-PART": case"SKIPPED-SEGMENTS": case"SCORE": case"PROGRAM-ID": e[t] = o(a); break; default: t.startsWith("SCTE35-") ? e[t] = E(a) : t.startsWith("X-") ? e[t] = (s = n).startsWith('"') ? b(s) : s.startsWith("0x") || s.startsWith("0X") ? E(s) : o(s) : ("VIDEO-RANGE" === t && "SDR" !== a && "HLG" !== a && "PQ" !== a && r(`VIDEO-RANGE: unknown value "${a}"`), e[t] = a) } } var s; return e } function w() { r("The file contains both media and master playlist tags.") } function B(t, e, s) { const i = function ({attributes: t}) { return new I({ type: t.TYPE, uri: t.URI, groupId: t["GROUP-ID"], language: t.LANGUAGE, assocLanguage: t["ASSOC-LANGUAGE"], name: t.NAME, isDefault: t.DEFAULT, autoselect: t.AUTOSELECT, forced: t.FORCED, instreamId: t["INSTREAM-ID"], characteristics: t.CHARACTERISTICS, channels: t.CHANNELS }) }(e), n = t[c(s)], a = function (t, e) { let s = !1; for (const i of t) { if (i.name === e.name) return "All EXT-X-MEDIA tags in the same Group MUST have different NAME attributes."; i.isDefault && (s = !0) } return s && e.isDefault ? "EXT-X-MEDIA A Group MUST NOT have more than one member with a DEFAULT attribute of YES." : "" }(n, i); a && r(a), n.push(i), i.isDefault && (t.currentRenditions[c(s)] = n.length - 1) } function H(t, e, s, i, n) { const a = new N({ uri: s, bandwidth: e.BANDWIDTH, averageBandwidth: e["AVERAGE-BANDWIDTH"], score: e.SCORE, codecs: e.CODECS, resolution: e.RESOLUTION, frameRate: e["FRAME-RATE"], hdcpLevel: e["HDCP-LEVEL"], allowedCpc: e["ALLOWED-CPC"], videoRange: e["VIDEO-RANGE"], stableVariantId: e["STABLE-VARIANT-ID"], programId: e["PROGRAM-ID"] }); for (const s of t) if ("EXT-X-MEDIA" === s.name) { const t = s.attributes, i = t.TYPE; if (i && t["GROUP-ID"] || r("EXT-X-MEDIA TYPE attribute is REQUIRED."), e[i] === t["GROUP-ID"] && (B(a, s, i), "CLOSED-CAPTIONS" === i)) for (const {instreamId: t} of a.closedCaptions) if (t && t.startsWith("SERVICE") && n.compatibleVersion < 7) { n.compatibleVersion = 7; break } } return function (t, e, s) { for (const i of ["AUDIO", "VIDEO", "SUBTITLES", "CLOSED-CAPTIONS"]) "CLOSED-CAPTIONS" === i && "NONE" === t[i] ? (s.isClosedCaptionsNone = !0, e.closedCaptions = []) : t[i] && !e[c(i)].some((e => e.groupId === t[i])) && r(`${i} attribute MUST match the value of the GROUP-ID attribute of an EXT-X-MEDIA tag whose TYPE attribute is ${i}.`) }(e, a, n), a.isIFrameOnly = i, a } function K(t, e) { if (t.method !== e.method) return !1; if (t.uri !== e.uri) return !1; if (t.iv) { if (!e.iv) return !1; if (t.iv.length !== e.iv.length) return !1; for (let s = 0; s < t.iv.length; s++) if (t.iv[s] !== e.iv[s]) return !1 } else if (e.iv) return !1; return t.format === e.format && t.formatVersion === e.formatVersion } function k(t, e, s, i, n, a, o) { const E = new P({uri: e, mediaSequenceNumber: n, discontinuitySequence: a}); let T = !1, u = !1; for (let e = s; e <= i; e++) { const {name: s, value: i, attributes: n} = t[e]; if ("EXTINF" === s) !Number.isInteger(i.duration) && o.compatibleVersion < 3 && (o.compatibleVersion = 3), Math.round(i.duration) > o.targetDuration && r("EXTINF duration, when rounded to the nearest integer, MUST be less than or equal to the target duration"), E.duration = i.duration, E.title = i.title; else if ("EXT-X-BYTERANGE" === s) o.compatibleVersion < 4 && (o.compatibleVersion = 4), E.byterange = i; else if ("EXT-X-DISCONTINUITY" === s) E.parts.length > 0 && r("EXT-X-DISCONTINUITY must appear before the first EXT-X-PART tag of the Parent Segment."), E.discontinuity = !0; else if ("EXT-X-KEY" === s) E.parts.length > 0 && r("EXT-X-KEY must appear before the first EXT-X-PART tag of the Parent Segment."), G(o, n), E.key = new A({ method: n.METHOD, uri: n.URI, iv: n.IV, format: n.KEYFORMAT, formatVersion: n.KEYFORMATVERSIONS }); else if ("EXT-X-MAP" === s) E.parts.length > 0 && r("EXT-X-MAP must appear before the first EXT-X-PART tag of the Parent Segment."), o.compatibleVersion < 5 && (o.compatibleVersion = 5), o.hasMap = !0, E.map = new f({ uri: n.URI, byterange: n.BYTERANGE }); else if ("EXT-X-PROGRAM-DATE-TIME" === s) E.programDateTime = i; else if ("EXT-X-DATERANGE" === s) { const t = {}; for (const e of Object.keys(n)) (e.startsWith("SCTE35-") || e.startsWith("X-")) && (t[e] = n[e]); E.dateRange = new S({ id: n.ID, classId: n.CLASS, start: n["START-DATE"], end: n["END-DATE"], duration: n.DURATION, plannedDuration: n["PLANNED-DURATION"], endOnNext: n["END-ON-NEXT"], attributes: t }) } else if ("EXT-X-CUE-OUT" === s) E.markers.push(new R({ type: "OUT", duration: n && n.DURATION || i })); else if ("EXT-X-CUE-IN" === s) E.markers.push(new R({type: "IN"})); else if ("EXT-X-CUE-OUT-CONT" === s || "EXT-X-CUE" === s || "EXT-OATCLS-SCTE35" === s || "EXT-X-ASSET" === s || "EXT-X-SCTE35" === s) E.markers.push(new R({ type: "RAW", tagName: s, value: i })); else if ("EXT-X-PRELOAD-HINT" !== s || n.TYPE) if ("EXT-X-PRELOAD-HINT" === s && "PART" === n.TYPE && u) r("Servers should not add more than one EXT-X-PRELOAD-HINT tag with the same TYPE attribute to a Playlist."); else if ("EXT-X-PART" !== s && "EXT-X-PRELOAD-HINT" !== s || n.URI) { if ("EXT-X-PRELOAD-HINT" === s && "MAP" === n.TYPE) T && r("Servers should not add more than one EXT-X-PRELOAD-HINT tag with the same TYPE attribute to a Playlist."), T = !0, o.hasMap = !0, E.map = new f({ hint: !0, uri: n.URI, byterange: {length: n["BYTERANGE-LENGTH"], offset: n["BYTERANGE-START"] || 0} }); else if ("EXT-X-PART" === s || "EXT-X-PRELOAD-HINT" === s && "PART" === n.TYPE) { "EXT-X-PART" !== s || n.DURATION || r("EXT-X-PART: DURATION attribute is mandatory"), "EXT-X-PRELOAD-HINT" === s && (u = !0); const t = new y({ hint: "EXT-X-PRELOAD-HINT" === s, uri: n.URI, byterange: "EXT-X-PART" === s ? n.BYTERANGE : { length: n["BYTERANGE-LENGTH"], offset: n["BYTERANGE-START"] || 0 }, duration: n.DURATION, independent: n.INDEPENDENT, gap: n.GAP }); E.parts.push(t) } } else r("EXT-X-PART / EXT-X-PRELOAD-HINT: URI attribute is mandatory"); else r("EXT-X-PRELOAD-HINT: TYPE attribute is mandatory") } return E } function W(t, e, s, i, n, a, o) { const E = new C({uri: e, mediaSequenceNumber: n, discontinuitySequence: a}); for (let e = s; e <= i; e++) { const {name: s, attributes: i} = t[e]; "EXTINF" === s ? r("A prefetch segment must not be advertised with an EXTINF tag.") : "EXT-X-DISCONTINUITY" === s ? r("A prefetch segment must not be advertised with an EXT-X-DISCONTINUITY tag.") : "EXT-X-PREFETCH-DISCONTINUITY" === s ? E.discontinuity = !0 : "EXT-X-KEY" === s ? (G(o, i), E.key = new A({ method: i.METHOD, uri: i.URI, iv: i.IV, format: i.KEYFORMAT, formatVersion: i.KEYFORMATVERSIONS })) : "EXT-X-MAP" === s && r("Prefetch segments must not be advertised with an EXT-X-MAP tag.") } return E } function q(t, e) { var s; const i = new D; let n = -1, a = 0, o = !1, E = !1, T = 0, u = null, c = null, l = !1; for (const [s, h] of t.entries()) { const {name: X, value: p, attributes: I, category: N} = h; if ("Segment" !== N) { if ("EXT-X-VERSION" === X) void 0 === i.version ? i.version = p : r("A Playlist file MUST NOT contain more than one EXT-X-VERSION tag."); else if ("EXT-X-TARGETDURATION" === X) i.targetDuration = e.targetDuration = p; else if ("EXT-X-MEDIA-SEQUENCE" === X) i.segments.length > 0 && r("The EXT-X-MEDIA-SEQUENCE tag MUST appear before the first Media Segment in the Playlist."), i.mediaSequenceBase = a = p; else if ("EXT-X-DISCONTINUITY-SEQUENCE" === X) i.segments.length > 0 && r("The EXT-X-DISCONTINUITY-SEQUENCE tag MUST appear before the first Media Segment in the Playlist."), o && r("The EXT-X-DISCONTINUITY-SEQUENCE tag MUST appear before any EXT-X-DISCONTINUITY tag."), i.discontinuitySequenceBase = T = p; else if ("EXT-X-ENDLIST" === X) i.endlist = !0; else if ("EXT-X-PLAYLIST-TYPE" === X) i.playlistType = p; else if ("EXT-X-I-FRAMES-ONLY" === X) e.compatibleVersion < 4 && (e.compatibleVersion = 4), i.isIFrame = !0; else if ("EXT-X-INDEPENDENT-SEGMENTS" === X) i.independentSegments && r("EXT-X-INDEPENDENT-SEGMENTS tag MUST NOT appear more than once in a Playlist"), i.independentSegments = !0; else if ("EXT-X-START" === X) i.start && r("EXT-X-START tag MUST NOT appear more than once in a Playlist"), "number" != typeof I["TIME-OFFSET"] && r("EXT-X-START: TIME-OFFSET attribute is REQUIRED"), i.start = { offset: I["TIME-OFFSET"], precise: I.PRECISE || !1 }; else if ("EXT-X-SERVER-CONTROL" === X) I["CAN-BLOCK-RELOAD"] || r("EXT-X-SERVER-CONTROL: CAN-BLOCK-RELOAD=YES is mandatory for Low-Latency HLS"), i.lowLatencyCompatibility = { canBlockReload: I["CAN-BLOCK-RELOAD"], canSkipUntil: I["CAN-SKIP-UNTIL"], holdBack: I["HOLD-BACK"], partHoldBack: I["PART-HOLD-BACK"] }; else if ("EXT-X-PART-INF" === X) I["PART-TARGET"] || r("EXT-X-PART-INF: PART-TARGET attribute is mandatory"), i.partTargetDuration = I["PART-TARGET"]; else if ("EXT-X-RENDITION-REPORT" === X) I.URI || r("EXT-X-RENDITION-REPORT: URI attribute is mandatory"), 0 === I.URI.search(/^[a-z]+:/) && r("EXT-X-RENDITION-REPORT: URI must be relative to the playlist uri"), i.renditionReports.push(new U({ uri: I.URI, lastMSN: I["LAST-MSN"], lastPart: I["LAST-PART"] })); else if ("EXT-X-SKIP" === X) I["SKIPPED-SEGMENTS"] || r("EXT-X-SKIP: SKIPPED-SEGMENTS attribute is mandatory"), e.compatibleVersion < 9 && (e.compatibleVersion = 9), i.skip = I["SKIPPED-SEGMENTS"], a += i.skip; else if ("EXT-X-PREFETCH" === X) { const r = W(t, p, -1 === n ? s : n, s - 1, a++, T, e); r && (r.discontinuity && (r.discontinuitySequence++, T = r.discontinuitySequence), r.key ? u = r.key : r.key = u, i.prefetchSegments.push(r)), E = !0, n = -1 } else if ("string" == typeof h) { -1 === n && r("A URI line is not preceded by any segment tags"), i.targetDuration || r("The EXT-X-TARGETDURATION tag is REQUIRED"), E && r("These segments must appear after all complete segments."); const o = k(t, h, n, s - 1, a++, T, e); o && ([T, u, c] = x(i, o, T, u, c), !l && o.parts.length > 0 && (l = !0)), n = -1 } } else -1 === n && (n = s), "EXT-X-DISCONTINUITY" === X && (o = !0) } if (-1 !== n) { const o = k(t, "", n, t.length - 1, a++, T, e); if (o) { const {parts: t} = o; t.length > 0 && !i.endlist && !(null === (s = p(t, -1)) || void 0 === s ? void 0 : s.hint) && r("If the Playlist contains EXT-X-PART tags and does not contain an EXT-X-ENDLIST tag, the Playlist must contain an EXT-X-PRELOAD-HINT tag with a TYPE=PART attribute"), x(i, o, u, c), !l && o.parts.length > 0 && (l = !0) } } return function (t) { const e = new Map, s = new Map; let i = !1, n = !1; for (let a = t.length - 1; a >= 0; a--) { const {programDateTime: o, dateRange: E} = t[a]; if (o && (n = !0), E && E.start) { i = !0, E.endOnNext && (E.end || E.duration) && r("An EXT-X-DATERANGE tag with an END-ON-NEXT=YES attribute MUST NOT contain DURATION or END-DATE attributes."); const t = E.start.getTime(), n = E.duration || 0; E.end && E.duration && t + 1e3 * n !== E.end.getTime() && r("END-DATE MUST be equal to the value of the START-DATE attribute plus the value of the DURATION"), E.endOnNext && (E.end = e.get(E.classId)), e.set(E.classId, E.start); const a = E.end ? E.end.getTime() : E.start.getTime() + 1e3 * (E.duration || 0), o = s.get(E.classId); if (o) { for (const e of o) (e.start <= t && e.end > t || e.start >= t && e.start < a) && r("DATERANGE tags with the same CLASS should not overlap"); o.push({start: t, end: a}) } else E.classId && s.set(E.classId, [{start: t, end: a}]) } } i && !n && r("If a Playlist contains an EXT-X-DATERANGE tag, it MUST also contain at least one EXT-X-PROGRAM-DATE-TIME tag.") }(i.segments), i.lowLatencyCompatibility && function ({ lowLatencyCompatibility: t, targetDuration: e, partTargetDuration: s, segments: i, renditionReports: n }, a) { const {canSkipUntil: o, holdBack: E, partHoldBack: T} = t; o < 6 * e && r("The Skip Boundary must be at least six times the EXT-X-TARGETDURATION."); E < 3 * e && r("HOLD-BACK must be at least three times the EXT-X-TARGETDURATION."); if (a) { void 0 === s && r("EXT-X-PART-INF is required if a Playlist contains one or more EXT-X-PART tags"), void 0 === T && r("EXT-X-PART: PART-HOLD-BACK attribute is mandatory"), T < s && r("PART-HOLD-BACK must be at least PART-TARGET"); for (const [t, {parts: e}] of i.entries()) { e.length > 0 && t < i.length - 3 && r("Remove EXT-X-PART tags from the Playlist after they are greater than three target durations from the end of the Playlist."); for (const [t, {duration: i}] of e.entries()) void 0 !== i && (i > s && r("PART-TARGET is the maximum duration of any Partial Segment"), t < e.length - 1 && i < .85 * s && r("All Partial Segments except the last part of a segment must have a duration of at least 85% of PART-TARGET")) } } for (const t of n) { const e = i.at(-1); null !== t.lastMSN && void 0 !== t.lastMSN || (t.lastMSN = e.mediaSequenceNumber), (null === t.lastPart || void 0 === t.lastPart) && e.parts.length > 0 && (t.lastPart = e.parts.length - 1) } }(i, l), i } function x(t, e, s, i, n) { const {discontinuity: a, key: o, map: E, byterange: T, uri: u} = e; if (a && (e.discontinuitySequence = s + 1), o || (e.key = i), E || (e.map = n), T && -1 === T.offset) { const {segments: e} = t; if (e.length > 0) { const t = p(e, -1); t.byterange && t.uri === u ? T.offset = t.byterange.offset + t.byterange.length : r("If offset of EXT-X-BYTERANGE is not present, a previous Media Segment MUST be a sub-range of the same media resource") } else r("If offset of EXT-X-BYTERANGE is not present, a previous Media Segment MUST appear in the Playlist file") } return t.segments.push(e), [e.discontinuitySequence, e.key, e.map] } function j(t, e) { const [s, i] = function (t) { const e = t.indexOf(":"); return -1 === e ? [t.slice(1).trim(), null] : [t.slice(1, e).trim(), t.slice(e + 1).trim()] }(t), n = function (t) { switch (t) { case"EXTM3U": case"EXT-X-VERSION": return "Basic"; case"EXTINF": case"EXT-X-BYTERANGE": case"EXT-X-DISCONTINUITY": case"EXT-X-PREFETCH-DISCONTINUITY": case"EXT-X-KEY": case"EXT-X-MAP": case"EXT-X-PROGRAM-DATE-TIME": case"EXT-X-DATERANGE": case"EXT-X-CUE-OUT": case"EXT-X-CUE-IN": case"EXT-X-CUE-OUT-CONT": case"EXT-X-CUE": case"EXT-OATCLS-SCTE35": case"EXT-X-ASSET": case"EXT-X-SCTE35": case"EXT-X-PART": case"EXT-X-PRELOAD-HINT": return "Segment"; case"EXT-X-TARGETDURATION": case"EXT-X-MEDIA-SEQUENCE": case"EXT-X-DISCONTINUITY-SEQUENCE": case"EXT-X-ENDLIST": case"EXT-X-PLAYLIST-TYPE": case"EXT-X-I-FRAMES-ONLY": case"EXT-X-SERVER-CONTROL": case"EXT-X-PART-INF": case"EXT-X-PREFETCH": case"EXT-X-RENDITION-REPORT": case"EXT-X-SKIP": return "MediaPlaylist"; case"EXT-X-MEDIA": case"EXT-X-STREAM-INF": case"EXT-X-I-FRAME-STREAM-INF": case"EXT-X-SESSION-DATA": case"EXT-X-SESSION-KEY": return "MasterPlaylist"; case"EXT-X-INDEPENDENT-SEGMENTS": case"EXT-X-START": return "MediaorMasterPlaylist"; default: return "Unknown" } }(s); if (function (t, e) { if ("Segment" === t || "MediaPlaylist" === t) return void 0 === e.isMasterPlaylist ? void (e.isMasterPlaylist = !1) : void (e.isMasterPlaylist && w()); if ("MasterPlaylist" === t) { if (void 0 === e.isMasterPlaylist) return void (e.isMasterPlaylist = !0); !1 === e.isMasterPlaylist && w() } }(n, e), "Unknown" === n) return null; "MediaPlaylist" === n && "EXT-X-RENDITION-REPORT" !== s && "EXT-X-PREFETCH" !== s && (e.hash[s] && r("There MUST NOT be more than one Media Playlist tag of each type in any Media Playlist"), e.hash[s] = !0); const [a, E] = function (t, e) { switch (t) { case"EXTM3U": case"EXT-X-DISCONTINUITY": case"EXT-X-ENDLIST": case"EXT-X-I-FRAMES-ONLY": case"EXT-X-INDEPENDENT-SEGMENTS": case"EXT-X-CUE-IN": return [null, null]; case"EXT-X-VERSION": case"EXT-X-TARGETDURATION": case"EXT-X-MEDIA-SEQUENCE": case"EXT-X-DISCONTINUITY-SEQUENCE": return [o(e), null]; case"EXT-X-CUE-OUT": return Number.isNaN(Number(e)) ? [null, V(e)] : [o(e), null]; case"EXT-X-KEY": case"EXT-X-MAP": case"EXT-X-DATERANGE": case"EXT-X-MEDIA": case"EXT-X-STREAM-INF": case"EXT-X-I-FRAME-STREAM-INF": case"EXT-X-SESSION-DATA": case"EXT-X-SESSION-KEY": case"EXT-X-START": case"EXT-X-SERVER-CONTROL": case"EXT-X-PART-INF": case"EXT-X-PART": case"EXT-X-PRELOAD-HINT": case"EXT-X-RENDITION-REPORT": case"EXT-X-SKIP": return [null, V(e)]; case"EXTINF": return [L(e), null]; case"EXT-X-BYTERANGE": return [v(e), null]; case"EXT-X-PROGRAM-DATE-TIME": return [new Date(e), null]; default: return [e, null] } }(s, i); return {name: s, category: n, value: a, attributes: E} } function Q(t, e) { let s; return e.isMasterPlaylist ? s = function (t, e) { const s = new O; let i = !1; for (const [n, { name: a, value: o, attributes: E }] of t.entries()) if ("EXT-X-VERSION" === a) s.version = o; else if ("EXT-X-STREAM-INF" === a) { const a = t[n + 1]; ("string" != typeof a || a.startsWith("#EXT")) && r("EXT-X-STREAM-INF must be followed by a URI line"); const o = H(t, E, a, !1, e); o && ("number" == typeof o.score && (i = !0, o.score < 0 && r("SCORE attribute on EXT-X-STREAM-INF must be positive decimal-floating-point number.")), s.variants.push(o)) } else if ("EXT-X-I-FRAME-STREAM-INF" === a) { const i = H(t, E, E.URI, !0, e); i && s.variants.push(i) } else if ("EXT-X-SESSION-DATA" === a) { const t = new d({id: E["DATA-ID"], value: E.VALUE, uri: E.URI, language: E.LANGUAGE}); s.sessionDataList.some((e => e.id === t.id && e.language === t.language)) && r("A Playlist MUST NOT contain more than one EXT-X-SESSION-DATA tag with the same DATA-ID attribute and the same LANGUAGE attribute."), s.sessionDataList.push(t) } else if ("EXT-X-SESSION-KEY" === a) { "NONE" === E.METHOD && r("EXT-X-SESSION-KEY: The value of the METHOD attribute MUST NOT be NONE"); const t = new A({ method: E.METHOD, uri: E.URI, iv: E.IV, format: E.KEYFORMAT, formatVersion: E.KEYFORMATVERSIONS }); s.sessionKeyList.some((e => K(e, t))) && r("A Master Playlist MUST NOT contain more than one EXT-X-SESSION-KEY tag with the same METHOD, URI, IV, KEYFORMAT, and KEYFORMATVERSIONS attribute values."), G(e, E), s.sessionKeyList.push(t) } else "EXT-X-INDEPENDENT-SEGMENTS" === a ? (s.independentSegments && r("EXT-X-INDEPENDENT-SEGMENTS tag MUST NOT appear more than once in a Playlist"), s.independentSegments = !0) : "EXT-X-START" === a && (s.start && r("EXT-X-START tag MUST NOT appear more than once in a Playlist"), "number" != typeof E["TIME-OFFSET"] && r("EXT-X-START: TIME-OFFSET attribute is REQUIRED"), s.start = { offset: E["TIME-OFFSET"], precise: E.PRECISE || !1 }); if (i) for (const t of s.variants) "number" != typeof t.score && r("If any Variant Stream contains the SCORE attribute, then all Variant Streams in the Master Playlist SHOULD have a SCORE attribute"); if (e.isClosedCaptionsNone) for (const t of s.variants) t.closedCaptions.length > 0 && r("If there is a variant with CLOSED-CAPTIONS attribute of NONE, all EXT-X-STREAM-INF tags MUST have this attribute with a value of NONE"); return s }(t, e) : (s = q(t, e), !s.isIFrame && e.hasMap && e.compatibleVersion < 6 && (e.compatibleVersion = 6)), e.compatibleVersion > 1 && (!s.version || s.version < e.compatibleVersion) && r(`EXT-X-VERSION needs to be ${e.compatibleVersion} or higher.`), s } function _(t) { const e = { version: void 0, isMasterPlaylist: void 0, hasMap: !1, targetDuration: 0, compatibleVersion: 1, isClosedCaptionsNone: !1, hash: {} }, s = function (t, e) { const s = []; for (const i of t.split("\n")) { const t = i.trim(); if (t) if (t.startsWith("#")) { if (t.startsWith("#EXT")) { const i = j(t, e); i && s.push(i) } } else s.push(t) } return 0 !== s.length && "EXTM3U" === s[0].name || r("The EXTM3U tag MUST be the first line."), s }(t, e), i = Q(s, e); return i.source = t, i } const z = ["#EXTINF", "#EXT-X-BYTERANGE", "#EXT-X-DISCONTINUITY", "#EXT-X-STREAM-INF", "#EXT-X-CUE-OUT", "#EXT-X-CUE-IN", "#EXT-X-KEY", "#EXT-X-MAP"], Z = ["#EXT-X-MEDIA"]; class J extends Array { constructor(t) { super(), this.baseUri = t } push(...t) { for (const e of t) if (e.startsWith("#")) if (z.some((t => e.startsWith(t)))) super.push(e); else { if (this.includes(e)) { if (Z.some((t => e.startsWith(t)))) continue; r(`Redundant item (${e})`) } super.push(e) } else super.push(e); return this.length } } function tt(t, e) { let s = 1e3; e && (s = Math.pow(10, e)); const i = Math.round(t * s) / s; return e ? i.toFixed(e) : i } function et(t) { const e = [`DATA-ID="${t.id}"`]; return t.language && e.push(`LANGUAGE="${t.language}"`), t.value ? e.push(`VALUE="${t.value}"`) : t.uri && e.push(`URI="${t.uri}"`), `#EXT-X-SESSION-DATA:${e.join(",")}` } function st(t, e) { const s = e ? "#EXT-X-SESSION-KEY" : "#EXT-X-KEY", i = [`METHOD=${t.method}`]; return t.uri && i.push(`URI="${t.uri}"`), t.iv && (16 !== t.iv.length && r("IV must be a 128-bit unsigned integer"), i.push(`IV=${T(t.iv)}`)), t.format && i.push(`KEYFORMAT="${t.format}"`), t.formatVersion && i.push(`KEYFORMATVERSIONS="${t.formatVersion}"`), `${s}:${i.join(",")}` } function it(t, e) { const s = e.isIFrameOnly ? "#EXT-X-I-FRAME-STREAM-INF" : "#EXT-X-STREAM-INF", i = [`BANDWIDTH=${e.bandwidth}`]; if (e.averageBandwidth && i.push(`AVERAGE-BANDWIDTH=${e.averageBandwidth}`), e.isIFrameOnly && i.push(`URI="${e.uri}"`), e.codecs && i.push(`CODECS="${e.codecs}"`), e.resolution && i.push(`RESOLUTION=${e.resolution.width}x${e.resolution.height}`), e.frameRate && i.push(`FRAME-RATE=${tt(e.frameRate, 3)}`), e.hdcpLevel && i.push(`HDCP-LEVEL=${e.hdcpLevel}`), e.audio.length > 0) { i.push(`AUDIO="${e.audio[0].groupId}"`); for (const s of e.audio) t.push(nt(s)) } if (e.video.length > 0) { i.push(`VIDEO="${e.video[0].groupId}"`); for (const s of e.video) t.push(nt(s)) } if (e.subtitles.length > 0) { i.push(`SUBTITLES="${e.subtitles[0].groupId}"`); for (const s of e.subtitles) t.push(nt(s)) } if (X().allowClosedCaptionsNone && 0 === e.closedCaptions.length) i.push("CLOSED-CAPTIONS=NONE"); else if (e.closedCaptions.length > 0) { i.push(`CLOSED-CAPTIONS="${e.closedCaptions[0].groupId}"`); for (const s of e.closedCaptions) t.push(nt(s)) } if (e.score && i.push(`SCORE=${e.score}`), e.allowedCpc) { const t = []; for (const {format: s, cpcList: i} of e.allowedCpc) t.push(`${s}:${i.join("/")}`); i.push(`ALLOWED-CPC="${t.join(",")}"`) } e.videoRange && i.push(`VIDEO-RANGE=${e.videoRange}`), e.stableVariantId && i.push(`STABLE-VARIANT-ID="${e.stableVariantId}"`), e.programId && i.push(`PROGRAM-ID=${e.programId}`), t.push(`${s}:${i.join(",")}`), e.isIFrameOnly || t.push(`${e.uri}`) } function nt(t) { const e = [`TYPE=${t.type}`, `GROUP-ID="${t.groupId}"`, `NAME="${t.name}"`]; return void 0 !== t.isDefault && e.push("DEFAULT=" + (t.isDefault ? "YES" : "NO")), void 0 !== t.autoselect && e.push("AUTOSELECT=" + (t.autoselect ? "YES" : "NO")), void 0 !== t.forced && e.push("FORCED=" + (t.forced ? "YES" : "NO")), t.language && e.push(`LANGUAGE="${t.language}"`), t.assocLanguage && e.push(`ASSOC-LANGUAGE="${t.assocLanguage}"`), t.instreamId && e.push(`INSTREAM-ID="${t.instreamId}"`), t.characteristics && e.push(`CHARACTERISTICS="${t.characteristics}"`), t.channels && e.push(`CHANNELS="${t.channels}"`), t.uri && e.push(`URI="${t.uri}"`), `#EXT-X-MEDIA:${e.join(",")}` } function at(t, e, s, i, n = 1, a = null) { let r = !1, o = ""; if (e.discontinuity && t.push("#EXT-X-DISCONTINUITY"), e.key) { const i = st(e.key); i !== s && (t.push(i), s = i) } if (e.map) { const s = function (t) { const e = [`URI="${t.uri}"`]; t.byterange && e.push(`BYTERANGE="${rt(t.byterange)}"`); return `#EXT-X-MAP:${e.join(",")}` }(e.map); s !== i && (t.push(s), i = s) } if (e.programDateTime && t.push(`#EXT-X-PROGRAM-DATE-TIME:${l(e.programDateTime)}`), e.dateRange && t.push(function (t) { const e = [`ID="${t.id}"`]; t.start && e.push(`START-DATE="${l(t.start)}"`); t.end && e.push(`END-DATE="${l(t.end)}"`); t.duration && e.push(`DURATION=${t.duration}`); t.plannedDuration && e.push(`PLANNED-DURATION=${t.plannedDuration}`); t.classId && e.push(`CLASS="${t.classId}"`); t.endOnNext && e.push("END-ON-NEXT=YES"); for (const s of Object.keys(t.attributes)) s.startsWith("X-") ? "number" == typeof t.attributes[s] ? e.push(`${s}=${t.attributes[s]}`) : e.push(`${s}="${t.attributes[s]}"`) : s.startsWith("SCTE35-") && e.push(`${s}=${T(t.attributes[s])}`); return `#EXT-X-DATERANGE:${e.join(",")}` }(e.dateRange)), e.markers.length > 0 && (o = function (t, e) { let s = ""; for (const i of e) if ("OUT" === i.type) s = "OUT", t.push(`#EXT-X-CUE-OUT:DURATION=${i.duration}`); else if ("IN" === i.type) s = "IN", t.push("#EXT-X-CUE-IN"); else if ("RAW" === i.type) { const e = i.value ? `:${i.value}` : ""; t.push(`#${i.tagName}${e}`) } return s }(t, e.markers)), e.parts.length > 0 && (r = function (t, e) { let s = !1; for (const i of e) if (i.hint) { const e = []; if (e.push("TYPE=PART", `URI="${i.uri}"`), i.byterange) { const {offset: t, length: s} = i.byterange; e.push(`BYTERANGE-START=${t}`), s && e.push(`BYTERANGE-LENGTH=${s}`) } t.push(`#EXT-X-PRELOAD-HINT:${e.join(",")}`), s = !0 } else { const e = []; e.push(`DURATION=${i.duration}`, `URI="${i.uri}"`), i.byterange && e.push(`BYTERANGE=${rt(i.byterange)}`), i.independent && e.push("INDEPENDENT=YES"), i.gap && e.push("GAP=YES"), t.push(`#EXT-X-PART:${e.join(",")}`) } return s }(t, e.parts)), r) return [s, i]; const E = n < 3 ? Math.round(e.duration) : tt(e.duration, function (t) { const e = t.toString(10), s = e.indexOf("."); return -1 === s ? 0 : e.length - s - 1 }(e.duration)); return t.push(`#EXTINF:${E},${unescape(encodeURIComponent(e.title || ""))}`), e.byterange && t.push(`#EXT-X-BYTERANGE:${rt(e.byterange)}`), null != a ? Array.prototype.push.call(t, a(e)) : Array.prototype.push.call(t, `${e.uri}`), [s, i, o] } function rt({offset: t, length: e}) { return `${e}@${t}` } function ot(t, e = null) { n(t), s("Not a playlist", "playlist" === t.type); const i = new J(t.uri); return i.push("#EXTM3U"), t.version && i.push(`#EXT-X-VERSION:${t.version}`), t.independentSegments && i.push("#EXT-X-INDEPENDENT-SEGMENTS"), t.start && i.push(`#EXT-X-START:TIME-OFFSET=${tt(t.start.offset)}${t.start.precise ? ",PRECISE=YES" : ""}`), t.isMasterPlaylist ? function (t, e) { for (const s of e.sessionDataList) t.push(et(s)); for (const s of e.sessionKeyList) t.push(st(s, !0)); for (const s of e.variants) it(t, s) }(i, t) : function (t, e, s = null) { let i = "", n = "", a = !1; if (e.targetDuration && t.push(`#EXT-X-TARGETDURATION:${e.targetDuration}`), e.lowLatencyCompatibility) { const {canBlockReload: s, canSkipUntil: i, holdBack: n, partHoldBack: a} = e.lowLatencyCompatibility, r = []; r.push("CAN-BLOCK-RELOAD=" + (s ? "YES" : "NO")), void 0 !== i && r.push(`CAN-SKIP-UNTIL=${i}`), void 0 !== n && r.push(`HOLD-BACK=${n}`), void 0 !== a && r.push(`PART-HOLD-BACK=${a}`), t.push(`#EXT-X-SERVER-CONTROL:${r.join(",")}`) } e.partTargetDuration && t.push(`#EXT-X-PART-INF:PART-TARGET=${e.partTargetDuration}`), e.mediaSequenceBase && t.push(`#EXT-X-MEDIA-SEQUENCE:${e.mediaSequenceBase}`), e.discontinuitySequenceBase && t.push(`#EXT-X-DISCONTINUITY-SEQUENCE:${e.discontinuitySequenceBase}`), e.playlistType && t.push(`#EXT-X-PLAYLIST-TYPE:${e.playlistType}`), e.isIFrame && t.push("#EXT-X-I-FRAMES-ONLY"), e.skip > 0 && t.push(`#EXT-X-SKIP:SKIPPED-SEGMENTS=${e.skip}`); for (const r of e.segments) { let o = ""; [i, n, o] = at(t, r, i, n, e.version, s), "OUT" === o ? a = !0 : "IN" === o && a && (a = !1) } "VOD" === e.playlistType && a && t.push("#EXT-X-CUE-IN"), e.prefetchSegments.length > 2 && r("The server must deliver no more than two prefetch segments"); for (const s of e.prefetchSegments) s.discontinuity && t.push("#EXT-X-PREFETCH-DISCONTINUITY"), t.push(`#EXT-X-PREFETCH:${s.uri}`); e.endlist && t.push("#EXT-X-ENDLIST"); for (const s of e.renditionReports) { const e = []; e.push(`URI="${s.uri}"`, `LAST-MSN=${s.lastMSN}`), void 0 !== s.lastPart && e.push(`LAST-PART=${s.lastPart}`), t.push(`#EXT-X-RENDITION-REPORT:${e.join(",")}`) } }(i, t, e), i.join("\n") } export {X as getOptions, _ as parse, h as setOptions, ot as stringify, M as types};