From e01f3960cbb68508337e326f39a9aa12c72b811c Mon Sep 17 00:00:00 2001 From: Mitarashi Date: Sat, 5 Feb 2022 19:43:13 +0200 Subject: [PATCH 001/409] Embed CSS tweak --- src/sass/tweet/_base.scss | 8 ++++++-- src/views/general.nim | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/sass/tweet/_base.scss b/src/sass/tweet/_base.scss index 7c1196a..a26a7b3 100644 --- a/src/sass/tweet/_base.scss +++ b/src/sass/tweet/_base.scss @@ -121,14 +121,18 @@ background-color: var(--bg_panel); .tweet-content { - font-size: 18px + font-size: 18px; } - .tweet-body { + .tweet-body { display: flex; flex-direction: column; max-height: calc(100vh - 0.75em * 2); } + + .card-image img { + height: auto; + } } .attribution { diff --git a/src/views/general.nim b/src/views/general.nim index 82902d4..34e80e9 100644 --- a/src/views/general.nim +++ b/src/views/general.nim @@ -52,7 +52,7 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc=""; let opensearchUrl = getUrlPrefix(cfg) & "/opensearch" buildHtml(head): - link(rel="stylesheet", type="text/css", href="/css/style.css?v=16") + link(rel="stylesheet", type="text/css", href="/css/style.css?v=18") link(rel="stylesheet", type="text/css", href="/css/fontello.css?v=2") if theme.len > 0: From adfd31c530cdfae052333123cd5999b333043bd3 Mon Sep 17 00:00:00 2001 From: Zed Date: Mon, 7 Feb 2022 21:58:06 +0100 Subject: [PATCH 002/409] Fix timeline parser --- src/parser.nim | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/parser.nim b/src/parser.nim index ae5e505..d3d62cf 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -375,7 +375,12 @@ proc parseTimeline*(js: JsonNode; after=""): Timeline = result.parseInstructions(global, instructions) - for e in instructions[0]{"addEntries", "entries"}: + var entries: JsonNode + for i in instructions: + if "addEntries" in i: + entries = i{"addEntries", "entries"} + + for e in ? entries: let entry = e{"entryId"}.getStr if "tweet" in entry or entry.startsWith("sq-I-t") or "tombstone" in entry: let tweet = finalizeTweet(global, e.getEntryId) From 4cdb8f78cbb1035e88174ca432e5e7dfedb4eb44 Mon Sep 17 00:00:00 2001 From: decoy-walrus Date: Mon, 7 Feb 2022 14:01:05 -0500 Subject: [PATCH 003/409] Add new endpoint for original resolution images This change is to work around the issue that chromium based browsers have handling the "name=orig" parameter appended to URLs. This parameter is needed to retrieve the full resolution image from twitter, but causes those browsers to fill in "jpg_name=orig" as the extension on the filename. This change adds a new endpoint, "/pic/orig/". This new endpoint will internally fetch the URL with ":orig" appended on the end for the full res image. Externally, the endpoint will serve the image without the extra parameter to expose the real extension to the browser. This new endpoint is used when rendering tweets with attached images. The old endpoint is still in place for all other proxied images, and for any legacy links. I also updated the "?name=small" parameter to ":small" since that seems to be the new pattern for image sizing. This should fix issue #458. --- src/routes/media.nim | 14 ++++++++++++++ src/utils.nim | 6 ++++++ src/views/tweet.nim | 6 +++--- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/routes/media.nim b/src/routes/media.nim index c953a93..95446a1 100644 --- a/src/routes/media.nim +++ b/src/routes/media.nim @@ -88,6 +88,20 @@ proc createMediaRouter*(cfg: Config) = get "/pic/?": resp Http404 + get re"^\/pic\/orig\/(enc)?\/?(.+)": + var url = decoded(request, 1) + if "twimg.com" notin url: + url.insert(twimg) + if not url.startsWith(https): + url.insert(https) + url.add(":orig") + + let uri = parseUri(url) + cond isTwitterUrl(uri) == true + + let code = await proxyMedia(request, url) + check code + get re"^\/pic\/(enc)?\/?(.+)": var url = decoded(request, 1) if "twimg.com" notin url: diff --git a/src/utils.nim b/src/utils.nim index 9c8414d..9002bbf 100644 --- a/src/utils.nim +++ b/src/utils.nim @@ -42,6 +42,12 @@ proc getPicUrl*(link: string): string = else: &"/pic/{encodeUrl(link)}" +proc getOrigPicUrl*(link: string): string = + if base64Media: + &"/pic/orig/enc/{encode(link, safe=true)}" + else: + &"/pic/orig/{encodeUrl(link)}" + proc filterParams*(params: Table): seq[(string, string)] = for p in params.pairs(): if p[1].len > 0 and p[0] notin nitterParams: diff --git a/src/views/tweet.nim b/src/views/tweet.nim index 8b712a6..e64f959 100644 --- a/src/views/tweet.nim +++ b/src/views/tweet.nim @@ -57,9 +57,9 @@ proc renderAlbum(tweet: Tweet): VNode = tdiv(class="attachment image"): let named = "name=" in photo - orig = if named: photo else: photo & "?name=orig" - small = if named: photo else: photo & "?name=small" - a(href=getPicUrl(orig), class="still-image", target="_blank"): + orig = photo + small = if named: photo else: photo & ":small" + a(href=getOrigPicUrl(orig), class="still-image", target="_blank"): genImg(small) proc isPlaybackEnabled(prefs: Prefs; video: Video): bool = From 644fe41a08e41313894a017b934e5d89889202ac Mon Sep 17 00:00:00 2001 From: decoy-walrus Date: Tue, 8 Feb 2022 14:50:56 -0500 Subject: [PATCH 004/409] Use the correct format string for fetching files from twitter. Per their docs https://developer.twitter.com/en/docs/twitter-api/v1/data-dictionary/object-model/entities#photo_format --- src/routes/media.nim | 2 +- src/views/tweet.nim | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/routes/media.nim b/src/routes/media.nim index 95446a1..e63a0f8 100644 --- a/src/routes/media.nim +++ b/src/routes/media.nim @@ -94,7 +94,7 @@ proc createMediaRouter*(cfg: Config) = url.insert(twimg) if not url.startsWith(https): url.insert(https) - url.add(":orig") + url.add("?name=orig") let uri = parseUri(url) cond isTwitterUrl(uri) == true diff --git a/src/views/tweet.nim b/src/views/tweet.nim index e64f959..51d427f 100644 --- a/src/views/tweet.nim +++ b/src/views/tweet.nim @@ -10,7 +10,7 @@ import general proc getSmallPic(url: string): string = result = url if "?" notin url and not url.endsWith("placeholder.png"): - result &= ":small" + result &= "?name=small" result = getPicUrl(result) proc renderMiniAvatar(user: User; prefs: Prefs): VNode = @@ -58,7 +58,7 @@ proc renderAlbum(tweet: Tweet): VNode = let named = "name=" in photo orig = photo - small = if named: photo else: photo & ":small" + small = if named: photo else: photo & "?name=small" a(href=getOrigPicUrl(orig), class="still-image", target="_blank"): genImg(small) From 6e4620070e804cd51d902689a8390eb24c2705ea Mon Sep 17 00:00:00 2001 From: Mitarashi Date: Thu, 10 Feb 2022 13:13:02 +0200 Subject: [PATCH 005/409] Bumped karax version --- nitter.nimble | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nitter.nimble b/nitter.nimble index ea2436c..78c1254 100644 --- a/nitter.nimble +++ b/nitter.nimble @@ -12,7 +12,7 @@ bin = @["nitter"] requires "nim >= 1.4.8" requires "jester >= 0.5.0" -requires "karax#c71bc92" +requires "karax#fa4a2dc" requires "sass#e683aa1" requires "nimcrypto#a5742a9" requires "markdown#abdbe5e" From 3e2756df6f64e3778d73d152282a5db05ff2e152 Mon Sep 17 00:00:00 2001 From: niladic Date: Fri, 11 Feb 2022 08:50:32 +0100 Subject: [PATCH 006/409] Fix RSS tag --- src/views/rss.nimf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/rss.nimf b/src/views/rss.nimf index cf69be1..f910f92 100644 --- a/src/views/rss.nimf +++ b/src/views/rss.nimf @@ -47,7 +47,7 @@ Twitter feed for: ${desc}. Generated by ${cfg.hostname} # let thumb = &"{urlPrefix}{getPicUrl(get(tweet.gif).thumb)}" # let url = &"{urlPrefix}{getPicUrl(get(tweet.gif).url)}" + #elif tweet.card.isSome: # let card = tweet.card.get() # if card.image.len > 0: From 6695784050605c77a301c0a66764fa9a9580a2f5 Mon Sep 17 00:00:00 2001 From: Zed Date: Fri, 11 Feb 2022 22:41:40 +0100 Subject: [PATCH 007/409] Fix user search parser --- src/experimental/parser/timeline.nim | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/experimental/parser/timeline.nim b/src/experimental/parser/timeline.nim index 351ca85..4663d00 100644 --- a/src/experimental/parser/timeline.nim +++ b/src/experimental/parser/timeline.nim @@ -15,14 +15,16 @@ proc parseUsers*(json: string; after=""): Result[User] = if raw.timeline.instructions.len == 0: return - for e in raw.timeline.instructions[0].addEntries.entries: - let id = e.entryId.getId - if e.entryId.startsWith("user"): - if id in raw.globalObjects.users: - result.content.add toUser raw.globalObjects.users[id] - elif e.entryId.startsWith("cursor"): - let cursor = e.content.operation.cursor - if cursor.cursorType == "Top": - result.top = cursor.value - elif cursor.cursorType == "Bottom": - result.bottom = cursor.value + for i in raw.timeline.instructions: + if i.addEntries.entries.len > 0: + for e in i.addEntries.entries: + let id = e.entryId.getId + if e.entryId.startsWith("user"): + if id in raw.globalObjects.users: + result.content.add toUser raw.globalObjects.users[id] + elif e.entryId.startsWith("cursor"): + let cursor = e.content.operation.cursor + if cursor.cursorType == "Top": + result.top = cursor.value + elif cursor.cursorType == "Bottom": + result.bottom = cursor.value From 7ca3591920b1cf1619f6c89e1e3c378cffd317d2 Mon Sep 17 00:00:00 2001 From: jackyzy823 Date: Mon, 14 Feb 2022 09:20:32 +0100 Subject: [PATCH 008/409] fix non-ascii hashtag regex --- src/experimental/parser/user.nim | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/experimental/parser/user.nim b/src/experimental/parser/user.nim index dc760f0..bf8c5ba 100644 --- a/src/experimental/parser/user.nim +++ b/src/experimental/parser/user.nim @@ -1,14 +1,14 @@ -import std/[algorithm, unicode, re, strutils, strformat, options] +import std/[algorithm, unicode, re, strutils, strformat, options, nre] import jsony import utils, slices import ../types/user as userType from ../../types import User, Error let - unRegex = re"(^|[^A-z0-9-_./?])@([A-z0-9_]{1,15})" + unRegex = re.re"(^|[^A-z0-9-_./?])@([A-z0-9_]{1,15})" unReplace = "$1@$2" - htRegex = re"(^|[^\w-_./?])([##$])([\w_]+)" + htRegex = nre.re"(*U)(^|[^\w-_./?])([##$])([\w_]+)" htReplace = "$1$2$3" proc expandUserEntities(user: var User; raw: RawUser) = @@ -29,7 +29,7 @@ proc expandUserEntities(user: var User; raw: RawUser) = user.bio = orig.replacedWith(replacements, 0 .. orig.len) .replacef(unRegex, unReplace) - .replacef(htRegex, htReplace) + .replace(htRegex, htReplace) proc getBanner(user: RawUser): string = if user.profileBannerUrl.len > 0: From 67c937a3a238b491ea79beb8b7f39fb5048bf9ff Mon Sep 17 00:00:00 2001 From: jackyzy823 Date: Tue, 8 Feb 2022 07:29:15 +0100 Subject: [PATCH 009/409] ignore banner url startwith hash in link preload --- src/views/general.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/general.nim b/src/views/general.nim index 34e80e9..b18dae5 100644 --- a/src/views/general.nim +++ b/src/views/general.nim @@ -93,7 +93,7 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc=""; meta(property="og:site_name", content="Nitter") meta(property="og:locale", content="en_US") - if banner.len > 0: + if banner.len > 0 and not banner.startsWith('#'): let bannerUrl = getPicUrl(banner) link(rel="preload", type="image/png", href=bannerUrl, `as`="image") From 2c97cb0763559e34466d3a42c09459ae48b332ac Mon Sep 17 00:00:00 2001 From: yoshiyoshyosh <36683489+yoshiyoshyosh@users.noreply.github.com> Date: Sat, 19 Feb 2022 16:37:12 -0600 Subject: [PATCH 010/409] Update _base.scss --- src/sass/tweet/_base.scss | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sass/tweet/_base.scss b/src/sass/tweet/_base.scss index a26a7b3..b2a5517 100644 --- a/src/sass/tweet/_base.scss +++ b/src/sass/tweet/_base.scss @@ -98,8 +98,6 @@ } .avatar { - position: absolute; - &.round { border-radius: 50%; } From 139b7edebce21239eea09fab8f7774e606c0c07c Mon Sep 17 00:00:00 2001 From: yoshiyoshyosh <36683489+yoshiyoshyosh@users.noreply.github.com> Date: Sat, 19 Feb 2022 19:12:26 -0600 Subject: [PATCH 011/409] fix embed css --- src/sass/tweet/_base.scss | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/sass/tweet/_base.scss b/src/sass/tweet/_base.scss index b2a5517..b056fe3 100644 --- a/src/sass/tweet/_base.scss +++ b/src/sass/tweet/_base.scss @@ -121,12 +121,6 @@ .tweet-content { font-size: 18px; } - - .tweet-body { - display: flex; - flex-direction: column; - max-height: calc(100vh - 0.75em * 2); - } .card-image img { height: auto; From 3590ded482a4e60c4ab76ba22fab83246a4bc0fd Mon Sep 17 00:00:00 2001 From: yoshiyoshyosh <36683489+yoshiyoshyosh@users.noreply.github.com> Date: Sat, 19 Feb 2022 20:01:11 -0600 Subject: [PATCH 012/409] revert previous "fix", move absolute under embed --- src/sass/tweet/_base.scss | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/sass/tweet/_base.scss b/src/sass/tweet/_base.scss index b056fe3..b5410f5 100644 --- a/src/sass/tweet/_base.scss +++ b/src/sass/tweet/_base.scss @@ -121,12 +121,23 @@ .tweet-content { font-size: 18px; } + + .tweet-body { + display: flex; + flex-direction: column; + max-height: calc(100vh - 0.75em * 2); + } .card-image img { height: auto; } + + .avatar { + position: absolute; + } } + .attribution { display: flex; pointer-events: all; From c7a78a7f434aeb5ce625a3c9e207a6217c741e09 Mon Sep 17 00:00:00 2001 From: jackyzy823 Date: Mon, 14 Feb 2022 09:38:46 +0100 Subject: [PATCH 013/409] fix slashes between hastags --- src/experimental/parser/user.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/experimental/parser/user.nim b/src/experimental/parser/user.nim index bf8c5ba..715c9a9 100644 --- a/src/experimental/parser/user.nim +++ b/src/experimental/parser/user.nim @@ -8,7 +8,7 @@ let unRegex = re.re"(^|[^A-z0-9-_./?])@([A-z0-9_]{1,15})" unReplace = "$1@$2" - htRegex = nre.re"(*U)(^|[^\w-_./?])([##$])([\w_]+)" + htRegex = nre.re"""(*U)(^|[^\w-_.?])([##$])([\w_]*+)(?!|">|#)""" htReplace = "$1$2$3" proc expandUserEntities(user: var User; raw: RawUser) = From b031ea762ea32c712ea85b7524f73bde76564a5f Mon Sep 17 00:00:00 2001 From: 110Percent <110percent@riseup.net> Date: Sat, 26 Feb 2022 13:37:17 -0500 Subject: [PATCH 014/409] Add source label to main tweets --- src/parser.nim | 3 +++ src/types.nim | 1 + src/views/tweet.nim | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/parser.nim b/src/parser.nim index d3d62cf..1f16d12 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -3,6 +3,7 @@ import strutils, options, tables, times, math import packedjson, packedjson/deserialiser import types, parserutils, utils import experimental/parser/unifiedcard +import std/strutils proc parseUser(js: JsonNode; id=""): User = if js.isNull: return @@ -183,12 +184,14 @@ proc parseCard(js: JsonNode; urls: JsonNode): Card = proc parseTweet(js: JsonNode): Tweet = if js.isNull: return + result = Tweet( id: js{"id_str"}.getId, threadId: js{"conversation_id_str"}.getId, replyId: js{"in_reply_to_status_id_str"}.getId, text: js{"full_text"}.getStr, time: js{"created_at"}.getTime, + source: js{"source"}.getStr.split(">")[1].split("<")[0], hasThread: js{"self_thread"}.notNull, available: true, user: User(id: js{"user_id_str"}.getStr), diff --git a/src/types.nim b/src/types.nim index 9a6ad7f..f28e90a 100644 --- a/src/types.nim +++ b/src/types.nim @@ -175,6 +175,7 @@ type tombstone*: string location*: string stats*: TweetStats + source*: string retweet*: Option[Tweet] attribution*: Option[User] mediaTags*: seq[User] diff --git a/src/views/tweet.nim b/src/views/tweet.nim index 8b712a6..b6f8d7e 100644 --- a/src/views/tweet.nim +++ b/src/views/tweet.nim @@ -344,7 +344,7 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class=""; index=0; renderQuote(tweet.quote.get(), prefs, path) if mainTweet: - p(class="tweet-published"): text getTime(tweet) + p(class="tweet-published"): text getTime(tweet) & " · " & tweet.source if tweet.mediaTags.len > 0: renderMediaTags(tweet.mediaTags) From 8f65bd583d059fb353825482cd4e970828132a78 Mon Sep 17 00:00:00 2001 From: 110Percent <110percent@riseup.net> Date: Sat, 26 Feb 2022 13:40:38 -0500 Subject: [PATCH 015/409] Remove accidental newline --- src/parser.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/src/parser.nim b/src/parser.nim index 1f16d12..778187b 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -184,7 +184,6 @@ proc parseCard(js: JsonNode; urls: JsonNode): Card = proc parseTweet(js: JsonNode): Tweet = if js.isNull: return - result = Tweet( id: js{"id_str"}.getId, threadId: js{"conversation_id_str"}.getId, From baf5bf89e79fd92fb68fd0d59c0e9facdad21525 Mon Sep 17 00:00:00 2001 From: Curtis <110percent@riseup.net> Date: Sat, 26 Feb 2022 19:00:06 -0500 Subject: [PATCH 016/409] Implement requested changes --- src/parser.nim | 1 - src/parserutils.nim | 6 ++++++ src/types.nim | 2 +- src/views/tweet.nim | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/parser.nim b/src/parser.nim index 778187b..aa55f60 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -3,7 +3,6 @@ import strutils, options, tables, times, math import packedjson, packedjson/deserialiser import types, parserutils, utils import experimental/parser/unifiedcard -import std/strutils proc parseUser(js: JsonNode; id=""): User = if js.isNull: return diff --git a/src/parserutils.nim b/src/parserutils.nim index a605ea4..e0b2669 100644 --- a/src/parserutils.nim +++ b/src/parserutils.nim @@ -133,6 +133,12 @@ proc getTombstone*(js: JsonNode): string = result = js{"tombstoneInfo", "richText", "text"}.getStr result.removeSuffix(" Learn more") +proc getSource*(js: JsonNode): string = + let + raw_src = js["source"].getStr + src = raw_src.substr(raw_src.find('>') + 1, raw_src.rfind('<') - 1) + return src + proc extractSlice(js: JsonNode): Slice[int] = result = js["indices"][0].getInt ..< js["indices"][1].getInt diff --git a/src/types.nim b/src/types.nim index f28e90a..98433aa 100644 --- a/src/types.nim +++ b/src/types.nim @@ -174,8 +174,8 @@ type available*: bool tombstone*: string location*: string - stats*: TweetStats source*: string + stats*: TweetStats retweet*: Option[Tweet] attribution*: Option[User] mediaTags*: seq[User] diff --git a/src/views/tweet.nim b/src/views/tweet.nim index b6f8d7e..6e6f3af 100644 --- a/src/views/tweet.nim +++ b/src/views/tweet.nim @@ -344,7 +344,7 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class=""; index=0; renderQuote(tweet.quote.get(), prefs, path) if mainTweet: - p(class="tweet-published"): text getTime(tweet) & " · " & tweet.source + p(class="tweet-published"): text &"{getTime(tweet)} · {tweet.source}" if tweet.mediaTags.len > 0: renderMediaTags(tweet.mediaTags) From ce5cbaeb8758fa6afbc89e436a96d0ed8c11b609 Mon Sep 17 00:00:00 2001 From: Curtis <110percent@riseup.net> Date: Sat, 26 Feb 2022 19:02:34 -0500 Subject: [PATCH 017/409] Implement getSource proc --- src/parser.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser.nim b/src/parser.nim index aa55f60..1a7c073 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -189,7 +189,7 @@ proc parseTweet(js: JsonNode): Tweet = replyId: js{"in_reply_to_status_id_str"}.getId, text: js{"full_text"}.getStr, time: js{"created_at"}.getTime, - source: js{"source"}.getStr.split(">")[1].split("<")[0], + source: getSource(js), hasThread: js{"self_thread"}.notNull, available: true, user: User(id: js{"user_id_str"}.getStr), From 36a24cf553e6aae27604700187a65d38a0432055 Mon Sep 17 00:00:00 2001 From: Dennis ten Hoove Date: Fri, 4 Mar 2022 16:42:27 +0100 Subject: [PATCH 018/409] Implement healthcheck on nitter and redis --- docker-compose.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index a98855f..50ae752 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,6 +12,11 @@ services: depends_on: - nitter-redis restart: unless-stopped + healthcheck: + test: wget -nv --tries=1 --spider http://127.0.0.1:8080/Jack/status/20 || exit 1 + interval: 30s + timeout: 5s + retries: 2 nitter-redis: image: redis:6-alpine @@ -20,6 +25,11 @@ services: volumes: - nitter-redis:/data restart: unless-stopped + healthcheck: + test: redis-cli ping + interval: 30s + timeout: 5s + retries: 2 volumes: nitter-redis: From 9b97ddc8d8b43f498b5d79e39a170955f3926fe1 Mon Sep 17 00:00:00 2001 From: jackyzy823 Date: Thu, 10 Mar 2022 21:24:57 +0800 Subject: [PATCH 019/409] Fix list members not displaying --- src/api.nim | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/api.nim b/src/api.nim index 708b72f..392795c 100644 --- a/src/api.nim +++ b/src/api.nim @@ -25,10 +25,9 @@ proc getGraphList*(id: string): Future[List] {.async.} = proc getGraphListMembers*(list: List; after=""): Future[Result[User]] {.async.} = if list.id.len == 0: return - let + var variables = %*{ "listId": list.id, - "cursor": after, "withSuperFollowsUserFields": false, "withBirdwatchPivots": false, "withDownvotePerspective": false, @@ -36,7 +35,9 @@ proc getGraphListMembers*(list: List; after=""): Future[Result[User]] {.async.} "withReactionsPerspective": false, "withSuperFollowsTweetFields": false } - url = graphListMembers ? {"variables": $variables} + if after.len > 0: + variables["cursor"] = % after + let url = graphListMembers ? {"variables": $variables} result = parseGraphListMembers(await fetchRaw(url, Api.listMembers), after) proc getListTimeline*(id: string; after=""): Future[Timeline] {.async.} = From 731f814d3706890da3d6f9349a2c31ae45291b3a Mon Sep 17 00:00:00 2001 From: Wasi Master Date: Mon, 21 Mar 2022 11:06:13 +0600 Subject: [PATCH 020/409] Add dracula theme --- public/css/themes/dracula.css | 37 +++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 public/css/themes/dracula.css diff --git a/public/css/themes/dracula.css b/public/css/themes/dracula.css new file mode 100644 index 0000000..c805f65 --- /dev/null +++ b/public/css/themes/dracula.css @@ -0,0 +1,37 @@ +body { + --bg_color: #282a36; + --fg_color: #f8f8f2; + --fg_faded: #6272a4; + --fg_dark: var(--fg_faded); + --fg_nav: var(--accent); + + --bg_panel: #343746; + --bg_elements: #44475a; + --bg_overlays: #44475a; + --bg_hover: #2f323f; + + --grey: var(--fg_faded); + --dark_grey: #44475a; + --darker_grey: #44475a; + --darkest_grey: #44475a; + --border_grey: #44475a; + + --accent: #bd93f9; + --accent_light: #caa9fa; + --accent_dark: var(--accent); + --accent_border: #ff79c696; + + --play_button: #ffb86c; + --play_button_hover: #ffc689; + + --more_replies_dots: #bd93f9; + --error_red: #ff5555; + + --verified_blue: var(--accent); + --icon_text: ##F8F8F2; + + --tab: #6272a4; + --tab_selected: var(--accent); + + --profile_stat: #919cbf; +} From 2b20b24abf9cd1d6a4508d0b474aa39703ad8b2e Mon Sep 17 00:00:00 2001 From: Wasi Master Date: Mon, 21 Mar 2022 17:49:07 +0600 Subject: [PATCH 021/409] Fix contrast for the dracula theme --- public/css/themes/dracula.css | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/public/css/themes/dracula.css b/public/css/themes/dracula.css index c805f65..6042f7d 100644 --- a/public/css/themes/dracula.css +++ b/public/css/themes/dracula.css @@ -1,19 +1,19 @@ body { --bg_color: #282a36; --fg_color: #f8f8f2; - --fg_faded: #6272a4; + --fg_faded: #818eb6; --fg_dark: var(--fg_faded); --fg_nav: var(--accent); --bg_panel: #343746; - --bg_elements: #44475a; + --bg_elements: #292b36; --bg_overlays: #44475a; --bg_hover: #2f323f; --grey: var(--fg_faded); --dark_grey: #44475a; - --darker_grey: #44475a; - --darkest_grey: #44475a; + --darker_grey: #3d4051; + --darkest_grey: #363948; --border_grey: #44475a; --accent: #bd93f9; @@ -35,3 +35,7 @@ body { --profile_stat: #919cbf; } + +.search-bar > form input::placeholder{ + color: var(--fg_faded); +} \ No newline at end of file From 2536370cd7480880fa1a7157c66fbf52937c09ab Mon Sep 17 00:00:00 2001 From: Zed Date: Fri, 1 Apr 2022 17:56:55 +0200 Subject: [PATCH 022/409] Minor fixes, update tests --- src/parserutils.nim | 6 ++---- tests/test_card.py | 7 +------ 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/parserutils.nim b/src/parserutils.nim index e0b2669..4929c2a 100644 --- a/src/parserutils.nim +++ b/src/parserutils.nim @@ -134,10 +134,8 @@ proc getTombstone*(js: JsonNode): string = result.removeSuffix(" Learn more") proc getSource*(js: JsonNode): string = - let - raw_src = js["source"].getStr - src = raw_src.substr(raw_src.find('>') + 1, raw_src.rfind('<') - 1) - return src + let src = js{"source"}.getStr + result = src.substr(src.find('>') + 1, src.rfind('<') - 1) proc extractSlice(js: JsonNode): Slice[int] = result = js["indices"][0].getInt ..< js["indices"][1].getInt diff --git a/tests/test_card.py b/tests/test_card.py index 7fd233c..e497649 100644 --- a/tests/test_card.py +++ b/tests/test_card.py @@ -11,7 +11,7 @@ card = [ ['nim_lang/status/1136652293510717440', 'Version 0.20.0 released', 'We are very proud to announce Nim version 0.20. This is a massive release, both literally and figuratively. It contains more than 1,000 commits and it marks our release candidate for version 1.0!', - 'nim-lang.org', True], + 'nim-lang.org', False], ['voidtarget/status/1094632512926605312', 'Basic OBS Studio plugin, written in nim, supporting C++ (C fine too)', @@ -25,11 +25,6 @@ card = [ ] no_thumb = [ - ['Bountysource/status/1141879700639215617', - 'Post a bounty on kivy/plyer!', - 'Automation and Screen Reader Support', - 'bountysource.com'], - ['brent_p/status/1088857328680488961', 'Hts Nim Sugar', 'hts-nim is a library that allows one to use htslib via the nim programming language. Nim is a garbage-collected language that compiles to C and often has similar performance. I have become very...', From b5262dc010b62e12c64086ef11dd75c0602547c6 Mon Sep 17 00:00:00 2001 From: Jay Carlson Date: Fri, 1 Apr 2022 16:37:02 -0700 Subject: [PATCH 023/409] Dockerfile: Fix EXPOSE outside the build stages --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 29e5d4e..1abf463 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,5 @@ FROM nimlang/nim:1.6.2-alpine-regular as nim LABEL maintainer="setenforce@protonmail.com" -EXPOSE 8080 RUN apk --no-cache add libsass-dev pcre @@ -20,4 +19,5 @@ RUN apk --no-cache add pcre COPY --from=nim /src/nitter/nitter ./ COPY --from=nim /src/nitter/nitter.example.conf ./nitter.conf COPY --from=nim /src/nitter/public ./public +EXPOSE 8080 CMD ./nitter From a21a33f8355ccfc4a7e986e8235c71e7fe490160 Mon Sep 17 00:00:00 2001 From: Jay Carlson Date: Fri, 1 Apr 2022 17:54:58 -0700 Subject: [PATCH 024/409] Docker: add ca-certificates to runtime Current Alpine doesn't ship with the "well-known" CA certificates. Without them present, outbound HTTPS or TLS connections aren't even initiated, leading to this at startup: ``` fetching token failed: No SSL/TLS CA certificates found. ``` This adds `ca-certificates` to the runtime image. --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 29e5d4e..20fa343 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,7 +16,7 @@ RUN nimble build -d:danger -d:lto -d:strip \ FROM alpine:latest WORKDIR /src/ -RUN apk --no-cache add pcre +RUN apk --no-cache add pcre ca-certificates COPY --from=nim /src/nitter/nitter ./ COPY --from=nim /src/nitter/nitter.example.conf ./nitter.conf COPY --from=nim /src/nitter/public ./public From a98cf9245ab42342709ca6bb896f00da76dffc95 Mon Sep 17 00:00:00 2001 From: Zed Date: Fri, 13 May 2022 19:20:34 +0200 Subject: [PATCH 025/409] Fix outdated tests --- tests/test_card.py | 2 +- tests/test_quote.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_card.py b/tests/test_card.py index e497649..b4b0998 100644 --- a/tests/test_card.py +++ b/tests/test_card.py @@ -11,7 +11,7 @@ card = [ ['nim_lang/status/1136652293510717440', 'Version 0.20.0 released', 'We are very proud to announce Nim version 0.20. This is a massive release, both literally and figuratively. It contains more than 1,000 commits and it marks our release candidate for version 1.0!', - 'nim-lang.org', False], + 'nim-lang.org', True], ['voidtarget/status/1094632512926605312', 'Basic OBS Studio plugin, written in nim, supporting C++ (C fine too)', diff --git a/tests/test_quote.py b/tests/test_quote.py index 0daec58..82ca385 100644 --- a/tests/test_quote.py +++ b/tests/test_quote.py @@ -3,7 +3,7 @@ from parameterized import parameterized text = [ ['elonmusk/status/1138136540096319488', - 'Tesla Owners Online', '@Model3Owners', + 'Trev Page', '@Model3Owners', """As of March 58.4% of new car sales in Norway are electric. What are we doing wrong? reuters.com/article/us-norwa…"""], From 683c052036b268028f0ecae020a1519bc586516d Mon Sep 17 00:00:00 2001 From: Zed Date: Fri, 13 May 2022 20:31:42 +0200 Subject: [PATCH 026/409] Fix placeholders in photo rail --- src/views/profile.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/views/profile.nim b/src/views/profile.nim index 9eda46d..73ce59d 100644 --- a/src/views/profile.nim +++ b/src/views/profile.nim @@ -78,8 +78,11 @@ proc renderPhotoRail(profile: Profile): VNode = tdiv(class="photo-rail-grid"): for i, photo in profile.photoRail: if i == 16: break + let photoSuffix = + if "format" in photo.url or "placeholder" in photo.url: "" + else: ":thumb" a(href=(&"/{profile.user.username}/status/{photo.tweetId}#m")): - genImg(photo.url & (if "format" in photo.url: "" else: ":thumb")) + genImg(photo.url & photoSuffix) proc renderBanner(banner: string): VNode = buildHtml(): From 0633ec2c3935aae068bfa6867a0547bbe28e685d Mon Sep 17 00:00:00 2001 From: girst Date: Fri, 25 Feb 2022 19:26:24 +0100 Subject: [PATCH 027/409] Prefer mp4 to m3u8 for Video Playback if proxyVideos is off m3u8 videos only work when the proxy is enabled. Further, this allows video playback without Javascript. This is only done when proxying is disabled to avoid excessive memory usage on the nitter instance that would result from loading longer videos in a single chunk. --- src/views/tweet.nim | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/views/tweet.nim b/src/views/tweet.nim index 6e6f3af..73548a1 100644 --- a/src/views/tweet.nim +++ b/src/views/tweet.nim @@ -62,11 +62,14 @@ proc renderAlbum(tweet: Tweet): VNode = a(href=getPicUrl(orig), class="still-image", target="_blank"): genImg(small) -proc isPlaybackEnabled(prefs: Prefs; video: Video): bool = - case video.playbackType +proc isPlaybackEnabled(prefs: Prefs; playbackType: VideoType): bool = + case playbackType of mp4: prefs.mp4Playback of m3u8, vmap: prefs.hlsPlayback +proc hasMp4Url(video: Video): bool = + video.variants.anyIt(it.contentType == mp4) + proc renderVideoDisabled(video: Video; path: string): VNode = buildHtml(tdiv(class="video-overlay")): case video.playbackType @@ -87,6 +90,9 @@ proc renderVideo*(video: Video; prefs: Prefs; path: string): VNode = let container = if video.description.len > 0 or video.title.len > 0: " card-container" else: "" + let playbackType = + if not prefs.proxyVideos and video.hasMp4Url: mp4 + else: video.playbackType buildHtml(tdiv(class="attachments card")): tdiv(class="gallery-video" & container): @@ -95,13 +101,13 @@ proc renderVideo*(video: Video; prefs: Prefs; path: string): VNode = if not video.available: img(src=thumb) renderVideoUnavailable(video) - elif not prefs.isPlaybackEnabled(video): + elif not prefs.isPlaybackEnabled(playbackType): img(src=thumb) renderVideoDisabled(video, path) else: - let vid = video.variants.filterIt(it.contentType == video.playbackType) - let source = getVidUrl(vid[0].url) - case video.playbackType + let vid = video.variants.filterIt(it.contentType == playbackType) + let source = if prefs.proxyVideos: getVidUrl(vid[0].url) else: vid[0].url + case playbackType of mp4: if prefs.muteVideos: video(poster=thumb, controls="", muted=""): From e2b8e17f857f3a1bcfdc7942e58f3e4070ea8533 Mon Sep 17 00:00:00 2001 From: girst Date: Wed, 18 May 2022 19:47:03 +0200 Subject: [PATCH 028/409] use largest resolution mp4 video available --- src/parser.nim | 9 +++++++-- src/parserutils.nim | 15 +++++++++++++++ src/types.nim | 1 + src/views/tweet.nim | 9 ++++++--- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/parser.nim b/src/parser.nim index 1a7c073..fc8dc3b 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -87,10 +87,15 @@ proc parseVideo(js: JsonNode): Video = result.description = description.getStr for v in js{"video_info", "variants"}: + let + contentType = parseEnum[VideoType](v{"content_type"}.getStr("summary")) + url = v{"url"}.getStr + resolution = getMp4Resolution(url) # only available if contentType == mp4 result.variants.add VideoVariant( - contentType: parseEnum[VideoType](v{"content_type"}.getStr("summary")), + contentType: contentType, bitrate: v{"bitrate"}.getInt, - url: v{"url"}.getStr + url: url, + resolution: resolution ) proc parsePromoVideo(js: JsonNode): Video = diff --git a/src/parserutils.nim b/src/parserutils.nim index 4929c2a..51ccea5 100644 --- a/src/parserutils.nim +++ b/src/parserutils.nim @@ -137,6 +137,21 @@ proc getSource*(js: JsonNode): string = let src = js{"source"}.getStr result = src.substr(src.find('>') + 1, src.rfind('<') - 1) +proc getMp4Resolution*(url: string): int = + # parses the height out of a URL like this one: + # https://video.twimg.com/ext_tw_video//pu/vid/720x1280/.mp4 + const vidSep = "/vid/" + let + vidIdx = url.find(vidSep) + vidSep.len + resIdx = url.find('x', vidIdx) + 1 + res = url[resIdx ..< url.find("/", resIdx)] + + try: + return parseInt(res) + except ValueError: + # cannot determine resolution (e.g. m3u8/non-mp4 video) + return 0 + proc extractSlice(js: JsonNode): Slice[int] = result = js["indices"][0].getInt ..< js["indices"][1].getInt diff --git a/src/types.nim b/src/types.nim index 98433aa..061ec8a 100644 --- a/src/types.nim +++ b/src/types.nim @@ -75,6 +75,7 @@ type contentType*: VideoType url*: string bitrate*: int + resolution*: int Video* = object durationMs*: int diff --git a/src/views/tweet.nim b/src/views/tweet.nim index 73548a1..d07ca06 100644 --- a/src/views/tweet.nim +++ b/src/views/tweet.nim @@ -1,5 +1,5 @@ # SPDX-License-Identifier: AGPL-3.0-only -import strutils, sequtils, strformat, options +import strutils, sequtils, strformat, options, algorithm import karax/[karaxdsl, vdom, vstyles] from jester import Request @@ -105,8 +105,11 @@ proc renderVideo*(video: Video; prefs: Prefs; path: string): VNode = img(src=thumb) renderVideoDisabled(video, path) else: - let vid = video.variants.filterIt(it.contentType == playbackType) - let source = if prefs.proxyVideos: getVidUrl(vid[0].url) else: vid[0].url + let + vars = video.variants.filterIt(it.contentType == playbackType) + vidUrl = vars.sortedByIt(it.resolution)[^1].url + source = if prefs.proxyVideos: getVidUrl(vidUrl) + else: vidUrl case playbackType of mp4: if prefs.muteVideos: From 6c83e872923c6d0eff9be0d74c2066f3b702eeed Mon Sep 17 00:00:00 2001 From: Zed Date: Sat, 4 Jun 2022 00:52:28 +0200 Subject: [PATCH 029/409] Update outdated tests --- .gitignore | 4 +++- tests/test_card.py | 4 ++-- tests/test_tweet.py | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 4d742bb..d43cc3f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,9 +3,11 @@ nitter *.db /tests/__pycache__ /tests/geckodriver.log -/tests/downloaded_files/* +/tests/downloaded_files +/tests/latest_logs /tools/gencss /tools/rendermd /public/css/style.css /public/md/*.html nitter.conf +dump.rdb diff --git a/tests/test_card.py b/tests/test_card.py index b4b0998..ab79e9c 100644 --- a/tests/test_card.py +++ b/tests/test_card.py @@ -4,8 +4,8 @@ from parameterized import parameterized card = [ ['Thom_Wolf/status/1122466524860702729', - 'pytorch/fairseq', - 'Facebook AI Research Sequence-to-Sequence Toolkit written in Python. - GitHub - pytorch/fairseq: Facebook AI Research Sequence-to-Sequence Toolkit written in Python.', + 'facebookresearch/fairseq', + 'Facebook AI Research Sequence-to-Sequence Toolkit written in Python. - GitHub - facebookresearch/fairseq: Facebook AI Research Sequence-to-Sequence Toolkit written in Python.', 'github.com', True], ['nim_lang/status/1136652293510717440', diff --git a/tests/test_tweet.py b/tests/test_tweet.py index e8ba0f7..9209e70 100644 --- a/tests/test_tweet.py +++ b/tests/test_tweet.py @@ -16,7 +16,7 @@ timeline = [ ] status = [ - [20, 'jack⚡️', 'jack', '21 Mar 2006', 'just setting up my twttr'], + [20, 'jack', 'jack', '21 Mar 2006', 'just setting up my twttr'], [134849778302464000, 'The Twoffice', 'TheTwoffice', '11 Nov 2011', 'test'], [105685475985080322, 'The Twoffice', 'TheTwoffice', '22 Aug 2011', 'regular tweet'], [572593440719912960, 'Test account', 'mobile_test', '3 Mar 2015', 'testing test'] @@ -71,7 +71,7 @@ emoji = [ retweet = [ [7, 'mobile_test_2', 'mobile test 2', 'Test account', '@mobile_test', '1234'], - [3, 'mobile_test_8', 'mobile test 8', 'jack⚡️', '@jack', 'twttr'] + [3, 'mobile_test_8', 'mobile test 8', 'jack', '@jack', 'twttr'] ] reply = [ From 93f605f4fe9f0787e3dd618cba85130b9cba2438 Mon Sep 17 00:00:00 2001 From: Zed Date: Sat, 4 Jun 2022 01:11:35 +0200 Subject: [PATCH 030/409] Update deps --- nitter.nimble | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nitter.nimble b/nitter.nimble index 78c1254..f24d08e 100644 --- a/nitter.nimble +++ b/nitter.nimble @@ -12,15 +12,15 @@ bin = @["nitter"] requires "nim >= 1.4.8" requires "jester >= 0.5.0" -requires "karax#fa4a2dc" +requires "karax#5498909" requires "sass#e683aa1" requires "nimcrypto#a5742a9" -requires "markdown#abdbe5e" +requires "markdown#a661c26" requires "packedjson#d11d167" requires "supersnappy#2.1.1" requires "redpool#8b7c1db" requires "https://github.com/zedeus/redis#d0a0e6f" -requires "zippy#0.7.3" +requires "zippy#0.9.9" requires "flatty#0.2.3" requires "jsony#d0e69bd" From 6709f6f1b52ed5ecdbbd791d570d9eaaa1e0ac00 Mon Sep 17 00:00:00 2001 From: Zed Date: Sat, 4 Jun 2022 01:32:02 +0200 Subject: [PATCH 031/409] Fix "playback disabled" message --- src/views/tweet.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/views/tweet.nim b/src/views/tweet.nim index fe96c8e..671ff36 100644 --- a/src/views/tweet.nim +++ b/src/views/tweet.nim @@ -70,9 +70,9 @@ proc isPlaybackEnabled(prefs: Prefs; playbackType: VideoType): bool = proc hasMp4Url(video: Video): bool = video.variants.anyIt(it.contentType == mp4) -proc renderVideoDisabled(video: Video; path: string): VNode = +proc renderVideoDisabled(playbackType: VideoType; path: string): VNode = buildHtml(tdiv(class="video-overlay")): - case video.playbackType + case playbackType of mp4: p: text "mp4 playback disabled in preferences" of m3u8, vmap: @@ -102,7 +102,7 @@ proc renderVideo*(video: Video; prefs: Prefs; path: string): VNode = renderVideoUnavailable(video) elif not prefs.isPlaybackEnabled(playbackType): img(src=thumb) - renderVideoDisabled(video, path) + renderVideoDisabled(playbackType, path) else: let vars = video.variants.filterIt(it.contentType == playbackType) From 21e8f04fa47cbef5332210eb650ef7819cf9bca8 Mon Sep 17 00:00:00 2001 From: Zed Date: Sat, 4 Jun 2022 02:18:26 +0200 Subject: [PATCH 032/409] Use strformat more --- src/formatters.nim | 6 +++--- src/query.nim | 6 +++--- src/routes/embed.nim | 4 ++-- src/routes/list.nim | 19 ++++++++++--------- src/routes/rss.nim | 20 ++++++++++---------- src/routes/search.nim | 4 ++-- src/routes/timeline.nim | 4 ++-- src/views/general.nim | 2 +- src/views/renderutils.nim | 4 ++-- src/views/rss.nimf | 4 ++-- src/views/tweet.nim | 2 +- 11 files changed, 38 insertions(+), 37 deletions(-) diff --git a/src/formatters.nim b/src/formatters.nim index bb8698c..28bfa1c 100644 --- a/src/formatters.nim +++ b/src/formatters.nim @@ -60,11 +60,11 @@ proc replaceUrls*(body: string; prefs: Prefs; absolute=""): string = result = result.replace("/c/", "/") if prefs.replaceTwitter.len > 0 and ("twitter.com" in body or tco in body): - result = result.replace(tco, https & prefs.replaceTwitter & "/t.co") + result = result.replace(tco, &"{https}{prefs.replaceTwitter}/t.co") result = result.replace(cards, prefs.replaceTwitter & "/cards") result = result.replace(twRegex, prefs.replaceTwitter) result = result.replacef(twLinkRegex, a( - prefs.replaceTwitter & "$2", href = https & prefs.replaceTwitter & "$1")) + prefs.replaceTwitter & "$2", href = &"{https}{prefs.replaceTwitter}$1")) if prefs.replaceReddit.len > 0 and ("reddit.com" in result or "redd.it" in result): result = result.replace(rdShortRegex, prefs.replaceReddit & "/comments/") @@ -76,7 +76,7 @@ proc replaceUrls*(body: string; prefs: Prefs; absolute=""): string = result = result.replace(igRegex, prefs.replaceInstagram) if absolute.len > 0 and "href" in result: - result = result.replace("href=\"/", "href=\"" & absolute & "/") + result = result.replace("href=\"/", &"href=\"{absolute}/") proc getM3u8Url*(content: string): string = var matches: array[1, string] diff --git a/src/query.nim b/src/query.nim index cf9b0e6..d128f6f 100644 --- a/src/query.nim +++ b/src/query.nim @@ -93,11 +93,11 @@ proc genQueryUrl*(query: Query): string = if query.text.len > 0: params.add "q=" & encodeUrl(query.text) for f in query.filters: - params.add "f-" & f & "=on" + params.add &"f-{f}=on" for e in query.excludes: - params.add "e-" & e & "=on" + params.add &"e-{e}=on" for i in query.includes.filterIt(it != "nativeretweets"): - params.add "i-" & i & "=on" + params.add &"i-{i}=on" if query.since.len > 0: params.add "since=" & query.since diff --git a/src/routes/embed.nim b/src/routes/embed.nim index 1a93d40..8690357 100644 --- a/src/routes/embed.nim +++ b/src/routes/embed.nim @@ -1,5 +1,5 @@ # SPDX-License-Identifier: AGPL-3.0-only -import asyncdispatch, strutils, options +import asyncdispatch, strutils, strformat, options import jester, karax/vdom import ".."/[types, api] import ../views/[embed, tweet, general] @@ -31,6 +31,6 @@ proc createEmbedRouter*(cfg: Config) = let id = @"id" if id.len > 0: - redirect("/i/status/" & id & "/embed") + redirect(&"/i/status/{id}/embed") else: resp Http404 diff --git a/src/routes/list.nim b/src/routes/list.nim index d466080..c97b1c1 100644 --- a/src/routes/list.nim +++ b/src/routes/list.nim @@ -1,5 +1,5 @@ # SPDX-License-Identifier: AGPL-3.0-only -import strutils, uri +import strutils, strformat, uri import jester @@ -10,14 +10,17 @@ export getListTimeline, getGraphList template respList*(list, timeline, title, vnode: typed) = if list.id.len == 0 or list.name.len == 0: - resp Http404, showError("List " & @"id" & " not found", cfg) + resp Http404, showError(&"""List "{@"id"}" not found""", cfg) let html = renderList(vnode, timeline.query, list) - rss = "/i/lists/$1/rss" % [@"id"] + rss = &"""/i/lists/{@"id"}/rss""" resp renderMain(html, request, cfg, prefs, titleText=title, rss=rss, banner=list.banner) +proc title*(list: List): string = + &"@{list.username}/{list.name}" + proc createListRouter*(cfg: Config) = router list: get "/@name/lists/@slug/?": @@ -28,24 +31,22 @@ proc createListRouter*(cfg: Config) = slug = decodeUrl(@"slug") list = await getCachedList(@"name", slug) if list.id.len == 0: - resp Http404, showError("List \"" & @"slug" & "\" not found", cfg) - redirect("/i/lists/" & list.id) + resp Http404, showError(&"""List "{@"slug"}" not found""", cfg) + redirect(&"/i/lists/{list.id}") get "/i/lists/@id/?": cond '.' notin @"id" let prefs = cookiePrefs() list = await getCachedList(id=(@"id")) - title = "@" & list.username & "/" & list.name timeline = await getListTimeline(list.id, getCursor()) vnode = renderTimelineTweets(timeline, prefs, request.path) - respList(list, timeline, title, vnode) + respList(list, timeline, list.title, vnode) get "/i/lists/@id/members": cond '.' notin @"id" let prefs = cookiePrefs() list = await getCachedList(id=(@"id")) - title = "@" & list.username & "/" & list.name members = await getGraphListMembers(list, getCursor()) - respList(list, members, title, renderTimelineUsers(members, prefs, request.path)) + respList(list, members, list.title, renderTimelineUsers(members, prefs, request.path)) diff --git a/src/routes/rss.nim b/src/routes/rss.nim index 40aa6a7..700c215 100644 --- a/src/routes/rss.nim +++ b/src/routes/rss.nim @@ -1,5 +1,5 @@ # SPDX-License-Identifier: AGPL-3.0-only -import asyncdispatch, strutils, tables, times, hashes, uri +import asyncdispatch, strutils, strformat, tables, times, hashes, uri import jester @@ -42,8 +42,8 @@ proc timelineRss*(req: Request; cfg: Config; query: Query): Future[Rss] {.async. template respRss*(rss, page) = if rss.cursor.len == 0: let info = case page - of "User": " \"$1\" " % @"name" - of "List": " $1 " % @"id" + of "User": &""" "{@"name"}" """ + of "List": &""" "{@"id"}" """ else: " " resp Http404, showError(page & info & "not found", cfg) @@ -67,7 +67,7 @@ proc createRssRouter*(cfg: Config) = let cursor = getCursor() - key = "search:" & $hash(genQueryUrl(query)) & ":" & cursor + key = &"search:{hash(genQueryUrl(query))}:cursor" var rss = await getCachedRss(key) if rss.cursor.len > 0: @@ -86,7 +86,7 @@ proc createRssRouter*(cfg: Config) = let cursor = getCursor() name = @"name" - key = "twitter:" & name & ":" & cursor + key = &"twitter:{name}:{cursor}" var rss = await getCachedRss(key) if rss.cursor.len > 0: @@ -109,7 +109,7 @@ proc createRssRouter*(cfg: Config) = of "search": initQuery(params(request), name=name) else: Query(fromUser: @[name]) - var key = @"tab" & ":" & @"name" & ":" + var key = &"""{@"tab"}:{@"name"}:""" if @"tab" == "search": key &= $hash(genQueryUrl(query)) & ":" key &= getCursor() @@ -132,11 +132,11 @@ proc createRssRouter*(cfg: Config) = cursor = getCursor() if list.id.len == 0: - resp Http404, showError("List \"" & @"slug" & "\" not found", cfg) + resp Http404, showError(&"""List "{@"slug"}" not found""", cfg) - let url = "/i/lists/" & list.id & "/rss" + let url = &"/i/lists/{list.id}/rss" if cursor.len > 0: - redirect(url & "?cursor=" & encodeUrl(cursor, false)) + redirect(&"{url}?cursor={encodeUrl(cursor, false)}") else: redirect(url) @@ -146,7 +146,7 @@ proc createRssRouter*(cfg: Config) = cursor = getCursor() key = if cursor.len == 0: "lists:" & @"id" - else: "lists:" & @"id" & ":" & cursor + else: &"""lists:{@"id"}:{cursor}""" var rss = await getCachedRss(key) if rss.cursor.len > 0: diff --git a/src/routes/search.nim b/src/routes/search.nim index 3fc44a9..554f2f6 100644 --- a/src/routes/search.nim +++ b/src/routes/search.nim @@ -1,5 +1,5 @@ # SPDX-License-Identifier: AGPL-3.0-only -import strutils, uri +import strutils, strformat, uri import jester @@ -37,7 +37,7 @@ proc createSearchRouter*(cfg: Config) = resp Http404, showError("Invalid search", cfg) get "/hashtag/@hash": - redirect("/search?q=" & encodeUrl("#" & @"hash")) + redirect(&"""/search?q={encodeUrl("#" & @"hash")}""") get "/opensearch": let url = getUrlPrefix(cfg) & "/search?q=" diff --git a/src/routes/timeline.nim b/src/routes/timeline.nim index a0a6e21..9d97c29 100644 --- a/src/routes/timeline.nim +++ b/src/routes/timeline.nim @@ -1,5 +1,5 @@ # SPDX-License-Identifier: AGPL-3.0-only -import asyncdispatch, strutils, sequtils, uri, options, times +import asyncdispatch, strutils, strformat, sequtils, uri, options, times import jester, karax/vdom import router_utils @@ -102,7 +102,7 @@ proc showTimeline*(request: Request; query: Query; cfg: Config; prefs: Prefs; template respTimeline*(timeline: typed) = let t = timeline if t.len == 0: - resp Http404, showError("User \"" & @"name" & "\" not found", cfg) + resp Http404, showError(&"""User "{@"name"}" not found""", cfg) resp t template respUserId*() = diff --git a/src/views/general.nim b/src/views/general.nim index b18dae5..f242e66 100644 --- a/src/views/general.nim +++ b/src/views/general.nim @@ -81,7 +81,7 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc=""; title: if titleText.len > 0: - text titleText & " | " & cfg.title + text &"{titleText}|{cfg.title}" else: text cfg.title diff --git a/src/views/renderutils.nim b/src/views/renderutils.nim index 3e0cd19..bab01cd 100644 --- a/src/views/renderutils.nim +++ b/src/views/renderutils.nim @@ -1,11 +1,11 @@ # SPDX-License-Identifier: AGPL-3.0-only -import strutils +import strutils, strformat import karax/[karaxdsl, vdom, vstyles] import ".."/[types, utils] proc icon*(icon: string; text=""; title=""; class=""; href=""): VNode = var c = "icon-" & icon - if class.len > 0: c = c & " " & class + if class.len > 0: c = &"{c} {class}" buildHtml(tdiv(class="icon-container")): if href.len > 0: a(class=c, title=title, href=href) diff --git a/src/views/rss.nimf b/src/views/rss.nimf index f910f92..96f6466 100644 --- a/src/views/rss.nimf +++ b/src/views/rss.nimf @@ -117,7 +117,7 @@ ${renderRssTweets(profile.tweets.content, cfg)} ${xmltree.escape(list.name)} / @${list.username} ${link} - ${getDescription(list.name & " by @" & list.username, cfg)} + ${getDescription(&"{list.name} by @{list.username}", cfg)} en-us 40 ${renderRssTweets(tweets, cfg)} @@ -135,7 +135,7 @@ ${renderRssTweets(tweets, cfg)} Search results for "${escName}" ${link} - ${getDescription("Search \"" & escName & "\"", cfg)} + ${getDescription(&"Search \"{escName}\"", cfg)} en-us 40 ${renderRssTweets(tweets, cfg)} diff --git a/src/views/tweet.nim b/src/views/tweet.nim index 671ff36..5bee864 100644 --- a/src/views/tweet.nim +++ b/src/views/tweet.nim @@ -154,7 +154,7 @@ proc renderPoll(poll: Poll): VNode = span(class="poll-choice-value"): text percStr span(class="poll-choice-option"): text poll.options[i] span(class="poll-info"): - text insertSep($poll.votes, ',') & " votes • " & poll.status + text &"{insertSep($poll.votes, ',')} votes • {poll.status}" proc renderCardImage(card: Card): VNode = buildHtml(tdiv(class="card-image-container")): From 778c6c64cb93a56faade09b55df25c8c82b793e4 Mon Sep 17 00:00:00 2001 From: Zed Date: Sat, 4 Jun 2022 02:24:43 +0200 Subject: [PATCH 033/409] Use a different quote for testing --- tests/test_quote.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/test_quote.py b/tests/test_quote.py index 82ca385..e1ee728 100644 --- a/tests/test_quote.py +++ b/tests/test_quote.py @@ -8,9 +8,13 @@ text = [ What are we doing wrong? reuters.com/article/us-norwa…"""], - ['nim_lang/status/924694255364341760', - 'Hacker News', '@newsycombinator', - 'Why Rust fails hard at scientific computing andre-ratsimbazafy.com/why-r…'] + ['nim_lang/status/1491461266849808397#m', + 'Nim language', '@nim_lang', + """What's better than Nim 1.6.0? + +Nim 1.6.2 :) + +nim-lang.org/blog/2021/12/17…"""] ] image = [ From c543a1df8cf438d04ed47936069639c0543fbece Mon Sep 17 00:00:00 2001 From: minus Date: Sat, 4 Jun 2022 15:48:25 +0000 Subject: [PATCH 034/409] Block search engines via robots.txt (#631) Prevents instances from being rate limited due to being senselessly crawled by search engines. Since there is no reason to index Nitter instances, simply block all robots. Notably, this does *not* affect link previews (e.g. in various chat software). --- public/robots.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 public/robots.txt diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..1f53798 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / From dfb2519870acf99d9e09a864910a6590552ff08a Mon Sep 17 00:00:00 2001 From: Zed Date: Sat, 4 Jun 2022 17:52:47 +0200 Subject: [PATCH 035/409] Explicitly allow Twitterbot to generate previews --- public/robots.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/robots.txt b/public/robots.txt index 1f53798..4c2b2a6 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -1,2 +1,4 @@ +User-agent: Twitterbot +Allow: / User-agent: * Disallow: / From 138826fb4fbdec73fc6fee2e025fda88f7f2fb49 Mon Sep 17 00:00:00 2001 From: Zed Date: Sat, 4 Jun 2022 17:55:35 +0200 Subject: [PATCH 036/409] Fix Twitterbot rule in robots.txt --- public/robots.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/robots.txt b/public/robots.txt index 4c2b2a6..7a74568 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -1,4 +1,4 @@ -User-agent: Twitterbot -Allow: / User-agent: * Disallow: / +User-agent: Twitterbot +Disallow: From adaa94d998f727f5820643f3bf64d02a10f2517a Mon Sep 17 00:00:00 2001 From: Zed Date: Sun, 5 Jun 2022 21:47:25 +0200 Subject: [PATCH 037/409] Add more logging to the token pool --- src/tokens.nim | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/tokens.nim b/src/tokens.nim index 8a68e46..4bbcd81 100644 --- a/src/tokens.nim +++ b/src/tokens.nim @@ -14,6 +14,10 @@ var clientPool: HttpPool tokenPool: seq[Token] lastFailed: Time + enableLogging = false + +template log(str) = + if enableLogging: echo "[tokens] ", str proc getPoolJson*(): JsonNode = var @@ -77,8 +81,10 @@ proc fetchToken(): Future[Token] {.async.} = return Token(tok: tok, init: time, lastUse: time) except Exception as e: - lastFailed = getTime() - echo "fetching token failed: ", e.msg + echo "[tokens] fetching token failed: ", e.msg + if "Try again" notin e.msg: + echo "[tokens] fetching tokens paused, resuming in 30 minutes" + lastFailed = getTime() proc expired(token: Token): bool = let time = getTime() @@ -100,6 +106,9 @@ proc isReady(token: Token; api: Api): bool = proc release*(token: Token; used=false; invalid=false) = if token.isNil: return if invalid or token.expired: + if invalid: log "discarding invalid token" + elif token.expired: log "discarding expired token" + let idx = tokenPool.find(token) if idx > -1: tokenPool.delete(idx) elif used: @@ -115,6 +124,7 @@ proc getToken*(api: Api): Future[Token] {.async.} = if not result.isReady(api): release(result) result = await fetchToken() + log "added new token to pool" tokenPool.add result if not result.isNil: @@ -143,10 +153,12 @@ proc poolTokens*(amount: int) {.async.} = except: discard if not newToken.isNil: + log "added new token to pool" tokenPool.add newToken proc initTokenPool*(cfg: Config) {.async.} = clientPool = HttpPool() + enableLogging = cfg.enableDebug while true: if tokenPool.countIt(not it.isLimited(Api.timeline)) < cfg.minTokens: From 38bbc677574a6e33409b830d6b66a15c49853a22 Mon Sep 17 00:00:00 2001 From: Zed Date: Sun, 5 Jun 2022 22:27:22 +0200 Subject: [PATCH 038/409] Remove old unnecessary rate limit error log --- src/nitter.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/src/nitter.nim b/src/nitter.nim index 9f8fcb7..d743599 100644 --- a/src/nitter.nim +++ b/src/nitter.nim @@ -85,7 +85,6 @@ routes: &"An error occurred, please {link} with the URL you tried to visit.", cfg) error RateLimitError: - echo error.exc.name, ": ", error.exc.msg const link = a("another instance", href = instancesUrl) resp Http429, showError( &"Instance has been rate limited.
Use {link} or try again later.", cfg) From d407051b66c3a7d0ed6a9aed16ad1a844af44398 Mon Sep 17 00:00:00 2001 From: Zed Date: Sun, 5 Jun 2022 22:40:10 +0200 Subject: [PATCH 039/409] Downgrade zippy library to fix checksum error --- nitter.nimble | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nitter.nimble b/nitter.nimble index f24d08e..d364562 100644 --- a/nitter.nimble +++ b/nitter.nimble @@ -20,7 +20,7 @@ requires "packedjson#d11d167" requires "supersnappy#2.1.1" requires "redpool#8b7c1db" requires "https://github.com/zedeus/redis#d0a0e6f" -requires "zippy#0.9.9" +requires "zippy#0.7.3" requires "flatty#0.2.3" requires "jsony#d0e69bd" From 5cceca4e9396e760517f616ae45d01ea37b704a8 Mon Sep 17 00:00:00 2001 From: zedeus Date: Thu, 9 Jun 2022 09:25:45 +0200 Subject: [PATCH 040/409] Bump zippy dependency --- nitter.nimble | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nitter.nimble b/nitter.nimble index d364562..90a7921 100644 --- a/nitter.nimble +++ b/nitter.nimble @@ -20,7 +20,7 @@ requires "packedjson#d11d167" requires "supersnappy#2.1.1" requires "redpool#8b7c1db" requires "https://github.com/zedeus/redis#d0a0e6f" -requires "zippy#0.7.3" +requires "zippy#0.9.11" requires "flatty#0.2.3" requires "jsony#d0e69bd" From 74c13b372dfe43014385e2ce165fd01d5d656fae Mon Sep 17 00:00:00 2001 From: zedeus Date: Thu, 9 Jun 2022 09:34:06 +0200 Subject: [PATCH 041/409] Format readme --- README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 460ce21..286bea7 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ XMR: 42hKayRoEAw4D6G6t8mQHPJHQcXqofjFuVfavqKeNMNUZfeJLJAcNU19i1bGdDvcdN6romiSscW ## Resources -The wiki contains +The wiki contains [a list of instances](https://github.com/zedeus/nitter/wiki/Instances) and [browser extensions](https://github.com/zedeus/nitter/wiki/Extensions) maintained by the community. @@ -67,9 +67,10 @@ Twitter account. ## Installation ### Dependencies -* libpcre -* libsass -* redis + +- libpcre +- libsass +- redis To compile Nitter you need a Nim installation, see [nim-lang.org](https://nim-lang.org/install.html) for details. It is possible to @@ -115,18 +116,21 @@ before you can run the container. See below for how to also run Redis using Docker. To build and run Nitter in Docker: + ```bash docker build -t nitter:latest . docker run -v $(pwd)/nitter.conf:/src/nitter.conf -d --network host nitter:latest ``` A prebuilt Docker image is provided as well: + ```bash docker run -v $(pwd)/nitter.conf:/src/nitter.conf -d --network host zedeus/nitter:latest ``` Using docker-compose to run both Nitter and Redis as different containers: Change `redisHost` from `localhost` to `nitter-redis` in `nitter.conf`, then run: + ```bash docker-compose up -d ``` From b398136742f9b784b2d5c8b5baa9f25a3560b5a0 Mon Sep 17 00:00:00 2001 From: Frank Moskal Date: Sun, 12 Jun 2022 17:11:39 +0000 Subject: [PATCH 042/409] update hls.js to v1.1.5 (#636) --- public/js/hls.light.min.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/js/hls.light.min.js b/public/js/hls.light.min.js index af4758a..2dcaa72 100644 --- a/public/js/hls.light.min.js +++ b/public/js/hls.light.min.js @@ -1,5 +1,5 @@ // @license http://www.apache.org/licenses/LICENSE-2.0 Apache-2.0 // @source https://github.com/video-dev/hls.js -// @version v1.0.6 -"undefined"!=typeof window&&function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Hls=e():t.Hls=e()}(this,(function(){return function(t){var e={};function r(i){if(e[i])return e[i].exports;var a=e[i]={i:i,l:!1,exports:{}};return t[i].call(a.exports,a,a.exports,r),a.l=!0,a.exports}return r.m=t,r.c=e,r.d=function(t,e,i){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:i})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(r.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var a in t)r.d(i,a,function(e){return t[e]}.bind(null,a));return i},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="/dist/",r(r.s=19)}([function(t,e,r){"use strict";var i;r.d(e,"a",(function(){return i})),function(t){t.MEDIA_ATTACHING="hlsMediaAttaching",t.MEDIA_ATTACHED="hlsMediaAttached",t.MEDIA_DETACHING="hlsMediaDetaching",t.MEDIA_DETACHED="hlsMediaDetached",t.BUFFER_RESET="hlsBufferReset",t.BUFFER_CODECS="hlsBufferCodecs",t.BUFFER_CREATED="hlsBufferCreated",t.BUFFER_APPENDING="hlsBufferAppending",t.BUFFER_APPENDED="hlsBufferAppended",t.BUFFER_EOS="hlsBufferEos",t.BUFFER_FLUSHING="hlsBufferFlushing",t.BUFFER_FLUSHED="hlsBufferFlushed",t.MANIFEST_LOADING="hlsManifestLoading",t.MANIFEST_LOADED="hlsManifestLoaded",t.MANIFEST_PARSED="hlsManifestParsed",t.LEVEL_SWITCHING="hlsLevelSwitching",t.LEVEL_SWITCHED="hlsLevelSwitched",t.LEVEL_LOADING="hlsLevelLoading",t.LEVEL_LOADED="hlsLevelLoaded",t.LEVEL_UPDATED="hlsLevelUpdated",t.LEVEL_PTS_UPDATED="hlsLevelPtsUpdated",t.LEVELS_UPDATED="hlsLevelsUpdated",t.AUDIO_TRACKS_UPDATED="hlsAudioTracksUpdated",t.AUDIO_TRACK_SWITCHING="hlsAudioTrackSwitching",t.AUDIO_TRACK_SWITCHED="hlsAudioTrackSwitched",t.AUDIO_TRACK_LOADING="hlsAudioTrackLoading",t.AUDIO_TRACK_LOADED="hlsAudioTrackLoaded",t.SUBTITLE_TRACKS_UPDATED="hlsSubtitleTracksUpdated",t.SUBTITLE_TRACKS_CLEARED="hlsSubtitleTracksCleared",t.SUBTITLE_TRACK_SWITCH="hlsSubtitleTrackSwitch",t.SUBTITLE_TRACK_LOADING="hlsSubtitleTrackLoading",t.SUBTITLE_TRACK_LOADED="hlsSubtitleTrackLoaded",t.SUBTITLE_FRAG_PROCESSED="hlsSubtitleFragProcessed",t.CUES_PARSED="hlsCuesParsed",t.NON_NATIVE_TEXT_TRACKS_FOUND="hlsNonNativeTextTracksFound",t.INIT_PTS_FOUND="hlsInitPtsFound",t.FRAG_LOADING="hlsFragLoading",t.FRAG_LOAD_EMERGENCY_ABORTED="hlsFragLoadEmergencyAborted",t.FRAG_LOADED="hlsFragLoaded",t.FRAG_DECRYPTED="hlsFragDecrypted",t.FRAG_PARSING_INIT_SEGMENT="hlsFragParsingInitSegment",t.FRAG_PARSING_USERDATA="hlsFragParsingUserdata",t.FRAG_PARSING_METADATA="hlsFragParsingMetadata",t.FRAG_PARSED="hlsFragParsed",t.FRAG_BUFFERED="hlsFragBuffered",t.FRAG_CHANGED="hlsFragChanged",t.FPS_DROP="hlsFpsDrop",t.FPS_DROP_LEVEL_CAPPING="hlsFpsDropLevelCapping",t.ERROR="hlsError",t.DESTROYING="hlsDestroying",t.KEY_LOADING="hlsKeyLoading",t.KEY_LOADED="hlsKeyLoaded",t.LIVE_BACK_BUFFER_REACHED="hlsLiveBackBufferReached",t.BACK_BUFFER_REACHED="hlsBackBufferReached"}(i||(i={}))},function(t,e,r){"use strict";r.d(e,"a",(function(){return o})),r.d(e,"b",(function(){return l}));var i=function(){},a={trace:i,debug:i,log:i,warn:i,info:i,error:i},n=a;function s(t){var e=self.console[t];return e?e.bind(self.console,"["+t+"] >"):i}function o(t){if(self.console&&!0===t||"object"==typeof t){!function(t){for(var e=arguments.length,r=new Array(e>1?e-1:0),i=1;i>8*(15-r)&255;return e},r.setDecryptDataFromLevelKey=function(t,e){var r=t;return"AES-128"===(null==t?void 0:t.method)&&t.uri&&!t.iv&&((r=o.a.fromURI(t.uri)).method=t.method,r.iv=this.createInitializationVector(e),r.keyFormat="identity"),r},r.setElementaryStreamInfo=function(t,e,r,i,a,n){void 0===n&&(n=!1);var s=this.elementaryStreams,o=s[t];o?(o.startPTS=Math.min(o.startPTS,e),o.endPTS=Math.max(o.endPTS,r),o.startDTS=Math.min(o.startDTS,i),o.endDTS=Math.max(o.endDTS,a)):s[t]={startPTS:e,endPTS:r,startDTS:i,endDTS:a,partial:n}},r.clearElementaryStreamInfo=function(){var t=this.elementaryStreams;t[i.AUDIO]=null,t[i.VIDEO]=null,t[i.AUDIOVIDEO]=null},f(e,[{key:"decryptdata",get:function(){if(!this.levelkey&&!this._decryptdata)return null;if(!this._decryptdata&&this.levelkey){var t=this.sn;"number"!=typeof t&&(this.levelkey&&"AES-128"===this.levelkey.method&&!this.levelkey.iv&&s.b.warn('missing IV for initialization segment with method="'+this.levelkey.method+'" - compliance issue'),t=0),this._decryptdata=this.setDecryptDataFromLevelKey(this.levelkey,t)}return this._decryptdata}},{key:"end",get:function(){return this.start+this.duration}},{key:"endProgramDateTime",get:function(){if(null===this.programDateTime)return null;if(!Object(a.a)(this.programDateTime))return null;var t=Object(a.a)(this.duration)?this.duration:0;return this.programDateTime+1e3*t}},{key:"encrypted",get:function(){var t;return!(null===(t=this.decryptdata)||void 0===t||!t.keyFormat||!this.decryptdata.uri)}}]),e}(c),g=function(t){function e(e,r,i,a,n){var s;(s=t.call(this,i)||this).fragOffset=0,s.duration=0,s.gap=!1,s.independent=!1,s.relurl=void 0,s.fragment=void 0,s.index=void 0,s.stats=new l.a,s.duration=e.decimalFloatingPoint("DURATION"),s.gap=e.bool("GAP"),s.independent=e.bool("INDEPENDENT"),s.relurl=e.enumeratedString("URI"),s.fragment=r,s.index=a;var o=e.enumeratedString("BYTERANGE");return o&&s.setByteRange(o,n),n&&(s.fragOffset=n.fragOffset+n.duration),s}return u(e,t),f(e,[{key:"start",get:function(){return this.fragment.start+this.fragOffset}},{key:"end",get:function(){return this.start+this.duration}},{key:"loaded",get:function(){var t=this.elementaryStreams;return!!(t.audio||t.video||t.audiovideo)}}]),e}(c)},function(t,e,r){"use strict";r.d(e,"b",(function(){return d})),r.d(e,"g",(function(){return h})),r.d(e,"f",(function(){return f})),r.d(e,"d",(function(){return c})),r.d(e,"c",(function(){return v})),r.d(e,"e",(function(){return p})),r.d(e,"h",(function(){return m})),r.d(e,"a",(function(){return y}));var i=r(8),a=r(5),n=Math.pow(2,32)-1,s=[].push;function o(t){return String.fromCharCode.apply(null,t)}function l(t,e){"data"in t&&(e+=t.start,t=t.data);var r=t[e]<<24|t[e+1]<<16|t[e+2]<<8|t[e+3];return r<0?4294967296+r:r}function u(t,e,r){"data"in t&&(e+=t.start,t=t.data),t[e]=r>>24,t[e+1]=r>>16&255,t[e+2]=r>>8&255,t[e+3]=255&r}function d(t,e){var r,i,a,n=[];if(!e.length)return n;"data"in t?(r=t.data,i=t.start,a=t.end):(i=0,a=(r=t).byteLength);for(var u=i;u1?u+h:a;if(o(r.subarray(u+4,u+8))===e[0])if(1===e.length)n.push({data:r,start:u+8,end:f});else{var c=d({data:r,start:u+8,end:f},e.slice(1));c.length&&s.apply(n,c)}u=f}return n}function h(t){var e=d(t,["moov"])[0],r=e?e.end:null,i=d(t,["sidx"]);if(!i||!i[0])return null;var a=[],n=i[0],s=n.data[0],o=0===s?8:16,u=l(n,o);o+=4;o+=0===s?8:16,o+=2;var h=n.end+0,f=function(t,e){"data"in t&&(e+=t.start,t=t.data);var r=t[e]<<8|t[e+1];return r<0?65536+r:r}(n,o);o+=2;for(var c=0;c>>31)return console.warn("SIDX has hierarchical references (not supported)"),null;var m=l(n,v);v+=4,a.push({referenceSize:p,subsegmentDuration:m,info:{duration:m/u,start:h,end:h+p-1}}),h+=p,o=v+=4}return{earliestPresentationTime:0,timescale:u,version:s,referencesCount:f,references:a,moovEndOffset:r}}function f(t){for(var e=[],r=d(t,["moov","trak"]),i=0;i0)return t.subarray(r,r+i)},o=function(t,e){var r=0;return r=(127&t[e])<<21,r|=(127&t[e+1])<<14,r|=(127&t[e+2])<<7,r|=127&t[e+3]},l=function(t,e){return a(t,e)&&o(t,e+6)+10<=t.length-e},u=function(t){for(var e=f(t),r=0;r>4){case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:u+=String.fromCharCode(n);break;case 12:case 13:s=t[d++],u+=String.fromCharCode((31&n)<<6|63&s);break;case 14:s=t[d++],o=t[d++],u+=String.fromCharCode((15&n)<<12|(63&s)<<6|(63&o)<<0)}}return u};function b(){return i||void 0===self.TextDecoder||(i=new self.TextDecoder("utf-8")),i}},function(t,e,r){"use strict";function i(t,e,r){return Uint8Array.prototype.slice?t.slice(e,r):new Uint8Array(Array.prototype.slice.call(t,e,r))}r.d(e,"a",(function(){return i}))},function(t,e,r){"use strict";r.d(e,"c",(function(){return St})),r.d(e,"d",(function(){return At})),r.d(e,"a",(function(){return Rt})),r.d(e,"b",(function(){return Dt}));var i=r(0),a=r(2),n=r(14),s=r(3),o=r(7);var l=r(6),u=r(8),d=function(){function t(){this._audioTrack=void 0,this._id3Track=void 0,this.frameIndex=0,this.cachedData=null,this.initPTS=null}var e=t.prototype;return e.resetInitSegment=function(t,e,r){this._id3Track={type:"id3",id:0,pid:-1,inputTimeScale:9e4,sequenceNumber:0,samples:[],dropped:0}},e.resetTimeStamp=function(){},e.resetContiguity=function(){},e.canParse=function(t,e){return!1},e.appendFrame=function(t,e,r){},e.demux=function(t,e){this.cachedData&&(t=Object(l.a)(this.cachedData,t),this.cachedData=null);var r,i,a=o.b(t,0),n=a?a.length:0,s=this._audioTrack,d=this._id3Track,f=a?o.d(a):void 0,c=t.length;for(0!==this.frameIndex&&null!==this.initPTS||(this.initPTS=h(f,e)),a&&a.length>0&&d.samples.push({pts:this.initPTS,dts:this.initPTS,data:a}),i=this.initPTS;n>>5}function m(t,e){return e+1=t.length)return!1;var i=p(t,e);if(i<=r)return!1;var a=e+i;return a===t.length||m(t,a)}return!1}function b(t,e,r,n,s){if(!t.samplerate){var o=function(t,e,r,n){var s,o,l,u,d=navigator.userAgent.toLowerCase(),h=n,f=[96e3,88200,64e3,48e3,44100,32e3,24e3,22050,16e3,12e3,11025,8e3,7350];s=1+((192&e[r+2])>>>6);var v=(60&e[r+2])>>>2;if(!(v>f.length-1))return l=(1&e[r+2])<<2,l|=(192&e[r+3])>>>6,c.b.log("manifest codec:"+n+", ADTS type:"+s+", samplingIndex:"+v),/firefox/i.test(d)?v>=6?(s=5,u=new Array(4),o=v-3):(s=2,u=new Array(2),o=v):-1!==d.indexOf("android")?(s=2,u=new Array(2),o=v):(s=5,u=new Array(4),n&&(-1!==n.indexOf("mp4a.40.29")||-1!==n.indexOf("mp4a.40.5"))||!n&&v>=6?o=v-3:((n&&-1!==n.indexOf("mp4a.40.2")&&(v>=6&&1===l||/vivaldi/i.test(d))||!n&&1===l)&&(s=2,u=new Array(2)),o=v)),u[0]=s<<3,u[0]|=(14&v)>>1,u[1]|=(1&v)<<7,u[1]|=l<<3,5===s&&(u[1]|=(14&o)>>1,u[2]=(1&o)<<7,u[2]|=8,u[3]=0),{config:u,samplerate:f[v],channelCount:l,codec:"mp4a.40."+s,manifestCodec:h};t.trigger(i.a.ERROR,{type:a.b.MEDIA_ERROR,details:a.a.FRAG_PARSING_ERROR,fatal:!0,reason:"invalid ADTS sampling index:"+v})}(e,r,n,s);if(!o)return;t.config=o.config,t.samplerate=o.samplerate,t.channelCount=o.channelCount,t.codec=o.codec,t.manifestCodec=o.manifestCodec,c.b.log("parsed codec:"+t.codec+", rate:"+o.samplerate+", channels:"+o.channelCount)}}function T(t){return 9216e4/t}function E(t,e,r,i,a){var n=function(t,e,r,i,a){var n=g(t,e),s=p(t,e);if((s-=n)>0)return{headerLength:n,frameLength:s,stamp:r+i*a}}(e,r,i,a,T(t.samplerate));if(n){var s,o=n.frameLength,l=n.headerLength,u=n.stamp,d=l+o,h=Math.max(0,r+d-e.length);h?(s=new Uint8Array(d-l)).set(e.subarray(r+l,e.length),0):s=e.subarray(r+l,r+d);var f={unit:s,pts:u};return h||t.samples.push(f),{sample:f,length:d,missing:h}}}function S(t,e){return(S=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}var L=function(t){var e,r;function i(e,r){var i;return(i=t.call(this)||this).observer=void 0,i.config=void 0,i.observer=e,i.config=r,i}r=t,(e=i).prototype=Object.create(r.prototype),e.prototype.constructor=e,S(e,r);var a=i.prototype;return a.resetInitSegment=function(e,r,i){t.prototype.resetInitSegment.call(this,e,r,i),this._audioTrack={container:"audio/adts",type:"audio",id:0,pid:-1,sequenceNumber:0,isAAC:!0,samples:[],manifestCodec:e,duration:i,inputTimeScale:9e4,dropped:0}},i.probe=function(t){if(!t)return!1;for(var e=(o.b(t,0)||[]).length,r=t.length;e0},e.demux=function(t){var e=t,r={type:"",id:-1,pid:-1,inputTimeScale:9e4,sequenceNumber:-1,samples:[],dropped:0};if(this.config.progressive){this.remainderData&&(e=Object(l.a)(this.remainderData,t));var i=Object(l.h)(e);this.remainderData=i.remainder,r.samples=i.valid||new Uint8Array}else r.samples=e;return{audioTrack:{type:"",id:-1,pid:-1,inputTimeScale:9e4,sequenceNumber:-1,samples:[],dropped:0},avcTrack:r,id3Track:{type:"",id:-1,pid:-1,inputTimeScale:9e4,sequenceNumber:-1,samples:[],dropped:0},textTrack:{type:"",id:-1,pid:-1,inputTimeScale:9e4,sequenceNumber:-1,samples:[],dropped:0}}},e.flush=function(){var t={type:"",id:-1,pid:-1,inputTimeScale:9e4,sequenceNumber:-1,samples:[],dropped:0};return t.samples=this.remainderData||new Uint8Array,this.remainderData=null,{audioTrack:{type:"",id:-1,pid:-1,inputTimeScale:9e4,sequenceNumber:-1,samples:[],dropped:0},avcTrack:t,id3Track:{type:"",id:-1,pid:-1,inputTimeScale:9e4,sequenceNumber:-1,samples:[],dropped:0},textTrack:{type:"",id:-1,pid:-1,inputTimeScale:9e4,sequenceNumber:-1,samples:[],dropped:0}}},e.demuxSampleAes=function(t,e,r){return Promise.reject(new Error("The MP4 demuxer does not support SAMPLE-AES decryption"))},e.destroy=function(){},t}();R.minProbeByteLength=1024;var D=R,_=null,k=[32,64,96,128,160,192,224,256,288,320,352,384,416,448,32,48,56,64,80,96,112,128,160,192,224,256,320,384,32,40,48,56,64,80,96,112,128,160,192,224,256,320,32,48,56,64,80,96,112,128,144,160,176,192,224,256,8,16,24,32,40,48,56,64,80,96,112,128,144,160],x=[44100,48e3,32e3,22050,24e3,16e3,11025,12e3,8e3],w=[[0,72,144,12],[0,0,0,0],[0,72,144,12],[0,144,144,12]],C=[0,1,1,4];function O(t,e,r,i,a){if(!(r+24>e.length)){var n=I(e,r);if(n&&r+n.frameLength<=e.length){var s=i+a*(9e4*n.samplesPerFrame/n.sampleRate),o={unit:e.subarray(r,r+n.frameLength),pts:s,dts:s};return t.config=[],t.channelCount=n.channelCount,t.samplerate=n.sampleRate,t.samples.push(o),{sample:o,length:n.frameLength,missing:0}}}}function I(t,e){var r=t[e+1]>>3&3,i=t[e+1]>>1&3,a=t[e+2]>>4&15,n=t[e+2]>>2&3;if(1!==r&&0!==a&&15!==a&&3!==n){var s=t[e+2]>>1&1,o=t[e+3]>>6,l=1e3*k[14*(3===r?3-i:3===i?3:4)+a-1],u=x[3*(3===r?0:2===r?1:2)+n],d=3===o?1:2,h=w[r][i],f=C[i],c=8*h*f,v=Math.floor(h*l/u+s)*f;if(null===_){var g=(navigator.userAgent||"").match(/Chrome\/(\d+)/i);_=g?parseInt(g[1]):0}return!!_&&_<=87&&2===i&&l>=224e3&&0===o&&(t[e+3]=128|t[e+3]),{sampleRate:u,channelCount:d,frameLength:v,samplesPerFrame:c}}}function P(t,e){return 255===t[e]&&224==(224&t[e+1])&&0!=(6&t[e+1])}function F(t,e){return e+1t?(this.word<<=t,this.bitsAvailable-=t):(t-=this.bitsAvailable,t-=(e=t>>3)>>3,this.bytesAvailable-=e,this.loadWord(),this.word<<=t,this.bitsAvailable-=t)},e.readBits=function(t){var e=Math.min(this.bitsAvailable,t),r=this.word>>>32-e;return t>32&&c.b.error("Cannot read more than 32 bits at a time"),this.bitsAvailable-=e,this.bitsAvailable>0?this.word<<=e:this.bytesAvailable>0&&this.loadWord(),(e=t-e)>0&&this.bitsAvailable?r<>>t))return this.word<<=t,this.bitsAvailable-=t,t;return this.loadWord(),t+this.skipLZ()},e.skipUEG=function(){this.skipBits(1+this.skipLZ())},e.skipEG=function(){this.skipBits(1+this.skipLZ())},e.readUEG=function(){var t=this.skipLZ();return this.readBits(t+1)-1},e.readEG=function(){var t=this.readUEG();return 1&t?1+t>>>1:-1*(t>>>1)},e.readBoolean=function(){return 1===this.readBits(1)},e.readUByte=function(){return this.readBits(8)},e.readUShort=function(){return this.readBits(16)},e.readUInt=function(){return this.readBits(32)},e.skipScalingList=function(t){for(var e=8,r=8,i=0;i=t.length)return void r();if(!(t[e].unit.length<32)){var i=this.decrypter.isSync();if(this.decryptAacSample(t,e,r,i),!i)return}}},e.getAvcEncryptedData=function(t){for(var e=16*Math.floor((t.length-48)/160)+16,r=new Int8Array(e),i=0,a=32;a<=t.length-16;a+=160,i+=16)r.set(t.subarray(a,a+16),i);return r},e.getAvcDecryptedUnit=function(t,e){for(var r=new Uint8Array(e),i=0,a=32;a<=t.length-16;a+=160,i+=16)t.set(r.subarray(i,i+16),a);return t},e.decryptAvcSample=function(t,e,r,i,a,n){var s=X(a.data),o=this.getAvcEncryptedData(s),l=this;this.decryptBuffer(o.buffer,(function(o){a.data=l.getAvcDecryptedUnit(s,o),n||l.decryptAvcSamples(t,e,r+1,i)}))},e.decryptAvcSamples=function(t,e,r,i){if(t instanceof Uint8Array)throw new Error("Cannot decrypt samples of type Uint8Array");for(;;e++,r=0){if(e>=t.length)return void i();for(var a=t[e].units;!(r>=a.length);r++){var n=a[r];if(!(n.data.length<=48||1!==n.type&&5!==n.type)){var s=this.decrypter.isSync();if(this.decryptAvcSample(t,e,r,i,n,s),!s)return}}}},t}(),U={video:1,audio:2,id3:3,text:4},G=function(){function t(t,e,r){this.observer=void 0,this.config=void 0,this.typeSupported=void 0,this.sampleAes=null,this.pmtParsed=!1,this.audioCodec=void 0,this.videoCodec=void 0,this._duration=0,this.aacLastPTS=null,this._initPTS=null,this._initDTS=null,this._pmtId=-1,this._avcTrack=void 0,this._audioTrack=void 0,this._id3Track=void 0,this._txtTrack=void 0,this.aacOverFlow=null,this.avcSample=null,this.remainderData=null,this.observer=t,this.config=e,this.typeSupported=r}t.probe=function(e){var r=t.syncOffset(e);return!(r<0)&&(r&&c.b.warn("MPEG2-TS detected but first sync word found @ offset "+r+", junk ahead ?"),!0)},t.syncOffset=function(t){for(var e=Math.min(1e3,t.length-564),r=0;r>4>1){if((_=A+5+e[A+4])===A+188)continue}else _=A+4;switch(D){case f:R&&(v&&(o=V(v))&&this.parseAVCPES(o,!1),v={data:[],size:0}),v&&(v.data.push(e.subarray(_,A+188)),v.size+=A+188-_);break;case g:R&&(m&&(o=V(m))&&(d.isAAC?this.parseAACPES(o):this.parseMPEGPES(o)),m={data:[],size:0}),m&&(m.data.push(e.subarray(_,A+188)),m.size+=A+188-_);break;case p:R&&(y&&(o=V(y))&&this.parseID3PES(o),y={data:[],size:0}),y&&(y.data.push(e.subarray(_,A+188)),y.size+=A+188-_);break;case 0:R&&(_+=e[_]+1),E=this._pmtId=K(e,_);break;case E:R&&(_+=e[_]+1);var k=H(e,_,!0===this.typeSupported.mpeg||!0===this.typeSupported.mp3,n);(f=k.avc)>0&&(u.pid=f),(g=k.audio)>0&&(d.pid=g,d.isAAC=k.isAAC),(p=k.id3)>0&&(h.pid=p),b&&!T&&(c.b.log("reparse from beginning"),b=!1,A=L-188),T=this.pmtParsed=!0;break;case 17:case 8191:break;default:b=!0}}else this.observer.emit(i.a.ERROR,i.a.ERROR,{type:a.b.MEDIA_ERROR,details:a.a.FRAG_PARSING_ERROR,fatal:!1,reason:"TS packet did not start with 0x47"});u.pesData=v,d.pesData=m,h.pesData=y;var x={audioTrack:d,avcTrack:u,id3Track:h,textTrack:this._txtTrack};return s&&this.extractRemainingSamples(x),x},e.flush=function(){var t,e=this.remainderData;return this.remainderData=null,t=e?this.demux(e,-1,!1,!0):{audioTrack:this._audioTrack,avcTrack:this._avcTrack,textTrack:this._txtTrack,id3Track:this._id3Track},this.extractRemainingSamples(t),this.sampleAes?this.decrypt(t,this.sampleAes):t},e.extractRemainingSamples=function(t){var e,r=t.audioTrack,i=t.avcTrack,a=t.id3Track,n=i.pesData,s=r.pesData,o=a.pesData;n&&(e=V(n))?(this.parseAVCPES(e,!0),i.pesData=null):i.pesData=n,s&&(e=V(s))?(r.isAAC?this.parseAACPES(e):this.parseMPEGPES(e),r.pesData=null):(null!=s&&s.size&&c.b.log("last AAC PES packet truncated,might overlap between fragments"),r.pesData=s),o&&(e=V(o))?(this.parseID3PES(e),a.pesData=null):a.pesData=o},e.demuxSampleAes=function(t,e,r){var i=this.demux(t,r,!0,!this.config.progressive),a=this.sampleAes=new B(this.observer,this.config,e);return this.decrypt(i,a)},e.decrypt=function(t,e){return new Promise((function(r){var i=t.audioTrack,a=t.avcTrack;i.samples&&i.isAAC?e.decryptAacSamples(i.samples,0,(function(){a.samples?e.decryptAvcSamples(a.samples,0,0,(function(){r(t)})):r(t)})):a.samples&&e.decryptAvcSamples(a.samples,0,0,(function(){r(t)}))}))},e.destroy=function(){this._initPTS=this._initDTS=null,this._duration=0},e.parseAVCPES=function(t,e){var r,i=this,a=this._avcTrack,n=this.parseAVCNALu(t.data),s=this.avcSample,l=!1;t.data=null,s&&n.length&&!a.audFound&&(W(s,a),s=this.avcSample=j(!1,t.pts,t.dts,"")),n.forEach((function(e){switch(e.type){case 1:r=!0,s||(s=i.avcSample=j(!0,t.pts,t.dts,"")),s.frame=!0;var n=e.data;if(l&&n.length>4){var u=new N(n).readSliceType();2!==u&&4!==u&&7!==u&&9!==u||(s.key=!0)}break;case 5:r=!0,s||(s=i.avcSample=j(!0,t.pts,t.dts,"")),s.key=!0,s.frame=!0;break;case 6:r=!0;var d=new N(X(e.data));d.readUByte();for(var h=0,f=0,c=!1,v=0;!c&&d.bytesAvailable>1;){h=0;do{h+=v=d.readUByte()}while(255===v);f=0;do{f+=v=d.readUByte()}while(255===v);if(4===h&&0!==d.bytesAvailable){if(c=!0,181===d.readUByte())if(49===d.readUShort())if(1195456820===d.readUInt())if(3===d.readUByte()){for(var g=d.readUByte(),p=31&g,m=[g,d.readUByte()],y=0;y16){for(var b=[],T=0;T<16;T++)b.push(d.readUByte().toString(16)),3!==T&&5!==T&&7!==T&&9!==T||b.push("-");for(var E=f-16,S=new Uint8Array(E),L=0;L=0){var h={data:t.subarray(u,l-n-1),type:d};o.push(h)}else{var f=this.getLastNalUnit();if(f&&(s&&l<=4-s&&f.state&&(f.data=f.data.subarray(0,f.data.byteLength-s)),(r=l-n-1)>0)){var c=new Uint8Array(f.data.byteLength+r);c.set(f.data,0),c.set(t.subarray(0,r),f.data.byteLength),f.data=c}}l=0&&n>=0){var v={data:t.subarray(u,i),type:d,state:n};o.push(v)}if(0===o.length){var g=this.getLastNalUnit();if(g){var p=new Uint8Array(g.data.byteLength+t.byteLength);p.set(g.data,0),p.set(t,g.data.byteLength),g.data=p}}return a.naluState=n,o},e.parseAACPES=function(t){var e,r,n,s,o,l=0,u=this._audioTrack,d=this.aacOverFlow,h=t.data;if(d){this.aacOverFlow=null;var f=d.sample.unit.byteLength,v=Math.min(d.missing,f),g=f-v;d.sample.unit.set(h.subarray(0,v),g),u.samples.push(d.sample),l=d.missing}for(e=l,r=h.length;e1;){var l=new Uint8Array(o[0].length+o[1].length);l.set(o[0]),l.set(o[1],o[0].length),o[0]=l,o.splice(1,1)}if(1===((e=o[0])[0]<<16)+(e[1]<<8)+e[2]){if((r=(e[4]<<8)+e[5])&&r>t.size-6)return null;var u=e[7];192&u&&(a=536870912*(14&e[9])+4194304*(255&e[10])+16384*(254&e[11])+128*(255&e[12])+(254&e[13])/2,64&u?a-(n=536870912*(14&e[14])+4194304*(255&e[15])+16384*(254&e[16])+128*(255&e[17])+(254&e[18])/2)>54e5&&(c.b.warn(Math.round((a-n)/9e4)+"s delta between PTS and DTS, align them"),a=n):n=a);var d=(i=e[8])+9;if(t.size<=d)return null;t.size-=d;for(var h=new Uint8Array(t.size),f=0,v=o.length;fg){d-=g;continue}e=e.subarray(d),g-=d,d=0}h.set(e,s),s+=g}return r&&(r-=i+3),{data:h,pts:a,dts:n,len:r}}return null}function W(t,e){if(t.units.length&&t.frame){if(void 0===t.pts){var r=e.samples,i=r.length;if(!i)return void e.dropped++;var a=r[i-1];t.pts=a.pts,t.dts=a.dts}e.samples.push(t)}t.debug.length&&c.b.log(t.pts+"/"+t.dts+":"+t.debug)}function Y(t,e){var r=t.length;if(r>0){if(e.pts>=t[r-1].pts)t.push(e);else for(var i=r-1;i>=0;i--)if(e.pts1?r-1:0),a=1;a>24&255,o[1]=e>>16&255,o[2]=e>>8&255,o[3]=255&e,o.set(t,4),n=0,e=8;n>24&255,e>>16&255,e>>8&255,255&e,i>>24,i>>16&255,i>>8&255,255&i,a>>24,a>>16&255,a>>8&255,255&a,85,196,0,0]))},t.mdia=function(e){return t.box(t.types.mdia,t.mdhd(e.timescale,e.duration),t.hdlr(e.type),t.minf(e))},t.mfhd=function(e){return t.box(t.types.mfhd,new Uint8Array([0,0,0,0,e>>24,e>>16&255,e>>8&255,255&e]))},t.minf=function(e){return"audio"===e.type?t.box(t.types.minf,t.box(t.types.smhd,t.SMHD),t.DINF,t.stbl(e)):t.box(t.types.minf,t.box(t.types.vmhd,t.VMHD),t.DINF,t.stbl(e))},t.moof=function(e,r,i){return t.box(t.types.moof,t.mfhd(e),t.traf(i,r))},t.moov=function(e){for(var r=e.length,i=[];r--;)i[r]=t.trak(e[r]);return t.box.apply(null,[t.types.moov,t.mvhd(e[0].timescale,e[0].duration)].concat(i).concat(t.mvex(e)))},t.mvex=function(e){for(var r=e.length,i=[];r--;)i[r]=t.trex(e[r]);return t.box.apply(null,[t.types.mvex].concat(i))},t.mvhd=function(e,r){r*=e;var i=Math.floor(r/(J+1)),a=Math.floor(r%(J+1)),n=new Uint8Array([1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,3,e>>24&255,e>>16&255,e>>8&255,255&e,i>>24,i>>16&255,i>>8&255,255&i,a>>24,a>>16&255,a>>8&255,255&a,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255]);return t.box(t.types.mvhd,n)},t.sdtp=function(e){var r,i,a=e.samples||[],n=new Uint8Array(4+a.length);for(r=0;r>>8&255),n.push(255&a),n=n.concat(Array.prototype.slice.call(i));for(r=0;r>>8&255),s.push(255&a),s=s.concat(Array.prototype.slice.call(i));var o=t.box(t.types.avcC,new Uint8Array([1,n[3],n[4],n[5],255,224|e.sps.length].concat(n).concat([e.pps.length]).concat(s))),l=e.width,u=e.height,d=e.pixelRatio[0],h=e.pixelRatio[1];return t.box(t.types.avc1,new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,l>>8&255,255&l,u>>8&255,255&u,0,72,0,0,0,72,0,0,0,0,0,0,0,1,18,100,97,105,108,121,109,111,116,105,111,110,47,104,108,115,46,106,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,17,17]),o,t.box(t.types.btrt,new Uint8Array([0,28,156,128,0,45,198,192,0,45,198,192])),t.box(t.types.pasp,new Uint8Array([d>>24,d>>16&255,d>>8&255,255&d,h>>24,h>>16&255,h>>8&255,255&h])))},t.esds=function(t){var e=t.config.length;return new Uint8Array([0,0,0,0,3,23+e,0,1,0,4,15+e,64,21,0,0,0,0,0,0,0,0,0,0,0,5].concat([e]).concat(t.config).concat([6,1,2]))},t.mp4a=function(e){var r=e.samplerate;return t.box(t.types.mp4a,new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,e.channelCount,0,16,0,0,0,0,r>>8&255,255&r,0,0]),t.box(t.types.esds,t.esds(e)))},t.mp3=function(e){var r=e.samplerate;return t.box(t.types[".mp3"],new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,e.channelCount,0,16,0,0,0,0,r>>8&255,255&r,0,0]))},t.stsd=function(e){return"audio"===e.type?e.isAAC||"mp3"!==e.codec?t.box(t.types.stsd,t.STSD,t.mp4a(e)):t.box(t.types.stsd,t.STSD,t.mp3(e)):t.box(t.types.stsd,t.STSD,t.avc1(e))},t.tkhd=function(e){var r=e.id,i=e.duration*e.timescale,a=e.width,n=e.height,s=Math.floor(i/(J+1)),o=Math.floor(i%(J+1));return t.box(t.types.tkhd,new Uint8Array([1,0,0,7,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,3,r>>24&255,r>>16&255,r>>8&255,255&r,0,0,0,0,s>>24,s>>16&255,s>>8&255,255&s,o>>24,o>>16&255,o>>8&255,255&o,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,a>>8&255,255&a,0,0,n>>8&255,255&n,0,0]))},t.traf=function(e,r){var i=t.sdtp(e),a=e.id,n=Math.floor(r/(J+1)),s=Math.floor(r%(J+1));return t.box(t.types.traf,t.box(t.types.tfhd,new Uint8Array([0,0,0,0,a>>24,a>>16&255,a>>8&255,255&a])),t.box(t.types.tfdt,new Uint8Array([1,0,0,0,n>>24,n>>16&255,n>>8&255,255&n,s>>24,s>>16&255,s>>8&255,255&s])),t.trun(e,i.length+16+20+8+16+8+8),i)},t.trak=function(e){return e.duration=e.duration||4294967295,t.box(t.types.trak,t.tkhd(e),t.mdia(e))},t.trex=function(e){var r=e.id;return t.box(t.types.trex,new Uint8Array([0,0,0,0,r>>24,r>>16&255,r>>8&255,255&r,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1]))},t.trun=function(e,r){var i,a,n,s,o,l,u=e.samples||[],d=u.length,h=12+16*d,f=new Uint8Array(h);for(r+=8+h,f.set([0,0,15,1,d>>>24&255,d>>>16&255,d>>>8&255,255&d,r>>>24&255,r>>>16&255,r>>>8&255,255&r],0),i=0;i>>24&255,n>>>16&255,n>>>8&255,255&n,s>>>24&255,s>>>16&255,s>>>8&255,255&s,o.isLeading<<2|o.dependsOn,o.isDependedOn<<6|o.hasRedundancy<<4|o.paddingValue<<1|o.isNonSync,61440&o.degradPrio,15&o.degradPrio,l>>>24&255,l>>>16&255,l>>>8&255,255&l],12+16*i);return t.box(t.types.trun,f)},t.initSegment=function(e){t.types||t.init();var r=t.moov(e),i=new Uint8Array(t.FTYP.byteLength+r.byteLength);return i.set(t.FTYP),i.set(r,t.FTYP.byteLength),i},t}();tt.types=void 0,tt.HDLR_TYPES=void 0,tt.STTS=void 0,tt.STSC=void 0,tt.STCO=void 0,tt.STSZ=void 0,tt.VMHD=void 0,tt.SMHD=void 0,tt.STSD=void 0,tt.FTYP=void 0,tt.DINF=void 0;var et=tt,rt=r(4);function it(t,e,r,i){void 0===r&&(r=1),void 0===i&&(i=!1);var a=t*e*r;return i?Math.round(a):a}function at(t,e){return void 0===e&&(e=!1),it(t,1e3,1/9e4,e)}function nt(){return(nt=Object.assign||function(t){for(var e=1;e0?t:r.pts}),t[0].pts);return e&&c.b.debug("PTS rollover detected"),r},e.remux=function(t,e,r,i,a,n,s,o){var l,u,d,h,f,v,g=a,p=a,m=t.pid>-1,y=e.pid>-1,b=e.samples.length,T=t.samples.length>0,E=b>1;if((!m||T)&&(!y||E)||this.ISGenerated||s){this.ISGenerated||(d=this.generateIS(t,e,a));var S=this.isVideoContiguous,L=-1;if(E&&(L=function(t){for(var e=0;e0){c.b.warn("[mp4-remuxer]: Dropped "+L+" out of "+b+" video samples due to a missing keyframe");var A=this.getVideoStartPts(e.samples);e.samples=e.samples.slice(L),e.dropped+=L,p+=(e.samples[0].pts-A)/(e.timescale||9e4)}else-1===L&&(c.b.warn("[mp4-remuxer]: No keyframe found out of "+b+" video samples"),v=!1);if(this.ISGenerated){if(T&&E){var R=this.getVideoStartPts(e.samples),D=(dt(t.samples[0].pts,R)-R)/e.inputTimeScale;g+=Math.max(0,D),p+=Math.max(0,-D)}if(T){if(t.samplerate||(c.b.warn("[mp4-remuxer]: regenerate InitSegment as audio detected"),d=this.generateIS(t,e,a)),u=this.remuxAudio(t,g,this.isAudioContiguous,n,y||E||o===rt.b.AUDIO?p:void 0),E){var _=u?u.endPTS-u.startPTS:0;e.inputTimeScale||(c.b.warn("[mp4-remuxer]: regenerate InitSegment as video detected"),d=this.generateIS(t,e,a)),l=this.remuxVideo(e,p,S,_)}}else E&&(l=this.remuxVideo(e,p,S,0));l&&(l.firstKeyFrame=L,l.independent=-1!==L)}}return this.ISGenerated&&(r.samples.length&&(f=this.remuxID3(r,a)),i.samples.length&&(h=this.remuxText(i,a))),{audio:u,video:l,initSegment:d,independent:v,text:h,id3:f}},e.generateIS=function(t,e,r){var i,a,n,o=t.samples,l=e.samples,u=this.typeSupported,d={},h=!Object(s.a)(this._initPTS),f="audio/mp4";if(h&&(i=a=1/0),t.config&&o.length&&(t.timescale=t.samplerate,t.isAAC||(u.mpeg?(f="audio/mpeg",t.codec=""):u.mp3&&(t.codec="mp3")),d.audio={id:"audio",container:f,codec:t.codec,initSegment:!t.isAAC&&u.mpeg?new Uint8Array(0):et.initSegment([t]),metadata:{channelCount:t.channelCount}},h&&(n=t.inputTimeScale,i=a=o[0].pts-Math.round(n*r))),e.sps&&e.pps&&l.length&&(e.timescale=e.inputTimeScale,d.video={id:"main",container:"video/mp4",codec:e.codec,initSegment:et.initSegment([e]),metadata:{width:e.width,height:e.height}},h)){n=e.inputTimeScale;var c=this.getVideoStartPts(l),v=Math.round(n*r);a=Math.min(a,dt(l[0].dts,c)-v),i=Math.min(i,c-v)}if(Object.keys(d).length)return this.ISGenerated=!0,h&&(this._initPTS=i,this._initDTS=a),{tracks:d,initPTS:i,timescale:n}},e.remuxVideo=function(t,e,r,n){var s,o,l,u=t.inputTimeScale,d=t.samples,h=[],f=d.length,v=this._initPTS,g=this.nextAvcDts,p=8,m=Number.POSITIVE_INFINITY,y=Number.NEGATIVE_INFINITY,b=0,T=!1;r&&null!==g||(g=e*u-(d[0].pts-dt(d[0].dts,d[0].pts)));for(var E=0;ES.pts){b=Math.max(Math.min(b,S.pts-S.dts),-18e3)}S.dts0?E-1:E].dts&&(T=!0)}T&&d.sort((function(t,e){var r=t.dts-e.dts,i=t.pts-e.pts;return r||i})),o=d[0].dts,l=d[d.length-1].dts;var L=Math.round((l-o)/(f-1));if(b<0){if(b<-2*L){c.b.warn("PTS < DTS detected in video samples, offsetting DTS from PTS by "+at(-L,!0)+" ms");for(var A=b,R=0;RL;if(k||_<-1){k?c.b.warn("AVC: "+at(_,!0)+" ms ("+_+"dts) hole between fragments detected, filling it"):c.b.warn("AVC: "+at(-_,!0)+" ms ("+_+"dts) overlapping between fragments detected"),o=g;var x=d[0].pts-_;d[0].dts=o,d[0].pts=x,c.b.log("Video: First PTS/DTS adjusted: "+at(x,!0)+"/"+at(o,!0)+", delta: "+at(_,!0)+" ms")}}lt&&(o=Math.max(0,o));for(var w=0,C=0,O=0;O0?j-1:j].dts;if(Q.stretchShortVideoTrack&&null!==this.nextAudioPts){var Z=Math.floor(Q.maxBufferHole*u),J=(n?m+n*u:this.nextAudioPts)-K.pts;J>Z?((s=J-$)<0&&(s=$),c.b.log("[mp4-remuxer]: It is approximately "+J/90+" ms to the next segment; using duration "+s/90+" ms for the last video frame.")):s=$}else s=$}var tt=Math.round(K.pts-K.dts);h.push(new ht(K.key,s,V,tt))}if(h.length&&st&&st<70){var rt=h[0].flags;rt.dependsOn=2,rt.isNonSync=0}this.nextAvcDts=g=l+s,this.isVideoContiguous=!0;var it={data1:et.moof(t.sequenceNumber++,o,nt({},t,{samples:h})),data2:B,startPTS:m/u,endPTS:(y+s)/u,startDTS:o/u,endDTS:g/u,type:"video",hasAudio:!1,hasVideo:!0,nb:h.length,dropped:t.dropped};return t.samples=[],t.dropped=0,it},e.remuxAudio=function(t,e,r,n,s){var o=t.inputTimeScale,l=o/(t.samplerate?t.samplerate:o),u=t.isAAC?1024:1152,d=u*l,h=this._initPTS,f=!t.isAAC&&this.typeSupported.mpeg,v=[],g=t.samples,p=f?0:8,m=this.nextAudioPts||-1,y=e*o;if(this.isAudioContiguous=r=r||g.length&&m>0&&(n&&Math.abs(y-m)<9e3||Math.abs(dt(g[0].pts-h,y)-m)<20*d),g.forEach((function(t){t.pts=dt(t.pts-h,y)})),!r||m<0){if(!(g=g.filter((function(t){return t.pts>=0}))).length)return;m=0===s?0:n?Math.max(0,y):g[0].pts}if(t.isAAC)for(var b=void 0!==s,T=this.config.maxAudioFramesDrift,E=0,S=m;E=T*d&&D<1e4&&b){var _=Math.round(R/d);(S=A-_*d)<0&&(_--,S+=d),0===E&&(this.nextAudioPts=m=S),c.b.warn("[mp4-remuxer]: Injecting "+_+" audio frame @ "+(S/o).toFixed(3)+"s due to "+Math.round(1e3*R/o)+" ms gap.");for(var k=0;k<_;k++){var x=Math.max(S,0),w=Z.getSilentFrame(t.manifestCodec||t.codec,t.channelCount);w||(c.b.log("[mp4-remuxer]: Unable to get silent frame for given audio codec; duplicating last frame instead."),w=L.unit.subarray()),g.splice(E,0,{unit:w,pts:x}),S+=d,E++}}L.pts=S,S+=d}for(var C,O=null,I=null,P=0,F=g.length;F--;)P+=g[F].unit.byteLength;for(var M=0,N=g.length;M0))return;P+=p;try{C=new Uint8Array(P)}catch(t){return void this.observer.emit(i.a.ERROR,i.a.ERROR,{type:a.b.MUX_ERROR,details:a.a.REMUX_ALLOC_ERROR,fatal:!1,bytes:P,reason:"fail allocating audio mdat "+P})}f||(new DataView(C.buffer).setUint32(0,P),C.set(et.types.mdat,4))}C.set(U,p);var j=U.byteLength;p+=j,v.push(new ht(!0,u,j,0)),I=G}var K=v.length;if(K){var H=v[v.length-1];this.nextAudioPts=m=I+l*H.duration;var V=f?new Uint8Array(0):et.moof(t.sequenceNumber++,O/l,nt({},t,{samples:v}));t.samples=[];var W=O/o,Y=m/o,X={data1:V,data2:C,startPTS:W,endPTS:Y,startDTS:W,endDTS:Y,type:"audio",hasAudio:!0,hasVideo:!1,nb:K};return this.isAudioContiguous=!0,X}},e.remuxEmptyAudio=function(t,e,r,i){var a=t.inputTimeScale,n=a/(t.samplerate?t.samplerate:a),s=this.nextAudioPts,o=(null!==s?s:i.startDTS*a)+this._initDTS,l=i.endDTS*a+this._initDTS,u=1024*n,d=Math.ceil((l-o)/u),h=Z.getSilentFrame(t.manifestCodec||t.codec,t.channelCount);if(c.b.warn("[mp4-remuxer]: remux empty Audio"),h){for(var f=[],v=0;v4294967296;)t+=r;return t}var ht=function(t,e,r,i){this.size=void 0,this.duration=void 0,this.cts=void 0,this.flags=void 0,this.duration=e,this.size=r,this.cts=i,this.flags=new ft(t)},ft=function(t){this.isLeading=0,this.isDependedOn=0,this.hasRedundancy=0,this.degradPrio=0,this.dependsOn=1,this.isNonSync=1,this.dependsOn=t?2:1,this.isNonSync=t?0:1},ct=r(5),vt=function(){function t(){this.emitInitSegment=!1,this.audioCodec=void 0,this.videoCodec=void 0,this.initData=void 0,this.initPTS=void 0,this.initTracks=void 0,this.lastEndDTS=null}var e=t.prototype;return e.destroy=function(){},e.resetTimeStamp=function(t){this.initPTS=t,this.lastEndDTS=null},e.resetNextTimestamp=function(){this.lastEndDTS=null},e.resetInitSegment=function(t,e,r){this.audioCodec=e,this.videoCodec=r,this.generateInitSegment(t),this.emitInitSegment=!0},e.generateInitSegment=function(t){var e=this.audioCodec,r=this.videoCodec;if(!t||!t.byteLength)return this.initTracks=void 0,void(this.initData=void 0);var i=this.initData=Object(l.f)(t);e||(e=pt(i.audio,ct.a.AUDIO)),r||(r=pt(i.video,ct.a.VIDEO));var a={};i.audio&&i.video?a.audiovideo={container:"video/mp4",codec:e+","+r,initSegment:t,id:"main"}:i.audio?a.audio={container:"audio/mp4",codec:e,initSegment:t,id:"audio"}:i.video?a.video={container:"video/mp4",codec:r,initSegment:t,id:"main"}:c.b.warn("[passthrough-remuxer.ts]: initSegment does not contain moov or trak boxes."),this.initTracks=a},e.remux=function(t,e,r,i,a){var n=this.initPTS,o=this.lastEndDTS,u={audio:void 0,video:void 0,text:i,id3:r,initSegment:void 0};Object(s.a)(o)||(o=this.lastEndDTS=a||0);var d=e.samples;if(!d||!d.length)return u;var h={initPTS:void 0,timescale:1},f=this.initData;if(f&&f.length||(this.generateInitSegment(d),f=this.initData),!f||!f.length)return c.b.warn("[passthrough-remuxer.ts]: Failed to generate initSegment."),u;this.emitInitSegment&&(h.tracks=this.initTracks,this.emitInitSegment=!1),Object(s.a)(n)||(this.initPTS=h.initPTS=n=gt(f,d,o));var v=Object(l.c)(d,f),g=o,p=v+g;Object(l.e)(f,d,n),v>0?this.lastEndDTS=p:(c.b.warn("Duration parsed from mp4 should be greater than zero"),this.resetNextTimestamp());var m=!!f.audio,y=!!f.video,b="";m&&(b+="audio"),y&&(b+="video");var T={data1:d,startPTS:g,startDTS:g,endPTS:p,endDTS:p,type:b,hasAudio:m,hasVideo:y,nb:1,dropped:0};return u.audio="audio"===T.type?T:void 0,u.video="audio"!==T.type?T:void 0,u.text=i,u.id3=r,u.initSegment=h,u},t}(),gt=function(t,e,r){return Object(l.d)(t,e)-r};function pt(t,e){var r=null==t?void 0:t.codec;return r&&r.length>4?r:"hvc1"===r?"hvc1.1.c.L120.90":"av01"===r?"av01.0.04M.08":"avc1"===r||e===ct.a.VIDEO?"avc1.42e01e":"mp4a.40.5"}var mt,yt=vt,bt=r(16);try{mt=self.performance.now.bind(self.performance)}catch(t){c.b.debug("Unable to use Performance API on this environment"),mt=self.Date.now}var Tt=[{demux:q,remux:ut},{demux:D,remux:yt},{demux:A,remux:ut},{demux:$,remux:ut}],Et=1024;Tt.forEach((function(t){var e=t.demux;Et=Math.max(Et,e.minProbeByteLength)}));var St=function(){function t(t,e,r,i,a){this.observer=void 0,this.typeSupported=void 0,this.config=void 0,this.vendor=void 0,this.id=void 0,this.demuxer=void 0,this.remuxer=void 0,this.decrypter=void 0,this.probe=void 0,this.decryptionPromise=null,this.transmuxConfig=void 0,this.currentTransmuxState=void 0,this.cache=new bt.a,this.observer=t,this.typeSupported=e,this.config=r,this.vendor=i,this.id=a}var e=t.prototype;return e.configure=function(t){this.transmuxConfig=t,this.decrypter&&this.decrypter.reset()},e.push=function(t,e,r,i){var a=this,n=r.transmuxing;n.executeStart=mt();var s=new Uint8Array(t),o=this.cache,u=this.config,d=this.currentTransmuxState,h=this.transmuxConfig;i&&(this.currentTransmuxState=i);var f=function(t,e){var r=null;t.byteLength>0&&null!=e&&null!=e.key&&null!==e.iv&&null!=e.method&&(r=e);return r}(s,e);if(f&&"AES-128"===f.method){var c=this.getDecrypter();if(!u.enableSoftwareAES)return this.decryptionPromise=c.webCryptoDecrypt(s,f.key.buffer,f.iv.buffer).then((function(t){var e=a.push(t,null,r);return a.decryptionPromise=null,e})),this.decryptionPromise;var v=c.softwareDecrypt(s,f.key.buffer,f.iv.buffer);if(!v)return n.executeEnd=mt(),Lt(r);s=new Uint8Array(v)}var g=i||d,p=g.contiguous,m=g.discontinuity,y=g.trackSwitch,b=g.accurateTimeOffset,T=g.timeOffset,E=h.audioCodec,S=h.videoCodec,L=h.defaultInitPts,A=h.duration,R=h.initSegmentData;if((m||y)&&this.resetInitSegment(R,E,S,A),m&&this.resetInitialTimestamp(L),p||this.resetContiguity(),this.needsProbing(s,m,y)){if(o.dataLength){var D=o.flush();s=Object(l.a)(D,s)}this.configureTransmuxer(s,h)}var _=this.transmux(s,f,T,b,r),k=this.currentTransmuxState;return k.contiguous=!0,k.discontinuity=!1,k.trackSwitch=!1,n.executeEnd=mt(),_},e.flush=function(t){var e=this,r=t.transmuxing;r.executeStart=mt();var n=this.decrypter,s=this.cache,o=this.currentTransmuxState,l=this.decryptionPromise;if(l)return l.then((function(){return e.flush(t)}));var u=[],d=o.timeOffset;if(n){var h=n.flush();h&&u.push(this.push(h,null,t))}var f=s.dataLength;s.reset();var c=this.demuxer,v=this.remuxer;if(!c||!v)return f>=Et&&this.observer.emit(i.a.ERROR,i.a.ERROR,{type:a.b.MEDIA_ERROR,details:a.a.FRAG_PARSING_ERROR,fatal:!0,reason:"no demux matching with content found"}),r.executeEnd=mt(),[Lt(t)];var g=c.flush(d);return At(g)?g.then((function(r){return e.flushRemux(u,r,t),u})):(this.flushRemux(u,g,t),u)},e.flushRemux=function(t,e,r){var i=e.audioTrack,a=e.avcTrack,n=e.id3Track,s=e.textTrack,o=this.currentTransmuxState,l=o.accurateTimeOffset,u=o.timeOffset;c.b.log("[transmuxer.ts]: Flushed fragment "+r.sn+(r.part>-1?" p: "+r.part:"")+" of level "+r.level);var d=this.remuxer.remux(i,a,n,s,u,l,!0,this.id);t.push({remuxResult:d,chunkMeta:r}),r.transmuxing.executeEnd=mt()},e.resetInitialTimestamp=function(t){var e=this.demuxer,r=this.remuxer;e&&r&&(e.resetTimeStamp(t),r.resetTimeStamp(t))},e.resetContiguity=function(){var t=this.demuxer,e=this.remuxer;t&&e&&(t.resetContiguity(),e.resetNextTimestamp())},e.resetInitSegment=function(t,e,r,i){var a=this.demuxer,n=this.remuxer;a&&n&&(a.resetInitSegment(e,r,i),n.resetInitSegment(t,e,r))},e.destroy=function(){this.demuxer&&(this.demuxer.destroy(),this.demuxer=void 0),this.remuxer&&(this.remuxer.destroy(),this.remuxer=void 0)},e.transmux=function(t,e,r,i,a){return e&&"SAMPLE-AES"===e.method?this.transmuxSampleAes(t,e,r,i,a):this.transmuxUnencrypted(t,r,i,a)},e.transmuxUnencrypted=function(t,e,r,i){var a=this.demuxer.demux(t,e,!1,!this.config.progressive),n=a.audioTrack,s=a.avcTrack,o=a.id3Track,l=a.textTrack;return{remuxResult:this.remuxer.remux(n,s,o,l,e,r,!1,this.id),chunkMeta:i}},e.transmuxSampleAes=function(t,e,r,i,a){var n=this;return this.demuxer.demuxSampleAes(t,e,r).then((function(t){return{remuxResult:n.remuxer.remux(t.audioTrack,t.avcTrack,t.id3Track,t.textTrack,r,i,!1,n.id),chunkMeta:a}}))},e.configureTransmuxer=function(t,e){for(var r,i=this.config,a=this.observer,n=this.typeSupported,s=this.vendor,o=e.audioCodec,l=e.defaultInitPts,u=e.duration,d=e.initSegmentData,h=e.videoCodec,f=0,v=Tt.length;f>>8^255&p^99,t[c]=p,e[p]=c;var m=f[c],y=f[m],b=f[y],T=257*f[p]^16843008*p;i[c]=T<<24|T>>>8,a[c]=T<<16|T>>>16,n[c]=T<<8|T>>>24,s[c]=T,T=16843009*b^65537*y^257*m^16843008*c,l[p]=T<<24|T>>>8,u[p]=T<<16|T>>>16,d[p]=T<<8|T>>>24,h[p]=T,c?(c=m^f[f[f[b^m]]],v^=f[f[v]]):c=v=1}},e.expandKey=function(t){for(var e=this.uint8ArrayToUint32Array_(t),r=!0,i=0;i0}),!1)}t.exports=function(t,e){e=e||{};var a={main:r.m},o=e.all?{main:Object.keys(a.main)}:function(t,e){for(var r={main:[e]},i={main:[]},a={main:{}};s(r);)for(var o=Object.keys(r),l=0;lt.endSN||e>0||0===e&&r>0,this.updated||this.advanced?this.misses=Math.floor(.6*t.misses):this.misses=t.misses+1,this.availabilityDelay=t.availabilityDelay},e=t,(r=[{key:"hasProgramDateTime",get:function(){return!!this.fragments.length&&Object(a.a)(this.fragments[this.fragments.length-1].programDateTime)}},{key:"levelTargetDuration",get:function(){return this.averagetargetduration||this.targetduration||10}},{key:"drift",get:function(){var t=this.driftEndTime-this.driftStartTime;return t>0?1e3*(this.driftEnd-this.driftStart)/t:1}},{key:"edge",get:function(){return this.partEnd||this.fragmentEnd}},{key:"partEnd",get:function(){var t;return null!==(t=this.partList)&&void 0!==t&&t.length?this.partList[this.partList.length-1].end:this.fragmentEnd}},{key:"fragmentEnd",get:function(){var t;return null!==(t=this.fragments)&&void 0!==t&&t.length?this.fragments[this.fragments.length-1].end:0}},{key:"age",get:function(){return this.advancedDateTime?Math.max(Date.now()-this.advancedDateTime,0)/1e3:0}},{key:"lastPartIndex",get:function(){var t;return null!==(t=this.partList)&&void 0!==t&&t.length?this.partList[this.partList.length-1].index:-1}},{key:"lastPartSn",get:function(){var t;return null!==(t=this.partList)&&void 0!==t&&t.length?this.partList[this.partList.length-1].fragment.sn:this.endSN}}])&&d(e.prototype,r),i&&d(e,i),t}(),f=r(15),c=/^(\d+)x(\d+)$/,v=/\s*(.+?)\s*=((?:\".*?\")|.*?)(?:,|$)/g,g=function(){function t(e){for(var r in"string"==typeof e&&(e=t.parseAttrList(e)),e)e.hasOwnProperty(r)&&(this[r]=e[r])}var e=t.prototype;return e.decimalInteger=function(t){var e=parseInt(this[t],10);return e>Number.MAX_SAFE_INTEGER?1/0:e},e.hexadecimalInteger=function(t){if(this[t]){var e=(this[t]||"0x").slice(2);e=(1&e.length?"0":"")+e;for(var r=new Uint8Array(e.length/2),i=0;iNumber.MAX_SAFE_INTEGER?1/0:e},e.decimalFloatingPoint=function(t){return parseFloat(this[t])},e.optionalFloat=function(t,e){var r=this[t];return r?parseFloat(r):e},e.enumeratedString=function(t){return this[t]},e.bool=function(t){return"YES"===this[t]},e.decimalResolution=function(t){var e=c.exec(this[t]);if(null!==e)return{width:parseInt(e[1],10),height:parseInt(e[2],10)}},t.parseAttrList=function(t){var e,r={};for(v.lastIndex=0;null!==(e=v.exec(t));){var i=e[2];0===i.indexOf('"')&&i.lastIndexOf('"')===i.length-1&&(i=i.slice(1,-1)),r[e[1]]=i}return r},t}(),p={audio:{a3ds:!0,"ac-3":!0,"ac-4":!0,alac:!0,alaw:!0,dra1:!0,"dts+":!0,"dts-":!0,dtsc:!0,dtse:!0,dtsh:!0,"ec-3":!0,enca:!0,g719:!0,g726:!0,m4ae:!0,mha1:!0,mha2:!0,mhm1:!0,mhm2:!0,mlpa:!0,mp4a:!0,"raw ":!0,Opus:!0,samr:!0,sawb:!0,sawp:!0,sevc:!0,sqcp:!0,ssmv:!0,twos:!0,ulaw:!0},video:{avc1:!0,avc2:!0,avc3:!0,avc4:!0,avcp:!0,av01:!0,drac:!0,dvav:!0,dvhe:!0,encv:!0,hev1:!0,hvc1:!0,mjp2:!0,mp4v:!0,mvc1:!0,mvc2:!0,mvc3:!0,mvc4:!0,resv:!0,rv60:!0,s263:!0,svc1:!0,svc2:!0,"vc-1":!0,vp08:!0,vp09:!0},text:{stpp:!0,wvtt:!0}};function m(t,e){return MediaSource.isTypeSupported((e||"video")+'/mp4;codecs="'+t+'"')}var y=/#EXT-X-STREAM-INF:([^\r\n]*)(?:[\r\n](?:#[^\r\n]*)?)*([^\r\n]+)|#EXT-X-SESSION-DATA:([^\r\n]*)[\r\n]+/g,b=/#EXT-X-MEDIA:(.*)/g,T=new RegExp([/#EXTINF:\s*(\d*(?:\.\d+)?)(?:,(.*)\s+)?/.source,/(?!#) *(\S[\S ]*)/.source,/#EXT-X-BYTERANGE:*(.+)/.source,/#EXT-X-PROGRAM-DATE-TIME:(.+)/.source,/#.*/.source].join("|"),"g"),E=new RegExp([/#(EXTM3U)/.source,/#EXT-X-(PLAYLIST-TYPE):(.+)/.source,/#EXT-X-(MEDIA-SEQUENCE): *(\d+)/.source,/#EXT-X-(SKIP):(.+)/.source,/#EXT-X-(TARGETDURATION): *(\d+)/.source,/#EXT-X-(KEY):(.+)/.source,/#EXT-X-(START):(.+)/.source,/#EXT-X-(ENDLIST)/.source,/#EXT-X-(DISCONTINUITY-SEQ)UENCE: *(\d+)/.source,/#EXT-X-(DIS)CONTINUITY/.source,/#EXT-X-(VERSION):(\d+)/.source,/#EXT-X-(MAP):(.+)/.source,/#EXT-X-(SERVER-CONTROL):(.+)/.source,/#EXT-X-(PART-INF):(.+)/.source,/#EXT-X-(GAP)/.source,/#EXT-X-(BITRATE):\s*(\d+)/.source,/#EXT-X-(PART):(.+)/.source,/#EXT-X-(PRELOAD-HINT):(.+)/.source,/#EXT-X-(RENDITION-REPORT):(.+)/.source,/(#)([^:]*):(.*)/.source,/(#)(.*)(?:.*)\r?\n?/.source].join("|")),S=/\.(mp4|m4s|m4v|m4a)$/i;var L=function(){function t(){}return t.findGroup=function(t,e){for(var r=0;r2){var r=e.shift()+".";return r+=parseInt(e.shift()).toString(16),r+=("000"+parseInt(e.shift()).toString(16)).substr(-4)}return t},t.resolve=function(t,e){return i.buildAbsoluteURL(e,t,{alwaysNormalize:!0})},t.parseMasterPlaylist=function(e,r){var i,a=[],n={},s=!1;for(y.lastIndex=0;null!=(i=y.exec(e));)if(i[1]){var o=new g(i[1]),l={attrs:o,bitrate:o.decimalInteger("AVERAGE-BANDWIDTH")||o.decimalInteger("BANDWIDTH"),name:o.NAME,url:t.resolve(i[2],r)},u=o.decimalResolution("RESOLUTION");u&&(l.width=u.width,l.height=u.height),A((o.CODECS||"").split(/[ ,]+/).filter((function(t){return t})),l),l.videoCodec&&-1!==l.videoCodec.indexOf("avc1")&&(l.videoCodec=t.convertAVC1ToAVCOTI(l.videoCodec)),a.push(l)}else if(i[3]){var d=new g(i[3]);d["DATA-ID"]&&(s=!0,n[d["DATA-ID"]]=d)}return{levels:a,sessionData:s?n:null}},t.parseMasterPlaylistMedia=function(e,r,i,a){var n;void 0===a&&(a=[]);var s=[],o=0;for(b.lastIndex=0;null!==(n=b.exec(e));){var l=new g(n[1]);if(l.TYPE===i){var u={attrs:l,bitrate:0,id:o++,groupId:l["GROUP-ID"],instreamId:l["INSTREAM-ID"],name:l.NAME||l.LANGUAGE||"",type:i,default:l.bool("DEFAULT"),autoselect:l.bool("AUTOSELECT"),forced:l.bool("FORCED"),lang:l.LANGUAGE,url:l.URI?t.resolve(l.URI,r):""};if(a.length){var d=t.findGroup(a,u.groupId)||a[0];R(u,d,"audioCodec"),R(u,d,"textCodec")}s.push(u)}}return s},t.parseLevelPlaylist=function(t,e,r,n,s){var l,d,c,v=new h(e),p=v.fragments,m=null,y=0,b=0,L=0,A=0,R=null,_=new u.b(n,e),k=-1,x=!1;for(T.lastIndex=0,v.m3u8=t;null!==(l=T.exec(t));){x&&(x=!1,(_=new u.b(n,e)).start=L,_.sn=y,_.cc=A,_.level=r,m&&(_.initSegment=m,_.rawProgramDateTime=m.rawProgramDateTime));var w=l[1];if(w){_.duration=parseFloat(w);var C=(" "+l[2]).slice(1);_.title=C||null,_.tagList.push(C?["INF",w,C]:["INF",w])}else if(l[3])Object(a.a)(_.duration)&&(_.start=L,c&&(_.levelkey=c),_.sn=y,_.level=r,_.cc=A,_.urlId=s,p.push(_),_.relurl=(" "+l[3]).slice(1),D(_,R),R=_,L+=_.duration,y++,b=0,x=!0);else if(l[4]){var O=(" "+l[4]).slice(1);R?_.setByteRange(O,R):_.setByteRange(O)}else if(l[5])_.rawProgramDateTime=(" "+l[5]).slice(1),_.tagList.push(["PROGRAM-DATE-TIME",_.rawProgramDateTime]),-1===k&&(k=p.length);else{if(!(l=l[0].match(E))){o.b.warn("No matches on slow regex match for level playlist!");continue}for(d=1;d-1){o.b.warn("Keyformat "+X+" is not supported from the manifest");continue}if("identity"!==X)continue;K&&(c=f.a.fromURL(e,H),H&&["AES-128","SAMPLE-AES","SAMPLE-AES-CENC"].indexOf(K)>=0&&(c.method=K,c.keyFormat=X,Y&&(c.keyID=Y),W&&(c.keyFormatVersions=W),c.iv=V));break;case"START":var q=new g(P).decimalFloatingPoint("TIME-OFFSET");Object(a.a)(q)&&(v.startTimeOffset=q);break;case"MAP":var z=new g(P);_.relurl=z.URI,z.BYTERANGE&&_.setByteRange(z.BYTERANGE),_.level=r,_.sn="initSegment",c&&(_.levelkey=c),_.initSegment=null,m=_,x=!0;break;case"SERVER-CONTROL":var Q=new g(P);v.canBlockReload=Q.bool("CAN-BLOCK-RELOAD"),v.canSkipUntil=Q.optionalFloat("CAN-SKIP-UNTIL",0),v.canSkipDateRanges=v.canSkipUntil>0&&Q.bool("CAN-SKIP-DATERANGES"),v.partHoldBack=Q.optionalFloat("PART-HOLD-BACK",0),v.holdBack=Q.optionalFloat("HOLD-BACK",0);break;case"PART-INF":var $=new g(P);v.partTarget=$.decimalFloatingPoint("PART-TARGET");break;case"PART":var Z=v.partList;Z||(Z=v.partList=[]);var J=b>0?Z[Z.length-1]:void 0,tt=b++,et=new u.c(new g(P),_,e,tt,J);Z.push(et),_.duration+=et.duration;break;case"PRELOAD-HINT":var rt=new g(P);v.preloadHint=rt;break;case"RENDITION-REPORT":var it=new g(P);v.renditionReports=v.renditionReports||[],v.renditionReports.push(it);break;default:o.b.warn("line parsed but not handled: "+l)}}}R&&!R.relurl?(p.pop(),L-=R.duration,v.partList&&(v.fragmentHint=R)):v.partList&&(D(_,R),_.cc=A,v.fragmentHint=_);var at=p.length,nt=p[0],st=p[at-1];if((L+=v.skippedSegments*v.targetduration)>0&&at&&st){v.averagetargetduration=L/at;var ot=st.sn;v.endSN="initSegment"!==ot?ot:0,nt&&(v.startCC=nt.cc,nt.initSegment||v.fragments.every((function(t){return t.relurl&&(e=t.relurl,S.test(null!=(r=null===(a=i.parseURL(e))||void 0===a?void 0:a.path)?r:""));var e,r,a}))&&(o.b.warn("MP4 fragments found but no init segment (probably no MAP, incomplete M3U8), trying to fetch SIDX"),(_=new u.b(n,e)).relurl=st.relurl,_.level=r,_.sn="initSegment",nt.initSegment=_,v.needSidxRanges=!0))}else v.endSN=0,v.startCC=0;return v.fragmentHint&&(L+=v.fragmentHint.duration),v.totalduration=L,v.endCC=A,k>0&&function(t,e){for(var r=t[e],i=e;i--;){var a=t[i];if(!a)return;a.programDateTime=r.programDateTime-1e3*a.duration,r=a}}(p,k),v},t}();function A(t,e){["video","audio","text"].forEach((function(r){var i=t.filter((function(t){return function(t,e){var r=p[e];return!!r&&!0===r[t.slice(0,4)]}(t,r)}));if(i.length){var a=i.filter((function(t){return 0===t.lastIndexOf("avc1",0)||0===t.lastIndexOf("mp4a",0)}));e[r+"Codec"]=a.length>0?a[0]:i[0],t=t.filter((function(t){return-1===i.indexOf(t)}))}})),e.unknownCodecs=t}function R(t,e,r){var i=e[r];i&&(t[r]=i)}function D(t,e){t.rawProgramDateTime?t.programDateTime=Date.parse(t.rawProgramDateTime):null!=e&&e.programDateTime&&(t.programDateTime=e.endProgramDateTime),Object(a.a)(t.programDateTime)||(t.programDateTime=null,t.rawProgramDateTime=null)}var _=r(4);function k(t,e){var r=t.url;return void 0!==r&&0!==r.indexOf("data:")||(r=e.url),r}var x=function(){function t(t){this.hls=void 0,this.loaders=Object.create(null),this.hls=t,this.registerListeners()}var e=t.prototype;return e.registerListeners=function(){var t=this.hls;t.on(n.a.MANIFEST_LOADING,this.onManifestLoading,this),t.on(n.a.LEVEL_LOADING,this.onLevelLoading,this),t.on(n.a.AUDIO_TRACK_LOADING,this.onAudioTrackLoading,this),t.on(n.a.SUBTITLE_TRACK_LOADING,this.onSubtitleTrackLoading,this)},e.unregisterListeners=function(){var t=this.hls;t.off(n.a.MANIFEST_LOADING,this.onManifestLoading,this),t.off(n.a.LEVEL_LOADING,this.onLevelLoading,this),t.off(n.a.AUDIO_TRACK_LOADING,this.onAudioTrackLoading,this),t.off(n.a.SUBTITLE_TRACK_LOADING,this.onSubtitleTrackLoading,this)},e.createInternalLoader=function(t){var e=this.hls.config,r=e.pLoader,i=e.loader,a=new(r||i)(e);return t.loader=a,this.loaders[t.type]=a,a},e.getInternalLoader=function(t){return this.loaders[t.type]},e.resetInternalLoader=function(t){this.loaders[t]&&delete this.loaders[t]},e.destroyInternalLoaders=function(){for(var t in this.loaders){var e=this.loaders[t];e&&e.destroy(),this.resetInternalLoader(t)}},e.destroy=function(){this.unregisterListeners(),this.destroyInternalLoaders()},e.onManifestLoading=function(t,e){var r=e.url;this.load({id:null,groupId:null,level:0,responseType:"text",type:_.a.MANIFEST,url:r,deliveryDirectives:null})},e.onLevelLoading=function(t,e){var r=e.id,i=e.level,a=e.url,n=e.deliveryDirectives;this.load({id:r,groupId:null,level:i,responseType:"text",type:_.a.LEVEL,url:a,deliveryDirectives:n})},e.onAudioTrackLoading=function(t,e){var r=e.id,i=e.groupId,a=e.url,n=e.deliveryDirectives;this.load({id:r,groupId:i,level:null,responseType:"text",type:_.a.AUDIO_TRACK,url:a,deliveryDirectives:n})},e.onSubtitleTrackLoading=function(t,e){var r=e.id,i=e.groupId,a=e.url,n=e.deliveryDirectives;this.load({id:r,groupId:i,level:null,responseType:"text",type:_.a.SUBTITLE_TRACK,url:a,deliveryDirectives:n})},e.load=function(t){var e,r,i,a,n,s,l=this.hls.config,u=this.getInternalLoader(t);if(u){var d=u.context;if(d&&d.url===t.url)return void o.b.trace("[playlist-loader]: playlist request ongoing");o.b.log("[playlist-loader]: aborting previous loader for type: "+t.type),u.abort()}switch(t.type){case _.a.MANIFEST:r=l.manifestLoadingMaxRetry,i=l.manifestLoadingTimeOut,a=l.manifestLoadingRetryDelay,n=l.manifestLoadingMaxRetryTimeout;break;case _.a.LEVEL:case _.a.AUDIO_TRACK:case _.a.SUBTITLE_TRACK:r=0,i=l.levelLoadingTimeOut;break;default:r=l.levelLoadingMaxRetry,i=l.levelLoadingTimeOut,a=l.levelLoadingRetryDelay,n=l.levelLoadingMaxRetryTimeout}if((u=this.createInternalLoader(t),null!==(e=t.deliveryDirectives)&&void 0!==e&&e.part)&&(t.type===_.a.LEVEL&&null!==t.level?s=this.hls.levels[t.level].details:t.type===_.a.AUDIO_TRACK&&null!==t.id?s=this.hls.audioTracks[t.id].details:t.type===_.a.SUBTITLE_TRACK&&null!==t.id&&(s=this.hls.subtitleTracks[t.id].details),s)){var h=s.partTarget,f=s.targetduration;h&&f&&(i=Math.min(1e3*Math.max(3*h,.8*f),i))}var c={timeout:i,maxRetry:r,retryDelay:a,maxRetryDelay:n,highWaterMark:0},v={onSuccess:this.loadsuccess.bind(this),onError:this.loaderror.bind(this),onTimeout:this.loadtimeout.bind(this)};u.load(t,c,v)},e.loadsuccess=function(t,e,r,i){if(void 0===i&&(i=null),r.isSidxRequest)return this.handleSidxRequest(t,r),void this.handlePlaylistLoaded(t,e,r,i);this.resetInternalLoader(r.type);var a=t.data;0===a.indexOf("#EXTM3U")?(e.parsing.start=performance.now(),a.indexOf("#EXTINF:")>0||a.indexOf("#EXT-X-TARGETDURATION:")>0?this.handleTrackOrLevelPlaylist(t,e,r,i):this.handleMasterPlaylist(t,e,r,i)):this.handleManifestParsingError(t,r,"no EXTM3U delimiter",i)},e.loaderror=function(t,e,r){void 0===r&&(r=null),this.handleNetworkError(e,r,!1,t)},e.loadtimeout=function(t,e,r){void 0===r&&(r=null),this.handleNetworkError(e,r,!0)},e.handleMasterPlaylist=function(t,e,r,i){var a=this.hls,s=t.data,l=k(t,r),u=L.parseMasterPlaylist(s,l),d=u.levels,h=u.sessionData;if(d.length){var f=d.map((function(t){return{id:t.attrs.AUDIO,audioCodec:t.audioCodec}})),c=d.map((function(t){return{id:t.attrs.SUBTITLES,textCodec:t.textCodec}})),v=L.parseMasterPlaylistMedia(s,l,"AUDIO",f),p=L.parseMasterPlaylistMedia(s,l,"SUBTITLES",c),m=L.parseMasterPlaylistMedia(s,l,"CLOSED-CAPTIONS");if(v.length)v.some((function(t){return!t.url}))||!d[0].audioCodec||d[0].attrs.AUDIO||(o.b.log("[playlist-loader]: audio codec signaled in quality level, but no embedded audio track signaled, create one"),v.unshift({type:"main",name:"main",default:!1,autoselect:!1,forced:!1,id:-1,attrs:new g({}),bitrate:0,url:""}));a.trigger(n.a.MANIFEST_LOADED,{levels:d,audioTracks:v,subtitles:p,captions:m,url:l,stats:e,networkDetails:i,sessionData:h})}else this.handleManifestParsingError(t,r,"no level found in manifest",i)},e.handleTrackOrLevelPlaylist=function(t,e,r,i){var o=this.hls,l=r.id,u=r.level,d=r.type,h=k(t,r),f=Object(a.a)(l)?l:0,c=Object(a.a)(u)?u:f,v=function(t){switch(t.type){case _.a.AUDIO_TRACK:return _.b.AUDIO;case _.a.SUBTITLE_TRACK:return _.b.SUBTITLE;default:return _.b.MAIN}}(r),p=L.parseLevelPlaylist(t.data,h,c,v,f);if(p.fragments.length){if(d===_.a.MANIFEST){var m={attrs:new g({}),bitrate:0,details:p,name:"",url:h};o.trigger(n.a.MANIFEST_LOADED,{levels:[m],audioTracks:[],url:h,stats:e,networkDetails:i,sessionData:null})}if(e.parsing.end=performance.now(),p.needSidxRanges){var y,b=null===(y=p.fragments[0].initSegment)||void 0===y?void 0:y.url;this.load({url:b,isSidxRequest:!0,type:d,level:u,levelDetails:p,id:l,groupId:null,rangeStart:0,rangeEnd:2048,responseType:"arraybuffer",deliveryDirectives:null})}else r.levelDetails=p,this.handlePlaylistLoaded(t,e,r,i)}else o.trigger(n.a.ERROR,{type:s.b.NETWORK_ERROR,details:s.a.LEVEL_EMPTY_ERROR,fatal:!1,url:h,reason:"no fragments found in level",level:"number"==typeof r.level?r.level:void 0})},e.handleSidxRequest=function(t,e){var r=Object(l.g)(new Uint8Array(t.data));if(r){var i=r.references,a=e.levelDetails;i.forEach((function(t,e){var i=t.info,n=a.fragments[e];0===n.byteRange.length&&n.setByteRange(String(1+i.end-i.start)+"@"+String(i.start)),n.initSegment&&n.initSegment.setByteRange(String(r.moovEndOffset)+"@0")}))}},e.handleManifestParsingError=function(t,e,r,i){this.hls.trigger(n.a.ERROR,{type:s.b.NETWORK_ERROR,details:s.a.MANIFEST_PARSING_ERROR,fatal:e.type===_.a.MANIFEST,url:t.url,reason:r,response:t,context:e,networkDetails:i})},e.handleNetworkError=function(t,e,r,i){void 0===r&&(r=!1),o.b.warn("[playlist-loader]: A network "+(r?"timeout":"error")+" occurred while loading "+t.type+" level: "+t.level+" id: "+t.id+' group-id: "'+t.groupId+'"');var a=s.a.UNKNOWN,l=!1,u=this.getInternalLoader(t);switch(t.type){case _.a.MANIFEST:a=r?s.a.MANIFEST_LOAD_TIMEOUT:s.a.MANIFEST_LOAD_ERROR,l=!0;break;case _.a.LEVEL:a=r?s.a.LEVEL_LOAD_TIMEOUT:s.a.LEVEL_LOAD_ERROR,l=!1;break;case _.a.AUDIO_TRACK:a=r?s.a.AUDIO_TRACK_LOAD_TIMEOUT:s.a.AUDIO_TRACK_LOAD_ERROR,l=!1;break;case _.a.SUBTITLE_TRACK:a=r?s.a.SUBTITLE_TRACK_LOAD_TIMEOUT:s.a.SUBTITLE_LOAD_ERROR,l=!1}u&&this.resetInternalLoader(t.type);var d={type:s.b.NETWORK_ERROR,details:a,fatal:l,url:t.url,loader:u,context:t,networkDetails:e};i&&(d.response=i),this.hls.trigger(n.a.ERROR,d)},e.handlePlaylistLoaded=function(t,e,r,i){var a=r.type,s=r.level,o=r.id,l=r.groupId,u=r.loader,d=r.levelDetails,h=r.deliveryDirectives;if(null!=d&&d.targetduration){if(u)switch(d.live&&(u.getCacheAge&&(d.ageHeader=u.getCacheAge()||0),u.getCacheAge&&!isNaN(d.ageHeader)||(d.ageHeader=0)),a){case _.a.MANIFEST:case _.a.LEVEL:this.hls.trigger(n.a.LEVEL_LOADED,{details:d,level:s||0,id:o||0,stats:e,networkDetails:i,deliveryDirectives:h});break;case _.a.AUDIO_TRACK:this.hls.trigger(n.a.AUDIO_TRACK_LOADED,{details:d,id:o||0,groupId:l||"",stats:e,networkDetails:i,deliveryDirectives:h});break;case _.a.SUBTITLE_TRACK:this.hls.trigger(n.a.SUBTITLE_TRACK_LOADED,{details:d,id:o||0,groupId:l||"",stats:e,networkDetails:i,deliveryDirectives:h})}}else this.handleManifestParsingError(t,r,"invalid target duration",i)},t}(),w=function(){function t(t){this.hls=void 0,this.loaders={},this.decryptkey=null,this.decrypturl=null,this.hls=t,this._registerListeners()}var e=t.prototype;return e._registerListeners=function(){this.hls.on(n.a.KEY_LOADING,this.onKeyLoading,this)},e._unregisterListeners=function(){this.hls.off(n.a.KEY_LOADING,this.onKeyLoading)},e.destroy=function(){for(var t in this._unregisterListeners(),this.loaders){var e=this.loaders[t];e&&e.destroy()}this.loaders={}},e.onKeyLoading=function(t,e){var r=e.frag,i=r.type,a=this.loaders[i];if(r.decryptdata){var s=r.decryptdata.uri;if(s!==this.decrypturl||null===this.decryptkey){var l=this.hls.config;if(a&&(o.b.warn("abort previous key loader for type:"+i),a.abort()),!s)return void o.b.warn("key uri is falsy");var u=l.loader,d=r.loader=this.loaders[i]=new u(l);this.decrypturl=s,this.decryptkey=null;var h={url:s,frag:r,responseType:"arraybuffer"},f={timeout:l.fragLoadingTimeOut,maxRetry:0,retryDelay:l.fragLoadingRetryDelay,maxRetryDelay:l.fragLoadingMaxRetryTimeout,highWaterMark:0},c={onSuccess:this.loadsuccess.bind(this),onError:this.loaderror.bind(this),onTimeout:this.loadtimeout.bind(this)};d.load(h,f,c)}else this.decryptkey&&(r.decryptdata.key=this.decryptkey,this.hls.trigger(n.a.KEY_LOADED,{frag:r}))}else o.b.warn("Missing decryption data on fragment in onKeyLoading")},e.loadsuccess=function(t,e,r){var i=r.frag;i.decryptdata?(this.decryptkey=i.decryptdata.key=new Uint8Array(t.data),i.loader=null,delete this.loaders[i.type],this.hls.trigger(n.a.KEY_LOADED,{frag:i})):o.b.error("after key load, decryptdata unset")},e.loaderror=function(t,e){var r=e.frag,i=r.loader;i&&i.abort(),delete this.loaders[r.type],this.hls.trigger(n.a.ERROR,{type:s.b.NETWORK_ERROR,details:s.a.KEY_LOAD_ERROR,fatal:!1,frag:r,response:t})},e.loadtimeout=function(t,e){var r=e.frag,i=r.loader;i&&i.abort(),delete this.loaders[r.type],this.hls.trigger(n.a.ERROR,{type:s.b.NETWORK_ERROR,details:s.a.KEY_LOAD_TIMEOUT,fatal:!1,frag:r})},t}();function C(t,e){var r;try{r=new Event("addtrack")}catch(t){(r=document.createEvent("Event")).initEvent("addtrack",!1,!1)}r.track=t,e.dispatchEvent(r)}function O(t,e,r){var i=t.mode;if("disabled"===i&&(t.mode="hidden"),t.cues&&t.cues.length>0)for(var a=function(t,e,r){var i=[],a=function(t,e){if(et[r].endTime)return-1;var i=0,a=r;for(;i<=a;){var n=Math.floor((a+i)/2);if(et[n].startTime&&i-1)for(var n=a,s=t.length;n=e&&o.endTime<=r)i.push(o);else if(o.startTime>r)return i}return i}(t.cues,e,r),n=0;n.05&&this.forwardBufferLength>1){var u=Math.min(2,Math.max(1,n)),d=Math.round(2/(1+Math.exp(-.75*o-this.edgeStalled))*20)/20;t.playbackRate=Math.min(u,Math.max(1,d))}else 1!==t.playbackRate&&0!==t.playbackRate&&(t.playbackRate=1)}}}}},a.estimateLiveEdge=function(){var t=this.levelDetails;return null===t?null:t.edge+t.age},a.computeLatency=function(){var t=this.estimateLiveEdge();return null===t?null:t-this.currentTime},e=t,(r=[{key:"latency",get:function(){return this._latency||0}},{key:"maxLatency",get:function(){var t=this.config,e=this.levelDetails;return void 0!==t.liveMaxLatencyDuration?t.liveMaxLatencyDuration:e?t.liveMaxLatencyDurationCount*e.targetduration:0}},{key:"targetLatency",get:function(){var t=this.levelDetails;if(null===t)return null;var e=t.holdBack,r=t.partHoldBack,i=t.targetduration,a=this.config,n=a.liveSyncDuration,s=a.liveSyncDurationCount,o=a.lowLatencyMode,l=this.hls.userConfig,u=o&&r||e;(l.liveSyncDuration||l.liveSyncDurationCount||0===u)&&(u=void 0!==n?n:s*i);var d=i;return u+Math.min(1*this.stallCount,d)}},{key:"liveSyncPosition",get:function(){var t=this.estimateLiveEdge(),e=this.targetLatency,r=this.levelDetails;if(null===t||null===e||null===r)return null;var i=r.edge,a=t-e-this.edgeStalled,n=i-r.totalduration,s=i-(this.config.lowLatencyMode&&r.partTarget||r.targetduration);return Math.min(Math.max(n,a),s)}},{key:"drift",get:function(){var t=this.levelDetails;return null===t?1:t.drift}},{key:"edgeStalled",get:function(){var t=this.levelDetails;if(null===t)return 0;var e=3*(this.config.lowLatencyMode&&t.partTarget||t.targetduration);return Math.max(t.age-e,0)}},{key:"forwardBufferLength",get:function(){var t=this.media,e=this.levelDetails;if(!t||!e)return 0;var r=t.buffered.length;return r?t.buffered.end(r-1):e.edge-this.currentTime}}])&&F(e.prototype,r),i&&F(e,i),t}();function B(t,e){for(var r=0;rt.sn?(n=r-t.start,i=t):(n=t.start-r,i=e),i.duration!==n&&(i.duration=n)}else if(e.sn>t.sn){t.cc===e.cc&&t.minEndPTS?e.start=t.start+(t.minEndPTS-t.start):e.start=t.start+t.duration}else e.start=Math.max(t.start-e.duration,0)}function V(t,e,r,i,n,s){i-r<=0&&(o.b.warn("Fragment should have a positive duration",e),i=r+e.duration,s=n+e.duration);var l=r,u=i,d=e.startPTS,h=e.endPTS;if(Object(a.a)(d)){var f=Math.abs(d-r);Object(a.a)(e.deltaPTS)?e.deltaPTS=Math.max(f,e.deltaPTS):e.deltaPTS=f,l=Math.max(r,d),r=Math.min(r,d),n=Math.min(n,e.startDTS),u=Math.min(i,h),i=Math.max(i,h),s=Math.max(s,e.endDTS)}e.duration=i-r;var c=r-e.start;e.appendedPTS=i,e.start=e.startPTS=r,e.maxStartPTS=l,e.startDTS=n,e.endPTS=i,e.minEndPTS=u,e.endDTS=s;var v,g=e.sn;if(!t||gt.endSN)return 0;var p=g-t.startSN,m=t.fragments;for(m[p]=e,v=p;v>0;v--)H(m[v],m[v-1]);for(v=p;v=0;n--){var s=i[n].initSegment;if(s){r=s;break}}t.fragmentHint&&delete t.fragmentHint.endPTS;var l,u=0;if(function(t,e,r){for(var i=e.skippedSegments,a=Math.max(t.startSN,e.startSN)-e.startSN,n=(t.fragmentHint?1:0)+(i?e.endSN:Math.min(t.endSN,e.endSN))-e.startSN,s=e.startSN-t.startSN,o=e.fragmentHint?e.fragments.concat(e.fragmentHint):e.fragments,l=t.fragmentHint?t.fragments.concat(t.fragmentHint):t.fragments,u=a;u<=n;u++){var d=l[s+u],h=o[u];i&&!h&&u=i.length||function(t,e){if(e){for(var r=t.fragments,i=t.skippedSegments;ie.partTarget&&(l+=1)}if(Object(a.a)(o))return new U(o,Object(a.a)(l)?l:void 0,M.No)}}},e.loadPlaylist=function(t){},e.shouldLoadTrack=function(t){return this.canLoad&&t&&!!t.url&&(!t.details||t.details.live)},e.playlistLoaded=function(t,e,r){var i=this,a=e.details,n=e.stats,s=n.loading.end?Math.max(0,self.performance.now()-n.loading.end):0;if(a.advancedDateTime=Date.now()-s,a.live||null!=r&&r.live){if(a.reloaded(r),r&&this.log("live playlist "+t+" "+(a.advanced?"REFRESHED "+a.lastPartSn+"-"+a.lastPartIndex:"MISSED")),r&&a.fragments.length>0&&W(r,a),!this.canLoad||!a.live)return;var o,l=void 0,u=void 0;if(a.canBlockReload&&a.endSN&&a.advanced){var d=this.hls.config.lowLatencyMode,h=a.lastPartSn,f=a.endSN,c=a.lastPartIndex,v=h===f;-1!==c?(l=v?f+1:h,u=v?d?0:c:c+1):l=f+1;var g=a.age,p=g+a.ageHeader,m=Math.min(p-a.partTarget,1.5*a.targetduration);if(m>0){if(r&&m>r.tuneInGoal)this.warn("CDN Tune-in goal increased from: "+r.tuneInGoal+" to: "+m+" with playlist age: "+a.age),m=0;else{var y=Math.floor(m/a.targetduration);if(l+=y,void 0!==u)u+=Math.round(m%a.targetduration/a.partTarget);this.log("CDN Tune-in age: "+a.ageHeader+"s last advanced "+g.toFixed(2)+"s goal: "+m+" skip sn "+y+" to part "+u)}a.tuneInGoal=m}if(o=this.getDeliveryDirectives(a,e.deliveryDirectives,l,u),d||!v)return void this.loadPlaylist(o)}else o=this.getDeliveryDirectives(a,e.deliveryDirectives,l,u);var b=function(t,e){var r,i=1e3*t.levelTargetDuration,a=i/2,n=t.age,s=n>0&&n<3*i,o=e.loading.end-e.loading.start,l=t.availabilityDelay;if(!1===t.updated)if(s){var u=333*t.misses;r=Math.max(Math.min(a,2*o),u),t.availabilityDelay=(t.availabilityDelay||0)+r}else r=a;else s?(l=Math.min(l||i/2,n),t.availabilityDelay=l,r=l+i-n):r=i-o;return Math.round(r)}(a,n);void 0!==l&&a.canBlockReload&&(b-=a.partTarget||1),this.log("reload live playlist "+t+" in "+Math.round(b)+" ms"),this.timer=self.setTimeout((function(){return i.loadPlaylist(o)}),b)}else this.clearTimer()},e.getDeliveryDirectives=function(t,e,r,i){var a=function(t,e){var r=t.canSkipUntil,i=t.canSkipDateRanges,a=t.endSN;return r&&(void 0!==e?e-a:0)-1&&null!==(e=t.context)&&void 0!==e&&e.deliveryDirectives)this.warn("retry playlist loading #"+this.retryCount+' after "'+t.details+'"'),this.loadPlaylist();else{var n=Math.min(Math.pow(2,this.retryCount)*i.levelLoadingRetryDelay,i.levelLoadingMaxRetryTimeout);this.timer=self.setTimeout((function(){return r.loadPlaylist()}),n),this.warn("retry playlist loading #"+this.retryCount+" in "+n+' ms after "'+t.details+'"')}else this.warn('cannot recover from error "'+t.details+'"'),this.clearTimer(),t.fatal=!0;return a},t}();function q(){return(q=Object.assign||function(t){for(var e=1;e0){r=a[0].bitrate,a.sort((function(t,e){return t.bitrate-e.bitrate})),this._levels=a;for(var c=0;cthis.hls.config.fragLoadingMaxRetry&&(n=r.frag.level)):n=r.frag.level}break;case s.a.LEVEL_LOAD_ERROR:case s.a.LEVEL_LOAD_TIMEOUT:i&&(i.deliveryDirectives&&(l=!1),n=i.level),o=!0;break;case s.a.REMUX_ALLOC_ERROR:n=r.level,o=!0}void 0!==n&&this.recoverLevel(r,n,o,l)}}},u.recoverLevel=function(t,e,r,i){var a=t.details,n=this._levels[e];if(n.loadError++,r){if(!this.retryLoadingOrFail(t))return void(this.currentLevelIndex=-1);t.levelRetry=!0}if(i){var s=n.url.length;if(s>1&&n.loadError1){var i=(e.urlId+1)%r;this.warn("Switching to redundant URL-id "+i),this._levels.forEach((function(t){t.urlId=i})),this.level=t}},u.onFragLoaded=function(t,e){var r=e.frag;if(void 0!==r&&r.type===_.b.MAIN){var i=this._levels[r.level];void 0!==i&&(i.fragmentError=0,i.loadError=0)}},u.onLevelLoaded=function(t,e){var r,i,a=e.level,n=e.details,s=this._levels[a];if(!s)return this.warn("Invalid level index "+a),void(null!==(i=e.deliveryDirectives)&&void 0!==i&&i.skip&&(n.deltaUpdateFailed=!0));a===this.currentLevelIndex?(0===s.fragmentError&&(s.loadError=0,this.retryCount=0),this.playlistLoaded(a,e,s.details)):null!==(r=e.deliveryDirectives)&&void 0!==r&&r.skip&&(n.deltaUpdateFailed=!0)},u.onAudioTrackSwitched=function(t,e){var r=this.hls.levels[this.currentLevelIndex];if(r&&r.audioGroupIds){for(var i=-1,a=this.hls.audioTracks[e.id].groupId,n=0;n0){var i=r.urlId,a=r.url[i];if(t)try{a=t.addDirectives(a)}catch(t){this.warn("Could not construct new URL with HLS Delivery Directives: "+t)}this.log("Attempt loading level index "+e+(t?" at sn "+t.msn+" part "+t.part:"")+" with URL-id "+i+" "+a),this.clearTimer(),this.hls.trigger(n.a.LEVEL_LOADING,{url:a,level:e,id:i,deliveryDirectives:t||null})}},u.removeLevel=function(t,e){var r=function(t,r){return r!==e},i=this._levels.filter((function(i,a){return a!==t||i.url.length>1&&void 0!==e&&(i.url=i.url.filter(r),i.audioGroupIds&&(i.audioGroupIds=i.audioGroupIds.filter(r)),i.textGroupIds&&(i.textGroupIds=i.textGroupIds.filter(r)),i.urlId=0,!0)})).map((function(t,e){var r=t.details;return null!=r&&r.fragments&&r.fragments.forEach((function(t){t.level=e})),t}));this._levels=i,this.hls.trigger(n.a.LEVELS_UPDATED,{levels:i})},a=i,(o=[{key:"levels",get:function(){return 0===this._levels.length?null:this._levels}},{key:"level",get:function(){return this.currentLevelIndex},set:function(t){var e,r=this._levels;if(0!==r.length&&(this.currentLevelIndex!==t||null===(e=r[t])||void 0===e||!e.details)){if(t<0||t>=r.length){var i=t<0;if(this.hls.trigger(n.a.ERROR,{type:s.b.OTHER_ERROR,details:s.a.LEVEL_SWITCH_ERROR,level:t,fatal:i,reason:"invalid level idx"}),i)return;t=Math.min(t,r.length-1)}this.clearTimer();var a=this.currentLevelIndex,o=r[a],l=r[t];this.log("switching to level "+t+" from "+a),this.currentLevelIndex=t;var u=q({},l,{level:t,maxBitrate:l.maxBitrate,uri:l.uri,urlId:l.urlId});delete u._urlId,this.hls.trigger(n.a.LEVEL_SWITCHING,u);var d=l.details;if(!d||d.live){var h=this.switchParams(l.uri,null==o?void 0:o.details);this.loadPlaylist(h)}}}},{key:"manualLevel",get:function(){return this.manualLevelIndex},set:function(t){this.manualLevelIndex=t,void 0===this._startLevel&&(this._startLevel=t),-1!==t&&(this.level=t)}},{key:"firstLevel",get:function(){return this._firstLevel},set:function(t){this._firstLevel=t}},{key:"startLevel",get:function(){if(void 0===this._startLevel){var t=this.hls.config.startLevel;return void 0!==t?t:this._firstLevel}return this._startLevel},set:function(t){this._startLevel=t}},{key:"nextLoadLevel",get:function(){return-1!==this.manualLevelIndex?this.manualLevelIndex:this.hls.nextAutoLevel},set:function(t){this.level=t,-1===this.manualLevelIndex&&(this.hls.nextAutoLevel=t)}}])&&z(a.prototype,o),l&&z(a,l),i}(X);!function(t){t.NOT_LOADED="NOT_LOADED",t.BACKTRACKED="BACKTRACKED",t.APPENDING="APPENDING",t.PARTIAL="PARTIAL",t.OK="OK"}($||($={}));var tt=function(){function t(t){this.activeFragment=null,this.activeParts=null,this.fragments=Object.create(null),this.timeRanges=Object.create(null),this.bufferPadding=.2,this.hls=void 0,this.hls=t,this._registerListeners()}var e=t.prototype;return e._registerListeners=function(){var t=this.hls;t.on(n.a.BUFFER_APPENDED,this.onBufferAppended,this),t.on(n.a.FRAG_BUFFERED,this.onFragBuffered,this),t.on(n.a.FRAG_LOADED,this.onFragLoaded,this)},e._unregisterListeners=function(){var t=this.hls;t.off(n.a.BUFFER_APPENDED,this.onBufferAppended,this),t.off(n.a.FRAG_BUFFERED,this.onFragBuffered,this),t.off(n.a.FRAG_LOADED,this.onFragLoaded,this)},e.destroy=function(){this._unregisterListeners(),this.fragments=this.timeRanges=null},e.getAppendedFrag=function(t,e){if(e===_.b.MAIN){var r=this.activeFragment,i=this.activeParts;if(!r)return null;if(i)for(var a=i.length;a--;){var n=i[a],s=n?n.end:r.appendedPTS;if(n.start<=t&&void 0!==s&&t<=s)return a>9&&(this.activeParts=i.slice(a-9)),n}else if(r.start<=t&&void 0!==r.appendedPTS&&t<=r.appendedPTS)return r}return this.getBufferedFrag(t,e)},e.getBufferedFrag=function(t,e){for(var r=this.fragments,i=Object.keys(r),a=i.length;a--;){var n=r[i[a]];if((null==n?void 0:n.body.type)===e&&n.buffered){var s=n.body;if(s.start<=t&&t<=s.end)return s}}return null},e.detectEvictedFragments=function(t,e,r){var i=this;Object.keys(this.fragments).forEach((function(a){var n=i.fragments[a];if(n)if(n.buffered){var s=n.range[t];s&&s.time.some((function(t){var r=!i.isTimeBuffered(t.startPTS,t.endPTS,e);return r&&i.removeFragment(n.body),r}))}else n.body.type===r&&i.removeFragment(n.body)}))},e.detectPartialFragments=function(t){var e=this,r=this.timeRanges,i=t.frag,a=t.part;if(r&&"initSegment"!==i.sn){var n=rt(i),s=this.fragments[n];s&&(Object.keys(r).forEach((function(t){var n=i.elementaryStreams[t];if(n){var o=r[t],l=null!==a||!0===n.partial;s.range[t]=e.getBufferedTimes(i,a,l,o)}})),s.backtrack=s.loaded=null,Object.keys(s.range).length?s.buffered=!0:this.removeFragment(s.body))}},e.fragBuffered=function(t){var e=rt(t),r=this.fragments[e];r&&(r.backtrack=r.loaded=null,r.buffered=!0)},e.getBufferedTimes=function(t,e,r,i){for(var a={time:[],partial:r},n=e?e.start:t.start,s=e?e.end:t.end,o=t.minEndPTS||s,l=t.maxStartPTS||n,u=0;u=d&&o<=h){a.time.push({startPTS:Math.max(n,i.start(u)),endPTS:Math.min(s,i.end(u))});break}if(nd)a.partial=!0,a.time.push({startPTS:Math.max(n,i.start(u)),endPTS:Math.min(s,i.end(u))});else if(s<=d)break}return a},e.getPartialFragment=function(t){var e,r,i,a=null,n=0,s=this.bufferPadding,o=this.fragments;return Object.keys(o).forEach((function(l){var u=o[l];u&&et(u)&&(r=u.body.start-s,i=u.body.end+s,t>=r&&t<=i&&(e=Math.min(t-r,i-t),n<=e&&(a=u.body,n=e)))})),a},e.getState=function(t){var e=rt(t),r=this.fragments[e];return r?r.buffered?et(r)?$.PARTIAL:$.OK:r.backtrack?$.BACKTRACKED:$.APPENDING:$.NOT_LOADED},e.backtrack=function(t,e){var r=rt(t),i=this.fragments[r];if(!i||i.backtrack)return null;var a=i.backtrack=e||i.loaded;return i.loaded=null,a},e.getBacktrackData=function(t){var e=rt(t),r=this.fragments[e];if(r){var i,a=r.backtrack;if(null!=a&&null!==(i=a.payload)&&void 0!==i&&i.byteLength)return a;this.removeFragment(t)}return null},e.isTimeBuffered=function(t,e,r){for(var i,a,n=0;n=i&&e<=a)return!0;if(e<=i)return!1}return!1},e.onFragLoaded=function(t,e){var r=e.frag,i=e.part;if("initSegment"!==r.sn&&!r.bitrateTest&&!i){var a=rt(r);this.fragments[a]={body:r,loaded:e,backtrack:null,buffered:!1,range:Object.create(null)}}},e.onBufferAppended=function(t,e){var r=this,i=e.frag,a=e.part,n=e.timeRanges;if(i.type===_.b.MAIN)if(this.activeFragment=i,a){var s=this.activeParts;s||(this.activeParts=s=[]),s.push(a)}else this.activeParts=null;this.timeRanges=n,Object.keys(n).forEach((function(t){var e=n[t];if(r.detectEvictedFragments(t,e),!a)for(var s=0;st&&i.removeFragment(s)}}))},e.removeFragment=function(t){var e=rt(t);t.stats.loaded=0,t.clearElementaryStreamInfo(),delete this.fragments[e]},e.removeAllFragments=function(){this.fragments=Object.create(null),this.activeFragment=null,this.activeParts=null},t}();function et(t){var e,r;return t.buffered&&((null===(e=t.range.video)||void 0===e?void 0:e.partial)||(null===(r=t.range.audio)||void 0===r?void 0:r.partial))}function rt(t){return t.type+"_"+t.level+"_"+t.urlId+"_"+t.sn}var it=function(){function t(){this._boundTick=void 0,this._tickTimer=null,this._tickInterval=null,this._tickCallCount=0,this._boundTick=this.tick.bind(this)}var e=t.prototype;return e.destroy=function(){this.onHandlerDestroying(),this.onHandlerDestroyed()},e.onHandlerDestroying=function(){this.clearNextTick(),this.clearInterval()},e.onHandlerDestroyed=function(){},e.hasInterval=function(){return!!this._tickInterval},e.hasNextTick=function(){return!!this._tickTimer},e.setInterval=function(t){return!this._tickInterval&&(this._tickInterval=self.setInterval(this._boundTick,t),!0)},e.clearInterval=function(){return!!this._tickInterval&&(self.clearInterval(this._tickInterval),this._tickInterval=null,!0)},e.clearNextTick=function(){return!!this._tickTimer&&(self.clearTimeout(this._tickTimer),this._tickTimer=null,!0)},e.tick=function(){this._tickCallCount++,1===this._tickCallCount&&(this.doTick(),this._tickCallCount>1&&this.tickImmediate(),this._tickCallCount=0)},e.tickImmediate=function(){this.clearNextTick(),this._tickTimer=self.setTimeout(this._boundTick,0)},e.doTick=function(){},t}(),at={length:0,start:function(){return 0},end:function(){return 0}},nt=function(){function t(){}return t.isBuffered=function(e,r){try{if(e)for(var i=t.getBuffered(e),a=0;a=i.start(a)&&r<=i.end(a))return!0}catch(t){}return!1},t.bufferInfo=function(e,r,i){try{if(e){var a,n=t.getBuffered(e),s=[];for(a=0;as&&(i[n-1].end=t[a].end):i.push(t[a])}else i.push(t[a])}else i=t;for(var o,l=0,u=e,d=e,h=0;h=f&&er.startCC||t&&t.cc0)r=a+1;else{if(!(s<0))return n;i=a-1}}return null}};function ht(t,e,r){void 0===t&&(t=0),void 0===e&&(e=0);var i=Math.min(e,r.duration+(r.deltaPTS?r.deltaPTS:0));return r.start+r.duration-i<=t?1:r.start-i>t&&r.start?-1:0}function ft(t,e,r){var i=1e3*Math.min(e,r.duration+(r.deltaPTS?r.deltaPTS:0));return(r.endProgramDateTime||0)-i>t}function ct(t){var e="function"==typeof Map?new Map:void 0;return(ct=function(t){if(null===t||(r=t,-1===Function.toString.call(r).indexOf("[native code]")))return t;var r;if("function"!=typeof t)throw new TypeError("Super expression must either be null or a function");if(void 0!==e){if(e.has(t))return e.get(t);e.set(t,i)}function i(){return vt(t,arguments,mt(this).constructor)}return i.prototype=Object.create(t.prototype,{constructor:{value:i,enumerable:!1,writable:!0,configurable:!0}}),pt(i,t)})(t)}function vt(t,e,r){return(vt=gt()?Reflect.construct:function(t,e,r){var i=[null];i.push.apply(i,e);var a=new(Function.bind.apply(t,i));return r&&pt(a,r.prototype),a}).apply(null,arguments)}function gt(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(t){return!1}}function pt(t,e){return(pt=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}function mt(t){return(mt=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}var yt=Math.pow(2,17),bt=function(){function t(t){this.config=void 0,this.loader=null,this.partLoadTimeout=-1,this.config=t}var e=t.prototype;return e.destroy=function(){this.loader&&(this.loader.destroy(),this.loader=null)},e.abort=function(){this.loader&&this.loader.abort()},e.load=function(t,e){var r=this,i=t.url;if(!i)return Promise.reject(new Et({type:s.b.NETWORK_ERROR,details:s.a.FRAG_LOAD_ERROR,fatal:!1,frag:t,networkDetails:null},"Fragment does not have a "+(i?"part list":"url")));this.abort();var a=this.config,n=a.fLoader,o=a.loader;return new Promise((function(i,l){r.loader&&r.loader.destroy();var u=r.loader=t.loader=n?new n(a):new o(a),d=Tt(t),h={timeout:a.fragLoadingTimeOut,maxRetry:0,retryDelay:0,maxRetryDelay:a.fragLoadingMaxRetryTimeout,highWaterMark:yt};t.stats=u.stats,u.load(d,h,{onSuccess:function(e,a,n,s){r.resetLoader(t,u),i({frag:t,part:null,payload:e.data,networkDetails:s})},onError:function(e,i,a){r.resetLoader(t,u),l(new Et({type:s.b.NETWORK_ERROR,details:s.a.FRAG_LOAD_ERROR,fatal:!1,frag:t,response:e,networkDetails:a}))},onAbort:function(e,i,a){r.resetLoader(t,u),l(new Et({type:s.b.NETWORK_ERROR,details:s.a.INTERNAL_ABORTED,fatal:!1,frag:t,networkDetails:a}))},onTimeout:function(e,i,a){r.resetLoader(t,u),l(new Et({type:s.b.NETWORK_ERROR,details:s.a.FRAG_LOAD_TIMEOUT,fatal:!1,frag:t,networkDetails:a}))},onProgress:function(r,i,a,n){e&&e({frag:t,part:null,payload:a,networkDetails:n})}})}))},e.loadPart=function(t,e,r){var i=this;this.abort();var a=this.config,n=a.fLoader,o=a.loader;return new Promise((function(l,u){i.loader&&i.loader.destroy();var d=i.loader=t.loader=n?new n(a):new o(a),h=Tt(t,e),f={timeout:a.fragLoadingTimeOut,maxRetry:0,retryDelay:0,maxRetryDelay:a.fragLoadingMaxRetryTimeout,highWaterMark:yt};e.stats=d.stats,d.load(h,f,{onSuccess:function(a,n,s,o){i.resetLoader(t,d),i.updateStatsFromPart(t,e);var u={frag:t,part:e,payload:a.data,networkDetails:o};r(u),l(u)},onError:function(r,a,n){i.resetLoader(t,d),u(new Et({type:s.b.NETWORK_ERROR,details:s.a.FRAG_LOAD_ERROR,fatal:!1,frag:t,part:e,response:r,networkDetails:n}))},onAbort:function(r,a,n){t.stats.aborted=e.stats.aborted,i.resetLoader(t,d),u(new Et({type:s.b.NETWORK_ERROR,details:s.a.INTERNAL_ABORTED,fatal:!1,frag:t,part:e,networkDetails:n}))},onTimeout:function(r,a,n){i.resetLoader(t,d),u(new Et({type:s.b.NETWORK_ERROR,details:s.a.FRAG_LOAD_TIMEOUT,fatal:!1,frag:t,part:e,networkDetails:n}))}})}))},e.updateStatsFromPart=function(t,e){var r=t.stats,i=e.stats,a=i.total;if(r.loaded+=i.loaded,a){var n=Math.round(t.duration/e.duration),s=Math.min(Math.round(r.loaded/a),n),o=(n-s)*Math.round(r.loaded/s);r.total=r.loaded+o}else r.total=Math.max(r.loaded,r.total);var l=r.loading,u=i.loading;l.start?l.first+=u.first-u.start:(l.start=u.start,l.first=u.first),l.end=u.end},e.resetLoader=function(t,e){t.loader=null,this.loader===e&&(self.clearTimeout(this.partLoadTimeout),this.loader=null),e.destroy()},t}();function Tt(t,e){void 0===e&&(e=null);var r=e||t,i={frag:t,part:e,responseType:"arraybuffer",url:r.url,rangeStart:0,rangeEnd:0},n=r.byteRangeStartOffset,s=r.byteRangeEndOffset;return Object(a.a)(n)&&Object(a.a)(s)&&(i.rangeStart=n,i.rangeEnd=s),i}var Et=function(t){var e,r;function i(e){for(var r,i=arguments.length,a=new Array(i>1?i-1:0),n=1;ne.start+e.duration+l;(s0&&s&&s.key&&s.iv&&"AES-128"===s.method){var o=self.performance.now();return e.decrypter.webCryptoDecrypt(new Uint8Array(a),s.key.buffer,s.iv.buffer).then((function(e){var a=self.performance.now();return i.trigger(n.a.FRAG_DECRYPTED,{frag:t,payload:e,stats:{tstart:o,tdecrypt:a}}),r.payload=e,r}))}return r})).then((function(r){var i=e.fragCurrent,a=e.hls,s=e.levels;if(!s)throw new Error("init load aborted, missing levels");s[t.level].details;var o=t.stats;e.state=_t,e.fragLoadError=0,t.data=new Uint8Array(r.payload),o.parsing.start=o.buffering.start=self.performance.now(),o.parsing.end=o.buffering.end=self.performance.now(),r.frag===i&&a.trigger(n.a.FRAG_BUFFERED,{stats:o,frag:i,part:null,id:t.type}),e.tick()})).catch((function(r){e.warn(r),e.resetFragmentLoading(t)}))},f.fragContextChanged=function(t){var e=this.fragCurrent;return!t||!e||t.level!==e.level||t.sn!==e.sn||t.urlId!==e.urlId},f.fragBufferedComplete=function(t,e){var r=this.mediaBuffer?this.mediaBuffer:this.media;this.log("Buffered "+t.type+" sn: "+t.sn+(e?" part: "+e.index:"")+" of "+("[stream-controller]"===this.logPrefix?"level":"track")+" "+t.level+" "+Lt.toString(nt.getBuffered(r))),this.state=_t,this.tick()},f._handleFragmentLoadComplete=function(t){var e=this.transmuxer;if(e){var r=t.frag,i=t.part,a=t.partsLoaded,n=!a||0===a.length||a.some((function(t){return!t})),s=new st(r.level,r.sn,r.stats.chunkCount+1,0,i?i.index:-1,!n);e.flush(s)}},f._handleFragmentLoadProgress=function(t){},f._doFragLoad=function(t,e,r,i){var s=this;if(void 0===r&&(r=null),!this.levels)throw new Error("frag load aborted, missing levels");if(r=Math.max(t.start,r||0),this.config.lowLatencyMode&&e){var o=e.partList;if(o&&i){r>t.end&&e.fragmentHint&&(t=e.fragmentHint);var l=this.getNextPart(o,t,r);if(l>-1){var u=o[l];return this.log("Loading part sn: "+t.sn+" p: "+u.index+" cc: "+t.cc+" of playlist ["+e.startSN+"-"+e.endSN+"] parts [0-"+l+"-"+(o.length-1)+"] "+("[stream-controller]"===this.logPrefix?"level":"track")+": "+t.level+", target: "+parseFloat(r.toFixed(3))),this.nextLoadPosition=u.start+u.duration,this.state=xt,this.hls.trigger(n.a.FRAG_LOADING,{frag:t,part:o[l],targetBufferTime:r}),this.doFragPartsLoad(t,o,l,i).catch((function(t){return s.handleFragLoadError(t)}))}if(!t.url||this.loadedEndOfParts(o,r))return Promise.resolve(null)}}return this.log("Loading fragment "+t.sn+" cc: "+t.cc+" "+(e?"of ["+e.startSN+"-"+e.endSN+"] ":"")+("[stream-controller]"===this.logPrefix?"level":"track")+": "+t.level+", target: "+parseFloat(r.toFixed(3))),Object(a.a)(t.sn)&&!this.bitrateTest&&(this.nextLoadPosition=t.start+t.duration),this.state=xt,this.hls.trigger(n.a.FRAG_LOADING,{frag:t,targetBufferTime:r}),this.fragmentLoader.load(t,i).catch((function(t){return s.handleFragLoadError(t)}))},f.doFragPartsLoad=function(t,e,r,i){var a=this;return new Promise((function(s,o){var l=[];!function r(u){var d=e[u];a.fragmentLoader.loadPart(t,d,i).then((function(i){l[d.index]=i;var o=i.part;a.hls.trigger(n.a.FRAG_LOADED,i);var h=e[u+1];if(!h||h.fragment!==t)return s({frag:t,part:o,partsLoaded:l});r(u+1)})).catch(o)}(r)}))},f.handleFragLoadError=function(t){var e=t.data;return e&&e.details===s.a.INTERNAL_ABORTED?this.handleFragLoadAborted(e.frag,e.part):this.hls.trigger(n.a.ERROR,e),null},f._handleTransmuxerFlush=function(t){var e=this.getCurrentContext(t);if(e&&this.state===Ct){var r=e.frag,i=e.part,a=e.level,n=self.performance.now();r.stats.parsing.end=n,i&&(i.stats.parsing.end=n),this.updateLevelTiming(r,i,a,t.partial)}else this.fragCurrent||(this.state=_t)},f.getCurrentContext=function(t){var e=this.levels,r=t.level,i=t.sn,a=t.part;if(!e||!e[r])return this.warn("Levels object was unset while buffering fragment "+i+" of level "+r+". The current chunk will not be buffered."),null;var n=e[r],s=a>-1?function(t,e,r){if(!t||!t.details)return null;var i=t.details.partList;if(i)for(var a=i.length;a--;){var n=i[a];if(n.index===r&&n.fragment.sn===e)return n}return null}(n,i,a):null,o=s?s.fragment:function(t,e,r){if(!t||!t.details)return null;var i=t.details,a=i.fragments[e-i.startSN];return a||((a=i.fragmentHint)&&a.sn===e?a:en&&this.flushMainBuffer(s,t.start)}else this.flushMainBuffer(0,t.start)},f.getFwdBufferInfo=function(t,e){var r=this.config,i=this.getLoadPosition();if(!Object(a.a)(i))return null;var n=nt.bufferInfo(t,i,r.maxBufferHole);if(0===n.len&&void 0!==n.nextStart){var s=this.fragmentTracker.getBufferedFrag(i,e);if(s&&n.nextStart=r&&(e.maxMaxBufferLength/=2,this.warn("Reduce max buffer length to "+e.maxMaxBufferLength+"s"),!0)},f.getNextFragment=function(t,e){var r,i,a=e.fragments,n=a.length;if(!n)return null;var s,o=this.config,l=a[0].start;if(e.live){var u=o.initialLiveManifestSize;if(n-1&&rr.start&&r.loaded},f.getInitialLiveFragment=function(t,e){var r=this.fragPrevious,i=null;if(r){if(t.hasProgramDateTime&&(this.log("Live playlist, switching playlist, load frag with same PDT: "+r.programDateTime),i=function(t,e,r){if(null===e||!Array.isArray(t)||!t.length||!Object(a.a)(e))return null;if(e<(t[0].programDateTime||0))return null;if(e>=(t[t.length-1].endProgramDateTime||0))return null;r=r||0;for(var i=0;i=t.startSN&&n<=t.endSN){var s=e[n-t.startSN];r.cc===s.cc&&(i=s,this.log("Live playlist, switching playlist, load frag with next SN: "+i.sn))}i||(i=function(t,e){return dt.search(t,(function(t){return t.cce?-1:0}))}(e,r.cc))&&this.log("Live playlist, switching playlist, load frag with same CC: "+i.sn)}}else{var o=this.hls.liveSyncPosition;null!==o&&(i=this.getFragmentAtPosition(o,this.bitrateTest?t.fragmentEnd:t.edge,t))}return i},f.getFragmentAtPosition=function(t,e,r){var i,a=this.config,n=this.fragPrevious,s=r.fragments,o=r.endSN,l=r.fragmentHint,u=a.maxFragLookUpTolerance,d=!!(a.lowLatencyMode&&r.partList&&l);(d&&l&&!this.bitrateTest&&(s=s.concat(l),o=l.sn),te-u?0:u):i=s[s.length-1];if(i){var h=i.sn-r.startSN,f=n&&i.level===n.level,c=s[h+1];if(this.fragmentTracker.getState(i)===$.BACKTRACKED){i=null;for(var v=h;s[v]&&this.fragmentTracker.getState(s[v])===$.BACKTRACKED;)i=n?s[v--]:s[--v];i||(i=c)}else n&&i.sn===n.sn&&!d&&f&&(i.sn=n-e.maxFragLookUpTolerance&&a<=s;if(null!==i&&r.duration>i&&(a"+t.startSN+" prev-sn: "+(n?n.sn:"na")+" fragments: "+o),h}return l},f.waitForCdnTuneIn=function(t){return t.live&&t.canBlockReload&&t.tuneInGoal>Math.max(t.partHoldBack,3*t.partTarget)},f.setStartPosition=function(t,e){var r=this.startPosition;if(r"+t))}}])&&At(u.prototype,d),h&&At(u,h),i}(it);function Bt(){return self.MediaSource||self.WebKitMediaSource}function Ut(){return self.SourceBuffer||self.WebKitSourceBuffer}var Gt=r(17),jt=r(9),Kt=r(13),Ht=Bt()||{isTypeSupported:function(){return!1}},Vt=function(){function t(t,e,r,i){var a=this;this.hls=void 0,this.id=void 0,this.observer=void 0,this.frag=null,this.part=null,this.worker=void 0,this.onwmsg=void 0,this.transmuxer=null,this.onTransmuxComplete=void 0,this.onFlush=void 0,this.hls=t,this.id=e,this.onTransmuxComplete=r,this.onFlush=i;var l=t.config,u=function(e,r){(r=r||{}).frag=a.frag,r.id=a.id,t.trigger(e,r)};this.observer=new Kt.EventEmitter,this.observer.on(n.a.FRAG_DECRYPTED,u),this.observer.on(n.a.ERROR,u);var d={mp4:Ht.isTypeSupported("video/mp4"),mpeg:Ht.isTypeSupported("audio/mpeg"),mp3:Ht.isTypeSupported('audio/mp4; codecs="mp3"')},h=navigator.vendor;if(l.enableWorker&&"undefined"!=typeof Worker){var f;o.b.log("demuxing in webworker");try{f=this.worker=Gt(18),this.onwmsg=this.onWorkerMessage.bind(this),f.addEventListener("message",this.onwmsg),f.onerror=function(e){t.trigger(n.a.ERROR,{type:s.b.OTHER_ERROR,details:s.a.INTERNAL_EXCEPTION,fatal:!0,event:"demuxerWorker",error:new Error(e.message+" ("+e.filename+":"+e.lineno+")")})},f.postMessage({cmd:"init",typeSupported:d,vendor:h,id:e,config:JSON.stringify(l)})}catch(t){o.b.warn("Error in worker:",t),o.b.error("Error while initializing DemuxerWorker, fallback to inline"),f&&self.URL.revokeObjectURL(f.objectURL),this.transmuxer=new jt.c(this.observer,d,l,h,e),this.worker=null}}else this.transmuxer=new jt.c(this.observer,d,l,h,e)}var e=t.prototype;return e.destroy=function(){var t=this.worker;if(t)t.removeEventListener("message",this.onwmsg),t.terminate(),this.worker=null;else{var e=this.transmuxer;e&&(e.destroy(),this.transmuxer=null)}var r=this.observer;r&&r.removeAllListeners(),this.observer=null},e.push=function(t,e,r,i,a,n,s,l,u,d){var h=this;u.transmuxing.start=self.performance.now();var f=this.transmuxer,c=this.worker,v=n?n.start:a.start,g=a.decryptdata,p=this.frag,m=!(p&&a.cc===p.cc),y=!(p&&u.level===p.level),b=p?u.sn-p.sn:-1,T=this.part?u.part-this.part.index:1,E=!y&&(1===b||0===b&&1===T),S=self.performance.now();(y||b||0===a.stats.parsing.start)&&(a.stats.parsing.start=S),!n||!T&&E||(n.stats.parsing.start=S);var L=new jt.b(m,E,l,y,v);if(!E||m){o.b.log("[transmuxer-interface, "+a.type+"]: Starting new transmux session for sn: "+u.sn+" p: "+u.part+" level: "+u.level+" id: "+u.id+"\n discontinuity: "+m+"\n trackSwitch: "+y+"\n contiguous: "+E+"\n accurateTimeOffset: "+l+"\n timeOffset: "+v);var A=new jt.a(r,i,e,s,d);this.configureTransmuxer(A)}if(this.frag=a,this.part=n,c)c.postMessage({cmd:"demux",data:t,decryptdata:g,chunkMeta:u,state:L},t instanceof ArrayBuffer?[t]:[]);else if(f){var R=f.push(t,g,u,L);Object(jt.d)(R)?R.then((function(t){h.handleTransmuxComplete(t)})):this.handleTransmuxComplete(R)}},e.flush=function(t){var e=this;t.transmuxing.start=self.performance.now();var r=this.transmuxer,i=this.worker;if(i)i.postMessage({cmd:"flush",chunkMeta:t});else if(r){var a=r.flush(t);Object(jt.d)(a)?a.then((function(r){e.handleFlushResult(r,t)})):this.handleFlushResult(a,t)}},e.handleFlushResult=function(t,e){var r=this;t.forEach((function(t){r.handleTransmuxComplete(t)})),this.onFlush(e)},e.onWorkerMessage=function(t){var e=t.data,r=this.hls;switch(e.event){case"init":self.URL.revokeObjectURL(this.worker.objectURL);break;case"transmuxComplete":this.handleTransmuxComplete(e.data);break;case"flush":this.onFlush(e.data);break;default:e.data=e.data||{},e.data.frag=this.frag,e.data.id=this.id,r.trigger(e.event,e.data)}},e.configureTransmuxer=function(t){var e=this.worker,r=this.transmuxer;e?e.postMessage({cmd:"configure",config:t}):r&&r.configure(t)},e.handleTransmuxComplete=function(t){t.chunkMeta.transmuxing.end=self.performance.now(),this.onTransmuxComplete(t)},t}(),Wt=function(){function t(t,e,r,i){this.config=void 0,this.media=void 0,this.fragmentTracker=void 0,this.hls=void 0,this.nudgeRetry=0,this.stallReported=!1,this.stalled=null,this.moved=!1,this.seeking=!1,this.config=t,this.media=e,this.fragmentTracker=r,this.hls=i}var e=t.prototype;return e.destroy=function(){this.hls=this.fragmentTracker=this.media=null},e.poll=function(t){var e=this.config,r=this.media,i=this.stalled,a=r.currentTime,n=r.seeking,s=this.seeking&&!n,l=!this.seeking&&n;if(this.seeking=n,a===t){if((l||s)&&(this.stalled=null),!r.paused&&!r.ended&&0!==r.playbackRate&&nt.getBuffered(r).length){var u=nt.bufferInfo(r,a,0),d=u.len>0,h=u.nextStart||0;if(d||h){if(n){var f=u.len>2,c=!h||h-a>2&&!this.fragmentTracker.getPartialFragment(a);if(f||c)return;this.moved=!1}if(!this.moved&&null!==this.stalled){var v,g=Math.max(h,u.start||0)-a,p=this.hls.levels?this.hls.levels[this.hls.currentLevel]:null,m=(null==p||null===(v=p.details)||void 0===v?void 0:v.live)?2*p.details.targetduration:2;if(g>0&&g<=m)return void this._trySkipBufferHole(null)}var y=self.performance.now();if(null!==i){var b=y-i;!n&&b>=250&&this._reportStall(u.len);var T=nt.bufferInfo(r,a,e.maxBufferHole);this._tryFixBufferStall(T,b)}else this.stalled=y}}}else if(this.moved=!0,null!==i){if(this.stallReported){var E=self.performance.now()-i;o.b.warn("playback not stuck anymore @"+a+", after "+Math.round(E)+"ms"),this.stallReported=!1}this.stalled=null,this.nudgeRetry=0}},e._tryFixBufferStall=function(t,e){var r=this.config,i=this.fragmentTracker,a=this.media.currentTime,n=i.getPartialFragment(a);if(n&&this._trySkipBufferHole(n))return;t.len>r.maxBufferHole&&e>1e3*r.highBufferWatchdogPeriod&&(o.b.warn("Trying to nudge playhead over buffer-hole"),this.stalled=null,this._tryNudgeBuffer())},e._reportStall=function(t){var e=this.hls,r=this.media;this.stallReported||(this.stallReported=!0,o.b.warn("Playback stalling at @"+r.currentTime+" due to low buffer (buffer="+t+")"),e.trigger(n.a.ERROR,{type:s.b.MEDIA_ERROR,details:s.a.BUFFER_STALLED_ERROR,fatal:!1,buffer:t}))},e._trySkipBufferHole=function(t){for(var e=this.config,r=this.hls,i=this.media,a=i.currentTime,l=0,u=nt.getBuffered(i),d=0;d=l&&a0&&-1===t&&(this.log("Override startPosition with lastCurrentTime @"+e.toFixed(3)),t=e),this.state=_t,this.nextLoadPosition=this.startPosition=this.lastCurrentTime=t,this.tick()}else this._forceStartLoad=!0,this.state=Dt},f.stopLoad=function(){this._forceStartLoad=!1,t.prototype.stopLoad.call(this)},f.doTick=function(){switch(this.state){case _t:this.doTickIdle();break;case Mt:var t,e=this.levels,r=this.level,i=null==e||null===(t=e[r])||void 0===t?void 0:t.details;if(i&&(!i.live||this.levelLastLoaded===this.level)){if(this.waitForCdnTuneIn(i))break;this.state=_t;break}break;case wt:var a,n=self.performance.now(),s=this.retryDate;(!s||n>=s||null!==(a=this.media)&&void 0!==a&&a.seeking)&&(this.log("retryDate reached, switch back to IDLE state"),this.state=_t)}this.onTickEnd()},f.onTickEnd=function(){t.prototype.onTickEnd.call(this),this.checkBuffer(),this.checkFragmentChanged()},f.doTickIdle=function(){var t,e,r=this.hls,i=this.levelLastLoaded,a=this.levels,s=this.media,o=r.config,l=r.nextLoadLevel;if(null!==i&&(s||!this.startFragRequested&&o.startFragPrefetch)&&(!this.altAudio||!this.audioOnly)&&a&&a[l]){var d=a[l];this.level=r.nextLoadLevel=l;var h=d.details;if(!h||this.state===Mt||h.live&&this.levelLastLoaded!==l)this.state=Mt;else{var f=this.getFwdBufferInfo(this.mediaBuffer?this.mediaBuffer:s,_.b.MAIN);if(null!==f)if(!(f.len>=this.getMaxBufferLength(d.maxBitrate))){if(this._streamEnded(f,h)){var c={};return this.altAudio&&(c.type="video"),this.hls.trigger(n.a.BUFFER_EOS,c),void(this.state=Pt)}var v=f.end,g=this.getNextFragment(v,h);if(this.couldBacktrack&&!this.fragPrevious&&g&&"initSegment"!==g.sn){var p=g.sn-h.startSN;p>1&&(g=h.fragments[p-1],this.fragmentTracker.removeFragment(g))}if(g&&this.fragmentTracker.getState(g)===$.OK&&this.nextLoadPosition>v){var m=this.audioOnly&&!this.altAudio?u.a.AUDIO:u.a.VIDEO;this.afterBufferFlushed(s,m,_.b.MAIN),g=this.getNextFragment(this.nextLoadPosition,h)}g&&(!g.initSegment||g.initSegment.data||this.bitrateTest||(g=g.initSegment),"identity"!==(null===(t=g.decryptdata)||void 0===t?void 0:t.keyFormat)||null!==(e=g.decryptdata)&&void 0!==e&&e.key?this.loadFragment(g,h,v):this.loadKey(g,h))}}}},f.loadFragment=function(e,r,i){var a,n=this.fragmentTracker.getState(e);if(this.fragCurrent=e,n===$.BACKTRACKED){var s=this.fragmentTracker.getBacktrackData(e);if(s)return this._handleFragmentLoadProgress(s),void this._handleFragmentLoadComplete(s);n=$.NOT_LOADED}n===$.NOT_LOADED||n===$.PARTIAL?"initSegment"===e.sn?this._loadInitSegment(e):this.bitrateTest?(e.bitrateTest=!0,this.log("Fragment "+e.sn+" of level "+e.level+" is being downloaded to test bitrate and will not be buffered"),this._loadBitrateTestFrag(e)):(this.startFragRequested=!0,t.prototype.loadFragment.call(this,e,r,i)):n===$.APPENDING?this.reduceMaxBufferLength(e.duration)&&this.fragmentTracker.removeFragment(e):0===(null===(a=this.media)||void 0===a?void 0:a.buffered.length)&&this.fragmentTracker.removeAllFragments()},f.getAppendedFrag=function(t){var e=this.fragmentTracker.getAppendedFrag(t,_.b.MAIN);return e&&"fragment"in e?e.fragment:e},f.getBufferedFrag=function(t){return this.fragmentTracker.getBufferedFrag(t,_.b.MAIN)},f.followingBufferedFrag=function(t){return t?this.getBufferedFrag(t.end+.5):null},f.immediateLevelSwitch=function(){this.abortCurrentFrag(),this.flushMainBuffer(0,Number.POSITIVE_INFINITY)},f.nextLevelSwitch=function(){var t=this.levels,e=this.media;if(null!=e&&e.readyState){var r,i=this.getAppendedFrag(e.currentTime);if(i&&i.start>1&&this.flushMainBuffer(0,i.start-1),!e.paused&&t){var a=t[this.hls.nextLoadLevel],n=this.fragLastKbps;r=n&&this.fragCurrent?this.fragCurrent.duration*a.maxBitrate/(1e3*n)+1:0}else r=0;var s=this.getBufferedFrag(e.currentTime+r);if(s){var o=this.followingBufferedFrag(s);if(o){this.abortCurrentFrag();var l=o.maxStartPTS?o.maxStartPTS:o.start,u=o.duration,d=Math.max(s.end,l+Math.min(Math.max(u-this.config.maxFragLookUpTolerance,.5*u),.75*u));this.flushMainBuffer(d,Number.POSITIVE_INFINITY)}}}},f.abortCurrentFrag=function(){var t=this.fragCurrent;this.fragCurrent=null,null!=t&&t.loader&&t.loader.abort(),this.state===kt&&(this.state=_t),this.nextLoadPosition=this.getLoadPosition()},f.flushMainBuffer=function(e,r){t.prototype.flushMainBuffer.call(this,e,r,this.altAudio?"video":null)},f.onMediaAttached=function(e,r){t.prototype.onMediaAttached.call(this,e,r);var i=r.media;this.onvplaying=this.onMediaPlaying.bind(this),this.onvseeked=this.onMediaSeeked.bind(this),i.addEventListener("playing",this.onvplaying),i.addEventListener("seeked",this.onvseeked),this.gapController=new Wt(this.config,i,this.fragmentTracker,this.hls)},f.onMediaDetaching=function(){var e=this.media;e&&(e.removeEventListener("playing",this.onvplaying),e.removeEventListener("seeked",this.onvseeked),this.onvplaying=this.onvseeked=null,this.videoBuffer=null),this.fragPlaying=null,this.gapController&&(this.gapController.destroy(),this.gapController=null),t.prototype.onMediaDetaching.call(this)},f.onMediaPlaying=function(){this.tick()},f.onMediaSeeked=function(){var t=this.media,e=t?t.currentTime:null;Object(a.a)(e)&&this.log("Media seeked to "+e.toFixed(3)),this.tick()},f.onManifestLoading=function(){this.log("Trigger BUFFER_RESET"),this.hls.trigger(n.a.BUFFER_RESET,void 0),this.fragmentTracker.removeAllFragments(),this.couldBacktrack=this.stalled=!1,this.startPosition=this.lastCurrentTime=0,this.fragPlaying=null},f.onManifestParsed=function(t,e){var r,i,a,n=!1,s=!1;e.levels.forEach((function(t){(r=t.audioCodec)&&(-1!==r.indexOf("mp4a.40.2")&&(n=!0),-1!==r.indexOf("mp4a.40.5")&&(s=!0))})),this.audioCodecSwitch=n&&s&&!("function"==typeof(null==(a=Ut())||null===(i=a.prototype)||void 0===i?void 0:i.changeType)),this.audioCodecSwitch&&this.log("Both AAC/HE-AAC audio found in levels; declaring level codec as HE-AAC"),this.levels=e.levels,this.startFragRequested=!1},f.onLevelLoading=function(t,e){var r=this.levels;if(r&&this.state===_t){var i=r[e.level];(!i.details||i.details.live&&this.levelLastLoaded!==e.level||this.waitForCdnTuneIn(i.details))&&(this.state=Mt)}},f.onLevelLoaded=function(t,e){var r,i=this.levels,a=e.level,s=e.details,o=s.totalduration;if(i){this.log("Level "+a+" loaded ["+s.startSN+","+s.endSN+"], cc ["+s.startCC+", "+s.endCC+"] duration:"+o);var l=this.fragCurrent;!l||this.state!==xt&&this.state!==wt||l.level!==e.level&&l.loader&&(this.state=_t,l.loader.abort());var u=i[a],d=0;if(s.live||null!==(r=u.details)&&void 0!==r&&r.live){if(s.fragments[0]||(s.deltaUpdateFailed=!0),s.deltaUpdateFailed)return;d=this.alignPlaylists(s,u.details)}if(u.details=s,this.levelLastLoaded=a,this.hls.trigger(n.a.LEVEL_UPDATED,{details:s,level:a}),this.state===Mt){if(this.waitForCdnTuneIn(s))return;this.state=_t}this.startFragRequested?s.live&&this.synchronizeToLiveEdge(s):this.setStartPosition(s,d),this.tick()}else this.warn("Levels were reset while loading level "+a)},f._handleFragmentLoadProgress=function(t){var e,r=t.frag,i=t.part,a=t.payload,n=this.levels;if(n){var s=n[r.level],o=s.details;if(o){var l=s.videoCodec,u=o.PTSKnown||!o.live,d=null===(e=r.initSegment)||void 0===e?void 0:e.data,h=this._getAudioCodec(s),f=this.transmuxer=this.transmuxer||new Vt(this.hls,_.b.MAIN,this._handleTransmuxComplete.bind(this),this._handleTransmuxerFlush.bind(this)),c=i?i.index:-1,v=-1!==c,g=new st(r.level,r.sn,r.stats.chunkCount,a.byteLength,c,v),p=this.initPTS[r.cc];f.push(a,d,h,l,r,i,o.totalduration,u,g,p)}else this.warn("Dropping fragment "+r.sn+" of level "+r.level+" after level details were reset")}else this.warn("Levels were reset while fragment load was in progress. Fragment "+r.sn+" of level "+r.level+" will not be buffered")},f.onAudioTrackSwitching=function(t,e){var r=this.altAudio,i=!!e.url,a=e.id;if(!i){if(this.mediaBuffer!==this.media){this.log("Switching on main audio, use media.buffered to schedule main fragment loading"),this.mediaBuffer=this.media;var s=this.fragCurrent;null!=s&&s.loader&&(this.log("Switching to main audio track, cancel main fragment load"),s.loader.abort()),this.resetTransmuxer(),this.resetLoadingState()}else this.audioOnly&&this.resetTransmuxer();var o=this.hls;r&&o.trigger(n.a.BUFFER_FLUSHING,{startOffset:0,endOffset:Number.POSITIVE_INFINITY,type:"audio"}),o.trigger(n.a.AUDIO_TRACK_SWITCHED,{id:a})}},f.onAudioTrackSwitched=function(t,e){var r=e.id,i=!!this.hls.audioTracks[r].url;if(i){var a=this.videoBuffer;a&&this.mediaBuffer!==a&&(this.log("Switching on alternate audio, use video.buffered to schedule main fragment loading"),this.mediaBuffer=a)}this.altAudio=i,this.tick()},f.onBufferCreated=function(t,e){var r,i,a=e.tracks,n=!1;for(var s in a){var o=a[s];if("main"===o.id){if(i=s,r=o,"video"===s){var l=a[s];l&&(this.videoBuffer=l.buffer)}}else n=!0}n&&r?(this.log("Alternate track found, use "+i+".buffered to schedule main fragment loading"),this.mediaBuffer=r.buffer):this.mediaBuffer=this.media},f.onFragBuffered=function(t,e){var r=e.frag,i=e.part;if(!r||r.type===_.b.MAIN){if(this.fragContextChanged(r))return this.warn("Fragment "+r.sn+(i?" p: "+i.index:"")+" of level "+r.level+" finished buffering, but was aborted. state: "+this.state),void(this.state===Ot&&(this.state=_t));var a=i?i.stats:r.stats;this.fragLastKbps=Math.round(8*a.total/(a.buffering.end-a.loading.first)),"initSegment"!==r.sn&&(this.fragPrevious=r),this.fragBufferedComplete(r,i)}},f.onError=function(t,e){switch(e.details){case s.a.FRAG_LOAD_ERROR:case s.a.FRAG_LOAD_TIMEOUT:case s.a.KEY_LOAD_ERROR:case s.a.KEY_LOAD_TIMEOUT:this.onFragmentOrKeyLoadError(_.b.MAIN,e);break;case s.a.LEVEL_LOAD_ERROR:case s.a.LEVEL_LOAD_TIMEOUT:this.state!==Ft&&(e.fatal?(this.warn(""+e.details),this.state=Ft):e.levelRetry||this.state!==Mt||(this.state=_t));break;case s.a.BUFFER_FULL_ERROR:if("main"===e.parent&&(this.state===Ct||this.state===Ot)){var r=!0,i=this.getFwdBufferInfo(this.media,_.b.MAIN);i&&i.len>.5&&(r=!this.reduceMaxBufferLength(i.len)),r&&(this.warn("buffer full error also media.currentTime is not buffered, flush main"),this.immediateLevelSwitch()),this.resetLoadingState()}}},f.checkBuffer=function(){var t=this.media,e=this.gapController;if(t&&e&&t.readyState){var r=nt.getBuffered(t);!this.loadedmetadata&&r.length?(this.loadedmetadata=!0,this.seekToStartPos()):e.poll(this.lastCurrentTime),this.lastCurrentTime=t.currentTime}},f.onFragLoadEmergencyAborted=function(){this.state=_t,this.loadedmetadata||(this.startFragRequested=!1,this.nextLoadPosition=this.startPosition),this.tickImmediate()},f.onBufferFlushed=function(t,e){var r=e.type;if(r!==u.a.AUDIO||this.audioOnly&&!this.altAudio){var i=(r===u.a.VIDEO?this.videoBuffer:this.mediaBuffer)||this.media;this.afterBufferFlushed(i,r,_.b.MAIN)}},f.onLevelsUpdated=function(t,e){this.levels=e.levels},f.swapAudioCodec=function(){this.audioCodecSwap=!this.audioCodecSwap},f.seekToStartPos=function(){var t=this.media,e=t.currentTime,r=this.startPosition;if(r>=0&&e0&&a1&&!1===t.seeking){var r=t.currentTime;if(nt.isBuffered(t,r)?e=this.getAppendedFrag(r):nt.isBuffered(t,r+.1)&&(e=this.getAppendedFrag(r+.1)),e){var i=this.fragPlaying,a=e.level;i&&e.sn===i.sn&&i.level===a&&e.urlId===i.urlId||(this.hls.trigger(n.a.FRAG_CHANGED,{frag:e}),i&&i.level===a||this.hls.trigger(n.a.LEVEL_SWITCHED,{level:a}),this.fragPlaying=e)}}},l=i,(d=[{key:"nextLevel",get:function(){var t=this.nextBufferedFrag;return t?t.level:-1}},{key:"currentLevel",get:function(){var t=this.media;if(t){var e=this.getAppendedFrag(t.currentTime);if(e)return e.level}return-1}},{key:"nextBufferedFrag",get:function(){var t=this.media;if(t){var e=this.getAppendedFrag(t.currentTime);return this.followingBufferedFrag(e)}return null}},{key:"forceStartLoad",get:function(){return this._forceStartLoad}}])&&Yt(l.prototype,d),h&&Yt(l,h),i}(Nt),zt=function(){function t(t,e,r){void 0===e&&(e=0),void 0===r&&(r=0),this.halfLife=void 0,this.alpha_=void 0,this.estimate_=void 0,this.totalWeight_=void 0,this.halfLife=t,this.alpha_=t?Math.exp(Math.log(.5)/t):0,this.estimate_=e,this.totalWeight_=r}var e=t.prototype;return e.sample=function(t,e){var r=Math.pow(this.alpha_,t);this.estimate_=e*(1-r)+r*this.estimate_,this.totalWeight_+=t},e.getTotalWeight=function(){return this.totalWeight_},e.getEstimate=function(){if(this.alpha_){var t=1-Math.pow(this.alpha_,this.totalWeight_);if(t)return this.estimate_/t}return this.estimate_},t}(),Qt=function(){function t(t,e,r){this.defaultEstimate_=void 0,this.minWeight_=void 0,this.minDelayMs_=void 0,this.slow_=void 0,this.fast_=void 0,this.defaultEstimate_=r,this.minWeight_=.001,this.minDelayMs_=50,this.slow_=new zt(t),this.fast_=new zt(e)}var e=t.prototype;return e.update=function(t,e){var r=this.slow_,i=this.fast_;this.slow_.halfLife!==t&&(this.slow_=new zt(t,r.getEstimate(),r.getTotalWeight())),this.fast_.halfLife!==e&&(this.fast_=new zt(e,i.getEstimate(),i.getTotalWeight()))},e.sample=function(t,e){var r=(t=Math.max(t,this.minDelayMs_))/1e3,i=8*e/r;this.fast_.sample(r,i),this.slow_.sample(r,i)},e.canEstimate=function(){var t=this.fast_;return t&&t.getTotalWeight()>=this.minWeight_},e.getEstimate=function(){return this.canEstimate()?Math.min(this.fast_.getEstimate(),this.slow_.getEstimate()):this.defaultEstimate_},e.destroy=function(){},t}();function $t(t,e){for(var r=0;r=2*d/f||y<=T)){var E,S=Number.POSITIVE_INFINITY;for(E=t.level-1;E>v;E--){if((S=d*c[E].maxBitrate/(6.4*m))=y)){var L=this.bwEstimator.getEstimate();o.b.warn("Fragment "+t.sn+(e?" part "+e.index:"")+" of level "+t.level+" is loading too slowly and will cause an underbuffer; aborting and switching to level "+E+"\n Current BW estimate: "+(Object(a.a)(L)?(L/1024).toFixed(3):"Unknown")+" Kb/s\n Estimated load time for current fragment: "+y.toFixed(3)+" s\n Estimated load time for the next fragment: "+S.toFixed(3)+" s\n Time to underbuffer: "+T.toFixed(3)+" s"),r.nextLoadLevel=E,this.bwEstimator.sample(h,u.loaded),this.clearTimer(),t.loader&&(this.fragCurrent=this.partCurrent=null,t.loader.abort()),r.trigger(n.a.FRAG_LOAD_EMERGENCY_ABORTED,{frag:t,part:e,stats:u})}}}}}},l.onFragLoaded=function(t,e){var r=e.frag,i=e.part;if(r.type===_.b.MAIN&&Object(a.a)(r.sn)){var s=i?i.stats:r.stats,o=i?i.duration:r.duration;if(this.clearTimer(),this.lastLoadedFragLevel=r.level,this._nextAutoLevel=-1,this.hls.config.abrMaxWithRealBitrate){var l=this.hls.levels[r.level],u=(l.loaded?l.loaded.bytes:0)+s.loaded,d=(l.loaded?l.loaded.duration:0)+o;l.loaded={bytes:u,duration:d},l.realBitrate=Math.round(8*u/d)}if(r.bitrateTest){var h={stats:s,frag:r,part:i,id:r.type};this.onFragBuffered(n.a.FRAG_BUFFERED,h),r.bitrateTest=!1}}},l.onFragBuffered=function(t,e){var r=e.frag,i=e.part,a=i?i.stats:r.stats;if(!a.aborted&&r.type===_.b.MAIN&&"initSegment"!==r.sn){var n=a.parsing.end-a.loading.start;this.bwEstimator.sample(n,a.loaded),a.bwEstimate=this.bwEstimator.getEstimate(),r.bitrateTest?this.bitrateTestDelay=n/1e3:this.bitrateTestDelay=0}},l.onError=function(t,e){switch(e.details){case s.a.FRAG_LOAD_ERROR:case s.a.FRAG_LOAD_TIMEOUT:this.clearTimer()}},l.clearTimer=function(){self.clearInterval(this.timer),this.timer=void 0},l.getNextABRAutoLevel=function(){var t=this.fragCurrent,e=this.partCurrent,r=this.hls,i=r.maxAutoLevel,a=r.config,n=r.minAutoLevel,s=r.media,l=e?e.duration:t?t.duration:0,u=s?s.currentTime:0,d=s&&0!==s.playbackRate?Math.abs(s.playbackRate):1,h=this.bwEstimator?this.bwEstimator.getEstimate():a.abrEwmaDefaultEstimate,f=(nt.bufferInfo(s,u,a.maxBufferHole).end-u)/d,c=this.findBestLevel(h,n,i,f,a.abrBandWidthFactor,a.abrBandWidthUpFactor);if(c>=0)return c;o.b.trace((f?"rebuffering expected":"buffer is empty")+", finding optimal quality level");var v=l?Math.min(l,a.maxStarvationDelay):a.maxStarvationDelay,g=a.abrBandWidthFactor,p=a.abrBandWidthUpFactor;if(!f){var m=this.bitrateTestDelay;if(m)v=(l?Math.min(l,a.maxLoadingDelay):a.maxLoadingDelay)-m,o.b.trace("bitrate test took "+Math.round(1e3*m)+"ms, set first fragment max fetchDuration to "+Math.round(1e3*v)+" ms"),g=p=1}return c=this.findBestLevel(h,n,i,f+v,g,p),Math.max(c,0)},l.findBestLevel=function(t,e,r,i,a,n){for(var s,l=this.fragCurrent,u=this.partCurrent,d=this.lastLoadedFragLevel,h=this.hls.levels,f=h[d],c=!(null==f||null===(s=f.details)||void 0===s||!s.live),v=null==f?void 0:f.codecSet,g=u?u.duration:l?l.duration:0,p=r;p>=e;p--){var m=h[p];if(m&&(!v||m.codecSet===v)){var y=m.details,b=(u?null==y?void 0:y.partTarget:null==y?void 0:y.averagetargetduration)||g,T=void 0;T=p<=d?a*t:n*t;var E=h[p].maxBitrate,S=E*b/T;if(o.b.trace("level/adjustedbw/bitrate/avgDuration/maxFetchDuration/fetchDuration: "+p+"/"+Math.round(T)+"/"+E+"/"+b+"/"+i+"/"+S),T>E&&(!S||c&&!this.bitrateTestDelay||S0||Object.keys(this.pendingTracks).length>0},e.destroy=function(){this.unregisterListeners(),this.details=null},e.registerListeners=function(){var t=this.hls;t.on(n.a.MEDIA_ATTACHING,this.onMediaAttaching,this),t.on(n.a.MEDIA_DETACHING,this.onMediaDetaching,this),t.on(n.a.MANIFEST_PARSED,this.onManifestParsed,this),t.on(n.a.BUFFER_RESET,this.onBufferReset,this),t.on(n.a.BUFFER_APPENDING,this.onBufferAppending,this),t.on(n.a.BUFFER_CODECS,this.onBufferCodecs,this),t.on(n.a.BUFFER_EOS,this.onBufferEos,this),t.on(n.a.BUFFER_FLUSHING,this.onBufferFlushing,this),t.on(n.a.LEVEL_UPDATED,this.onLevelUpdated,this),t.on(n.a.FRAG_PARSED,this.onFragParsed,this),t.on(n.a.FRAG_CHANGED,this.onFragChanged,this)},e.unregisterListeners=function(){var t=this.hls;t.off(n.a.MEDIA_ATTACHING,this.onMediaAttaching,this),t.off(n.a.MEDIA_DETACHING,this.onMediaDetaching,this),t.off(n.a.MANIFEST_PARSED,this.onManifestParsed,this),t.off(n.a.BUFFER_RESET,this.onBufferReset,this),t.off(n.a.BUFFER_APPENDING,this.onBufferAppending,this),t.off(n.a.BUFFER_CODECS,this.onBufferCodecs,this),t.off(n.a.BUFFER_EOS,this.onBufferEos,this),t.off(n.a.BUFFER_FLUSHING,this.onBufferFlushing,this),t.off(n.a.LEVEL_UPDATED,this.onLevelUpdated,this),t.off(n.a.FRAG_PARSED,this.onFragParsed,this),t.off(n.a.FRAG_CHANGED,this.onFragChanged,this)},e._initSourceBuffer=function(){this.sourceBuffer={},this.operationQueue=new ee(this.sourceBuffer),this.listeners={audio:[],video:[],audiovideo:[]}},e.onManifestParsed=function(t,e){var r=2;(e.audio&&!e.video||!e.altAudio)&&(r=1),this.bufferCodecEventsExpected=this._bufferCodecEventsTotal=r,this.details=null,o.b.log(this.bufferCodecEventsExpected+" bufferCodec event(s) expected")},e.onMediaAttaching=function(t,e){var r=this.media=e.media;if(r&&re){var i=this.mediaSource=new re;i.addEventListener("sourceopen",this._onMediaSourceOpen),i.addEventListener("sourceended",this._onMediaSourceEnded),i.addEventListener("sourceclose",this._onMediaSourceClose),r.src=self.URL.createObjectURL(i),this._objectUrl=r.src}},e.onMediaDetaching=function(){var t=this.media,e=this.mediaSource,r=this._objectUrl;if(e){if(o.b.log("[buffer-controller]: media source detaching"),"open"===e.readyState)try{e.endOfStream()}catch(t){o.b.warn("[buffer-controller]: onMediaDetaching: "+t.message+" while calling endOfStream")}this.onBufferReset(),e.removeEventListener("sourceopen",this._onMediaSourceOpen),e.removeEventListener("sourceended",this._onMediaSourceEnded),e.removeEventListener("sourceclose",this._onMediaSourceClose),t&&(r&&self.URL.revokeObjectURL(r),t.src===r?(t.removeAttribute("src"),t.load()):o.b.warn("[buffer-controller]: media.src was changed by a third party - skip cleanup")),this.mediaSource=null,this.media=null,this._objectUrl=null,this.bufferCodecEventsExpected=this._bufferCodecEventsTotal,this.pendingTracks={},this.tracks={}}this.hls.trigger(n.a.MEDIA_DETACHED,void 0)},e.onBufferReset=function(){var t=this;this.getSourceBufferTypes().forEach((function(e){var r=t.sourceBuffer[e];try{r&&(t.removeBufferListeners(e),t.mediaSource&&t.mediaSource.removeSourceBuffer(r),t.sourceBuffer[e]=void 0)}catch(t){o.b.warn("[buffer-controller]: Failed to reset the "+e+" buffer",t)}})),this._initSourceBuffer()},e.onBufferCodecs=function(t,e){var r=this,i=this.getSourceBufferTypes().length;Object.keys(e).forEach((function(t){if(i){var a=r.tracks[t];if(a&&"function"==typeof a.buffer.changeType){var n=e[t],s=n.codec,o=n.levelCodec,l=n.container;if((a.levelCodec||a.codec).replace(ie,"$1")!==(o||s).replace(ie,"$1")){var u=l+";codecs="+(o||s);r.appendChangeType(t,u)}}}else r.pendingTracks[t]=e[t]})),i||(this.bufferCodecEventsExpected=Math.max(this.bufferCodecEventsExpected-1,0),this.mediaSource&&"open"===this.mediaSource.readyState&&this.checkPendingTracks())},e.appendChangeType=function(t,e){var r=this,i=this.operationQueue,a={execute:function(){var a=r.sourceBuffer[t];a&&(o.b.log("[buffer-controller]: changing "+t+" sourceBuffer type to "+e),a.changeType(e)),i.shiftAndExecuteNext(t)},onStart:function(){},onComplete:function(){},onError:function(e){o.b.warn("[buffer-controller]: Failed to change "+t+" SourceBuffer type",e)}};i.append(a,t)},e.onBufferAppending=function(t,e){var r=this,i=this.hls,a=this.operationQueue,l=this.tracks,u=e.data,d=e.type,h=e.frag,f=e.part,c=e.chunkMeta,v=c.buffering[d],g=self.performance.now();v.start=g;var p=h.stats.buffering,m=f?f.stats.buffering:null;0===p.start&&(p.start=g),m&&0===m.start&&(m.start=g);var y=l.audio,b="audio"===d&&1===c.id&&"audio/mpeg"===(null==y?void 0:y.container),T={execute:function(){if(v.executeStart=self.performance.now(),b){var t=r.sourceBuffer[d];if(t){var e=h.start-t.timestampOffset;Math.abs(e)>=.1&&(o.b.log("[buffer-controller]: Updating audio SourceBuffer timestampOffset to "+h.start+" (delta: "+e+") sn: "+h.sn+")"),t.timestampOffset=h.start)}}r.appendExecutor(u,d)},onStart:function(){},onComplete:function(){var t=self.performance.now();v.executeEnd=v.end=t,0===p.first&&(p.first=t),m&&0===m.first&&(m.first=t);var e=r.sourceBuffer,i={};for(var a in e)i[a]=nt.getBuffered(e[a]);r.appendError=0,r.hls.trigger(n.a.BUFFER_APPENDED,{type:d,frag:h,part:f,chunkMeta:c,parent:h.type,timeRanges:i})},onError:function(t){o.b.error("[buffer-controller]: Error encountered while trying to append to the "+d+" SourceBuffer",t);var e={type:s.b.MEDIA_ERROR,parent:h.type,details:s.a.BUFFER_APPEND_ERROR,err:t,fatal:!1};t.code===DOMException.QUOTA_EXCEEDED_ERR?e.details=s.a.BUFFER_FULL_ERROR:(r.appendError++,e.details=s.a.BUFFER_APPEND_ERROR,r.appendError>i.config.appendErrorMaxRetry&&(o.b.error("[buffer-controller]: Failed "+i.config.appendErrorMaxRetry+" times to append segment in sourceBuffer"),e.fatal=!0)),i.trigger(n.a.ERROR,e)}};a.append(T,d)},e.onBufferFlushing=function(t,e){var r=this,i=this.operationQueue,a=function(t){return{execute:r.removeExecutor.bind(r,t,e.startOffset,e.endOffset),onStart:function(){},onComplete:function(){r.hls.trigger(n.a.BUFFER_FLUSHED,{type:t})},onError:function(e){o.b.warn("[buffer-controller]: Failed to remove from "+t+" SourceBuffer",e)}}};e.type?i.append(a(e.type),e.type):this.getSourceBufferTypes().forEach((function(t){i.append(a(t),t)}))},e.onFragParsed=function(t,e){var r=this,i=e.frag,a=e.part,s=[],l=a?a.elementaryStreams:i.elementaryStreams;l[u.a.AUDIOVIDEO]?s.push("audiovideo"):(l[u.a.AUDIO]&&s.push("audio"),l[u.a.VIDEO]&&s.push("video"));0===s.length&&o.b.warn("Fragments must have at least one ElementaryStreamType set. type: "+i.type+" level: "+i.level+" sn: "+i.sn),this.blockBuffers((function(){var t=self.performance.now();i.stats.buffering.end=t,a&&(a.stats.buffering.end=t);var e=a?a.stats:i.stats;r.hls.trigger(n.a.FRAG_BUFFERED,{frag:i,part:a,stats:e,id:i.type})}),s)},e.onFragChanged=function(t,e){this.flushBackBuffer()},e.onBufferEos=function(t,e){var r=this;this.getSourceBufferTypes().reduce((function(t,i){var a=r.sourceBuffer[i];return e.type&&e.type!==i||a&&!a.ended&&(a.ended=!0,o.b.log("[buffer-controller]: "+i+" sourceBuffer now EOS")),t&&!(a&&!a.ended)}),!0)&&this.blockBuffers((function(){var t=r.mediaSource;t&&"open"===t.readyState&&t.endOfStream()}))},e.onLevelUpdated=function(t,e){var r=e.details;r.fragments.length&&(this.details=r,this.getSourceBufferTypes().length?this.blockBuffers(this.updateMediaElementDuration.bind(this)):this.updateMediaElementDuration())},e.flushBackBuffer=function(){var t=this.hls,e=this.details,r=this.media,i=this.sourceBuffer;if(r&&null!==e){var s=this.getSourceBufferTypes();if(s.length){var o=e.live&&null!==t.config.liveBackBufferLength?t.config.liveBackBufferLength:t.config.backBufferLength;if(Object(a.a)(o)&&!(o<0)){var l=r.currentTime,u=e.levelTargetDuration,d=Math.max(o,u),h=Math.floor(l/u)*u-d;s.forEach((function(r){var a=i[r];if(a){var s=nt.getBuffered(a);s.length>0&&h>s.start(0)&&(t.trigger(n.a.BACK_BUFFER_REACHED,{bufferEnd:h}),e.live&&t.trigger(n.a.LIVE_BACK_BUFFER_REACHED,{bufferEnd:h}),t.trigger(n.a.BUFFER_FLUSHING,{startOffset:0,endOffset:h,type:r}))}}))}}}},e.updateMediaElementDuration=function(){if(this.details&&this.media&&this.mediaSource&&"open"===this.mediaSource.readyState){var t=this.details,e=this.hls,r=this.media,i=this.mediaSource,n=t.fragments[0].start+t.totalduration,s=r.duration,l=Object(a.a)(i.duration)?i.duration:0;t.live&&e.config.liveDurationInfinity?(o.b.log("[buffer-controller]: Media Source duration is set to Infinity"),i.duration=1/0,this.updateSeekableRange(t)):(n>l&&n>s||!Object(a.a)(s))&&(o.b.log("[buffer-controller]: Updating Media Source duration to "+n.toFixed(3)),i.duration=n)}},e.updateSeekableRange=function(t){var e=this.mediaSource,r=t.fragments;if(r.length&&t.live&&null!=e&&e.setLiveSeekableRange){var i=Math.max(0,r[0].start),a=Math.max(i,i+t.totalduration);e.setLiveSeekableRange(i,a)}},e.checkPendingTracks=function(){var t=this.bufferCodecEventsExpected,e=this.operationQueue,r=this.pendingTracks,i=Object.keys(r).length;if(i&&!t||2===i){this.createSourceBuffers(r),this.pendingTracks={};var a=this.getSourceBufferTypes();if(0===a.length)return void this.hls.trigger(n.a.ERROR,{type:s.b.MEDIA_ERROR,details:s.a.BUFFER_INCOMPATIBLE_CODECS_ERROR,fatal:!0,reason:"could not create source buffer for media codec(s)"});a.forEach((function(t){e.executeNext(t)}))}},e.createSourceBuffers=function(t){var e=this.sourceBuffer,r=this.mediaSource;if(!r)throw Error("createSourceBuffers called when mediaSource was null");var i=0;for(var a in t)if(!e[a]){var l=t[a];if(!l)throw Error("source buffer exists for track "+a+", however track does not");var u=l.levelCodec||l.codec,d=l.container+";codecs="+u;o.b.log("[buffer-controller]: creating sourceBuffer("+d+")");try{var h=e[a]=r.addSourceBuffer(d),f=a;this.addBufferListener(f,"updatestart",this._onSBUpdateStart),this.addBufferListener(f,"updateend",this._onSBUpdateEnd),this.addBufferListener(f,"error",this._onSBUpdateError),this.tracks[a]={buffer:h,codec:u,container:l.container,levelCodec:l.levelCodec,id:l.id},i++}catch(t){o.b.error("[buffer-controller]: error while trying to add sourceBuffer: "+t.message),this.hls.trigger(n.a.ERROR,{type:s.b.MEDIA_ERROR,details:s.a.BUFFER_ADD_CODEC_ERROR,fatal:!1,error:t,mimeType:d})}}i&&this.hls.trigger(n.a.BUFFER_CREATED,{tracks:this.tracks})},e._onSBUpdateStart=function(t){this.operationQueue.current(t).onStart()},e._onSBUpdateEnd=function(t){var e=this.operationQueue;e.current(t).onComplete(),e.shiftAndExecuteNext(t)},e._onSBUpdateError=function(t,e){o.b.error("[buffer-controller]: "+t+" SourceBuffer error",e),this.hls.trigger(n.a.ERROR,{type:s.b.MEDIA_ERROR,details:s.a.BUFFER_APPENDING_ERROR,fatal:!1});var r=this.operationQueue.current(t);r&&r.onError(e)},e.removeExecutor=function(t,e,r){var i=this.media,n=this.mediaSource,s=this.operationQueue,l=this.sourceBuffer[t];if(!i||!n||!l)return o.b.warn("[buffer-controller]: Attempting to remove from the "+t+" SourceBuffer, but it does not exist"),void s.shiftAndExecuteNext(t);var u=Object(a.a)(i.duration)?i.duration:1/0,d=Object(a.a)(n.duration)?n.duration:1/0,h=Math.max(0,e),f=Math.min(r,u,d);f>h?(o.b.log("[buffer-controller]: Removing ["+h+","+f+"] from the "+t+" SourceBuffer"),l.remove(h,f)):s.shiftAndExecuteNext(t)},e.appendExecutor=function(t,e){var r=this.operationQueue,i=this.sourceBuffer[e];if(!i)return o.b.warn("[buffer-controller]: Attempting to append to the "+e+" SourceBuffer, but it does not exist"),void r.shiftAndExecuteNext(e);i.ended=!1,i.appendBuffer(t)},e.blockBuffers=function(t,e){var r=this;if(void 0===e&&(e=this.getSourceBufferTypes()),!e.length)return o.b.log("[buffer-controller]: Blocking operation requested, but no SourceBuffers exist"),void Promise.resolve(t);var i=this.operationQueue,a=e.map((function(t){return i.appendBlocker(t)}));Promise.all(a).then((function(){t(),e.forEach((function(t){var e=r.sourceBuffer[t];e&&e.updating||i.shiftAndExecuteNext(t)}))}))},e.getSourceBufferTypes=function(){return Object.keys(this.sourceBuffer)},e.addBufferListener=function(t,e,r){var i=this.sourceBuffer[t];if(i){var a=r.bind(this,t);this.listeners[t].push({event:e,listener:a}),i.addEventListener(e,a)}},e.removeBufferListeners=function(t){var e=this.sourceBuffer[t];e&&this.listeners[t].forEach((function(t){e.removeEventListener(t.event,t.listener)}))},t}();function ne(t,e){for(var r=0;r0&&this.mediaWidth>0){var t=this.hls.levels;if(t.length){var e=this.hls;e.autoLevelCapping=this.getMaxLevel(t.length-1),e.autoLevelCapping>this.autoLevelCapping&&this.streamController&&this.streamController.nextLevelSwitch(),this.autoLevelCapping=e.autoLevelCapping}}},a.getMaxLevel=function(e){var r=this,i=this.hls.levels;if(!i.length)return-1;var a=i.filter((function(i,a){return t.isLevelAllowed(a,r.restrictedLevels)&&a<=e}));return this.clientRect=null,t.getMaxLevelByMediaSize(a,this.mediaWidth,this.mediaHeight)},a.startCapping=function(){this.timer||(this.autoLevelCapping=Number.POSITIVE_INFINITY,this.hls.firstLevel=this.getMaxLevel(this.firstLevel),self.clearInterval(this.timer),this.timer=self.setInterval(this.detectPlayerSize.bind(this),1e3),this.detectPlayerSize())},a.stopCapping=function(){this.restrictedLevels=[],this.firstLevel=-1,this.autoLevelCapping=Number.POSITIVE_INFINITY,this.timer&&(self.clearInterval(this.timer),this.timer=void 0)},a.getDimensions=function(){if(this.clientRect)return this.clientRect;var t=this.media,e={width:0,height:0};if(t){var r=t.getBoundingClientRect();e.width=r.width,e.height=r.height,e.width||e.height||(e.width=r.right-r.left||t.width||0,e.height=r.bottom-r.top||t.height||0)}return this.clientRect=e,e},t.isLevelAllowed=function(t,e){return void 0===e&&(e=[]),-1===e.indexOf(t)},t.getMaxLevelByMediaSize=function(t,e,r){if(!t||!t.length)return-1;for(var i,a,n=t.length-1,s=0;s=e||o.height>=r)&&(i=o,!(a=t[s+1])||i.width!==a.width||i.height!==a.height)){n=s;break}}return n},e=t,i=[{key:"contentScaleFactor",get:function(){var t=1;try{t=self.devicePixelRatio}catch(t){}return t}}],(r=[{key:"mediaWidth",get:function(){return this.getDimensions().width*t.contentScaleFactor}},{key:"mediaHeight",get:function(){return this.getDimensions().height*t.contentScaleFactor}}])&&ne(e.prototype,r),i&&ne(e,i),t}(),oe=function(){function t(t){this.hls=void 0,this.isVideoPlaybackQualityAvailable=!1,this.timer=void 0,this.media=null,this.lastTime=void 0,this.lastDroppedFrames=0,this.lastDecodedFrames=0,this.streamController=void 0,this.hls=t,this.registerListeners()}var e=t.prototype;return e.setStreamController=function(t){this.streamController=t},e.registerListeners=function(){this.hls.on(n.a.MEDIA_ATTACHING,this.onMediaAttaching,this)},e.unregisterListeners=function(){this.hls.off(n.a.MEDIA_ATTACHING,this.onMediaAttaching)},e.destroy=function(){this.timer&&clearInterval(this.timer),this.unregisterListeners(),this.isVideoPlaybackQualityAvailable=!1,this.media=null},e.onMediaAttaching=function(t,e){var r=this.hls.config;if(r.capLevelOnFPSDrop){var i=e.media instanceof self.HTMLVideoElement?e.media:null;this.media=i,i&&"function"==typeof i.getVideoPlaybackQuality&&(this.isVideoPlaybackQualityAvailable=!0),self.clearInterval(this.timer),this.timer=self.setInterval(this.checkFPSInterval.bind(this),r.fpsDroppedMonitoringPeriod)}},e.checkFPS=function(t,e,r){var i=performance.now();if(e){if(this.lastTime){var a=i-this.lastTime,s=r-this.lastDroppedFrames,l=e-this.lastDecodedFrames,u=1e3*s/a,d=this.hls;if(d.trigger(n.a.FPS_DROP,{currentDropped:s,currentDecoded:l,totalDroppedFrames:r}),u>0&&s>d.config.fpsDroppedMonitoringThreshold*l){var h=d.currentLevel;o.b.warn("drop FPS ratio greater than max allowed value for currentLevel: "+h),h>0&&(-1===d.autoLevelCapping||d.autoLevelCapping>=h)&&(h-=1,d.trigger(n.a.FPS_DROP_LEVEL_CAPPING,{level:h,droppedLevel:d.currentLevel}),d.autoLevelCapping=h,this.streamController.nextLevelSwitch())}}this.lastTime=i,this.lastDroppedFrames=r,this.lastDecodedFrames=e}},e.checkFPSInterval=function(){var t=this.media;if(t)if(this.isVideoPlaybackQualityAvailable){var e=t.getVideoPlaybackQuality();this.checkFPS(t,e.totalVideoFrames,e.droppedVideoFrames)}else this.checkFPS(t,t.webkitDecodedFrameCount,t.webkitDroppedFrameCount)},t}(),le=r(12),ue=/^age:\s*[\d.]+\s*$/m,de=function(){function t(t){this.xhrSetup=void 0,this.requestTimeout=void 0,this.retryTimeout=void 0,this.retryDelay=void 0,this.config=null,this.callbacks=null,this.context=void 0,this.loader=null,this.stats=void 0,this.xhrSetup=t?t.xhrSetup:null,this.stats=new le.a,this.retryDelay=0}var e=t.prototype;return e.destroy=function(){this.callbacks=null,this.abortInternal(),this.loader=null,this.config=null},e.abortInternal=function(){var t=this.loader;self.clearTimeout(this.requestTimeout),self.clearTimeout(this.retryTimeout),t&&(t.onreadystatechange=null,t.onprogress=null,4!==t.readyState&&(this.stats.aborted=!0,t.abort()))},e.abort=function(){var t;this.abortInternal(),null!==(t=this.callbacks)&&void 0!==t&&t.onAbort&&this.callbacks.onAbort(this.stats,this.context,this.loader)},e.load=function(t,e,r){if(this.stats.loading.start)throw new Error("Loader can only be used once.");this.stats.loading.start=self.performance.now(),this.context=t,this.config=e,this.callbacks=r,this.retryDelay=e.retryDelay,this.loadInternal()},e.loadInternal=function(){var t=this.config,e=this.context;if(t){var r=this.loader=new self.XMLHttpRequest,i=this.stats;i.loading.first=0,i.loaded=0;var a=this.xhrSetup;try{if(a)try{a(r,e.url)}catch(t){r.open("GET",e.url,!0),a(r,e.url)}r.readyState||r.open("GET",e.url,!0)}catch(t){return void this.callbacks.onError({code:r.status,text:t.message},e,r)}e.rangeEnd&&r.setRequestHeader("Range","bytes="+e.rangeStart+"-"+(e.rangeEnd-1)),r.onreadystatechange=this.readystatechange.bind(this),r.onprogress=this.loadprogress.bind(this),r.responseType=e.responseType,self.clearTimeout(this.requestTimeout),this.requestTimeout=self.setTimeout(this.loadtimeout.bind(this),t.timeout),r.send()}},e.readystatechange=function(){var t=this.context,e=this.loader,r=this.stats;if(t&&e){var i=e.readyState,a=this.config;if(!r.aborted&&i>=2)if(self.clearTimeout(this.requestTimeout),0===r.loading.first&&(r.loading.first=Math.max(self.performance.now(),r.loading.start)),4===i){e.onreadystatechange=null,e.onprogress=null;var n=e.status;if(n>=200&&n<300){var s,l;if(r.loading.end=Math.max(self.performance.now(),r.loading.first),l="arraybuffer"===t.responseType?(s=e.response).byteLength:(s=e.responseText).length,r.loaded=r.total=l,!this.callbacks)return;var u=this.callbacks.onProgress;if(u&&u(r,t,s,e),!this.callbacks)return;var d={url:e.responseURL,data:s};this.callbacks.onSuccess(d,r,t,e)}else r.retry>=a.maxRetry||n>=400&&n<499?(o.b.error(n+" while loading "+t.url),this.callbacks.onError({code:n,text:e.statusText},t,e)):(o.b.warn(n+" while loading "+t.url+", retrying in "+this.retryDelay+"..."),this.abortInternal(),this.loader=null,self.clearTimeout(this.retryTimeout),this.retryTimeout=self.setTimeout(this.loadInternal.bind(this),this.retryDelay),this.retryDelay=Math.min(2*this.retryDelay,a.maxRetryDelay),r.retry++)}else self.clearTimeout(this.requestTimeout),this.requestTimeout=self.setTimeout(this.loadtimeout.bind(this),a.timeout)}},e.loadtimeout=function(){o.b.warn("timeout while loading "+this.context.url);var t=this.callbacks;t&&(this.abortInternal(),t.onTimeout(this.stats,this.context,this.loader))},e.loadprogress=function(t){var e=this.stats;e.loaded=t.loaded,t.lengthComputable&&(e.total=t.total)},e.getCacheAge=function(){var t=null;if(this.loader&&ue.test(this.loader.getAllResponseHeaders())){var e=this.loader.getResponseHeader("age");t=e?parseFloat(e):null}return t},t}(),he=r(16);function fe(t){var e="function"==typeof Map?new Map:void 0;return(fe=function(t){if(null===t||(r=t,-1===Function.toString.call(r).indexOf("[native code]")))return t;var r;if("function"!=typeof t)throw new TypeError("Super expression must either be null or a function");if(void 0!==e){if(e.has(t))return e.get(t);e.set(t,i)}function i(){return ce(t,arguments,pe(this).constructor)}return i.prototype=Object.create(t.prototype,{constructor:{value:i,enumerable:!1,writable:!0,configurable:!0}}),ge(i,t)})(t)}function ce(t,e,r){return(ce=ve()?Reflect.construct:function(t,e,r){var i=[null];i.push.apply(i,e);var a=new(Function.bind.apply(t,i));return r&&ge(a,r.prototype),a}).apply(null,arguments)}function ve(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(t){return!1}}function ge(t,e){return(ge=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}function pe(t){return(pe=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}var me=function(){function t(t){this.fetchSetup=void 0,this.requestTimeout=void 0,this.request=void 0,this.response=void 0,this.controller=void 0,this.context=void 0,this.config=null,this.callbacks=null,this.stats=void 0,this.loader=null,this.fetchSetup=t.fetchSetup||ye,this.controller=new self.AbortController,this.stats=new le.a}var e=t.prototype;return e.destroy=function(){this.loader=this.callbacks=null,this.abortInternal()},e.abortInternal=function(){var t=this.response;t&&t.ok||(this.stats.aborted=!0,this.controller.abort())},e.abort=function(){var t;this.abortInternal(),null!==(t=this.callbacks)&&void 0!==t&&t.onAbort&&this.callbacks.onAbort(this.stats,this.context,this.response)},e.load=function(t,e,r){var i=this,n=this.stats;if(n.loading.start)throw new Error("Loader can only be used once.");n.loading.start=self.performance.now();var s=function(t,e){var r={method:"GET",mode:"cors",credentials:"same-origin",signal:e};t.rangeEnd&&(r.headers=new self.Headers({Range:"bytes="+t.rangeStart+"-"+String(t.rangeEnd-1)}));return r}(t,this.controller.signal),o=r.onProgress,l="arraybuffer"===t.responseType,u=l?"byteLength":"length";this.context=t,this.config=e,this.callbacks=r,this.request=this.fetchSetup(t,s),self.clearTimeout(this.requestTimeout),this.requestTimeout=self.setTimeout((function(){i.abortInternal(),r.onTimeout(n,t,i.response)}),e.timeout),self.fetch(this.request).then((function(r){if(i.response=i.loader=r,!r.ok){var s=r.status,u=r.statusText;throw new Te(u||"fetch, bad network response",s,r)}return n.loading.first=Math.max(self.performance.now(),n.loading.start),n.total=parseInt(r.headers.get("Content-Length")||"0"),o&&Object(a.a)(e.highWaterMark)?i.loadProgressively(r,n,t,e.highWaterMark,o):l?r.arrayBuffer():r.text()})).then((function(s){var l=i.response;self.clearTimeout(i.requestTimeout),n.loading.end=Math.max(self.performance.now(),n.loading.first),n.loaded=n.total=s[u];var d={url:l.url,data:s};o&&!Object(a.a)(e.highWaterMark)&&o(n,t,s,l),r.onSuccess(d,n,t,l)})).catch((function(e){if(self.clearTimeout(i.requestTimeout),!n.aborted){var a=e.code||0;r.onError({code:a,text:e.message},t,e.details)}}))},e.getCacheAge=function(){var t=null;if(this.response){var e=this.response.headers.get("age");t=e?parseFloat(e):null}return t},e.loadProgressively=function(t,e,r,i,a){void 0===i&&(i=0);var n=new he.a,s=t.body.getReader();return function o(){return s.read().then((function(s){if(s.done)return n.dataLength&&a(e,r,n.flush(),t),Promise.resolve(new ArrayBuffer(0));var l=s.value,u=l.length;return e.loaded+=u,u=i&&a(e,r,n.flush(),t)):a(e,r,l,t),o()})).catch((function(){return Promise.reject()}))}()},t}();function ye(t,e){return new self.Request(t.url,e)}var be,Te=function(t){var e,r;function i(e,r,i){var a;return(a=t.call(this,e)||this).code=void 0,a.details=void 0,a.code=r,a.details=i,a}return r=t,(e=i).prototype=Object.create(r.prototype),e.prototype.constructor=e,ge(e,r),i}(fe(Error)),Ee=me;!function(t){t.WIDEVINE="com.widevine.alpha",t.PLAYREADY="com.microsoft.playready"}(be||(be={}));var Se="undefined"!=typeof self&&self.navigator&&self.navigator.requestMediaKeySystemAccess?self.navigator.requestMediaKeySystemAccess.bind(self.navigator):null;function Le(){return(Le=Object.assign||function(t){for(var e=1;ee)return i;return 0}},{key:"maxAutoLevel",get:function(){var t=this.levels,e=this.autoLevelCapping;return-1===e&&t&&t.length?t.length-1:e}},{key:"nextAutoLevel",get:function(){return Math.min(Math.max(this.abrController.nextAutoLevel,this.minAutoLevel),this.maxAutoLevel)},set:function(t){this.abrController.nextAutoLevel=Math.max(this.minAutoLevel,t)}},{key:"audioTracks",get:function(){var t=this.audioTrackController;return t?t.audioTracks:[]}},{key:"audioTrack",get:function(){var t=this.audioTrackController;return t?t.audioTrack:-1},set:function(t){var e=this.audioTrackController;e&&(e.audioTrack=t)}},{key:"subtitleTracks",get:function(){var t=this.subtitleTrackController;return t?t.subtitleTracks:[]}},{key:"subtitleTrack",get:function(){var t=this.subtitleTrackController;return t?t.subtitleTrack:-1},set:function(t){var e=this.subtitleTrackController;e&&(e.subtitleTrack=t)}},{key:"media",get:function(){return this._media}},{key:"subtitleDisplay",get:function(){var t=this.subtitleTrackController;return!!t&&t.subtitleDisplay},set:function(t){var e=this.subtitleTrackController;e&&(e.subtitleDisplay=t)}},{key:"lowLatencyMode",get:function(){return this.config.lowLatencyMode},set:function(t){this.config.lowLatencyMode=t}},{key:"liveSyncPosition",get:function(){return this.latencyController.liveSyncPosition}},{key:"latency",get:function(){return this.latencyController.latency}},{key:"maxLatency",get:function(){return this.latencyController.maxLatency}},{key:"targetLatency",get:function(){return this.latencyController.targetLatency}},{key:"drift",get:function(){return this.latencyController.drift}},{key:"forceStartLoad",get:function(){return this.streamController.forceStartLoad}}])&&xe(e.prototype,r),a&&xe(e,a),t}();we.defaultConfig=void 0}]).default})); +// @version v1.1.5 +"undefined"!=typeof window&&function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Hls=e():t.Hls=e()}(this,(function(){return function(t){var e={};function r(i){if(e[i])return e[i].exports;var a=e[i]={i:i,l:!1,exports:{}};return t[i].call(a.exports,a,a.exports,r),a.l=!0,a.exports}return r.m=t,r.c=e,r.d=function(t,e,i){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:i})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(r.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var a in t)r.d(i,a,function(e){return t[e]}.bind(null,a));return i},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="/dist/",r(r.s=19)}([function(t,e,r){"use strict";var i;r.d(e,"a",(function(){return i})),function(t){t.MEDIA_ATTACHING="hlsMediaAttaching",t.MEDIA_ATTACHED="hlsMediaAttached",t.MEDIA_DETACHING="hlsMediaDetaching",t.MEDIA_DETACHED="hlsMediaDetached",t.BUFFER_RESET="hlsBufferReset",t.BUFFER_CODECS="hlsBufferCodecs",t.BUFFER_CREATED="hlsBufferCreated",t.BUFFER_APPENDING="hlsBufferAppending",t.BUFFER_APPENDED="hlsBufferAppended",t.BUFFER_EOS="hlsBufferEos",t.BUFFER_FLUSHING="hlsBufferFlushing",t.BUFFER_FLUSHED="hlsBufferFlushed",t.MANIFEST_LOADING="hlsManifestLoading",t.MANIFEST_LOADED="hlsManifestLoaded",t.MANIFEST_PARSED="hlsManifestParsed",t.LEVEL_SWITCHING="hlsLevelSwitching",t.LEVEL_SWITCHED="hlsLevelSwitched",t.LEVEL_LOADING="hlsLevelLoading",t.LEVEL_LOADED="hlsLevelLoaded",t.LEVEL_UPDATED="hlsLevelUpdated",t.LEVEL_PTS_UPDATED="hlsLevelPtsUpdated",t.LEVELS_UPDATED="hlsLevelsUpdated",t.AUDIO_TRACKS_UPDATED="hlsAudioTracksUpdated",t.AUDIO_TRACK_SWITCHING="hlsAudioTrackSwitching",t.AUDIO_TRACK_SWITCHED="hlsAudioTrackSwitched",t.AUDIO_TRACK_LOADING="hlsAudioTrackLoading",t.AUDIO_TRACK_LOADED="hlsAudioTrackLoaded",t.SUBTITLE_TRACKS_UPDATED="hlsSubtitleTracksUpdated",t.SUBTITLE_TRACKS_CLEARED="hlsSubtitleTracksCleared",t.SUBTITLE_TRACK_SWITCH="hlsSubtitleTrackSwitch",t.SUBTITLE_TRACK_LOADING="hlsSubtitleTrackLoading",t.SUBTITLE_TRACK_LOADED="hlsSubtitleTrackLoaded",t.SUBTITLE_FRAG_PROCESSED="hlsSubtitleFragProcessed",t.CUES_PARSED="hlsCuesParsed",t.NON_NATIVE_TEXT_TRACKS_FOUND="hlsNonNativeTextTracksFound",t.INIT_PTS_FOUND="hlsInitPtsFound",t.FRAG_LOADING="hlsFragLoading",t.FRAG_LOAD_EMERGENCY_ABORTED="hlsFragLoadEmergencyAborted",t.FRAG_LOADED="hlsFragLoaded",t.FRAG_DECRYPTED="hlsFragDecrypted",t.FRAG_PARSING_INIT_SEGMENT="hlsFragParsingInitSegment",t.FRAG_PARSING_USERDATA="hlsFragParsingUserdata",t.FRAG_PARSING_METADATA="hlsFragParsingMetadata",t.FRAG_PARSED="hlsFragParsed",t.FRAG_BUFFERED="hlsFragBuffered",t.FRAG_CHANGED="hlsFragChanged",t.FPS_DROP="hlsFpsDrop",t.FPS_DROP_LEVEL_CAPPING="hlsFpsDropLevelCapping",t.ERROR="hlsError",t.DESTROYING="hlsDestroying",t.KEY_LOADING="hlsKeyLoading",t.KEY_LOADED="hlsKeyLoaded",t.LIVE_BACK_BUFFER_REACHED="hlsLiveBackBufferReached",t.BACK_BUFFER_REACHED="hlsBackBufferReached"}(i||(i={}))},function(t,e,r){"use strict";r.d(e,"a",(function(){return o})),r.d(e,"b",(function(){return l}));var i=function(){},a={trace:i,debug:i,log:i,warn:i,info:i,error:i},n=a;function s(t){var e=self.console[t];return e?e.bind(self.console,"["+t+"] >"):i}function o(t){if(self.console&&!0===t||"object"==typeof t){!function(t){for(var e=arguments.length,r=new Array(e>1?e-1:0),i=1;i>8*(15-r)&255;return e},r.setDecryptDataFromLevelKey=function(t,e){var r=t;return"AES-128"===(null==t?void 0:t.method)&&t.uri&&!t.iv&&((r=o.a.fromURI(t.uri)).method=t.method,r.iv=this.createInitializationVector(e),r.keyFormat="identity"),r},r.setElementaryStreamInfo=function(t,e,r,i,a,n){void 0===n&&(n=!1);var s=this.elementaryStreams,o=s[t];o?(o.startPTS=Math.min(o.startPTS,e),o.endPTS=Math.max(o.endPTS,r),o.startDTS=Math.min(o.startDTS,i),o.endDTS=Math.max(o.endDTS,a)):s[t]={startPTS:e,endPTS:r,startDTS:i,endDTS:a,partial:n}},r.clearElementaryStreamInfo=function(){var t=this.elementaryStreams;t[i.AUDIO]=null,t[i.VIDEO]=null,t[i.AUDIOVIDEO]=null},f(e,[{key:"decryptdata",get:function(){if(!this.levelkey&&!this._decryptdata)return null;if(!this._decryptdata&&this.levelkey){var t=this.sn;"number"!=typeof t&&(this.levelkey&&"AES-128"===this.levelkey.method&&!this.levelkey.iv&&s.b.warn('missing IV for initialization segment with method="'+this.levelkey.method+'" - compliance issue'),t=0),this._decryptdata=this.setDecryptDataFromLevelKey(this.levelkey,t)}return this._decryptdata}},{key:"end",get:function(){return this.start+this.duration}},{key:"endProgramDateTime",get:function(){if(null===this.programDateTime)return null;if(!Object(a.a)(this.programDateTime))return null;var t=Object(a.a)(this.duration)?this.duration:0;return this.programDateTime+1e3*t}},{key:"encrypted",get:function(){var t;return!(null===(t=this.decryptdata)||void 0===t||!t.keyFormat||!this.decryptdata.uri)}}]),e}(c),g=function(t){function e(e,r,i,a,n){var s;(s=t.call(this,i)||this).fragOffset=0,s.duration=0,s.gap=!1,s.independent=!1,s.relurl=void 0,s.fragment=void 0,s.index=void 0,s.stats=new l.a,s.duration=e.decimalFloatingPoint("DURATION"),s.gap=e.bool("GAP"),s.independent=e.bool("INDEPENDENT"),s.relurl=e.enumeratedString("URI"),s.fragment=r,s.index=a;var o=e.enumeratedString("BYTERANGE");return o&&s.setByteRange(o,n),n&&(s.fragOffset=n.fragOffset+n.duration),s}return u(e,t),f(e,[{key:"start",get:function(){return this.fragment.start+this.fragOffset}},{key:"end",get:function(){return this.start+this.duration}},{key:"loaded",get:function(){var t=this.elementaryStreams;return!!(t.audio||t.video||t.audiovideo)}}]),e}(c)},function(t,e,r){"use strict";r.d(e,"b",(function(){return d})),r.d(e,"g",(function(){return h})),r.d(e,"f",(function(){return f})),r.d(e,"d",(function(){return c})),r.d(e,"c",(function(){return v})),r.d(e,"e",(function(){return p})),r.d(e,"h",(function(){return m})),r.d(e,"a",(function(){return y}));var i=r(8),a=r(5),n=Math.pow(2,32)-1,s=[].push;function o(t){return String.fromCharCode.apply(null,t)}function l(t,e){"data"in t&&(e+=t.start,t=t.data);var r=t[e]<<24|t[e+1]<<16|t[e+2]<<8|t[e+3];return r<0?4294967296+r:r}function u(t,e,r){"data"in t&&(e+=t.start,t=t.data),t[e]=r>>24,t[e+1]=r>>16&255,t[e+2]=r>>8&255,t[e+3]=255&r}function d(t,e){var r,i,a,n=[];if(!e.length)return n;"data"in t?(r=t.data,i=t.start,a=t.end):(i=0,a=(r=t).byteLength);for(var u=i;u1?u+h:a;if(o(r.subarray(u+4,u+8))===e[0])if(1===e.length)n.push({data:r,start:u+8,end:f});else{var c=d({data:r,start:u+8,end:f},e.slice(1));c.length&&s.apply(n,c)}u=f}return n}function h(t){var e=d(t,["moov"])[0],r=e?e.end:null,i=d(t,["sidx"]);if(!i||!i[0])return null;var a=[],n=i[0],s=n.data[0],o=0===s?8:16,u=l(n,o);o+=4;o+=0===s?8:16,o+=2;var h=n.end+0,f=function(t,e){"data"in t&&(e+=t.start,t=t.data);var r=t[e]<<8|t[e+1];return r<0?65536+r:r}(n,o);o+=2;for(var c=0;c>>31)return console.warn("SIDX has hierarchical references (not supported)"),null;var m=l(n,v);v+=4,a.push({referenceSize:p,subsegmentDuration:m,info:{duration:m/u,start:h,end:h+p-1}}),h+=p,o=v+=4}return{earliestPresentationTime:0,timescale:u,version:s,referencesCount:f,references:a,moovEndOffset:r}}function f(t){for(var e=[],r=d(t,["moov","trak"]),i=0;i0)return t.subarray(r,r+i)},o=function(t,e){var r=0;return r=(127&t[e])<<21,r|=(127&t[e+1])<<14,r|=(127&t[e+2])<<7,r|=127&t[e+3]},l=function(t,e){return a(t,e)&&o(t,e+6)+10<=t.length-e},u=function(t){for(var e=f(t),r=0;r>4){case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:u+=String.fromCharCode(n);break;case 12:case 13:s=t[d++],u+=String.fromCharCode((31&n)<<6|63&s);break;case 14:s=t[d++],o=t[d++],u+=String.fromCharCode((15&n)<<12|(63&s)<<6|(63&o)<<0)}}return u};function b(){return i||void 0===self.TextDecoder||(i=new self.TextDecoder("utf-8")),i}},function(t,e,r){"use strict";function i(t,e,r){return Uint8Array.prototype.slice?t.slice(e,r):new Uint8Array(Array.prototype.slice.call(t,e,r))}r.d(e,"a",(function(){return i}))},function(t,e,r){"use strict";r.d(e,"c",(function(){return St})),r.d(e,"d",(function(){return At})),r.d(e,"a",(function(){return Rt})),r.d(e,"b",(function(){return Dt}));var i=r(0),a=r(2),n=r(14),s=r(3),o=r(7);var l=r(6),u=r(8),d=function(){function t(){this._audioTrack=void 0,this._id3Track=void 0,this.frameIndex=0,this.cachedData=null,this.initPTS=null}var e=t.prototype;return e.resetInitSegment=function(t,e,r){this._id3Track={type:"id3",id:3,pid:-1,inputTimeScale:9e4,sequenceNumber:0,samples:[],dropped:0}},e.resetTimeStamp=function(){},e.resetContiguity=function(){},e.canParse=function(t,e){return!1},e.appendFrame=function(t,e,r){},e.demux=function(t,e){this.cachedData&&(t=Object(l.a)(this.cachedData,t),this.cachedData=null);var r,i,a=o.b(t,0),n=a?a.length:0,s=this._audioTrack,d=this._id3Track,f=a?o.d(a):void 0,c=t.length;for(0!==this.frameIndex&&null!==this.initPTS||(this.initPTS=h(f,e)),a&&a.length>0&&d.samples.push({pts:this.initPTS,dts:this.initPTS,data:a}),i=this.initPTS;n>>5}function m(t,e){return e+1=t.length)return!1;var i=p(t,e);if(i<=r)return!1;var a=e+i;return a===t.length||m(t,a)}return!1}function b(t,e,r,n,s){if(!t.samplerate){var o=function(t,e,r,n){var s,o,l,u,d=navigator.userAgent.toLowerCase(),h=n,f=[96e3,88200,64e3,48e3,44100,32e3,24e3,22050,16e3,12e3,11025,8e3,7350];s=1+((192&e[r+2])>>>6);var v=(60&e[r+2])>>>2;if(!(v>f.length-1))return l=(1&e[r+2])<<2,l|=(192&e[r+3])>>>6,c.b.log("manifest codec:"+n+", ADTS type:"+s+", samplingIndex:"+v),/firefox/i.test(d)?v>=6?(s=5,u=new Array(4),o=v-3):(s=2,u=new Array(2),o=v):-1!==d.indexOf("android")?(s=2,u=new Array(2),o=v):(s=5,u=new Array(4),n&&(-1!==n.indexOf("mp4a.40.29")||-1!==n.indexOf("mp4a.40.5"))||!n&&v>=6?o=v-3:((n&&-1!==n.indexOf("mp4a.40.2")&&(v>=6&&1===l||/vivaldi/i.test(d))||!n&&1===l)&&(s=2,u=new Array(2)),o=v)),u[0]=s<<3,u[0]|=(14&v)>>1,u[1]|=(1&v)<<7,u[1]|=l<<3,5===s&&(u[1]|=(14&o)>>1,u[2]=(1&o)<<7,u[2]|=8,u[3]=0),{config:u,samplerate:f[v],channelCount:l,codec:"mp4a.40."+s,manifestCodec:h};t.trigger(i.a.ERROR,{type:a.b.MEDIA_ERROR,details:a.a.FRAG_PARSING_ERROR,fatal:!0,reason:"invalid ADTS sampling index:"+v})}(e,r,n,s);if(!o)return;t.config=o.config,t.samplerate=o.samplerate,t.channelCount=o.channelCount,t.codec=o.codec,t.manifestCodec=o.manifestCodec,c.b.log("parsed codec:"+t.codec+", rate:"+o.samplerate+", channels:"+o.channelCount)}}function T(t){return 9216e4/t}function E(t,e,r,i,a){var n=function(t,e,r,i,a){var n=g(t,e),s=p(t,e);if((s-=n)>0)return{headerLength:n,frameLength:s,stamp:r+i*a}}(e,r,i,a,T(t.samplerate));if(n){var s,o=n.frameLength,l=n.headerLength,u=n.stamp,d=l+o,h=Math.max(0,r+d-e.length);h?(s=new Uint8Array(d-l)).set(e.subarray(r+l,e.length),0):s=e.subarray(r+l,r+d);var f={unit:s,pts:u};return h||t.samples.push(f),{sample:f,length:d,missing:h}}}function S(t,e){return(S=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}var L=function(t){var e,r;function i(e,r){var i;return(i=t.call(this)||this).observer=void 0,i.config=void 0,i.observer=e,i.config=r,i}r=t,(e=i).prototype=Object.create(r.prototype),e.prototype.constructor=e,S(e,r);var a=i.prototype;return a.resetInitSegment=function(e,r,i){t.prototype.resetInitSegment.call(this,e,r,i),this._audioTrack={container:"audio/adts",type:"audio",id:2,pid:-1,sequenceNumber:0,isAAC:!0,samples:[],manifestCodec:e,duration:i,inputTimeScale:9e4,dropped:0}},i.probe=function(t){if(!t)return!1;for(var e=(o.b(t,0)||[]).length,r=t.length;e0},e.demux=function(t){var e=t,r={type:"",id:-1,pid:-1,inputTimeScale:9e4,sequenceNumber:-1,samples:[],dropped:0};if(this.config.progressive){this.remainderData&&(e=Object(l.a)(this.remainderData,t));var i=Object(l.h)(e);this.remainderData=i.remainder,r.samples=i.valid||new Uint8Array}else r.samples=e;return{audioTrack:{type:"",id:-1,pid:-1,inputTimeScale:9e4,sequenceNumber:-1,samples:[],dropped:0},avcTrack:r,id3Track:{type:"",id:-1,pid:-1,inputTimeScale:9e4,sequenceNumber:-1,samples:[],dropped:0},textTrack:{type:"",id:-1,pid:-1,inputTimeScale:9e4,sequenceNumber:-1,samples:[],dropped:0}}},e.flush=function(){var t={type:"",id:-1,pid:-1,inputTimeScale:9e4,sequenceNumber:-1,samples:[],dropped:0};return t.samples=this.remainderData||new Uint8Array,this.remainderData=null,{audioTrack:{type:"",id:-1,pid:-1,inputTimeScale:9e4,sequenceNumber:-1,samples:[],dropped:0},avcTrack:t,id3Track:{type:"",id:-1,pid:-1,inputTimeScale:9e4,sequenceNumber:-1,samples:[],dropped:0},textTrack:{type:"",id:-1,pid:-1,inputTimeScale:9e4,sequenceNumber:-1,samples:[],dropped:0}}},e.demuxSampleAes=function(t,e,r){return Promise.reject(new Error("The MP4 demuxer does not support SAMPLE-AES decryption"))},e.destroy=function(){},t}();R.minProbeByteLength=1024;var D=R,_=null,k=[32,64,96,128,160,192,224,256,288,320,352,384,416,448,32,48,56,64,80,96,112,128,160,192,224,256,320,384,32,40,48,56,64,80,96,112,128,160,192,224,256,320,32,48,56,64,80,96,112,128,144,160,176,192,224,256,8,16,24,32,40,48,56,64,80,96,112,128,144,160],x=[44100,48e3,32e3,22050,24e3,16e3,11025,12e3,8e3],C=[[0,72,144,12],[0,0,0,0],[0,72,144,12],[0,144,144,12]],w=[0,1,1,4];function O(t,e,r,i,a){if(!(r+24>e.length)){var n=I(e,r);if(n&&r+n.frameLength<=e.length){var s=i+a*(9e4*n.samplesPerFrame/n.sampleRate),o={unit:e.subarray(r,r+n.frameLength),pts:s,dts:s};return t.config=[],t.channelCount=n.channelCount,t.samplerate=n.sampleRate,t.samples.push(o),{sample:o,length:n.frameLength,missing:0}}}}function I(t,e){var r=t[e+1]>>3&3,i=t[e+1]>>1&3,a=t[e+2]>>4&15,n=t[e+2]>>2&3;if(1!==r&&0!==a&&15!==a&&3!==n){var s=t[e+2]>>1&1,o=t[e+3]>>6,l=1e3*k[14*(3===r?3-i:3===i?3:4)+a-1],u=x[3*(3===r?0:2===r?1:2)+n],d=3===o?1:2,h=C[r][i],f=w[i],c=8*h*f,v=Math.floor(h*l/u+s)*f;if(null===_){var g=(navigator.userAgent||"").match(/Chrome\/(\d+)/i);_=g?parseInt(g[1]):0}return!!_&&_<=87&&2===i&&l>=224e3&&0===o&&(t[e+3]=128|t[e+3]),{sampleRate:u,channelCount:d,frameLength:v,samplesPerFrame:c}}}function P(t,e){return 255===t[e]&&224==(224&t[e+1])&&0!=(6&t[e+1])}function F(t,e){return e+1t?(this.word<<=t,this.bitsAvailable-=t):(t-=this.bitsAvailable,t-=(e=t>>3)>>3,this.bytesAvailable-=e,this.loadWord(),this.word<<=t,this.bitsAvailable-=t)},e.readBits=function(t){var e=Math.min(this.bitsAvailable,t),r=this.word>>>32-e;return t>32&&c.b.error("Cannot read more than 32 bits at a time"),this.bitsAvailable-=e,this.bitsAvailable>0?this.word<<=e:this.bytesAvailable>0&&this.loadWord(),(e=t-e)>0&&this.bitsAvailable?r<>>t))return this.word<<=t,this.bitsAvailable-=t,t;return this.loadWord(),t+this.skipLZ()},e.skipUEG=function(){this.skipBits(1+this.skipLZ())},e.skipEG=function(){this.skipBits(1+this.skipLZ())},e.readUEG=function(){var t=this.skipLZ();return this.readBits(t+1)-1},e.readEG=function(){var t=this.readUEG();return 1&t?1+t>>>1:-1*(t>>>1)},e.readBoolean=function(){return 1===this.readBits(1)},e.readUByte=function(){return this.readBits(8)},e.readUShort=function(){return this.readBits(16)},e.readUInt=function(){return this.readBits(32)},e.skipScalingList=function(t){for(var e=8,r=8,i=0;i=t.length)return void r();if(!(t[e].unit.length<32)){var i=this.decrypter.isSync();if(this.decryptAacSample(t,e,r,i),!i)return}}},e.getAvcEncryptedData=function(t){for(var e=16*Math.floor((t.length-48)/160)+16,r=new Int8Array(e),i=0,a=32;a=t.length)return void i();for(var a=t[e].units;!(r>=a.length);r++){var n=a[r];if(!(n.data.length<=48||1!==n.type&&5!==n.type)){var s=this.decrypter.isSync();if(this.decryptAvcSample(t,e,r,i,n,s),!s)return}}}},t}(),U={video:1,audio:2,id3:3,text:4},G=function(){function t(t,e,r){this.observer=void 0,this.config=void 0,this.typeSupported=void 0,this.sampleAes=null,this.pmtParsed=!1,this.audioCodec=void 0,this.videoCodec=void 0,this._duration=0,this.aacLastPTS=null,this._initPTS=null,this._initDTS=null,this._pmtId=-1,this._avcTrack=void 0,this._audioTrack=void 0,this._id3Track=void 0,this._txtTrack=void 0,this.aacOverFlow=null,this.avcSample=null,this.remainderData=null,this.observer=t,this.config=e,this.typeSupported=r}t.probe=function(e){var r=t.syncOffset(e);return!(r<0)&&(r&&c.b.warn("MPEG2-TS detected but first sync word found @ offset "+r+", junk ahead ?"),!0)},t.syncOffset=function(t){for(var e=Math.min(1e3,t.length-564),r=0;r>4>1){if((k=R+5+e[R+4])===R+188)continue}else k=R+4;switch(_){case f:D&&(v&&(o=V(v))&&this.parseAVCPES(o,!1),v={data:[],size:0}),v&&(v.data.push(e.subarray(k,R+188)),v.size+=R+188-k);break;case g:D&&(m&&(o=V(m))&&(d.isAAC?this.parseAACPES(o):this.parseMPEGPES(o)),m={data:[],size:0}),m&&(m.data.push(e.subarray(k,R+188)),m.size+=R+188-k);break;case p:D&&(y&&(o=V(y))&&this.parseID3PES(o),y={data:[],size:0}),y&&(y.data.push(e.subarray(k,R+188)),y.size+=R+188-k);break;case 0:D&&(k+=e[k]+1),E=this._pmtId=H(e,k);break;case E:D&&(k+=e[k]+1);var x=K(e,k,!0===this.typeSupported.mpeg||!0===this.typeSupported.mp3,n);(f=x.avc)>0&&(u.pid=f),(g=x.audio)>0&&(d.pid=g,d.isAAC=x.isAAC),(p=x.id3)>0&&(h.pid=p),b&&!T&&(c.b.log("reparse from beginning"),b=!1,R=L-188),T=this.pmtParsed=!0;break;case 17:case 8191:break;default:b=!0}}else A++;A>0&&this.observer.emit(i.a.ERROR,i.a.ERROR,{type:a.b.MEDIA_ERROR,details:a.a.FRAG_PARSING_ERROR,fatal:!1,reason:"Found "+A+" TS packet/s that do not start with 0x47"}),u.pesData=v,d.pesData=m,h.pesData=y;var C={audioTrack:d,avcTrack:u,id3Track:h,textTrack:this._txtTrack};return s&&this.extractRemainingSamples(C),C},e.flush=function(){var t,e=this.remainderData;return this.remainderData=null,t=e?this.demux(e,-1,!1,!0):{audioTrack:this._audioTrack,avcTrack:this._avcTrack,textTrack:this._txtTrack,id3Track:this._id3Track},this.extractRemainingSamples(t),this.sampleAes?this.decrypt(t,this.sampleAes):t},e.extractRemainingSamples=function(t){var e,r=t.audioTrack,i=t.avcTrack,a=t.id3Track,n=i.pesData,s=r.pesData,o=a.pesData;n&&(e=V(n))?(this.parseAVCPES(e,!0),i.pesData=null):i.pesData=n,s&&(e=V(s))?(r.isAAC?this.parseAACPES(e):this.parseMPEGPES(e),r.pesData=null):(null!=s&&s.size&&c.b.log("last AAC PES packet truncated,might overlap between fragments"),r.pesData=s),o&&(e=V(o))?(this.parseID3PES(e),a.pesData=null):a.pesData=o},e.demuxSampleAes=function(t,e,r){var i=this.demux(t,r,!0,!this.config.progressive),a=this.sampleAes=new B(this.observer,this.config,e);return this.decrypt(i,a)},e.decrypt=function(t,e){return new Promise((function(r){var i=t.audioTrack,a=t.avcTrack;i.samples&&i.isAAC?e.decryptAacSamples(i.samples,0,(function(){a.samples?e.decryptAvcSamples(a.samples,0,0,(function(){r(t)})):r(t)})):a.samples&&e.decryptAvcSamples(a.samples,0,0,(function(){r(t)}))}))},e.destroy=function(){this._initPTS=this._initDTS=null,this._duration=0},e.parseAVCPES=function(t,e){var r,i=this,a=this._avcTrack,n=this.parseAVCNALu(t.data),s=this.avcSample,l=!1;t.data=null,s&&n.length&&!a.audFound&&(W(s,a),s=this.avcSample=j(!1,t.pts,t.dts,"")),n.forEach((function(e){switch(e.type){case 1:r=!0,s||(s=i.avcSample=j(!0,t.pts,t.dts,"")),s.frame=!0;var n=e.data;if(l&&n.length>4){var u=new N(n).readSliceType();2!==u&&4!==u&&7!==u&&9!==u||(s.key=!0)}break;case 5:r=!0,s||(s=i.avcSample=j(!0,t.pts,t.dts,"")),s.key=!0,s.frame=!0;break;case 6:r=!0;var d=new N(X(e.data));d.readUByte();for(var h=0,f=0,c=!1,v=0;!c&&d.bytesAvailable>1;){h=0;do{h+=v=d.readUByte()}while(255===v);f=0;do{f+=v=d.readUByte()}while(255===v);if(4===h&&0!==d.bytesAvailable){if(c=!0,181===d.readUByte())if(49===d.readUShort())if(1195456820===d.readUInt())if(3===d.readUByte()){for(var g=d.readUByte(),p=31&g,m=[g,d.readUByte()],y=0;y16){for(var b=[],T=0;T<16;T++)b.push(d.readUByte().toString(16)),3!==T&&5!==T&&7!==T&&9!==T||b.push("-");for(var E=f-16,S=new Uint8Array(E),L=0;L=0){var h={data:t.subarray(u,l-n-1),type:d};o.push(h)}else{var f=this.getLastNalUnit();if(f&&(s&&l<=4-s&&f.state&&(f.data=f.data.subarray(0,f.data.byteLength-s)),(r=l-n-1)>0)){var c=new Uint8Array(f.data.byteLength+r);c.set(f.data,0),c.set(t.subarray(0,r),f.data.byteLength),f.data=c,f.state=0}}l=0&&n>=0){var v={data:t.subarray(u,i),type:d,state:n};o.push(v)}if(0===o.length){var g=this.getLastNalUnit();if(g){var p=new Uint8Array(g.data.byteLength+t.byteLength);p.set(g.data,0),p.set(t,g.data.byteLength),g.data=p}}return a.naluState=n,o},e.parseAACPES=function(t){var e,r,n,s,o,l=0,u=this._audioTrack,d=this.aacOverFlow,h=t.data;if(d){this.aacOverFlow=null;var f=d.sample.unit.byteLength,v=Math.min(d.missing,f),g=f-v;d.sample.unit.set(h.subarray(0,v),g),u.samples.push(d.sample),l=d.missing}for(e=l,r=h.length;e1;){var l=new Uint8Array(o[0].length+o[1].length);l.set(o[0]),l.set(o[1],o[0].length),o[0]=l,o.splice(1,1)}if(1===((e=o[0])[0]<<16)+(e[1]<<8)+e[2]){if((r=(e[4]<<8)+e[5])&&r>t.size-6)return null;var u=e[7];192&u&&(a=536870912*(14&e[9])+4194304*(255&e[10])+16384*(254&e[11])+128*(255&e[12])+(254&e[13])/2,64&u?a-(n=536870912*(14&e[14])+4194304*(255&e[15])+16384*(254&e[16])+128*(255&e[17])+(254&e[18])/2)>54e5&&(c.b.warn(Math.round((a-n)/9e4)+"s delta between PTS and DTS, align them"),a=n):n=a);var d=(i=e[8])+9;if(t.size<=d)return null;t.size-=d;for(var h=new Uint8Array(t.size),f=0,v=o.length;fg){d-=g;continue}e=e.subarray(d),g-=d,d=0}h.set(e,s),s+=g}return r&&(r-=i+3),{data:h,pts:a,dts:n,len:r}}return null}function W(t,e){if(t.units.length&&t.frame){if(void 0===t.pts){var r=e.samples,i=r.length;if(!i)return void e.dropped++;var a=r[i-1];t.pts=a.pts,t.dts=a.dts}e.samples.push(t)}t.debug.length&&c.b.log(t.pts+"/"+t.dts+":"+t.debug)}function Y(t,e){var r=t.length;if(r>0){if(e.pts>=t[r-1].pts)t.push(e);else for(var i=r-1;i>=0;i--)if(e.pts1?r-1:0),a=1;a>24&255,o[1]=e>>16&255,o[2]=e>>8&255,o[3]=255&e,o.set(t,4),n=0,e=8;n>24&255,e>>16&255,e>>8&255,255&e,i>>24,i>>16&255,i>>8&255,255&i,a>>24,a>>16&255,a>>8&255,255&a,85,196,0,0]))},t.mdia=function(e){return t.box(t.types.mdia,t.mdhd(e.timescale,e.duration),t.hdlr(e.type),t.minf(e))},t.mfhd=function(e){return t.box(t.types.mfhd,new Uint8Array([0,0,0,0,e>>24,e>>16&255,e>>8&255,255&e]))},t.minf=function(e){return"audio"===e.type?t.box(t.types.minf,t.box(t.types.smhd,t.SMHD),t.DINF,t.stbl(e)):t.box(t.types.minf,t.box(t.types.vmhd,t.VMHD),t.DINF,t.stbl(e))},t.moof=function(e,r,i){return t.box(t.types.moof,t.mfhd(e),t.traf(i,r))},t.moov=function(e){for(var r=e.length,i=[];r--;)i[r]=t.trak(e[r]);return t.box.apply(null,[t.types.moov,t.mvhd(e[0].timescale,e[0].duration)].concat(i).concat(t.mvex(e)))},t.mvex=function(e){for(var r=e.length,i=[];r--;)i[r]=t.trex(e[r]);return t.box.apply(null,[t.types.mvex].concat(i))},t.mvhd=function(e,r){r*=e;var i=Math.floor(r/(J+1)),a=Math.floor(r%(J+1)),n=new Uint8Array([1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,3,e>>24&255,e>>16&255,e>>8&255,255&e,i>>24,i>>16&255,i>>8&255,255&i,a>>24,a>>16&255,a>>8&255,255&a,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255]);return t.box(t.types.mvhd,n)},t.sdtp=function(e){var r,i,a=e.samples||[],n=new Uint8Array(4+a.length);for(r=0;r>>8&255),n.push(255&a),n=n.concat(Array.prototype.slice.call(i));for(r=0;r>>8&255),s.push(255&a),s=s.concat(Array.prototype.slice.call(i));var o=t.box(t.types.avcC,new Uint8Array([1,n[3],n[4],n[5],255,224|e.sps.length].concat(n).concat([e.pps.length]).concat(s))),l=e.width,u=e.height,d=e.pixelRatio[0],h=e.pixelRatio[1];return t.box(t.types.avc1,new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,l>>8&255,255&l,u>>8&255,255&u,0,72,0,0,0,72,0,0,0,0,0,0,0,1,18,100,97,105,108,121,109,111,116,105,111,110,47,104,108,115,46,106,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,17,17]),o,t.box(t.types.btrt,new Uint8Array([0,28,156,128,0,45,198,192,0,45,198,192])),t.box(t.types.pasp,new Uint8Array([d>>24,d>>16&255,d>>8&255,255&d,h>>24,h>>16&255,h>>8&255,255&h])))},t.esds=function(t){var e=t.config.length;return new Uint8Array([0,0,0,0,3,23+e,0,1,0,4,15+e,64,21,0,0,0,0,0,0,0,0,0,0,0,5].concat([e]).concat(t.config).concat([6,1,2]))},t.mp4a=function(e){var r=e.samplerate;return t.box(t.types.mp4a,new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,e.channelCount,0,16,0,0,0,0,r>>8&255,255&r,0,0]),t.box(t.types.esds,t.esds(e)))},t.mp3=function(e){var r=e.samplerate;return t.box(t.types[".mp3"],new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,e.channelCount,0,16,0,0,0,0,r>>8&255,255&r,0,0]))},t.stsd=function(e){return"audio"===e.type?e.isAAC||"mp3"!==e.codec?t.box(t.types.stsd,t.STSD,t.mp4a(e)):t.box(t.types.stsd,t.STSD,t.mp3(e)):t.box(t.types.stsd,t.STSD,t.avc1(e))},t.tkhd=function(e){var r=e.id,i=e.duration*e.timescale,a=e.width,n=e.height,s=Math.floor(i/(J+1)),o=Math.floor(i%(J+1));return t.box(t.types.tkhd,new Uint8Array([1,0,0,7,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,3,r>>24&255,r>>16&255,r>>8&255,255&r,0,0,0,0,s>>24,s>>16&255,s>>8&255,255&s,o>>24,o>>16&255,o>>8&255,255&o,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,a>>8&255,255&a,0,0,n>>8&255,255&n,0,0]))},t.traf=function(e,r){var i=t.sdtp(e),a=e.id,n=Math.floor(r/(J+1)),s=Math.floor(r%(J+1));return t.box(t.types.traf,t.box(t.types.tfhd,new Uint8Array([0,0,0,0,a>>24,a>>16&255,a>>8&255,255&a])),t.box(t.types.tfdt,new Uint8Array([1,0,0,0,n>>24,n>>16&255,n>>8&255,255&n,s>>24,s>>16&255,s>>8&255,255&s])),t.trun(e,i.length+16+20+8+16+8+8),i)},t.trak=function(e){return e.duration=e.duration||4294967295,t.box(t.types.trak,t.tkhd(e),t.mdia(e))},t.trex=function(e){var r=e.id;return t.box(t.types.trex,new Uint8Array([0,0,0,0,r>>24,r>>16&255,r>>8&255,255&r,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1]))},t.trun=function(e,r){var i,a,n,s,o,l,u=e.samples||[],d=u.length,h=12+16*d,f=new Uint8Array(h);for(r+=8+h,f.set([0,0,15,1,d>>>24&255,d>>>16&255,d>>>8&255,255&d,r>>>24&255,r>>>16&255,r>>>8&255,255&r],0),i=0;i>>24&255,n>>>16&255,n>>>8&255,255&n,s>>>24&255,s>>>16&255,s>>>8&255,255&s,o.isLeading<<2|o.dependsOn,o.isDependedOn<<6|o.hasRedundancy<<4|o.paddingValue<<1|o.isNonSync,61440&o.degradPrio,15&o.degradPrio,l>>>24&255,l>>>16&255,l>>>8&255,255&l],12+16*i);return t.box(t.types.trun,f)},t.initSegment=function(e){t.types||t.init();var r=t.moov(e),i=new Uint8Array(t.FTYP.byteLength+r.byteLength);return i.set(t.FTYP),i.set(r,t.FTYP.byteLength),i},t}();tt.types=void 0,tt.HDLR_TYPES=void 0,tt.STTS=void 0,tt.STSC=void 0,tt.STCO=void 0,tt.STSZ=void 0,tt.VMHD=void 0,tt.SMHD=void 0,tt.STSD=void 0,tt.FTYP=void 0,tt.DINF=void 0;var et=tt,rt=r(4);function it(t,e,r,i){void 0===r&&(r=1),void 0===i&&(i=!1);var a=t*e*r;return i?Math.round(a):a}function at(t,e){return void 0===e&&(e=!1),it(t,1e3,1/9e4,e)}function nt(){return(nt=Object.assign||function(t){for(var e=1;e0?t:r.pts}),t[0].pts);return e&&c.b.debug("PTS rollover detected"),r},e.remux=function(t,e,r,i,a,n,s,o){var l,u,d,h,f,v,g=a,p=a,m=t.pid>-1,y=e.pid>-1,b=e.samples.length,T=t.samples.length>0,E=b>1;if((!m||T)&&(!y||E)||this.ISGenerated||s){this.ISGenerated||(d=this.generateIS(t,e,a));var S=this.isVideoContiguous,L=-1;if(E&&(L=function(t){for(var e=0;e0){c.b.warn("[mp4-remuxer]: Dropped "+L+" out of "+b+" video samples due to a missing keyframe");var A=this.getVideoStartPts(e.samples);e.samples=e.samples.slice(L),e.dropped+=L,p+=(e.samples[0].pts-A)/(e.timescale||9e4)}else-1===L&&(c.b.warn("[mp4-remuxer]: No keyframe found out of "+b+" video samples"),v=!1);if(this.ISGenerated){if(T&&E){var R=this.getVideoStartPts(e.samples),D=(dt(t.samples[0].pts,R)-R)/e.inputTimeScale;g+=Math.max(0,D),p+=Math.max(0,-D)}if(T){if(t.samplerate||(c.b.warn("[mp4-remuxer]: regenerate InitSegment as audio detected"),d=this.generateIS(t,e,a)),u=this.remuxAudio(t,g,this.isAudioContiguous,n,y||E||o===rt.b.AUDIO?p:void 0),E){var _=u?u.endPTS-u.startPTS:0;e.inputTimeScale||(c.b.warn("[mp4-remuxer]: regenerate InitSegment as video detected"),d=this.generateIS(t,e,a)),l=this.remuxVideo(e,p,S,_)}}else E&&(l=this.remuxVideo(e,p,S,0));l&&(l.firstKeyFrame=L,l.independent=-1!==L)}}return this.ISGenerated&&(r.samples.length&&(f=this.remuxID3(r,a)),i.samples.length&&(h=this.remuxText(i,a))),{audio:u,video:l,initSegment:d,independent:v,text:h,id3:f}},e.generateIS=function(t,e,r){var i,a,n,o=t.samples,l=e.samples,u=this.typeSupported,d={},h=!Object(s.a)(this._initPTS),f="audio/mp4";if(h&&(i=a=1/0),t.config&&o.length&&(t.timescale=t.samplerate,t.isAAC||(u.mpeg?(f="audio/mpeg",t.codec=""):u.mp3&&(t.codec="mp3")),d.audio={id:"audio",container:f,codec:t.codec,initSegment:!t.isAAC&&u.mpeg?new Uint8Array(0):et.initSegment([t]),metadata:{channelCount:t.channelCount}},h&&(n=t.inputTimeScale,i=a=o[0].pts-Math.round(n*r))),e.sps&&e.pps&&l.length&&(e.timescale=e.inputTimeScale,d.video={id:"main",container:"video/mp4",codec:e.codec,initSegment:et.initSegment([e]),metadata:{width:e.width,height:e.height}},h)){n=e.inputTimeScale;var c=this.getVideoStartPts(l),v=Math.round(n*r);a=Math.min(a,dt(l[0].dts,c)-v),i=Math.min(i,c-v)}if(Object.keys(d).length)return this.ISGenerated=!0,h&&(this._initPTS=i,this._initDTS=a),{tracks:d,initPTS:i,timescale:n}},e.remuxVideo=function(t,e,r,n){var s,o,l,u=t.inputTimeScale,d=t.samples,h=[],f=d.length,v=this._initPTS,g=this.nextAvcDts,p=8,m=Number.POSITIVE_INFINITY,y=Number.NEGATIVE_INFINITY,b=0,T=!1;r&&null!==g||(g=e*u-(d[0].pts-dt(d[0].dts,d[0].pts)));for(var E=0;ES.pts){b=Math.max(Math.min(b,S.pts-S.dts),-18e3)}S.dts0?E-1:E].dts&&(T=!0)}T&&d.sort((function(t,e){var r=t.dts-e.dts,i=t.pts-e.pts;return r||i})),o=d[0].dts,l=d[d.length-1].dts;var L=Math.round((l-o)/(f-1));if(b<0){if(b<-2*L){c.b.warn("PTS < DTS detected in video samples, offsetting DTS from PTS by "+at(-L,!0)+" ms");for(var A=b,R=0;RL;if(k||_<-1){k?c.b.warn("AVC: "+at(_,!0)+" ms ("+_+"dts) hole between fragments detected, filling it"):c.b.warn("AVC: "+at(-_,!0)+" ms ("+_+"dts) overlapping between fragments detected"),o=g;var x=d[0].pts-_;d[0].dts=o,d[0].pts=x,c.b.log("Video: First PTS/DTS adjusted: "+at(x,!0)+"/"+at(o,!0)+", delta: "+at(_,!0)+" ms")}}lt&&(o=Math.max(0,o));for(var C=0,w=0,O=0;O0?j-1:j].dts;if(Q.stretchShortVideoTrack&&null!==this.nextAudioPts){var Z=Math.floor(Q.maxBufferHole*u),J=(n?m+n*u:this.nextAudioPts)-H.pts;J>Z?((s=J-$)<0&&(s=$),c.b.log("[mp4-remuxer]: It is approximately "+J/90+" ms to the next segment; using duration "+s/90+" ms for the last video frame.")):s=$}else s=$}var tt=Math.round(H.pts-H.dts);h.push(new ht(H.key,s,V,tt))}if(h.length&&st&&st<70){var rt=h[0].flags;rt.dependsOn=2,rt.isNonSync=0}this.nextAvcDts=g=l+s,this.isVideoContiguous=!0;var it={data1:et.moof(t.sequenceNumber++,o,nt({},t,{samples:h})),data2:B,startPTS:m/u,endPTS:(y+s)/u,startDTS:o/u,endDTS:g/u,type:"video",hasAudio:!1,hasVideo:!0,nb:h.length,dropped:t.dropped};return t.samples=[],t.dropped=0,it},e.remuxAudio=function(t,e,r,n,s){var o=t.inputTimeScale,l=o/(t.samplerate?t.samplerate:o),u=t.isAAC?1024:1152,d=u*l,h=this._initPTS,f=!t.isAAC&&this.typeSupported.mpeg,v=[],g=t.samples,p=f?0:8,m=this.nextAudioPts||-1,y=e*o;if(this.isAudioContiguous=r=r||g.length&&m>0&&(n&&Math.abs(y-m)<9e3||Math.abs(dt(g[0].pts-h,y)-m)<20*d),g.forEach((function(t){t.pts=dt(t.pts-h,y)})),!r||m<0){if(!(g=g.filter((function(t){return t.pts>=0}))).length)return;m=0===s?0:n?Math.max(0,y):g[0].pts}if(t.isAAC)for(var b=void 0!==s,T=this.config.maxAudioFramesDrift,E=0,S=m;E=T*d&&D<1e4&&b){var _=Math.round(R/d);(S=A-_*d)<0&&(_--,S+=d),0===E&&(this.nextAudioPts=m=S),c.b.warn("[mp4-remuxer]: Injecting "+_+" audio frame @ "+(S/o).toFixed(3)+"s due to "+Math.round(1e3*R/o)+" ms gap.");for(var k=0;k<_;k++){var x=Math.max(S,0),C=Z.getSilentFrame(t.manifestCodec||t.codec,t.channelCount);C||(c.b.log("[mp4-remuxer]: Unable to get silent frame for given audio codec; duplicating last frame instead."),C=L.unit.subarray()),g.splice(E,0,{unit:C,pts:x}),S+=d,E++}}L.pts=S,S+=d}for(var w,O=null,I=null,P=0,F=g.length;F--;)P+=g[F].unit.byteLength;for(var M=0,N=g.length;M0))return;P+=p;try{w=new Uint8Array(P)}catch(t){return void this.observer.emit(i.a.ERROR,i.a.ERROR,{type:a.b.MUX_ERROR,details:a.a.REMUX_ALLOC_ERROR,fatal:!1,bytes:P,reason:"fail allocating audio mdat "+P})}f||(new DataView(w.buffer).setUint32(0,P),w.set(et.types.mdat,4))}w.set(U,p);var j=U.byteLength;p+=j,v.push(new ht(!0,u,j,0)),I=G}var H=v.length;if(H){var K=v[v.length-1];this.nextAudioPts=m=I+l*K.duration;var V=f?new Uint8Array(0):et.moof(t.sequenceNumber++,O/l,nt({},t,{samples:v}));t.samples=[];var W=O/o,Y=m/o,X={data1:V,data2:w,startPTS:W,endPTS:Y,startDTS:W,endDTS:Y,type:"audio",hasAudio:!0,hasVideo:!1,nb:H};return this.isAudioContiguous=!0,X}},e.remuxEmptyAudio=function(t,e,r,i){var a=t.inputTimeScale,n=a/(t.samplerate?t.samplerate:a),s=this.nextAudioPts,o=(null!==s?s:i.startDTS*a)+this._initDTS,l=i.endDTS*a+this._initDTS,u=1024*n,d=Math.ceil((l-o)/u),h=Z.getSilentFrame(t.manifestCodec||t.codec,t.channelCount);if(c.b.warn("[mp4-remuxer]: remux empty Audio"),h){for(var f=[],v=0;v4294967296;)t+=r;return t}var ht=function(t,e,r,i){this.size=void 0,this.duration=void 0,this.cts=void 0,this.flags=void 0,this.duration=e,this.size=r,this.cts=i,this.flags=new ft(t)},ft=function(t){this.isLeading=0,this.isDependedOn=0,this.hasRedundancy=0,this.degradPrio=0,this.dependsOn=1,this.isNonSync=1,this.dependsOn=t?2:1,this.isNonSync=t?0:1},ct=r(5),vt=function(){function t(){this.emitInitSegment=!1,this.audioCodec=void 0,this.videoCodec=void 0,this.initData=void 0,this.initPTS=void 0,this.initTracks=void 0,this.lastEndDTS=null}var e=t.prototype;return e.destroy=function(){},e.resetTimeStamp=function(t){this.initPTS=t,this.lastEndDTS=null},e.resetNextTimestamp=function(){this.lastEndDTS=null},e.resetInitSegment=function(t,e,r){this.audioCodec=e,this.videoCodec=r,this.generateInitSegment(t),this.emitInitSegment=!0},e.generateInitSegment=function(t){var e=this.audioCodec,r=this.videoCodec;if(!t||!t.byteLength)return this.initTracks=void 0,void(this.initData=void 0);var i=this.initData=Object(l.f)(t);e||(e=pt(i.audio,ct.a.AUDIO)),r||(r=pt(i.video,ct.a.VIDEO));var a={};i.audio&&i.video?a.audiovideo={container:"video/mp4",codec:e+","+r,initSegment:t,id:"main"}:i.audio?a.audio={container:"audio/mp4",codec:e,initSegment:t,id:"audio"}:i.video?a.video={container:"video/mp4",codec:r,initSegment:t,id:"main"}:c.b.warn("[passthrough-remuxer.ts]: initSegment does not contain moov or trak boxes."),this.initTracks=a},e.remux=function(t,e,r,i,a){var n=this.initPTS,o=this.lastEndDTS,u={audio:void 0,video:void 0,text:i,id3:r,initSegment:void 0};Object(s.a)(o)||(o=this.lastEndDTS=a||0);var d=e.samples;if(!d||!d.length)return u;var h={initPTS:void 0,timescale:1},f=this.initData;if(f&&f.length||(this.generateInitSegment(d),f=this.initData),!f||!f.length)return c.b.warn("[passthrough-remuxer.ts]: Failed to generate initSegment."),u;this.emitInitSegment&&(h.tracks=this.initTracks,this.emitInitSegment=!1),Object(s.a)(n)||(this.initPTS=h.initPTS=n=gt(f,d,o));var v=Object(l.c)(d,f),g=o,p=v+g;Object(l.e)(f,d,n),v>0?this.lastEndDTS=p:(c.b.warn("Duration parsed from mp4 should be greater than zero"),this.resetNextTimestamp());var m=!!f.audio,y=!!f.video,b="";m&&(b+="audio"),y&&(b+="video");var T={data1:d,startPTS:g,startDTS:g,endPTS:p,endDTS:p,type:b,hasAudio:m,hasVideo:y,nb:1,dropped:0};return u.audio="audio"===T.type?T:void 0,u.video="audio"!==T.type?T:void 0,u.text=i,u.id3=r,u.initSegment=h,u},t}(),gt=function(t,e,r){return Object(l.d)(t,e)-r};function pt(t,e){var r=null==t?void 0:t.codec;return r&&r.length>4?r:"hvc1"===r?"hvc1.1.c.L120.90":"av01"===r?"av01.0.04M.08":"avc1"===r||e===ct.a.VIDEO?"avc1.42e01e":"mp4a.40.5"}var mt,yt=vt,bt=r(16);try{mt=self.performance.now.bind(self.performance)}catch(t){c.b.debug("Unable to use Performance API on this environment"),mt=self.Date.now}var Tt=[{demux:q,remux:ut},{demux:D,remux:yt},{demux:A,remux:ut},{demux:$,remux:ut}],Et=1024;Tt.forEach((function(t){var e=t.demux;Et=Math.max(Et,e.minProbeByteLength)}));var St=function(){function t(t,e,r,i,a){this.observer=void 0,this.typeSupported=void 0,this.config=void 0,this.vendor=void 0,this.id=void 0,this.demuxer=void 0,this.remuxer=void 0,this.decrypter=void 0,this.probe=void 0,this.decryptionPromise=null,this.transmuxConfig=void 0,this.currentTransmuxState=void 0,this.cache=new bt.a,this.observer=t,this.typeSupported=e,this.config=r,this.vendor=i,this.id=a}var e=t.prototype;return e.configure=function(t){this.transmuxConfig=t,this.decrypter&&this.decrypter.reset()},e.push=function(t,e,r,i){var a=this,n=r.transmuxing;n.executeStart=mt();var s=new Uint8Array(t),o=this.cache,u=this.config,d=this.currentTransmuxState,h=this.transmuxConfig;i&&(this.currentTransmuxState=i);var f=function(t,e){var r=null;t.byteLength>0&&null!=e&&null!=e.key&&null!==e.iv&&null!=e.method&&(r=e);return r}(s,e);if(f&&"AES-128"===f.method){var c=this.getDecrypter();if(!u.enableSoftwareAES)return this.decryptionPromise=c.webCryptoDecrypt(s,f.key.buffer,f.iv.buffer).then((function(t){var e=a.push(t,null,r);return a.decryptionPromise=null,e})),this.decryptionPromise;var v=c.softwareDecrypt(s,f.key.buffer,f.iv.buffer);if(!v)return n.executeEnd=mt(),Lt(r);s=new Uint8Array(v)}var g=i||d,p=g.contiguous,m=g.discontinuity,y=g.trackSwitch,b=g.accurateTimeOffset,T=g.timeOffset,E=g.initSegmentChange,S=h.audioCodec,L=h.videoCodec,A=h.defaultInitPts,R=h.duration,D=h.initSegmentData;if((m||y||E)&&this.resetInitSegment(D,S,L,R),(m||E)&&this.resetInitialTimestamp(A),p||this.resetContiguity(),this.needsProbing(s,m,y)){if(o.dataLength){var _=o.flush();s=Object(l.a)(_,s)}this.configureTransmuxer(s,h)}var k=this.transmux(s,f,T,b,r),x=this.currentTransmuxState;return x.contiguous=!0,x.discontinuity=!1,x.trackSwitch=!1,n.executeEnd=mt(),k},e.flush=function(t){var e=this,r=t.transmuxing;r.executeStart=mt();var n=this.decrypter,s=this.cache,o=this.currentTransmuxState,l=this.decryptionPromise;if(l)return l.then((function(){return e.flush(t)}));var u=[],d=o.timeOffset;if(n){var h=n.flush();h&&u.push(this.push(h,null,t))}var f=s.dataLength;s.reset();var c=this.demuxer,v=this.remuxer;if(!c||!v)return f>=Et&&this.observer.emit(i.a.ERROR,i.a.ERROR,{type:a.b.MEDIA_ERROR,details:a.a.FRAG_PARSING_ERROR,fatal:!0,reason:"no demux matching with content found"}),r.executeEnd=mt(),[Lt(t)];var g=c.flush(d);return At(g)?g.then((function(r){return e.flushRemux(u,r,t),u})):(this.flushRemux(u,g,t),u)},e.flushRemux=function(t,e,r){var i=e.audioTrack,a=e.avcTrack,n=e.id3Track,s=e.textTrack,o=this.currentTransmuxState,l=o.accurateTimeOffset,u=o.timeOffset;c.b.log("[transmuxer.ts]: Flushed fragment "+r.sn+(r.part>-1?" p: "+r.part:"")+" of level "+r.level);var d=this.remuxer.remux(i,a,n,s,u,l,!0,this.id);t.push({remuxResult:d,chunkMeta:r}),r.transmuxing.executeEnd=mt()},e.resetInitialTimestamp=function(t){var e=this.demuxer,r=this.remuxer;e&&r&&(e.resetTimeStamp(t),r.resetTimeStamp(t))},e.resetContiguity=function(){var t=this.demuxer,e=this.remuxer;t&&e&&(t.resetContiguity(),e.resetNextTimestamp())},e.resetInitSegment=function(t,e,r,i){var a=this.demuxer,n=this.remuxer;a&&n&&(a.resetInitSegment(e,r,i),n.resetInitSegment(t,e,r))},e.destroy=function(){this.demuxer&&(this.demuxer.destroy(),this.demuxer=void 0),this.remuxer&&(this.remuxer.destroy(),this.remuxer=void 0)},e.transmux=function(t,e,r,i,a){return e&&"SAMPLE-AES"===e.method?this.transmuxSampleAes(t,e,r,i,a):this.transmuxUnencrypted(t,r,i,a)},e.transmuxUnencrypted=function(t,e,r,i){var a=this.demuxer.demux(t,e,!1,!this.config.progressive),n=a.audioTrack,s=a.avcTrack,o=a.id3Track,l=a.textTrack;return{remuxResult:this.remuxer.remux(n,s,o,l,e,r,!1,this.id),chunkMeta:i}},e.transmuxSampleAes=function(t,e,r,i,a){var n=this;return this.demuxer.demuxSampleAes(t,e,r).then((function(t){return{remuxResult:n.remuxer.remux(t.audioTrack,t.avcTrack,t.id3Track,t.textTrack,r,i,!1,n.id),chunkMeta:a}}))},e.configureTransmuxer=function(t,e){for(var r,i=this.config,a=this.observer,n=this.typeSupported,s=this.vendor,o=e.audioCodec,l=e.defaultInitPts,u=e.duration,d=e.initSegmentData,h=e.videoCodec,f=0,v=Tt.length;f>>8^255&p^99,t[c]=p,e[p]=c;var m=f[c],y=f[m],b=f[y],T=257*f[p]^16843008*p;i[c]=T<<24|T>>>8,a[c]=T<<16|T>>>16,n[c]=T<<8|T>>>24,s[c]=T,T=16843009*b^65537*y^257*m^16843008*c,l[p]=T<<24|T>>>8,u[p]=T<<16|T>>>16,d[p]=T<<8|T>>>24,h[p]=T,c?(c=m^f[f[f[b^m]]],v^=f[f[v]]):c=v=1}},e.expandKey=function(t){for(var e=this.uint8ArrayToUint32Array_(t),r=!0,i=0;i0}),!1)}t.exports=function(t,e){e=e||{};var a={main:r.m},o=e.all?{main:Object.keys(a.main)}:function(t,e){for(var r={main:[e]},i={main:[]},a={main:{}};s(r);)for(var o=Object.keys(r),l=0;lt.endSN||e>0||0===e&&r>0,this.updated||this.advanced?this.misses=Math.floor(.6*t.misses):this.misses=t.misses+1,this.availabilityDelay=t.availabilityDelay},e=t,(r=[{key:"hasProgramDateTime",get:function(){return!!this.fragments.length&&Object(a.a)(this.fragments[this.fragments.length-1].programDateTime)}},{key:"levelTargetDuration",get:function(){return this.averagetargetduration||this.targetduration||10}},{key:"drift",get:function(){var t=this.driftEndTime-this.driftStartTime;return t>0?1e3*(this.driftEnd-this.driftStart)/t:1}},{key:"edge",get:function(){return this.partEnd||this.fragmentEnd}},{key:"partEnd",get:function(){var t;return null!==(t=this.partList)&&void 0!==t&&t.length?this.partList[this.partList.length-1].end:this.fragmentEnd}},{key:"fragmentEnd",get:function(){var t;return null!==(t=this.fragments)&&void 0!==t&&t.length?this.fragments[this.fragments.length-1].end:0}},{key:"age",get:function(){return this.advancedDateTime?Math.max(Date.now()-this.advancedDateTime,0)/1e3:0}},{key:"lastPartIndex",get:function(){var t;return null!==(t=this.partList)&&void 0!==t&&t.length?this.partList[this.partList.length-1].index:-1}},{key:"lastPartSn",get:function(){var t;return null!==(t=this.partList)&&void 0!==t&&t.length?this.partList[this.partList.length-1].fragment.sn:this.endSN}}])&&d(e.prototype,r),i&&d(e,i),t}(),f=r(15),c=/^(\d+)x(\d+)$/,v=/\s*(.+?)\s*=((?:\".*?\")|.*?)(?:,|$)/g,g=function(){function t(e){for(var r in"string"==typeof e&&(e=t.parseAttrList(e)),e)e.hasOwnProperty(r)&&(this[r]=e[r])}var e=t.prototype;return e.decimalInteger=function(t){var e=parseInt(this[t],10);return e>Number.MAX_SAFE_INTEGER?1/0:e},e.hexadecimalInteger=function(t){if(this[t]){var e=(this[t]||"0x").slice(2);e=(1&e.length?"0":"")+e;for(var r=new Uint8Array(e.length/2),i=0;iNumber.MAX_SAFE_INTEGER?1/0:e},e.decimalFloatingPoint=function(t){return parseFloat(this[t])},e.optionalFloat=function(t,e){var r=this[t];return r?parseFloat(r):e},e.enumeratedString=function(t){return this[t]},e.bool=function(t){return"YES"===this[t]},e.decimalResolution=function(t){var e=c.exec(this[t]);if(null!==e)return{width:parseInt(e[1],10),height:parseInt(e[2],10)}},t.parseAttrList=function(t){var e,r={};for(v.lastIndex=0;null!==(e=v.exec(t));){var i=e[2];0===i.indexOf('"')&&i.lastIndexOf('"')===i.length-1&&(i=i.slice(1,-1)),r[e[1]]=i}return r},t}(),p={audio:{a3ds:!0,"ac-3":!0,"ac-4":!0,alac:!0,alaw:!0,dra1:!0,"dts+":!0,"dts-":!0,dtsc:!0,dtse:!0,dtsh:!0,"ec-3":!0,enca:!0,g719:!0,g726:!0,m4ae:!0,mha1:!0,mha2:!0,mhm1:!0,mhm2:!0,mlpa:!0,mp4a:!0,"raw ":!0,Opus:!0,samr:!0,sawb:!0,sawp:!0,sevc:!0,sqcp:!0,ssmv:!0,twos:!0,ulaw:!0},video:{avc1:!0,avc2:!0,avc3:!0,avc4:!0,avcp:!0,av01:!0,drac:!0,dvav:!0,dvhe:!0,encv:!0,hev1:!0,hvc1:!0,mjp2:!0,mp4v:!0,mvc1:!0,mvc2:!0,mvc3:!0,mvc4:!0,resv:!0,rv60:!0,s263:!0,svc1:!0,svc2:!0,"vc-1":!0,vp08:!0,vp09:!0},text:{stpp:!0,wvtt:!0}};function m(t,e){return MediaSource.isTypeSupported((e||"video")+'/mp4;codecs="'+t+'"')}var y=/#EXT-X-STREAM-INF:([^\r\n]*)(?:[\r\n](?:#[^\r\n]*)?)*([^\r\n]+)|#EXT-X-SESSION-DATA:([^\r\n]*)[\r\n]+/g,b=/#EXT-X-MEDIA:(.*)/g,T=new RegExp([/#EXTINF:\s*(\d*(?:\.\d+)?)(?:,(.*)\s+)?/.source,/(?!#) *(\S[\S ]*)/.source,/#EXT-X-BYTERANGE:*(.+)/.source,/#EXT-X-PROGRAM-DATE-TIME:(.+)/.source,/#.*/.source].join("|"),"g"),E=new RegExp([/#(EXTM3U)/.source,/#EXT-X-(PLAYLIST-TYPE):(.+)/.source,/#EXT-X-(MEDIA-SEQUENCE): *(\d+)/.source,/#EXT-X-(SKIP):(.+)/.source,/#EXT-X-(TARGETDURATION): *(\d+)/.source,/#EXT-X-(KEY):(.+)/.source,/#EXT-X-(START):(.+)/.source,/#EXT-X-(ENDLIST)/.source,/#EXT-X-(DISCONTINUITY-SEQ)UENCE: *(\d+)/.source,/#EXT-X-(DIS)CONTINUITY/.source,/#EXT-X-(VERSION):(\d+)/.source,/#EXT-X-(MAP):(.+)/.source,/#EXT-X-(SERVER-CONTROL):(.+)/.source,/#EXT-X-(PART-INF):(.+)/.source,/#EXT-X-(GAP)/.source,/#EXT-X-(BITRATE):\s*(\d+)/.source,/#EXT-X-(PART):(.+)/.source,/#EXT-X-(PRELOAD-HINT):(.+)/.source,/#EXT-X-(RENDITION-REPORT):(.+)/.source,/(#)([^:]*):(.*)/.source,/(#)(.*)(?:.*)\r?\n?/.source].join("|")),S=/\.(mp4|m4s|m4v|m4a)$/i;var L=function(){function t(){}return t.findGroup=function(t,e){for(var r=0;r2){var r=e.shift()+".";return r+=parseInt(e.shift()).toString(16),r+=("000"+parseInt(e.shift()).toString(16)).substr(-4)}return t},t.resolve=function(t,e){return i.buildAbsoluteURL(e,t,{alwaysNormalize:!0})},t.parseMasterPlaylist=function(e,r){var i,a=[],n={},s=!1;for(y.lastIndex=0;null!=(i=y.exec(e));)if(i[1]){var o=new g(i[1]),l={attrs:o,bitrate:o.decimalInteger("AVERAGE-BANDWIDTH")||o.decimalInteger("BANDWIDTH"),name:o.NAME,url:t.resolve(i[2],r)},u=o.decimalResolution("RESOLUTION");u&&(l.width=u.width,l.height=u.height),A((o.CODECS||"").split(/[ ,]+/).filter((function(t){return t})),l),l.videoCodec&&-1!==l.videoCodec.indexOf("avc1")&&(l.videoCodec=t.convertAVC1ToAVCOTI(l.videoCodec)),a.push(l)}else if(i[3]){var d=new g(i[3]);d["DATA-ID"]&&(s=!0,n[d["DATA-ID"]]=d)}return{levels:a,sessionData:s?n:null}},t.parseMasterPlaylistMedia=function(e,r,i,a){var n;void 0===a&&(a=[]);var s=[],o=0;for(b.lastIndex=0;null!==(n=b.exec(e));){var l=new g(n[1]);if(l.TYPE===i){var u={attrs:l,bitrate:0,id:o++,groupId:l["GROUP-ID"],instreamId:l["INSTREAM-ID"],name:l.NAME||l.LANGUAGE||"",type:i,default:l.bool("DEFAULT"),autoselect:l.bool("AUTOSELECT"),forced:l.bool("FORCED"),lang:l.LANGUAGE,url:l.URI?t.resolve(l.URI,r):""};if(a.length){var d=t.findGroup(a,u.groupId)||a[0];R(u,d,"audioCodec"),R(u,d,"textCodec")}s.push(u)}}return s},t.parseLevelPlaylist=function(t,e,r,n,s){var l,d,c,v=new h(e),p=v.fragments,m=null,y=0,b=0,L=0,A=0,R=null,_=new u.b(n,e),k=-1,x=!1;for(T.lastIndex=0,v.m3u8=t;null!==(l=T.exec(t));){x&&(x=!1,(_=new u.b(n,e)).start=L,_.sn=y,_.cc=A,_.level=r,m&&(_.initSegment=m,_.rawProgramDateTime=m.rawProgramDateTime));var C=l[1];if(C){_.duration=parseFloat(C);var w=(" "+l[2]).slice(1);_.title=w||null,_.tagList.push(w?["INF",C,w]:["INF",C])}else if(l[3])Object(a.a)(_.duration)&&(_.start=L,c&&(_.levelkey=c),_.sn=y,_.level=r,_.cc=A,_.urlId=s,p.push(_),_.relurl=(" "+l[3]).slice(1),D(_,R),R=_,L+=_.duration,y++,b=0,x=!0);else if(l[4]){var O=(" "+l[4]).slice(1);R?_.setByteRange(O,R):_.setByteRange(O)}else if(l[5])_.rawProgramDateTime=(" "+l[5]).slice(1),_.tagList.push(["PROGRAM-DATE-TIME",_.rawProgramDateTime]),-1===k&&(k=p.length);else{if(!(l=l[0].match(E))){o.b.warn("No matches on slow regex match for level playlist!");continue}for(d=1;d-1){o.b.warn("Keyformat "+X+" is not supported from the manifest");continue}if("identity"!==X)continue;H&&(c=f.a.fromURL(e,K),K&&["AES-128","SAMPLE-AES","SAMPLE-AES-CENC"].indexOf(H)>=0&&(c.method=H,c.keyFormat=X,Y&&(c.keyID=Y),W&&(c.keyFormatVersions=W),c.iv=V));break;case"START":var q=new g(P).decimalFloatingPoint("TIME-OFFSET");Object(a.a)(q)&&(v.startTimeOffset=q);break;case"MAP":var z=new g(P);_.relurl=z.URI,z.BYTERANGE&&_.setByteRange(z.BYTERANGE),_.level=r,_.sn="initSegment",c&&(_.levelkey=c),_.initSegment=null,m=_,x=!0;break;case"SERVER-CONTROL":var Q=new g(P);v.canBlockReload=Q.bool("CAN-BLOCK-RELOAD"),v.canSkipUntil=Q.optionalFloat("CAN-SKIP-UNTIL",0),v.canSkipDateRanges=v.canSkipUntil>0&&Q.bool("CAN-SKIP-DATERANGES"),v.partHoldBack=Q.optionalFloat("PART-HOLD-BACK",0),v.holdBack=Q.optionalFloat("HOLD-BACK",0);break;case"PART-INF":var $=new g(P);v.partTarget=$.decimalFloatingPoint("PART-TARGET");break;case"PART":var Z=v.partList;Z||(Z=v.partList=[]);var J=b>0?Z[Z.length-1]:void 0,tt=b++,et=new u.c(new g(P),_,e,tt,J);Z.push(et),_.duration+=et.duration;break;case"PRELOAD-HINT":var rt=new g(P);v.preloadHint=rt;break;case"RENDITION-REPORT":var it=new g(P);v.renditionReports=v.renditionReports||[],v.renditionReports.push(it);break;default:o.b.warn("line parsed but not handled: "+l)}}}R&&!R.relurl?(p.pop(),L-=R.duration,v.partList&&(v.fragmentHint=R)):v.partList&&(D(_,R),_.cc=A,v.fragmentHint=_);var at=p.length,nt=p[0],st=p[at-1];if((L+=v.skippedSegments*v.targetduration)>0&&at&&st){v.averagetargetduration=L/at;var ot=st.sn;v.endSN="initSegment"!==ot?ot:0,nt&&(v.startCC=nt.cc,nt.initSegment||v.fragments.every((function(t){return t.relurl&&(e=t.relurl,S.test(null!=(r=null===(a=i.parseURL(e))||void 0===a?void 0:a.path)?r:""));var e,r,a}))&&(o.b.warn("MP4 fragments found but no init segment (probably no MAP, incomplete M3U8), trying to fetch SIDX"),(_=new u.b(n,e)).relurl=st.relurl,_.level=r,_.sn="initSegment",nt.initSegment=_,v.needSidxRanges=!0))}else v.endSN=0,v.startCC=0;return v.fragmentHint&&(L+=v.fragmentHint.duration),v.totalduration=L,v.endCC=A,k>0&&function(t,e){for(var r=t[e],i=e;i--;){var a=t[i];if(!a)return;a.programDateTime=r.programDateTime-1e3*a.duration,r=a}}(p,k),v},t}();function A(t,e){["video","audio","text"].forEach((function(r){var i=t.filter((function(t){return function(t,e){var r=p[e];return!!r&&!0===r[t.slice(0,4)]}(t,r)}));if(i.length){var a=i.filter((function(t){return 0===t.lastIndexOf("avc1",0)||0===t.lastIndexOf("mp4a",0)}));e[r+"Codec"]=a.length>0?a[0]:i[0],t=t.filter((function(t){return-1===i.indexOf(t)}))}})),e.unknownCodecs=t}function R(t,e,r){var i=e[r];i&&(t[r]=i)}function D(t,e){t.rawProgramDateTime?t.programDateTime=Date.parse(t.rawProgramDateTime):null!=e&&e.programDateTime&&(t.programDateTime=e.endProgramDateTime),Object(a.a)(t.programDateTime)||(t.programDateTime=null,t.rawProgramDateTime=null)}var _=r(4);function k(t,e){var r=t.url;return void 0!==r&&0!==r.indexOf("data:")||(r=e.url),r}var x=function(){function t(t){this.hls=void 0,this.loaders=Object.create(null),this.hls=t,this.registerListeners()}var e=t.prototype;return e.registerListeners=function(){var t=this.hls;t.on(n.a.MANIFEST_LOADING,this.onManifestLoading,this),t.on(n.a.LEVEL_LOADING,this.onLevelLoading,this),t.on(n.a.AUDIO_TRACK_LOADING,this.onAudioTrackLoading,this),t.on(n.a.SUBTITLE_TRACK_LOADING,this.onSubtitleTrackLoading,this)},e.unregisterListeners=function(){var t=this.hls;t.off(n.a.MANIFEST_LOADING,this.onManifestLoading,this),t.off(n.a.LEVEL_LOADING,this.onLevelLoading,this),t.off(n.a.AUDIO_TRACK_LOADING,this.onAudioTrackLoading,this),t.off(n.a.SUBTITLE_TRACK_LOADING,this.onSubtitleTrackLoading,this)},e.createInternalLoader=function(t){var e=this.hls.config,r=e.pLoader,i=e.loader,a=new(r||i)(e);return t.loader=a,this.loaders[t.type]=a,a},e.getInternalLoader=function(t){return this.loaders[t.type]},e.resetInternalLoader=function(t){this.loaders[t]&&delete this.loaders[t]},e.destroyInternalLoaders=function(){for(var t in this.loaders){var e=this.loaders[t];e&&e.destroy(),this.resetInternalLoader(t)}},e.destroy=function(){this.unregisterListeners(),this.destroyInternalLoaders()},e.onManifestLoading=function(t,e){var r=e.url;this.load({id:null,groupId:null,level:0,responseType:"text",type:_.a.MANIFEST,url:r,deliveryDirectives:null})},e.onLevelLoading=function(t,e){var r=e.id,i=e.level,a=e.url,n=e.deliveryDirectives;this.load({id:r,groupId:null,level:i,responseType:"text",type:_.a.LEVEL,url:a,deliveryDirectives:n})},e.onAudioTrackLoading=function(t,e){var r=e.id,i=e.groupId,a=e.url,n=e.deliveryDirectives;this.load({id:r,groupId:i,level:null,responseType:"text",type:_.a.AUDIO_TRACK,url:a,deliveryDirectives:n})},e.onSubtitleTrackLoading=function(t,e){var r=e.id,i=e.groupId,a=e.url,n=e.deliveryDirectives;this.load({id:r,groupId:i,level:null,responseType:"text",type:_.a.SUBTITLE_TRACK,url:a,deliveryDirectives:n})},e.load=function(t){var e,r,i,a,n,s,l=this.hls.config,u=this.getInternalLoader(t);if(u){var d=u.context;if(d&&d.url===t.url)return void o.b.trace("[playlist-loader]: playlist request ongoing");o.b.log("[playlist-loader]: aborting previous loader for type: "+t.type),u.abort()}switch(t.type){case _.a.MANIFEST:r=l.manifestLoadingMaxRetry,i=l.manifestLoadingTimeOut,a=l.manifestLoadingRetryDelay,n=l.manifestLoadingMaxRetryTimeout;break;case _.a.LEVEL:case _.a.AUDIO_TRACK:case _.a.SUBTITLE_TRACK:r=0,i=l.levelLoadingTimeOut;break;default:r=l.levelLoadingMaxRetry,i=l.levelLoadingTimeOut,a=l.levelLoadingRetryDelay,n=l.levelLoadingMaxRetryTimeout}if((u=this.createInternalLoader(t),null!==(e=t.deliveryDirectives)&&void 0!==e&&e.part)&&(t.type===_.a.LEVEL&&null!==t.level?s=this.hls.levels[t.level].details:t.type===_.a.AUDIO_TRACK&&null!==t.id?s=this.hls.audioTracks[t.id].details:t.type===_.a.SUBTITLE_TRACK&&null!==t.id&&(s=this.hls.subtitleTracks[t.id].details),s)){var h=s.partTarget,f=s.targetduration;h&&f&&(i=Math.min(1e3*Math.max(3*h,.8*f),i))}var c={timeout:i,maxRetry:r,retryDelay:a,maxRetryDelay:n,highWaterMark:0},v={onSuccess:this.loadsuccess.bind(this),onError:this.loaderror.bind(this),onTimeout:this.loadtimeout.bind(this)};u.load(t,c,v)},e.loadsuccess=function(t,e,r,i){if(void 0===i&&(i=null),r.isSidxRequest)return this.handleSidxRequest(t,r),void this.handlePlaylistLoaded(t,e,r,i);this.resetInternalLoader(r.type);var a=t.data;0===a.indexOf("#EXTM3U")?(e.parsing.start=performance.now(),a.indexOf("#EXTINF:")>0||a.indexOf("#EXT-X-TARGETDURATION:")>0?this.handleTrackOrLevelPlaylist(t,e,r,i):this.handleMasterPlaylist(t,e,r,i)):this.handleManifestParsingError(t,r,"no EXTM3U delimiter",i)},e.loaderror=function(t,e,r){void 0===r&&(r=null),this.handleNetworkError(e,r,!1,t)},e.loadtimeout=function(t,e,r){void 0===r&&(r=null),this.handleNetworkError(e,r,!0)},e.handleMasterPlaylist=function(t,e,r,i){var a=this.hls,s=t.data,l=k(t,r),u=L.parseMasterPlaylist(s,l),d=u.levels,h=u.sessionData;if(d.length){var f=d.map((function(t){return{id:t.attrs.AUDIO,audioCodec:t.audioCodec}})),c=d.map((function(t){return{id:t.attrs.SUBTITLES,textCodec:t.textCodec}})),v=L.parseMasterPlaylistMedia(s,l,"AUDIO",f),p=L.parseMasterPlaylistMedia(s,l,"SUBTITLES",c),m=L.parseMasterPlaylistMedia(s,l,"CLOSED-CAPTIONS");if(v.length)v.some((function(t){return!t.url}))||!d[0].audioCodec||d[0].attrs.AUDIO||(o.b.log("[playlist-loader]: audio codec signaled in quality level, but no embedded audio track signaled, create one"),v.unshift({type:"main",name:"main",default:!1,autoselect:!1,forced:!1,id:-1,attrs:new g({}),bitrate:0,url:""}));a.trigger(n.a.MANIFEST_LOADED,{levels:d,audioTracks:v,subtitles:p,captions:m,url:l,stats:e,networkDetails:i,sessionData:h})}else this.handleManifestParsingError(t,r,"no level found in manifest",i)},e.handleTrackOrLevelPlaylist=function(t,e,r,i){var o=this.hls,l=r.id,u=r.level,d=r.type,h=k(t,r),f=Object(a.a)(l)?l:0,c=Object(a.a)(u)?u:f,v=function(t){switch(t.type){case _.a.AUDIO_TRACK:return _.b.AUDIO;case _.a.SUBTITLE_TRACK:return _.b.SUBTITLE;default:return _.b.MAIN}}(r),p=L.parseLevelPlaylist(t.data,h,c,v,f);if(p.fragments.length){if(d===_.a.MANIFEST){var m={attrs:new g({}),bitrate:0,details:p,name:"",url:h};o.trigger(n.a.MANIFEST_LOADED,{levels:[m],audioTracks:[],url:h,stats:e,networkDetails:i,sessionData:null})}if(e.parsing.end=performance.now(),p.needSidxRanges){var y,b=null===(y=p.fragments[0].initSegment)||void 0===y?void 0:y.url;this.load({url:b,isSidxRequest:!0,type:d,level:u,levelDetails:p,id:l,groupId:null,rangeStart:0,rangeEnd:2048,responseType:"arraybuffer",deliveryDirectives:null})}else r.levelDetails=p,this.handlePlaylistLoaded(t,e,r,i)}else o.trigger(n.a.ERROR,{type:s.b.NETWORK_ERROR,details:s.a.LEVEL_EMPTY_ERROR,fatal:!1,url:h,reason:"no fragments found in level",level:"number"==typeof r.level?r.level:void 0})},e.handleSidxRequest=function(t,e){var r=Object(l.g)(new Uint8Array(t.data));if(r){var i=r.references,a=e.levelDetails;i.forEach((function(t,e){var i=t.info,n=a.fragments[e];0===n.byteRange.length&&n.setByteRange(String(1+i.end-i.start)+"@"+String(i.start)),n.initSegment&&n.initSegment.setByteRange(String(r.moovEndOffset)+"@0")}))}},e.handleManifestParsingError=function(t,e,r,i){this.hls.trigger(n.a.ERROR,{type:s.b.NETWORK_ERROR,details:s.a.MANIFEST_PARSING_ERROR,fatal:e.type===_.a.MANIFEST,url:t.url,reason:r,response:t,context:e,networkDetails:i})},e.handleNetworkError=function(t,e,r,i){void 0===r&&(r=!1),o.b.warn("[playlist-loader]: A network "+(r?"timeout":"error")+" occurred while loading "+t.type+" level: "+t.level+" id: "+t.id+' group-id: "'+t.groupId+'"');var a=s.a.UNKNOWN,l=!1,u=this.getInternalLoader(t);switch(t.type){case _.a.MANIFEST:a=r?s.a.MANIFEST_LOAD_TIMEOUT:s.a.MANIFEST_LOAD_ERROR,l=!0;break;case _.a.LEVEL:a=r?s.a.LEVEL_LOAD_TIMEOUT:s.a.LEVEL_LOAD_ERROR,l=!1;break;case _.a.AUDIO_TRACK:a=r?s.a.AUDIO_TRACK_LOAD_TIMEOUT:s.a.AUDIO_TRACK_LOAD_ERROR,l=!1;break;case _.a.SUBTITLE_TRACK:a=r?s.a.SUBTITLE_TRACK_LOAD_TIMEOUT:s.a.SUBTITLE_LOAD_ERROR,l=!1}u&&this.resetInternalLoader(t.type);var d={type:s.b.NETWORK_ERROR,details:a,fatal:l,url:t.url,loader:u,context:t,networkDetails:e};i&&(d.response=i),this.hls.trigger(n.a.ERROR,d)},e.handlePlaylistLoaded=function(t,e,r,i){var a=r.type,s=r.level,o=r.id,l=r.groupId,u=r.loader,d=r.levelDetails,h=r.deliveryDirectives;if(null!=d&&d.targetduration){if(u)switch(d.live&&(u.getCacheAge&&(d.ageHeader=u.getCacheAge()||0),u.getCacheAge&&!isNaN(d.ageHeader)||(d.ageHeader=0)),a){case _.a.MANIFEST:case _.a.LEVEL:this.hls.trigger(n.a.LEVEL_LOADED,{details:d,level:s||0,id:o||0,stats:e,networkDetails:i,deliveryDirectives:h});break;case _.a.AUDIO_TRACK:this.hls.trigger(n.a.AUDIO_TRACK_LOADED,{details:d,id:o||0,groupId:l||"",stats:e,networkDetails:i,deliveryDirectives:h});break;case _.a.SUBTITLE_TRACK:this.hls.trigger(n.a.SUBTITLE_TRACK_LOADED,{details:d,id:o||0,groupId:l||"",stats:e,networkDetails:i,deliveryDirectives:h})}}else this.handleManifestParsingError(t,r,"invalid target duration",i)},t}(),C=function(){function t(t){this.hls=void 0,this.loaders={},this.decryptkey=null,this.decrypturl=null,this.hls=t,this._registerListeners()}var e=t.prototype;return e._registerListeners=function(){this.hls.on(n.a.KEY_LOADING,this.onKeyLoading,this)},e._unregisterListeners=function(){this.hls.off(n.a.KEY_LOADING,this.onKeyLoading)},e.destroy=function(){for(var t in this._unregisterListeners(),this.loaders){var e=this.loaders[t];e&&e.destroy()}this.loaders={}},e.onKeyLoading=function(t,e){var r=e.frag,i=r.type,a=this.loaders[i];if(r.decryptdata){var s=r.decryptdata.uri;if(s!==this.decrypturl||null===this.decryptkey){var l=this.hls.config;if(a&&(o.b.warn("abort previous key loader for type:"+i),a.abort()),!s)return void o.b.warn("key uri is falsy");var u=l.loader,d=r.loader=this.loaders[i]=new u(l);this.decrypturl=s,this.decryptkey=null;var h={url:s,frag:r,responseType:"arraybuffer"},f={timeout:l.fragLoadingTimeOut,maxRetry:0,retryDelay:l.fragLoadingRetryDelay,maxRetryDelay:l.fragLoadingMaxRetryTimeout,highWaterMark:0},c={onSuccess:this.loadsuccess.bind(this),onError:this.loaderror.bind(this),onTimeout:this.loadtimeout.bind(this)};d.load(h,f,c)}else this.decryptkey&&(r.decryptdata.key=this.decryptkey,this.hls.trigger(n.a.KEY_LOADED,{frag:r}))}else o.b.warn("Missing decryption data on fragment in onKeyLoading")},e.loadsuccess=function(t,e,r){var i=r.frag;i.decryptdata?(this.decryptkey=i.decryptdata.key=new Uint8Array(t.data),i.loader=null,delete this.loaders[i.type],this.hls.trigger(n.a.KEY_LOADED,{frag:i})):o.b.error("after key load, decryptdata unset")},e.loaderror=function(t,e){var r=e.frag,i=r.loader;i&&i.abort(),delete this.loaders[r.type],this.hls.trigger(n.a.ERROR,{type:s.b.NETWORK_ERROR,details:s.a.KEY_LOAD_ERROR,fatal:!1,frag:r,response:t})},e.loadtimeout=function(t,e){var r=e.frag,i=r.loader;i&&i.abort(),delete this.loaders[r.type],this.hls.trigger(n.a.ERROR,{type:s.b.NETWORK_ERROR,details:s.a.KEY_LOAD_TIMEOUT,fatal:!1,frag:r})},t}();function w(t,e){var r;try{r=new Event("addtrack")}catch(t){(r=document.createEvent("Event")).initEvent("addtrack",!1,!1)}r.track=t,e.dispatchEvent(r)}function O(t,e,r){var i=t.mode;if("disabled"===i&&(t.mode="hidden"),t.cues&&t.cues.length>0)for(var a=function(t,e,r){var i=[],a=function(t,e){if(et[r].endTime)return-1;var i=0,a=r;for(;i<=a;){var n=Math.floor((a+i)/2);if(et[n].startTime&&i-1)for(var n=a,s=t.length;n=e&&o.endTime<=r)i.push(o);else if(o.startTime>r)return i}return i}(t.cues,e,r),n=0;n.05&&this.forwardBufferLength>1){var u=Math.min(2,Math.max(1,n)),d=Math.round(2/(1+Math.exp(-.75*o-this.edgeStalled))*20)/20;t.playbackRate=Math.min(u,Math.max(1,d))}else 1!==t.playbackRate&&0!==t.playbackRate&&(t.playbackRate=1)}}}}},a.estimateLiveEdge=function(){var t=this.levelDetails;return null===t?null:t.edge+t.age},a.computeLatency=function(){var t=this.estimateLiveEdge();return null===t?null:t-this.currentTime},e=t,(r=[{key:"latency",get:function(){return this._latency||0}},{key:"maxLatency",get:function(){var t=this.config,e=this.levelDetails;return void 0!==t.liveMaxLatencyDuration?t.liveMaxLatencyDuration:e?t.liveMaxLatencyDurationCount*e.targetduration:0}},{key:"targetLatency",get:function(){var t=this.levelDetails;if(null===t)return null;var e=t.holdBack,r=t.partHoldBack,i=t.targetduration,a=this.config,n=a.liveSyncDuration,s=a.liveSyncDurationCount,o=a.lowLatencyMode,l=this.hls.userConfig,u=o&&r||e;(l.liveSyncDuration||l.liveSyncDurationCount||0===u)&&(u=void 0!==n?n:s*i);var d=i;return u+Math.min(1*this.stallCount,d)}},{key:"liveSyncPosition",get:function(){var t=this.estimateLiveEdge(),e=this.targetLatency,r=this.levelDetails;if(null===t||null===e||null===r)return null;var i=r.edge,a=t-e-this.edgeStalled,n=i-r.totalduration,s=i-(this.config.lowLatencyMode&&r.partTarget||r.targetduration);return Math.min(Math.max(n,a),s)}},{key:"drift",get:function(){var t=this.levelDetails;return null===t?1:t.drift}},{key:"edgeStalled",get:function(){var t=this.levelDetails;if(null===t)return 0;var e=3*(this.config.lowLatencyMode&&t.partTarget||t.targetduration);return Math.max(t.age-e,0)}},{key:"forwardBufferLength",get:function(){var t=this.media,e=this.levelDetails;if(!t||!e)return 0;var r=t.buffered.length;return r?t.buffered.end(r-1):e.edge-this.currentTime}}])&&F(e.prototype,r),i&&F(e,i),t}();function B(t,e){for(var r=0;rt.sn?(n=r-t.start,i=t):(n=t.start-r,i=e),i.duration!==n&&(i.duration=n)}else if(e.sn>t.sn){t.cc===e.cc&&t.minEndPTS?e.start=t.start+(t.minEndPTS-t.start):e.start=t.start+t.duration}else e.start=Math.max(t.start-e.duration,0)}function V(t,e,r,i,n,s){i-r<=0&&(o.b.warn("Fragment should have a positive duration",e),i=r+e.duration,s=n+e.duration);var l=r,u=i,d=e.startPTS,h=e.endPTS;if(Object(a.a)(d)){var f=Math.abs(d-r);Object(a.a)(e.deltaPTS)?e.deltaPTS=Math.max(f,e.deltaPTS):e.deltaPTS=f,l=Math.max(r,d),r=Math.min(r,d),n=Math.min(n,e.startDTS),u=Math.min(i,h),i=Math.max(i,h),s=Math.max(s,e.endDTS)}e.duration=i-r;var c=r-e.start;e.appendedPTS=i,e.start=e.startPTS=r,e.maxStartPTS=l,e.startDTS=n,e.endPTS=i,e.minEndPTS=u,e.endDTS=s;var v,g=e.sn;if(!t||gt.endSN)return 0;var p=g-t.startSN,m=t.fragments;for(m[p]=e,v=p;v>0;v--)K(m[v],m[v-1]);for(v=p;v=0;n--){var s=i[n].initSegment;if(s){r=s;break}}t.fragmentHint&&delete t.fragmentHint.endPTS;var l,u=0;(function(t,e,r){for(var i=e.skippedSegments,a=Math.max(t.startSN,e.startSN)-e.startSN,n=(t.fragmentHint?1:0)+(i?e.endSN:Math.min(t.endSN,e.endSN))-e.startSN,s=e.startSN-t.startSN,o=e.fragmentHint?e.fragments.concat(e.fragmentHint):e.fragments,l=t.fragmentHint?t.fragments.concat(t.fragmentHint):t.fragments,u=a;u<=n;u++){var d=l[s+u],h=o[u];i&&!h&&u=i.length||function(t,e){if(e){for(var r=t.fragments,i=t.skippedSegments;ie.partTarget&&(l+=1)}if(Object(a.a)(o))return new U(o,Object(a.a)(l)?l:void 0,M.No)}}},e.loadPlaylist=function(t){},e.shouldLoadTrack=function(t){return this.canLoad&&t&&!!t.url&&(!t.details||t.details.live)},e.playlistLoaded=function(t,e,r){var i=this,a=e.details,n=e.stats,s=n.loading.end?Math.max(0,self.performance.now()-n.loading.end):0;if(a.advancedDateTime=Date.now()-s,a.live||null!=r&&r.live){if(a.reloaded(r),r&&this.log("live playlist "+t+" "+(a.advanced?"REFRESHED "+a.lastPartSn+"-"+a.lastPartIndex:"MISSED")),r&&a.fragments.length>0&&W(r,a),!this.canLoad||!a.live)return;var o,l=void 0,u=void 0;if(a.canBlockReload&&a.endSN&&a.advanced){var d=this.hls.config.lowLatencyMode,h=a.lastPartSn,f=a.endSN,c=a.lastPartIndex,v=h===f;-1!==c?(l=v?f+1:h,u=v?d?0:c:c+1):l=f+1;var g=a.age,p=g+a.ageHeader,m=Math.min(p-a.partTarget,1.5*a.targetduration);if(m>0){if(r&&m>r.tuneInGoal)this.warn("CDN Tune-in goal increased from: "+r.tuneInGoal+" to: "+m+" with playlist age: "+a.age),m=0;else{var y=Math.floor(m/a.targetduration);if(l+=y,void 0!==u)u+=Math.round(m%a.targetduration/a.partTarget);this.log("CDN Tune-in age: "+a.ageHeader+"s last advanced "+g.toFixed(2)+"s goal: "+m+" skip sn "+y+" to part "+u)}a.tuneInGoal=m}if(o=this.getDeliveryDirectives(a,e.deliveryDirectives,l,u),d||!v)return void this.loadPlaylist(o)}else o=this.getDeliveryDirectives(a,e.deliveryDirectives,l,u);var b=function(t,e){var r,i=1e3*t.levelTargetDuration,a=i/2,n=t.age,s=n>0&&n<3*i,o=e.loading.end-e.loading.start,l=t.availabilityDelay;if(!1===t.updated)if(s){var u=333*t.misses;r=Math.max(Math.min(a,2*o),u),t.availabilityDelay=(t.availabilityDelay||0)+r}else r=a;else s?(l=Math.min(l||i/2,n),t.availabilityDelay=l,r=l+i-n):r=i-o;return Math.round(r)}(a,n);void 0!==l&&a.canBlockReload&&(b-=a.partTarget||1),this.log("reload live playlist "+t+" in "+Math.round(b)+" ms"),this.timer=self.setTimeout((function(){return i.loadPlaylist(o)}),b)}else this.clearTimer()},e.getDeliveryDirectives=function(t,e,r,i){var a=function(t,e){var r=t.canSkipUntil,i=t.canSkipDateRanges,a=t.endSN;return r&&(void 0!==e?e-a:0)-1&&null!==(e=t.context)&&void 0!==e&&e.deliveryDirectives)this.warn("retry playlist loading #"+this.retryCount+' after "'+t.details+'"'),this.loadPlaylist();else{var n=Math.min(Math.pow(2,this.retryCount)*i.levelLoadingRetryDelay,i.levelLoadingMaxRetryTimeout);this.timer=self.setTimeout((function(){return r.loadPlaylist()}),n),this.warn("retry playlist loading #"+this.retryCount+" in "+n+' ms after "'+t.details+'"')}else this.warn('cannot recover from error "'+t.details+'"'),this.clearTimer(),t.fatal=!0;return a},t}();function q(){return(q=Object.assign||function(t){for(var e=1;e0){r=a[0].bitrate,a.sort((function(t,e){return t.bitrate-e.bitrate})),this._levels=a;for(var c=0;cthis.hls.config.fragLoadingMaxRetry&&(n=r.frag.level)):n=r.frag.level}break;case s.a.LEVEL_LOAD_ERROR:case s.a.LEVEL_LOAD_TIMEOUT:i&&(i.deliveryDirectives&&(l=!1),n=i.level),o=!0;break;case s.a.REMUX_ALLOC_ERROR:n=r.level,o=!0}void 0!==n&&this.recoverLevel(r,n,o,l)}}},u.recoverLevel=function(t,e,r,i){var a=t.details,n=this._levels[e];if(n.loadError++,r){if(!this.retryLoadingOrFail(t))return void(this.currentLevelIndex=-1);t.levelRetry=!0}if(i){var s=n.url.length;if(s>1&&n.loadError1){var i=(e.urlId+1)%r;this.warn("Switching to redundant URL-id "+i),this._levels.forEach((function(t){t.urlId=i})),this.level=t}},u.onFragLoaded=function(t,e){var r=e.frag;if(void 0!==r&&r.type===_.b.MAIN){var i=this._levels[r.level];void 0!==i&&(i.fragmentError=0,i.loadError=0)}},u.onLevelLoaded=function(t,e){var r,i,a=e.level,n=e.details,s=this._levels[a];if(!s)return this.warn("Invalid level index "+a),void(null!==(i=e.deliveryDirectives)&&void 0!==i&&i.skip&&(n.deltaUpdateFailed=!0));a===this.currentLevelIndex?(0===s.fragmentError&&(s.loadError=0,this.retryCount=0),this.playlistLoaded(a,e,s.details)):null!==(r=e.deliveryDirectives)&&void 0!==r&&r.skip&&(n.deltaUpdateFailed=!0)},u.onAudioTrackSwitched=function(t,e){var r=this.hls.levels[this.currentLevelIndex];if(r&&r.audioGroupIds){for(var i=-1,a=this.hls.audioTracks[e.id].groupId,n=0;n0){var i=r.urlId,a=r.url[i];if(t)try{a=t.addDirectives(a)}catch(t){this.warn("Could not construct new URL with HLS Delivery Directives: "+t)}this.log("Attempt loading level index "+e+(t?" at sn "+t.msn+" part "+t.part:"")+" with URL-id "+i+" "+a),this.clearTimer(),this.hls.trigger(n.a.LEVEL_LOADING,{url:a,level:e,id:i,deliveryDirectives:t||null})}},u.removeLevel=function(t,e){var r=function(t,r){return r!==e},i=this._levels.filter((function(i,a){return a!==t||i.url.length>1&&void 0!==e&&(i.url=i.url.filter(r),i.audioGroupIds&&(i.audioGroupIds=i.audioGroupIds.filter(r)),i.textGroupIds&&(i.textGroupIds=i.textGroupIds.filter(r)),i.urlId=0,!0)})).map((function(t,e){var r=t.details;return null!=r&&r.fragments&&r.fragments.forEach((function(t){t.level=e})),t}));this._levels=i,this.hls.trigger(n.a.LEVELS_UPDATED,{levels:i})},a=i,(o=[{key:"levels",get:function(){return 0===this._levels.length?null:this._levels}},{key:"level",get:function(){return this.currentLevelIndex},set:function(t){var e,r=this._levels;if(0!==r.length&&(this.currentLevelIndex!==t||null===(e=r[t])||void 0===e||!e.details)){if(t<0||t>=r.length){var i=t<0;if(this.hls.trigger(n.a.ERROR,{type:s.b.OTHER_ERROR,details:s.a.LEVEL_SWITCH_ERROR,level:t,fatal:i,reason:"invalid level idx"}),i)return;t=Math.min(t,r.length-1)}this.clearTimer();var a=this.currentLevelIndex,o=r[a],l=r[t];this.log("switching to level "+t+" from "+a),this.currentLevelIndex=t;var u=q({},l,{level:t,maxBitrate:l.maxBitrate,uri:l.uri,urlId:l.urlId});delete u._urlId,this.hls.trigger(n.a.LEVEL_SWITCHING,u);var d=l.details;if(!d||d.live){var h=this.switchParams(l.uri,null==o?void 0:o.details);this.loadPlaylist(h)}}}},{key:"manualLevel",get:function(){return this.manualLevelIndex},set:function(t){this.manualLevelIndex=t,void 0===this._startLevel&&(this._startLevel=t),-1!==t&&(this.level=t)}},{key:"firstLevel",get:function(){return this._firstLevel},set:function(t){this._firstLevel=t}},{key:"startLevel",get:function(){if(void 0===this._startLevel){var t=this.hls.config.startLevel;return void 0!==t?t:this._firstLevel}return this._startLevel},set:function(t){this._startLevel=t}},{key:"nextLoadLevel",get:function(){return-1!==this.manualLevelIndex?this.manualLevelIndex:this.hls.nextAutoLevel},set:function(t){this.level=t,-1===this.manualLevelIndex&&(this.hls.nextAutoLevel=t)}}])&&z(a.prototype,o),l&&z(a,l),i}(X);!function(t){t.NOT_LOADED="NOT_LOADED",t.BACKTRACKED="BACKTRACKED",t.APPENDING="APPENDING",t.PARTIAL="PARTIAL",t.OK="OK"}($||($={}));var tt=function(){function t(t){this.activeFragment=null,this.activeParts=null,this.fragments=Object.create(null),this.timeRanges=Object.create(null),this.bufferPadding=.2,this.hls=void 0,this.hls=t,this._registerListeners()}var e=t.prototype;return e._registerListeners=function(){var t=this.hls;t.on(n.a.BUFFER_APPENDED,this.onBufferAppended,this),t.on(n.a.FRAG_BUFFERED,this.onFragBuffered,this),t.on(n.a.FRAG_LOADED,this.onFragLoaded,this)},e._unregisterListeners=function(){var t=this.hls;t.off(n.a.BUFFER_APPENDED,this.onBufferAppended,this),t.off(n.a.FRAG_BUFFERED,this.onFragBuffered,this),t.off(n.a.FRAG_LOADED,this.onFragLoaded,this)},e.destroy=function(){this._unregisterListeners(),this.fragments=this.timeRanges=null},e.getAppendedFrag=function(t,e){if(e===_.b.MAIN){var r=this.activeFragment,i=this.activeParts;if(!r)return null;if(i)for(var a=i.length;a--;){var n=i[a],s=n?n.end:r.appendedPTS;if(n.start<=t&&void 0!==s&&t<=s)return a>9&&(this.activeParts=i.slice(a-9)),n}else if(r.start<=t&&void 0!==r.appendedPTS&&t<=r.appendedPTS)return r}return this.getBufferedFrag(t,e)},e.getBufferedFrag=function(t,e){for(var r=this.fragments,i=Object.keys(r),a=i.length;a--;){var n=r[i[a]];if((null==n?void 0:n.body.type)===e&&n.buffered){var s=n.body;if(s.start<=t&&t<=s.end)return s}}return null},e.detectEvictedFragments=function(t,e,r){var i=this;Object.keys(this.fragments).forEach((function(a){var n=i.fragments[a];if(n)if(n.buffered){var s=n.range[t];s&&s.time.some((function(t){var r=!i.isTimeBuffered(t.startPTS,t.endPTS,e);return r&&i.removeFragment(n.body),r}))}else n.body.type===r&&i.removeFragment(n.body)}))},e.detectPartialFragments=function(t){var e=this,r=this.timeRanges,i=t.frag,a=t.part;if(r&&"initSegment"!==i.sn){var n=rt(i),s=this.fragments[n];s&&(Object.keys(r).forEach((function(t){var n=i.elementaryStreams[t];if(n){var o=r[t],l=null!==a||!0===n.partial;s.range[t]=e.getBufferedTimes(i,a,l,o)}})),s.backtrack=s.loaded=null,Object.keys(s.range).length?s.buffered=!0:this.removeFragment(s.body))}},e.fragBuffered=function(t){var e=rt(t),r=this.fragments[e];r&&(r.backtrack=r.loaded=null,r.buffered=!0)},e.getBufferedTimes=function(t,e,r,i){for(var a={time:[],partial:r},n=e?e.start:t.start,s=e?e.end:t.end,o=t.minEndPTS||s,l=t.maxStartPTS||n,u=0;u=d&&o<=h){a.time.push({startPTS:Math.max(n,i.start(u)),endPTS:Math.min(s,i.end(u))});break}if(nd)a.partial=!0,a.time.push({startPTS:Math.max(n,i.start(u)),endPTS:Math.min(s,i.end(u))});else if(s<=d)break}return a},e.getPartialFragment=function(t){var e,r,i,a=null,n=0,s=this.bufferPadding,o=this.fragments;return Object.keys(o).forEach((function(l){var u=o[l];u&&et(u)&&(r=u.body.start-s,i=u.body.end+s,t>=r&&t<=i&&(e=Math.min(t-r,i-t),n<=e&&(a=u.body,n=e)))})),a},e.getState=function(t){var e=rt(t),r=this.fragments[e];return r?r.buffered?et(r)?$.PARTIAL:$.OK:r.backtrack?$.BACKTRACKED:$.APPENDING:$.NOT_LOADED},e.backtrack=function(t,e){var r=rt(t),i=this.fragments[r];if(!i||i.backtrack)return null;var a=i.backtrack=e||i.loaded;return i.loaded=null,a},e.getBacktrackData=function(t){var e=rt(t),r=this.fragments[e];if(r){var i,a=r.backtrack;if(null!=a&&null!==(i=a.payload)&&void 0!==i&&i.byteLength)return a;this.removeFragment(t)}return null},e.isTimeBuffered=function(t,e,r){for(var i,a,n=0;n=i&&e<=a)return!0;if(e<=i)return!1}return!1},e.onFragLoaded=function(t,e){var r=e.frag,i=e.part;if("initSegment"!==r.sn&&!r.bitrateTest&&!i){var a=rt(r);this.fragments[a]={body:r,loaded:e,backtrack:null,buffered:!1,range:Object.create(null)}}},e.onBufferAppended=function(t,e){var r=this,i=e.frag,a=e.part,n=e.timeRanges;if(i.type===_.b.MAIN)if(this.activeFragment=i,a){var s=this.activeParts;s||(this.activeParts=s=[]),s.push(a)}else this.activeParts=null;this.timeRanges=n,Object.keys(n).forEach((function(t){var e=n[t];if(r.detectEvictedFragments(t,e),!a)for(var s=0;st&&i.removeFragment(s)}}))},e.removeFragment=function(t){var e=rt(t);t.stats.loaded=0,t.clearElementaryStreamInfo(),delete this.fragments[e]},e.removeAllFragments=function(){this.fragments=Object.create(null),this.activeFragment=null,this.activeParts=null},t}();function et(t){var e,r;return t.buffered&&((null===(e=t.range.video)||void 0===e?void 0:e.partial)||(null===(r=t.range.audio)||void 0===r?void 0:r.partial))}function rt(t){return t.type+"_"+t.level+"_"+t.urlId+"_"+t.sn}var it=function(){function t(){this._boundTick=void 0,this._tickTimer=null,this._tickInterval=null,this._tickCallCount=0,this._boundTick=this.tick.bind(this)}var e=t.prototype;return e.destroy=function(){this.onHandlerDestroying(),this.onHandlerDestroyed()},e.onHandlerDestroying=function(){this.clearNextTick(),this.clearInterval()},e.onHandlerDestroyed=function(){},e.hasInterval=function(){return!!this._tickInterval},e.hasNextTick=function(){return!!this._tickTimer},e.setInterval=function(t){return!this._tickInterval&&(this._tickInterval=self.setInterval(this._boundTick,t),!0)},e.clearInterval=function(){return!!this._tickInterval&&(self.clearInterval(this._tickInterval),this._tickInterval=null,!0)},e.clearNextTick=function(){return!!this._tickTimer&&(self.clearTimeout(this._tickTimer),this._tickTimer=null,!0)},e.tick=function(){this._tickCallCount++,1===this._tickCallCount&&(this.doTick(),this._tickCallCount>1&&this.tickImmediate(),this._tickCallCount=0)},e.tickImmediate=function(){this.clearNextTick(),this._tickTimer=self.setTimeout(this._boundTick,0)},e.doTick=function(){},t}(),at={length:0,start:function(){return 0},end:function(){return 0}},nt=function(){function t(){}return t.isBuffered=function(e,r){try{if(e)for(var i=t.getBuffered(e),a=0;a=i.start(a)&&r<=i.end(a))return!0}catch(t){}return!1},t.bufferInfo=function(e,r,i){try{if(e){var a,n=t.getBuffered(e),s=[];for(a=0;as&&(i[n-1].end=t[a].end):i.push(t[a])}else i.push(t[a])}else i=t;for(var o,l=0,u=e,d=e,h=0;h=f&&er.startCC||t&&t.cc0)r=a+1;else{if(!(s<0))return n;i=a-1}}return null}};function ht(t,e,r){void 0===t&&(t=0),void 0===e&&(e=0);var i=Math.min(e,r.duration+(r.deltaPTS?r.deltaPTS:0));return r.start+r.duration-i<=t?1:r.start-i>t&&r.start?-1:0}function ft(t,e,r){var i=1e3*Math.min(e,r.duration+(r.deltaPTS?r.deltaPTS:0));return(r.endProgramDateTime||0)-i>t}function ct(t){var e="function"==typeof Map?new Map:void 0;return(ct=function(t){if(null===t||(r=t,-1===Function.toString.call(r).indexOf("[native code]")))return t;var r;if("function"!=typeof t)throw new TypeError("Super expression must either be null or a function");if(void 0!==e){if(e.has(t))return e.get(t);e.set(t,i)}function i(){return vt(t,arguments,mt(this).constructor)}return i.prototype=Object.create(t.prototype,{constructor:{value:i,enumerable:!1,writable:!0,configurable:!0}}),pt(i,t)})(t)}function vt(t,e,r){return(vt=gt()?Reflect.construct:function(t,e,r){var i=[null];i.push.apply(i,e);var a=new(Function.bind.apply(t,i));return r&&pt(a,r.prototype),a}).apply(null,arguments)}function gt(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(t){return!1}}function pt(t,e){return(pt=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}function mt(t){return(mt=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}var yt=Math.pow(2,17),bt=function(){function t(t){this.config=void 0,this.loader=null,this.partLoadTimeout=-1,this.config=t}var e=t.prototype;return e.destroy=function(){this.loader&&(this.loader.destroy(),this.loader=null)},e.abort=function(){this.loader&&this.loader.abort()},e.load=function(t,e){var r=this,i=t.url;if(!i)return Promise.reject(new Et({type:s.b.NETWORK_ERROR,details:s.a.FRAG_LOAD_ERROR,fatal:!1,frag:t,networkDetails:null},"Fragment does not have a "+(i?"part list":"url")));this.abort();var a=this.config,n=a.fLoader,o=a.loader;return new Promise((function(i,l){r.loader&&r.loader.destroy();var u=r.loader=t.loader=n?new n(a):new o(a),d=Tt(t),h={timeout:a.fragLoadingTimeOut,maxRetry:0,retryDelay:0,maxRetryDelay:a.fragLoadingMaxRetryTimeout,highWaterMark:yt};t.stats=u.stats,u.load(d,h,{onSuccess:function(e,a,n,s){r.resetLoader(t,u),i({frag:t,part:null,payload:e.data,networkDetails:s})},onError:function(e,i,a){r.resetLoader(t,u),l(new Et({type:s.b.NETWORK_ERROR,details:s.a.FRAG_LOAD_ERROR,fatal:!1,frag:t,response:e,networkDetails:a}))},onAbort:function(e,i,a){r.resetLoader(t,u),l(new Et({type:s.b.NETWORK_ERROR,details:s.a.INTERNAL_ABORTED,fatal:!1,frag:t,networkDetails:a}))},onTimeout:function(e,i,a){r.resetLoader(t,u),l(new Et({type:s.b.NETWORK_ERROR,details:s.a.FRAG_LOAD_TIMEOUT,fatal:!1,frag:t,networkDetails:a}))},onProgress:function(r,i,a,n){e&&e({frag:t,part:null,payload:a,networkDetails:n})}})}))},e.loadPart=function(t,e,r){var i=this;this.abort();var a=this.config,n=a.fLoader,o=a.loader;return new Promise((function(l,u){i.loader&&i.loader.destroy();var d=i.loader=t.loader=n?new n(a):new o(a),h=Tt(t,e),f={timeout:a.fragLoadingTimeOut,maxRetry:0,retryDelay:0,maxRetryDelay:a.fragLoadingMaxRetryTimeout,highWaterMark:yt};e.stats=d.stats,d.load(h,f,{onSuccess:function(a,n,s,o){i.resetLoader(t,d),i.updateStatsFromPart(t,e);var u={frag:t,part:e,payload:a.data,networkDetails:o};r(u),l(u)},onError:function(r,a,n){i.resetLoader(t,d),u(new Et({type:s.b.NETWORK_ERROR,details:s.a.FRAG_LOAD_ERROR,fatal:!1,frag:t,part:e,response:r,networkDetails:n}))},onAbort:function(r,a,n){t.stats.aborted=e.stats.aborted,i.resetLoader(t,d),u(new Et({type:s.b.NETWORK_ERROR,details:s.a.INTERNAL_ABORTED,fatal:!1,frag:t,part:e,networkDetails:n}))},onTimeout:function(r,a,n){i.resetLoader(t,d),u(new Et({type:s.b.NETWORK_ERROR,details:s.a.FRAG_LOAD_TIMEOUT,fatal:!1,frag:t,part:e,networkDetails:n}))}})}))},e.updateStatsFromPart=function(t,e){var r=t.stats,i=e.stats,a=i.total;if(r.loaded+=i.loaded,a){var n=Math.round(t.duration/e.duration),s=Math.min(Math.round(r.loaded/a),n),o=(n-s)*Math.round(r.loaded/s);r.total=r.loaded+o}else r.total=Math.max(r.loaded,r.total);var l=r.loading,u=i.loading;l.start?l.first+=u.first-u.start:(l.start=u.start,l.first=u.first),l.end=u.end},e.resetLoader=function(t,e){t.loader=null,this.loader===e&&(self.clearTimeout(this.partLoadTimeout),this.loader=null),e.destroy()},t}();function Tt(t,e){void 0===e&&(e=null);var r=e||t,i={frag:t,part:e,responseType:"arraybuffer",url:r.url,headers:{},rangeStart:0,rangeEnd:0},n=r.byteRangeStartOffset,s=r.byteRangeEndOffset;return Object(a.a)(n)&&Object(a.a)(s)&&(i.rangeStart=n,i.rangeEnd=s),i}var Et=function(t){var e,r;function i(e){for(var r,i=arguments.length,a=new Array(i>1?i-1:0),n=1;n=e.endSN&&!t.nextStart){var a=e.partList;if(null!=a&&a.length){var n=a[a.length-1];return nt.isBuffered(this.media,n.start+n.duration/2)}var s=i.getState(r);return s===$.PARTIAL||s===$.OK}return!1},f.onMediaAttached=function(t,e){var r=this.media=this.mediaBuffer=e.media;this.onvseeking=this.onMediaSeeking.bind(this),this.onvended=this.onMediaEnded.bind(this),r.addEventListener("seeking",this.onvseeking),r.addEventListener("ended",this.onvended);var i=this.config;this.levels&&i.autoStartLoad&&this.state===Dt&&this.startLoad(i.startPosition)},f.onMediaDetaching=function(){var t=this.media;null!=t&&t.ended&&(this.log("MSE detaching and video ended, reset startPosition"),this.startPosition=this.lastCurrentTime=0),t&&(t.removeEventListener("seeking",this.onvseeking),t.removeEventListener("ended",this.onvended),this.onvseeking=this.onvended=null),this.media=this.mediaBuffer=null,this.loadedmetadata=!1,this.fragmentTracker.removeAllFragments(),this.stopLoad()},f.onMediaSeeking=function(){var t=this.config,e=this.fragCurrent,r=this.media,i=this.mediaBuffer,n=this.state,s=r?r.currentTime:0,o=nt.bufferInfo(i||r,s,t.maxBufferHole);if(this.log("media seeking to "+(Object(a.a)(s)?s.toFixed(3):s)+", state: "+n),n===Pt)this.resetLoadingState();else if(e&&!o.len){var l=t.maxFragLookUpTolerance,u=e.start-l,d=s>e.start+e.duration+l;(s0&&s&&s.key&&s.iv&&"AES-128"===s.method){var o=self.performance.now();return e.decrypter.webCryptoDecrypt(new Uint8Array(a),s.key.buffer,s.iv.buffer).then((function(e){var a=self.performance.now();return i.trigger(n.a.FRAG_DECRYPTED,{frag:t,payload:e,stats:{tstart:o,tdecrypt:a}}),r.payload=e,r}))}return r})).then((function(r){var i=e.fragCurrent,a=e.hls,s=e.levels;if(!s)throw new Error("init load aborted, missing levels");s[t.level].details;var o=t.stats;e.state=_t,e.fragLoadError=0,t.data=new Uint8Array(r.payload),o.parsing.start=o.buffering.start=self.performance.now(),o.parsing.end=o.buffering.end=self.performance.now(),r.frag===i&&a.trigger(n.a.FRAG_BUFFERED,{stats:o,frag:i,part:null,id:t.type}),e.tick()})).catch((function(r){e.warn(r),e.resetFragmentLoading(t)}))},f.fragContextChanged=function(t){var e=this.fragCurrent;return!t||!e||t.level!==e.level||t.sn!==e.sn||t.urlId!==e.urlId},f.fragBufferedComplete=function(t,e){var r=this.mediaBuffer?this.mediaBuffer:this.media;this.log("Buffered "+t.type+" sn: "+t.sn+(e?" part: "+e.index:"")+" of "+("[stream-controller]"===this.logPrefix?"level":"track")+" "+t.level+" "+Lt.toString(nt.getBuffered(r))),this.state=_t,this.tick()},f._handleFragmentLoadComplete=function(t){var e=this.transmuxer;if(e){var r=t.frag,i=t.part,a=t.partsLoaded,n=!a||0===a.length||a.some((function(t){return!t})),s=new st(r.level,r.sn,r.stats.chunkCount+1,0,i?i.index:-1,!n);e.flush(s)}},f._handleFragmentLoadProgress=function(t){},f._doFragLoad=function(t,e,r,i){var s=this;if(void 0===r&&(r=null),!this.levels)throw new Error("frag load aborted, missing levels");if(r=Math.max(t.start,r||0),this.config.lowLatencyMode&&e){var o=e.partList;if(o&&i){r>t.end&&e.fragmentHint&&(t=e.fragmentHint);var l=this.getNextPart(o,t,r);if(l>-1){var u=o[l];return this.log("Loading part sn: "+t.sn+" p: "+u.index+" cc: "+t.cc+" of playlist ["+e.startSN+"-"+e.endSN+"] parts [0-"+l+"-"+(o.length-1)+"] "+("[stream-controller]"===this.logPrefix?"level":"track")+": "+t.level+", target: "+parseFloat(r.toFixed(3))),this.nextLoadPosition=u.start+u.duration,this.state=xt,this.hls.trigger(n.a.FRAG_LOADING,{frag:t,part:o[l],targetBufferTime:r}),this.doFragPartsLoad(t,o,l,i).catch((function(t){return s.handleFragLoadError(t)}))}if(!t.url||this.loadedEndOfParts(o,r))return Promise.resolve(null)}}return this.log("Loading fragment "+t.sn+" cc: "+t.cc+" "+(e?"of ["+e.startSN+"-"+e.endSN+"] ":"")+("[stream-controller]"===this.logPrefix?"level":"track")+": "+t.level+", target: "+parseFloat(r.toFixed(3))),Object(a.a)(t.sn)&&!this.bitrateTest&&(this.nextLoadPosition=t.start+t.duration),this.state=xt,this.hls.trigger(n.a.FRAG_LOADING,{frag:t,targetBufferTime:r}),this.fragmentLoader.load(t,i).catch((function(t){return s.handleFragLoadError(t)}))},f.doFragPartsLoad=function(t,e,r,i){var a=this;return new Promise((function(s,o){var l=[];!function r(u){var d=e[u];a.fragmentLoader.loadPart(t,d,i).then((function(i){l[d.index]=i;var o=i.part;a.hls.trigger(n.a.FRAG_LOADED,i);var h=e[u+1];if(!h||h.fragment!==t)return s({frag:t,part:o,partsLoaded:l});r(u+1)})).catch(o)}(r)}))},f.handleFragLoadError=function(t){var e=t.data;return e&&e.details===s.a.INTERNAL_ABORTED?this.handleFragLoadAborted(e.frag,e.part):this.hls.trigger(n.a.ERROR,e),null},f._handleTransmuxerFlush=function(t){var e=this.getCurrentContext(t);if(e&&this.state===wt){var r=e.frag,i=e.part,a=e.level,n=self.performance.now();r.stats.parsing.end=n,i&&(i.stats.parsing.end=n),this.updateLevelTiming(r,i,a,t.partial)}else this.fragCurrent||(this.state=_t)},f.getCurrentContext=function(t){var e=this.levels,r=t.level,i=t.sn,a=t.part;if(!e||!e[r])return this.warn("Levels object was unset while buffering fragment "+i+" of level "+r+". The current chunk will not be buffered."),null;var n=e[r],s=a>-1?function(t,e,r){if(!t||!t.details)return null;var i=t.details.partList;if(i)for(var a=i.length;a--;){var n=i[a];if(n.index===r&&n.fragment.sn===e)return n}return null}(n,i,a):null,o=s?s.fragment:function(t,e,r){if(!t||!t.details)return null;var i=t.details,a=i.fragments[e-i.startSN];return a||((a=i.fragmentHint)&&a.sn===e?a:en&&this.flushMainBuffer(s,t.start)}else this.flushMainBuffer(0,t.start)},f.getFwdBufferInfo=function(t,e){var r=this.config,i=this.getLoadPosition();if(!Object(a.a)(i))return null;var n=nt.bufferInfo(t,i,r.maxBufferHole);if(0===n.len&&void 0!==n.nextStart){var s=this.fragmentTracker.getBufferedFrag(i,e);if(s&&n.nextStart=r&&(e.maxMaxBufferLength/=2,this.warn("Reduce max buffer length to "+e.maxMaxBufferLength+"s"),!0)},f.getNextFragment=function(t,e){var r,i,a=e.fragments,n=a.length;if(!n)return null;var s,o=this.config,l=a[0].start;if(e.live){var u=o.initialLiveManifestSize;if(n-1&&rr.start&&r.loaded},f.getInitialLiveFragment=function(t,e){var r=this.fragPrevious,i=null;if(r){if(t.hasProgramDateTime&&(this.log("Live playlist, switching playlist, load frag with same PDT: "+r.programDateTime),i=function(t,e,r){if(null===e||!Array.isArray(t)||!t.length||!Object(a.a)(e))return null;if(e<(t[0].programDateTime||0))return null;if(e>=(t[t.length-1].endProgramDateTime||0))return null;r=r||0;for(var i=0;i=t.startSN&&n<=t.endSN){var s=e[n-t.startSN];r.cc===s.cc&&(i=s,this.log("Live playlist, switching playlist, load frag with next SN: "+i.sn))}i||(i=function(t,e){return dt.search(t,(function(t){return t.cce?-1:0}))}(e,r.cc))&&this.log("Live playlist, switching playlist, load frag with same CC: "+i.sn)}}else{var o=this.hls.liveSyncPosition;null!==o&&(i=this.getFragmentAtPosition(o,this.bitrateTest?t.fragmentEnd:t.edge,t))}return i},f.getFragmentAtPosition=function(t,e,r){var i,a=this.config,n=this.fragPrevious,s=r.fragments,o=r.endSN,l=r.fragmentHint,u=a.maxFragLookUpTolerance,d=!!(a.lowLatencyMode&&r.partList&&l);(d&&l&&!this.bitrateTest&&(s=s.concat(l),o=l.sn),te-u?0:u):i=s[s.length-1];if(i){var h=i.sn-r.startSN,f=n&&i.level===n.level,c=s[h+1];if(this.fragmentTracker.getState(i)===$.BACKTRACKED){i=null;for(var v=h;s[v]&&this.fragmentTracker.getState(s[v])===$.BACKTRACKED;)i=n?s[v--]:s[--v];i||(i=c)}else n&&i.sn===n.sn&&!d&&f&&(i.sn=n-e.maxFragLookUpTolerance&&a<=s;if(null!==i&&r.duration>i&&(a"+t.startSN+" prev-sn: "+(n?n.sn:"na")+" fragments: "+o),h}return l},f.waitForCdnTuneIn=function(t){return t.live&&t.canBlockReload&&t.tuneInGoal>Math.max(t.partHoldBack,3*t.partTarget)},f.setStartPosition=function(t,e){var r=this.startPosition;if(r"+t))}}])&&At(u.prototype,d),h&&At(u,h),i}(it);function Bt(){return self.MediaSource||self.WebKitMediaSource}function Ut(){return self.SourceBuffer||self.WebKitSourceBuffer}var Gt=r(17),jt=r(9),Ht=r(13),Kt=Bt()||{isTypeSupported:function(){return!1}},Vt=function(){function t(t,e,r,i){var a=this;this.hls=void 0,this.id=void 0,this.observer=void 0,this.frag=null,this.part=null,this.worker=void 0,this.onwmsg=void 0,this.transmuxer=null,this.onTransmuxComplete=void 0,this.onFlush=void 0,this.hls=t,this.id=e,this.onTransmuxComplete=r,this.onFlush=i;var l=t.config,u=function(e,r){(r=r||{}).frag=a.frag,r.id=a.id,t.trigger(e,r)};this.observer=new Ht.EventEmitter,this.observer.on(n.a.FRAG_DECRYPTED,u),this.observer.on(n.a.ERROR,u);var d={mp4:Kt.isTypeSupported("video/mp4"),mpeg:Kt.isTypeSupported("audio/mpeg"),mp3:Kt.isTypeSupported('audio/mp4; codecs="mp3"')},h=navigator.vendor;if(l.enableWorker&&"undefined"!=typeof Worker){var f;o.b.log("demuxing in webworker");try{f=this.worker=Gt(18),this.onwmsg=this.onWorkerMessage.bind(this),f.addEventListener("message",this.onwmsg),f.onerror=function(e){t.trigger(n.a.ERROR,{type:s.b.OTHER_ERROR,details:s.a.INTERNAL_EXCEPTION,fatal:!0,event:"demuxerWorker",error:new Error(e.message+" ("+e.filename+":"+e.lineno+")")})},f.postMessage({cmd:"init",typeSupported:d,vendor:h,id:e,config:JSON.stringify(l)})}catch(t){o.b.warn("Error in worker:",t),o.b.error("Error while initializing DemuxerWorker, fallback to inline"),f&&self.URL.revokeObjectURL(f.objectURL),this.transmuxer=new jt.c(this.observer,d,l,h,e),this.worker=null}}else this.transmuxer=new jt.c(this.observer,d,l,h,e)}var e=t.prototype;return e.destroy=function(){var t=this.worker;if(t)t.removeEventListener("message",this.onwmsg),t.terminate(),this.worker=null;else{var e=this.transmuxer;e&&(e.destroy(),this.transmuxer=null)}var r=this.observer;r&&r.removeAllListeners(),this.observer=null},e.push=function(t,e,r,i,a,n,s,l,u,d){var h,f,c=this;u.transmuxing.start=self.performance.now();var v=this.transmuxer,g=this.worker,p=n?n.start:a.start,m=a.decryptdata,y=this.frag,b=!(y&&a.cc===y.cc),T=!(y&&u.level===y.level),E=y?u.sn-y.sn:-1,S=this.part?u.part-this.part.index:1,L=!T&&(1===E||0===E&&1===S),A=self.performance.now();(T||E||0===a.stats.parsing.start)&&(a.stats.parsing.start=A),!n||!S&&L||(n.stats.parsing.start=A);var R=!(y&&(null===(h=a.initSegment)||void 0===h?void 0:h.url)===(null===(f=y.initSegment)||void 0===f?void 0:f.url)),D=new jt.b(b,L,l,T,p,R);if(!L||b||R){o.b.log("[transmuxer-interface, "+a.type+"]: Starting new transmux session for sn: "+u.sn+" p: "+u.part+" level: "+u.level+" id: "+u.id+"\n discontinuity: "+b+"\n trackSwitch: "+T+"\n contiguous: "+L+"\n accurateTimeOffset: "+l+"\n timeOffset: "+p+"\n initSegmentChange: "+R);var _=new jt.a(r,i,e,s,d);this.configureTransmuxer(_)}if(this.frag=a,this.part=n,g)g.postMessage({cmd:"demux",data:t,decryptdata:m,chunkMeta:u,state:D},t instanceof ArrayBuffer?[t]:[]);else if(v){var k=v.push(t,m,u,D);Object(jt.d)(k)?k.then((function(t){c.handleTransmuxComplete(t)})):this.handleTransmuxComplete(k)}},e.flush=function(t){var e=this;t.transmuxing.start=self.performance.now();var r=this.transmuxer,i=this.worker;if(i)i.postMessage({cmd:"flush",chunkMeta:t});else if(r){var a=r.flush(t);Object(jt.d)(a)?a.then((function(r){e.handleFlushResult(r,t)})):this.handleFlushResult(a,t)}},e.handleFlushResult=function(t,e){var r=this;t.forEach((function(t){r.handleTransmuxComplete(t)})),this.onFlush(e)},e.onWorkerMessage=function(t){var e=t.data,r=this.hls;switch(e.event){case"init":self.URL.revokeObjectURL(this.worker.objectURL);break;case"transmuxComplete":this.handleTransmuxComplete(e.data);break;case"flush":this.onFlush(e.data);break;default:e.data=e.data||{},e.data.frag=this.frag,e.data.id=this.id,r.trigger(e.event,e.data)}},e.configureTransmuxer=function(t){var e=this.worker,r=this.transmuxer;e?e.postMessage({cmd:"configure",config:t}):r&&r.configure(t)},e.handleTransmuxComplete=function(t){t.chunkMeta.transmuxing.end=self.performance.now(),this.onTransmuxComplete(t)},t}(),Wt=function(){function t(t,e,r,i){this.config=void 0,this.media=void 0,this.fragmentTracker=void 0,this.hls=void 0,this.nudgeRetry=0,this.stallReported=!1,this.stalled=null,this.moved=!1,this.seeking=!1,this.config=t,this.media=e,this.fragmentTracker=r,this.hls=i}var e=t.prototype;return e.destroy=function(){this.hls=this.fragmentTracker=this.media=null},e.poll=function(t){var e=this.config,r=this.media,i=this.stalled,a=r.currentTime,n=r.seeking,s=this.seeking&&!n,l=!this.seeking&&n;if(this.seeking=n,a===t){if((l||s)&&(this.stalled=null),!r.paused&&!r.ended&&0!==r.playbackRate&&nt.getBuffered(r).length){var u=nt.bufferInfo(r,a,0),d=u.len>0,h=u.nextStart||0;if(d||h){if(n){var f=u.len>2,c=!h||h-a>2&&!this.fragmentTracker.getPartialFragment(a);if(f||c)return;this.moved=!1}if(!this.moved&&null!==this.stalled){var v,g=Math.max(h,u.start||0)-a,p=this.hls.levels?this.hls.levels[this.hls.currentLevel]:null,m=(null==p||null===(v=p.details)||void 0===v?void 0:v.live)?2*p.details.targetduration:2;if(g>0&&g<=m)return void this._trySkipBufferHole(null)}var y=self.performance.now();if(null!==i){var b=y-i;!n&&b>=250&&this._reportStall(u.len);var T=nt.bufferInfo(r,a,e.maxBufferHole);this._tryFixBufferStall(T,b)}else this.stalled=y}}}else if(this.moved=!0,null!==i){if(this.stallReported){var E=self.performance.now()-i;o.b.warn("playback not stuck anymore @"+a+", after "+Math.round(E)+"ms"),this.stallReported=!1}this.stalled=null,this.nudgeRetry=0}},e._tryFixBufferStall=function(t,e){var r=this.config,i=this.fragmentTracker,a=this.media.currentTime,n=i.getPartialFragment(a);if(n&&this._trySkipBufferHole(n))return;t.len>r.maxBufferHole&&e>1e3*r.highBufferWatchdogPeriod&&(o.b.warn("Trying to nudge playhead over buffer-hole"),this.stalled=null,this._tryNudgeBuffer())},e._reportStall=function(t){var e=this.hls,r=this.media;this.stallReported||(this.stallReported=!0,o.b.warn("Playback stalling at @"+r.currentTime+" due to low buffer (buffer="+t+")"),e.trigger(n.a.ERROR,{type:s.b.MEDIA_ERROR,details:s.a.BUFFER_STALLED_ERROR,fatal:!1,buffer:t}))},e._trySkipBufferHole=function(t){for(var e=this.config,r=this.hls,i=this.media,a=i.currentTime,l=0,u=nt.getBuffered(i),d=0;d=l&&a0&&-1===t&&(this.log("Override startPosition with lastCurrentTime @"+e.toFixed(3)),t=e),this.state=_t,this.nextLoadPosition=this.startPosition=this.lastCurrentTime=t,this.tick()}else this._forceStartLoad=!0,this.state=Dt},f.stopLoad=function(){this._forceStartLoad=!1,t.prototype.stopLoad.call(this)},f.doTick=function(){switch(this.state){case _t:this.doTickIdle();break;case Mt:var t,e=this.levels,r=this.level,i=null==e||null===(t=e[r])||void 0===t?void 0:t.details;if(i&&(!i.live||this.levelLastLoaded===this.level)){if(this.waitForCdnTuneIn(i))break;this.state=_t;break}break;case Ct:var a,n=self.performance.now(),s=this.retryDate;(!s||n>=s||null!==(a=this.media)&&void 0!==a&&a.seeking)&&(this.log("retryDate reached, switch back to IDLE state"),this.state=_t)}this.onTickEnd()},f.onTickEnd=function(){t.prototype.onTickEnd.call(this),this.checkBuffer(),this.checkFragmentChanged()},f.doTickIdle=function(){var t,e,r=this.hls,i=this.levelLastLoaded,a=this.levels,s=this.media,o=r.config,l=r.nextLoadLevel;if(null!==i&&(s||!this.startFragRequested&&o.startFragPrefetch)&&(!this.altAudio||!this.audioOnly)&&a&&a[l]){var d=a[l];this.level=r.nextLoadLevel=l;var h=d.details;if(!h||this.state===Mt||h.live&&this.levelLastLoaded!==l)this.state=Mt;else{var f=this.getFwdBufferInfo(this.mediaBuffer?this.mediaBuffer:s,_.b.MAIN);if(null!==f)if(!(f.len>=this.getMaxBufferLength(d.maxBitrate))){if(this._streamEnded(f,h)){var c={};return this.altAudio&&(c.type="video"),this.hls.trigger(n.a.BUFFER_EOS,c),void(this.state=Pt)}var v=f.end,g=this.getNextFragment(v,h);if(this.couldBacktrack&&!this.fragPrevious&&g&&"initSegment"!==g.sn){var p=g.sn-h.startSN;p>1&&(g=h.fragments[p-1],this.fragmentTracker.removeFragment(g))}if(g&&this.fragmentTracker.getState(g)===$.OK&&this.nextLoadPosition>v){var m=this.audioOnly&&!this.altAudio?u.a.AUDIO:u.a.VIDEO;this.afterBufferFlushed(s,m,_.b.MAIN),g=this.getNextFragment(this.nextLoadPosition,h)}g&&(!g.initSegment||g.initSegment.data||this.bitrateTest||(g=g.initSegment),"identity"!==(null===(t=g.decryptdata)||void 0===t?void 0:t.keyFormat)||null!==(e=g.decryptdata)&&void 0!==e&&e.key?this.loadFragment(g,h,v):this.loadKey(g,h))}}}},f.loadFragment=function(e,r,i){var a,n=this.fragmentTracker.getState(e);if(this.fragCurrent=e,n===$.BACKTRACKED){var s=this.fragmentTracker.getBacktrackData(e);if(s)return this._handleFragmentLoadProgress(s),void this._handleFragmentLoadComplete(s);n=$.NOT_LOADED}n===$.NOT_LOADED||n===$.PARTIAL?"initSegment"===e.sn?this._loadInitSegment(e):this.bitrateTest?(e.bitrateTest=!0,this.log("Fragment "+e.sn+" of level "+e.level+" is being downloaded to test bitrate and will not be buffered"),this._loadBitrateTestFrag(e)):(this.startFragRequested=!0,t.prototype.loadFragment.call(this,e,r,i)):n===$.APPENDING?this.reduceMaxBufferLength(e.duration)&&this.fragmentTracker.removeFragment(e):0===(null===(a=this.media)||void 0===a?void 0:a.buffered.length)&&this.fragmentTracker.removeAllFragments()},f.getAppendedFrag=function(t){var e=this.fragmentTracker.getAppendedFrag(t,_.b.MAIN);return e&&"fragment"in e?e.fragment:e},f.getBufferedFrag=function(t){return this.fragmentTracker.getBufferedFrag(t,_.b.MAIN)},f.followingBufferedFrag=function(t){return t?this.getBufferedFrag(t.end+.5):null},f.immediateLevelSwitch=function(){this.abortCurrentFrag(),this.flushMainBuffer(0,Number.POSITIVE_INFINITY)},f.nextLevelSwitch=function(){var t=this.levels,e=this.media;if(null!=e&&e.readyState){var r,i=this.getAppendedFrag(e.currentTime);if(i&&i.start>1&&this.flushMainBuffer(0,i.start-1),!e.paused&&t){var a=t[this.hls.nextLoadLevel],n=this.fragLastKbps;r=n&&this.fragCurrent?this.fragCurrent.duration*a.maxBitrate/(1e3*n)+1:0}else r=0;var s=this.getBufferedFrag(e.currentTime+r);if(s){var o=this.followingBufferedFrag(s);if(o){this.abortCurrentFrag();var l=o.maxStartPTS?o.maxStartPTS:o.start,u=o.duration,d=Math.max(s.end,l+Math.min(Math.max(u-this.config.maxFragLookUpTolerance,.5*u),.75*u));this.flushMainBuffer(d,Number.POSITIVE_INFINITY)}}}},f.abortCurrentFrag=function(){var t=this.fragCurrent;this.fragCurrent=null,null!=t&&t.loader&&t.loader.abort(),this.state===kt&&(this.state=_t),this.nextLoadPosition=this.getLoadPosition()},f.flushMainBuffer=function(e,r){t.prototype.flushMainBuffer.call(this,e,r,this.altAudio?"video":null)},f.onMediaAttached=function(e,r){t.prototype.onMediaAttached.call(this,e,r);var i=r.media;this.onvplaying=this.onMediaPlaying.bind(this),this.onvseeked=this.onMediaSeeked.bind(this),i.addEventListener("playing",this.onvplaying),i.addEventListener("seeked",this.onvseeked),this.gapController=new Wt(this.config,i,this.fragmentTracker,this.hls)},f.onMediaDetaching=function(){var e=this.media;e&&(e.removeEventListener("playing",this.onvplaying),e.removeEventListener("seeked",this.onvseeked),this.onvplaying=this.onvseeked=null,this.videoBuffer=null),this.fragPlaying=null,this.gapController&&(this.gapController.destroy(),this.gapController=null),t.prototype.onMediaDetaching.call(this)},f.onMediaPlaying=function(){this.tick()},f.onMediaSeeked=function(){var t=this.media,e=t?t.currentTime:null;Object(a.a)(e)&&this.log("Media seeked to "+e.toFixed(3)),this.tick()},f.onManifestLoading=function(){this.log("Trigger BUFFER_RESET"),this.hls.trigger(n.a.BUFFER_RESET,void 0),this.fragmentTracker.removeAllFragments(),this.couldBacktrack=this.stalled=!1,this.startPosition=this.lastCurrentTime=0,this.fragPlaying=null},f.onManifestParsed=function(t,e){var r,i,a,n=!1,s=!1;e.levels.forEach((function(t){(r=t.audioCodec)&&(-1!==r.indexOf("mp4a.40.2")&&(n=!0),-1!==r.indexOf("mp4a.40.5")&&(s=!0))})),this.audioCodecSwitch=n&&s&&!("function"==typeof(null==(a=Ut())||null===(i=a.prototype)||void 0===i?void 0:i.changeType)),this.audioCodecSwitch&&this.log("Both AAC/HE-AAC audio found in levels; declaring level codec as HE-AAC"),this.levels=e.levels,this.startFragRequested=!1},f.onLevelLoading=function(t,e){var r=this.levels;if(r&&this.state===_t){var i=r[e.level];(!i.details||i.details.live&&this.levelLastLoaded!==e.level||this.waitForCdnTuneIn(i.details))&&(this.state=Mt)}},f.onLevelLoaded=function(t,e){var r,i=this.levels,a=e.level,s=e.details,o=s.totalduration;if(i){this.log("Level "+a+" loaded ["+s.startSN+","+s.endSN+"], cc ["+s.startCC+", "+s.endCC+"] duration:"+o);var l=this.fragCurrent;!l||this.state!==xt&&this.state!==Ct||l.level!==e.level&&l.loader&&(this.state=_t,l.loader.abort());var u=i[a],d=0;if(s.live||null!==(r=u.details)&&void 0!==r&&r.live){if(s.fragments[0]||(s.deltaUpdateFailed=!0),s.deltaUpdateFailed)return;d=this.alignPlaylists(s,u.details)}if(u.details=s,this.levelLastLoaded=a,this.hls.trigger(n.a.LEVEL_UPDATED,{details:s,level:a}),this.state===Mt){if(this.waitForCdnTuneIn(s))return;this.state=_t}this.startFragRequested?s.live&&this.synchronizeToLiveEdge(s):this.setStartPosition(s,d),this.tick()}else this.warn("Levels were reset while loading level "+a)},f._handleFragmentLoadProgress=function(t){var e,r=t.frag,i=t.part,a=t.payload,n=this.levels;if(n){var s=n[r.level],o=s.details;if(o){var l=s.videoCodec,u=o.PTSKnown||!o.live,d=null===(e=r.initSegment)||void 0===e?void 0:e.data,h=this._getAudioCodec(s),f=this.transmuxer=this.transmuxer||new Vt(this.hls,_.b.MAIN,this._handleTransmuxComplete.bind(this),this._handleTransmuxerFlush.bind(this)),c=i?i.index:-1,v=-1!==c,g=new st(r.level,r.sn,r.stats.chunkCount,a.byteLength,c,v),p=this.initPTS[r.cc];f.push(a,d,h,l,r,i,o.totalduration,u,g,p)}else this.warn("Dropping fragment "+r.sn+" of level "+r.level+" after level details were reset")}else this.warn("Levels were reset while fragment load was in progress. Fragment "+r.sn+" of level "+r.level+" will not be buffered")},f.onAudioTrackSwitching=function(t,e){var r=this.altAudio,i=!!e.url,a=e.id;if(!i){if(this.mediaBuffer!==this.media){this.log("Switching on main audio, use media.buffered to schedule main fragment loading"),this.mediaBuffer=this.media;var s=this.fragCurrent;null!=s&&s.loader&&(this.log("Switching to main audio track, cancel main fragment load"),s.loader.abort()),this.resetTransmuxer(),this.resetLoadingState()}else this.audioOnly&&this.resetTransmuxer();var o=this.hls;r&&o.trigger(n.a.BUFFER_FLUSHING,{startOffset:0,endOffset:Number.POSITIVE_INFINITY,type:"audio"}),o.trigger(n.a.AUDIO_TRACK_SWITCHED,{id:a})}},f.onAudioTrackSwitched=function(t,e){var r=e.id,i=!!this.hls.audioTracks[r].url;if(i){var a=this.videoBuffer;a&&this.mediaBuffer!==a&&(this.log("Switching on alternate audio, use video.buffered to schedule main fragment loading"),this.mediaBuffer=a)}this.altAudio=i,this.tick()},f.onBufferCreated=function(t,e){var r,i,a=e.tracks,n=!1;for(var s in a){var o=a[s];if("main"===o.id){if(i=s,r=o,"video"===s){var l=a[s];l&&(this.videoBuffer=l.buffer)}}else n=!0}n&&r?(this.log("Alternate track found, use "+i+".buffered to schedule main fragment loading"),this.mediaBuffer=r.buffer):this.mediaBuffer=this.media},f.onFragBuffered=function(t,e){var r=e.frag,i=e.part;if(!r||r.type===_.b.MAIN){if(this.fragContextChanged(r))return this.warn("Fragment "+r.sn+(i?" p: "+i.index:"")+" of level "+r.level+" finished buffering, but was aborted. state: "+this.state),void(this.state===Ot&&(this.state=_t));var a=i?i.stats:r.stats;this.fragLastKbps=Math.round(8*a.total/(a.buffering.end-a.loading.first)),"initSegment"!==r.sn&&(this.fragPrevious=r),this.fragBufferedComplete(r,i)}},f.onError=function(t,e){switch(e.details){case s.a.FRAG_LOAD_ERROR:case s.a.FRAG_LOAD_TIMEOUT:case s.a.KEY_LOAD_ERROR:case s.a.KEY_LOAD_TIMEOUT:this.onFragmentOrKeyLoadError(_.b.MAIN,e);break;case s.a.LEVEL_LOAD_ERROR:case s.a.LEVEL_LOAD_TIMEOUT:this.state!==Ft&&(e.fatal?(this.warn(""+e.details),this.state=Ft):e.levelRetry||this.state!==Mt||(this.state=_t));break;case s.a.BUFFER_FULL_ERROR:if("main"===e.parent&&(this.state===wt||this.state===Ot)){var r=!0,i=this.getFwdBufferInfo(this.media,_.b.MAIN);i&&i.len>.5&&(r=!this.reduceMaxBufferLength(i.len)),r&&(this.warn("buffer full error also media.currentTime is not buffered, flush main"),this.immediateLevelSwitch()),this.resetLoadingState()}}},f.checkBuffer=function(){var t=this.media,e=this.gapController;if(t&&e&&t.readyState){var r=nt.getBuffered(t);!this.loadedmetadata&&r.length?(this.loadedmetadata=!0,this.seekToStartPos()):e.poll(this.lastCurrentTime),this.lastCurrentTime=t.currentTime}},f.onFragLoadEmergencyAborted=function(){this.state=_t,this.loadedmetadata||(this.startFragRequested=!1,this.nextLoadPosition=this.startPosition),this.tickImmediate()},f.onBufferFlushed=function(t,e){var r=e.type;if(r!==u.a.AUDIO||this.audioOnly&&!this.altAudio){var i=(r===u.a.VIDEO?this.videoBuffer:this.mediaBuffer)||this.media;this.afterBufferFlushed(i,r,_.b.MAIN)}},f.onLevelsUpdated=function(t,e){this.levels=e.levels},f.swapAudioCodec=function(){this.audioCodecSwap=!this.audioCodecSwap},f.seekToStartPos=function(){var t=this.media,e=t.currentTime,r=this.startPosition;if(r>=0&&e0&&(a1&&!1===t.seeking){var r=t.currentTime;if(nt.isBuffered(t,r)?e=this.getAppendedFrag(r):nt.isBuffered(t,r+.1)&&(e=this.getAppendedFrag(r+.1)),e){var i=this.fragPlaying,a=e.level;i&&e.sn===i.sn&&i.level===a&&e.urlId===i.urlId||(this.hls.trigger(n.a.FRAG_CHANGED,{frag:e}),i&&i.level===a||this.hls.trigger(n.a.LEVEL_SWITCHED,{level:a}),this.fragPlaying=e)}}},l=i,(d=[{key:"nextLevel",get:function(){var t=this.nextBufferedFrag;return t?t.level:-1}},{key:"currentLevel",get:function(){var t=this.media;if(t){var e=this.getAppendedFrag(t.currentTime);if(e)return e.level}return-1}},{key:"nextBufferedFrag",get:function(){var t=this.media;if(t){var e=this.getAppendedFrag(t.currentTime);return this.followingBufferedFrag(e)}return null}},{key:"forceStartLoad",get:function(){return this._forceStartLoad}}])&&Yt(l.prototype,d),h&&Yt(l,h),i}(Nt),zt=function(){function t(t,e,r){void 0===e&&(e=0),void 0===r&&(r=0),this.halfLife=void 0,this.alpha_=void 0,this.estimate_=void 0,this.totalWeight_=void 0,this.halfLife=t,this.alpha_=t?Math.exp(Math.log(.5)/t):0,this.estimate_=e,this.totalWeight_=r}var e=t.prototype;return e.sample=function(t,e){var r=Math.pow(this.alpha_,t);this.estimate_=e*(1-r)+r*this.estimate_,this.totalWeight_+=t},e.getTotalWeight=function(){return this.totalWeight_},e.getEstimate=function(){if(this.alpha_){var t=1-Math.pow(this.alpha_,this.totalWeight_);if(t)return this.estimate_/t}return this.estimate_},t}(),Qt=function(){function t(t,e,r){this.defaultEstimate_=void 0,this.minWeight_=void 0,this.minDelayMs_=void 0,this.slow_=void 0,this.fast_=void 0,this.defaultEstimate_=r,this.minWeight_=.001,this.minDelayMs_=50,this.slow_=new zt(t),this.fast_=new zt(e)}var e=t.prototype;return e.update=function(t,e){var r=this.slow_,i=this.fast_;this.slow_.halfLife!==t&&(this.slow_=new zt(t,r.getEstimate(),r.getTotalWeight())),this.fast_.halfLife!==e&&(this.fast_=new zt(e,i.getEstimate(),i.getTotalWeight()))},e.sample=function(t,e){var r=(t=Math.max(t,this.minDelayMs_))/1e3,i=8*e/r;this.fast_.sample(r,i),this.slow_.sample(r,i)},e.canEstimate=function(){var t=this.fast_;return t&&t.getTotalWeight()>=this.minWeight_},e.getEstimate=function(){return this.canEstimate()?Math.min(this.fast_.getEstimate(),this.slow_.getEstimate()):this.defaultEstimate_},e.destroy=function(){},t}();function $t(t,e){for(var r=0;r=2*d/f||y<=T)){var E,S=Number.POSITIVE_INFINITY;for(E=t.level-1;E>v;E--){if((S=d*c[E].maxBitrate/(6.4*m))=y)){var L=this.bwEstimator.getEstimate();o.b.warn("Fragment "+t.sn+(e?" part "+e.index:"")+" of level "+t.level+" is loading too slowly and will cause an underbuffer; aborting and switching to level "+E+"\n Current BW estimate: "+(Object(a.a)(L)?(L/1024).toFixed(3):"Unknown")+" Kb/s\n Estimated load time for current fragment: "+y.toFixed(3)+" s\n Estimated load time for the next fragment: "+S.toFixed(3)+" s\n Time to underbuffer: "+T.toFixed(3)+" s"),r.nextLoadLevel=E,this.bwEstimator.sample(h,u.loaded),this.clearTimer(),t.loader&&(this.fragCurrent=this.partCurrent=null,t.loader.abort()),r.trigger(n.a.FRAG_LOAD_EMERGENCY_ABORTED,{frag:t,part:e,stats:u})}}}}}},l.onFragLoaded=function(t,e){var r=e.frag,i=e.part;if(r.type===_.b.MAIN&&Object(a.a)(r.sn)){var s=i?i.stats:r.stats,o=i?i.duration:r.duration;if(this.clearTimer(),this.lastLoadedFragLevel=r.level,this._nextAutoLevel=-1,this.hls.config.abrMaxWithRealBitrate){var l=this.hls.levels[r.level],u=(l.loaded?l.loaded.bytes:0)+s.loaded,d=(l.loaded?l.loaded.duration:0)+o;l.loaded={bytes:u,duration:d},l.realBitrate=Math.round(8*u/d)}if(r.bitrateTest){var h={stats:s,frag:r,part:i,id:r.type};this.onFragBuffered(n.a.FRAG_BUFFERED,h),r.bitrateTest=!1}}},l.onFragBuffered=function(t,e){var r=e.frag,i=e.part,a=i?i.stats:r.stats;if(!a.aborted&&r.type===_.b.MAIN&&"initSegment"!==r.sn){var n=a.parsing.end-a.loading.start;this.bwEstimator.sample(n,a.loaded),a.bwEstimate=this.bwEstimator.getEstimate(),r.bitrateTest?this.bitrateTestDelay=n/1e3:this.bitrateTestDelay=0}},l.onError=function(t,e){switch(e.details){case s.a.FRAG_LOAD_ERROR:case s.a.FRAG_LOAD_TIMEOUT:this.clearTimer()}},l.clearTimer=function(){self.clearInterval(this.timer),this.timer=void 0},l.getNextABRAutoLevel=function(){var t=this.fragCurrent,e=this.partCurrent,r=this.hls,i=r.maxAutoLevel,a=r.config,n=r.minAutoLevel,s=r.media,l=e?e.duration:t?t.duration:0,u=s?s.currentTime:0,d=s&&0!==s.playbackRate?Math.abs(s.playbackRate):1,h=this.bwEstimator?this.bwEstimator.getEstimate():a.abrEwmaDefaultEstimate,f=(nt.bufferInfo(s,u,a.maxBufferHole).end-u)/d,c=this.findBestLevel(h,n,i,f,a.abrBandWidthFactor,a.abrBandWidthUpFactor);if(c>=0)return c;o.b.trace((f?"rebuffering expected":"buffer is empty")+", finding optimal quality level");var v=l?Math.min(l,a.maxStarvationDelay):a.maxStarvationDelay,g=a.abrBandWidthFactor,p=a.abrBandWidthUpFactor;if(!f){var m=this.bitrateTestDelay;if(m)v=(l?Math.min(l,a.maxLoadingDelay):a.maxLoadingDelay)-m,o.b.trace("bitrate test took "+Math.round(1e3*m)+"ms, set first fragment max fetchDuration to "+Math.round(1e3*v)+" ms"),g=p=1}return c=this.findBestLevel(h,n,i,f+v,g,p),Math.max(c,0)},l.findBestLevel=function(t,e,r,i,a,n){for(var s,l=this.fragCurrent,u=this.partCurrent,d=this.lastLoadedFragLevel,h=this.hls.levels,f=h[d],c=!(null==f||null===(s=f.details)||void 0===s||!s.live),v=null==f?void 0:f.codecSet,g=u?u.duration:l?l.duration:0,p=r;p>=e;p--){var m=h[p];if(m&&(!v||m.codecSet===v)){var y=m.details,b=(u?null==y?void 0:y.partTarget:null==y?void 0:y.averagetargetduration)||g,T=void 0;T=p<=d?a*t:n*t;var E=h[p].maxBitrate,S=E*b/T;if(o.b.trace("level/adjustedbw/bitrate/avgDuration/maxFetchDuration/fetchDuration: "+p+"/"+Math.round(T)+"/"+E+"/"+b+"/"+i+"/"+S),T>E&&(!S||c&&!this.bitrateTestDelay||S0||Object.keys(this.pendingTracks).length>0},e.destroy=function(){this.unregisterListeners(),this.details=null},e.registerListeners=function(){var t=this.hls;t.on(n.a.MEDIA_ATTACHING,this.onMediaAttaching,this),t.on(n.a.MEDIA_DETACHING,this.onMediaDetaching,this),t.on(n.a.MANIFEST_PARSED,this.onManifestParsed,this),t.on(n.a.BUFFER_RESET,this.onBufferReset,this),t.on(n.a.BUFFER_APPENDING,this.onBufferAppending,this),t.on(n.a.BUFFER_CODECS,this.onBufferCodecs,this),t.on(n.a.BUFFER_EOS,this.onBufferEos,this),t.on(n.a.BUFFER_FLUSHING,this.onBufferFlushing,this),t.on(n.a.LEVEL_UPDATED,this.onLevelUpdated,this),t.on(n.a.FRAG_PARSED,this.onFragParsed,this),t.on(n.a.FRAG_CHANGED,this.onFragChanged,this)},e.unregisterListeners=function(){var t=this.hls;t.off(n.a.MEDIA_ATTACHING,this.onMediaAttaching,this),t.off(n.a.MEDIA_DETACHING,this.onMediaDetaching,this),t.off(n.a.MANIFEST_PARSED,this.onManifestParsed,this),t.off(n.a.BUFFER_RESET,this.onBufferReset,this),t.off(n.a.BUFFER_APPENDING,this.onBufferAppending,this),t.off(n.a.BUFFER_CODECS,this.onBufferCodecs,this),t.off(n.a.BUFFER_EOS,this.onBufferEos,this),t.off(n.a.BUFFER_FLUSHING,this.onBufferFlushing,this),t.off(n.a.LEVEL_UPDATED,this.onLevelUpdated,this),t.off(n.a.FRAG_PARSED,this.onFragParsed,this),t.off(n.a.FRAG_CHANGED,this.onFragChanged,this)},e._initSourceBuffer=function(){this.sourceBuffer={},this.operationQueue=new ee(this.sourceBuffer),this.listeners={audio:[],video:[],audiovideo:[]}},e.onManifestParsed=function(t,e){var r=2;(e.audio&&!e.video||!e.altAudio)&&(r=1),this.bufferCodecEventsExpected=this._bufferCodecEventsTotal=r,this.details=null,o.b.log(this.bufferCodecEventsExpected+" bufferCodec event(s) expected")},e.onMediaAttaching=function(t,e){var r=this.media=e.media;if(r&&re){var i=this.mediaSource=new re;i.addEventListener("sourceopen",this._onMediaSourceOpen),i.addEventListener("sourceended",this._onMediaSourceEnded),i.addEventListener("sourceclose",this._onMediaSourceClose),r.src=self.URL.createObjectURL(i),this._objectUrl=r.src}},e.onMediaDetaching=function(){var t=this.media,e=this.mediaSource,r=this._objectUrl;if(e){if(o.b.log("[buffer-controller]: media source detaching"),"open"===e.readyState)try{e.endOfStream()}catch(t){o.b.warn("[buffer-controller]: onMediaDetaching: "+t.message+" while calling endOfStream")}this.onBufferReset(),e.removeEventListener("sourceopen",this._onMediaSourceOpen),e.removeEventListener("sourceended",this._onMediaSourceEnded),e.removeEventListener("sourceclose",this._onMediaSourceClose),t&&(r&&self.URL.revokeObjectURL(r),t.src===r?(t.removeAttribute("src"),t.load()):o.b.warn("[buffer-controller]: media.src was changed by a third party - skip cleanup")),this.mediaSource=null,this.media=null,this._objectUrl=null,this.bufferCodecEventsExpected=this._bufferCodecEventsTotal,this.pendingTracks={},this.tracks={}}this.hls.trigger(n.a.MEDIA_DETACHED,void 0)},e.onBufferReset=function(){var t=this;this.getSourceBufferTypes().forEach((function(e){var r=t.sourceBuffer[e];try{r&&(t.removeBufferListeners(e),t.mediaSource&&t.mediaSource.removeSourceBuffer(r),t.sourceBuffer[e]=void 0)}catch(t){o.b.warn("[buffer-controller]: Failed to reset the "+e+" buffer",t)}})),this._initSourceBuffer()},e.onBufferCodecs=function(t,e){var r=this,i=this.getSourceBufferTypes().length;Object.keys(e).forEach((function(t){if(i){var a=r.tracks[t];if(a&&"function"==typeof a.buffer.changeType){var n=e[t],s=n.codec,o=n.levelCodec,l=n.container;if((a.levelCodec||a.codec).replace(ie,"$1")!==(o||s).replace(ie,"$1")){var u=l+";codecs="+(o||s);r.appendChangeType(t,u)}}}else r.pendingTracks[t]=e[t]})),i||(this.bufferCodecEventsExpected=Math.max(this.bufferCodecEventsExpected-1,0),this.mediaSource&&"open"===this.mediaSource.readyState&&this.checkPendingTracks())},e.appendChangeType=function(t,e){var r=this,i=this.operationQueue,a={execute:function(){var a=r.sourceBuffer[t];a&&(o.b.log("[buffer-controller]: changing "+t+" sourceBuffer type to "+e),a.changeType(e)),i.shiftAndExecuteNext(t)},onStart:function(){},onComplete:function(){},onError:function(e){o.b.warn("[buffer-controller]: Failed to change "+t+" SourceBuffer type",e)}};i.append(a,t)},e.onBufferAppending=function(t,e){var r=this,i=this.hls,a=this.operationQueue,l=this.tracks,u=e.data,d=e.type,h=e.frag,f=e.part,c=e.chunkMeta,v=c.buffering[d],g=self.performance.now();v.start=g;var p=h.stats.buffering,m=f?f.stats.buffering:null;0===p.start&&(p.start=g),m&&0===m.start&&(m.start=g);var y=l.audio,b="audio"===d&&1===c.id&&"audio/mpeg"===(null==y?void 0:y.container),T={execute:function(){if(v.executeStart=self.performance.now(),b){var t=r.sourceBuffer[d];if(t){var e=h.start-t.timestampOffset;Math.abs(e)>=.1&&(o.b.log("[buffer-controller]: Updating audio SourceBuffer timestampOffset to "+h.start+" (delta: "+e+") sn: "+h.sn+")"),t.timestampOffset=h.start)}}r.appendExecutor(u,d)},onStart:function(){},onComplete:function(){var t=self.performance.now();v.executeEnd=v.end=t,0===p.first&&(p.first=t),m&&0===m.first&&(m.first=t);var e=r.sourceBuffer,i={};for(var a in e)i[a]=nt.getBuffered(e[a]);r.appendError=0,r.hls.trigger(n.a.BUFFER_APPENDED,{type:d,frag:h,part:f,chunkMeta:c,parent:h.type,timeRanges:i})},onError:function(t){o.b.error("[buffer-controller]: Error encountered while trying to append to the "+d+" SourceBuffer",t);var e={type:s.b.MEDIA_ERROR,parent:h.type,details:s.a.BUFFER_APPEND_ERROR,err:t,fatal:!1};t.code===DOMException.QUOTA_EXCEEDED_ERR?e.details=s.a.BUFFER_FULL_ERROR:(r.appendError++,e.details=s.a.BUFFER_APPEND_ERROR,r.appendError>i.config.appendErrorMaxRetry&&(o.b.error("[buffer-controller]: Failed "+i.config.appendErrorMaxRetry+" times to append segment in sourceBuffer"),e.fatal=!0)),i.trigger(n.a.ERROR,e)}};a.append(T,d)},e.onBufferFlushing=function(t,e){var r=this,i=this.operationQueue,a=function(t){return{execute:r.removeExecutor.bind(r,t,e.startOffset,e.endOffset),onStart:function(){},onComplete:function(){r.hls.trigger(n.a.BUFFER_FLUSHED,{type:t})},onError:function(e){o.b.warn("[buffer-controller]: Failed to remove from "+t+" SourceBuffer",e)}}};e.type?i.append(a(e.type),e.type):this.getSourceBufferTypes().forEach((function(t){i.append(a(t),t)}))},e.onFragParsed=function(t,e){var r=this,i=e.frag,a=e.part,s=[],l=a?a.elementaryStreams:i.elementaryStreams;l[u.a.AUDIOVIDEO]?s.push("audiovideo"):(l[u.a.AUDIO]&&s.push("audio"),l[u.a.VIDEO]&&s.push("video"));0===s.length&&o.b.warn("Fragments must have at least one ElementaryStreamType set. type: "+i.type+" level: "+i.level+" sn: "+i.sn),this.blockBuffers((function(){var t=self.performance.now();i.stats.buffering.end=t,a&&(a.stats.buffering.end=t);var e=a?a.stats:i.stats;r.hls.trigger(n.a.FRAG_BUFFERED,{frag:i,part:a,stats:e,id:i.type})}),s)},e.onFragChanged=function(t,e){this.flushBackBuffer()},e.onBufferEos=function(t,e){var r=this;this.getSourceBufferTypes().reduce((function(t,i){var a=r.sourceBuffer[i];return e.type&&e.type!==i||a&&!a.ended&&(a.ended=!0,o.b.log("[buffer-controller]: "+i+" sourceBuffer now EOS")),t&&!(a&&!a.ended)}),!0)&&this.blockBuffers((function(){var t=r.mediaSource;t&&"open"===t.readyState&&t.endOfStream()}))},e.onLevelUpdated=function(t,e){var r=e.details;r.fragments.length&&(this.details=r,this.getSourceBufferTypes().length?this.blockBuffers(this.updateMediaElementDuration.bind(this)):this.updateMediaElementDuration())},e.flushBackBuffer=function(){var t=this.hls,e=this.details,r=this.media,i=this.sourceBuffer;if(r&&null!==e){var s=this.getSourceBufferTypes();if(s.length){var o=e.live&&null!==t.config.liveBackBufferLength?t.config.liveBackBufferLength:t.config.backBufferLength;if(Object(a.a)(o)&&!(o<0)){var l=r.currentTime,u=e.levelTargetDuration,d=Math.max(o,u),h=Math.floor(l/u)*u-d;s.forEach((function(r){var a=i[r];if(a){var s=nt.getBuffered(a);s.length>0&&h>s.start(0)&&(t.trigger(n.a.BACK_BUFFER_REACHED,{bufferEnd:h}),e.live&&t.trigger(n.a.LIVE_BACK_BUFFER_REACHED,{bufferEnd:h}),t.trigger(n.a.BUFFER_FLUSHING,{startOffset:0,endOffset:h,type:r}))}}))}}}},e.updateMediaElementDuration=function(){if(this.details&&this.media&&this.mediaSource&&"open"===this.mediaSource.readyState){var t=this.details,e=this.hls,r=this.media,i=this.mediaSource,n=t.fragments[0].start+t.totalduration,s=r.duration,l=Object(a.a)(i.duration)?i.duration:0;t.live&&e.config.liveDurationInfinity?(o.b.log("[buffer-controller]: Media Source duration is set to Infinity"),i.duration=1/0,this.updateSeekableRange(t)):(n>l&&n>s||!Object(a.a)(s))&&(o.b.log("[buffer-controller]: Updating Media Source duration to "+n.toFixed(3)),i.duration=n)}},e.updateSeekableRange=function(t){var e=this.mediaSource,r=t.fragments;if(r.length&&t.live&&null!=e&&e.setLiveSeekableRange){var i=Math.max(0,r[0].start),a=Math.max(i,i+t.totalduration);e.setLiveSeekableRange(i,a)}},e.checkPendingTracks=function(){var t=this.bufferCodecEventsExpected,e=this.operationQueue,r=this.pendingTracks,i=Object.keys(r).length;if(i&&!t||2===i){this.createSourceBuffers(r),this.pendingTracks={};var a=this.getSourceBufferTypes();if(0===a.length)return void this.hls.trigger(n.a.ERROR,{type:s.b.MEDIA_ERROR,details:s.a.BUFFER_INCOMPATIBLE_CODECS_ERROR,fatal:!0,reason:"could not create source buffer for media codec(s)"});a.forEach((function(t){e.executeNext(t)}))}},e.createSourceBuffers=function(t){var e=this.sourceBuffer,r=this.mediaSource;if(!r)throw Error("createSourceBuffers called when mediaSource was null");var i=0;for(var a in t)if(!e[a]){var l=t[a];if(!l)throw Error("source buffer exists for track "+a+", however track does not");var u=l.levelCodec||l.codec,d=l.container+";codecs="+u;o.b.log("[buffer-controller]: creating sourceBuffer("+d+")");try{var h=e[a]=r.addSourceBuffer(d),f=a;this.addBufferListener(f,"updatestart",this._onSBUpdateStart),this.addBufferListener(f,"updateend",this._onSBUpdateEnd),this.addBufferListener(f,"error",this._onSBUpdateError),this.tracks[a]={buffer:h,codec:u,container:l.container,levelCodec:l.levelCodec,id:l.id},i++}catch(t){o.b.error("[buffer-controller]: error while trying to add sourceBuffer: "+t.message),this.hls.trigger(n.a.ERROR,{type:s.b.MEDIA_ERROR,details:s.a.BUFFER_ADD_CODEC_ERROR,fatal:!1,error:t,mimeType:d})}}i&&this.hls.trigger(n.a.BUFFER_CREATED,{tracks:this.tracks})},e._onSBUpdateStart=function(t){this.operationQueue.current(t).onStart()},e._onSBUpdateEnd=function(t){var e=this.operationQueue;e.current(t).onComplete(),e.shiftAndExecuteNext(t)},e._onSBUpdateError=function(t,e){o.b.error("[buffer-controller]: "+t+" SourceBuffer error",e),this.hls.trigger(n.a.ERROR,{type:s.b.MEDIA_ERROR,details:s.a.BUFFER_APPENDING_ERROR,fatal:!1});var r=this.operationQueue.current(t);r&&r.onError(e)},e.removeExecutor=function(t,e,r){var i=this.media,n=this.mediaSource,s=this.operationQueue,l=this.sourceBuffer[t];if(!i||!n||!l)return o.b.warn("[buffer-controller]: Attempting to remove from the "+t+" SourceBuffer, but it does not exist"),void s.shiftAndExecuteNext(t);var u=Object(a.a)(i.duration)?i.duration:1/0,d=Object(a.a)(n.duration)?n.duration:1/0,h=Math.max(0,e),f=Math.min(r,u,d);f>h?(o.b.log("[buffer-controller]: Removing ["+h+","+f+"] from the "+t+" SourceBuffer"),l.remove(h,f)):s.shiftAndExecuteNext(t)},e.appendExecutor=function(t,e){var r=this.operationQueue,i=this.sourceBuffer[e];if(!i)return o.b.warn("[buffer-controller]: Attempting to append to the "+e+" SourceBuffer, but it does not exist"),void r.shiftAndExecuteNext(e);i.ended=!1,i.appendBuffer(t)},e.blockBuffers=function(t,e){var r=this;if(void 0===e&&(e=this.getSourceBufferTypes()),!e.length)return o.b.log("[buffer-controller]: Blocking operation requested, but no SourceBuffers exist"),void Promise.resolve(t);var i=this.operationQueue,a=e.map((function(t){return i.appendBlocker(t)}));Promise.all(a).then((function(){t(),e.forEach((function(t){var e=r.sourceBuffer[t];e&&e.updating||i.shiftAndExecuteNext(t)}))}))},e.getSourceBufferTypes=function(){return Object.keys(this.sourceBuffer)},e.addBufferListener=function(t,e,r){var i=this.sourceBuffer[t];if(i){var a=r.bind(this,t);this.listeners[t].push({event:e,listener:a}),i.addEventListener(e,a)}},e.removeBufferListeners=function(t){var e=this.sourceBuffer[t];e&&this.listeners[t].forEach((function(t){e.removeEventListener(t.event,t.listener)}))},t}();function ne(t,e){for(var r=0;r0&&this.mediaWidth>0){var t=this.hls.levels;if(t.length){var e=this.hls;e.autoLevelCapping=this.getMaxLevel(t.length-1),e.autoLevelCapping>this.autoLevelCapping&&this.streamController&&this.streamController.nextLevelSwitch(),this.autoLevelCapping=e.autoLevelCapping}}},a.getMaxLevel=function(e){var r=this,i=this.hls.levels;if(!i.length)return-1;var a=i.filter((function(i,a){return t.isLevelAllowed(a,r.restrictedLevels)&&a<=e}));return this.clientRect=null,t.getMaxLevelByMediaSize(a,this.mediaWidth,this.mediaHeight)},a.startCapping=function(){this.timer||(this.autoLevelCapping=Number.POSITIVE_INFINITY,this.hls.firstLevel=this.getMaxLevel(this.firstLevel),self.clearInterval(this.timer),this.timer=self.setInterval(this.detectPlayerSize.bind(this),1e3),this.detectPlayerSize())},a.stopCapping=function(){this.restrictedLevels=[],this.firstLevel=-1,this.autoLevelCapping=Number.POSITIVE_INFINITY,this.timer&&(self.clearInterval(this.timer),this.timer=void 0)},a.getDimensions=function(){if(this.clientRect)return this.clientRect;var t=this.media,e={width:0,height:0};if(t){var r=t.getBoundingClientRect();e.width=r.width,e.height=r.height,e.width||e.height||(e.width=r.right-r.left||t.width||0,e.height=r.bottom-r.top||t.height||0)}return this.clientRect=e,e},t.isLevelAllowed=function(t,e){return void 0===e&&(e=[]),-1===e.indexOf(t)},t.getMaxLevelByMediaSize=function(t,e,r){if(!t||!t.length)return-1;for(var i,a,n=t.length-1,s=0;s=e||o.height>=r)&&(i=o,!(a=t[s+1])||i.width!==a.width||i.height!==a.height)){n=s;break}}return n},e=t,i=[{key:"contentScaleFactor",get:function(){var t=1;try{t=self.devicePixelRatio}catch(t){}return t}}],(r=[{key:"mediaWidth",get:function(){return this.getDimensions().width*t.contentScaleFactor}},{key:"mediaHeight",get:function(){return this.getDimensions().height*t.contentScaleFactor}}])&&ne(e.prototype,r),i&&ne(e,i),t}(),oe=function(){function t(t){this.hls=void 0,this.isVideoPlaybackQualityAvailable=!1,this.timer=void 0,this.media=null,this.lastTime=void 0,this.lastDroppedFrames=0,this.lastDecodedFrames=0,this.streamController=void 0,this.hls=t,this.registerListeners()}var e=t.prototype;return e.setStreamController=function(t){this.streamController=t},e.registerListeners=function(){this.hls.on(n.a.MEDIA_ATTACHING,this.onMediaAttaching,this)},e.unregisterListeners=function(){this.hls.off(n.a.MEDIA_ATTACHING,this.onMediaAttaching)},e.destroy=function(){this.timer&&clearInterval(this.timer),this.unregisterListeners(),this.isVideoPlaybackQualityAvailable=!1,this.media=null},e.onMediaAttaching=function(t,e){var r=this.hls.config;if(r.capLevelOnFPSDrop){var i=e.media instanceof self.HTMLVideoElement?e.media:null;this.media=i,i&&"function"==typeof i.getVideoPlaybackQuality&&(this.isVideoPlaybackQualityAvailable=!0),self.clearInterval(this.timer),this.timer=self.setInterval(this.checkFPSInterval.bind(this),r.fpsDroppedMonitoringPeriod)}},e.checkFPS=function(t,e,r){var i=performance.now();if(e){if(this.lastTime){var a=i-this.lastTime,s=r-this.lastDroppedFrames,l=e-this.lastDecodedFrames,u=1e3*s/a,d=this.hls;if(d.trigger(n.a.FPS_DROP,{currentDropped:s,currentDecoded:l,totalDroppedFrames:r}),u>0&&s>d.config.fpsDroppedMonitoringThreshold*l){var h=d.currentLevel;o.b.warn("drop FPS ratio greater than max allowed value for currentLevel: "+h),h>0&&(-1===d.autoLevelCapping||d.autoLevelCapping>=h)&&(h-=1,d.trigger(n.a.FPS_DROP_LEVEL_CAPPING,{level:h,droppedLevel:d.currentLevel}),d.autoLevelCapping=h,this.streamController.nextLevelSwitch())}}this.lastTime=i,this.lastDroppedFrames=r,this.lastDecodedFrames=e}},e.checkFPSInterval=function(){var t=this.media;if(t)if(this.isVideoPlaybackQualityAvailable){var e=t.getVideoPlaybackQuality();this.checkFPS(t,e.totalVideoFrames,e.droppedVideoFrames)}else this.checkFPS(t,t.webkitDecodedFrameCount,t.webkitDroppedFrameCount)},t}(),le=r(12),ue=/^age:\s*[\d.]+\s*$/m,de=function(){function t(t){this.xhrSetup=void 0,this.requestTimeout=void 0,this.retryTimeout=void 0,this.retryDelay=void 0,this.config=null,this.callbacks=null,this.context=void 0,this.loader=null,this.stats=void 0,this.xhrSetup=t?t.xhrSetup:null,this.stats=new le.a,this.retryDelay=0}var e=t.prototype;return e.destroy=function(){this.callbacks=null,this.abortInternal(),this.loader=null,this.config=null},e.abortInternal=function(){var t=this.loader;self.clearTimeout(this.requestTimeout),self.clearTimeout(this.retryTimeout),t&&(t.onreadystatechange=null,t.onprogress=null,4!==t.readyState&&(this.stats.aborted=!0,t.abort()))},e.abort=function(){var t;this.abortInternal(),null!==(t=this.callbacks)&&void 0!==t&&t.onAbort&&this.callbacks.onAbort(this.stats,this.context,this.loader)},e.load=function(t,e,r){if(this.stats.loading.start)throw new Error("Loader can only be used once.");this.stats.loading.start=self.performance.now(),this.context=t,this.config=e,this.callbacks=r,this.retryDelay=e.retryDelay,this.loadInternal()},e.loadInternal=function(){var t=this.config,e=this.context;if(t){var r=this.loader=new self.XMLHttpRequest,i=this.stats;i.loading.first=0,i.loaded=0;var a=this.xhrSetup;try{if(a)try{a(r,e.url)}catch(t){r.open("GET",e.url,!0),a(r,e.url)}r.readyState||r.open("GET",e.url,!0);var n=this.context.headers;if(n)for(var s in n)r.setRequestHeader(s,n[s])}catch(t){return void this.callbacks.onError({code:r.status,text:t.message},e,r)}e.rangeEnd&&r.setRequestHeader("Range","bytes="+e.rangeStart+"-"+(e.rangeEnd-1)),r.onreadystatechange=this.readystatechange.bind(this),r.onprogress=this.loadprogress.bind(this),r.responseType=e.responseType,self.clearTimeout(this.requestTimeout),this.requestTimeout=self.setTimeout(this.loadtimeout.bind(this),t.timeout),r.send()}},e.readystatechange=function(){var t=this.context,e=this.loader,r=this.stats;if(t&&e){var i=e.readyState,a=this.config;if(!r.aborted&&i>=2)if(self.clearTimeout(this.requestTimeout),0===r.loading.first&&(r.loading.first=Math.max(self.performance.now(),r.loading.start)),4===i){e.onreadystatechange=null,e.onprogress=null;var n=e.status;if(n>=200&&n<300){var s,l;if(r.loading.end=Math.max(self.performance.now(),r.loading.first),l="arraybuffer"===t.responseType?(s=e.response).byteLength:(s=e.responseText).length,r.loaded=r.total=l,!this.callbacks)return;var u=this.callbacks.onProgress;if(u&&u(r,t,s,e),!this.callbacks)return;var d={url:e.responseURL,data:s};this.callbacks.onSuccess(d,r,t,e)}else r.retry>=a.maxRetry||n>=400&&n<499?(o.b.error(n+" while loading "+t.url),this.callbacks.onError({code:n,text:e.statusText},t,e)):(o.b.warn(n+" while loading "+t.url+", retrying in "+this.retryDelay+"..."),this.abortInternal(),this.loader=null,self.clearTimeout(this.retryTimeout),this.retryTimeout=self.setTimeout(this.loadInternal.bind(this),this.retryDelay),this.retryDelay=Math.min(2*this.retryDelay,a.maxRetryDelay),r.retry++)}else self.clearTimeout(this.requestTimeout),this.requestTimeout=self.setTimeout(this.loadtimeout.bind(this),a.timeout)}},e.loadtimeout=function(){o.b.warn("timeout while loading "+this.context.url);var t=this.callbacks;t&&(this.abortInternal(),t.onTimeout(this.stats,this.context,this.loader))},e.loadprogress=function(t){var e=this.stats;e.loaded=t.loaded,t.lengthComputable&&(e.total=t.total)},e.getCacheAge=function(){var t=null;if(this.loader&&ue.test(this.loader.getAllResponseHeaders())){var e=this.loader.getResponseHeader("age");t=e?parseFloat(e):null}return t},t}(),he=r(16);function fe(t){var e="function"==typeof Map?new Map:void 0;return(fe=function(t){if(null===t||(r=t,-1===Function.toString.call(r).indexOf("[native code]")))return t;var r;if("function"!=typeof t)throw new TypeError("Super expression must either be null or a function");if(void 0!==e){if(e.has(t))return e.get(t);e.set(t,i)}function i(){return ce(t,arguments,pe(this).constructor)}return i.prototype=Object.create(t.prototype,{constructor:{value:i,enumerable:!1,writable:!0,configurable:!0}}),ge(i,t)})(t)}function ce(t,e,r){return(ce=ve()?Reflect.construct:function(t,e,r){var i=[null];i.push.apply(i,e);var a=new(Function.bind.apply(t,i));return r&&ge(a,r.prototype),a}).apply(null,arguments)}function ve(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(t){return!1}}function ge(t,e){return(ge=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}function pe(t){return(pe=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}function me(){return(me=Object.assign||function(t){for(var e=1;e=i&&a(e,r,n.flush(),t)):a(e,r,l,t),o()})).catch((function(){return Promise.reject()}))}()},t}();function be(t,e){return new self.Request(t.url,e)}var Te,Ee=function(t){var e,r;function i(e,r,i){var a;return(a=t.call(this,e)||this).code=void 0,a.details=void 0,a.code=r,a.details=i,a}return r=t,(e=i).prototype=Object.create(r.prototype),e.prototype.constructor=e,ge(e,r),i}(fe(Error)),Se=ye;!function(t){t.WIDEVINE="com.widevine.alpha",t.PLAYREADY="com.microsoft.playready"}(Te||(Te={}));var Le="undefined"!=typeof self&&self.navigator&&self.navigator.requestMediaKeySystemAccess?self.navigator.requestMediaKeySystemAccess.bind(self.navigator):null;function Ae(){return(Ae=Object.assign||function(t){for(var e=1;ee)return i;return 0}},{key:"maxAutoLevel",get:function(){var t=this.levels,e=this.autoLevelCapping;return-1===e&&t&&t.length?t.length-1:e}},{key:"nextAutoLevel",get:function(){return Math.min(Math.max(this.abrController.nextAutoLevel,this.minAutoLevel),this.maxAutoLevel)},set:function(t){this.abrController.nextAutoLevel=Math.max(this.minAutoLevel,t)}},{key:"audioTracks",get:function(){var t=this.audioTrackController;return t?t.audioTracks:[]}},{key:"audioTrack",get:function(){var t=this.audioTrackController;return t?t.audioTrack:-1},set:function(t){var e=this.audioTrackController;e&&(e.audioTrack=t)}},{key:"subtitleTracks",get:function(){var t=this.subtitleTrackController;return t?t.subtitleTracks:[]}},{key:"subtitleTrack",get:function(){var t=this.subtitleTrackController;return t?t.subtitleTrack:-1},set:function(t){var e=this.subtitleTrackController;e&&(e.subtitleTrack=t)}},{key:"media",get:function(){return this._media}},{key:"subtitleDisplay",get:function(){var t=this.subtitleTrackController;return!!t&&t.subtitleDisplay},set:function(t){var e=this.subtitleTrackController;e&&(e.subtitleDisplay=t)}},{key:"lowLatencyMode",get:function(){return this.config.lowLatencyMode},set:function(t){this.config.lowLatencyMode=t}},{key:"liveSyncPosition",get:function(){return this.latencyController.liveSyncPosition}},{key:"latency",get:function(){return this.latencyController.latency}},{key:"maxLatency",get:function(){return this.latencyController.maxLatency}},{key:"targetLatency",get:function(){return this.latencyController.targetLatency}},{key:"drift",get:function(){return this.latencyController.drift}},{key:"forceStartLoad",get:function(){return this.streamController.forceStartLoad}}])&&Ce(e.prototype,r),a&&Ce(e,a),t}();we.defaultConfig=void 0}]).default})); // @license-end \ No newline at end of file From 5e827c91aa1b5f41cc03c5bb07b6fe629002430b Mon Sep 17 00:00:00 2001 From: HookedBehemoth Date: Thu, 16 Jun 2022 18:46:41 +0200 Subject: [PATCH 043/409] emit body and doctype on iframe embed endpoint (#640) --- src/routes/embed.nim | 2 +- src/views/embed.nim | 5 +++-- src/views/tweet.nim | 14 +++++++++++--- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/routes/embed.nim b/src/routes/embed.nim index 8690357..baaec68 100644 --- a/src/routes/embed.nim +++ b/src/routes/embed.nim @@ -25,7 +25,7 @@ proc createEmbedRouter*(cfg: Config) = if convo == nil or convo.tweet == nil: resp Http404 - resp $renderTweetEmbed(convo.tweet, path, prefs, cfg, request) + resp renderTweetEmbed(convo.tweet, path, prefs, cfg, request) get "/embed/Tweet.html": let id = @"id" diff --git a/src/views/embed.nim b/src/views/embed.nim index e6afffd..a884cf3 100644 --- a/src/views/embed.nim +++ b/src/views/embed.nim @@ -15,7 +15,8 @@ proc renderVideoEmbed*(tweet: Tweet; cfg: Config; req: Request): string = let node = buildHtml(html(lang="en")): renderHead(prefs, cfg, req, video=vidUrl, images=(@[thumb])) - tdiv(class="embed-video"): - renderVideo(get(tweet.video), prefs, "") + body: + tdiv(class="embed-video"): + renderVideo(get(tweet.video), prefs, "") result = doctype & $node diff --git a/src/views/tweet.nim b/src/views/tweet.nim index 5bee864..daad61a 100644 --- a/src/views/tweet.nim +++ b/src/views/tweet.nim @@ -7,6 +7,9 @@ import renderutils import ".."/[types, utils, formatters] import general +const + doctype = "\n" + proc getSmallPic(url: string): string = result = url if "?" notin url and not url.endsWith("placeholder.png"): @@ -364,7 +367,12 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class=""; index=0; a(class="show-thread", href=("/i/status/" & $tweet.threadId)): text "Show this thread" -proc renderTweetEmbed*(tweet: Tweet; path: string; prefs: Prefs; cfg: Config; req: Request): VNode = - buildHtml(tdiv(class="tweet-embed")): +proc renderTweetEmbed*(tweet: Tweet; path: string; prefs: Prefs; cfg: Config; req: Request): string = + let node = buildHtml(html(lang="en")): renderHead(prefs, cfg, req) - renderTweet(tweet, prefs, path, mainTweet=true) + + body: + tdiv(class="tweet-embed"): + renderTweet(tweet, prefs, path, mainTweet=true) + + result = doctype & $node From 81ec41328d5684dd395f584254d723abee213ac0 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Thu, 23 Jun 2022 16:47:03 -0400 Subject: [PATCH 044/409] Add redirect for `thread` links (#647) --- src/routes/status.nim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/routes/status.nim b/src/routes/status.nim index 0303152..1104282 100644 --- a/src/routes/status.nim +++ b/src/routes/status.nim @@ -72,3 +72,6 @@ proc createStatusRouter*(cfg: Config) = get "/i/web/status/@id": redirect("/i/status/" & @"id") + + get "/@name/thread/@id/?": + redirect("/$1/status/$2" % [@"name", @"id"]) From 0200cc21a32e0ed87025744066894cdc26f73ff3 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Tue, 5 Jul 2022 15:15:38 +0800 Subject: [PATCH 045/409] add `threads:off` to config file (#662) --- config.nims | 1 + 1 file changed, 1 insertion(+) diff --git a/config.nims b/config.nims index ee77289..b7e52d0 100644 --- a/config.nims +++ b/config.nims @@ -1,5 +1,6 @@ --define:ssl --define:useStdLib +--threads:off # workaround httpbeast file upload bug --assertions:off From 17cb977e70cfefe505b3d294609aa69098e2f9e8 Mon Sep 17 00:00:00 2001 From: jackyzy823 Date: Sun, 17 Jul 2022 17:42:40 +0100 Subject: [PATCH 046/409] fix profile-website css (#669) --- src/sass/profile/card.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sass/profile/card.scss b/src/sass/profile/card.scss index cc68d7d..85878e4 100644 --- a/src/sass/profile/card.scss +++ b/src/sass/profile/card.scss @@ -73,9 +73,9 @@ } } - .profile-joindate, .profile-location, profile-website { + .profile-joindate, .profile-location, .profile-website { color: var(--fg_faded); - margin: 2px 0; + margin: 1px 0; width: 100%; } } From 5f31e86e0e8578377fa7d5aeb9631bbb2d35ef1e Mon Sep 17 00:00:00 2001 From: Mico Date: Thu, 21 Jul 2022 16:04:27 +0300 Subject: [PATCH 047/409] Fixes selection issues on iOS devices (#671) --- src/sass/tweet/_base.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sass/tweet/_base.scss b/src/sass/tweet/_base.scss index b5410f5..549298a 100644 --- a/src/sass/tweet/_base.scss +++ b/src/sass/tweet/_base.scss @@ -100,6 +100,7 @@ .avatar { &.round { border-radius: 50%; + -webkit-user-select: none; } &.mini { @@ -200,6 +201,7 @@ .tweet-stats { margin-bottom: -3px; + -webkit-user-select: none; } .tweet-stat { @@ -231,6 +233,7 @@ left: 0; top: 0; position: absolute; + -webkit-user-select: none; &:hover { background-color: var(--bg_hover); From 077ebc95fa5c3e7700581f66ba19ee313edee46a Mon Sep 17 00:00:00 2001 From: jackyzy823 Date: Mon, 15 Aug 2022 00:07:40 +0100 Subject: [PATCH 048/409] make video control bar fit parent div (#683) --- src/sass/tweet/video.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sass/tweet/video.scss b/src/sass/tweet/video.scss index 2dd257a..1e9096e 100644 --- a/src/sass/tweet/video.scss +++ b/src/sass/tweet/video.scss @@ -3,7 +3,7 @@ video { max-height: 100%; - max-width: 100%; + width: 100%; } .gallery-video { From 8edfda39ecb36855d66b51cd706b08c77602de28 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 30 Aug 2022 15:18:46 +0800 Subject: [PATCH 049/409] bump `packedjson` dependency to include a fix for ARC/ORC (#691) Hello. `shallowCopy` has been removed for ARC/ORC since it does a deep copy for strings/seqs, which breaks the semantics of `shallowCopy`. https://github.com/Araq/packedjson/pull/13 is a fix for `packedjson` to support ARC/ORC. The PR bumps `packedjson` dependency to include [a fix](https://github.com/Araq/packedjson/commit/9e6fbb63cb9e0ab8ff570c04bec28cc713ca1f7c) for ARC/ORC. --- nitter.nimble | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nitter.nimble b/nitter.nimble index 90a7921..4dcb946 100644 --- a/nitter.nimble +++ b/nitter.nimble @@ -16,7 +16,7 @@ requires "karax#5498909" requires "sass#e683aa1" requires "nimcrypto#a5742a9" requires "markdown#a661c26" -requires "packedjson#d11d167" +requires "packedjson#9e6fbb6" requires "supersnappy#2.1.1" requires "redpool#8b7c1db" requires "https://github.com/zedeus/redis#d0a0e6f" From 1cf8e6756707b9841fea39915e154bb2ef03c1c7 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 15 Sep 2022 20:47:02 +0800 Subject: [PATCH 050/409] bump karax version (#694) Co-authored-by: xflywind <43030857+xflywind@users.noreply.github.com> --- nitter.nimble | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nitter.nimble b/nitter.nimble index 4dcb946..72863bd 100644 --- a/nitter.nimble +++ b/nitter.nimble @@ -12,7 +12,7 @@ bin = @["nitter"] requires "nim >= 1.4.8" requires "jester >= 0.5.0" -requires "karax#5498909" +requires "karax#6abcb77" requires "sass#e683aa1" requires "nimcrypto#a5742a9" requires "markdown#a661c26" From 2ac3afa5b273a502d7632e9346c7c3bc9283fb48 Mon Sep 17 00:00:00 2001 From: Zed Date: Wed, 21 Sep 2022 05:47:16 +0200 Subject: [PATCH 051/409] Retry intermittent 401 Unauthorized requests --- src/apiutils.nim | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/apiutils.nim b/src/apiutils.nim index fd6960f..77ff368 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -61,12 +61,22 @@ template fetchImpl(result, fetchBody) {.dirty.} = try: var resp: AsyncResponse pool.use(genHeaders(token)): - resp = await c.get($url) - result = await resp.body + template getContent = + resp = await c.get($url) + result = await resp.body - if resp.status == $Http503: - badClient = true - raise newException(InternalError, result) + getContent() + + # Twitter randomly returns 401 errors with an empty body quite often. + # Retrying the request usually works. + var attempt = 0 + while resp.status == "401 Unauthorized" and result.len == 0 and attempt < 3: + inc attempt + getContent() + + if resp.status == $Http503: + badClient = true + raise newException(InternalError, result) if result.len > 0: if resp.headers.getOrDefault("content-encoding") == "gzip": From 6272297d64d4cc8f1268626bee31a0e3d5899f51 Mon Sep 17 00:00:00 2001 From: Kavin <20838718+FireMasterK@users.noreply.github.com> Date: Sat, 19 Nov 2022 02:47:29 +0000 Subject: [PATCH 052/409] Update hostname for piped (#728) --- nitter.example.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nitter.example.conf b/nitter.example.conf index b987cd5..cc7551a 100644 --- a/nitter.example.conf +++ b/nitter.example.conf @@ -37,7 +37,7 @@ tokenCount = 10 [Preferences] theme = "Nitter" replaceTwitter = "nitter.net" -replaceYouTube = "piped.kavin.rocks" +replaceYouTube = "piped.video" replaceReddit = "teddit.net" replaceInstagram = "" proxyVideos = true From 02557b67c39831005690118b651cc03e719d8735 Mon Sep 17 00:00:00 2001 From: Zed Date: Sun, 27 Nov 2022 00:03:11 +0100 Subject: [PATCH 053/409] Fix minor bug --- src/parserutils.nim | 8 ++++---- src/routes/rss.nim | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/parserutils.nim b/src/parserutils.nim index 51ccea5..4b89236 100644 --- a/src/parserutils.nim +++ b/src/parserutils.nim @@ -28,13 +28,13 @@ template `?`*(js: JsonNode): untyped = if j.isNull: return j -template `with`*(ident, value, body): untyped = - block: +template with*(ident, value, body): untyped = + if true: let ident {.inject.} = value if ident != nil: body -template `with`*(ident; value: JsonNode; body): untyped = - block: +template with*(ident; value: JsonNode; body): untyped = + if true: let ident {.inject.} = value if value.notNull: body diff --git a/src/routes/rss.nim b/src/routes/rss.nim index 700c215..2b23d0a 100644 --- a/src/routes/rss.nim +++ b/src/routes/rss.nim @@ -1,5 +1,5 @@ # SPDX-License-Identifier: AGPL-3.0-only -import asyncdispatch, strutils, strformat, tables, times, hashes, uri +import asyncdispatch, tables, times, hashes, uri import jester From dd184715681ff311e5cd58be25f86022a47d3644 Mon Sep 17 00:00:00 2001 From: Zed Date: Sun, 27 Nov 2022 00:28:49 +0100 Subject: [PATCH 054/409] Make YouTube regex case insensitive Fixes #726 --- src/formatters.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/formatters.nim b/src/formatters.nim index 28bfa1c..96b3612 100644 --- a/src/formatters.nim +++ b/src/formatters.nim @@ -12,7 +12,7 @@ let twRegex = re"(?<=(?twitter\.com(\S+)""" - ytRegex = re"([A-z.]+\.)?youtu(be\.com|\.be)" + ytRegex = re(r"([A-z.]+\.)?youtu(be\.com|\.be)", {reStudy, reIgnoreCase}) igRegex = re"(www\.)?instagram\.com" rdRegex = re"(? Date: Sun, 27 Nov 2022 00:40:33 +0100 Subject: [PATCH 055/409] Revert /c/ removal from YouTube replacer Fixes #724 --- src/formatters.nim | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/formatters.nim b/src/formatters.nim index 96b3612..7b939ca 100644 --- a/src/formatters.nim +++ b/src/formatters.nim @@ -56,8 +56,6 @@ proc replaceUrls*(body: string; prefs: Prefs; absolute=""): string = if prefs.replaceYouTube.len > 0 and "youtu" in result: result = result.replace(ytRegex, prefs.replaceYouTube) - if prefs.replaceYouTube in result: - result = result.replace("/c/", "/") if prefs.replaceTwitter.len > 0 and ("twitter.com" in body or tco in body): result = result.replace(tco, &"{https}{prefs.replaceTwitter}/t.co") From 9ef0f649fd575876c0abe4d35af6d2f2ca07c12e Mon Sep 17 00:00:00 2001 From: Zed Date: Sun, 27 Nov 2022 00:59:54 +0100 Subject: [PATCH 056/409] Bump dependencies --- nitter.nimble | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nitter.nimble b/nitter.nimble index 72863bd..a4d2e87 100644 --- a/nitter.nimble +++ b/nitter.nimble @@ -11,17 +11,17 @@ bin = @["nitter"] # Dependencies requires "nim >= 1.4.8" -requires "jester >= 0.5.0" +requires "jester#baca3f" requires "karax#6abcb77" requires "sass#e683aa1" -requires "nimcrypto#a5742a9" +requires "nimcrypto#b41129f" requires "markdown#a661c26" requires "packedjson#9e6fbb6" -requires "supersnappy#2.1.1" +requires "supersnappy#6c94198" requires "redpool#8b7c1db" requires "https://github.com/zedeus/redis#d0a0e6f" -requires "zippy#0.9.11" -requires "flatty#0.2.3" +requires "zippy#61922b9" +requires "flatty#9f885d7" requires "jsony#d0e69bd" From b1ef505cffe075a4c988c9989a4e9d81e65a5266 Mon Sep 17 00:00:00 2001 From: Zed Date: Sun, 27 Nov 2022 01:57:32 +0100 Subject: [PATCH 057/409] Fix "Show this thread" for pinned threads --- src/parser.nim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/parser.nim b/src/parser.nim index b731bdb..10186be 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -206,6 +206,10 @@ proc parseTweet(js: JsonNode): Tweet = ) ) + # fix for pinned threads + if result.hasThread and result.threadId == 0: + result.threadId = js{"self_thread", "id_str"}.getId + result.expandTweetEntities(js) if js{"is_quote_status"}.getBool: From 92a6eb0339b8288ce6af4e51c6b5198b1ce0d823 Mon Sep 17 00:00:00 2001 From: Zed Date: Sun, 27 Nov 2022 12:49:50 +0100 Subject: [PATCH 058/409] Bump Dockerfile Nim to 1.6.10 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 52dadf6..f65c3ed 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM nimlang/nim:1.6.2-alpine-regular as nim +FROM nimlang/nim:1.6.10-alpine-regular as nim LABEL maintainer="setenforce@protonmail.com" RUN apk --no-cache add libsass-dev pcre From 85316f8f8d265ea4449e7166c1f6651fb595c41d Mon Sep 17 00:00:00 2001 From: Zed Date: Sun, 27 Nov 2022 15:50:08 +0100 Subject: [PATCH 059/409] Reduce usage of strformat, minor perf improvement --- src/formatters.nim | 4 ++-- src/routes/rss.nim | 51 ++++++++++++++++++++++------------------- src/routes/search.nim | 4 ++-- src/routes/timeline.nim | 4 ++-- src/views/general.nim | 2 +- src/views/profile.nim | 2 +- 6 files changed, 36 insertions(+), 31 deletions(-) diff --git a/src/formatters.nim b/src/formatters.nim index 7b939ca..87ff647 100644 --- a/src/formatters.nim +++ b/src/formatters.nim @@ -58,11 +58,11 @@ proc replaceUrls*(body: string; prefs: Prefs; absolute=""): string = result = result.replace(ytRegex, prefs.replaceYouTube) if prefs.replaceTwitter.len > 0 and ("twitter.com" in body or tco in body): - result = result.replace(tco, &"{https}{prefs.replaceTwitter}/t.co") + result = result.replace(tco, https & prefs.replaceTwitter & "/t.co") result = result.replace(cards, prefs.replaceTwitter & "/cards") result = result.replace(twRegex, prefs.replaceTwitter) result = result.replacef(twLinkRegex, a( - prefs.replaceTwitter & "$2", href = &"{https}{prefs.replaceTwitter}$1")) + prefs.replaceTwitter & "$2", href = https & prefs.replaceTwitter & "$1")) if prefs.replaceReddit.len > 0 and ("reddit.com" in result or "redd.it" in result): result = result.replace(rdShortRegex, prefs.replaceReddit & "/comments/") diff --git a/src/routes/rss.nim b/src/routes/rss.nim index 2b23d0a..5da29b0 100644 --- a/src/routes/rss.nim +++ b/src/routes/rss.nim @@ -10,6 +10,11 @@ include "../views/rss.nimf" export times, hashes +proc redisKey*(page, name, cursor: string): string = + result = page & ":" & name + if cursor.len > 0: + result &= ":" & cursor + proc timelineRss*(req: Request; cfg: Config; query: Query): Future[Rss] {.async.} = var profile: Profile let @@ -42,8 +47,8 @@ proc timelineRss*(req: Request; cfg: Config; query: Query): Future[Rss] {.async. template respRss*(rss, page) = if rss.cursor.len == 0: let info = case page - of "User": &""" "{@"name"}" """ - of "List": &""" "{@"id"}" """ + of "User": " \"" & @"name" & "\" " + of "List": " \"" & @"id" & "\" " else: " " resp Http404, showError(page & info & "not found", cfg) @@ -67,7 +72,7 @@ proc createRssRouter*(cfg: Config) = let cursor = getCursor() - key = &"search:{hash(genQueryUrl(query))}:cursor" + key = redisKey("search", $hash(genQueryUrl(query)), cursor) var rss = await getCachedRss(key) if rss.cursor.len > 0: @@ -84,9 +89,8 @@ proc createRssRouter*(cfg: Config) = cond cfg.enableRss cond '.' notin @"name" let - cursor = getCursor() name = @"name" - key = &"twitter:{name}:{cursor}" + key = redisKey("twitter", name, getCursor()) var rss = await getCachedRss(key) if rss.cursor.len > 0: @@ -101,18 +105,20 @@ proc createRssRouter*(cfg: Config) = cond cfg.enableRss cond '.' notin @"name" cond @"tab" in ["with_replies", "media", "search"] - let name = @"name" - let query = - case @"tab" - of "with_replies": getReplyQuery(name) - of "media": getMediaQuery(name) - of "search": initQuery(params(request), name=name) - else: Query(fromUser: @[name]) + let + name = @"name" + tab = @"tab" + query = + case tab + of "with_replies": getReplyQuery(name) + of "media": getMediaQuery(name) + of "search": initQuery(params(request), name=name) + else: Query(fromUser: @[name]) - var key = &"""{@"tab"}:{@"name"}:""" - if @"tab" == "search": - key &= $hash(genQueryUrl(query)) & ":" - key &= getCursor() + let searchKey = if tab != "search": "" + else: ":" & $hash(genQueryUrl(query)) + + let key = redisKey(tab, name & searchKey, getCursor()) var rss = await getCachedRss(key) if rss.cursor.len > 0: @@ -132,28 +138,27 @@ proc createRssRouter*(cfg: Config) = cursor = getCursor() if list.id.len == 0: - resp Http404, showError(&"""List "{@"slug"}" not found""", cfg) + resp Http404, showError("List \"" & @"slug" & "\" not found", cfg) - let url = &"/i/lists/{list.id}/rss" + let url = "/i/lists/" & list.id & "/rss" if cursor.len > 0: - redirect(&"{url}?cursor={encodeUrl(cursor, false)}") + redirect(url & "?cursor=" & encodeUrl(cursor, false)) else: redirect(url) get "/i/lists/@id/rss": cond cfg.enableRss let + id = @"id" cursor = getCursor() - key = - if cursor.len == 0: "lists:" & @"id" - else: &"""lists:{@"id"}:{cursor}""" + key = redisKey("lists", id, cursor) var rss = await getCachedRss(key) if rss.cursor.len > 0: respRss(rss, "List") let - list = await getCachedList(id=(@"id")) + list = await getCachedList(id=id) timeline = await getListTimeline(list.id, cursor) rss.cursor = timeline.bottom rss.feed = renderListRss(timeline.content, list, cfg) diff --git a/src/routes/search.nim b/src/routes/search.nim index 554f2f6..3fc44a9 100644 --- a/src/routes/search.nim +++ b/src/routes/search.nim @@ -1,5 +1,5 @@ # SPDX-License-Identifier: AGPL-3.0-only -import strutils, strformat, uri +import strutils, uri import jester @@ -37,7 +37,7 @@ proc createSearchRouter*(cfg: Config) = resp Http404, showError("Invalid search", cfg) get "/hashtag/@hash": - redirect(&"""/search?q={encodeUrl("#" & @"hash")}""") + redirect("/search?q=" & encodeUrl("#" & @"hash")) get "/opensearch": let url = getUrlPrefix(cfg) & "/search?q=" diff --git a/src/routes/timeline.nim b/src/routes/timeline.nim index 9d97c29..a0a6e21 100644 --- a/src/routes/timeline.nim +++ b/src/routes/timeline.nim @@ -1,5 +1,5 @@ # SPDX-License-Identifier: AGPL-3.0-only -import asyncdispatch, strutils, strformat, sequtils, uri, options, times +import asyncdispatch, strutils, sequtils, uri, options, times import jester, karax/vdom import router_utils @@ -102,7 +102,7 @@ proc showTimeline*(request: Request; query: Query; cfg: Config; prefs: Prefs; template respTimeline*(timeline: typed) = let t = timeline if t.len == 0: - resp Http404, showError(&"""User "{@"name"}" not found""", cfg) + resp Http404, showError("User \"" & @"name" & "\" not found", cfg) resp t template respUserId*() = diff --git a/src/views/general.nim b/src/views/general.nim index f242e66..b18dae5 100644 --- a/src/views/general.nim +++ b/src/views/general.nim @@ -81,7 +81,7 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc=""; title: if titleText.len > 0: - text &"{titleText}|{cfg.title}" + text titleText & " | " & cfg.title else: text cfg.title diff --git a/src/views/profile.nim b/src/views/profile.nim index 73ce59d..2a007b1 100644 --- a/src/views/profile.nim +++ b/src/views/profile.nim @@ -108,7 +108,7 @@ proc renderProfile*(profile: var Profile; prefs: Prefs; path: string): VNode = renderBanner(profile.user.banner) let sticky = if prefs.stickyProfile: " sticky" else: "" - tdiv(class=(&"profile-tab{sticky}")): + tdiv(class=("profile-tab" & sticky)): renderUserCard(profile.user, prefs) if profile.photoRail.len > 0: renderPhotoRail(profile) From 310b0ab9a1d80ff6df64232ff52f7b214d35d9f3 Mon Sep 17 00:00:00 2001 From: Zed Date: Sun, 27 Nov 2022 16:00:16 +0100 Subject: [PATCH 060/409] Update test --- tests/test_quote.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_quote.py b/tests/test_quote.py index e1ee728..1b458ea 100644 --- a/tests/test_quote.py +++ b/tests/test_quote.py @@ -3,7 +3,7 @@ from parameterized import parameterized text = [ ['elonmusk/status/1138136540096319488', - 'Trev Page', '@Model3Owners', + 'TREV PAGE', '@Model3Owners', """As of March 58.4% of new car sales in Norway are electric. What are we doing wrong? reuters.com/article/us-norwa…"""], From 096d44856fb219d467519dbfc3fc330fe49ac712 Mon Sep 17 00:00:00 2001 From: Zed Date: Sun, 27 Nov 2022 16:06:36 +0100 Subject: [PATCH 061/409] Remove Location field autofocus from search panel --- src/views/renderutils.nim | 4 ++-- src/views/search.nim | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/views/renderutils.nim b/src/views/renderutils.nim index bab01cd..8e8d530 100644 --- a/src/views/renderutils.nim +++ b/src/views/renderutils.nim @@ -55,12 +55,12 @@ proc genCheckbox*(pref, label: string; state: bool): VNode = else: input(name=pref, `type`="checkbox") span(class="checkbox") -proc genInput*(pref, label, state, placeholder: string; class=""): VNode = +proc genInput*(pref, label, state, placeholder: string; class=""; autofocus=true): VNode = let p = placeholder buildHtml(tdiv(class=("pref-group pref-input " & class))): if label.len > 0: label(`for`=pref): text label - if state.len == 0: + if autofocus and state.len == 0: input(name=pref, `type`="text", placeholder=p, value=state, autofocus="") else: input(name=pref, `type`="text", placeholder=p, value=state) diff --git a/src/views/search.nim b/src/views/search.nim index 94bbac8..77ba14f 100644 --- a/src/views/search.nim +++ b/src/views/search.nim @@ -88,7 +88,7 @@ proc renderSearchPanel*(query: Query): VNode = genDate("until", query.until) tdiv: span(class="search-title"): text "Near" - genInput("near", "", query.near, placeholder="Location...") + genInput("near", "", query.near, "Location...", autofocus=false) proc renderTweetSearch*(results: Result[Tweet]; prefs: Prefs; path: string; pinned=none(Tweet)): VNode = From 78b6877b02d1787c6c5ea07523ccb944421130fb Mon Sep 17 00:00:00 2001 From: Zed Date: Sun, 27 Nov 2022 16:19:30 +0100 Subject: [PATCH 062/409] Add "Search (...)" to tab title Fixes #247 --- src/routes/search.nim | 12 +++++++----- src/views/profile.nim | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/routes/search.nim b/src/routes/search.nim index 3fc44a9..da1ba55 100644 --- a/src/routes/search.nim +++ b/src/routes/search.nim @@ -14,25 +14,27 @@ export search proc createSearchRouter*(cfg: Config) = router search: get "/search/?": - if @"q".len > 500: + let q = @"q" + if q.len > 500: resp Http400, showError("Search input too long.", cfg) let prefs = cookiePrefs() query = initQuery(params(request)) + title = "Search" & (if q.len > 0: " (" & q & ")" else: "") case query.kind of users: - if "," in @"q": - redirect("/" & @"q") + if "," in q: + redirect("/" & q) let users = await getSearch[User](query, getCursor()) - resp renderMain(renderUserSearch(users, prefs), request, cfg, prefs) + resp renderMain(renderUserSearch(users, prefs), request, cfg, prefs, title) of tweets: let tweets = await getSearch[Tweet](query, getCursor()) rss = "/search/rss?" & genQueryUrl(query) resp renderMain(renderTweetSearch(tweets, prefs, getPath()), - request, cfg, prefs, rss=rss) + request, cfg, prefs, title, rss=rss) else: resp Http404, showError("Invalid search", cfg) diff --git a/src/views/profile.nim b/src/views/profile.nim index 2a007b1..2b2e410 100644 --- a/src/views/profile.nim +++ b/src/views/profile.nim @@ -50,7 +50,7 @@ proc renderUserCard*(user: User; prefs: Prefs): VNode = span: let url = replaceUrls(user.website, prefs) icon "link" - a(href=url): text shortLink(url) + a(href=url): text url.shortLink tdiv(class="profile-joindate"): span(title=getJoinDateFull(user)): From 6a7e56b61366b8564defff4d8d3e58591a0ba58f Mon Sep 17 00:00:00 2001 From: Zed Date: Sun, 27 Nov 2022 16:20:53 +0100 Subject: [PATCH 063/409] Linting --- src/parser.nim | 1 - src/routes/search.nim | 2 +- src/sass/tweet/_base.scss | 1 - src/sass/tweet/thread.scss | 1 - 4 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/parser.nim b/src/parser.nim index 10186be..2b0c6a4 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -45,7 +45,6 @@ proc parseGraphList*(js: JsonNode): List = banner: list{"custom_banner_media", "media_info", "url"}.getImageStr ) - proc parsePoll(js: JsonNode): Poll = let vals = js{"binding_values"} # name format is pollNchoice_* diff --git a/src/routes/search.nim b/src/routes/search.nim index da1ba55..b2fd718 100644 --- a/src/routes/search.nim +++ b/src/routes/search.nim @@ -44,4 +44,4 @@ proc createSearchRouter*(cfg: Config) = get "/opensearch": let url = getUrlPrefix(cfg) & "/search?q=" resp Http200, {"Content-Type": "application/opensearchdescription+xml"}, - generateOpenSearchXML(cfg.title, cfg.hostname, url) + generateOpenSearchXML(cfg.title, cfg.hostname, url) diff --git a/src/sass/tweet/_base.scss b/src/sass/tweet/_base.scss index 549298a..69f51c0 100644 --- a/src/sass/tweet/_base.scss +++ b/src/sass/tweet/_base.scss @@ -138,7 +138,6 @@ } } - .attribution { display: flex; pointer-events: all; diff --git a/src/sass/tweet/thread.scss b/src/sass/tweet/thread.scss index f8ad603..5fbad21 100644 --- a/src/sass/tweet/thread.scss +++ b/src/sass/tweet/thread.scss @@ -23,7 +23,6 @@ font-size: 18px; } - @media(max-width: 600px) { .main-tweet .tweet-content { font-size: 16px; From da4d6eabddc322306b152b7d856b13c530f58e95 Mon Sep 17 00:00:00 2001 From: Zed Date: Sun, 27 Nov 2022 17:24:29 +0100 Subject: [PATCH 064/409] Add enum hooks to log parseHook jsony errors --- src/experimental/parser/unifiedcard.nim | 2 ++ src/experimental/types/unifiedcard.nim | 26 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/experimental/parser/unifiedcard.nim b/src/experimental/parser/unifiedcard.nim index 337c3b9..12689e1 100644 --- a/src/experimental/parser/unifiedcard.nim +++ b/src/experimental/parser/unifiedcard.nim @@ -82,6 +82,8 @@ proc parseUnifiedCard*(json: string): Card = component.parseMedia(card, result) of buttonGroup: discard + of unknown: + echo "ERROR: Unknown component type: ", json case component.kind of twitterListDetails: diff --git a/src/experimental/types/unifiedcard.nim b/src/experimental/types/unifiedcard.nim index 16500df..737fdd6 100644 --- a/src/experimental/types/unifiedcard.nim +++ b/src/experimental/types/unifiedcard.nim @@ -17,6 +17,7 @@ type twitterListDetails communityDetails mediaWithDetailsHorizontal + unknown Component* = object kind*: ComponentType @@ -77,3 +78,28 @@ converter fromText*(text: Text): string = text.content proc renameHook*(v: var HasTypeField; fieldName: var string) = if fieldName == "type": fieldName = "kind" + +proc enumHook*(s: string; v: var ComponentType) = + v = case s + of "details": details + of "media": media + of "swipeable_media": swipeableMedia + of "button_group": buttonGroup + of "app_store_details": appStoreDetails + of "twitter_list_details": twitterListDetails + of "community_details": communityDetails + of "media_with_details_horizontal": mediaWithDetailsHorizontal + else: echo "ERROR: Unknown enum value (ComponentType): ", s; unknown + +proc enumHook*(s: string; v: var AppType) = + v = case s + of "android_app": androidApp + of "iphone_app": iPhoneApp + of "ipad_app": iPadApp + else: echo "ERROR: Unknown enum value (AppType): ", s; androidApp + +proc enumHook*(s: string; v: var MediaType) = + v = case s + of "video": video + of "photo": photo + else: echo "ERROR: Unknown enum value (MediaType): ", s; photo From 9bae009bbc2276f4930aa5927ee6608cfe5b3bd1 Mon Sep 17 00:00:00 2001 From: Zed Date: Sun, 27 Nov 2022 17:27:07 +0100 Subject: [PATCH 065/409] Fix 'unknown' compilation error --- src/experimental/parser/unifiedcard.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/experimental/parser/unifiedcard.nim b/src/experimental/parser/unifiedcard.nim index 12689e1..a3e8111 100644 --- a/src/experimental/parser/unifiedcard.nim +++ b/src/experimental/parser/unifiedcard.nim @@ -82,7 +82,7 @@ proc parseUnifiedCard*(json: string): Card = component.parseMedia(card, result) of buttonGroup: discard - of unknown: + of ComponentType.unknown: echo "ERROR: Unknown component type: ", json case component.kind From d923c20aa6a87224343562f15b00e72e7780aa3d Mon Sep 17 00:00:00 2001 From: Zed Date: Sun, 27 Nov 2022 18:18:51 +0100 Subject: [PATCH 066/409] Explicitly don't support 'model3d' cards Fixes #597 --- src/experimental/parser/unifiedcard.nim | 2 ++ src/experimental/types/unifiedcard.nim | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/experimental/parser/unifiedcard.nim b/src/experimental/parser/unifiedcard.nim index a3e8111..3c5158a 100644 --- a/src/experimental/parser/unifiedcard.nim +++ b/src/experimental/parser/unifiedcard.nim @@ -66,6 +66,8 @@ proc parseMedia(component: Component; card: UnifiedCard; result: var Card) = durationMs: videoInfo.durationMillis, variants: videoInfo.variants ) + of model3d: + result.title = "Unsupported 3D model ad" proc parseUnifiedCard*(json: string): Card = let card = json.fromJson(UnifiedCard) diff --git a/src/experimental/types/unifiedcard.nim b/src/experimental/types/unifiedcard.nim index 737fdd6..4ec587c 100644 --- a/src/experimental/types/unifiedcard.nim +++ b/src/experimental/types/unifiedcard.nim @@ -48,7 +48,7 @@ type vanity*: string MediaType* = enum - photo, video + photo, video, model3d MediaEntity* = object kind*: MediaType @@ -102,4 +102,5 @@ proc enumHook*(s: string; v: var MediaType) = v = case s of "video": video of "photo": photo + of "model3d": model3d else: echo "ERROR: Unknown enum value (MediaType): ", s; photo From 687447cea43089e0550ed47656535e11ba8f69f8 Mon Sep 17 00:00:00 2001 From: Zed Date: Mon, 19 Dec 2022 10:07:24 +0100 Subject: [PATCH 067/409] Only retry on 401 once --- src/apiutils.nim | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/apiutils.nim b/src/apiutils.nim index 77ff368..917932a 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -23,7 +23,7 @@ proc genParams*(pars: openArray[(string, string)] = @[]; cursor=""; result &= ("count", count) if cursor.len > 0: # The raw cursor often has plus signs, which sometimes get turned into spaces, - # so we need to them back into a plus + # so we need to turn them back into a plus if " " in cursor: result &= ("cursor", cursor.replace(" ", "+")) else: @@ -69,9 +69,7 @@ template fetchImpl(result, fetchBody) {.dirty.} = # Twitter randomly returns 401 errors with an empty body quite often. # Retrying the request usually works. - var attempt = 0 - while resp.status == "401 Unauthorized" and result.len == 0 and attempt < 3: - inc attempt + if resp.status == "401 Unauthorized" and result.len == 0: getContent() if resp.status == $Http503: From d549a77ebeea7fbdd1825e38d973291f013bb40f Mon Sep 17 00:00:00 2001 From: Andrew Sun <14020276+apsun@users.noreply.github.com> Date: Sat, 31 Dec 2022 11:46:02 -0800 Subject: [PATCH 068/409] Fix arm64 build (#756) --- nitter.nimble | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nitter.nimble b/nitter.nimble index a4d2e87..6facdbd 100644 --- a/nitter.nimble +++ b/nitter.nimble @@ -20,7 +20,7 @@ requires "packedjson#9e6fbb6" requires "supersnappy#6c94198" requires "redpool#8b7c1db" requires "https://github.com/zedeus/redis#d0a0e6f" -requires "zippy#61922b9" +requires "zippy#88a5a5a" requires "flatty#9f885d7" requires "jsony#d0e69bd" From b5dee25b379c561eeb2e71a8bd08882b6b9699ff Mon Sep 17 00:00:00 2001 From: Zed Date: Mon, 9 Jan 2023 00:11:33 +0100 Subject: [PATCH 069/409] Enable SO_REUSEPORT for simplier load balancing --- src/nitter.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/src/nitter.nim b/src/nitter.nim index d743599..2e868a4 100644 --- a/src/nitter.nim +++ b/src/nitter.nim @@ -56,6 +56,7 @@ settings: port = Port(cfg.port) staticDir = cfg.staticDir bindAddr = cfg.address + reusePort = true routes: get "/": From 6c4c691fe781454d1e7f271f78bc5fa7225539a8 Mon Sep 17 00:00:00 2001 From: Zed Date: Mon, 9 Jan 2023 00:12:07 +0100 Subject: [PATCH 070/409] Add Crawl-delay to robots.txt --- public/robots.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/public/robots.txt b/public/robots.txt index 7a74568..812656a 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -1,4 +1,5 @@ User-agent: * Disallow: / +Crawl-delay: 1 User-agent: Twitterbot Disallow: From 88c6135030a3f5bcd45a6b71fe2bcebc4129dc39 Mon Sep 17 00:00:00 2001 From: Zed Date: Mon, 9 Jan 2023 00:13:59 +0100 Subject: [PATCH 071/409] Bump dependencies --- nitter.nimble | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nitter.nimble b/nitter.nimble index 6facdbd..fdc1f9f 100644 --- a/nitter.nimble +++ b/nitter.nimble @@ -12,15 +12,15 @@ bin = @["nitter"] requires "nim >= 1.4.8" requires "jester#baca3f" -requires "karax#6abcb77" -requires "sass#e683aa1" +requires "karax#0af2c85" +requires "sass#7dfdd03" requires "nimcrypto#b41129f" -requires "markdown#a661c26" +requires "markdown#158efe3" requires "packedjson#9e6fbb6" requires "supersnappy#6c94198" requires "redpool#8b7c1db" requires "https://github.com/zedeus/redis#d0a0e6f" -requires "zippy#88a5a5a" +requires "zippy#123cd59" requires "flatty#9f885d7" requires "jsony#d0e69bd" From cd163b26a3f6c2751276475072406b397d8355c5 Mon Sep 17 00:00:00 2001 From: Zed Date: Mon, 9 Jan 2023 00:29:59 +0100 Subject: [PATCH 072/409] Use webp for thumbnails when available --- src/views/general.nim | 5 ++--- src/views/renderutils.nim | 8 ++++++++ src/views/tweet.nim | 14 +++----------- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/views/general.nim b/src/views/general.nim index b18dae5..5e96d02 100644 --- a/src/views/general.nim +++ b/src/views/general.nim @@ -98,9 +98,8 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc=""; link(rel="preload", type="image/png", href=bannerUrl, `as`="image") for url in images: - let suffix = if "400x400" in url or url.endsWith("placeholder.png"): "" - else: "?name=small" - let preloadUrl = getPicUrl(url & suffix) + let preloadUrl = if "400x400" in url: getPicUrl(url) + else: getSmallPic(url) link(rel="preload", type="image/png", href=preloadUrl, `as`="image") let image = getUrlPrefix(cfg) & getPicUrl(url) diff --git a/src/views/renderutils.nim b/src/views/renderutils.nim index 8e8d530..cdfeb28 100644 --- a/src/views/renderutils.nim +++ b/src/views/renderutils.nim @@ -3,6 +3,14 @@ import strutils, strformat import karax/[karaxdsl, vdom, vstyles] import ".."/[types, utils] +const smallWebp* = "?name=small&format=webp" + +proc getSmallPic*(url: string): string = + result = url + if "?" notin url and not url.endsWith("placeholder.png"): + result &= smallWebp + result = getPicUrl(result) + proc icon*(icon: string; text=""; title=""; class=""; href=""): VNode = var c = "icon-" & icon if class.len > 0: c = &"{c} {class}" diff --git a/src/views/tweet.nim b/src/views/tweet.nim index daad61a..69335fa 100644 --- a/src/views/tweet.nim +++ b/src/views/tweet.nim @@ -7,14 +7,7 @@ import renderutils import ".."/[types, utils, formatters] import general -const - doctype = "\n" - -proc getSmallPic(url: string): string = - result = url - if "?" notin url and not url.endsWith("placeholder.png"): - result &= "?name=small" - result = getPicUrl(result) +const doctype = "\n" proc renderMiniAvatar(user: User; prefs: Prefs): VNode = let url = getPicUrl(user.getUserPic("_mini")) @@ -60,9 +53,8 @@ proc renderAlbum(tweet: Tweet): VNode = tdiv(class="attachment image"): let named = "name=" in photo - orig = photo - small = if named: photo else: photo & "?name=small" - a(href=getOrigPicUrl(orig), class="still-image", target="_blank"): + small = if named: photo else: photo & smallWebp + a(href=getOrigPicUrl(photo), class="still-image", target="_blank"): genImg(small) proc isPlaybackEnabled(prefs: Prefs; playbackType: VideoType): bool = From d38b63f5a9ec9206305f1bef8d15ac4e8446d489 Mon Sep 17 00:00:00 2001 From: Zed Date: Mon, 9 Jan 2023 00:37:32 +0100 Subject: [PATCH 073/409] Bump hls.js --- public/js/hls.light.min.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/public/js/hls.light.min.js b/public/js/hls.light.min.js index 2dcaa72..5a1fd1d 100644 --- a/public/js/hls.light.min.js +++ b/public/js/hls.light.min.js @@ -1,5 +1,5 @@ // @license http://www.apache.org/licenses/LICENSE-2.0 Apache-2.0 // @source https://github.com/video-dev/hls.js -// @version v1.1.5 -"undefined"!=typeof window&&function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Hls=e():t.Hls=e()}(this,(function(){return function(t){var e={};function r(i){if(e[i])return e[i].exports;var a=e[i]={i:i,l:!1,exports:{}};return t[i].call(a.exports,a,a.exports,r),a.l=!0,a.exports}return r.m=t,r.c=e,r.d=function(t,e,i){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:i})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(r.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var a in t)r.d(i,a,function(e){return t[e]}.bind(null,a));return i},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="/dist/",r(r.s=19)}([function(t,e,r){"use strict";var i;r.d(e,"a",(function(){return i})),function(t){t.MEDIA_ATTACHING="hlsMediaAttaching",t.MEDIA_ATTACHED="hlsMediaAttached",t.MEDIA_DETACHING="hlsMediaDetaching",t.MEDIA_DETACHED="hlsMediaDetached",t.BUFFER_RESET="hlsBufferReset",t.BUFFER_CODECS="hlsBufferCodecs",t.BUFFER_CREATED="hlsBufferCreated",t.BUFFER_APPENDING="hlsBufferAppending",t.BUFFER_APPENDED="hlsBufferAppended",t.BUFFER_EOS="hlsBufferEos",t.BUFFER_FLUSHING="hlsBufferFlushing",t.BUFFER_FLUSHED="hlsBufferFlushed",t.MANIFEST_LOADING="hlsManifestLoading",t.MANIFEST_LOADED="hlsManifestLoaded",t.MANIFEST_PARSED="hlsManifestParsed",t.LEVEL_SWITCHING="hlsLevelSwitching",t.LEVEL_SWITCHED="hlsLevelSwitched",t.LEVEL_LOADING="hlsLevelLoading",t.LEVEL_LOADED="hlsLevelLoaded",t.LEVEL_UPDATED="hlsLevelUpdated",t.LEVEL_PTS_UPDATED="hlsLevelPtsUpdated",t.LEVELS_UPDATED="hlsLevelsUpdated",t.AUDIO_TRACKS_UPDATED="hlsAudioTracksUpdated",t.AUDIO_TRACK_SWITCHING="hlsAudioTrackSwitching",t.AUDIO_TRACK_SWITCHED="hlsAudioTrackSwitched",t.AUDIO_TRACK_LOADING="hlsAudioTrackLoading",t.AUDIO_TRACK_LOADED="hlsAudioTrackLoaded",t.SUBTITLE_TRACKS_UPDATED="hlsSubtitleTracksUpdated",t.SUBTITLE_TRACKS_CLEARED="hlsSubtitleTracksCleared",t.SUBTITLE_TRACK_SWITCH="hlsSubtitleTrackSwitch",t.SUBTITLE_TRACK_LOADING="hlsSubtitleTrackLoading",t.SUBTITLE_TRACK_LOADED="hlsSubtitleTrackLoaded",t.SUBTITLE_FRAG_PROCESSED="hlsSubtitleFragProcessed",t.CUES_PARSED="hlsCuesParsed",t.NON_NATIVE_TEXT_TRACKS_FOUND="hlsNonNativeTextTracksFound",t.INIT_PTS_FOUND="hlsInitPtsFound",t.FRAG_LOADING="hlsFragLoading",t.FRAG_LOAD_EMERGENCY_ABORTED="hlsFragLoadEmergencyAborted",t.FRAG_LOADED="hlsFragLoaded",t.FRAG_DECRYPTED="hlsFragDecrypted",t.FRAG_PARSING_INIT_SEGMENT="hlsFragParsingInitSegment",t.FRAG_PARSING_USERDATA="hlsFragParsingUserdata",t.FRAG_PARSING_METADATA="hlsFragParsingMetadata",t.FRAG_PARSED="hlsFragParsed",t.FRAG_BUFFERED="hlsFragBuffered",t.FRAG_CHANGED="hlsFragChanged",t.FPS_DROP="hlsFpsDrop",t.FPS_DROP_LEVEL_CAPPING="hlsFpsDropLevelCapping",t.ERROR="hlsError",t.DESTROYING="hlsDestroying",t.KEY_LOADING="hlsKeyLoading",t.KEY_LOADED="hlsKeyLoaded",t.LIVE_BACK_BUFFER_REACHED="hlsLiveBackBufferReached",t.BACK_BUFFER_REACHED="hlsBackBufferReached"}(i||(i={}))},function(t,e,r){"use strict";r.d(e,"a",(function(){return o})),r.d(e,"b",(function(){return l}));var i=function(){},a={trace:i,debug:i,log:i,warn:i,info:i,error:i},n=a;function s(t){var e=self.console[t];return e?e.bind(self.console,"["+t+"] >"):i}function o(t){if(self.console&&!0===t||"object"==typeof t){!function(t){for(var e=arguments.length,r=new Array(e>1?e-1:0),i=1;i>8*(15-r)&255;return e},r.setDecryptDataFromLevelKey=function(t,e){var r=t;return"AES-128"===(null==t?void 0:t.method)&&t.uri&&!t.iv&&((r=o.a.fromURI(t.uri)).method=t.method,r.iv=this.createInitializationVector(e),r.keyFormat="identity"),r},r.setElementaryStreamInfo=function(t,e,r,i,a,n){void 0===n&&(n=!1);var s=this.elementaryStreams,o=s[t];o?(o.startPTS=Math.min(o.startPTS,e),o.endPTS=Math.max(o.endPTS,r),o.startDTS=Math.min(o.startDTS,i),o.endDTS=Math.max(o.endDTS,a)):s[t]={startPTS:e,endPTS:r,startDTS:i,endDTS:a,partial:n}},r.clearElementaryStreamInfo=function(){var t=this.elementaryStreams;t[i.AUDIO]=null,t[i.VIDEO]=null,t[i.AUDIOVIDEO]=null},f(e,[{key:"decryptdata",get:function(){if(!this.levelkey&&!this._decryptdata)return null;if(!this._decryptdata&&this.levelkey){var t=this.sn;"number"!=typeof t&&(this.levelkey&&"AES-128"===this.levelkey.method&&!this.levelkey.iv&&s.b.warn('missing IV for initialization segment with method="'+this.levelkey.method+'" - compliance issue'),t=0),this._decryptdata=this.setDecryptDataFromLevelKey(this.levelkey,t)}return this._decryptdata}},{key:"end",get:function(){return this.start+this.duration}},{key:"endProgramDateTime",get:function(){if(null===this.programDateTime)return null;if(!Object(a.a)(this.programDateTime))return null;var t=Object(a.a)(this.duration)?this.duration:0;return this.programDateTime+1e3*t}},{key:"encrypted",get:function(){var t;return!(null===(t=this.decryptdata)||void 0===t||!t.keyFormat||!this.decryptdata.uri)}}]),e}(c),g=function(t){function e(e,r,i,a,n){var s;(s=t.call(this,i)||this).fragOffset=0,s.duration=0,s.gap=!1,s.independent=!1,s.relurl=void 0,s.fragment=void 0,s.index=void 0,s.stats=new l.a,s.duration=e.decimalFloatingPoint("DURATION"),s.gap=e.bool("GAP"),s.independent=e.bool("INDEPENDENT"),s.relurl=e.enumeratedString("URI"),s.fragment=r,s.index=a;var o=e.enumeratedString("BYTERANGE");return o&&s.setByteRange(o,n),n&&(s.fragOffset=n.fragOffset+n.duration),s}return u(e,t),f(e,[{key:"start",get:function(){return this.fragment.start+this.fragOffset}},{key:"end",get:function(){return this.start+this.duration}},{key:"loaded",get:function(){var t=this.elementaryStreams;return!!(t.audio||t.video||t.audiovideo)}}]),e}(c)},function(t,e,r){"use strict";r.d(e,"b",(function(){return d})),r.d(e,"g",(function(){return h})),r.d(e,"f",(function(){return f})),r.d(e,"d",(function(){return c})),r.d(e,"c",(function(){return v})),r.d(e,"e",(function(){return p})),r.d(e,"h",(function(){return m})),r.d(e,"a",(function(){return y}));var i=r(8),a=r(5),n=Math.pow(2,32)-1,s=[].push;function o(t){return String.fromCharCode.apply(null,t)}function l(t,e){"data"in t&&(e+=t.start,t=t.data);var r=t[e]<<24|t[e+1]<<16|t[e+2]<<8|t[e+3];return r<0?4294967296+r:r}function u(t,e,r){"data"in t&&(e+=t.start,t=t.data),t[e]=r>>24,t[e+1]=r>>16&255,t[e+2]=r>>8&255,t[e+3]=255&r}function d(t,e){var r,i,a,n=[];if(!e.length)return n;"data"in t?(r=t.data,i=t.start,a=t.end):(i=0,a=(r=t).byteLength);for(var u=i;u1?u+h:a;if(o(r.subarray(u+4,u+8))===e[0])if(1===e.length)n.push({data:r,start:u+8,end:f});else{var c=d({data:r,start:u+8,end:f},e.slice(1));c.length&&s.apply(n,c)}u=f}return n}function h(t){var e=d(t,["moov"])[0],r=e?e.end:null,i=d(t,["sidx"]);if(!i||!i[0])return null;var a=[],n=i[0],s=n.data[0],o=0===s?8:16,u=l(n,o);o+=4;o+=0===s?8:16,o+=2;var h=n.end+0,f=function(t,e){"data"in t&&(e+=t.start,t=t.data);var r=t[e]<<8|t[e+1];return r<0?65536+r:r}(n,o);o+=2;for(var c=0;c>>31)return console.warn("SIDX has hierarchical references (not supported)"),null;var m=l(n,v);v+=4,a.push({referenceSize:p,subsegmentDuration:m,info:{duration:m/u,start:h,end:h+p-1}}),h+=p,o=v+=4}return{earliestPresentationTime:0,timescale:u,version:s,referencesCount:f,references:a,moovEndOffset:r}}function f(t){for(var e=[],r=d(t,["moov","trak"]),i=0;i0)return t.subarray(r,r+i)},o=function(t,e){var r=0;return r=(127&t[e])<<21,r|=(127&t[e+1])<<14,r|=(127&t[e+2])<<7,r|=127&t[e+3]},l=function(t,e){return a(t,e)&&o(t,e+6)+10<=t.length-e},u=function(t){for(var e=f(t),r=0;r>4){case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:u+=String.fromCharCode(n);break;case 12:case 13:s=t[d++],u+=String.fromCharCode((31&n)<<6|63&s);break;case 14:s=t[d++],o=t[d++],u+=String.fromCharCode((15&n)<<12|(63&s)<<6|(63&o)<<0)}}return u};function b(){return i||void 0===self.TextDecoder||(i=new self.TextDecoder("utf-8")),i}},function(t,e,r){"use strict";function i(t,e,r){return Uint8Array.prototype.slice?t.slice(e,r):new Uint8Array(Array.prototype.slice.call(t,e,r))}r.d(e,"a",(function(){return i}))},function(t,e,r){"use strict";r.d(e,"c",(function(){return St})),r.d(e,"d",(function(){return At})),r.d(e,"a",(function(){return Rt})),r.d(e,"b",(function(){return Dt}));var i=r(0),a=r(2),n=r(14),s=r(3),o=r(7);var l=r(6),u=r(8),d=function(){function t(){this._audioTrack=void 0,this._id3Track=void 0,this.frameIndex=0,this.cachedData=null,this.initPTS=null}var e=t.prototype;return e.resetInitSegment=function(t,e,r){this._id3Track={type:"id3",id:3,pid:-1,inputTimeScale:9e4,sequenceNumber:0,samples:[],dropped:0}},e.resetTimeStamp=function(){},e.resetContiguity=function(){},e.canParse=function(t,e){return!1},e.appendFrame=function(t,e,r){},e.demux=function(t,e){this.cachedData&&(t=Object(l.a)(this.cachedData,t),this.cachedData=null);var r,i,a=o.b(t,0),n=a?a.length:0,s=this._audioTrack,d=this._id3Track,f=a?o.d(a):void 0,c=t.length;for(0!==this.frameIndex&&null!==this.initPTS||(this.initPTS=h(f,e)),a&&a.length>0&&d.samples.push({pts:this.initPTS,dts:this.initPTS,data:a}),i=this.initPTS;n>>5}function m(t,e){return e+1=t.length)return!1;var i=p(t,e);if(i<=r)return!1;var a=e+i;return a===t.length||m(t,a)}return!1}function b(t,e,r,n,s){if(!t.samplerate){var o=function(t,e,r,n){var s,o,l,u,d=navigator.userAgent.toLowerCase(),h=n,f=[96e3,88200,64e3,48e3,44100,32e3,24e3,22050,16e3,12e3,11025,8e3,7350];s=1+((192&e[r+2])>>>6);var v=(60&e[r+2])>>>2;if(!(v>f.length-1))return l=(1&e[r+2])<<2,l|=(192&e[r+3])>>>6,c.b.log("manifest codec:"+n+", ADTS type:"+s+", samplingIndex:"+v),/firefox/i.test(d)?v>=6?(s=5,u=new Array(4),o=v-3):(s=2,u=new Array(2),o=v):-1!==d.indexOf("android")?(s=2,u=new Array(2),o=v):(s=5,u=new Array(4),n&&(-1!==n.indexOf("mp4a.40.29")||-1!==n.indexOf("mp4a.40.5"))||!n&&v>=6?o=v-3:((n&&-1!==n.indexOf("mp4a.40.2")&&(v>=6&&1===l||/vivaldi/i.test(d))||!n&&1===l)&&(s=2,u=new Array(2)),o=v)),u[0]=s<<3,u[0]|=(14&v)>>1,u[1]|=(1&v)<<7,u[1]|=l<<3,5===s&&(u[1]|=(14&o)>>1,u[2]=(1&o)<<7,u[2]|=8,u[3]=0),{config:u,samplerate:f[v],channelCount:l,codec:"mp4a.40."+s,manifestCodec:h};t.trigger(i.a.ERROR,{type:a.b.MEDIA_ERROR,details:a.a.FRAG_PARSING_ERROR,fatal:!0,reason:"invalid ADTS sampling index:"+v})}(e,r,n,s);if(!o)return;t.config=o.config,t.samplerate=o.samplerate,t.channelCount=o.channelCount,t.codec=o.codec,t.manifestCodec=o.manifestCodec,c.b.log("parsed codec:"+t.codec+", rate:"+o.samplerate+", channels:"+o.channelCount)}}function T(t){return 9216e4/t}function E(t,e,r,i,a){var n=function(t,e,r,i,a){var n=g(t,e),s=p(t,e);if((s-=n)>0)return{headerLength:n,frameLength:s,stamp:r+i*a}}(e,r,i,a,T(t.samplerate));if(n){var s,o=n.frameLength,l=n.headerLength,u=n.stamp,d=l+o,h=Math.max(0,r+d-e.length);h?(s=new Uint8Array(d-l)).set(e.subarray(r+l,e.length),0):s=e.subarray(r+l,r+d);var f={unit:s,pts:u};return h||t.samples.push(f),{sample:f,length:d,missing:h}}}function S(t,e){return(S=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}var L=function(t){var e,r;function i(e,r){var i;return(i=t.call(this)||this).observer=void 0,i.config=void 0,i.observer=e,i.config=r,i}r=t,(e=i).prototype=Object.create(r.prototype),e.prototype.constructor=e,S(e,r);var a=i.prototype;return a.resetInitSegment=function(e,r,i){t.prototype.resetInitSegment.call(this,e,r,i),this._audioTrack={container:"audio/adts",type:"audio",id:2,pid:-1,sequenceNumber:0,isAAC:!0,samples:[],manifestCodec:e,duration:i,inputTimeScale:9e4,dropped:0}},i.probe=function(t){if(!t)return!1;for(var e=(o.b(t,0)||[]).length,r=t.length;e0},e.demux=function(t){var e=t,r={type:"",id:-1,pid:-1,inputTimeScale:9e4,sequenceNumber:-1,samples:[],dropped:0};if(this.config.progressive){this.remainderData&&(e=Object(l.a)(this.remainderData,t));var i=Object(l.h)(e);this.remainderData=i.remainder,r.samples=i.valid||new Uint8Array}else r.samples=e;return{audioTrack:{type:"",id:-1,pid:-1,inputTimeScale:9e4,sequenceNumber:-1,samples:[],dropped:0},avcTrack:r,id3Track:{type:"",id:-1,pid:-1,inputTimeScale:9e4,sequenceNumber:-1,samples:[],dropped:0},textTrack:{type:"",id:-1,pid:-1,inputTimeScale:9e4,sequenceNumber:-1,samples:[],dropped:0}}},e.flush=function(){var t={type:"",id:-1,pid:-1,inputTimeScale:9e4,sequenceNumber:-1,samples:[],dropped:0};return t.samples=this.remainderData||new Uint8Array,this.remainderData=null,{audioTrack:{type:"",id:-1,pid:-1,inputTimeScale:9e4,sequenceNumber:-1,samples:[],dropped:0},avcTrack:t,id3Track:{type:"",id:-1,pid:-1,inputTimeScale:9e4,sequenceNumber:-1,samples:[],dropped:0},textTrack:{type:"",id:-1,pid:-1,inputTimeScale:9e4,sequenceNumber:-1,samples:[],dropped:0}}},e.demuxSampleAes=function(t,e,r){return Promise.reject(new Error("The MP4 demuxer does not support SAMPLE-AES decryption"))},e.destroy=function(){},t}();R.minProbeByteLength=1024;var D=R,_=null,k=[32,64,96,128,160,192,224,256,288,320,352,384,416,448,32,48,56,64,80,96,112,128,160,192,224,256,320,384,32,40,48,56,64,80,96,112,128,160,192,224,256,320,32,48,56,64,80,96,112,128,144,160,176,192,224,256,8,16,24,32,40,48,56,64,80,96,112,128,144,160],x=[44100,48e3,32e3,22050,24e3,16e3,11025,12e3,8e3],C=[[0,72,144,12],[0,0,0,0],[0,72,144,12],[0,144,144,12]],w=[0,1,1,4];function O(t,e,r,i,a){if(!(r+24>e.length)){var n=I(e,r);if(n&&r+n.frameLength<=e.length){var s=i+a*(9e4*n.samplesPerFrame/n.sampleRate),o={unit:e.subarray(r,r+n.frameLength),pts:s,dts:s};return t.config=[],t.channelCount=n.channelCount,t.samplerate=n.sampleRate,t.samples.push(o),{sample:o,length:n.frameLength,missing:0}}}}function I(t,e){var r=t[e+1]>>3&3,i=t[e+1]>>1&3,a=t[e+2]>>4&15,n=t[e+2]>>2&3;if(1!==r&&0!==a&&15!==a&&3!==n){var s=t[e+2]>>1&1,o=t[e+3]>>6,l=1e3*k[14*(3===r?3-i:3===i?3:4)+a-1],u=x[3*(3===r?0:2===r?1:2)+n],d=3===o?1:2,h=C[r][i],f=w[i],c=8*h*f,v=Math.floor(h*l/u+s)*f;if(null===_){var g=(navigator.userAgent||"").match(/Chrome\/(\d+)/i);_=g?parseInt(g[1]):0}return!!_&&_<=87&&2===i&&l>=224e3&&0===o&&(t[e+3]=128|t[e+3]),{sampleRate:u,channelCount:d,frameLength:v,samplesPerFrame:c}}}function P(t,e){return 255===t[e]&&224==(224&t[e+1])&&0!=(6&t[e+1])}function F(t,e){return e+1t?(this.word<<=t,this.bitsAvailable-=t):(t-=this.bitsAvailable,t-=(e=t>>3)>>3,this.bytesAvailable-=e,this.loadWord(),this.word<<=t,this.bitsAvailable-=t)},e.readBits=function(t){var e=Math.min(this.bitsAvailable,t),r=this.word>>>32-e;return t>32&&c.b.error("Cannot read more than 32 bits at a time"),this.bitsAvailable-=e,this.bitsAvailable>0?this.word<<=e:this.bytesAvailable>0&&this.loadWord(),(e=t-e)>0&&this.bitsAvailable?r<>>t))return this.word<<=t,this.bitsAvailable-=t,t;return this.loadWord(),t+this.skipLZ()},e.skipUEG=function(){this.skipBits(1+this.skipLZ())},e.skipEG=function(){this.skipBits(1+this.skipLZ())},e.readUEG=function(){var t=this.skipLZ();return this.readBits(t+1)-1},e.readEG=function(){var t=this.readUEG();return 1&t?1+t>>>1:-1*(t>>>1)},e.readBoolean=function(){return 1===this.readBits(1)},e.readUByte=function(){return this.readBits(8)},e.readUShort=function(){return this.readBits(16)},e.readUInt=function(){return this.readBits(32)},e.skipScalingList=function(t){for(var e=8,r=8,i=0;i=t.length)return void r();if(!(t[e].unit.length<32)){var i=this.decrypter.isSync();if(this.decryptAacSample(t,e,r,i),!i)return}}},e.getAvcEncryptedData=function(t){for(var e=16*Math.floor((t.length-48)/160)+16,r=new Int8Array(e),i=0,a=32;a=t.length)return void i();for(var a=t[e].units;!(r>=a.length);r++){var n=a[r];if(!(n.data.length<=48||1!==n.type&&5!==n.type)){var s=this.decrypter.isSync();if(this.decryptAvcSample(t,e,r,i,n,s),!s)return}}}},t}(),U={video:1,audio:2,id3:3,text:4},G=function(){function t(t,e,r){this.observer=void 0,this.config=void 0,this.typeSupported=void 0,this.sampleAes=null,this.pmtParsed=!1,this.audioCodec=void 0,this.videoCodec=void 0,this._duration=0,this.aacLastPTS=null,this._initPTS=null,this._initDTS=null,this._pmtId=-1,this._avcTrack=void 0,this._audioTrack=void 0,this._id3Track=void 0,this._txtTrack=void 0,this.aacOverFlow=null,this.avcSample=null,this.remainderData=null,this.observer=t,this.config=e,this.typeSupported=r}t.probe=function(e){var r=t.syncOffset(e);return!(r<0)&&(r&&c.b.warn("MPEG2-TS detected but first sync word found @ offset "+r+", junk ahead ?"),!0)},t.syncOffset=function(t){for(var e=Math.min(1e3,t.length-564),r=0;r>4>1){if((k=R+5+e[R+4])===R+188)continue}else k=R+4;switch(_){case f:D&&(v&&(o=V(v))&&this.parseAVCPES(o,!1),v={data:[],size:0}),v&&(v.data.push(e.subarray(k,R+188)),v.size+=R+188-k);break;case g:D&&(m&&(o=V(m))&&(d.isAAC?this.parseAACPES(o):this.parseMPEGPES(o)),m={data:[],size:0}),m&&(m.data.push(e.subarray(k,R+188)),m.size+=R+188-k);break;case p:D&&(y&&(o=V(y))&&this.parseID3PES(o),y={data:[],size:0}),y&&(y.data.push(e.subarray(k,R+188)),y.size+=R+188-k);break;case 0:D&&(k+=e[k]+1),E=this._pmtId=H(e,k);break;case E:D&&(k+=e[k]+1);var x=K(e,k,!0===this.typeSupported.mpeg||!0===this.typeSupported.mp3,n);(f=x.avc)>0&&(u.pid=f),(g=x.audio)>0&&(d.pid=g,d.isAAC=x.isAAC),(p=x.id3)>0&&(h.pid=p),b&&!T&&(c.b.log("reparse from beginning"),b=!1,R=L-188),T=this.pmtParsed=!0;break;case 17:case 8191:break;default:b=!0}}else A++;A>0&&this.observer.emit(i.a.ERROR,i.a.ERROR,{type:a.b.MEDIA_ERROR,details:a.a.FRAG_PARSING_ERROR,fatal:!1,reason:"Found "+A+" TS packet/s that do not start with 0x47"}),u.pesData=v,d.pesData=m,h.pesData=y;var C={audioTrack:d,avcTrack:u,id3Track:h,textTrack:this._txtTrack};return s&&this.extractRemainingSamples(C),C},e.flush=function(){var t,e=this.remainderData;return this.remainderData=null,t=e?this.demux(e,-1,!1,!0):{audioTrack:this._audioTrack,avcTrack:this._avcTrack,textTrack:this._txtTrack,id3Track:this._id3Track},this.extractRemainingSamples(t),this.sampleAes?this.decrypt(t,this.sampleAes):t},e.extractRemainingSamples=function(t){var e,r=t.audioTrack,i=t.avcTrack,a=t.id3Track,n=i.pesData,s=r.pesData,o=a.pesData;n&&(e=V(n))?(this.parseAVCPES(e,!0),i.pesData=null):i.pesData=n,s&&(e=V(s))?(r.isAAC?this.parseAACPES(e):this.parseMPEGPES(e),r.pesData=null):(null!=s&&s.size&&c.b.log("last AAC PES packet truncated,might overlap between fragments"),r.pesData=s),o&&(e=V(o))?(this.parseID3PES(e),a.pesData=null):a.pesData=o},e.demuxSampleAes=function(t,e,r){var i=this.demux(t,r,!0,!this.config.progressive),a=this.sampleAes=new B(this.observer,this.config,e);return this.decrypt(i,a)},e.decrypt=function(t,e){return new Promise((function(r){var i=t.audioTrack,a=t.avcTrack;i.samples&&i.isAAC?e.decryptAacSamples(i.samples,0,(function(){a.samples?e.decryptAvcSamples(a.samples,0,0,(function(){r(t)})):r(t)})):a.samples&&e.decryptAvcSamples(a.samples,0,0,(function(){r(t)}))}))},e.destroy=function(){this._initPTS=this._initDTS=null,this._duration=0},e.parseAVCPES=function(t,e){var r,i=this,a=this._avcTrack,n=this.parseAVCNALu(t.data),s=this.avcSample,l=!1;t.data=null,s&&n.length&&!a.audFound&&(W(s,a),s=this.avcSample=j(!1,t.pts,t.dts,"")),n.forEach((function(e){switch(e.type){case 1:r=!0,s||(s=i.avcSample=j(!0,t.pts,t.dts,"")),s.frame=!0;var n=e.data;if(l&&n.length>4){var u=new N(n).readSliceType();2!==u&&4!==u&&7!==u&&9!==u||(s.key=!0)}break;case 5:r=!0,s||(s=i.avcSample=j(!0,t.pts,t.dts,"")),s.key=!0,s.frame=!0;break;case 6:r=!0;var d=new N(X(e.data));d.readUByte();for(var h=0,f=0,c=!1,v=0;!c&&d.bytesAvailable>1;){h=0;do{h+=v=d.readUByte()}while(255===v);f=0;do{f+=v=d.readUByte()}while(255===v);if(4===h&&0!==d.bytesAvailable){if(c=!0,181===d.readUByte())if(49===d.readUShort())if(1195456820===d.readUInt())if(3===d.readUByte()){for(var g=d.readUByte(),p=31&g,m=[g,d.readUByte()],y=0;y16){for(var b=[],T=0;T<16;T++)b.push(d.readUByte().toString(16)),3!==T&&5!==T&&7!==T&&9!==T||b.push("-");for(var E=f-16,S=new Uint8Array(E),L=0;L=0){var h={data:t.subarray(u,l-n-1),type:d};o.push(h)}else{var f=this.getLastNalUnit();if(f&&(s&&l<=4-s&&f.state&&(f.data=f.data.subarray(0,f.data.byteLength-s)),(r=l-n-1)>0)){var c=new Uint8Array(f.data.byteLength+r);c.set(f.data,0),c.set(t.subarray(0,r),f.data.byteLength),f.data=c,f.state=0}}l=0&&n>=0){var v={data:t.subarray(u,i),type:d,state:n};o.push(v)}if(0===o.length){var g=this.getLastNalUnit();if(g){var p=new Uint8Array(g.data.byteLength+t.byteLength);p.set(g.data,0),p.set(t,g.data.byteLength),g.data=p}}return a.naluState=n,o},e.parseAACPES=function(t){var e,r,n,s,o,l=0,u=this._audioTrack,d=this.aacOverFlow,h=t.data;if(d){this.aacOverFlow=null;var f=d.sample.unit.byteLength,v=Math.min(d.missing,f),g=f-v;d.sample.unit.set(h.subarray(0,v),g),u.samples.push(d.sample),l=d.missing}for(e=l,r=h.length;e1;){var l=new Uint8Array(o[0].length+o[1].length);l.set(o[0]),l.set(o[1],o[0].length),o[0]=l,o.splice(1,1)}if(1===((e=o[0])[0]<<16)+(e[1]<<8)+e[2]){if((r=(e[4]<<8)+e[5])&&r>t.size-6)return null;var u=e[7];192&u&&(a=536870912*(14&e[9])+4194304*(255&e[10])+16384*(254&e[11])+128*(255&e[12])+(254&e[13])/2,64&u?a-(n=536870912*(14&e[14])+4194304*(255&e[15])+16384*(254&e[16])+128*(255&e[17])+(254&e[18])/2)>54e5&&(c.b.warn(Math.round((a-n)/9e4)+"s delta between PTS and DTS, align them"),a=n):n=a);var d=(i=e[8])+9;if(t.size<=d)return null;t.size-=d;for(var h=new Uint8Array(t.size),f=0,v=o.length;fg){d-=g;continue}e=e.subarray(d),g-=d,d=0}h.set(e,s),s+=g}return r&&(r-=i+3),{data:h,pts:a,dts:n,len:r}}return null}function W(t,e){if(t.units.length&&t.frame){if(void 0===t.pts){var r=e.samples,i=r.length;if(!i)return void e.dropped++;var a=r[i-1];t.pts=a.pts,t.dts=a.dts}e.samples.push(t)}t.debug.length&&c.b.log(t.pts+"/"+t.dts+":"+t.debug)}function Y(t,e){var r=t.length;if(r>0){if(e.pts>=t[r-1].pts)t.push(e);else for(var i=r-1;i>=0;i--)if(e.pts1?r-1:0),a=1;a>24&255,o[1]=e>>16&255,o[2]=e>>8&255,o[3]=255&e,o.set(t,4),n=0,e=8;n>24&255,e>>16&255,e>>8&255,255&e,i>>24,i>>16&255,i>>8&255,255&i,a>>24,a>>16&255,a>>8&255,255&a,85,196,0,0]))},t.mdia=function(e){return t.box(t.types.mdia,t.mdhd(e.timescale,e.duration),t.hdlr(e.type),t.minf(e))},t.mfhd=function(e){return t.box(t.types.mfhd,new Uint8Array([0,0,0,0,e>>24,e>>16&255,e>>8&255,255&e]))},t.minf=function(e){return"audio"===e.type?t.box(t.types.minf,t.box(t.types.smhd,t.SMHD),t.DINF,t.stbl(e)):t.box(t.types.minf,t.box(t.types.vmhd,t.VMHD),t.DINF,t.stbl(e))},t.moof=function(e,r,i){return t.box(t.types.moof,t.mfhd(e),t.traf(i,r))},t.moov=function(e){for(var r=e.length,i=[];r--;)i[r]=t.trak(e[r]);return t.box.apply(null,[t.types.moov,t.mvhd(e[0].timescale,e[0].duration)].concat(i).concat(t.mvex(e)))},t.mvex=function(e){for(var r=e.length,i=[];r--;)i[r]=t.trex(e[r]);return t.box.apply(null,[t.types.mvex].concat(i))},t.mvhd=function(e,r){r*=e;var i=Math.floor(r/(J+1)),a=Math.floor(r%(J+1)),n=new Uint8Array([1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,3,e>>24&255,e>>16&255,e>>8&255,255&e,i>>24,i>>16&255,i>>8&255,255&i,a>>24,a>>16&255,a>>8&255,255&a,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255]);return t.box(t.types.mvhd,n)},t.sdtp=function(e){var r,i,a=e.samples||[],n=new Uint8Array(4+a.length);for(r=0;r>>8&255),n.push(255&a),n=n.concat(Array.prototype.slice.call(i));for(r=0;r>>8&255),s.push(255&a),s=s.concat(Array.prototype.slice.call(i));var o=t.box(t.types.avcC,new Uint8Array([1,n[3],n[4],n[5],255,224|e.sps.length].concat(n).concat([e.pps.length]).concat(s))),l=e.width,u=e.height,d=e.pixelRatio[0],h=e.pixelRatio[1];return t.box(t.types.avc1,new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,l>>8&255,255&l,u>>8&255,255&u,0,72,0,0,0,72,0,0,0,0,0,0,0,1,18,100,97,105,108,121,109,111,116,105,111,110,47,104,108,115,46,106,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,17,17]),o,t.box(t.types.btrt,new Uint8Array([0,28,156,128,0,45,198,192,0,45,198,192])),t.box(t.types.pasp,new Uint8Array([d>>24,d>>16&255,d>>8&255,255&d,h>>24,h>>16&255,h>>8&255,255&h])))},t.esds=function(t){var e=t.config.length;return new Uint8Array([0,0,0,0,3,23+e,0,1,0,4,15+e,64,21,0,0,0,0,0,0,0,0,0,0,0,5].concat([e]).concat(t.config).concat([6,1,2]))},t.mp4a=function(e){var r=e.samplerate;return t.box(t.types.mp4a,new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,e.channelCount,0,16,0,0,0,0,r>>8&255,255&r,0,0]),t.box(t.types.esds,t.esds(e)))},t.mp3=function(e){var r=e.samplerate;return t.box(t.types[".mp3"],new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,e.channelCount,0,16,0,0,0,0,r>>8&255,255&r,0,0]))},t.stsd=function(e){return"audio"===e.type?e.isAAC||"mp3"!==e.codec?t.box(t.types.stsd,t.STSD,t.mp4a(e)):t.box(t.types.stsd,t.STSD,t.mp3(e)):t.box(t.types.stsd,t.STSD,t.avc1(e))},t.tkhd=function(e){var r=e.id,i=e.duration*e.timescale,a=e.width,n=e.height,s=Math.floor(i/(J+1)),o=Math.floor(i%(J+1));return t.box(t.types.tkhd,new Uint8Array([1,0,0,7,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,3,r>>24&255,r>>16&255,r>>8&255,255&r,0,0,0,0,s>>24,s>>16&255,s>>8&255,255&s,o>>24,o>>16&255,o>>8&255,255&o,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,a>>8&255,255&a,0,0,n>>8&255,255&n,0,0]))},t.traf=function(e,r){var i=t.sdtp(e),a=e.id,n=Math.floor(r/(J+1)),s=Math.floor(r%(J+1));return t.box(t.types.traf,t.box(t.types.tfhd,new Uint8Array([0,0,0,0,a>>24,a>>16&255,a>>8&255,255&a])),t.box(t.types.tfdt,new Uint8Array([1,0,0,0,n>>24,n>>16&255,n>>8&255,255&n,s>>24,s>>16&255,s>>8&255,255&s])),t.trun(e,i.length+16+20+8+16+8+8),i)},t.trak=function(e){return e.duration=e.duration||4294967295,t.box(t.types.trak,t.tkhd(e),t.mdia(e))},t.trex=function(e){var r=e.id;return t.box(t.types.trex,new Uint8Array([0,0,0,0,r>>24,r>>16&255,r>>8&255,255&r,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1]))},t.trun=function(e,r){var i,a,n,s,o,l,u=e.samples||[],d=u.length,h=12+16*d,f=new Uint8Array(h);for(r+=8+h,f.set([0,0,15,1,d>>>24&255,d>>>16&255,d>>>8&255,255&d,r>>>24&255,r>>>16&255,r>>>8&255,255&r],0),i=0;i>>24&255,n>>>16&255,n>>>8&255,255&n,s>>>24&255,s>>>16&255,s>>>8&255,255&s,o.isLeading<<2|o.dependsOn,o.isDependedOn<<6|o.hasRedundancy<<4|o.paddingValue<<1|o.isNonSync,61440&o.degradPrio,15&o.degradPrio,l>>>24&255,l>>>16&255,l>>>8&255,255&l],12+16*i);return t.box(t.types.trun,f)},t.initSegment=function(e){t.types||t.init();var r=t.moov(e),i=new Uint8Array(t.FTYP.byteLength+r.byteLength);return i.set(t.FTYP),i.set(r,t.FTYP.byteLength),i},t}();tt.types=void 0,tt.HDLR_TYPES=void 0,tt.STTS=void 0,tt.STSC=void 0,tt.STCO=void 0,tt.STSZ=void 0,tt.VMHD=void 0,tt.SMHD=void 0,tt.STSD=void 0,tt.FTYP=void 0,tt.DINF=void 0;var et=tt,rt=r(4);function it(t,e,r,i){void 0===r&&(r=1),void 0===i&&(i=!1);var a=t*e*r;return i?Math.round(a):a}function at(t,e){return void 0===e&&(e=!1),it(t,1e3,1/9e4,e)}function nt(){return(nt=Object.assign||function(t){for(var e=1;e0?t:r.pts}),t[0].pts);return e&&c.b.debug("PTS rollover detected"),r},e.remux=function(t,e,r,i,a,n,s,o){var l,u,d,h,f,v,g=a,p=a,m=t.pid>-1,y=e.pid>-1,b=e.samples.length,T=t.samples.length>0,E=b>1;if((!m||T)&&(!y||E)||this.ISGenerated||s){this.ISGenerated||(d=this.generateIS(t,e,a));var S=this.isVideoContiguous,L=-1;if(E&&(L=function(t){for(var e=0;e0){c.b.warn("[mp4-remuxer]: Dropped "+L+" out of "+b+" video samples due to a missing keyframe");var A=this.getVideoStartPts(e.samples);e.samples=e.samples.slice(L),e.dropped+=L,p+=(e.samples[0].pts-A)/(e.timescale||9e4)}else-1===L&&(c.b.warn("[mp4-remuxer]: No keyframe found out of "+b+" video samples"),v=!1);if(this.ISGenerated){if(T&&E){var R=this.getVideoStartPts(e.samples),D=(dt(t.samples[0].pts,R)-R)/e.inputTimeScale;g+=Math.max(0,D),p+=Math.max(0,-D)}if(T){if(t.samplerate||(c.b.warn("[mp4-remuxer]: regenerate InitSegment as audio detected"),d=this.generateIS(t,e,a)),u=this.remuxAudio(t,g,this.isAudioContiguous,n,y||E||o===rt.b.AUDIO?p:void 0),E){var _=u?u.endPTS-u.startPTS:0;e.inputTimeScale||(c.b.warn("[mp4-remuxer]: regenerate InitSegment as video detected"),d=this.generateIS(t,e,a)),l=this.remuxVideo(e,p,S,_)}}else E&&(l=this.remuxVideo(e,p,S,0));l&&(l.firstKeyFrame=L,l.independent=-1!==L)}}return this.ISGenerated&&(r.samples.length&&(f=this.remuxID3(r,a)),i.samples.length&&(h=this.remuxText(i,a))),{audio:u,video:l,initSegment:d,independent:v,text:h,id3:f}},e.generateIS=function(t,e,r){var i,a,n,o=t.samples,l=e.samples,u=this.typeSupported,d={},h=!Object(s.a)(this._initPTS),f="audio/mp4";if(h&&(i=a=1/0),t.config&&o.length&&(t.timescale=t.samplerate,t.isAAC||(u.mpeg?(f="audio/mpeg",t.codec=""):u.mp3&&(t.codec="mp3")),d.audio={id:"audio",container:f,codec:t.codec,initSegment:!t.isAAC&&u.mpeg?new Uint8Array(0):et.initSegment([t]),metadata:{channelCount:t.channelCount}},h&&(n=t.inputTimeScale,i=a=o[0].pts-Math.round(n*r))),e.sps&&e.pps&&l.length&&(e.timescale=e.inputTimeScale,d.video={id:"main",container:"video/mp4",codec:e.codec,initSegment:et.initSegment([e]),metadata:{width:e.width,height:e.height}},h)){n=e.inputTimeScale;var c=this.getVideoStartPts(l),v=Math.round(n*r);a=Math.min(a,dt(l[0].dts,c)-v),i=Math.min(i,c-v)}if(Object.keys(d).length)return this.ISGenerated=!0,h&&(this._initPTS=i,this._initDTS=a),{tracks:d,initPTS:i,timescale:n}},e.remuxVideo=function(t,e,r,n){var s,o,l,u=t.inputTimeScale,d=t.samples,h=[],f=d.length,v=this._initPTS,g=this.nextAvcDts,p=8,m=Number.POSITIVE_INFINITY,y=Number.NEGATIVE_INFINITY,b=0,T=!1;r&&null!==g||(g=e*u-(d[0].pts-dt(d[0].dts,d[0].pts)));for(var E=0;ES.pts){b=Math.max(Math.min(b,S.pts-S.dts),-18e3)}S.dts0?E-1:E].dts&&(T=!0)}T&&d.sort((function(t,e){var r=t.dts-e.dts,i=t.pts-e.pts;return r||i})),o=d[0].dts,l=d[d.length-1].dts;var L=Math.round((l-o)/(f-1));if(b<0){if(b<-2*L){c.b.warn("PTS < DTS detected in video samples, offsetting DTS from PTS by "+at(-L,!0)+" ms");for(var A=b,R=0;RL;if(k||_<-1){k?c.b.warn("AVC: "+at(_,!0)+" ms ("+_+"dts) hole between fragments detected, filling it"):c.b.warn("AVC: "+at(-_,!0)+" ms ("+_+"dts) overlapping between fragments detected"),o=g;var x=d[0].pts-_;d[0].dts=o,d[0].pts=x,c.b.log("Video: First PTS/DTS adjusted: "+at(x,!0)+"/"+at(o,!0)+", delta: "+at(_,!0)+" ms")}}lt&&(o=Math.max(0,o));for(var C=0,w=0,O=0;O0?j-1:j].dts;if(Q.stretchShortVideoTrack&&null!==this.nextAudioPts){var Z=Math.floor(Q.maxBufferHole*u),J=(n?m+n*u:this.nextAudioPts)-H.pts;J>Z?((s=J-$)<0&&(s=$),c.b.log("[mp4-remuxer]: It is approximately "+J/90+" ms to the next segment; using duration "+s/90+" ms for the last video frame.")):s=$}else s=$}var tt=Math.round(H.pts-H.dts);h.push(new ht(H.key,s,V,tt))}if(h.length&&st&&st<70){var rt=h[0].flags;rt.dependsOn=2,rt.isNonSync=0}this.nextAvcDts=g=l+s,this.isVideoContiguous=!0;var it={data1:et.moof(t.sequenceNumber++,o,nt({},t,{samples:h})),data2:B,startPTS:m/u,endPTS:(y+s)/u,startDTS:o/u,endDTS:g/u,type:"video",hasAudio:!1,hasVideo:!0,nb:h.length,dropped:t.dropped};return t.samples=[],t.dropped=0,it},e.remuxAudio=function(t,e,r,n,s){var o=t.inputTimeScale,l=o/(t.samplerate?t.samplerate:o),u=t.isAAC?1024:1152,d=u*l,h=this._initPTS,f=!t.isAAC&&this.typeSupported.mpeg,v=[],g=t.samples,p=f?0:8,m=this.nextAudioPts||-1,y=e*o;if(this.isAudioContiguous=r=r||g.length&&m>0&&(n&&Math.abs(y-m)<9e3||Math.abs(dt(g[0].pts-h,y)-m)<20*d),g.forEach((function(t){t.pts=dt(t.pts-h,y)})),!r||m<0){if(!(g=g.filter((function(t){return t.pts>=0}))).length)return;m=0===s?0:n?Math.max(0,y):g[0].pts}if(t.isAAC)for(var b=void 0!==s,T=this.config.maxAudioFramesDrift,E=0,S=m;E=T*d&&D<1e4&&b){var _=Math.round(R/d);(S=A-_*d)<0&&(_--,S+=d),0===E&&(this.nextAudioPts=m=S),c.b.warn("[mp4-remuxer]: Injecting "+_+" audio frame @ "+(S/o).toFixed(3)+"s due to "+Math.round(1e3*R/o)+" ms gap.");for(var k=0;k<_;k++){var x=Math.max(S,0),C=Z.getSilentFrame(t.manifestCodec||t.codec,t.channelCount);C||(c.b.log("[mp4-remuxer]: Unable to get silent frame for given audio codec; duplicating last frame instead."),C=L.unit.subarray()),g.splice(E,0,{unit:C,pts:x}),S+=d,E++}}L.pts=S,S+=d}for(var w,O=null,I=null,P=0,F=g.length;F--;)P+=g[F].unit.byteLength;for(var M=0,N=g.length;M0))return;P+=p;try{w=new Uint8Array(P)}catch(t){return void this.observer.emit(i.a.ERROR,i.a.ERROR,{type:a.b.MUX_ERROR,details:a.a.REMUX_ALLOC_ERROR,fatal:!1,bytes:P,reason:"fail allocating audio mdat "+P})}f||(new DataView(w.buffer).setUint32(0,P),w.set(et.types.mdat,4))}w.set(U,p);var j=U.byteLength;p+=j,v.push(new ht(!0,u,j,0)),I=G}var H=v.length;if(H){var K=v[v.length-1];this.nextAudioPts=m=I+l*K.duration;var V=f?new Uint8Array(0):et.moof(t.sequenceNumber++,O/l,nt({},t,{samples:v}));t.samples=[];var W=O/o,Y=m/o,X={data1:V,data2:w,startPTS:W,endPTS:Y,startDTS:W,endDTS:Y,type:"audio",hasAudio:!0,hasVideo:!1,nb:H};return this.isAudioContiguous=!0,X}},e.remuxEmptyAudio=function(t,e,r,i){var a=t.inputTimeScale,n=a/(t.samplerate?t.samplerate:a),s=this.nextAudioPts,o=(null!==s?s:i.startDTS*a)+this._initDTS,l=i.endDTS*a+this._initDTS,u=1024*n,d=Math.ceil((l-o)/u),h=Z.getSilentFrame(t.manifestCodec||t.codec,t.channelCount);if(c.b.warn("[mp4-remuxer]: remux empty Audio"),h){for(var f=[],v=0;v4294967296;)t+=r;return t}var ht=function(t,e,r,i){this.size=void 0,this.duration=void 0,this.cts=void 0,this.flags=void 0,this.duration=e,this.size=r,this.cts=i,this.flags=new ft(t)},ft=function(t){this.isLeading=0,this.isDependedOn=0,this.hasRedundancy=0,this.degradPrio=0,this.dependsOn=1,this.isNonSync=1,this.dependsOn=t?2:1,this.isNonSync=t?0:1},ct=r(5),vt=function(){function t(){this.emitInitSegment=!1,this.audioCodec=void 0,this.videoCodec=void 0,this.initData=void 0,this.initPTS=void 0,this.initTracks=void 0,this.lastEndDTS=null}var e=t.prototype;return e.destroy=function(){},e.resetTimeStamp=function(t){this.initPTS=t,this.lastEndDTS=null},e.resetNextTimestamp=function(){this.lastEndDTS=null},e.resetInitSegment=function(t,e,r){this.audioCodec=e,this.videoCodec=r,this.generateInitSegment(t),this.emitInitSegment=!0},e.generateInitSegment=function(t){var e=this.audioCodec,r=this.videoCodec;if(!t||!t.byteLength)return this.initTracks=void 0,void(this.initData=void 0);var i=this.initData=Object(l.f)(t);e||(e=pt(i.audio,ct.a.AUDIO)),r||(r=pt(i.video,ct.a.VIDEO));var a={};i.audio&&i.video?a.audiovideo={container:"video/mp4",codec:e+","+r,initSegment:t,id:"main"}:i.audio?a.audio={container:"audio/mp4",codec:e,initSegment:t,id:"audio"}:i.video?a.video={container:"video/mp4",codec:r,initSegment:t,id:"main"}:c.b.warn("[passthrough-remuxer.ts]: initSegment does not contain moov or trak boxes."),this.initTracks=a},e.remux=function(t,e,r,i,a){var n=this.initPTS,o=this.lastEndDTS,u={audio:void 0,video:void 0,text:i,id3:r,initSegment:void 0};Object(s.a)(o)||(o=this.lastEndDTS=a||0);var d=e.samples;if(!d||!d.length)return u;var h={initPTS:void 0,timescale:1},f=this.initData;if(f&&f.length||(this.generateInitSegment(d),f=this.initData),!f||!f.length)return c.b.warn("[passthrough-remuxer.ts]: Failed to generate initSegment."),u;this.emitInitSegment&&(h.tracks=this.initTracks,this.emitInitSegment=!1),Object(s.a)(n)||(this.initPTS=h.initPTS=n=gt(f,d,o));var v=Object(l.c)(d,f),g=o,p=v+g;Object(l.e)(f,d,n),v>0?this.lastEndDTS=p:(c.b.warn("Duration parsed from mp4 should be greater than zero"),this.resetNextTimestamp());var m=!!f.audio,y=!!f.video,b="";m&&(b+="audio"),y&&(b+="video");var T={data1:d,startPTS:g,startDTS:g,endPTS:p,endDTS:p,type:b,hasAudio:m,hasVideo:y,nb:1,dropped:0};return u.audio="audio"===T.type?T:void 0,u.video="audio"!==T.type?T:void 0,u.text=i,u.id3=r,u.initSegment=h,u},t}(),gt=function(t,e,r){return Object(l.d)(t,e)-r};function pt(t,e){var r=null==t?void 0:t.codec;return r&&r.length>4?r:"hvc1"===r?"hvc1.1.c.L120.90":"av01"===r?"av01.0.04M.08":"avc1"===r||e===ct.a.VIDEO?"avc1.42e01e":"mp4a.40.5"}var mt,yt=vt,bt=r(16);try{mt=self.performance.now.bind(self.performance)}catch(t){c.b.debug("Unable to use Performance API on this environment"),mt=self.Date.now}var Tt=[{demux:q,remux:ut},{demux:D,remux:yt},{demux:A,remux:ut},{demux:$,remux:ut}],Et=1024;Tt.forEach((function(t){var e=t.demux;Et=Math.max(Et,e.minProbeByteLength)}));var St=function(){function t(t,e,r,i,a){this.observer=void 0,this.typeSupported=void 0,this.config=void 0,this.vendor=void 0,this.id=void 0,this.demuxer=void 0,this.remuxer=void 0,this.decrypter=void 0,this.probe=void 0,this.decryptionPromise=null,this.transmuxConfig=void 0,this.currentTransmuxState=void 0,this.cache=new bt.a,this.observer=t,this.typeSupported=e,this.config=r,this.vendor=i,this.id=a}var e=t.prototype;return e.configure=function(t){this.transmuxConfig=t,this.decrypter&&this.decrypter.reset()},e.push=function(t,e,r,i){var a=this,n=r.transmuxing;n.executeStart=mt();var s=new Uint8Array(t),o=this.cache,u=this.config,d=this.currentTransmuxState,h=this.transmuxConfig;i&&(this.currentTransmuxState=i);var f=function(t,e){var r=null;t.byteLength>0&&null!=e&&null!=e.key&&null!==e.iv&&null!=e.method&&(r=e);return r}(s,e);if(f&&"AES-128"===f.method){var c=this.getDecrypter();if(!u.enableSoftwareAES)return this.decryptionPromise=c.webCryptoDecrypt(s,f.key.buffer,f.iv.buffer).then((function(t){var e=a.push(t,null,r);return a.decryptionPromise=null,e})),this.decryptionPromise;var v=c.softwareDecrypt(s,f.key.buffer,f.iv.buffer);if(!v)return n.executeEnd=mt(),Lt(r);s=new Uint8Array(v)}var g=i||d,p=g.contiguous,m=g.discontinuity,y=g.trackSwitch,b=g.accurateTimeOffset,T=g.timeOffset,E=g.initSegmentChange,S=h.audioCodec,L=h.videoCodec,A=h.defaultInitPts,R=h.duration,D=h.initSegmentData;if((m||y||E)&&this.resetInitSegment(D,S,L,R),(m||E)&&this.resetInitialTimestamp(A),p||this.resetContiguity(),this.needsProbing(s,m,y)){if(o.dataLength){var _=o.flush();s=Object(l.a)(_,s)}this.configureTransmuxer(s,h)}var k=this.transmux(s,f,T,b,r),x=this.currentTransmuxState;return x.contiguous=!0,x.discontinuity=!1,x.trackSwitch=!1,n.executeEnd=mt(),k},e.flush=function(t){var e=this,r=t.transmuxing;r.executeStart=mt();var n=this.decrypter,s=this.cache,o=this.currentTransmuxState,l=this.decryptionPromise;if(l)return l.then((function(){return e.flush(t)}));var u=[],d=o.timeOffset;if(n){var h=n.flush();h&&u.push(this.push(h,null,t))}var f=s.dataLength;s.reset();var c=this.demuxer,v=this.remuxer;if(!c||!v)return f>=Et&&this.observer.emit(i.a.ERROR,i.a.ERROR,{type:a.b.MEDIA_ERROR,details:a.a.FRAG_PARSING_ERROR,fatal:!0,reason:"no demux matching with content found"}),r.executeEnd=mt(),[Lt(t)];var g=c.flush(d);return At(g)?g.then((function(r){return e.flushRemux(u,r,t),u})):(this.flushRemux(u,g,t),u)},e.flushRemux=function(t,e,r){var i=e.audioTrack,a=e.avcTrack,n=e.id3Track,s=e.textTrack,o=this.currentTransmuxState,l=o.accurateTimeOffset,u=o.timeOffset;c.b.log("[transmuxer.ts]: Flushed fragment "+r.sn+(r.part>-1?" p: "+r.part:"")+" of level "+r.level);var d=this.remuxer.remux(i,a,n,s,u,l,!0,this.id);t.push({remuxResult:d,chunkMeta:r}),r.transmuxing.executeEnd=mt()},e.resetInitialTimestamp=function(t){var e=this.demuxer,r=this.remuxer;e&&r&&(e.resetTimeStamp(t),r.resetTimeStamp(t))},e.resetContiguity=function(){var t=this.demuxer,e=this.remuxer;t&&e&&(t.resetContiguity(),e.resetNextTimestamp())},e.resetInitSegment=function(t,e,r,i){var a=this.demuxer,n=this.remuxer;a&&n&&(a.resetInitSegment(e,r,i),n.resetInitSegment(t,e,r))},e.destroy=function(){this.demuxer&&(this.demuxer.destroy(),this.demuxer=void 0),this.remuxer&&(this.remuxer.destroy(),this.remuxer=void 0)},e.transmux=function(t,e,r,i,a){return e&&"SAMPLE-AES"===e.method?this.transmuxSampleAes(t,e,r,i,a):this.transmuxUnencrypted(t,r,i,a)},e.transmuxUnencrypted=function(t,e,r,i){var a=this.demuxer.demux(t,e,!1,!this.config.progressive),n=a.audioTrack,s=a.avcTrack,o=a.id3Track,l=a.textTrack;return{remuxResult:this.remuxer.remux(n,s,o,l,e,r,!1,this.id),chunkMeta:i}},e.transmuxSampleAes=function(t,e,r,i,a){var n=this;return this.demuxer.demuxSampleAes(t,e,r).then((function(t){return{remuxResult:n.remuxer.remux(t.audioTrack,t.avcTrack,t.id3Track,t.textTrack,r,i,!1,n.id),chunkMeta:a}}))},e.configureTransmuxer=function(t,e){for(var r,i=this.config,a=this.observer,n=this.typeSupported,s=this.vendor,o=e.audioCodec,l=e.defaultInitPts,u=e.duration,d=e.initSegmentData,h=e.videoCodec,f=0,v=Tt.length;f>>8^255&p^99,t[c]=p,e[p]=c;var m=f[c],y=f[m],b=f[y],T=257*f[p]^16843008*p;i[c]=T<<24|T>>>8,a[c]=T<<16|T>>>16,n[c]=T<<8|T>>>24,s[c]=T,T=16843009*b^65537*y^257*m^16843008*c,l[p]=T<<24|T>>>8,u[p]=T<<16|T>>>16,d[p]=T<<8|T>>>24,h[p]=T,c?(c=m^f[f[f[b^m]]],v^=f[f[v]]):c=v=1}},e.expandKey=function(t){for(var e=this.uint8ArrayToUint32Array_(t),r=!0,i=0;i0}),!1)}t.exports=function(t,e){e=e||{};var a={main:r.m},o=e.all?{main:Object.keys(a.main)}:function(t,e){for(var r={main:[e]},i={main:[]},a={main:{}};s(r);)for(var o=Object.keys(r),l=0;lt.endSN||e>0||0===e&&r>0,this.updated||this.advanced?this.misses=Math.floor(.6*t.misses):this.misses=t.misses+1,this.availabilityDelay=t.availabilityDelay},e=t,(r=[{key:"hasProgramDateTime",get:function(){return!!this.fragments.length&&Object(a.a)(this.fragments[this.fragments.length-1].programDateTime)}},{key:"levelTargetDuration",get:function(){return this.averagetargetduration||this.targetduration||10}},{key:"drift",get:function(){var t=this.driftEndTime-this.driftStartTime;return t>0?1e3*(this.driftEnd-this.driftStart)/t:1}},{key:"edge",get:function(){return this.partEnd||this.fragmentEnd}},{key:"partEnd",get:function(){var t;return null!==(t=this.partList)&&void 0!==t&&t.length?this.partList[this.partList.length-1].end:this.fragmentEnd}},{key:"fragmentEnd",get:function(){var t;return null!==(t=this.fragments)&&void 0!==t&&t.length?this.fragments[this.fragments.length-1].end:0}},{key:"age",get:function(){return this.advancedDateTime?Math.max(Date.now()-this.advancedDateTime,0)/1e3:0}},{key:"lastPartIndex",get:function(){var t;return null!==(t=this.partList)&&void 0!==t&&t.length?this.partList[this.partList.length-1].index:-1}},{key:"lastPartSn",get:function(){var t;return null!==(t=this.partList)&&void 0!==t&&t.length?this.partList[this.partList.length-1].fragment.sn:this.endSN}}])&&d(e.prototype,r),i&&d(e,i),t}(),f=r(15),c=/^(\d+)x(\d+)$/,v=/\s*(.+?)\s*=((?:\".*?\")|.*?)(?:,|$)/g,g=function(){function t(e){for(var r in"string"==typeof e&&(e=t.parseAttrList(e)),e)e.hasOwnProperty(r)&&(this[r]=e[r])}var e=t.prototype;return e.decimalInteger=function(t){var e=parseInt(this[t],10);return e>Number.MAX_SAFE_INTEGER?1/0:e},e.hexadecimalInteger=function(t){if(this[t]){var e=(this[t]||"0x").slice(2);e=(1&e.length?"0":"")+e;for(var r=new Uint8Array(e.length/2),i=0;iNumber.MAX_SAFE_INTEGER?1/0:e},e.decimalFloatingPoint=function(t){return parseFloat(this[t])},e.optionalFloat=function(t,e){var r=this[t];return r?parseFloat(r):e},e.enumeratedString=function(t){return this[t]},e.bool=function(t){return"YES"===this[t]},e.decimalResolution=function(t){var e=c.exec(this[t]);if(null!==e)return{width:parseInt(e[1],10),height:parseInt(e[2],10)}},t.parseAttrList=function(t){var e,r={};for(v.lastIndex=0;null!==(e=v.exec(t));){var i=e[2];0===i.indexOf('"')&&i.lastIndexOf('"')===i.length-1&&(i=i.slice(1,-1)),r[e[1]]=i}return r},t}(),p={audio:{a3ds:!0,"ac-3":!0,"ac-4":!0,alac:!0,alaw:!0,dra1:!0,"dts+":!0,"dts-":!0,dtsc:!0,dtse:!0,dtsh:!0,"ec-3":!0,enca:!0,g719:!0,g726:!0,m4ae:!0,mha1:!0,mha2:!0,mhm1:!0,mhm2:!0,mlpa:!0,mp4a:!0,"raw ":!0,Opus:!0,samr:!0,sawb:!0,sawp:!0,sevc:!0,sqcp:!0,ssmv:!0,twos:!0,ulaw:!0},video:{avc1:!0,avc2:!0,avc3:!0,avc4:!0,avcp:!0,av01:!0,drac:!0,dvav:!0,dvhe:!0,encv:!0,hev1:!0,hvc1:!0,mjp2:!0,mp4v:!0,mvc1:!0,mvc2:!0,mvc3:!0,mvc4:!0,resv:!0,rv60:!0,s263:!0,svc1:!0,svc2:!0,"vc-1":!0,vp08:!0,vp09:!0},text:{stpp:!0,wvtt:!0}};function m(t,e){return MediaSource.isTypeSupported((e||"video")+'/mp4;codecs="'+t+'"')}var y=/#EXT-X-STREAM-INF:([^\r\n]*)(?:[\r\n](?:#[^\r\n]*)?)*([^\r\n]+)|#EXT-X-SESSION-DATA:([^\r\n]*)[\r\n]+/g,b=/#EXT-X-MEDIA:(.*)/g,T=new RegExp([/#EXTINF:\s*(\d*(?:\.\d+)?)(?:,(.*)\s+)?/.source,/(?!#) *(\S[\S ]*)/.source,/#EXT-X-BYTERANGE:*(.+)/.source,/#EXT-X-PROGRAM-DATE-TIME:(.+)/.source,/#.*/.source].join("|"),"g"),E=new RegExp([/#(EXTM3U)/.source,/#EXT-X-(PLAYLIST-TYPE):(.+)/.source,/#EXT-X-(MEDIA-SEQUENCE): *(\d+)/.source,/#EXT-X-(SKIP):(.+)/.source,/#EXT-X-(TARGETDURATION): *(\d+)/.source,/#EXT-X-(KEY):(.+)/.source,/#EXT-X-(START):(.+)/.source,/#EXT-X-(ENDLIST)/.source,/#EXT-X-(DISCONTINUITY-SEQ)UENCE: *(\d+)/.source,/#EXT-X-(DIS)CONTINUITY/.source,/#EXT-X-(VERSION):(\d+)/.source,/#EXT-X-(MAP):(.+)/.source,/#EXT-X-(SERVER-CONTROL):(.+)/.source,/#EXT-X-(PART-INF):(.+)/.source,/#EXT-X-(GAP)/.source,/#EXT-X-(BITRATE):\s*(\d+)/.source,/#EXT-X-(PART):(.+)/.source,/#EXT-X-(PRELOAD-HINT):(.+)/.source,/#EXT-X-(RENDITION-REPORT):(.+)/.source,/(#)([^:]*):(.*)/.source,/(#)(.*)(?:.*)\r?\n?/.source].join("|")),S=/\.(mp4|m4s|m4v|m4a)$/i;var L=function(){function t(){}return t.findGroup=function(t,e){for(var r=0;r2){var r=e.shift()+".";return r+=parseInt(e.shift()).toString(16),r+=("000"+parseInt(e.shift()).toString(16)).substr(-4)}return t},t.resolve=function(t,e){return i.buildAbsoluteURL(e,t,{alwaysNormalize:!0})},t.parseMasterPlaylist=function(e,r){var i,a=[],n={},s=!1;for(y.lastIndex=0;null!=(i=y.exec(e));)if(i[1]){var o=new g(i[1]),l={attrs:o,bitrate:o.decimalInteger("AVERAGE-BANDWIDTH")||o.decimalInteger("BANDWIDTH"),name:o.NAME,url:t.resolve(i[2],r)},u=o.decimalResolution("RESOLUTION");u&&(l.width=u.width,l.height=u.height),A((o.CODECS||"").split(/[ ,]+/).filter((function(t){return t})),l),l.videoCodec&&-1!==l.videoCodec.indexOf("avc1")&&(l.videoCodec=t.convertAVC1ToAVCOTI(l.videoCodec)),a.push(l)}else if(i[3]){var d=new g(i[3]);d["DATA-ID"]&&(s=!0,n[d["DATA-ID"]]=d)}return{levels:a,sessionData:s?n:null}},t.parseMasterPlaylistMedia=function(e,r,i,a){var n;void 0===a&&(a=[]);var s=[],o=0;for(b.lastIndex=0;null!==(n=b.exec(e));){var l=new g(n[1]);if(l.TYPE===i){var u={attrs:l,bitrate:0,id:o++,groupId:l["GROUP-ID"],instreamId:l["INSTREAM-ID"],name:l.NAME||l.LANGUAGE||"",type:i,default:l.bool("DEFAULT"),autoselect:l.bool("AUTOSELECT"),forced:l.bool("FORCED"),lang:l.LANGUAGE,url:l.URI?t.resolve(l.URI,r):""};if(a.length){var d=t.findGroup(a,u.groupId)||a[0];R(u,d,"audioCodec"),R(u,d,"textCodec")}s.push(u)}}return s},t.parseLevelPlaylist=function(t,e,r,n,s){var l,d,c,v=new h(e),p=v.fragments,m=null,y=0,b=0,L=0,A=0,R=null,_=new u.b(n,e),k=-1,x=!1;for(T.lastIndex=0,v.m3u8=t;null!==(l=T.exec(t));){x&&(x=!1,(_=new u.b(n,e)).start=L,_.sn=y,_.cc=A,_.level=r,m&&(_.initSegment=m,_.rawProgramDateTime=m.rawProgramDateTime));var C=l[1];if(C){_.duration=parseFloat(C);var w=(" "+l[2]).slice(1);_.title=w||null,_.tagList.push(w?["INF",C,w]:["INF",C])}else if(l[3])Object(a.a)(_.duration)&&(_.start=L,c&&(_.levelkey=c),_.sn=y,_.level=r,_.cc=A,_.urlId=s,p.push(_),_.relurl=(" "+l[3]).slice(1),D(_,R),R=_,L+=_.duration,y++,b=0,x=!0);else if(l[4]){var O=(" "+l[4]).slice(1);R?_.setByteRange(O,R):_.setByteRange(O)}else if(l[5])_.rawProgramDateTime=(" "+l[5]).slice(1),_.tagList.push(["PROGRAM-DATE-TIME",_.rawProgramDateTime]),-1===k&&(k=p.length);else{if(!(l=l[0].match(E))){o.b.warn("No matches on slow regex match for level playlist!");continue}for(d=1;d-1){o.b.warn("Keyformat "+X+" is not supported from the manifest");continue}if("identity"!==X)continue;H&&(c=f.a.fromURL(e,K),K&&["AES-128","SAMPLE-AES","SAMPLE-AES-CENC"].indexOf(H)>=0&&(c.method=H,c.keyFormat=X,Y&&(c.keyID=Y),W&&(c.keyFormatVersions=W),c.iv=V));break;case"START":var q=new g(P).decimalFloatingPoint("TIME-OFFSET");Object(a.a)(q)&&(v.startTimeOffset=q);break;case"MAP":var z=new g(P);_.relurl=z.URI,z.BYTERANGE&&_.setByteRange(z.BYTERANGE),_.level=r,_.sn="initSegment",c&&(_.levelkey=c),_.initSegment=null,m=_,x=!0;break;case"SERVER-CONTROL":var Q=new g(P);v.canBlockReload=Q.bool("CAN-BLOCK-RELOAD"),v.canSkipUntil=Q.optionalFloat("CAN-SKIP-UNTIL",0),v.canSkipDateRanges=v.canSkipUntil>0&&Q.bool("CAN-SKIP-DATERANGES"),v.partHoldBack=Q.optionalFloat("PART-HOLD-BACK",0),v.holdBack=Q.optionalFloat("HOLD-BACK",0);break;case"PART-INF":var $=new g(P);v.partTarget=$.decimalFloatingPoint("PART-TARGET");break;case"PART":var Z=v.partList;Z||(Z=v.partList=[]);var J=b>0?Z[Z.length-1]:void 0,tt=b++,et=new u.c(new g(P),_,e,tt,J);Z.push(et),_.duration+=et.duration;break;case"PRELOAD-HINT":var rt=new g(P);v.preloadHint=rt;break;case"RENDITION-REPORT":var it=new g(P);v.renditionReports=v.renditionReports||[],v.renditionReports.push(it);break;default:o.b.warn("line parsed but not handled: "+l)}}}R&&!R.relurl?(p.pop(),L-=R.duration,v.partList&&(v.fragmentHint=R)):v.partList&&(D(_,R),_.cc=A,v.fragmentHint=_);var at=p.length,nt=p[0],st=p[at-1];if((L+=v.skippedSegments*v.targetduration)>0&&at&&st){v.averagetargetduration=L/at;var ot=st.sn;v.endSN="initSegment"!==ot?ot:0,nt&&(v.startCC=nt.cc,nt.initSegment||v.fragments.every((function(t){return t.relurl&&(e=t.relurl,S.test(null!=(r=null===(a=i.parseURL(e))||void 0===a?void 0:a.path)?r:""));var e,r,a}))&&(o.b.warn("MP4 fragments found but no init segment (probably no MAP, incomplete M3U8), trying to fetch SIDX"),(_=new u.b(n,e)).relurl=st.relurl,_.level=r,_.sn="initSegment",nt.initSegment=_,v.needSidxRanges=!0))}else v.endSN=0,v.startCC=0;return v.fragmentHint&&(L+=v.fragmentHint.duration),v.totalduration=L,v.endCC=A,k>0&&function(t,e){for(var r=t[e],i=e;i--;){var a=t[i];if(!a)return;a.programDateTime=r.programDateTime-1e3*a.duration,r=a}}(p,k),v},t}();function A(t,e){["video","audio","text"].forEach((function(r){var i=t.filter((function(t){return function(t,e){var r=p[e];return!!r&&!0===r[t.slice(0,4)]}(t,r)}));if(i.length){var a=i.filter((function(t){return 0===t.lastIndexOf("avc1",0)||0===t.lastIndexOf("mp4a",0)}));e[r+"Codec"]=a.length>0?a[0]:i[0],t=t.filter((function(t){return-1===i.indexOf(t)}))}})),e.unknownCodecs=t}function R(t,e,r){var i=e[r];i&&(t[r]=i)}function D(t,e){t.rawProgramDateTime?t.programDateTime=Date.parse(t.rawProgramDateTime):null!=e&&e.programDateTime&&(t.programDateTime=e.endProgramDateTime),Object(a.a)(t.programDateTime)||(t.programDateTime=null,t.rawProgramDateTime=null)}var _=r(4);function k(t,e){var r=t.url;return void 0!==r&&0!==r.indexOf("data:")||(r=e.url),r}var x=function(){function t(t){this.hls=void 0,this.loaders=Object.create(null),this.hls=t,this.registerListeners()}var e=t.prototype;return e.registerListeners=function(){var t=this.hls;t.on(n.a.MANIFEST_LOADING,this.onManifestLoading,this),t.on(n.a.LEVEL_LOADING,this.onLevelLoading,this),t.on(n.a.AUDIO_TRACK_LOADING,this.onAudioTrackLoading,this),t.on(n.a.SUBTITLE_TRACK_LOADING,this.onSubtitleTrackLoading,this)},e.unregisterListeners=function(){var t=this.hls;t.off(n.a.MANIFEST_LOADING,this.onManifestLoading,this),t.off(n.a.LEVEL_LOADING,this.onLevelLoading,this),t.off(n.a.AUDIO_TRACK_LOADING,this.onAudioTrackLoading,this),t.off(n.a.SUBTITLE_TRACK_LOADING,this.onSubtitleTrackLoading,this)},e.createInternalLoader=function(t){var e=this.hls.config,r=e.pLoader,i=e.loader,a=new(r||i)(e);return t.loader=a,this.loaders[t.type]=a,a},e.getInternalLoader=function(t){return this.loaders[t.type]},e.resetInternalLoader=function(t){this.loaders[t]&&delete this.loaders[t]},e.destroyInternalLoaders=function(){for(var t in this.loaders){var e=this.loaders[t];e&&e.destroy(),this.resetInternalLoader(t)}},e.destroy=function(){this.unregisterListeners(),this.destroyInternalLoaders()},e.onManifestLoading=function(t,e){var r=e.url;this.load({id:null,groupId:null,level:0,responseType:"text",type:_.a.MANIFEST,url:r,deliveryDirectives:null})},e.onLevelLoading=function(t,e){var r=e.id,i=e.level,a=e.url,n=e.deliveryDirectives;this.load({id:r,groupId:null,level:i,responseType:"text",type:_.a.LEVEL,url:a,deliveryDirectives:n})},e.onAudioTrackLoading=function(t,e){var r=e.id,i=e.groupId,a=e.url,n=e.deliveryDirectives;this.load({id:r,groupId:i,level:null,responseType:"text",type:_.a.AUDIO_TRACK,url:a,deliveryDirectives:n})},e.onSubtitleTrackLoading=function(t,e){var r=e.id,i=e.groupId,a=e.url,n=e.deliveryDirectives;this.load({id:r,groupId:i,level:null,responseType:"text",type:_.a.SUBTITLE_TRACK,url:a,deliveryDirectives:n})},e.load=function(t){var e,r,i,a,n,s,l=this.hls.config,u=this.getInternalLoader(t);if(u){var d=u.context;if(d&&d.url===t.url)return void o.b.trace("[playlist-loader]: playlist request ongoing");o.b.log("[playlist-loader]: aborting previous loader for type: "+t.type),u.abort()}switch(t.type){case _.a.MANIFEST:r=l.manifestLoadingMaxRetry,i=l.manifestLoadingTimeOut,a=l.manifestLoadingRetryDelay,n=l.manifestLoadingMaxRetryTimeout;break;case _.a.LEVEL:case _.a.AUDIO_TRACK:case _.a.SUBTITLE_TRACK:r=0,i=l.levelLoadingTimeOut;break;default:r=l.levelLoadingMaxRetry,i=l.levelLoadingTimeOut,a=l.levelLoadingRetryDelay,n=l.levelLoadingMaxRetryTimeout}if((u=this.createInternalLoader(t),null!==(e=t.deliveryDirectives)&&void 0!==e&&e.part)&&(t.type===_.a.LEVEL&&null!==t.level?s=this.hls.levels[t.level].details:t.type===_.a.AUDIO_TRACK&&null!==t.id?s=this.hls.audioTracks[t.id].details:t.type===_.a.SUBTITLE_TRACK&&null!==t.id&&(s=this.hls.subtitleTracks[t.id].details),s)){var h=s.partTarget,f=s.targetduration;h&&f&&(i=Math.min(1e3*Math.max(3*h,.8*f),i))}var c={timeout:i,maxRetry:r,retryDelay:a,maxRetryDelay:n,highWaterMark:0},v={onSuccess:this.loadsuccess.bind(this),onError:this.loaderror.bind(this),onTimeout:this.loadtimeout.bind(this)};u.load(t,c,v)},e.loadsuccess=function(t,e,r,i){if(void 0===i&&(i=null),r.isSidxRequest)return this.handleSidxRequest(t,r),void this.handlePlaylistLoaded(t,e,r,i);this.resetInternalLoader(r.type);var a=t.data;0===a.indexOf("#EXTM3U")?(e.parsing.start=performance.now(),a.indexOf("#EXTINF:")>0||a.indexOf("#EXT-X-TARGETDURATION:")>0?this.handleTrackOrLevelPlaylist(t,e,r,i):this.handleMasterPlaylist(t,e,r,i)):this.handleManifestParsingError(t,r,"no EXTM3U delimiter",i)},e.loaderror=function(t,e,r){void 0===r&&(r=null),this.handleNetworkError(e,r,!1,t)},e.loadtimeout=function(t,e,r){void 0===r&&(r=null),this.handleNetworkError(e,r,!0)},e.handleMasterPlaylist=function(t,e,r,i){var a=this.hls,s=t.data,l=k(t,r),u=L.parseMasterPlaylist(s,l),d=u.levels,h=u.sessionData;if(d.length){var f=d.map((function(t){return{id:t.attrs.AUDIO,audioCodec:t.audioCodec}})),c=d.map((function(t){return{id:t.attrs.SUBTITLES,textCodec:t.textCodec}})),v=L.parseMasterPlaylistMedia(s,l,"AUDIO",f),p=L.parseMasterPlaylistMedia(s,l,"SUBTITLES",c),m=L.parseMasterPlaylistMedia(s,l,"CLOSED-CAPTIONS");if(v.length)v.some((function(t){return!t.url}))||!d[0].audioCodec||d[0].attrs.AUDIO||(o.b.log("[playlist-loader]: audio codec signaled in quality level, but no embedded audio track signaled, create one"),v.unshift({type:"main",name:"main",default:!1,autoselect:!1,forced:!1,id:-1,attrs:new g({}),bitrate:0,url:""}));a.trigger(n.a.MANIFEST_LOADED,{levels:d,audioTracks:v,subtitles:p,captions:m,url:l,stats:e,networkDetails:i,sessionData:h})}else this.handleManifestParsingError(t,r,"no level found in manifest",i)},e.handleTrackOrLevelPlaylist=function(t,e,r,i){var o=this.hls,l=r.id,u=r.level,d=r.type,h=k(t,r),f=Object(a.a)(l)?l:0,c=Object(a.a)(u)?u:f,v=function(t){switch(t.type){case _.a.AUDIO_TRACK:return _.b.AUDIO;case _.a.SUBTITLE_TRACK:return _.b.SUBTITLE;default:return _.b.MAIN}}(r),p=L.parseLevelPlaylist(t.data,h,c,v,f);if(p.fragments.length){if(d===_.a.MANIFEST){var m={attrs:new g({}),bitrate:0,details:p,name:"",url:h};o.trigger(n.a.MANIFEST_LOADED,{levels:[m],audioTracks:[],url:h,stats:e,networkDetails:i,sessionData:null})}if(e.parsing.end=performance.now(),p.needSidxRanges){var y,b=null===(y=p.fragments[0].initSegment)||void 0===y?void 0:y.url;this.load({url:b,isSidxRequest:!0,type:d,level:u,levelDetails:p,id:l,groupId:null,rangeStart:0,rangeEnd:2048,responseType:"arraybuffer",deliveryDirectives:null})}else r.levelDetails=p,this.handlePlaylistLoaded(t,e,r,i)}else o.trigger(n.a.ERROR,{type:s.b.NETWORK_ERROR,details:s.a.LEVEL_EMPTY_ERROR,fatal:!1,url:h,reason:"no fragments found in level",level:"number"==typeof r.level?r.level:void 0})},e.handleSidxRequest=function(t,e){var r=Object(l.g)(new Uint8Array(t.data));if(r){var i=r.references,a=e.levelDetails;i.forEach((function(t,e){var i=t.info,n=a.fragments[e];0===n.byteRange.length&&n.setByteRange(String(1+i.end-i.start)+"@"+String(i.start)),n.initSegment&&n.initSegment.setByteRange(String(r.moovEndOffset)+"@0")}))}},e.handleManifestParsingError=function(t,e,r,i){this.hls.trigger(n.a.ERROR,{type:s.b.NETWORK_ERROR,details:s.a.MANIFEST_PARSING_ERROR,fatal:e.type===_.a.MANIFEST,url:t.url,reason:r,response:t,context:e,networkDetails:i})},e.handleNetworkError=function(t,e,r,i){void 0===r&&(r=!1),o.b.warn("[playlist-loader]: A network "+(r?"timeout":"error")+" occurred while loading "+t.type+" level: "+t.level+" id: "+t.id+' group-id: "'+t.groupId+'"');var a=s.a.UNKNOWN,l=!1,u=this.getInternalLoader(t);switch(t.type){case _.a.MANIFEST:a=r?s.a.MANIFEST_LOAD_TIMEOUT:s.a.MANIFEST_LOAD_ERROR,l=!0;break;case _.a.LEVEL:a=r?s.a.LEVEL_LOAD_TIMEOUT:s.a.LEVEL_LOAD_ERROR,l=!1;break;case _.a.AUDIO_TRACK:a=r?s.a.AUDIO_TRACK_LOAD_TIMEOUT:s.a.AUDIO_TRACK_LOAD_ERROR,l=!1;break;case _.a.SUBTITLE_TRACK:a=r?s.a.SUBTITLE_TRACK_LOAD_TIMEOUT:s.a.SUBTITLE_LOAD_ERROR,l=!1}u&&this.resetInternalLoader(t.type);var d={type:s.b.NETWORK_ERROR,details:a,fatal:l,url:t.url,loader:u,context:t,networkDetails:e};i&&(d.response=i),this.hls.trigger(n.a.ERROR,d)},e.handlePlaylistLoaded=function(t,e,r,i){var a=r.type,s=r.level,o=r.id,l=r.groupId,u=r.loader,d=r.levelDetails,h=r.deliveryDirectives;if(null!=d&&d.targetduration){if(u)switch(d.live&&(u.getCacheAge&&(d.ageHeader=u.getCacheAge()||0),u.getCacheAge&&!isNaN(d.ageHeader)||(d.ageHeader=0)),a){case _.a.MANIFEST:case _.a.LEVEL:this.hls.trigger(n.a.LEVEL_LOADED,{details:d,level:s||0,id:o||0,stats:e,networkDetails:i,deliveryDirectives:h});break;case _.a.AUDIO_TRACK:this.hls.trigger(n.a.AUDIO_TRACK_LOADED,{details:d,id:o||0,groupId:l||"",stats:e,networkDetails:i,deliveryDirectives:h});break;case _.a.SUBTITLE_TRACK:this.hls.trigger(n.a.SUBTITLE_TRACK_LOADED,{details:d,id:o||0,groupId:l||"",stats:e,networkDetails:i,deliveryDirectives:h})}}else this.handleManifestParsingError(t,r,"invalid target duration",i)},t}(),C=function(){function t(t){this.hls=void 0,this.loaders={},this.decryptkey=null,this.decrypturl=null,this.hls=t,this._registerListeners()}var e=t.prototype;return e._registerListeners=function(){this.hls.on(n.a.KEY_LOADING,this.onKeyLoading,this)},e._unregisterListeners=function(){this.hls.off(n.a.KEY_LOADING,this.onKeyLoading)},e.destroy=function(){for(var t in this._unregisterListeners(),this.loaders){var e=this.loaders[t];e&&e.destroy()}this.loaders={}},e.onKeyLoading=function(t,e){var r=e.frag,i=r.type,a=this.loaders[i];if(r.decryptdata){var s=r.decryptdata.uri;if(s!==this.decrypturl||null===this.decryptkey){var l=this.hls.config;if(a&&(o.b.warn("abort previous key loader for type:"+i),a.abort()),!s)return void o.b.warn("key uri is falsy");var u=l.loader,d=r.loader=this.loaders[i]=new u(l);this.decrypturl=s,this.decryptkey=null;var h={url:s,frag:r,responseType:"arraybuffer"},f={timeout:l.fragLoadingTimeOut,maxRetry:0,retryDelay:l.fragLoadingRetryDelay,maxRetryDelay:l.fragLoadingMaxRetryTimeout,highWaterMark:0},c={onSuccess:this.loadsuccess.bind(this),onError:this.loaderror.bind(this),onTimeout:this.loadtimeout.bind(this)};d.load(h,f,c)}else this.decryptkey&&(r.decryptdata.key=this.decryptkey,this.hls.trigger(n.a.KEY_LOADED,{frag:r}))}else o.b.warn("Missing decryption data on fragment in onKeyLoading")},e.loadsuccess=function(t,e,r){var i=r.frag;i.decryptdata?(this.decryptkey=i.decryptdata.key=new Uint8Array(t.data),i.loader=null,delete this.loaders[i.type],this.hls.trigger(n.a.KEY_LOADED,{frag:i})):o.b.error("after key load, decryptdata unset")},e.loaderror=function(t,e){var r=e.frag,i=r.loader;i&&i.abort(),delete this.loaders[r.type],this.hls.trigger(n.a.ERROR,{type:s.b.NETWORK_ERROR,details:s.a.KEY_LOAD_ERROR,fatal:!1,frag:r,response:t})},e.loadtimeout=function(t,e){var r=e.frag,i=r.loader;i&&i.abort(),delete this.loaders[r.type],this.hls.trigger(n.a.ERROR,{type:s.b.NETWORK_ERROR,details:s.a.KEY_LOAD_TIMEOUT,fatal:!1,frag:r})},t}();function w(t,e){var r;try{r=new Event("addtrack")}catch(t){(r=document.createEvent("Event")).initEvent("addtrack",!1,!1)}r.track=t,e.dispatchEvent(r)}function O(t,e,r){var i=t.mode;if("disabled"===i&&(t.mode="hidden"),t.cues&&t.cues.length>0)for(var a=function(t,e,r){var i=[],a=function(t,e){if(et[r].endTime)return-1;var i=0,a=r;for(;i<=a;){var n=Math.floor((a+i)/2);if(et[n].startTime&&i-1)for(var n=a,s=t.length;n=e&&o.endTime<=r)i.push(o);else if(o.startTime>r)return i}return i}(t.cues,e,r),n=0;n.05&&this.forwardBufferLength>1){var u=Math.min(2,Math.max(1,n)),d=Math.round(2/(1+Math.exp(-.75*o-this.edgeStalled))*20)/20;t.playbackRate=Math.min(u,Math.max(1,d))}else 1!==t.playbackRate&&0!==t.playbackRate&&(t.playbackRate=1)}}}}},a.estimateLiveEdge=function(){var t=this.levelDetails;return null===t?null:t.edge+t.age},a.computeLatency=function(){var t=this.estimateLiveEdge();return null===t?null:t-this.currentTime},e=t,(r=[{key:"latency",get:function(){return this._latency||0}},{key:"maxLatency",get:function(){var t=this.config,e=this.levelDetails;return void 0!==t.liveMaxLatencyDuration?t.liveMaxLatencyDuration:e?t.liveMaxLatencyDurationCount*e.targetduration:0}},{key:"targetLatency",get:function(){var t=this.levelDetails;if(null===t)return null;var e=t.holdBack,r=t.partHoldBack,i=t.targetduration,a=this.config,n=a.liveSyncDuration,s=a.liveSyncDurationCount,o=a.lowLatencyMode,l=this.hls.userConfig,u=o&&r||e;(l.liveSyncDuration||l.liveSyncDurationCount||0===u)&&(u=void 0!==n?n:s*i);var d=i;return u+Math.min(1*this.stallCount,d)}},{key:"liveSyncPosition",get:function(){var t=this.estimateLiveEdge(),e=this.targetLatency,r=this.levelDetails;if(null===t||null===e||null===r)return null;var i=r.edge,a=t-e-this.edgeStalled,n=i-r.totalduration,s=i-(this.config.lowLatencyMode&&r.partTarget||r.targetduration);return Math.min(Math.max(n,a),s)}},{key:"drift",get:function(){var t=this.levelDetails;return null===t?1:t.drift}},{key:"edgeStalled",get:function(){var t=this.levelDetails;if(null===t)return 0;var e=3*(this.config.lowLatencyMode&&t.partTarget||t.targetduration);return Math.max(t.age-e,0)}},{key:"forwardBufferLength",get:function(){var t=this.media,e=this.levelDetails;if(!t||!e)return 0;var r=t.buffered.length;return r?t.buffered.end(r-1):e.edge-this.currentTime}}])&&F(e.prototype,r),i&&F(e,i),t}();function B(t,e){for(var r=0;rt.sn?(n=r-t.start,i=t):(n=t.start-r,i=e),i.duration!==n&&(i.duration=n)}else if(e.sn>t.sn){t.cc===e.cc&&t.minEndPTS?e.start=t.start+(t.minEndPTS-t.start):e.start=t.start+t.duration}else e.start=Math.max(t.start-e.duration,0)}function V(t,e,r,i,n,s){i-r<=0&&(o.b.warn("Fragment should have a positive duration",e),i=r+e.duration,s=n+e.duration);var l=r,u=i,d=e.startPTS,h=e.endPTS;if(Object(a.a)(d)){var f=Math.abs(d-r);Object(a.a)(e.deltaPTS)?e.deltaPTS=Math.max(f,e.deltaPTS):e.deltaPTS=f,l=Math.max(r,d),r=Math.min(r,d),n=Math.min(n,e.startDTS),u=Math.min(i,h),i=Math.max(i,h),s=Math.max(s,e.endDTS)}e.duration=i-r;var c=r-e.start;e.appendedPTS=i,e.start=e.startPTS=r,e.maxStartPTS=l,e.startDTS=n,e.endPTS=i,e.minEndPTS=u,e.endDTS=s;var v,g=e.sn;if(!t||gt.endSN)return 0;var p=g-t.startSN,m=t.fragments;for(m[p]=e,v=p;v>0;v--)K(m[v],m[v-1]);for(v=p;v=0;n--){var s=i[n].initSegment;if(s){r=s;break}}t.fragmentHint&&delete t.fragmentHint.endPTS;var l,u=0;(function(t,e,r){for(var i=e.skippedSegments,a=Math.max(t.startSN,e.startSN)-e.startSN,n=(t.fragmentHint?1:0)+(i?e.endSN:Math.min(t.endSN,e.endSN))-e.startSN,s=e.startSN-t.startSN,o=e.fragmentHint?e.fragments.concat(e.fragmentHint):e.fragments,l=t.fragmentHint?t.fragments.concat(t.fragmentHint):t.fragments,u=a;u<=n;u++){var d=l[s+u],h=o[u];i&&!h&&u=i.length||function(t,e){if(e){for(var r=t.fragments,i=t.skippedSegments;ie.partTarget&&(l+=1)}if(Object(a.a)(o))return new U(o,Object(a.a)(l)?l:void 0,M.No)}}},e.loadPlaylist=function(t){},e.shouldLoadTrack=function(t){return this.canLoad&&t&&!!t.url&&(!t.details||t.details.live)},e.playlistLoaded=function(t,e,r){var i=this,a=e.details,n=e.stats,s=n.loading.end?Math.max(0,self.performance.now()-n.loading.end):0;if(a.advancedDateTime=Date.now()-s,a.live||null!=r&&r.live){if(a.reloaded(r),r&&this.log("live playlist "+t+" "+(a.advanced?"REFRESHED "+a.lastPartSn+"-"+a.lastPartIndex:"MISSED")),r&&a.fragments.length>0&&W(r,a),!this.canLoad||!a.live)return;var o,l=void 0,u=void 0;if(a.canBlockReload&&a.endSN&&a.advanced){var d=this.hls.config.lowLatencyMode,h=a.lastPartSn,f=a.endSN,c=a.lastPartIndex,v=h===f;-1!==c?(l=v?f+1:h,u=v?d?0:c:c+1):l=f+1;var g=a.age,p=g+a.ageHeader,m=Math.min(p-a.partTarget,1.5*a.targetduration);if(m>0){if(r&&m>r.tuneInGoal)this.warn("CDN Tune-in goal increased from: "+r.tuneInGoal+" to: "+m+" with playlist age: "+a.age),m=0;else{var y=Math.floor(m/a.targetduration);if(l+=y,void 0!==u)u+=Math.round(m%a.targetduration/a.partTarget);this.log("CDN Tune-in age: "+a.ageHeader+"s last advanced "+g.toFixed(2)+"s goal: "+m+" skip sn "+y+" to part "+u)}a.tuneInGoal=m}if(o=this.getDeliveryDirectives(a,e.deliveryDirectives,l,u),d||!v)return void this.loadPlaylist(o)}else o=this.getDeliveryDirectives(a,e.deliveryDirectives,l,u);var b=function(t,e){var r,i=1e3*t.levelTargetDuration,a=i/2,n=t.age,s=n>0&&n<3*i,o=e.loading.end-e.loading.start,l=t.availabilityDelay;if(!1===t.updated)if(s){var u=333*t.misses;r=Math.max(Math.min(a,2*o),u),t.availabilityDelay=(t.availabilityDelay||0)+r}else r=a;else s?(l=Math.min(l||i/2,n),t.availabilityDelay=l,r=l+i-n):r=i-o;return Math.round(r)}(a,n);void 0!==l&&a.canBlockReload&&(b-=a.partTarget||1),this.log("reload live playlist "+t+" in "+Math.round(b)+" ms"),this.timer=self.setTimeout((function(){return i.loadPlaylist(o)}),b)}else this.clearTimer()},e.getDeliveryDirectives=function(t,e,r,i){var a=function(t,e){var r=t.canSkipUntil,i=t.canSkipDateRanges,a=t.endSN;return r&&(void 0!==e?e-a:0)-1&&null!==(e=t.context)&&void 0!==e&&e.deliveryDirectives)this.warn("retry playlist loading #"+this.retryCount+' after "'+t.details+'"'),this.loadPlaylist();else{var n=Math.min(Math.pow(2,this.retryCount)*i.levelLoadingRetryDelay,i.levelLoadingMaxRetryTimeout);this.timer=self.setTimeout((function(){return r.loadPlaylist()}),n),this.warn("retry playlist loading #"+this.retryCount+" in "+n+' ms after "'+t.details+'"')}else this.warn('cannot recover from error "'+t.details+'"'),this.clearTimer(),t.fatal=!0;return a},t}();function q(){return(q=Object.assign||function(t){for(var e=1;e0){r=a[0].bitrate,a.sort((function(t,e){return t.bitrate-e.bitrate})),this._levels=a;for(var c=0;cthis.hls.config.fragLoadingMaxRetry&&(n=r.frag.level)):n=r.frag.level}break;case s.a.LEVEL_LOAD_ERROR:case s.a.LEVEL_LOAD_TIMEOUT:i&&(i.deliveryDirectives&&(l=!1),n=i.level),o=!0;break;case s.a.REMUX_ALLOC_ERROR:n=r.level,o=!0}void 0!==n&&this.recoverLevel(r,n,o,l)}}},u.recoverLevel=function(t,e,r,i){var a=t.details,n=this._levels[e];if(n.loadError++,r){if(!this.retryLoadingOrFail(t))return void(this.currentLevelIndex=-1);t.levelRetry=!0}if(i){var s=n.url.length;if(s>1&&n.loadError1){var i=(e.urlId+1)%r;this.warn("Switching to redundant URL-id "+i),this._levels.forEach((function(t){t.urlId=i})),this.level=t}},u.onFragLoaded=function(t,e){var r=e.frag;if(void 0!==r&&r.type===_.b.MAIN){var i=this._levels[r.level];void 0!==i&&(i.fragmentError=0,i.loadError=0)}},u.onLevelLoaded=function(t,e){var r,i,a=e.level,n=e.details,s=this._levels[a];if(!s)return this.warn("Invalid level index "+a),void(null!==(i=e.deliveryDirectives)&&void 0!==i&&i.skip&&(n.deltaUpdateFailed=!0));a===this.currentLevelIndex?(0===s.fragmentError&&(s.loadError=0,this.retryCount=0),this.playlistLoaded(a,e,s.details)):null!==(r=e.deliveryDirectives)&&void 0!==r&&r.skip&&(n.deltaUpdateFailed=!0)},u.onAudioTrackSwitched=function(t,e){var r=this.hls.levels[this.currentLevelIndex];if(r&&r.audioGroupIds){for(var i=-1,a=this.hls.audioTracks[e.id].groupId,n=0;n0){var i=r.urlId,a=r.url[i];if(t)try{a=t.addDirectives(a)}catch(t){this.warn("Could not construct new URL with HLS Delivery Directives: "+t)}this.log("Attempt loading level index "+e+(t?" at sn "+t.msn+" part "+t.part:"")+" with URL-id "+i+" "+a),this.clearTimer(),this.hls.trigger(n.a.LEVEL_LOADING,{url:a,level:e,id:i,deliveryDirectives:t||null})}},u.removeLevel=function(t,e){var r=function(t,r){return r!==e},i=this._levels.filter((function(i,a){return a!==t||i.url.length>1&&void 0!==e&&(i.url=i.url.filter(r),i.audioGroupIds&&(i.audioGroupIds=i.audioGroupIds.filter(r)),i.textGroupIds&&(i.textGroupIds=i.textGroupIds.filter(r)),i.urlId=0,!0)})).map((function(t,e){var r=t.details;return null!=r&&r.fragments&&r.fragments.forEach((function(t){t.level=e})),t}));this._levels=i,this.hls.trigger(n.a.LEVELS_UPDATED,{levels:i})},a=i,(o=[{key:"levels",get:function(){return 0===this._levels.length?null:this._levels}},{key:"level",get:function(){return this.currentLevelIndex},set:function(t){var e,r=this._levels;if(0!==r.length&&(this.currentLevelIndex!==t||null===(e=r[t])||void 0===e||!e.details)){if(t<0||t>=r.length){var i=t<0;if(this.hls.trigger(n.a.ERROR,{type:s.b.OTHER_ERROR,details:s.a.LEVEL_SWITCH_ERROR,level:t,fatal:i,reason:"invalid level idx"}),i)return;t=Math.min(t,r.length-1)}this.clearTimer();var a=this.currentLevelIndex,o=r[a],l=r[t];this.log("switching to level "+t+" from "+a),this.currentLevelIndex=t;var u=q({},l,{level:t,maxBitrate:l.maxBitrate,uri:l.uri,urlId:l.urlId});delete u._urlId,this.hls.trigger(n.a.LEVEL_SWITCHING,u);var d=l.details;if(!d||d.live){var h=this.switchParams(l.uri,null==o?void 0:o.details);this.loadPlaylist(h)}}}},{key:"manualLevel",get:function(){return this.manualLevelIndex},set:function(t){this.manualLevelIndex=t,void 0===this._startLevel&&(this._startLevel=t),-1!==t&&(this.level=t)}},{key:"firstLevel",get:function(){return this._firstLevel},set:function(t){this._firstLevel=t}},{key:"startLevel",get:function(){if(void 0===this._startLevel){var t=this.hls.config.startLevel;return void 0!==t?t:this._firstLevel}return this._startLevel},set:function(t){this._startLevel=t}},{key:"nextLoadLevel",get:function(){return-1!==this.manualLevelIndex?this.manualLevelIndex:this.hls.nextAutoLevel},set:function(t){this.level=t,-1===this.manualLevelIndex&&(this.hls.nextAutoLevel=t)}}])&&z(a.prototype,o),l&&z(a,l),i}(X);!function(t){t.NOT_LOADED="NOT_LOADED",t.BACKTRACKED="BACKTRACKED",t.APPENDING="APPENDING",t.PARTIAL="PARTIAL",t.OK="OK"}($||($={}));var tt=function(){function t(t){this.activeFragment=null,this.activeParts=null,this.fragments=Object.create(null),this.timeRanges=Object.create(null),this.bufferPadding=.2,this.hls=void 0,this.hls=t,this._registerListeners()}var e=t.prototype;return e._registerListeners=function(){var t=this.hls;t.on(n.a.BUFFER_APPENDED,this.onBufferAppended,this),t.on(n.a.FRAG_BUFFERED,this.onFragBuffered,this),t.on(n.a.FRAG_LOADED,this.onFragLoaded,this)},e._unregisterListeners=function(){var t=this.hls;t.off(n.a.BUFFER_APPENDED,this.onBufferAppended,this),t.off(n.a.FRAG_BUFFERED,this.onFragBuffered,this),t.off(n.a.FRAG_LOADED,this.onFragLoaded,this)},e.destroy=function(){this._unregisterListeners(),this.fragments=this.timeRanges=null},e.getAppendedFrag=function(t,e){if(e===_.b.MAIN){var r=this.activeFragment,i=this.activeParts;if(!r)return null;if(i)for(var a=i.length;a--;){var n=i[a],s=n?n.end:r.appendedPTS;if(n.start<=t&&void 0!==s&&t<=s)return a>9&&(this.activeParts=i.slice(a-9)),n}else if(r.start<=t&&void 0!==r.appendedPTS&&t<=r.appendedPTS)return r}return this.getBufferedFrag(t,e)},e.getBufferedFrag=function(t,e){for(var r=this.fragments,i=Object.keys(r),a=i.length;a--;){var n=r[i[a]];if((null==n?void 0:n.body.type)===e&&n.buffered){var s=n.body;if(s.start<=t&&t<=s.end)return s}}return null},e.detectEvictedFragments=function(t,e,r){var i=this;Object.keys(this.fragments).forEach((function(a){var n=i.fragments[a];if(n)if(n.buffered){var s=n.range[t];s&&s.time.some((function(t){var r=!i.isTimeBuffered(t.startPTS,t.endPTS,e);return r&&i.removeFragment(n.body),r}))}else n.body.type===r&&i.removeFragment(n.body)}))},e.detectPartialFragments=function(t){var e=this,r=this.timeRanges,i=t.frag,a=t.part;if(r&&"initSegment"!==i.sn){var n=rt(i),s=this.fragments[n];s&&(Object.keys(r).forEach((function(t){var n=i.elementaryStreams[t];if(n){var o=r[t],l=null!==a||!0===n.partial;s.range[t]=e.getBufferedTimes(i,a,l,o)}})),s.backtrack=s.loaded=null,Object.keys(s.range).length?s.buffered=!0:this.removeFragment(s.body))}},e.fragBuffered=function(t){var e=rt(t),r=this.fragments[e];r&&(r.backtrack=r.loaded=null,r.buffered=!0)},e.getBufferedTimes=function(t,e,r,i){for(var a={time:[],partial:r},n=e?e.start:t.start,s=e?e.end:t.end,o=t.minEndPTS||s,l=t.maxStartPTS||n,u=0;u=d&&o<=h){a.time.push({startPTS:Math.max(n,i.start(u)),endPTS:Math.min(s,i.end(u))});break}if(nd)a.partial=!0,a.time.push({startPTS:Math.max(n,i.start(u)),endPTS:Math.min(s,i.end(u))});else if(s<=d)break}return a},e.getPartialFragment=function(t){var e,r,i,a=null,n=0,s=this.bufferPadding,o=this.fragments;return Object.keys(o).forEach((function(l){var u=o[l];u&&et(u)&&(r=u.body.start-s,i=u.body.end+s,t>=r&&t<=i&&(e=Math.min(t-r,i-t),n<=e&&(a=u.body,n=e)))})),a},e.getState=function(t){var e=rt(t),r=this.fragments[e];return r?r.buffered?et(r)?$.PARTIAL:$.OK:r.backtrack?$.BACKTRACKED:$.APPENDING:$.NOT_LOADED},e.backtrack=function(t,e){var r=rt(t),i=this.fragments[r];if(!i||i.backtrack)return null;var a=i.backtrack=e||i.loaded;return i.loaded=null,a},e.getBacktrackData=function(t){var e=rt(t),r=this.fragments[e];if(r){var i,a=r.backtrack;if(null!=a&&null!==(i=a.payload)&&void 0!==i&&i.byteLength)return a;this.removeFragment(t)}return null},e.isTimeBuffered=function(t,e,r){for(var i,a,n=0;n=i&&e<=a)return!0;if(e<=i)return!1}return!1},e.onFragLoaded=function(t,e){var r=e.frag,i=e.part;if("initSegment"!==r.sn&&!r.bitrateTest&&!i){var a=rt(r);this.fragments[a]={body:r,loaded:e,backtrack:null,buffered:!1,range:Object.create(null)}}},e.onBufferAppended=function(t,e){var r=this,i=e.frag,a=e.part,n=e.timeRanges;if(i.type===_.b.MAIN)if(this.activeFragment=i,a){var s=this.activeParts;s||(this.activeParts=s=[]),s.push(a)}else this.activeParts=null;this.timeRanges=n,Object.keys(n).forEach((function(t){var e=n[t];if(r.detectEvictedFragments(t,e),!a)for(var s=0;st&&i.removeFragment(s)}}))},e.removeFragment=function(t){var e=rt(t);t.stats.loaded=0,t.clearElementaryStreamInfo(),delete this.fragments[e]},e.removeAllFragments=function(){this.fragments=Object.create(null),this.activeFragment=null,this.activeParts=null},t}();function et(t){var e,r;return t.buffered&&((null===(e=t.range.video)||void 0===e?void 0:e.partial)||(null===(r=t.range.audio)||void 0===r?void 0:r.partial))}function rt(t){return t.type+"_"+t.level+"_"+t.urlId+"_"+t.sn}var it=function(){function t(){this._boundTick=void 0,this._tickTimer=null,this._tickInterval=null,this._tickCallCount=0,this._boundTick=this.tick.bind(this)}var e=t.prototype;return e.destroy=function(){this.onHandlerDestroying(),this.onHandlerDestroyed()},e.onHandlerDestroying=function(){this.clearNextTick(),this.clearInterval()},e.onHandlerDestroyed=function(){},e.hasInterval=function(){return!!this._tickInterval},e.hasNextTick=function(){return!!this._tickTimer},e.setInterval=function(t){return!this._tickInterval&&(this._tickInterval=self.setInterval(this._boundTick,t),!0)},e.clearInterval=function(){return!!this._tickInterval&&(self.clearInterval(this._tickInterval),this._tickInterval=null,!0)},e.clearNextTick=function(){return!!this._tickTimer&&(self.clearTimeout(this._tickTimer),this._tickTimer=null,!0)},e.tick=function(){this._tickCallCount++,1===this._tickCallCount&&(this.doTick(),this._tickCallCount>1&&this.tickImmediate(),this._tickCallCount=0)},e.tickImmediate=function(){this.clearNextTick(),this._tickTimer=self.setTimeout(this._boundTick,0)},e.doTick=function(){},t}(),at={length:0,start:function(){return 0},end:function(){return 0}},nt=function(){function t(){}return t.isBuffered=function(e,r){try{if(e)for(var i=t.getBuffered(e),a=0;a=i.start(a)&&r<=i.end(a))return!0}catch(t){}return!1},t.bufferInfo=function(e,r,i){try{if(e){var a,n=t.getBuffered(e),s=[];for(a=0;as&&(i[n-1].end=t[a].end):i.push(t[a])}else i.push(t[a])}else i=t;for(var o,l=0,u=e,d=e,h=0;h=f&&er.startCC||t&&t.cc0)r=a+1;else{if(!(s<0))return n;i=a-1}}return null}};function ht(t,e,r){void 0===t&&(t=0),void 0===e&&(e=0);var i=Math.min(e,r.duration+(r.deltaPTS?r.deltaPTS:0));return r.start+r.duration-i<=t?1:r.start-i>t&&r.start?-1:0}function ft(t,e,r){var i=1e3*Math.min(e,r.duration+(r.deltaPTS?r.deltaPTS:0));return(r.endProgramDateTime||0)-i>t}function ct(t){var e="function"==typeof Map?new Map:void 0;return(ct=function(t){if(null===t||(r=t,-1===Function.toString.call(r).indexOf("[native code]")))return t;var r;if("function"!=typeof t)throw new TypeError("Super expression must either be null or a function");if(void 0!==e){if(e.has(t))return e.get(t);e.set(t,i)}function i(){return vt(t,arguments,mt(this).constructor)}return i.prototype=Object.create(t.prototype,{constructor:{value:i,enumerable:!1,writable:!0,configurable:!0}}),pt(i,t)})(t)}function vt(t,e,r){return(vt=gt()?Reflect.construct:function(t,e,r){var i=[null];i.push.apply(i,e);var a=new(Function.bind.apply(t,i));return r&&pt(a,r.prototype),a}).apply(null,arguments)}function gt(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(t){return!1}}function pt(t,e){return(pt=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}function mt(t){return(mt=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}var yt=Math.pow(2,17),bt=function(){function t(t){this.config=void 0,this.loader=null,this.partLoadTimeout=-1,this.config=t}var e=t.prototype;return e.destroy=function(){this.loader&&(this.loader.destroy(),this.loader=null)},e.abort=function(){this.loader&&this.loader.abort()},e.load=function(t,e){var r=this,i=t.url;if(!i)return Promise.reject(new Et({type:s.b.NETWORK_ERROR,details:s.a.FRAG_LOAD_ERROR,fatal:!1,frag:t,networkDetails:null},"Fragment does not have a "+(i?"part list":"url")));this.abort();var a=this.config,n=a.fLoader,o=a.loader;return new Promise((function(i,l){r.loader&&r.loader.destroy();var u=r.loader=t.loader=n?new n(a):new o(a),d=Tt(t),h={timeout:a.fragLoadingTimeOut,maxRetry:0,retryDelay:0,maxRetryDelay:a.fragLoadingMaxRetryTimeout,highWaterMark:yt};t.stats=u.stats,u.load(d,h,{onSuccess:function(e,a,n,s){r.resetLoader(t,u),i({frag:t,part:null,payload:e.data,networkDetails:s})},onError:function(e,i,a){r.resetLoader(t,u),l(new Et({type:s.b.NETWORK_ERROR,details:s.a.FRAG_LOAD_ERROR,fatal:!1,frag:t,response:e,networkDetails:a}))},onAbort:function(e,i,a){r.resetLoader(t,u),l(new Et({type:s.b.NETWORK_ERROR,details:s.a.INTERNAL_ABORTED,fatal:!1,frag:t,networkDetails:a}))},onTimeout:function(e,i,a){r.resetLoader(t,u),l(new Et({type:s.b.NETWORK_ERROR,details:s.a.FRAG_LOAD_TIMEOUT,fatal:!1,frag:t,networkDetails:a}))},onProgress:function(r,i,a,n){e&&e({frag:t,part:null,payload:a,networkDetails:n})}})}))},e.loadPart=function(t,e,r){var i=this;this.abort();var a=this.config,n=a.fLoader,o=a.loader;return new Promise((function(l,u){i.loader&&i.loader.destroy();var d=i.loader=t.loader=n?new n(a):new o(a),h=Tt(t,e),f={timeout:a.fragLoadingTimeOut,maxRetry:0,retryDelay:0,maxRetryDelay:a.fragLoadingMaxRetryTimeout,highWaterMark:yt};e.stats=d.stats,d.load(h,f,{onSuccess:function(a,n,s,o){i.resetLoader(t,d),i.updateStatsFromPart(t,e);var u={frag:t,part:e,payload:a.data,networkDetails:o};r(u),l(u)},onError:function(r,a,n){i.resetLoader(t,d),u(new Et({type:s.b.NETWORK_ERROR,details:s.a.FRAG_LOAD_ERROR,fatal:!1,frag:t,part:e,response:r,networkDetails:n}))},onAbort:function(r,a,n){t.stats.aborted=e.stats.aborted,i.resetLoader(t,d),u(new Et({type:s.b.NETWORK_ERROR,details:s.a.INTERNAL_ABORTED,fatal:!1,frag:t,part:e,networkDetails:n}))},onTimeout:function(r,a,n){i.resetLoader(t,d),u(new Et({type:s.b.NETWORK_ERROR,details:s.a.FRAG_LOAD_TIMEOUT,fatal:!1,frag:t,part:e,networkDetails:n}))}})}))},e.updateStatsFromPart=function(t,e){var r=t.stats,i=e.stats,a=i.total;if(r.loaded+=i.loaded,a){var n=Math.round(t.duration/e.duration),s=Math.min(Math.round(r.loaded/a),n),o=(n-s)*Math.round(r.loaded/s);r.total=r.loaded+o}else r.total=Math.max(r.loaded,r.total);var l=r.loading,u=i.loading;l.start?l.first+=u.first-u.start:(l.start=u.start,l.first=u.first),l.end=u.end},e.resetLoader=function(t,e){t.loader=null,this.loader===e&&(self.clearTimeout(this.partLoadTimeout),this.loader=null),e.destroy()},t}();function Tt(t,e){void 0===e&&(e=null);var r=e||t,i={frag:t,part:e,responseType:"arraybuffer",url:r.url,headers:{},rangeStart:0,rangeEnd:0},n=r.byteRangeStartOffset,s=r.byteRangeEndOffset;return Object(a.a)(n)&&Object(a.a)(s)&&(i.rangeStart=n,i.rangeEnd=s),i}var Et=function(t){var e,r;function i(e){for(var r,i=arguments.length,a=new Array(i>1?i-1:0),n=1;n=e.endSN&&!t.nextStart){var a=e.partList;if(null!=a&&a.length){var n=a[a.length-1];return nt.isBuffered(this.media,n.start+n.duration/2)}var s=i.getState(r);return s===$.PARTIAL||s===$.OK}return!1},f.onMediaAttached=function(t,e){var r=this.media=this.mediaBuffer=e.media;this.onvseeking=this.onMediaSeeking.bind(this),this.onvended=this.onMediaEnded.bind(this),r.addEventListener("seeking",this.onvseeking),r.addEventListener("ended",this.onvended);var i=this.config;this.levels&&i.autoStartLoad&&this.state===Dt&&this.startLoad(i.startPosition)},f.onMediaDetaching=function(){var t=this.media;null!=t&&t.ended&&(this.log("MSE detaching and video ended, reset startPosition"),this.startPosition=this.lastCurrentTime=0),t&&(t.removeEventListener("seeking",this.onvseeking),t.removeEventListener("ended",this.onvended),this.onvseeking=this.onvended=null),this.media=this.mediaBuffer=null,this.loadedmetadata=!1,this.fragmentTracker.removeAllFragments(),this.stopLoad()},f.onMediaSeeking=function(){var t=this.config,e=this.fragCurrent,r=this.media,i=this.mediaBuffer,n=this.state,s=r?r.currentTime:0,o=nt.bufferInfo(i||r,s,t.maxBufferHole);if(this.log("media seeking to "+(Object(a.a)(s)?s.toFixed(3):s)+", state: "+n),n===Pt)this.resetLoadingState();else if(e&&!o.len){var l=t.maxFragLookUpTolerance,u=e.start-l,d=s>e.start+e.duration+l;(s0&&s&&s.key&&s.iv&&"AES-128"===s.method){var o=self.performance.now();return e.decrypter.webCryptoDecrypt(new Uint8Array(a),s.key.buffer,s.iv.buffer).then((function(e){var a=self.performance.now();return i.trigger(n.a.FRAG_DECRYPTED,{frag:t,payload:e,stats:{tstart:o,tdecrypt:a}}),r.payload=e,r}))}return r})).then((function(r){var i=e.fragCurrent,a=e.hls,s=e.levels;if(!s)throw new Error("init load aborted, missing levels");s[t.level].details;var o=t.stats;e.state=_t,e.fragLoadError=0,t.data=new Uint8Array(r.payload),o.parsing.start=o.buffering.start=self.performance.now(),o.parsing.end=o.buffering.end=self.performance.now(),r.frag===i&&a.trigger(n.a.FRAG_BUFFERED,{stats:o,frag:i,part:null,id:t.type}),e.tick()})).catch((function(r){e.warn(r),e.resetFragmentLoading(t)}))},f.fragContextChanged=function(t){var e=this.fragCurrent;return!t||!e||t.level!==e.level||t.sn!==e.sn||t.urlId!==e.urlId},f.fragBufferedComplete=function(t,e){var r=this.mediaBuffer?this.mediaBuffer:this.media;this.log("Buffered "+t.type+" sn: "+t.sn+(e?" part: "+e.index:"")+" of "+("[stream-controller]"===this.logPrefix?"level":"track")+" "+t.level+" "+Lt.toString(nt.getBuffered(r))),this.state=_t,this.tick()},f._handleFragmentLoadComplete=function(t){var e=this.transmuxer;if(e){var r=t.frag,i=t.part,a=t.partsLoaded,n=!a||0===a.length||a.some((function(t){return!t})),s=new st(r.level,r.sn,r.stats.chunkCount+1,0,i?i.index:-1,!n);e.flush(s)}},f._handleFragmentLoadProgress=function(t){},f._doFragLoad=function(t,e,r,i){var s=this;if(void 0===r&&(r=null),!this.levels)throw new Error("frag load aborted, missing levels");if(r=Math.max(t.start,r||0),this.config.lowLatencyMode&&e){var o=e.partList;if(o&&i){r>t.end&&e.fragmentHint&&(t=e.fragmentHint);var l=this.getNextPart(o,t,r);if(l>-1){var u=o[l];return this.log("Loading part sn: "+t.sn+" p: "+u.index+" cc: "+t.cc+" of playlist ["+e.startSN+"-"+e.endSN+"] parts [0-"+l+"-"+(o.length-1)+"] "+("[stream-controller]"===this.logPrefix?"level":"track")+": "+t.level+", target: "+parseFloat(r.toFixed(3))),this.nextLoadPosition=u.start+u.duration,this.state=xt,this.hls.trigger(n.a.FRAG_LOADING,{frag:t,part:o[l],targetBufferTime:r}),this.doFragPartsLoad(t,o,l,i).catch((function(t){return s.handleFragLoadError(t)}))}if(!t.url||this.loadedEndOfParts(o,r))return Promise.resolve(null)}}return this.log("Loading fragment "+t.sn+" cc: "+t.cc+" "+(e?"of ["+e.startSN+"-"+e.endSN+"] ":"")+("[stream-controller]"===this.logPrefix?"level":"track")+": "+t.level+", target: "+parseFloat(r.toFixed(3))),Object(a.a)(t.sn)&&!this.bitrateTest&&(this.nextLoadPosition=t.start+t.duration),this.state=xt,this.hls.trigger(n.a.FRAG_LOADING,{frag:t,targetBufferTime:r}),this.fragmentLoader.load(t,i).catch((function(t){return s.handleFragLoadError(t)}))},f.doFragPartsLoad=function(t,e,r,i){var a=this;return new Promise((function(s,o){var l=[];!function r(u){var d=e[u];a.fragmentLoader.loadPart(t,d,i).then((function(i){l[d.index]=i;var o=i.part;a.hls.trigger(n.a.FRAG_LOADED,i);var h=e[u+1];if(!h||h.fragment!==t)return s({frag:t,part:o,partsLoaded:l});r(u+1)})).catch(o)}(r)}))},f.handleFragLoadError=function(t){var e=t.data;return e&&e.details===s.a.INTERNAL_ABORTED?this.handleFragLoadAborted(e.frag,e.part):this.hls.trigger(n.a.ERROR,e),null},f._handleTransmuxerFlush=function(t){var e=this.getCurrentContext(t);if(e&&this.state===wt){var r=e.frag,i=e.part,a=e.level,n=self.performance.now();r.stats.parsing.end=n,i&&(i.stats.parsing.end=n),this.updateLevelTiming(r,i,a,t.partial)}else this.fragCurrent||(this.state=_t)},f.getCurrentContext=function(t){var e=this.levels,r=t.level,i=t.sn,a=t.part;if(!e||!e[r])return this.warn("Levels object was unset while buffering fragment "+i+" of level "+r+". The current chunk will not be buffered."),null;var n=e[r],s=a>-1?function(t,e,r){if(!t||!t.details)return null;var i=t.details.partList;if(i)for(var a=i.length;a--;){var n=i[a];if(n.index===r&&n.fragment.sn===e)return n}return null}(n,i,a):null,o=s?s.fragment:function(t,e,r){if(!t||!t.details)return null;var i=t.details,a=i.fragments[e-i.startSN];return a||((a=i.fragmentHint)&&a.sn===e?a:en&&this.flushMainBuffer(s,t.start)}else this.flushMainBuffer(0,t.start)},f.getFwdBufferInfo=function(t,e){var r=this.config,i=this.getLoadPosition();if(!Object(a.a)(i))return null;var n=nt.bufferInfo(t,i,r.maxBufferHole);if(0===n.len&&void 0!==n.nextStart){var s=this.fragmentTracker.getBufferedFrag(i,e);if(s&&n.nextStart=r&&(e.maxMaxBufferLength/=2,this.warn("Reduce max buffer length to "+e.maxMaxBufferLength+"s"),!0)},f.getNextFragment=function(t,e){var r,i,a=e.fragments,n=a.length;if(!n)return null;var s,o=this.config,l=a[0].start;if(e.live){var u=o.initialLiveManifestSize;if(n-1&&rr.start&&r.loaded},f.getInitialLiveFragment=function(t,e){var r=this.fragPrevious,i=null;if(r){if(t.hasProgramDateTime&&(this.log("Live playlist, switching playlist, load frag with same PDT: "+r.programDateTime),i=function(t,e,r){if(null===e||!Array.isArray(t)||!t.length||!Object(a.a)(e))return null;if(e<(t[0].programDateTime||0))return null;if(e>=(t[t.length-1].endProgramDateTime||0))return null;r=r||0;for(var i=0;i=t.startSN&&n<=t.endSN){var s=e[n-t.startSN];r.cc===s.cc&&(i=s,this.log("Live playlist, switching playlist, load frag with next SN: "+i.sn))}i||(i=function(t,e){return dt.search(t,(function(t){return t.cce?-1:0}))}(e,r.cc))&&this.log("Live playlist, switching playlist, load frag with same CC: "+i.sn)}}else{var o=this.hls.liveSyncPosition;null!==o&&(i=this.getFragmentAtPosition(o,this.bitrateTest?t.fragmentEnd:t.edge,t))}return i},f.getFragmentAtPosition=function(t,e,r){var i,a=this.config,n=this.fragPrevious,s=r.fragments,o=r.endSN,l=r.fragmentHint,u=a.maxFragLookUpTolerance,d=!!(a.lowLatencyMode&&r.partList&&l);(d&&l&&!this.bitrateTest&&(s=s.concat(l),o=l.sn),te-u?0:u):i=s[s.length-1];if(i){var h=i.sn-r.startSN,f=n&&i.level===n.level,c=s[h+1];if(this.fragmentTracker.getState(i)===$.BACKTRACKED){i=null;for(var v=h;s[v]&&this.fragmentTracker.getState(s[v])===$.BACKTRACKED;)i=n?s[v--]:s[--v];i||(i=c)}else n&&i.sn===n.sn&&!d&&f&&(i.sn=n-e.maxFragLookUpTolerance&&a<=s;if(null!==i&&r.duration>i&&(a"+t.startSN+" prev-sn: "+(n?n.sn:"na")+" fragments: "+o),h}return l},f.waitForCdnTuneIn=function(t){return t.live&&t.canBlockReload&&t.tuneInGoal>Math.max(t.partHoldBack,3*t.partTarget)},f.setStartPosition=function(t,e){var r=this.startPosition;if(r"+t))}}])&&At(u.prototype,d),h&&At(u,h),i}(it);function Bt(){return self.MediaSource||self.WebKitMediaSource}function Ut(){return self.SourceBuffer||self.WebKitSourceBuffer}var Gt=r(17),jt=r(9),Ht=r(13),Kt=Bt()||{isTypeSupported:function(){return!1}},Vt=function(){function t(t,e,r,i){var a=this;this.hls=void 0,this.id=void 0,this.observer=void 0,this.frag=null,this.part=null,this.worker=void 0,this.onwmsg=void 0,this.transmuxer=null,this.onTransmuxComplete=void 0,this.onFlush=void 0,this.hls=t,this.id=e,this.onTransmuxComplete=r,this.onFlush=i;var l=t.config,u=function(e,r){(r=r||{}).frag=a.frag,r.id=a.id,t.trigger(e,r)};this.observer=new Ht.EventEmitter,this.observer.on(n.a.FRAG_DECRYPTED,u),this.observer.on(n.a.ERROR,u);var d={mp4:Kt.isTypeSupported("video/mp4"),mpeg:Kt.isTypeSupported("audio/mpeg"),mp3:Kt.isTypeSupported('audio/mp4; codecs="mp3"')},h=navigator.vendor;if(l.enableWorker&&"undefined"!=typeof Worker){var f;o.b.log("demuxing in webworker");try{f=this.worker=Gt(18),this.onwmsg=this.onWorkerMessage.bind(this),f.addEventListener("message",this.onwmsg),f.onerror=function(e){t.trigger(n.a.ERROR,{type:s.b.OTHER_ERROR,details:s.a.INTERNAL_EXCEPTION,fatal:!0,event:"demuxerWorker",error:new Error(e.message+" ("+e.filename+":"+e.lineno+")")})},f.postMessage({cmd:"init",typeSupported:d,vendor:h,id:e,config:JSON.stringify(l)})}catch(t){o.b.warn("Error in worker:",t),o.b.error("Error while initializing DemuxerWorker, fallback to inline"),f&&self.URL.revokeObjectURL(f.objectURL),this.transmuxer=new jt.c(this.observer,d,l,h,e),this.worker=null}}else this.transmuxer=new jt.c(this.observer,d,l,h,e)}var e=t.prototype;return e.destroy=function(){var t=this.worker;if(t)t.removeEventListener("message",this.onwmsg),t.terminate(),this.worker=null;else{var e=this.transmuxer;e&&(e.destroy(),this.transmuxer=null)}var r=this.observer;r&&r.removeAllListeners(),this.observer=null},e.push=function(t,e,r,i,a,n,s,l,u,d){var h,f,c=this;u.transmuxing.start=self.performance.now();var v=this.transmuxer,g=this.worker,p=n?n.start:a.start,m=a.decryptdata,y=this.frag,b=!(y&&a.cc===y.cc),T=!(y&&u.level===y.level),E=y?u.sn-y.sn:-1,S=this.part?u.part-this.part.index:1,L=!T&&(1===E||0===E&&1===S),A=self.performance.now();(T||E||0===a.stats.parsing.start)&&(a.stats.parsing.start=A),!n||!S&&L||(n.stats.parsing.start=A);var R=!(y&&(null===(h=a.initSegment)||void 0===h?void 0:h.url)===(null===(f=y.initSegment)||void 0===f?void 0:f.url)),D=new jt.b(b,L,l,T,p,R);if(!L||b||R){o.b.log("[transmuxer-interface, "+a.type+"]: Starting new transmux session for sn: "+u.sn+" p: "+u.part+" level: "+u.level+" id: "+u.id+"\n discontinuity: "+b+"\n trackSwitch: "+T+"\n contiguous: "+L+"\n accurateTimeOffset: "+l+"\n timeOffset: "+p+"\n initSegmentChange: "+R);var _=new jt.a(r,i,e,s,d);this.configureTransmuxer(_)}if(this.frag=a,this.part=n,g)g.postMessage({cmd:"demux",data:t,decryptdata:m,chunkMeta:u,state:D},t instanceof ArrayBuffer?[t]:[]);else if(v){var k=v.push(t,m,u,D);Object(jt.d)(k)?k.then((function(t){c.handleTransmuxComplete(t)})):this.handleTransmuxComplete(k)}},e.flush=function(t){var e=this;t.transmuxing.start=self.performance.now();var r=this.transmuxer,i=this.worker;if(i)i.postMessage({cmd:"flush",chunkMeta:t});else if(r){var a=r.flush(t);Object(jt.d)(a)?a.then((function(r){e.handleFlushResult(r,t)})):this.handleFlushResult(a,t)}},e.handleFlushResult=function(t,e){var r=this;t.forEach((function(t){r.handleTransmuxComplete(t)})),this.onFlush(e)},e.onWorkerMessage=function(t){var e=t.data,r=this.hls;switch(e.event){case"init":self.URL.revokeObjectURL(this.worker.objectURL);break;case"transmuxComplete":this.handleTransmuxComplete(e.data);break;case"flush":this.onFlush(e.data);break;default:e.data=e.data||{},e.data.frag=this.frag,e.data.id=this.id,r.trigger(e.event,e.data)}},e.configureTransmuxer=function(t){var e=this.worker,r=this.transmuxer;e?e.postMessage({cmd:"configure",config:t}):r&&r.configure(t)},e.handleTransmuxComplete=function(t){t.chunkMeta.transmuxing.end=self.performance.now(),this.onTransmuxComplete(t)},t}(),Wt=function(){function t(t,e,r,i){this.config=void 0,this.media=void 0,this.fragmentTracker=void 0,this.hls=void 0,this.nudgeRetry=0,this.stallReported=!1,this.stalled=null,this.moved=!1,this.seeking=!1,this.config=t,this.media=e,this.fragmentTracker=r,this.hls=i}var e=t.prototype;return e.destroy=function(){this.hls=this.fragmentTracker=this.media=null},e.poll=function(t){var e=this.config,r=this.media,i=this.stalled,a=r.currentTime,n=r.seeking,s=this.seeking&&!n,l=!this.seeking&&n;if(this.seeking=n,a===t){if((l||s)&&(this.stalled=null),!r.paused&&!r.ended&&0!==r.playbackRate&&nt.getBuffered(r).length){var u=nt.bufferInfo(r,a,0),d=u.len>0,h=u.nextStart||0;if(d||h){if(n){var f=u.len>2,c=!h||h-a>2&&!this.fragmentTracker.getPartialFragment(a);if(f||c)return;this.moved=!1}if(!this.moved&&null!==this.stalled){var v,g=Math.max(h,u.start||0)-a,p=this.hls.levels?this.hls.levels[this.hls.currentLevel]:null,m=(null==p||null===(v=p.details)||void 0===v?void 0:v.live)?2*p.details.targetduration:2;if(g>0&&g<=m)return void this._trySkipBufferHole(null)}var y=self.performance.now();if(null!==i){var b=y-i;!n&&b>=250&&this._reportStall(u.len);var T=nt.bufferInfo(r,a,e.maxBufferHole);this._tryFixBufferStall(T,b)}else this.stalled=y}}}else if(this.moved=!0,null!==i){if(this.stallReported){var E=self.performance.now()-i;o.b.warn("playback not stuck anymore @"+a+", after "+Math.round(E)+"ms"),this.stallReported=!1}this.stalled=null,this.nudgeRetry=0}},e._tryFixBufferStall=function(t,e){var r=this.config,i=this.fragmentTracker,a=this.media.currentTime,n=i.getPartialFragment(a);if(n&&this._trySkipBufferHole(n))return;t.len>r.maxBufferHole&&e>1e3*r.highBufferWatchdogPeriod&&(o.b.warn("Trying to nudge playhead over buffer-hole"),this.stalled=null,this._tryNudgeBuffer())},e._reportStall=function(t){var e=this.hls,r=this.media;this.stallReported||(this.stallReported=!0,o.b.warn("Playback stalling at @"+r.currentTime+" due to low buffer (buffer="+t+")"),e.trigger(n.a.ERROR,{type:s.b.MEDIA_ERROR,details:s.a.BUFFER_STALLED_ERROR,fatal:!1,buffer:t}))},e._trySkipBufferHole=function(t){for(var e=this.config,r=this.hls,i=this.media,a=i.currentTime,l=0,u=nt.getBuffered(i),d=0;d=l&&a0&&-1===t&&(this.log("Override startPosition with lastCurrentTime @"+e.toFixed(3)),t=e),this.state=_t,this.nextLoadPosition=this.startPosition=this.lastCurrentTime=t,this.tick()}else this._forceStartLoad=!0,this.state=Dt},f.stopLoad=function(){this._forceStartLoad=!1,t.prototype.stopLoad.call(this)},f.doTick=function(){switch(this.state){case _t:this.doTickIdle();break;case Mt:var t,e=this.levels,r=this.level,i=null==e||null===(t=e[r])||void 0===t?void 0:t.details;if(i&&(!i.live||this.levelLastLoaded===this.level)){if(this.waitForCdnTuneIn(i))break;this.state=_t;break}break;case Ct:var a,n=self.performance.now(),s=this.retryDate;(!s||n>=s||null!==(a=this.media)&&void 0!==a&&a.seeking)&&(this.log("retryDate reached, switch back to IDLE state"),this.state=_t)}this.onTickEnd()},f.onTickEnd=function(){t.prototype.onTickEnd.call(this),this.checkBuffer(),this.checkFragmentChanged()},f.doTickIdle=function(){var t,e,r=this.hls,i=this.levelLastLoaded,a=this.levels,s=this.media,o=r.config,l=r.nextLoadLevel;if(null!==i&&(s||!this.startFragRequested&&o.startFragPrefetch)&&(!this.altAudio||!this.audioOnly)&&a&&a[l]){var d=a[l];this.level=r.nextLoadLevel=l;var h=d.details;if(!h||this.state===Mt||h.live&&this.levelLastLoaded!==l)this.state=Mt;else{var f=this.getFwdBufferInfo(this.mediaBuffer?this.mediaBuffer:s,_.b.MAIN);if(null!==f)if(!(f.len>=this.getMaxBufferLength(d.maxBitrate))){if(this._streamEnded(f,h)){var c={};return this.altAudio&&(c.type="video"),this.hls.trigger(n.a.BUFFER_EOS,c),void(this.state=Pt)}var v=f.end,g=this.getNextFragment(v,h);if(this.couldBacktrack&&!this.fragPrevious&&g&&"initSegment"!==g.sn){var p=g.sn-h.startSN;p>1&&(g=h.fragments[p-1],this.fragmentTracker.removeFragment(g))}if(g&&this.fragmentTracker.getState(g)===$.OK&&this.nextLoadPosition>v){var m=this.audioOnly&&!this.altAudio?u.a.AUDIO:u.a.VIDEO;this.afterBufferFlushed(s,m,_.b.MAIN),g=this.getNextFragment(this.nextLoadPosition,h)}g&&(!g.initSegment||g.initSegment.data||this.bitrateTest||(g=g.initSegment),"identity"!==(null===(t=g.decryptdata)||void 0===t?void 0:t.keyFormat)||null!==(e=g.decryptdata)&&void 0!==e&&e.key?this.loadFragment(g,h,v):this.loadKey(g,h))}}}},f.loadFragment=function(e,r,i){var a,n=this.fragmentTracker.getState(e);if(this.fragCurrent=e,n===$.BACKTRACKED){var s=this.fragmentTracker.getBacktrackData(e);if(s)return this._handleFragmentLoadProgress(s),void this._handleFragmentLoadComplete(s);n=$.NOT_LOADED}n===$.NOT_LOADED||n===$.PARTIAL?"initSegment"===e.sn?this._loadInitSegment(e):this.bitrateTest?(e.bitrateTest=!0,this.log("Fragment "+e.sn+" of level "+e.level+" is being downloaded to test bitrate and will not be buffered"),this._loadBitrateTestFrag(e)):(this.startFragRequested=!0,t.prototype.loadFragment.call(this,e,r,i)):n===$.APPENDING?this.reduceMaxBufferLength(e.duration)&&this.fragmentTracker.removeFragment(e):0===(null===(a=this.media)||void 0===a?void 0:a.buffered.length)&&this.fragmentTracker.removeAllFragments()},f.getAppendedFrag=function(t){var e=this.fragmentTracker.getAppendedFrag(t,_.b.MAIN);return e&&"fragment"in e?e.fragment:e},f.getBufferedFrag=function(t){return this.fragmentTracker.getBufferedFrag(t,_.b.MAIN)},f.followingBufferedFrag=function(t){return t?this.getBufferedFrag(t.end+.5):null},f.immediateLevelSwitch=function(){this.abortCurrentFrag(),this.flushMainBuffer(0,Number.POSITIVE_INFINITY)},f.nextLevelSwitch=function(){var t=this.levels,e=this.media;if(null!=e&&e.readyState){var r,i=this.getAppendedFrag(e.currentTime);if(i&&i.start>1&&this.flushMainBuffer(0,i.start-1),!e.paused&&t){var a=t[this.hls.nextLoadLevel],n=this.fragLastKbps;r=n&&this.fragCurrent?this.fragCurrent.duration*a.maxBitrate/(1e3*n)+1:0}else r=0;var s=this.getBufferedFrag(e.currentTime+r);if(s){var o=this.followingBufferedFrag(s);if(o){this.abortCurrentFrag();var l=o.maxStartPTS?o.maxStartPTS:o.start,u=o.duration,d=Math.max(s.end,l+Math.min(Math.max(u-this.config.maxFragLookUpTolerance,.5*u),.75*u));this.flushMainBuffer(d,Number.POSITIVE_INFINITY)}}}},f.abortCurrentFrag=function(){var t=this.fragCurrent;this.fragCurrent=null,null!=t&&t.loader&&t.loader.abort(),this.state===kt&&(this.state=_t),this.nextLoadPosition=this.getLoadPosition()},f.flushMainBuffer=function(e,r){t.prototype.flushMainBuffer.call(this,e,r,this.altAudio?"video":null)},f.onMediaAttached=function(e,r){t.prototype.onMediaAttached.call(this,e,r);var i=r.media;this.onvplaying=this.onMediaPlaying.bind(this),this.onvseeked=this.onMediaSeeked.bind(this),i.addEventListener("playing",this.onvplaying),i.addEventListener("seeked",this.onvseeked),this.gapController=new Wt(this.config,i,this.fragmentTracker,this.hls)},f.onMediaDetaching=function(){var e=this.media;e&&(e.removeEventListener("playing",this.onvplaying),e.removeEventListener("seeked",this.onvseeked),this.onvplaying=this.onvseeked=null,this.videoBuffer=null),this.fragPlaying=null,this.gapController&&(this.gapController.destroy(),this.gapController=null),t.prototype.onMediaDetaching.call(this)},f.onMediaPlaying=function(){this.tick()},f.onMediaSeeked=function(){var t=this.media,e=t?t.currentTime:null;Object(a.a)(e)&&this.log("Media seeked to "+e.toFixed(3)),this.tick()},f.onManifestLoading=function(){this.log("Trigger BUFFER_RESET"),this.hls.trigger(n.a.BUFFER_RESET,void 0),this.fragmentTracker.removeAllFragments(),this.couldBacktrack=this.stalled=!1,this.startPosition=this.lastCurrentTime=0,this.fragPlaying=null},f.onManifestParsed=function(t,e){var r,i,a,n=!1,s=!1;e.levels.forEach((function(t){(r=t.audioCodec)&&(-1!==r.indexOf("mp4a.40.2")&&(n=!0),-1!==r.indexOf("mp4a.40.5")&&(s=!0))})),this.audioCodecSwitch=n&&s&&!("function"==typeof(null==(a=Ut())||null===(i=a.prototype)||void 0===i?void 0:i.changeType)),this.audioCodecSwitch&&this.log("Both AAC/HE-AAC audio found in levels; declaring level codec as HE-AAC"),this.levels=e.levels,this.startFragRequested=!1},f.onLevelLoading=function(t,e){var r=this.levels;if(r&&this.state===_t){var i=r[e.level];(!i.details||i.details.live&&this.levelLastLoaded!==e.level||this.waitForCdnTuneIn(i.details))&&(this.state=Mt)}},f.onLevelLoaded=function(t,e){var r,i=this.levels,a=e.level,s=e.details,o=s.totalduration;if(i){this.log("Level "+a+" loaded ["+s.startSN+","+s.endSN+"], cc ["+s.startCC+", "+s.endCC+"] duration:"+o);var l=this.fragCurrent;!l||this.state!==xt&&this.state!==Ct||l.level!==e.level&&l.loader&&(this.state=_t,l.loader.abort());var u=i[a],d=0;if(s.live||null!==(r=u.details)&&void 0!==r&&r.live){if(s.fragments[0]||(s.deltaUpdateFailed=!0),s.deltaUpdateFailed)return;d=this.alignPlaylists(s,u.details)}if(u.details=s,this.levelLastLoaded=a,this.hls.trigger(n.a.LEVEL_UPDATED,{details:s,level:a}),this.state===Mt){if(this.waitForCdnTuneIn(s))return;this.state=_t}this.startFragRequested?s.live&&this.synchronizeToLiveEdge(s):this.setStartPosition(s,d),this.tick()}else this.warn("Levels were reset while loading level "+a)},f._handleFragmentLoadProgress=function(t){var e,r=t.frag,i=t.part,a=t.payload,n=this.levels;if(n){var s=n[r.level],o=s.details;if(o){var l=s.videoCodec,u=o.PTSKnown||!o.live,d=null===(e=r.initSegment)||void 0===e?void 0:e.data,h=this._getAudioCodec(s),f=this.transmuxer=this.transmuxer||new Vt(this.hls,_.b.MAIN,this._handleTransmuxComplete.bind(this),this._handleTransmuxerFlush.bind(this)),c=i?i.index:-1,v=-1!==c,g=new st(r.level,r.sn,r.stats.chunkCount,a.byteLength,c,v),p=this.initPTS[r.cc];f.push(a,d,h,l,r,i,o.totalduration,u,g,p)}else this.warn("Dropping fragment "+r.sn+" of level "+r.level+" after level details were reset")}else this.warn("Levels were reset while fragment load was in progress. Fragment "+r.sn+" of level "+r.level+" will not be buffered")},f.onAudioTrackSwitching=function(t,e){var r=this.altAudio,i=!!e.url,a=e.id;if(!i){if(this.mediaBuffer!==this.media){this.log("Switching on main audio, use media.buffered to schedule main fragment loading"),this.mediaBuffer=this.media;var s=this.fragCurrent;null!=s&&s.loader&&(this.log("Switching to main audio track, cancel main fragment load"),s.loader.abort()),this.resetTransmuxer(),this.resetLoadingState()}else this.audioOnly&&this.resetTransmuxer();var o=this.hls;r&&o.trigger(n.a.BUFFER_FLUSHING,{startOffset:0,endOffset:Number.POSITIVE_INFINITY,type:"audio"}),o.trigger(n.a.AUDIO_TRACK_SWITCHED,{id:a})}},f.onAudioTrackSwitched=function(t,e){var r=e.id,i=!!this.hls.audioTracks[r].url;if(i){var a=this.videoBuffer;a&&this.mediaBuffer!==a&&(this.log("Switching on alternate audio, use video.buffered to schedule main fragment loading"),this.mediaBuffer=a)}this.altAudio=i,this.tick()},f.onBufferCreated=function(t,e){var r,i,a=e.tracks,n=!1;for(var s in a){var o=a[s];if("main"===o.id){if(i=s,r=o,"video"===s){var l=a[s];l&&(this.videoBuffer=l.buffer)}}else n=!0}n&&r?(this.log("Alternate track found, use "+i+".buffered to schedule main fragment loading"),this.mediaBuffer=r.buffer):this.mediaBuffer=this.media},f.onFragBuffered=function(t,e){var r=e.frag,i=e.part;if(!r||r.type===_.b.MAIN){if(this.fragContextChanged(r))return this.warn("Fragment "+r.sn+(i?" p: "+i.index:"")+" of level "+r.level+" finished buffering, but was aborted. state: "+this.state),void(this.state===Ot&&(this.state=_t));var a=i?i.stats:r.stats;this.fragLastKbps=Math.round(8*a.total/(a.buffering.end-a.loading.first)),"initSegment"!==r.sn&&(this.fragPrevious=r),this.fragBufferedComplete(r,i)}},f.onError=function(t,e){switch(e.details){case s.a.FRAG_LOAD_ERROR:case s.a.FRAG_LOAD_TIMEOUT:case s.a.KEY_LOAD_ERROR:case s.a.KEY_LOAD_TIMEOUT:this.onFragmentOrKeyLoadError(_.b.MAIN,e);break;case s.a.LEVEL_LOAD_ERROR:case s.a.LEVEL_LOAD_TIMEOUT:this.state!==Ft&&(e.fatal?(this.warn(""+e.details),this.state=Ft):e.levelRetry||this.state!==Mt||(this.state=_t));break;case s.a.BUFFER_FULL_ERROR:if("main"===e.parent&&(this.state===wt||this.state===Ot)){var r=!0,i=this.getFwdBufferInfo(this.media,_.b.MAIN);i&&i.len>.5&&(r=!this.reduceMaxBufferLength(i.len)),r&&(this.warn("buffer full error also media.currentTime is not buffered, flush main"),this.immediateLevelSwitch()),this.resetLoadingState()}}},f.checkBuffer=function(){var t=this.media,e=this.gapController;if(t&&e&&t.readyState){var r=nt.getBuffered(t);!this.loadedmetadata&&r.length?(this.loadedmetadata=!0,this.seekToStartPos()):e.poll(this.lastCurrentTime),this.lastCurrentTime=t.currentTime}},f.onFragLoadEmergencyAborted=function(){this.state=_t,this.loadedmetadata||(this.startFragRequested=!1,this.nextLoadPosition=this.startPosition),this.tickImmediate()},f.onBufferFlushed=function(t,e){var r=e.type;if(r!==u.a.AUDIO||this.audioOnly&&!this.altAudio){var i=(r===u.a.VIDEO?this.videoBuffer:this.mediaBuffer)||this.media;this.afterBufferFlushed(i,r,_.b.MAIN)}},f.onLevelsUpdated=function(t,e){this.levels=e.levels},f.swapAudioCodec=function(){this.audioCodecSwap=!this.audioCodecSwap},f.seekToStartPos=function(){var t=this.media,e=t.currentTime,r=this.startPosition;if(r>=0&&e0&&(a1&&!1===t.seeking){var r=t.currentTime;if(nt.isBuffered(t,r)?e=this.getAppendedFrag(r):nt.isBuffered(t,r+.1)&&(e=this.getAppendedFrag(r+.1)),e){var i=this.fragPlaying,a=e.level;i&&e.sn===i.sn&&i.level===a&&e.urlId===i.urlId||(this.hls.trigger(n.a.FRAG_CHANGED,{frag:e}),i&&i.level===a||this.hls.trigger(n.a.LEVEL_SWITCHED,{level:a}),this.fragPlaying=e)}}},l=i,(d=[{key:"nextLevel",get:function(){var t=this.nextBufferedFrag;return t?t.level:-1}},{key:"currentLevel",get:function(){var t=this.media;if(t){var e=this.getAppendedFrag(t.currentTime);if(e)return e.level}return-1}},{key:"nextBufferedFrag",get:function(){var t=this.media;if(t){var e=this.getAppendedFrag(t.currentTime);return this.followingBufferedFrag(e)}return null}},{key:"forceStartLoad",get:function(){return this._forceStartLoad}}])&&Yt(l.prototype,d),h&&Yt(l,h),i}(Nt),zt=function(){function t(t,e,r){void 0===e&&(e=0),void 0===r&&(r=0),this.halfLife=void 0,this.alpha_=void 0,this.estimate_=void 0,this.totalWeight_=void 0,this.halfLife=t,this.alpha_=t?Math.exp(Math.log(.5)/t):0,this.estimate_=e,this.totalWeight_=r}var e=t.prototype;return e.sample=function(t,e){var r=Math.pow(this.alpha_,t);this.estimate_=e*(1-r)+r*this.estimate_,this.totalWeight_+=t},e.getTotalWeight=function(){return this.totalWeight_},e.getEstimate=function(){if(this.alpha_){var t=1-Math.pow(this.alpha_,this.totalWeight_);if(t)return this.estimate_/t}return this.estimate_},t}(),Qt=function(){function t(t,e,r){this.defaultEstimate_=void 0,this.minWeight_=void 0,this.minDelayMs_=void 0,this.slow_=void 0,this.fast_=void 0,this.defaultEstimate_=r,this.minWeight_=.001,this.minDelayMs_=50,this.slow_=new zt(t),this.fast_=new zt(e)}var e=t.prototype;return e.update=function(t,e){var r=this.slow_,i=this.fast_;this.slow_.halfLife!==t&&(this.slow_=new zt(t,r.getEstimate(),r.getTotalWeight())),this.fast_.halfLife!==e&&(this.fast_=new zt(e,i.getEstimate(),i.getTotalWeight()))},e.sample=function(t,e){var r=(t=Math.max(t,this.minDelayMs_))/1e3,i=8*e/r;this.fast_.sample(r,i),this.slow_.sample(r,i)},e.canEstimate=function(){var t=this.fast_;return t&&t.getTotalWeight()>=this.minWeight_},e.getEstimate=function(){return this.canEstimate()?Math.min(this.fast_.getEstimate(),this.slow_.getEstimate()):this.defaultEstimate_},e.destroy=function(){},t}();function $t(t,e){for(var r=0;r=2*d/f||y<=T)){var E,S=Number.POSITIVE_INFINITY;for(E=t.level-1;E>v;E--){if((S=d*c[E].maxBitrate/(6.4*m))=y)){var L=this.bwEstimator.getEstimate();o.b.warn("Fragment "+t.sn+(e?" part "+e.index:"")+" of level "+t.level+" is loading too slowly and will cause an underbuffer; aborting and switching to level "+E+"\n Current BW estimate: "+(Object(a.a)(L)?(L/1024).toFixed(3):"Unknown")+" Kb/s\n Estimated load time for current fragment: "+y.toFixed(3)+" s\n Estimated load time for the next fragment: "+S.toFixed(3)+" s\n Time to underbuffer: "+T.toFixed(3)+" s"),r.nextLoadLevel=E,this.bwEstimator.sample(h,u.loaded),this.clearTimer(),t.loader&&(this.fragCurrent=this.partCurrent=null,t.loader.abort()),r.trigger(n.a.FRAG_LOAD_EMERGENCY_ABORTED,{frag:t,part:e,stats:u})}}}}}},l.onFragLoaded=function(t,e){var r=e.frag,i=e.part;if(r.type===_.b.MAIN&&Object(a.a)(r.sn)){var s=i?i.stats:r.stats,o=i?i.duration:r.duration;if(this.clearTimer(),this.lastLoadedFragLevel=r.level,this._nextAutoLevel=-1,this.hls.config.abrMaxWithRealBitrate){var l=this.hls.levels[r.level],u=(l.loaded?l.loaded.bytes:0)+s.loaded,d=(l.loaded?l.loaded.duration:0)+o;l.loaded={bytes:u,duration:d},l.realBitrate=Math.round(8*u/d)}if(r.bitrateTest){var h={stats:s,frag:r,part:i,id:r.type};this.onFragBuffered(n.a.FRAG_BUFFERED,h),r.bitrateTest=!1}}},l.onFragBuffered=function(t,e){var r=e.frag,i=e.part,a=i?i.stats:r.stats;if(!a.aborted&&r.type===_.b.MAIN&&"initSegment"!==r.sn){var n=a.parsing.end-a.loading.start;this.bwEstimator.sample(n,a.loaded),a.bwEstimate=this.bwEstimator.getEstimate(),r.bitrateTest?this.bitrateTestDelay=n/1e3:this.bitrateTestDelay=0}},l.onError=function(t,e){switch(e.details){case s.a.FRAG_LOAD_ERROR:case s.a.FRAG_LOAD_TIMEOUT:this.clearTimer()}},l.clearTimer=function(){self.clearInterval(this.timer),this.timer=void 0},l.getNextABRAutoLevel=function(){var t=this.fragCurrent,e=this.partCurrent,r=this.hls,i=r.maxAutoLevel,a=r.config,n=r.minAutoLevel,s=r.media,l=e?e.duration:t?t.duration:0,u=s?s.currentTime:0,d=s&&0!==s.playbackRate?Math.abs(s.playbackRate):1,h=this.bwEstimator?this.bwEstimator.getEstimate():a.abrEwmaDefaultEstimate,f=(nt.bufferInfo(s,u,a.maxBufferHole).end-u)/d,c=this.findBestLevel(h,n,i,f,a.abrBandWidthFactor,a.abrBandWidthUpFactor);if(c>=0)return c;o.b.trace((f?"rebuffering expected":"buffer is empty")+", finding optimal quality level");var v=l?Math.min(l,a.maxStarvationDelay):a.maxStarvationDelay,g=a.abrBandWidthFactor,p=a.abrBandWidthUpFactor;if(!f){var m=this.bitrateTestDelay;if(m)v=(l?Math.min(l,a.maxLoadingDelay):a.maxLoadingDelay)-m,o.b.trace("bitrate test took "+Math.round(1e3*m)+"ms, set first fragment max fetchDuration to "+Math.round(1e3*v)+" ms"),g=p=1}return c=this.findBestLevel(h,n,i,f+v,g,p),Math.max(c,0)},l.findBestLevel=function(t,e,r,i,a,n){for(var s,l=this.fragCurrent,u=this.partCurrent,d=this.lastLoadedFragLevel,h=this.hls.levels,f=h[d],c=!(null==f||null===(s=f.details)||void 0===s||!s.live),v=null==f?void 0:f.codecSet,g=u?u.duration:l?l.duration:0,p=r;p>=e;p--){var m=h[p];if(m&&(!v||m.codecSet===v)){var y=m.details,b=(u?null==y?void 0:y.partTarget:null==y?void 0:y.averagetargetduration)||g,T=void 0;T=p<=d?a*t:n*t;var E=h[p].maxBitrate,S=E*b/T;if(o.b.trace("level/adjustedbw/bitrate/avgDuration/maxFetchDuration/fetchDuration: "+p+"/"+Math.round(T)+"/"+E+"/"+b+"/"+i+"/"+S),T>E&&(!S||c&&!this.bitrateTestDelay||S0||Object.keys(this.pendingTracks).length>0},e.destroy=function(){this.unregisterListeners(),this.details=null},e.registerListeners=function(){var t=this.hls;t.on(n.a.MEDIA_ATTACHING,this.onMediaAttaching,this),t.on(n.a.MEDIA_DETACHING,this.onMediaDetaching,this),t.on(n.a.MANIFEST_PARSED,this.onManifestParsed,this),t.on(n.a.BUFFER_RESET,this.onBufferReset,this),t.on(n.a.BUFFER_APPENDING,this.onBufferAppending,this),t.on(n.a.BUFFER_CODECS,this.onBufferCodecs,this),t.on(n.a.BUFFER_EOS,this.onBufferEos,this),t.on(n.a.BUFFER_FLUSHING,this.onBufferFlushing,this),t.on(n.a.LEVEL_UPDATED,this.onLevelUpdated,this),t.on(n.a.FRAG_PARSED,this.onFragParsed,this),t.on(n.a.FRAG_CHANGED,this.onFragChanged,this)},e.unregisterListeners=function(){var t=this.hls;t.off(n.a.MEDIA_ATTACHING,this.onMediaAttaching,this),t.off(n.a.MEDIA_DETACHING,this.onMediaDetaching,this),t.off(n.a.MANIFEST_PARSED,this.onManifestParsed,this),t.off(n.a.BUFFER_RESET,this.onBufferReset,this),t.off(n.a.BUFFER_APPENDING,this.onBufferAppending,this),t.off(n.a.BUFFER_CODECS,this.onBufferCodecs,this),t.off(n.a.BUFFER_EOS,this.onBufferEos,this),t.off(n.a.BUFFER_FLUSHING,this.onBufferFlushing,this),t.off(n.a.LEVEL_UPDATED,this.onLevelUpdated,this),t.off(n.a.FRAG_PARSED,this.onFragParsed,this),t.off(n.a.FRAG_CHANGED,this.onFragChanged,this)},e._initSourceBuffer=function(){this.sourceBuffer={},this.operationQueue=new ee(this.sourceBuffer),this.listeners={audio:[],video:[],audiovideo:[]}},e.onManifestParsed=function(t,e){var r=2;(e.audio&&!e.video||!e.altAudio)&&(r=1),this.bufferCodecEventsExpected=this._bufferCodecEventsTotal=r,this.details=null,o.b.log(this.bufferCodecEventsExpected+" bufferCodec event(s) expected")},e.onMediaAttaching=function(t,e){var r=this.media=e.media;if(r&&re){var i=this.mediaSource=new re;i.addEventListener("sourceopen",this._onMediaSourceOpen),i.addEventListener("sourceended",this._onMediaSourceEnded),i.addEventListener("sourceclose",this._onMediaSourceClose),r.src=self.URL.createObjectURL(i),this._objectUrl=r.src}},e.onMediaDetaching=function(){var t=this.media,e=this.mediaSource,r=this._objectUrl;if(e){if(o.b.log("[buffer-controller]: media source detaching"),"open"===e.readyState)try{e.endOfStream()}catch(t){o.b.warn("[buffer-controller]: onMediaDetaching: "+t.message+" while calling endOfStream")}this.onBufferReset(),e.removeEventListener("sourceopen",this._onMediaSourceOpen),e.removeEventListener("sourceended",this._onMediaSourceEnded),e.removeEventListener("sourceclose",this._onMediaSourceClose),t&&(r&&self.URL.revokeObjectURL(r),t.src===r?(t.removeAttribute("src"),t.load()):o.b.warn("[buffer-controller]: media.src was changed by a third party - skip cleanup")),this.mediaSource=null,this.media=null,this._objectUrl=null,this.bufferCodecEventsExpected=this._bufferCodecEventsTotal,this.pendingTracks={},this.tracks={}}this.hls.trigger(n.a.MEDIA_DETACHED,void 0)},e.onBufferReset=function(){var t=this;this.getSourceBufferTypes().forEach((function(e){var r=t.sourceBuffer[e];try{r&&(t.removeBufferListeners(e),t.mediaSource&&t.mediaSource.removeSourceBuffer(r),t.sourceBuffer[e]=void 0)}catch(t){o.b.warn("[buffer-controller]: Failed to reset the "+e+" buffer",t)}})),this._initSourceBuffer()},e.onBufferCodecs=function(t,e){var r=this,i=this.getSourceBufferTypes().length;Object.keys(e).forEach((function(t){if(i){var a=r.tracks[t];if(a&&"function"==typeof a.buffer.changeType){var n=e[t],s=n.codec,o=n.levelCodec,l=n.container;if((a.levelCodec||a.codec).replace(ie,"$1")!==(o||s).replace(ie,"$1")){var u=l+";codecs="+(o||s);r.appendChangeType(t,u)}}}else r.pendingTracks[t]=e[t]})),i||(this.bufferCodecEventsExpected=Math.max(this.bufferCodecEventsExpected-1,0),this.mediaSource&&"open"===this.mediaSource.readyState&&this.checkPendingTracks())},e.appendChangeType=function(t,e){var r=this,i=this.operationQueue,a={execute:function(){var a=r.sourceBuffer[t];a&&(o.b.log("[buffer-controller]: changing "+t+" sourceBuffer type to "+e),a.changeType(e)),i.shiftAndExecuteNext(t)},onStart:function(){},onComplete:function(){},onError:function(e){o.b.warn("[buffer-controller]: Failed to change "+t+" SourceBuffer type",e)}};i.append(a,t)},e.onBufferAppending=function(t,e){var r=this,i=this.hls,a=this.operationQueue,l=this.tracks,u=e.data,d=e.type,h=e.frag,f=e.part,c=e.chunkMeta,v=c.buffering[d],g=self.performance.now();v.start=g;var p=h.stats.buffering,m=f?f.stats.buffering:null;0===p.start&&(p.start=g),m&&0===m.start&&(m.start=g);var y=l.audio,b="audio"===d&&1===c.id&&"audio/mpeg"===(null==y?void 0:y.container),T={execute:function(){if(v.executeStart=self.performance.now(),b){var t=r.sourceBuffer[d];if(t){var e=h.start-t.timestampOffset;Math.abs(e)>=.1&&(o.b.log("[buffer-controller]: Updating audio SourceBuffer timestampOffset to "+h.start+" (delta: "+e+") sn: "+h.sn+")"),t.timestampOffset=h.start)}}r.appendExecutor(u,d)},onStart:function(){},onComplete:function(){var t=self.performance.now();v.executeEnd=v.end=t,0===p.first&&(p.first=t),m&&0===m.first&&(m.first=t);var e=r.sourceBuffer,i={};for(var a in e)i[a]=nt.getBuffered(e[a]);r.appendError=0,r.hls.trigger(n.a.BUFFER_APPENDED,{type:d,frag:h,part:f,chunkMeta:c,parent:h.type,timeRanges:i})},onError:function(t){o.b.error("[buffer-controller]: Error encountered while trying to append to the "+d+" SourceBuffer",t);var e={type:s.b.MEDIA_ERROR,parent:h.type,details:s.a.BUFFER_APPEND_ERROR,err:t,fatal:!1};t.code===DOMException.QUOTA_EXCEEDED_ERR?e.details=s.a.BUFFER_FULL_ERROR:(r.appendError++,e.details=s.a.BUFFER_APPEND_ERROR,r.appendError>i.config.appendErrorMaxRetry&&(o.b.error("[buffer-controller]: Failed "+i.config.appendErrorMaxRetry+" times to append segment in sourceBuffer"),e.fatal=!0)),i.trigger(n.a.ERROR,e)}};a.append(T,d)},e.onBufferFlushing=function(t,e){var r=this,i=this.operationQueue,a=function(t){return{execute:r.removeExecutor.bind(r,t,e.startOffset,e.endOffset),onStart:function(){},onComplete:function(){r.hls.trigger(n.a.BUFFER_FLUSHED,{type:t})},onError:function(e){o.b.warn("[buffer-controller]: Failed to remove from "+t+" SourceBuffer",e)}}};e.type?i.append(a(e.type),e.type):this.getSourceBufferTypes().forEach((function(t){i.append(a(t),t)}))},e.onFragParsed=function(t,e){var r=this,i=e.frag,a=e.part,s=[],l=a?a.elementaryStreams:i.elementaryStreams;l[u.a.AUDIOVIDEO]?s.push("audiovideo"):(l[u.a.AUDIO]&&s.push("audio"),l[u.a.VIDEO]&&s.push("video"));0===s.length&&o.b.warn("Fragments must have at least one ElementaryStreamType set. type: "+i.type+" level: "+i.level+" sn: "+i.sn),this.blockBuffers((function(){var t=self.performance.now();i.stats.buffering.end=t,a&&(a.stats.buffering.end=t);var e=a?a.stats:i.stats;r.hls.trigger(n.a.FRAG_BUFFERED,{frag:i,part:a,stats:e,id:i.type})}),s)},e.onFragChanged=function(t,e){this.flushBackBuffer()},e.onBufferEos=function(t,e){var r=this;this.getSourceBufferTypes().reduce((function(t,i){var a=r.sourceBuffer[i];return e.type&&e.type!==i||a&&!a.ended&&(a.ended=!0,o.b.log("[buffer-controller]: "+i+" sourceBuffer now EOS")),t&&!(a&&!a.ended)}),!0)&&this.blockBuffers((function(){var t=r.mediaSource;t&&"open"===t.readyState&&t.endOfStream()}))},e.onLevelUpdated=function(t,e){var r=e.details;r.fragments.length&&(this.details=r,this.getSourceBufferTypes().length?this.blockBuffers(this.updateMediaElementDuration.bind(this)):this.updateMediaElementDuration())},e.flushBackBuffer=function(){var t=this.hls,e=this.details,r=this.media,i=this.sourceBuffer;if(r&&null!==e){var s=this.getSourceBufferTypes();if(s.length){var o=e.live&&null!==t.config.liveBackBufferLength?t.config.liveBackBufferLength:t.config.backBufferLength;if(Object(a.a)(o)&&!(o<0)){var l=r.currentTime,u=e.levelTargetDuration,d=Math.max(o,u),h=Math.floor(l/u)*u-d;s.forEach((function(r){var a=i[r];if(a){var s=nt.getBuffered(a);s.length>0&&h>s.start(0)&&(t.trigger(n.a.BACK_BUFFER_REACHED,{bufferEnd:h}),e.live&&t.trigger(n.a.LIVE_BACK_BUFFER_REACHED,{bufferEnd:h}),t.trigger(n.a.BUFFER_FLUSHING,{startOffset:0,endOffset:h,type:r}))}}))}}}},e.updateMediaElementDuration=function(){if(this.details&&this.media&&this.mediaSource&&"open"===this.mediaSource.readyState){var t=this.details,e=this.hls,r=this.media,i=this.mediaSource,n=t.fragments[0].start+t.totalduration,s=r.duration,l=Object(a.a)(i.duration)?i.duration:0;t.live&&e.config.liveDurationInfinity?(o.b.log("[buffer-controller]: Media Source duration is set to Infinity"),i.duration=1/0,this.updateSeekableRange(t)):(n>l&&n>s||!Object(a.a)(s))&&(o.b.log("[buffer-controller]: Updating Media Source duration to "+n.toFixed(3)),i.duration=n)}},e.updateSeekableRange=function(t){var e=this.mediaSource,r=t.fragments;if(r.length&&t.live&&null!=e&&e.setLiveSeekableRange){var i=Math.max(0,r[0].start),a=Math.max(i,i+t.totalduration);e.setLiveSeekableRange(i,a)}},e.checkPendingTracks=function(){var t=this.bufferCodecEventsExpected,e=this.operationQueue,r=this.pendingTracks,i=Object.keys(r).length;if(i&&!t||2===i){this.createSourceBuffers(r),this.pendingTracks={};var a=this.getSourceBufferTypes();if(0===a.length)return void this.hls.trigger(n.a.ERROR,{type:s.b.MEDIA_ERROR,details:s.a.BUFFER_INCOMPATIBLE_CODECS_ERROR,fatal:!0,reason:"could not create source buffer for media codec(s)"});a.forEach((function(t){e.executeNext(t)}))}},e.createSourceBuffers=function(t){var e=this.sourceBuffer,r=this.mediaSource;if(!r)throw Error("createSourceBuffers called when mediaSource was null");var i=0;for(var a in t)if(!e[a]){var l=t[a];if(!l)throw Error("source buffer exists for track "+a+", however track does not");var u=l.levelCodec||l.codec,d=l.container+";codecs="+u;o.b.log("[buffer-controller]: creating sourceBuffer("+d+")");try{var h=e[a]=r.addSourceBuffer(d),f=a;this.addBufferListener(f,"updatestart",this._onSBUpdateStart),this.addBufferListener(f,"updateend",this._onSBUpdateEnd),this.addBufferListener(f,"error",this._onSBUpdateError),this.tracks[a]={buffer:h,codec:u,container:l.container,levelCodec:l.levelCodec,id:l.id},i++}catch(t){o.b.error("[buffer-controller]: error while trying to add sourceBuffer: "+t.message),this.hls.trigger(n.a.ERROR,{type:s.b.MEDIA_ERROR,details:s.a.BUFFER_ADD_CODEC_ERROR,fatal:!1,error:t,mimeType:d})}}i&&this.hls.trigger(n.a.BUFFER_CREATED,{tracks:this.tracks})},e._onSBUpdateStart=function(t){this.operationQueue.current(t).onStart()},e._onSBUpdateEnd=function(t){var e=this.operationQueue;e.current(t).onComplete(),e.shiftAndExecuteNext(t)},e._onSBUpdateError=function(t,e){o.b.error("[buffer-controller]: "+t+" SourceBuffer error",e),this.hls.trigger(n.a.ERROR,{type:s.b.MEDIA_ERROR,details:s.a.BUFFER_APPENDING_ERROR,fatal:!1});var r=this.operationQueue.current(t);r&&r.onError(e)},e.removeExecutor=function(t,e,r){var i=this.media,n=this.mediaSource,s=this.operationQueue,l=this.sourceBuffer[t];if(!i||!n||!l)return o.b.warn("[buffer-controller]: Attempting to remove from the "+t+" SourceBuffer, but it does not exist"),void s.shiftAndExecuteNext(t);var u=Object(a.a)(i.duration)?i.duration:1/0,d=Object(a.a)(n.duration)?n.duration:1/0,h=Math.max(0,e),f=Math.min(r,u,d);f>h?(o.b.log("[buffer-controller]: Removing ["+h+","+f+"] from the "+t+" SourceBuffer"),l.remove(h,f)):s.shiftAndExecuteNext(t)},e.appendExecutor=function(t,e){var r=this.operationQueue,i=this.sourceBuffer[e];if(!i)return o.b.warn("[buffer-controller]: Attempting to append to the "+e+" SourceBuffer, but it does not exist"),void r.shiftAndExecuteNext(e);i.ended=!1,i.appendBuffer(t)},e.blockBuffers=function(t,e){var r=this;if(void 0===e&&(e=this.getSourceBufferTypes()),!e.length)return o.b.log("[buffer-controller]: Blocking operation requested, but no SourceBuffers exist"),void Promise.resolve(t);var i=this.operationQueue,a=e.map((function(t){return i.appendBlocker(t)}));Promise.all(a).then((function(){t(),e.forEach((function(t){var e=r.sourceBuffer[t];e&&e.updating||i.shiftAndExecuteNext(t)}))}))},e.getSourceBufferTypes=function(){return Object.keys(this.sourceBuffer)},e.addBufferListener=function(t,e,r){var i=this.sourceBuffer[t];if(i){var a=r.bind(this,t);this.listeners[t].push({event:e,listener:a}),i.addEventListener(e,a)}},e.removeBufferListeners=function(t){var e=this.sourceBuffer[t];e&&this.listeners[t].forEach((function(t){e.removeEventListener(t.event,t.listener)}))},t}();function ne(t,e){for(var r=0;r0&&this.mediaWidth>0){var t=this.hls.levels;if(t.length){var e=this.hls;e.autoLevelCapping=this.getMaxLevel(t.length-1),e.autoLevelCapping>this.autoLevelCapping&&this.streamController&&this.streamController.nextLevelSwitch(),this.autoLevelCapping=e.autoLevelCapping}}},a.getMaxLevel=function(e){var r=this,i=this.hls.levels;if(!i.length)return-1;var a=i.filter((function(i,a){return t.isLevelAllowed(a,r.restrictedLevels)&&a<=e}));return this.clientRect=null,t.getMaxLevelByMediaSize(a,this.mediaWidth,this.mediaHeight)},a.startCapping=function(){this.timer||(this.autoLevelCapping=Number.POSITIVE_INFINITY,this.hls.firstLevel=this.getMaxLevel(this.firstLevel),self.clearInterval(this.timer),this.timer=self.setInterval(this.detectPlayerSize.bind(this),1e3),this.detectPlayerSize())},a.stopCapping=function(){this.restrictedLevels=[],this.firstLevel=-1,this.autoLevelCapping=Number.POSITIVE_INFINITY,this.timer&&(self.clearInterval(this.timer),this.timer=void 0)},a.getDimensions=function(){if(this.clientRect)return this.clientRect;var t=this.media,e={width:0,height:0};if(t){var r=t.getBoundingClientRect();e.width=r.width,e.height=r.height,e.width||e.height||(e.width=r.right-r.left||t.width||0,e.height=r.bottom-r.top||t.height||0)}return this.clientRect=e,e},t.isLevelAllowed=function(t,e){return void 0===e&&(e=[]),-1===e.indexOf(t)},t.getMaxLevelByMediaSize=function(t,e,r){if(!t||!t.length)return-1;for(var i,a,n=t.length-1,s=0;s=e||o.height>=r)&&(i=o,!(a=t[s+1])||i.width!==a.width||i.height!==a.height)){n=s;break}}return n},e=t,i=[{key:"contentScaleFactor",get:function(){var t=1;try{t=self.devicePixelRatio}catch(t){}return t}}],(r=[{key:"mediaWidth",get:function(){return this.getDimensions().width*t.contentScaleFactor}},{key:"mediaHeight",get:function(){return this.getDimensions().height*t.contentScaleFactor}}])&&ne(e.prototype,r),i&&ne(e,i),t}(),oe=function(){function t(t){this.hls=void 0,this.isVideoPlaybackQualityAvailable=!1,this.timer=void 0,this.media=null,this.lastTime=void 0,this.lastDroppedFrames=0,this.lastDecodedFrames=0,this.streamController=void 0,this.hls=t,this.registerListeners()}var e=t.prototype;return e.setStreamController=function(t){this.streamController=t},e.registerListeners=function(){this.hls.on(n.a.MEDIA_ATTACHING,this.onMediaAttaching,this)},e.unregisterListeners=function(){this.hls.off(n.a.MEDIA_ATTACHING,this.onMediaAttaching)},e.destroy=function(){this.timer&&clearInterval(this.timer),this.unregisterListeners(),this.isVideoPlaybackQualityAvailable=!1,this.media=null},e.onMediaAttaching=function(t,e){var r=this.hls.config;if(r.capLevelOnFPSDrop){var i=e.media instanceof self.HTMLVideoElement?e.media:null;this.media=i,i&&"function"==typeof i.getVideoPlaybackQuality&&(this.isVideoPlaybackQualityAvailable=!0),self.clearInterval(this.timer),this.timer=self.setInterval(this.checkFPSInterval.bind(this),r.fpsDroppedMonitoringPeriod)}},e.checkFPS=function(t,e,r){var i=performance.now();if(e){if(this.lastTime){var a=i-this.lastTime,s=r-this.lastDroppedFrames,l=e-this.lastDecodedFrames,u=1e3*s/a,d=this.hls;if(d.trigger(n.a.FPS_DROP,{currentDropped:s,currentDecoded:l,totalDroppedFrames:r}),u>0&&s>d.config.fpsDroppedMonitoringThreshold*l){var h=d.currentLevel;o.b.warn("drop FPS ratio greater than max allowed value for currentLevel: "+h),h>0&&(-1===d.autoLevelCapping||d.autoLevelCapping>=h)&&(h-=1,d.trigger(n.a.FPS_DROP_LEVEL_CAPPING,{level:h,droppedLevel:d.currentLevel}),d.autoLevelCapping=h,this.streamController.nextLevelSwitch())}}this.lastTime=i,this.lastDroppedFrames=r,this.lastDecodedFrames=e}},e.checkFPSInterval=function(){var t=this.media;if(t)if(this.isVideoPlaybackQualityAvailable){var e=t.getVideoPlaybackQuality();this.checkFPS(t,e.totalVideoFrames,e.droppedVideoFrames)}else this.checkFPS(t,t.webkitDecodedFrameCount,t.webkitDroppedFrameCount)},t}(),le=r(12),ue=/^age:\s*[\d.]+\s*$/m,de=function(){function t(t){this.xhrSetup=void 0,this.requestTimeout=void 0,this.retryTimeout=void 0,this.retryDelay=void 0,this.config=null,this.callbacks=null,this.context=void 0,this.loader=null,this.stats=void 0,this.xhrSetup=t?t.xhrSetup:null,this.stats=new le.a,this.retryDelay=0}var e=t.prototype;return e.destroy=function(){this.callbacks=null,this.abortInternal(),this.loader=null,this.config=null},e.abortInternal=function(){var t=this.loader;self.clearTimeout(this.requestTimeout),self.clearTimeout(this.retryTimeout),t&&(t.onreadystatechange=null,t.onprogress=null,4!==t.readyState&&(this.stats.aborted=!0,t.abort()))},e.abort=function(){var t;this.abortInternal(),null!==(t=this.callbacks)&&void 0!==t&&t.onAbort&&this.callbacks.onAbort(this.stats,this.context,this.loader)},e.load=function(t,e,r){if(this.stats.loading.start)throw new Error("Loader can only be used once.");this.stats.loading.start=self.performance.now(),this.context=t,this.config=e,this.callbacks=r,this.retryDelay=e.retryDelay,this.loadInternal()},e.loadInternal=function(){var t=this.config,e=this.context;if(t){var r=this.loader=new self.XMLHttpRequest,i=this.stats;i.loading.first=0,i.loaded=0;var a=this.xhrSetup;try{if(a)try{a(r,e.url)}catch(t){r.open("GET",e.url,!0),a(r,e.url)}r.readyState||r.open("GET",e.url,!0);var n=this.context.headers;if(n)for(var s in n)r.setRequestHeader(s,n[s])}catch(t){return void this.callbacks.onError({code:r.status,text:t.message},e,r)}e.rangeEnd&&r.setRequestHeader("Range","bytes="+e.rangeStart+"-"+(e.rangeEnd-1)),r.onreadystatechange=this.readystatechange.bind(this),r.onprogress=this.loadprogress.bind(this),r.responseType=e.responseType,self.clearTimeout(this.requestTimeout),this.requestTimeout=self.setTimeout(this.loadtimeout.bind(this),t.timeout),r.send()}},e.readystatechange=function(){var t=this.context,e=this.loader,r=this.stats;if(t&&e){var i=e.readyState,a=this.config;if(!r.aborted&&i>=2)if(self.clearTimeout(this.requestTimeout),0===r.loading.first&&(r.loading.first=Math.max(self.performance.now(),r.loading.start)),4===i){e.onreadystatechange=null,e.onprogress=null;var n=e.status;if(n>=200&&n<300){var s,l;if(r.loading.end=Math.max(self.performance.now(),r.loading.first),l="arraybuffer"===t.responseType?(s=e.response).byteLength:(s=e.responseText).length,r.loaded=r.total=l,!this.callbacks)return;var u=this.callbacks.onProgress;if(u&&u(r,t,s,e),!this.callbacks)return;var d={url:e.responseURL,data:s};this.callbacks.onSuccess(d,r,t,e)}else r.retry>=a.maxRetry||n>=400&&n<499?(o.b.error(n+" while loading "+t.url),this.callbacks.onError({code:n,text:e.statusText},t,e)):(o.b.warn(n+" while loading "+t.url+", retrying in "+this.retryDelay+"..."),this.abortInternal(),this.loader=null,self.clearTimeout(this.retryTimeout),this.retryTimeout=self.setTimeout(this.loadInternal.bind(this),this.retryDelay),this.retryDelay=Math.min(2*this.retryDelay,a.maxRetryDelay),r.retry++)}else self.clearTimeout(this.requestTimeout),this.requestTimeout=self.setTimeout(this.loadtimeout.bind(this),a.timeout)}},e.loadtimeout=function(){o.b.warn("timeout while loading "+this.context.url);var t=this.callbacks;t&&(this.abortInternal(),t.onTimeout(this.stats,this.context,this.loader))},e.loadprogress=function(t){var e=this.stats;e.loaded=t.loaded,t.lengthComputable&&(e.total=t.total)},e.getCacheAge=function(){var t=null;if(this.loader&&ue.test(this.loader.getAllResponseHeaders())){var e=this.loader.getResponseHeader("age");t=e?parseFloat(e):null}return t},t}(),he=r(16);function fe(t){var e="function"==typeof Map?new Map:void 0;return(fe=function(t){if(null===t||(r=t,-1===Function.toString.call(r).indexOf("[native code]")))return t;var r;if("function"!=typeof t)throw new TypeError("Super expression must either be null or a function");if(void 0!==e){if(e.has(t))return e.get(t);e.set(t,i)}function i(){return ce(t,arguments,pe(this).constructor)}return i.prototype=Object.create(t.prototype,{constructor:{value:i,enumerable:!1,writable:!0,configurable:!0}}),ge(i,t)})(t)}function ce(t,e,r){return(ce=ve()?Reflect.construct:function(t,e,r){var i=[null];i.push.apply(i,e);var a=new(Function.bind.apply(t,i));return r&&ge(a,r.prototype),a}).apply(null,arguments)}function ve(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(t){return!1}}function ge(t,e){return(ge=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}function pe(t){return(pe=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}function me(){return(me=Object.assign||function(t){for(var e=1;e=i&&a(e,r,n.flush(),t)):a(e,r,l,t),o()})).catch((function(){return Promise.reject()}))}()},t}();function be(t,e){return new self.Request(t.url,e)}var Te,Ee=function(t){var e,r;function i(e,r,i){var a;return(a=t.call(this,e)||this).code=void 0,a.details=void 0,a.code=r,a.details=i,a}return r=t,(e=i).prototype=Object.create(r.prototype),e.prototype.constructor=e,ge(e,r),i}(fe(Error)),Se=ye;!function(t){t.WIDEVINE="com.widevine.alpha",t.PLAYREADY="com.microsoft.playready"}(Te||(Te={}));var Le="undefined"!=typeof self&&self.navigator&&self.navigator.requestMediaKeySystemAccess?self.navigator.requestMediaKeySystemAccess.bind(self.navigator):null;function Ae(){return(Ae=Object.assign||function(t){for(var e=1;ee)return i;return 0}},{key:"maxAutoLevel",get:function(){var t=this.levels,e=this.autoLevelCapping;return-1===e&&t&&t.length?t.length-1:e}},{key:"nextAutoLevel",get:function(){return Math.min(Math.max(this.abrController.nextAutoLevel,this.minAutoLevel),this.maxAutoLevel)},set:function(t){this.abrController.nextAutoLevel=Math.max(this.minAutoLevel,t)}},{key:"audioTracks",get:function(){var t=this.audioTrackController;return t?t.audioTracks:[]}},{key:"audioTrack",get:function(){var t=this.audioTrackController;return t?t.audioTrack:-1},set:function(t){var e=this.audioTrackController;e&&(e.audioTrack=t)}},{key:"subtitleTracks",get:function(){var t=this.subtitleTrackController;return t?t.subtitleTracks:[]}},{key:"subtitleTrack",get:function(){var t=this.subtitleTrackController;return t?t.subtitleTrack:-1},set:function(t){var e=this.subtitleTrackController;e&&(e.subtitleTrack=t)}},{key:"media",get:function(){return this._media}},{key:"subtitleDisplay",get:function(){var t=this.subtitleTrackController;return!!t&&t.subtitleDisplay},set:function(t){var e=this.subtitleTrackController;e&&(e.subtitleDisplay=t)}},{key:"lowLatencyMode",get:function(){return this.config.lowLatencyMode},set:function(t){this.config.lowLatencyMode=t}},{key:"liveSyncPosition",get:function(){return this.latencyController.liveSyncPosition}},{key:"latency",get:function(){return this.latencyController.latency}},{key:"maxLatency",get:function(){return this.latencyController.maxLatency}},{key:"targetLatency",get:function(){return this.latencyController.targetLatency}},{key:"drift",get:function(){return this.latencyController.drift}},{key:"forceStartLoad",get:function(){return this.streamController.forceStartLoad}}])&&Ce(e.prototype,r),a&&Ce(e,a),t}();we.defaultConfig=void 0}]).default})); -// @license-end \ No newline at end of file +// @version v1.2.9 +"undefined"!=typeof window&&function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Hls=t():e.Hls=t()}(this,(()=>(()=>{var e={21:(e,t,r)=>{"use strict";r.r(t),r.d(t,{default:()=>u});var i=function(){function e(e,t){this.subtle=void 0,this.aesIV=void 0,this.subtle=e,this.aesIV=t}return e.prototype.decrypt=function(e,t){return this.subtle.decrypt({name:"AES-CBC",iv:this.aesIV},t,e)},e}(),n=function(){function e(e,t){this.subtle=void 0,this.key=void 0,this.subtle=e,this.key=t}return e.prototype.expandKey=function(){return this.subtle.importKey("raw",this.key,{name:"AES-CBC"},!1,["encrypt","decrypt"])},e}(),a=r(145),s=function(){function e(){this.rcon=[0,1,2,4,8,16,32,64,128,27,54],this.subMix=[new Uint32Array(256),new Uint32Array(256),new Uint32Array(256),new Uint32Array(256)],this.invSubMix=[new Uint32Array(256),new Uint32Array(256),new Uint32Array(256),new Uint32Array(256)],this.sBox=new Uint32Array(256),this.invSBox=new Uint32Array(256),this.key=new Uint32Array(0),this.ksRows=0,this.keySize=0,this.keySchedule=void 0,this.invKeySchedule=void 0,this.initTable()}var t=e.prototype;return t.uint8ArrayToUint32Array_=function(e){for(var t=new DataView(e),r=new Uint32Array(4),i=0;i<4;i++)r[i]=t.getUint32(4*i);return r},t.initTable=function(){var e=this.sBox,t=this.invSBox,r=this.subMix,i=r[0],n=r[1],a=r[2],s=r[3],o=this.invSubMix,l=o[0],u=o[1],d=o[2],h=o[3],f=new Uint32Array(256),c=0,v=0,g=0;for(g=0;g<256;g++)f[g]=g<128?g<<1:g<<1^283;for(g=0;g<256;g++){var p=v^v<<1^v<<2^v<<3^v<<4;p=p>>>8^255&p^99,e[c]=p,t[p]=c;var m=f[c],y=f[m],E=f[y],T=257*f[p]^16843008*p;i[c]=T<<24|T>>>8,n[c]=T<<16|T>>>16,a[c]=T<<8|T>>>24,s[c]=T,T=16843009*E^65537*y^257*m^16843008*c,l[p]=T<<24|T>>>8,u[p]=T<<16|T>>>16,d[p]=T<<8|T>>>24,h[p]=T,c?(c=m^f[f[f[E^m]]],v^=f[f[v]]):c=v=1}},t.expandKey=function(e){for(var t=this.uint8ArrayToUint32Array_(e),r=!0,i=0;i{"use strict";r.r(t),r.d(t,{canParse:()=>l,decodeFrame:()=>c,getID3Data:()=>s,getID3Frames:()=>f,getTimeStamp:()=>u,isFooter:()=>a,isHeader:()=>n,isTimeStampFrame:()=>d,testables:()=>E,utf8ArrayToStr:()=>y});var i,n=function(e,t){return t+10<=e.length&&73===e[t]&&68===e[t+1]&&51===e[t+2]&&e[t+3]<255&&e[t+4]<255&&e[t+6]<128&&e[t+7]<128&&e[t+8]<128&&e[t+9]<128},a=function(e,t){return t+10<=e.length&&51===e[t]&&68===e[t+1]&&73===e[t+2]&&e[t+3]<255&&e[t+4]<255&&e[t+6]<128&&e[t+7]<128&&e[t+8]<128&&e[t+9]<128},s=function(e,t){for(var r=t,i=0;n(e,t);)i+=10,i+=o(e,t+6),a(e,t+10)&&(i+=10),t+=i;if(i>0)return e.subarray(r,r+i)},o=function(e,t){var r=0;return r=(127&e[t])<<21,r|=(127&e[t+1])<<14,(r|=(127&e[t+2])<<7)|127&e[t+3]},l=function(e,t){return n(e,t)&&o(e,t+6)+10<=e.length-t},u=function(e){for(var t=f(e),r=0;r>4){case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:u+=String.fromCharCode(a);break;case 12:case 13:s=e[d++],u+=String.fromCharCode((31&a)<<6|63&s);break;case 14:s=e[d++],o=e[d++],u+=String.fromCharCode((15&a)<<12|(63&s)<<6|(63&o)<<0)}}return u},E={decodeTextFrame:g};function T(){return i||void 0===self.TextDecoder||(i=new self.TextDecoder("utf-8")),i}},182:(e,t,r)=>{"use strict";r.r(t),r.d(t,{default:()=>o});var i=r(764),n=r(851),a=r(93),s=r(729);function o(e){var t=new s.EventEmitter,r=function(t,r){e.postMessage({event:t,data:r})};t.on(n.Events.FRAG_DECRYPTED,r),t.on(n.Events.ERROR,r),e.addEventListener("message",(function(n){var s=n.data;switch(s.cmd){case"init":var o=JSON.parse(s.config);e.transmuxer=new i.default(t,s.typeSupported,o,s.vendor,s.id),(0,a.enableLogs)(o.debug,s.id),function(){var e=function(e){a.logger[e]=function(t){r("workerLog",{logType:e,message:t})}};for(var t in a.logger)e(t)}(),r("init",null);break;case"configure":e.transmuxer.configure(s.config);break;case"demux":var u=e.transmuxer.push(s.data,s.decryptdata,s.chunkMeta,s.state);(0,i.isPromise)(u)?u.then((function(t){l(e,t)})):l(e,u);break;case"flush":var h=s.chunkMeta,f=e.transmuxer.flush(h);(0,i.isPromise)(f)?f.then((function(t){d(e,t,h)})):d(e,f,h)}}))}function l(e,t){if(!((r=t.remuxResult).audio||r.video||r.text||r.id3||r.initSegment))return!1;var r,i=[],n=t.remuxResult,a=n.audio,s=n.video;return a&&u(i,a),s&&u(i,s),e.postMessage({event:"transmuxComplete",data:t},i),!0}function u(e,t){t.data1&&e.push(t.data1.buffer),t.data2&&e.push(t.data2.buffer)}function d(e,t,r){t.reduce((function(t,r){return l(e,r)||t}),!1)||e.postMessage({event:"transmuxComplete",data:t[0]}),e.postMessage({event:"flush",data:r})}},764:(e,t,r)=>{"use strict";r.r(t),r.d(t,{TransmuxConfig:()=>Ee,TransmuxState:()=>Te,default:()=>pe,isPromise:()=>ye});var i=r(851),n=r(973),a=r(21),s=r(965),o=r(181),l=r(856);function u(e,t){return void 0===e&&(e=""),void 0===t&&(t=9e4),{type:e,id:-1,pid:-1,inputTimeScale:t,sequenceNumber:-1,samples:[],dropped:0}}var d=r(63),h=r(145),f=function(){function e(){this._audioTrack=void 0,this._id3Track=void 0,this.frameIndex=0,this.cachedData=null,this.basePTS=null,this.initPTS=null,this.lastPTS=null}var t=e.prototype;return t.resetInitSegment=function(e,t,r,i){this._id3Track={type:"id3",id:3,pid:-1,inputTimeScale:9e4,sequenceNumber:0,samples:[],dropped:0}},t.resetTimeStamp=function(e){this.initPTS=e,this.resetContiguity()},t.resetContiguity=function(){this.basePTS=null,this.lastPTS=null,this.frameIndex=0},t.canParse=function(e,t){return!1},t.appendFrame=function(e,t,r){},t.demux=function(e,t){this.cachedData&&(e=(0,d.appendUint8Array)(this.cachedData,e),this.cachedData=null);var r,i=o.getID3Data(e,0),n=i?i.length:0,a=this._audioTrack,f=this._id3Track,v=i?o.getTimeStamp(i):void 0,g=e.length;for((null===this.basePTS||0===this.frameIndex&&(0,s.isFiniteNumber)(v))&&(this.basePTS=c(v,t,this.initPTS),this.lastPTS=this.basePTS),null===this.lastPTS&&(this.lastPTS=this.basePTS),i&&i.length>0&&f.samples.push({pts:this.lastPTS,dts:this.lastPTS,data:i,type:l.MetadataSchema.audioId3,duration:Number.POSITIVE_INFINITY});n>>5}function E(e,t){return t+1=e.length)return!1;var i=y(e,t);if(i<=r)return!1;var n=t+i;return n===e.length||E(e,n)}return!1}function S(e,t,r,a,s){if(!e.samplerate){var o=function(e,t,r,a){var s,o,l,u,d=navigator.userAgent.toLowerCase(),h=a,f=[96e3,88200,64e3,48e3,44100,32e3,24e3,22050,16e3,12e3,11025,8e3,7350];s=1+((192&t[r+2])>>>6);var c=(60&t[r+2])>>>2;if(!(c>f.length-1))return l=(1&t[r+2])<<2,l|=(192&t[r+3])>>>6,g.logger.log("manifest codec:"+a+", ADTS type:"+s+", samplingIndex:"+c),/firefox/i.test(d)?c>=6?(s=5,u=new Array(4),o=c-3):(s=2,u=new Array(2),o=c):-1!==d.indexOf("android")?(s=2,u=new Array(2),o=c):(s=5,u=new Array(4),a&&(-1!==a.indexOf("mp4a.40.29")||-1!==a.indexOf("mp4a.40.5"))||!a&&c>=6?o=c-3:((a&&-1!==a.indexOf("mp4a.40.2")&&(c>=6&&1===l||/vivaldi/i.test(d))||!a&&1===l)&&(s=2,u=new Array(2)),o=c)),u[0]=s<<3,u[0]|=(14&c)>>1,u[1]|=(1&c)<<7,u[1]|=l<<3,5===s&&(u[1]|=(14&o)>>1,u[2]=(1&o)<<7,u[2]|=8,u[3]=0),{config:u,samplerate:f[c],channelCount:l,codec:"mp4a.40."+s,manifestCodec:h};e.trigger(i.Events.ERROR,{type:n.ErrorTypes.MEDIA_ERROR,details:n.ErrorDetails.FRAG_PARSING_ERROR,fatal:!0,reason:"invalid ADTS sampling index:"+c})}(t,r,a,s);if(!o)return;e.config=o.config,e.samplerate=o.samplerate,e.channelCount=o.channelCount,e.codec=o.codec,e.manifestCodec=o.manifestCodec,g.logger.log("parsed codec:"+e.codec+", rate:"+o.samplerate+", channels:"+o.channelCount)}}function b(e){return 9216e4/e}function L(e,t,r,i,n){var a,s=i+n*b(e.samplerate),o=function(e,t){var r=m(e,t);if(t+r<=e.length){var i=y(e,t)-r;if(i>0)return{headerLength:r,frameLength:i}}}(t,r);if(o){var l=o.frameLength,u=o.headerLength,d=u+l,h=Math.max(0,r+d-t.length);h?(a=new Uint8Array(d-u)).set(t.subarray(r+u,t.length),0):a=t.subarray(r+u,r+d);var f={unit:a,pts:s};return h||e.samples.push(f),{sample:f,length:d,missing:h}}var c=t.length-r;return(a=new Uint8Array(c)).set(t.subarray(r,t.length),0),{sample:{unit:a,pts:s},length:c,missing:-1}}function A(e,t){return A=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},A(e,t)}const D=function(e){var t,r;function i(t,r){var i;return(i=e.call(this)||this).observer=void 0,i.config=void 0,i.observer=t,i.config=r,i}r=e,(t=i).prototype=Object.create(r.prototype),t.prototype.constructor=t,A(t,r);var n=i.prototype;return n.resetInitSegment=function(t,r,i,n){e.prototype.resetInitSegment.call(this,t,r,i,n),this._audioTrack={container:"audio/adts",type:"audio",id:2,pid:-1,sequenceNumber:0,segmentCodec:"aac",samples:[],manifestCodec:r,duration:n,inputTimeScale:9e4,dropped:0}},i.probe=function(e){if(!e)return!1;for(var t=(o.getID3Data(e,0)||[]).length,r=e.length;t16384?e.subarray(0,16384):e,(0,d.findBox)(e,["moof"]).length>0},t.demux=function(e,t){this.timeOffset=t;var r=e,i=this.videoTrack,n=this.txtTrack;if(this.config.progressive){this.remainderData&&(r=(0,d.appendUint8Array)(this.remainderData,e));var a=(0,d.segmentValidRange)(r);this.remainderData=a.remainder,i.samples=a.valid||new Uint8Array}else i.samples=r;var s=this.extractID3Track(i,t);return n.samples=(0,d.parseSamples)(t,i),{videoTrack:i,audioTrack:this.audioTrack,id3Track:s,textTrack:this.txtTrack}},t.flush=function(){var e=this.timeOffset,t=this.videoTrack,r=this.txtTrack;t.samples=this.remainderData||new Uint8Array,this.remainderData=null;var i=this.extractID3Track(t,this.timeOffset);return r.samples=(0,d.parseSamples)(e,t),{videoTrack:t,audioTrack:u(),id3Track:i,textTrack:u()}},t.extractID3Track=function(e,t){var r=this.id3Track;if(e.samples.length){var i=(0,d.findBox)(e.samples,["emsg"]);i&&i.forEach((function(e){var i=(0,d.parseEmsg)(e);if(R.test(i.schemeIdUri)){var n=(0,s.isFiniteNumber)(i.presentationTime)?i.presentationTime/i.timeScale:t+i.presentationTimeDelta/i.timeScale,a=4294967295===i.eventDuration?Number.POSITIVE_INFINITY:i.eventDuration/i.timeScale;a<=.001&&(a=Number.POSITIVE_INFINITY);var o=i.payload;r.samples.push({data:o,len:o.byteLength,dts:n,pts:n,type:l.MetadataSchema.emsg,duration:a})}}))}return r},t.demuxSampleAes=function(e,t,r){return Promise.reject(new Error("The MP4 demuxer does not support SAMPLE-AES decryption"))},t.destroy=function(){},e}();var _=null,x=[32,64,96,128,160,192,224,256,288,320,352,384,416,448,32,48,56,64,80,96,112,128,160,192,224,256,320,384,32,40,48,56,64,80,96,112,128,160,192,224,256,320,32,48,56,64,80,96,112,128,144,160,176,192,224,256,8,16,24,32,40,48,56,64,80,96,112,128,144,160],I=[44100,48e3,32e3,22050,24e3,16e3,11025,12e3,8e3],w=[[0,72,144,12],[0,0,0,0],[0,72,144,12],[0,144,144,12]],C=[0,1,1,4];function P(e,t,r,i,n){if(!(r+24>t.length)){var a=O(t,r);if(a&&r+a.frameLength<=t.length){var s=i+n*(9e4*a.samplesPerFrame/a.sampleRate),o={unit:t.subarray(r,r+a.frameLength),pts:s,dts:s};return e.config=[],e.channelCount=a.channelCount,e.samplerate=a.sampleRate,e.samples.push(o),{sample:o,length:a.frameLength,missing:0}}}}function O(e,t){var r=e[t+1]>>3&3,i=e[t+1]>>1&3,n=e[t+2]>>4&15,a=e[t+2]>>2&3;if(1!==r&&0!==n&&15!==n&&3!==a){var s=e[t+2]>>1&1,o=e[t+3]>>6,l=1e3*x[14*(3===r?3-i:3===i?3:4)+n-1],u=I[3*(3===r?0:2===r?1:2)+a],d=3===o?1:2,h=w[r][i],f=C[i],c=8*h*f,v=Math.floor(h*l/u+s)*f;if(null===_){var g=(navigator.userAgent||"").match(/Chrome\/(\d+)/i);_=g?parseInt(g[1]):0}return!!_&&_<=87&&2===i&&l>=224e3&&0===o&&(e[t+3]=128|e[t+3]),{sampleRate:u,channelCount:d,frameLength:v,samplesPerFrame:c}}}function F(e,t){return 255===e[t]&&224==(224&e[t+1])&&0!=(6&e[t+1])}function M(e,t){return t+1e?(this.word<<=e,this.bitsAvailable-=e):(e-=this.bitsAvailable,e-=(t=e>>3)>>3,this.bytesAvailable-=t,this.loadWord(),this.word<<=e,this.bitsAvailable-=e)},t.readBits=function(e){var t=Math.min(this.bitsAvailable,e),r=this.word>>>32-t;return e>32&&g.logger.error("Cannot read more than 32 bits at a time"),this.bitsAvailable-=t,this.bitsAvailable>0?this.word<<=t:this.bytesAvailable>0&&this.loadWord(),(t=e-t)>0&&this.bitsAvailable?r<>>e))return this.word<<=e,this.bitsAvailable-=e,e;return this.loadWord(),e+this.skipLZ()},t.skipUEG=function(){this.skipBits(1+this.skipLZ())},t.skipEG=function(){this.skipBits(1+this.skipLZ())},t.readUEG=function(){var e=this.skipLZ();return this.readBits(e+1)-1},t.readEG=function(){var e=this.readUEG();return 1&e?1+e>>>1:-1*(e>>>1)},t.readBoolean=function(){return 1===this.readBits(1)},t.readUByte=function(){return this.readBits(8)},t.readUShort=function(){return this.readBits(16)},t.readUInt=function(){return this.readBits(32)},t.skipScalingList=function(e){for(var t=8,r=8,i=0;i=e.length)return void r();if(!(e[t].unit.length<32)){var i=this.decrypter.isSync();if(this.decryptAacSample(e,t,r,i),!i)return}}},t.getAvcEncryptedData=function(e){for(var t=16*Math.floor((e.length-48)/160)+16,r=new Int8Array(t),i=0,n=32;n=e.length)return void i();for(var n=e[t].units;!(r>=n.length);r++){var a=n[r];if(!(a.data.length<=48||1!==a.type&&5!==a.type)){var s=this.decrypter.isSync();if(this.decryptAvcSample(e,t,r,i,a,s),!s)return}}}},e}();function G(){return G=Object.assign?Object.assign.bind():function(e){for(var t=1;t1;){var l=new Uint8Array(o[0].length+o[1].length);l.set(o[0]),l.set(o[1],o[0].length),o[0]=l,o.splice(1,1)}if(1===((t=o[0])[0]<<16)+(t[1]<<8)+t[2]){if((r=(t[4]<<8)+t[5])&&r>e.size-6)return null;var u=t[7];192&u&&(n=536870912*(14&t[9])+4194304*(255&t[10])+16384*(254&t[11])+128*(255&t[12])+(254&t[13])/2,64&u?n-(a=536870912*(14&t[14])+4194304*(255&t[15])+16384*(254&t[16])+128*(255&t[17])+(254&t[18])/2)>54e5&&(g.logger.warn(Math.round((n-a)/9e4)+"s delta between PTS and DTS, align them"),n=a):a=n);var d=(i=t[8])+9;if(e.size<=d)return null;e.size-=d;for(var h=new Uint8Array(e.size),f=0,c=o.length;fv){d-=v;continue}t=t.subarray(d),v-=d,d=0}h.set(t,s),s+=v}return r&&(r-=i+3),{data:h,pts:n,dts:a,len:r}}return null}function Y(e,t){if(e.units.length&&e.frame){if(void 0===e.pts){var r=t.samples,i=r.length;if(!i)return void t.dropped++;var n=r[i-1];e.pts=n.pts,e.dts=n.dts}t.samples.push(e)}e.debug.length&&g.logger.log(e.pts+"/"+e.dts+":"+e.debug)}const q=function(){function e(e,t,r){this.observer=void 0,this.config=void 0,this.typeSupported=void 0,this.sampleAes=null,this.pmtParsed=!1,this.audioCodec=void 0,this.videoCodec=void 0,this._duration=0,this._pmtId=-1,this._avcTrack=void 0,this._audioTrack=void 0,this._id3Track=void 0,this._txtTrack=void 0,this.aacOverFlow=null,this.avcSample=null,this.remainderData=null,this.observer=e,this.config=t,this.typeSupported=r}e.probe=function(t){var r=e.syncOffset(t);return r>0&&g.logger.warn("MPEG2-TS detected but first sync word found @ offset "+r),-1!==r},e.syncOffset=function(e){for(var t=Math.min(940,e.length-376)+1,r=0;r>4>1){if((x=R+5+t[R+4])===R+V)continue}else x=R+4;switch(_){case c:k&&(v&&(o=W(v))&&this.parseAVCPES(l,f,o,!1),v={data:[],size:0}),v&&(v.data.push(t.subarray(x,R+V)),v.size+=R+V-x);break;case p:if(k){if(y&&(o=W(y)))switch(u.segmentCodec){case"aac":this.parseAACPES(u,o);break;case"mp3":this.parseMPEGPES(u,o)}y={data:[],size:0}}y&&(y.data.push(t.subarray(x,R+V)),y.size+=R+V-x);break;case m:k&&(E&&(o=W(E))&&this.parseID3PES(h,o),E={data:[],size:0}),E&&(E.data.push(t.subarray(x,R+V)),E.size+=R+V-x);break;case 0:k&&(x+=t[x]+1),b=this._pmtId=j(t,x);break;case b:k&&(x+=t[x]+1);var I=K(t,x,this.typeSupported,a);(c=I.avc)>0&&(l.pid=c),(p=I.audio)>0&&(u.pid=p,u.segmentCodec=I.segmentCodec),(m=I.id3)>0&&(h.pid=m),null===T||S||(g.logger.log("unknown PID '"+T+"' in TS found"),T=null,R=A-188),S=this.pmtParsed=!0;break;case 17:case 8191:break;default:T=_}}else D++;D>0&&this.observer.emit(i.Events.ERROR,i.Events.ERROR,{type:n.ErrorTypes.MEDIA_ERROR,details:n.ErrorDetails.FRAG_PARSING_ERROR,fatal:!1,reason:"Found "+D+" TS packet/s that do not start with 0x47"}),l.pesData=v,u.pesData=y,h.pesData=E;var w={audioTrack:u,videoTrack:l,id3Track:h,textTrack:f};return s&&this.extractRemainingSamples(w),w},t.flush=function(){var e,t=this.remainderData;return this.remainderData=null,e=t?this.demux(t,-1,!1,!0):{videoTrack:this._avcTrack,audioTrack:this._audioTrack,id3Track:this._id3Track,textTrack:this._txtTrack},this.extractRemainingSamples(e),this.sampleAes?this.decrypt(e,this.sampleAes):e},t.extractRemainingSamples=function(e){var t,r=e.audioTrack,i=e.videoTrack,n=e.id3Track,a=e.textTrack,s=i.pesData,o=r.pesData,l=n.pesData;if(s&&(t=W(s))?(this.parseAVCPES(i,a,t,!0),i.pesData=null):i.pesData=s,o&&(t=W(o))){switch(r.segmentCodec){case"aac":this.parseAACPES(r,t);break;case"mp3":this.parseMPEGPES(r,t)}r.pesData=null}else null!=o&&o.size&&g.logger.log("last AAC PES packet truncated,might overlap between fragments"),r.pesData=o;l&&(t=W(l))?(this.parseID3PES(n,t),n.pesData=null):n.pesData=l},t.demuxSampleAes=function(e,t,r){var i=this.demux(e,r,!0,!this.config.progressive),n=this.sampleAes=new B(this.observer,this.config,t);return this.decrypt(i,n)},t.decrypt=function(e,t){return new Promise((function(r){var i=e.audioTrack,n=e.videoTrack;i.samples&&"aac"===i.segmentCodec?t.decryptAacSamples(i.samples,0,(function(){n.samples?t.decryptAvcSamples(n.samples,0,0,(function(){r(e)})):r(e)})):n.samples&&t.decryptAvcSamples(n.samples,0,0,(function(){r(e)}))}))},t.destroy=function(){this._duration=0},t.parseAVCPES=function(e,t,r,i){var n,a=this,s=this.parseAVCNALu(e,r.data),o=this.avcSample,l=!1;r.data=null,o&&s.length&&!e.audFound&&(Y(o,e),o=this.avcSample=H(!1,r.pts,r.dts,"")),s.forEach((function(i){switch(i.type){case 1:n=!0,o||(o=a.avcSample=H(!0,r.pts,r.dts,"")),o.frame=!0;var s=i.data;if(l&&s.length>4){var u=new U(s).readSliceType();2!==u&&4!==u&&7!==u&&9!==u||(o.key=!0)}break;case 5:n=!0,o||(o=a.avcSample=H(!0,r.pts,r.dts,"")),o.key=!0,o.frame=!0;break;case 6:n=!0,(0,d.parseSEIMessageFromNALu)(i.data,1,r.pts,t.samples);break;case 7:if(n=!0,l=!0,!e.sps){var h=new U(i.data).readSPS();e.width=h.width,e.height=h.height,e.pixelRatio=h.pixelRatio,e.sps=[i.data],e.duration=a._duration;for(var f=i.data.subarray(1,4),c="avc1.",v=0;v<3;v++){var g=f[v].toString(16);g.length<2&&(g="0"+g),c+=g}e.codec=c}break;case 8:n=!0,e.pps||(e.pps=[i.data]);break;case 9:n=!1,e.audFound=!0,o&&Y(o,e),o=a.avcSample=H(!1,r.pts,r.dts,"");break;case 12:n=!0;break;default:n=!1,o&&(o.debug+="unknown NAL "+i.type+" ")}o&&n&&o.units.push(i)})),i&&o&&(Y(o,e),this.avcSample=null)},t.getLastNalUnit=function(e){var t,r,i=this.avcSample;if(i&&0!==i.units.length||(i=e[e.length-1]),null!==(t=i)&&void 0!==t&&t.units){var n=i.units;r=n[n.length-1]}return r},t.parseAVCNALu=function(e,t){var r,i,n=t.byteLength,a=e.naluState||0,s=a,o=[],l=0,u=-1,d=0;for(-1===a&&(u=0,d=31&t[0],a=0,l=1);l=0){var h={data:t.subarray(u,l-a-1),type:d};o.push(h)}else{var f=this.getLastNalUnit(e.samples);if(f&&(s&&l<=4-s&&f.state&&(f.data=f.data.subarray(0,f.data.byteLength-s)),(i=l-a-1)>0)){var c=new Uint8Array(f.data.byteLength+i);c.set(f.data,0),c.set(t.subarray(0,i),f.data.byteLength),f.data=c,f.state=0}}l=0&&a>=0){var v={data:t.subarray(u,n),type:d,state:a};o.push(v)}if(0===o.length){var g=this.getLastNalUnit(e.samples);if(g){var p=new Uint8Array(g.data.byteLength+t.byteLength);p.set(g.data,0),p.set(t,g.data.byteLength),g.data=p}}return e.naluState=a,o},t.parseAACPES=function(e,t){var r,a,s,o,l,u=0,d=this.aacOverFlow,h=t.data;if(d){this.aacOverFlow=null;var f=d.missing,c=d.sample.unit.byteLength;if(-1===f){var v=new Uint8Array(c+h.byteLength);v.set(d.sample.unit,0),v.set(h,c),h=v}else{var p=c-f;d.sample.unit.set(h.subarray(0,f),p),e.samples.push(d.sample),u=d.missing}}for(r=u,a=h.length;r1?r-1:0),n=1;n>24&255,o[1]=t>>16&255,o[2]=t>>8&255,o[3]=255&t,o.set(e,4),a=0,t=8;a>24&255,t>>16&255,t>>8&255,255&t,i>>24,i>>16&255,i>>8&255,255&i,n>>24,n>>16&255,n>>8&255,255&n,85,196,0,0]))},e.mdia=function(t){return e.box(e.types.mdia,e.mdhd(t.timescale,t.duration),e.hdlr(t.type),e.minf(t))},e.mfhd=function(t){return e.box(e.types.mfhd,new Uint8Array([0,0,0,0,t>>24,t>>16&255,t>>8&255,255&t]))},e.minf=function(t){return"audio"===t.type?e.box(e.types.minf,e.box(e.types.smhd,e.SMHD),e.DINF,e.stbl(t)):e.box(e.types.minf,e.box(e.types.vmhd,e.VMHD),e.DINF,e.stbl(t))},e.moof=function(t,r,i){return e.box(e.types.moof,e.mfhd(t),e.traf(i,r))},e.moov=function(t){for(var r=t.length,i=[];r--;)i[r]=e.trak(t[r]);return e.box.apply(null,[e.types.moov,e.mvhd(t[0].timescale,t[0].duration)].concat(i).concat(e.mvex(t)))},e.mvex=function(t){for(var r=t.length,i=[];r--;)i[r]=e.trex(t[r]);return e.box.apply(null,[e.types.mvex].concat(i))},e.mvhd=function(t,r){r*=t;var i=Math.floor(r/($+1)),n=Math.floor(r%($+1)),a=new Uint8Array([1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,3,t>>24&255,t>>16&255,t>>8&255,255&t,i>>24,i>>16&255,i>>8&255,255&i,n>>24,n>>16&255,n>>8&255,255&n,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255]);return e.box(e.types.mvhd,a)},e.sdtp=function(t){var r,i,n=t.samples||[],a=new Uint8Array(4+n.length);for(r=0;r>>8&255),a.push(255&n),a=a.concat(Array.prototype.slice.call(i));for(r=0;r>>8&255),s.push(255&n),s=s.concat(Array.prototype.slice.call(i));var o=e.box(e.types.avcC,new Uint8Array([1,a[3],a[4],a[5],255,224|t.sps.length].concat(a).concat([t.pps.length]).concat(s))),l=t.width,u=t.height,d=t.pixelRatio[0],h=t.pixelRatio[1];return e.box(e.types.avc1,new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,l>>8&255,255&l,u>>8&255,255&u,0,72,0,0,0,72,0,0,0,0,0,0,0,1,18,100,97,105,108,121,109,111,116,105,111,110,47,104,108,115,46,106,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,17,17]),o,e.box(e.types.btrt,new Uint8Array([0,28,156,128,0,45,198,192,0,45,198,192])),e.box(e.types.pasp,new Uint8Array([d>>24,d>>16&255,d>>8&255,255&d,h>>24,h>>16&255,h>>8&255,255&h])))},e.esds=function(e){var t=e.config.length;return new Uint8Array([0,0,0,0,3,23+t,0,1,0,4,15+t,64,21,0,0,0,0,0,0,0,0,0,0,0,5].concat([t]).concat(e.config).concat([6,1,2]))},e.mp4a=function(t){var r=t.samplerate;return e.box(e.types.mp4a,new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,t.channelCount,0,16,0,0,0,0,r>>8&255,255&r,0,0]),e.box(e.types.esds,e.esds(t)))},e.mp3=function(t){var r=t.samplerate;return e.box(e.types[".mp3"],new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,t.channelCount,0,16,0,0,0,0,r>>8&255,255&r,0,0]))},e.stsd=function(t){return"audio"===t.type?"mp3"===t.segmentCodec&&"mp3"===t.codec?e.box(e.types.stsd,e.STSD,e.mp3(t)):e.box(e.types.stsd,e.STSD,e.mp4a(t)):e.box(e.types.stsd,e.STSD,e.avc1(t))},e.tkhd=function(t){var r=t.id,i=t.duration*t.timescale,n=t.width,a=t.height,s=Math.floor(i/($+1)),o=Math.floor(i%($+1));return e.box(e.types.tkhd,new Uint8Array([1,0,0,7,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,3,r>>24&255,r>>16&255,r>>8&255,255&r,0,0,0,0,s>>24,s>>16&255,s>>8&255,255&s,o>>24,o>>16&255,o>>8&255,255&o,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,n>>8&255,255&n,0,0,a>>8&255,255&a,0,0]))},e.traf=function(t,r){var i=e.sdtp(t),n=t.id,a=Math.floor(r/($+1)),s=Math.floor(r%($+1));return e.box(e.types.traf,e.box(e.types.tfhd,new Uint8Array([0,0,0,0,n>>24,n>>16&255,n>>8&255,255&n])),e.box(e.types.tfdt,new Uint8Array([1,0,0,0,a>>24,a>>16&255,a>>8&255,255&a,s>>24,s>>16&255,s>>8&255,255&s])),e.trun(t,i.length+16+20+8+16+8+8),i)},e.trak=function(t){return t.duration=t.duration||4294967295,e.box(e.types.trak,e.tkhd(t),e.mdia(t))},e.trex=function(t){var r=t.id;return e.box(e.types.trex,new Uint8Array([0,0,0,0,r>>24,r>>16&255,r>>8&255,255&r,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1]))},e.trun=function(t,r){var i,n,a,s,o,l,u=t.samples||[],d=u.length,h=12+16*d,f=new Uint8Array(h);for(r+=8+h,f.set(["video"===t.type?1:0,0,15,1,d>>>24&255,d>>>16&255,d>>>8&255,255&d,r>>>24&255,r>>>16&255,r>>>8&255,255&r],0),i=0;i>>24&255,a>>>16&255,a>>>8&255,255&a,s>>>24&255,s>>>16&255,s>>>8&255,255&s,o.isLeading<<2|o.dependsOn,o.isDependedOn<<6|o.hasRedundancy<<4|o.paddingValue<<1|o.isNonSync,61440&o.degradPrio,15&o.degradPrio,l>>>24&255,l>>>16&255,l>>>8&255,255&l],12+16*i);return e.box(e.types.trun,f)},e.initSegment=function(t){e.types||e.init();var r=e.moov(t),i=new Uint8Array(e.FTYP.byteLength+r.byteLength);return i.set(e.FTYP),i.set(r,e.FTYP.byteLength),i},e}();Z.types=void 0,Z.HDLR_TYPES=void 0,Z.STTS=void 0,Z.STSC=void 0,Z.STCO=void 0,Z.STSZ=void 0,Z.VMHD=void 0,Z.SMHD=void 0,Z.STSD=void 0,Z.FTYP=void 0,Z.DINF=void 0;const J=Z;var ee=r(308);function te(e,t){return void 0===t&&(t=!1),function(e,t,r,i){void 0===r&&(r=1),void 0===i&&(i=!1);var n=e*t*r;return i?Math.round(n):n}(e,1e3,1/9e4,t)}function re(){return re=Object.assign?Object.assign.bind():function(e){for(var t=1;t0?e:r.pts}),e[0].pts);return t&&g.logger.debug("PTS rollover detected"),r},t.remux=function(e,t,r,i,n,a,s,o){var l,u,d,h,f,c,v=n,p=n,m=e.pid>-1,y=t.pid>-1,E=t.samples.length,T=e.samples.length>0,S=s&&E>0||E>1;if((!m||T)&&(!y||S)||this.ISGenerated||s){this.ISGenerated||(d=this.generateIS(e,t,n));var b,L=this.isVideoContiguous,A=-1;if(S&&(A=function(e){for(var t=0;t0){g.logger.warn("[mp4-remuxer]: Dropped "+A+" out of "+E+" video samples due to a missing keyframe");var D=this.getVideoStartPts(t.samples);t.samples=t.samples.slice(A),t.dropped+=A,b=p+=(t.samples[0].pts-D)/t.inputTimeScale}else-1===A&&(g.logger.warn("[mp4-remuxer]: No keyframe found out of "+E+" video samples"),c=!1);if(this.ISGenerated){if(T&&S){var R=this.getVideoStartPts(t.samples),k=(se(e.samples[0].pts,R)-R)/t.inputTimeScale;v+=Math.max(0,k),p+=Math.max(0,-k)}if(T){if(e.samplerate||(g.logger.warn("[mp4-remuxer]: regenerate InitSegment as audio detected"),d=this.generateIS(e,t,n)),u=this.remuxAudio(e,v,this.isAudioContiguous,a,y||S||o===ee.PlaylistLevelType.AUDIO?p:void 0),S){var _=u?u.endPTS-u.startPTS:0;t.inputTimeScale||(g.logger.warn("[mp4-remuxer]: regenerate InitSegment as video detected"),d=this.generateIS(e,t,n)),l=this.remuxVideo(t,p,L,_)}}else S&&(l=this.remuxVideo(t,p,L,0));l&&(l.firstKeyFrame=A,l.independent=-1!==A,l.firstKeyFramePTS=b)}}return this.ISGenerated&&(r.samples.length&&(f=oe(r,n,this._initPTS,this._initDTS)),i.samples.length&&(h=le(i,n,this._initPTS))),{audio:u,video:l,initSegment:d,independent:c,text:h,id3:f}},t.generateIS=function(e,t,r){var i,n,a,o=e.samples,l=t.samples,u=this.typeSupported,d={},h=!(0,s.isFiniteNumber)(this._initPTS),f="audio/mp4";if(h&&(i=n=1/0),e.config&&o.length&&(e.timescale=e.samplerate,"mp3"===e.segmentCodec&&(u.mpeg?(f="audio/mpeg",e.codec=""):u.mp3&&(e.codec="mp3")),d.audio={id:"audio",container:f,codec:e.codec,initSegment:"mp3"===e.segmentCodec&&u.mpeg?new Uint8Array(0):J.initSegment([e]),metadata:{channelCount:e.channelCount}},h&&(a=e.inputTimeScale,i=n=o[0].pts-Math.round(a*r))),t.sps&&t.pps&&l.length&&(t.timescale=t.inputTimeScale,d.video={id:"main",container:"video/mp4",codec:t.codec,initSegment:J.initSegment([t]),metadata:{width:t.width,height:t.height}},h)){a=t.inputTimeScale;var c=this.getVideoStartPts(l),v=Math.round(a*r);n=Math.min(n,se(l[0].dts,c)-v),i=Math.min(i,c-v)}if(Object.keys(d).length)return this.ISGenerated=!0,h&&(this._initPTS=i,this._initDTS=n),{tracks:d,initPTS:i,timescale:a}},t.remuxVideo=function(e,t,r,a){var s,o,l=e.inputTimeScale,u=e.samples,d=[],h=u.length,f=this._initPTS,c=this.nextAvcDts,v=8,p=this.videoSampleDuration,m=Number.POSITIVE_INFINITY,y=Number.NEGATIVE_INFINITY,E=!1;r&&null!==c||(c=t*l-(u[0].pts-se(u[0].dts,u[0].pts)));for(var T=0;T0?T-1:T].dts&&(E=!0)}E&&u.sort((function(e,t){var r=e.dts-t.dts,i=e.pts-t.pts;return r||i})),s=u[0].dts;var b=u[u.length-1].dts-s,L=b?Math.round(b/(h-1)):p||e.inputTimeScale/30;if(r){var A=s-c,D=A>L,R=A<-1;if((D||R)&&(D?g.logger.warn("AVC: "+te(A,!0)+" ms ("+A+"dts) hole between fragments detected, filling it"):g.logger.warn("AVC: "+te(-A,!0)+" ms ("+A+"dts) overlapping between fragments detected"),!R||c>u[0].pts)){s=c;var k=u[0].pts-A;u[0].dts=s,u[0].pts=k,g.logger.log("Video: First PTS/DTS adjusted: "+te(k,!0)+"/"+te(s,!0)+", delta: "+te(A,!0)+" ms")}}s=Math.max(0,s);for(var _=0,x=0,I=0;I0?W.dts-u[K-1].dts:L;if(ee=K>0?W.pts-u[K-1].pts:L,ae.stretchShortVideoTrack&&null!==this.nextAudioPts){var le=Math.floor(ae.maxBufferHole*l),de=(a?m+a*l:this.nextAudioPts)-W.pts;de>le?((p=de-oe)<0?p=oe:B=!0,g.logger.log("[mp4-remuxer]: It is approximately "+de/90+" ms to the next segment; using duration "+p/90+" ms for the last video frame.")):p=oe}else p=oe}var he=Math.round(W.pts-W.dts);G=Math.min(G,p),H=Math.max(H,p),V=Math.min(V,ee),j=Math.max(j,ee),d.push(new ue(W.key,p,q,he))}if(d.length)if(ie){if(ie<70){var fe=d[0].flags;fe.dependsOn=2,fe.isNonSync=0}}else if(ne&&j-V0&&(a&&Math.abs(E-y)<9e3||Math.abs(se(p[0].pts-h,E)-y)<20*d),p.forEach((function(e){e.pts=se(e.pts-h,E)})),!r||y<0){if(p=p.filter((function(e){return e.pts>=0})),!p.length)return;y=0===s?0:a&&!v?Math.max(0,E):p[0].pts}if("aac"===e.segmentCodec)for(var T=this.config.maxAudioFramesDrift,S=0,b=y;S=T*d&&R<1e4&&v){var k=Math.round(D/d);(b=A-k*d)<0&&(k--,b+=d),0===S&&(this.nextAudioPts=y=b),g.logger.warn("[mp4-remuxer]: Injecting "+k+" audio frame @ "+(b/o).toFixed(3)+"s due to "+Math.round(1e3*D/o)+" ms gap.");for(var _=0;_0))return;O+=m;try{w=new Uint8Array(O)}catch(e){return void this.observer.emit(i.Events.ERROR,i.Events.ERROR,{type:n.ErrorTypes.MUX_ERROR,details:n.ErrorDetails.REMUX_ALLOC_ERROR,fatal:!1,bytes:O,reason:"fail allocating audio mdat "+O})}f||(new DataView(w.buffer).setUint32(0,O),w.set(J.types.mdat,4))}w.set(B,m);var V=B.byteLength;m+=V,c.push(new ue(!0,u,V,0)),P=G}var H=c.length;if(H){var j=c[c.length-1];this.nextAudioPts=y=P+l*j.duration;var K=f?new Uint8Array(0):J.moof(e.sequenceNumber++,C/l,re({},e,{samples:c}));e.samples=[];var W=C/o,Y=y/o,q={data1:K,data2:w,startPTS:W,endPTS:Y,startDTS:W,endDTS:Y,type:"audio",hasAudio:!0,hasVideo:!1,nb:H};return this.isAudioContiguous=!0,q}},t.remuxEmptyAudio=function(e,t,r,i){var n=e.inputTimeScale,a=n/(e.samplerate?e.samplerate:n),s=this.nextAudioPts,o=(null!==s?s:i.startDTS*n)+this._initDTS,l=i.endDTS*n+this._initDTS,u=1024*a,d=Math.ceil((l-o)/u),h=Q.getSilentFrame(e.manifestCodec||e.codec,e.channelCount);if(g.logger.warn("[mp4-remuxer]: remux empty Audio"),h){for(var f=[],c=0;c4294967296;)e+=r;return e}function oe(e,t,r,i){var n=e.samples.length;if(n){for(var a=e.inputTimeScale,s=0;s4?r:"hvc1"===r||"hev1"===r?"hvc1.1.c.L120.90":"av01"===r?"av01.0.04M.08":"avc1"===r||t===he.ElementaryStreamTypes.VIDEO?"avc1.42e01e":"mp4a.40.5"}const ce=function(){function e(){this.emitInitSegment=!1,this.audioCodec=void 0,this.videoCodec=void 0,this.initData=void 0,this.initPTS=void 0,this.initTracks=void 0,this.lastEndTime=null}var t=e.prototype;return t.destroy=function(){},t.resetTimeStamp=function(e){this.initPTS=e,this.lastEndTime=null},t.resetNextTimestamp=function(){this.lastEndTime=null},t.resetInitSegment=function(e,t,r){this.audioCodec=t,this.videoCodec=r,this.generateInitSegment(e),this.emitInitSegment=!0},t.generateInitSegment=function(e){var t=this.audioCodec,r=this.videoCodec;if(!e||!e.byteLength)return this.initTracks=void 0,void(this.initData=void 0);var i=this.initData=(0,d.parseInitSegment)(e);t||(t=fe(i.audio,he.ElementaryStreamTypes.AUDIO)),r||(r=fe(i.video,he.ElementaryStreamTypes.VIDEO));var n={};i.audio&&i.video?n.audiovideo={container:"video/mp4",codec:t+","+r,initSegment:e,id:"main"}:i.audio?n.audio={container:"audio/mp4",codec:t,initSegment:e,id:"audio"}:i.video?n.video={container:"video/mp4",codec:r,initSegment:e,id:"main"}:g.logger.warn("[passthrough-remuxer.ts]: initSegment does not contain moov or trak boxes."),this.initTracks=n},t.remux=function(e,t,r,i,n){var a,o=this.initPTS,l=this.lastEndTime,u={audio:void 0,video:void 0,text:i,id3:r,initSegment:void 0};(0,s.isFiniteNumber)(l)||(l=this.lastEndTime=n||0);var h=t.samples;if(!h||!h.length)return u;var f={initPTS:void 0,timescale:1},c=this.initData;if(c&&c.length||(this.generateInitSegment(h),c=this.initData),!c||!c.length)return g.logger.warn("[passthrough-remuxer.ts]: Failed to generate initSegment."),u;this.emitInitSegment&&(f.tracks=this.initTracks,this.emitInitSegment=!1);var v=(0,d.getStartDTS)(c,h);(0,s.isFiniteNumber)(o)||(this.initPTS=f.initPTS=o=v-n);var p=(0,d.getDuration)(h,c),m=e?v-o:l,y=m+p;(0,d.offsetStartDTS)(c,h,o),p>0?this.lastEndTime=y:(g.logger.warn("Duration parsed from mp4 should be greater than zero"),this.resetNextTimestamp());var E=!!c.audio,T=!!c.video,S="";E&&(S+="audio"),T&&(S+="video");var b={data1:h,startPTS:m,startDTS:m,endPTS:y,endDTS:y,type:S,hasAudio:E,hasVideo:T,nb:1,dropped:0};u.audio="audio"===b.type?b:void 0,u.video="audio"!==b.type?b:void 0,u.initSegment=f;var L=null!=(a=this.initPTS)?a:0;return u.id3=oe(r,n,L,L),i.samples.length&&(u.text=le(i,n,L)),u},e}();var ve;try{ve=self.performance.now.bind(self.performance)}catch(e){g.logger.debug("Unable to use Performance API on this environment"),ve=self.Date.now}var ge=[{demux:q,remux:ae},{demux:k,remux:ce},{demux:D,remux:ae},{demux:X,remux:ae}],pe=function(){function e(e,t,r,i,n){this.observer=void 0,this.typeSupported=void 0,this.config=void 0,this.vendor=void 0,this.id=void 0,this.demuxer=void 0,this.remuxer=void 0,this.decrypter=void 0,this.probe=void 0,this.decryptionPromise=null,this.transmuxConfig=void 0,this.currentTransmuxState=void 0,this.observer=e,this.typeSupported=t,this.config=r,this.vendor=i,this.id=n}var t=e.prototype;return t.configure=function(e){this.transmuxConfig=e,this.decrypter&&this.decrypter.reset()},t.push=function(e,t,r,i){var n=this,a=r.transmuxing;a.executeStart=ve();var s=new Uint8Array(e),o=this.config,l=this.currentTransmuxState,u=this.transmuxConfig;i&&(this.currentTransmuxState=i);var d=i||l,h=d.contiguous,f=d.discontinuity,c=d.trackSwitch,v=d.accurateTimeOffset,g=d.timeOffset,p=d.initSegmentChange,m=u.audioCodec,y=u.videoCodec,E=u.defaultInitPts,T=u.duration,S=u.initSegmentData;(f||c||p)&&this.resetInitSegment(S,m,y,T),(f||p)&&this.resetInitialTimestamp(E),h||this.resetContiguity();var b=function(e,t){var r=null;return e.byteLength>0&&null!=t&&null!=t.key&&null!==t.iv&&null!=t.method&&(r=t),r}(s,t);if(b&&"AES-128"===b.method){var L=this.getDecrypter();if(!o.enableSoftwareAES)return this.decryptionPromise=L.webCryptoDecrypt(s,b.key.buffer,b.iv.buffer).then((function(e){var t=n.push(e,null,r);return n.decryptionPromise=null,t})),this.decryptionPromise;var A=L.softwareDecrypt(s,b.key.buffer,b.iv.buffer);if(!A)return a.executeEnd=ve(),me(r);s=new Uint8Array(A)}this.needsProbing(s,f,c)&&this.configureTransmuxer(s,u);var D=this.transmux(s,b,g,v,r),R=this.currentTransmuxState;return R.contiguous=!0,R.discontinuity=!1,R.trackSwitch=!1,a.executeEnd=ve(),D},t.flush=function(e){var t=this,r=e.transmuxing;r.executeStart=ve();var a=this.decrypter,s=this.currentTransmuxState,o=this.decryptionPromise;if(o)return o.then((function(){return t.flush(e)}));var l=[],u=s.timeOffset;if(a){var d=a.flush();d&&l.push(this.push(d,null,e))}var h=this.demuxer,f=this.remuxer;if(!h||!f)return this.observer.emit(i.Events.ERROR,i.Events.ERROR,{type:n.ErrorTypes.MEDIA_ERROR,details:n.ErrorDetails.FRAG_PARSING_ERROR,fatal:!0,reason:"no demux matching with content found"}),r.executeEnd=ve(),[me(e)];var c=h.flush(u);return ye(c)?c.then((function(r){return t.flushRemux(l,r,e),l})):(this.flushRemux(l,c,e),l)},t.flushRemux=function(e,t,r){var i=t.audioTrack,n=t.videoTrack,a=t.id3Track,s=t.textTrack,o=this.currentTransmuxState,l=o.accurateTimeOffset,u=o.timeOffset;g.logger.log("[transmuxer.ts]: Flushed fragment "+r.sn+(r.part>-1?" p: "+r.part:"")+" of level "+r.level);var d=this.remuxer.remux(i,n,a,s,u,l,!0,this.id);e.push({remuxResult:d,chunkMeta:r}),r.transmuxing.executeEnd=ve()},t.resetInitialTimestamp=function(e){var t=this.demuxer,r=this.remuxer;t&&r&&(t.resetTimeStamp(e),r.resetTimeStamp(e))},t.resetContiguity=function(){var e=this.demuxer,t=this.remuxer;e&&t&&(e.resetContiguity(),t.resetNextTimestamp())},t.resetInitSegment=function(e,t,r,i){var n=this.demuxer,a=this.remuxer;n&&a&&(n.resetInitSegment(e,t,r,i),a.resetInitSegment(e,t,r))},t.destroy=function(){this.demuxer&&(this.demuxer.destroy(),this.demuxer=void 0),this.remuxer&&(this.remuxer.destroy(),this.remuxer=void 0)},t.transmux=function(e,t,r,i,n){return t&&"SAMPLE-AES"===t.method?this.transmuxSampleAes(e,t,r,i,n):this.transmuxUnencrypted(e,r,i,n)},t.transmuxUnencrypted=function(e,t,r,i){var n=this.demuxer.demux(e,t,!1,!this.config.progressive),a=n.audioTrack,s=n.videoTrack,o=n.id3Track,l=n.textTrack;return{remuxResult:this.remuxer.remux(a,s,o,l,t,r,!1,this.id),chunkMeta:i}},t.transmuxSampleAes=function(e,t,r,i,n){var a=this;return this.demuxer.demuxSampleAes(e,t,r).then((function(e){return{remuxResult:a.remuxer.remux(e.audioTrack,e.videoTrack,e.id3Track,e.textTrack,r,i,!1,a.id),chunkMeta:n}}))},t.configureTransmuxer=function(e,t){for(var r,i=this.config,n=this.observer,a=this.typeSupported,s=this.vendor,o=t.audioCodec,l=t.defaultInitPts,u=t.duration,d=t.initSegmentData,h=t.videoCodec,f=0,c=ge.length;f{e.exports=void 0},973:(e,t,r)=>{"use strict";var i,n;r.r(t),r.d(t,{ErrorDetails:()=>n,ErrorTypes:()=>i}),function(e){e.NETWORK_ERROR="networkError",e.MEDIA_ERROR="mediaError",e.KEY_SYSTEM_ERROR="keySystemError",e.MUX_ERROR="muxError",e.OTHER_ERROR="otherError"}(i||(i={})),function(e){e.KEY_SYSTEM_NO_KEYS="keySystemNoKeys",e.KEY_SYSTEM_NO_ACCESS="keySystemNoAccess",e.KEY_SYSTEM_NO_SESSION="keySystemNoSession",e.KEY_SYSTEM_LICENSE_REQUEST_FAILED="keySystemLicenseRequestFailed",e.KEY_SYSTEM_NO_INIT_DATA="keySystemNoInitData",e.MANIFEST_LOAD_ERROR="manifestLoadError",e.MANIFEST_LOAD_TIMEOUT="manifestLoadTimeOut",e.MANIFEST_PARSING_ERROR="manifestParsingError",e.MANIFEST_INCOMPATIBLE_CODECS_ERROR="manifestIncompatibleCodecsError",e.LEVEL_EMPTY_ERROR="levelEmptyError",e.LEVEL_LOAD_ERROR="levelLoadError",e.LEVEL_LOAD_TIMEOUT="levelLoadTimeOut",e.LEVEL_SWITCH_ERROR="levelSwitchError",e.AUDIO_TRACK_LOAD_ERROR="audioTrackLoadError",e.AUDIO_TRACK_LOAD_TIMEOUT="audioTrackLoadTimeOut",e.SUBTITLE_LOAD_ERROR="subtitleTrackLoadError",e.SUBTITLE_TRACK_LOAD_TIMEOUT="subtitleTrackLoadTimeOut",e.FRAG_LOAD_ERROR="fragLoadError",e.FRAG_LOAD_TIMEOUT="fragLoadTimeOut",e.FRAG_DECRYPT_ERROR="fragDecryptError",e.FRAG_PARSING_ERROR="fragParsingError",e.REMUX_ALLOC_ERROR="remuxAllocError",e.KEY_LOAD_ERROR="keyLoadError",e.KEY_LOAD_TIMEOUT="keyLoadTimeOut",e.BUFFER_ADD_CODEC_ERROR="bufferAddCodecError",e.BUFFER_INCOMPATIBLE_CODECS_ERROR="bufferIncompatibleCodecsError",e.BUFFER_APPEND_ERROR="bufferAppendError",e.BUFFER_APPENDING_ERROR="bufferAppendingError",e.BUFFER_STALLED_ERROR="bufferStalledError",e.BUFFER_FULL_ERROR="bufferFullError",e.BUFFER_SEEK_OVER_HOLE="bufferSeekOverHole",e.BUFFER_NUDGE_ON_STALL="bufferNudgeOnStall",e.INTERNAL_EXCEPTION="internalException",e.INTERNAL_ABORTED="aborted",e.UNKNOWN="unknown"}(n||(n={}))},851:(e,t,r)=>{"use strict";var i;r.r(t),r.d(t,{Events:()=>i}),function(e){e.MEDIA_ATTACHING="hlsMediaAttaching",e.MEDIA_ATTACHED="hlsMediaAttached",e.MEDIA_DETACHING="hlsMediaDetaching",e.MEDIA_DETACHED="hlsMediaDetached",e.BUFFER_RESET="hlsBufferReset",e.BUFFER_CODECS="hlsBufferCodecs",e.BUFFER_CREATED="hlsBufferCreated",e.BUFFER_APPENDING="hlsBufferAppending",e.BUFFER_APPENDED="hlsBufferAppended",e.BUFFER_EOS="hlsBufferEos",e.BUFFER_FLUSHING="hlsBufferFlushing",e.BUFFER_FLUSHED="hlsBufferFlushed",e.MANIFEST_LOADING="hlsManifestLoading",e.MANIFEST_LOADED="hlsManifestLoaded",e.MANIFEST_PARSED="hlsManifestParsed",e.LEVEL_SWITCHING="hlsLevelSwitching",e.LEVEL_SWITCHED="hlsLevelSwitched",e.LEVEL_LOADING="hlsLevelLoading",e.LEVEL_LOADED="hlsLevelLoaded",e.LEVEL_UPDATED="hlsLevelUpdated",e.LEVEL_PTS_UPDATED="hlsLevelPtsUpdated",e.LEVELS_UPDATED="hlsLevelsUpdated",e.AUDIO_TRACKS_UPDATED="hlsAudioTracksUpdated",e.AUDIO_TRACK_SWITCHING="hlsAudioTrackSwitching",e.AUDIO_TRACK_SWITCHED="hlsAudioTrackSwitched",e.AUDIO_TRACK_LOADING="hlsAudioTrackLoading",e.AUDIO_TRACK_LOADED="hlsAudioTrackLoaded",e.SUBTITLE_TRACKS_UPDATED="hlsSubtitleTracksUpdated",e.SUBTITLE_TRACKS_CLEARED="hlsSubtitleTracksCleared",e.SUBTITLE_TRACK_SWITCH="hlsSubtitleTrackSwitch",e.SUBTITLE_TRACK_LOADING="hlsSubtitleTrackLoading",e.SUBTITLE_TRACK_LOADED="hlsSubtitleTrackLoaded",e.SUBTITLE_FRAG_PROCESSED="hlsSubtitleFragProcessed",e.CUES_PARSED="hlsCuesParsed",e.NON_NATIVE_TEXT_TRACKS_FOUND="hlsNonNativeTextTracksFound",e.INIT_PTS_FOUND="hlsInitPtsFound",e.FRAG_LOADING="hlsFragLoading",e.FRAG_LOAD_EMERGENCY_ABORTED="hlsFragLoadEmergencyAborted",e.FRAG_LOADED="hlsFragLoaded",e.FRAG_DECRYPTED="hlsFragDecrypted",e.FRAG_PARSING_INIT_SEGMENT="hlsFragParsingInitSegment",e.FRAG_PARSING_USERDATA="hlsFragParsingUserdata",e.FRAG_PARSING_METADATA="hlsFragParsingMetadata",e.FRAG_PARSED="hlsFragParsed",e.FRAG_BUFFERED="hlsFragBuffered",e.FRAG_CHANGED="hlsFragChanged",e.FPS_DROP="hlsFpsDrop",e.FPS_DROP_LEVEL_CAPPING="hlsFpsDropLevelCapping",e.ERROR="hlsError",e.DESTROYING="hlsDestroying",e.KEY_LOADING="hlsKeyLoading",e.KEY_LOADED="hlsKeyLoaded",e.LIVE_BACK_BUFFER_REACHED="hlsLiveBackBufferReached",e.BACK_BUFFER_REACHED="hlsBackBufferReached"}(i||(i={}))},392:(e,t,r)=>{"use strict";r.r(t),r.d(t,{default:()=>Ht});var i,n=r(945),a=r(965),s=r(851),o=r(973),l=r(93),u=r(63),d=/^(\d+)x(\d+)$/,h=/\s*(.+?)\s*=((?:\".*?\")|.*?)(?:,|$)/g,f=function(){function e(t){for(var r in"string"==typeof t&&(t=e.parseAttrList(t)),t)t.hasOwnProperty(r)&&(this[r]=t[r])}var t=e.prototype;return t.decimalInteger=function(e){var t=parseInt(this[e],10);return t>Number.MAX_SAFE_INTEGER?1/0:t},t.hexadecimalInteger=function(e){if(this[e]){var t=(this[e]||"0x").slice(2);t=(1&t.length?"0":"")+t;for(var r=new Uint8Array(t.length/2),i=0;iNumber.MAX_SAFE_INTEGER?1/0:t},t.decimalFloatingPoint=function(e){return parseFloat(this[e])},t.optionalFloat=function(e,t){var r=this[e];return r?parseFloat(r):t},t.enumeratedString=function(e){return this[e]},t.bool=function(e){return"YES"===this[e]},t.decimalResolution=function(e){var t=d.exec(this[e]);if(null!==t)return{width:parseInt(t[1],10),height:parseInt(t[2],10)}},e.parseAttrList=function(e){var t,r={};for(h.lastIndex=0;null!==(t=h.exec(e));){var i=t[2];0===i.indexOf('"')&&i.lastIndexOf('"')===i.length-1&&(i=i.slice(1,-1)),r[t[1]]=i}return r},e}();function c(){return c=Object.assign?Object.assign.bind():function(e){for(var t=1;t=0)&&(!this.endOnNext||!!this.class)}}])&&v(t.prototype,r),Object.defineProperty(t,"prototype",{writable:!1}),e}(),p=r(923);function m(e,t){for(var r=0;re.endSN||t>0||0===t&&r>0,this.updated||this.advanced?this.misses=Math.floor(.6*e.misses):this.misses=e.misses+1,this.availabilityDelay=e.availabilityDelay},t=e,(r=[{key:"hasProgramDateTime",get:function(){return!!this.fragments.length&&(0,a.isFiniteNumber)(this.fragments[this.fragments.length-1].programDateTime)}},{key:"levelTargetDuration",get:function(){return this.averagetargetduration||this.targetduration||10}},{key:"drift",get:function(){var e=this.driftEndTime-this.driftStartTime;return e>0?1e3*(this.driftEnd-this.driftStart)/e:1}},{key:"edge",get:function(){return this.partEnd||this.fragmentEnd}},{key:"partEnd",get:function(){var e;return null!==(e=this.partList)&&void 0!==e&&e.length?this.partList[this.partList.length-1].end:this.fragmentEnd}},{key:"fragmentEnd",get:function(){var e;return null!==(e=this.fragments)&&void 0!==e&&e.length?this.fragments[this.fragments.length-1].end:0}},{key:"age",get:function(){return this.advancedDateTime?Math.max(Date.now()-this.advancedDateTime,0)/1e3:0}},{key:"lastPartIndex",get:function(){var e;return null!==(e=this.partList)&&void 0!==e&&e.length?this.partList[this.partList.length-1].index:-1}},{key:"lastPartSn",get:function(){var e;return null!==(e=this.partList)&&void 0!==e&&e.length?this.partList[this.partList.length-1].fragment.sn:this.endSN}}])&&m(t.prototype,r),Object.defineProperty(t,"prototype",{writable:!1}),e}(),E=r(960),T={audio:{a3ds:!0,"ac-3":!0,"ac-4":!0,alac:!0,alaw:!0,dra1:!0,"dts+":!0,"dts-":!0,dtsc:!0,dtse:!0,dtsh:!0,"ec-3":!0,enca:!0,g719:!0,g726:!0,m4ae:!0,mha1:!0,mha2:!0,mhm1:!0,mhm2:!0,mlpa:!0,mp4a:!0,"raw ":!0,Opus:!0,opus:!0,samr:!0,sawb:!0,sawp:!0,sevc:!0,sqcp:!0,ssmv:!0,twos:!0,ulaw:!0},video:{avc1:!0,avc2:!0,avc3:!0,avc4:!0,avcp:!0,av01:!0,drac:!0,dva1:!0,dvav:!0,dvh1:!0,dvhe:!0,encv:!0,hev1:!0,hvc1:!0,mjp2:!0,mp4v:!0,mvc1:!0,mvc2:!0,mvc3:!0,mvc4:!0,resv:!0,rv60:!0,s263:!0,svc1:!0,svc2:!0,"vc-1":!0,vp08:!0,vp09:!0},text:{stpp:!0,wvtt:!0}};function S(e,t){return MediaSource.isTypeSupported((t||"video")+'/mp4;codecs="'+e+'"')}var b=/#EXT-X-STREAM-INF:([^\r\n]*)(?:[\r\n](?:#[^\r\n]*)?)*([^\r\n]+)|#EXT-X-SESSION-DATA:([^\r\n]*)[\r\n]+/g,L=/#EXT-X-MEDIA:(.*)/g,A=new RegExp([/#EXTINF:\s*(\d*(?:\.\d+)?)(?:,(.*)\s+)?/.source,/(?!#) *(\S[\S ]*)/.source,/#EXT-X-BYTERANGE:*(.+)/.source,/#EXT-X-PROGRAM-DATE-TIME:(.+)/.source,/#.*/.source].join("|"),"g"),D=new RegExp([/#(EXTM3U)/.source,/#EXT-X-(DATERANGE|KEY|MAP|PART|PART-INF|PLAYLIST-TYPE|PRELOAD-HINT|RENDITION-REPORT|SERVER-CONTROL|SKIP|START):(.+)/.source,/#EXT-X-(BITRATE|DISCONTINUITY-SEQUENCE|MEDIA-SEQUENCE|TARGETDURATION|VERSION): *(\d+)/.source,/#EXT-X-(DISCONTINUITY|ENDLIST|GAP)/.source,/(#)([^:]*):(.*)/.source,/(#)(.*)(?:.*)\r?\n?/.source].join("|")),R=/\.(mp4|m4s|m4v|m4a)$/i,k=function(){function e(){}return e.findGroup=function(e,t){for(var r=0;r2){var r=t.shift()+".";return(r+=parseInt(t.shift()).toString(16))+("000"+parseInt(t.shift()).toString(16)).slice(-4)}return e},e.resolve=function(e,t){return n.buildAbsoluteURL(t,e,{alwaysNormalize:!0})},e.parseMasterPlaylist=function(t,r){var i,n=[],a=[],s={},o=!1;for(b.lastIndex=0;null!=(i=b.exec(t));)if(i[1]){var l,u=new f(i[1]),d={attrs:u,bitrate:u.decimalInteger("AVERAGE-BANDWIDTH")||u.decimalInteger("BANDWIDTH"),name:u.NAME,url:e.resolve(i[2],r)},h=u.decimalResolution("RESOLUTION");h&&(d.width=h.width,d.height=h.height),_((u.CODECS||"").split(/[ ,]+/).filter((function(e){return e})),d),d.videoCodec&&-1!==d.videoCodec.indexOf("avc1")&&(d.videoCodec=e.convertAVC1ToAVCOTI(d.videoCodec)),null!==(l=d.unknownCodecs)&&void 0!==l&&l.length||a.push(d),n.push(d)}else if(i[3]){var c=new f(i[3]);c["DATA-ID"]&&(o=!0,s[c["DATA-ID"]]=c)}return{levels:a.length>0&&a.length-1){l.logger.warn("Keyformat "+$+" is not supported from the manifest");continue}if("identity"!==$)continue;Y&&(d=E.LevelKey.fromURL(t,q),q&&["AES-128","SAMPLE-AES","SAMPLE-AES-CENC"].indexOf(Y)>=0&&(d.method=Y,d.keyFormat=$,Q&&(d.keyID=Q),X&&(d.keyFormatVersions=X),d.iv=z));break;case"START":var Z=new f(M).decimalFloatingPoint("TIME-OFFSET");(0,a.isFiniteNumber)(Z)&&(h.startTimeOffset=Z);break;case"MAP":var J=new f(M);if(k.duration){var ee=new p.Fragment(i,t);w(ee,J,r,d),v=ee,k.initSegment=v,v.rawProgramDateTime&&!k.rawProgramDateTime&&(k.rawProgramDateTime=v.rawProgramDateTime)}else w(k,J,r,d),v=k,x=!0;break;case"SERVER-CONTROL":var te=new f(M);h.canBlockReload=te.bool("CAN-BLOCK-RELOAD"),h.canSkipUntil=te.optionalFloat("CAN-SKIP-UNTIL",0),h.canSkipDateRanges=h.canSkipUntil>0&&te.bool("CAN-SKIP-DATERANGES"),h.partHoldBack=te.optionalFloat("PART-HOLD-BACK",0),h.holdBack=te.optionalFloat("HOLD-BACK",0);break;case"PART-INF":var re=new f(M);h.partTarget=re.decimalFloatingPoint("PART-TARGET");break;case"PART":var ie=h.partList;ie||(ie=h.partList=[]);var ne=T>0?ie[ie.length-1]:void 0,ae=T++,se=new p.Part(new f(M),k,t,ae,ne);ie.push(se),k.duration+=se.duration;break;case"PRELOAD-HINT":var oe=new f(M);h.preloadHint=oe;break;case"RENDITION-REPORT":var le=new f(M);h.renditionReports=h.renditionReports||[],h.renditionReports.push(le);break;default:l.logger.warn("line parsed but not handled: "+o)}}}L&&!L.relurl?(c.pop(),S-=L.duration,h.partList&&(h.fragmentHint=L)):h.partList&&(I(k,L),k.cc=b,h.fragmentHint=k);var ue=c.length,de=c[0],he=c[ue-1];if((S+=h.skippedSegments*h.targetduration)>0&&ue&&he){h.averagetargetduration=S/ue;var fe=he.sn;h.endSN="initSegment"!==fe?fe:0,de&&(h.startCC=de.cc,de.initSegment||h.fragments.every((function(e){return e.relurl&&(t=e.relurl,R.test(null!=(r=null===(i=n.parseURL(t))||void 0===i?void 0:i.path)?r:""));var t,r,i}))&&(l.logger.warn("MP4 fragments found but no init segment (probably no MAP, incomplete M3U8), trying to fetch SIDX"),(k=new p.Fragment(i,t)).relurl=he.relurl,k.level=r,k.sn="initSegment",de.initSegment=k,h.needSidxRanges=!0))}else h.endSN=0,h.startCC=0;return h.fragmentHint&&(S+=h.fragmentHint.duration),h.totalduration=S,h.endCC=b,_>0&&function(e,t){for(var r=e[t],i=t;i--;){var n=e[i];if(!n)return;n.programDateTime=r.programDateTime-1e3*n.duration,r=n}}(c,_),h},e}();function _(e,t){["video","audio","text"].forEach((function(r){var i=e.filter((function(e){return function(e,t){var r=T[t];return!!r&&!0===r[e.slice(0,4)]}(e,r)}));if(i.length){var n=i.filter((function(e){return 0===e.lastIndexOf("avc1",0)||0===e.lastIndexOf("mp4a",0)}));t[r+"Codec"]=n.length>0?n[0]:i[0],e=e.filter((function(e){return-1===i.indexOf(e)}))}})),t.unknownCodecs=e}function x(e,t,r){var i=t[r];i&&(e[r]=i)}function I(e,t){e.rawProgramDateTime?e.programDateTime=Date.parse(e.rawProgramDateTime):null!=t&&t.programDateTime&&(e.programDateTime=t.endProgramDateTime),(0,a.isFiniteNumber)(e.programDateTime)||(e.programDateTime=null,e.rawProgramDateTime=null)}function w(e,t,r,i){e.relurl=t.URI,t.BYTERANGE&&e.setByteRange(t.BYTERANGE),e.level=r,e.sn="initSegment",i&&(e.levelkey=i),e.initSegment=null}var C=r(308);function P(e,t){var r=e.url;return void 0!==r&&0!==r.indexOf("data:")||(r=t.url),r}const O=function(){function e(e){this.hls=void 0,this.loaders=Object.create(null),this.hls=e,this.registerListeners()}var t=e.prototype;return t.startLoad=function(e){},t.stopLoad=function(){this.destroyInternalLoaders()},t.registerListeners=function(){var e=this.hls;e.on(s.Events.MANIFEST_LOADING,this.onManifestLoading,this),e.on(s.Events.LEVEL_LOADING,this.onLevelLoading,this),e.on(s.Events.AUDIO_TRACK_LOADING,this.onAudioTrackLoading,this),e.on(s.Events.SUBTITLE_TRACK_LOADING,this.onSubtitleTrackLoading,this)},t.unregisterListeners=function(){var e=this.hls;e.off(s.Events.MANIFEST_LOADING,this.onManifestLoading,this),e.off(s.Events.LEVEL_LOADING,this.onLevelLoading,this),e.off(s.Events.AUDIO_TRACK_LOADING,this.onAudioTrackLoading,this),e.off(s.Events.SUBTITLE_TRACK_LOADING,this.onSubtitleTrackLoading,this)},t.createInternalLoader=function(e){var t=this.hls.config,r=t.pLoader,i=t.loader,n=new(r||i)(t);return e.loader=n,this.loaders[e.type]=n,n},t.getInternalLoader=function(e){return this.loaders[e.type]},t.resetInternalLoader=function(e){this.loaders[e]&&delete this.loaders[e]},t.destroyInternalLoaders=function(){for(var e in this.loaders){var t=this.loaders[e];t&&t.destroy(),this.resetInternalLoader(e)}},t.destroy=function(){this.unregisterListeners(),this.destroyInternalLoaders()},t.onManifestLoading=function(e,t){var r=t.url;this.load({id:null,groupId:null,level:0,responseType:"text",type:C.PlaylistContextType.MANIFEST,url:r,deliveryDirectives:null})},t.onLevelLoading=function(e,t){var r=t.id,i=t.level,n=t.url,a=t.deliveryDirectives;this.load({id:r,groupId:null,level:i,responseType:"text",type:C.PlaylistContextType.LEVEL,url:n,deliveryDirectives:a})},t.onAudioTrackLoading=function(e,t){var r=t.id,i=t.groupId,n=t.url,a=t.deliveryDirectives;this.load({id:r,groupId:i,level:null,responseType:"text",type:C.PlaylistContextType.AUDIO_TRACK,url:n,deliveryDirectives:a})},t.onSubtitleTrackLoading=function(e,t){var r=t.id,i=t.groupId,n=t.url,a=t.deliveryDirectives;this.load({id:r,groupId:i,level:null,responseType:"text",type:C.PlaylistContextType.SUBTITLE_TRACK,url:n,deliveryDirectives:a})},t.load=function(e){var t,r,i,n,a,s,o=this.hls.config,u=this.getInternalLoader(e);if(u){var d=u.context;if(d&&d.url===e.url)return void l.logger.trace("[playlist-loader]: playlist request ongoing");l.logger.log("[playlist-loader]: aborting previous loader for type: "+e.type),u.abort()}switch(e.type){case C.PlaylistContextType.MANIFEST:r=o.manifestLoadingMaxRetry,i=o.manifestLoadingTimeOut,n=o.manifestLoadingRetryDelay,a=o.manifestLoadingMaxRetryTimeout;break;case C.PlaylistContextType.LEVEL:case C.PlaylistContextType.AUDIO_TRACK:case C.PlaylistContextType.SUBTITLE_TRACK:r=0,i=o.levelLoadingTimeOut;break;default:r=o.levelLoadingMaxRetry,i=o.levelLoadingTimeOut,n=o.levelLoadingRetryDelay,a=o.levelLoadingMaxRetryTimeout}if(u=this.createInternalLoader(e),null!==(t=e.deliveryDirectives)&&void 0!==t&&t.part&&(e.type===C.PlaylistContextType.LEVEL&&null!==e.level?s=this.hls.levels[e.level].details:e.type===C.PlaylistContextType.AUDIO_TRACK&&null!==e.id?s=this.hls.audioTracks[e.id].details:e.type===C.PlaylistContextType.SUBTITLE_TRACK&&null!==e.id&&(s=this.hls.subtitleTracks[e.id].details),s)){var h=s.partTarget,f=s.targetduration;h&&f&&(i=Math.min(1e3*Math.max(3*h,.8*f),i))}var c={timeout:i,maxRetry:r,retryDelay:n,maxRetryDelay:a,highWaterMark:0},v={onSuccess:this.loadsuccess.bind(this),onError:this.loaderror.bind(this),onTimeout:this.loadtimeout.bind(this)};u.load(e,c,v)},t.loadsuccess=function(e,t,r,i){if(void 0===i&&(i=null),r.isSidxRequest)return this.handleSidxRequest(e,r),void this.handlePlaylistLoaded(e,t,r,i);this.resetInternalLoader(r.type);var n=e.data;0===n.indexOf("#EXTM3U")?(t.parsing.start=performance.now(),n.indexOf("#EXTINF:")>0||n.indexOf("#EXT-X-TARGETDURATION:")>0?this.handleTrackOrLevelPlaylist(e,t,r,i):this.handleMasterPlaylist(e,t,r,i)):this.handleManifestParsingError(e,r,"no EXTM3U delimiter",i)},t.loaderror=function(e,t,r){void 0===r&&(r=null),this.handleNetworkError(t,r,!1,e)},t.loadtimeout=function(e,t,r){void 0===r&&(r=null),this.handleNetworkError(t,r,!0)},t.handleMasterPlaylist=function(e,t,r,i){var n=this.hls,a=e.data,o=P(e,r),u=k.parseMasterPlaylist(a,o),d=u.levels,h=u.sessionData;if(d.length){var c=d.map((function(e){return{id:e.attrs.AUDIO,audioCodec:e.audioCodec}})),v=d.map((function(e){return{id:e.attrs.SUBTITLES,textCodec:e.textCodec}})),g=k.parseMasterPlaylistMedia(a,o,"AUDIO",c),p=k.parseMasterPlaylistMedia(a,o,"SUBTITLES",v),m=k.parseMasterPlaylistMedia(a,o,"CLOSED-CAPTIONS");g.length&&(g.some((function(e){return!e.url}))||!d[0].audioCodec||d[0].attrs.AUDIO||(l.logger.log("[playlist-loader]: audio codec signaled in quality level, but no embedded audio track signaled, create one"),g.unshift({type:"main",name:"main",default:!1,autoselect:!1,forced:!1,id:-1,attrs:new f({}),bitrate:0,url:""}))),n.trigger(s.Events.MANIFEST_LOADED,{levels:d,audioTracks:g,subtitles:p,captions:m,url:o,stats:t,networkDetails:i,sessionData:h})}else this.handleManifestParsingError(e,r,"no level found in manifest",i)},t.handleTrackOrLevelPlaylist=function(e,t,r,i){var n=this.hls,l=r.id,u=r.level,d=r.type,h=P(e,r),c=(0,a.isFiniteNumber)(l)?l:0,v=(0,a.isFiniteNumber)(u)?u:c,g=function(e){switch(e.type){case C.PlaylistContextType.AUDIO_TRACK:return C.PlaylistLevelType.AUDIO;case C.PlaylistContextType.SUBTITLE_TRACK:return C.PlaylistLevelType.SUBTITLE;default:return C.PlaylistLevelType.MAIN}}(r),p=k.parseLevelPlaylist(e.data,h,v,g,c);if(p.fragments.length){if(d===C.PlaylistContextType.MANIFEST){var m={attrs:new f({}),bitrate:0,details:p,name:"",url:h};n.trigger(s.Events.MANIFEST_LOADED,{levels:[m],audioTracks:[],url:h,stats:t,networkDetails:i,sessionData:null})}if(t.parsing.end=performance.now(),p.needSidxRanges){var y,E=null===(y=p.fragments[0].initSegment)||void 0===y?void 0:y.url;this.load({url:E,isSidxRequest:!0,type:d,level:u,levelDetails:p,id:l,groupId:null,rangeStart:0,rangeEnd:2048,responseType:"arraybuffer",deliveryDirectives:null})}else r.levelDetails=p,this.handlePlaylistLoaded(e,t,r,i)}else n.trigger(s.Events.ERROR,{type:o.ErrorTypes.NETWORK_ERROR,details:o.ErrorDetails.LEVEL_EMPTY_ERROR,fatal:!1,url:h,reason:"no fragments found in level",level:"number"==typeof r.level?r.level:void 0})},t.handleSidxRequest=function(e,t){var r=new Uint8Array(e.data),i=(0,u.findBox)(r,["sidx"])[0];if(i){var n=(0,u.parseSegmentIndex)(i);if(n){var a=n.references,s=t.levelDetails;a.forEach((function(e,t){var i=e.info,n=s.fragments[t];if(0===n.byteRange.length&&n.setByteRange(String(1+i.end-i.start)+"@"+String(i.start)),n.initSegment){var a=(0,u.findBox)(r,["moov"])[0],o=a?a.length:null;n.initSegment.setByteRange(String(o)+"@0")}}))}}},t.handleManifestParsingError=function(e,t,r,i){this.hls.trigger(s.Events.ERROR,{type:o.ErrorTypes.NETWORK_ERROR,details:o.ErrorDetails.MANIFEST_PARSING_ERROR,fatal:t.type===C.PlaylistContextType.MANIFEST,url:e.url,reason:r,response:e,context:t,networkDetails:i})},t.handleNetworkError=function(e,t,r,i){void 0===r&&(r=!1),l.logger.warn("[playlist-loader]: A network "+(r?"timeout":"error")+" occurred while loading "+e.type+" level: "+e.level+" id: "+e.id+' group-id: "'+e.groupId+'"');var n=o.ErrorDetails.UNKNOWN,a=!1,u=this.getInternalLoader(e);switch(e.type){case C.PlaylistContextType.MANIFEST:n=r?o.ErrorDetails.MANIFEST_LOAD_TIMEOUT:o.ErrorDetails.MANIFEST_LOAD_ERROR,a=!0;break;case C.PlaylistContextType.LEVEL:n=r?o.ErrorDetails.LEVEL_LOAD_TIMEOUT:o.ErrorDetails.LEVEL_LOAD_ERROR,a=!1;break;case C.PlaylistContextType.AUDIO_TRACK:n=r?o.ErrorDetails.AUDIO_TRACK_LOAD_TIMEOUT:o.ErrorDetails.AUDIO_TRACK_LOAD_ERROR,a=!1;break;case C.PlaylistContextType.SUBTITLE_TRACK:n=r?o.ErrorDetails.SUBTITLE_TRACK_LOAD_TIMEOUT:o.ErrorDetails.SUBTITLE_LOAD_ERROR,a=!1}u&&this.resetInternalLoader(e.type);var d={type:o.ErrorTypes.NETWORK_ERROR,details:n,fatal:a,url:e.url,loader:u,context:e,networkDetails:t};i&&(d.response=i),this.hls.trigger(s.Events.ERROR,d)},t.handlePlaylistLoaded=function(e,t,r,i){var n=r.type,a=r.level,o=r.id,l=r.groupId,u=r.loader,d=r.levelDetails,h=r.deliveryDirectives;if(null!=d&&d.targetduration){if(u)switch(d.live&&(u.getCacheAge&&(d.ageHeader=u.getCacheAge()||0),u.getCacheAge&&!isNaN(d.ageHeader)||(d.ageHeader=0)),n){case C.PlaylistContextType.MANIFEST:case C.PlaylistContextType.LEVEL:this.hls.trigger(s.Events.LEVEL_LOADED,{details:d,level:a||0,id:o||0,stats:t,networkDetails:i,deliveryDirectives:h});break;case C.PlaylistContextType.AUDIO_TRACK:this.hls.trigger(s.Events.AUDIO_TRACK_LOADED,{details:d,id:o||0,groupId:l||"",stats:t,networkDetails:i,deliveryDirectives:h});break;case C.PlaylistContextType.SUBTITLE_TRACK:this.hls.trigger(s.Events.SUBTITLE_TRACK_LOADED,{details:d,id:o||0,groupId:l||"",stats:t,networkDetails:i,deliveryDirectives:h})}}else this.handleManifestParsingError(e,r,"invalid target duration",i)},e}();var F=function(){function e(e){this.hls=void 0,this.loaders={},this.decryptkey=null,this.decrypturl=null,this.hls=e,this.registerListeners()}var t=e.prototype;return t.startLoad=function(e){},t.stopLoad=function(){this.destroyInternalLoaders()},t.registerListeners=function(){this.hls.on(s.Events.KEY_LOADING,this.onKeyLoading,this)},t.unregisterListeners=function(){this.hls.off(s.Events.KEY_LOADING,this.onKeyLoading)},t.destroyInternalLoaders=function(){for(var e in this.loaders){var t=this.loaders[e];t&&t.destroy()}this.loaders={}},t.destroy=function(){this.unregisterListeners(),this.destroyInternalLoaders()},t.onKeyLoading=function(e,t){var r=t.frag,i=r.type,n=this.loaders[i];if(r.decryptdata){var a=r.decryptdata.uri;if(a!==this.decrypturl||null===this.decryptkey){var o=this.hls.config;if(n&&(l.logger.warn("abort previous key loader for type:"+i),n.abort()),!a)return void l.logger.warn("key uri is falsy");var u=o.loader,d=r.loader=this.loaders[i]=new u(o);this.decrypturl=a,this.decryptkey=null;var h={url:a,frag:r,responseType:"arraybuffer"},f={timeout:o.fragLoadingTimeOut,maxRetry:0,retryDelay:o.fragLoadingRetryDelay,maxRetryDelay:o.fragLoadingMaxRetryTimeout,highWaterMark:0},c={onSuccess:this.loadsuccess.bind(this),onError:this.loaderror.bind(this),onTimeout:this.loadtimeout.bind(this)};d.load(h,f,c)}else this.decryptkey&&(r.decryptdata.key=this.decryptkey,this.hls.trigger(s.Events.KEY_LOADED,{frag:r}))}else l.logger.warn("Missing decryption data on fragment in onKeyLoading")},t.loadsuccess=function(e,t,r){var i=r.frag;i.decryptdata?(this.decryptkey=i.decryptdata.key=new Uint8Array(e.data),i.loader=null,delete this.loaders[i.type],this.hls.trigger(s.Events.KEY_LOADED,{frag:i})):l.logger.error("after key load, decryptdata unset")},t.loaderror=function(e,t){var r=t.frag,i=r.loader;i&&i.abort(),delete this.loaders[r.type],this.hls.trigger(s.Events.ERROR,{type:o.ErrorTypes.NETWORK_ERROR,details:o.ErrorDetails.KEY_LOAD_ERROR,fatal:!1,frag:r,response:e})},t.loadtimeout=function(e,t){var r=t.frag,i=r.loader;i&&i.abort(),delete this.loaders[r.type],this.hls.trigger(s.Events.ERROR,{type:o.ErrorTypes.NETWORK_ERROR,details:o.ErrorDetails.KEY_LOAD_TIMEOUT,fatal:!1,frag:r})},e}();function M(e,t){var r;try{r=new Event("addtrack")}catch(e){(r=document.createEvent("Event")).initEvent("addtrack",!1,!1)}r.track=e,t.dispatchEvent(r)}var N=r(181),U=r(856);function B(){return self.WebKitDataCue||self.VTTCue||self.TextTrackCue}var G=function(){var e=B();try{new e(0,Number.POSITIVE_INFINITY,"")}catch(e){return Number.MAX_VALUE}return Number.POSITIVE_INFINITY}();function V(e,t){return e.getTime()/1e3-t}const H=function(){function e(e){this.hls=void 0,this.id3Track=null,this.media=null,this.dateRangeCuesAppended={},this.hls=e,this._registerListeners()}var t=e.prototype;return t.destroy=function(){this._unregisterListeners(),this.id3Track=null,this.media=null,this.dateRangeCuesAppended={},this.hls=null},t._registerListeners=function(){var e=this.hls;e.on(s.Events.MEDIA_ATTACHED,this.onMediaAttached,this),e.on(s.Events.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(s.Events.MANIFEST_LOADING,this.onManifestLoading,this),e.on(s.Events.FRAG_PARSING_METADATA,this.onFragParsingMetadata,this),e.on(s.Events.BUFFER_FLUSHING,this.onBufferFlushing,this),e.on(s.Events.LEVEL_UPDATED,this.onLevelUpdated,this)},t._unregisterListeners=function(){var e=this.hls;e.off(s.Events.MEDIA_ATTACHED,this.onMediaAttached,this),e.off(s.Events.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(s.Events.MANIFEST_LOADING,this.onManifestLoading,this),e.off(s.Events.FRAG_PARSING_METADATA,this.onFragParsingMetadata,this),e.off(s.Events.BUFFER_FLUSHING,this.onBufferFlushing,this),e.off(s.Events.LEVEL_UPDATED,this.onLevelUpdated,this)},t.onMediaAttached=function(e,t){this.media=t.media},t.onMediaDetaching=function(){this.id3Track&&(function(e){var t=e.mode;if("disabled"===t&&(e.mode="hidden"),e.cues)for(var r=e.cues.length;r--;)e.removeCue(e.cues[r]);"disabled"===t&&(e.mode=t)}(this.id3Track),this.id3Track=null,this.media=null,this.dateRangeCuesAppended={})},t.onManifestLoading=function(){this.dateRangeCuesAppended={}},t.createTrack=function(e){var t=this.getID3Track(e.textTracks);return t.mode="hidden",t},t.getID3Track=function(e){if(this.media){for(var t=0;tG&&(h=G),h-d<=0&&(h=d+.25);for(var f=0;f0)for(var a=function(e,t,r){var i=[],n=function(e,t){if(te[r].endTime)return-1;for(var i=0,n=r;i<=n;){var a=Math.floor((n+i)/2);if(te[a].startTime&&i-1)for(var a=n,s=e.length;a=t&&o.endTime<=r)i.push(o);else if(o.startTime>r)return i}return i}(e.cues,t,r),s=0;sn.startDate&&e.push(r),e}),[]).sort((function(e,t){return e.startDate.getTime()-t.startDate.getTime()}))[0];p&&(f=V(p.startDate,v),d=!0)}for(var m,y=Object.keys(n.attr),E=0;E.05&&this.forwardBufferLength>1){var u=Math.min(2,Math.max(1,a)),d=Math.round(2/(1+Math.exp(-.75*o-this.edgeStalled))*20)/20;e.playbackRate=Math.min(u,Math.max(1,d))}else 1!==e.playbackRate&&0!==e.playbackRate&&(e.playbackRate=1)}}}}},i.estimateLiveEdge=function(){var e=this.levelDetails;return null===e?null:e.edge+e.age},i.computeLatency=function(){var e=this.estimateLiveEdge();return null===e?null:e-this.currentTime},t=e,(r=[{key:"latency",get:function(){return this._latency||0}},{key:"maxLatency",get:function(){var e=this.config,t=this.levelDetails;return void 0!==e.liveMaxLatencyDuration?e.liveMaxLatencyDuration:t?e.liveMaxLatencyDurationCount*t.targetduration:0}},{key:"targetLatency",get:function(){var e=this.levelDetails;if(null===e)return null;var t=e.holdBack,r=e.partHoldBack,i=e.targetduration,n=this.config,a=n.liveSyncDuration,s=n.liveSyncDurationCount,o=n.lowLatencyMode,l=this.hls.userConfig,u=o&&r||t;(l.liveSyncDuration||l.liveSyncDurationCount||0===u)&&(u=void 0!==a?a:s*i);var d=i;return u+Math.min(1*this.stallCount,d)}},{key:"liveSyncPosition",get:function(){var e=this.estimateLiveEdge(),t=this.targetLatency,r=this.levelDetails;if(null===e||null===t||null===r)return null;var i=r.edge,n=e-t-this.edgeStalled,a=i-r.totalduration,s=i-(this.config.lowLatencyMode&&r.partTarget||r.targetduration);return Math.min(Math.max(a,n),s)}},{key:"drift",get:function(){var e=this.levelDetails;return null===e?1:e.drift}},{key:"edgeStalled",get:function(){var e=this.levelDetails;if(null===e)return 0;var t=3*(this.config.lowLatencyMode&&e.partTarget||e.targetduration);return Math.max(e.age-t,0)}},{key:"forwardBufferLength",get:function(){var e=this.media,t=this.levelDetails;if(!e||!t)return 0;var r=e.buffered.length;return(r?e.buffered.end(r-1):t.edge)-this.currentTime}}])&&j(t.prototype,r),Object.defineProperty(t,"prototype",{writable:!1}),e}();function Y(e,t){for(var r=0;re.sn?(n=r-e.start,i=e):(n=e.start-r,i=t),i.duration!==n&&(i.duration=n)}else t.sn>e.sn?e.cc===t.cc&&e.minEndPTS?t.start=e.start+(e.minEndPTS-e.start):t.start=e.start+e.duration:t.start=Math.max(e.start-t.duration,0)}function J(e,t,r,i,n,s){i-r<=0&&(l.logger.warn("Fragment should have a positive duration",t),i=r+t.duration,s=n+t.duration);var o=r,u=i,d=t.startPTS,h=t.endPTS;if((0,a.isFiniteNumber)(d)){var f=Math.abs(d-r);(0,a.isFiniteNumber)(t.deltaPTS)?t.deltaPTS=Math.max(f,t.deltaPTS):t.deltaPTS=f,o=Math.max(r,d),r=Math.min(r,d),n=Math.min(n,t.startDTS),u=Math.min(i,h),i=Math.max(i,h),s=Math.max(s,t.endDTS)}t.duration=i-r;var c=r-t.start;t.appendedPTS=i,t.start=t.startPTS=r,t.maxStartPTS=o,t.startDTS=n,t.endPTS=i,t.minEndPTS=u,t.endDTS=s;var v,g=t.sn;if(!e||ge.endSN)return 0;var p=g-e.startSN,m=e.fragments;for(m[p]=t,v=p;v>0;v--)Z(m[v],m[v-1]);for(v=p;v=i.length||function(e,t){if(t){for(var r=e.fragments,i=e.skippedSegments;it.partTarget&&(l+=1)}if((0,a.isFiniteNumber)(o))return new q(o,(0,a.isFiniteNumber)(l)?l:void 0,K.No)}}},t.loadPlaylist=function(e){},t.shouldLoadTrack=function(e){return this.canLoad&&e&&!!e.url&&(!e.details||e.details.live)},t.playlistLoaded=function(e,t,r){var i=this,n=t.details,s=t.stats,o=s.loading.end?Math.max(0,self.performance.now()-s.loading.end):0;if(n.advancedDateTime=Date.now()-o,n.live||null!=r&&r.live){if(n.reloaded(r),r&&this.log("live playlist "+e+" "+(n.advanced?"REFRESHED "+n.lastPartSn+"-"+n.lastPartIndex:"MISSED")),r&&n.fragments.length>0&&function(e,t){for(var r=null,i=e.fragments,n=i.length-1;n>=0;n--){var s=i[n].initSegment;if(s){r=s;break}}e.fragmentHint&&delete e.fragmentHint.endPTS;var o,u,d,h,f,c=0;if(function(e,t,r){for(var i=t.skippedSegments,n=Math.max(e.startSN,t.startSN)-t.startSN,a=(e.fragmentHint?1:0)+(i?t.endSN:Math.min(e.endSN,t.endSN))-t.startSN,s=t.startSN-e.startSN,o=t.fragmentHint?t.fragments.concat(t.fragmentHint):t.fragments,l=e.fragmentHint?e.fragments.concat(e.fragmentHint):e.fragments,u=n;u<=a;u++){var d=l[s+u],h=o[u];i&&!h&&u0){if(r&&T>r.tuneInGoal)this.warn("CDN Tune-in goal increased from: "+r.tuneInGoal+" to: "+T+" with playlist age: "+n.age),T=0;else{var S=Math.floor(T/n.targetduration);d+=S,void 0!==h&&(h+=Math.round(T%n.targetduration/n.partTarget)),this.log("CDN Tune-in age: "+n.ageHeader+"s last advanced "+y.toFixed(2)+"s goal: "+T+" skip sn "+S+" to part "+h)}n.tuneInGoal=T}if(u=this.getDeliveryDirectives(n,t.deliveryDirectives,d,h),f||!m)return void this.loadPlaylist(u)}else u=this.getDeliveryDirectives(n,t.deliveryDirectives,d,h);var b=function(e,t){var r,i=1e3*e.levelTargetDuration,n=i/2,a=e.age,s=a>0&&a<3*i,o=t.loading.end-t.loading.start,l=e.availabilityDelay;if(!1===e.updated)if(s){var u=333*e.misses;r=Math.max(Math.min(n,2*o),u),e.availabilityDelay=(e.availabilityDelay||0)+r}else r=n;else s?(l=Math.min(l||i/2,a),e.availabilityDelay=l,r=l+i-a):r=i-o;return Math.round(r)}(n,s);void 0!==d&&n.canBlockReload&&(b-=n.partTarget||1),this.log("reload live playlist "+e+" in "+Math.round(b)+" ms"),this.timer=self.setTimeout((function(){return i.loadPlaylist(u)}),b)}else this.clearTimer()},t.getDeliveryDirectives=function(e,t,r,i){var n=function(e,t){var r=e.canSkipUntil,i=e.canSkipDateRanges,n=e.endSN;return r&&(void 0!==t?t-n:0)-1&&null!==(t=e.context)&&void 0!==t&&t.deliveryDirectives)this.warn("retry playlist loading #"+this.retryCount+' after "'+e.details+'"'),this.loadPlaylist();else{var a=Math.min(Math.pow(2,this.retryCount)*i.levelLoadingRetryDelay,i.levelLoadingMaxRetryTimeout);this.timer=self.setTimeout((function(){return r.loadPlaylist()}),a),this.warn("retry playlist loading #"+this.retryCount+" in "+a+' ms after "'+e.details+'"')}else this.warn('cannot recover from error "'+e.details+'"'),this.clearTimer(),e.fatal=!0;return n},e}();function re(){return re=Object.assign?Object.assign.bind():function(e){for(var t=1;t0){r=n[0].bitrate,n.sort((function(e,t){return e.bitrate-t.bitrate})),this._levels=n;for(var c=0;cthis.hls.config.fragLoadingMaxRetry&&(s=d)):s=d}break;case o.ErrorDetails.LEVEL_LOAD_ERROR:case o.ErrorDetails.LEVEL_LOAD_TIMEOUT:n&&(n.deliveryDirectives&&(u=!1),s=n.level),l=!0;break;case o.ErrorDetails.REMUX_ALLOC_ERROR:s=null!=(i=r.level)?i:this.currentLevelIndex,l=!0}void 0!==s&&this.recoverLevel(r,s,l,u)}}},l.recoverLevel=function(e,t,r,i){var n=e.details,a=this._levels[t];if(a.loadError++,r){if(!this.retryLoadingOrFail(e))return void(this.currentLevelIndex=-1);e.levelRetry=!0}if(i){var s=a.url.length;if(s>1&&a.loadError-1&&this.currentLevelIndex!==o&&(this.warn(n+": switch to "+o),e.levelRetry=!0,this.hls.nextAutoLevel=o)}}},l.redundantFailover=function(e){var t=this._levels[e],r=t.url.length;if(r>1){var i=(t.urlId+1)%r;this.warn("Switching to redundant URL-id "+i),this._levels.forEach((function(e){e.urlId=i})),this.level=e}},l.onFragLoaded=function(e,t){var r=t.frag;if(void 0!==r&&r.type===C.PlaylistLevelType.MAIN){var i=this._levels[r.level];void 0!==i&&(i.fragmentError=0,i.loadError=0)}},l.onLevelLoaded=function(e,t){var r,i,n=t.level,a=t.details,s=this._levels[n];if(!s)return this.warn("Invalid level index "+n),void(null!==(i=t.deliveryDirectives)&&void 0!==i&&i.skip&&(a.deltaUpdateFailed=!0));n===this.currentLevelIndex?(0===s.fragmentError&&(s.loadError=0,this.retryCount=0),this.playlistLoaded(n,t,s.details)):null!==(r=t.deliveryDirectives)&&void 0!==r&&r.skip&&(a.deltaUpdateFailed=!0)},l.onAudioTrackSwitched=function(e,t){var r=this.hls.levels[this.currentLevelIndex];if(r&&r.audioGroupIds){for(var i=-1,n=this.hls.audioTracks[t.id].groupId,a=0;a0){var i=r.urlId,n=r.url[i];if(e)try{n=e.addDirectives(n)}catch(e){this.warn("Could not construct new URL with HLS Delivery Directives: "+e)}this.log("Attempt loading level index "+t+(e?" at sn "+e.msn+" part "+e.part:"")+" with URL-id "+i+" "+n),this.clearTimer(),this.hls.trigger(s.Events.LEVEL_LOADING,{url:n,level:t,id:i,deliveryDirectives:e||null})}},l.removeLevel=function(e,t){var r=function(e,r){return r!==t},i=this._levels.filter((function(i,n){return n!==e||i.url.length>1&&void 0!==t&&(i.url=i.url.filter(r),i.audioGroupIds&&(i.audioGroupIds=i.audioGroupIds.filter(r)),i.textGroupIds&&(i.textGroupIds=i.textGroupIds.filter(r)),i.urlId=0,!0)})).map((function(e,t){var r=e.details;return null!=r&&r.fragments&&r.fragments.forEach((function(e){e.level=t})),e}));this._levels=i,this.hls.trigger(s.Events.LEVELS_UPDATED,{levels:i})},n=i,(a=[{key:"levels",get:function(){return 0===this._levels.length?null:this._levels}},{key:"level",get:function(){return this.currentLevelIndex},set:function(e){var t,r=this._levels;if(0!==r.length&&(this.currentLevelIndex!==e||null===(t=r[e])||void 0===t||!t.details)){if(e<0||e>=r.length){var i=e<0;if(this.hls.trigger(s.Events.ERROR,{type:o.ErrorTypes.OTHER_ERROR,details:o.ErrorDetails.LEVEL_SWITCH_ERROR,level:e,fatal:i,reason:"invalid level idx"}),i)return;e=Math.min(e,r.length-1)}this.clearTimer();var n=this.currentLevelIndex,a=r[n],l=r[e];this.log("switching to level "+e+" from "+n),this.currentLevelIndex=e;var u=re({},l,{level:e,maxBitrate:l.maxBitrate,uri:l.uri,urlId:l.urlId});delete u._urlId,this.hls.trigger(s.Events.LEVEL_SWITCHING,u);var d=l.details;if(!d||d.live){var h=this.switchParams(l.uri,null==a?void 0:a.details);this.loadPlaylist(h)}}}},{key:"manualLevel",get:function(){return this.manualLevelIndex},set:function(e){this.manualLevelIndex=e,void 0===this._startLevel&&(this._startLevel=e),-1!==e&&(this.level=e)}},{key:"firstLevel",get:function(){return this._firstLevel},set:function(e){this._firstLevel=e}},{key:"startLevel",get:function(){if(void 0===this._startLevel){var e=this.hls.config.startLevel;return void 0!==e?e:this._firstLevel}return this._startLevel},set:function(e){this._startLevel=e}},{key:"nextLoadLevel",get:function(){return-1!==this.manualLevelIndex?this.manualLevelIndex:this.hls.nextAutoLevel},set:function(e){this.level=e,-1===this.manualLevelIndex&&(this.hls.nextAutoLevel=e)}}])&&ie(n.prototype,a),Object.defineProperty(n,"prototype",{writable:!1}),i}(te);!function(e){e.NOT_LOADED="NOT_LOADED",e.APPENDING="APPENDING",e.PARTIAL="PARTIAL",e.OK="OK"}(ae||(ae={}));var le=function(){function e(e){this.activeFragment=null,this.activeParts=null,this.fragments=Object.create(null),this.timeRanges=Object.create(null),this.bufferPadding=.2,this.hls=void 0,this.hls=e,this._registerListeners()}var t=e.prototype;return t._registerListeners=function(){var e=this.hls;e.on(s.Events.BUFFER_APPENDED,this.onBufferAppended,this),e.on(s.Events.FRAG_BUFFERED,this.onFragBuffered,this),e.on(s.Events.FRAG_LOADED,this.onFragLoaded,this)},t._unregisterListeners=function(){var e=this.hls;e.off(s.Events.BUFFER_APPENDED,this.onBufferAppended,this),e.off(s.Events.FRAG_BUFFERED,this.onFragBuffered,this),e.off(s.Events.FRAG_LOADED,this.onFragLoaded,this)},t.destroy=function(){this._unregisterListeners(),this.fragments=this.timeRanges=null},t.getAppendedFrag=function(e,t){if(t===C.PlaylistLevelType.MAIN){var r=this.activeFragment,i=this.activeParts;if(!r)return null;if(i)for(var n=i.length;n--;){var a=i[n],s=a?a.end:r.appendedPTS;if(a.start<=e&&void 0!==s&&e<=s)return n>9&&(this.activeParts=i.slice(n-9)),a}else if(r.start<=e&&void 0!==r.appendedPTS&&e<=r.appendedPTS)return r}return this.getBufferedFrag(e,t)},t.getBufferedFrag=function(e,t){for(var r=this.fragments,i=Object.keys(r),n=i.length;n--;){var a=r[i[n]];if((null==a?void 0:a.body.type)===t&&a.buffered){var s=a.body;if(s.start<=e&&e<=s.end)return s}}return null},t.detectEvictedFragments=function(e,t,r){var i=this;Object.keys(this.fragments).forEach((function(n){var a=i.fragments[n];if(a)if(a.buffered){var s=a.range[e];s&&s.time.some((function(e){var r=!i.isTimeBuffered(e.startPTS,e.endPTS,t);return r&&i.removeFragment(a.body),r}))}else a.body.type===r&&i.removeFragment(a.body)}))},t.detectPartialFragments=function(e){var t=this,r=this.timeRanges,i=e.frag,n=e.part;if(r&&"initSegment"!==i.sn){var a=de(i),s=this.fragments[a];s&&(Object.keys(r).forEach((function(e){var a=i.elementaryStreams[e];if(a){var o=r[e],l=null!==n||!0===a.partial;s.range[e]=t.getBufferedTimes(i,n,l,o)}})),s.loaded=null,Object.keys(s.range).length?s.buffered=!0:this.removeFragment(s.body))}},t.fragBuffered=function(e){var t=de(e),r=this.fragments[t];r&&(r.loaded=null,r.buffered=!0)},t.getBufferedTimes=function(e,t,r,i){for(var n={time:[],partial:r},a=t?t.start:e.start,s=t?t.end:e.end,o=e.minEndPTS||s,l=e.maxStartPTS||a,u=0;u=d&&o<=h){n.time.push({startPTS:Math.max(a,i.start(u)),endPTS:Math.min(s,i.end(u))});break}if(ad)n.partial=!0,n.time.push({startPTS:Math.max(a,i.start(u)),endPTS:Math.min(s,i.end(u))});else if(s<=d)break}return n},t.getPartialFragment=function(e){var t,r,i,n=null,a=0,s=this.bufferPadding,o=this.fragments;return Object.keys(o).forEach((function(l){var u=o[l];u&&ue(u)&&(r=u.body.start-s,i=u.body.end+s,e>=r&&e<=i&&(t=Math.min(e-r,i-e),a<=t&&(n=u.body,a=t)))})),n},t.getState=function(e){var t=de(e),r=this.fragments[t];return r?r.buffered?ue(r)?ae.PARTIAL:ae.OK:ae.APPENDING:ae.NOT_LOADED},t.isTimeBuffered=function(e,t,r){for(var i,n,a=0;a=i&&t<=n)return!0;if(t<=i)return!1}return!1},t.onFragLoaded=function(e,t){var r=t.frag,i=t.part;if("initSegment"!==r.sn&&!r.bitrateTest&&!i){var n=de(r);this.fragments[n]={body:r,loaded:t,buffered:!1,range:Object.create(null)}}},t.onBufferAppended=function(e,t){var r=this,i=t.frag,n=t.part,a=t.timeRanges;if(i.type===C.PlaylistLevelType.MAIN)if(this.activeFragment=i,n){var s=this.activeParts;s||(this.activeParts=s=[]),s.push(n)}else this.activeParts=null;this.timeRanges=a,Object.keys(a).forEach((function(e){var t=a[e];if(r.detectEvictedFragments(e,t),!n)for(var s=0;se&&i.removeFragment(s)}}))},t.removeFragment=function(e){var t=de(e);e.stats.loaded=0,e.clearElementaryStreamInfo(),delete this.fragments[t]},t.removeAllFragments=function(){this.fragments=Object.create(null),this.activeFragment=null,this.activeParts=null},e}();function ue(e){var t,r;return e.buffered&&((null===(t=e.range.video)||void 0===t?void 0:t.partial)||(null===(r=e.range.audio)||void 0===r?void 0:r.partial))}function de(e){return e.type+"_"+e.level+"_"+e.urlId+"_"+e.sn}var he=function(){function e(){this._boundTick=void 0,this._tickTimer=null,this._tickInterval=null,this._tickCallCount=0,this._boundTick=this.tick.bind(this)}var t=e.prototype;return t.destroy=function(){this.onHandlerDestroying(),this.onHandlerDestroyed()},t.onHandlerDestroying=function(){this.clearNextTick(),this.clearInterval()},t.onHandlerDestroyed=function(){},t.hasInterval=function(){return!!this._tickInterval},t.hasNextTick=function(){return!!this._tickTimer},t.setInterval=function(e){return!this._tickInterval&&(this._tickInterval=self.setInterval(this._boundTick,e),!0)},t.clearInterval=function(){return!!this._tickInterval&&(self.clearInterval(this._tickInterval),this._tickInterval=null,!0)},t.clearNextTick=function(){return!!this._tickTimer&&(self.clearTimeout(this._tickTimer),this._tickTimer=null,!0)},t.tick=function(){this._tickCallCount++,1===this._tickCallCount&&(this.doTick(),this._tickCallCount>1&&this.tickImmediate(),this._tickCallCount=0)},t.tickImmediate=function(){this.clearNextTick(),this._tickTimer=self.setTimeout(this._boundTick,0)},t.doTick=function(){},e}(),fe={length:0,start:function(){return 0},end:function(){return 0}},ce=function(){function e(){}return e.isBuffered=function(t,r){try{if(t)for(var i=e.getBuffered(t),n=0;n=i.start(n)&&r<=i.end(n))return!0}catch(e){}return!1},e.bufferInfo=function(t,r,i){try{if(t){var n,a=e.getBuffered(t),s=[];for(n=0;ns&&(i[a-1].end=e[n].end):i.push(e[n])}else i.push(e[n])}else i=e;for(var o,l=0,u=t,d=t,h=0;h=f&&t0)r=n+1;else{if(!(s<0))return a;i=n-1}}return null};function ye(e,t,r){void 0===e&&(e=0),void 0===t&&(t=0);var i=Math.min(t,r.duration+(r.deltaPTS?r.deltaPTS:0));return r.start+r.duration-i<=e?1:r.start-i>e&&r.start?-1:0}function Ee(e,t,r){var i=1e3*Math.min(t,r.duration+(r.deltaPTS?r.deltaPTS:0));return(r.endProgramDateTime||0)-i>e}function Te(e){var t="function"==typeof Map?new Map:void 0;return Te=function(e){if(null===e||(r=e,-1===Function.toString.call(r).indexOf("[native code]")))return e;var r;if("function"!=typeof e)throw new TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,i)}function i(){return Se(e,arguments,Ae(this).constructor)}return i.prototype=Object.create(e.prototype,{constructor:{value:i,enumerable:!1,writable:!0,configurable:!0}}),Le(i,e)},Te(e)}function Se(e,t,r){return Se=be()?Reflect.construct.bind():function(e,t,r){var i=[null];i.push.apply(i,t);var n=new(Function.bind.apply(e,i));return r&&Le(n,r.prototype),n},Se.apply(null,arguments)}function be(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}function Le(e,t){return Le=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},Le(e,t)}function Ae(e){return Ae=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(e){return e.__proto__||Object.getPrototypeOf(e)},Ae(e)}var De=Math.pow(2,17),Re=function(){function e(e){this.config=void 0,this.loader=null,this.partLoadTimeout=-1,this.config=e}var t=e.prototype;return t.destroy=function(){this.loader&&(this.loader.destroy(),this.loader=null)},t.abort=function(){this.loader&&this.loader.abort()},t.load=function(e,t){var r=this,i=e.url;if(!i)return Promise.reject(new _e({type:o.ErrorTypes.NETWORK_ERROR,details:o.ErrorDetails.FRAG_LOAD_ERROR,fatal:!1,frag:e,networkDetails:null},"Fragment does not have a "+(i?"part list":"url")));this.abort();var n=this.config,a=n.fLoader,s=n.loader;return new Promise((function(i,l){r.loader&&r.loader.destroy();var u=r.loader=e.loader=a?new a(n):new s(n),d=ke(e),h={timeout:n.fragLoadingTimeOut,maxRetry:0,retryDelay:0,maxRetryDelay:n.fragLoadingMaxRetryTimeout,highWaterMark:"initSegment"===e.sn?1/0:De};e.stats=u.stats,u.load(d,h,{onSuccess:function(t,n,a,s){r.resetLoader(e,u),i({frag:e,part:null,payload:t.data,networkDetails:s})},onError:function(t,i,n){r.resetLoader(e,u),l(new _e({type:o.ErrorTypes.NETWORK_ERROR,details:o.ErrorDetails.FRAG_LOAD_ERROR,fatal:!1,frag:e,response:t,networkDetails:n}))},onAbort:function(t,i,n){r.resetLoader(e,u),l(new _e({type:o.ErrorTypes.NETWORK_ERROR,details:o.ErrorDetails.INTERNAL_ABORTED,fatal:!1,frag:e,networkDetails:n}))},onTimeout:function(t,i,n){r.resetLoader(e,u),l(new _e({type:o.ErrorTypes.NETWORK_ERROR,details:o.ErrorDetails.FRAG_LOAD_TIMEOUT,fatal:!1,frag:e,networkDetails:n}))},onProgress:function(r,i,n,a){t&&t({frag:e,part:null,payload:n,networkDetails:a})}})}))},t.loadPart=function(e,t,r){var i=this;this.abort();var n=this.config,a=n.fLoader,s=n.loader;return new Promise((function(l,u){i.loader&&i.loader.destroy();var d=i.loader=e.loader=a?new a(n):new s(n),h=ke(e,t),f={timeout:n.fragLoadingTimeOut,maxRetry:0,retryDelay:0,maxRetryDelay:n.fragLoadingMaxRetryTimeout,highWaterMark:De};t.stats=d.stats,d.load(h,f,{onSuccess:function(n,a,s,o){i.resetLoader(e,d),i.updateStatsFromPart(e,t);var u={frag:e,part:t,payload:n.data,networkDetails:o};r(u),l(u)},onError:function(r,n,a){i.resetLoader(e,d),u(new _e({type:o.ErrorTypes.NETWORK_ERROR,details:o.ErrorDetails.FRAG_LOAD_ERROR,fatal:!1,frag:e,part:t,response:r,networkDetails:a}))},onAbort:function(r,n,a){e.stats.aborted=t.stats.aborted,i.resetLoader(e,d),u(new _e({type:o.ErrorTypes.NETWORK_ERROR,details:o.ErrorDetails.INTERNAL_ABORTED,fatal:!1,frag:e,part:t,networkDetails:a}))},onTimeout:function(r,n,a){i.resetLoader(e,d),u(new _e({type:o.ErrorTypes.NETWORK_ERROR,details:o.ErrorDetails.FRAG_LOAD_TIMEOUT,fatal:!1,frag:e,part:t,networkDetails:a}))}})}))},t.updateStatsFromPart=function(e,t){var r=e.stats,i=t.stats,n=i.total;if(r.loaded+=i.loaded,n){var a=Math.round(e.duration/t.duration),s=Math.min(Math.round(r.loaded/n),a),o=(a-s)*Math.round(r.loaded/s);r.total=r.loaded+o}else r.total=Math.max(r.loaded,r.total);var l=r.loading,u=i.loading;l.start?l.first+=u.first-u.start:(l.start=u.start,l.first=u.first),l.end=u.end},t.resetLoader=function(e,t){e.loader=null,this.loader===t&&(self.clearTimeout(this.partLoadTimeout),this.loader=null),t.destroy()},e}();function ke(e,t){void 0===t&&(t=null);var r=t||e,i={frag:e,part:t,responseType:"arraybuffer",url:r.url,headers:{},rangeStart:0,rangeEnd:0},n=r.byteRangeStartOffset,s=r.byteRangeEndOffset;return(0,a.isFiniteNumber)(n)&&(0,a.isFiniteNumber)(s)&&(i.rangeStart=n,i.rangeEnd=s),i}var _e=function(e){var t,r;function i(t){for(var r,i=arguments.length,n=new Array(i>1?i-1:0),a=1;a=t.endSN&&!e.nextStart){var n=t.partList;if(null!=n&&n.length){var a=n[n.length-1];return ce.isBuffered(this.media,a.start+a.duration/2)}var s=i.getState(r);return s===ae.PARTIAL||s===ae.OK}return!1},h.onMediaAttached=function(e,t){var r=this.media=this.mediaBuffer=t.media;this.onvseeking=this.onMediaSeeking.bind(this),this.onvended=this.onMediaEnded.bind(this),r.addEventListener("seeking",this.onvseeking),r.addEventListener("ended",this.onvended);var i=this.config;this.levels&&i.autoStartLoad&&this.state===Oe&&this.startLoad(i.startPosition)},h.onMediaDetaching=function(){var e=this.media;null!=e&&e.ended&&(this.log("MSE detaching and video ended, reset startPosition"),this.startPosition=this.lastCurrentTime=0),e&&this.onvseeking&&this.onvended&&(e.removeEventListener("seeking",this.onvseeking),e.removeEventListener("ended",this.onvended),this.onvseeking=this.onvended=null),this.media=this.mediaBuffer=null,this.loadedmetadata=!1,this.fragmentTracker.removeAllFragments(),this.stopLoad()},h.onMediaSeeking=function(){var e=this.config,t=this.fragCurrent,r=this.media,i=this.mediaBuffer,n=this.state,s=r?r.currentTime:0,o=ce.bufferInfo(i||r,s,e.maxBufferHole);if(this.log("media seeking to "+((0,a.isFiniteNumber)(s)?s.toFixed(3):s)+", state: "+n),n===Ve)this.resetLoadingState();else if(t){var l=e.maxFragLookUpTolerance,u=t.start-l,d=t.start+t.duration+l;if(!o.len||do.end){var h=s>d;(s0&&a&&a.key&&a.iv&&"AES-128"===a.method){var o=self.performance.now();return t.decrypter.webCryptoDecrypt(new Uint8Array(n),a.key.buffer,a.iv.buffer).then((function(t){var n=self.performance.now();return i.trigger(s.Events.FRAG_DECRYPTED,{frag:e,payload:t,stats:{tstart:o,tdecrypt:n}}),r.payload=t,r}))}return r})).then((function(r){var i=t.fragCurrent,n=t.hls,a=t.levels;if(!a)throw new Error("init load aborted, missing levels");a[e.level].details;var o=e.stats;t.state=Fe,t.fragLoadError=0,e.data=new Uint8Array(r.payload),o.parsing.start=o.buffering.start=self.performance.now(),o.parsing.end=o.buffering.end=self.performance.now(),r.frag===i&&n.trigger(s.Events.FRAG_BUFFERED,{stats:o,frag:i,part:null,id:e.type}),t.tick()})).catch((function(r){t.state!==Oe&&t.state!==He&&(t.warn(r),t.resetFragmentLoading(e))}))},h.fragContextChanged=function(e){var t=this.fragCurrent;return!e||!t||e.level!==t.level||e.sn!==t.sn||e.urlId!==t.urlId},h.fragBufferedComplete=function(e,t){var r,i,n=this.mediaBuffer?this.mediaBuffer:this.media;this.log("Buffered "+e.type+" sn: "+e.sn+(t?" part: "+t.index:"")+" of "+("[stream-controller]"===this.logPrefix?"level":"track")+" "+e.level+" "+(n?Ie(ce.getBuffered(n)):"(detached)")),this.state=Fe,n&&(!this.loadedmetadata&&e.type==C.PlaylistLevelType.MAIN&&n.buffered.length&&(null===(r=this.fragCurrent)||void 0===r?void 0:r.sn)===(null===(i=this.fragPrevious)||void 0===i?void 0:i.sn)&&(this.loadedmetadata=!0,this.seekToStartPos()),this.tick())},h.seekToStartPos=function(){},h._handleFragmentLoadComplete=function(e){var t=this.transmuxer;if(t){var r=e.frag,i=e.part,n=e.partsLoaded,a=!n||0===n.length||n.some((function(e){return!e})),s=new ve(r.level,r.sn,r.stats.chunkCount+1,0,i?i.index:-1,!a);t.flush(s)}},h._handleFragmentLoadProgress=function(e){},h._doFragLoad=function(e,t,r,i){var n=this;if(void 0===r&&(r=null),!this.levels)throw new Error("frag load aborted, missing levels");if(r=Math.max(e.start,r||0),this.config.lowLatencyMode&&t){var o=t.partList;if(o&&i){r>e.end&&t.fragmentHint&&(e=t.fragmentHint);var l=this.getNextPart(o,e,r);if(l>-1){var u=o[l];return this.log("Loading part sn: "+e.sn+" p: "+u.index+" cc: "+e.cc+" of playlist ["+t.startSN+"-"+t.endSN+"] parts [0-"+l+"-"+(o.length-1)+"] "+("[stream-controller]"===this.logPrefix?"level":"track")+": "+e.level+", target: "+parseFloat(r.toFixed(3))),this.nextLoadPosition=u.start+u.duration,this.state=Ne,this.hls.trigger(s.Events.FRAG_LOADING,{frag:e,part:o[l],targetBufferTime:r}),this.doFragPartsLoad(e,o,l,i).catch((function(e){return n.handleFragLoadError(e)}))}if(!e.url||this.loadedEndOfParts(o,r))return Promise.resolve(null)}}return this.log("Loading fragment "+e.sn+" cc: "+e.cc+" "+(t?"of ["+t.startSN+"-"+t.endSN+"] ":"")+("[stream-controller]"===this.logPrefix?"level":"track")+": "+e.level+", target: "+parseFloat(r.toFixed(3))),(0,a.isFiniteNumber)(e.sn)&&!this.bitrateTest&&(this.nextLoadPosition=e.start+e.duration),this.state=Ne,this.hls.trigger(s.Events.FRAG_LOADING,{frag:e,targetBufferTime:r}),this.fragmentLoader.load(e,i).catch((function(e){return n.handleFragLoadError(e)}))},h.doFragPartsLoad=function(e,t,r,i){var n=this;return new Promise((function(a,o){var l=[];!function r(u){var d=t[u];n.fragmentLoader.loadPart(e,d,i).then((function(i){l[d.index]=i;var o=i.part;n.hls.trigger(s.Events.FRAG_LOADED,i);var h=t[u+1];if(!h||h.fragment!==e)return a({frag:e,part:o,partsLoaded:l});r(u+1)})).catch(o)}(r)}))},h.handleFragLoadError=function(e){var t=e.data;return t&&t.details===o.ErrorDetails.INTERNAL_ABORTED?this.handleFragLoadAborted(t.frag,t.part):this.hls.trigger(s.Events.ERROR,t),null},h._handleTransmuxerFlush=function(e){var t=this.getCurrentContext(e);if(t&&this.state===Be){var r=t.frag,i=t.part,n=t.level,a=self.performance.now();r.stats.parsing.end=a,i&&(i.stats.parsing.end=a),this.updateLevelTiming(r,i,n,e.partial)}else this.fragCurrent||(this.state=Fe)},h.getCurrentContext=function(e){var t=this.levels,r=e.level,i=e.sn,n=e.part;if(!t||!t[r])return this.warn("Levels object was unset while buffering fragment "+i+" of level "+r+". The current chunk will not be buffered."),null;var a=t[r],s=n>-1?function(e,t,r){if(!e||!e.details)return null;var i=e.details.partList;if(i)for(var n=i.length;n--;){var a=i[n];if(a.index===r&&a.fragment.sn===t)return a}return null}(a,i,n):null,o=s?s.fragment:function(e,t,r){if(!e||!e.details)return null;var i=e.details,n=i.fragments[t-i.startSN];return n||((n=i.fragmentHint)&&n.sn===t?n:ta&&this.flushMainBuffer(s,e.start)}else this.flushMainBuffer(0,e.start)},h.getFwdBufferInfo=function(e,t){var r=this.config,i=this.getLoadPosition();if(!(0,a.isFiniteNumber)(i))return null;var n=ce.bufferInfo(e,i,r.maxBufferHole);if(0===n.len&&void 0!==n.nextStart){var s=this.fragmentTracker.getBufferedFrag(i,t);if(s&&n.nextStart=r&&(t.maxMaxBufferLength/=2,this.warn("Reduce max buffer length to "+t.maxMaxBufferLength+"s"),!0)},h.getNextFragment=function(e,t){var r=t.fragments,i=r.length;if(!i)return null;var n,a=this.config,s=r[0].start;if(t.live){var o=a.initialLiveManifestSize;if(i-1&&rr.start&&r.loaded},h.getInitialLiveFragment=function(e,t){var r=this.fragPrevious,i=null;if(r){if(e.hasProgramDateTime&&(this.log("Live playlist, switching playlist, load frag with same PDT: "+r.programDateTime),i=function(e,t,r){if(null===t||!Array.isArray(e)||!e.length||!(0,a.isFiniteNumber)(t))return null;if(t<(e[0].programDateTime||0))return null;if(t>=(e[e.length-1].endProgramDateTime||0))return null;r=r||0;for(var i=0;i=e.startSN&&n<=e.endSN){var s=t[n-e.startSN];r.cc===s.cc&&(i=s,this.log("Live playlist, switching playlist, load frag with next SN: "+i.sn))}i||(i=function(e,t){return me(e,(function(e){return e.cct?-1:0}))}(t,r.cc),i&&this.log("Live playlist, switching playlist, load frag with same CC: "+i.sn))}}else{var o=this.hls.liveSyncPosition;null!==o&&(i=this.getFragmentAtPosition(o,this.bitrateTest?e.fragmentEnd:e.edge,e))}return i},h.getFragmentAtPosition=function(e,t,r){var i,n=this.config,a=this.fragPrevious,s=r.fragments,o=r.endSN,l=r.fragmentHint,u=n.maxFragLookUpTolerance,d=!!(n.lowLatencyMode&&r.partList&&l);if(d&&l&&!this.bitrateTest&&(s=s.concat(l),o=l.sn),i=et-u?0:u):s[s.length-1],i){var h=i.sn-r.startSN;if(this.fragmentTracker.getState(i)===ae.OK&&(a=i),a&&i.sn===a.sn&&!d&&a&&i.level===a.level){var f=s[h+1];i.sn=a-t.maxFragLookUpTolerance&&n<=s;if(null!==i&&r.duration>i&&(nr.startCC||e&&e.cc"+e.startSN+" prev-sn: "+(n?n.sn:"na")+" fragments: "+o),f}return u},h.waitForCdnTuneIn=function(e){return e.live&&e.canBlockReload&&e.partTarget&&e.tuneInGoal>Math.max(e.partHoldBack,3*e.partTarget)},h.setStartPosition=function(e,t){var r=this.startPosition;if(r"+e))}}])&&we(n.prototype,d),Object.defineProperty(n,"prototype",{writable:!1}),i}(he);function We(){return self.MediaSource||self.WebKitMediaSource}function Ye(){return self.SourceBuffer||self.WebKitSourceBuffer}var qe=function(){var e=ENTRY_MODULE,t={},r=function r(i){var n=t[i];if(void 0!==n)return n.exports;var a=t[i]={exports:{}};return e[i].call(a.exports,a,a.exports,r),a.exports};r.m=e,r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,{a:t}),t},r.d=function(e,t){for(var i in t)r.o(t,i)&&!r.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var i=r(ENTRY_MODULE);return i.default||i}.toString().split("ENTRY_MODULE"),ze="\\(\\s*(/\\*.*?\\*/)?\\s*.*?([\\.|\\-|\\+|\\w|/|@]+).*?\\)";function Xe(e){return(e+"").replace(/[.?*+^$[\]\\(){}|-]/g,"\\$&")}function Qe(e,t,i){var n={};n[i]=[];var a=t.toString().replace(/^"[^"]+"/,"function"),s=a.match(/^function\s?\w*\(\w+,\s*\w+,\s*(\w+)\)/)||a.match(/^\(\w+,\s*\w+,\s*(\w+)\)\s?\=\s?\>/);if(!s)return n;for(var o,l=s[1],u=new RegExp("(\\\\n|\\W)"+Xe(l)+ze,"g");o=u.exec(a);)"dll-reference"!==o[3]&&n[i].push(o[3]);for(u=new RegExp("\\("+Xe(l)+'\\("(dll-reference\\s([\\.|\\-|\\+|\\w|/|@]+))"\\)\\)'+ze,"g");o=u.exec(a);)e[o[2]]||(n[i].push(o[1]),e[o[2]]=r(o[1]).m),n[o[2]]=n[o[2]]||[],n[o[2]].push(o[4]);for(var d,h=Object.keys(n),f=0;f0}),!1)}function Ze(e,t,r,i){var n=e[i].map((function(e){return'"'+e+'": '+t[i][e].toString().replace(/^"[^"]+"/,"function")})).join(",");return qe[0]+"{"+n+"}"+qe[1]+'"'+r+'"'+qe[2]}var Je=r(764),et=r(729),tt=We()||{isTypeSupported:function(){return!1}},rt=function(){function e(e,t,i,n){var a=this;this.hls=void 0,this.id=void 0,this.observer=void 0,this.frag=null,this.part=null,this.useWorker=void 0,this.worker=void 0,this.onwmsg=void 0,this.transmuxer=null,this.onTransmuxComplete=void 0,this.onFlush=void 0;var u=e.config;this.hls=e,this.id=t,this.useWorker=!!u.enableWorker,this.onTransmuxComplete=i,this.onFlush=n;var d=function(e,t){(t=t||{}).frag=a.frag,t.id=a.id,a.hls.trigger(e,t)};this.observer=new et.EventEmitter,this.observer.on(s.Events.FRAG_DECRYPTED,d),this.observer.on(s.Events.ERROR,d);var h={mp4:tt.isTypeSupported("video/mp4"),mpeg:tt.isTypeSupported("audio/mpeg"),mp3:tt.isTypeSupported('audio/mp4; codecs="mp3"')},f=navigator.vendor;if(this.useWorker&&"undefined"!=typeof Worker){var c;l.logger.log("demuxing in webworker");try{c=this.worker=function(e,t){t=t||{};var i={main:r.m},n=t.all?{main:Object.keys(i.main)}:function(e,t){for(var r={main:[t]},i={main:[]},n={main:{}};$e(r);)for(var a=Object.keys(r),s=0;s1&&u.id===(null==y?void 0:y.stats.chunkCount),A=!T&&(1===S||0===S&&(1===b||L&&b<=0)),D=self.performance.now();(T||S||0===n.stats.parsing.start)&&(n.stats.parsing.start=D),!a||!b&&A||(a.stats.parsing.start=D);var R=!(y&&(null===(h=n.initSegment)||void 0===h?void 0:h.url)===(null===(f=y.initSegment)||void 0===f?void 0:f.url)),k=new Je.TransmuxState(E,A,o,T,p,R);if(!A||E||R){l.logger.log("[transmuxer-interface, "+n.type+"]: Starting new transmux session for sn: "+u.sn+" p: "+u.part+" level: "+u.level+" id: "+u.id+"\n discontinuity: "+E+"\n trackSwitch: "+T+"\n contiguous: "+A+"\n accurateTimeOffset: "+o+"\n timeOffset: "+p+"\n initSegmentChange: "+R);var _=new Je.TransmuxConfig(r,i,t,s,d);this.configureTransmuxer(_)}if(this.frag=n,this.part=a,g)g.postMessage({cmd:"demux",data:e,decryptdata:m,chunkMeta:u,state:k},e instanceof ArrayBuffer?[e]:[]);else if(v){var x=v.push(e,m,u,k);(0,Je.isPromise)(x)?x.then((function(e){c.handleTransmuxComplete(e)})):this.handleTransmuxComplete(x)}},t.flush=function(e){var t=this;e.transmuxing.start=self.performance.now();var r=this.transmuxer,i=this.worker;if(i)i.postMessage({cmd:"flush",chunkMeta:e});else if(r){var n=r.flush(e);(0,Je.isPromise)(n)?n.then((function(r){t.handleFlushResult(r,e)})):this.handleFlushResult(n,e)}},t.handleFlushResult=function(e,t){var r=this;e.forEach((function(e){r.handleTransmuxComplete(e)})),this.onFlush(t)},t.onWorkerMessage=function(e){var t=e.data,r=this.hls;switch(t.event){case"init":self.URL.revokeObjectURL(this.worker.objectURL);break;case"transmuxComplete":this.handleTransmuxComplete(t.data);break;case"flush":this.onFlush(t.data);break;case"workerLog":l.logger[t.data.logType]&&l.logger[t.data.logType](t.data.message);break;default:t.data=t.data||{},t.data.frag=this.frag,t.data.id=this.id,r.trigger(t.event,t.data)}},t.configureTransmuxer=function(e){var t=this.worker,r=this.transmuxer;t?t.postMessage({cmd:"configure",config:e}):r&&r.configure(e)},t.handleTransmuxComplete=function(e){e.chunkMeta.transmuxing.end=self.performance.now(),this.onTransmuxComplete(e)},e}(),it=function(){function e(e,t,r,i){this.config=void 0,this.media=null,this.fragmentTracker=void 0,this.hls=void 0,this.nudgeRetry=0,this.stallReported=!1,this.stalled=null,this.moved=!1,this.seeking=!1,this.config=e,this.media=t,this.fragmentTracker=r,this.hls=i}var t=e.prototype;return t.destroy=function(){this.media=null,this.hls=this.fragmentTracker=null},t.poll=function(e,t){var r=this.config,i=this.media,n=this.stalled;if(null!==i){var a=i.currentTime,s=i.seeking,o=this.seeking&&!s,u=!this.seeking&&s;if(this.seeking=s,a===e){if((u||o)&&(this.stalled=null),!(i.paused&&!s||i.ended||0===i.playbackRate)&&ce.getBuffered(i).length){var d=ce.bufferInfo(i,a,0),h=d.len>0,f=d.nextStart||0;if(h||f){if(s){var c=d.len>2,v=!f||t&&t.start<=a||f-a>2&&!this.fragmentTracker.getPartialFragment(a);if(c||v)return;this.moved=!1}if(!this.moved&&null!==this.stalled){var g,p=Math.max(f,d.start||0)-a,m=this.hls.levels?this.hls.levels[this.hls.currentLevel]:null,y=(null==m||null===(g=m.details)||void 0===g?void 0:g.live)?2*m.details.targetduration:2;if(p>0&&p<=y)return void this._trySkipBufferHole(null)}var E=self.performance.now();if(null!==n){var T=E-n;if(s||!(T>=250)||(this._reportStall(d),this.media)){var S=ce.bufferInfo(i,a,r.maxBufferHole);this._tryFixBufferStall(S,T)}}else this.stalled=E}}}else if(this.moved=!0,null!==n){if(this.stallReported){var b=self.performance.now()-n;l.logger.warn("playback not stuck anymore @"+a+", after "+Math.round(b)+"ms"),this.stallReported=!1}this.stalled=null,this.nudgeRetry=0}}},t._tryFixBufferStall=function(e,t){var r=this.config,i=this.fragmentTracker,n=this.media;if(null!==n){var a=n.currentTime,s=i.getPartialFragment(a);if(s&&(this._trySkipBufferHole(s)||!this.media))return;e.len>r.maxBufferHole&&t>1e3*r.highBufferWatchdogPeriod&&(l.logger.warn("Trying to nudge playhead over buffer-hole"),this.stalled=null,this._tryNudgeBuffer())}},t._reportStall=function(e){var t=this.hls,r=this.media;!this.stallReported&&r&&(this.stallReported=!0,l.logger.warn("Playback stalling at @"+r.currentTime+" due to low buffer ("+JSON.stringify(e)+")"),t.trigger(s.Events.ERROR,{type:o.ErrorTypes.MEDIA_ERROR,details:o.ErrorDetails.BUFFER_STALLED_ERROR,fatal:!1,buffer:e.len}))},t._trySkipBufferHole=function(e){var t=this.config,r=this.hls,i=this.media;if(null===i)return 0;for(var n=i.currentTime,a=0,u=ce.getBuffered(i),d=0;d=a&&n1?(i=0,this.bitrateTest=!0):i=r.nextAutoLevel),this.level=r.nextLoadLevel=i,this.loadedmetadata=!1}t>0&&-1===e&&(this.log("Override startPosition with lastCurrentTime @"+t.toFixed(3)),e=t),this.state=Fe,this.nextLoadPosition=this.startPosition=this.lastCurrentTime=e,this.tick()}else this._forceStartLoad=!0,this.state=Oe},u.stopLoad=function(){this._forceStartLoad=!1,e.prototype.stopLoad.call(this)},u.doTick=function(){switch(this.state){case Fe:this.doTickIdle();break;case je:var e,t=this.levels,r=this.level,i=null==t||null===(e=t[r])||void 0===e?void 0:e.details;if(i&&(!i.live||this.levelLastLoaded===this.level)){if(this.waitForCdnTuneIn(i))break;this.state=Fe;break}break;case Ue:var n,a=self.performance.now(),s=this.retryDate;(!s||a>=s||null!==(n=this.media)&&void 0!==n&&n.seeking)&&(this.log("retryDate reached, switch back to IDLE state"),this.resetStartWhenNotLoaded(this.level),this.state=Fe)}this.onTickEnd()},u.onTickEnd=function(){e.prototype.onTickEnd.call(this),this.checkBuffer(),this.checkFragmentChanged()},u.doTickIdle=function(){var e,t,r=this.hls,i=this.levelLastLoaded,n=this.levels,a=this.media,o=r.config,l=r.nextLoadLevel;if(null!==i&&(a||!this.startFragRequested&&o.startFragPrefetch)&&(!this.altAudio||!this.audioOnly)&&n&&n[l]){var u=n[l];this.level=r.nextLoadLevel=l;var d=u.details;if(!d||this.state===je||d.live&&this.levelLastLoaded!==l)this.state=je;else{var h=this.getMainFwdBufferInfo();if(null!==h&&!(h.len>=this.getMaxBufferLength(u.maxBitrate))){if(this._streamEnded(h,d)){var f={};return this.altAudio&&(f.type="video"),this.hls.trigger(s.Events.BUFFER_EOS,f),void(this.state=Ve)}this.backtrackFragment&&this.backtrackFragment.start>h.end&&(this.backtrackFragment=null);var c=this.backtrackFragment?this.backtrackFragment.start:h.end,v=this.getNextFragment(c,d);if(this.couldBacktrack&&!this.fragPrevious&&v&&"initSegment"!==v.sn&&this.fragmentTracker.getState(v)!==ae.OK){var g,m=(null!=(g=this.backtrackFragment)?g:v).sn-d.startSN,y=d.fragments[m-1];y&&v.cc===y.cc&&(v=y,this.fragmentTracker.removeFragment(y))}else this.backtrackFragment&&h.len&&(this.backtrackFragment=null);if(v&&this.fragmentTracker.getState(v)===ae.OK&&this.nextLoadPosition>c){var E=this.audioOnly&&!this.altAudio?p.ElementaryStreamTypes.AUDIO:p.ElementaryStreamTypes.VIDEO;a&&this.afterBufferFlushed(a,E,C.PlaylistLevelType.MAIN),v=this.getNextFragment(this.nextLoadPosition,d)}v&&(!v.initSegment||v.initSegment.data||this.bitrateTest||(v=v.initSegment),"identity"!==(null===(e=v.decryptdata)||void 0===e?void 0:e.keyFormat)||null!==(t=v.decryptdata)&&void 0!==t&&t.key?this.loadFragment(v,d,c):this.loadKey(v,d))}}}},u.loadFragment=function(t,r,i){var n,a=this.fragmentTracker.getState(t);this.fragCurrent=t,a===ae.NOT_LOADED?"initSegment"===t.sn?this._loadInitSegment(t):this.bitrateTest?(this.log("Fragment "+t.sn+" of level "+t.level+" is being downloaded to test bitrate and will not be buffered"),this._loadBitrateTestFrag(t)):(this.startFragRequested=!0,e.prototype.loadFragment.call(this,t,r,i)):a===ae.APPENDING?this.reduceMaxBufferLength(t.duration)&&this.fragmentTracker.removeFragment(t):0===(null===(n=this.media)||void 0===n?void 0:n.buffered.length)&&this.fragmentTracker.removeAllFragments()},u.getAppendedFrag=function(e){var t=this.fragmentTracker.getAppendedFrag(e,C.PlaylistLevelType.MAIN);return t&&"fragment"in t?t.fragment:t},u.getBufferedFrag=function(e){return this.fragmentTracker.getBufferedFrag(e,C.PlaylistLevelType.MAIN)},u.followingBufferedFrag=function(e){return e?this.getBufferedFrag(e.end+.5):null},u.immediateLevelSwitch=function(){this.abortCurrentFrag(),this.flushMainBuffer(0,Number.POSITIVE_INFINITY)},u.nextLevelSwitch=function(){var e=this.levels,t=this.media;if(null!=t&&t.readyState){var r,i=this.getAppendedFrag(t.currentTime);if(i&&i.start>1&&this.flushMainBuffer(0,i.start-1),!t.paused&&e){var n=e[this.hls.nextLoadLevel],a=this.fragLastKbps;r=a&&this.fragCurrent?this.fragCurrent.duration*n.maxBitrate/(1e3*a)+1:0}else r=0;var s=this.getBufferedFrag(t.currentTime+r);if(s){var o=this.followingBufferedFrag(s);if(o){this.abortCurrentFrag();var l=o.maxStartPTS?o.maxStartPTS:o.start,u=o.duration,d=Math.max(s.end,l+Math.min(Math.max(u-this.config.maxFragLookUpTolerance,.5*u),.75*u));this.flushMainBuffer(d,Number.POSITIVE_INFINITY)}}}},u.abortCurrentFrag=function(){var e=this.fragCurrent;switch(this.fragCurrent=null,this.backtrackFragment=null,null!=e&&e.loader&&e.loader.abort(),this.state){case Me:case Ne:case Ue:case Be:case Ge:this.state=Fe}this.nextLoadPosition=this.getLoadPosition()},u.flushMainBuffer=function(t,r){e.prototype.flushMainBuffer.call(this,t,r,this.altAudio?"video":null)},u.onMediaAttached=function(t,r){e.prototype.onMediaAttached.call(this,t,r);var i=r.media;this.onvplaying=this.onMediaPlaying.bind(this),this.onvseeked=this.onMediaSeeked.bind(this),i.addEventListener("playing",this.onvplaying),i.addEventListener("seeked",this.onvseeked),this.gapController=new it(this.config,i,this.fragmentTracker,this.hls)},u.onMediaDetaching=function(){var t=this.media;t&&this.onvplaying&&this.onvseeked&&(t.removeEventListener("playing",this.onvplaying),t.removeEventListener("seeked",this.onvseeked),this.onvplaying=this.onvseeked=null,this.videoBuffer=null),this.fragPlaying=null,this.gapController&&(this.gapController.destroy(),this.gapController=null),e.prototype.onMediaDetaching.call(this)},u.onMediaPlaying=function(){this.tick()},u.onMediaSeeked=function(){var e=this.media,t=e?e.currentTime:null;(0,a.isFiniteNumber)(t)&&this.log("Media seeked to "+t.toFixed(3)),this.tick()},u.onManifestLoading=function(){this.log("Trigger BUFFER_RESET"),this.hls.trigger(s.Events.BUFFER_RESET,void 0),this.fragmentTracker.removeAllFragments(),this.couldBacktrack=!1,this.startPosition=this.lastCurrentTime=0,this.fragPlaying=null,this.backtrackFragment=null},u.onManifestParsed=function(e,t){var r,i,n,a=!1,s=!1;t.levels.forEach((function(e){(r=e.audioCodec)&&(-1!==r.indexOf("mp4a.40.2")&&(a=!0),-1!==r.indexOf("mp4a.40.5")&&(s=!0))})),this.audioCodecSwitch=a&&s&&!("function"==typeof(null==(n=Ye())||null===(i=n.prototype)||void 0===i?void 0:i.changeType)),this.audioCodecSwitch&&this.log("Both AAC/HE-AAC audio found in levels; declaring level codec as HE-AAC"),this.levels=t.levels,this.startFragRequested=!1},u.onLevelLoading=function(e,t){var r=this.levels;if(r&&this.state===Fe){var i=r[t.level];(!i.details||i.details.live&&this.levelLastLoaded!==t.level||this.waitForCdnTuneIn(i.details))&&(this.state=je)}},u.onLevelLoaded=function(e,t){var r,i=this.levels,n=t.level,a=t.details,o=a.totalduration;if(i){this.log("Level "+n+" loaded ["+a.startSN+","+a.endSN+"], cc ["+a.startCC+", "+a.endCC+"] duration:"+o);var l=this.fragCurrent;!l||this.state!==Ne&&this.state!==Ue||l.level!==t.level&&l.loader&&(this.state=Fe,this.backtrackFragment=null,l.loader.abort());var u=i[n],d=0;if(a.live||null!==(r=u.details)&&void 0!==r&&r.live){if(a.fragments[0]||(a.deltaUpdateFailed=!0),a.deltaUpdateFailed)return;d=this.alignPlaylists(a,u.details)}if(u.details=a,this.levelLastLoaded=n,this.hls.trigger(s.Events.LEVEL_UPDATED,{details:a,level:n}),this.state===je){if(this.waitForCdnTuneIn(a))return;this.state=Fe}this.startFragRequested?a.live&&this.synchronizeToLiveEdge(a):this.setStartPosition(a,d),this.tick()}else this.warn("Levels were reset while loading level "+n)},u._handleFragmentLoadProgress=function(e){var t,r=e.frag,i=e.part,n=e.payload,a=this.levels;if(a){var s=a[r.level],o=s.details;if(o){var l=s.videoCodec,u=o.PTSKnown||!o.live,d=null===(t=r.initSegment)||void 0===t?void 0:t.data,h=this._getAudioCodec(s),f=this.transmuxer=this.transmuxer||new rt(this.hls,C.PlaylistLevelType.MAIN,this._handleTransmuxComplete.bind(this),this._handleTransmuxerFlush.bind(this)),c=i?i.index:-1,v=-1!==c,g=new ve(r.level,r.sn,r.stats.chunkCount,n.byteLength,c,v),p=this.initPTS[r.cc];f.push(n,d,h,l,r,i,o.totalduration,u,g,p)}else this.warn("Dropping fragment "+r.sn+" of level "+r.level+" after level details were reset")}else this.warn("Levels were reset while fragment load was in progress. Fragment "+r.sn+" of level "+r.level+" will not be buffered")},u.onAudioTrackSwitching=function(e,t){var r=this.altAudio,i=!!t.url,n=t.id;if(!i){if(this.mediaBuffer!==this.media){this.log("Switching on main audio, use media.buffered to schedule main fragment loading"),this.mediaBuffer=this.media;var a=this.fragCurrent;null!=a&&a.loader&&(this.log("Switching to main audio track, cancel main fragment load"),a.loader.abort()),this.resetTransmuxer(),this.resetLoadingState()}else this.audioOnly&&this.resetTransmuxer();var o=this.hls;r&&o.trigger(s.Events.BUFFER_FLUSHING,{startOffset:0,endOffset:Number.POSITIVE_INFINITY,type:"audio"}),o.trigger(s.Events.AUDIO_TRACK_SWITCHED,{id:n})}},u.onAudioTrackSwitched=function(e,t){var r=t.id,i=!!this.hls.audioTracks[r].url;if(i){var n=this.videoBuffer;n&&this.mediaBuffer!==n&&(this.log("Switching on alternate audio, use video.buffered to schedule main fragment loading"),this.mediaBuffer=n)}this.altAudio=i,this.tick()},u.onBufferCreated=function(e,t){var r,i,n=t.tracks,a=!1;for(var s in n){var o=n[s];if("main"===o.id){if(i=s,r=o,"video"===s){var l=n[s];l&&(this.videoBuffer=l.buffer)}}else a=!0}a&&r?(this.log("Alternate track found, use "+i+".buffered to schedule main fragment loading"),this.mediaBuffer=r.buffer):this.mediaBuffer=this.media},u.onFragBuffered=function(e,t){var r=t.frag,i=t.part;if(!r||r.type===C.PlaylistLevelType.MAIN){if(this.fragContextChanged(r))return this.warn("Fragment "+r.sn+(i?" p: "+i.index:"")+" of level "+r.level+" finished buffering, but was aborted. state: "+this.state),void(this.state===Ge&&(this.state=Fe));var n=i?i.stats:r.stats;this.fragLastKbps=Math.round(8*n.total/(n.buffering.end-n.loading.first)),"initSegment"!==r.sn&&(this.fragPrevious=r),this.fragBufferedComplete(r,i)}},u.onError=function(e,t){switch(t.details){case o.ErrorDetails.FRAG_LOAD_ERROR:case o.ErrorDetails.FRAG_LOAD_TIMEOUT:case o.ErrorDetails.KEY_LOAD_ERROR:case o.ErrorDetails.KEY_LOAD_TIMEOUT:this.onFragmentOrKeyLoadError(C.PlaylistLevelType.MAIN,t);break;case o.ErrorDetails.LEVEL_LOAD_ERROR:case o.ErrorDetails.LEVEL_LOAD_TIMEOUT:this.state!==He&&(t.fatal?(this.warn(""+t.details),this.state=He):t.levelRetry||this.state!==je||(this.state=Fe));break;case o.ErrorDetails.BUFFER_FULL_ERROR:if("main"===t.parent&&(this.state===Be||this.state===Ge)){var r=!0,i=this.getFwdBufferInfo(this.media,C.PlaylistLevelType.MAIN);i&&i.len>.5&&(r=!this.reduceMaxBufferLength(i.len)),r&&(this.warn("buffer full error also media.currentTime is not buffered, flush main"),this.immediateLevelSwitch()),this.resetLoadingState()}}},u.checkBuffer=function(){var e=this.media,t=this.gapController;if(e&&t&&e.readyState){if(this.loadedmetadata||!ce.getBuffered(e).length){var r=this.state!==Fe?this.fragCurrent:null;t.poll(this.lastCurrentTime,r)}this.lastCurrentTime=e.currentTime}},u.onFragLoadEmergencyAborted=function(){this.state=Fe,this.loadedmetadata||(this.startFragRequested=!1,this.nextLoadPosition=this.startPosition),this.tickImmediate()},u.onBufferFlushed=function(e,t){var r=t.type;if(r!==p.ElementaryStreamTypes.AUDIO||this.audioOnly&&!this.altAudio){var i=(r===p.ElementaryStreamTypes.VIDEO?this.videoBuffer:this.mediaBuffer)||this.media;this.afterBufferFlushed(i,r,C.PlaylistLevelType.MAIN)}},u.onLevelsUpdated=function(e,t){this.levels=t.levels},u.swapAudioCodec=function(){this.audioCodecSwap=!this.audioCodecSwap},u.seekToStartPos=function(){var e=this.media;if(e){var t=e.currentTime,r=this.startPosition;if(r>=0&&t0&&(n1&&!1===e.seeking){var r=e.currentTime;if(ce.isBuffered(e,r)?t=this.getAppendedFrag(r):ce.isBuffered(e,r+.1)&&(t=this.getAppendedFrag(r+.1)),t){this.backtrackFragment=null;var i=this.fragPlaying,n=t.level;i&&t.sn===i.sn&&i.level===n&&t.urlId===i.urlId||(this.hls.trigger(s.Events.FRAG_CHANGED,{frag:t}),i&&i.level===n||this.hls.trigger(s.Events.LEVEL_SWITCHED,{level:n}),this.fragPlaying=t)}}},n=i,(l=[{key:"nextLevel",get:function(){var e=this.nextBufferedFrag;return e?e.level:-1}},{key:"currentFrag",get:function(){var e=this.media;return e?this.fragPlaying||this.getAppendedFrag(e.currentTime):null}},{key:"currentProgramDateTime",get:function(){var e=this.media;if(e){var t=e.currentTime,r=this.currentFrag;if(r&&(0,a.isFiniteNumber)(t)&&(0,a.isFiniteNumber)(r.programDateTime)){var i=r.programDateTime+1e3*(t-r.start);return new Date(i)}}return null}},{key:"currentLevel",get:function(){var e=this.currentFrag;return e?e.level:-1}},{key:"nextBufferedFrag",get:function(){var e=this.currentFrag;return e?this.followingBufferedFrag(e):null}},{key:"forceStartLoad",get:function(){return this._forceStartLoad}}])&&nt(n.prototype,l),Object.defineProperty(n,"prototype",{writable:!1}),i}(Ke);const ot=function(){function e(e,t,r){void 0===t&&(t=0),void 0===r&&(r=0),this.halfLife=void 0,this.alpha_=void 0,this.estimate_=void 0,this.totalWeight_=void 0,this.halfLife=e,this.alpha_=e?Math.exp(Math.log(.5)/e):0,this.estimate_=t,this.totalWeight_=r}var t=e.prototype;return t.sample=function(e,t){var r=Math.pow(this.alpha_,e);this.estimate_=t*(1-r)+r*this.estimate_,this.totalWeight_+=e},t.getTotalWeight=function(){return this.totalWeight_},t.getEstimate=function(){if(this.alpha_){var e=1-Math.pow(this.alpha_,this.totalWeight_);if(e)return this.estimate_/e}return this.estimate_},e}(),lt=function(){function e(e,t,r){this.defaultEstimate_=void 0,this.minWeight_=void 0,this.minDelayMs_=void 0,this.slow_=void 0,this.fast_=void 0,this.defaultEstimate_=r,this.minWeight_=.001,this.minDelayMs_=50,this.slow_=new ot(e),this.fast_=new ot(t)}var t=e.prototype;return t.update=function(e,t){var r=this.slow_,i=this.fast_;this.slow_.halfLife!==e&&(this.slow_=new ot(e,r.getEstimate(),r.getTotalWeight())),this.fast_.halfLife!==t&&(this.fast_=new ot(t,i.getEstimate(),i.getTotalWeight()))},t.sample=function(e,t){var r=(e=Math.max(e,this.minDelayMs_))/1e3,i=8*t/r;this.fast_.sample(r,i),this.slow_.sample(r,i)},t.canEstimate=function(){var e=this.fast_;return e&&e.getTotalWeight()>=this.minWeight_},t.getEstimate=function(){return this.canEstimate()?Math.min(this.fast_.getEstimate(),this.slow_.getEstimate()):this.defaultEstimate_},t.destroy=function(){},e}();function ut(e,t){for(var r=0;rp;b--){var A=g[b].maxBitrate;if((L=E?u*A/(6.4*E):u*A/v)=T||(l.logger.warn("Fragment "+e.sn+(t?" part "+t.index:"")+" of level "+e.level+" is loading too slowly and will cause an underbuffer; aborting and switching to level "+b+"\n Current BW estimate: "+((0,a.isFiniteNumber)(v)?(v/1024).toFixed(3):"Unknown")+" Kb/s\n Estimated load time for current fragment: "+T.toFixed(3)+" s\n Estimated load time for the next fragment: "+L.toFixed(3)+" s\n Time to underbuffer: "+S.toFixed(3)+" s"),r.nextLoadLevel=b,c&&this.bwEstimator.sample(h,o.loaded),this.clearTimer(),e.loader&&(this.fragCurrent=this.partCurrent=null,e.loader.abort()),r.trigger(s.Events.FRAG_LOAD_EMERGENCY_ABORTED,{frag:e,part:t,stats:o}))}}}}}},i.onFragLoaded=function(e,t){var r=t.frag,i=t.part;if(r.type===C.PlaylistLevelType.MAIN&&(0,a.isFiniteNumber)(r.sn)){var n=i?i.stats:r.stats,o=i?i.duration:r.duration;if(this.clearTimer(),this.lastLoadedFragLevel=r.level,this._nextAutoLevel=-1,this.hls.config.abrMaxWithRealBitrate){var l=this.hls.levels[r.level],u=(l.loaded?l.loaded.bytes:0)+n.loaded,d=(l.loaded?l.loaded.duration:0)+o;l.loaded={bytes:u,duration:d},l.realBitrate=Math.round(8*u/d)}if(r.bitrateTest){var h={stats:n,frag:r,part:i,id:r.type};this.onFragBuffered(s.Events.FRAG_BUFFERED,h)}}},i.onFragBuffered=function(e,t){var r=t.frag,i=t.part,n=i?i.stats:r.stats;if(!n.aborted&&r.type===C.PlaylistLevelType.MAIN&&"initSegment"!==r.sn){var a=n.parsing.end-n.loading.start;this.bwEstimator.sample(a,n.loaded),n.bwEstimate=this.bwEstimator.getEstimate(),r.bitrateTest?this.bitrateTestDelay=a/1e3:this.bitrateTestDelay=0}},i.onError=function(e,t){switch(t.details){case o.ErrorDetails.FRAG_LOAD_ERROR:case o.ErrorDetails.FRAG_LOAD_TIMEOUT:this.clearTimer()}},i.clearTimer=function(){self.clearInterval(this.timer),this.timer=void 0},i.getNextABRAutoLevel=function(){var e=this.fragCurrent,t=this.partCurrent,r=this.hls,i=r.maxAutoLevel,n=r.config,a=r.minAutoLevel,s=r.media,o=t?t.duration:e?e.duration:0,u=(s&&s.currentTime,s&&0!==s.playbackRate?Math.abs(s.playbackRate):1),d=this.bwEstimator?this.bwEstimator.getEstimate():n.abrEwmaDefaultEstimate,h=r.mainForwardBufferInfo,f=(h?h.len:0)/u,c=this.findBestLevel(d,a,i,f,n.abrBandWidthFactor,n.abrBandWidthUpFactor);if(c>=0)return c;l.logger.trace((f?"rebuffering expected":"buffer is empty")+", finding optimal quality level");var v=o?Math.min(o,n.maxStarvationDelay):n.maxStarvationDelay,g=n.abrBandWidthFactor,p=n.abrBandWidthUpFactor;if(!f){var m=this.bitrateTestDelay;m&&(v=(o?Math.min(o,n.maxLoadingDelay):n.maxLoadingDelay)-m,l.logger.trace("bitrate test took "+Math.round(1e3*m)+"ms, set first fragment max fetchDuration to "+Math.round(1e3*v)+" ms"),g=p=1)}return c=this.findBestLevel(d,a,i,f+v,g,p),Math.max(c,0)},i.findBestLevel=function(e,t,r,i,n,s){for(var o,u=this.fragCurrent,d=this.partCurrent,h=this.lastLoadedFragLevel,f=this.hls.levels,c=f[h],v=!(null==c||null===(o=c.details)||void 0===o||!o.live),g=null==c?void 0:c.codecSet,p=d?d.duration:u?u.duration:0,m=r;m>=t;m--){var y=f[m];if(y&&(!g||y.codecSet===g)){var E,T=y.details,S=(d?null==T?void 0:T.partTarget:null==T?void 0:T.averagetargetduration)||p;E=m<=h?n*e:s*e;var b=f[m].maxBitrate,L=b*S/E;if(l.logger.trace("level/adjustedbw/bitrate/avgDuration/maxFetchDuration/fetchDuration: "+m+"/"+Math.round(E)+"/"+b+"/"+S+"/"+i+"/"+L),E>b&&(0===L||!(0,a.isFiniteNumber)(L)||v&&!this.bitrateTestDelay||L0||Object.keys(this.pendingTracks).length>0},t.destroy=function(){this.unregisterListeners(),this.details=null},t.registerListeners=function(){var e=this.hls;e.on(s.Events.MEDIA_ATTACHING,this.onMediaAttaching,this),e.on(s.Events.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(s.Events.MANIFEST_PARSED,this.onManifestParsed,this),e.on(s.Events.BUFFER_RESET,this.onBufferReset,this),e.on(s.Events.BUFFER_APPENDING,this.onBufferAppending,this),e.on(s.Events.BUFFER_CODECS,this.onBufferCodecs,this),e.on(s.Events.BUFFER_EOS,this.onBufferEos,this),e.on(s.Events.BUFFER_FLUSHING,this.onBufferFlushing,this),e.on(s.Events.LEVEL_UPDATED,this.onLevelUpdated,this),e.on(s.Events.FRAG_PARSED,this.onFragParsed,this),e.on(s.Events.FRAG_CHANGED,this.onFragChanged,this)},t.unregisterListeners=function(){var e=this.hls;e.off(s.Events.MEDIA_ATTACHING,this.onMediaAttaching,this),e.off(s.Events.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(s.Events.MANIFEST_PARSED,this.onManifestParsed,this),e.off(s.Events.BUFFER_RESET,this.onBufferReset,this),e.off(s.Events.BUFFER_APPENDING,this.onBufferAppending,this),e.off(s.Events.BUFFER_CODECS,this.onBufferCodecs,this),e.off(s.Events.BUFFER_EOS,this.onBufferEos,this),e.off(s.Events.BUFFER_FLUSHING,this.onBufferFlushing,this),e.off(s.Events.LEVEL_UPDATED,this.onLevelUpdated,this),e.off(s.Events.FRAG_PARSED,this.onFragParsed,this),e.off(s.Events.FRAG_CHANGED,this.onFragChanged,this)},t._initSourceBuffer=function(){this.sourceBuffer={},this.operationQueue=new ct(this.sourceBuffer),this.listeners={audio:[],video:[],audiovideo:[]}},t.onManifestParsed=function(e,t){var r=2;(t.audio&&!t.video||!t.altAudio)&&(r=1),this.bufferCodecEventsExpected=this._bufferCodecEventsTotal=r,this.details=null,l.logger.log(this.bufferCodecEventsExpected+" bufferCodec event(s) expected")},t.onMediaAttaching=function(e,t){var r=this.media=t.media;if(r&&vt){var i=this.mediaSource=new vt;i.addEventListener("sourceopen",this._onMediaSourceOpen),i.addEventListener("sourceended",this._onMediaSourceEnded),i.addEventListener("sourceclose",this._onMediaSourceClose),r.src=self.URL.createObjectURL(i),this._objectUrl=r.src}},t.onMediaDetaching=function(){var e=this.media,t=this.mediaSource,r=this._objectUrl;if(t){if(l.logger.log("[buffer-controller]: media source detaching"),"open"===t.readyState)try{t.endOfStream()}catch(e){l.logger.warn("[buffer-controller]: onMediaDetaching: "+e.message+" while calling endOfStream")}this.onBufferReset(),t.removeEventListener("sourceopen",this._onMediaSourceOpen),t.removeEventListener("sourceended",this._onMediaSourceEnded),t.removeEventListener("sourceclose",this._onMediaSourceClose),e&&(r&&self.URL.revokeObjectURL(r),e.src===r?(e.removeAttribute("src"),e.load()):l.logger.warn("[buffer-controller]: media.src was changed by a third party - skip cleanup")),this.mediaSource=null,this.media=null,this._objectUrl=null,this.bufferCodecEventsExpected=this._bufferCodecEventsTotal,this.pendingTracks={},this.tracks={}}this.hls.trigger(s.Events.MEDIA_DETACHED,void 0)},t.onBufferReset=function(){var e=this;this.getSourceBufferTypes().forEach((function(t){var r=e.sourceBuffer[t];try{r&&(e.removeBufferListeners(t),e.mediaSource&&e.mediaSource.removeSourceBuffer(r),e.sourceBuffer[t]=void 0)}catch(e){l.logger.warn("[buffer-controller]: Failed to reset the "+t+" buffer",e)}})),this._initSourceBuffer()},t.onBufferCodecs=function(e,t){var r=this,i=this.getSourceBufferTypes().length;Object.keys(t).forEach((function(e){if(i){var n=r.tracks[e];if(n&&"function"==typeof n.buffer.changeType){var a=t[e],s=a.id,o=a.codec,u=a.levelCodec,d=a.container,h=a.metadata,f=(n.levelCodec||n.codec).replace(gt,"$1"),c=(u||o).replace(gt,"$1");if(f!==c){var v=d+";codecs="+(u||o);r.appendChangeType(e,v),l.logger.log("[buffer-controller]: switching codec "+f+" to "+c),r.tracks[e]={buffer:n.buffer,codec:o,container:d,levelCodec:u,metadata:h,id:s}}}}else r.pendingTracks[e]=t[e]})),i||(this.bufferCodecEventsExpected=Math.max(this.bufferCodecEventsExpected-1,0),this.mediaSource&&"open"===this.mediaSource.readyState&&this.checkPendingTracks())},t.appendChangeType=function(e,t){var r=this,i=this.operationQueue,n={execute:function(){var n=r.sourceBuffer[e];n&&(l.logger.log("[buffer-controller]: changing "+e+" sourceBuffer type to "+t),n.changeType(t)),i.shiftAndExecuteNext(e)},onStart:function(){},onComplete:function(){},onError:function(t){l.logger.warn("[buffer-controller]: Failed to change "+e+" SourceBuffer type",t)}};i.append(n,e)},t.onBufferAppending=function(e,t){var r=this,i=this.hls,n=this.operationQueue,a=this.tracks,u=t.data,d=t.type,h=t.frag,f=t.part,c=t.chunkMeta,v=c.buffering[d],g=self.performance.now();v.start=g;var p=h.stats.buffering,m=f?f.stats.buffering:null;0===p.start&&(p.start=g),m&&0===m.start&&(m.start=g);var y=a.audio,E="audio"===d&&1===c.id&&"audio/mpeg"===(null==y?void 0:y.container),T={execute:function(){if(v.executeStart=self.performance.now(),E){var e=r.sourceBuffer[d];if(e){var t=h.start-e.timestampOffset;Math.abs(t)>=.1&&(l.logger.log("[buffer-controller]: Updating audio SourceBuffer timestampOffset to "+h.start+" (delta: "+t+") sn: "+h.sn+")"),e.timestampOffset=h.start)}}r.appendExecutor(u,d)},onStart:function(){},onComplete:function(){var e=self.performance.now();v.executeEnd=v.end=e,0===p.first&&(p.first=e),m&&0===m.first&&(m.first=e);var t=r.sourceBuffer,i={};for(var n in t)i[n]=ce.getBuffered(t[n]);r.appendError=0,r.hls.trigger(s.Events.BUFFER_APPENDED,{type:d,frag:h,part:f,chunkMeta:c,parent:h.type,timeRanges:i})},onError:function(e){l.logger.error("[buffer-controller]: Error encountered while trying to append to the "+d+" SourceBuffer",e);var t={type:o.ErrorTypes.MEDIA_ERROR,parent:h.type,details:o.ErrorDetails.BUFFER_APPEND_ERROR,err:e,fatal:!1};e.code===DOMException.QUOTA_EXCEEDED_ERR?t.details=o.ErrorDetails.BUFFER_FULL_ERROR:(r.appendError++,t.details=o.ErrorDetails.BUFFER_APPEND_ERROR,r.appendError>i.config.appendErrorMaxRetry&&(l.logger.error("[buffer-controller]: Failed "+i.config.appendErrorMaxRetry+" times to append segment in sourceBuffer"),t.fatal=!0,i.stopLoad())),i.trigger(s.Events.ERROR,t)}};n.append(T,d)},t.onBufferFlushing=function(e,t){var r=this,i=this.operationQueue,n=function(e){return{execute:r.removeExecutor.bind(r,e,t.startOffset,t.endOffset),onStart:function(){},onComplete:function(){r.hls.trigger(s.Events.BUFFER_FLUSHED,{type:e})},onError:function(t){l.logger.warn("[buffer-controller]: Failed to remove from "+e+" SourceBuffer",t)}}};t.type?i.append(n(t.type),t.type):this.getSourceBufferTypes().forEach((function(e){i.append(n(e),e)}))},t.onFragParsed=function(e,t){var r=this,i=t.frag,n=t.part,a=[],o=n?n.elementaryStreams:i.elementaryStreams;o[p.ElementaryStreamTypes.AUDIOVIDEO]?a.push("audiovideo"):(o[p.ElementaryStreamTypes.AUDIO]&&a.push("audio"),o[p.ElementaryStreamTypes.VIDEO]&&a.push("video")),0===a.length&&l.logger.warn("Fragments must have at least one ElementaryStreamType set. type: "+i.type+" level: "+i.level+" sn: "+i.sn),this.blockBuffers((function(){var e=self.performance.now();i.stats.buffering.end=e,n&&(n.stats.buffering.end=e);var t=n?n.stats:i.stats;r.hls.trigger(s.Events.FRAG_BUFFERED,{frag:i,part:n,stats:t,id:i.type})}),a)},t.onFragChanged=function(e,t){this.flushBackBuffer()},t.onBufferEos=function(e,t){var r=this;this.getSourceBufferTypes().reduce((function(e,i){var n=r.sourceBuffer[i];return t.type&&t.type!==i||n&&!n.ended&&(n.ended=!0,l.logger.log("[buffer-controller]: "+i+" sourceBuffer now EOS")),e&&!(n&&!n.ended)}),!0)&&this.blockBuffers((function(){var e=r.mediaSource;e&&"open"===e.readyState&&e.endOfStream()}))},t.onLevelUpdated=function(e,t){var r=t.details;r.fragments.length&&(this.details=r,this.getSourceBufferTypes().length?this.blockBuffers(this.updateMediaElementDuration.bind(this)):this.updateMediaElementDuration())},t.flushBackBuffer=function(){var e=this.hls,t=this.details,r=this.media,i=this.sourceBuffer;if(r&&null!==t){var n=this.getSourceBufferTypes();if(n.length){var o=t.live&&null!==e.config.liveBackBufferLength?e.config.liveBackBufferLength:e.config.backBufferLength;if((0,a.isFiniteNumber)(o)&&!(o<0)){var l=r.currentTime,u=t.levelTargetDuration,d=Math.max(o,u),h=Math.floor(l/u)*u-d;n.forEach((function(r){var n=i[r];if(n){var a=ce.getBuffered(n);a.length>0&&h>a.start(0)&&(e.trigger(s.Events.BACK_BUFFER_REACHED,{bufferEnd:h}),t.live&&e.trigger(s.Events.LIVE_BACK_BUFFER_REACHED,{bufferEnd:h}),e.trigger(s.Events.BUFFER_FLUSHING,{startOffset:0,endOffset:h,type:r}))}}))}}}},t.updateMediaElementDuration=function(){if(this.details&&this.media&&this.mediaSource&&"open"===this.mediaSource.readyState){var e=this.details,t=this.hls,r=this.media,i=this.mediaSource,n=e.fragments[0].start+e.totalduration,s=r.duration,o=(0,a.isFiniteNumber)(i.duration)?i.duration:0;e.live&&t.config.liveDurationInfinity?(l.logger.log("[buffer-controller]: Media Source duration is set to Infinity"),i.duration=1/0,this.updateSeekableRange(e)):(n>o&&n>s||!(0,a.isFiniteNumber)(s))&&(l.logger.log("[buffer-controller]: Updating Media Source duration to "+n.toFixed(3)),i.duration=n)}},t.updateSeekableRange=function(e){var t=this.mediaSource,r=e.fragments;if(r.length&&e.live&&null!=t&&t.setLiveSeekableRange){var i=Math.max(0,r[0].start),n=Math.max(i,i+e.totalduration);t.setLiveSeekableRange(i,n)}},t.checkPendingTracks=function(){var e=this.bufferCodecEventsExpected,t=this.operationQueue,r=this.pendingTracks,i=Object.keys(r).length;if(i&&!e||2===i){this.createSourceBuffers(r),this.pendingTracks={};var n=this.getSourceBufferTypes();if(0===n.length)return void this.hls.trigger(s.Events.ERROR,{type:o.ErrorTypes.MEDIA_ERROR,details:o.ErrorDetails.BUFFER_INCOMPATIBLE_CODECS_ERROR,fatal:!0,reason:"could not create source buffer for media codec(s)"});n.forEach((function(e){t.executeNext(e)}))}},t.createSourceBuffers=function(e){var t=this.sourceBuffer,r=this.mediaSource;if(!r)throw Error("createSourceBuffers called when mediaSource was null");var i=0;for(var n in e)if(!t[n]){var a=e[n];if(!a)throw Error("source buffer exists for track "+n+", however track does not");var u=a.levelCodec||a.codec,d=a.container+";codecs="+u;l.logger.log("[buffer-controller]: creating sourceBuffer("+d+")");try{var h=t[n]=r.addSourceBuffer(d),f=n;this.addBufferListener(f,"updatestart",this._onSBUpdateStart),this.addBufferListener(f,"updateend",this._onSBUpdateEnd),this.addBufferListener(f,"error",this._onSBUpdateError),this.tracks[n]={buffer:h,codec:u,container:a.container,levelCodec:a.levelCodec,metadata:a.metadata,id:a.id},i++}catch(e){l.logger.error("[buffer-controller]: error while trying to add sourceBuffer: "+e.message),this.hls.trigger(s.Events.ERROR,{type:o.ErrorTypes.MEDIA_ERROR,details:o.ErrorDetails.BUFFER_ADD_CODEC_ERROR,fatal:!1,error:e,mimeType:d})}}i&&this.hls.trigger(s.Events.BUFFER_CREATED,{tracks:this.tracks})},t._onSBUpdateStart=function(e){this.operationQueue.current(e).onStart()},t._onSBUpdateEnd=function(e){var t=this.operationQueue;t.current(e).onComplete(),t.shiftAndExecuteNext(e)},t._onSBUpdateError=function(e,t){l.logger.error("[buffer-controller]: "+e+" SourceBuffer error",t),this.hls.trigger(s.Events.ERROR,{type:o.ErrorTypes.MEDIA_ERROR,details:o.ErrorDetails.BUFFER_APPENDING_ERROR,fatal:!1});var r=this.operationQueue.current(e);r&&r.onError(t)},t.removeExecutor=function(e,t,r){var i=this.media,n=this.mediaSource,s=this.operationQueue,o=this.sourceBuffer[e];if(!i||!n||!o)return l.logger.warn("[buffer-controller]: Attempting to remove from the "+e+" SourceBuffer, but it does not exist"),void s.shiftAndExecuteNext(e);var u=(0,a.isFiniteNumber)(i.duration)?i.duration:1/0,d=(0,a.isFiniteNumber)(n.duration)?n.duration:1/0,h=Math.max(0,t),f=Math.min(r,u,d);f>h?(l.logger.log("[buffer-controller]: Removing ["+h+","+f+"] from the "+e+" SourceBuffer"),o.remove(h,f)):s.shiftAndExecuteNext(e)},t.appendExecutor=function(e,t){var r=this.operationQueue,i=this.sourceBuffer[t];if(!i)return l.logger.warn("[buffer-controller]: Attempting to append to the "+t+" SourceBuffer, but it does not exist"),void r.shiftAndExecuteNext(t);i.ended=!1,i.appendBuffer(e)},t.blockBuffers=function(e,t){var r=this;if(void 0===t&&(t=this.getSourceBufferTypes()),!t.length)return l.logger.log("[buffer-controller]: Blocking operation requested, but no SourceBuffers exist"),void Promise.resolve().then(e);var i=this.operationQueue,n=t.map((function(e){return i.appendBlocker(e)}));Promise.all(n).then((function(){e(),t.forEach((function(e){var t=r.sourceBuffer[e];t&&t.updating||i.shiftAndExecuteNext(e)}))}))},t.getSourceBufferTypes=function(){return Object.keys(this.sourceBuffer)},t.addBufferListener=function(e,t,r){var i=this.sourceBuffer[e];if(i){var n=r.bind(this,e);this.listeners[e].push({event:t,listener:n}),i.addEventListener(t,n)}},t.removeBufferListeners=function(e){var t=this.sourceBuffer[e];t&&this.listeners[e].forEach((function(e){t.removeEventListener(e.event,e.listener)}))},e}();function mt(e,t){for(var r=0;r0&&this.mediaWidth>0){var e=this.hls.levels;if(e.length){var t=this.hls;t.autoLevelCapping=this.getMaxLevel(e.length-1),t.autoLevelCapping>this.autoLevelCapping&&this.streamController&&this.streamController.nextLevelSwitch(),this.autoLevelCapping=t.autoLevelCapping}}},i.getMaxLevel=function(t){var r=this,i=this.hls.levels;if(!i.length)return-1;var n=i.filter((function(i,n){return e.isLevelAllowed(n,r.restrictedLevels)&&n<=t}));return this.clientRect=null,e.getMaxLevelByMediaSize(n,this.mediaWidth,this.mediaHeight)},i.startCapping=function(){this.timer||(this.autoLevelCapping=Number.POSITIVE_INFINITY,this.hls.firstLevel=this.getMaxLevel(this.firstLevel),self.clearInterval(this.timer),this.timer=self.setInterval(this.detectPlayerSize.bind(this),1e3),this.detectPlayerSize())},i.stopCapping=function(){this.restrictedLevels=[],this.firstLevel=-1,this.autoLevelCapping=Number.POSITIVE_INFINITY,this.timer&&(self.clearInterval(this.timer),this.timer=void 0)},i.getDimensions=function(){if(this.clientRect)return this.clientRect;var e=this.media,t={width:0,height:0};if(e){var r=e.getBoundingClientRect();t.width=r.width,t.height=r.height,t.width||t.height||(t.width=r.right-r.left||e.width||0,t.height=r.bottom-r.top||e.height||0)}return this.clientRect=t,t},e.isLevelAllowed=function(e,t){return void 0===t&&(t=[]),-1===t.indexOf(e)},e.getMaxLevelByMediaSize=function(e,t,r){if(!e||!e.length)return-1;for(var i,n,a=e.length-1,s=0;s=t||o.height>=r)&&(i=o,!(n=e[s+1])||i.width!==n.width||i.height!==n.height)){a=s;break}}return a},t=e,(r=[{key:"mediaWidth",get:function(){return this.getDimensions().width*this.contentScaleFactor}},{key:"mediaHeight",get:function(){return this.getDimensions().height*this.contentScaleFactor}},{key:"contentScaleFactor",get:function(){var e=1;if(!this.hls.config.ignoreDevicePixelRatio)try{e=self.devicePixelRatio}catch(e){}return e}}])&&mt(t.prototype,r),Object.defineProperty(t,"prototype",{writable:!1}),e}(),Et=function(){function e(e){this.hls=void 0,this.isVideoPlaybackQualityAvailable=!1,this.timer=void 0,this.media=null,this.lastTime=void 0,this.lastDroppedFrames=0,this.lastDecodedFrames=0,this.streamController=void 0,this.hls=e,this.registerListeners()}var t=e.prototype;return t.setStreamController=function(e){this.streamController=e},t.registerListeners=function(){this.hls.on(s.Events.MEDIA_ATTACHING,this.onMediaAttaching,this)},t.unregisterListeners=function(){this.hls.off(s.Events.MEDIA_ATTACHING,this.onMediaAttaching)},t.destroy=function(){this.timer&&clearInterval(this.timer),this.unregisterListeners(),this.isVideoPlaybackQualityAvailable=!1,this.media=null},t.onMediaAttaching=function(e,t){var r=this.hls.config;if(r.capLevelOnFPSDrop){var i=t.media instanceof self.HTMLVideoElement?t.media:null;this.media=i,i&&"function"==typeof i.getVideoPlaybackQuality&&(this.isVideoPlaybackQualityAvailable=!0),self.clearInterval(this.timer),this.timer=self.setInterval(this.checkFPSInterval.bind(this),r.fpsDroppedMonitoringPeriod)}},t.checkFPS=function(e,t,r){var i=performance.now();if(t){if(this.lastTime){var n=i-this.lastTime,a=r-this.lastDroppedFrames,o=t-this.lastDecodedFrames,u=1e3*a/n,d=this.hls;if(d.trigger(s.Events.FPS_DROP,{currentDropped:a,currentDecoded:o,totalDroppedFrames:r}),u>0&&a>d.config.fpsDroppedMonitoringThreshold*o){var h=d.currentLevel;l.logger.warn("drop FPS ratio greater than max allowed value for currentLevel: "+h),h>0&&(-1===d.autoLevelCapping||d.autoLevelCapping>=h)&&(h-=1,d.trigger(s.Events.FPS_DROP_LEVEL_CAPPING,{level:h,droppedLevel:d.currentLevel}),d.autoLevelCapping=h,this.streamController.nextLevelSwitch())}}this.lastTime=i,this.lastDroppedFrames=r,this.lastDecodedFrames=t}},t.checkFPSInterval=function(){var e=this.media;if(e)if(this.isVideoPlaybackQualityAvailable){var t=e.getVideoPlaybackQuality();this.checkFPS(e,t.totalVideoFrames,t.droppedVideoFrames)}else this.checkFPS(e,e.webkitDecodedFrameCount,e.webkitDroppedFrameCount)},e}();var Tt=r(408),St=/^age:\s*[\d.]+\s*$/m;const bt=function(){function e(e){this.xhrSetup=void 0,this.requestTimeout=void 0,this.retryTimeout=void 0,this.retryDelay=void 0,this.config=null,this.callbacks=null,this.context=void 0,this.loader=null,this.stats=void 0,this.xhrSetup=e?e.xhrSetup:null,this.stats=new Tt.LoadStats,this.retryDelay=0}var t=e.prototype;return t.destroy=function(){this.callbacks=null,this.abortInternal(),this.loader=null,this.config=null},t.abortInternal=function(){var e=this.loader;self.clearTimeout(this.requestTimeout),self.clearTimeout(this.retryTimeout),e&&(e.onreadystatechange=null,e.onprogress=null,4!==e.readyState&&(this.stats.aborted=!0,e.abort()))},t.abort=function(){var e;this.abortInternal(),null!==(e=this.callbacks)&&void 0!==e&&e.onAbort&&this.callbacks.onAbort(this.stats,this.context,this.loader)},t.load=function(e,t,r){if(this.stats.loading.start)throw new Error("Loader can only be used once.");this.stats.loading.start=self.performance.now(),this.context=e,this.config=t,this.callbacks=r,this.retryDelay=t.retryDelay,this.loadInternal()},t.loadInternal=function(){var e=this.config,t=this.context;if(e){var r=this.loader=new self.XMLHttpRequest,i=this.stats;i.loading.first=0,i.loaded=0;var n=this.xhrSetup;try{if(n)try{n(r,t.url)}catch(e){r.open("GET",t.url,!0),n(r,t.url)}r.readyState||r.open("GET",t.url,!0);var a=this.context.headers;if(a)for(var s in a)r.setRequestHeader(s,a[s])}catch(e){return void this.callbacks.onError({code:r.status,text:e.message},t,r)}t.rangeEnd&&r.setRequestHeader("Range","bytes="+t.rangeStart+"-"+(t.rangeEnd-1)),r.onreadystatechange=this.readystatechange.bind(this),r.onprogress=this.loadprogress.bind(this),r.responseType=t.responseType,self.clearTimeout(this.requestTimeout),this.requestTimeout=self.setTimeout(this.loadtimeout.bind(this),e.timeout),r.send()}},t.readystatechange=function(){var e=this.context,t=this.loader,r=this.stats;if(e&&t){var i=t.readyState,n=this.config;if(!r.aborted&&i>=2)if(self.clearTimeout(this.requestTimeout),0===r.loading.first&&(r.loading.first=Math.max(self.performance.now(),r.loading.start)),4===i){t.onreadystatechange=null,t.onprogress=null;var a=t.status,s="arraybuffer"===t.responseType;if(a>=200&&a<300&&(s&&t.response||null!==t.responseText)){var o,u;if(r.loading.end=Math.max(self.performance.now(),r.loading.first),u=s?(o=t.response).byteLength:(o=t.responseText).length,r.loaded=r.total=u,!this.callbacks)return;var d=this.callbacks.onProgress;if(d&&d(r,e,o,t),!this.callbacks)return;var h={url:t.responseURL,data:o};this.callbacks.onSuccess(h,r,e,t)}else r.retry>=n.maxRetry||a>=400&&a<499?(l.logger.error(a+" while loading "+e.url),this.callbacks.onError({code:a,text:t.statusText},e,t)):(l.logger.warn(a+" while loading "+e.url+", retrying in "+this.retryDelay+"..."),this.abortInternal(),this.loader=null,self.clearTimeout(this.retryTimeout),this.retryTimeout=self.setTimeout(this.loadInternal.bind(this),this.retryDelay),this.retryDelay=Math.min(2*this.retryDelay,n.maxRetryDelay),r.retry++)}else self.clearTimeout(this.requestTimeout),this.requestTimeout=self.setTimeout(this.loadtimeout.bind(this),n.timeout)}},t.loadtimeout=function(){l.logger.warn("timeout while loading "+this.context.url);var e=this.callbacks;e&&(this.abortInternal(),e.onTimeout(this.stats,this.context,this.loader))},t.loadprogress=function(e){var t=this.stats;t.loaded=e.loaded,e.lengthComputable&&(t.total=e.total)},t.getCacheAge=function(){var e=null;if(this.loader&&St.test(this.loader.getAllResponseHeaders())){var t=this.loader.getResponseHeader("age");e=t?parseFloat(t):null}return e},e}();var Lt=function(){function e(){this.chunks=[],this.dataLength=0}var t=e.prototype;return t.push=function(e){this.chunks.push(e),this.dataLength+=e.length},t.flush=function(){var e,t=this.chunks,r=this.dataLength;return t.length?(e=1===t.length?t[0]:function(e,t){for(var r=new Uint8Array(t),i=0,n=0;n=i&&n(t,r,a.flush(),e)):n(t,r,l,e),o()})).catch((function(){return Promise.reject()}))}()},e}();function wt(e,t){return new self.Request(e.url,t)}var Ct=function(e){var t,r;function i(t,r,i){var n;return(n=e.call(this,t)||this).code=void 0,n.details=void 0,n.code=r,n.details=i,n}return r=e,(t=i).prototype=Object.create(r.prototype),t.prototype.constructor=t,kt(t,r),i}(At(Error));const Pt=It;var Ot;!function(e){e.WIDEVINE="com.widevine.alpha",e.PLAYREADY="com.microsoft.playready"}(Ot||(Ot={}));var Ft="undefined"!=typeof self&&self.navigator&&self.navigator.requestMediaKeySystemAccess?self.navigator.requestMediaKeySystemAccess.bind(self.navigator):null;function Mt(){return Mt=Object.assign?Object.assign.bind():function(e){for(var t=1;t=t)return i;return 0}},{key:"maxAutoLevel",get:function(){var e=this.levels,t=this.autoLevelCapping;return-1===t&&e&&e.length?e.length-1:t}},{key:"nextAutoLevel",get:function(){return Math.min(Math.max(this.abrController.nextAutoLevel,this.minAutoLevel),this.maxAutoLevel)},set:function(e){this.abrController.nextAutoLevel=Math.max(this.minAutoLevel,e)}},{key:"playingDate",get:function(){return this.streamController.currentProgramDateTime}},{key:"mainForwardBufferInfo",get:function(){return this.streamController.getMainFwdBufferInfo()}},{key:"audioTracks",get:function(){var e=this.audioTrackController;return e?e.audioTracks:[]}},{key:"audioTrack",get:function(){var e=this.audioTrackController;return e?e.audioTrack:-1},set:function(e){var t=this.audioTrackController;t&&(t.audioTrack=e)}},{key:"subtitleTracks",get:function(){var e=this.subtitleTrackController;return e?e.subtitleTracks:[]}},{key:"subtitleTrack",get:function(){var e=this.subtitleTrackController;return e?e.subtitleTrack:-1},set:function(e){var t=this.subtitleTrackController;t&&(t.subtitleTrack=e)}},{key:"media",get:function(){return this._media}},{key:"subtitleDisplay",get:function(){var e=this.subtitleTrackController;return!!e&&e.subtitleDisplay},set:function(e){var t=this.subtitleTrackController;t&&(t.subtitleDisplay=e)}},{key:"lowLatencyMode",get:function(){return this.config.lowLatencyMode},set:function(e){this.config.lowLatencyMode=e}},{key:"liveSyncPosition",get:function(){return this.latencyController.liveSyncPosition}},{key:"latency",get:function(){return this.latencyController.latency}},{key:"maxLatency",get:function(){return this.latencyController.maxLatency}},{key:"targetLatency",get:function(){return this.latencyController.targetLatency}},{key:"drift",get:function(){return this.latencyController.drift}},{key:"forceStartLoad",get:function(){return this.streamController.forceStartLoad}}])&&Vt(t.prototype,r),i&&Vt(t,i),Object.defineProperty(t,"prototype",{writable:!1}),e}();Ht.defaultConfig=void 0},923:(e,t,r)=>{"use strict";r.r(t),r.d(t,{BaseSegment:()=>c,ElementaryStreamTypes:()=>i,Fragment:()=>v,Part:()=>g});var i,n=r(965),a=r(945),s=r(93),o=r(960),l=r(408);function u(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,d(e,t)}function d(e,t){return d=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},d(e,t)}function h(e,t){for(var r=0;r>8*(15-r)&255;return t},r.setDecryptDataFromLevelKey=function(e,t){var r=e;return"AES-128"===(null==e?void 0:e.method)&&e.uri&&!e.iv&&((r=o.LevelKey.fromURI(e.uri)).method=e.method,r.iv=this.createInitializationVector(t),r.keyFormat="identity"),r},r.setElementaryStreamInfo=function(e,t,r,i,n,a){void 0===a&&(a=!1);var s=this.elementaryStreams,o=s[e];o?(o.startPTS=Math.min(o.startPTS,t),o.endPTS=Math.max(o.endPTS,r),o.startDTS=Math.min(o.startDTS,i),o.endDTS=Math.max(o.endDTS,n)):s[e]={startPTS:t,endPTS:r,startDTS:i,endDTS:n,partial:a}},r.clearElementaryStreamInfo=function(){var e=this.elementaryStreams;e[i.AUDIO]=null,e[i.VIDEO]=null,e[i.AUDIOVIDEO]=null},f(t,[{key:"decryptdata",get:function(){if(!this.levelkey&&!this._decryptdata)return null;if(!this._decryptdata&&this.levelkey){var e=this.sn;"number"!=typeof e&&(this.levelkey&&"AES-128"===this.levelkey.method&&!this.levelkey.iv&&s.logger.warn('missing IV for initialization segment with method="'+this.levelkey.method+'" - compliance issue'),e=0),this._decryptdata=this.setDecryptDataFromLevelKey(this.levelkey,e)}return this._decryptdata}},{key:"end",get:function(){return this.start+this.duration}},{key:"endProgramDateTime",get:function(){if(null===this.programDateTime)return null;if(!(0,n.isFiniteNumber)(this.programDateTime))return null;var e=(0,n.isFiniteNumber)(this.duration)?this.duration:0;return this.programDateTime+1e3*e}},{key:"encrypted",get:function(){var e;return!(null===(e=this.decryptdata)||void 0===e||!e.keyFormat||!this.decryptdata.uri)}}]),t}(c),g=function(e){function t(t,r,i,n,a){var s;(s=e.call(this,i)||this).fragOffset=0,s.duration=0,s.gap=!1,s.independent=!1,s.relurl=void 0,s.fragment=void 0,s.index=void 0,s.stats=new l.LoadStats,s.duration=t.decimalFloatingPoint("DURATION"),s.gap=t.bool("GAP"),s.independent=t.bool("INDEPENDENT"),s.relurl=t.enumeratedString("URI"),s.fragment=r,s.index=n;var o=t.enumeratedString("BYTERANGE");return o&&s.setByteRange(o,a),a&&(s.fragOffset=a.fragOffset+a.duration),s}return u(t,e),f(t,[{key:"start",get:function(){return this.fragment.start+this.fragOffset}},{key:"end",get:function(){return this.start+this.duration}},{key:"loaded",get:function(){var e=this.elementaryStreams;return!!(e.audio||e.video||e.audiovideo)}}]),t}(c)},960:(e,t,r)=>{"use strict";r.r(t),r.d(t,{LevelKey:()=>a});var i=r(945);function n(e,t){for(var r=0;r{"use strict";r.r(t),r.d(t,{LoadStats:()=>i});var i=function(){this.aborted=!1,this.loaded=0,this.retry=0,this.total=0,this.chunkCount=0,this.bwEstimate=0,this.loading={start:0,first:0,end:0},this.parsing={start:0,end:0},this.buffering={start:0,first:0,end:0}}},965:(e,t,r)=>{"use strict";r.r(t),r.d(t,{MAX_SAFE_INTEGER:()=>n,isFiniteNumber:()=>i});var i=Number.isFinite||function(e){return"number"==typeof e&&isFinite(e)},n=Number.MAX_SAFE_INTEGER||9007199254740991},856:(e,t,r)=>{"use strict";var i;r.r(t),r.d(t,{MetadataSchema:()=>i}),function(e){e.audioId3="org.id3",e.dateRange="com.apple.quicktime.HLS",e.emsg="https://aomedia.org/emsg/ID3"}(i||(i={}))},308:(e,t,r)=>{"use strict";var i,n;r.r(t),r.d(t,{PlaylistContextType:()=>i,PlaylistLevelType:()=>n}),function(e){e.MANIFEST="manifest",e.LEVEL="level",e.AUDIO_TRACK="audioTrack",e.SUBTITLE_TRACK="subtitleTrack"}(i||(i={})),function(e){e.MAIN="main",e.AUDIO="audio",e.SUBTITLE="subtitle"}(n||(n={}))},93:(e,t,r)=>{"use strict";r.r(t),r.d(t,{enableLogs:()=>o,logger:()=>l});var i=function(){},n={trace:i,debug:i,log:i,warn:i,info:i,error:i},a=n;function s(e){var t=self.console[e];return t?t.bind(self.console,"["+e+"] >"):i}function o(e,t){if(self.console&&!0===e||"object"==typeof e){!function(e){for(var t=arguments.length,r=new Array(t>1?t-1:0),i=1;i{"use strict";r.r(t),r.d(t,{RemuxerTrackIdConfig:()=>l,appendUint8Array:()=>b,bin2str:()=>u,computeRawDurationFromSamples:()=>E,discardEPB:()=>R,findBox:()=>v,getDuration:()=>y,getStartDTS:()=>m,offsetStartDTS:()=>T,parseEmsg:()=>k,parseInitSegment:()=>p,parseSEIMessageFromNALu:()=>D,parseSamples:()=>L,parseSegmentIndex:()=>g,readSint32:()=>f,readUint16:()=>d,readUint32:()=>h,segmentValidRange:()=>S,writeUint32:()=>c});var i=r(145),n=r(923),a=r(181),s=Math.pow(2,32)-1,o=[].push,l={video:1,audio:2,id3:3,text:4};function u(e){return String.fromCharCode.apply(null,e)}function d(e,t){var r=e[t]<<8|e[t+1];return r<0?65536+r:r}function h(e,t){var r=f(e,t);return r<0?4294967296+r:r}function f(e,t){return e[t]<<24|e[t+1]<<16|e[t+2]<<8|e[t+3]}function c(e,t,r){e[t]=r>>24,e[t+1]=r>>16&255,e[t+2]=r>>8&255,e[t+3]=255&r}function v(e,t){var r=[];if(!t.length)return r;for(var i=e.byteLength,n=0;n1?n+a:i;if(u(e.subarray(n+4,n+8))===t[0])if(1===t.length)r.push(e.subarray(n+8,s));else{var l=v(e.subarray(n+8,s),t.slice(1));l.length&&o.apply(r,l)}n=s}return r}function g(e){var t=[],r=e[0],i=8,n=h(e,i);i+=4,i+=0===r?8:16,i+=2;var a=e.length+0,s=d(e,i);i+=2;for(var o=0;o>>31)return console.warn("SIDX has hierarchical references (not supported)"),null;var c=h(e,l);l+=4,t.push({referenceSize:f,subsegmentDuration:c,info:{duration:c/n,start:a,end:a+f-1}}),a+=f,i=l+=4}return{earliestPresentationTime:0,timescale:n,version:r,referencesCount:s,references:t}}function p(e){for(var t=[],r=v(e,["moov","trak"]),i=0;i>1&63;return 39===r||40===r}return 6==(31&t)}function D(e,t,r,i){var n=R(e),s=0;s+=t;for(var o=0,l=0,u=!1,f=0;s=n.length)break;o+=f=n[s++]}while(255===f);l=0;do{if(s>=n.length)break;l+=f=n[s++]}while(255===f);var c=n.length-s;if(!u&&4===o&&s16){for(var b=[],L=0;L<16;L++){var A=n[s++].toString(16);b.push(1==A.length?"0"+A:A),3!==L&&5!==L&&7!==L&&9!==L||b.push("-")}for(var D=l-16,k=new Uint8Array(D),_=0;_c)break}}function R(e){for(var t=e.byteLength,r=[],i=1;i{"use strict";function i(e,t,r){return Uint8Array.prototype.slice?e.slice(t,r):new Uint8Array(Array.prototype.slice.call(e,t,r))}r.r(t),r.d(t,{sliceUint8:()=>i})},729:e=>{"use strict";var t=Object.prototype.hasOwnProperty,r="~";function i(){}function n(e,t,r){this.fn=e,this.context=t,this.once=r||!1}function a(e,t,i,a,s){if("function"!=typeof i)throw new TypeError("The listener must be a function");var o=new n(i,a||e,s),l=r?r+t:t;return e._events[l]?e._events[l].fn?e._events[l]=[e._events[l],o]:e._events[l].push(o):(e._events[l]=o,e._eventsCount++),e}function s(e,t){0==--e._eventsCount?e._events=new i:delete e._events[t]}function o(){this._events=new i,this._eventsCount=0}Object.create&&(i.prototype=Object.create(null),(new i).__proto__||(r=!1)),o.prototype.eventNames=function(){var e,i,n=[];if(0===this._eventsCount)return n;for(i in e=this._events)t.call(e,i)&&n.push(r?i.slice(1):i);return Object.getOwnPropertySymbols?n.concat(Object.getOwnPropertySymbols(e)):n},o.prototype.listeners=function(e){var t=r?r+e:e,i=this._events[t];if(!i)return[];if(i.fn)return[i.fn];for(var n=0,a=i.length,s=new Array(a);n{var t=e&&e.__esModule?()=>e.default:()=>e;return r.d(t,{a:t}),t},r.d=(e,t)=>{for(var i in t)r.o(t,i)&&!r.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var i=r(392);return i.default})())); +// @license-end From ff61d97a1dc37ffc4f65a0dbfabf54952a83e01f Mon Sep 17 00:00:00 2001 From: Zed Date: Fri, 20 Jan 2023 04:54:19 +0100 Subject: [PATCH 074/409] Optimize profile fetching and caching --- src/api.nim | 31 +++++++++++++--------------- src/consts.nim | 3 ++- src/experimental/parser/graphql.nim | 5 +++++ src/experimental/types/graphuser.nim | 2 ++ src/redis_cache.nim | 11 +++++----- src/tokens.nim | 2 +- src/types.nim | 1 + 7 files changed, 31 insertions(+), 24 deletions(-) diff --git a/src/api.nim b/src/api.nim index 392795c..dd94b69 100644 --- a/src/api.nim +++ b/src/api.nim @@ -4,11 +4,22 @@ import packedjson import types, query, formatters, consts, apiutils, parser import experimental/parser as newParser -proc getGraphUser*(id: string): Future[User] {.async.} = +proc getGraphUser*(username: string): Future[User] {.async.} = + if username.len == 0: return + let + variables = """{ + "screen_name": "$1", + "withSafetyModeUserFields": false, + "withSuperFollowsUserFields": false + }""" % [username] + js = await fetchRaw(graphUser ? {"variables": variables}, Api.userScreenName) + result = parseGraphUser(js) + +proc getGraphUserById*(id: string): Future[User] {.async.} = if id.len == 0 or id.any(c => not c.isDigit): return let - variables = %*{"userId": id, "withSuperFollowsUserFields": true} - js = await fetchRaw(graphUser ? {"variables": $variables}, Api.userRestId) + variables = """{"userId": "$1", "withSuperFollowsUserFields": true}""" % [id] + js = await fetchRaw(graphUserById ? {"variables": variables}, Api.userRestId) result = parseGraphUser(js) proc getGraphListBySlug*(name, list: string): Future[List] {.async.} = @@ -47,20 +58,6 @@ proc getListTimeline*(id: string; after=""): Future[Timeline] {.async.} = url = listTimeline ? ps result = parseTimeline(await fetch(url, Api.timeline), after) -proc getUser*(username: string): Future[User] {.async.} = - if username.len == 0: return - let - ps = genParams({"screen_name": username}) - json = await fetchRaw(userShow ? ps, Api.userShow) - result = parseUser(json, username) - -proc getUserById*(userId: string): Future[User] {.async.} = - if userId.len == 0: return - let - ps = genParams({"user_id": userId}) - json = await fetchRaw(userShow ? ps, Api.userShow) - result = parseUser(json) - proc getTimeline*(id: string; after=""; replies=false): Future[Timeline] {.async.} = if id.len == 0: return let diff --git a/src/consts.nim b/src/consts.nim index 3687a54..2d3ea56 100644 --- a/src/consts.nim +++ b/src/consts.nim @@ -19,7 +19,8 @@ const tweet* = timelineApi / "conversation" graphql = api / "graphql" - graphUser* = graphql / "I5nvpI91ljifos1Y3Lltyg/UserByRestId" + graphUser* = graphql / "7mjxD3-C6BxitPMVQ6w0-Q/UserByScreenName" + graphUserById* = graphql / "I5nvpI91ljifos1Y3Lltyg/UserByRestId" graphList* = graphql / "JADTh6cjebfgetzvF3tQvQ/List" graphListBySlug* = graphql / "ErWsz9cObLel1BF-HjuBlA/ListBySlug" graphListMembers* = graphql / "Ke6urWMeCV2UlKXGRy4sow/ListMembers" diff --git a/src/experimental/parser/graphql.nim b/src/experimental/parser/graphql.nim index b00ab24..4431db3 100644 --- a/src/experimental/parser/graphql.nim +++ b/src/experimental/parser/graphql.nim @@ -1,9 +1,14 @@ +import options import jsony import user, ../types/[graphuser, graphlistmembers] from ../../types import User, Result, Query, QueryKind proc parseGraphUser*(json: string): User = let raw = json.fromJson(GraphUser) + + if raw.data.user.result.reason.get("") == "Suspended": + return User(suspended: true) + result = toUser raw.data.user.result.legacy result.id = raw.data.user.result.restId diff --git a/src/experimental/types/graphuser.nim b/src/experimental/types/graphuser.nim index dded4eb..e13383a 100644 --- a/src/experimental/types/graphuser.nim +++ b/src/experimental/types/graphuser.nim @@ -1,3 +1,4 @@ +import options import user type @@ -10,3 +11,4 @@ type UserResult = object legacy*: RawUser restId*: string + reason*: Option[string] diff --git a/src/redis_cache.nim b/src/redis_cache.nim index 469157a..b91d153 100644 --- a/src/redis_cache.nim +++ b/src/redis_cache.nim @@ -118,11 +118,11 @@ proc getUserId*(username: string): Future[string] {.async.} = pool.withAcquire(r): result = await r.hGet(name.uidKey, name) if result == redisNil: - let user = await getUser(username) + let user = await getGraphUser(username) if user.suspended: return "suspended" else: - await cacheUserId(name, user.id) + await all(cacheUserId(name, user.id), cache(user)) return user.id proc getCachedUser*(username: string; fetch=true): Future[User] {.async.} = @@ -130,8 +130,7 @@ proc getCachedUser*(username: string; fetch=true): Future[User] {.async.} = if prof != redisNil: prof.deserialize(User) elif fetch: - let userId = await getUserId(username) - result = await getGraphUser(userId) + result = await getGraphUser(username) await cache(result) proc getCachedUsername*(userId: string): Future[string] {.async.} = @@ -142,9 +141,11 @@ proc getCachedUsername*(userId: string): Future[string] {.async.} = if username != redisNil: result = username else: - let user = await getUserById(userId) + let user = await getGraphUserById(userId) result = user.username await setEx(key, baseCacheTime, result) + if result.len > 0 and user.id.len > 0: + await all(cacheUserId(result, user.id), cache(user)) proc getCachedTweet*(id: int64): Future[Tweet] {.async.} = if id == 0: return diff --git a/src/tokens.nim b/src/tokens.nim index 4bbcd81..e3b916a 100644 --- a/src/tokens.nim +++ b/src/tokens.nim @@ -41,7 +41,7 @@ proc getPoolJson*(): JsonNode = let maxReqs = case api - of Api.listMembers, Api.listBySlug, Api.list, Api.userRestId: 500 + of Api.listMembers, Api.listBySlug, Api.list, Api.userRestId, Api.userScreenName: 500 of Api.timeline: 187 else: 180 reqs = maxReqs - token.apis[api].remaining diff --git a/src/types.nim b/src/types.nim index 061ec8a..07f9bf7 100644 --- a/src/types.nim +++ b/src/types.nim @@ -17,6 +17,7 @@ type listBySlug listMembers userRestId + userScreenName status RateLimit* = object From 22b51b414b62620633de8ba456eaebdd2de54934 Mon Sep 17 00:00:00 2001 From: Zed Date: Fri, 20 Jan 2023 04:55:56 +0100 Subject: [PATCH 075/409] Fix typo to actually cache pinned tweets --- src/redis_cache.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/redis_cache.nim b/src/redis_cache.nim index b91d153..742b7ae 100644 --- a/src/redis_cache.nim +++ b/src/redis_cache.nim @@ -154,7 +154,7 @@ proc getCachedTweet*(id: int64): Future[Tweet] {.async.} = tweet.deserialize(Tweet) else: result = await getStatus($id) - if result.isNil: + if not result.isNil: await cache(result) proc getCachedPhotoRail*(name: string): Future[PhotoRail] {.async.} = From 19adc658c317c71e671201385350343add6275c8 Mon Sep 17 00:00:00 2001 From: Zed Date: Thu, 2 Feb 2023 12:31:49 +0100 Subject: [PATCH 076/409] Replace tweet endpoint with GraphQL --- src/api.nim | 15 ++++-- src/consts.nim | 32 ++++++++++++ src/parser.nim | 120 ++++++++++++++++++++++++-------------------- src/parserutils.nim | 4 -- src/tokens.nim | 3 +- src/types.nim | 2 +- src/views/tweet.nim | 2 +- 7 files changed, 111 insertions(+), 67 deletions(-) diff --git a/src/api.nim b/src/api.nim index dd94b69..dfcf413 100644 --- a/src/api.nim +++ b/src/api.nim @@ -101,16 +101,21 @@ proc getSearch*[T](query: Query; after=""): Future[Result[T]] {.async.} = except InternalError: return Result[T](beginning: true, query: query) -proc getTweetImpl(id: string; after=""): Future[Conversation] {.async.} = - let url = tweet / (id & ".json") ? genParams(cursor=after) - result = parseConversation(await fetch(url, Api.tweet), id) +proc getGraphTweet(id: string; after=""): Future[Conversation] {.async.} = + if id.len == 0: return + let + cursor = if after.len > 0: "\"cursor\":\"$1\"," % after else: "" + variables = tweetVariables % [id, cursor] + params = {"variables": variables, "features": tweetFeatures} + js = await fetch(graphTweet ? params, Api.tweetDetail) + result = parseGraphConversation(js, id) proc getReplies*(id, after: string): Future[Result[Chain]] {.async.} = - result = (await getTweetImpl(id, after)).replies + result = (await getGraphTweet(id, after)).replies result.beginning = after.len == 0 proc getTweet*(id: string; after=""): Future[Conversation] {.async.} = - result = await getTweetImpl(id) + result = await getGraphTweet(id) if after.len > 0: result.replies = await getReplies(id, after) diff --git a/src/consts.nim b/src/consts.nim index 2d3ea56..18ecb2c 100644 --- a/src/consts.nim +++ b/src/consts.nim @@ -19,6 +19,7 @@ const tweet* = timelineApi / "conversation" graphql = api / "graphql" + graphTweet* = graphql / "6lWNh96EXDJCXl05SAtn_g/TweetDetail" graphUser* = graphql / "7mjxD3-C6BxitPMVQ6w0-Q/UserByScreenName" graphUserById* = graphql / "I5nvpI91ljifos1Y3Lltyg/UserByRestId" graphList* = graphql / "JADTh6cjebfgetzvF3tQvQ/List" @@ -58,3 +59,34 @@ const ## user: "result_filter: user" ## photos: "result_filter: photos" ## videos: "result_filter: videos" + + tweetVariables* = """{ + "focalTweetId": "$1", + $2 + "includePromotedContent": false, + "withBirdwatchNotes": false, + "withDownvotePerspective": false, + "withReactionsMetadata": false, + "withReactionsPerspective": false, + "withSuperFollowsTweetFields": false, + "withSuperFollowsUserFields": false, + "withVoice": false, + "withV2Timeline": true +}""" + + tweetFeatures* = """{ + "graphql_is_translatable_rweb_tweet_is_translatable_enabled": false, + "responsive_web_graphql_timeline_navigation_enabled": false, + "standardized_nudges_misinfo": false, + "verified_phone_label_enabled": false, + "responsive_web_twitter_blue_verified_badge_is_enabled": false, + "tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled": false, + "view_counts_everywhere_api_enabled": false, + "responsive_web_edit_tweet_api_enabled": false, + "tweetypie_unmention_optimization_enabled": false, + "vibe_api_enabled": false, + "longform_notetweets_consumption_enabled": false, + "responsive_web_text_conversations_enabled": false, + "responsive_web_enhance_cards_enabled": false, + "interactive_text_enabled": false +}""" diff --git a/src/parser.nim b/src/parser.nim index 2b0c6a4..544f943 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -72,8 +72,8 @@ proc parseGif(js: JsonNode): Gif = proc parseVideo(js: JsonNode): Video = result = Video( thumb: js{"media_url_https"}.getImageStr, - views: js{"ext", "mediaStats", "r", "ok", "viewCount"}.getStr, - available: js{"ext_media_availability", "status"}.getStr == "available", + views: js{"ext", "mediaStats", "r", "ok", "viewCount"}.getStr($js{"mediaStats", "viewCount"}.getInt), + available: js{"ext_media_availability", "status"}.getStr.toLowerAscii == "available", title: js{"ext_alt_text"}.getStr, durationMs: js{"video_info", "duration_millis"}.getInt # playbackType: mp4 @@ -185,7 +185,7 @@ proc parseCard(js: JsonNode; urls: JsonNode): Card = result.url.len == 0 or result.url.startsWith("card://"): result.url = getPicUrl(result.image) -proc parseTweet(js: JsonNode): Tweet = +proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet = if js.isNull: return result = Tweet( id: js{"id_str"}.getId, @@ -193,7 +193,6 @@ proc parseTweet(js: JsonNode): Tweet = replyId: js{"in_reply_to_status_id_str"}.getId, text: js{"full_text"}.getStr, time: js{"created_at"}.getTime, - source: getSource(js), hasThread: js{"self_thread"}.notNull, available: true, user: User(id: js{"user_id_str"}.getStr), @@ -218,7 +217,7 @@ proc parseTweet(js: JsonNode): Tweet = result.retweet = some Tweet(id: rt.getId) return - with jsCard, js{"card"}: + if jsCard.kind != JNull: let name = jsCard{"name"}.getStr if "poll" in name: if "image" in name: @@ -295,64 +294,17 @@ proc parseGlobalObjects(js: JsonNode): GlobalObjects = result.users[k] = parseUser(v, k) for k, v in tweets: - var tweet = parseTweet(v) + var tweet = parseTweet(v, v{"card"}) if tweet.user.id in result.users: tweet.user = result.users[tweet.user.id] result.tweets[k] = tweet -proc parseThread(js: JsonNode; global: GlobalObjects): tuple[thread: Chain, self: bool] = - result.thread = Chain() - - let thread = js{"content", "item", "content", "conversationThread"} - with cursor, thread{"showMoreCursor"}: - result.thread.cursor = cursor{"value"}.getStr - result.thread.hasMore = true - - for t in thread{"conversationComponents"}: - let content = t{"conversationTweetComponent", "tweet"} - - if content{"displayType"}.getStr == "SelfThread": - result.self = true - - var tweet = finalizeTweet(global, content{"id"}.getStr) - if not tweet.available: - tweet.tombstone = getTombstone(content{"tombstone"}) - result.thread.content.add tweet - -proc parseConversation*(js: JsonNode; tweetId: string): Conversation = - result = Conversation(replies: Result[Chain](beginning: true)) - let global = parseGlobalObjects(? js) - - let instructions = ? js{"timeline", "instructions"} - if instructions.len == 0: - return - - for e in instructions[0]{"addEntries", "entries"}: - let entry = e{"entryId"}.getStr - if "tweet" in entry or "tombstone" in entry: - let tweet = finalizeTweet(global, e.getEntryId) - if $tweet.id != tweetId: - result.before.content.add tweet - else: - result.tweet = tweet - elif "conversationThread" in entry: - let (thread, self) = parseThread(e, global) - if thread.content.len > 0: - if self: - result.after = thread - else: - result.replies.content.add thread - elif "cursor-showMore" in entry: - result.replies.bottom = e.getCursor - elif "cursor-bottom" in entry: - result.replies.bottom = e.getCursor - proc parseStatus*(js: JsonNode): Tweet = with e, js{"errors"}: if e.getError == tweetNotFound: return - result = parseTweet(js) + result = parseTweet(js, js{"card"}) if not result.isNil: result.user = parseUser(js{"user"}) @@ -409,7 +361,7 @@ proc parseTimeline*(js: JsonNode; after=""): Timeline = proc parsePhotoRail*(js: JsonNode): PhotoRail = for tweet in js: let - t = parseTweet(tweet) + t = parseTweet(tweet, js{"card"}) url = if t.photos.len > 0: t.photos[0] elif t.video.isSome: get(t.video).thumb elif t.gif.isSome: get(t.gif).thumb @@ -418,3 +370,61 @@ proc parsePhotoRail*(js: JsonNode): PhotoRail = if url.len == 0: continue result.add GalleryPhoto(url: url, tweetId: $t.id) + +proc parseGraphTweet(js: JsonNode): Tweet = + if js.kind == JNull: + return Tweet(available: false) + + var jsCard = copy(js{"card", "legacy"}) + if jsCard.kind != JNull: + var values = newJObject() + for val in jsCard["binding_values"]: + values[val["key"].getStr] = val["value"] + jsCard["binding_values"] = values + + result = parseTweet(js{"legacy"}, jsCard) + result.user = parseUser(js{"core", "user_results", "result", "legacy"}) + + if result.quote.isSome: + result.quote = some(parseGraphTweet(js{"quoted_status_result", "result"})) + +proc parseGraphThread(js: JsonNode): tuple[thread: Chain; self: bool] = + let thread = js{"content", "items"} + for t in js{"content", "items"}: + let entryId = t{"entryId"}.getStr + if "cursor-showmore" in entryId: + let cursor = t{"item", "itemContent", "value"} + result.thread.cursor = cursor.getStr + result.thread.hasMore = true + elif "tweet" in entryId: + let tweet = parseGraphTweet(t{"item", "itemContent", "tweet_results", "result"}) + result.thread.content.add tweet + + if t{"item", "itemContent", "tweetDisplayType"}.getStr == "SelfThread": + result.self = true + +proc parseGraphConversation*(js: JsonNode; tweetId: string): Conversation = + result = Conversation(replies: Result[Chain](beginning: true)) + + let instructions = ? js{"data", "threaded_conversation_with_injections_v2", "instructions"} + if instructions.len == 0: + return + + for e in instructions[0]{"entries"}: + let entryId = e{"entryId"}.getStr + # echo entryId + if entryId.startsWith("tweet"): + let tweet = parseGraphTweet(e{"content", "itemContent", "tweet_results", "result"}) + + if $tweet.id == tweetId: + result.tweet = tweet + else: + result.before.content.add tweet + elif entryId.startsWith("conversationthread"): + let (thread, self) = parseGraphThread(e) + if self: + result.after = thread + else: + result.replies.content.add thread + elif entryId.startsWith("cursor-bottom"): + result.replies.bottom = e{"content", "itemContent", "value"}.getStr diff --git a/src/parserutils.nim b/src/parserutils.nim index 4b89236..af4d062 100644 --- a/src/parserutils.nim +++ b/src/parserutils.nim @@ -133,10 +133,6 @@ proc getTombstone*(js: JsonNode): string = result = js{"tombstoneInfo", "richText", "text"}.getStr result.removeSuffix(" Learn more") -proc getSource*(js: JsonNode): string = - let src = js{"source"}.getStr - result = src.substr(src.find('>') + 1, src.rfind('<') - 1) - proc getMp4Resolution*(url: string): int = # parses the height out of a URL like this one: # https://video.twimg.com/ext_tw_video//pu/vid/720x1280/.mp4 diff --git a/src/tokens.nim b/src/tokens.nim index e3b916a..e6a4449 100644 --- a/src/tokens.nim +++ b/src/tokens.nim @@ -41,7 +41,8 @@ proc getPoolJson*(): JsonNode = let maxReqs = case api - of Api.listMembers, Api.listBySlug, Api.list, Api.userRestId, Api.userScreenName: 500 + of Api.listMembers, Api.listBySlug, Api.list, + Api.userRestId, Api.userScreenName, Api.tweetDetail: 500 of Api.timeline: 187 else: 180 reqs = maxReqs - token.apis[api].remaining diff --git a/src/types.nim b/src/types.nim index 07f9bf7..087acb2 100644 --- a/src/types.nim +++ b/src/types.nim @@ -9,6 +9,7 @@ type InternalError* = object of CatchableError Api* {.pure.} = enum + tweetDetail userShow timeline search @@ -176,7 +177,6 @@ type available*: bool tombstone*: string location*: string - source*: string stats*: TweetStats retweet*: Option[Tweet] attribution*: Option[User] diff --git a/src/views/tweet.nim b/src/views/tweet.nim index 69335fa..ea94e28 100644 --- a/src/views/tweet.nim +++ b/src/views/tweet.nim @@ -347,7 +347,7 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class=""; index=0; renderQuote(tweet.quote.get(), prefs, path) if mainTweet: - p(class="tweet-published"): text &"{getTime(tweet)} · {tweet.source}" + p(class="tweet-published"): text &"{getTime(tweet)}" if tweet.mediaTags.len > 0: renderMediaTags(tweet.mediaTags) From 36c72f98603e6387431224b3fb65ada765e9ab65 Mon Sep 17 00:00:00 2001 From: Zed Date: Thu, 2 Feb 2023 12:38:58 +0100 Subject: [PATCH 077/409] Revert "Replace tweet endpoint with GraphQL" This reverts commit 19adc658c317c71e671201385350343add6275c8. --- src/api.nim | 15 ++---- src/consts.nim | 32 ------------ src/parser.nim | 120 ++++++++++++++++++++------------------------ src/parserutils.nim | 4 ++ src/tokens.nim | 3 +- src/types.nim | 2 +- src/views/tweet.nim | 2 +- 7 files changed, 67 insertions(+), 111 deletions(-) diff --git a/src/api.nim b/src/api.nim index dfcf413..dd94b69 100644 --- a/src/api.nim +++ b/src/api.nim @@ -101,21 +101,16 @@ proc getSearch*[T](query: Query; after=""): Future[Result[T]] {.async.} = except InternalError: return Result[T](beginning: true, query: query) -proc getGraphTweet(id: string; after=""): Future[Conversation] {.async.} = - if id.len == 0: return - let - cursor = if after.len > 0: "\"cursor\":\"$1\"," % after else: "" - variables = tweetVariables % [id, cursor] - params = {"variables": variables, "features": tweetFeatures} - js = await fetch(graphTweet ? params, Api.tweetDetail) - result = parseGraphConversation(js, id) +proc getTweetImpl(id: string; after=""): Future[Conversation] {.async.} = + let url = tweet / (id & ".json") ? genParams(cursor=after) + result = parseConversation(await fetch(url, Api.tweet), id) proc getReplies*(id, after: string): Future[Result[Chain]] {.async.} = - result = (await getGraphTweet(id, after)).replies + result = (await getTweetImpl(id, after)).replies result.beginning = after.len == 0 proc getTweet*(id: string; after=""): Future[Conversation] {.async.} = - result = await getGraphTweet(id) + result = await getTweetImpl(id) if after.len > 0: result.replies = await getReplies(id, after) diff --git a/src/consts.nim b/src/consts.nim index 18ecb2c..2d3ea56 100644 --- a/src/consts.nim +++ b/src/consts.nim @@ -19,7 +19,6 @@ const tweet* = timelineApi / "conversation" graphql = api / "graphql" - graphTweet* = graphql / "6lWNh96EXDJCXl05SAtn_g/TweetDetail" graphUser* = graphql / "7mjxD3-C6BxitPMVQ6w0-Q/UserByScreenName" graphUserById* = graphql / "I5nvpI91ljifos1Y3Lltyg/UserByRestId" graphList* = graphql / "JADTh6cjebfgetzvF3tQvQ/List" @@ -59,34 +58,3 @@ const ## user: "result_filter: user" ## photos: "result_filter: photos" ## videos: "result_filter: videos" - - tweetVariables* = """{ - "focalTweetId": "$1", - $2 - "includePromotedContent": false, - "withBirdwatchNotes": false, - "withDownvotePerspective": false, - "withReactionsMetadata": false, - "withReactionsPerspective": false, - "withSuperFollowsTweetFields": false, - "withSuperFollowsUserFields": false, - "withVoice": false, - "withV2Timeline": true -}""" - - tweetFeatures* = """{ - "graphql_is_translatable_rweb_tweet_is_translatable_enabled": false, - "responsive_web_graphql_timeline_navigation_enabled": false, - "standardized_nudges_misinfo": false, - "verified_phone_label_enabled": false, - "responsive_web_twitter_blue_verified_badge_is_enabled": false, - "tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled": false, - "view_counts_everywhere_api_enabled": false, - "responsive_web_edit_tweet_api_enabled": false, - "tweetypie_unmention_optimization_enabled": false, - "vibe_api_enabled": false, - "longform_notetweets_consumption_enabled": false, - "responsive_web_text_conversations_enabled": false, - "responsive_web_enhance_cards_enabled": false, - "interactive_text_enabled": false -}""" diff --git a/src/parser.nim b/src/parser.nim index 544f943..2b0c6a4 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -72,8 +72,8 @@ proc parseGif(js: JsonNode): Gif = proc parseVideo(js: JsonNode): Video = result = Video( thumb: js{"media_url_https"}.getImageStr, - views: js{"ext", "mediaStats", "r", "ok", "viewCount"}.getStr($js{"mediaStats", "viewCount"}.getInt), - available: js{"ext_media_availability", "status"}.getStr.toLowerAscii == "available", + views: js{"ext", "mediaStats", "r", "ok", "viewCount"}.getStr, + available: js{"ext_media_availability", "status"}.getStr == "available", title: js{"ext_alt_text"}.getStr, durationMs: js{"video_info", "duration_millis"}.getInt # playbackType: mp4 @@ -185,7 +185,7 @@ proc parseCard(js: JsonNode; urls: JsonNode): Card = result.url.len == 0 or result.url.startsWith("card://"): result.url = getPicUrl(result.image) -proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet = +proc parseTweet(js: JsonNode): Tweet = if js.isNull: return result = Tweet( id: js{"id_str"}.getId, @@ -193,6 +193,7 @@ proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet = replyId: js{"in_reply_to_status_id_str"}.getId, text: js{"full_text"}.getStr, time: js{"created_at"}.getTime, + source: getSource(js), hasThread: js{"self_thread"}.notNull, available: true, user: User(id: js{"user_id_str"}.getStr), @@ -217,7 +218,7 @@ proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet = result.retweet = some Tweet(id: rt.getId) return - if jsCard.kind != JNull: + with jsCard, js{"card"}: let name = jsCard{"name"}.getStr if "poll" in name: if "image" in name: @@ -294,17 +295,64 @@ proc parseGlobalObjects(js: JsonNode): GlobalObjects = result.users[k] = parseUser(v, k) for k, v in tweets: - var tweet = parseTweet(v, v{"card"}) + var tweet = parseTweet(v) if tweet.user.id in result.users: tweet.user = result.users[tweet.user.id] result.tweets[k] = tweet +proc parseThread(js: JsonNode; global: GlobalObjects): tuple[thread: Chain, self: bool] = + result.thread = Chain() + + let thread = js{"content", "item", "content", "conversationThread"} + with cursor, thread{"showMoreCursor"}: + result.thread.cursor = cursor{"value"}.getStr + result.thread.hasMore = true + + for t in thread{"conversationComponents"}: + let content = t{"conversationTweetComponent", "tweet"} + + if content{"displayType"}.getStr == "SelfThread": + result.self = true + + var tweet = finalizeTweet(global, content{"id"}.getStr) + if not tweet.available: + tweet.tombstone = getTombstone(content{"tombstone"}) + result.thread.content.add tweet + +proc parseConversation*(js: JsonNode; tweetId: string): Conversation = + result = Conversation(replies: Result[Chain](beginning: true)) + let global = parseGlobalObjects(? js) + + let instructions = ? js{"timeline", "instructions"} + if instructions.len == 0: + return + + for e in instructions[0]{"addEntries", "entries"}: + let entry = e{"entryId"}.getStr + if "tweet" in entry or "tombstone" in entry: + let tweet = finalizeTweet(global, e.getEntryId) + if $tweet.id != tweetId: + result.before.content.add tweet + else: + result.tweet = tweet + elif "conversationThread" in entry: + let (thread, self) = parseThread(e, global) + if thread.content.len > 0: + if self: + result.after = thread + else: + result.replies.content.add thread + elif "cursor-showMore" in entry: + result.replies.bottom = e.getCursor + elif "cursor-bottom" in entry: + result.replies.bottom = e.getCursor + proc parseStatus*(js: JsonNode): Tweet = with e, js{"errors"}: if e.getError == tweetNotFound: return - result = parseTweet(js, js{"card"}) + result = parseTweet(js) if not result.isNil: result.user = parseUser(js{"user"}) @@ -361,7 +409,7 @@ proc parseTimeline*(js: JsonNode; after=""): Timeline = proc parsePhotoRail*(js: JsonNode): PhotoRail = for tweet in js: let - t = parseTweet(tweet, js{"card"}) + t = parseTweet(tweet) url = if t.photos.len > 0: t.photos[0] elif t.video.isSome: get(t.video).thumb elif t.gif.isSome: get(t.gif).thumb @@ -370,61 +418,3 @@ proc parsePhotoRail*(js: JsonNode): PhotoRail = if url.len == 0: continue result.add GalleryPhoto(url: url, tweetId: $t.id) - -proc parseGraphTweet(js: JsonNode): Tweet = - if js.kind == JNull: - return Tweet(available: false) - - var jsCard = copy(js{"card", "legacy"}) - if jsCard.kind != JNull: - var values = newJObject() - for val in jsCard["binding_values"]: - values[val["key"].getStr] = val["value"] - jsCard["binding_values"] = values - - result = parseTweet(js{"legacy"}, jsCard) - result.user = parseUser(js{"core", "user_results", "result", "legacy"}) - - if result.quote.isSome: - result.quote = some(parseGraphTweet(js{"quoted_status_result", "result"})) - -proc parseGraphThread(js: JsonNode): tuple[thread: Chain; self: bool] = - let thread = js{"content", "items"} - for t in js{"content", "items"}: - let entryId = t{"entryId"}.getStr - if "cursor-showmore" in entryId: - let cursor = t{"item", "itemContent", "value"} - result.thread.cursor = cursor.getStr - result.thread.hasMore = true - elif "tweet" in entryId: - let tweet = parseGraphTweet(t{"item", "itemContent", "tweet_results", "result"}) - result.thread.content.add tweet - - if t{"item", "itemContent", "tweetDisplayType"}.getStr == "SelfThread": - result.self = true - -proc parseGraphConversation*(js: JsonNode; tweetId: string): Conversation = - result = Conversation(replies: Result[Chain](beginning: true)) - - let instructions = ? js{"data", "threaded_conversation_with_injections_v2", "instructions"} - if instructions.len == 0: - return - - for e in instructions[0]{"entries"}: - let entryId = e{"entryId"}.getStr - # echo entryId - if entryId.startsWith("tweet"): - let tweet = parseGraphTweet(e{"content", "itemContent", "tweet_results", "result"}) - - if $tweet.id == tweetId: - result.tweet = tweet - else: - result.before.content.add tweet - elif entryId.startsWith("conversationthread"): - let (thread, self) = parseGraphThread(e) - if self: - result.after = thread - else: - result.replies.content.add thread - elif entryId.startsWith("cursor-bottom"): - result.replies.bottom = e{"content", "itemContent", "value"}.getStr diff --git a/src/parserutils.nim b/src/parserutils.nim index af4d062..4b89236 100644 --- a/src/parserutils.nim +++ b/src/parserutils.nim @@ -133,6 +133,10 @@ proc getTombstone*(js: JsonNode): string = result = js{"tombstoneInfo", "richText", "text"}.getStr result.removeSuffix(" Learn more") +proc getSource*(js: JsonNode): string = + let src = js{"source"}.getStr + result = src.substr(src.find('>') + 1, src.rfind('<') - 1) + proc getMp4Resolution*(url: string): int = # parses the height out of a URL like this one: # https://video.twimg.com/ext_tw_video//pu/vid/720x1280/.mp4 diff --git a/src/tokens.nim b/src/tokens.nim index e6a4449..e3b916a 100644 --- a/src/tokens.nim +++ b/src/tokens.nim @@ -41,8 +41,7 @@ proc getPoolJson*(): JsonNode = let maxReqs = case api - of Api.listMembers, Api.listBySlug, Api.list, - Api.userRestId, Api.userScreenName, Api.tweetDetail: 500 + of Api.listMembers, Api.listBySlug, Api.list, Api.userRestId, Api.userScreenName: 500 of Api.timeline: 187 else: 180 reqs = maxReqs - token.apis[api].remaining diff --git a/src/types.nim b/src/types.nim index 087acb2..07f9bf7 100644 --- a/src/types.nim +++ b/src/types.nim @@ -9,7 +9,6 @@ type InternalError* = object of CatchableError Api* {.pure.} = enum - tweetDetail userShow timeline search @@ -177,6 +176,7 @@ type available*: bool tombstone*: string location*: string + source*: string stats*: TweetStats retweet*: Option[Tweet] attribution*: Option[User] diff --git a/src/views/tweet.nim b/src/views/tweet.nim index ea94e28..69335fa 100644 --- a/src/views/tweet.nim +++ b/src/views/tweet.nim @@ -347,7 +347,7 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class=""; index=0; renderQuote(tweet.quote.get(), prefs, path) if mainTweet: - p(class="tweet-published"): text &"{getTime(tweet)}" + p(class="tweet-published"): text &"{getTime(tweet)} · {tweet.source}" if tweet.mediaTags.len > 0: renderMediaTags(tweet.mediaTags) From 670a3bca6ea8d20761adc36799bb68d614b34a28 Mon Sep 17 00:00:00 2001 From: Zed Date: Fri, 24 Feb 2023 01:01:22 +0100 Subject: [PATCH 078/409] Revert "Revert "Replace tweet endpoint with GraphQL"" This reverts commit 36c72f98603e6387431224b3fb65ada765e9ab65. --- src/api.nim | 15 ++++-- src/consts.nim | 32 ++++++++++++ src/parser.nim | 120 ++++++++++++++++++++++++-------------------- src/parserutils.nim | 4 -- src/tokens.nim | 3 +- src/types.nim | 2 +- src/views/tweet.nim | 2 +- 7 files changed, 111 insertions(+), 67 deletions(-) diff --git a/src/api.nim b/src/api.nim index dd94b69..dfcf413 100644 --- a/src/api.nim +++ b/src/api.nim @@ -101,16 +101,21 @@ proc getSearch*[T](query: Query; after=""): Future[Result[T]] {.async.} = except InternalError: return Result[T](beginning: true, query: query) -proc getTweetImpl(id: string; after=""): Future[Conversation] {.async.} = - let url = tweet / (id & ".json") ? genParams(cursor=after) - result = parseConversation(await fetch(url, Api.tweet), id) +proc getGraphTweet(id: string; after=""): Future[Conversation] {.async.} = + if id.len == 0: return + let + cursor = if after.len > 0: "\"cursor\":\"$1\"," % after else: "" + variables = tweetVariables % [id, cursor] + params = {"variables": variables, "features": tweetFeatures} + js = await fetch(graphTweet ? params, Api.tweetDetail) + result = parseGraphConversation(js, id) proc getReplies*(id, after: string): Future[Result[Chain]] {.async.} = - result = (await getTweetImpl(id, after)).replies + result = (await getGraphTweet(id, after)).replies result.beginning = after.len == 0 proc getTweet*(id: string; after=""): Future[Conversation] {.async.} = - result = await getTweetImpl(id) + result = await getGraphTweet(id) if after.len > 0: result.replies = await getReplies(id, after) diff --git a/src/consts.nim b/src/consts.nim index 2d3ea56..18ecb2c 100644 --- a/src/consts.nim +++ b/src/consts.nim @@ -19,6 +19,7 @@ const tweet* = timelineApi / "conversation" graphql = api / "graphql" + graphTweet* = graphql / "6lWNh96EXDJCXl05SAtn_g/TweetDetail" graphUser* = graphql / "7mjxD3-C6BxitPMVQ6w0-Q/UserByScreenName" graphUserById* = graphql / "I5nvpI91ljifos1Y3Lltyg/UserByRestId" graphList* = graphql / "JADTh6cjebfgetzvF3tQvQ/List" @@ -58,3 +59,34 @@ const ## user: "result_filter: user" ## photos: "result_filter: photos" ## videos: "result_filter: videos" + + tweetVariables* = """{ + "focalTweetId": "$1", + $2 + "includePromotedContent": false, + "withBirdwatchNotes": false, + "withDownvotePerspective": false, + "withReactionsMetadata": false, + "withReactionsPerspective": false, + "withSuperFollowsTweetFields": false, + "withSuperFollowsUserFields": false, + "withVoice": false, + "withV2Timeline": true +}""" + + tweetFeatures* = """{ + "graphql_is_translatable_rweb_tweet_is_translatable_enabled": false, + "responsive_web_graphql_timeline_navigation_enabled": false, + "standardized_nudges_misinfo": false, + "verified_phone_label_enabled": false, + "responsive_web_twitter_blue_verified_badge_is_enabled": false, + "tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled": false, + "view_counts_everywhere_api_enabled": false, + "responsive_web_edit_tweet_api_enabled": false, + "tweetypie_unmention_optimization_enabled": false, + "vibe_api_enabled": false, + "longform_notetweets_consumption_enabled": false, + "responsive_web_text_conversations_enabled": false, + "responsive_web_enhance_cards_enabled": false, + "interactive_text_enabled": false +}""" diff --git a/src/parser.nim b/src/parser.nim index 2b0c6a4..544f943 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -72,8 +72,8 @@ proc parseGif(js: JsonNode): Gif = proc parseVideo(js: JsonNode): Video = result = Video( thumb: js{"media_url_https"}.getImageStr, - views: js{"ext", "mediaStats", "r", "ok", "viewCount"}.getStr, - available: js{"ext_media_availability", "status"}.getStr == "available", + views: js{"ext", "mediaStats", "r", "ok", "viewCount"}.getStr($js{"mediaStats", "viewCount"}.getInt), + available: js{"ext_media_availability", "status"}.getStr.toLowerAscii == "available", title: js{"ext_alt_text"}.getStr, durationMs: js{"video_info", "duration_millis"}.getInt # playbackType: mp4 @@ -185,7 +185,7 @@ proc parseCard(js: JsonNode; urls: JsonNode): Card = result.url.len == 0 or result.url.startsWith("card://"): result.url = getPicUrl(result.image) -proc parseTweet(js: JsonNode): Tweet = +proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet = if js.isNull: return result = Tweet( id: js{"id_str"}.getId, @@ -193,7 +193,6 @@ proc parseTweet(js: JsonNode): Tweet = replyId: js{"in_reply_to_status_id_str"}.getId, text: js{"full_text"}.getStr, time: js{"created_at"}.getTime, - source: getSource(js), hasThread: js{"self_thread"}.notNull, available: true, user: User(id: js{"user_id_str"}.getStr), @@ -218,7 +217,7 @@ proc parseTweet(js: JsonNode): Tweet = result.retweet = some Tweet(id: rt.getId) return - with jsCard, js{"card"}: + if jsCard.kind != JNull: let name = jsCard{"name"}.getStr if "poll" in name: if "image" in name: @@ -295,64 +294,17 @@ proc parseGlobalObjects(js: JsonNode): GlobalObjects = result.users[k] = parseUser(v, k) for k, v in tweets: - var tweet = parseTweet(v) + var tweet = parseTweet(v, v{"card"}) if tweet.user.id in result.users: tweet.user = result.users[tweet.user.id] result.tweets[k] = tweet -proc parseThread(js: JsonNode; global: GlobalObjects): tuple[thread: Chain, self: bool] = - result.thread = Chain() - - let thread = js{"content", "item", "content", "conversationThread"} - with cursor, thread{"showMoreCursor"}: - result.thread.cursor = cursor{"value"}.getStr - result.thread.hasMore = true - - for t in thread{"conversationComponents"}: - let content = t{"conversationTweetComponent", "tweet"} - - if content{"displayType"}.getStr == "SelfThread": - result.self = true - - var tweet = finalizeTweet(global, content{"id"}.getStr) - if not tweet.available: - tweet.tombstone = getTombstone(content{"tombstone"}) - result.thread.content.add tweet - -proc parseConversation*(js: JsonNode; tweetId: string): Conversation = - result = Conversation(replies: Result[Chain](beginning: true)) - let global = parseGlobalObjects(? js) - - let instructions = ? js{"timeline", "instructions"} - if instructions.len == 0: - return - - for e in instructions[0]{"addEntries", "entries"}: - let entry = e{"entryId"}.getStr - if "tweet" in entry or "tombstone" in entry: - let tweet = finalizeTweet(global, e.getEntryId) - if $tweet.id != tweetId: - result.before.content.add tweet - else: - result.tweet = tweet - elif "conversationThread" in entry: - let (thread, self) = parseThread(e, global) - if thread.content.len > 0: - if self: - result.after = thread - else: - result.replies.content.add thread - elif "cursor-showMore" in entry: - result.replies.bottom = e.getCursor - elif "cursor-bottom" in entry: - result.replies.bottom = e.getCursor - proc parseStatus*(js: JsonNode): Tweet = with e, js{"errors"}: if e.getError == tweetNotFound: return - result = parseTweet(js) + result = parseTweet(js, js{"card"}) if not result.isNil: result.user = parseUser(js{"user"}) @@ -409,7 +361,7 @@ proc parseTimeline*(js: JsonNode; after=""): Timeline = proc parsePhotoRail*(js: JsonNode): PhotoRail = for tweet in js: let - t = parseTweet(tweet) + t = parseTweet(tweet, js{"card"}) url = if t.photos.len > 0: t.photos[0] elif t.video.isSome: get(t.video).thumb elif t.gif.isSome: get(t.gif).thumb @@ -418,3 +370,61 @@ proc parsePhotoRail*(js: JsonNode): PhotoRail = if url.len == 0: continue result.add GalleryPhoto(url: url, tweetId: $t.id) + +proc parseGraphTweet(js: JsonNode): Tweet = + if js.kind == JNull: + return Tweet(available: false) + + var jsCard = copy(js{"card", "legacy"}) + if jsCard.kind != JNull: + var values = newJObject() + for val in jsCard["binding_values"]: + values[val["key"].getStr] = val["value"] + jsCard["binding_values"] = values + + result = parseTweet(js{"legacy"}, jsCard) + result.user = parseUser(js{"core", "user_results", "result", "legacy"}) + + if result.quote.isSome: + result.quote = some(parseGraphTweet(js{"quoted_status_result", "result"})) + +proc parseGraphThread(js: JsonNode): tuple[thread: Chain; self: bool] = + let thread = js{"content", "items"} + for t in js{"content", "items"}: + let entryId = t{"entryId"}.getStr + if "cursor-showmore" in entryId: + let cursor = t{"item", "itemContent", "value"} + result.thread.cursor = cursor.getStr + result.thread.hasMore = true + elif "tweet" in entryId: + let tweet = parseGraphTweet(t{"item", "itemContent", "tweet_results", "result"}) + result.thread.content.add tweet + + if t{"item", "itemContent", "tweetDisplayType"}.getStr == "SelfThread": + result.self = true + +proc parseGraphConversation*(js: JsonNode; tweetId: string): Conversation = + result = Conversation(replies: Result[Chain](beginning: true)) + + let instructions = ? js{"data", "threaded_conversation_with_injections_v2", "instructions"} + if instructions.len == 0: + return + + for e in instructions[0]{"entries"}: + let entryId = e{"entryId"}.getStr + # echo entryId + if entryId.startsWith("tweet"): + let tweet = parseGraphTweet(e{"content", "itemContent", "tweet_results", "result"}) + + if $tweet.id == tweetId: + result.tweet = tweet + else: + result.before.content.add tweet + elif entryId.startsWith("conversationthread"): + let (thread, self) = parseGraphThread(e) + if self: + result.after = thread + else: + result.replies.content.add thread + elif entryId.startsWith("cursor-bottom"): + result.replies.bottom = e{"content", "itemContent", "value"}.getStr diff --git a/src/parserutils.nim b/src/parserutils.nim index 4b89236..af4d062 100644 --- a/src/parserutils.nim +++ b/src/parserutils.nim @@ -133,10 +133,6 @@ proc getTombstone*(js: JsonNode): string = result = js{"tombstoneInfo", "richText", "text"}.getStr result.removeSuffix(" Learn more") -proc getSource*(js: JsonNode): string = - let src = js{"source"}.getStr - result = src.substr(src.find('>') + 1, src.rfind('<') - 1) - proc getMp4Resolution*(url: string): int = # parses the height out of a URL like this one: # https://video.twimg.com/ext_tw_video//pu/vid/720x1280/.mp4 diff --git a/src/tokens.nim b/src/tokens.nim index e3b916a..e6a4449 100644 --- a/src/tokens.nim +++ b/src/tokens.nim @@ -41,7 +41,8 @@ proc getPoolJson*(): JsonNode = let maxReqs = case api - of Api.listMembers, Api.listBySlug, Api.list, Api.userRestId, Api.userScreenName: 500 + of Api.listMembers, Api.listBySlug, Api.list, + Api.userRestId, Api.userScreenName, Api.tweetDetail: 500 of Api.timeline: 187 else: 180 reqs = maxReqs - token.apis[api].remaining diff --git a/src/types.nim b/src/types.nim index 07f9bf7..087acb2 100644 --- a/src/types.nim +++ b/src/types.nim @@ -9,6 +9,7 @@ type InternalError* = object of CatchableError Api* {.pure.} = enum + tweetDetail userShow timeline search @@ -176,7 +177,6 @@ type available*: bool tombstone*: string location*: string - source*: string stats*: TweetStats retweet*: Option[Tweet] attribution*: Option[User] diff --git a/src/views/tweet.nim b/src/views/tweet.nim index 69335fa..ea94e28 100644 --- a/src/views/tweet.nim +++ b/src/views/tweet.nim @@ -347,7 +347,7 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class=""; index=0; renderQuote(tweet.quote.get(), prefs, path) if mainTweet: - p(class="tweet-published"): text &"{getTime(tweet)} · {tweet.source}" + p(class="tweet-published"): text &"{getTime(tweet)}" if tweet.mediaTags.len > 0: renderMediaTags(tweet.mediaTags) From 85f14c9a64d70ebed0ed2f203e9e682d3e415d0b Mon Sep 17 00:00:00 2001 From: Zed Date: Fri, 24 Feb 2023 01:02:28 +0100 Subject: [PATCH 079/409] Fix crash when parsing non-GraphQL cached tweets --- src/types.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/types.nim b/src/types.nim index 087acb2..b5353b5 100644 --- a/src/types.nim +++ b/src/types.nim @@ -177,6 +177,8 @@ type available*: bool tombstone*: string location*: string + ## Unused, needed for backwards compat + source*: string stats*: TweetStats retweet*: Option[Tweet] attribution*: Option[User] From 2751504d2f41d2fba8d4ef74a5ac413833750c00 Mon Sep 17 00:00:00 2001 From: Zed Date: Sat, 25 Feb 2023 18:25:02 +0100 Subject: [PATCH 080/409] Fix crash on unavailable tweets --- src/parser.nim | 5 ++++- src/types.nim | 2 +- tests/test_card.py | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/parser.nim b/src/parser.nim index 544f943..6a77a5f 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -372,7 +372,7 @@ proc parsePhotoRail*(js: JsonNode): PhotoRail = result.add GalleryPhoto(url: url, tweetId: $t.id) proc parseGraphTweet(js: JsonNode): Tweet = - if js.kind == JNull: + if js.kind == JNull or js{"__typename"}.getStr == "TweetUnavailable": return Tweet(available: false) var jsCard = copy(js{"card", "legacy"}) @@ -416,6 +416,9 @@ proc parseGraphConversation*(js: JsonNode; tweetId: string): Conversation = if entryId.startsWith("tweet"): let tweet = parseGraphTweet(e{"content", "itemContent", "tweet_results", "result"}) + if not tweet.available: + tweet.id = parseBiggestInt(entryId.getId()) + if $tweet.id == tweetId: result.tweet = tweet else: diff --git a/src/types.nim b/src/types.nim index b5353b5..625dcb0 100644 --- a/src/types.nim +++ b/src/types.nim @@ -177,7 +177,7 @@ type available*: bool tombstone*: string location*: string - ## Unused, needed for backwards compat + # Unused, needed for backwards compat source*: string stats*: TweetStats retweet*: Option[Tweet] diff --git a/tests/test_card.py b/tests/test_card.py index ab79e9c..77e44e1 100644 --- a/tests/test_card.py +++ b/tests/test_card.py @@ -42,7 +42,7 @@ no_thumb = [ ['nim_lang/status/1082989146040340480', 'Nim in 2018: A short recap', - 'Posted in r/programming by u/miran1', + '36 votes and 46 comments so far on Reddit', 'reddit.com'] ] From 54cc790bdb3e3f2757f37c74c28ce634fa445752 Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Sun, 26 Feb 2023 07:39:08 +0800 Subject: [PATCH 081/409] Remove Instagram -> Bibliogram replacement as it's discontinued (#772) Close #761 Reference: https://cadence.moe/blog/2022-09-01-discontinuing-bibliogram --- nitter.example.conf | 1 - src/formatters.nim | 4 ---- src/prefs_impl.nim | 4 ---- 3 files changed, 9 deletions(-) diff --git a/nitter.example.conf b/nitter.example.conf index cc7551a..a7abea8 100644 --- a/nitter.example.conf +++ b/nitter.example.conf @@ -39,7 +39,6 @@ theme = "Nitter" replaceTwitter = "nitter.net" replaceYouTube = "piped.video" replaceReddit = "teddit.net" -replaceInstagram = "" proxyVideos = true hlsPlayback = false infiniteScroll = false diff --git a/src/formatters.nim b/src/formatters.nim index 87ff647..3630917 100644 --- a/src/formatters.nim +++ b/src/formatters.nim @@ -13,7 +13,6 @@ let twLinkRegex = re"""twitter\.com(\S+)""" ytRegex = re(r"([A-z.]+\.)?youtu(be\.com|\.be)", {reStudy, reIgnoreCase}) - igRegex = re"(www\.)?instagram\.com" rdRegex = re"(? 0 and "instagram.com" in result: - result = result.replace(igRegex, prefs.replaceInstagram) - if absolute.len > 0 and "href" in result: result = result.replace("href=\"/", &"href=\"{absolute}/") diff --git a/src/prefs_impl.nim b/src/prefs_impl.nim index 0223c82..87de88d 100644 --- a/src/prefs_impl.nim +++ b/src/prefs_impl.nim @@ -107,10 +107,6 @@ genPrefs: "Reddit -> Teddit/Libreddit" placeholder: "Teddit hostname" - replaceInstagram(input, ""): - "Instagram -> Bibliogram" - placeholder: "Bibliogram hostname" - iterator allPrefs*(): Pref = for k, v in prefList: for pref in v: From c21511037efc45988b6aa415f86d91fc4a912aed Mon Sep 17 00:00:00 2001 From: Zed Date: Sun, 26 Feb 2023 00:43:11 +0100 Subject: [PATCH 082/409] Uppercase HLS in preference description --- src/prefs_impl.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/prefs_impl.nim b/src/prefs_impl.nim index 87de88d..8e2ac8f 100644 --- a/src/prefs_impl.nim +++ b/src/prefs_impl.nim @@ -83,7 +83,7 @@ genPrefs: "Enable mp4 video playback (only for gifs)" hlsPlayback(checkbox, false): - "Enable hls video streaming (requires JavaScript)" + "Enable HLS video streaming (requires JavaScript)" proxyVideos(checkbox, true): "Proxy video streaming through the server (might be slow)" From f4c20a48a0eb9b615959ff692f80e5fc9f9052d3 Mon Sep 17 00:00:00 2001 From: Whizanth <107213432+Whizanth@users.noreply.github.com> Date: Sun, 26 Feb 2023 14:59:06 +0100 Subject: [PATCH 083/409] Add Dockerfile for arm64 (#762) --- Dockerfile.arm64 | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Dockerfile.arm64 diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 new file mode 100644 index 0000000..899a430 --- /dev/null +++ b/Dockerfile.arm64 @@ -0,0 +1,23 @@ +FROM alpine:3.17 as nim +LABEL maintainer="setenforce@protonmail.com" + +RUN apk --no-cache add gcc git libc-dev libsass-dev "nim=1.6.8-r0" nimble pcre + +WORKDIR /src/nitter + +COPY nitter.nimble . +RUN nimble install -y --depsOnly + +COPY . . +RUN nimble build -d:danger -d:lto -d:strip \ + && nimble scss \ + && nimble md + +FROM alpine:3.17 +WORKDIR /src/ +RUN apk --no-cache add pcre ca-certificates +COPY --from=nim /src/nitter/nitter ./ +COPY --from=nim /src/nitter/nitter.example.conf ./nitter.conf +COPY --from=nim /src/nitter/public ./public +EXPOSE 8080 +CMD ./nitter From 514b16c417557880987fe1d25011919a79d3cc5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milien=20Devos=20=28perso=29?= Date: Sun, 26 Feb 2023 15:06:52 +0100 Subject: [PATCH 084/409] Build and push ARM64 docker image (#798) --- .github/workflows/build-docker.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index 30a209b..81d547a 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -28,7 +28,7 @@ jobs: with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - - name: Build and push + - name: Build and push AMD64 Docker image uses: docker/build-push-action@v2 with: context: . @@ -36,3 +36,11 @@ jobs: platforms: linux/amd64 push: true tags: zedeus/nitter:latest,zedeus/nitter:${{ github.sha }} + - name: Build and push ARM64 Docker image + uses: docker/build-push-action@v2 + with: + context: . + file: ./Dockerfile.arm64 + platforms: linux/arm64 + push: true + tags: zedeus/nitter:latest-arm64,zedeus/nitter:${{ github.sha }}-arm64 From 33e34c2d17de95458e9b9624f625aea7502d4088 Mon Sep 17 00:00:00 2001 From: Whizanth <107213432+Whizanth@users.noreply.github.com> Date: Sun, 26 Feb 2023 17:47:36 +0100 Subject: [PATCH 085/409] Add openssl1.1-compat to Dockerfile.arm64 (#800) --- Dockerfile.arm64 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 index 899a430..6cd6744 100644 --- a/Dockerfile.arm64 +++ b/Dockerfile.arm64 @@ -15,7 +15,7 @@ RUN nimble build -d:danger -d:lto -d:strip \ FROM alpine:3.17 WORKDIR /src/ -RUN apk --no-cache add pcre ca-certificates +RUN apk --no-cache add ca-certificates pcre openssl1.1-compat COPY --from=nim /src/nitter/nitter ./ COPY --from=nim /src/nitter/nitter.example.conf ./nitter.conf COPY --from=nim /src/nitter/public ./public From 756a39bdd2b69b1faadba6d996ed58ac01699a9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milien=20Devos=20=28perso=29?= Date: Sun, 26 Feb 2023 18:24:31 +0100 Subject: [PATCH 086/409] Update note about ARM64 docker image + add docker hub links (#799) --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 286bea7..5ca2ee6 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,9 @@ performance reasons. ### Docker -#### NOTE: For ARM64/ARM support, please use [unixfox's image](https://quay.io/repository/unixfox/nitter?tab=tags), more info [here](https://github.com/zedeus/nitter/issues/399#issuecomment-997263495) +Page for the Docker image: https://hub.docker.com/r/zedeus/nitter + +#### NOTE: For ARM64 support, please use the separate ARM64 docker image: [`zedeus/nitter:latest-arm64`](https://hub.docker.com/r/zedeus/nitter/tags). To run Nitter with Docker, you'll need to install and run Redis separately before you can run the container. See below for how to also run Redis using @@ -122,6 +124,8 @@ docker build -t nitter:latest . docker run -v $(pwd)/nitter.conf:/src/nitter.conf -d --network host nitter:latest ``` +Note: For ARM64, use this Dockerfile: [`Dockerfile.arm64`](https://github.com/zedeus/nitter/blob/master/Dockerfile.arm64). + A prebuilt Docker image is provided as well: ```bash From 3a5faded86755f5cefd179375deb34fc93e704ab Mon Sep 17 00:00:00 2001 From: HookedBehemoth Date: Wed, 1 Mar 2023 00:53:44 +0100 Subject: [PATCH 087/409] parse recently added long tweets --- src/consts.nim | 2 +- src/parser.nim | 4 ++++ src/parserutils.nim | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/consts.nim b/src/consts.nim index 18ecb2c..bb4e1a3 100644 --- a/src/consts.nim +++ b/src/consts.nim @@ -85,7 +85,7 @@ const "responsive_web_edit_tweet_api_enabled": false, "tweetypie_unmention_optimization_enabled": false, "vibe_api_enabled": false, - "longform_notetweets_consumption_enabled": false, + "longform_notetweets_consumption_enabled": true, "responsive_web_text_conversations_enabled": false, "responsive_web_enhance_cards_enabled": false, "interactive_text_enabled": false diff --git a/src/parser.nim b/src/parser.nim index 6a77a5f..f55f116 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -385,6 +385,10 @@ proc parseGraphTweet(js: JsonNode): Tweet = result = parseTweet(js{"legacy"}, jsCard) result.user = parseUser(js{"core", "user_results", "result", "legacy"}) + var note_tweet = js{"note_tweet", "note_tweet_results", "result"} + if note_tweet.kind != JNull: + result.expandNoteTweetEntities(note_tweet) + if result.quote.isSome: result.quote = some(parseGraphTweet(js{"quoted_status_result", "result"})) diff --git a/src/parserutils.nim b/src/parserutils.nim index af4d062..22c3d86 100644 --- a/src/parserutils.nim +++ b/src/parserutils.nim @@ -289,3 +289,45 @@ proc expandTweetEntities*(tweet: Tweet; js: JsonNode) = tweet.text = orig.replacedWith(replacements, textSlice) .strip(leading=false) + +proc expandNoteTweetEntities*(tweet: Tweet; noteTweet: JsonNode) = + let + text = noteTweet{"text"}.getStr + orig = text.toRunes + ent = ? noteTweet{"entity_set"} + hasCard = tweet.card.isSome + + var replacements = newSeq[ReplaceSlice]() + + with urls, ent{"urls"}: + for u in urls: + let urlStr = u["url"].getStr + if urlStr.len == 0 or urlStr notin text: + continue + replacements.extractUrls(u, orig.len, hideTwitter = false) + if hasCard and u{"url"}.getStr == get(tweet.card).url: + get(tweet.card).url = u{"expanded_url"}.getStr + + if "hashtags" in ent: + for hashtag in ent["hashtags"]: + replacements.extractHashtags(hashtag) + + if "symbols" in ent: + for symbol in ent["symbols"]: + replacements.extractHashtags(symbol) + + if "user_mentions" in ent: + for mention in ent["user_mentions"]: + let + name = mention{"screen_name"}.getStr + slice = mention.extractSlice + idx = tweet.reply.find(name) + + replacements.add ReplaceSlice(kind: rkMention, slice: slice, + url: "/" & name, display: mention["name"].getStr) + + replacements.deduplicate + replacements.sort(cmp) + + tweet.text = orig.replacedWith(replacements, 0..orig.len) + .strip(leading=false) From 368974c803858a88c4a327b2ad6ae5966cc3e239 Mon Sep 17 00:00:00 2001 From: HookedBehemoth Date: Wed, 1 Mar 2023 16:13:36 +0100 Subject: [PATCH 088/409] Update src/parser.nim Co-authored-by: Zed --- src/parser.nim | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/parser.nim b/src/parser.nim index f55f116..3858956 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -385,9 +385,8 @@ proc parseGraphTweet(js: JsonNode): Tweet = result = parseTweet(js{"legacy"}, jsCard) result.user = parseUser(js{"core", "user_results", "result", "legacy"}) - var note_tweet = js{"note_tweet", "note_tweet_results", "result"} - if note_tweet.kind != JNull: - result.expandNoteTweetEntities(note_tweet) + with noteTweet, js{"note_tweet", "note_tweet_results", "result"}: + result.expandNoteTweetEntities(noteTweet) if result.quote.isSome: result.quote = some(parseGraphTweet(js{"quoted_status_result", "result"})) From aea884c48ef1a429790e8c401e98548d2d6de841 Mon Sep 17 00:00:00 2001 From: Zed Date: Fri, 3 Mar 2023 21:19:21 +0100 Subject: [PATCH 089/409] Deduplicate note tweet parsing --- src/parser.nim | 4 +- src/parserutils.nim | 94 +++++++++++++++------------------------------ tests/test_card.py | 2 +- 3 files changed, 35 insertions(+), 65 deletions(-) diff --git a/src/parser.nim b/src/parser.nim index 3858956..a491cea 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -204,12 +204,12 @@ proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet = ) ) + result.expandTweetEntities(js) + # fix for pinned threads if result.hasThread and result.threadId == 0: result.threadId = js{"self_thread", "id_str"}.getId - result.expandTweetEntities(js) - if js{"is_quote_status"}.getBool: result.quote = some Tweet(id: js{"quoted_status_id_str"}.getId) diff --git a/src/parserutils.nim b/src/parserutils.nim index 22c3d86..8ae9cd0 100644 --- a/src/parserutils.nim +++ b/src/parserutils.nim @@ -230,47 +230,37 @@ proc expandUserEntities*(user: var User; js: JsonNode) = user.bio = user.bio.replacef(unRegex, unReplace) .replacef(htRegex, htReplace) -proc expandTweetEntities*(tweet: Tweet; js: JsonNode) = - let - orig = tweet.text.toRunes - textRange = js{"display_text_range"} - textSlice = textRange{0}.getInt .. textRange{1}.getInt - hasQuote = js{"is_quote_status"}.getBool - hasCard = tweet.card.isSome - - var replyTo = "" - if tweet.replyId != 0: - with reply, js{"in_reply_to_screen_name"}: - tweet.reply.add reply.getStr - replyTo = reply.getStr - - let ent = ? js{"entities"} +proc expandTextEntities(tweet: Tweet; entities: JsonNode; text: string; textSlice: Slice[int]; + replyTo=""; hasQuote=false) = + let hasCard = tweet.card.isSome var replacements = newSeq[ReplaceSlice]() - with urls, ent{"urls"}: + with urls, entities{"urls"}: for u in urls: let urlStr = u["url"].getStr - if urlStr.len == 0 or urlStr notin tweet.text: + if urlStr.len == 0 or urlStr notin text: continue + replacements.extractUrls(u, textSlice.b, hideTwitter = hasQuote) + if hasCard and u{"url"}.getStr == get(tweet.card).url: get(tweet.card).url = u{"expanded_url"}.getStr - with media, ent{"media"}: + with media, entities{"media"}: for m in media: replacements.extractUrls(m, textSlice.b, hideTwitter = true) - if "hashtags" in ent: - for hashtag in ent["hashtags"]: + if "hashtags" in entities: + for hashtag in entities["hashtags"]: replacements.extractHashtags(hashtag) - if "symbols" in ent: - for symbol in ent["symbols"]: + if "symbols" in entities: + for symbol in entities["symbols"]: replacements.extractHashtags(symbol) - if "user_mentions" in ent: - for mention in ent["user_mentions"]: + if "user_mentions" in entities: + for mention in entities["user_mentions"]: let name = mention{"screen_name"}.getStr slice = mention.extractSlice @@ -287,47 +277,27 @@ proc expandTweetEntities*(tweet: Tweet; js: JsonNode) = replacements.deduplicate replacements.sort(cmp) - tweet.text = orig.replacedWith(replacements, textSlice) - .strip(leading=false) + tweet.text = text.toRunes.replacedWith(replacements, textSlice).strip(leading=false) -proc expandNoteTweetEntities*(tweet: Tweet; noteTweet: JsonNode) = +proc expandTweetEntities*(tweet: Tweet; js: JsonNode) = let - text = noteTweet{"text"}.getStr - orig = text.toRunes - ent = ? noteTweet{"entity_set"} - hasCard = tweet.card.isSome + entities = ? js{"entities"} + hasQuote = js{"is_quote_status"}.getBool + textRange = js{"display_text_range"} + textSlice = textRange{0}.getInt .. textRange{1}.getInt - var replacements = newSeq[ReplaceSlice]() + var replyTo = "" + if tweet.replyId != 0: + with reply, js{"in_reply_to_screen_name"}: + replyTo = reply.getStr + tweet.reply.add replyTo - with urls, ent{"urls"}: - for u in urls: - let urlStr = u["url"].getStr - if urlStr.len == 0 or urlStr notin text: - continue - replacements.extractUrls(u, orig.len, hideTwitter = false) - if hasCard and u{"url"}.getStr == get(tweet.card).url: - get(tweet.card).url = u{"expanded_url"}.getStr + tweet.expandTextEntities(entities, tweet.text, textSlice, replyTo, hasQuote) - if "hashtags" in ent: - for hashtag in ent["hashtags"]: - replacements.extractHashtags(hashtag) +proc expandNoteTweetEntities*(tweet: Tweet; js: JsonNode) = + let + entities = ? js{"entity_set"} + text = js{"text"}.getStr + textSlice = 0..text.runeLen - if "symbols" in ent: - for symbol in ent["symbols"]: - replacements.extractHashtags(symbol) - - if "user_mentions" in ent: - for mention in ent["user_mentions"]: - let - name = mention{"screen_name"}.getStr - slice = mention.extractSlice - idx = tweet.reply.find(name) - - replacements.add ReplaceSlice(kind: rkMention, slice: slice, - url: "/" & name, display: mention["name"].getStr) - - replacements.deduplicate - replacements.sort(cmp) - - tweet.text = orig.replacedWith(replacements, 0..orig.len) - .strip(leading=false) + tweet.expandTextEntities(entities, text, textSlice) diff --git a/tests/test_card.py b/tests/test_card.py index 77e44e1..51945d6 100644 --- a/tests/test_card.py +++ b/tests/test_card.py @@ -42,7 +42,7 @@ no_thumb = [ ['nim_lang/status/1082989146040340480', 'Nim in 2018: A short recap', - '36 votes and 46 comments so far on Reddit', + 'Posted by u/miran1 - 36 votes and 46 comments', 'reddit.com'] ] From 725f601c9b006cd774024464caf4529068eb17a9 Mon Sep 17 00:00:00 2001 From: Zed Date: Sat, 4 Mar 2023 00:46:44 +0100 Subject: [PATCH 090/409] Add missing error codes for unavailable tweets --- src/parser.nim | 2 +- src/types.nim | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/parser.nim b/src/parser.nim index a491cea..977d579 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -301,7 +301,7 @@ proc parseGlobalObjects(js: JsonNode): GlobalObjects = proc parseStatus*(js: JsonNode): Tweet = with e, js{"errors"}: - if e.getError == tweetNotFound: + if e.getError in {tweetNotFound, tweetUnavailable, tweetCensored}: return result = parseTweet(js, js{"card"}) diff --git a/src/types.nim b/src/types.nim index 625dcb0..573fe8c 100644 --- a/src/types.nim +++ b/src/types.nim @@ -47,6 +47,8 @@ type forbidden = 200 badToken = 239 noCsrf = 353 + tweetUnavailable = 421 + tweetCensored = 422 User* = object id*: string From 56420a28bda8e8cbcef9c65e31cdb4d71c29bd15 Mon Sep 17 00:00:00 2001 From: Zed Date: Mon, 6 Mar 2023 00:22:27 +0100 Subject: [PATCH 091/409] Add another error code to parseStatus --- src/parser.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser.nim b/src/parser.nim index 977d579..daf7aea 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -301,7 +301,7 @@ proc parseGlobalObjects(js: JsonNode): GlobalObjects = proc parseStatus*(js: JsonNode): Tweet = with e, js{"errors"}: - if e.getError in {tweetNotFound, tweetUnavailable, tweetCensored}: + if e.getError in {tweetNotFound, tweetUnavailable, tweetCensored, doesntExist}: return result = parseTweet(js, js{"card"}) From ad9083149c75f7bf0cf20e3433a272ef3ff55c24 Mon Sep 17 00:00:00 2001 From: Zed Date: Mon, 6 Mar 2023 11:00:27 +0100 Subject: [PATCH 092/409] Add yet another status error code --- src/parser.nim | 2 +- src/types.nim | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/parser.nim b/src/parser.nim index daf7aea..3f39c3f 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -301,7 +301,7 @@ proc parseGlobalObjects(js: JsonNode): GlobalObjects = proc parseStatus*(js: JsonNode): Tweet = with e, js{"errors"}: - if e.getError in {tweetNotFound, tweetUnavailable, tweetCensored, doesntExist}: + if e.getError in {tweetNotFound, tweetUnavailable, tweetCensored, doesntExist, tweetNotAuthorized}: return result = parseTweet(js, js{"card"}) diff --git a/src/types.nim b/src/types.nim index 573fe8c..6f742d1 100644 --- a/src/types.nim +++ b/src/types.nim @@ -44,6 +44,7 @@ type invalidToken = 89 listIdOrSlug = 112 tweetNotFound = 144 + tweetNotAuthorized = 179 forbidden = 200 badToken = 239 noCsrf = 353 From 7edc5646fee5ad5171e9f3fd0c8d7d1b79bbfbcd Mon Sep 17 00:00:00 2001 From: Zed Date: Mon, 6 Mar 2023 11:06:27 +0100 Subject: [PATCH 093/409] And another one --- src/parser.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/parser.nim b/src/parser.nim index 3f39c3f..fa877f9 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -301,7 +301,8 @@ proc parseGlobalObjects(js: JsonNode): GlobalObjects = proc parseStatus*(js: JsonNode): Tweet = with e, js{"errors"}: - if e.getError in {tweetNotFound, tweetUnavailable, tweetCensored, doesntExist, tweetNotAuthorized}: + if e.getError in {tweetNotFound, tweetUnavailable, tweetCensored, doesntExist, + tweetNotAuthorized, suspended}: return result = parseTweet(js, js{"card"}) From bc219aa792cc0e4117888b2036a969559f4f2689 Mon Sep 17 00:00:00 2001 From: Tommy Date: Mon, 6 Mar 2023 17:40:01 -0500 Subject: [PATCH 094/409] Change :ro to :Z (#735) * Change :ro to :Z * Update docker-compose.yml Co-authored-by: Mark Vainomaa --------- Co-authored-by: Mark Vainomaa --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 50ae752..1cf4e09 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,7 +8,7 @@ services: ports: - "127.0.0.1:8080:8080" # Replace with "8080:8080" if you don't use a reverse proxy volumes: - - ./nitter.conf:/src/nitter.conf:ro + - ./nitter.conf:/src/nitter.conf:Z,ro depends_on: - nitter-redis restart: unless-stopped From e93de591181f1a796da869014891f226c6125a83 Mon Sep 17 00:00:00 2001 From: Jonas Chopin-Revel <45389356+JChopinRevel@users.noreply.github.com> Date: Tue, 28 Mar 2023 15:18:16 +0200 Subject: [PATCH 095/409] Edit Dockerfile (#763) Co-authored-by: Jonas Chopin-Revel --- Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfile b/Dockerfile index f65c3ed..c100394 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,4 +20,6 @@ COPY --from=nim /src/nitter/nitter ./ COPY --from=nim /src/nitter/nitter.example.conf ./nitter.conf COPY --from=nim /src/nitter/public ./public EXPOSE 8080 +RUN adduser -h /src/ -D -s /bin/sh nitter +USER nitter CMD ./nitter From 23f4c6114c2b790405a9cefef1a7979655de44d4 Mon Sep 17 00:00:00 2001 From: Zed Date: Tue, 28 Mar 2023 16:03:15 +0200 Subject: [PATCH 096/409] Switch to BuildJet runners (#817) * Update deps * Use BuildJet runners to speed up arm64 --- .github/workflows/build-docker.yml | 34 ++++++++++++++++++++---------- nitter.nimble | 10 ++++----- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index 81d547a..c0c105e 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -8,36 +8,48 @@ on: - master jobs: - build-docker: - runs-on: ubuntu-latest + build-docker-amd64: + runs-on: buildjet-2vcpu-ubuntu-2204 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: 0 - - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - with: - platforms: all - name: Set up Docker Buildx id: buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v2 with: version: latest - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Build and push AMD64 Docker image - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v3 with: context: . file: ./Dockerfile platforms: linux/amd64 push: true tags: zedeus/nitter:latest,zedeus/nitter:${{ github.sha }} + build-docker-arm64: + runs-on: buildjet-2vcpu-ubuntu-2204-arm + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v2 + with: + version: latest + - name: Login to DockerHub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} - name: Build and push ARM64 Docker image - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v3 with: context: . file: ./Dockerfile.arm64 diff --git a/nitter.nimble b/nitter.nimble index fdc1f9f..f9aa72a 100644 --- a/nitter.nimble +++ b/nitter.nimble @@ -12,17 +12,17 @@ bin = @["nitter"] requires "nim >= 1.4.8" requires "jester#baca3f" -requires "karax#0af2c85" +requires "karax#9ee695b" requires "sass#7dfdd03" -requires "nimcrypto#b41129f" +requires "nimcrypto#4014ef9" requires "markdown#158efe3" requires "packedjson#9e6fbb6" requires "supersnappy#6c94198" requires "redpool#8b7c1db" requires "https://github.com/zedeus/redis#d0a0e6f" -requires "zippy#123cd59" -requires "flatty#9f885d7" -requires "jsony#d0e69bd" +requires "zippy#ca5989a" +requires "flatty#e668085" +requires "jsony#ea811be" # Tasks From 78cb405acd192ee9391c2739f9edeab49c3ce797 Mon Sep 17 00:00:00 2001 From: Tommy Date: Tue, 28 Mar 2023 10:20:07 -0400 Subject: [PATCH 097/409] Additional hardening for Docker-Compose (#734) --- docker-compose.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 1cf4e09..ec8ade5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,6 +17,12 @@ services: interval: 30s timeout: 5s retries: 2 + user: "998:998" + read_only: true + security_opt: + - no-new-privileges:true + cap_drop: + - ALL nitter-redis: image: redis:6-alpine @@ -30,6 +36,12 @@ services: interval: 30s timeout: 5s retries: 2 + user: "999:1000" + read_only: true + security_opt: + - no-new-privileges:true + cap_drop: + - ALL volumes: nitter-redis: From 95893eedaa2fb0ca0a0a15257d81b720f7f3eb67 Mon Sep 17 00:00:00 2001 From: Zed Date: Tue, 28 Mar 2023 17:36:48 +0200 Subject: [PATCH 098/409] Add actions workflow to run Selenium tests (#818) --- .github/workflows/build-docker.yml | 2 +- .github/workflows/run-tests.yml | 42 ++++++++++++++++++++++++++++++ tests/requirements.txt | 1 + 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/run-tests.yml create mode 100644 tests/requirements.txt diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index c0c105e..754f080 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -1,4 +1,4 @@ -name: CI/CD +name: Docker on: push: diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml new file mode 100644 index 0000000..d6315b3 --- /dev/null +++ b/.github/workflows/run-tests.yml @@ -0,0 +1,42 @@ +name: Run tests + +on: + push: + paths-ignore: + - "*.md" + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Cache nimble + id: cache-nimble + uses: actions/cache@v3 + with: + path: ~/.nimble + key: nimble-${{ hashFiles('*.nimble') }} + restore-keys: "nimble-" + - uses: actions/setup-python@v4 + with: + python-version: "3.10" + cache: "pip" + - uses: jiro4989/setup-nim-action@v1 + with: + nim-version: "1.x" + - run: nimble build -d:release -Y + - run: pip install seleniumbase + - run: seleniumbase install chromedriver + - uses: supercharge/redis-github-action@1.5.0 + - name: Prepare Nitter + run: | + sudo apt install libsass-dev -y + cp nitter.example.conf nitter.conf + nimble md + nimble scss + - name: Run tests + run: | + ./nitter & + pytest -n4 tests diff --git a/tests/requirements.txt b/tests/requirements.txt new file mode 100644 index 0000000..56ea4c0 --- /dev/null +++ b/tests/requirements.txt @@ -0,0 +1 @@ +seleniumbase From e2560dc1f14173049ea80f7b8e2d69405dfc047d Mon Sep 17 00:00:00 2001 From: Zed Date: Fri, 21 Apr 2023 01:01:18 +0200 Subject: [PATCH 099/409] Prevent search endpoint from discarding tokens --- src/apiutils.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/apiutils.nim b/src/apiutils.nim index 917932a..96ace0c 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -108,7 +108,7 @@ proc fetch*(url: Uri; api: Api): Future[JsonNode] {.async.} = updateToken() let error = result.getError - if error in {invalidToken, forbidden, badToken}: + if error in {invalidToken, badToken}: echo "fetch error: ", result.getError release(token, invalid=true) raise rateLimitError() @@ -123,7 +123,7 @@ proc fetchRaw*(url: Uri; api: Api): Future[string] {.async.} = if result.startsWith("{\"errors"): let errors = result.fromJson(Errors) - if errors in {invalidToken, forbidden, badToken}: + if errors in {invalidToken, badToken}: echo "fetch error: ", errors release(token, invalid=true) raise rateLimitError() From 1ac389e7c75ced1762aa9c698ca2899cde668424 Mon Sep 17 00:00:00 2001 From: Zed Date: Fri, 21 Apr 2023 12:41:30 +0000 Subject: [PATCH 100/409] GraphQL timeline (#812) * Update deps * Replace profile timeline with GraphQL endpoint * Update GraphQL endpoint versions * Use GraphQL for profile media tab * Fix UserByRestId request * Improve routing, fixes #814 * Fix token pool JSON * Deduplicate GraphQL timeline endpoints * Update list endpoints * Use GraphQL for list tweets * Remove debug leftover * Replace old pinned tweet endpoint with GraphQL * Validate tweet ID * Minor token handling fix * Hide US-only commerce cards * Update config example * Remove http pool and gzip from token pool * Support tombstoned tweets in threads * Retry GraphQL timeout errors * Remove unnecessary 401 retry * Remove broken timeout retry * Update karax, use new bool attribute feature * Update card test * Fix odd edgecase with broken retweets * Replace search endpoints, switch Bearer token * Only parse user search if it's a list * Fix quoted tweet crash * Fix empty search query handling * Fix invalid user search errors again --- nitter.example.conf | 18 +-- nitter.nimble | 2 +- src/api.nim | 156 +++++++++++++----------- src/apiutils.nim | 20 ++- src/consts.nim | 129 ++++++++++++-------- src/experimental/parser.nim | 4 +- src/experimental/parser/graphql.nim | 1 + src/experimental/parser/timeline.nim | 30 ----- src/experimental/parser/unifiedcard.nim | 2 + src/experimental/parser/user.nim | 11 +- src/experimental/types/graphuser.nim | 1 + src/experimental/types/unifiedcard.nim | 6 +- src/http_pool.nim | 6 + src/nitter.nim | 14 ++- src/parser.nim | 134 ++++++++++++++++---- src/parserutils.nim | 2 +- src/redis_cache.nim | 2 +- src/routes/list.nim | 3 +- src/routes/rss.nim | 6 +- src/routes/search.nim | 8 +- src/routes/status.nim | 10 +- src/routes/timeline.nim | 15 +-- src/tokens.nim | 29 ++--- src/types.nim | 18 ++- src/views/renderutils.nim | 25 ++-- src/views/search.nim | 10 +- src/views/tweet.nim | 23 ++-- tests/test_card.py | 10 +- tests/test_profile.py | 11 -- 29 files changed, 405 insertions(+), 301 deletions(-) delete mode 100644 src/experimental/parser/timeline.nim diff --git a/nitter.example.conf b/nitter.example.conf index a7abea8..0d4deb7 100644 --- a/nitter.example.conf +++ b/nitter.example.conf @@ -1,11 +1,11 @@ [Server] +hostname = "nitter.net" # for generating links, change this to your own domain/ip +title = "nitter" address = "0.0.0.0" port = 8080 https = false # disable to enable cookies when not using https httpMaxConnections = 100 staticDir = "./public" -title = "nitter" -hostname = "nitter.net" [Cache] listMinutes = 240 # how long to cache list info (not the tweets, so keep it high) @@ -13,9 +13,9 @@ rssMinutes = 10 # how long to cache rss queries redisHost = "localhost" # Change to "nitter-redis" if using docker-compose redisPort = 6379 redisPassword = "" -redisConnections = 20 # connection pool size +redisConnections = 20 # minimum open connections in pool redisMaxConnections = 30 -# max, new connections are opened when none are available, but if the pool size +# new connections are opened when none are available, but if the pool size # goes above this, they're closed when released. don't worry about this unless # you receive tons of requests per second @@ -23,15 +23,15 @@ redisMaxConnections = 30 hmacKey = "secretkey" # random key for cryptographic signing of video urls base64Media = false # use base64 encoding for proxied media urls enableRSS = true # set this to false to disable RSS feeds -enableDebug = false # enable request logs and debug endpoints +enableDebug = false # enable request logs and debug endpoints (/.tokens) proxy = "" # http/https url, SOCKS proxies are not supported proxyAuth = "" tokenCount = 10 # minimum amount of usable tokens. tokens are used to authorize API requests, -# but they expire after ~1 hour, and have a limit of 187 requests. -# the limit gets reset every 15 minutes, and the pool is filled up so there's -# always at least $tokenCount usable tokens. again, only increase this if -# you receive major bursts all the time +# but they expire after ~1 hour, and have a limit of 500 requests per endpoint. +# the limits reset every 15 minutes, and the pool is filled up so there's +# always at least `tokenCount` usable tokens. only increase this if you receive +# major bursts all the time and don't have a rate limiting setup via e.g. nginx # Change default preferences here, see src/prefs_impl.nim for a complete list [Preferences] diff --git a/nitter.nimble b/nitter.nimble index f9aa72a..7771b31 100644 --- a/nitter.nimble +++ b/nitter.nimble @@ -12,7 +12,7 @@ bin = @["nitter"] requires "nim >= 1.4.8" requires "jester#baca3f" -requires "karax#9ee695b" +requires "karax#5cf360c" requires "sass#7dfdd03" requires "nimcrypto#4014ef9" requires "markdown#158efe3" diff --git a/src/api.nim b/src/api.nim index dfcf413..b23aa87 100644 --- a/src/api.nim +++ b/src/api.nim @@ -7,106 +7,82 @@ import experimental/parser as newParser proc getGraphUser*(username: string): Future[User] {.async.} = if username.len == 0: return let - variables = """{ - "screen_name": "$1", - "withSafetyModeUserFields": false, - "withSuperFollowsUserFields": false - }""" % [username] - js = await fetchRaw(graphUser ? {"variables": variables}, Api.userScreenName) + variables = %*{"screen_name": username} + params = {"variables": $variables, "features": gqlFeatures} + js = await fetchRaw(graphUser ? params, Api.userScreenName) result = parseGraphUser(js) proc getGraphUserById*(id: string): Future[User] {.async.} = if id.len == 0 or id.any(c => not c.isDigit): return let - variables = """{"userId": "$1", "withSuperFollowsUserFields": true}""" % [id] - js = await fetchRaw(graphUserById ? {"variables": variables}, Api.userRestId) + variables = %*{"userId": id} + params = {"variables": $variables, "features": gqlFeatures} + js = await fetchRaw(graphUserById ? params, Api.userRestId) result = parseGraphUser(js) +proc getGraphUserTweets*(id: string; kind: TimelineKind; after=""): Future[Timeline] {.async.} = + if id.len == 0: return + let + cursor = if after.len > 0: "\"cursor\":\"$1\"," % after else: "" + variables = userTweetsVariables % [id, cursor] + params = {"variables": variables, "features": gqlFeatures} + (url, apiId) = case kind + of TimelineKind.tweets: (graphUserTweets, Api.userTweets) + of TimelineKind.replies: (graphUserTweetsAndReplies, Api.userTweetsAndReplies) + of TimelineKind.media: (graphUserMedia, Api.userMedia) + js = await fetch(url ? params, apiId) + result = parseGraphTimeline(js, "user", after) + +proc getGraphListTweets*(id: string; after=""): Future[Timeline] {.async.} = + if id.len == 0: return + let + cursor = if after.len > 0: "\"cursor\":\"$1\"," % after else: "" + variables = listTweetsVariables % [id, cursor] + params = {"variables": variables, "features": gqlFeatures} + js = await fetch(graphListTweets ? params, Api.listTweets) + result = parseGraphTimeline(js, "list", after) + proc getGraphListBySlug*(name, list: string): Future[List] {.async.} = let - variables = %*{"screenName": name, "listSlug": list, "withHighlightedLabel": false} - url = graphListBySlug ? {"variables": $variables} - result = parseGraphList(await fetch(url, Api.listBySlug)) + variables = %*{"screenName": name, "listSlug": list} + params = {"variables": $variables, "features": gqlFeatures} + result = parseGraphList(await fetch(graphListBySlug ? params, Api.listBySlug)) proc getGraphList*(id: string): Future[List] {.async.} = let - variables = %*{"listId": id, "withHighlightedLabel": false} - url = graphList ? {"variables": $variables} - result = parseGraphList(await fetch(url, Api.list)) + variables = %*{"listId": id} + params = {"variables": $variables, "features": gqlFeatures} + result = parseGraphList(await fetch(graphListById ? params, Api.list)) proc getGraphListMembers*(list: List; after=""): Future[Result[User]] {.async.} = if list.id.len == 0: return var variables = %*{ "listId": list.id, - "withSuperFollowsUserFields": false, "withBirdwatchPivots": false, "withDownvotePerspective": false, "withReactionsMetadata": false, - "withReactionsPerspective": false, - "withSuperFollowsTweetFields": false + "withReactionsPerspective": false } if after.len > 0: variables["cursor"] = % after - let url = graphListMembers ? {"variables": $variables} + let url = graphListMembers ? {"variables": $variables, "features": gqlFeatures} result = parseGraphListMembers(await fetchRaw(url, Api.listMembers), after) -proc getListTimeline*(id: string; after=""): Future[Timeline] {.async.} = +proc getGraphTweetResult*(id: string): Future[Tweet] {.async.} = if id.len == 0: return let - ps = genParams({"list_id": id, "ranking_mode": "reverse_chronological"}, after) - url = listTimeline ? ps - result = parseTimeline(await fetch(url, Api.timeline), after) - -proc getTimeline*(id: string; after=""; replies=false): Future[Timeline] {.async.} = - if id.len == 0: return - let - ps = genParams({"userId": id, "include_tweet_replies": $replies}, after) - url = timeline / (id & ".json") ? ps - result = parseTimeline(await fetch(url, Api.timeline), after) - -proc getMediaTimeline*(id: string; after=""): Future[Timeline] {.async.} = - if id.len == 0: return - let url = mediaTimeline / (id & ".json") ? genParams(cursor=after) - result = parseTimeline(await fetch(url, Api.timeline), after) - -proc getPhotoRail*(name: string): Future[PhotoRail] {.async.} = - if name.len == 0: return - let - ps = genParams({"screen_name": name, "trim_user": "true"}, - count="18", ext=false) - url = photoRail ? ps - result = parsePhotoRail(await fetch(url, Api.timeline)) - -proc getSearch*[T](query: Query; after=""): Future[Result[T]] {.async.} = - when T is User: - const - searchMode = ("result_filter", "user") - parse = parseUsers - fetchFunc = fetchRaw - else: - const - searchMode = ("tweet_search_mode", "live") - parse = parseTimeline - fetchFunc = fetch - - let q = genQueryParam(query) - if q.len == 0 or q == emptyQuery: - return Result[T](beginning: true, query: query) - - let url = search ? genParams(searchParams & @[("q", q), searchMode], after) - try: - result = parse(await fetchFunc(url, Api.search), after) - result.query = query - except InternalError: - return Result[T](beginning: true, query: query) + variables = tweetResultVariables % id + params = {"variables": variables, "features": gqlFeatures} + js = await fetch(graphTweetResult ? params, Api.tweetResult) + result = parseGraphTweetResult(js) proc getGraphTweet(id: string; after=""): Future[Conversation] {.async.} = if id.len == 0: return let cursor = if after.len > 0: "\"cursor\":\"$1\"," % after else: "" variables = tweetVariables % [id, cursor] - params = {"variables": variables, "features": tweetFeatures} + params = {"variables": variables, "features": gqlFeatures} js = await fetch(graphTweet ? params, Api.tweetDetail) result = parseGraphConversation(js, id) @@ -119,9 +95,51 @@ proc getTweet*(id: string; after=""): Future[Conversation] {.async.} = if after.len > 0: result.replies = await getReplies(id, after) -proc getStatus*(id: string): Future[Tweet] {.async.} = - let url = status / (id & ".json") ? genParams() - result = parseStatus(await fetch(url, Api.status)) +proc getGraphSearch*(query: Query; after=""): Future[Result[Tweet]] {.async.} = + let q = genQueryParam(query) + if q.len == 0 or q == emptyQuery: + return Result[Tweet](query: query, beginning: true) + + var + variables = %*{ + "rawQuery": q, + "count": 20, + "product": "Latest", + "withDownvotePerspective": false, + "withReactionsMetadata": false, + "withReactionsPerspective": false + } + if after.len > 0: + variables["cursor"] = % after + let url = graphSearchTimeline ? {"variables": $variables, "features": gqlFeatures} + result = parseGraphSearch(await fetch(url, Api.search), after) + result.query = query + +proc getUserSearch*(query: Query; page="1"): Future[Result[User]] {.async.} = + if query.text.len == 0: + return Result[User](query: query, beginning: true) + + var url = userSearch ? { + "q": query.text, + "skip_status": "1", + "count": "20", + "page": page + } + + result = parseUsers(await fetchRaw(url, Api.userSearch)) + result.query = query + if page.len == 0: + result.bottom = "2" + elif page.allCharsInSet(Digits): + result.bottom = $(parseInt(page) + 1) + +proc getPhotoRail*(name: string): Future[PhotoRail] {.async.} = + if name.len == 0: return + let + ps = genParams({"screen_name": name, "trim_user": "true"}, + count="18", ext=false) + url = photoRail ? ps + result = parsePhotoRail(await fetch(url, Api.timeline)) proc resolve*(url: string; prefs: Prefs): Future[string] {.async.} = let client = newAsyncHttpClient(maxRedirects=0) diff --git a/src/apiutils.nim b/src/apiutils.nim index 96ace0c..dbc6cca 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -17,8 +17,8 @@ proc genParams*(pars: openArray[(string, string)] = @[]; cursor=""; result &= p if ext: result &= ("ext", "mediaStats") - result &= ("include_ext_alt_text", "true") - result &= ("include_ext_media_availability", "true") + result &= ("include_ext_alt_text", "1") + result &= ("include_ext_media_availability", "1") if count.len > 0: result &= ("count", count) if cursor.len > 0: @@ -44,7 +44,7 @@ proc genHeaders*(token: Token = nil): HttpHeaders = }) template updateToken() = - if api != Api.search and resp.headers.hasKey(rlRemaining): + if resp.headers.hasKey(rlRemaining): let remaining = parseInt(resp.headers[rlRemaining]) reset = parseInt(resp.headers[rlReset]) @@ -67,14 +67,9 @@ template fetchImpl(result, fetchBody) {.dirty.} = getContent() - # Twitter randomly returns 401 errors with an empty body quite often. - # Retrying the request usually works. - if resp.status == "401 Unauthorized" and result.len == 0: - getContent() - - if resp.status == $Http503: - badClient = true - raise newException(InternalError, result) + if resp.status == $Http503: + badClient = true + raise newException(BadClientError, "Bad client") if result.len > 0: if resp.headers.getOrDefault("content-encoding") == "gzip": @@ -90,6 +85,9 @@ template fetchImpl(result, fetchBody) {.dirty.} = raise newException(InternalError, $url) except InternalError as e: raise e + except BadClientError as e: + release(token, used=true) + raise e except Exception as e: echo "error: ", e.name, ", msg: ", e.msg, ", token: ", token[], ", url: ", url if "length" notin e.msg and "descriptor" notin e.msg: diff --git a/src/consts.nim b/src/consts.nim index bb4e1a3..27e82f9 100644 --- a/src/consts.nim +++ b/src/consts.nim @@ -1,30 +1,28 @@ # SPDX-License-Identifier: AGPL-3.0-only -import uri, sequtils +import uri, sequtils, strutils const - auth* = "Bearer AAAAAAAAAAAAAAAAAAAAAPYXBAAAAAAACLXUNDekMxqa8h%2F40K4moUkGsoc%3DTYfbDKbT3jJPCEVnMYqilB28NHfOPqkca3qaAxGfsyKCs0wRbw" + auth* = "Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA" api = parseUri("https://api.twitter.com") activate* = $(api / "1.1/guest/activate.json") - userShow* = api / "1.1/users/show.json" photoRail* = api / "1.1/statuses/media_timeline.json" - status* = api / "1.1/statuses/show" - search* = api / "2/search/adaptive.json" - - timelineApi = api / "2/timeline" - timeline* = timelineApi / "profile" - mediaTimeline* = timelineApi / "media" - listTimeline* = timelineApi / "list.json" - tweet* = timelineApi / "conversation" + userSearch* = api / "1.1/users/search.json" graphql = api / "graphql" - graphTweet* = graphql / "6lWNh96EXDJCXl05SAtn_g/TweetDetail" - graphUser* = graphql / "7mjxD3-C6BxitPMVQ6w0-Q/UserByScreenName" - graphUserById* = graphql / "I5nvpI91ljifos1Y3Lltyg/UserByRestId" - graphList* = graphql / "JADTh6cjebfgetzvF3tQvQ/List" - graphListBySlug* = graphql / "ErWsz9cObLel1BF-HjuBlA/ListBySlug" - graphListMembers* = graphql / "Ke6urWMeCV2UlKXGRy4sow/ListMembers" + graphUser* = graphql / "8mPfHBetXOg-EHAyeVxUoA/UserByScreenName" + graphUserById* = graphql / "nI8WydSd-X-lQIVo6bdktQ/UserByRestId" + graphUserTweets* = graphql / "9rys0A7w1EyqVd2ME0QCJg/UserTweets" + graphUserTweetsAndReplies* = graphql / "ehMCHF3Mkgjsfz_aImqOsg/UserTweetsAndReplies" + graphUserMedia* = graphql / "MA_EP2a21zpzNWKRkaPBMg/UserMedia" + graphTweet* = graphql / "6I7Hm635Q6ftv69L8VrSeQ/TweetDetail" + graphTweetResult* = graphql / "rt-rHeSJ-2H9O9gxWQcPcg/TweetResultByRestId" + graphSearchTimeline* = graphql / "gkjsKepM6gl_HmFWoWKfgg/SearchTimeline" + graphListById* = graphql / "iTpgCtbdxrsJfyx0cFjHqg/ListByRestId" + graphListBySlug* = graphql / "-kmqNvm5Y-cVrfvBy6docg/ListBySlug" + graphListMembers* = graphql / "P4NpVZDqUD_7MEM84L-8nw/ListMembers" + graphListTweets* = graphql / "jZntL0oVJSdjhmPcdbw_eA/ListLatestTweetsTimeline" timelineParams* = { "include_profile_interstitial_type": "0", @@ -35,58 +33,85 @@ const "include_mute_edge": "0", "include_can_dm": "0", "include_can_media_tag": "1", + "include_ext_is_blue_verified": "1", "skip_status": "1", "cards_platform": "Web-12", "include_cards": "1", - "include_composer_source": "false", + "include_composer_source": "0", "include_reply_count": "1", "tweet_mode": "extended", - "include_entities": "true", - "include_user_entities": "true", - "include_ext_media_color": "false", - "send_error_codes": "true", - "simple_quoted_tweet": "true", - "include_quote_count": "true" + "include_entities": "1", + "include_user_entities": "1", + "include_ext_media_color": "0", + "send_error_codes": "1", + "simple_quoted_tweet": "1", + "include_quote_count": "1" }.toSeq - searchParams* = { - "query_source": "typed_query", - "pc": "1", - "spelling_corrections": "1" - }.toSeq - ## top: nothing - ## latest: "tweet_search_mode: live" - ## user: "result_filter: user" - ## photos: "result_filter: photos" - ## videos: "result_filter: videos" + gqlFeatures* = """{ + "blue_business_profile_image_shape_enabled": false, + "freedom_of_speech_not_reach_fetch_enabled": false, + "graphql_is_translatable_rweb_tweet_is_translatable_enabled": false, + "interactive_text_enabled": false, + "longform_notetweets_consumption_enabled": true, + "longform_notetweets_richtext_consumption_enabled": true, + "longform_notetweets_rich_text_read_enabled": false, + "responsive_web_edit_tweet_api_enabled": false, + "responsive_web_enhance_cards_enabled": false, + "responsive_web_graphql_exclude_directive_enabled": true, + "responsive_web_graphql_skip_user_profile_image_extensions_enabled": false, + "responsive_web_graphql_timeline_navigation_enabled": false, + "responsive_web_text_conversations_enabled": false, + "responsive_web_twitter_blue_verified_badge_is_enabled": true, + "spaces_2022_h2_clipping": true, + "spaces_2022_h2_spaces_communities": true, + "standardized_nudges_misinfo": false, + "tweet_awards_web_tipping_enabled": false, + "tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled": false, + "tweetypie_unmention_optimization_enabled": false, + "verified_phone_label_enabled": false, + "vibe_api_enabled": false, + "view_counts_everywhere_api_enabled": false +}""".replace(" ", "").replace("\n", "") tweetVariables* = """{ "focalTweetId": "$1", $2 - "includePromotedContent": false, "withBirdwatchNotes": false, + "includePromotedContent": false, + "withDownvotePerspective": false, + "withReactionsMetadata": false, + "withReactionsPerspective": false, + "withVoice": false +}""" + + tweetResultVariables* = """{ + "tweetId": "$1", + "includePromotedContent": false, + "withDownvotePerspective": false, + "withReactionsMetadata": false, + "withReactionsPerspective": false, + "withVoice": false, + "withCommunity": false +}""" + + userTweetsVariables* = """{ + "userId": "$1", $2 + "count": 20, + "includePromotedContent": false, "withDownvotePerspective": false, "withReactionsMetadata": false, "withReactionsPerspective": false, - "withSuperFollowsTweetFields": false, - "withSuperFollowsUserFields": false, "withVoice": false, "withV2Timeline": true }""" - tweetFeatures* = """{ - "graphql_is_translatable_rweb_tweet_is_translatable_enabled": false, - "responsive_web_graphql_timeline_navigation_enabled": false, - "standardized_nudges_misinfo": false, - "verified_phone_label_enabled": false, - "responsive_web_twitter_blue_verified_badge_is_enabled": false, - "tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled": false, - "view_counts_everywhere_api_enabled": false, - "responsive_web_edit_tweet_api_enabled": false, - "tweetypie_unmention_optimization_enabled": false, - "vibe_api_enabled": false, - "longform_notetweets_consumption_enabled": true, - "responsive_web_text_conversations_enabled": false, - "responsive_web_enhance_cards_enabled": false, - "interactive_text_enabled": false + listTweetsVariables* = """{ + "listId": "$1", $2 + "count": 20, + "includePromotedContent": false, + "withDownvotePerspective": false, + "withReactionsMetadata": false, + "withReactionsPerspective": false, + "withVoice": false }""" diff --git a/src/experimental/parser.nim b/src/experimental/parser.nim index 98ce7df..40986f5 100644 --- a/src/experimental/parser.nim +++ b/src/experimental/parser.nim @@ -1,2 +1,2 @@ -import parser/[user, graphql, timeline] -export user, graphql, timeline +import parser/[user, graphql] +export user, graphql diff --git a/src/experimental/parser/graphql.nim b/src/experimental/parser/graphql.nim index 4431db3..36014e3 100644 --- a/src/experimental/parser/graphql.nim +++ b/src/experimental/parser/graphql.nim @@ -11,6 +11,7 @@ proc parseGraphUser*(json: string): User = result = toUser raw.data.user.result.legacy result.id = raw.data.user.result.restId + result.verified = result.verified or raw.data.user.result.isBlueVerified proc parseGraphListMembers*(json, cursor: string): Result[User] = result = Result[User]( diff --git a/src/experimental/parser/timeline.nim b/src/experimental/parser/timeline.nim deleted file mode 100644 index 4663d00..0000000 --- a/src/experimental/parser/timeline.nim +++ /dev/null @@ -1,30 +0,0 @@ -import std/[strutils, tables] -import jsony -import user, ../types/timeline -from ../../types import Result, User - -proc getId(id: string): string {.inline.} = - let start = id.rfind("-") - if start < 0: return id - id[start + 1 ..< id.len] - -proc parseUsers*(json: string; after=""): Result[User] = - result = Result[User](beginning: after.len == 0) - - let raw = json.fromJson(Search) - if raw.timeline.instructions.len == 0: - return - - for i in raw.timeline.instructions: - if i.addEntries.entries.len > 0: - for e in i.addEntries.entries: - let id = e.entryId.getId - if e.entryId.startsWith("user"): - if id in raw.globalObjects.users: - result.content.add toUser raw.globalObjects.users[id] - elif e.entryId.startsWith("cursor"): - let cursor = e.content.operation.cursor - if cursor.cursorType == "Top": - result.top = cursor.value - elif cursor.cursorType == "Bottom": - result.bottom = cursor.value diff --git a/src/experimental/parser/unifiedcard.nim b/src/experimental/parser/unifiedcard.nim index 3c5158a..c9af437 100644 --- a/src/experimental/parser/unifiedcard.nim +++ b/src/experimental/parser/unifiedcard.nim @@ -84,6 +84,8 @@ proc parseUnifiedCard*(json: string): Card = component.parseMedia(card, result) of buttonGroup: discard + of ComponentType.hidden: + result.kind = CardKind.hidden of ComponentType.unknown: echo "ERROR: Unknown component type: ", json diff --git a/src/experimental/parser/user.nim b/src/experimental/parser/user.nim index 715c9a9..b4d710f 100644 --- a/src/experimental/parser/user.nim +++ b/src/experimental/parser/user.nim @@ -2,7 +2,7 @@ import std/[algorithm, unicode, re, strutils, strformat, options, nre] import jsony import utils, slices import ../types/user as userType -from ../../types import User, Error +from ../../types import Result, User, Error let unRegex = re.re"(^|[^A-z0-9-_./?])@([A-z0-9_]{1,15})" @@ -76,3 +76,12 @@ proc parseUser*(json: string; username=""): User = else: echo "[error - parseUser]: ", error result = toUser json.fromJson(RawUser) + +proc parseUsers*(json: string; after=""): Result[User] = + result = Result[User](beginning: after.len == 0) + + # starting with '{' means it's an error + if json[0] == '[': + let raw = json.fromJson(seq[RawUser]) + for user in raw: + result.content.add user.toUser diff --git a/src/experimental/types/graphuser.nim b/src/experimental/types/graphuser.nim index e13383a..478e7f3 100644 --- a/src/experimental/types/graphuser.nim +++ b/src/experimental/types/graphuser.nim @@ -11,4 +11,5 @@ type UserResult = object legacy*: RawUser restId*: string + isBlueVerified*: bool reason*: Option[string] diff --git a/src/experimental/types/unifiedcard.nim b/src/experimental/types/unifiedcard.nim index 4ec587c..6e83cad 100644 --- a/src/experimental/types/unifiedcard.nim +++ b/src/experimental/types/unifiedcard.nim @@ -17,6 +17,7 @@ type twitterListDetails communityDetails mediaWithDetailsHorizontal + hidden unknown Component* = object @@ -71,11 +72,11 @@ type Text = object content: string - HasTypeField = Component | Destination | MediaEntity | AppStoreData + TypeField = Component | Destination | MediaEntity | AppStoreData converter fromText*(text: Text): string = text.content -proc renameHook*(v: var HasTypeField; fieldName: var string) = +proc renameHook*(v: var TypeField; fieldName: var string) = if fieldName == "type": fieldName = "kind" @@ -89,6 +90,7 @@ proc enumHook*(s: string; v: var ComponentType) = of "twitter_list_details": twitterListDetails of "community_details": communityDetails of "media_with_details_horizontal": mediaWithDetailsHorizontal + of "commerce_drop_details": hidden else: echo "ERROR: Unknown enum value (ComponentType): ", s; unknown proc enumHook*(s: string; v: var AppType) = diff --git a/src/http_pool.nim b/src/http_pool.nim index 2037520..b4e3cee 100644 --- a/src/http_pool.nim +++ b/src/http_pool.nim @@ -42,5 +42,11 @@ template use*(pool: HttpPool; heads: HttpHeaders; body: untyped): untyped = except ProtocolError: # Twitter closed the connection, retry body + except BadClientError: + # Twitter returned 503, we need a new client + pool.release(c, true) + badClient = false + c = pool.acquire(heads) + body finally: pool.release(c, badClient) diff --git a/src/nitter.nim b/src/nitter.nim index 2e868a4..627af75 100644 --- a/src/nitter.nim +++ b/src/nitter.nim @@ -85,19 +85,23 @@ routes: resp Http500, showError( &"An error occurred, please {link} with the URL you tried to visit.", cfg) + error BadClientError: + echo error.exc.name, ": ", error.exc.msg + resp Http500, showError("Network error occured, please try again.", cfg) + error RateLimitError: const link = a("another instance", href = instancesUrl) resp Http429, showError( &"Instance has been rate limited.
Use {link} or try again later.", cfg) - extend unsupported, "" - extend preferences, "" - extend resolver, "" extend rss, "" + extend status, "" extend search, "" extend timeline, "" - extend list, "" - extend status, "" extend media, "" + extend list, "" + extend preferences, "" + extend resolver, "" extend embed, "" extend debug, "" + extend unsupported, "" diff --git a/src/parser.nim b/src/parser.nim index fa877f9..fe2fe5b 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -4,6 +4,8 @@ import packedjson, packedjson/deserialiser import types, parserutils, utils import experimental/parser/unifiedcard +proc parseGraphTweet(js: JsonNode): Tweet + proc parseUser(js: JsonNode; id=""): User = if js.isNull: return result = User( @@ -19,13 +21,20 @@ proc parseUser(js: JsonNode; id=""): User = tweets: js{"statuses_count"}.getInt, likes: js{"favourites_count"}.getInt, media: js{"media_count"}.getInt, - verified: js{"verified"}.getBool, + verified: js{"verified"}.getBool or js{"ext_is_blue_verified"}.getBool, protected: js{"protected"}.getBool, joinDate: js{"created_at"}.getTime ) result.expandUserEntities(js) +proc parseGraphUser(js: JsonNode): User = + let user = ? js{"user_results", "result"} + result = parseUser(user{"legacy"}) + + if "is_blue_verified" in user: + result.verified = true + proc parseGraphList*(js: JsonNode): List = if js.isNull: return @@ -38,11 +47,11 @@ proc parseGraphList*(js: JsonNode): List = result = List( id: list{"id_str"}.getStr, name: list{"name"}.getStr, - username: list{"user", "legacy", "screen_name"}.getStr, - userId: list{"user", "rest_id"}.getStr, + username: list{"user_results", "result", "legacy", "screen_name"}.getStr, + userId: list{"user_results", "result", "rest_id"}.getStr, description: list{"description"}.getStr, members: list{"member_count"}.getInt, - banner: list{"custom_banner_media", "media_info", "url"}.getImageStr + banner: list{"custom_banner_media", "media_info", "original_img_url"}.getImageStr ) proc parsePoll(js: JsonNode): Poll = @@ -213,10 +222,18 @@ proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet = if js{"is_quote_status"}.getBool: result.quote = some Tweet(id: js{"quoted_status_id_str"}.getId) + # legacy with rt, js{"retweeted_status_id_str"}: result.retweet = some Tweet(id: rt.getId) return + # graphql + with rt, js{"retweeted_status_result", "result"}: + # needed due to weird edgecase where the actual tweet data isn't included + if "legacy" in rt: + result.retweet = some parseGraphTweet(rt) + return + if jsCard.kind != JNull: let name = jsCard{"name"}.getStr if "poll" in name: @@ -237,7 +254,10 @@ proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet = of "video": result.video = some(parseVideo(m)) with user, m{"additional_media_info", "source_user"}: - result.attribution = some(parseUser(user)) + if user{"id"}.getInt > 0: + result.attribution = some(parseUser(user)) + else: + result.attribution = some(parseGraphUser(user)) of "animated_gif": result.gif = some(parseGif(m)) else: discard @@ -299,19 +319,6 @@ proc parseGlobalObjects(js: JsonNode): GlobalObjects = tweet.user = result.users[tweet.user.id] result.tweets[k] = tweet -proc parseStatus*(js: JsonNode): Tweet = - with e, js{"errors"}: - if e.getError in {tweetNotFound, tweetUnavailable, tweetCensored, doesntExist, - tweetNotAuthorized, suspended}: - return - - result = parseTweet(js, js{"card"}) - if not result.isNil: - result.user = parseUser(js{"user"}) - - with quote, js{"quoted_status"}: - result.quote = some parseStatus(js{"quoted_status"}) - proc parseInstructions[T](res: var Result[T]; global: GlobalObjects; js: JsonNode) = if js.kind != JArray or js.len == 0: return @@ -352,7 +359,7 @@ proc parseTimeline*(js: JsonNode; after=""): Timeline = result.top = e.getCursor elif "cursor-bottom" in entry: result.bottom = e.getCursor - elif entry.startsWith("sq-C"): + elif entry.startsWith("sq-cursor"): with cursor, e{"content", "operation", "cursor"}: if cursor{"cursorType"}.getStr == "Bottom": result.bottom = cursor{"value"}.getStr @@ -373,9 +380,20 @@ proc parsePhotoRail*(js: JsonNode): PhotoRail = result.add GalleryPhoto(url: url, tweetId: $t.id) proc parseGraphTweet(js: JsonNode): Tweet = - if js.kind == JNull or js{"__typename"}.getStr == "TweetUnavailable": + if js.kind == JNull: return Tweet(available: false) + case js{"__typename"}.getStr + of "TweetUnavailable": + return Tweet(available: false) + of "TweetTombstone": + return Tweet( + available: false, + text: js{"tombstone", "text"}.getTombstone + ) + of "TweetWithVisibilityResults": + return parseGraphTweet(js{"tweet"}) + var jsCard = copy(js{"card", "legacy"}) if jsCard.kind != JNull: var values = newJObject() @@ -384,7 +402,7 @@ proc parseGraphTweet(js: JsonNode): Tweet = jsCard["binding_values"] = values result = parseTweet(js{"legacy"}, jsCard) - result.user = parseUser(js{"core", "user_results", "result", "legacy"}) + result.user = parseGraphUser(js{"core"}) with noteTweet, js{"note_tweet", "note_tweet_results", "result"}: result.expandNoteTweetEntities(noteTweet) @@ -407,10 +425,14 @@ proc parseGraphThread(js: JsonNode): tuple[thread: Chain; self: bool] = if t{"item", "itemContent", "tweetDisplayType"}.getStr == "SelfThread": result.self = true +proc parseGraphTweetResult*(js: JsonNode): Tweet = + with tweet, js{"data", "tweetResult", "result"}: + result = parseGraphTweet(tweet) + proc parseGraphConversation*(js: JsonNode; tweetId: string): Conversation = result = Conversation(replies: Result[Chain](beginning: true)) - let instructions = ? js{"data", "threaded_conversation_with_injections_v2", "instructions"} + let instructions = ? js{"data", "threaded_conversation_with_injections", "instructions"} if instructions.len == 0: return @@ -418,12 +440,25 @@ proc parseGraphConversation*(js: JsonNode; tweetId: string): Conversation = let entryId = e{"entryId"}.getStr # echo entryId if entryId.startsWith("tweet"): - let tweet = parseGraphTweet(e{"content", "itemContent", "tweet_results", "result"}) + with tweetResult, e{"content", "itemContent", "tweet_results", "result"}: + let tweet = parseGraphTweet(tweetResult) - if not tweet.available: - tweet.id = parseBiggestInt(entryId.getId()) + if not tweet.available: + tweet.id = parseBiggestInt(entryId.getId()) - if $tweet.id == tweetId: + if $tweet.id == tweetId: + result.tweet = tweet + else: + result.before.content.add tweet + elif entryId.startsWith("tombstone"): + let id = entryId.getId() + let tweet = Tweet( + id: parseBiggestInt(id), + available: false, + text: e{"content", "itemContent", "tombstoneInfo", "richText"}.getTombstone + ) + + if id == tweetId: result.tweet = tweet else: result.before.content.add tweet @@ -435,3 +470,50 @@ proc parseGraphConversation*(js: JsonNode; tweetId: string): Conversation = result.replies.content.add thread elif entryId.startsWith("cursor-bottom"): result.replies.bottom = e{"content", "itemContent", "value"}.getStr + +proc parseGraphTimeline*(js: JsonNode; root: string; after=""): Timeline = + result = Timeline(beginning: after.len == 0) + + let instructions = + if root == "list": ? js{"data", "list", "tweets_timeline", "timeline", "instructions"} + else: ? js{"data", "user", "result", "timeline_v2", "timeline", "instructions"} + + if instructions.len == 0: + return + + for i in instructions: + if i{"type"}.getStr == "TimelineAddEntries": + for e in i{"entries"}: + let entryId = e{"entryId"}.getStr + if entryId.startsWith("tweet"): + with tweetResult, e{"content", "itemContent", "tweet_results", "result"}: + let tweet = parseGraphTweet(tweetResult) + if not tweet.available: + tweet.id = parseBiggestInt(entryId.getId()) + result.content.add tweet + elif entryId.startsWith("cursor-bottom"): + result.bottom = e{"content", "value"}.getStr + +proc parseGraphSearch*(js: JsonNode; after=""): Timeline = + result = Timeline(beginning: after.len == 0) + + let instructions = js{"data", "search_by_raw_query", "search_timeline", "timeline", "instructions"} + if instructions.len == 0: + return + + for instruction in instructions: + let typ = instruction{"type"}.getStr + if typ == "TimelineAddEntries": + for e in instructions[0]{"entries"}: + let entryId = e{"entryId"}.getStr + if entryId.startsWith("tweet"): + with tweetResult, e{"content", "itemContent", "tweet_results", "result"}: + let tweet = parseGraphTweet(tweetResult) + if not tweet.available: + tweet.id = parseBiggestInt(entryId.getId()) + result.content.add tweet + elif entryId.startsWith("cursor-bottom"): + result.bottom = e{"content", "value"}.getStr + elif typ == "TimelineReplaceEntry": + if instruction{"entry_id_to_replace"}.getStr.startsWith("cursor-bottom"): + result.bottom = instruction{"entry", "content", "value"}.getStr diff --git a/src/parserutils.nim b/src/parserutils.nim index 8ae9cd0..f28bd52 100644 --- a/src/parserutils.nim +++ b/src/parserutils.nim @@ -130,7 +130,7 @@ proc getBanner*(js: JsonNode): string = return proc getTombstone*(js: JsonNode): string = - result = js{"tombstoneInfo", "richText", "text"}.getStr + result = js{"text"}.getStr result.removeSuffix(" Learn more") proc getMp4Resolution*(url: string): int = diff --git a/src/redis_cache.nim b/src/redis_cache.nim index 742b7ae..89161be 100644 --- a/src/redis_cache.nim +++ b/src/redis_cache.nim @@ -153,7 +153,7 @@ proc getCachedTweet*(id: int64): Future[Tweet] {.async.} = if tweet != redisNil: tweet.deserialize(Tweet) else: - result = await getStatus($id) + result = await getGraphTweetResult($id) if not result.isNil: await cache(result) diff --git a/src/routes/list.nim b/src/routes/list.nim index c97b1c1..ac3e97e 100644 --- a/src/routes/list.nim +++ b/src/routes/list.nim @@ -6,7 +6,6 @@ import jester import router_utils import ".."/[types, redis_cache, api] import ../views/[general, timeline, list] -export getListTimeline, getGraphList template respList*(list, timeline, title, vnode: typed) = if list.id.len == 0 or list.name.len == 0: @@ -39,7 +38,7 @@ proc createListRouter*(cfg: Config) = let prefs = cookiePrefs() list = await getCachedList(id=(@"id")) - timeline = await getListTimeline(list.id, getCursor()) + timeline = await getGraphListTweets(list.id, getCursor()) vnode = renderTimelineTweets(timeline, prefs, request.path) respList(list, timeline, list.title, vnode) diff --git a/src/routes/rss.nim b/src/routes/rss.nim index 5da29b0..1323ed3 100644 --- a/src/routes/rss.nim +++ b/src/routes/rss.nim @@ -28,7 +28,7 @@ proc timelineRss*(req: Request; cfg: Config; query: Query): Future[Rss] {.async. var q = query q.fromUser = names profile = Profile( - tweets: await getSearch[Tweet](q, after), + tweets: await getGraphSearch(q, after), # this is kinda dumb user: User( username: name, @@ -78,7 +78,7 @@ proc createRssRouter*(cfg: Config) = if rss.cursor.len > 0: respRss(rss, "Search") - let tweets = await getSearch[Tweet](query, cursor) + let tweets = await getGraphSearch(query, cursor) rss.cursor = tweets.bottom rss.feed = renderSearchRss(tweets.content, query.text, genQueryUrl(query), cfg) @@ -159,7 +159,7 @@ proc createRssRouter*(cfg: Config) = let list = await getCachedList(id=id) - timeline = await getListTimeline(list.id, cursor) + timeline = await getGraphListTweets(list.id, cursor) rss.cursor = timeline.bottom rss.feed = renderListRss(timeline.content, list, cfg) diff --git a/src/routes/search.nim b/src/routes/search.nim index b2fd718..02c14e3 100644 --- a/src/routes/search.nim +++ b/src/routes/search.nim @@ -27,11 +27,15 @@ proc createSearchRouter*(cfg: Config) = of users: if "," in q: redirect("/" & q) - let users = await getSearch[User](query, getCursor()) + var users: Result[User] + try: + users = await getUserSearch(query, getCursor()) + except InternalError: + users = Result[User](beginning: true, query: query) resp renderMain(renderUserSearch(users, prefs), request, cfg, prefs, title) of tweets: let - tweets = await getSearch[Tweet](query, getCursor()) + tweets = await getGraphSearch(query, getCursor()) rss = "/search/rss?" & genQueryUrl(query) resp renderMain(renderTweetSearch(tweets, prefs, getPath()), request, cfg, prefs, title, rss=rss) diff --git a/src/routes/status.nim b/src/routes/status.nim index 1104282..7e89220 100644 --- a/src/routes/status.nim +++ b/src/routes/status.nim @@ -16,17 +16,21 @@ proc createStatusRouter*(cfg: Config) = router status: get "/@name/status/@id/?": cond '.' notin @"name" - cond not @"id".any(c => not c.isDigit) + let id = @"id" + + if id.len > 19 or id.any(c => not c.isDigit): + resp Http404, showError("Invalid tweet ID", cfg) + let prefs = cookiePrefs() # used for the infinite scroll feature if @"scroll".len > 0: - let replies = await getReplies(@"id", getCursor()) + let replies = await getReplies(id, getCursor()) if replies.content.len == 0: resp Http404, "" resp $renderReplies(replies, prefs, getPath()) - let conv = await getTweet(@"id", getCursor()) + let conv = await getTweet(id, getCursor()) if conv == nil: echo "nil conv" diff --git a/src/routes/timeline.nim b/src/routes/timeline.nim index a0a6e21..331b8ae 100644 --- a/src/routes/timeline.nim +++ b/src/routes/timeline.nim @@ -47,10 +47,10 @@ proc fetchProfile*(after: string; query: Query; skipRail=false; let timeline = case query.kind - of posts: getTimeline(userId, after) - of replies: getTimeline(userId, after, replies=true) - of media: getMediaTimeline(userId, after) - else: getSearch[Tweet](query, after) + of posts: getGraphUserTweets(userId, TimelineKind.tweets, after) + of replies: getGraphUserTweets(userId, TimelineKind.replies, after) + of media: getGraphUserTweets(userId, TimelineKind.media, after) + else: getGraphSearch(query, after) rail = skipIf(skipRail or query.kind == media, @[]): @@ -64,6 +64,7 @@ proc fetchProfile*(after: string; query: Query; skipRail=false; let tweet = await getCachedTweet(user.pinnedTweet) if not tweet.isNil: tweet.pinned = true + tweet.user = user pinned = some tweet result = Profile( @@ -82,7 +83,7 @@ proc showTimeline*(request: Request; query: Query; cfg: Config; prefs: Prefs; rss, after: string): Future[string] {.async.} = if query.fromUser.len != 1: let - timeline = await getSearch[Tweet](query, after) + timeline = await getGraphSearch(query, after) html = renderTweetSearch(timeline, prefs, getPath()) return renderMain(html, request, cfg, prefs, "Multi", rss=rss) @@ -123,7 +124,7 @@ proc createTimelineRouter*(cfg: Config) = get "/@name/?@tab?/?": cond '.' notin @"name" - cond @"name" notin ["pic", "gif", "video"] + cond @"name" notin ["pic", "gif", "video", "search", "settings", "login", "intent", "i"] cond @"tab" in ["with_replies", "media", "search", ""] let prefs = cookiePrefs() @@ -137,7 +138,7 @@ proc createTimelineRouter*(cfg: Config) = # used for the infinite scroll feature if @"scroll".len > 0: if query.fromUser.len != 1: - var timeline = await getSearch[Tweet](query, after) + var timeline = await getGraphSearch(query, after) if timeline.content.len == 0: resp Http404 timeline.beginning = true resp $renderTweetSearch(timeline, prefs, getPath()) diff --git a/src/tokens.nim b/src/tokens.nim index e6a4449..6ef81f5 100644 --- a/src/tokens.nim +++ b/src/tokens.nim @@ -1,8 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only import asyncdispatch, httpclient, times, sequtils, json, random import strutils, tables -import zippy -import types, consts, http_pool +import types, consts const maxConcurrentReqs = 5 # max requests at a time per token, to avoid race conditions @@ -11,11 +10,12 @@ const failDelay = initDuration(minutes=30) var - clientPool: HttpPool tokenPool: seq[Token] lastFailed: Time enableLogging = false +let headers = newHttpHeaders({"authorization": auth}) + template log(str) = if enableLogging: echo "[tokens] ", str @@ -41,10 +41,12 @@ proc getPoolJson*(): JsonNode = let maxReqs = case api - of Api.listMembers, Api.listBySlug, Api.list, - Api.userRestId, Api.userScreenName, Api.tweetDetail: 500 of Api.timeline: 187 - else: 180 + of Api.listMembers, Api.listBySlug, Api.list, Api.listTweets, + Api.userTweets, Api.userTweetsAndReplies, Api.userMedia, + Api.userRestId, Api.userScreenName, + Api.tweetDetail, Api.tweetResult, Api.search: 500 + of Api.userSearch: 900 reqs = maxReqs - token.apis[api].remaining reqsPerApi[$api] = reqsPerApi.getOrDefault($api, 0) + reqs @@ -65,18 +67,12 @@ proc fetchToken(): Future[Token] {.async.} = if getTime() - lastFailed < failDelay: raise rateLimitError() - let headers = newHttpHeaders({ - "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", - "accept-encoding": "gzip", - "accept-language": "en-US,en;q=0.5", - "connection": "keep-alive", - "authorization": auth - }) + let client = newAsyncHttpClient(headers=headers) try: let - resp = clientPool.use(headers): await c.postContent(activate) - tokNode = parseJson(uncompress(resp))["guest_token"] + resp = await client.postContent(activate) + tokNode = parseJson(resp)["guest_token"] tok = tokNode.getStr($(tokNode.getInt)) time = getTime() @@ -86,6 +82,8 @@ proc fetchToken(): Future[Token] {.async.} = if "Try again" notin e.msg: echo "[tokens] fetching tokens paused, resuming in 30 minutes" lastFailed = getTime() + finally: + client.close() proc expired(token: Token): bool = let time = getTime() @@ -158,7 +156,6 @@ proc poolTokens*(amount: int) {.async.} = tokenPool.add newToken proc initTokenPool*(cfg: Config) {.async.} = - clientPool = HttpPool() enableLogging = cfg.enableDebug while true: diff --git a/src/types.nim b/src/types.nim index 6f742d1..4dca5f0 100644 --- a/src/types.nim +++ b/src/types.nim @@ -7,19 +7,28 @@ genPrefsType() type RateLimitError* = object of CatchableError InternalError* = object of CatchableError + BadClientError* = object of CatchableError + + TimelineKind* {.pure.} = enum + tweets + replies + media Api* {.pure.} = enum tweetDetail - userShow + tweetResult timeline search - tweet + userSearch list listBySlug listMembers + listTweets userRestId userScreenName - status + userTweets + userTweetsAndReplies + userMedia RateLimit* = object remaining*: int @@ -36,8 +45,10 @@ type null = 0 noUserMatches = 17 protectedUser = 22 + missingParams = 25 couldntAuth = 32 doesntExist = 34 + invalidParam = 47 userNotFound = 50 suspended = 63 rateLimited = 88 @@ -150,6 +161,7 @@ type imageDirectMessage = "image_direct_message" audiospace = "audiospace" newsletterPublication = "newsletter_publication" + hidden unknown Card* = object diff --git a/src/views/renderutils.nim b/src/views/renderutils.nim index cdfeb28..9dffdcb 100644 --- a/src/views/renderutils.nim +++ b/src/views/renderutils.nim @@ -59,8 +59,7 @@ proc buttonReferer*(action, text, path: string; class=""; `method`="post"): VNod proc genCheckbox*(pref, label: string; state: bool): VNode = buildHtml(label(class="pref-group checkbox-container")): text label - if state: input(name=pref, `type`="checkbox", checked="") - else: input(name=pref, `type`="checkbox") + input(name=pref, `type`="checkbox", checked=state) span(class="checkbox") proc genInput*(pref, label, state, placeholder: string; class=""; autofocus=true): VNode = @@ -68,20 +67,15 @@ proc genInput*(pref, label, state, placeholder: string; class=""; autofocus=true buildHtml(tdiv(class=("pref-group pref-input " & class))): if label.len > 0: label(`for`=pref): text label - if autofocus and state.len == 0: - input(name=pref, `type`="text", placeholder=p, value=state, autofocus="") - else: - input(name=pref, `type`="text", placeholder=p, value=state) + input(name=pref, `type`="text", placeholder=p, value=state, autofocus=(autofocus and state.len == 0)) proc genSelect*(pref, label, state: string; options: seq[string]): VNode = buildHtml(tdiv(class="pref-group pref-input")): label(`for`=pref): text label select(name=pref): for opt in options: - if opt == state: - option(value=opt, selected=""): text opt - else: - option(value=opt): text opt + option(value=opt, selected=(opt == state)): + text opt proc genDate*(pref, state: string): VNode = buildHtml(span(class="date-input")): @@ -93,12 +87,9 @@ proc genImg*(url: string; class=""): VNode = img(src=getPicUrl(url), class=class, alt="") proc getTabClass*(query: Query; tab: QueryKind): string = - result = "tab-item" - if query.kind == tab: - result &= " active" + if query.kind == tab: "tab-item active" + else: "tab-item" proc getAvatarClass*(prefs: Prefs): string = - if prefs.squareAvatars: - "avatar" - else: - "avatar round" + if prefs.squareAvatars: "avatar" + else: "avatar round" diff --git a/src/views/search.nim b/src/views/search.nim index 77ba14f..72c59f5 100644 --- a/src/views/search.nim +++ b/src/views/search.nim @@ -63,12 +63,10 @@ proc renderSearchPanel*(query: Query): VNode = hiddenField("f", "tweets") genInput("q", "", query.text, "Enter search...", class="pref-inline") button(`type`="submit"): icon "search" - if isPanelOpen(query): - input(id="search-panel-toggle", `type`="checkbox", checked="") - else: - input(id="search-panel-toggle", `type`="checkbox") - label(`for`="search-panel-toggle"): - icon "down" + + input(id="search-panel-toggle", `type`="checkbox", checked=isPanelOpen(query)) + label(`for`="search-panel-toggle"): icon "down" + tdiv(class="search-panel"): for f in @["filter", "exclude"]: span(class="search-title"): text capitalize(f) diff --git a/src/views/tweet.nim b/src/views/tweet.nim index ea94e28..3338b71 100644 --- a/src/views/tweet.nim +++ b/src/views/tweet.nim @@ -106,14 +106,10 @@ proc renderVideo*(video: Video; prefs: Prefs; path: string): VNode = else: vidUrl case playbackType of mp4: - if prefs.muteVideos: - video(poster=thumb, controls="", muted=""): - source(src=source, `type`="video/mp4") - else: - video(poster=thumb, controls=""): - source(src=source, `type`="video/mp4") + video(poster=thumb, controls="", muted=prefs.muteVideos): + source(src=source, `type`="video/mp4") of m3u8, vmap: - video(poster=thumb, data-url=source, data-autoload="false") + video(poster=thumb, data-url=source, data-autoload="false", muted=prefs.muteVideos) verbatim "
" tdiv(class="overlay-circle"): span(class="overlay-triangle") verbatim "
" @@ -127,14 +123,9 @@ proc renderGif(gif: Gif; prefs: Prefs): VNode = buildHtml(tdiv(class="attachments media-gif")): tdiv(class="gallery-gif", style={maxHeight: "unset"}): tdiv(class="attachment"): - let thumb = getSmallPic(gif.thumb) - let url = getPicUrl(gif.url) - if prefs.autoplayGifs: - video(class="gif", poster=thumb, controls="", autoplay="", muted="", loop=""): - source(src=url, `type`="video/mp4") - else: - video(class="gif", poster=thumb, controls="", muted="", loop=""): - source(src=url, `type`="video/mp4") + video(class="gif", poster=getSmallPic(gif.thumb), autoplay=prefs.autoplayGifs, + controls="", muted="", loop=""): + source(src=getPicUrl(gif.url), `type`="video/mp4") proc renderPoll(poll: Poll): VNode = buildHtml(tdiv(class="poll")): @@ -328,7 +319,7 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class=""; index=0; if tweet.attribution.isSome: renderAttribution(tweet.attribution.get(), prefs) - if tweet.card.isSome: + if tweet.card.isSome and tweet.card.get().kind != hidden: renderCard(tweet.card.get(), prefs, path) if tweet.photos.len > 0: diff --git a/tests/test_card.py b/tests/test_card.py index 51945d6..da6ffde 100644 --- a/tests/test_card.py +++ b/tests/test_card.py @@ -3,11 +3,6 @@ from parameterized import parameterized card = [ - ['Thom_Wolf/status/1122466524860702729', - 'facebookresearch/fairseq', - 'Facebook AI Research Sequence-to-Sequence Toolkit written in Python. - GitHub - facebookresearch/fairseq: Facebook AI Research Sequence-to-Sequence Toolkit written in Python.', - 'github.com', True], - ['nim_lang/status/1136652293510717440', 'Version 0.20.0 released', 'We are very proud to announce Nim version 0.20. This is a massive release, both literally and figuratively. It contains more than 1,000 commits and it marks our release candidate for version 1.0!', @@ -25,6 +20,11 @@ card = [ ] no_thumb = [ + ['Thom_Wolf/status/1122466524860702729', + 'facebookresearch/fairseq', + 'Facebook AI Research Sequence-to-Sequence Toolkit written in Python. - GitHub - facebookresearch/fairseq: Facebook AI Research Sequence-to-Sequence Toolkit written in Python.', + 'github.com'], + ['brent_p/status/1088857328680488961', 'Hts Nim Sugar', 'hts-nim is a library that allows one to use htslib via the nim programming language. Nim is a garbage-collected language that compiles to C and often has similar performance. I have become very...', diff --git a/tests/test_profile.py b/tests/test_profile.py index e62f7b9..f9b5047 100644 --- a/tests/test_profile.py +++ b/tests/test_profile.py @@ -17,11 +17,6 @@ protected = [ invalid = [['thisprofiledoesntexist'], ['%']] -banner_color = [ - ['nim_lang', '22, 25, 32'], - ['rustlang', '35, 31, 32'] -] - banner_image = [ ['mobile_test', 'profile_banners%2F82135242%2F1384108037%2F1500x500'] ] @@ -74,12 +69,6 @@ class ProfileTest(BaseTestCase): self.open_nitter('user') self.assert_text('User "user" has been suspended') - @parameterized.expand(banner_color) - def test_banner_color(self, username, color): - self.open_nitter(username) - banner = self.find_element(Profile.banner + ' a') - self.assertIn(color, banner.value_of_css_property('background-color')) - @parameterized.expand(banner_image) def test_banner_image(self, username, url): self.open_nitter(username) From a64e2286d592b907eba6706ba0a8615b8161c4f1 Mon Sep 17 00:00:00 2001 From: Zed Date: Fri, 21 Apr 2023 17:41:48 +0200 Subject: [PATCH 101/409] Update badges --- .github/workflows/run-tests.yml | 2 +- README.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index d6315b3..ca93f30 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -1,4 +1,4 @@ -name: Run tests +name: Tests on: push: diff --git a/README.md b/README.md index 5ca2ee6..4f8235d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Nitter -[![Test Matrix](https://github.com/zedeus/nitter/workflows/CI/CD/badge.svg)](https://github.com/zedeus/nitter/actions?query=workflow%3ACI/CD) +[![Test Matrix](https://github.com/zedeus/nitter/workflows/Tests/badge.svg)](https://github.com/zedeus/nitter/actions/workflows/run-tests.yml) +[![Test Matrix](https://github.com/zedeus/nitter/workflows/Docker/badge.svg)](https://github.com/zedeus/nitter/actions/workflows/build-docker.yml) [![License](https://img.shields.io/github/license/zedeus/nitter?style=flat)](#license) A free and open source alternative Twitter front-end focused on privacy and From b67f3062fed36f78a827be4b31340ca79a6da4cf Mon Sep 17 00:00:00 2001 From: Zed Date: Fri, 21 Apr 2023 23:33:52 +0200 Subject: [PATCH 102/409] Fix card tests --- tests/test_card.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_card.py b/tests/test_card.py index da6ffde..78fe79c 100644 --- a/tests/test_card.py +++ b/tests/test_card.py @@ -76,7 +76,7 @@ class CardTest(BaseTestCase): c = Card(Conversation.main + " ") self.assert_text(title, c.title) self.assert_text(destination, c.destination) - self.assertIn('_img', self.get_image_url(c.image + ' img')) + self.assertIn('/pic/', self.get_image_url(c.image + ' img')) if len(description) > 0: self.assert_text(description, c.description) if large: @@ -99,7 +99,7 @@ class CardTest(BaseTestCase): c = Card(Conversation.main + " ") self.assert_text(title, c.title) self.assert_text(destination, c.destination) - self.assertIn('_img', self.get_image_url(c.image + ' img')) + self.assertIn('/pic/', self.get_image_url(c.image + ' img')) self.assert_element_visible('.card-overlay') if len(description) > 0: self.assert_text(description, c.description) From fe22a45c7e3221f9abd7106b304f1309c904b967 Mon Sep 17 00:00:00 2001 From: Zed Date: Fri, 21 Apr 2023 23:43:46 +0200 Subject: [PATCH 103/409] Make Docker workflow depend on tests --- .github/workflows/build-docker.yml | 4 ++++ .github/workflows/run-tests.yml | 3 +++ 2 files changed, 7 insertions(+) diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index 754f080..1250195 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -8,7 +8,10 @@ on: - master jobs: + tests: + uses: ./.github/workflows/run-tests.yml build-docker-amd64: + depends: [tests] runs-on: buildjet-2vcpu-ubuntu-2204 steps: - uses: actions/checkout@v3 @@ -33,6 +36,7 @@ jobs: push: true tags: zedeus/nitter:latest,zedeus/nitter:${{ github.sha }} build-docker-arm64: + depends: [tests] runs-on: buildjet-2vcpu-ubuntu-2204-arm steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index ca93f30..140b6bf 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -4,6 +4,9 @@ on: push: paths-ignore: - "*.md" + branches-ignore: + - master + workflow_call: jobs: test: From 2254a0728c587ebcec51ff08da0bf145606a629e Mon Sep 17 00:00:00 2001 From: Zed Date: Fri, 21 Apr 2023 23:52:14 +0200 Subject: [PATCH 104/409] depends -> needs --- .github/workflows/build-docker.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index 1250195..765e7a0 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -11,7 +11,7 @@ jobs: tests: uses: ./.github/workflows/run-tests.yml build-docker-amd64: - depends: [tests] + needs: [tests] runs-on: buildjet-2vcpu-ubuntu-2204 steps: - uses: actions/checkout@v3 @@ -36,7 +36,7 @@ jobs: push: true tags: zedeus/nitter:latest,zedeus/nitter:${{ github.sha }} build-docker-arm64: - depends: [tests] + needs: [tests] runs-on: buildjet-2vcpu-ubuntu-2204-arm steps: - uses: actions/checkout@v3 From 774063fb5a46633a1984900734b7300ecb1bfb4f Mon Sep 17 00:00:00 2001 From: Zed Date: Sat, 20 May 2023 00:52:55 +0200 Subject: [PATCH 105/409] Fix pinned "TweetPreviewDisplay" crash --- src/parser.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser.nim b/src/parser.nim index fe2fe5b..78f9bca 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -384,7 +384,7 @@ proc parseGraphTweet(js: JsonNode): Tweet = return Tweet(available: false) case js{"__typename"}.getStr - of "TweetUnavailable": + of "TweetUnavailable", "TweetPreviewDisplay": return Tweet(available: false) of "TweetTombstone": return Tweet( From b516ec367b41fad1185e3e408230cd61e24e2ea2 Mon Sep 17 00:00:00 2001 From: Zed Date: Sat, 20 May 2023 00:59:34 +0200 Subject: [PATCH 106/409] Remove outdated tweet from card test --- tests/test_card.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/test_card.py b/tests/test_card.py index 78fe79c..696b9d5 100644 --- a/tests/test_card.py +++ b/tests/test_card.py @@ -35,11 +35,6 @@ no_thumb = [ 'A sample of a Qt app written using mostly nim. Contribute to sinkingsugar/nimqt-example development by creating an account on GitHub.', 'github.com'], - ['mobile_test/status/490378953744318464', - 'Nantasket Beach', - 'Explore this photo titled Nantasket Beach by Ben Sandofsky (@sandofsky) on 500px', - '500px.com'], - ['nim_lang/status/1082989146040340480', 'Nim in 2018: A short recap', 'Posted by u/miran1 - 36 votes and 46 comments', From e3b3b38a2d43a83b5fc2239ab41e864ee686fb2f Mon Sep 17 00:00:00 2001 From: Zed Date: Sat, 20 May 2023 02:10:37 +0200 Subject: [PATCH 107/409] Add proper tombstone for subscriber tweets --- src/consts.nim | 18 +++++++++++------- src/parser.nim | 13 ++++++------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/consts.nim b/src/consts.nim index 27e82f9..f22581f 100644 --- a/src/consts.nim +++ b/src/consts.nim @@ -11,13 +11,13 @@ const userSearch* = api / "1.1/users/search.json" graphql = api / "graphql" - graphUser* = graphql / "8mPfHBetXOg-EHAyeVxUoA/UserByScreenName" - graphUserById* = graphql / "nI8WydSd-X-lQIVo6bdktQ/UserByRestId" - graphUserTweets* = graphql / "9rys0A7w1EyqVd2ME0QCJg/UserTweets" - graphUserTweetsAndReplies* = graphql / "ehMCHF3Mkgjsfz_aImqOsg/UserTweetsAndReplies" - graphUserMedia* = graphql / "MA_EP2a21zpzNWKRkaPBMg/UserMedia" - graphTweet* = graphql / "6I7Hm635Q6ftv69L8VrSeQ/TweetDetail" - graphTweetResult* = graphql / "rt-rHeSJ-2H9O9gxWQcPcg/TweetResultByRestId" + graphUser* = graphql / "pVrmNaXcxPjisIvKtLDMEA/UserByScreenName" + graphUserById* = graphql / "1YAM811Q8Ry4XyPpJclURQ/UserByRestId" + graphUserTweets* = graphql / "WzJjibAcDa-oCjCcLOotcg/UserTweets" + graphUserTweetsAndReplies* = graphql / "fn9oRltM1N4thkh5CVusPg/UserTweetsAndReplies" + graphUserMedia* = graphql / "qQoeS7szGavsi8-ehD2AWg/UserMedia" + graphTweet* = graphql / "miKSMGb2R1SewIJv2-ablQ/TweetDetail" + graphTweetResult* = graphql / "0kc0a_7TTr3dvweZlMslsQ/TweetResultByRestId" graphSearchTimeline* = graphql / "gkjsKepM6gl_HmFWoWKfgg/SearchTimeline" graphListById* = graphql / "iTpgCtbdxrsJfyx0cFjHqg/ListByRestId" graphListBySlug* = graphql / "-kmqNvm5Y-cVrfvBy6docg/ListBySlug" @@ -50,10 +50,13 @@ const gqlFeatures* = """{ "blue_business_profile_image_shape_enabled": false, + "creator_subscriptions_tweet_preview_api_enabled": true, "freedom_of_speech_not_reach_fetch_enabled": false, "graphql_is_translatable_rweb_tweet_is_translatable_enabled": false, + "highlights_tweets_tab_ui_enabled": false, "interactive_text_enabled": false, "longform_notetweets_consumption_enabled": true, + "longform_notetweets_inline_media_enabled": false, "longform_notetweets_richtext_consumption_enabled": true, "longform_notetweets_rich_text_read_enabled": false, "responsive_web_edit_tweet_api_enabled": false, @@ -63,6 +66,7 @@ const "responsive_web_graphql_timeline_navigation_enabled": false, "responsive_web_text_conversations_enabled": false, "responsive_web_twitter_blue_verified_badge_is_enabled": true, + "rweb_lists_timeline_redesign_enabled": true, "spaces_2022_h2_clipping": true, "spaces_2022_h2_spaces_communities": true, "standardized_nudges_misinfo": false, diff --git a/src/parser.nim b/src/parser.nim index 78f9bca..5ec21e4 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -381,16 +381,15 @@ proc parsePhotoRail*(js: JsonNode): PhotoRail = proc parseGraphTweet(js: JsonNode): Tweet = if js.kind == JNull: - return Tweet(available: false) + return Tweet() case js{"__typename"}.getStr - of "TweetUnavailable", "TweetPreviewDisplay": - return Tweet(available: false) + of "TweetUnavailable": + return Tweet() of "TweetTombstone": - return Tweet( - available: false, - text: js{"tombstone", "text"}.getTombstone - ) + return Tweet(text: js{"tombstone", "text"}.getTombstone) + of "TweetPreviewDisplay": + return Tweet(text: "You're unable to view this Tweet because it's only available to the Subscribers of the account owner.") of "TweetWithVisibilityResults": return parseGraphTweet(js{"tweet"}) From f7e878c1260acc87fa53331bd5dc4584791baefd Mon Sep 17 00:00:00 2001 From: PrivacyDevel <105459436+PrivacyDevel@users.noreply.github.com> Date: Tue, 30 May 2023 11:37:35 +0000 Subject: [PATCH 108/409] fixed bug that caused threads on user profiles to be hidden (#885) --- src/parser.nim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/parser.nim b/src/parser.nim index 5ec21e4..5b0d584 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -490,6 +490,10 @@ proc parseGraphTimeline*(js: JsonNode; root: string; after=""): Timeline = if not tweet.available: tweet.id = parseBiggestInt(entryId.getId()) result.content.add tweet + elif entryId.startsWith("profile-conversation") or entryId.startsWith("homeConversation"): + let (thread, self) = parseGraphThread(e) + for tweet in thread.content: + result.content.add tweet elif entryId.startsWith("cursor-bottom"): result.bottom = e{"content", "value"}.getStr From 38985af6ed30f050201b15425cdac0dc2e286b6d Mon Sep 17 00:00:00 2001 From: PrivacyDevel <105459436+PrivacyDevel@users.noreply.github.com> Date: Tue, 30 May 2023 21:42:14 +0000 Subject: [PATCH 109/409] fixed bug that caused everybody to be displayed as verified (#890) --- src/parser.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser.nim b/src/parser.nim index 5b0d584..38dbb24 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -33,7 +33,7 @@ proc parseGraphUser(js: JsonNode): User = result = parseUser(user{"legacy"}) if "is_blue_verified" in user: - result.verified = true + result.verified = user{"is_blue_verified"}.getBool() proc parseGraphList*(js: JsonNode): List = if js.isNull: return From dcf73354ff173c0407c62f84ea3bb90a130303c1 Mon Sep 17 00:00:00 2001 From: Zed Date: Sat, 1 Jul 2023 22:07:37 +0200 Subject: [PATCH 110/409] Fix GraphQL user crash with invalid JSON --- src/experimental/parser/graphql.nim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/experimental/parser/graphql.nim b/src/experimental/parser/graphql.nim index 36014e3..0f08c4f 100644 --- a/src/experimental/parser/graphql.nim +++ b/src/experimental/parser/graphql.nim @@ -4,6 +4,9 @@ import user, ../types/[graphuser, graphlistmembers] from ../../types import User, Result, Query, QueryKind proc parseGraphUser*(json: string): User = + if json.len == 0 or json[0] != '{': + return + let raw = json.fromJson(GraphUser) if raw.data.user.result.reason.get("") == "Suspended": From 0bc3c153d9b38a3c02f321fb64a375fef6b97e8e Mon Sep 17 00:00:00 2001 From: Zed Date: Mon, 10 Jul 2023 11:25:34 +0200 Subject: [PATCH 111/409] Fix everything (#927) * Switch bearer token and endpoints, update parser * Enable user search, disable tweet search * Disable multi-user timelines for now * Fix parsing of pinned tombstone --- src/api.nim | 26 ++++----- src/consts.nim | 67 +++++++++-------------- src/experimental/parser/graphql.nim | 8 +-- src/experimental/types/graphuser.nim | 4 +- src/parser.nim | 82 ++++++++++++++-------------- src/routes/embed.nim | 12 ++-- src/routes/rss.nim | 52 +++++++++--------- src/routes/search.nim | 14 +++-- src/routes/timeline.nim | 43 ++++++--------- src/sass/tweet/thread.scss | 26 +++++++++ src/tokens.nim | 9 ++- src/types.nim | 5 +- src/views/rss.nimf | 43 ++++++++------- src/views/search.nim | 2 +- src/views/timeline.nim | 48 ++++++++-------- src/views/tweet.nim | 18 +++--- tests/test_card.py | 35 +++--------- tests/test_profile.py | 4 +- tests/test_search.py | 10 ++-- tests/test_tweet.py | 16 +++--- 20 files changed, 260 insertions(+), 264 deletions(-) diff --git a/src/api.nim b/src/api.nim index b23aa87..d99eb3d 100644 --- a/src/api.nim +++ b/src/api.nim @@ -7,20 +7,20 @@ import experimental/parser as newParser proc getGraphUser*(username: string): Future[User] {.async.} = if username.len == 0: return let - variables = %*{"screen_name": username} - params = {"variables": $variables, "features": gqlFeatures} + variables = """{"screen_name": "$1"}""" % username + params = {"variables": variables, "features": gqlFeatures} js = await fetchRaw(graphUser ? params, Api.userScreenName) result = parseGraphUser(js) proc getGraphUserById*(id: string): Future[User] {.async.} = if id.len == 0 or id.any(c => not c.isDigit): return let - variables = %*{"userId": id} - params = {"variables": $variables, "features": gqlFeatures} + variables = """{"rest_id": "$1"}""" % id + params = {"variables": variables, "features": gqlFeatures} js = await fetchRaw(graphUserById ? params, Api.userRestId) result = parseGraphUser(js) -proc getGraphUserTweets*(id: string; kind: TimelineKind; after=""): Future[Timeline] {.async.} = +proc getGraphUserTweets*(id: string; kind: TimelineKind; after=""): Future[Profile] {.async.} = if id.len == 0: return let cursor = if after.len > 0: "\"cursor\":\"$1\"," % after else: "" @@ -40,7 +40,7 @@ proc getGraphListTweets*(id: string; after=""): Future[Timeline] {.async.} = variables = listTweetsVariables % [id, cursor] params = {"variables": variables, "features": gqlFeatures} js = await fetch(graphListTweets ? params, Api.listTweets) - result = parseGraphTimeline(js, "list", after) + result = parseGraphTimeline(js, "list", after).tweets proc getGraphListBySlug*(name, list: string): Future[List] {.async.} = let @@ -50,8 +50,8 @@ proc getGraphListBySlug*(name, list: string): Future[List] {.async.} = proc getGraphList*(id: string): Future[List] {.async.} = let - variables = %*{"listId": id} - params = {"variables": $variables, "features": gqlFeatures} + variables = """{"listId": "$1"}""" % id + params = {"variables": variables, "features": gqlFeatures} result = parseGraphList(await fetch(graphListById ? params, Api.list)) proc getGraphListMembers*(list: List; after=""): Future[Result[User]] {.async.} = @@ -72,7 +72,7 @@ proc getGraphListMembers*(list: List; after=""): Future[Result[User]] {.async.} proc getGraphTweetResult*(id: string): Future[Tweet] {.async.} = if id.len == 0: return let - variables = tweetResultVariables % id + variables = """{"rest_id": "$1"}""" % id params = {"variables": variables, "features": gqlFeatures} js = await fetch(graphTweetResult ? params, Api.tweetResult) result = parseGraphTweetResult(js) @@ -95,10 +95,10 @@ proc getTweet*(id: string; after=""): Future[Conversation] {.async.} = if after.len > 0: result.replies = await getReplies(id, after) -proc getGraphSearch*(query: Query; after=""): Future[Result[Tweet]] {.async.} = +proc getGraphSearch*(query: Query; after=""): Future[Profile] {.async.} = let q = genQueryParam(query) if q.len == 0 or q == emptyQuery: - return Result[Tweet](query: query, beginning: true) + return Profile(tweets: Timeline(query: query, beginning: true)) var variables = %*{ @@ -112,8 +112,8 @@ proc getGraphSearch*(query: Query; after=""): Future[Result[Tweet]] {.async.} = if after.len > 0: variables["cursor"] = % after let url = graphSearchTimeline ? {"variables": $variables, "features": gqlFeatures} - result = parseGraphSearch(await fetch(url, Api.search), after) - result.query = query + result = Profile(tweets: parseGraphSearch(await fetch(url, Api.search), after)) + result.tweets.query = query proc getUserSearch*(query: Query; page="1"): Future[Result[User]] {.async.} = if query.text.len == 0: diff --git a/src/consts.nim b/src/consts.nim index f22581f..184f9da 100644 --- a/src/consts.nim +++ b/src/consts.nim @@ -2,7 +2,7 @@ import uri, sequtils, strutils const - auth* = "Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA" + auth* = "Bearer AAAAAAAAAAAAAAAAAAAAAFQODgEAAAAAVHTp76lzh3rFzcHbmHVvQxYYpTw%3DckAlMINMjmCwxUcaXbAN4XqJVdgMJaHqNOFgPMK0zN1qLqLQCF" api = parseUri("https://api.twitter.com") activate* = $(api / "1.1/guest/activate.json") @@ -11,18 +11,18 @@ const userSearch* = api / "1.1/users/search.json" graphql = api / "graphql" - graphUser* = graphql / "pVrmNaXcxPjisIvKtLDMEA/UserByScreenName" - graphUserById* = graphql / "1YAM811Q8Ry4XyPpJclURQ/UserByRestId" - graphUserTweets* = graphql / "WzJjibAcDa-oCjCcLOotcg/UserTweets" - graphUserTweetsAndReplies* = graphql / "fn9oRltM1N4thkh5CVusPg/UserTweetsAndReplies" - graphUserMedia* = graphql / "qQoeS7szGavsi8-ehD2AWg/UserMedia" - graphTweet* = graphql / "miKSMGb2R1SewIJv2-ablQ/TweetDetail" - graphTweetResult* = graphql / "0kc0a_7TTr3dvweZlMslsQ/TweetResultByRestId" + graphUser* = graphql / "u7wQyGi6oExe8_TRWGMq4Q/UserResultByScreenNameQuery" + graphUserById* = graphql / "oPppcargziU1uDQHAUmH-A/UserResultByIdQuery" + graphUserTweets* = graphql / "3JNH4e9dq1BifLxAa3UMWg/UserWithProfileTweetsQueryV2" + graphUserTweetsAndReplies* = graphql / "8IS8MaO-2EN6GZZZb8jF0g/UserWithProfileTweetsAndRepliesQueryV2" + graphUserMedia* = graphql / "PDfFf8hGeJvUCiTyWtw4wQ/MediaTimelineV2" + graphTweet* = graphql / "83h5UyHZ9wEKBVzALX8R_g/ConversationTimelineV2" + graphTweetResult* = graphql / "sITyJdhRPpvpEjg4waUmTA/TweetResultByIdQuery" graphSearchTimeline* = graphql / "gkjsKepM6gl_HmFWoWKfgg/SearchTimeline" graphListById* = graphql / "iTpgCtbdxrsJfyx0cFjHqg/ListByRestId" graphListBySlug* = graphql / "-kmqNvm5Y-cVrfvBy6docg/ListBySlug" graphListMembers* = graphql / "P4NpVZDqUD_7MEM84L-8nw/ListMembers" - graphListTweets* = graphql / "jZntL0oVJSdjhmPcdbw_eA/ListLatestTweetsTimeline" + graphListTweets* = graphql / "BbGLL1ZfMibdFNWlk7a0Pw/ListTimeline" timelineParams* = { "include_profile_interstitial_type": "0", @@ -49,10 +49,13 @@ const }.toSeq gqlFeatures* = """{ + "android_graphql_skip_api_media_color_palette": false, "blue_business_profile_image_shape_enabled": false, + "creator_subscriptions_subscription_count_enabled": false, "creator_subscriptions_tweet_preview_api_enabled": true, "freedom_of_speech_not_reach_fetch_enabled": false, "graphql_is_translatable_rweb_tweet_is_translatable_enabled": false, + "hidden_profile_likes_enabled": false, "highlights_tweets_tab_ui_enabled": false, "interactive_text_enabled": false, "longform_notetweets_consumption_enabled": true, @@ -64,15 +67,25 @@ const "responsive_web_graphql_exclude_directive_enabled": true, "responsive_web_graphql_skip_user_profile_image_extensions_enabled": false, "responsive_web_graphql_timeline_navigation_enabled": false, + "responsive_web_media_download_video_enabled": false, "responsive_web_text_conversations_enabled": false, + "responsive_web_twitter_article_tweet_consumption_enabled": false, "responsive_web_twitter_blue_verified_badge_is_enabled": true, "rweb_lists_timeline_redesign_enabled": true, "spaces_2022_h2_clipping": true, "spaces_2022_h2_spaces_communities": true, "standardized_nudges_misinfo": false, + "subscriptions_verification_info_enabled": true, + "subscriptions_verification_info_reason_enabled": true, + "subscriptions_verification_info_verified_since_enabled": true, + "super_follow_badge_privacy_enabled": false, + "super_follow_exclusive_tweet_notifications_enabled": false, + "super_follow_tweet_api_enabled": false, + "super_follow_user_api_enabled": false, "tweet_awards_web_tipping_enabled": false, "tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled": false, "tweetypie_unmention_optimization_enabled": false, + "unified_cards_ad_metadata_container_dynamic_card_content_query_enabled": false, "verified_phone_label_enabled": false, "vibe_api_enabled": false, "view_counts_everywhere_api_enabled": false @@ -81,41 +94,15 @@ const tweetVariables* = """{ "focalTweetId": "$1", $2 - "withBirdwatchNotes": false, - "includePromotedContent": false, - "withDownvotePerspective": false, - "withReactionsMetadata": false, - "withReactionsPerspective": false, - "withVoice": false -}""" - - tweetResultVariables* = """{ - "tweetId": "$1", - "includePromotedContent": false, - "withDownvotePerspective": false, - "withReactionsMetadata": false, - "withReactionsPerspective": false, - "withVoice": false, - "withCommunity": false + "includeHasBirdwatchNotes": false }""" userTweetsVariables* = """{ - "userId": "$1", $2 - "count": 20, - "includePromotedContent": false, - "withDownvotePerspective": false, - "withReactionsMetadata": false, - "withReactionsPerspective": false, - "withVoice": false, - "withV2Timeline": true + "rest_id": "$1", $2 + "count": 20 }""" listTweetsVariables* = """{ - "listId": "$1", $2 - "count": 20, - "includePromotedContent": false, - "withDownvotePerspective": false, - "withReactionsMetadata": false, - "withReactionsPerspective": false, - "withVoice": false + "rest_id": "$1", $2 + "count": 20 }""" diff --git a/src/experimental/parser/graphql.nim b/src/experimental/parser/graphql.nim index 0f08c4f..b9da7c4 100644 --- a/src/experimental/parser/graphql.nim +++ b/src/experimental/parser/graphql.nim @@ -9,12 +9,12 @@ proc parseGraphUser*(json: string): User = let raw = json.fromJson(GraphUser) - if raw.data.user.result.reason.get("") == "Suspended": + if raw.data.userResult.result.unavailableReason.get("") == "Suspended": return User(suspended: true) - result = toUser raw.data.user.result.legacy - result.id = raw.data.user.result.restId - result.verified = result.verified or raw.data.user.result.isBlueVerified + result = toUser raw.data.userResult.result.legacy + result.id = raw.data.userResult.result.restId + result.verified = result.verified or raw.data.userResult.result.isBlueVerified proc parseGraphListMembers*(json, cursor: string): Result[User] = result = Result[User]( diff --git a/src/experimental/types/graphuser.nim b/src/experimental/types/graphuser.nim index 478e7f3..c30eed9 100644 --- a/src/experimental/types/graphuser.nim +++ b/src/experimental/types/graphuser.nim @@ -3,7 +3,7 @@ import user type GraphUser* = object - data*: tuple[user: UserData] + data*: tuple[userResult: UserData] UserData* = object result*: UserResult @@ -12,4 +12,4 @@ type legacy*: RawUser restId*: string isBlueVerified*: bool - reason*: Option[string] + unavailableReason*: Option[string] diff --git a/src/parser.nim b/src/parser.nim index 38dbb24..7b178f3 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -29,7 +29,7 @@ proc parseUser(js: JsonNode; id=""): User = result.expandUserEntities(js) proc parseGraphUser(js: JsonNode): User = - let user = ? js{"user_results", "result"} + let user = ? js{"user_result", "result"} result = parseUser(user{"legacy"}) if "is_blue_verified" in user: @@ -262,6 +262,11 @@ proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet = result.gif = some(parseGif(m)) else: discard + with url, m{"url"}: + if result.text.endsWith(url.getStr): + result.text.removeSuffix(url.getStr) + result.text = result.text.strip() + with jsWithheld, js{"withheld_in_countries"}: let withheldInCountries: seq[string] = if jsWithheld.kind != JArray: @[] @@ -294,16 +299,6 @@ proc finalizeTweet(global: GlobalObjects; id: string): Tweet = else: result.retweet = some Tweet() -proc parsePin(js: JsonNode; global: GlobalObjects): Tweet = - let pin = js{"pinEntry", "entry", "entryId"}.getStr - if pin.len == 0: return - - let id = pin.getId - if id notin global.tweets: return - - global.tweets[id].pinned = true - return finalizeTweet(global, id) - proc parseGlobalObjects(js: JsonNode): GlobalObjects = result = GlobalObjects() let @@ -314,7 +309,7 @@ proc parseGlobalObjects(js: JsonNode): GlobalObjects = result.users[k] = parseUser(v, k) for k, v in tweets: - var tweet = parseTweet(v, v{"card"}) + var tweet = parseTweet(v, v{"tweet_card"}) if tweet.user.id in result.users: tweet.user = result.users[tweet.user.id] result.tweets[k] = tweet @@ -324,11 +319,6 @@ proc parseInstructions[T](res: var Result[T]; global: GlobalObjects; js: JsonNod return for i in js: - when T is Tweet: - if res.beginning and i{"pinEntry"}.notNull: - with pin, parsePin(i, global): - res.content.add pin - with r, i{"replaceEntry", "entry"}: if "top" in r{"entryId"}.getStr: res.top = r.getCursor @@ -369,7 +359,7 @@ proc parseTimeline*(js: JsonNode; after=""): Timeline = proc parsePhotoRail*(js: JsonNode): PhotoRail = for tweet in js: let - t = parseTweet(tweet, js{"card"}) + t = parseTweet(tweet, js{"tweet_card"}) url = if t.photos.len > 0: t.photos[0] elif t.video.isSome: get(t.video).thumb elif t.gif.isSome: get(t.gif).thumb @@ -387,13 +377,17 @@ proc parseGraphTweet(js: JsonNode): Tweet = of "TweetUnavailable": return Tweet() of "TweetTombstone": - return Tweet(text: js{"tombstone", "text"}.getTombstone) + with text, js{"tombstone", "richText"}: + return Tweet(text: text.getTombstone) + with text, js{"tombstone", "text"}: + return Tweet(text: text.getTombstone) + return Tweet() of "TweetPreviewDisplay": return Tweet(text: "You're unable to view this Tweet because it's only available to the Subscribers of the account owner.") of "TweetWithVisibilityResults": return parseGraphTweet(js{"tweet"}) - var jsCard = copy(js{"card", "legacy"}) + var jsCard = copy(js{"tweet_card", "legacy"}) if jsCard.kind != JNull: var values = newJObject() for val in jsCard["binding_values"]: @@ -401,6 +395,7 @@ proc parseGraphTweet(js: JsonNode): Tweet = jsCard["binding_values"] = values result = parseTweet(js{"legacy"}, jsCard) + result.id = js{"rest_id"}.getId result.user = parseGraphUser(js{"core"}) with noteTweet, js{"note_tweet", "note_tweet_results", "result"}: @@ -414,32 +409,31 @@ proc parseGraphThread(js: JsonNode): tuple[thread: Chain; self: bool] = for t in js{"content", "items"}: let entryId = t{"entryId"}.getStr if "cursor-showmore" in entryId: - let cursor = t{"item", "itemContent", "value"} + let cursor = t{"item", "content", "value"} result.thread.cursor = cursor.getStr result.thread.hasMore = true elif "tweet" in entryId: - let tweet = parseGraphTweet(t{"item", "itemContent", "tweet_results", "result"}) + let tweet = parseGraphTweet(t{"item", "content", "tweetResult", "result"}) result.thread.content.add tweet - if t{"item", "itemContent", "tweetDisplayType"}.getStr == "SelfThread": + if t{"item", "content", "tweetDisplayType"}.getStr == "SelfThread": result.self = true proc parseGraphTweetResult*(js: JsonNode): Tweet = - with tweet, js{"data", "tweetResult", "result"}: + with tweet, js{"data", "tweet_result", "result"}: result = parseGraphTweet(tweet) proc parseGraphConversation*(js: JsonNode; tweetId: string): Conversation = result = Conversation(replies: Result[Chain](beginning: true)) - let instructions = ? js{"data", "threaded_conversation_with_injections", "instructions"} + let instructions = ? js{"data", "timeline_response", "instructions"} if instructions.len == 0: return for e in instructions[0]{"entries"}: let entryId = e{"entryId"}.getStr - # echo entryId if entryId.startsWith("tweet"): - with tweetResult, e{"content", "itemContent", "tweet_results", "result"}: + with tweetResult, e{"content", "content", "tweetResult", "result"}: let tweet = parseGraphTweet(tweetResult) if not tweet.available: @@ -454,7 +448,7 @@ proc parseGraphConversation*(js: JsonNode; tweetId: string): Conversation = let tweet = Tweet( id: parseBiggestInt(id), available: false, - text: e{"content", "itemContent", "tombstoneInfo", "richText"}.getTombstone + text: e{"content", "content", "tombstoneInfo", "richText"}.getTombstone ) if id == tweetId: @@ -468,34 +462,42 @@ proc parseGraphConversation*(js: JsonNode; tweetId: string): Conversation = else: result.replies.content.add thread elif entryId.startsWith("cursor-bottom"): - result.replies.bottom = e{"content", "itemContent", "value"}.getStr + result.replies.bottom = e{"content", "content", "value"}.getStr -proc parseGraphTimeline*(js: JsonNode; root: string; after=""): Timeline = - result = Timeline(beginning: after.len == 0) +proc parseGraphTimeline*(js: JsonNode; root: string; after=""): Profile = + result = Profile(tweets: Timeline(beginning: after.len == 0)) let instructions = - if root == "list": ? js{"data", "list", "tweets_timeline", "timeline", "instructions"} - else: ? js{"data", "user", "result", "timeline_v2", "timeline", "instructions"} + if root == "list": ? js{"data", "list", "timeline_response", "timeline", "instructions"} + else: ? js{"data", "user_result", "result", "timeline_response", "timeline", "instructions"} if instructions.len == 0: return for i in instructions: - if i{"type"}.getStr == "TimelineAddEntries": + if i{"__typename"}.getStr == "TimelineAddEntries": for e in i{"entries"}: let entryId = e{"entryId"}.getStr if entryId.startsWith("tweet"): - with tweetResult, e{"content", "itemContent", "tweet_results", "result"}: + with tweetResult, e{"content", "content", "tweetResult", "result"}: let tweet = parseGraphTweet(tweetResult) if not tweet.available: tweet.id = parseBiggestInt(entryId.getId()) - result.content.add tweet - elif entryId.startsWith("profile-conversation") or entryId.startsWith("homeConversation"): + result.tweets.content.add tweet + elif "-conversation-" in entryId or entryId.startsWith("homeConversation"): let (thread, self) = parseGraphThread(e) - for tweet in thread.content: - result.content.add tweet + result.tweets.content.add thread elif entryId.startsWith("cursor-bottom"): - result.bottom = e{"content", "value"}.getStr + result.tweets.bottom = e{"content", "value"}.getStr + if after.len == 0 and i{"__typename"}.getStr == "TimelinePinEntry": + with tweetResult, i{"entry", "content", "content", "tweetResult", "result"}: + let tweet = parseGraphTweet(tweetResult) + tweet.pinned = true + if not tweet.available and tweet.tombstone.len == 0: + let entryId = i{"entry", "entryId"}.getEntryId + if entryId.len > 0: + tweet.id = parseBiggestInt(entryId) + result.pinned = some tweet proc parseGraphSearch*(js: JsonNode; after=""): Timeline = result = Timeline(beginning: after.len == 0) diff --git a/src/routes/embed.nim b/src/routes/embed.nim index baaec68..994364b 100644 --- a/src/routes/embed.nim +++ b/src/routes/embed.nim @@ -10,22 +10,22 @@ export api, embed, vdom, tweet, general, router_utils proc createEmbedRouter*(cfg: Config) = router embed: get "/i/videos/tweet/@id": - let convo = await getTweet(@"id") - if convo == nil or convo.tweet == nil or convo.tweet.video.isNone: + let tweet = await getGraphTweetResult(@"id") + if tweet == nil or tweet.video.isNone: resp Http404 - resp renderVideoEmbed(convo.tweet, cfg, request) + resp renderVideoEmbed(tweet, cfg, request) get "/@user/status/@id/embed": let - convo = await getTweet(@"id") + tweet = await getGraphTweetResult(@"id") prefs = cookiePrefs() path = getPath() - if convo == nil or convo.tweet == nil: + if tweet == nil: resp Http404 - resp renderTweetEmbed(convo.tweet, path, prefs, cfg, request) + resp renderTweetEmbed(tweet, path, prefs, cfg, request) get "/embed/Tweet.html": let id = @"id" diff --git a/src/routes/rss.nim b/src/routes/rss.nim index 1323ed3..8eec399 100644 --- a/src/routes/rss.nim +++ b/src/routes/rss.nim @@ -27,14 +27,12 @@ proc timelineRss*(req: Request; cfg: Config; query: Query): Future[Rss] {.async. else: var q = query q.fromUser = names - profile = Profile( - tweets: await getGraphSearch(q, after), - # this is kinda dumb - user: User( - username: name, - fullname: names.join(" | "), - userpic: "https://abs.twimg.com/sticky/default_profile_images/default_profile.png" - ) + profile = await getGraphSearch(q, after) + # this is kinda dumb + profile.user = User( + username: name, + fullname: names.join(" | "), + userpic: "https://abs.twimg.com/sticky/default_profile_images/default_profile.png" ) if profile.user.suspended: @@ -61,29 +59,29 @@ template respRss*(rss, page) = proc createRssRouter*(cfg: Config) = router rss: - get "/search/rss": - cond cfg.enableRss - if @"q".len > 200: - resp Http400, showError("Search input too long.", cfg) + # get "/search/rss": + # cond cfg.enableRss + # if @"q".len > 200: + # resp Http400, showError("Search input too long.", cfg) - let query = initQuery(params(request)) - if query.kind != tweets: - resp Http400, showError("Only Tweet searches are allowed for RSS feeds.", cfg) + # let query = initQuery(params(request)) + # if query.kind != tweets: + # resp Http400, showError("Only Tweet searches are allowed for RSS feeds.", cfg) - let - cursor = getCursor() - key = redisKey("search", $hash(genQueryUrl(query)), cursor) + # let + # cursor = getCursor() + # key = redisKey("search", $hash(genQueryUrl(query)), cursor) - var rss = await getCachedRss(key) - if rss.cursor.len > 0: - respRss(rss, "Search") + # var rss = await getCachedRss(key) + # if rss.cursor.len > 0: + # respRss(rss, "Search") - let tweets = await getGraphSearch(query, cursor) - rss.cursor = tweets.bottom - rss.feed = renderSearchRss(tweets.content, query.text, genQueryUrl(query), cfg) + # let tweets = await getGraphSearch(query, cursor) + # rss.cursor = tweets.bottom + # rss.feed = renderSearchRss(tweets.content, query.text, genQueryUrl(query), cfg) - await cacheRss(key, rss) - respRss(rss, "Search") + # await cacheRss(key, rss) + # respRss(rss, "Search") get "/@name/rss": cond cfg.enableRss @@ -112,7 +110,7 @@ proc createRssRouter*(cfg: Config) = case tab of "with_replies": getReplyQuery(name) of "media": getMediaQuery(name) - of "search": initQuery(params(request), name=name) + # of "search": initQuery(params(request), name=name) else: Query(fromUser: @[name]) let searchKey = if tab != "search": "" diff --git a/src/routes/search.nim b/src/routes/search.nim index 02c14e3..ed2c397 100644 --- a/src/routes/search.nim +++ b/src/routes/search.nim @@ -34,11 +34,15 @@ proc createSearchRouter*(cfg: Config) = users = Result[User](beginning: true, query: query) resp renderMain(renderUserSearch(users, prefs), request, cfg, prefs, title) of tweets: - let - tweets = await getGraphSearch(query, getCursor()) - rss = "/search/rss?" & genQueryUrl(query) - resp renderMain(renderTweetSearch(tweets, prefs, getPath()), - request, cfg, prefs, title, rss=rss) + # let + # tweets = await getGraphSearch(query, getCursor()) + # rss = "/search/rss?" & genQueryUrl(query) + # resp renderMain(renderTweetSearch(tweets, prefs, getPath()), + # request, cfg, prefs, title, rss=rss) + var fakeTimeline = Timeline(beginning: true) + fakeTimeline.content.add Tweet(tombstone: "Tweet search is unavailable for now") + + resp renderMain(renderTweetSearch(fakeTimeline, prefs, getPath()), request, cfg, prefs, title) else: resp Http404, showError("Invalid search", cfg) diff --git a/src/routes/timeline.nim b/src/routes/timeline.nim index 331b8ae..4ac60d2 100644 --- a/src/routes/timeline.nim +++ b/src/routes/timeline.nim @@ -45,34 +45,24 @@ proc fetchProfile*(after: string; query: Query; skipRail=false; after.setLen 0 let - timeline = - case query.kind - of posts: getGraphUserTweets(userId, TimelineKind.tweets, after) - of replies: getGraphUserTweets(userId, TimelineKind.replies, after) - of media: getGraphUserTweets(userId, TimelineKind.media, after) - else: getGraphSearch(query, after) - rail = skipIf(skipRail or query.kind == media, @[]): getCachedPhotoRail(name) - user = await getCachedUser(name) + user = getCachedUser(name) - var pinned: Option[Tweet] - if not skipPinned and user.pinnedTweet > 0 and - after.len == 0 and query.kind in {posts, replies}: - let tweet = await getCachedTweet(user.pinnedTweet) - if not tweet.isNil: - tweet.pinned = true - tweet.user = user - pinned = some tweet + result = + case query.kind + of posts: await getGraphUserTweets(userId, TimelineKind.tweets, after) + of replies: await getGraphUserTweets(userId, TimelineKind.replies, after) + of media: await getGraphUserTweets(userId, TimelineKind.media, after) + else: Profile(tweets: Timeline(beginning: true, content: @[Chain(content: + @[Tweet(tombstone: "Tweet search is unavailable for now")] + )])) + # else: await getGraphSearch(query, after) - result = Profile( - user: user, - pinned: pinned, - tweets: await timeline, - photoRail: await rail - ) + result.user = await user + result.photoRail = await rail if result.user.protected or result.user.suspended: return @@ -83,8 +73,11 @@ proc showTimeline*(request: Request; query: Query; cfg: Config; prefs: Prefs; rss, after: string): Future[string] {.async.} = if query.fromUser.len != 1: let - timeline = await getGraphSearch(query, after) - html = renderTweetSearch(timeline, prefs, getPath()) + # timeline = await getGraphSearch(query, after) + timeline = Profile(tweets: Timeline(beginning: true, content: @[Chain(content: + @[Tweet(tombstone: "This features is unavailable for now")] + )])) + html = renderTweetSearch(timeline.tweets, prefs, getPath()) return renderMain(html, request, cfg, prefs, "Multi", rss=rss) var profile = await fetchProfile(after, query, skipPinned=prefs.hidePins) @@ -138,7 +131,7 @@ proc createTimelineRouter*(cfg: Config) = # used for the infinite scroll feature if @"scroll".len > 0: if query.fromUser.len != 1: - var timeline = await getGraphSearch(query, after) + var timeline = (await getGraphSearch(query, after)).tweets if timeline.content.len == 0: resp Http404 timeline.beginning = true resp $renderTweetSearch(timeline, prefs, getPath()) diff --git a/src/sass/tweet/thread.scss b/src/sass/tweet/thread.scss index 5fbad21..19fb3e0 100644 --- a/src/sass/tweet/thread.scss +++ b/src/sass/tweet/thread.scss @@ -110,3 +110,29 @@ margin-left: 58px; padding: 7px 0; } + +.timeline-item.thread.more-replies-thread { + padding: 0 0.75em; + + &::before { + top: 40px; + margin-bottom: 31px; + } + + .more-replies { + display: flex; + padding-top: unset !important; + margin-top: 8px; + + &::before { + display: inline-block; + position: relative; + top: -1px; + line-height: 0.4em; + } + + .more-replies-text { + display: inline; + } + } +} diff --git a/src/tokens.nim b/src/tokens.nim index 6ef81f5..6643de3 100644 --- a/src/tokens.nim +++ b/src/tokens.nim @@ -41,11 +41,10 @@ proc getPoolJson*(): JsonNode = let maxReqs = case api - of Api.timeline: 187 - of Api.listMembers, Api.listBySlug, Api.list, Api.listTweets, - Api.userTweets, Api.userTweetsAndReplies, Api.userMedia, - Api.userRestId, Api.userScreenName, - Api.tweetDetail, Api.tweetResult, Api.search: 500 + of Api.timeline: 180 + of Api.userTweets, Api.userTweetsAndReplies, Api.userRestId, + Api.userScreenName, Api.tweetDetail, Api.tweetResult, Api.search: 500 + of Api.list, Api.listTweets, Api.listMembers, Api.listBySlug, Api.userMedia: 500 of Api.userSearch: 900 reqs = maxReqs - token.apis[api].remaining diff --git a/src/types.nim b/src/types.nim index 4dca5f0..e7f3303 100644 --- a/src/types.nim +++ b/src/types.nim @@ -222,7 +222,7 @@ type after*: Chain replies*: Result[Chain] - Timeline* = Result[Tweet] + Timeline* = Result[Chain] Profile* = object user*: User @@ -274,3 +274,6 @@ type proc contains*(thread: Chain; tweet: Tweet): bool = thread.content.anyIt(it.id == tweet.id) + +proc add*(timeline: var seq[Chain]; tweet: Tweet) = + timeline.add Chain(content: @[tweet]) diff --git a/src/views/rss.nimf b/src/views/rss.nimf index 96f6466..ce2518a 100644 --- a/src/views/rss.nimf +++ b/src/views/rss.nimf @@ -56,24 +56,29 @@ Twitter feed for: ${desc}. Generated by ${cfg.hostname} #end if #end proc # -#proc renderRssTweets(tweets: seq[Tweet]; cfg: Config): string = +#proc renderRssTweets(tweets: seq[Chain]; cfg: Config; userId=""): string = #let urlPrefix = getUrlPrefix(cfg) #var links: seq[string] -#for t in tweets: -# let retweet = if t.retweet.isSome: t.user.username else: "" -# let tweet = if retweet.len > 0: t.retweet.get else: t -# let link = getLink(tweet) -# if link in links: continue -# end if -# links.add link - - ${getTitle(tweet, retweet)} - @${tweet.user.username} - - ${getRfc822Time(tweet)} - ${urlPrefix & link} - ${urlPrefix & link} - +#for c in tweets: +# for t in c.content: +# if userId.len > 0 and t.user.id != userId: continue +# end if +# +# let retweet = if t.retweet.isSome: t.user.username else: "" +# let tweet = if retweet.len > 0: t.retweet.get else: t +# let link = getLink(tweet) +# if link in links: continue +# end if +# links.add link + + ${getTitle(tweet, retweet)} + @${tweet.user.username} + + ${getRfc822Time(tweet)} + ${urlPrefix & link} + ${urlPrefix & link} + +# end for #end for #end proc # @@ -102,13 +107,13 @@ Twitter feed for: ${desc}. Generated by ${cfg.hostname} 128 #if profile.tweets.content.len > 0: -${renderRssTweets(profile.tweets.content, cfg)} +${renderRssTweets(profile.tweets.content, cfg, userId=profile.user.id)} #end if #end proc # -#proc renderListRss*(tweets: seq[Tweet]; list: List; cfg: Config): string = +#proc renderListRss*(tweets: seq[Chain]; list: List; cfg: Config): string = #let link = &"{getUrlPrefix(cfg)}/i/lists/{list.id}" #result = "" @@ -125,7 +130,7 @@ ${renderRssTweets(tweets, cfg)} #end proc # -#proc renderSearchRss*(tweets: seq[Tweet]; name, param: string; cfg: Config): string = +#proc renderSearchRss*(tweets: seq[Chain]; name, param: string; cfg: Config): string = #let link = &"{getUrlPrefix(cfg)}/search" #let escName = xmltree.escape(name) #result = "" diff --git a/src/views/search.nim b/src/views/search.nim index 72c59f5..401e6da 100644 --- a/src/views/search.nim +++ b/src/views/search.nim @@ -88,7 +88,7 @@ proc renderSearchPanel*(query: Query): VNode = span(class="search-title"): text "Near" genInput("near", "", query.near, "Location...", autofocus=false) -proc renderTweetSearch*(results: Result[Tweet]; prefs: Prefs; path: string; +proc renderTweetSearch*(results: Timeline; prefs: Prefs; path: string; pinned=none(Tweet)): VNode = let query = results.query buildHtml(tdiv(class="timeline-container")): diff --git a/src/views/timeline.nim b/src/views/timeline.nim index 54cad7a..8ae888e 100644 --- a/src/views/timeline.nim +++ b/src/views/timeline.nim @@ -1,5 +1,5 @@ # SPDX-License-Identifier: AGPL-3.0-only -import strutils, strformat, sequtils, algorithm, uri, options +import strutils, strformat, algorithm, uri, options import karax/[karaxdsl, vdom] import ".."/[types, query, formatters] @@ -43,20 +43,18 @@ proc renderThread(thread: seq[Tweet]; prefs: Prefs; path: string): VNode = buildHtml(tdiv(class="thread-line")): let sortedThread = thread.sortedByIt(it.id) for i, tweet in sortedThread: + # thread has a gap, display "more replies" link + if i > 0 and tweet.replyId != sortedThread[i - 1].id: + tdiv(class="timeline-item thread more-replies-thread"): + tdiv(class="more-replies"): + a(class="more-replies-text", href=getLink(tweet)): + text "more replies" + let show = i == thread.high and sortedThread[0].id != tweet.threadId let header = if tweet.pinned or tweet.retweet.isSome: "with-header " else: "" renderTweet(tweet, prefs, path, class=(header & "thread"), index=i, last=(i == thread.high), showThread=show) -proc threadFilter(tweets: openArray[Tweet]; threads: openArray[int64]; it: Tweet): seq[Tweet] = - result = @[it] - if it.retweet.isSome or it.replyId in threads: return - for t in tweets: - if t.id == result[0].replyId: - result.insert t - elif t.replyId == result[0].id: - result.add t - proc renderUser(user: User; prefs: Prefs): VNode = buildHtml(tdiv(class="timeline-item")): a(class="tweet-link", href=("/" & user.username)) @@ -89,7 +87,7 @@ proc renderTimelineUsers*(results: Result[User]; prefs: Prefs; path=""): VNode = else: renderNoMore() -proc renderTimelineTweets*(results: Result[Tweet]; prefs: Prefs; path: string; +proc renderTimelineTweets*(results: Timeline; prefs: Prefs; path: string; pinned=none(Tweet)): VNode = buildHtml(tdiv(class="timeline")): if not results.beginning: @@ -105,26 +103,26 @@ proc renderTimelineTweets*(results: Result[Tweet]; prefs: Prefs; path: string; else: renderNoneFound() else: - var - threads: seq[int64] - retweets: seq[int64] + var retweets: seq[int64] - for tweet in results.content: - let rt = if tweet.retweet.isSome: get(tweet.retweet).id else: 0 + for thread in results.content: + if thread.content.len == 1: + let + tweet = thread.content[0] + retweetId = if tweet.retweet.isSome: get(tweet.retweet).id else: 0 - if tweet.id in threads or rt in retweets or tweet.id in retweets or - tweet.pinned and prefs.hidePins: continue + if retweetId in retweets or tweet.id in retweets or + tweet.pinned and prefs.hidePins: + continue - let thread = results.content.threadFilter(threads, tweet) - if thread.len < 2: var hasThread = tweet.hasThread - if rt != 0: - retweets &= rt + if retweetId != 0 and tweet.retweet.isSome: + retweets &= retweetId hasThread = get(tweet.retweet).hasThread renderTweet(tweet, prefs, path, showThread=hasThread) else: - renderThread(thread, prefs, path) - threads &= thread.mapIt(it.id) + renderThread(thread.content, prefs, path) - renderMore(results.query, results.bottom) + if results.bottom.len > 0: + renderMore(results.query, results.bottom) renderToTop() diff --git a/src/views/tweet.nim b/src/views/tweet.nim index 3338b71..f47ae9a 100644 --- a/src/views/tweet.nim +++ b/src/views/tweet.nim @@ -14,15 +14,14 @@ proc renderMiniAvatar(user: User; prefs: Prefs): VNode = buildHtml(): img(class=(prefs.getAvatarClass & " mini"), src=url) -proc renderHeader(tweet: Tweet; retweet: string; prefs: Prefs): VNode = +proc renderHeader(tweet: Tweet; retweet: string; pinned: bool; prefs: Prefs): VNode = buildHtml(tdiv): - if retweet.len > 0: - tdiv(class="retweet-header"): - span: icon "retweet", retweet & " retweeted" - - if tweet.pinned: + if pinned: tdiv(class="pinned"): span: icon "pin", "Pinned Tweet" + elif retweet.len > 0: + tdiv(class="retweet-header"): + span: icon "retweet", retweet & " retweeted" tdiv(class="tweet-header"): a(class="tweet-avatar", href=("/" & tweet.user.username)): @@ -290,7 +289,10 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class=""; index=0; if tweet.quote.isSome: renderQuote(tweet.quote.get(), prefs, path) - let fullTweet = tweet + let + fullTweet = tweet + pinned = tweet.pinned + var retweet: string var tweet = fullTweet if tweet.retweet.isSome: @@ -303,7 +305,7 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class=""; index=0; tdiv(class="tweet-body"): var views = "" - renderHeader(tweet, retweet, prefs) + renderHeader(tweet, retweet, pinned, prefs) if not afterTweet and index == 0 and tweet.reply.len > 0 and (tweet.reply.len > 1 or tweet.reply[0] != tweet.user.username): diff --git a/tests/test_card.py b/tests/test_card.py index 696b9d5..f84ddca 100644 --- a/tests/test_card.py +++ b/tests/test_card.py @@ -16,7 +16,12 @@ card = [ ['FluentAI/status/1116417904831029248', 'Amazon’s Alexa isn’t just AI — thousands of humans are listening', 'One of the only ways to improve Alexa is to have human beings check it for errors', - 'theverge.com', True] + 'theverge.com', True], + + ['nim_lang/status/1082989146040340480', + 'Nim in 2018: A short recap', + 'There were several big news in the Nim world in 2018 – two new major releases, partnership with Status, and much more. But let us go chronologically.', + 'nim-lang.org', True] ] no_thumb = [ @@ -33,12 +38,7 @@ no_thumb = [ ['voidtarget/status/1133028231672582145', 'sinkingsugar/nimqt-example', 'A sample of a Qt app written using mostly nim. Contribute to sinkingsugar/nimqt-example development by creating an account on GitHub.', - 'github.com'], - - ['nim_lang/status/1082989146040340480', - 'Nim in 2018: A short recap', - 'Posted by u/miran1 - 36 votes and 46 comments', - 'reddit.com'] + 'github.com'] ] playable = [ @@ -53,17 +53,6 @@ playable = [ 'youtube.com'] ] -# promo = [ - # ['BangOlufsen/status/1145698701517754368', - # 'Upgrade your journey', '', - # 'www.bang-olufsen.com'], - - # ['BangOlufsen/status/1154934429900406784', - # 'Learn more about Beosound Shape', '', - # 'www.bang-olufsen.com'] -# ] - - class CardTest(BaseTestCase): @parameterized.expand(card) def test_card(self, tweet, title, description, destination, large): @@ -98,13 +87,3 @@ class CardTest(BaseTestCase): self.assert_element_visible('.card-overlay') if len(description) > 0: self.assert_text(description, c.description) - - # @parameterized.expand(promo) - # def test_card_promo(self, tweet, title, description, destination): - # self.open_nitter(tweet) - # c = Card(Conversation.main + " ") - # self.assert_text(title, c.title) - # self.assert_text(destination, c.destination) - # self.assert_element_visible('.video-overlay') - # if len(description) > 0: - # self.assert_text(description, c.description) diff --git a/tests/test_profile.py b/tests/test_profile.py index f9b5047..4c75ad2 100644 --- a/tests/test_profile.py +++ b/tests/test_profile.py @@ -66,8 +66,8 @@ class ProfileTest(BaseTestCase): self.assert_text(f'User "{username}" not found') def test_suspended(self): - self.open_nitter('user') - self.assert_text('User "user" has been suspended') + self.open_nitter('suspendme') + self.assert_text('User "suspendme" has been suspended') @parameterized.expand(banner_image) def test_banner_image(self, username, url): diff --git a/tests/test_search.py b/tests/test_search.py index 80ee36a..62c4640 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -2,8 +2,8 @@ from base import BaseTestCase from parameterized import parameterized -class SearchTest(BaseTestCase): - @parameterized.expand([['@mobile_test'], ['@mobile_test_2']]) - def test_username_search(self, username): - self.search_username(username) - self.assert_text(f'{username}') +#class SearchTest(BaseTestCase): + #@parameterized.expand([['@mobile_test'], ['@mobile_test_2']]) + #def test_username_search(self, username): + #self.search_username(username) + #self.assert_text(f'{username}') diff --git a/tests/test_tweet.py b/tests/test_tweet.py index 9209e70..e4231a4 100644 --- a/tests/test_tweet.py +++ b/tests/test_tweet.py @@ -74,9 +74,9 @@ retweet = [ [3, 'mobile_test_8', 'mobile test 8', 'jack', '@jack', 'twttr'] ] -reply = [ - ['mobile_test/with_replies', 15] -] +# reply = [ +# ['mobile_test/with_replies', 15] +# ] class TweetTest(BaseTestCase): @@ -137,8 +137,8 @@ class TweetTest(BaseTestCase): self.open_nitter(tweet) self.assert_text('Tweet not found', '.error-panel') - @parameterized.expand(reply) - def test_thread(self, tweet, num): - self.open_nitter(tweet) - thread = self.find_element(f'.timeline > div:nth-child({num})') - self.assertIn(thread.get_attribute('class'), 'thread-line') + # @parameterized.expand(reply) + # def test_thread(self, tweet, num): + # self.open_nitter(tweet) + # thread = self.find_element(f'.timeline > div:nth-child({num})') + # self.assertIn(thread.get_attribute('class'), 'thread-line') From b290f6fd29ac20717bad359fc55d32822d3e054d Mon Sep 17 00:00:00 2001 From: Zed Date: Wed, 12 Jul 2023 01:34:39 +0200 Subject: [PATCH 112/409] Optimize timeline data structure --- src/parser.nim | 2 +- src/routes/timeline.nim | 8 ++------ src/types.nim | 10 ++++++---- src/views/rss.nimf | 16 ++++++++-------- src/views/timeline.nim | 8 ++++---- 5 files changed, 21 insertions(+), 23 deletions(-) diff --git a/src/parser.nim b/src/parser.nim index 7b178f3..5dc96df 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -486,7 +486,7 @@ proc parseGraphTimeline*(js: JsonNode; root: string; after=""): Profile = result.tweets.content.add tweet elif "-conversation-" in entryId or entryId.startsWith("homeConversation"): let (thread, self) = parseGraphThread(e) - result.tweets.content.add thread + result.tweets.content.add thread.content elif entryId.startsWith("cursor-bottom"): result.tweets.bottom = e{"content", "value"}.getStr if after.len == 0 and i{"__typename"}.getStr == "TimelinePinEntry": diff --git a/src/routes/timeline.nim b/src/routes/timeline.nim index 4ac60d2..e62c9e0 100644 --- a/src/routes/timeline.nim +++ b/src/routes/timeline.nim @@ -56,9 +56,7 @@ proc fetchProfile*(after: string; query: Query; skipRail=false; of posts: await getGraphUserTweets(userId, TimelineKind.tweets, after) of replies: await getGraphUserTweets(userId, TimelineKind.replies, after) of media: await getGraphUserTweets(userId, TimelineKind.media, after) - else: Profile(tweets: Timeline(beginning: true, content: @[Chain(content: - @[Tweet(tombstone: "Tweet search is unavailable for now")] - )])) + else: Profile(tweets: Timeline(beginning: true, content: @[@[Tweet(tombstone: "Tweet search is unavailable for now")]])) # else: await getGraphSearch(query, after) result.user = await user @@ -74,9 +72,7 @@ proc showTimeline*(request: Request; query: Query; cfg: Config; prefs: Prefs; if query.fromUser.len != 1: let # timeline = await getGraphSearch(query, after) - timeline = Profile(tweets: Timeline(beginning: true, content: @[Chain(content: - @[Tweet(tombstone: "This features is unavailable for now")] - )])) + timeline = Profile(tweets: Timeline(beginning: true, content: @[@[Tweet(tombstone: "This features is unavailable for now")]])) html = renderTweetSearch(timeline.tweets, prefs, getPath()) return renderMain(html, request, cfg, prefs, "Multi", rss=rss) diff --git a/src/types.nim b/src/types.nim index e7f3303..f7d5f6b 100644 --- a/src/types.nim +++ b/src/types.nim @@ -205,6 +205,8 @@ type video*: Option[Video] photos*: seq[string] + Tweets* = seq[Tweet] + Result*[T] = object content*: seq[T] top*, bottom*: string @@ -212,7 +214,7 @@ type query*: Query Chain* = object - content*: seq[Tweet] + content*: Tweets hasMore*: bool cursor*: string @@ -222,7 +224,7 @@ type after*: Chain replies*: Result[Chain] - Timeline* = Result[Chain] + Timeline* = Result[Tweets] Profile* = object user*: User @@ -275,5 +277,5 @@ type proc contains*(thread: Chain; tweet: Tweet): bool = thread.content.anyIt(it.id == tweet.id) -proc add*(timeline: var seq[Chain]; tweet: Tweet) = - timeline.add Chain(content: @[tweet]) +proc add*(timeline: var seq[Tweets]; tweet: Tweet) = + timeline.add @[tweet] diff --git a/src/views/rss.nimf b/src/views/rss.nimf index ce2518a..036a7b9 100644 --- a/src/views/rss.nimf +++ b/src/views/rss.nimf @@ -56,16 +56,16 @@ Twitter feed for: ${desc}. Generated by ${cfg.hostname} #end if #end proc # -#proc renderRssTweets(tweets: seq[Chain]; cfg: Config; userId=""): string = +#proc renderRssTweets(tweets: seq[Tweets]; cfg: Config; userId=""): string = #let urlPrefix = getUrlPrefix(cfg) #var links: seq[string] -#for c in tweets: -# for t in c.content: -# if userId.len > 0 and t.user.id != userId: continue +#for thread in tweets: +# for tweet in thread: +# if userId.len > 0 and tweet.user.id != userId: continue # end if # -# let retweet = if t.retweet.isSome: t.user.username else: "" -# let tweet = if retweet.len > 0: t.retweet.get else: t +# let retweet = if tweet.retweet.isSome: tweet.user.username else: "" +# let tweet = if retweet.len > 0: tweet.retweet.get else: tweet # let link = getLink(tweet) # if link in links: continue # end if @@ -113,7 +113,7 @@ ${renderRssTweets(profile.tweets.content, cfg, userId=profile.user.id)} #end proc # -#proc renderListRss*(tweets: seq[Chain]; list: List; cfg: Config): string = +#proc renderListRss*(tweets: seq[Tweets]; list: List; cfg: Config): string = #let link = &"{getUrlPrefix(cfg)}/i/lists/{list.id}" #result = "" @@ -130,7 +130,7 @@ ${renderRssTweets(tweets, cfg)} #end proc # -#proc renderSearchRss*(tweets: seq[Chain]; name, param: string; cfg: Config): string = +#proc renderSearchRss*(tweets: seq[Tweets]; name, param: string; cfg: Config): string = #let link = &"{getUrlPrefix(cfg)}/search" #let escName = xmltree.escape(name) #result = "" diff --git a/src/views/timeline.nim b/src/views/timeline.nim index 8ae888e..abeb6d3 100644 --- a/src/views/timeline.nim +++ b/src/views/timeline.nim @@ -39,7 +39,7 @@ proc renderNoneFound(): VNode = h2(class="timeline-none"): text "No items found" -proc renderThread(thread: seq[Tweet]; prefs: Prefs; path: string): VNode = +proc renderThread(thread: Tweets; prefs: Prefs; path: string): VNode = buildHtml(tdiv(class="thread-line")): let sortedThread = thread.sortedByIt(it.id) for i, tweet in sortedThread: @@ -106,9 +106,9 @@ proc renderTimelineTweets*(results: Timeline; prefs: Prefs; path: string; var retweets: seq[int64] for thread in results.content: - if thread.content.len == 1: + if thread.len == 1: let - tweet = thread.content[0] + tweet = thread[0] retweetId = if tweet.retweet.isSome: get(tweet.retweet).id else: 0 if retweetId in retweets or tweet.id in retweets or @@ -121,7 +121,7 @@ proc renderTimelineTweets*(results: Timeline; prefs: Prefs; path: string; hasThread = get(tweet.retweet).hasThread renderTweet(tweet, prefs, path, showThread=hasThread) else: - renderThread(thread.content, prefs, path) + renderThread(thread, prefs, path) if results.bottom.len > 0: renderMore(results.query, results.bottom) From 67203a431d9242e2f0d5fbdbfa86ba55f0a4cc54 Mon Sep 17 00:00:00 2001 From: Zed Date: Wed, 12 Jul 2023 03:37:44 +0200 Subject: [PATCH 113/409] Add back search --- src/api.nim | 16 ++++++++++++++++ src/consts.nim | 1 + src/parser.nim | 34 ++++++++++++++++++++++++++++++++-- src/routes/rss.nim | 38 +++++++++++++++++++------------------- src/routes/search.nim | 14 +++++--------- src/routes/timeline.nim | 8 +++----- src/tokens.nim | 4 ++-- 7 files changed, 78 insertions(+), 37 deletions(-) diff --git a/src/api.nim b/src/api.nim index d99eb3d..60af68d 100644 --- a/src/api.nim +++ b/src/api.nim @@ -115,6 +115,22 @@ proc getGraphSearch*(query: Query; after=""): Future[Profile] {.async.} = result = Profile(tweets: parseGraphSearch(await fetch(url, Api.search), after)) result.tweets.query = query +proc getTweetSearch*(query: Query; after=""): Future[Timeline] {.async.} = + let q = genQueryParam(query) + if q.len == 0 or q == emptyQuery: + return Timeline(query: query, beginning: true) + + let url = tweetSearch ? genParams({ + "q": q, + "tweet_search_mode": "live", + "max_id": after + }) + + result = parseTweetSearch(await fetch(url, Api.search)) + result.query = query + if after.len == 0: + result.beginning = true + proc getUserSearch*(query: Query; page="1"): Future[Result[User]] {.async.} = if query.text.len == 0: return Result[User](query: query, beginning: true) diff --git a/src/consts.nim b/src/consts.nim index 184f9da..8dd1b14 100644 --- a/src/consts.nim +++ b/src/consts.nim @@ -9,6 +9,7 @@ const photoRail* = api / "1.1/statuses/media_timeline.json" userSearch* = api / "1.1/users/search.json" + tweetSearch* = api / "1.1/search/tweets.json" graphql = api / "graphql" graphUser* = graphql / "u7wQyGi6oExe8_TRWGMq4Q/UserResultByScreenNameQuery" diff --git a/src/parser.nim b/src/parser.nim index 5dc96df..f298160 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -82,12 +82,16 @@ proc parseVideo(js: JsonNode): Video = result = Video( thumb: js{"media_url_https"}.getImageStr, views: js{"ext", "mediaStats", "r", "ok", "viewCount"}.getStr($js{"mediaStats", "viewCount"}.getInt), - available: js{"ext_media_availability", "status"}.getStr.toLowerAscii == "available", + available: true, title: js{"ext_alt_text"}.getStr, durationMs: js{"video_info", "duration_millis"}.getInt # playbackType: mp4 ) + with status, js{"ext_media_availability", "status"}: + if status.getStr.len > 0 and status.getStr.toLowerAscii != "available": + result.available = false + with title, js{"additional_media_info", "title"}: result.title = title.getStr @@ -219,7 +223,9 @@ proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet = if result.hasThread and result.threadId == 0: result.threadId = js{"self_thread", "id_str"}.getId - if js{"is_quote_status"}.getBool: + if "retweeted_status" in js: + result.retweet = some Tweet() + elif js{"is_quote_status"}.getBool: result.quote = some Tweet(id: js{"quoted_status_id_str"}.getId) # legacy @@ -281,6 +287,30 @@ proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet = result.text.removeSuffix(" Learn more.") result.available = false +proc parseLegacyTweet(js: JsonNode): Tweet = + result = parseTweet(js, js{"card"}) + if not result.isNil and result.available: + result.user = parseUser(js{"user"}) + + if result.quote.isSome: + result.quote = some parseLegacyTweet(js{"quoted_status"}) + +proc parseTweetSearch*(js: JsonNode): Timeline = + if js.kind == JNull or "statuses" notin js: + return Timeline(beginning: true) + + for tweet in js{"statuses"}: + let parsed = parseLegacyTweet(tweet) + + if parsed.retweet.isSome: + parsed.retweet = some parseLegacyTweet(tweet{"retweeted_status"}) + + result.content.add @[parsed] + + let cursor = js{"search_metadata", "next_results"}.getStr + if cursor.len > 0 and "max_id" in cursor: + result.bottom = cursor[cursor.find("=") + 1 .. cursor.find("&q=")] + proc finalizeTweet(global: GlobalObjects; id: string): Tweet = let intId = if id.len > 0: parseBiggestInt(id) else: 0 result = global.tweets.getOrDefault(id, Tweet(id: intId)) diff --git a/src/routes/rss.nim b/src/routes/rss.nim index 8eec399..d378396 100644 --- a/src/routes/rss.nim +++ b/src/routes/rss.nim @@ -27,7 +27,7 @@ proc timelineRss*(req: Request; cfg: Config; query: Query): Future[Rss] {.async. else: var q = query q.fromUser = names - profile = await getGraphSearch(q, after) + profile.tweets = await getTweetSearch(q, after) # this is kinda dumb profile.user = User( username: name, @@ -59,29 +59,29 @@ template respRss*(rss, page) = proc createRssRouter*(cfg: Config) = router rss: - # get "/search/rss": - # cond cfg.enableRss - # if @"q".len > 200: - # resp Http400, showError("Search input too long.", cfg) + get "/search/rss": + cond cfg.enableRss + if @"q".len > 200: + resp Http400, showError("Search input too long.", cfg) - # let query = initQuery(params(request)) - # if query.kind != tweets: - # resp Http400, showError("Only Tweet searches are allowed for RSS feeds.", cfg) + let query = initQuery(params(request)) + if query.kind != tweets: + resp Http400, showError("Only Tweet searches are allowed for RSS feeds.", cfg) - # let - # cursor = getCursor() - # key = redisKey("search", $hash(genQueryUrl(query)), cursor) + let + cursor = getCursor() + key = redisKey("search", $hash(genQueryUrl(query)), cursor) - # var rss = await getCachedRss(key) - # if rss.cursor.len > 0: - # respRss(rss, "Search") + var rss = await getCachedRss(key) + if rss.cursor.len > 0: + respRss(rss, "Search") - # let tweets = await getGraphSearch(query, cursor) - # rss.cursor = tweets.bottom - # rss.feed = renderSearchRss(tweets.content, query.text, genQueryUrl(query), cfg) + let tweets = await getTweetSearch(query, cursor) + rss.cursor = tweets.bottom + rss.feed = renderSearchRss(tweets.content, query.text, genQueryUrl(query), cfg) - # await cacheRss(key, rss) - # respRss(rss, "Search") + await cacheRss(key, rss) + respRss(rss, "Search") get "/@name/rss": cond cfg.enableRss diff --git a/src/routes/search.nim b/src/routes/search.nim index ed2c397..c270df5 100644 --- a/src/routes/search.nim +++ b/src/routes/search.nim @@ -34,15 +34,11 @@ proc createSearchRouter*(cfg: Config) = users = Result[User](beginning: true, query: query) resp renderMain(renderUserSearch(users, prefs), request, cfg, prefs, title) of tweets: - # let - # tweets = await getGraphSearch(query, getCursor()) - # rss = "/search/rss?" & genQueryUrl(query) - # resp renderMain(renderTweetSearch(tweets, prefs, getPath()), - # request, cfg, prefs, title, rss=rss) - var fakeTimeline = Timeline(beginning: true) - fakeTimeline.content.add Tweet(tombstone: "Tweet search is unavailable for now") - - resp renderMain(renderTweetSearch(fakeTimeline, prefs, getPath()), request, cfg, prefs, title) + let + tweets = await getTweetSearch(query, getCursor()) + rss = "/search/rss?" & genQueryUrl(query) + resp renderMain(renderTweetSearch(tweets, prefs, getPath()), + request, cfg, prefs, title, rss=rss) else: resp Http404, showError("Invalid search", cfg) diff --git a/src/routes/timeline.nim b/src/routes/timeline.nim index e62c9e0..ef3d012 100644 --- a/src/routes/timeline.nim +++ b/src/routes/timeline.nim @@ -56,8 +56,7 @@ proc fetchProfile*(after: string; query: Query; skipRail=false; of posts: await getGraphUserTweets(userId, TimelineKind.tweets, after) of replies: await getGraphUserTweets(userId, TimelineKind.replies, after) of media: await getGraphUserTweets(userId, TimelineKind.media, after) - else: Profile(tweets: Timeline(beginning: true, content: @[@[Tweet(tombstone: "Tweet search is unavailable for now")]])) - # else: await getGraphSearch(query, after) + else: Profile(tweets: await getTweetSearch(query, after)) result.user = await user result.photoRail = await rail @@ -71,9 +70,8 @@ proc showTimeline*(request: Request; query: Query; cfg: Config; prefs: Prefs; rss, after: string): Future[string] {.async.} = if query.fromUser.len != 1: let - # timeline = await getGraphSearch(query, after) - timeline = Profile(tweets: Timeline(beginning: true, content: @[@[Tweet(tombstone: "This features is unavailable for now")]])) - html = renderTweetSearch(timeline.tweets, prefs, getPath()) + timeline = await getTweetSearch(query, after) + html = renderTweetSearch(timeline, prefs, getPath()) return renderMain(html, request, cfg, prefs, "Multi", rss=rss) var profile = await fetchProfile(after, query, skipPinned=prefs.hidePins) diff --git a/src/tokens.nim b/src/tokens.nim index 6643de3..531f557 100644 --- a/src/tokens.nim +++ b/src/tokens.nim @@ -41,9 +41,9 @@ proc getPoolJson*(): JsonNode = let maxReqs = case api - of Api.timeline: 180 + of Api.timeline, Api.search: 180 of Api.userTweets, Api.userTweetsAndReplies, Api.userRestId, - Api.userScreenName, Api.tweetDetail, Api.tweetResult, Api.search: 500 + Api.userScreenName, Api.tweetDetail, Api.tweetResult: 500 of Api.list, Api.listTweets, Api.listMembers, Api.listBySlug, Api.userMedia: 500 of Api.userSearch: 900 reqs = maxReqs - token.apis[api].remaining From afbdbd293e30f614ee288731717868c6d618b55f Mon Sep 17 00:00:00 2001 From: Zed Date: Wed, 12 Jul 2023 03:47:37 +0200 Subject: [PATCH 114/409] Fix protected user photo rail crash --- src/parser.nim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/parser.nim b/src/parser.nim index f298160..b988cf7 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -387,6 +387,10 @@ proc parseTimeline*(js: JsonNode; after=""): Timeline = result.top = cursor{"value"}.getStr proc parsePhotoRail*(js: JsonNode): PhotoRail = + with error, js{"error"}: + if error.getStr == "Not authorized.": + return + for tweet in js: let t = parseTweet(tweet, js{"tweet_card"}) From 4c4d5485a06c37977c54a9ae226bb36cd4dadeab Mon Sep 17 00:00:00 2001 From: Jakub Wilk Date: Fri, 14 Jul 2023 18:11:56 +0200 Subject: [PATCH 115/409] Fix typo (#943) --- src/nitter.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nitter.nim b/src/nitter.nim index 627af75..25a569d 100644 --- a/src/nitter.nim +++ b/src/nitter.nim @@ -87,7 +87,7 @@ routes: error BadClientError: echo error.exc.name, ": ", error.exc.msg - resp Http500, showError("Network error occured, please try again.", cfg) + resp Http500, showError("Network error occurred, please try again.", cfg) error RateLimitError: const link = a("another instance", href = instancesUrl) From f881226b223f1650f7f1621991baa38513ddb61f Mon Sep 17 00:00:00 2001 From: Zed Date: Fri, 14 Jul 2023 21:35:37 +0200 Subject: [PATCH 116/409] Fix video embed --- src/views/embed.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/embed.nim b/src/views/embed.nim index a884cf3..ba49f45 100644 --- a/src/views/embed.nim +++ b/src/views/embed.nim @@ -11,7 +11,7 @@ const doctype = "\n" proc renderVideoEmbed*(tweet: Tweet; cfg: Config; req: Request): string = let thumb = get(tweet.video).thumb let vidUrl = getVideoEmbed(cfg, tweet.id) - let prefs = Prefs(hlsPlayback: true) + let prefs = Prefs(hlsPlayback: true, mp4Playback: true) let node = buildHtml(html(lang="en")): renderHead(prefs, cfg, req, video=vidUrl, images=(@[thumb])) From cc5841df308506356d329662d0f0c2ec4713a35c Mon Sep 17 00:00:00 2001 From: Zed Date: Fri, 21 Jul 2023 18:56:39 +0200 Subject: [PATCH 117/409] Use old timeline endpoint --- src/api.nim | 9 ++++++++- src/consts.nim | 14 ++++++++++++++ src/parser.nim | 36 +++++++++++++++++++++++++----------- src/routes/timeline.nim | 2 +- src/tokens.nim | 6 ++++-- src/types.nim | 1 + 6 files changed, 53 insertions(+), 15 deletions(-) diff --git a/src/api.nim b/src/api.nim index 60af68d..e8d0830 100644 --- a/src/api.nim +++ b/src/api.nim @@ -33,6 +33,13 @@ proc getGraphUserTweets*(id: string; kind: TimelineKind; after=""): Future[Profi js = await fetch(url ? params, apiId) result = parseGraphTimeline(js, "user", after) +proc getTimeline*(id: string; after=""; replies=false): Future[Profile] {.async.} = + if id.len == 0: return + let + ps = genParams({"userId": id, "include_tweet_replies": $replies}, after) + url = oldUserTweets / (id & ".json") ? ps + result = parseTimeline(await fetch(url, Api.timeline), after) + proc getGraphListTweets*(id: string; after=""): Future[Timeline] {.async.} = if id.len == 0: return let @@ -155,7 +162,7 @@ proc getPhotoRail*(name: string): Future[PhotoRail] {.async.} = ps = genParams({"screen_name": name, "trim_user": "true"}, count="18", ext=false) url = photoRail ? ps - result = parsePhotoRail(await fetch(url, Api.timeline)) + result = parsePhotoRail(await fetch(url, Api.photoRail)) proc resolve*(url: string; prefs: Prefs): Future[string] {.async.} = let client = newAsyncHttpClient(maxRedirects=0) diff --git a/src/consts.nim b/src/consts.nim index 8dd1b14..7ba09fb 100644 --- a/src/consts.nim +++ b/src/consts.nim @@ -11,6 +11,8 @@ const userSearch* = api / "1.1/users/search.json" tweetSearch* = api / "1.1/search/tweets.json" + oldUserTweets* = api / "2/timeline/profile" + graphql = api / "graphql" graphUser* = graphql / "u7wQyGi6oExe8_TRWGMq4Q/UserResultByScreenNameQuery" graphUserById* = graphql / "oPppcargziU1uDQHAUmH-A/UserResultByIdQuery" @@ -98,6 +100,18 @@ const "includeHasBirdwatchNotes": false }""" + oldUserTweetsVariables* = """{ + "userId": "$1", $2 + "count": 20, + "includePromotedContent": false, + "withDownvotePerspective": false, + "withReactionsMetadata": false, + "withReactionsPerspective": false, + "withVoice": false, + "withV2Timeline": true +} +""" + userTweetsVariables* = """{ "rest_id": "$1", $2 "count": 20 diff --git a/src/parser.nim b/src/parser.nim index b988cf7..193d77f 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -329,6 +329,16 @@ proc finalizeTweet(global: GlobalObjects; id: string): Tweet = else: result.retweet = some Tweet() +proc parsePin(js: JsonNode; global: GlobalObjects): Tweet = + let pin = js{"pinEntry", "entry", "entryId"}.getStr + if pin.len == 0: return + + let id = pin.getId + if id notin global.tweets: return + + global.tweets[id].pinned = true + return finalizeTweet(global, id) + proc parseGlobalObjects(js: JsonNode): GlobalObjects = result = GlobalObjects() let @@ -339,24 +349,28 @@ proc parseGlobalObjects(js: JsonNode): GlobalObjects = result.users[k] = parseUser(v, k) for k, v in tweets: - var tweet = parseTweet(v, v{"tweet_card"}) + var tweet = parseTweet(v, v{"card"}) if tweet.user.id in result.users: tweet.user = result.users[tweet.user.id] result.tweets[k] = tweet -proc parseInstructions[T](res: var Result[T]; global: GlobalObjects; js: JsonNode) = +proc parseInstructions(res: var Profile; global: GlobalObjects; js: JsonNode) = if js.kind != JArray or js.len == 0: return for i in js: + if res.tweets.beginning and i{"pinEntry"}.notNull: + with pin, parsePin(i, global): + res.pinned = some pin + with r, i{"replaceEntry", "entry"}: if "top" in r{"entryId"}.getStr: - res.top = r.getCursor + res.tweets.top = r.getCursor elif "bottom" in r{"entryId"}.getStr: - res.bottom = r.getCursor + res.tweets.bottom = r.getCursor -proc parseTimeline*(js: JsonNode; after=""): Timeline = - result = Timeline(beginning: after.len == 0) +proc parseTimeline*(js: JsonNode; after=""): Profile = + result = Profile(tweets: Timeline(beginning: after.len == 0)) let global = parseGlobalObjects(? js) let instructions = ? js{"timeline", "instructions"} @@ -374,17 +388,17 @@ proc parseTimeline*(js: JsonNode; after=""): Timeline = if "tweet" in entry or entry.startsWith("sq-I-t") or "tombstone" in entry: let tweet = finalizeTweet(global, e.getEntryId) if not tweet.available: continue - result.content.add tweet + result.tweets.content.add tweet elif "cursor-top" in entry: - result.top = e.getCursor + result.tweets.top = e.getCursor elif "cursor-bottom" in entry: - result.bottom = e.getCursor + result.tweets.bottom = e.getCursor elif entry.startsWith("sq-cursor"): with cursor, e{"content", "operation", "cursor"}: if cursor{"cursorType"}.getStr == "Bottom": - result.bottom = cursor{"value"}.getStr + result.tweets.bottom = cursor{"value"}.getStr else: - result.top = cursor{"value"}.getStr + result.tweets.top = cursor{"value"}.getStr proc parsePhotoRail*(js: JsonNode): PhotoRail = with error, js{"error"}: diff --git a/src/routes/timeline.nim b/src/routes/timeline.nim index ef3d012..b574631 100644 --- a/src/routes/timeline.nim +++ b/src/routes/timeline.nim @@ -53,7 +53,7 @@ proc fetchProfile*(after: string; query: Query; skipRail=false; result = case query.kind - of posts: await getGraphUserTweets(userId, TimelineKind.tweets, after) + of posts: await getTimeline(userId, after) of replies: await getGraphUserTweets(userId, TimelineKind.replies, after) of media: await getGraphUserTweets(userId, TimelineKind.media, after) else: Profile(tweets: await getTweetSearch(query, after)) diff --git a/src/tokens.nim b/src/tokens.nim index 531f557..8a25257 100644 --- a/src/tokens.nim +++ b/src/tokens.nim @@ -41,8 +41,10 @@ proc getPoolJson*(): JsonNode = let maxReqs = case api - of Api.timeline, Api.search: 180 - of Api.userTweets, Api.userTweetsAndReplies, Api.userRestId, + of Api.photoRail, Api.search: 180 + of Api.timeline: 187 + of Api.userTweets: 300 + of Api.userTweetsAndReplies, Api.userRestId, Api.userScreenName, Api.tweetDetail, Api.tweetResult: 500 of Api.list, Api.listTweets, Api.listMembers, Api.listBySlug, Api.userMedia: 500 of Api.userSearch: 900 diff --git a/src/types.nim b/src/types.nim index f7d5f6b..5db9ec3 100644 --- a/src/types.nim +++ b/src/types.nim @@ -18,6 +18,7 @@ type tweetDetail tweetResult timeline + photoRail search userSearch list From 50f821dbd8a7bbea75e9cdf2c4189e108b7b0541 Mon Sep 17 00:00:00 2001 From: Zed Date: Sat, 22 Jul 2023 03:03:45 +0200 Subject: [PATCH 118/409] Use search instead of old timeline endpoint --- src/api.nim | 28 +++--- src/apiutils.nim | 2 +- src/consts.nim | 63 ++++++------- src/parser.nim | 194 ++++++++++++++++++++-------------------- src/routes/timeline.nim | 12 ++- tests/test_timeline.py | 8 +- tests/test_tweet.py | 36 ++++---- 7 files changed, 178 insertions(+), 165 deletions(-) diff --git a/src/api.nim b/src/api.nim index e8d0830..c7dc0e0 100644 --- a/src/api.nim +++ b/src/api.nim @@ -33,12 +33,12 @@ proc getGraphUserTweets*(id: string; kind: TimelineKind; after=""): Future[Profi js = await fetch(url ? params, apiId) result = parseGraphTimeline(js, "user", after) -proc getTimeline*(id: string; after=""; replies=false): Future[Profile] {.async.} = - if id.len == 0: return - let - ps = genParams({"userId": id, "include_tweet_replies": $replies}, after) - url = oldUserTweets / (id & ".json") ? ps - result = parseTimeline(await fetch(url, Api.timeline), after) +# proc getTimeline*(id: string; after=""; replies=false): Future[Profile] {.async.} = +# if id.len == 0: return +# let +# ps = genParams({"userId": id, "include_tweet_replies": $replies}, after) +# url = oldUserTweets / (id & ".json") ? ps +# result = parseTimeline(await fetch(url, Api.timeline), after) proc getGraphListTweets*(id: string; after=""): Future[Timeline] {.async.} = if id.len == 0: return @@ -123,20 +123,22 @@ proc getGraphSearch*(query: Query; after=""): Future[Profile] {.async.} = result.tweets.query = query proc getTweetSearch*(query: Query; after=""): Future[Timeline] {.async.} = - let q = genQueryParam(query) + var q = genQueryParam(query) + if q.len == 0 or q == emptyQuery: return Timeline(query: query, beginning: true) + if after.len > 0: + q &= " max_id:" & after + let url = tweetSearch ? genParams({ - "q": q, - "tweet_search_mode": "live", - "max_id": after + "q": q , + "modules": "status", + "result_type": "recent", }) - result = parseTweetSearch(await fetch(url, Api.search)) + result = parseTweetSearch(await fetch(url, Api.search), after) result.query = query - if after.len == 0: - result.beginning = true proc getUserSearch*(query: Query; page="1"): Future[Result[User]] {.async.} = if query.text.len == 0: diff --git a/src/apiutils.nim b/src/apiutils.nim index dbc6cca..c0c01d4 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -16,7 +16,7 @@ proc genParams*(pars: openArray[(string, string)] = @[]; cursor=""; for p in pars: result &= p if ext: - result &= ("ext", "mediaStats") + result &= ("ext", "mediaStats,isBlueVerified,isVerified,blue,blueVerified") result &= ("include_ext_alt_text", "1") result &= ("include_ext_media_availability", "1") if count.len > 0: diff --git a/src/consts.nim b/src/consts.nim index 7ba09fb..80a098f 100644 --- a/src/consts.nim +++ b/src/consts.nim @@ -9,9 +9,9 @@ const photoRail* = api / "1.1/statuses/media_timeline.json" userSearch* = api / "1.1/users/search.json" - tweetSearch* = api / "1.1/search/tweets.json" + tweetSearch* = api / "1.1/search/universal.json" - oldUserTweets* = api / "2/timeline/profile" + # oldUserTweets* = api / "2/timeline/profile" graphql = api / "graphql" graphUser* = graphql / "u7wQyGi6oExe8_TRWGMq4Q/UserResultByScreenNameQuery" @@ -28,27 +28,28 @@ const graphListTweets* = graphql / "BbGLL1ZfMibdFNWlk7a0Pw/ListTimeline" timelineParams* = { - "include_profile_interstitial_type": "0", - "include_blocking": "0", - "include_blocked_by": "0", - "include_followed_by": "0", - "include_want_retweets": "0", - "include_mute_edge": "0", - "include_can_dm": "0", - "include_can_media_tag": "1", - "include_ext_is_blue_verified": "1", - "skip_status": "1", - "cards_platform": "Web-12", - "include_cards": "1", - "include_composer_source": "0", - "include_reply_count": "1", + "cards_platform": "Web-13", "tweet_mode": "extended", - "include_entities": "1", - "include_user_entities": "1", - "include_ext_media_color": "0", + "ui_lang": "en-US", "send_error_codes": "1", "simple_quoted_tweet": "1", - "include_quote_count": "1" + "skip_status": "1", + "include_blocked_by": "0", + "include_blocking": "0", + "include_can_dm": "0", + "include_can_media_tag": "1", + "include_cards": "1", + "include_composer_source": "0", + "include_entities": "1", + "include_ext_is_blue_verified": "1", + "include_ext_media_color": "0", + "include_followed_by": "0", + "include_mute_edge": "0", + "include_profile_interstitial_type": "0", + "include_quote_count": "1", + "include_reply_count": "1", + "include_user_entities": "1", + "include_want_retweets": "0", }.toSeq gqlFeatures* = """{ @@ -100,17 +101,17 @@ const "includeHasBirdwatchNotes": false }""" - oldUserTweetsVariables* = """{ - "userId": "$1", $2 - "count": 20, - "includePromotedContent": false, - "withDownvotePerspective": false, - "withReactionsMetadata": false, - "withReactionsPerspective": false, - "withVoice": false, - "withV2Timeline": true -} -""" +# oldUserTweetsVariables* = """{ +# "userId": "$1", $2 +# "count": 20, +# "includePromotedContent": false, +# "withDownvotePerspective": false, +# "withReactionsMetadata": false, +# "withReactionsPerspective": false, +# "withVoice": false, +# "withV2Timeline": true +# } +# """ userTweetsVariables* = """{ "rest_id": "$1", $2 diff --git a/src/parser.nim b/src/parser.nim index 193d77f..991ca6e 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -1,5 +1,5 @@ # SPDX-License-Identifier: AGPL-3.0-only -import strutils, options, tables, times, math +import strutils, options, times, math import packedjson, packedjson/deserialiser import types, parserutils, utils import experimental/parser/unifiedcard @@ -295,110 +295,112 @@ proc parseLegacyTweet(js: JsonNode): Tweet = if result.quote.isSome: result.quote = some parseLegacyTweet(js{"quoted_status"}) -proc parseTweetSearch*(js: JsonNode): Timeline = - if js.kind == JNull or "statuses" notin js: - return Timeline(beginning: true) +proc parseTweetSearch*(js: JsonNode; after=""): Timeline = + result.beginning = after.len == 0 - for tweet in js{"statuses"}: - let parsed = parseLegacyTweet(tweet) - - if parsed.retweet.isSome: - parsed.retweet = some parseLegacyTweet(tweet{"retweeted_status"}) - - result.content.add @[parsed] - - let cursor = js{"search_metadata", "next_results"}.getStr - if cursor.len > 0 and "max_id" in cursor: - result.bottom = cursor[cursor.find("=") + 1 .. cursor.find("&q=")] - -proc finalizeTweet(global: GlobalObjects; id: string): Tweet = - let intId = if id.len > 0: parseBiggestInt(id) else: 0 - result = global.tweets.getOrDefault(id, Tweet(id: intId)) - - if result.quote.isSome: - let quote = get(result.quote).id - if $quote in global.tweets: - result.quote = some global.tweets[$quote] - else: - result.quote = some Tweet() - - if result.retweet.isSome: - let rt = get(result.retweet).id - if $rt in global.tweets: - result.retweet = some finalizeTweet(global, $rt) - else: - result.retweet = some Tweet() - -proc parsePin(js: JsonNode; global: GlobalObjects): Tweet = - let pin = js{"pinEntry", "entry", "entryId"}.getStr - if pin.len == 0: return - - let id = pin.getId - if id notin global.tweets: return - - global.tweets[id].pinned = true - return finalizeTweet(global, id) - -proc parseGlobalObjects(js: JsonNode): GlobalObjects = - result = GlobalObjects() - let - tweets = ? js{"globalObjects", "tweets"} - users = ? js{"globalObjects", "users"} - - for k, v in users: - result.users[k] = parseUser(v, k) - - for k, v in tweets: - var tweet = parseTweet(v, v{"card"}) - if tweet.user.id in result.users: - tweet.user = result.users[tweet.user.id] - result.tweets[k] = tweet - -proc parseInstructions(res: var Profile; global: GlobalObjects; js: JsonNode) = - if js.kind != JArray or js.len == 0: + if js.kind == JNull or "modules" notin js or js{"modules"}.len == 0: return - for i in js: - if res.tweets.beginning and i{"pinEntry"}.notNull: - with pin, parsePin(i, global): - res.pinned = some pin + for item in js{"modules"}: + with tweet, item{"status", "data"}: + let parsed = parseLegacyTweet(tweet) - with r, i{"replaceEntry", "entry"}: - if "top" in r{"entryId"}.getStr: - res.tweets.top = r.getCursor - elif "bottom" in r{"entryId"}.getStr: - res.tweets.bottom = r.getCursor + if parsed.retweet.isSome: + parsed.retweet = some parseLegacyTweet(tweet{"retweeted_status"}) -proc parseTimeline*(js: JsonNode; after=""): Profile = - result = Profile(tweets: Timeline(beginning: after.len == 0)) - let global = parseGlobalObjects(? js) + result.content.add @[parsed] - let instructions = ? js{"timeline", "instructions"} - if instructions.len == 0: return + if result.content.len > 0: + result.bottom = $(result.content[^1][0].id - 1) - result.parseInstructions(global, instructions) +# proc finalizeTweet(global: GlobalObjects; id: string): Tweet = +# let intId = if id.len > 0: parseBiggestInt(id) else: 0 +# result = global.tweets.getOrDefault(id, Tweet(id: intId)) - var entries: JsonNode - for i in instructions: - if "addEntries" in i: - entries = i{"addEntries", "entries"} +# if result.quote.isSome: +# let quote = get(result.quote).id +# if $quote in global.tweets: +# result.quote = some global.tweets[$quote] +# else: +# result.quote = some Tweet() - for e in ? entries: - let entry = e{"entryId"}.getStr - if "tweet" in entry or entry.startsWith("sq-I-t") or "tombstone" in entry: - let tweet = finalizeTweet(global, e.getEntryId) - if not tweet.available: continue - result.tweets.content.add tweet - elif "cursor-top" in entry: - result.tweets.top = e.getCursor - elif "cursor-bottom" in entry: - result.tweets.bottom = e.getCursor - elif entry.startsWith("sq-cursor"): - with cursor, e{"content", "operation", "cursor"}: - if cursor{"cursorType"}.getStr == "Bottom": - result.tweets.bottom = cursor{"value"}.getStr - else: - result.tweets.top = cursor{"value"}.getStr +# if result.retweet.isSome: +# let rt = get(result.retweet).id +# if $rt in global.tweets: +# result.retweet = some finalizeTweet(global, $rt) +# else: +# result.retweet = some Tweet() + +# proc parsePin(js: JsonNode; global: GlobalObjects): Tweet = +# let pin = js{"pinEntry", "entry", "entryId"}.getStr +# if pin.len == 0: return + +# let id = pin.getId +# if id notin global.tweets: return + +# global.tweets[id].pinned = true +# return finalizeTweet(global, id) + +# proc parseGlobalObjects(js: JsonNode): GlobalObjects = +# result = GlobalObjects() +# let +# tweets = ? js{"globalObjects", "tweets"} +# users = ? js{"globalObjects", "users"} + +# for k, v in users: +# result.users[k] = parseUser(v, k) + +# for k, v in tweets: +# var tweet = parseTweet(v, v{"card"}) +# if tweet.user.id in result.users: +# tweet.user = result.users[tweet.user.id] +# result.tweets[k] = tweet + +# proc parseInstructions(res: var Profile; global: GlobalObjects; js: JsonNode) = +# if js.kind != JArray or js.len == 0: +# return + +# for i in js: +# if res.tweets.beginning and i{"pinEntry"}.notNull: +# with pin, parsePin(i, global): +# res.pinned = some pin + +# with r, i{"replaceEntry", "entry"}: +# if "top" in r{"entryId"}.getStr: +# res.tweets.top = r.getCursor +# elif "bottom" in r{"entryId"}.getStr: +# res.tweets.bottom = r.getCursor + +# proc parseTimeline*(js: JsonNode; after=""): Profile = +# result = Profile(tweets: Timeline(beginning: after.len == 0)) +# let global = parseGlobalObjects(? js) + +# let instructions = ? js{"timeline", "instructions"} +# if instructions.len == 0: return + +# result.parseInstructions(global, instructions) + +# var entries: JsonNode +# for i in instructions: +# if "addEntries" in i: +# entries = i{"addEntries", "entries"} + +# for e in ? entries: +# let entry = e{"entryId"}.getStr +# if "tweet" in entry or entry.startsWith("sq-I-t") or "tombstone" in entry: +# let tweet = finalizeTweet(global, e.getEntryId) +# if not tweet.available: continue +# result.tweets.content.add tweet +# elif "cursor-top" in entry: +# result.tweets.top = e.getCursor +# elif "cursor-bottom" in entry: +# result.tweets.bottom = e.getCursor +# elif entry.startsWith("sq-cursor"): +# with cursor, e{"content", "operation", "cursor"}: +# if cursor{"cursorType"}.getStr == "Bottom": +# result.tweets.bottom = cursor{"value"}.getStr +# else: +# result.tweets.top = cursor{"value"}.getStr proc parsePhotoRail*(js: JsonNode): PhotoRail = with error, js{"error"}: diff --git a/src/routes/timeline.nim b/src/routes/timeline.nim index b574631..8b8a23c 100644 --- a/src/routes/timeline.nim +++ b/src/routes/timeline.nim @@ -53,7 +53,7 @@ proc fetchProfile*(after: string; query: Query; skipRail=false; result = case query.kind - of posts: await getTimeline(userId, after) + # of posts: await getTimeline(userId, after) of replies: await getGraphUserTweets(userId, TimelineKind.replies, after) of media: await getGraphUserTweets(userId, TimelineKind.media, after) else: Profile(tweets: await getTweetSearch(query, after)) @@ -61,10 +61,18 @@ proc fetchProfile*(after: string; query: Query; skipRail=false; result.user = await user result.photoRail = await rail + result.tweets.query = query + if result.user.protected or result.user.suspended: return - result.tweets.query = query + if not skipPinned and query.kind == posts and + result.user.pinnedTweet > 0 and after.len == 0: + let tweet = await getCachedTweet(result.user.pinnedTweet) + if not tweet.isNil: + tweet.pinned = true + tweet.user = result.user + result.pinned = some tweet proc showTimeline*(request: Request; query: Query; cfg: Config; prefs: Prefs; rss, after: string): Future[string] {.async.} = diff --git a/tests/test_timeline.py b/tests/test_timeline.py index dd78396..a630ae1 100644 --- a/tests/test_timeline.py +++ b/tests/test_timeline.py @@ -1,12 +1,12 @@ from base import BaseTestCase, Timeline from parameterized import parameterized -normal = [['mobile_test'], ['mobile_test_2']] +normal = [['jack'], ['elonmusk']] -after = [['mobile_test', 'HBaAgJPsqtGNhA0AAA%3D%3D'], - ['mobile_test_2', 'HBaAgJPsqtGNhA0AAA%3D%3D']] +after = [['jack', '1681686036294803456'], + ['elonmusk', '1681686036294803456']] -no_more = [['mobile_test_8?cursor=HBaAwJCsk%2F6%2FtgQAAA%3D%3D']] +no_more = [['mobile_test_8?cursor=1000']] empty = [['emptyuser'], ['mobile_test_10']] diff --git a/tests/test_tweet.py b/tests/test_tweet.py index e4231a4..7a3c4ed 100644 --- a/tests/test_tweet.py +++ b/tests/test_tweet.py @@ -80,16 +80,16 @@ retweet = [ class TweetTest(BaseTestCase): - @parameterized.expand(timeline) - def test_timeline(self, index, fullname, username, date, tid, text): - self.open_nitter(username) - tweet = get_timeline_tweet(index) - self.assert_exact_text(fullname, tweet.fullname) - self.assert_exact_text('@' + username, tweet.username) - self.assert_exact_text(date, tweet.date) - self.assert_text(text, tweet.text) - permalink = self.find_element(tweet.date + ' a') - self.assertIn(tid, permalink.get_attribute('href')) + # @parameterized.expand(timeline) + # def test_timeline(self, index, fullname, username, date, tid, text): + # self.open_nitter(username) + # tweet = get_timeline_tweet(index) + # self.assert_exact_text(fullname, tweet.fullname) + # self.assert_exact_text('@' + username, tweet.username) + # self.assert_exact_text(date, tweet.date) + # self.assert_text(text, tweet.text) + # permalink = self.find_element(tweet.date + ' a') + # self.assertIn(tid, permalink.get_attribute('href')) @parameterized.expand(status) def test_status(self, tid, fullname, username, date, text): @@ -123,14 +123,14 @@ class TweetTest(BaseTestCase): link = self.find_link_text(f'@{un}') self.assertIn(f'/{un}', link.get_property('href')) - @parameterized.expand(retweet) - def test_retweet(self, index, url, retweet_by, fullname, username, text): - self.open_nitter(url) - tweet = get_timeline_tweet(index) - self.assert_text(f'{retweet_by} retweeted', tweet.retweet) - self.assert_text(text, tweet.text) - self.assert_exact_text(fullname, tweet.fullname) - self.assert_exact_text(username, tweet.username) + # @parameterized.expand(retweet) + # def test_retweet(self, index, url, retweet_by, fullname, username, text): + # self.open_nitter(url) + # tweet = get_timeline_tweet(index) + # self.assert_text(f'{retweet_by} retweeted', tweet.retweet) + # self.assert_text(text, tweet.text) + # self.assert_exact_text(fullname, tweet.fullname) + # self.assert_exact_text(username, tweet.username) @parameterized.expand(invalid) def test_invalid_id(self, tweet): From 72d8f35cd1ec1205824711a41dab4b8d7a6b298a Mon Sep 17 00:00:00 2001 From: Zed Date: Sat, 22 Jul 2023 04:06:04 +0200 Subject: [PATCH 119/409] Search isn't rate limited --- src/tokens.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tokens.nim b/src/tokens.nim index 8a25257..b69786e 100644 --- a/src/tokens.nim +++ b/src/tokens.nim @@ -41,7 +41,8 @@ proc getPoolJson*(): JsonNode = let maxReqs = case api - of Api.photoRail, Api.search: 180 + of Api.search: 100000 + of Api.photoRail: 180 of Api.timeline: 187 of Api.userTweets: 300 of Api.userTweetsAndReplies, Api.userRestId, From 59a72831c749b2198cb83d1b7cee74a5d05da723 Mon Sep 17 00:00:00 2001 From: Zed Date: Mon, 24 Jul 2023 04:26:32 +0200 Subject: [PATCH 120/409] Apply cached profile verified status to tweets --- src/routes/timeline.nim | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/routes/timeline.nim b/src/routes/timeline.nim index 8b8a23c..bf2a08e 100644 --- a/src/routes/timeline.nim +++ b/src/routes/timeline.nim @@ -66,13 +66,17 @@ proc fetchProfile*(after: string; query: Query; skipRail=false; if result.user.protected or result.user.suspended: return - if not skipPinned and query.kind == posts and - result.user.pinnedTweet > 0 and after.len == 0: - let tweet = await getCachedTweet(result.user.pinnedTweet) - if not tweet.isNil: - tweet.pinned = true - tweet.user = result.user - result.pinned = some tweet + if query.kind == posts: + if result.user.verified: + for chain in result.tweets.content: + if chain[0].user.id == result.user.id: + chain[0].user.verified = true + if not skipPinned and result.user.pinnedTweet > 0 and after.len == 0: + let tweet = await getCachedTweet(result.user.pinnedTweet) + if not tweet.isNil: + tweet.pinned = true + tweet.user = result.user + result.pinned = some tweet proc showTimeline*(request: Request; query: Query; cfg: Config; prefs: Prefs; rss, after: string): Future[string] {.async.} = From 39192bf191dfc8c5645aa8101afd04474b899897 Mon Sep 17 00:00:00 2001 From: Zed Date: Mon, 24 Jul 2023 10:18:50 +0200 Subject: [PATCH 121/409] Fix multi-timeline infinite scroll --- src/routes/timeline.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/timeline.nim b/src/routes/timeline.nim index bf2a08e..82dc45b 100644 --- a/src/routes/timeline.nim +++ b/src/routes/timeline.nim @@ -137,7 +137,7 @@ proc createTimelineRouter*(cfg: Config) = # used for the infinite scroll feature if @"scroll".len > 0: if query.fromUser.len != 1: - var timeline = (await getGraphSearch(query, after)).tweets + var timeline = await getTweetSearch(query, after) if timeline.content.len == 0: resp Http404 timeline.beginning = true resp $renderTweetSearch(timeline, prefs, getPath()) From 20b5cce5dc6437ffc06ea53e9efd884f2fc66abe Mon Sep 17 00:00:00 2001 From: Zed Date: Mon, 24 Jul 2023 10:37:25 +0200 Subject: [PATCH 122/409] Retry infinite scroll errors --- public/js/infiniteScroll.js | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/public/js/infiniteScroll.js b/public/js/infiniteScroll.js index 9939c03..be27e0c 100644 --- a/public/js/infiniteScroll.js +++ b/public/js/infiniteScroll.js @@ -5,7 +5,7 @@ function insertBeforeLast(node, elem) { } function getLoadMore(doc) { - return doc.querySelector('.show-more:not(.timeline-item)'); + return doc.querySelector(".show-more:not(.timeline-item)"); } function isDuplicate(item, itemClass) { @@ -15,18 +15,19 @@ function isDuplicate(item, itemClass) { return document.querySelector(itemClass + " .tweet-link[href='" + href + "']") != null; } -window.onload = function() { +window.onload = function () { const url = window.location.pathname; const isTweet = url.indexOf("/status/") !== -1; const containerClass = isTweet ? ".replies" : ".timeline"; - const itemClass = containerClass + ' > div:not(.top-ref)'; + const itemClass = containerClass + " > div:not(.top-ref)"; var html = document.querySelector("html"); var container = document.querySelector(containerClass); var loading = false; - window.addEventListener('scroll', function() { + function handleScroll(failed) { if (loading) return; + if (html.scrollTop + html.clientHeight >= html.scrollHeight - 3000) { loading = true; var loadMore = getLoadMore(document); @@ -35,13 +36,15 @@ window.onload = function() { loadMore.children[0].text = "Loading..."; var url = new URL(loadMore.children[0].href); - url.searchParams.append('scroll', 'true'); + url.searchParams.append("scroll", "true"); fetch(url.toString()).then(function (response) { + if (response.status === 404) throw "error"; + return response.text(); }).then(function (html) { var parser = new DOMParser(); - var doc = parser.parseFromString(html, 'text/html'); + var doc = parser.parseFromString(html, "text/html"); loadMore.remove(); for (var item of doc.querySelectorAll(itemClass)) { @@ -57,10 +60,18 @@ window.onload = function() { if (isTweet) container.appendChild(newLoadMore); else insertBeforeLast(container, newLoadMore); }).catch(function (err) { - console.warn('Something went wrong.', err); - loading = true; + console.warn("Something went wrong.", err); + if (failed > 3) { + loadMore.children[0].text = "Error"; + return; + } + + loading = false; + handleScroll((failed || 0) + 1); }); } - }); + } + + window.addEventListener("scroll", () => handleScroll()); }; // @license-end From 5725780c990cd55f00ed6558d052f0e5a148652a Mon Sep 17 00:00:00 2001 From: Zed Date: Sun, 6 Aug 2023 21:02:22 +0200 Subject: [PATCH 123/409] Bump Nim version in Docker image --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index c100394..138dc64 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM nimlang/nim:1.6.10-alpine-regular as nim +FROM nimlang/nim:2.0.0-alpine-regular as nim LABEL maintainer="setenforce@protonmail.com" RUN apk --no-cache add libsass-dev pcre From 624394430c0989d18c279153006c6a7e48f4dd03 Mon Sep 17 00:00:00 2001 From: Zed Date: Tue, 8 Aug 2023 02:09:56 +0200 Subject: [PATCH 124/409] Use legacy timeline/user endpoint for Tweets tab --- src/api.nim | 10 ++++++++ src/apiutils.nim | 2 +- src/consts.nim | 23 +++++++----------- src/parser.nim | 52 +++++++++++++++++++++++++++++++++++++++-- src/parserutils.nim | 6 +++++ src/routes/timeline.nim | 17 +------------- src/tokens.nim | 8 +++---- src/types.nim | 1 + 8 files changed, 81 insertions(+), 38 deletions(-) diff --git a/src/api.nim b/src/api.nim index c7dc0e0..c313aa2 100644 --- a/src/api.nim +++ b/src/api.nim @@ -40,6 +40,16 @@ proc getGraphUserTweets*(id: string; kind: TimelineKind; after=""): Future[Profi # url = oldUserTweets / (id & ".json") ? ps # result = parseTimeline(await fetch(url, Api.timeline), after) +proc getUserTimeline*(id: string; after=""): Future[Profile] {.async.} = + var ps = genParams({"id": id}) + if after.len > 0: + ps.add ("down_cursor", after) + + let + url = legacyUserTweets ? ps + js = await fetch(url, Api.userTimeline) + result = parseUserTimeline(js, after) + proc getGraphListTweets*(id: string; after=""): Future[Timeline] {.async.} = if id.len == 0: return let diff --git a/src/apiutils.nim b/src/apiutils.nim index c0c01d4..1da971a 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -16,8 +16,8 @@ proc genParams*(pars: openArray[(string, string)] = @[]; cursor=""; for p in pars: result &= p if ext: - result &= ("ext", "mediaStats,isBlueVerified,isVerified,blue,blueVerified") result &= ("include_ext_alt_text", "1") + result &= ("include_ext_media_stats", "1") result &= ("include_ext_media_availability", "1") if count.len > 0: result &= ("count", count) diff --git a/src/consts.nim b/src/consts.nim index 80a098f..a25f6ea 100644 --- a/src/consts.nim +++ b/src/consts.nim @@ -7,6 +7,7 @@ const api = parseUri("https://api.twitter.com") activate* = $(api / "1.1/guest/activate.json") + legacyUserTweets* = api / "1.1/timeline/user.json" photoRail* = api / "1.1/statuses/media_timeline.json" userSearch* = api / "1.1/users/search.json" tweetSearch* = api / "1.1/search/universal.json" @@ -28,28 +29,20 @@ const graphListTweets* = graphql / "BbGLL1ZfMibdFNWlk7a0Pw/ListTimeline" timelineParams* = { - "cards_platform": "Web-13", - "tweet_mode": "extended", - "ui_lang": "en-US", - "send_error_codes": "1", - "simple_quoted_tweet": "1", - "skip_status": "1", - "include_blocked_by": "0", - "include_blocking": "0", - "include_can_dm": "0", "include_can_media_tag": "1", "include_cards": "1", - "include_composer_source": "0", "include_entities": "1", - "include_ext_is_blue_verified": "1", - "include_ext_media_color": "0", - "include_followed_by": "0", - "include_mute_edge": "0", "include_profile_interstitial_type": "0", "include_quote_count": "1", "include_reply_count": "1", "include_user_entities": "1", - "include_want_retweets": "0", + "include_ext_reply_count": "1", + "include_ext_is_blue_verified": "1", + "include_ext_media_color": "0", + "cards_platform": "Web-13", + "tweet_mode": "extended", + "send_error_codes": "1", + "simple_quoted_tweet": "1" }.toSeq gqlFeatures* = """{ diff --git a/src/parser.nim b/src/parser.nim index 991ca6e..c7d8bd1 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -1,5 +1,5 @@ # SPDX-License-Identifier: AGPL-3.0-only -import strutils, options, times, math +import strutils, options, times, math, tables import packedjson, packedjson/deserialiser import types, parserutils, utils import experimental/parser/unifiedcard @@ -81,7 +81,7 @@ proc parseGif(js: JsonNode): Gif = proc parseVideo(js: JsonNode): Video = result = Video( thumb: js{"media_url_https"}.getImageStr, - views: js{"ext", "mediaStats", "r", "ok", "viewCount"}.getStr($js{"mediaStats", "viewCount"}.getInt), + views: getVideoViewCount(js), available: true, title: js{"ext_alt_text"}.getStr, durationMs: js{"video_info", "duration_millis"}.getInt @@ -313,6 +313,54 @@ proc parseTweetSearch*(js: JsonNode; after=""): Timeline = if result.content.len > 0: result.bottom = $(result.content[^1][0].id - 1) +proc parseUserTimelineTweet(tweet: JsonNode; users: TableRef[string, User]): Tweet = + result = parseTweet(tweet, tweet{"card"}) + + if result.isNil or not result.available: + return + + with user, tweet{"user"}: + let userId = user{"id_str"}.getStr + if user{"ext_is_blue_verified"}.getBool(false): + users[userId].verified = users[userId].verified or true + result.user = users[userId] + +proc parseUserTimeline*(js: JsonNode; after=""): Profile = + result = Profile(tweets: Timeline(beginning: after.len == 0)) + + if js.kind == JNull or "response" notin js or "twitter_objects" notin js: + return + + var users = newTable[string, User]() + for userId, user in js{"twitter_objects", "users"}: + users[userId] = parseUser(user) + + for entity in js{"response", "timeline"}: + let + tweetId = entity{"tweet", "id"}.getId + isPinned = entity{"tweet", "is_pinned"}.getBool(false) + + with tweet, js{"twitter_objects", "tweets", $tweetId}: + var parsed = parseUserTimelineTweet(tweet, users) + + if not parsed.isNil and parsed.available: + if parsed.quote.isSome: + parsed.quote = some parseUserTimelineTweet(tweet{"quoted_status"}, users) + + if parsed.retweet.isSome: + let retweet = parseUserTimelineTweet(tweet{"retweeted_status"}, users) + if retweet.quote.isSome: + retweet.quote = some parseUserTimelineTweet(tweet{"retweeted_status", "quoted_status"}, users) + parsed.retweet = some retweet + + if isPinned: + parsed.pinned = true + result.pinned = some parsed + else: + result.tweets.content.add parsed + + result.tweets.bottom = js{"response", "cursor", "bottom"}.getStr + # proc finalizeTweet(global: GlobalObjects; id: string): Tweet = # let intId = if id.len > 0: parseBiggestInt(id) else: 0 # result = global.tweets.getOrDefault(id, Tweet(id: intId)) diff --git a/src/parserutils.nim b/src/parserutils.nim index f28bd52..c65052e 100644 --- a/src/parserutils.nim +++ b/src/parserutils.nim @@ -148,6 +148,12 @@ proc getMp4Resolution*(url: string): int = # cannot determine resolution (e.g. m3u8/non-mp4 video) return 0 +proc getVideoViewCount*(js: JsonNode): string = + with stats, js{"ext_media_stats"}: + return stats{"view_count"}.getStr($stats{"viewCount"}.getInt) + + return $js{"mediaStats", "viewCount"}.getInt(0) + proc extractSlice(js: JsonNode): Slice[int] = result = js["indices"][0].getInt ..< js["indices"][1].getInt diff --git a/src/routes/timeline.nim b/src/routes/timeline.nim index 82dc45b..8d02b68 100644 --- a/src/routes/timeline.nim +++ b/src/routes/timeline.nim @@ -53,7 +53,7 @@ proc fetchProfile*(after: string; query: Query; skipRail=false; result = case query.kind - # of posts: await getTimeline(userId, after) + of posts: await getUserTimeline(userId, after) of replies: await getGraphUserTweets(userId, TimelineKind.replies, after) of media: await getGraphUserTweets(userId, TimelineKind.media, after) else: Profile(tweets: await getTweetSearch(query, after)) @@ -63,21 +63,6 @@ proc fetchProfile*(after: string; query: Query; skipRail=false; result.tweets.query = query - if result.user.protected or result.user.suspended: - return - - if query.kind == posts: - if result.user.verified: - for chain in result.tweets.content: - if chain[0].user.id == result.user.id: - chain[0].user.verified = true - if not skipPinned and result.user.pinnedTweet > 0 and after.len == 0: - let tweet = await getCachedTweet(result.user.pinnedTweet) - if not tweet.isNil: - tweet.pinned = true - tweet.user = result.user - result.pinned = some tweet - proc showTimeline*(request: Request; query: Query; cfg: Config; prefs: Prefs; rss, after: string): Future[string] {.async.} = if query.fromUser.len != 1: diff --git a/src/tokens.nim b/src/tokens.nim index b69786e..decf228 100644 --- a/src/tokens.nim +++ b/src/tokens.nim @@ -44,10 +44,10 @@ proc getPoolJson*(): JsonNode = of Api.search: 100000 of Api.photoRail: 180 of Api.timeline: 187 - of Api.userTweets: 300 + of Api.userTweets, Api.userTimeline: 300 of Api.userTweetsAndReplies, Api.userRestId, - Api.userScreenName, Api.tweetDetail, Api.tweetResult: 500 - of Api.list, Api.listTweets, Api.listMembers, Api.listBySlug, Api.userMedia: 500 + Api.userScreenName, Api.tweetDetail, Api.tweetResult, + Api.list, Api.listTweets, Api.listMembers, Api.listBySlug, Api.userMedia: 500 of Api.userSearch: 900 reqs = maxReqs - token.apis[api].remaining @@ -161,6 +161,6 @@ proc initTokenPool*(cfg: Config) {.async.} = enableLogging = cfg.enableDebug while true: - if tokenPool.countIt(not it.isLimited(Api.timeline)) < cfg.minTokens: + if tokenPool.countIt(not it.isLimited(Api.userTimeline)) < cfg.minTokens: await poolTokens(min(4, cfg.minTokens - tokenPool.len)) await sleepAsync(2000) diff --git a/src/types.nim b/src/types.nim index 5db9ec3..1a47d25 100644 --- a/src/types.nim +++ b/src/types.nim @@ -18,6 +18,7 @@ type tweetDetail tweetResult timeline + userTimeline photoRail search userSearch From 967f5e50f9c2ba4ac50dfb39fc559e104cedbc99 Mon Sep 17 00:00:00 2001 From: Zed Date: Tue, 8 Aug 2023 02:15:32 +0200 Subject: [PATCH 125/409] Update and disable some tests --- tests/test_profile.py | 2 +- tests/test_timeline.py | 7 +++---- tests/test_tweet_media.py | 14 +++++++------- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/tests/test_profile.py b/tests/test_profile.py index 4c75ad2..38c5189 100644 --- a/tests/test_profile.py +++ b/tests/test_profile.py @@ -4,7 +4,7 @@ from parameterized import parameterized profiles = [ ['mobile_test', 'Test account', 'Test Account. test test Testing username with @mobile_test_2 and a #hashtag', - 'San Francisco, CA', 'example.com/foobar', 'Joined October 2009', '100'], + 'San Francisco, CA', 'example.com/foobar', 'Joined October 2009', '98'], ['mobile_test_2', 'mobile test 2', '', '', '', 'Joined January 2011', '13'] ] diff --git a/tests/test_timeline.py b/tests/test_timeline.py index a630ae1..90eaa28 100644 --- a/tests/test_timeline.py +++ b/tests/test_timeline.py @@ -13,10 +13,9 @@ empty = [['emptyuser'], ['mobile_test_10']] protected = [['mobile_test_7'], ['Empty_user']] photo_rail = [['mobile_test', [ - 'BzUnaDFCUAAmrjs', 'Bo0nDsYIYAIjqVn', 'Bos--KNIQAAA7Li', 'Boq1sDJIYAAxaoi', - 'BonISmPIEAAhP3G', 'BoQbwJAIUAA0QCY', 'BoQbRQxIIAA3FWD', 'Bn8Qh8iIIAABXrG', - 'Bn8QIG3IYAA0IGT', 'Bn8O3QeIUAAONai', 'Bn8NGViIAAATNG4', 'BkKovdrCUAAEz79', - 'BkKoe_oCIAASAqr', 'BkKoRLNCAAAYfDf', 'BkKndxoCQAE1vFt', 'BPEmIbYCMAE44dl' + 'Bo0nDsYIYAIjqVn', 'BoQbwJAIUAA0QCY', 'BoQbRQxIIAA3FWD', 'Bn8Qh8iIIAABXrG', + 'Bn8QIG3IYAA0IGT', 'Bn8O3QeIUAAONai', 'Bn8NGViIAAATNG4', 'BkKoRLNCAAAYfDf', + 'BkKndxoCQAE1vFt' ]]] diff --git a/tests/test_tweet_media.py b/tests/test_tweet_media.py index 233990e..7a00983 100644 --- a/tests/test_tweet_media.py +++ b/tests/test_tweet_media.py @@ -28,14 +28,14 @@ video_m3u8 = [ ] gallery = [ - ['mobile_test/status/451108446603980803', [ - ['BkKovdrCUAAEz79', 'BkKovdcCEAAfoBO'] - ]], + # ['mobile_test/status/451108446603980803', [ + # ['BkKovdrCUAAEz79', 'BkKovdcCEAAfoBO'] + # ]], - ['mobile_test/status/471539824713691137', [ - ['Bos--KNIQAAA7Li', 'Bos--FAIAAAWpah'], - ['Bos--IqIQAAav23'] - ]], + # ['mobile_test/status/471539824713691137', [ + # ['Bos--KNIQAAA7Li', 'Bos--FAIAAAWpah'], + # ['Bos--IqIQAAav23'] + # ]], ['mobile_test/status/469530783384743936', [ ['BoQbwJAIUAA0QCY', 'BoQbwN1IMAAuTiP'], From 54e6ce14ac48409c0552b96e1dadf674c1926c83 Mon Sep 17 00:00:00 2001 From: Zed Date: Tue, 8 Aug 2023 02:35:43 +0200 Subject: [PATCH 126/409] Simplify photo rail test --- tests/test_timeline.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/test_timeline.py b/tests/test_timeline.py index 90eaa28..9261b44 100644 --- a/tests/test_timeline.py +++ b/tests/test_timeline.py @@ -12,11 +12,7 @@ empty = [['emptyuser'], ['mobile_test_10']] protected = [['mobile_test_7'], ['Empty_user']] -photo_rail = [['mobile_test', [ - 'Bo0nDsYIYAIjqVn', 'BoQbwJAIUAA0QCY', 'BoQbRQxIIAA3FWD', 'Bn8Qh8iIIAABXrG', - 'Bn8QIG3IYAA0IGT', 'Bn8O3QeIUAAONai', 'Bn8NGViIAAATNG4', 'BkKoRLNCAAAYfDf', - 'BkKndxoCQAE1vFt' -]]] +photo_rail = [['mobile_test', ['Bo0nDsYIYAIjqVn', 'BoQbwJAIUAA0QCY', 'BoQbRQxIIAA3FWD', 'Bn8Qh8iIIAABXrG']]] class TweetTest(BaseTestCase): From d7ca353a55ea3440a2ec1f09155951210a374cc7 Mon Sep 17 00:00:00 2001 From: Zed Date: Tue, 8 Aug 2023 02:49:58 +0200 Subject: [PATCH 127/409] Disable photo rail test --- tests/test_timeline.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/test_timeline.py b/tests/test_timeline.py index 9261b44..b56d6ad 100644 --- a/tests/test_timeline.py +++ b/tests/test_timeline.py @@ -55,10 +55,10 @@ class TweetTest(BaseTestCase): self.assert_element_absent(Timeline.older) self.assert_element_absent(Timeline.end) - @parameterized.expand(photo_rail) - def test_photo_rail(self, username, images): - self.open_nitter(username) - self.assert_element_visible(Timeline.photo_rail) - for i, url in enumerate(images): - img = self.get_attribute(Timeline.photo_rail + f' a:nth-child({i + 1}) img', 'src') - self.assertIn(url, img) + #@parameterized.expand(photo_rail) + #def test_photo_rail(self, username, images): + #self.open_nitter(username) + #self.assert_element_visible(Timeline.photo_rail) + #for i, url in enumerate(images): + #img = self.get_attribute(Timeline.photo_rail + f' a:nth-child({i + 1}) img', 'src') + #self.assertIn(url, img) From 3572dd77719f549d18aa0872b04564ede1091ca3 Mon Sep 17 00:00:00 2001 From: Zed Date: Sat, 19 Aug 2023 00:25:14 +0200 Subject: [PATCH 128/409] Replace tokens with guest accounts, swap endpoints --- nitter.nimble | 2 +- src/api.nim | 68 ++++---------- src/apiutils.nim | 54 +++++++---- src/consts.nim | 8 +- src/nitter.nim | 13 ++- src/parser.nim | 193 ++++------------------------------------ src/redis_cache.nim | 18 ++-- src/routes/rss.nim | 4 +- src/routes/search.nim | 4 +- src/routes/timeline.nim | 8 +- src/tokens.nim | 158 +++++++++++--------------------- src/types.nim | 11 ++- 12 files changed, 159 insertions(+), 382 deletions(-) diff --git a/nitter.nimble b/nitter.nimble index 7771b31..e6a1909 100644 --- a/nitter.nimble +++ b/nitter.nimble @@ -23,7 +23,7 @@ requires "https://github.com/zedeus/redis#d0a0e6f" requires "zippy#ca5989a" requires "flatty#e668085" requires "jsony#ea811be" - +requires "oauth#b8c163b" # Tasks diff --git a/src/api.nim b/src/api.nim index c313aa2..d6a4564 100644 --- a/src/api.nim +++ b/src/api.nim @@ -33,23 +33,6 @@ proc getGraphUserTweets*(id: string; kind: TimelineKind; after=""): Future[Profi js = await fetch(url ? params, apiId) result = parseGraphTimeline(js, "user", after) -# proc getTimeline*(id: string; after=""; replies=false): Future[Profile] {.async.} = -# if id.len == 0: return -# let -# ps = genParams({"userId": id, "include_tweet_replies": $replies}, after) -# url = oldUserTweets / (id & ".json") ? ps -# result = parseTimeline(await fetch(url, Api.timeline), after) - -proc getUserTimeline*(id: string; after=""): Future[Profile] {.async.} = - var ps = genParams({"id": id}) - if after.len > 0: - ps.add ("down_cursor", after) - - let - url = legacyUserTweets ? ps - js = await fetch(url, Api.userTimeline) - result = parseUserTimeline(js, after) - proc getGraphListTweets*(id: string; after=""): Future[Timeline] {.async.} = if id.len == 0: return let @@ -112,10 +95,10 @@ proc getTweet*(id: string; after=""): Future[Conversation] {.async.} = if after.len > 0: result.replies = await getReplies(id, after) -proc getGraphSearch*(query: Query; after=""): Future[Profile] {.async.} = +proc getGraphTweetSearch*(query: Query; after=""): Future[Timeline] {.async.} = let q = genQueryParam(query) if q.len == 0 or q == emptyQuery: - return Profile(tweets: Timeline(query: query, beginning: true)) + return Timeline(query: query, beginning: true) var variables = %*{ @@ -129,44 +112,29 @@ proc getGraphSearch*(query: Query; after=""): Future[Profile] {.async.} = if after.len > 0: variables["cursor"] = % after let url = graphSearchTimeline ? {"variables": $variables, "features": gqlFeatures} - result = Profile(tweets: parseGraphSearch(await fetch(url, Api.search), after)) - result.tweets.query = query - -proc getTweetSearch*(query: Query; after=""): Future[Timeline] {.async.} = - var q = genQueryParam(query) - - if q.len == 0 or q == emptyQuery: - return Timeline(query: query, beginning: true) - - if after.len > 0: - q &= " max_id:" & after - - let url = tweetSearch ? genParams({ - "q": q , - "modules": "status", - "result_type": "recent", - }) - - result = parseTweetSearch(await fetch(url, Api.search), after) + result = parseGraphSearch[Tweets](await fetch(url, Api.search), after) result.query = query -proc getUserSearch*(query: Query; page="1"): Future[Result[User]] {.async.} = +proc getGraphUserSearch*(query: Query; after=""): Future[Result[User]] {.async.} = if query.text.len == 0: return Result[User](query: query, beginning: true) - var url = userSearch ? { - "q": query.text, - "skip_status": "1", - "count": "20", - "page": page - } + var + variables = %*{ + "rawQuery": query.text, + "count": 20, + "product": "People", + "withDownvotePerspective": false, + "withReactionsMetadata": false, + "withReactionsPerspective": false + } + if after.len > 0: + variables["cursor"] = % after + result.beginning = false - result = parseUsers(await fetchRaw(url, Api.userSearch)) + let url = graphSearchTimeline ? {"variables": $variables, "features": gqlFeatures} + result = parseGraphSearch[User](await fetch(url, Api.search), after) result.query = query - if page.len == 0: - result.bottom = "2" - elif page.allCharsInSet(Digits): - result.bottom = $(parseInt(page) + 1) proc getPhotoRail*(name: string): Future[PhotoRail] {.async.} = if name.len == 0: return diff --git a/src/apiutils.nim b/src/apiutils.nim index 1da971a..d1ecfa3 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -1,6 +1,6 @@ # SPDX-License-Identifier: AGPL-3.0-only -import httpclient, asyncdispatch, options, strutils, uri -import jsony, packedjson, zippy +import httpclient, asyncdispatch, options, strutils, uri, times, math +import jsony, packedjson, zippy, oauth1 import types, tokens, consts, parserutils, http_pool import experimental/types/common @@ -29,12 +29,30 @@ proc genParams*(pars: openArray[(string, string)] = @[]; cursor=""; else: result &= ("cursor", cursor) -proc genHeaders*(token: Token = nil): HttpHeaders = +proc getOauthHeader(url, oauthToken, oauthTokenSecret: string): string = + let + encodedUrl = url.replace(",", "%2C").replace("+", "%20") + params = OAuth1Parameters( + consumerKey: consumerKey, + signatureMethod: "HMAC-SHA1", + timestamp: $int(round(epochTime())), + nonce: "0", + isIncludeVersionToHeader: true, + token: oauthToken + ) + signature = getSignature(HttpGet, encodedUrl, "", params, consumerSecret, oauthTokenSecret) + + params.signature = percentEncode(signature) + + return getOauth1RequestHeader(params)["authorization"] + +proc genHeaders*(url, oauthToken, oauthTokenSecret: string): HttpHeaders = + let header = getOauthHeader(url, oauthToken, oauthTokenSecret) + result = newHttpHeaders({ "connection": "keep-alive", - "authorization": auth, + "authorization": header, "content-type": "application/json", - "x-guest-token": if token == nil: "" else: token.tok, "x-twitter-active-user": "yes", "authority": "api.twitter.com", "accept-encoding": "gzip", @@ -43,24 +61,24 @@ proc genHeaders*(token: Token = nil): HttpHeaders = "DNT": "1" }) -template updateToken() = +template updateAccount() = if resp.headers.hasKey(rlRemaining): let remaining = parseInt(resp.headers[rlRemaining]) reset = parseInt(resp.headers[rlReset]) - token.setRateLimit(api, remaining, reset) + account.setRateLimit(api, remaining, reset) template fetchImpl(result, fetchBody) {.dirty.} = once: pool = HttpPool() - var token = await getToken(api) - if token.tok.len == 0: + var account = await getGuestAccount(api) + if account.oauthToken.len == 0: raise rateLimitError() try: var resp: AsyncResponse - pool.use(genHeaders(token)): + pool.use(genHeaders($url, account.oauthToken, account.oauthSecret)): template getContent = resp = await c.get($url) result = await resp.body @@ -79,19 +97,19 @@ template fetchImpl(result, fetchBody) {.dirty.} = fetchBody - release(token, used=true) + release(account, used=true) if resp.status == $Http400: raise newException(InternalError, $url) except InternalError as e: raise e except BadClientError as e: - release(token, used=true) + release(account, used=true) raise e except Exception as e: - echo "error: ", e.name, ", msg: ", e.msg, ", token: ", token[], ", url: ", url + echo "error: ", e.name, ", msg: ", e.msg, ", accountId: ", account.id, ", url: ", url if "length" notin e.msg and "descriptor" notin e.msg: - release(token, invalid=true) + release(account, invalid=true) raise rateLimitError() proc fetch*(url: Uri; api: Api): Future[JsonNode] {.async.} = @@ -103,12 +121,12 @@ proc fetch*(url: Uri; api: Api): Future[JsonNode] {.async.} = echo resp.status, ": ", body, " --- url: ", url result = newJNull() - updateToken() + updateAccount() let error = result.getError if error in {invalidToken, badToken}: echo "fetch error: ", result.getError - release(token, invalid=true) + release(account, invalid=true) raise rateLimitError() proc fetchRaw*(url: Uri; api: Api): Future[string] {.async.} = @@ -117,11 +135,11 @@ proc fetchRaw*(url: Uri; api: Api): Future[string] {.async.} = echo resp.status, ": ", result, " --- url: ", url result.setLen(0) - updateToken() + updateAccount() if result.startsWith("{\"errors"): let errors = result.fromJson(Errors) if errors in {invalidToken, badToken}: echo "fetch error: ", errors - release(token, invalid=true) + release(account, invalid=true) raise rateLimitError() diff --git a/src/consts.nim b/src/consts.nim index a25f6ea..2cfd1ed 100644 --- a/src/consts.nim +++ b/src/consts.nim @@ -2,17 +2,13 @@ import uri, sequtils, strutils const - auth* = "Bearer AAAAAAAAAAAAAAAAAAAAAFQODgEAAAAAVHTp76lzh3rFzcHbmHVvQxYYpTw%3DckAlMINMjmCwxUcaXbAN4XqJVdgMJaHqNOFgPMK0zN1qLqLQCF" + consumerKey* = "3nVuSoBZnx6U4vzUxf5w" + consumerSecret* = "Bcs59EFbbsdF6Sl9Ng71smgStWEGwXXKSjYvPVt7qys" api = parseUri("https://api.twitter.com") activate* = $(api / "1.1/guest/activate.json") - legacyUserTweets* = api / "1.1/timeline/user.json" photoRail* = api / "1.1/statuses/media_timeline.json" - userSearch* = api / "1.1/users/search.json" - tweetSearch* = api / "1.1/search/universal.json" - - # oldUserTweets* = api / "2/timeline/profile" graphql = api / "graphql" graphUser* = graphql / "u7wQyGi6oExe8_TRWGMq4Q/UserResultByScreenNameQuery" diff --git a/src/nitter.nim b/src/nitter.nim index 25a569d..4a4ec13 100644 --- a/src/nitter.nim +++ b/src/nitter.nim @@ -3,6 +3,7 @@ import asyncdispatch, strformat, logging from net import Port from htmlgen import a from os import getEnv +from json import parseJson import jester @@ -15,8 +16,14 @@ import routes/[ const instancesUrl = "https://github.com/zedeus/nitter/wiki/Instances" const issuesUrl = "https://github.com/zedeus/nitter/issues" -let configPath = getEnv("NITTER_CONF_FILE", "./nitter.conf") -let (cfg, fullCfg) = getConfig(configPath) +let + configPath = getEnv("NITTER_CONF_FILE", "./nitter.conf") + (cfg, fullCfg) = getConfig(configPath) + + accountsPath = getEnv("NITTER_ACCOUNTS_FILE", "./guest_accounts.json") + accounts = parseJson(readFile(accountsPath)) + +initAccountPool(cfg, parseJson(readFile(accountsPath))) if not cfg.enableDebug: # Silence Jester's query warning @@ -38,8 +45,6 @@ waitFor initRedisPool(cfg) stdout.write &"Connected to Redis at {cfg.redisHost}:{cfg.redisPort}\n" stdout.flushFile -asyncCheck initTokenPool(cfg) - createUnsupportedRouter(cfg) createResolverRouter(cfg) createPrefRouter(cfg) diff --git a/src/parser.nim b/src/parser.nim index c7d8bd1..9262b28 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -29,7 +29,9 @@ proc parseUser(js: JsonNode; id=""): User = result.expandUserEntities(js) proc parseGraphUser(js: JsonNode): User = - let user = ? js{"user_result", "result"} + var user = js{"user_result", "result"} + if user.isNull: + user = ? js{"user_results", "result"} result = parseUser(user{"legacy"}) if "is_blue_verified" in user: @@ -287,169 +289,6 @@ proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet = result.text.removeSuffix(" Learn more.") result.available = false -proc parseLegacyTweet(js: JsonNode): Tweet = - result = parseTweet(js, js{"card"}) - if not result.isNil and result.available: - result.user = parseUser(js{"user"}) - - if result.quote.isSome: - result.quote = some parseLegacyTweet(js{"quoted_status"}) - -proc parseTweetSearch*(js: JsonNode; after=""): Timeline = - result.beginning = after.len == 0 - - if js.kind == JNull or "modules" notin js or js{"modules"}.len == 0: - return - - for item in js{"modules"}: - with tweet, item{"status", "data"}: - let parsed = parseLegacyTweet(tweet) - - if parsed.retweet.isSome: - parsed.retweet = some parseLegacyTweet(tweet{"retweeted_status"}) - - result.content.add @[parsed] - - if result.content.len > 0: - result.bottom = $(result.content[^1][0].id - 1) - -proc parseUserTimelineTweet(tweet: JsonNode; users: TableRef[string, User]): Tweet = - result = parseTweet(tweet, tweet{"card"}) - - if result.isNil or not result.available: - return - - with user, tweet{"user"}: - let userId = user{"id_str"}.getStr - if user{"ext_is_blue_verified"}.getBool(false): - users[userId].verified = users[userId].verified or true - result.user = users[userId] - -proc parseUserTimeline*(js: JsonNode; after=""): Profile = - result = Profile(tweets: Timeline(beginning: after.len == 0)) - - if js.kind == JNull or "response" notin js or "twitter_objects" notin js: - return - - var users = newTable[string, User]() - for userId, user in js{"twitter_objects", "users"}: - users[userId] = parseUser(user) - - for entity in js{"response", "timeline"}: - let - tweetId = entity{"tweet", "id"}.getId - isPinned = entity{"tweet", "is_pinned"}.getBool(false) - - with tweet, js{"twitter_objects", "tweets", $tweetId}: - var parsed = parseUserTimelineTweet(tweet, users) - - if not parsed.isNil and parsed.available: - if parsed.quote.isSome: - parsed.quote = some parseUserTimelineTweet(tweet{"quoted_status"}, users) - - if parsed.retweet.isSome: - let retweet = parseUserTimelineTweet(tweet{"retweeted_status"}, users) - if retweet.quote.isSome: - retweet.quote = some parseUserTimelineTweet(tweet{"retweeted_status", "quoted_status"}, users) - parsed.retweet = some retweet - - if isPinned: - parsed.pinned = true - result.pinned = some parsed - else: - result.tweets.content.add parsed - - result.tweets.bottom = js{"response", "cursor", "bottom"}.getStr - -# proc finalizeTweet(global: GlobalObjects; id: string): Tweet = -# let intId = if id.len > 0: parseBiggestInt(id) else: 0 -# result = global.tweets.getOrDefault(id, Tweet(id: intId)) - -# if result.quote.isSome: -# let quote = get(result.quote).id -# if $quote in global.tweets: -# result.quote = some global.tweets[$quote] -# else: -# result.quote = some Tweet() - -# if result.retweet.isSome: -# let rt = get(result.retweet).id -# if $rt in global.tweets: -# result.retweet = some finalizeTweet(global, $rt) -# else: -# result.retweet = some Tweet() - -# proc parsePin(js: JsonNode; global: GlobalObjects): Tweet = -# let pin = js{"pinEntry", "entry", "entryId"}.getStr -# if pin.len == 0: return - -# let id = pin.getId -# if id notin global.tweets: return - -# global.tweets[id].pinned = true -# return finalizeTweet(global, id) - -# proc parseGlobalObjects(js: JsonNode): GlobalObjects = -# result = GlobalObjects() -# let -# tweets = ? js{"globalObjects", "tweets"} -# users = ? js{"globalObjects", "users"} - -# for k, v in users: -# result.users[k] = parseUser(v, k) - -# for k, v in tweets: -# var tweet = parseTweet(v, v{"card"}) -# if tweet.user.id in result.users: -# tweet.user = result.users[tweet.user.id] -# result.tweets[k] = tweet - -# proc parseInstructions(res: var Profile; global: GlobalObjects; js: JsonNode) = -# if js.kind != JArray or js.len == 0: -# return - -# for i in js: -# if res.tweets.beginning and i{"pinEntry"}.notNull: -# with pin, parsePin(i, global): -# res.pinned = some pin - -# with r, i{"replaceEntry", "entry"}: -# if "top" in r{"entryId"}.getStr: -# res.tweets.top = r.getCursor -# elif "bottom" in r{"entryId"}.getStr: -# res.tweets.bottom = r.getCursor - -# proc parseTimeline*(js: JsonNode; after=""): Profile = -# result = Profile(tweets: Timeline(beginning: after.len == 0)) -# let global = parseGlobalObjects(? js) - -# let instructions = ? js{"timeline", "instructions"} -# if instructions.len == 0: return - -# result.parseInstructions(global, instructions) - -# var entries: JsonNode -# for i in instructions: -# if "addEntries" in i: -# entries = i{"addEntries", "entries"} - -# for e in ? entries: -# let entry = e{"entryId"}.getStr -# if "tweet" in entry or entry.startsWith("sq-I-t") or "tombstone" in entry: -# let tweet = finalizeTweet(global, e.getEntryId) -# if not tweet.available: continue -# result.tweets.content.add tweet -# elif "cursor-top" in entry: -# result.tweets.top = e.getCursor -# elif "cursor-bottom" in entry: -# result.tweets.bottom = e.getCursor -# elif entry.startsWith("sq-cursor"): -# with cursor, e{"content", "operation", "cursor"}: -# if cursor{"cursorType"}.getStr == "Bottom": -# result.tweets.bottom = cursor{"value"}.getStr -# else: -# result.tweets.top = cursor{"value"}.getStr - proc parsePhotoRail*(js: JsonNode): PhotoRail = with error, js{"error"}: if error.getStr == "Not authorized.": @@ -597,8 +436,8 @@ proc parseGraphTimeline*(js: JsonNode; root: string; after=""): Profile = tweet.id = parseBiggestInt(entryId) result.pinned = some tweet -proc parseGraphSearch*(js: JsonNode; after=""): Timeline = - result = Timeline(beginning: after.len == 0) +proc parseGraphSearch*[T: User | Tweets](js: JsonNode; after=""): Result[T] = + result = Result[T](beginning: after.len == 0) let instructions = js{"data", "search_by_raw_query", "search_timeline", "timeline", "instructions"} if instructions.len == 0: @@ -607,15 +446,21 @@ proc parseGraphSearch*(js: JsonNode; after=""): Timeline = for instruction in instructions: let typ = instruction{"type"}.getStr if typ == "TimelineAddEntries": - for e in instructions[0]{"entries"}: + for e in instruction{"entries"}: let entryId = e{"entryId"}.getStr - if entryId.startsWith("tweet"): - with tweetResult, e{"content", "itemContent", "tweet_results", "result"}: - let tweet = parseGraphTweet(tweetResult) - if not tweet.available: - tweet.id = parseBiggestInt(entryId.getId()) - result.content.add tweet - elif entryId.startsWith("cursor-bottom"): + when T is Tweets: + if entryId.startsWith("tweet"): + with tweetRes, e{"content", "itemContent", "tweet_results", "result"}: + let tweet = parseGraphTweet(tweetRes) + if not tweet.available: + tweet.id = parseBiggestInt(entryId.getId()) + result.content.add tweet + elif T is User: + if entryId.startsWith("user"): + with userRes, e{"content", "itemContent"}: + result.content.add parseGraphUser(userRes) + + if entryId.startsWith("cursor-bottom"): result.bottom = e{"content", "value"}.getStr elif typ == "TimelineReplaceEntry": if instruction{"entry_id_to_replace"}.getStr.startsWith("cursor-bottom"): diff --git a/src/redis_cache.nim b/src/redis_cache.nim index 89161be..2387a42 100644 --- a/src/redis_cache.nim +++ b/src/redis_cache.nim @@ -147,15 +147,15 @@ proc getCachedUsername*(userId: string): Future[string] {.async.} = if result.len > 0 and user.id.len > 0: await all(cacheUserId(result, user.id), cache(user)) -proc getCachedTweet*(id: int64): Future[Tweet] {.async.} = - if id == 0: return - let tweet = await get(id.tweetKey) - if tweet != redisNil: - tweet.deserialize(Tweet) - else: - result = await getGraphTweetResult($id) - if not result.isNil: - await cache(result) +# proc getCachedTweet*(id: int64): Future[Tweet] {.async.} = +# if id == 0: return +# let tweet = await get(id.tweetKey) +# if tweet != redisNil: +# tweet.deserialize(Tweet) +# else: +# result = await getGraphTweetResult($id) +# if not result.isNil: +# await cache(result) proc getCachedPhotoRail*(name: string): Future[PhotoRail] {.async.} = if name.len == 0: return diff --git a/src/routes/rss.nim b/src/routes/rss.nim index d378396..6c77992 100644 --- a/src/routes/rss.nim +++ b/src/routes/rss.nim @@ -27,7 +27,7 @@ proc timelineRss*(req: Request; cfg: Config; query: Query): Future[Rss] {.async. else: var q = query q.fromUser = names - profile.tweets = await getTweetSearch(q, after) + profile.tweets = await getGraphTweetSearch(q, after) # this is kinda dumb profile.user = User( username: name, @@ -76,7 +76,7 @@ proc createRssRouter*(cfg: Config) = if rss.cursor.len > 0: respRss(rss, "Search") - let tweets = await getTweetSearch(query, cursor) + let tweets = await getGraphTweetSearch(query, cursor) rss.cursor = tweets.bottom rss.feed = renderSearchRss(tweets.content, query.text, genQueryUrl(query), cfg) diff --git a/src/routes/search.nim b/src/routes/search.nim index c270df5..e9f991d 100644 --- a/src/routes/search.nim +++ b/src/routes/search.nim @@ -29,13 +29,13 @@ proc createSearchRouter*(cfg: Config) = redirect("/" & q) var users: Result[User] try: - users = await getUserSearch(query, getCursor()) + users = await getGraphUserSearch(query, getCursor()) except InternalError: users = Result[User](beginning: true, query: query) resp renderMain(renderUserSearch(users, prefs), request, cfg, prefs, title) of tweets: let - tweets = await getTweetSearch(query, getCursor()) + tweets = await getGraphTweetSearch(query, getCursor()) rss = "/search/rss?" & genQueryUrl(query) resp renderMain(renderTweetSearch(tweets, prefs, getPath()), request, cfg, prefs, title, rss=rss) diff --git a/src/routes/timeline.nim b/src/routes/timeline.nim index 8d02b68..3568ab7 100644 --- a/src/routes/timeline.nim +++ b/src/routes/timeline.nim @@ -53,10 +53,10 @@ proc fetchProfile*(after: string; query: Query; skipRail=false; result = case query.kind - of posts: await getUserTimeline(userId, after) + of posts: await getGraphUserTweets(userId, TimelineKind.tweets, after) of replies: await getGraphUserTweets(userId, TimelineKind.replies, after) of media: await getGraphUserTweets(userId, TimelineKind.media, after) - else: Profile(tweets: await getTweetSearch(query, after)) + else: Profile(tweets: await getGraphTweetSearch(query, after)) result.user = await user result.photoRail = await rail @@ -67,7 +67,7 @@ proc showTimeline*(request: Request; query: Query; cfg: Config; prefs: Prefs; rss, after: string): Future[string] {.async.} = if query.fromUser.len != 1: let - timeline = await getTweetSearch(query, after) + timeline = await getGraphTweetSearch(query, after) html = renderTweetSearch(timeline, prefs, getPath()) return renderMain(html, request, cfg, prefs, "Multi", rss=rss) @@ -122,7 +122,7 @@ proc createTimelineRouter*(cfg: Config) = # used for the infinite scroll feature if @"scroll".len > 0: if query.fromUser.len != 1: - var timeline = await getTweetSearch(query, after) + var timeline = await getGraphTweetSearch(query, after) if timeline.content.len == 0: resp Http404 timeline.beginning = true resp $renderTweetSearch(timeline, prefs, getPath()) diff --git a/src/tokens.nim b/src/tokens.nim index decf228..71a7abd 100644 --- a/src/tokens.nim +++ b/src/tokens.nim @@ -1,23 +1,16 @@ # SPDX-License-Identifier: AGPL-3.0-only -import asyncdispatch, httpclient, times, sequtils, json, random -import strutils, tables -import types, consts +import asyncdispatch, times, json, random, strutils, tables +import types -const - maxConcurrentReqs = 5 # max requests at a time per token, to avoid race conditions - maxLastUse = 1.hours # if a token is unused for 60 minutes, it expires - maxAge = 2.hours + 55.minutes # tokens expire after 3 hours - failDelay = initDuration(minutes=30) +# max requests at a time per account to avoid race conditions +const maxConcurrentReqs = 5 var - tokenPool: seq[Token] - lastFailed: Time + accountPool: seq[GuestAccount] enableLogging = false -let headers = newHttpHeaders({"authorization": auth}) - template log(str) = - if enableLogging: echo "[tokens] ", str + if enableLogging: echo "[accounts] ", str proc getPoolJson*(): JsonNode = var @@ -26,141 +19,94 @@ proc getPoolJson*(): JsonNode = totalPending = 0 reqsPerApi: Table[string, int] - for token in tokenPool: - totalPending.inc(token.pending) - list[token.tok] = %*{ + for account in accountPool: + totalPending.inc(account.pending) + list[account.id] = %*{ "apis": newJObject(), - "pending": token.pending, - "init": $token.init, - "lastUse": $token.lastUse + "pending": account.pending, } - for api in token.apis.keys: - list[token.tok]["apis"][$api] = %token.apis[api] + for api in account.apis.keys: + list[account.id]["apis"][$api] = %account.apis[api].remaining let maxReqs = case api - of Api.search: 100000 + of Api.search: 50 of Api.photoRail: 180 - of Api.timeline: 187 - of Api.userTweets, Api.userTimeline: 300 - of Api.userTweetsAndReplies, Api.userRestId, - Api.userScreenName, Api.tweetDetail, Api.tweetResult, - Api.list, Api.listTweets, Api.listMembers, Api.listBySlug, Api.userMedia: 500 - of Api.userSearch: 900 - reqs = maxReqs - token.apis[api].remaining + of Api.userTweets, Api.userTweetsAndReplies, Api.userMedia, + Api.userRestId, Api.userScreenName, + Api.tweetDetail, Api.tweetResult, + Api.list, Api.listTweets, Api.listMembers, Api.listBySlug: 500 + reqs = maxReqs - account.apis[api].remaining reqsPerApi[$api] = reqsPerApi.getOrDefault($api, 0) + reqs totalReqs.inc(reqs) return %*{ - "amount": tokenPool.len, + "amount": accountPool.len, "requests": totalReqs, "pending": totalPending, "apis": reqsPerApi, - "tokens": list + "accounts": list } proc rateLimitError*(): ref RateLimitError = newException(RateLimitError, "rate limited") -proc fetchToken(): Future[Token] {.async.} = - if getTime() - lastFailed < failDelay: - raise rateLimitError() - - let client = newAsyncHttpClient(headers=headers) - - try: - let - resp = await client.postContent(activate) - tokNode = parseJson(resp)["guest_token"] - tok = tokNode.getStr($(tokNode.getInt)) - time = getTime() - - return Token(tok: tok, init: time, lastUse: time) - except Exception as e: - echo "[tokens] fetching token failed: ", e.msg - if "Try again" notin e.msg: - echo "[tokens] fetching tokens paused, resuming in 30 minutes" - lastFailed = getTime() - finally: - client.close() - -proc expired(token: Token): bool = - let time = getTime() - token.init < time - maxAge or token.lastUse < time - maxLastUse - -proc isLimited(token: Token; api: Api): bool = - if token.isNil or token.expired: +proc isLimited(account: GuestAccount; api: Api): bool = + if account.isNil: return true - if api in token.apis: - let limit = token.apis[api] + if api in account.apis: + let limit = account.apis[api] return (limit.remaining <= 10 and limit.reset > epochTime().int) else: return false -proc isReady(token: Token; api: Api): bool = - not (token.isNil or token.pending > maxConcurrentReqs or token.isLimited(api)) +proc isReady(account: GuestAccount; api: Api): bool = + not (account.isNil or account.pending > maxConcurrentReqs or account.isLimited(api)) -proc release*(token: Token; used=false; invalid=false) = - if token.isNil: return - if invalid or token.expired: - if invalid: log "discarding invalid token" - elif token.expired: log "discarding expired token" +proc release*(account: GuestAccount; used=false; invalid=false) = + if account.isNil: return + if invalid: + log "discarding invalid account: " & account.id - let idx = tokenPool.find(token) - if idx > -1: tokenPool.delete(idx) + let idx = accountPool.find(account) + if idx > -1: accountPool.delete(idx) elif used: - dec token.pending - token.lastUse = getTime() + dec account.pending -proc getToken*(api: Api): Future[Token] {.async.} = - for i in 0 ..< tokenPool.len: +proc getGuestAccount*(api: Api): Future[GuestAccount] {.async.} = + for i in 0 ..< accountPool.len: if result.isReady(api): break release(result) - result = tokenPool.sample() + result = accountPool.sample() - if not result.isReady(api): - release(result) - result = await fetchToken() - log "added new token to pool" - tokenPool.add result - - if not result.isNil: + if not result.isNil and result.isReady(api): inc result.pending else: + log "no accounts available for API: " & $api raise rateLimitError() -proc setRateLimit*(token: Token; api: Api; remaining, reset: int) = +proc setRateLimit*(account: GuestAccount; api: Api; remaining, reset: int) = # avoid undefined behavior in race conditions - if api in token.apis: - let limit = token.apis[api] + if api in account.apis: + let limit = account.apis[api] if limit.reset >= reset and limit.remaining < remaining: return + if limit.reset == reset and limit.remaining >= remaining: + account.apis[api].remaining = remaining + return - token.apis[api] = RateLimit(remaining: remaining, reset: reset) + account.apis[api] = RateLimit(remaining: remaining, reset: reset) -proc poolTokens*(amount: int) {.async.} = - var futs: seq[Future[Token]] - for i in 0 ..< amount: - futs.add fetchToken() - - for token in futs: - var newToken: Token - - try: newToken = await token - except: discard - - if not newToken.isNil: - log "added new token to pool" - tokenPool.add newToken - -proc initTokenPool*(cfg: Config) {.async.} = +proc initAccountPool*(cfg: Config; accounts: JsonNode) = enableLogging = cfg.enableDebug - while true: - if tokenPool.countIt(not it.isLimited(Api.userTimeline)) < cfg.minTokens: - await poolTokens(min(4, cfg.minTokens - tokenPool.len)) - await sleepAsync(2000) + for account in accounts: + accountPool.add GuestAccount( + id: account{"user", "id_str"}.getStr, + oauthToken: account{"oauth_token"}.getStr, + oauthSecret: account{"oauth_token_secret"}.getStr, + ) diff --git a/src/types.nim b/src/types.nim index 1a47d25..2a553dd 100644 --- a/src/types.nim +++ b/src/types.nim @@ -17,11 +17,8 @@ type Api* {.pure.} = enum tweetDetail tweetResult - timeline - userTimeline photoRail search - userSearch list listBySlug listMembers @@ -36,9 +33,11 @@ type remaining*: int reset*: int - Token* = ref object - tok*: string - init*: Time + GuestAccount* = ref object + id*: string + oauthToken*: string + oauthSecret*: string + # init*: Time lastUse*: Time pending*: int apis*: Table[Api, RateLimit] From bbd68e684071e8b26fca89587db48f12417a9bf0 Mon Sep 17 00:00:00 2001 From: Zed Date: Sat, 19 Aug 2023 01:13:36 +0200 Subject: [PATCH 129/409] Filter out account limits that already reset --- src/tokens.nim | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/tokens.nim b/src/tokens.nim index 71a7abd..401dc05 100644 --- a/src/tokens.nim +++ b/src/tokens.nim @@ -19,6 +19,8 @@ proc getPoolJson*(): JsonNode = totalPending = 0 reqsPerApi: Table[string, int] + let now = epochTime() + for account in accountPool: totalPending.inc(account.pending) list[account.id] = %*{ @@ -27,6 +29,9 @@ proc getPoolJson*(): JsonNode = } for api in account.apis.keys: + if (now.int - account.apis[api].reset) / 60 > 15: + continue + list[account.id]["apis"][$api] = %account.apis[api].remaining let From 3d8858f0d86d09ce815bc78db417290557f23908 Mon Sep 17 00:00:00 2001 From: Zed Date: Sun, 20 Aug 2023 11:56:42 +0200 Subject: [PATCH 130/409] Track rate limits, reset after 24 hours --- src/apiutils.nim | 12 +++++++++++- src/tokens.nim | 26 ++++++++++++++++++++------ src/types.nim | 2 ++ 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/apiutils.nim b/src/apiutils.nim index d1ecfa3..54e6777 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -1,5 +1,5 @@ # SPDX-License-Identifier: AGPL-3.0-only -import httpclient, asyncdispatch, options, strutils, uri, times, math +import httpclient, asyncdispatch, options, strutils, uri, times, math, tables import jsony, packedjson, zippy, oauth1 import types, tokens, consts, parserutils, http_pool import experimental/types/common @@ -129,6 +129,16 @@ proc fetch*(url: Uri; api: Api): Future[JsonNode] {.async.} = release(account, invalid=true) raise rateLimitError() + if body.startsWith("{\"errors"): + let errors = body.fromJson(Errors) + if errors in {invalidToken, badToken}: + echo "fetch error: ", errors + release(account, invalid=true) + raise rateLimitError() + elif errors in {rateLimited}: + account.apis[api].limited = true + echo "rate limited, api: ", $api, ", reqs left: ", account.apis[api].remaining, ", id: ", account.id + proc fetchRaw*(url: Uri; api: Api): Future[string] {.async.} = fetchImpl result: if not (result.startsWith('{') or result.startsWith('[')): diff --git a/src/tokens.nim b/src/tokens.nim index 401dc05..45aa895 100644 --- a/src/tokens.nim +++ b/src/tokens.nim @@ -3,7 +3,9 @@ import asyncdispatch, times, json, random, strutils, tables import types # max requests at a time per account to avoid race conditions -const maxConcurrentReqs = 5 +const + maxConcurrentReqs = 5 + dayInSeconds = 24 * 60 * 60 var accountPool: seq[GuestAccount] @@ -19,7 +21,7 @@ proc getPoolJson*(): JsonNode = totalPending = 0 reqsPerApi: Table[string, int] - let now = epochTime() + let now = epochTime().int for account in accountPool: totalPending.inc(account.pending) @@ -29,10 +31,17 @@ proc getPoolJson*(): JsonNode = } for api in account.apis.keys: - if (now.int - account.apis[api].reset) / 60 > 15: - continue + let obj = %*{} + if account.apis[api].limited: + obj["limited"] = %true - list[account.id]["apis"][$api] = %account.apis[api].remaining + if account.apis[api].reset > now.int: + obj["remaining"] = %account.apis[api].remaining + + list[account.id]["apis"][$api] = obj + + if "remaining" notin obj: + continue let maxReqs = @@ -65,7 +74,12 @@ proc isLimited(account: GuestAccount; api: Api): bool = if api in account.apis: let limit = account.apis[api] - return (limit.remaining <= 10 and limit.reset > epochTime().int) + + if limit.limited and (epochTime().int - limit.limitedAt) > dayInSeconds: + account.apis[api].limited = false + echo "account limit reset, api: ", api, ", id: ", account.id + + return limit.limited or (limit.remaining <= 10 and limit.reset > epochTime().int) else: return false diff --git a/src/types.nim b/src/types.nim index 2a553dd..33d0cda 100644 --- a/src/types.nim +++ b/src/types.nim @@ -32,6 +32,8 @@ type RateLimit* = object remaining*: int reset*: int + limited*: bool + limitedAt*: int GuestAccount* = ref object id*: string From e8b5cbef7b984c527675a4df8fe2c7b4d841b13c Mon Sep 17 00:00:00 2001 From: Zed Date: Sun, 20 Aug 2023 12:31:08 +0200 Subject: [PATCH 131/409] Add missing limitedAt assignment --- src/apiutils.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/src/apiutils.nim b/src/apiutils.nim index 54e6777..6e333e7 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -137,6 +137,7 @@ proc fetch*(url: Uri; api: Api): Future[JsonNode] {.async.} = raise rateLimitError() elif errors in {rateLimited}: account.apis[api].limited = true + account.apis[api].limitedAt = epochTime().int echo "rate limited, api: ", $api, ", reqs left: ", account.apis[api].remaining, ", id: ", account.id proc fetchRaw*(url: Uri; api: Api): Future[string] {.async.} = From 51714b5ad2f8992972f43fd9cb2178aba2729f39 Mon Sep 17 00:00:00 2001 From: Zed Date: Mon, 21 Aug 2023 11:25:27 +0200 Subject: [PATCH 132/409] Add guest accounts variable to GitHub action --- .github/workflows/run-tests.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 140b6bf..37979cb 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -40,6 +40,9 @@ jobs: nimble md nimble scss - name: Run tests + env: + GUEST_ACCOUNTS: ${{ secrets.GUEST_ACCOUNTS }} run: | + echo $GUEST_ACCOUNTS > ./guest_accounts.json ./nitter & pytest -n4 tests From c3d9441370e9740a02834dbf1da849263a9075c3 Mon Sep 17 00:00:00 2001 From: Zed Date: Mon, 21 Aug 2023 14:49:50 +0200 Subject: [PATCH 133/409] Unify some guest account logs --- src/apiutils.nim | 2 +- src/tokens.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/apiutils.nim b/src/apiutils.nim index 6e333e7..453b36a 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -138,7 +138,7 @@ proc fetch*(url: Uri; api: Api): Future[JsonNode] {.async.} = elif errors in {rateLimited}: account.apis[api].limited = true account.apis[api].limitedAt = epochTime().int - echo "rate limited, api: ", $api, ", reqs left: ", account.apis[api].remaining, ", id: ", account.id + echo "[accounts] rate limited, api: ", api, ", reqs left: ", account.apis[api].remaining, ", id: ", account.id proc fetchRaw*(url: Uri; api: Api): Future[string] {.async.} = fetchImpl result: diff --git a/src/tokens.nim b/src/tokens.nim index 45aa895..a44866f 100644 --- a/src/tokens.nim +++ b/src/tokens.nim @@ -77,7 +77,7 @@ proc isLimited(account: GuestAccount; api: Api): bool = if limit.limited and (epochTime().int - limit.limitedAt) > dayInSeconds: account.apis[api].limited = false - echo "account limit reset, api: ", api, ", id: ", account.id + log "resetting limit, api: ", api, ", id: ", account.id return limit.limited or (limit.remaining <= 10 and limit.reset > epochTime().int) else: From 12504bcffed59efe626b90a574b27857f7450515 Mon Sep 17 00:00:00 2001 From: Zed Date: Mon, 21 Aug 2023 18:12:06 +0200 Subject: [PATCH 134/409] Fix compilation error --- src/tokens.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tokens.nim b/src/tokens.nim index a44866f..d1f1e68 100644 --- a/src/tokens.nim +++ b/src/tokens.nim @@ -77,7 +77,7 @@ proc isLimited(account: GuestAccount; api: Api): bool = if limit.limited and (epochTime().int - limit.limitedAt) > dayInSeconds: account.apis[api].limited = false - log "resetting limit, api: ", api, ", id: ", account.id + log "resetting limit, api: " & $api & ", id: " & $account.id return limit.limited or (limit.remaining <= 10 and limit.reset > epochTime().int) else: From 30bdf3a14e900c653f1c277c5e0221cab846e1de Mon Sep 17 00:00:00 2001 From: Zed Date: Tue, 22 Aug 2023 01:32:09 +0200 Subject: [PATCH 135/409] Reduce max concurrent pending requests per account --- src/tokens.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tokens.nim b/src/tokens.nim index d1f1e68..f16a816 100644 --- a/src/tokens.nim +++ b/src/tokens.nim @@ -1,10 +1,10 @@ -# SPDX-License-Identifier: AGPL-3.0-only +#i hate begging for this too em SPDX-License-Identifier: AGPL-3.0-only import asyncdispatch, times, json, random, strutils, tables import types # max requests at a time per account to avoid race conditions const - maxConcurrentReqs = 5 + maxConcurrentReqs = 2 dayInSeconds = 24 * 60 * 60 var From 5c08e6a774f428b4193a33e0400729c279ded07c Mon Sep 17 00:00:00 2001 From: Zed Date: Tue, 22 Aug 2023 02:27:44 +0200 Subject: [PATCH 136/409] Fix compilation on older versions of Nim --- src/parserutils.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/parserutils.nim b/src/parserutils.nim index c65052e..7cf696e 100644 --- a/src/parserutils.nim +++ b/src/parserutils.nim @@ -36,7 +36,8 @@ template with*(ident, value, body): untyped = template with*(ident; value: JsonNode; body): untyped = if true: let ident {.inject.} = value - if value.notNull: body + # value.notNull causes a compilation error for versions < 1.6.14 + if notNull(value): body template getCursor*(js: JsonNode): string = js{"content", "operation", "cursor", "value"}.getStr From 6e8744943f9c47cbc7150785a86fa05a18d7b98b Mon Sep 17 00:00:00 2001 From: Zed Date: Tue, 22 Aug 2023 03:43:18 +0200 Subject: [PATCH 137/409] Tweak /.tokens, add amount of limited accounts --- src/tokens.nim | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/tokens.nim b/src/tokens.nim index f16a816..5582a51 100644 --- a/src/tokens.nim +++ b/src/tokens.nim @@ -19,6 +19,7 @@ proc getPoolJson*(): JsonNode = list = newJObject() totalReqs = 0 totalPending = 0 + totalLimited = 0 reqsPerApi: Table[string, int] let now = epochTime().int @@ -31,34 +32,40 @@ proc getPoolJson*(): JsonNode = } for api in account.apis.keys: - let obj = %*{} - if account.apis[api].limited: - obj["limited"] = %true + let + apiStatus = account.apis[api] + obj = %*{} - if account.apis[api].reset > now.int: - obj["remaining"] = %account.apis[api].remaining + if apiStatus.limited: + obj["limited"] = %true + inc totalLimited + + if apiStatus.reset > now.int: + obj["remaining"] = %apiStatus.remaining + + if "remaining" notin obj and not apiStatus.limited: + continue list[account.id]["apis"][$api] = obj - if "remaining" notin obj: - continue - let maxReqs = case api of Api.search: 50 + of Api.tweetDetail: 150 of Api.photoRail: 180 of Api.userTweets, Api.userTweetsAndReplies, Api.userMedia, - Api.userRestId, Api.userScreenName, - Api.tweetDetail, Api.tweetResult, + Api.userRestId, Api.userScreenName, Api.tweetResult, Api.list, Api.listTweets, Api.listMembers, Api.listBySlug: 500 - reqs = maxReqs - account.apis[api].remaining + of Api.userSearch: 900 + reqs = maxReqs - apiStatus.remaining reqsPerApi[$api] = reqsPerApi.getOrDefault($api, 0) + reqs totalReqs.inc(reqs) return %*{ "amount": accountPool.len, + "limited": totalLimited, "requests": totalReqs, "pending": totalPending, "apis": reqsPerApi, From 8df5256c1dd3970bcbb71cfa7753c3b50d59287e Mon Sep 17 00:00:00 2001 From: Zed Date: Tue, 22 Aug 2023 03:44:11 +0200 Subject: [PATCH 138/409] Switch back to old user search endpoint --- src/api.nim | 28 ++++++++++++---------------- src/consts.nim | 2 ++ src/experimental/parser/user.nim | 2 +- src/experimental/types/user.nim | 1 + src/parser.nim | 26 ++++++++++---------------- src/routes/search.nim | 2 +- src/types.nim | 1 + 7 files changed, 28 insertions(+), 34 deletions(-) diff --git a/src/api.nim b/src/api.nim index d6a4564..4ac999c 100644 --- a/src/api.nim +++ b/src/api.nim @@ -112,29 +112,25 @@ proc getGraphTweetSearch*(query: Query; after=""): Future[Timeline] {.async.} = if after.len > 0: variables["cursor"] = % after let url = graphSearchTimeline ? {"variables": $variables, "features": gqlFeatures} - result = parseGraphSearch[Tweets](await fetch(url, Api.search), after) + result = parseGraphSearch(await fetch(url, Api.search), after) result.query = query -proc getGraphUserSearch*(query: Query; after=""): Future[Result[User]] {.async.} = +proc getUserSearch*(query: Query; page="1"): Future[Result[User]] {.async.} = if query.text.len == 0: return Result[User](query: query, beginning: true) - var - variables = %*{ - "rawQuery": query.text, - "count": 20, - "product": "People", - "withDownvotePerspective": false, - "withReactionsMetadata": false, - "withReactionsPerspective": false - } - if after.len > 0: - variables["cursor"] = % after - result.beginning = false + let + page = if page.len == 0: "1" else: page + url = userSearch ? genParams({"q": query.text, "skip_status": "1", "page": page}) + js = await fetchRaw(url, Api.userSearch) + + result = parseUsers(js) - let url = graphSearchTimeline ? {"variables": $variables, "features": gqlFeatures} - result = parseGraphSearch[User](await fetch(url, Api.search), after) result.query = query + if page.len == 0: + result.bottom = "2" + elif page.allCharsInSet(Digits): + result.bottom = $(parseInt(page) + 1) proc getPhotoRail*(name: string): Future[PhotoRail] {.async.} = if name.len == 0: return diff --git a/src/consts.nim b/src/consts.nim index 2cfd1ed..8bf6422 100644 --- a/src/consts.nim +++ b/src/consts.nim @@ -9,6 +9,7 @@ const activate* = $(api / "1.1/guest/activate.json") photoRail* = api / "1.1/statuses/media_timeline.json" + userSearch* = api / "1.1/users/search.json" graphql = api / "graphql" graphUser* = graphql / "u7wQyGi6oExe8_TRWGMq4Q/UserResultByScreenNameQuery" @@ -34,6 +35,7 @@ const "include_user_entities": "1", "include_ext_reply_count": "1", "include_ext_is_blue_verified": "1", + #"include_ext_verified_type": "1", "include_ext_media_color": "0", "cards_platform": "Web-13", "tweet_mode": "extended", diff --git a/src/experimental/parser/user.nim b/src/experimental/parser/user.nim index b4d710f..5962a87 100644 --- a/src/experimental/parser/user.nim +++ b/src/experimental/parser/user.nim @@ -56,7 +56,7 @@ proc toUser*(raw: RawUser): User = tweets: raw.statusesCount, likes: raw.favouritesCount, media: raw.mediaCount, - verified: raw.verified, + verified: raw.verified or raw.extIsBlueVerified, protected: raw.protected, joinDate: parseTwitterDate(raw.createdAt), banner: getBanner(raw), diff --git a/src/experimental/types/user.nim b/src/experimental/types/user.nim index 1c8a5c3..39331a0 100644 --- a/src/experimental/types/user.nim +++ b/src/experimental/types/user.nim @@ -16,6 +16,7 @@ type statusesCount*: int mediaCount*: int verified*: bool + extIsBlueVerified*: bool protected*: bool profileLinkColor*: string profileBannerUrl*: string diff --git a/src/parser.nim b/src/parser.nim index 9262b28..03242c1 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -1,5 +1,5 @@ # SPDX-License-Identifier: AGPL-3.0-only -import strutils, options, times, math, tables +import strutils, options, times, math import packedjson, packedjson/deserialiser import types, parserutils, utils import experimental/parser/unifiedcard @@ -436,8 +436,8 @@ proc parseGraphTimeline*(js: JsonNode; root: string; after=""): Profile = tweet.id = parseBiggestInt(entryId) result.pinned = some tweet -proc parseGraphSearch*[T: User | Tweets](js: JsonNode; after=""): Result[T] = - result = Result[T](beginning: after.len == 0) +proc parseGraphSearch*(js: JsonNode; after=""): Timeline = + result = Timeline(beginning: after.len == 0) let instructions = js{"data", "search_by_raw_query", "search_timeline", "timeline", "instructions"} if instructions.len == 0: @@ -448,19 +448,13 @@ proc parseGraphSearch*[T: User | Tweets](js: JsonNode; after=""): Result[T] = if typ == "TimelineAddEntries": for e in instruction{"entries"}: let entryId = e{"entryId"}.getStr - when T is Tweets: - if entryId.startsWith("tweet"): - with tweetRes, e{"content", "itemContent", "tweet_results", "result"}: - let tweet = parseGraphTweet(tweetRes) - if not tweet.available: - tweet.id = parseBiggestInt(entryId.getId()) - result.content.add tweet - elif T is User: - if entryId.startsWith("user"): - with userRes, e{"content", "itemContent"}: - result.content.add parseGraphUser(userRes) - - if entryId.startsWith("cursor-bottom"): + if entryId.startsWith("tweet"): + with tweetRes, e{"content", "itemContent", "tweet_results", "result"}: + let tweet = parseGraphTweet(tweetRes) + if not tweet.available: + tweet.id = parseBiggestInt(entryId.getId()) + result.content.add tweet + elif entryId.startsWith("cursor-bottom"): result.bottom = e{"content", "value"}.getStr elif typ == "TimelineReplaceEntry": if instruction{"entry_id_to_replace"}.getStr.startsWith("cursor-bottom"): diff --git a/src/routes/search.nim b/src/routes/search.nim index e9f991d..676229e 100644 --- a/src/routes/search.nim +++ b/src/routes/search.nim @@ -29,7 +29,7 @@ proc createSearchRouter*(cfg: Config) = redirect("/" & q) var users: Result[User] try: - users = await getGraphUserSearch(query, getCursor()) + users = await getUserSearch(query, getCursor()) except InternalError: users = Result[User](beginning: true, query: query) resp renderMain(renderUserSearch(users, prefs), request, cfg, prefs, title) diff --git a/src/types.nim b/src/types.nim index 33d0cda..fcb24c0 100644 --- a/src/types.nim +++ b/src/types.nim @@ -19,6 +19,7 @@ type tweetResult photoRail search + userSearch list listBySlug listMembers From 45808361af63e848ce322a27190d3ed7aba0723a Mon Sep 17 00:00:00 2001 From: Zed Date: Tue, 22 Aug 2023 04:45:49 +0200 Subject: [PATCH 139/409] Fix tweetDetail stats --- src/tokens.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tokens.nim b/src/tokens.nim index 5582a51..a3ed78a 100644 --- a/src/tokens.nim +++ b/src/tokens.nim @@ -52,10 +52,10 @@ proc getPoolJson*(): JsonNode = maxReqs = case api of Api.search: 50 - of Api.tweetDetail: 150 of Api.photoRail: 180 of Api.userTweets, Api.userTweetsAndReplies, Api.userMedia, - Api.userRestId, Api.userScreenName, Api.tweetResult, + Api.userRestId, Api.userScreenName, + Api.tweetResult, Api.tweetDetail, Api.list, Api.listTweets, Api.listMembers, Api.listBySlug: 500 of Api.userSearch: 900 reqs = maxReqs - apiStatus.remaining From a3e11e3272ebdd6118cd818591530ac8be5ae491 Mon Sep 17 00:00:00 2001 From: Zed Date: Wed, 23 Aug 2023 10:14:44 +0200 Subject: [PATCH 140/409] Switch to using typeahead for user search --- src/api.nim | 12 +++--------- src/consts.nim | 2 +- src/experimental/parser/user.nim | 7 +++++++ src/experimental/types/user.nim | 3 +++ src/routes/search.nim | 2 +- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/api.nim b/src/api.nim index 4ac999c..a7efbd0 100644 --- a/src/api.nim +++ b/src/api.nim @@ -115,22 +115,16 @@ proc getGraphTweetSearch*(query: Query; after=""): Future[Timeline] {.async.} = result = parseGraphSearch(await fetch(url, Api.search), after) result.query = query -proc getUserSearch*(query: Query; page="1"): Future[Result[User]] {.async.} = +proc getUserSearch*(query: Query): Future[Result[User]] {.async.} = if query.text.len == 0: return Result[User](query: query, beginning: true) let - page = if page.len == 0: "1" else: page - url = userSearch ? genParams({"q": query.text, "skip_status": "1", "page": page}) + url = userSearch ? genParams({"q": query.text, "result_type": "users"}) js = await fetchRaw(url, Api.userSearch) - result = parseUsers(js) - + result = parseTypeahead(js) result.query = query - if page.len == 0: - result.bottom = "2" - elif page.allCharsInSet(Digits): - result.bottom = $(parseInt(page) + 1) proc getPhotoRail*(name: string): Future[PhotoRail] {.async.} = if name.len == 0: return diff --git a/src/consts.nim b/src/consts.nim index 8bf6422..1bb950e 100644 --- a/src/consts.nim +++ b/src/consts.nim @@ -9,7 +9,7 @@ const activate* = $(api / "1.1/guest/activate.json") photoRail* = api / "1.1/statuses/media_timeline.json" - userSearch* = api / "1.1/users/search.json" + userSearch* = api / "1.1/search/typeahead.json" graphql = api / "graphql" graphUser* = graphql / "u7wQyGi6oExe8_TRWGMq4Q/UserResultByScreenNameQuery" diff --git a/src/experimental/parser/user.nim b/src/experimental/parser/user.nim index 5962a87..400e740 100644 --- a/src/experimental/parser/user.nim +++ b/src/experimental/parser/user.nim @@ -85,3 +85,10 @@ proc parseUsers*(json: string; after=""): Result[User] = let raw = json.fromJson(seq[RawUser]) for user in raw: result.content.add user.toUser + +proc parseTypeahead*(json: string): Result[User] = + result = Result[User](beginning: true) + + let raw = json.fromJson(Typeahead) + for user in raw.users: + result.content.add user.toUser diff --git a/src/experimental/types/user.nim b/src/experimental/types/user.nim index 39331a0..7d34f7b 100644 --- a/src/experimental/types/user.nim +++ b/src/experimental/types/user.nim @@ -42,3 +42,6 @@ type Color* = object red*, green*, blue*: int + + Typeahead* = object + users*: seq[RawUser] diff --git a/src/routes/search.nim b/src/routes/search.nim index 676229e..e0a888c 100644 --- a/src/routes/search.nim +++ b/src/routes/search.nim @@ -29,7 +29,7 @@ proc createSearchRouter*(cfg: Config) = redirect("/" & q) var users: Result[User] try: - users = await getUserSearch(query, getCursor()) + users = await getUserSearch(query) except InternalError: users = Result[User](beginning: true, query: query) resp renderMain(renderUserSearch(users, prefs), request, cfg, prefs, title) From 88b005c9da726af3f0858339da60949b42278744 Mon Sep 17 00:00:00 2001 From: Zed Date: Wed, 23 Aug 2023 19:31:40 +0200 Subject: [PATCH 141/409] Revert "Switch to using typeahead for user search" This reverts commit a3e11e3272ebdd6118cd818591530ac8be5ae491. --- src/api.nim | 12 +++++++++--- src/consts.nim | 2 +- src/experimental/parser/user.nim | 7 ------- src/experimental/types/user.nim | 3 --- src/routes/search.nim | 2 +- 5 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/api.nim b/src/api.nim index a7efbd0..4ac999c 100644 --- a/src/api.nim +++ b/src/api.nim @@ -115,16 +115,22 @@ proc getGraphTweetSearch*(query: Query; after=""): Future[Timeline] {.async.} = result = parseGraphSearch(await fetch(url, Api.search), after) result.query = query -proc getUserSearch*(query: Query): Future[Result[User]] {.async.} = +proc getUserSearch*(query: Query; page="1"): Future[Result[User]] {.async.} = if query.text.len == 0: return Result[User](query: query, beginning: true) let - url = userSearch ? genParams({"q": query.text, "result_type": "users"}) + page = if page.len == 0: "1" else: page + url = userSearch ? genParams({"q": query.text, "skip_status": "1", "page": page}) js = await fetchRaw(url, Api.userSearch) - result = parseTypeahead(js) + result = parseUsers(js) + result.query = query + if page.len == 0: + result.bottom = "2" + elif page.allCharsInSet(Digits): + result.bottom = $(parseInt(page) + 1) proc getPhotoRail*(name: string): Future[PhotoRail] {.async.} = if name.len == 0: return diff --git a/src/consts.nim b/src/consts.nim index 1bb950e..8bf6422 100644 --- a/src/consts.nim +++ b/src/consts.nim @@ -9,7 +9,7 @@ const activate* = $(api / "1.1/guest/activate.json") photoRail* = api / "1.1/statuses/media_timeline.json" - userSearch* = api / "1.1/search/typeahead.json" + userSearch* = api / "1.1/users/search.json" graphql = api / "graphql" graphUser* = graphql / "u7wQyGi6oExe8_TRWGMq4Q/UserResultByScreenNameQuery" diff --git a/src/experimental/parser/user.nim b/src/experimental/parser/user.nim index 400e740..5962a87 100644 --- a/src/experimental/parser/user.nim +++ b/src/experimental/parser/user.nim @@ -85,10 +85,3 @@ proc parseUsers*(json: string; after=""): Result[User] = let raw = json.fromJson(seq[RawUser]) for user in raw: result.content.add user.toUser - -proc parseTypeahead*(json: string): Result[User] = - result = Result[User](beginning: true) - - let raw = json.fromJson(Typeahead) - for user in raw.users: - result.content.add user.toUser diff --git a/src/experimental/types/user.nim b/src/experimental/types/user.nim index 7d34f7b..39331a0 100644 --- a/src/experimental/types/user.nim +++ b/src/experimental/types/user.nim @@ -42,6 +42,3 @@ type Color* = object red*, green*, blue*: int - - Typeahead* = object - users*: seq[RawUser] diff --git a/src/routes/search.nim b/src/routes/search.nim index e0a888c..676229e 100644 --- a/src/routes/search.nim +++ b/src/routes/search.nim @@ -29,7 +29,7 @@ proc createSearchRouter*(cfg: Config) = redirect("/" & q) var users: Result[User] try: - users = await getUserSearch(query) + users = await getUserSearch(query, getCursor()) except InternalError: users = Result[User](beginning: true, query: query) resp renderMain(renderUserSearch(users, prefs), request, cfg, prefs, title) From ae9fa02bf5f8459e59f0f18120fee9f06404aef5 Mon Sep 17 00:00:00 2001 From: Zed Date: Fri, 25 Aug 2023 16:28:30 +0200 Subject: [PATCH 142/409] Switch to TweetDetail for tweets --- src/consts.nim | 10 +++++++--- src/parser.nim | 25 +++++++++++++++---------- src/tokens.nim | 3 ++- tests/test_card.py | 10 +++++----- tests/test_timeline.py | 2 +- tests/test_tweet_media.py | 2 +- 6 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/consts.nim b/src/consts.nim index 8bf6422..96cea47 100644 --- a/src/consts.nim +++ b/src/consts.nim @@ -17,7 +17,7 @@ const graphUserTweets* = graphql / "3JNH4e9dq1BifLxAa3UMWg/UserWithProfileTweetsQueryV2" graphUserTweetsAndReplies* = graphql / "8IS8MaO-2EN6GZZZb8jF0g/UserWithProfileTweetsAndRepliesQueryV2" graphUserMedia* = graphql / "PDfFf8hGeJvUCiTyWtw4wQ/MediaTimelineV2" - graphTweet* = graphql / "83h5UyHZ9wEKBVzALX8R_g/ConversationTimelineV2" + graphTweet* = graphql / "q94uRCEn65LZThakYcPT6g/TweetDetail" graphTweetResult* = graphql / "sITyJdhRPpvpEjg4waUmTA/TweetResultByIdQuery" graphSearchTimeline* = graphql / "gkjsKepM6gl_HmFWoWKfgg/SearchTimeline" graphListById* = graphql / "iTpgCtbdxrsJfyx0cFjHqg/ListByRestId" @@ -89,8 +89,12 @@ const tweetVariables* = """{ "focalTweetId": "$1", $2 - "includeHasBirdwatchNotes": false -}""" + "includeHasBirdwatchNotes": false, + "includePromotedContent": false, + "withBirdwatchNotes": false, + "withVoice": false, + "withV2Timeline": true +}""".replace(" ", "").replace("\n", "") # oldUserTweetsVariables* = """{ # "userId": "$1", $2 diff --git a/src/parser.nim b/src/parser.nim index 03242c1..d5190a3 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -324,7 +324,7 @@ proc parseGraphTweet(js: JsonNode): Tweet = of "TweetWithVisibilityResults": return parseGraphTweet(js{"tweet"}) - var jsCard = copy(js{"tweet_card", "legacy"}) + var jsCard = copy(js{"card", "legacy"}) if jsCard.kind != JNull: var values = newJObject() for val in jsCard["binding_values"]: @@ -342,7 +342,6 @@ proc parseGraphTweet(js: JsonNode): Tweet = result.quote = some(parseGraphTweet(js{"quoted_status_result", "result"})) proc parseGraphThread(js: JsonNode): tuple[thread: Chain; self: bool] = - let thread = js{"content", "items"} for t in js{"content", "items"}: let entryId = t{"entryId"}.getStr if "cursor-showmore" in entryId: @@ -350,11 +349,16 @@ proc parseGraphThread(js: JsonNode): tuple[thread: Chain; self: bool] = result.thread.cursor = cursor.getStr result.thread.hasMore = true elif "tweet" in entryId: - let tweet = parseGraphTweet(t{"item", "content", "tweetResult", "result"}) - result.thread.content.add tweet + let + isLegacy = t{"item"}.hasKey("itemContent") + (contentKey, resultKey) = if isLegacy: ("itemContent", "tweet_results") + else: ("content", "tweetResult") - if t{"item", "content", "tweetDisplayType"}.getStr == "SelfThread": - result.self = true + with content, t{"item", contentKey}: + result.thread.content.add parseGraphTweet(content{resultKey, "result"}) + + if content{"tweetDisplayType"}.getStr == "SelfThread": + result.self = true proc parseGraphTweetResult*(js: JsonNode): Tweet = with tweet, js{"data", "tweet_result", "result"}: @@ -363,14 +367,14 @@ proc parseGraphTweetResult*(js: JsonNode): Tweet = proc parseGraphConversation*(js: JsonNode; tweetId: string): Conversation = result = Conversation(replies: Result[Chain](beginning: true)) - let instructions = ? js{"data", "timeline_response", "instructions"} + let instructions = ? js{"data", "threaded_conversation_with_injections_v2", "instructions"} if instructions.len == 0: return for e in instructions[0]{"entries"}: let entryId = e{"entryId"}.getStr if entryId.startsWith("tweet"): - with tweetResult, e{"content", "content", "tweetResult", "result"}: + with tweetResult, e{"content", "itemContent", "tweet_results", "result"}: let tweet = parseGraphTweet(tweetResult) if not tweet.available: @@ -385,7 +389,7 @@ proc parseGraphConversation*(js: JsonNode; tweetId: string): Conversation = let tweet = Tweet( id: parseBiggestInt(id), available: false, - text: e{"content", "content", "tombstoneInfo", "richText"}.getTombstone + text: e{"content", "itemContent", "tombstoneInfo", "richText"}.getTombstone ) if id == tweetId: @@ -397,9 +401,10 @@ proc parseGraphConversation*(js: JsonNode; tweetId: string): Conversation = if self: result.after = thread else: + echo "adding thread: ", thread.content.len result.replies.content.add thread elif entryId.startsWith("cursor-bottom"): - result.replies.bottom = e{"content", "content", "value"}.getStr + result.replies.bottom = e{"content", "itemContent", "value"}.getStr proc parseGraphTimeline*(js: JsonNode; root: string; after=""): Profile = result = Profile(tweets: Timeline(beginning: after.len == 0)) diff --git a/src/tokens.nim b/src/tokens.nim index a3ed78a..bb9696c 100644 --- a/src/tokens.nim +++ b/src/tokens.nim @@ -52,10 +52,11 @@ proc getPoolJson*(): JsonNode = maxReqs = case api of Api.search: 50 + of Api.tweetDetail: 150 of Api.photoRail: 180 of Api.userTweets, Api.userTweetsAndReplies, Api.userMedia, Api.userRestId, Api.userScreenName, - Api.tweetResult, Api.tweetDetail, + Api.tweetResult, Api.list, Api.listTweets, Api.listMembers, Api.listBySlug: 500 of Api.userSearch: 900 reqs = maxReqs - apiStatus.remaining diff --git a/tests/test_card.py b/tests/test_card.py index f84ddca..733bd40 100644 --- a/tests/test_card.py +++ b/tests/test_card.py @@ -13,11 +13,6 @@ card = [ 'Basic OBS Studio plugin, written in nim, supporting C++ (C fine too) - obsplugin.nim', 'gist.github.com', True], - ['FluentAI/status/1116417904831029248', - 'Amazon’s Alexa isn’t just AI — thousands of humans are listening', - 'One of the only ways to improve Alexa is to have human beings check it for errors', - 'theverge.com', True], - ['nim_lang/status/1082989146040340480', 'Nim in 2018: A short recap', 'There were several big news in the Nim world in 2018 – two new major releases, partnership with Status, and much more. But let us go chronologically.', @@ -25,6 +20,11 @@ card = [ ] no_thumb = [ + ['FluentAI/status/1116417904831029248', + 'Amazon’s Alexa isn’t just AI — thousands of humans are listening', + 'One of the only ways to improve Alexa is to have human beings check it for errors', + 'theverge.com'], + ['Thom_Wolf/status/1122466524860702729', 'facebookresearch/fairseq', 'Facebook AI Research Sequence-to-Sequence Toolkit written in Python. - GitHub - facebookresearch/fairseq: Facebook AI Research Sequence-to-Sequence Toolkit written in Python.', diff --git a/tests/test_timeline.py b/tests/test_timeline.py index b56d6ad..919aa70 100644 --- a/tests/test_timeline.py +++ b/tests/test_timeline.py @@ -6,7 +6,7 @@ normal = [['jack'], ['elonmusk']] after = [['jack', '1681686036294803456'], ['elonmusk', '1681686036294803456']] -no_more = [['mobile_test_8?cursor=1000']] +no_more = [['mobile_test_8?cursor=DAABCgABF4YVAqN___kKAAICNn_4msIQAAgAAwAAAAIAAA']] empty = [['emptyuser'], ['mobile_test_10']] diff --git a/tests/test_tweet_media.py b/tests/test_tweet_media.py index 7a00983..f54cea7 100644 --- a/tests/test_tweet_media.py +++ b/tests/test_tweet_media.py @@ -14,7 +14,7 @@ poll = [ image = [ ['mobile_test/status/519364660823207936', 'BzUnaDFCUAAmrjs'], - ['mobile_test_2/status/324619691039543297', 'BIFH45vCUAAQecj'] + #['mobile_test_2/status/324619691039543297', 'BIFH45vCUAAQecj'] ] gif = [ From 03794a8d4a0eb13fccdf88d5f6633956daaf6564 Mon Sep 17 00:00:00 2001 From: Zed Date: Fri, 25 Aug 2023 16:32:33 +0200 Subject: [PATCH 143/409] Cleanup --- src/parser.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/src/parser.nim b/src/parser.nim index d5190a3..4087a79 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -401,7 +401,6 @@ proc parseGraphConversation*(js: JsonNode; tweetId: string): Conversation = if self: result.after = thread else: - echo "adding thread: ", thread.content.len result.replies.content.add thread elif entryId.startsWith("cursor-bottom"): result.replies.bottom = e{"content", "itemContent", "value"}.getStr From 7630f57f17246ffb60d4f5472d17af5fc3c6fa9f Mon Sep 17 00:00:00 2001 From: Zed Date: Sat, 26 Aug 2023 05:16:38 +0200 Subject: [PATCH 144/409] Fix cards not being displayed --- src/parser.nim | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/parser.nim b/src/parser.nim index 4087a79..914c038 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -4,7 +4,7 @@ import packedjson, packedjson/deserialiser import types, parserutils, utils import experimental/parser/unifiedcard -proc parseGraphTweet(js: JsonNode): Tweet +proc parseGraphTweet(js: JsonNode; isLegacy=false): Tweet proc parseUser(js: JsonNode; id=""): User = if js.isNull: return @@ -306,7 +306,7 @@ proc parsePhotoRail*(js: JsonNode): PhotoRail = if url.len == 0: continue result.add GalleryPhoto(url: url, tweetId: $t.id) -proc parseGraphTweet(js: JsonNode): Tweet = +proc parseGraphTweet(js: JsonNode; isLegacy=false): Tweet = if js.kind == JNull: return Tweet() @@ -322,9 +322,9 @@ proc parseGraphTweet(js: JsonNode): Tweet = of "TweetPreviewDisplay": return Tweet(text: "You're unable to view this Tweet because it's only available to the Subscribers of the account owner.") of "TweetWithVisibilityResults": - return parseGraphTweet(js{"tweet"}) + return parseGraphTweet(js{"tweet"}, isLegacy) - var jsCard = copy(js{"card", "legacy"}) + var jsCard = copy(js{if isLegacy: "card" else: "tweet_card", "legacy"}) if jsCard.kind != JNull: var values = newJObject() for val in jsCard["binding_values"]: @@ -339,7 +339,7 @@ proc parseGraphTweet(js: JsonNode): Tweet = result.expandNoteTweetEntities(noteTweet) if result.quote.isSome: - result.quote = some(parseGraphTweet(js{"quoted_status_result", "result"})) + result.quote = some(parseGraphTweet(js{"quoted_status_result", "result"}, isLegacy)) proc parseGraphThread(js: JsonNode): tuple[thread: Chain; self: bool] = for t in js{"content", "items"}: @@ -355,14 +355,14 @@ proc parseGraphThread(js: JsonNode): tuple[thread: Chain; self: bool] = else: ("content", "tweetResult") with content, t{"item", contentKey}: - result.thread.content.add parseGraphTweet(content{resultKey, "result"}) + result.thread.content.add parseGraphTweet(content{resultKey, "result"}, isLegacy) if content{"tweetDisplayType"}.getStr == "SelfThread": result.self = true proc parseGraphTweetResult*(js: JsonNode): Tweet = with tweet, js{"data", "tweet_result", "result"}: - result = parseGraphTweet(tweet) + result = parseGraphTweet(tweet, false) proc parseGraphConversation*(js: JsonNode; tweetId: string): Conversation = result = Conversation(replies: Result[Chain](beginning: true)) @@ -375,7 +375,7 @@ proc parseGraphConversation*(js: JsonNode; tweetId: string): Conversation = let entryId = e{"entryId"}.getStr if entryId.startsWith("tweet"): with tweetResult, e{"content", "itemContent", "tweet_results", "result"}: - let tweet = parseGraphTweet(tweetResult) + let tweet = parseGraphTweet(tweetResult, true) if not tweet.available: tweet.id = parseBiggestInt(entryId.getId()) @@ -421,7 +421,7 @@ proc parseGraphTimeline*(js: JsonNode; root: string; after=""): Profile = let entryId = e{"entryId"}.getStr if entryId.startsWith("tweet"): with tweetResult, e{"content", "content", "tweetResult", "result"}: - let tweet = parseGraphTweet(tweetResult) + let tweet = parseGraphTweet(tweetResult, false) if not tweet.available: tweet.id = parseBiggestInt(entryId.getId()) result.tweets.content.add tweet @@ -432,7 +432,7 @@ proc parseGraphTimeline*(js: JsonNode; root: string; after=""): Profile = result.tweets.bottom = e{"content", "value"}.getStr if after.len == 0 and i{"__typename"}.getStr == "TimelinePinEntry": with tweetResult, i{"entry", "content", "content", "tweetResult", "result"}: - let tweet = parseGraphTweet(tweetResult) + let tweet = parseGraphTweet(tweetResult, false) tweet.pinned = true if not tweet.available and tweet.tombstone.len == 0: let entryId = i{"entry", "entryId"}.getEntryId @@ -454,7 +454,7 @@ proc parseGraphSearch*(js: JsonNode; after=""): Timeline = let entryId = e{"entryId"}.getStr if entryId.startsWith("tweet"): with tweetRes, e{"content", "itemContent", "tweet_results", "result"}: - let tweet = parseGraphTweet(tweetRes) + let tweet = parseGraphTweet(tweetRes, true) if not tweet.available: tweet.id = parseBiggestInt(entryId.getId()) result.content.add tweet From 4ccf350dc74416173cad16eb0a7dd4b49239fd2c Mon Sep 17 00:00:00 2001 From: Zed Date: Tue, 29 Aug 2023 23:45:18 +0200 Subject: [PATCH 145/409] Improve .tokens output --- src/tokens.nim | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/tokens.nim b/src/tokens.nim index bb9696c..a3af9bf 100644 --- a/src/tokens.nim +++ b/src/tokens.nim @@ -1,5 +1,5 @@ -#i hate begging for this too em SPDX-License-Identifier: AGPL-3.0-only -import asyncdispatch, times, json, random, strutils, tables +#SPDX-License-Identifier: AGPL-3.0-only +import asyncdispatch, times, json, random, strutils, tables, sets import types # max requests at a time per account to avoid race conditions @@ -19,14 +19,16 @@ proc getPoolJson*(): JsonNode = list = newJObject() totalReqs = 0 totalPending = 0 - totalLimited = 0 + limited: HashSet[string] reqsPerApi: Table[string, int] let now = epochTime().int for account in accountPool: totalPending.inc(account.pending) - list[account.id] = %*{ + + var includeAccount = false + let accountJson = %*{ "apis": newJObject(), "pending": account.pending, } @@ -36,17 +38,18 @@ proc getPoolJson*(): JsonNode = apiStatus = account.apis[api] obj = %*{} - if apiStatus.limited: - obj["limited"] = %true - inc totalLimited - if apiStatus.reset > now.int: obj["remaining"] = %apiStatus.remaining if "remaining" notin obj and not apiStatus.limited: continue - list[account.id]["apis"][$api] = obj + if apiStatus.limited: + obj["limited"] = %true + limited.incl account.id + + accountJson{"apis", $api} = obj + includeAccount = true let maxReqs = @@ -64,9 +67,12 @@ proc getPoolJson*(): JsonNode = reqsPerApi[$api] = reqsPerApi.getOrDefault($api, 0) + reqs totalReqs.inc(reqs) + if includeAccount: + list[account.id] = accountJson + return %*{ "amount": accountPool.len, - "limited": totalLimited, + "limited": limited.card, "requests": totalReqs, "pending": totalPending, "apis": reqsPerApi, From 986b91ac733b36949bd9ac13c78409c4a98f83a0 Mon Sep 17 00:00:00 2001 From: Zed Date: Tue, 29 Aug 2023 23:58:03 +0200 Subject: [PATCH 146/409] Handle ProtocolError and BadClientError equally --- src/http_pool.nim | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/http_pool.nim b/src/http_pool.nim index b4e3cee..664e9a6 100644 --- a/src/http_pool.nim +++ b/src/http_pool.nim @@ -39,11 +39,8 @@ template use*(pool: HttpPool; heads: HttpHeaders; body: untyped): untyped = try: body - except ProtocolError: - # Twitter closed the connection, retry - body - except BadClientError: - # Twitter returned 503, we need a new client + except BadClientError, ProtocolError: + # Twitter returned 503 or closed the connection, we need a new client pool.release(c, true) badClient = false c = pool.acquire(heads) From 898b19b92f3121b2185ba3a59381be2ea8299f44 Mon Sep 17 00:00:00 2001 From: Zed Date: Wed, 30 Aug 2023 03:04:22 +0200 Subject: [PATCH 147/409] Improve rate limit handling, minor refactor --- src/apiutils.nim | 67 ++++++++++++++++++++---------------------------- src/tokens.nim | 28 ++++++++++++-------- src/types.nim | 2 +- 3 files changed, 47 insertions(+), 50 deletions(-) diff --git a/src/apiutils.nim b/src/apiutils.nim index 453b36a..37afded 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -61,13 +61,6 @@ proc genHeaders*(url, oauthToken, oauthTokenSecret: string): HttpHeaders = "DNT": "1" }) -template updateAccount() = - if resp.headers.hasKey(rlRemaining): - let - remaining = parseInt(resp.headers[rlRemaining]) - reset = parseInt(resp.headers[rlReset]) - account.setRateLimit(api, remaining, reset) - template fetchImpl(result, fetchBody) {.dirty.} = once: pool = HttpPool() @@ -89,28 +82,46 @@ template fetchImpl(result, fetchBody) {.dirty.} = badClient = true raise newException(BadClientError, "Bad client") + if resp.headers.hasKey(rlRemaining): + let + remaining = parseInt(resp.headers[rlRemaining]) + reset = parseInt(resp.headers[rlReset]) + account.setRateLimit(api, remaining, reset) + if result.len > 0: if resp.headers.getOrDefault("content-encoding") == "gzip": result = uncompress(result, dfGzip) - else: - echo "non-gzip body, url: ", url, ", body: ", result + + if result.startsWith("{\"errors"): + let errors = result.fromJson(Errors) + if errors in {expiredToken, badToken}: + echo "fetch error: ", errors + invalidate(account) + raise rateLimitError() + elif errors in {rateLimited}: + # rate limit hit, resets after 24 hours + setLimited(account, api) + raise rateLimitError() + elif result.startsWith("429 Too Many Requests"): + account.apis[api].remaining = 0 + # rate limit hit, resets after the 15 minute window + raise rateLimitError() fetchBody - release(account, used=true) - if resp.status == $Http400: raise newException(InternalError, $url) except InternalError as e: raise e except BadClientError as e: - release(account, used=true) + raise e + except OSError as e: raise e except Exception as e: echo "error: ", e.name, ", msg: ", e.msg, ", accountId: ", account.id, ", url: ", url - if "length" notin e.msg and "descriptor" notin e.msg: - release(account, invalid=true) raise rateLimitError() + finally: + release(account) proc fetch*(url: Uri; api: Api): Future[JsonNode] {.async.} = var body: string @@ -121,36 +132,14 @@ proc fetch*(url: Uri; api: Api): Future[JsonNode] {.async.} = echo resp.status, ": ", body, " --- url: ", url result = newJNull() - updateAccount() - let error = result.getError - if error in {invalidToken, badToken}: - echo "fetch error: ", result.getError - release(account, invalid=true) + if error in {expiredToken, badToken}: + echo "fetchBody error: ", error + invalidate(account) raise rateLimitError() - if body.startsWith("{\"errors"): - let errors = body.fromJson(Errors) - if errors in {invalidToken, badToken}: - echo "fetch error: ", errors - release(account, invalid=true) - raise rateLimitError() - elif errors in {rateLimited}: - account.apis[api].limited = true - account.apis[api].limitedAt = epochTime().int - echo "[accounts] rate limited, api: ", api, ", reqs left: ", account.apis[api].remaining, ", id: ", account.id - proc fetchRaw*(url: Uri; api: Api): Future[string] {.async.} = fetchImpl result: if not (result.startsWith('{') or result.startsWith('[')): echo resp.status, ": ", result, " --- url: ", url result.setLen(0) - - updateAccount() - - if result.startsWith("{\"errors"): - let errors = result.fromJson(Errors) - if errors in {invalidToken, badToken}: - echo "fetch error: ", errors - release(account, invalid=true) - raise rateLimitError() diff --git a/src/tokens.nim b/src/tokens.nim index a3af9bf..c620bc7 100644 --- a/src/tokens.nim +++ b/src/tokens.nim @@ -11,7 +11,7 @@ var accountPool: seq[GuestAccount] enableLogging = false -template log(str) = +template log(str: varargs[string, `$`]) = if enableLogging: echo "[accounts] ", str proc getPoolJson*(): JsonNode = @@ -91,7 +91,7 @@ proc isLimited(account: GuestAccount; api: Api): bool = if limit.limited and (epochTime().int - limit.limitedAt) > dayInSeconds: account.apis[api].limited = false - log "resetting limit, api: " & $api & ", id: " & $account.id + log "resetting limit, api: ", api, ", id: ", account.id return limit.limited or (limit.remaining <= 10 and limit.reset > epochTime().int) else: @@ -100,15 +100,18 @@ proc isLimited(account: GuestAccount; api: Api): bool = proc isReady(account: GuestAccount; api: Api): bool = not (account.isNil or account.pending > maxConcurrentReqs or account.isLimited(api)) -proc release*(account: GuestAccount; used=false; invalid=false) = +proc invalidate*(account: var GuestAccount) = if account.isNil: return - if invalid: - log "discarding invalid account: " & account.id + log "invalidating expired account: ", account.id - let idx = accountPool.find(account) - if idx > -1: accountPool.delete(idx) - elif used: - dec account.pending + # TODO: This isn't sufficient, but it works for now + let idx = accountPool.find(account) + if idx > -1: accountPool.delete(idx) + account = nil + +proc release*(account: GuestAccount; invalid=false) = + if account.isNil: return + dec account.pending proc getGuestAccount*(api: Api): Future[GuestAccount] {.async.} = for i in 0 ..< accountPool.len: @@ -119,9 +122,14 @@ proc getGuestAccount*(api: Api): Future[GuestAccount] {.async.} = if not result.isNil and result.isReady(api): inc result.pending else: - log "no accounts available for API: " & $api + log "no accounts available for API: ", api raise rateLimitError() +proc setLimited*(account: GuestAccount; api: Api) = + account.apis[api].limited = true + account.apis[api].limitedAt = epochTime().int + log "rate limited, api: ", api, ", reqs left: ", account.apis[api].remaining, ", id: ", account.id + proc setRateLimit*(account: GuestAccount; api: Api; remaining, reset: int) = # avoid undefined behavior in race conditions if api in account.apis: diff --git a/src/types.nim b/src/types.nim index fcb24c0..8a7a66e 100644 --- a/src/types.nim +++ b/src/types.nim @@ -56,7 +56,7 @@ type userNotFound = 50 suspended = 63 rateLimited = 88 - invalidToken = 89 + expiredToken = 89 listIdOrSlug = 112 tweetNotFound = 144 tweetNotAuthorized = 179 From 37b58a5a7e52b897c565677016c8d7bf56494bc4 Mon Sep 17 00:00:00 2001 From: Zed Date: Wed, 30 Aug 2023 03:43:49 +0200 Subject: [PATCH 148/409] Fix accounts logging --- src/tokens.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tokens.nim b/src/tokens.nim index c620bc7..3628ef6 100644 --- a/src/tokens.nim +++ b/src/tokens.nim @@ -12,7 +12,7 @@ var enableLogging = false template log(str: varargs[string, `$`]) = - if enableLogging: echo "[accounts] ", str + if enableLogging: echo "[accounts] ", str.join("") proc getPoolJson*(): JsonNode = var From 282ce8b0e9088a0a215116ce234c0b46f1e0d55f Mon Sep 17 00:00:00 2001 From: Zed Date: Thu, 31 Aug 2023 01:29:54 +0200 Subject: [PATCH 149/409] Add 429 logging --- src/apiutils.nim | 1 + src/tokens.nim | 2 +- src/types.nim | 2 -- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/apiutils.nim b/src/apiutils.nim index 37afded..e5b9be2 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -103,6 +103,7 @@ template fetchImpl(result, fetchBody) {.dirty.} = setLimited(account, api) raise rateLimitError() elif result.startsWith("429 Too Many Requests"): + echo "[accounts] 429 error, API: ", api, ", account: ", account.id account.apis[api].remaining = 0 # rate limit hit, resets after the 15 minute window raise rateLimitError() diff --git a/src/tokens.nim b/src/tokens.nim index 3628ef6..a4ebe7f 100644 --- a/src/tokens.nim +++ b/src/tokens.nim @@ -109,7 +109,7 @@ proc invalidate*(account: var GuestAccount) = if idx > -1: accountPool.delete(idx) account = nil -proc release*(account: GuestAccount; invalid=false) = +proc release*(account: GuestAccount) = if account.isNil: return dec account.pending diff --git a/src/types.nim b/src/types.nim index 8a7a66e..4cacc4b 100644 --- a/src/types.nim +++ b/src/types.nim @@ -40,8 +40,6 @@ type id*: string oauthToken*: string oauthSecret*: string - # init*: Time - lastUse*: Time pending*: int apis*: Table[Api, RateLimit] From 82beb5da8c60a981f0ce61fc99c8f23dff3b3865 Mon Sep 17 00:00:00 2001 From: Zed Date: Thu, 31 Aug 2023 01:31:27 +0200 Subject: [PATCH 150/409] Add empty oauth token logging --- src/apiutils.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/src/apiutils.nim b/src/apiutils.nim index e5b9be2..0b1db26 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -67,6 +67,7 @@ template fetchImpl(result, fetchBody) {.dirty.} = var account = await getGuestAccount(api) if account.oauthToken.len == 0: + echo "[accounts] Empty oauth token, account: ", account.id raise rateLimitError() try: From 84dcf4907907fc59a60e7bf2a7db738789e3e868 Mon Sep 17 00:00:00 2001 From: Zed Date: Thu, 31 Aug 2023 05:06:47 +0200 Subject: [PATCH 151/409] Fix negative pending requests bug --- src/tokens.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tokens.nim b/src/tokens.nim index a4ebe7f..3e20597 100644 --- a/src/tokens.nim +++ b/src/tokens.nim @@ -116,7 +116,6 @@ proc release*(account: GuestAccount) = proc getGuestAccount*(api: Api): Future[GuestAccount] {.async.} = for i in 0 ..< accountPool.len: if result.isReady(api): break - release(result) result = accountPool.sample() if not result.isNil and result.isReady(api): From b8fe212e941ebb27a39b0900cfe271d700738f22 Mon Sep 17 00:00:00 2001 From: Zed Date: Fri, 1 Sep 2023 21:37:34 +0200 Subject: [PATCH 152/409] Add media proxying error logging --- src/routes/media.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/routes/media.nim b/src/routes/media.nim index e63a0f8..d335c97 100644 --- a/src/routes/media.nim +++ b/src/routes/media.nim @@ -37,6 +37,7 @@ proc proxyMedia*(req: jester.Request; url: string): Future[HttpCode] {.async.} = try: let res = await client.get(url) if res.status != "200 OK": + echo "[media] Proxying media failed, status: $1, url: $2, body: $3" % [res.status, url, await res.body] return Http404 let hashed = $hash(url) @@ -65,6 +66,7 @@ proc proxyMedia*(req: jester.Request; url: string): Future[HttpCode] {.async.} = await request.client.send(data) data.setLen 0 except HttpRequestError, ProtocolError, OSError: + echo "[media] Proxying media exception, error: $1, url: $2" % [getCurrentExceptionMsg(), url] result = Http404 finally: client.close() From 4250245263b77bd2b0ecd9a31c0e4000d8e2e6f7 Mon Sep 17 00:00:00 2001 From: Zed Date: Sat, 2 Sep 2023 07:28:56 +0200 Subject: [PATCH 153/409] Shorten media proxy error log --- src/routes/media.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/media.nim b/src/routes/media.nim index d335c97..eacd1f8 100644 --- a/src/routes/media.nim +++ b/src/routes/media.nim @@ -37,7 +37,7 @@ proc proxyMedia*(req: jester.Request; url: string): Future[HttpCode] {.async.} = try: let res = await client.get(url) if res.status != "200 OK": - echo "[media] Proxying media failed, status: $1, url: $2, body: $3" % [res.status, url, await res.body] + echo "[media] Proxying failed, status: $1, url: $2" % [res.status, url] return Http404 let hashed = $hash(url) @@ -66,7 +66,7 @@ proc proxyMedia*(req: jester.Request; url: string): Future[HttpCode] {.async.} = await request.client.send(data) data.setLen 0 except HttpRequestError, ProtocolError, OSError: - echo "[media] Proxying media exception, error: $1, url: $2" % [getCurrentExceptionMsg(), url] + echo "[media] Proxying exception, error: $1, url: $2" % [getCurrentExceptionMsg(), url] result = Http404 finally: client.close() From fcd74e8048362fcf8284871ee067099e8de28a89 Mon Sep 17 00:00:00 2001 From: Zed Date: Sat, 2 Sep 2023 08:15:58 +0200 Subject: [PATCH 154/409] Retry rate limited requests with different account --- src/apiutils.nim | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/src/apiutils.nim b/src/apiutils.nim index 0b1db26..9ac101e 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -120,28 +120,38 @@ template fetchImpl(result, fetchBody) {.dirty.} = except OSError as e: raise e except Exception as e: - echo "error: ", e.name, ", msg: ", e.msg, ", accountId: ", account.id, ", url: ", url + let id = if account.isNil: "null" else: account.id + echo "error: ", e.name, ", msg: ", e.msg, ", accountId: ", id, ", url: ", url raise rateLimitError() finally: release(account) -proc fetch*(url: Uri; api: Api): Future[JsonNode] {.async.} = - var body: string - fetchImpl body: - if body.startsWith('{') or body.startsWith('['): - result = parseJson(body) - else: - echo resp.status, ": ", body, " --- url: ", url - result = newJNull() +template retry(bod) = + try: + bod + except RateLimitError: + echo "[accounts] Rate limited, retrying ", api, " request..." + bod - let error = result.getError - if error in {expiredToken, badToken}: - echo "fetchBody error: ", error - invalidate(account) - raise rateLimitError() +proc fetch*(url: Uri; api: Api): Future[JsonNode] {.async.} = + retry: + var body: string + fetchImpl body: + if body.startsWith('{') or body.startsWith('['): + result = parseJson(body) + else: + echo resp.status, ": ", body, " --- url: ", url + result = newJNull() + + let error = result.getError + if error in {expiredToken, badToken}: + echo "fetchBody error: ", error + invalidate(account) + raise rateLimitError() proc fetchRaw*(url: Uri; api: Api): Future[string] {.async.} = - fetchImpl result: - if not (result.startsWith('{') or result.startsWith('[')): - echo resp.status, ": ", result, " --- url: ", url - result.setLen(0) + retry: + fetchImpl result: + if not (result.startsWith('{') or result.startsWith('[')): + echo resp.status, ": ", result, " --- url: ", url + result.setLen(0) From 14f9a092d832c0aaf7eb4900ab36a76c6653c364 Mon Sep 17 00:00:00 2001 From: Zed Date: Thu, 14 Sep 2023 23:35:41 +0000 Subject: [PATCH 155/409] Fix crash on missing quote tweet data crash --- src/parser.nim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/parser.nim b/src/parser.nim index 914c038..776f176 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -324,6 +324,9 @@ proc parseGraphTweet(js: JsonNode; isLegacy=false): Tweet = of "TweetWithVisibilityResults": return parseGraphTweet(js{"tweet"}, isLegacy) + if not js.hasKey("legacy"): + return Tweet() + var jsCard = copy(js{if isLegacy: "card" else: "tweet_card", "legacy"}) if jsCard.kind != JNull: var values = newJObject() From 7abcb489f4176532322669e97eb2503a78b2bbc2 Mon Sep 17 00:00:00 2001 From: Zed Date: Mon, 18 Sep 2023 17:15:09 +0000 Subject: [PATCH 156/409] Increase photo rail cache ttl --- src/redis_cache.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/redis_cache.nim b/src/redis_cache.nim index 2387a42..a8b5ff8 100644 --- a/src/redis_cache.nim +++ b/src/redis_cache.nim @@ -85,7 +85,7 @@ proc cache*(data: List) {.async.} = await setEx(data.listKey, listCacheTime, compress(toFlatty(data))) proc cache*(data: PhotoRail; name: string) {.async.} = - await setEx("pr:" & toLower(name), baseCacheTime, compress(toFlatty(data))) + await setEx("pr:" & toLower(name), baseCacheTime * 2, compress(toFlatty(data))) proc cache*(data: User) {.async.} = if data.username.len == 0: return From 7d147899103bf1a21b291641fd4230a02ba48fab Mon Sep 17 00:00:00 2001 From: Zed Date: Mon, 18 Sep 2023 18:24:23 +0000 Subject: [PATCH 157/409] Improve guest accounts loading, add JSONL support --- .gitignore | 1 + src/experimental/parser/guestaccount.nim | 20 ++++++++++++++++++++ src/experimental/types/guestaccount.nim | 4 ++++ src/nitter.nim | 4 +--- src/tokens.nim | 23 +++++++++++++++-------- 5 files changed, 41 insertions(+), 11 deletions(-) create mode 100644 src/experimental/parser/guestaccount.nim create mode 100644 src/experimental/types/guestaccount.nim diff --git a/.gitignore b/.gitignore index d43cc3f..ea520dc 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ nitter /public/css/style.css /public/md/*.html nitter.conf +guest_accounts.json* dump.rdb diff --git a/src/experimental/parser/guestaccount.nim b/src/experimental/parser/guestaccount.nim new file mode 100644 index 0000000..4d8ff47 --- /dev/null +++ b/src/experimental/parser/guestaccount.nim @@ -0,0 +1,20 @@ +import jsony +import ../types/guestaccount +from ../../types import GuestAccount + +proc toGuestAccount(account: RawAccount): GuestAccount = + let id = account.oauthToken[0 ..< account.oauthToken.find('-')] + result = GuestAccount( + id: id, + oauthToken: account.oauthToken, + oauthSecret: account.oauthTokenSecret + ) + +proc parseGuestAccount*(raw: string): GuestAccount = + let rawAccount = raw.fromJson(RawAccount) + result = rawAccount.toGuestAccount + +proc parseGuestAccounts*(path: string): seq[GuestAccount] = + let rawAccounts = readFile(path).fromJson(seq[RawAccount]) + for account in rawAccounts: + result.add account.toGuestAccount diff --git a/src/experimental/types/guestaccount.nim b/src/experimental/types/guestaccount.nim new file mode 100644 index 0000000..244edb3 --- /dev/null +++ b/src/experimental/types/guestaccount.nim @@ -0,0 +1,4 @@ +type + RawAccount* = object + oauthToken*: string + oauthTokenSecret*: string diff --git a/src/nitter.nim b/src/nitter.nim index 4a4ec13..1b4862b 100644 --- a/src/nitter.nim +++ b/src/nitter.nim @@ -3,7 +3,6 @@ import asyncdispatch, strformat, logging from net import Port from htmlgen import a from os import getEnv -from json import parseJson import jester @@ -21,9 +20,8 @@ let (cfg, fullCfg) = getConfig(configPath) accountsPath = getEnv("NITTER_ACCOUNTS_FILE", "./guest_accounts.json") - accounts = parseJson(readFile(accountsPath)) -initAccountPool(cfg, parseJson(readFile(accountsPath))) +initAccountPool(cfg, accountsPath) if not cfg.enableDebug: # Silence Jester's query warning diff --git a/src/tokens.nim b/src/tokens.nim index 3e20597..b8a50e3 100644 --- a/src/tokens.nim +++ b/src/tokens.nim @@ -1,6 +1,7 @@ #SPDX-License-Identifier: AGPL-3.0-only -import asyncdispatch, times, json, random, strutils, tables, sets +import asyncdispatch, times, json, random, strutils, tables, sets, os import types +import experimental/parser/guestaccount # max requests at a time per account to avoid race conditions const @@ -141,12 +142,18 @@ proc setRateLimit*(account: GuestAccount; api: Api; remaining, reset: int) = account.apis[api] = RateLimit(remaining: remaining, reset: reset) -proc initAccountPool*(cfg: Config; accounts: JsonNode) = +proc initAccountPool*(cfg: Config; path: string) = enableLogging = cfg.enableDebug - for account in accounts: - accountPool.add GuestAccount( - id: account{"user", "id_str"}.getStr, - oauthToken: account{"oauth_token"}.getStr, - oauthSecret: account{"oauth_token_secret"}.getStr, - ) + let jsonlPath = if path.endsWith(".json"): (path & 'l') else: path + + if fileExists(jsonlPath): + log "Parsing JSONL guest accounts file: ", jsonlPath + for line in jsonlPath.lines: + accountPool.add parseGuestAccount(line) + elif fileExists(path): + log "Parsing JSON guest accounts file: ", path + accountPool = parseGuestAccounts(path) + else: + echo "[accounts] ERROR: ", path, " not found. This file is required to authenticate API requests." + quit 1 From 537af7fd5e846e614315fbbff8741d3c7f0c74ba Mon Sep 17 00:00:00 2001 From: Zed Date: Tue, 19 Sep 2023 01:29:41 +0000 Subject: [PATCH 158/409] Improve Liberapay css for Firefox compatibility --- src/sass/navbar.scss | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sass/navbar.scss b/src/sass/navbar.scss index cf9c80e..47a8765 100644 --- a/src/sass/navbar.scss +++ b/src/sass/navbar.scss @@ -70,8 +70,9 @@ nav { .lp { height: 14px; - margin-top: 2px; - display: block; + display: inline-block; + position: relative; + top: 2px; fill: var(--fg_nav); &:hover { From 735b30c2da336cc57be2b98c48a6f7e826fdaed0 Mon Sep 17 00:00:00 2001 From: LS <66217791+DrSocket@users.noreply.github.com> Date: Mon, 30 Oct 2023 13:13:06 +0100 Subject: [PATCH 159/409] fix(nitter): add graphql user search (#1047) * fix(nitter): add graphql user search * fix(nitter): rm gitignore 2nd guest_accounts * fix(nitter): keep query from user search in result. remove personal mods * fix(nitter): removce useless line gitignore --- src/api.nim | 28 ++++++++++++++++------------ src/consts.nim | 3 +-- src/parser.nim | 24 +++++++++++++++--------- src/routes/search.nim | 2 +- src/tokens.nim | 1 - src/types.nim | 1 - 6 files changed, 33 insertions(+), 26 deletions(-) diff --git a/src/api.nim b/src/api.nim index 4ac999c..d6a4564 100644 --- a/src/api.nim +++ b/src/api.nim @@ -112,25 +112,29 @@ proc getGraphTweetSearch*(query: Query; after=""): Future[Timeline] {.async.} = if after.len > 0: variables["cursor"] = % after let url = graphSearchTimeline ? {"variables": $variables, "features": gqlFeatures} - result = parseGraphSearch(await fetch(url, Api.search), after) + result = parseGraphSearch[Tweets](await fetch(url, Api.search), after) result.query = query -proc getUserSearch*(query: Query; page="1"): Future[Result[User]] {.async.} = +proc getGraphUserSearch*(query: Query; after=""): Future[Result[User]] {.async.} = if query.text.len == 0: return Result[User](query: query, beginning: true) - let - page = if page.len == 0: "1" else: page - url = userSearch ? genParams({"q": query.text, "skip_status": "1", "page": page}) - js = await fetchRaw(url, Api.userSearch) - - result = parseUsers(js) + var + variables = %*{ + "rawQuery": query.text, + "count": 20, + "product": "People", + "withDownvotePerspective": false, + "withReactionsMetadata": false, + "withReactionsPerspective": false + } + if after.len > 0: + variables["cursor"] = % after + result.beginning = false + let url = graphSearchTimeline ? {"variables": $variables, "features": gqlFeatures} + result = parseGraphSearch[User](await fetch(url, Api.search), after) result.query = query - if page.len == 0: - result.bottom = "2" - elif page.allCharsInSet(Digits): - result.bottom = $(parseInt(page) + 1) proc getPhotoRail*(name: string): Future[PhotoRail] {.async.} = if name.len == 0: return diff --git a/src/consts.nim b/src/consts.nim index 96cea47..d3a3d80 100644 --- a/src/consts.nim +++ b/src/consts.nim @@ -9,7 +9,6 @@ const activate* = $(api / "1.1/guest/activate.json") photoRail* = api / "1.1/statuses/media_timeline.json" - userSearch* = api / "1.1/users/search.json" graphql = api / "graphql" graphUser* = graphql / "u7wQyGi6oExe8_TRWGMq4Q/UserResultByScreenNameQuery" @@ -35,7 +34,7 @@ const "include_user_entities": "1", "include_ext_reply_count": "1", "include_ext_is_blue_verified": "1", - #"include_ext_verified_type": "1", + # "include_ext_verified_type": "1", "include_ext_media_color": "0", "cards_platform": "Web-13", "tweet_mode": "extended", diff --git a/src/parser.nim b/src/parser.nim index 776f176..cebf6f1 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -443,8 +443,8 @@ proc parseGraphTimeline*(js: JsonNode; root: string; after=""): Profile = tweet.id = parseBiggestInt(entryId) result.pinned = some tweet -proc parseGraphSearch*(js: JsonNode; after=""): Timeline = - result = Timeline(beginning: after.len == 0) +proc parseGraphSearch*[T: User | Tweets](js: JsonNode; after=""): Result[T] = + result = Result[T](beginning: after.len == 0) let instructions = js{"data", "search_by_raw_query", "search_timeline", "timeline", "instructions"} if instructions.len == 0: @@ -455,13 +455,19 @@ proc parseGraphSearch*(js: JsonNode; after=""): Timeline = if typ == "TimelineAddEntries": for e in instruction{"entries"}: let entryId = e{"entryId"}.getStr - if entryId.startsWith("tweet"): - with tweetRes, e{"content", "itemContent", "tweet_results", "result"}: - let tweet = parseGraphTweet(tweetRes, true) - if not tweet.available: - tweet.id = parseBiggestInt(entryId.getId()) - result.content.add tweet - elif entryId.startsWith("cursor-bottom"): + when T is Tweets: + if entryId.startsWith("tweet"): + with tweetRes, e{"content", "itemContent", "tweet_results", "result"}: + let tweet = parseGraphTweet(tweetRes) + if not tweet.available: + tweet.id = parseBiggestInt(entryId.getId()) + result.content.add tweet + elif T is User: + if entryId.startsWith("user"): + with userRes, e{"content", "itemContent"}: + result.content.add parseGraphUser(userRes) + + if entryId.startsWith("cursor-bottom"): result.bottom = e{"content", "value"}.getStr elif typ == "TimelineReplaceEntry": if instruction{"entry_id_to_replace"}.getStr.startsWith("cursor-bottom"): diff --git a/src/routes/search.nim b/src/routes/search.nim index 676229e..e9f991d 100644 --- a/src/routes/search.nim +++ b/src/routes/search.nim @@ -29,7 +29,7 @@ proc createSearchRouter*(cfg: Config) = redirect("/" & q) var users: Result[User] try: - users = await getUserSearch(query, getCursor()) + users = await getGraphUserSearch(query, getCursor()) except InternalError: users = Result[User](beginning: true, query: query) resp renderMain(renderUserSearch(users, prefs), request, cfg, prefs, title) diff --git a/src/tokens.nim b/src/tokens.nim index b8a50e3..ca74ddc 100644 --- a/src/tokens.nim +++ b/src/tokens.nim @@ -62,7 +62,6 @@ proc getPoolJson*(): JsonNode = Api.userRestId, Api.userScreenName, Api.tweetResult, Api.list, Api.listTweets, Api.listMembers, Api.listBySlug: 500 - of Api.userSearch: 900 reqs = maxReqs - apiStatus.remaining reqsPerApi[$api] = reqsPerApi.getOrDefault($api, 0) + reqs diff --git a/src/types.nim b/src/types.nim index 4cacc4b..3f5f8ac 100644 --- a/src/types.nim +++ b/src/types.nim @@ -19,7 +19,6 @@ type tweetResult photoRail search - userSearch list listBySlug listMembers From 32e3469e3a580464a79c6b2b6bfbaa6757bd8cfe Mon Sep 17 00:00:00 2001 From: Zed Date: Tue, 31 Oct 2023 05:53:55 +0000 Subject: [PATCH 160/409] Fix multi-user timelines --- src/query.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/query.nim b/src/query.nim index d128f6f..06e1da2 100644 --- a/src/query.nim +++ b/src/query.nim @@ -60,7 +60,7 @@ proc genQueryParam*(query: Query): string = param &= "OR " if query.fromUser.len > 0 and query.kind in {posts, media}: - param &= "filter:self_threads OR-filter:replies " + param &= "filter:self_threads OR -filter:replies " if "nativeretweets" notin query.excludes: param &= "include:nativeretweets " From edad09f4c934fb44da008256994ba40347b0f9c9 Mon Sep 17 00:00:00 2001 From: Zed Date: Tue, 31 Oct 2023 08:31:51 +0000 Subject: [PATCH 161/409] Update nimcrypto and jsony --- nitter.nimble | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nitter.nimble b/nitter.nimble index e6a1909..20aab81 100644 --- a/nitter.nimble +++ b/nitter.nimble @@ -14,7 +14,7 @@ requires "nim >= 1.4.8" requires "jester#baca3f" requires "karax#5cf360c" requires "sass#7dfdd03" -requires "nimcrypto#4014ef9" +requires "nimcrypto#a079df9" requires "markdown#158efe3" requires "packedjson#9e6fbb6" requires "supersnappy#6c94198" @@ -22,7 +22,7 @@ requires "redpool#8b7c1db" requires "https://github.com/zedeus/redis#d0a0e6f" requires "zippy#ca5989a" requires "flatty#e668085" -requires "jsony#ea811be" +requires "jsony#1de1f08" requires "oauth#b8c163b" # Tasks From 089275826cae70c30183e1bd21b49538e43bd4d9 Mon Sep 17 00:00:00 2001 From: Zed Date: Tue, 31 Oct 2023 11:32:21 +0000 Subject: [PATCH 162/409] Bump minimum Nim version --- config.nims | 7 +------ nitter.nimble | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/config.nims b/config.nims index b7e52d0..4a7af27 100644 --- a/config.nims +++ b/config.nims @@ -7,12 +7,7 @@ # disable annoying warnings warning("GcUnsafe2", off) +warning("HoleEnumConv", off) hint("XDeclaredButNotUsed", off) hint("XCannotRaiseY", off) hint("User", off) - -const - nimVersion = (major: NimMajor, minor: NimMinor, patch: NimPatch) - -when nimVersion >= (1, 6, 0): - warning("HoleEnumConv", off) diff --git a/nitter.nimble b/nitter.nimble index 20aab81..37f9229 100644 --- a/nitter.nimble +++ b/nitter.nimble @@ -10,7 +10,7 @@ bin = @["nitter"] # Dependencies -requires "nim >= 1.4.8" +requires "nim >= 1.6.10" requires "jester#baca3f" requires "karax#5cf360c" requires "sass#7dfdd03" From 412055864940a79d3203aa41568c6ed67c5bb5f8 Mon Sep 17 00:00:00 2001 From: Zed Date: Tue, 31 Oct 2023 12:04:32 +0000 Subject: [PATCH 163/409] Replace /.tokens with /.health and /.accounts --- nitter.example.conf | 2 +- src/apiutils.nim | 4 +- src/{tokens.nim => auth.nim} | 113 +++++++++++++++-------- src/experimental/parser/guestaccount.nim | 3 +- src/nitter.nim | 2 +- src/routes/debug.nim | 9 +- src/types.nim | 2 +- 7 files changed, 86 insertions(+), 49 deletions(-) rename src/{tokens.nim => auth.nim} (69%) diff --git a/nitter.example.conf b/nitter.example.conf index 0d4deb7..f0b4214 100644 --- a/nitter.example.conf +++ b/nitter.example.conf @@ -23,7 +23,7 @@ redisMaxConnections = 30 hmacKey = "secretkey" # random key for cryptographic signing of video urls base64Media = false # use base64 encoding for proxied media urls enableRSS = true # set this to false to disable RSS feeds -enableDebug = false # enable request logs and debug endpoints (/.tokens) +enableDebug = false # enable request logs and debug endpoints (/.accounts) proxy = "" # http/https url, SOCKS proxies are not supported proxyAuth = "" tokenCount = 10 diff --git a/src/apiutils.nim b/src/apiutils.nim index 9ac101e..1ff05eb 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only import httpclient, asyncdispatch, options, strutils, uri, times, math, tables import jsony, packedjson, zippy, oauth1 -import types, tokens, consts, parserutils, http_pool +import types, auth, consts, parserutils, http_pool import experimental/types/common const @@ -120,7 +120,7 @@ template fetchImpl(result, fetchBody) {.dirty.} = except OSError as e: raise e except Exception as e: - let id = if account.isNil: "null" else: account.id + let id = if account.isNil: "null" else: $account.id echo "error: ", e.name, ", msg: ", e.msg, ", accountId: ", id, ", url: ", url raise rateLimitError() finally: diff --git a/src/tokens.nim b/src/auth.nim similarity index 69% rename from src/tokens.nim rename to src/auth.nim index ca74ddc..560fb84 100644 --- a/src/tokens.nim +++ b/src/auth.nim @@ -1,5 +1,5 @@ #SPDX-License-Identifier: AGPL-3.0-only -import asyncdispatch, times, json, random, strutils, tables, sets, os +import asyncdispatch, times, json, random, strutils, tables, intsets, os import types import experimental/parser/guestaccount @@ -7,6 +7,21 @@ import experimental/parser/guestaccount const maxConcurrentReqs = 2 dayInSeconds = 24 * 60 * 60 + apiMaxReqs: Table[Api, int] = { + Api.search: 50, + Api.tweetDetail: 150, + Api.photoRail: 180, + Api.userTweets: 500, + Api.userTweetsAndReplies: 500, + Api.userMedia: 500, + Api.userRestId: 500, + Api.userScreenName: 500, + Api.tweetResult: 500, + Api.list: 500, + Api.listTweets: 500, + Api.listMembers: 500, + Api.listBySlug: 500 + }.toTable var accountPool: seq[GuestAccount] @@ -15,20 +30,64 @@ var template log(str: varargs[string, `$`]) = if enableLogging: echo "[accounts] ", str.join("") -proc getPoolJson*(): JsonNode = - var - list = newJObject() - totalReqs = 0 - totalPending = 0 - limited: HashSet[string] - reqsPerApi: Table[string, int] - +proc getAccountPoolHealth*(): JsonNode = let now = epochTime().int - for account in accountPool: - totalPending.inc(account.pending) + var + totalReqs = 0 + limited: IntSet + reqsPerApi: Table[string, int] + oldest = now + newest = 0 + average = 0 - var includeAccount = false + for account in accountPool: + # Twitter snowflake conversion + let created = ((account.id shr 22) + 1288834974657) div 1000 + + if created > newest: + newest = created + if created < oldest: + oldest = created + average.inc created + + for api in account.apis.keys: + let + apiStatus = account.apis[api] + reqs = apiMaxReqs[api] - apiStatus.remaining + + reqsPerApi.mgetOrPut($api, 0).inc reqs + totalReqs.inc reqs + + if apiStatus.limited: + limited.incl account.id + + if accountPool.len > 0: + average = average div accountPool.len + else: + oldest = 0 + average = 0 + + return %*{ + "accounts": %*{ + "total": accountPool.len, + "active": accountPool.len - limited.card, + "limited": limited.card, + "oldest": $fromUnix(oldest), + "newest": $fromUnix(newest), + "average": $fromUnix(average) + }, + "requests": %*{ + "total": totalReqs, + "apis": reqsPerApi + } + } + +proc getAccountPoolDebug*(): JsonNode = + let now = epochTime().int + var list = newJObject() + + for account in accountPool: let accountJson = %*{ "apis": newJObject(), "pending": account.pending, @@ -47,37 +106,11 @@ proc getPoolJson*(): JsonNode = if apiStatus.limited: obj["limited"] = %true - limited.incl account.id accountJson{"apis", $api} = obj - includeAccount = true + list[$account.id] = accountJson - let - maxReqs = - case api - of Api.search: 50 - of Api.tweetDetail: 150 - of Api.photoRail: 180 - of Api.userTweets, Api.userTweetsAndReplies, Api.userMedia, - Api.userRestId, Api.userScreenName, - Api.tweetResult, - Api.list, Api.listTweets, Api.listMembers, Api.listBySlug: 500 - reqs = maxReqs - apiStatus.remaining - - reqsPerApi[$api] = reqsPerApi.getOrDefault($api, 0) + reqs - totalReqs.inc(reqs) - - if includeAccount: - list[account.id] = accountJson - - return %*{ - "amount": accountPool.len, - "limited": limited.card, - "requests": totalReqs, - "pending": totalPending, - "apis": reqsPerApi, - "accounts": list - } + return %list proc rateLimitError*(): ref RateLimitError = newException(RateLimitError, "rate limited") diff --git a/src/experimental/parser/guestaccount.nim b/src/experimental/parser/guestaccount.nim index 4d8ff47..f7e6d34 100644 --- a/src/experimental/parser/guestaccount.nim +++ b/src/experimental/parser/guestaccount.nim @@ -1,3 +1,4 @@ +import std/strutils import jsony import ../types/guestaccount from ../../types import GuestAccount @@ -5,7 +6,7 @@ from ../../types import GuestAccount proc toGuestAccount(account: RawAccount): GuestAccount = let id = account.oauthToken[0 ..< account.oauthToken.find('-')] result = GuestAccount( - id: id, + id: parseBiggestInt(id), oauthToken: account.oauthToken, oauthSecret: account.oauthTokenSecret ) diff --git a/src/nitter.nim b/src/nitter.nim index 1b4862b..dfc1dfd 100644 --- a/src/nitter.nim +++ b/src/nitter.nim @@ -6,7 +6,7 @@ from os import getEnv import jester -import types, config, prefs, formatters, redis_cache, http_pool, tokens +import types, config, prefs, formatters, redis_cache, http_pool, auth import views/[general, about] import routes/[ preferences, timeline, status, media, search, rss, list, debug, diff --git a/src/routes/debug.nim b/src/routes/debug.nim index 192786e..895a285 100644 --- a/src/routes/debug.nim +++ b/src/routes/debug.nim @@ -1,10 +1,13 @@ # SPDX-License-Identifier: AGPL-3.0-only import jester import router_utils -import ".."/[tokens, types] +import ".."/[auth, types] proc createDebugRouter*(cfg: Config) = router debug: - get "/.tokens": + get "/.health": + respJson getAccountPoolHealth() + + get "/.accounts": cond cfg.enableDebug - respJson getPoolJson() + respJson getAccountPoolDebug() diff --git a/src/types.nim b/src/types.nim index 3f5f8ac..3b0d55c 100644 --- a/src/types.nim +++ b/src/types.nim @@ -36,7 +36,7 @@ type limitedAt*: int GuestAccount* = ref object - id*: string + id*: BiggestInt oauthToken*: string oauthSecret*: string pending*: int From b62d73dbd373f08af07c7a79efcd790d3bc1a49c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milien=20=28perso=29?= <4016501+unixfox@users.noreply.github.com> Date: Tue, 31 Oct 2023 23:33:08 +0100 Subject: [PATCH 164/409] nim version min require + update dockerfile arm (#1053) --- Dockerfile.arm64 | 6 +++--- nitter.nimble | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 index 6cd6744..fbad812 100644 --- a/Dockerfile.arm64 +++ b/Dockerfile.arm64 @@ -1,7 +1,7 @@ -FROM alpine:3.17 as nim +FROM alpine:3.18 as nim LABEL maintainer="setenforce@protonmail.com" -RUN apk --no-cache add gcc git libc-dev libsass-dev "nim=1.6.8-r0" nimble pcre +RUN apk --no-cache add gcc git libc-dev libsass-dev "nim=1.6.14-r0" nimble pcre WORKDIR /src/nitter @@ -13,7 +13,7 @@ RUN nimble build -d:danger -d:lto -d:strip \ && nimble scss \ && nimble md -FROM alpine:3.17 +FROM alpine:3.18 WORKDIR /src/ RUN apk --no-cache add ca-certificates pcre openssl1.1-compat COPY --from=nim /src/nitter/nitter ./ diff --git a/nitter.nimble b/nitter.nimble index 7771b31..3a490a5 100644 --- a/nitter.nimble +++ b/nitter.nimble @@ -10,7 +10,7 @@ bin = @["nitter"] # Dependencies -requires "nim >= 1.4.8" +requires "nim >= 1.6.10" requires "jester#baca3f" requires "karax#5cf360c" requires "sass#7dfdd03" From b8103cf5010ea515c3b9722dbda19f374c6ebdaa Mon Sep 17 00:00:00 2001 From: Zed Date: Tue, 31 Oct 2023 23:02:45 +0000 Subject: [PATCH 165/409] Fix compilation on Nim 1.6.x --- src/auth.nim | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/auth.nim b/src/auth.nim index 560fb84..97ee301 100644 --- a/src/auth.nim +++ b/src/auth.nim @@ -1,5 +1,5 @@ #SPDX-License-Identifier: AGPL-3.0-only -import asyncdispatch, times, json, random, strutils, tables, intsets, os +import std/[asyncdispatch, times, json, random, strutils, tables, packedsets, os] import types import experimental/parser/guestaccount @@ -35,21 +35,21 @@ proc getAccountPoolHealth*(): JsonNode = var totalReqs = 0 - limited: IntSet + limited: PackedSet[BiggestInt] reqsPerApi: Table[string, int] - oldest = now - newest = 0 - average = 0 + oldest = now.int64 + newest = 0'i64 + average = 0'i64 for account in accountPool: # Twitter snowflake conversion - let created = ((account.id shr 22) + 1288834974657) div 1000 + let created = int64(((account.id shr 22) + 1288834974657) div 1000) if created > newest: newest = created if created < oldest: oldest = created - average.inc created + average += created for api in account.apis.keys: let From 60a82563da979f81c24eb51b7ae031f4086c03fd Mon Sep 17 00:00:00 2001 From: Zed Date: Tue, 31 Oct 2023 23:46:24 +0000 Subject: [PATCH 166/409] Run tests on multiple Nim versions --- .github/workflows/run-tests.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 37979cb..0af20a3 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -11,6 +11,12 @@ on: jobs: test: runs-on: ubuntu-latest + strategy: + matrix: + nim: + - "1.6.10" + - "1.6.x" + - "2.0.x" steps: - uses: actions/checkout@v3 with: @@ -28,7 +34,8 @@ jobs: cache: "pip" - uses: jiro4989/setup-nim-action@v1 with: - nim-version: "1.x" + nim-version: ${{ matrix.nim }} + repo-token: ${{ secrets.GITHUB_TOKEN }} - run: nimble build -d:release -Y - run: pip install seleniumbase - run: seleniumbase install chromedriver From b930a3d5bf4b0ce679b5086ae712b65f279f1a49 Mon Sep 17 00:00:00 2001 From: Zed Date: Tue, 31 Oct 2023 23:54:11 +0000 Subject: [PATCH 167/409] Fix guest accounts CI setup --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 0af20a3..3fbe64a 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -50,6 +50,6 @@ jobs: env: GUEST_ACCOUNTS: ${{ secrets.GUEST_ACCOUNTS }} run: | - echo $GUEST_ACCOUNTS > ./guest_accounts.json + echo $GUEST_ACCOUNTS > ./guest_accounts.jsonl ./nitter & pytest -n4 tests From 33bad37128abd3987e823d32e2ef90ea91f8e4f5 Mon Sep 17 00:00:00 2001 From: Zed Date: Wed, 1 Nov 2023 01:24:51 +0000 Subject: [PATCH 168/409] Fix guest accounts CI setup attempt 2 --- .github/workflows/run-tests.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 3fbe64a..12adb1f 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -44,12 +44,11 @@ jobs: run: | sudo apt install libsass-dev -y cp nitter.example.conf nitter.conf + sed -i 's/enableDebug = false/enableDebug = true/g' nimble md nimble scss + echo "${{ env.GUEST_ACCOUNTS }}" > ./guest_accounts.jsonl - name: Run tests - env: - GUEST_ACCOUNTS: ${{ secrets.GUEST_ACCOUNTS }} run: | - echo $GUEST_ACCOUNTS > ./guest_accounts.jsonl ./nitter & pytest -n4 tests From 006b91c90391e972b8e1377df18f7f1da5718c87 Mon Sep 17 00:00:00 2001 From: Zed Date: Wed, 1 Nov 2023 04:04:45 +0000 Subject: [PATCH 169/409] Prevent annoying warnings on devel --- src/parser.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/parser.nim b/src/parser.nim index cebf6f1..fcee13f 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -323,6 +323,8 @@ proc parseGraphTweet(js: JsonNode; isLegacy=false): Tweet = return Tweet(text: "You're unable to view this Tweet because it's only available to the Subscribers of the account owner.") of "TweetWithVisibilityResults": return parseGraphTweet(js{"tweet"}, isLegacy) + else: + discard if not js.hasKey("legacy"): return Tweet() From b0b335106d992acdca2d82da82fe9dee89044404 Mon Sep 17 00:00:00 2001 From: Zed Date: Wed, 1 Nov 2023 04:06:42 +0000 Subject: [PATCH 170/409] Fix missing CI file argument --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 12adb1f..f9df235 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -44,7 +44,7 @@ jobs: run: | sudo apt install libsass-dev -y cp nitter.example.conf nitter.conf - sed -i 's/enableDebug = false/enableDebug = true/g' + sed -i 's/enableDebug = false/enableDebug = true/g' nitter.conf nimble md nimble scss echo "${{ env.GUEST_ACCOUNTS }}" > ./guest_accounts.jsonl From 58e73a14c576b275dde9f9af8d4f31f0f6202255 Mon Sep 17 00:00:00 2001 From: Zed Date: Wed, 1 Nov 2023 04:13:22 +0000 Subject: [PATCH 171/409] Fix guest accounts CI setup attempt 3 --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index f9df235..948d284 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -47,7 +47,7 @@ jobs: sed -i 's/enableDebug = false/enableDebug = true/g' nitter.conf nimble md nimble scss - echo "${{ env.GUEST_ACCOUNTS }}" > ./guest_accounts.jsonl + echo "${{ vars.GUEST_ACCOUNTS }}" > ./guest_accounts.jsonl - name: Run tests run: | ./nitter & From 1d20bd01cb9db816e47b1911f9836c442f2726c9 Mon Sep 17 00:00:00 2001 From: Zed Date: Wed, 1 Nov 2023 04:16:26 +0000 Subject: [PATCH 172/409] Remove redundant "active" field from /.health --- src/auth.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/src/auth.nim b/src/auth.nim index 97ee301..8be435c 100644 --- a/src/auth.nim +++ b/src/auth.nim @@ -71,7 +71,6 @@ proc getAccountPoolHealth*(): JsonNode = return %*{ "accounts": %*{ "total": accountPool.len, - "active": accountPool.len - limited.card, "limited": limited.card, "oldest": $fromUnix(oldest), "newest": $fromUnix(newest), From 7b3fcdc622628febc306cf9c0d4a33c493d690db Mon Sep 17 00:00:00 2001 From: Zed Date: Wed, 1 Nov 2023 04:19:10 +0000 Subject: [PATCH 173/409] Fix guest accounts CI setup attempt 4 --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 948d284..76af329 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -47,7 +47,7 @@ jobs: sed -i 's/enableDebug = false/enableDebug = true/g' nitter.conf nimble md nimble scss - echo "${{ vars.GUEST_ACCOUNTS }}" > ./guest_accounts.jsonl + echo '${{ secrets.GUEST_ACCOUNTS }}' > ./guest_accounts.jsonl - name: Run tests run: | ./nitter & From 623424f5160b3e99e6e4b9675c8705f87fbf9d72 Mon Sep 17 00:00:00 2001 From: Zed Date: Wed, 1 Nov 2023 04:52:44 +0000 Subject: [PATCH 174/409] Fix outdated test --- tests/test_card.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_card.py b/tests/test_card.py index 733bd40..8da91a2 100644 --- a/tests/test_card.py +++ b/tests/test_card.py @@ -21,9 +21,9 @@ card = [ no_thumb = [ ['FluentAI/status/1116417904831029248', - 'Amazon’s Alexa isn’t just AI — thousands of humans are listening', - 'One of the only ways to improve Alexa is to have human beings check it for errors', - 'theverge.com'], + 'LinkedIn', + 'This link will take you to a page that’s not on LinkedIn', + 'lnkd.in'], ['Thom_Wolf/status/1122466524860702729', 'facebookresearch/fairseq', From e1838e093335fab02f36649bba7b65aa4420993f Mon Sep 17 00:00:00 2001 From: Zed Date: Wed, 1 Nov 2023 05:09:21 +0000 Subject: [PATCH 175/409] Move CI workflow to buildjet --- .github/workflows/run-tests.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 76af329..ee28e33 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -10,24 +10,26 @@ on: jobs: test: - runs-on: ubuntu-latest + runs-on: buildjet-2vcpu-ubuntu-2204 strategy: matrix: nim: - "1.6.10" - "1.6.x" - "2.0.x" + - "devel" steps: - uses: actions/checkout@v3 with: fetch-depth: 0 - name: Cache nimble id: cache-nimble - uses: actions/cache@v3 + uses: buildjet/cache@v3 with: path: ~/.nimble - key: nimble-${{ hashFiles('*.nimble') }} - restore-keys: "nimble-" + key: ${{ matrix.nim }}-nimble-${{ hashFiles('*.nimble') }} + restore-keys: | + ${{ matrix.nim }}-nimble- - uses: actions/setup-python@v4 with: python-version: "3.10" @@ -51,4 +53,4 @@ jobs: - name: Run tests run: | ./nitter & - pytest -n4 tests + pytest -n8 tests From 209f453b7998d0ac43950618d61f87efbf8c2b26 Mon Sep 17 00:00:00 2001 From: Zed Date: Wed, 1 Nov 2023 05:09:44 +0000 Subject: [PATCH 176/409] Purge expired accounts after parsing --- src/auth.nim | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/auth.nim b/src/auth.nim index 8be435c..8c901e2 100644 --- a/src/auth.nim +++ b/src/auth.nim @@ -1,5 +1,5 @@ #SPDX-License-Identifier: AGPL-3.0-only -import std/[asyncdispatch, times, json, random, strutils, tables, packedsets, os] +import std/[asyncdispatch, times, json, random, sequtils, strutils, tables, packedsets, os] import types import experimental/parser/guestaccount @@ -30,6 +30,16 @@ var template log(str: varargs[string, `$`]) = if enableLogging: echo "[accounts] ", str.join("") +proc snowflakeToEpoch(flake: int64): int64 = + int64(((flake shr 22) + 1288834974657) div 1000) + +proc hasExpired(account: GuestAccount): bool = + let + created = snowflakeToEpoch(account.id) + now = epochTime().int64 + daysOld = int(now - created) div (24 * 60 * 60) + return daysOld > 30 + proc getAccountPoolHealth*(): JsonNode = let now = epochTime().int @@ -42,9 +52,7 @@ proc getAccountPoolHealth*(): JsonNode = average = 0'i64 for account in accountPool: - # Twitter snowflake conversion - let created = int64(((account.id shr 22) + 1288834974657) div 1000) - + let created = snowflakeToEpoch(account.id) if created > newest: newest = created if created < oldest: @@ -188,3 +196,10 @@ proc initAccountPool*(cfg: Config; path: string) = else: echo "[accounts] ERROR: ", path, " not found. This file is required to authenticate API requests." quit 1 + + let accountsPrePurge = accountPool.len + accountPool.keepItIf(not it.hasExpired) + + log "Successfully added ", accountPool.len, " valid accounts." + if accountsPrePurge > accountPool.len: + log "Purged ", accountsPrePurge - accountPool.len, " expired accounts." From d17583286a11586c6ff5cffc43bc997e525a578e Mon Sep 17 00:00:00 2001 From: Zed Date: Wed, 1 Nov 2023 05:44:08 +0000 Subject: [PATCH 177/409] Don't requests made before reset --- src/auth.nim | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/auth.nim b/src/auth.nim index 8c901e2..fed7df3 100644 --- a/src/auth.nim +++ b/src/auth.nim @@ -64,12 +64,16 @@ proc getAccountPoolHealth*(): JsonNode = apiStatus = account.apis[api] reqs = apiMaxReqs[api] - apiStatus.remaining - reqsPerApi.mgetOrPut($api, 0).inc reqs - totalReqs.inc reqs - if apiStatus.limited: limited.incl account.id + # no requests made with this account and endpoint since the limit reset + if apiStatus.reset < now: + continue + + reqsPerApi.mgetOrPut($api, 0).inc reqs + totalReqs.inc reqs + if accountPool.len > 0: average = average div accountPool.len else: From e0d9dd0f9c8175fe9f5bf5aa86c0f56ccbc970a9 Mon Sep 17 00:00:00 2001 From: Zed Date: Sat, 4 Nov 2023 02:56:32 +0000 Subject: [PATCH 178/409] Fix #670 --- src/auth.nim | 4 ++-- src/sass/profile/card.scss | 2 +- src/types.nim | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/auth.nim b/src/auth.nim index fed7df3..b288c50 100644 --- a/src/auth.nim +++ b/src/auth.nim @@ -37,7 +37,7 @@ proc hasExpired(account: GuestAccount): bool = let created = snowflakeToEpoch(account.id) now = epochTime().int64 - daysOld = int(now - created) div (24 * 60 * 60) + daysOld = int(now - created) div dayInSeconds return daysOld > 30 proc getAccountPoolHealth*(): JsonNode = @@ -45,7 +45,7 @@ proc getAccountPoolHealth*(): JsonNode = var totalReqs = 0 - limited: PackedSet[BiggestInt] + limited: PackedSet[int64] reqsPerApi: Table[string, int] oldest = now.int64 newest = 0'i64 diff --git a/src/sass/profile/card.scss b/src/sass/profile/card.scss index 85878e4..46a9679 100644 --- a/src/sass/profile/card.scss +++ b/src/sass/profile/card.scss @@ -115,7 +115,7 @@ } .profile-card-tabs-name { - @include breakable; + flex-shrink: 100; } .profile-card-avatar { diff --git a/src/types.nim b/src/types.nim index 3b0d55c..9ddf283 100644 --- a/src/types.nim +++ b/src/types.nim @@ -36,7 +36,7 @@ type limitedAt*: int GuestAccount* = ref object - id*: BiggestInt + id*: int64 oauthToken*: string oauthSecret*: string pending*: int @@ -164,7 +164,7 @@ type newsletterPublication = "newsletter_publication" hidden unknown - + Card* = object kind*: CardKind url*: string From 5e188647fc5ddcc38084127f1db32f17f07fe727 Mon Sep 17 00:00:00 2001 From: Zed Date: Wed, 8 Nov 2023 14:53:35 +0000 Subject: [PATCH 179/409] Bump Nim in the ARM64 Dockerfile, add nitter user --- Dockerfile.arm64 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 index fbad812..c82be8a 100644 --- a/Dockerfile.arm64 +++ b/Dockerfile.arm64 @@ -1,7 +1,7 @@ FROM alpine:3.18 as nim LABEL maintainer="setenforce@protonmail.com" -RUN apk --no-cache add gcc git libc-dev libsass-dev "nim=1.6.14-r0" nimble pcre +RUN apk --no-cache add libsass-dev pcre gcc git libc-dev "nim=1.6.16-r0" "nimble=0.13.1-r3" WORKDIR /src/nitter @@ -15,9 +15,11 @@ RUN nimble build -d:danger -d:lto -d:strip \ FROM alpine:3.18 WORKDIR /src/ -RUN apk --no-cache add ca-certificates pcre openssl1.1-compat +RUN apk --no-cache add pcre ca-certificates openssl1.1-compat COPY --from=nim /src/nitter/nitter ./ COPY --from=nim /src/nitter/nitter.example.conf ./nitter.conf COPY --from=nim /src/nitter/public ./public EXPOSE 8080 +RUN adduser -h /src/ -D -s /bin/sh nitter +USER nitter CMD ./nitter From eaedd2aee7be6bc3dd2dceee09dc93052d0046f4 Mon Sep 17 00:00:00 2001 From: Zed Date: Wed, 8 Nov 2023 16:38:43 +0000 Subject: [PATCH 180/409] Fix ARM64 Dockerfile versions --- Dockerfile.arm64 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 index c82be8a..70024b2 100644 --- a/Dockerfile.arm64 +++ b/Dockerfile.arm64 @@ -1,7 +1,7 @@ FROM alpine:3.18 as nim LABEL maintainer="setenforce@protonmail.com" -RUN apk --no-cache add libsass-dev pcre gcc git libc-dev "nim=1.6.16-r0" "nimble=0.13.1-r3" +RUN apk --no-cache add libsass-dev pcre gcc git libc-dev "nim=1.6.14-r0" "nimble=0.13.1-r2" WORKDIR /src/nitter From c2819dab441b8ad8220b03dd0fe79f5a5d51b841 Mon Sep 17 00:00:00 2001 From: Zed Date: Wed, 15 Nov 2023 10:40:21 +0000 Subject: [PATCH 181/409] Fix #1106 Closes #831 --- src/parserutils.nim | 14 ++++++++++++-- tests/test_quote.py | 2 +- tests/test_tweet.py | 11 ++++++++++- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/parserutils.nim b/src/parserutils.nim index 7cf696e..6b8263f 100644 --- a/src/parserutils.nim +++ b/src/parserutils.nim @@ -1,9 +1,17 @@ # SPDX-License-Identifier: AGPL-3.0-only -import std/[strutils, times, macros, htmlgen, options, algorithm, re] +import std/[times, macros, htmlgen, options, algorithm, re] +import std/strutils except escape import std/unicode except strip +from xmltree import escape import packedjson import types, utils, formatters +const + unicodeOpen = "\uFFFA" + unicodeClose = "\uFFFB" + xmlOpen = escape("<") + xmlClose = escape(">") + let unRegex = re"(^|[^A-z0-9-_./?])@([A-z0-9_]{1,15})" unReplace = "$1@$2" @@ -304,7 +312,9 @@ proc expandTweetEntities*(tweet: Tweet; js: JsonNode) = proc expandNoteTweetEntities*(tweet: Tweet; js: JsonNode) = let entities = ? js{"entity_set"} - text = js{"text"}.getStr + text = js{"text"}.getStr.multiReplace(("<", unicodeOpen), (">", unicodeClose)) textSlice = 0..text.runeLen tweet.expandTextEntities(entities, text, textSlice) + + tweet.text = tweet.text.multiReplace((unicodeOpen, xmlOpen), (unicodeClose, xmlClose)) diff --git a/tests/test_quote.py b/tests/test_quote.py index 1b458ea..4921c21 100644 --- a/tests/test_quote.py +++ b/tests/test_quote.py @@ -9,7 +9,7 @@ text = [ What are we doing wrong? reuters.com/article/us-norwa…"""], ['nim_lang/status/1491461266849808397#m', - 'Nim language', '@nim_lang', + 'Nim', '@nim_lang', """What's better than Nim 1.6.0? Nim 1.6.2 :) diff --git a/tests/test_tweet.py b/tests/test_tweet.py index 7a3c4ed..ac89782 100644 --- a/tests/test_tweet.py +++ b/tests/test_tweet.py @@ -35,7 +35,16 @@ multiline = [ CALM AND CLICHÉ - ON"""] + ON"""], + [1718660434457239868, 'WebDesignMuseum', + """ +Happy 32nd Birthday HTML tags! + +On October 29, 1991, the internet pioneer, Tim Berners-Lee, published a document entitled HTML Tags. + +The document contained a description of the first 18 HTML tags: , <nextid>, <a>, <isindex>, <plaintext>, <listing>, <p>, <h1>…<h6>, <address>, <hp1>, <hp2>…, <dl>, <dt>, <dd>, <ul>, <li>,<menu> and <dir>. The design of the first version of HTML language was influenced by the SGML universal markup language. + +#WebDesignHistory"""] ] link = [ From 06ab1ea2e7341a239447e0ca7d1e9c6246b896c6 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Wed, 15 Nov 2023 11:11:56 +0000 Subject: [PATCH 182/409] Enable disabled tests --- tests/test_tweet.py | 58 +++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/tests/test_tweet.py b/tests/test_tweet.py index ac89782..839e6c5 100644 --- a/tests/test_tweet.py +++ b/tests/test_tweet.py @@ -1,4 +1,4 @@ -from base import BaseTestCase, Tweet, get_timeline_tweet +from base import BaseTestCase, Tweet, Conversation, get_timeline_tweet from parameterized import parameterized # image = tweet + 'div.attachments.media-body > div > div > a > div > img' @@ -83,22 +83,18 @@ retweet = [ [3, 'mobile_test_8', 'mobile test 8', 'jack', '@jack', 'twttr'] ] -# reply = [ -# ['mobile_test/with_replies', 15] -# ] - class TweetTest(BaseTestCase): - # @parameterized.expand(timeline) - # def test_timeline(self, index, fullname, username, date, tid, text): - # self.open_nitter(username) - # tweet = get_timeline_tweet(index) - # self.assert_exact_text(fullname, tweet.fullname) - # self.assert_exact_text('@' + username, tweet.username) - # self.assert_exact_text(date, tweet.date) - # self.assert_text(text, tweet.text) - # permalink = self.find_element(tweet.date + ' a') - # self.assertIn(tid, permalink.get_attribute('href')) + @parameterized.expand(timeline) + def test_timeline(self, index, fullname, username, date, tid, text): + self.open_nitter(username) + tweet = get_timeline_tweet(index) + self.assert_exact_text(fullname, tweet.fullname) + self.assert_exact_text('@' + username, tweet.username) + self.assert_exact_text(date, tweet.date) + self.assert_text(text, tweet.text) + permalink = self.find_element(tweet.date + ' a') + self.assertIn(tid, permalink.get_attribute('href')) @parameterized.expand(status) def test_status(self, tid, fullname, username, date, text): @@ -112,18 +108,18 @@ class TweetTest(BaseTestCase): @parameterized.expand(multiline) def test_multiline_formatting(self, tid, username, text): self.open_nitter(f'{username}/status/{tid}') - self.assert_text(text.strip('\n'), '.main-tweet') + self.assert_text(text.strip('\n'), Conversation.main) @parameterized.expand(emoji) def test_emoji(self, tweet, text): self.open_nitter(tweet) - self.assert_text(text, '.main-tweet') + self.assert_text(text, Conversation.main) @parameterized.expand(link) def test_link(self, tweet, links): self.open_nitter(tweet) for link in links: - self.assert_text(link, '.main-tweet') + self.assert_text(link, Conversation.main) @parameterized.expand(username) def test_username(self, tweet, usernames): @@ -132,22 +128,22 @@ class TweetTest(BaseTestCase): link = self.find_link_text(f'@{un}') self.assertIn(f'/{un}', link.get_property('href')) - # @parameterized.expand(retweet) - # def test_retweet(self, index, url, retweet_by, fullname, username, text): - # self.open_nitter(url) - # tweet = get_timeline_tweet(index) - # self.assert_text(f'{retweet_by} retweeted', tweet.retweet) - # self.assert_text(text, tweet.text) - # self.assert_exact_text(fullname, tweet.fullname) - # self.assert_exact_text(username, tweet.username) + @parameterized.expand(retweet) + def test_retweet(self, index, url, retweet_by, fullname, username, text): + self.open_nitter(url) + tweet = get_timeline_tweet(index) + self.assert_text(f'{retweet_by} retweeted', tweet.retweet) + self.assert_text(text, tweet.text) + self.assert_exact_text(fullname, tweet.fullname) + self.assert_exact_text(username, tweet.username) @parameterized.expand(invalid) def test_invalid_id(self, tweet): self.open_nitter(tweet) self.assert_text('Tweet not found', '.error-panel') - # @parameterized.expand(reply) - # def test_thread(self, tweet, num): - # self.open_nitter(tweet) - # thread = self.find_element(f'.timeline > div:nth-child({num})') - # self.assertIn(thread.get_attribute('class'), 'thread-line') + #@parameterized.expand(reply) + #def test_thread(self, tweet, num): + #self.open_nitter(tweet) + #thread = self.find_element(f'.timeline > div:nth-child({num})') + #self.assertIn(thread.get_attribute('class'), 'thread-line') From 4dac9f0798b885130b01f9a5a5535c729d351467 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Sat, 25 Nov 2023 05:31:15 +0000 Subject: [PATCH 183/409] Add simple job_details card support --- src/experimental/parser/graphql.nim | 4 ++-- src/experimental/parser/unifiedcard.nim | 11 +++++++++ src/experimental/parser/user.nim | 7 +++++- src/experimental/types/graphuser.nim | 4 ++-- src/experimental/types/timeline.nim | 4 ++-- src/experimental/types/unifiedcard.nim | 32 ++++++++++++++++++++----- src/parser.nim | 4 ++-- src/parserutils.nim | 9 +++---- src/types.nim | 1 + src/utils.nim | 5 ++-- 10 files changed, 60 insertions(+), 21 deletions(-) diff --git a/src/experimental/parser/graphql.nim b/src/experimental/parser/graphql.nim index b9da7c4..0e9a678 100644 --- a/src/experimental/parser/graphql.nim +++ b/src/experimental/parser/graphql.nim @@ -12,7 +12,7 @@ proc parseGraphUser*(json: string): User = if raw.data.userResult.result.unavailableReason.get("") == "Suspended": return User(suspended: true) - result = toUser raw.data.userResult.result.legacy + result = raw.data.userResult.result.legacy result.id = raw.data.userResult.result.restId result.verified = result.verified or raw.data.userResult.result.isBlueVerified @@ -30,7 +30,7 @@ proc parseGraphListMembers*(json, cursor: string): Result[User] = of TimelineTimelineItem: let userResult = entry.content.itemContent.userResults.result if userResult.restId.len > 0: - result.content.add toUser userResult.legacy + result.content.add userResult.legacy of TimelineTimelineCursor: if entry.content.cursorType == "Bottom": result.bottom = entry.content.value diff --git a/src/experimental/parser/unifiedcard.nim b/src/experimental/parser/unifiedcard.nim index c9af437..4a50e48 100644 --- a/src/experimental/parser/unifiedcard.nim +++ b/src/experimental/parser/unifiedcard.nim @@ -1,5 +1,6 @@ import std/[options, tables, strutils, strformat, sugar] import jsony +import user import ../types/unifiedcard from ../../types import Card, CardKind, Video from ../../utils import twimg, https @@ -27,6 +28,14 @@ proc parseMediaDetails(data: ComponentData; card: UnifiedCard; result: var Card) result.text = data.topicDetail.title result.dest = "Topic" +proc parseJobDetails(data: ComponentData; card: UnifiedCard; result: var Card) = + data.destination.parseDestination(card, result) + + result.kind = jobDetails + result.title = data.title + result.text = data.shortDescriptionText + result.dest = &"@{data.profileUser.username} · {data.location}" + proc parseAppDetails(data: ComponentData; card: UnifiedCard; result: var Card) = let app = card.appStoreData[data.appId][0] @@ -84,6 +93,8 @@ proc parseUnifiedCard*(json: string): Card = component.parseMedia(card, result) of buttonGroup: discard + of jobDetails: + component.data.parseJobDetails(card, result) of ComponentType.hidden: result.kind = CardKind.hidden of ComponentType.unknown: diff --git a/src/experimental/parser/user.nim b/src/experimental/parser/user.nim index 5962a87..78f596e 100644 --- a/src/experimental/parser/user.nim +++ b/src/experimental/parser/user.nim @@ -68,6 +68,11 @@ proc toUser*(raw: RawUser): User = result.expandUserEntities(raw) +proc parseHook*(s: string; i: var int; v: var User) = + var u: RawUser + parseHook(s, i, u) + v = toUser u + proc parseUser*(json: string; username=""): User = handleErrors: case error.code @@ -75,7 +80,7 @@ proc parseUser*(json: string; username=""): User = of userNotFound: return else: echo "[error - parseUser]: ", error - result = toUser json.fromJson(RawUser) + result = json.fromJson(User) proc parseUsers*(json: string; after=""): Result[User] = result = Result[User](beginning: after.len == 0) diff --git a/src/experimental/types/graphuser.nim b/src/experimental/types/graphuser.nim index c30eed9..08100f9 100644 --- a/src/experimental/types/graphuser.nim +++ b/src/experimental/types/graphuser.nim @@ -1,5 +1,5 @@ import options -import user +from ../../types import User type GraphUser* = object @@ -9,7 +9,7 @@ type result*: UserResult UserResult = object - legacy*: RawUser + legacy*: User restId*: string isBlueVerified*: bool unavailableReason*: Option[string] diff --git a/src/experimental/types/timeline.nim b/src/experimental/types/timeline.nim index 28239ad..5ce6d9f 100644 --- a/src/experimental/types/timeline.nim +++ b/src/experimental/types/timeline.nim @@ -1,5 +1,5 @@ import std/tables -import user +from ../../types import User type Search* = object @@ -7,7 +7,7 @@ type timeline*: Timeline GlobalObjects = object - users*: Table[string, RawUser] + users*: Table[string, User] Timeline = object instructions*: seq[Instructions] diff --git a/src/experimental/types/unifiedcard.nim b/src/experimental/types/unifiedcard.nim index 6e83cad..e540a64 100644 --- a/src/experimental/types/unifiedcard.nim +++ b/src/experimental/types/unifiedcard.nim @@ -1,7 +1,10 @@ -import options, tables -from ../../types import VideoType, VideoVariant +import std/[options, tables, times] +import jsony +from ../../types import VideoType, VideoVariant, User type + Text* = distinct string + UnifiedCard* = object componentObjects*: Table[string, Component] destinationObjects*: Table[string, Destination] @@ -13,6 +16,7 @@ type media swipeableMedia buttonGroup + jobDetails appStoreDetails twitterListDetails communityDetails @@ -29,12 +33,15 @@ type appId*: string mediaId*: string destination*: string + location*: string title*: Text subtitle*: Text name*: Text memberCount*: int mediaList*: seq[MediaItem] topicDetail*: tuple[title: Text] + profileUser*: User + shortDescriptionText*: string MediaItem* = object id*: string @@ -69,12 +76,9 @@ type title*: Text category*: Text - Text = object - content: string - TypeField = Component | Destination | MediaEntity | AppStoreData -converter fromText*(text: Text): string = text.content +converter fromText*(text: Text): string = string(text) proc renameHook*(v: var TypeField; fieldName: var string) = if fieldName == "type": @@ -86,6 +90,7 @@ proc enumHook*(s: string; v: var ComponentType) = of "media": media of "swipeable_media": swipeableMedia of "button_group": buttonGroup + of "job_details": jobDetails of "app_store_details": appStoreDetails of "twitter_list_details": twitterListDetails of "community_details": communityDetails @@ -106,3 +111,18 @@ proc enumHook*(s: string; v: var MediaType) = of "photo": photo of "model3d": model3d else: echo "ERROR: Unknown enum value (MediaType): ", s; photo + +proc parseHook*(s: string; i: var int; v: var DateTime) = + var str: string + parseHook(s, i, str) + v = parse(str, "yyyy-MM-dd hh:mm:ss") + +proc parseHook*(s: string; i: var int; v: var Text) = + if s[i] == '"': + var str: string + parseHook(s, i, str) + v = Text(str) + else: + var t: tuple[content: string] + parseHook(s, i, t) + v = Text(t.content) diff --git a/src/parser.nim b/src/parser.nim index fcee13f..d7ba613 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -219,8 +219,6 @@ proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet = ) ) - result.expandTweetEntities(js) - # fix for pinned threads if result.hasThread and result.threadId == 0: result.threadId = js{"self_thread", "id_str"}.getId @@ -254,6 +252,8 @@ proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet = else: result.card = some parseCard(jsCard, js{"entities", "urls"}) + result.expandTweetEntities(js) + with jsMedia, js{"extended_entities", "media"}: for m in jsMedia: case m{"type"}.getStr diff --git a/src/parserutils.nim b/src/parserutils.nim index 6b8263f..00ea6f4 100644 --- a/src/parserutils.nim +++ b/src/parserutils.nim @@ -246,7 +246,7 @@ proc expandUserEntities*(user: var User; js: JsonNode) = .replacef(htRegex, htReplace) proc expandTextEntities(tweet: Tweet; entities: JsonNode; text: string; textSlice: Slice[int]; - replyTo=""; hasQuote=false) = + replyTo=""; hasRedundantLink=false) = let hasCard = tweet.card.isSome var replacements = newSeq[ReplaceSlice]() @@ -257,7 +257,7 @@ proc expandTextEntities(tweet: Tweet; entities: JsonNode; text: string; textSlic if urlStr.len == 0 or urlStr notin text: continue - replacements.extractUrls(u, textSlice.b, hideTwitter = hasQuote) + replacements.extractUrls(u, textSlice.b, hideTwitter = hasRedundantLink) if hasCard and u{"url"}.getStr == get(tweet.card).url: get(tweet.card).url = u{"expanded_url"}.getStr @@ -297,9 +297,10 @@ proc expandTextEntities(tweet: Tweet; entities: JsonNode; text: string; textSlic proc expandTweetEntities*(tweet: Tweet; js: JsonNode) = let entities = ? js{"entities"} - hasQuote = js{"is_quote_status"}.getBool textRange = js{"display_text_range"} textSlice = textRange{0}.getInt .. textRange{1}.getInt + hasQuote = js{"is_quote_status"}.getBool + hasJobCard = tweet.card.isSome and get(tweet.card).kind == jobDetails var replyTo = "" if tweet.replyId != 0: @@ -307,7 +308,7 @@ proc expandTweetEntities*(tweet: Tweet; js: JsonNode) = replyTo = reply.getStr tweet.reply.add replyTo - tweet.expandTextEntities(entities, tweet.text, textSlice, replyTo, hasQuote) + tweet.expandTextEntities(entities, tweet.text, textSlice, replyTo, hasQuote or hasJobCard) proc expandNoteTweetEntities*(tweet: Tweet; js: JsonNode) = let diff --git a/src/types.nim b/src/types.nim index 9ddf283..bc791b1 100644 --- a/src/types.nim +++ b/src/types.nim @@ -162,6 +162,7 @@ type imageDirectMessage = "image_direct_message" audiospace = "audiospace" newsletterPublication = "newsletter_publication" + jobDetails = "job_details" hidden unknown diff --git a/src/utils.nim b/src/utils.nim index 9002bbf..c96a6dd 100644 --- a/src/utils.nim +++ b/src/utils.nim @@ -16,7 +16,8 @@ const "twimg.com", "abs.twimg.com", "pbs.twimg.com", - "video.twimg.com" + "video.twimg.com", + "x.com" ] proc setHmacKey*(key: string) = @@ -57,4 +58,4 @@ proc isTwitterUrl*(uri: Uri): bool = uri.hostname in twitterDomains proc isTwitterUrl*(url: string): bool = - parseUri(url).hostname in twitterDomains + isTwitterUrl(parseUri(url)) From d6be08d093bcde38286268b789395cf7a99e67ed Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Sat, 25 Nov 2023 05:53:13 +0000 Subject: [PATCH 184/409] Fix jobDetails error on old Nim versions --- src/experimental/parser/unifiedcard.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/experimental/parser/unifiedcard.nim b/src/experimental/parser/unifiedcard.nim index 4a50e48..1f7b825 100644 --- a/src/experimental/parser/unifiedcard.nim +++ b/src/experimental/parser/unifiedcard.nim @@ -31,7 +31,7 @@ proc parseMediaDetails(data: ComponentData; card: UnifiedCard; result: var Card) proc parseJobDetails(data: ComponentData; card: UnifiedCard; result: var Card) = data.destination.parseDestination(card, result) - result.kind = jobDetails + result.kind = CardKind.jobDetails result.title = data.title result.text = data.shortDescriptionText result.dest = &"@{data.profileUser.username} · {data.location}" @@ -93,7 +93,7 @@ proc parseUnifiedCard*(json: string): Card = component.parseMedia(card, result) of buttonGroup: discard - of jobDetails: + of ComponentType.jobDetails: component.data.parseJobDetails(card, result) of ComponentType.hidden: result.kind = CardKind.hidden From f8254c2f0f3bfacb754d7ad69be9b55258c8337c Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Sat, 25 Nov 2023 10:06:12 +0000 Subject: [PATCH 185/409] Add support for business and gov verification Also improve icon rendering on Firefox --- src/consts.nim | 10 ++++------ src/experimental/parser/graphql.nim | 5 +++-- src/experimental/parser/unifiedcard.nim | 3 +-- src/experimental/parser/user.nim | 2 +- src/experimental/types/user.nim | 4 ++-- src/parser.nim | 6 +++--- src/redis_cache.nim | 1 + src/sass/include/_variables.scss | 2 ++ src/sass/index.scss | 21 ++++++++++++++++++--- src/sass/search.scss | 2 ++ src/types.nim | 12 ++++++++---- src/views/general.nim | 2 +- src/views/renderutils.nim | 17 ++++++++++++----- src/views/tweet.nim | 3 +-- 14 files changed, 59 insertions(+), 31 deletions(-) diff --git a/src/consts.nim b/src/consts.nim index d3a3d80..e1c35e6 100644 --- a/src/consts.nim +++ b/src/consts.nim @@ -29,12 +29,10 @@ const "include_cards": "1", "include_entities": "1", "include_profile_interstitial_type": "0", - "include_quote_count": "1", - "include_reply_count": "1", - "include_user_entities": "1", - "include_ext_reply_count": "1", - "include_ext_is_blue_verified": "1", - # "include_ext_verified_type": "1", + "include_quote_count": "0", + "include_reply_count": "0", + "include_user_entities": "0", + "include_ext_reply_count": "0", "include_ext_media_color": "0", "cards_platform": "Web-13", "tweet_mode": "extended", diff --git a/src/experimental/parser/graphql.nim b/src/experimental/parser/graphql.nim index 0e9a678..c7f115f 100644 --- a/src/experimental/parser/graphql.nim +++ b/src/experimental/parser/graphql.nim @@ -1,7 +1,7 @@ import options import jsony import user, ../types/[graphuser, graphlistmembers] -from ../../types import User, Result, Query, QueryKind +from ../../types import User, VerifiedType, Result, Query, QueryKind proc parseGraphUser*(json: string): User = if json.len == 0 or json[0] != '{': @@ -14,7 +14,8 @@ proc parseGraphUser*(json: string): User = result = raw.data.userResult.result.legacy result.id = raw.data.userResult.result.restId - result.verified = result.verified or raw.data.userResult.result.isBlueVerified + if result.verifiedType == none and raw.data.userResult.result.isBlueVerified: + result.verifiedType = blue proc parseGraphListMembers*(json, cursor: string): Result[User] = result = Result[User]( diff --git a/src/experimental/parser/unifiedcard.nim b/src/experimental/parser/unifiedcard.nim index 1f7b825..a112974 100644 --- a/src/experimental/parser/unifiedcard.nim +++ b/src/experimental/parser/unifiedcard.nim @@ -1,7 +1,6 @@ import std/[options, tables, strutils, strformat, sugar] import jsony -import user -import ../types/unifiedcard +import user, ../types/unifiedcard from ../../types import Card, CardKind, Video from ../../utils import twimg, https diff --git a/src/experimental/parser/user.nim b/src/experimental/parser/user.nim index 78f596e..07e0477 100644 --- a/src/experimental/parser/user.nim +++ b/src/experimental/parser/user.nim @@ -56,7 +56,7 @@ proc toUser*(raw: RawUser): User = tweets: raw.statusesCount, likes: raw.favouritesCount, media: raw.mediaCount, - verified: raw.verified or raw.extIsBlueVerified, + verifiedType: raw.verifiedType, protected: raw.protected, joinDate: parseTwitterDate(raw.createdAt), banner: getBanner(raw), diff --git a/src/experimental/types/user.nim b/src/experimental/types/user.nim index 39331a0..7dc0194 100644 --- a/src/experimental/types/user.nim +++ b/src/experimental/types/user.nim @@ -1,5 +1,6 @@ import options import common +from ../../types import VerifiedType type RawUser* = object @@ -15,8 +16,7 @@ type favouritesCount*: int statusesCount*: int mediaCount*: int - verified*: bool - extIsBlueVerified*: bool + verifiedType*: VerifiedType protected*: bool profileLinkColor*: string profileBannerUrl*: string diff --git a/src/parser.nim b/src/parser.nim index d7ba613..a7bf89d 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -21,7 +21,7 @@ proc parseUser(js: JsonNode; id=""): User = tweets: js{"statuses_count"}.getInt, likes: js{"favourites_count"}.getInt, media: js{"media_count"}.getInt, - verified: js{"verified"}.getBool or js{"ext_is_blue_verified"}.getBool, + verifiedType: parseEnum[VerifiedType](js{"verified_type"}.getStr("None")), protected: js{"protected"}.getBool, joinDate: js{"created_at"}.getTime ) @@ -34,8 +34,8 @@ proc parseGraphUser(js: JsonNode): User = user = ? js{"user_results", "result"} result = parseUser(user{"legacy"}) - if "is_blue_verified" in user: - result.verified = user{"is_blue_verified"}.getBool() + if result.verifiedType == none and user{"is_blue_verified"}.getBool(false): + result.verifiedType = blue proc parseGraphList*(js: JsonNode): List = if js.isNull: return diff --git a/src/redis_cache.nim b/src/redis_cache.nim index a8b5ff8..1d77cca 100644 --- a/src/redis_cache.nim +++ b/src/redis_cache.nim @@ -52,6 +52,7 @@ proc initRedisPool*(cfg: Config) {.async.} = await migrate("profileDates", "p:*") await migrate("profileStats", "p:*") await migrate("userType", "p:*") + await migrate("verifiedType", "p:*") pool.withAcquire(r): # optimize memory usage for user ID buckets diff --git a/src/sass/include/_variables.scss b/src/sass/include/_variables.scss index 0f81235..0c95ff6 100644 --- a/src/sass/include/_variables.scss +++ b/src/sass/include/_variables.scss @@ -28,6 +28,8 @@ $more_replies_dots: #AD433B; $error_red: #420A05; $verified_blue: #1DA1F2; +$verified_business: #FAC82B; +$verified_government: #C1B6A4; $icon_text: $fg_color; $tab: $fg_color; diff --git a/src/sass/index.scss b/src/sass/index.scss index 9e2e347..6cab48e 100644 --- a/src/sass/index.scss +++ b/src/sass/index.scss @@ -39,6 +39,8 @@ body { --error_red: #{$error_red}; --verified_blue: #{$verified_blue}; + --verified_business: #{$verified_business}; + --verified_government: #{$verified_government}; --icon_text: #{$icon_text}; --tab: #{$fg_color}; @@ -141,17 +143,30 @@ ul { .verified-icon { color: var(--icon_text); - background-color: var(--verified_blue); border-radius: 50%; flex-shrink: 0; margin: 2px 0 3px 3px; - padding-top: 2px; - height: 12px; + padding-top: 3px; + height: 11px; width: 14px; font-size: 8px; display: inline-block; text-align: center; vertical-align: middle; + + &.blue { + background-color: var(--verified_blue); + } + + &.business { + color: var(--bg_panel); + background-color: var(--verified_business); + } + + &.government { + color: var(--bg_panel); + background-color: var(--verified_government); + } } @media(max-width: 600px) { diff --git a/src/sass/search.scss b/src/sass/search.scss index 0311fb0..f70f7ea 100644 --- a/src/sass/search.scss +++ b/src/sass/search.scss @@ -14,6 +14,8 @@ button { margin: 0 2px 0 0; height: 23px; + display: flex; + align-items: center; } .pref-input { diff --git a/src/types.nim b/src/types.nim index bc791b1..ddbebdf 100644 --- a/src/types.nim +++ b/src/types.nim @@ -10,9 +10,7 @@ type BadClientError* = object of CatchableError TimelineKind* {.pure.} = enum - tweets - replies - media + tweets, replies, media Api* {.pure.} = enum tweetDetail @@ -63,6 +61,12 @@ type tweetUnavailable = 421 tweetCensored = 422 + VerifiedType* = enum + none = "None" + blue = "Blue" + business = "Business" + government = "Government" + User* = object id*: string username*: string @@ -78,7 +82,7 @@ type tweets*: int likes*: int media*: int - verified*: bool + verifiedType*: VerifiedType protected*: bool suspended*: bool joinDate*: DateTime diff --git a/src/views/general.nim b/src/views/general.nim index 5e96d02..87d30f2 100644 --- a/src/views/general.nim +++ b/src/views/general.nim @@ -52,7 +52,7 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc=""; let opensearchUrl = getUrlPrefix(cfg) & "/opensearch" buildHtml(head): - link(rel="stylesheet", type="text/css", href="/css/style.css?v=18") + link(rel="stylesheet", type="text/css", href="/css/style.css?v=19") link(rel="stylesheet", type="text/css", href="/css/fontello.css?v=2") if theme.len > 0: diff --git a/src/views/renderutils.nim b/src/views/renderutils.nim index 9dffdcb..451ddfb 100644 --- a/src/views/renderutils.nim +++ b/src/views/renderutils.nim @@ -23,6 +23,13 @@ proc icon*(icon: string; text=""; title=""; class=""; href=""): VNode = if text.len > 0: text " " & text +template verifiedIcon*(user: User): untyped {.dirty.} = + if user.verifiedType != none: + let lower = ($user.verifiedType).toLowerAscii() + icon "ok", class=(&"verified-icon {lower}"), title=(&"Verified {lower} account") + else: + text "" + proc linkUser*(user: User, class=""): VNode = let isName = "username" notin class @@ -32,11 +39,11 @@ proc linkUser*(user: User, class=""): VNode = buildHtml(a(href=href, class=class, title=nameText)): text nameText - if isName and user.verified: - icon "ok", class="verified-icon", title="Verified account" - if isName and user.protected: - text " " - icon "lock", title="Protected account" + if isName: + verifiedIcon(user) + if user.protected: + text " " + icon "lock", title="Protected account" proc linkText*(text: string; class=""): VNode = let url = if "http" notin text: https & text else: text diff --git a/src/views/tweet.nim b/src/views/tweet.nim index f47ae9a..2fe4ac9 100644 --- a/src/views/tweet.nim +++ b/src/views/tweet.nim @@ -200,8 +200,7 @@ proc renderAttribution(user: User; prefs: Prefs): VNode = buildHtml(a(class="attribution", href=("/" & user.username))): renderMiniAvatar(user, prefs) strong: text user.fullname - if user.verified: - icon "ok", class="verified-icon", title="Verified account" + verifiedIcon(user) proc renderMediaTags(tags: seq[User]): VNode = buildHtml(tdiv(class="media-tag-block")): From a9740fec8b2d9d4469322cc66d3f3547e0b6ccdb Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Sat, 25 Nov 2023 10:11:57 +0000 Subject: [PATCH 186/409] Fix compilation with old Nim again --- src/experimental/parser/graphql.nim | 2 +- src/parser.nim | 2 +- src/views/renderutils.nim | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/experimental/parser/graphql.nim b/src/experimental/parser/graphql.nim index c7f115f..69837ab 100644 --- a/src/experimental/parser/graphql.nim +++ b/src/experimental/parser/graphql.nim @@ -14,7 +14,7 @@ proc parseGraphUser*(json: string): User = result = raw.data.userResult.result.legacy result.id = raw.data.userResult.result.restId - if result.verifiedType == none and raw.data.userResult.result.isBlueVerified: + if result.verifiedType == VerifiedType.none and raw.data.userResult.result.isBlueVerified: result.verifiedType = blue proc parseGraphListMembers*(json, cursor: string): Result[User] = diff --git a/src/parser.nim b/src/parser.nim index a7bf89d..f2547e4 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -34,7 +34,7 @@ proc parseGraphUser(js: JsonNode): User = user = ? js{"user_results", "result"} result = parseUser(user{"legacy"}) - if result.verifiedType == none and user{"is_blue_verified"}.getBool(false): + if result.verifiedType == VerifiedType.none and user{"is_blue_verified"}.getBool(false): result.verifiedType = blue proc parseGraphList*(js: JsonNode): List = diff --git a/src/views/renderutils.nim b/src/views/renderutils.nim index 451ddfb..f298fad 100644 --- a/src/views/renderutils.nim +++ b/src/views/renderutils.nim @@ -24,7 +24,7 @@ proc icon*(icon: string; text=""; title=""; class=""; href=""): VNode = text " " & text template verifiedIcon*(user: User): untyped {.dirty.} = - if user.verifiedType != none: + if user.verifiedType != VerifiedType.none: let lower = ($user.verifiedType).toLowerAscii() icon "ok", class=(&"verified-icon {lower}"), title=(&"Verified {lower} account") else: From 583c858cdf3486451ed6a0627640844f27009dbe Mon Sep 17 00:00:00 2001 From: blankie <blankie@nixnetmail.com> Date: Sun, 3 Dec 2023 08:54:24 +0000 Subject: [PATCH 187/409] Fix search queries in user search RSS feeds (#1126) Fixes #992 --- src/parser.nim | 2 +- src/routes/rss.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/parser.nim b/src/parser.nim index f2547e4..ec856a6 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -32,7 +32,7 @@ proc parseGraphUser(js: JsonNode): User = var user = js{"user_result", "result"} if user.isNull: user = ? js{"user_results", "result"} - result = parseUser(user{"legacy"}) + result = parseUser(user{"legacy"}, user{"rest_id"}.getStr) if result.verifiedType == VerifiedType.none and user{"is_blue_verified"}.getBool(false): result.verifiedType = blue diff --git a/src/routes/rss.nim b/src/routes/rss.nim index 6c77992..447f4ad 100644 --- a/src/routes/rss.nim +++ b/src/routes/rss.nim @@ -110,7 +110,7 @@ proc createRssRouter*(cfg: Config) = case tab of "with_replies": getReplyQuery(name) of "media": getMediaQuery(name) - # of "search": initQuery(params(request), name=name) + of "search": initQuery(params(request), name=name) else: Query(fromUser: @[name]) let searchKey = if tab != "search": "" From 52db03b73ad5f83f67c83ab197ae3b20a2523d39 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Fri, 12 Jan 2024 03:47:06 +0100 Subject: [PATCH 188/409] Fix broken video playback by forcing fmp4 --- src/utils.nim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/utils.nim b/src/utils.nim index c96a6dd..ede9aed 100644 --- a/src/utils.nim +++ b/src/utils.nim @@ -31,7 +31,9 @@ proc getHmac*(data: string): string = proc getVidUrl*(link: string): string = if link.len == 0: return - let sig = getHmac(link) + let + link = link.replace("cmaf", "fmp4") + sig = getHmac(link) if base64Media: &"/video/enc/{sig}/{encode(link, safe=true)}" else: From cdff5e9b1c1ff9d61d991f95c45d0588a0d90434 Mon Sep 17 00:00:00 2001 From: jackyzy823 <jackyzy823@gmail.com> Date: Thu, 22 Feb 2024 07:10:54 +0800 Subject: [PATCH 189/409] Fix for #1147, Proxy for audio URL and upgrade hls.js (#1178) * Revert "Fix broken video playback by forcing fmp4" This reverts commit 52db03b73ad5f83f67c83ab197ae3b20a2523d39. * Fix audio url in video m3u8 * Upgrade hls.js to 1.5.1 and use full version --- public/js/hls.light.min.js | 5 ----- public/js/hls.min.js | 5 +++++ src/formatters.nim | 2 ++ src/utils.nim | 4 +--- src/views/general.nim | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) delete mode 100644 public/js/hls.light.min.js create mode 100644 public/js/hls.min.js diff --git a/public/js/hls.light.min.js b/public/js/hls.light.min.js deleted file mode 100644 index 5a1fd1d..0000000 --- a/public/js/hls.light.min.js +++ /dev/null @@ -1,5 +0,0 @@ -// @license http://www.apache.org/licenses/LICENSE-2.0 Apache-2.0 -// @source https://github.com/video-dev/hls.js -// @version v1.2.9 -"undefined"!=typeof window&&function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Hls=t():e.Hls=t()}(this,(()=>(()=>{var e={21:(e,t,r)=>{"use strict";r.r(t),r.d(t,{default:()=>u});var i=function(){function e(e,t){this.subtle=void 0,this.aesIV=void 0,this.subtle=e,this.aesIV=t}return e.prototype.decrypt=function(e,t){return this.subtle.decrypt({name:"AES-CBC",iv:this.aesIV},t,e)},e}(),n=function(){function e(e,t){this.subtle=void 0,this.key=void 0,this.subtle=e,this.key=t}return e.prototype.expandKey=function(){return this.subtle.importKey("raw",this.key,{name:"AES-CBC"},!1,["encrypt","decrypt"])},e}(),a=r(145),s=function(){function e(){this.rcon=[0,1,2,4,8,16,32,64,128,27,54],this.subMix=[new Uint32Array(256),new Uint32Array(256),new Uint32Array(256),new Uint32Array(256)],this.invSubMix=[new Uint32Array(256),new Uint32Array(256),new Uint32Array(256),new Uint32Array(256)],this.sBox=new Uint32Array(256),this.invSBox=new Uint32Array(256),this.key=new Uint32Array(0),this.ksRows=0,this.keySize=0,this.keySchedule=void 0,this.invKeySchedule=void 0,this.initTable()}var t=e.prototype;return t.uint8ArrayToUint32Array_=function(e){for(var t=new DataView(e),r=new Uint32Array(4),i=0;i<4;i++)r[i]=t.getUint32(4*i);return r},t.initTable=function(){var e=this.sBox,t=this.invSBox,r=this.subMix,i=r[0],n=r[1],a=r[2],s=r[3],o=this.invSubMix,l=o[0],u=o[1],d=o[2],h=o[3],f=new Uint32Array(256),c=0,v=0,g=0;for(g=0;g<256;g++)f[g]=g<128?g<<1:g<<1^283;for(g=0;g<256;g++){var p=v^v<<1^v<<2^v<<3^v<<4;p=p>>>8^255&p^99,e[c]=p,t[p]=c;var m=f[c],y=f[m],E=f[y],T=257*f[p]^16843008*p;i[c]=T<<24|T>>>8,n[c]=T<<16|T>>>16,a[c]=T<<8|T>>>24,s[c]=T,T=16843009*E^65537*y^257*m^16843008*c,l[p]=T<<24|T>>>8,u[p]=T<<16|T>>>16,d[p]=T<<8|T>>>24,h[p]=T,c?(c=m^f[f[f[E^m]]],v^=f[f[v]]):c=v=1}},t.expandKey=function(e){for(var t=this.uint8ArrayToUint32Array_(e),r=!0,i=0;i<t.length&&r;)r=t[i]===this.key[i],i++;if(!r){this.key=t;var n=this.keySize=t.length;if(4!==n&&6!==n&&8!==n)throw new Error("Invalid aes key size="+n);var a,s,o,l,u=this.ksRows=4*(n+6+1),d=this.keySchedule=new Uint32Array(u),h=this.invKeySchedule=new Uint32Array(u),f=this.sBox,c=this.rcon,v=this.invSubMix,g=v[0],p=v[1],m=v[2],y=v[3];for(a=0;a<u;a++)a<n?o=d[a]=t[a]:(l=o,a%n==0?(l=f[(l=l<<8|l>>>24)>>>24]<<24|f[l>>>16&255]<<16|f[l>>>8&255]<<8|f[255&l],l^=c[a/n|0]<<24):n>6&&a%n==4&&(l=f[l>>>24]<<24|f[l>>>16&255]<<16|f[l>>>8&255]<<8|f[255&l]),d[a]=o=(d[a-n]^l)>>>0);for(s=0;s<u;s++)a=u-s,l=3&s?d[a]:d[a-4],h[s]=s<4||a<=4?l:g[f[l>>>24]]^p[f[l>>>16&255]]^m[f[l>>>8&255]]^y[f[255&l]],h[s]=h[s]>>>0}},t.networkToHostOrderSwap=function(e){return e<<24|(65280&e)<<8|(16711680&e)>>8|e>>>24},t.decrypt=function(e,t,r){for(var i,n,a,s,o,l,u,d,h,f,c,v,g,p,m=this.keySize+6,y=this.invKeySchedule,E=this.invSBox,T=this.invSubMix,S=T[0],b=T[1],L=T[2],A=T[3],D=this.uint8ArrayToUint32Array_(r),R=D[0],k=D[1],_=D[2],x=D[3],I=new Int32Array(e),w=new Int32Array(I.length),C=this.networkToHostOrderSwap;t<I.length;){for(h=C(I[t]),f=C(I[t+1]),c=C(I[t+2]),v=C(I[t+3]),o=h^y[0],l=v^y[1],u=c^y[2],d=f^y[3],g=4,p=1;p<m;p++)i=S[o>>>24]^b[l>>16&255]^L[u>>8&255]^A[255&d]^y[g],n=S[l>>>24]^b[u>>16&255]^L[d>>8&255]^A[255&o]^y[g+1],a=S[u>>>24]^b[d>>16&255]^L[o>>8&255]^A[255&l]^y[g+2],s=S[d>>>24]^b[o>>16&255]^L[l>>8&255]^A[255&u]^y[g+3],o=i,l=n,u=a,d=s,g+=4;i=E[o>>>24]<<24^E[l>>16&255]<<16^E[u>>8&255]<<8^E[255&d]^y[g],n=E[l>>>24]<<24^E[u>>16&255]<<16^E[d>>8&255]<<8^E[255&o]^y[g+1],a=E[u>>>24]<<24^E[d>>16&255]<<16^E[o>>8&255]<<8^E[255&l]^y[g+2],s=E[d>>>24]<<24^E[o>>16&255]<<16^E[l>>8&255]<<8^E[255&u]^y[g+3],w[t]=C(i^R),w[t+1]=C(s^k),w[t+2]=C(a^_),w[t+3]=C(n^x),R=h,k=f,_=c,x=v,t+=4}return w.buffer},e}(),o=r(93),l=r(63),u=function(){function e(e,t,r){var i=(void 0===r?{}:r).removePKCS7Padding,n=void 0===i||i;if(this.logEnabled=!0,this.observer=void 0,this.config=void 0,this.removePKCS7Padding=void 0,this.subtle=null,this.softwareDecrypter=null,this.key=null,this.fastAesKey=null,this.remainderData=null,this.currentIV=null,this.currentResult=null,this.observer=e,this.config=t,this.removePKCS7Padding=n,n)try{var a=self.crypto;a&&(this.subtle=a.subtle||a.webkitSubtle)}catch(e){}null===this.subtle&&(this.config.enableSoftwareAES=!0)}var t=e.prototype;return t.destroy=function(){this.observer=null},t.isSync=function(){return this.config.enableSoftwareAES},t.flush=function(){var e=this.currentResult;if(e){var t,r,i,n=new Uint8Array(e);return this.reset(),this.removePKCS7Padding?(i=(r=(t=n).byteLength)&&new DataView(t.buffer).getUint8(r-1))?(0,a.sliceUint8)(t,0,r-i):t:n}this.reset()},t.reset=function(){this.currentResult=null,this.currentIV=null,this.remainderData=null,this.softwareDecrypter&&(this.softwareDecrypter=null)},t.decrypt=function(e,t,r,i){if(this.config.enableSoftwareAES){this.softwareDecrypt(new Uint8Array(e),t,r);var n=this.flush();n&&i(n.buffer)}else this.webCryptoDecrypt(new Uint8Array(e),t,r).then(i)},t.softwareDecrypt=function(e,t,r){var i=this.currentIV,n=this.currentResult,o=this.remainderData;this.logOnce("JS AES decrypt"),o&&(e=(0,l.appendUint8Array)(o,e),this.remainderData=null);var u=this.getValidChunk(e);if(!u.length)return null;i&&(r=i);var d=this.softwareDecrypter;d||(d=this.softwareDecrypter=new s),d.expandKey(t);var h=n;return this.currentResult=d.decrypt(u.buffer,0,r),this.currentIV=(0,a.sliceUint8)(u,-16).buffer,h||null},t.webCryptoDecrypt=function(e,t,r){var a=this,s=this.subtle;return this.key===t&&this.fastAesKey||(this.key=t,this.fastAesKey=new n(s,t)),this.fastAesKey.expandKey().then((function(t){return s?new i(s,r).decrypt(e.buffer,t):Promise.reject(new Error("web crypto not initialized"))})).catch((function(i){return a.onWebCryptoError(i,e,t,r)}))},t.onWebCryptoError=function(e,t,r,i){return o.logger.warn("[decrypter.ts]: WebCrypto Error, disable WebCrypto API:",e),this.config.enableSoftwareAES=!0,this.logEnabled=!0,this.softwareDecrypt(t,r,i)},t.getValidChunk=function(e){var t=e,r=e.length-e.length%16;return r!==e.length&&(t=(0,a.sliceUint8)(e,0,r),this.remainderData=(0,a.sliceUint8)(e,r)),t},t.logOnce=function(e){this.logEnabled&&(o.logger.log("[decrypter.ts]: "+e),this.logEnabled=!1)},e}()},181:(e,t,r)=>{"use strict";r.r(t),r.d(t,{canParse:()=>l,decodeFrame:()=>c,getID3Data:()=>s,getID3Frames:()=>f,getTimeStamp:()=>u,isFooter:()=>a,isHeader:()=>n,isTimeStampFrame:()=>d,testables:()=>E,utf8ArrayToStr:()=>y});var i,n=function(e,t){return t+10<=e.length&&73===e[t]&&68===e[t+1]&&51===e[t+2]&&e[t+3]<255&&e[t+4]<255&&e[t+6]<128&&e[t+7]<128&&e[t+8]<128&&e[t+9]<128},a=function(e,t){return t+10<=e.length&&51===e[t]&&68===e[t+1]&&73===e[t+2]&&e[t+3]<255&&e[t+4]<255&&e[t+6]<128&&e[t+7]<128&&e[t+8]<128&&e[t+9]<128},s=function(e,t){for(var r=t,i=0;n(e,t);)i+=10,i+=o(e,t+6),a(e,t+10)&&(i+=10),t+=i;if(i>0)return e.subarray(r,r+i)},o=function(e,t){var r=0;return r=(127&e[t])<<21,r|=(127&e[t+1])<<14,(r|=(127&e[t+2])<<7)|127&e[t+3]},l=function(e,t){return n(e,t)&&o(e,t+6)+10<=e.length-t},u=function(e){for(var t=f(e),r=0;r<t.length;r++){var i=t[r];if(d(i))return m(i)}},d=function(e){return e&&"PRIV"===e.key&&"com.apple.streaming.transportStreamTimestamp"===e.info},h=function(e){var t=String.fromCharCode(e[0],e[1],e[2],e[3]),r=o(e,4);return{type:t,size:r,data:e.subarray(10,10+r)}},f=function(e){for(var t=0,r=[];n(e,t);){for(var i=o(e,t+6),s=(t+=10)+i;t+8<s;){var l=h(e.subarray(t)),u=c(l);u&&r.push(u),t+=l.size+10}a(e,t)&&(t+=10)}return r},c=function(e){return"PRIV"===e.type?v(e):"W"===e.type[0]?p(e):g(e)},v=function(e){if(!(e.size<2)){var t=y(e.data,!0),r=new Uint8Array(e.data.subarray(t.length+1));return{key:e.type,info:t,data:r.buffer}}},g=function(e){if(!(e.size<2)){if("TXXX"===e.type){var t=1,r=y(e.data.subarray(t),!0);t+=r.length+1;var i=y(e.data.subarray(t));return{key:e.type,info:r,data:i}}var n=y(e.data.subarray(1));return{key:e.type,data:n}}},p=function(e){if("WXXX"===e.type){if(e.size<2)return;var t=1,r=y(e.data.subarray(t),!0);t+=r.length+1;var i=y(e.data.subarray(t));return{key:e.type,info:r,data:i}}var n=y(e.data);return{key:e.type,data:n}},m=function(e){if(8===e.data.byteLength){var t=new Uint8Array(e.data),r=1&t[3],i=(t[4]<<23)+(t[5]<<15)+(t[6]<<7)+t[7];return i/=45,r&&(i+=47721858.84),Math.round(i)}},y=function(e,t){void 0===t&&(t=!1);var r=T();if(r){var i=r.decode(e);if(t){var n=i.indexOf("\0");return-1!==n?i.substring(0,n):i}return i.replace(/\0/g,"")}for(var a,s,o,l=e.length,u="",d=0;d<l;){if(0===(a=e[d++])&&t)return u;if(0!==a&&3!==a)switch(a>>4){case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:u+=String.fromCharCode(a);break;case 12:case 13:s=e[d++],u+=String.fromCharCode((31&a)<<6|63&s);break;case 14:s=e[d++],o=e[d++],u+=String.fromCharCode((15&a)<<12|(63&s)<<6|(63&o)<<0)}}return u},E={decodeTextFrame:g};function T(){return i||void 0===self.TextDecoder||(i=new self.TextDecoder("utf-8")),i}},182:(e,t,r)=>{"use strict";r.r(t),r.d(t,{default:()=>o});var i=r(764),n=r(851),a=r(93),s=r(729);function o(e){var t=new s.EventEmitter,r=function(t,r){e.postMessage({event:t,data:r})};t.on(n.Events.FRAG_DECRYPTED,r),t.on(n.Events.ERROR,r),e.addEventListener("message",(function(n){var s=n.data;switch(s.cmd){case"init":var o=JSON.parse(s.config);e.transmuxer=new i.default(t,s.typeSupported,o,s.vendor,s.id),(0,a.enableLogs)(o.debug,s.id),function(){var e=function(e){a.logger[e]=function(t){r("workerLog",{logType:e,message:t})}};for(var t in a.logger)e(t)}(),r("init",null);break;case"configure":e.transmuxer.configure(s.config);break;case"demux":var u=e.transmuxer.push(s.data,s.decryptdata,s.chunkMeta,s.state);(0,i.isPromise)(u)?u.then((function(t){l(e,t)})):l(e,u);break;case"flush":var h=s.chunkMeta,f=e.transmuxer.flush(h);(0,i.isPromise)(f)?f.then((function(t){d(e,t,h)})):d(e,f,h)}}))}function l(e,t){if(!((r=t.remuxResult).audio||r.video||r.text||r.id3||r.initSegment))return!1;var r,i=[],n=t.remuxResult,a=n.audio,s=n.video;return a&&u(i,a),s&&u(i,s),e.postMessage({event:"transmuxComplete",data:t},i),!0}function u(e,t){t.data1&&e.push(t.data1.buffer),t.data2&&e.push(t.data2.buffer)}function d(e,t,r){t.reduce((function(t,r){return l(e,r)||t}),!1)||e.postMessage({event:"transmuxComplete",data:t[0]}),e.postMessage({event:"flush",data:r})}},764:(e,t,r)=>{"use strict";r.r(t),r.d(t,{TransmuxConfig:()=>Ee,TransmuxState:()=>Te,default:()=>pe,isPromise:()=>ye});var i=r(851),n=r(973),a=r(21),s=r(965),o=r(181),l=r(856);function u(e,t){return void 0===e&&(e=""),void 0===t&&(t=9e4),{type:e,id:-1,pid:-1,inputTimeScale:t,sequenceNumber:-1,samples:[],dropped:0}}var d=r(63),h=r(145),f=function(){function e(){this._audioTrack=void 0,this._id3Track=void 0,this.frameIndex=0,this.cachedData=null,this.basePTS=null,this.initPTS=null,this.lastPTS=null}var t=e.prototype;return t.resetInitSegment=function(e,t,r,i){this._id3Track={type:"id3",id:3,pid:-1,inputTimeScale:9e4,sequenceNumber:0,samples:[],dropped:0}},t.resetTimeStamp=function(e){this.initPTS=e,this.resetContiguity()},t.resetContiguity=function(){this.basePTS=null,this.lastPTS=null,this.frameIndex=0},t.canParse=function(e,t){return!1},t.appendFrame=function(e,t,r){},t.demux=function(e,t){this.cachedData&&(e=(0,d.appendUint8Array)(this.cachedData,e),this.cachedData=null);var r,i=o.getID3Data(e,0),n=i?i.length:0,a=this._audioTrack,f=this._id3Track,v=i?o.getTimeStamp(i):void 0,g=e.length;for((null===this.basePTS||0===this.frameIndex&&(0,s.isFiniteNumber)(v))&&(this.basePTS=c(v,t,this.initPTS),this.lastPTS=this.basePTS),null===this.lastPTS&&(this.lastPTS=this.basePTS),i&&i.length>0&&f.samples.push({pts:this.lastPTS,dts:this.lastPTS,data:i,type:l.MetadataSchema.audioId3,duration:Number.POSITIVE_INFINITY});n<g;){if(this.canParse(e,n)){var p=this.appendFrame(a,e,n);p?(this.frameIndex++,this.lastPTS=p.sample.pts,r=n+=p.length):n=g}else o.canParse(e,n)?(i=o.getID3Data(e,n),f.samples.push({pts:this.lastPTS,dts:this.lastPTS,data:i,type:l.MetadataSchema.audioId3,duration:Number.POSITIVE_INFINITY}),r=n+=i.length):n++;if(n===g&&r!==g){var m=(0,h.sliceUint8)(e,r);this.cachedData?this.cachedData=(0,d.appendUint8Array)(this.cachedData,m):this.cachedData=m}}return{audioTrack:a,videoTrack:u(),id3Track:f,textTrack:u()}},t.demuxSampleAes=function(e,t,r){return Promise.reject(new Error("["+this+"] This demuxer does not support Sample-AES decryption"))},t.flush=function(e){var t=this.cachedData;return t&&(this.cachedData=null,this.demux(t,0)),{audioTrack:this._audioTrack,videoTrack:u(),id3Track:this._id3Track,textTrack:u()}},t.destroy=function(){},e}(),c=function(e,t,r){return(0,s.isFiniteNumber)(e)?90*e:9e4*t+(r||0)};const v=f;var g=r(93);function p(e,t){return 255===e[t]&&240==(246&e[t+1])}function m(e,t){return 1&e[t+1]?7:9}function y(e,t){return(3&e[t+3])<<11|e[t+4]<<3|(224&e[t+5])>>>5}function E(e,t){return t+1<e.length&&p(e,t)}function T(e,t){if(E(e,t)){var r=m(e,t);if(t+r>=e.length)return!1;var i=y(e,t);if(i<=r)return!1;var n=t+i;return n===e.length||E(e,n)}return!1}function S(e,t,r,a,s){if(!e.samplerate){var o=function(e,t,r,a){var s,o,l,u,d=navigator.userAgent.toLowerCase(),h=a,f=[96e3,88200,64e3,48e3,44100,32e3,24e3,22050,16e3,12e3,11025,8e3,7350];s=1+((192&t[r+2])>>>6);var c=(60&t[r+2])>>>2;if(!(c>f.length-1))return l=(1&t[r+2])<<2,l|=(192&t[r+3])>>>6,g.logger.log("manifest codec:"+a+", ADTS type:"+s+", samplingIndex:"+c),/firefox/i.test(d)?c>=6?(s=5,u=new Array(4),o=c-3):(s=2,u=new Array(2),o=c):-1!==d.indexOf("android")?(s=2,u=new Array(2),o=c):(s=5,u=new Array(4),a&&(-1!==a.indexOf("mp4a.40.29")||-1!==a.indexOf("mp4a.40.5"))||!a&&c>=6?o=c-3:((a&&-1!==a.indexOf("mp4a.40.2")&&(c>=6&&1===l||/vivaldi/i.test(d))||!a&&1===l)&&(s=2,u=new Array(2)),o=c)),u[0]=s<<3,u[0]|=(14&c)>>1,u[1]|=(1&c)<<7,u[1]|=l<<3,5===s&&(u[1]|=(14&o)>>1,u[2]=(1&o)<<7,u[2]|=8,u[3]=0),{config:u,samplerate:f[c],channelCount:l,codec:"mp4a.40."+s,manifestCodec:h};e.trigger(i.Events.ERROR,{type:n.ErrorTypes.MEDIA_ERROR,details:n.ErrorDetails.FRAG_PARSING_ERROR,fatal:!0,reason:"invalid ADTS sampling index:"+c})}(t,r,a,s);if(!o)return;e.config=o.config,e.samplerate=o.samplerate,e.channelCount=o.channelCount,e.codec=o.codec,e.manifestCodec=o.manifestCodec,g.logger.log("parsed codec:"+e.codec+", rate:"+o.samplerate+", channels:"+o.channelCount)}}function b(e){return 9216e4/e}function L(e,t,r,i,n){var a,s=i+n*b(e.samplerate),o=function(e,t){var r=m(e,t);if(t+r<=e.length){var i=y(e,t)-r;if(i>0)return{headerLength:r,frameLength:i}}}(t,r);if(o){var l=o.frameLength,u=o.headerLength,d=u+l,h=Math.max(0,r+d-t.length);h?(a=new Uint8Array(d-u)).set(t.subarray(r+u,t.length),0):a=t.subarray(r+u,r+d);var f={unit:a,pts:s};return h||e.samples.push(f),{sample:f,length:d,missing:h}}var c=t.length-r;return(a=new Uint8Array(c)).set(t.subarray(r,t.length),0),{sample:{unit:a,pts:s},length:c,missing:-1}}function A(e,t){return A=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},A(e,t)}const D=function(e){var t,r;function i(t,r){var i;return(i=e.call(this)||this).observer=void 0,i.config=void 0,i.observer=t,i.config=r,i}r=e,(t=i).prototype=Object.create(r.prototype),t.prototype.constructor=t,A(t,r);var n=i.prototype;return n.resetInitSegment=function(t,r,i,n){e.prototype.resetInitSegment.call(this,t,r,i,n),this._audioTrack={container:"audio/adts",type:"audio",id:2,pid:-1,sequenceNumber:0,segmentCodec:"aac",samples:[],manifestCodec:r,duration:n,inputTimeScale:9e4,dropped:0}},i.probe=function(e){if(!e)return!1;for(var t=(o.getID3Data(e,0)||[]).length,r=e.length;t<r;t++)if(T(e,t))return g.logger.log("ADTS sync word found !"),!0;return!1},n.canParse=function(e,t){return function(e,t){return function(e,t){return t+5<e.length}(e,t)&&p(e,t)&&y(e,t)<=e.length-t}(e,t)},n.appendFrame=function(e,t,r){S(e,this.observer,t,r,e.manifestCodec);var i=L(e,t,r,this.basePTS,this.frameIndex);if(i&&0===i.missing)return i},i}(v);var R=/\/emsg[-/]ID3/i;const k=function(){function e(e,t){this.remainderData=null,this.timeOffset=0,this.config=void 0,this.videoTrack=void 0,this.audioTrack=void 0,this.id3Track=void 0,this.txtTrack=void 0,this.config=t}var t=e.prototype;return t.resetTimeStamp=function(){},t.resetInitSegment=function(e,t,r,i){var n=(0,d.parseInitSegment)(e),a=this.videoTrack=u("video",1),s=this.audioTrack=u("audio",1),o=this.txtTrack=u("text",1);if(this.id3Track=u("id3",1),this.timeOffset=0,n.video){var l=n.video,h=l.id,f=l.timescale,c=l.codec;a.id=h,a.timescale=o.timescale=f,a.codec=c}if(n.audio){var v=n.audio,g=v.id,p=v.timescale,m=v.codec;s.id=g,s.timescale=p,s.codec=m}o.id=d.RemuxerTrackIdConfig.text,a.sampleDuration=0,a.duration=s.duration=i},t.resetContiguity=function(){},e.probe=function(e){return e=e.length>16384?e.subarray(0,16384):e,(0,d.findBox)(e,["moof"]).length>0},t.demux=function(e,t){this.timeOffset=t;var r=e,i=this.videoTrack,n=this.txtTrack;if(this.config.progressive){this.remainderData&&(r=(0,d.appendUint8Array)(this.remainderData,e));var a=(0,d.segmentValidRange)(r);this.remainderData=a.remainder,i.samples=a.valid||new Uint8Array}else i.samples=r;var s=this.extractID3Track(i,t);return n.samples=(0,d.parseSamples)(t,i),{videoTrack:i,audioTrack:this.audioTrack,id3Track:s,textTrack:this.txtTrack}},t.flush=function(){var e=this.timeOffset,t=this.videoTrack,r=this.txtTrack;t.samples=this.remainderData||new Uint8Array,this.remainderData=null;var i=this.extractID3Track(t,this.timeOffset);return r.samples=(0,d.parseSamples)(e,t),{videoTrack:t,audioTrack:u(),id3Track:i,textTrack:u()}},t.extractID3Track=function(e,t){var r=this.id3Track;if(e.samples.length){var i=(0,d.findBox)(e.samples,["emsg"]);i&&i.forEach((function(e){var i=(0,d.parseEmsg)(e);if(R.test(i.schemeIdUri)){var n=(0,s.isFiniteNumber)(i.presentationTime)?i.presentationTime/i.timeScale:t+i.presentationTimeDelta/i.timeScale,a=4294967295===i.eventDuration?Number.POSITIVE_INFINITY:i.eventDuration/i.timeScale;a<=.001&&(a=Number.POSITIVE_INFINITY);var o=i.payload;r.samples.push({data:o,len:o.byteLength,dts:n,pts:n,type:l.MetadataSchema.emsg,duration:a})}}))}return r},t.demuxSampleAes=function(e,t,r){return Promise.reject(new Error("The MP4 demuxer does not support SAMPLE-AES decryption"))},t.destroy=function(){},e}();var _=null,x=[32,64,96,128,160,192,224,256,288,320,352,384,416,448,32,48,56,64,80,96,112,128,160,192,224,256,320,384,32,40,48,56,64,80,96,112,128,160,192,224,256,320,32,48,56,64,80,96,112,128,144,160,176,192,224,256,8,16,24,32,40,48,56,64,80,96,112,128,144,160],I=[44100,48e3,32e3,22050,24e3,16e3,11025,12e3,8e3],w=[[0,72,144,12],[0,0,0,0],[0,72,144,12],[0,144,144,12]],C=[0,1,1,4];function P(e,t,r,i,n){if(!(r+24>t.length)){var a=O(t,r);if(a&&r+a.frameLength<=t.length){var s=i+n*(9e4*a.samplesPerFrame/a.sampleRate),o={unit:t.subarray(r,r+a.frameLength),pts:s,dts:s};return e.config=[],e.channelCount=a.channelCount,e.samplerate=a.sampleRate,e.samples.push(o),{sample:o,length:a.frameLength,missing:0}}}}function O(e,t){var r=e[t+1]>>3&3,i=e[t+1]>>1&3,n=e[t+2]>>4&15,a=e[t+2]>>2&3;if(1!==r&&0!==n&&15!==n&&3!==a){var s=e[t+2]>>1&1,o=e[t+3]>>6,l=1e3*x[14*(3===r?3-i:3===i?3:4)+n-1],u=I[3*(3===r?0:2===r?1:2)+a],d=3===o?1:2,h=w[r][i],f=C[i],c=8*h*f,v=Math.floor(h*l/u+s)*f;if(null===_){var g=(navigator.userAgent||"").match(/Chrome\/(\d+)/i);_=g?parseInt(g[1]):0}return!!_&&_<=87&&2===i&&l>=224e3&&0===o&&(e[t+3]=128|e[t+3]),{sampleRate:u,channelCount:d,frameLength:v,samplesPerFrame:c}}}function F(e,t){return 255===e[t]&&224==(224&e[t+1])&&0!=(6&e[t+1])}function M(e,t){return t+1<e.length&&F(e,t)}function N(e,t){if(t+1<e.length&&F(e,t)){var r=O(e,t),i=4;null!=r&&r.frameLength&&(i=r.frameLength);var n=t+i;return n===e.length||M(e,n)}return!1}const U=function(){function e(e){this.data=void 0,this.bytesAvailable=void 0,this.word=void 0,this.bitsAvailable=void 0,this.data=e,this.bytesAvailable=e.byteLength,this.word=0,this.bitsAvailable=0}var t=e.prototype;return t.loadWord=function(){var e=this.data,t=this.bytesAvailable,r=e.byteLength-t,i=new Uint8Array(4),n=Math.min(4,t);if(0===n)throw new Error("no bytes available");i.set(e.subarray(r,r+n)),this.word=new DataView(i.buffer).getUint32(0),this.bitsAvailable=8*n,this.bytesAvailable-=n},t.skipBits=function(e){var t;this.bitsAvailable>e?(this.word<<=e,this.bitsAvailable-=e):(e-=this.bitsAvailable,e-=(t=e>>3)>>3,this.bytesAvailable-=t,this.loadWord(),this.word<<=e,this.bitsAvailable-=e)},t.readBits=function(e){var t=Math.min(this.bitsAvailable,e),r=this.word>>>32-t;return e>32&&g.logger.error("Cannot read more than 32 bits at a time"),this.bitsAvailable-=t,this.bitsAvailable>0?this.word<<=t:this.bytesAvailable>0&&this.loadWord(),(t=e-t)>0&&this.bitsAvailable?r<<t|this.readBits(t):r},t.skipLZ=function(){var e;for(e=0;e<this.bitsAvailable;++e)if(0!=(this.word&2147483648>>>e))return this.word<<=e,this.bitsAvailable-=e,e;return this.loadWord(),e+this.skipLZ()},t.skipUEG=function(){this.skipBits(1+this.skipLZ())},t.skipEG=function(){this.skipBits(1+this.skipLZ())},t.readUEG=function(){var e=this.skipLZ();return this.readBits(e+1)-1},t.readEG=function(){var e=this.readUEG();return 1&e?1+e>>>1:-1*(e>>>1)},t.readBoolean=function(){return 1===this.readBits(1)},t.readUByte=function(){return this.readBits(8)},t.readUShort=function(){return this.readBits(16)},t.readUInt=function(){return this.readBits(32)},t.skipScalingList=function(e){for(var t=8,r=8,i=0;i<e;i++)0!==r&&(r=(t+this.readEG()+256)%256),t=0===r?t:r},t.readSPS=function(){var e,t,r,i=0,n=0,a=0,s=0,o=this.readUByte.bind(this),l=this.readBits.bind(this),u=this.readUEG.bind(this),d=this.readBoolean.bind(this),h=this.skipBits.bind(this),f=this.skipEG.bind(this),c=this.skipUEG.bind(this),v=this.skipScalingList.bind(this);o();var g=o();if(l(5),h(3),o(),c(),100===g||110===g||122===g||244===g||44===g||83===g||86===g||118===g||128===g){var p=u();if(3===p&&h(1),c(),c(),h(1),d())for(t=3!==p?8:12,r=0;r<t;r++)d()&&v(r<6?16:64)}c();var m=u();if(0===m)u();else if(1===m)for(h(1),f(),f(),e=u(),r=0;r<e;r++)f();c(),h(1);var y=u(),E=u(),T=l(1);0===T&&h(1),h(1),d()&&(i=u(),n=u(),a=u(),s=u());var S=[1,1];if(d()&&d())switch(o()){case 1:S=[1,1];break;case 2:S=[12,11];break;case 3:S=[10,11];break;case 4:S=[16,11];break;case 5:S=[40,33];break;case 6:S=[24,11];break;case 7:S=[20,11];break;case 8:S=[32,11];break;case 9:S=[80,33];break;case 10:S=[18,11];break;case 11:S=[15,11];break;case 12:S=[64,33];break;case 13:S=[160,99];break;case 14:S=[4,3];break;case 15:S=[3,2];break;case 16:S=[2,1];break;case 255:S=[o()<<8|o(),o()<<8|o()]}return{width:Math.ceil(16*(y+1)-2*i-2*n),height:(2-T)*(E+1)*16-(T?2:4)*(a+s),pixelRatio:S}},t.readSliceType=function(){return this.readUByte(),this.readUEG(),this.readUEG()},e}(),B=function(){function e(e,t,r){this.keyData=void 0,this.decrypter=void 0,this.keyData=r,this.decrypter=new a.default(e,t,{removePKCS7Padding:!1})}var t=e.prototype;return t.decryptBuffer=function(e,t){this.decrypter.decrypt(e,this.keyData.key.buffer,this.keyData.iv.buffer,t)},t.decryptAacSample=function(e,t,r,i){var n=e[t].unit;if(!(n.length<=16)){var a=n.subarray(16,n.length-n.length%16),s=a.buffer.slice(a.byteOffset,a.byteOffset+a.length),o=this;this.decryptBuffer(s,(function(a){var s=new Uint8Array(a);n.set(s,16),i||o.decryptAacSamples(e,t+1,r)}))}},t.decryptAacSamples=function(e,t,r){for(;;t++){if(t>=e.length)return void r();if(!(e[t].unit.length<32)){var i=this.decrypter.isSync();if(this.decryptAacSample(e,t,r,i),!i)return}}},t.getAvcEncryptedData=function(e){for(var t=16*Math.floor((e.length-48)/160)+16,r=new Int8Array(t),i=0,n=32;n<e.length-16;n+=160,i+=16)r.set(e.subarray(n,n+16),i);return r},t.getAvcDecryptedUnit=function(e,t){for(var r=new Uint8Array(t),i=0,n=32;n<e.length-16;n+=160,i+=16)e.set(r.subarray(i,i+16),n);return e},t.decryptAvcSample=function(e,t,r,i,n,a){var s=(0,d.discardEPB)(n.data),o=this.getAvcEncryptedData(s),l=this;this.decryptBuffer(o.buffer,(function(o){n.data=l.getAvcDecryptedUnit(s,o),a||l.decryptAvcSamples(e,t,r+1,i)}))},t.decryptAvcSamples=function(e,t,r,i){if(e instanceof Uint8Array)throw new Error("Cannot decrypt samples of type Uint8Array");for(;;t++,r=0){if(t>=e.length)return void i();for(var n=e[t].units;!(r>=n.length);r++){var a=n[r];if(!(a.data.length<=48||1!==a.type&&5!==a.type)){var s=this.decrypter.isSync();if(this.decryptAvcSample(e,t,r,i,a,s),!s)return}}}},e}();function G(){return G=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var i in r)Object.prototype.hasOwnProperty.call(r,i)&&(e[i]=r[i])}return e},G.apply(this,arguments)}var V=188;function H(e,t,r,i){return{key:e,frame:!1,pts:t,dts:r,units:[],debug:i,length:0}}function j(e,t){return(31&e[t+10])<<8|e[t+11]}function K(e,t,r,i){var n={audio:-1,avc:-1,id3:-1,segmentCodec:"aac"},a=t+3+((15&e[t+1])<<8|e[t+2])-4;for(t+=12+((15&e[t+10])<<8|e[t+11]);t<a;){var s=(31&e[t+1])<<8|e[t+2];switch(e[t]){case 207:if(!i){g.logger.log("ADTS AAC with AES-128-CBC frame encryption found in unencrypted stream");break}case 15:-1===n.audio&&(n.audio=s);break;case 21:-1===n.id3&&(n.id3=s);break;case 219:if(!i){g.logger.log("H.264 with AES-128-CBC slice encryption found in unencrypted stream");break}case 27:-1===n.avc&&(n.avc=s);break;case 3:case 4:!0!==r.mpeg&&!0!==r.mp3?g.logger.log("MPEG audio found, not supported in this browser"):-1===n.audio&&(n.audio=s,n.segmentCodec="mp3");break;case 36:g.logger.warn("Unsupported HEVC stream type found")}t+=5+((15&e[t+3])<<8|e[t+4])}return n}function W(e){var t,r,i,n,a,s=0,o=e.data;if(!e||0===e.size)return null;for(;o[0].length<19&&o.length>1;){var l=new Uint8Array(o[0].length+o[1].length);l.set(o[0]),l.set(o[1],o[0].length),o[0]=l,o.splice(1,1)}if(1===((t=o[0])[0]<<16)+(t[1]<<8)+t[2]){if((r=(t[4]<<8)+t[5])&&r>e.size-6)return null;var u=t[7];192&u&&(n=536870912*(14&t[9])+4194304*(255&t[10])+16384*(254&t[11])+128*(255&t[12])+(254&t[13])/2,64&u?n-(a=536870912*(14&t[14])+4194304*(255&t[15])+16384*(254&t[16])+128*(255&t[17])+(254&t[18])/2)>54e5&&(g.logger.warn(Math.round((n-a)/9e4)+"s delta between PTS and DTS, align them"),n=a):a=n);var d=(i=t[8])+9;if(e.size<=d)return null;e.size-=d;for(var h=new Uint8Array(e.size),f=0,c=o.length;f<c;f++){var v=(t=o[f]).byteLength;if(d){if(d>v){d-=v;continue}t=t.subarray(d),v-=d,d=0}h.set(t,s),s+=v}return r&&(r-=i+3),{data:h,pts:n,dts:a,len:r}}return null}function Y(e,t){if(e.units.length&&e.frame){if(void 0===e.pts){var r=t.samples,i=r.length;if(!i)return void t.dropped++;var n=r[i-1];e.pts=n.pts,e.dts=n.dts}t.samples.push(e)}e.debug.length&&g.logger.log(e.pts+"/"+e.dts+":"+e.debug)}const q=function(){function e(e,t,r){this.observer=void 0,this.config=void 0,this.typeSupported=void 0,this.sampleAes=null,this.pmtParsed=!1,this.audioCodec=void 0,this.videoCodec=void 0,this._duration=0,this._pmtId=-1,this._avcTrack=void 0,this._audioTrack=void 0,this._id3Track=void 0,this._txtTrack=void 0,this.aacOverFlow=null,this.avcSample=null,this.remainderData=null,this.observer=e,this.config=t,this.typeSupported=r}e.probe=function(t){var r=e.syncOffset(t);return r>0&&g.logger.warn("MPEG2-TS detected but first sync word found @ offset "+r),-1!==r},e.syncOffset=function(e){for(var t=Math.min(940,e.length-376)+1,r=0;r<t;){if(71===e[r]&&71===e[r+V])return r;r++}return-1},e.createTrack=function(e,t){return{container:"video"===e||"audio"===e?"video/mp2t":void 0,type:e,id:d.RemuxerTrackIdConfig[e],pid:-1,inputTimeScale:9e4,sequenceNumber:0,samples:[],dropped:0,duration:"audio"===e?t:void 0}};var t=e.prototype;return t.resetInitSegment=function(t,r,i,n){this.pmtParsed=!1,this._pmtId=-1,this._avcTrack=e.createTrack("video"),this._audioTrack=e.createTrack("audio",n),this._id3Track=e.createTrack("id3"),this._txtTrack=e.createTrack("text"),this._audioTrack.segmentCodec="aac",this.aacOverFlow=null,this.avcSample=null,this.remainderData=null,this.audioCodec=r,this.videoCodec=i,this._duration=n},t.resetTimeStamp=function(){},t.resetContiguity=function(){var e=this._audioTrack,t=this._avcTrack,r=this._id3Track;e&&(e.pesData=null),t&&(t.pesData=null),r&&(r.pesData=null),this.aacOverFlow=null,this.avcSample=null,this.remainderData=null},t.demux=function(t,r,a,s){var o;void 0===a&&(a=!1),void 0===s&&(s=!1),a||(this.sampleAes=null);var l=this._avcTrack,u=this._audioTrack,h=this._id3Track,f=this._txtTrack,c=l.pid,v=l.pesData,p=u.pid,m=h.pid,y=u.pesData,E=h.pesData,T=null,S=this.pmtParsed,b=this._pmtId,L=t.length;if(this.remainderData&&(L=(t=(0,d.appendUint8Array)(this.remainderData,t)).length,this.remainderData=null),L<V&&!s)return this.remainderData=t,{audioTrack:u,videoTrack:l,id3Track:h,textTrack:f};var A=Math.max(0,e.syncOffset(t));(L-=(L-A)%V)<t.byteLength&&!s&&(this.remainderData=new Uint8Array(t.buffer,L,t.buffer.byteLength-L));for(var D=0,R=A;R<L;R+=V)if(71===t[R]){var k=!!(64&t[R+1]),_=((31&t[R+1])<<8)+t[R+2],x=void 0;if((48&t[R+3])>>4>1){if((x=R+5+t[R+4])===R+V)continue}else x=R+4;switch(_){case c:k&&(v&&(o=W(v))&&this.parseAVCPES(l,f,o,!1),v={data:[],size:0}),v&&(v.data.push(t.subarray(x,R+V)),v.size+=R+V-x);break;case p:if(k){if(y&&(o=W(y)))switch(u.segmentCodec){case"aac":this.parseAACPES(u,o);break;case"mp3":this.parseMPEGPES(u,o)}y={data:[],size:0}}y&&(y.data.push(t.subarray(x,R+V)),y.size+=R+V-x);break;case m:k&&(E&&(o=W(E))&&this.parseID3PES(h,o),E={data:[],size:0}),E&&(E.data.push(t.subarray(x,R+V)),E.size+=R+V-x);break;case 0:k&&(x+=t[x]+1),b=this._pmtId=j(t,x);break;case b:k&&(x+=t[x]+1);var I=K(t,x,this.typeSupported,a);(c=I.avc)>0&&(l.pid=c),(p=I.audio)>0&&(u.pid=p,u.segmentCodec=I.segmentCodec),(m=I.id3)>0&&(h.pid=m),null===T||S||(g.logger.log("unknown PID '"+T+"' in TS found"),T=null,R=A-188),S=this.pmtParsed=!0;break;case 17:case 8191:break;default:T=_}}else D++;D>0&&this.observer.emit(i.Events.ERROR,i.Events.ERROR,{type:n.ErrorTypes.MEDIA_ERROR,details:n.ErrorDetails.FRAG_PARSING_ERROR,fatal:!1,reason:"Found "+D+" TS packet/s that do not start with 0x47"}),l.pesData=v,u.pesData=y,h.pesData=E;var w={audioTrack:u,videoTrack:l,id3Track:h,textTrack:f};return s&&this.extractRemainingSamples(w),w},t.flush=function(){var e,t=this.remainderData;return this.remainderData=null,e=t?this.demux(t,-1,!1,!0):{videoTrack:this._avcTrack,audioTrack:this._audioTrack,id3Track:this._id3Track,textTrack:this._txtTrack},this.extractRemainingSamples(e),this.sampleAes?this.decrypt(e,this.sampleAes):e},t.extractRemainingSamples=function(e){var t,r=e.audioTrack,i=e.videoTrack,n=e.id3Track,a=e.textTrack,s=i.pesData,o=r.pesData,l=n.pesData;if(s&&(t=W(s))?(this.parseAVCPES(i,a,t,!0),i.pesData=null):i.pesData=s,o&&(t=W(o))){switch(r.segmentCodec){case"aac":this.parseAACPES(r,t);break;case"mp3":this.parseMPEGPES(r,t)}r.pesData=null}else null!=o&&o.size&&g.logger.log("last AAC PES packet truncated,might overlap between fragments"),r.pesData=o;l&&(t=W(l))?(this.parseID3PES(n,t),n.pesData=null):n.pesData=l},t.demuxSampleAes=function(e,t,r){var i=this.demux(e,r,!0,!this.config.progressive),n=this.sampleAes=new B(this.observer,this.config,t);return this.decrypt(i,n)},t.decrypt=function(e,t){return new Promise((function(r){var i=e.audioTrack,n=e.videoTrack;i.samples&&"aac"===i.segmentCodec?t.decryptAacSamples(i.samples,0,(function(){n.samples?t.decryptAvcSamples(n.samples,0,0,(function(){r(e)})):r(e)})):n.samples&&t.decryptAvcSamples(n.samples,0,0,(function(){r(e)}))}))},t.destroy=function(){this._duration=0},t.parseAVCPES=function(e,t,r,i){var n,a=this,s=this.parseAVCNALu(e,r.data),o=this.avcSample,l=!1;r.data=null,o&&s.length&&!e.audFound&&(Y(o,e),o=this.avcSample=H(!1,r.pts,r.dts,"")),s.forEach((function(i){switch(i.type){case 1:n=!0,o||(o=a.avcSample=H(!0,r.pts,r.dts,"")),o.frame=!0;var s=i.data;if(l&&s.length>4){var u=new U(s).readSliceType();2!==u&&4!==u&&7!==u&&9!==u||(o.key=!0)}break;case 5:n=!0,o||(o=a.avcSample=H(!0,r.pts,r.dts,"")),o.key=!0,o.frame=!0;break;case 6:n=!0,(0,d.parseSEIMessageFromNALu)(i.data,1,r.pts,t.samples);break;case 7:if(n=!0,l=!0,!e.sps){var h=new U(i.data).readSPS();e.width=h.width,e.height=h.height,e.pixelRatio=h.pixelRatio,e.sps=[i.data],e.duration=a._duration;for(var f=i.data.subarray(1,4),c="avc1.",v=0;v<3;v++){var g=f[v].toString(16);g.length<2&&(g="0"+g),c+=g}e.codec=c}break;case 8:n=!0,e.pps||(e.pps=[i.data]);break;case 9:n=!1,e.audFound=!0,o&&Y(o,e),o=a.avcSample=H(!1,r.pts,r.dts,"");break;case 12:n=!0;break;default:n=!1,o&&(o.debug+="unknown NAL "+i.type+" ")}o&&n&&o.units.push(i)})),i&&o&&(Y(o,e),this.avcSample=null)},t.getLastNalUnit=function(e){var t,r,i=this.avcSample;if(i&&0!==i.units.length||(i=e[e.length-1]),null!==(t=i)&&void 0!==t&&t.units){var n=i.units;r=n[n.length-1]}return r},t.parseAVCNALu=function(e,t){var r,i,n=t.byteLength,a=e.naluState||0,s=a,o=[],l=0,u=-1,d=0;for(-1===a&&(u=0,d=31&t[0],a=0,l=1);l<n;)if(r=t[l++],a)if(1!==a)if(r)if(1===r){if(u>=0){var h={data:t.subarray(u,l-a-1),type:d};o.push(h)}else{var f=this.getLastNalUnit(e.samples);if(f&&(s&&l<=4-s&&f.state&&(f.data=f.data.subarray(0,f.data.byteLength-s)),(i=l-a-1)>0)){var c=new Uint8Array(f.data.byteLength+i);c.set(f.data,0),c.set(t.subarray(0,i),f.data.byteLength),f.data=c,f.state=0}}l<n?(u=l,d=31&t[l],a=0):a=-1}else a=0;else a=3;else a=r?0:2;else a=r?0:1;if(u>=0&&a>=0){var v={data:t.subarray(u,n),type:d,state:a};o.push(v)}if(0===o.length){var g=this.getLastNalUnit(e.samples);if(g){var p=new Uint8Array(g.data.byteLength+t.byteLength);p.set(g.data,0),p.set(t,g.data.byteLength),g.data=p}}return e.naluState=a,o},t.parseAACPES=function(e,t){var r,a,s,o,l,u=0,d=this.aacOverFlow,h=t.data;if(d){this.aacOverFlow=null;var f=d.missing,c=d.sample.unit.byteLength;if(-1===f){var v=new Uint8Array(c+h.byteLength);v.set(d.sample.unit,0),v.set(h,c),h=v}else{var p=c-f;d.sample.unit.set(h.subarray(0,f),p),e.samples.push(d.sample),u=d.missing}}for(r=u,a=h.length;r<a-1&&!E(h,r);r++);if(r===u||(r<a-1?(s="AAC PES did not start with ADTS header,offset:"+r,o=!1):(s="no ADTS header found in AAC PES",o=!0),g.logger.warn("parsing error:"+s),this.observer.emit(i.Events.ERROR,i.Events.ERROR,{type:n.ErrorTypes.MEDIA_ERROR,details:n.ErrorDetails.FRAG_PARSING_ERROR,fatal:o,reason:s}),!o)){if(S(e,this.observer,h,r,this.audioCodec),void 0!==t.pts)l=t.pts;else{if(!d)return void g.logger.warn("[tsdemuxer]: AAC PES unknown PTS");var m=b(e.samplerate);l=d.sample.pts+m}for(var y,T=0;r<a;){if(r+=(y=L(e,h,r,l,T)).length,y.missing){this.aacOverFlow=y;break}for(T++;r<a-1&&!E(h,r);r++);}}},t.parseMPEGPES=function(e,t){var r=t.data,i=r.length,n=0,a=0,s=t.pts;if(void 0!==s)for(;a<i;)if(M(r,a)){var o=P(e,r,a,s,n);if(!o)break;a+=o.length,n++}else a++;else g.logger.warn("[tsdemuxer]: MPEG PES unknown PTS")},t.parseID3PES=function(e,t){if(void 0!==t.pts){var r=G({},t,{type:this._avcTrack?l.MetadataSchema.emsg:l.MetadataSchema.audioId3,duration:Number.POSITIVE_INFINITY});e.samples.push(r)}else g.logger.warn("[tsdemuxer]: ID3 PES unknown PTS")},e}();function z(e,t){return z=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},z(e,t)}const X=function(e){var t,r;function i(){return e.apply(this,arguments)||this}r=e,(t=i).prototype=Object.create(r.prototype),t.prototype.constructor=t,z(t,r);var n=i.prototype;return n.resetInitSegment=function(t,r,i,n){e.prototype.resetInitSegment.call(this,t,r,i,n),this._audioTrack={container:"audio/mpeg",type:"audio",id:2,pid:-1,sequenceNumber:0,segmentCodec:"mp3",samples:[],manifestCodec:r,duration:n,inputTimeScale:9e4,dropped:0}},i.probe=function(e){if(!e)return!1;for(var t=(o.getID3Data(e,0)||[]).length,r=e.length;t<r;t++)if(N(e,t))return g.logger.log("MPEG Audio sync word found !"),!0;return!1},n.canParse=function(e,t){return function(e,t){return F(e,t)&&4<=e.length-t}(e,t)},n.appendFrame=function(e,t,r){if(null!==this.basePTS)return P(e,t,r,this.basePTS,this.frameIndex)},i}(v),Q=function(){function e(){}return e.getSilentFrame=function(e,t){if("mp4a.40.2"===e){if(1===t)return new Uint8Array([0,200,0,128,35,128]);if(2===t)return new Uint8Array([33,0,73,144,2,25,0,35,128]);if(3===t)return new Uint8Array([0,200,0,128,32,132,1,38,64,8,100,0,142]);if(4===t)return new Uint8Array([0,200,0,128,32,132,1,38,64,8,100,0,128,44,128,8,2,56]);if(5===t)return new Uint8Array([0,200,0,128,32,132,1,38,64,8,100,0,130,48,4,153,0,33,144,2,56]);if(6===t)return new Uint8Array([0,200,0,128,32,132,1,38,64,8,100,0,130,48,4,153,0,33,144,2,0,178,0,32,8,224])}else{if(1===t)return new Uint8Array([1,64,34,128,163,78,230,128,186,8,0,0,0,28,6,241,193,10,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,94]);if(2===t)return new Uint8Array([1,64,34,128,163,94,230,128,186,8,0,0,0,0,149,0,6,241,161,10,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,94]);if(3===t)return new Uint8Array([1,64,34,128,163,94,230,128,186,8,0,0,0,0,149,0,6,241,161,10,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,94])}},e}();var $=Math.pow(2,32)-1,Z=function(){function e(){}return e.init=function(){var t;for(t in e.types={avc1:[],avcC:[],btrt:[],dinf:[],dref:[],esds:[],ftyp:[],hdlr:[],mdat:[],mdhd:[],mdia:[],mfhd:[],minf:[],moof:[],moov:[],mp4a:[],".mp3":[],mvex:[],mvhd:[],pasp:[],sdtp:[],stbl:[],stco:[],stsc:[],stsd:[],stsz:[],stts:[],tfdt:[],tfhd:[],traf:[],trak:[],trun:[],trex:[],tkhd:[],vmhd:[],smhd:[]},e.types)e.types.hasOwnProperty(t)&&(e.types[t]=[t.charCodeAt(0),t.charCodeAt(1),t.charCodeAt(2),t.charCodeAt(3)]);var r=new Uint8Array([0,0,0,0,0,0,0,0,118,105,100,101,0,0,0,0,0,0,0,0,0,0,0,0,86,105,100,101,111,72,97,110,100,108,101,114,0]),i=new Uint8Array([0,0,0,0,0,0,0,0,115,111,117,110,0,0,0,0,0,0,0,0,0,0,0,0,83,111,117,110,100,72,97,110,100,108,101,114,0]);e.HDLR_TYPES={video:r,audio:i};var n=new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,12,117,114,108,32,0,0,0,1]),a=new Uint8Array([0,0,0,0,0,0,0,0]);e.STTS=e.STSC=e.STCO=a,e.STSZ=new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0]),e.VMHD=new Uint8Array([0,0,0,1,0,0,0,0,0,0,0,0]),e.SMHD=new Uint8Array([0,0,0,0,0,0,0,0]),e.STSD=new Uint8Array([0,0,0,0,0,0,0,1]);var s=new Uint8Array([105,115,111,109]),o=new Uint8Array([97,118,99,49]),l=new Uint8Array([0,0,0,1]);e.FTYP=e.box(e.types.ftyp,s,l,s,o),e.DINF=e.box(e.types.dinf,e.box(e.types.dref,n))},e.box=function(e){for(var t=8,r=arguments.length,i=new Array(r>1?r-1:0),n=1;n<r;n++)i[n-1]=arguments[n];for(var a=i.length,s=a;a--;)t+=i[a].byteLength;var o=new Uint8Array(t);for(o[0]=t>>24&255,o[1]=t>>16&255,o[2]=t>>8&255,o[3]=255&t,o.set(e,4),a=0,t=8;a<s;a++)o.set(i[a],t),t+=i[a].byteLength;return o},e.hdlr=function(t){return e.box(e.types.hdlr,e.HDLR_TYPES[t])},e.mdat=function(t){return e.box(e.types.mdat,t)},e.mdhd=function(t,r){r*=t;var i=Math.floor(r/($+1)),n=Math.floor(r%($+1));return e.box(e.types.mdhd,new Uint8Array([1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,3,t>>24&255,t>>16&255,t>>8&255,255&t,i>>24,i>>16&255,i>>8&255,255&i,n>>24,n>>16&255,n>>8&255,255&n,85,196,0,0]))},e.mdia=function(t){return e.box(e.types.mdia,e.mdhd(t.timescale,t.duration),e.hdlr(t.type),e.minf(t))},e.mfhd=function(t){return e.box(e.types.mfhd,new Uint8Array([0,0,0,0,t>>24,t>>16&255,t>>8&255,255&t]))},e.minf=function(t){return"audio"===t.type?e.box(e.types.minf,e.box(e.types.smhd,e.SMHD),e.DINF,e.stbl(t)):e.box(e.types.minf,e.box(e.types.vmhd,e.VMHD),e.DINF,e.stbl(t))},e.moof=function(t,r,i){return e.box(e.types.moof,e.mfhd(t),e.traf(i,r))},e.moov=function(t){for(var r=t.length,i=[];r--;)i[r]=e.trak(t[r]);return e.box.apply(null,[e.types.moov,e.mvhd(t[0].timescale,t[0].duration)].concat(i).concat(e.mvex(t)))},e.mvex=function(t){for(var r=t.length,i=[];r--;)i[r]=e.trex(t[r]);return e.box.apply(null,[e.types.mvex].concat(i))},e.mvhd=function(t,r){r*=t;var i=Math.floor(r/($+1)),n=Math.floor(r%($+1)),a=new Uint8Array([1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,3,t>>24&255,t>>16&255,t>>8&255,255&t,i>>24,i>>16&255,i>>8&255,255&i,n>>24,n>>16&255,n>>8&255,255&n,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255]);return e.box(e.types.mvhd,a)},e.sdtp=function(t){var r,i,n=t.samples||[],a=new Uint8Array(4+n.length);for(r=0;r<n.length;r++)i=n[r].flags,a[r+4]=i.dependsOn<<4|i.isDependedOn<<2|i.hasRedundancy;return e.box(e.types.sdtp,a)},e.stbl=function(t){return e.box(e.types.stbl,e.stsd(t),e.box(e.types.stts,e.STTS),e.box(e.types.stsc,e.STSC),e.box(e.types.stsz,e.STSZ),e.box(e.types.stco,e.STCO))},e.avc1=function(t){var r,i,n,a=[],s=[];for(r=0;r<t.sps.length;r++)n=(i=t.sps[r]).byteLength,a.push(n>>>8&255),a.push(255&n),a=a.concat(Array.prototype.slice.call(i));for(r=0;r<t.pps.length;r++)n=(i=t.pps[r]).byteLength,s.push(n>>>8&255),s.push(255&n),s=s.concat(Array.prototype.slice.call(i));var o=e.box(e.types.avcC,new Uint8Array([1,a[3],a[4],a[5],255,224|t.sps.length].concat(a).concat([t.pps.length]).concat(s))),l=t.width,u=t.height,d=t.pixelRatio[0],h=t.pixelRatio[1];return e.box(e.types.avc1,new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,l>>8&255,255&l,u>>8&255,255&u,0,72,0,0,0,72,0,0,0,0,0,0,0,1,18,100,97,105,108,121,109,111,116,105,111,110,47,104,108,115,46,106,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,17,17]),o,e.box(e.types.btrt,new Uint8Array([0,28,156,128,0,45,198,192,0,45,198,192])),e.box(e.types.pasp,new Uint8Array([d>>24,d>>16&255,d>>8&255,255&d,h>>24,h>>16&255,h>>8&255,255&h])))},e.esds=function(e){var t=e.config.length;return new Uint8Array([0,0,0,0,3,23+t,0,1,0,4,15+t,64,21,0,0,0,0,0,0,0,0,0,0,0,5].concat([t]).concat(e.config).concat([6,1,2]))},e.mp4a=function(t){var r=t.samplerate;return e.box(e.types.mp4a,new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,t.channelCount,0,16,0,0,0,0,r>>8&255,255&r,0,0]),e.box(e.types.esds,e.esds(t)))},e.mp3=function(t){var r=t.samplerate;return e.box(e.types[".mp3"],new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,t.channelCount,0,16,0,0,0,0,r>>8&255,255&r,0,0]))},e.stsd=function(t){return"audio"===t.type?"mp3"===t.segmentCodec&&"mp3"===t.codec?e.box(e.types.stsd,e.STSD,e.mp3(t)):e.box(e.types.stsd,e.STSD,e.mp4a(t)):e.box(e.types.stsd,e.STSD,e.avc1(t))},e.tkhd=function(t){var r=t.id,i=t.duration*t.timescale,n=t.width,a=t.height,s=Math.floor(i/($+1)),o=Math.floor(i%($+1));return e.box(e.types.tkhd,new Uint8Array([1,0,0,7,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,3,r>>24&255,r>>16&255,r>>8&255,255&r,0,0,0,0,s>>24,s>>16&255,s>>8&255,255&s,o>>24,o>>16&255,o>>8&255,255&o,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,n>>8&255,255&n,0,0,a>>8&255,255&a,0,0]))},e.traf=function(t,r){var i=e.sdtp(t),n=t.id,a=Math.floor(r/($+1)),s=Math.floor(r%($+1));return e.box(e.types.traf,e.box(e.types.tfhd,new Uint8Array([0,0,0,0,n>>24,n>>16&255,n>>8&255,255&n])),e.box(e.types.tfdt,new Uint8Array([1,0,0,0,a>>24,a>>16&255,a>>8&255,255&a,s>>24,s>>16&255,s>>8&255,255&s])),e.trun(t,i.length+16+20+8+16+8+8),i)},e.trak=function(t){return t.duration=t.duration||4294967295,e.box(e.types.trak,e.tkhd(t),e.mdia(t))},e.trex=function(t){var r=t.id;return e.box(e.types.trex,new Uint8Array([0,0,0,0,r>>24,r>>16&255,r>>8&255,255&r,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1]))},e.trun=function(t,r){var i,n,a,s,o,l,u=t.samples||[],d=u.length,h=12+16*d,f=new Uint8Array(h);for(r+=8+h,f.set(["video"===t.type?1:0,0,15,1,d>>>24&255,d>>>16&255,d>>>8&255,255&d,r>>>24&255,r>>>16&255,r>>>8&255,255&r],0),i=0;i<d;i++)a=(n=u[i]).duration,s=n.size,o=n.flags,l=n.cts,f.set([a>>>24&255,a>>>16&255,a>>>8&255,255&a,s>>>24&255,s>>>16&255,s>>>8&255,255&s,o.isLeading<<2|o.dependsOn,o.isDependedOn<<6|o.hasRedundancy<<4|o.paddingValue<<1|o.isNonSync,61440&o.degradPrio,15&o.degradPrio,l>>>24&255,l>>>16&255,l>>>8&255,255&l],12+16*i);return e.box(e.types.trun,f)},e.initSegment=function(t){e.types||e.init();var r=e.moov(t),i=new Uint8Array(e.FTYP.byteLength+r.byteLength);return i.set(e.FTYP),i.set(r,e.FTYP.byteLength),i},e}();Z.types=void 0,Z.HDLR_TYPES=void 0,Z.STTS=void 0,Z.STSC=void 0,Z.STCO=void 0,Z.STSZ=void 0,Z.VMHD=void 0,Z.SMHD=void 0,Z.STSD=void 0,Z.FTYP=void 0,Z.DINF=void 0;const J=Z;var ee=r(308);function te(e,t){return void 0===t&&(t=!1),function(e,t,r,i){void 0===r&&(r=1),void 0===i&&(i=!1);var n=e*t*r;return i?Math.round(n):n}(e,1e3,1/9e4,t)}function re(){return re=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var i in r)Object.prototype.hasOwnProperty.call(r,i)&&(e[i]=r[i])}return e},re.apply(this,arguments)}var ie=null,ne=null,ae=function(){function e(e,t,r,i){if(void 0===i&&(i=""),this.observer=void 0,this.config=void 0,this.typeSupported=void 0,this.ISGenerated=!1,this._initPTS=void 0,this._initDTS=void 0,this.nextAvcDts=null,this.nextAudioPts=null,this.videoSampleDuration=null,this.isAudioContiguous=!1,this.isVideoContiguous=!1,this.observer=e,this.config=t,this.typeSupported=r,this.ISGenerated=!1,null===ie){var n=(navigator.userAgent||"").match(/Chrome\/(\d+)/i);ie=n?parseInt(n[1]):0}if(null===ne){var a=navigator.userAgent.match(/Safari\/(\d+)/i);ne=a?parseInt(a[1]):0}}var t=e.prototype;return t.destroy=function(){},t.resetTimeStamp=function(e){g.logger.log("[mp4-remuxer]: initPTS & initDTS reset"),this._initPTS=this._initDTS=e},t.resetNextTimestamp=function(){g.logger.log("[mp4-remuxer]: reset next timestamp"),this.isVideoContiguous=!1,this.isAudioContiguous=!1},t.resetInitSegment=function(){g.logger.log("[mp4-remuxer]: ISGenerated flag reset"),this.ISGenerated=!1},t.getVideoStartPts=function(e){var t=!1,r=e.reduce((function(e,r){var i=r.pts-e;return i<-4294967296?(t=!0,se(e,r.pts)):i>0?e:r.pts}),e[0].pts);return t&&g.logger.debug("PTS rollover detected"),r},t.remux=function(e,t,r,i,n,a,s,o){var l,u,d,h,f,c,v=n,p=n,m=e.pid>-1,y=t.pid>-1,E=t.samples.length,T=e.samples.length>0,S=s&&E>0||E>1;if((!m||T)&&(!y||S)||this.ISGenerated||s){this.ISGenerated||(d=this.generateIS(e,t,n));var b,L=this.isVideoContiguous,A=-1;if(S&&(A=function(e){for(var t=0;t<e.length;t++)if(e[t].key)return t;return-1}(t.samples),!L&&this.config.forceKeyFrameOnDiscontinuity))if(c=!0,A>0){g.logger.warn("[mp4-remuxer]: Dropped "+A+" out of "+E+" video samples due to a missing keyframe");var D=this.getVideoStartPts(t.samples);t.samples=t.samples.slice(A),t.dropped+=A,b=p+=(t.samples[0].pts-D)/t.inputTimeScale}else-1===A&&(g.logger.warn("[mp4-remuxer]: No keyframe found out of "+E+" video samples"),c=!1);if(this.ISGenerated){if(T&&S){var R=this.getVideoStartPts(t.samples),k=(se(e.samples[0].pts,R)-R)/t.inputTimeScale;v+=Math.max(0,k),p+=Math.max(0,-k)}if(T){if(e.samplerate||(g.logger.warn("[mp4-remuxer]: regenerate InitSegment as audio detected"),d=this.generateIS(e,t,n)),u=this.remuxAudio(e,v,this.isAudioContiguous,a,y||S||o===ee.PlaylistLevelType.AUDIO?p:void 0),S){var _=u?u.endPTS-u.startPTS:0;t.inputTimeScale||(g.logger.warn("[mp4-remuxer]: regenerate InitSegment as video detected"),d=this.generateIS(e,t,n)),l=this.remuxVideo(t,p,L,_)}}else S&&(l=this.remuxVideo(t,p,L,0));l&&(l.firstKeyFrame=A,l.independent=-1!==A,l.firstKeyFramePTS=b)}}return this.ISGenerated&&(r.samples.length&&(f=oe(r,n,this._initPTS,this._initDTS)),i.samples.length&&(h=le(i,n,this._initPTS))),{audio:u,video:l,initSegment:d,independent:c,text:h,id3:f}},t.generateIS=function(e,t,r){var i,n,a,o=e.samples,l=t.samples,u=this.typeSupported,d={},h=!(0,s.isFiniteNumber)(this._initPTS),f="audio/mp4";if(h&&(i=n=1/0),e.config&&o.length&&(e.timescale=e.samplerate,"mp3"===e.segmentCodec&&(u.mpeg?(f="audio/mpeg",e.codec=""):u.mp3&&(e.codec="mp3")),d.audio={id:"audio",container:f,codec:e.codec,initSegment:"mp3"===e.segmentCodec&&u.mpeg?new Uint8Array(0):J.initSegment([e]),metadata:{channelCount:e.channelCount}},h&&(a=e.inputTimeScale,i=n=o[0].pts-Math.round(a*r))),t.sps&&t.pps&&l.length&&(t.timescale=t.inputTimeScale,d.video={id:"main",container:"video/mp4",codec:t.codec,initSegment:J.initSegment([t]),metadata:{width:t.width,height:t.height}},h)){a=t.inputTimeScale;var c=this.getVideoStartPts(l),v=Math.round(a*r);n=Math.min(n,se(l[0].dts,c)-v),i=Math.min(i,c-v)}if(Object.keys(d).length)return this.ISGenerated=!0,h&&(this._initPTS=i,this._initDTS=n),{tracks:d,initPTS:i,timescale:a}},t.remuxVideo=function(e,t,r,a){var s,o,l=e.inputTimeScale,u=e.samples,d=[],h=u.length,f=this._initPTS,c=this.nextAvcDts,v=8,p=this.videoSampleDuration,m=Number.POSITIVE_INFINITY,y=Number.NEGATIVE_INFINITY,E=!1;r&&null!==c||(c=t*l-(u[0].pts-se(u[0].dts,u[0].pts)));for(var T=0;T<h;T++){var S=u[T];S.pts=se(S.pts-f,c),S.dts=se(S.dts-f,c),S.dts<u[T>0?T-1:T].dts&&(E=!0)}E&&u.sort((function(e,t){var r=e.dts-t.dts,i=e.pts-t.pts;return r||i})),s=u[0].dts;var b=u[u.length-1].dts-s,L=b?Math.round(b/(h-1)):p||e.inputTimeScale/30;if(r){var A=s-c,D=A>L,R=A<-1;if((D||R)&&(D?g.logger.warn("AVC: "+te(A,!0)+" ms ("+A+"dts) hole between fragments detected, filling it"):g.logger.warn("AVC: "+te(-A,!0)+" ms ("+A+"dts) overlapping between fragments detected"),!R||c>u[0].pts)){s=c;var k=u[0].pts-A;u[0].dts=s,u[0].pts=k,g.logger.log("Video: First PTS/DTS adjusted: "+te(k,!0)+"/"+te(s,!0)+", delta: "+te(A,!0)+" ms")}}s=Math.max(0,s);for(var _=0,x=0,I=0;I<h;I++){for(var w=u[I],C=w.units,P=C.length,O=0,F=0;F<P;F++)O+=C[F].data.length;x+=O,_+=P,w.length=O,w.dts=Math.max(w.dts,s),m=Math.min(w.pts,m),y=Math.max(w.pts,y)}o=u[h-1].dts;var M,N=x+4*_+8;try{M=new Uint8Array(N)}catch(e){return void this.observer.emit(i.Events.ERROR,i.Events.ERROR,{type:n.ErrorTypes.MUX_ERROR,details:n.ErrorDetails.REMUX_ALLOC_ERROR,fatal:!1,bytes:N,reason:"fail allocating video mdat "+N})}var U=new DataView(M.buffer);U.setUint32(0,N),M.set(J.types.mdat,4);for(var B=!1,G=Number.POSITIVE_INFINITY,V=Number.POSITIVE_INFINITY,H=Number.NEGATIVE_INFINITY,j=Number.NEGATIVE_INFINITY,K=0;K<h;K++){for(var W=u[K],Y=W.units,q=0,z=0,X=Y.length;z<X;z++){var Q=Y[z],$=Q.data,Z=Q.data.byteLength;U.setUint32(v,Z),v+=4,M.set($,v),v+=Z,q+=4+Z}var ee=void 0;if(K<h-1)p=u[K+1].dts-W.dts,ee=u[K+1].pts-W.pts;else{var ae=this.config,oe=K>0?W.dts-u[K-1].dts:L;if(ee=K>0?W.pts-u[K-1].pts:L,ae.stretchShortVideoTrack&&null!==this.nextAudioPts){var le=Math.floor(ae.maxBufferHole*l),de=(a?m+a*l:this.nextAudioPts)-W.pts;de>le?((p=de-oe)<0?p=oe:B=!0,g.logger.log("[mp4-remuxer]: It is approximately "+de/90+" ms to the next segment; using duration "+p/90+" ms for the last video frame.")):p=oe}else p=oe}var he=Math.round(W.pts-W.dts);G=Math.min(G,p),H=Math.max(H,p),V=Math.min(V,ee),j=Math.max(j,ee),d.push(new ue(W.key,p,q,he))}if(d.length)if(ie){if(ie<70){var fe=d[0].flags;fe.dependsOn=2,fe.isNonSync=0}}else if(ne&&j-V<H-G&&L/H<.025&&0===d[0].cts){g.logger.warn("Found irregular gaps in sample duration. Using PTS instead of DTS to determine MP4 sample duration.");for(var ce=s,ve=0,ge=d.length;ve<ge;ve++){var pe=ce+d[ve].duration,me=ce+d[ve].cts;if(ve<ge-1){var ye=pe+d[ve+1].cts;d[ve].duration=ye-me}else d[ve].duration=ve?d[ve-1].duration:L;d[ve].cts=0,ce=pe}}p=B||!p?L:p,this.nextAvcDts=c=o+p,this.videoSampleDuration=p,this.isVideoContiguous=!0;var Ee={data1:J.moof(e.sequenceNumber++,s,re({},e,{samples:d})),data2:M,startPTS:m/l,endPTS:(y+p)/l,startDTS:s/l,endDTS:c/l,type:"video",hasAudio:!1,hasVideo:!0,nb:d.length,dropped:e.dropped};return e.samples=[],e.dropped=0,Ee},t.remuxAudio=function(e,t,r,a,s){var o=e.inputTimeScale,l=o/(e.samplerate?e.samplerate:o),u="aac"===e.segmentCodec?1024:1152,d=u*l,h=this._initPTS,f="mp3"===e.segmentCodec&&this.typeSupported.mpeg,c=[],v=void 0!==s,p=e.samples,m=f?0:8,y=this.nextAudioPts||-1,E=t*o;if(this.isAudioContiguous=r=r||p.length&&y>0&&(a&&Math.abs(E-y)<9e3||Math.abs(se(p[0].pts-h,E)-y)<20*d),p.forEach((function(e){e.pts=se(e.pts-h,E)})),!r||y<0){if(p=p.filter((function(e){return e.pts>=0})),!p.length)return;y=0===s?0:a&&!v?Math.max(0,E):p[0].pts}if("aac"===e.segmentCodec)for(var T=this.config.maxAudioFramesDrift,S=0,b=y;S<p.length;S++){var L=p[S],A=L.pts,D=A-b,R=Math.abs(1e3*D/o);if(D<=-T*d&&v)0===S&&(g.logger.warn("Audio frame @ "+(A/o).toFixed(3)+"s overlaps nextAudioPts by "+Math.round(1e3*D/o)+" ms."),this.nextAudioPts=y=b=A);else if(D>=T*d&&R<1e4&&v){var k=Math.round(D/d);(b=A-k*d)<0&&(k--,b+=d),0===S&&(this.nextAudioPts=y=b),g.logger.warn("[mp4-remuxer]: Injecting "+k+" audio frame @ "+(b/o).toFixed(3)+"s due to "+Math.round(1e3*D/o)+" ms gap.");for(var _=0;_<k;_++){var x=Math.max(b,0),I=Q.getSilentFrame(e.manifestCodec||e.codec,e.channelCount);I||(g.logger.log("[mp4-remuxer]: Unable to get silent frame for given audio codec; duplicating last frame instead."),I=L.unit.subarray()),p.splice(S,0,{unit:I,pts:x}),b+=d,S++}}L.pts=b,b+=d}for(var w,C=null,P=null,O=0,F=p.length;F--;)O+=p[F].unit.byteLength;for(var M=0,N=p.length;M<N;M++){var U=p[M],B=U.unit,G=U.pts;if(null!==P)c[M-1].duration=Math.round((G-P)/l);else{if(r&&"aac"===e.segmentCodec&&(G=y),C=G,!(O>0))return;O+=m;try{w=new Uint8Array(O)}catch(e){return void this.observer.emit(i.Events.ERROR,i.Events.ERROR,{type:n.ErrorTypes.MUX_ERROR,details:n.ErrorDetails.REMUX_ALLOC_ERROR,fatal:!1,bytes:O,reason:"fail allocating audio mdat "+O})}f||(new DataView(w.buffer).setUint32(0,O),w.set(J.types.mdat,4))}w.set(B,m);var V=B.byteLength;m+=V,c.push(new ue(!0,u,V,0)),P=G}var H=c.length;if(H){var j=c[c.length-1];this.nextAudioPts=y=P+l*j.duration;var K=f?new Uint8Array(0):J.moof(e.sequenceNumber++,C/l,re({},e,{samples:c}));e.samples=[];var W=C/o,Y=y/o,q={data1:K,data2:w,startPTS:W,endPTS:Y,startDTS:W,endDTS:Y,type:"audio",hasAudio:!0,hasVideo:!1,nb:H};return this.isAudioContiguous=!0,q}},t.remuxEmptyAudio=function(e,t,r,i){var n=e.inputTimeScale,a=n/(e.samplerate?e.samplerate:n),s=this.nextAudioPts,o=(null!==s?s:i.startDTS*n)+this._initDTS,l=i.endDTS*n+this._initDTS,u=1024*a,d=Math.ceil((l-o)/u),h=Q.getSilentFrame(e.manifestCodec||e.codec,e.channelCount);if(g.logger.warn("[mp4-remuxer]: remux empty Audio"),h){for(var f=[],c=0;c<d;c++){var v=o+c*u;f.push({unit:h,pts:v,dts:v})}return e.samples=f,this.remuxAudio(e,t,r,!1)}g.logger.trace("[mp4-remuxer]: Unable to remuxEmptyAudio since we were unable to get a silent frame for given audio codec")},e}();function se(e,t){var r;if(null===t)return e;for(r=t<e?-8589934592:8589934592;Math.abs(e-t)>4294967296;)e+=r;return e}function oe(e,t,r,i){var n=e.samples.length;if(n){for(var a=e.inputTimeScale,s=0;s<n;s++){var o=e.samples[s];o.pts=se(o.pts-r,t*a)/a,o.dts=se(o.dts-i,t*a)/a}var l=e.samples;return e.samples=[],{samples:l}}}function le(e,t,r){var i=e.samples.length;if(i){for(var n=e.inputTimeScale,a=0;a<i;a++){var s=e.samples[a];s.pts=se(s.pts-r,t*n)/n}e.samples.sort((function(e,t){return e.pts-t.pts}));var o=e.samples;return e.samples=[],{samples:o}}}var ue=function(e,t,r,i){this.size=void 0,this.duration=void 0,this.cts=void 0,this.flags=void 0,this.duration=t,this.size=r,this.cts=i,this.flags=new de(e)},de=function(e){this.isLeading=0,this.isDependedOn=0,this.hasRedundancy=0,this.degradPrio=0,this.dependsOn=1,this.isNonSync=1,this.dependsOn=e?2:1,this.isNonSync=e?0:1},he=r(923);function fe(e,t){var r=null==e?void 0:e.codec;return r&&r.length>4?r:"hvc1"===r||"hev1"===r?"hvc1.1.c.L120.90":"av01"===r?"av01.0.04M.08":"avc1"===r||t===he.ElementaryStreamTypes.VIDEO?"avc1.42e01e":"mp4a.40.5"}const ce=function(){function e(){this.emitInitSegment=!1,this.audioCodec=void 0,this.videoCodec=void 0,this.initData=void 0,this.initPTS=void 0,this.initTracks=void 0,this.lastEndTime=null}var t=e.prototype;return t.destroy=function(){},t.resetTimeStamp=function(e){this.initPTS=e,this.lastEndTime=null},t.resetNextTimestamp=function(){this.lastEndTime=null},t.resetInitSegment=function(e,t,r){this.audioCodec=t,this.videoCodec=r,this.generateInitSegment(e),this.emitInitSegment=!0},t.generateInitSegment=function(e){var t=this.audioCodec,r=this.videoCodec;if(!e||!e.byteLength)return this.initTracks=void 0,void(this.initData=void 0);var i=this.initData=(0,d.parseInitSegment)(e);t||(t=fe(i.audio,he.ElementaryStreamTypes.AUDIO)),r||(r=fe(i.video,he.ElementaryStreamTypes.VIDEO));var n={};i.audio&&i.video?n.audiovideo={container:"video/mp4",codec:t+","+r,initSegment:e,id:"main"}:i.audio?n.audio={container:"audio/mp4",codec:t,initSegment:e,id:"audio"}:i.video?n.video={container:"video/mp4",codec:r,initSegment:e,id:"main"}:g.logger.warn("[passthrough-remuxer.ts]: initSegment does not contain moov or trak boxes."),this.initTracks=n},t.remux=function(e,t,r,i,n){var a,o=this.initPTS,l=this.lastEndTime,u={audio:void 0,video:void 0,text:i,id3:r,initSegment:void 0};(0,s.isFiniteNumber)(l)||(l=this.lastEndTime=n||0);var h=t.samples;if(!h||!h.length)return u;var f={initPTS:void 0,timescale:1},c=this.initData;if(c&&c.length||(this.generateInitSegment(h),c=this.initData),!c||!c.length)return g.logger.warn("[passthrough-remuxer.ts]: Failed to generate initSegment."),u;this.emitInitSegment&&(f.tracks=this.initTracks,this.emitInitSegment=!1);var v=(0,d.getStartDTS)(c,h);(0,s.isFiniteNumber)(o)||(this.initPTS=f.initPTS=o=v-n);var p=(0,d.getDuration)(h,c),m=e?v-o:l,y=m+p;(0,d.offsetStartDTS)(c,h,o),p>0?this.lastEndTime=y:(g.logger.warn("Duration parsed from mp4 should be greater than zero"),this.resetNextTimestamp());var E=!!c.audio,T=!!c.video,S="";E&&(S+="audio"),T&&(S+="video");var b={data1:h,startPTS:m,startDTS:m,endPTS:y,endDTS:y,type:S,hasAudio:E,hasVideo:T,nb:1,dropped:0};u.audio="audio"===b.type?b:void 0,u.video="audio"!==b.type?b:void 0,u.initSegment=f;var L=null!=(a=this.initPTS)?a:0;return u.id3=oe(r,n,L,L),i.samples.length&&(u.text=le(i,n,L)),u},e}();var ve;try{ve=self.performance.now.bind(self.performance)}catch(e){g.logger.debug("Unable to use Performance API on this environment"),ve=self.Date.now}var ge=[{demux:q,remux:ae},{demux:k,remux:ce},{demux:D,remux:ae},{demux:X,remux:ae}],pe=function(){function e(e,t,r,i,n){this.observer=void 0,this.typeSupported=void 0,this.config=void 0,this.vendor=void 0,this.id=void 0,this.demuxer=void 0,this.remuxer=void 0,this.decrypter=void 0,this.probe=void 0,this.decryptionPromise=null,this.transmuxConfig=void 0,this.currentTransmuxState=void 0,this.observer=e,this.typeSupported=t,this.config=r,this.vendor=i,this.id=n}var t=e.prototype;return t.configure=function(e){this.transmuxConfig=e,this.decrypter&&this.decrypter.reset()},t.push=function(e,t,r,i){var n=this,a=r.transmuxing;a.executeStart=ve();var s=new Uint8Array(e),o=this.config,l=this.currentTransmuxState,u=this.transmuxConfig;i&&(this.currentTransmuxState=i);var d=i||l,h=d.contiguous,f=d.discontinuity,c=d.trackSwitch,v=d.accurateTimeOffset,g=d.timeOffset,p=d.initSegmentChange,m=u.audioCodec,y=u.videoCodec,E=u.defaultInitPts,T=u.duration,S=u.initSegmentData;(f||c||p)&&this.resetInitSegment(S,m,y,T),(f||p)&&this.resetInitialTimestamp(E),h||this.resetContiguity();var b=function(e,t){var r=null;return e.byteLength>0&&null!=t&&null!=t.key&&null!==t.iv&&null!=t.method&&(r=t),r}(s,t);if(b&&"AES-128"===b.method){var L=this.getDecrypter();if(!o.enableSoftwareAES)return this.decryptionPromise=L.webCryptoDecrypt(s,b.key.buffer,b.iv.buffer).then((function(e){var t=n.push(e,null,r);return n.decryptionPromise=null,t})),this.decryptionPromise;var A=L.softwareDecrypt(s,b.key.buffer,b.iv.buffer);if(!A)return a.executeEnd=ve(),me(r);s=new Uint8Array(A)}this.needsProbing(s,f,c)&&this.configureTransmuxer(s,u);var D=this.transmux(s,b,g,v,r),R=this.currentTransmuxState;return R.contiguous=!0,R.discontinuity=!1,R.trackSwitch=!1,a.executeEnd=ve(),D},t.flush=function(e){var t=this,r=e.transmuxing;r.executeStart=ve();var a=this.decrypter,s=this.currentTransmuxState,o=this.decryptionPromise;if(o)return o.then((function(){return t.flush(e)}));var l=[],u=s.timeOffset;if(a){var d=a.flush();d&&l.push(this.push(d,null,e))}var h=this.demuxer,f=this.remuxer;if(!h||!f)return this.observer.emit(i.Events.ERROR,i.Events.ERROR,{type:n.ErrorTypes.MEDIA_ERROR,details:n.ErrorDetails.FRAG_PARSING_ERROR,fatal:!0,reason:"no demux matching with content found"}),r.executeEnd=ve(),[me(e)];var c=h.flush(u);return ye(c)?c.then((function(r){return t.flushRemux(l,r,e),l})):(this.flushRemux(l,c,e),l)},t.flushRemux=function(e,t,r){var i=t.audioTrack,n=t.videoTrack,a=t.id3Track,s=t.textTrack,o=this.currentTransmuxState,l=o.accurateTimeOffset,u=o.timeOffset;g.logger.log("[transmuxer.ts]: Flushed fragment "+r.sn+(r.part>-1?" p: "+r.part:"")+" of level "+r.level);var d=this.remuxer.remux(i,n,a,s,u,l,!0,this.id);e.push({remuxResult:d,chunkMeta:r}),r.transmuxing.executeEnd=ve()},t.resetInitialTimestamp=function(e){var t=this.demuxer,r=this.remuxer;t&&r&&(t.resetTimeStamp(e),r.resetTimeStamp(e))},t.resetContiguity=function(){var e=this.demuxer,t=this.remuxer;e&&t&&(e.resetContiguity(),t.resetNextTimestamp())},t.resetInitSegment=function(e,t,r,i){var n=this.demuxer,a=this.remuxer;n&&a&&(n.resetInitSegment(e,t,r,i),a.resetInitSegment(e,t,r))},t.destroy=function(){this.demuxer&&(this.demuxer.destroy(),this.demuxer=void 0),this.remuxer&&(this.remuxer.destroy(),this.remuxer=void 0)},t.transmux=function(e,t,r,i,n){return t&&"SAMPLE-AES"===t.method?this.transmuxSampleAes(e,t,r,i,n):this.transmuxUnencrypted(e,r,i,n)},t.transmuxUnencrypted=function(e,t,r,i){var n=this.demuxer.demux(e,t,!1,!this.config.progressive),a=n.audioTrack,s=n.videoTrack,o=n.id3Track,l=n.textTrack;return{remuxResult:this.remuxer.remux(a,s,o,l,t,r,!1,this.id),chunkMeta:i}},t.transmuxSampleAes=function(e,t,r,i,n){var a=this;return this.demuxer.demuxSampleAes(e,t,r).then((function(e){return{remuxResult:a.remuxer.remux(e.audioTrack,e.videoTrack,e.id3Track,e.textTrack,r,i,!1,a.id),chunkMeta:n}}))},t.configureTransmuxer=function(e,t){for(var r,i=this.config,n=this.observer,a=this.typeSupported,s=this.vendor,o=t.audioCodec,l=t.defaultInitPts,u=t.duration,d=t.initSegmentData,h=t.videoCodec,f=0,c=ge.length;f<c;f++)if(ge[f].demux.probe(e)){r=ge[f];break}r||(g.logger.warn("Failed to find demuxer by probing frag, treating as mp4 passthrough"),r={demux:k,remux:ce});var v=this.demuxer,p=this.remuxer,m=r.remux,y=r.demux;p&&p instanceof m||(this.remuxer=new m(n,i,a,s)),v&&v instanceof y||(this.demuxer=new y(n,i,a),this.probe=y.probe),this.resetInitSegment(d,o,h,u),this.resetInitialTimestamp(l)},t.needsProbing=function(e,t,r){return!this.demuxer||!this.remuxer||t||r},t.getDecrypter=function(){var e=this.decrypter;return e||(e=this.decrypter=new a.default(this.observer,this.config)),e},e}(),me=function(e){return{remuxResult:{},chunkMeta:e}};function ye(e){return"then"in e&&e.then instanceof Function}var Ee=function(e,t,r,i,n){this.audioCodec=void 0,this.videoCodec=void 0,this.initSegmentData=void 0,this.duration=void 0,this.defaultInitPts=void 0,this.audioCodec=e,this.videoCodec=t,this.initSegmentData=r,this.duration=i,this.defaultInitPts=n},Te=function(e,t,r,i,n,a){this.discontinuity=void 0,this.contiguous=void 0,this.accurateTimeOffset=void 0,this.trackSwitch=void 0,this.timeOffset=void 0,this.initSegmentChange=void 0,this.discontinuity=e,this.contiguous=t,this.accurateTimeOffset=r,this.trackSwitch=i,this.timeOffset=n,this.initSegmentChange=a}},514:e=>{e.exports=void 0},973:(e,t,r)=>{"use strict";var i,n;r.r(t),r.d(t,{ErrorDetails:()=>n,ErrorTypes:()=>i}),function(e){e.NETWORK_ERROR="networkError",e.MEDIA_ERROR="mediaError",e.KEY_SYSTEM_ERROR="keySystemError",e.MUX_ERROR="muxError",e.OTHER_ERROR="otherError"}(i||(i={})),function(e){e.KEY_SYSTEM_NO_KEYS="keySystemNoKeys",e.KEY_SYSTEM_NO_ACCESS="keySystemNoAccess",e.KEY_SYSTEM_NO_SESSION="keySystemNoSession",e.KEY_SYSTEM_LICENSE_REQUEST_FAILED="keySystemLicenseRequestFailed",e.KEY_SYSTEM_NO_INIT_DATA="keySystemNoInitData",e.MANIFEST_LOAD_ERROR="manifestLoadError",e.MANIFEST_LOAD_TIMEOUT="manifestLoadTimeOut",e.MANIFEST_PARSING_ERROR="manifestParsingError",e.MANIFEST_INCOMPATIBLE_CODECS_ERROR="manifestIncompatibleCodecsError",e.LEVEL_EMPTY_ERROR="levelEmptyError",e.LEVEL_LOAD_ERROR="levelLoadError",e.LEVEL_LOAD_TIMEOUT="levelLoadTimeOut",e.LEVEL_SWITCH_ERROR="levelSwitchError",e.AUDIO_TRACK_LOAD_ERROR="audioTrackLoadError",e.AUDIO_TRACK_LOAD_TIMEOUT="audioTrackLoadTimeOut",e.SUBTITLE_LOAD_ERROR="subtitleTrackLoadError",e.SUBTITLE_TRACK_LOAD_TIMEOUT="subtitleTrackLoadTimeOut",e.FRAG_LOAD_ERROR="fragLoadError",e.FRAG_LOAD_TIMEOUT="fragLoadTimeOut",e.FRAG_DECRYPT_ERROR="fragDecryptError",e.FRAG_PARSING_ERROR="fragParsingError",e.REMUX_ALLOC_ERROR="remuxAllocError",e.KEY_LOAD_ERROR="keyLoadError",e.KEY_LOAD_TIMEOUT="keyLoadTimeOut",e.BUFFER_ADD_CODEC_ERROR="bufferAddCodecError",e.BUFFER_INCOMPATIBLE_CODECS_ERROR="bufferIncompatibleCodecsError",e.BUFFER_APPEND_ERROR="bufferAppendError",e.BUFFER_APPENDING_ERROR="bufferAppendingError",e.BUFFER_STALLED_ERROR="bufferStalledError",e.BUFFER_FULL_ERROR="bufferFullError",e.BUFFER_SEEK_OVER_HOLE="bufferSeekOverHole",e.BUFFER_NUDGE_ON_STALL="bufferNudgeOnStall",e.INTERNAL_EXCEPTION="internalException",e.INTERNAL_ABORTED="aborted",e.UNKNOWN="unknown"}(n||(n={}))},851:(e,t,r)=>{"use strict";var i;r.r(t),r.d(t,{Events:()=>i}),function(e){e.MEDIA_ATTACHING="hlsMediaAttaching",e.MEDIA_ATTACHED="hlsMediaAttached",e.MEDIA_DETACHING="hlsMediaDetaching",e.MEDIA_DETACHED="hlsMediaDetached",e.BUFFER_RESET="hlsBufferReset",e.BUFFER_CODECS="hlsBufferCodecs",e.BUFFER_CREATED="hlsBufferCreated",e.BUFFER_APPENDING="hlsBufferAppending",e.BUFFER_APPENDED="hlsBufferAppended",e.BUFFER_EOS="hlsBufferEos",e.BUFFER_FLUSHING="hlsBufferFlushing",e.BUFFER_FLUSHED="hlsBufferFlushed",e.MANIFEST_LOADING="hlsManifestLoading",e.MANIFEST_LOADED="hlsManifestLoaded",e.MANIFEST_PARSED="hlsManifestParsed",e.LEVEL_SWITCHING="hlsLevelSwitching",e.LEVEL_SWITCHED="hlsLevelSwitched",e.LEVEL_LOADING="hlsLevelLoading",e.LEVEL_LOADED="hlsLevelLoaded",e.LEVEL_UPDATED="hlsLevelUpdated",e.LEVEL_PTS_UPDATED="hlsLevelPtsUpdated",e.LEVELS_UPDATED="hlsLevelsUpdated",e.AUDIO_TRACKS_UPDATED="hlsAudioTracksUpdated",e.AUDIO_TRACK_SWITCHING="hlsAudioTrackSwitching",e.AUDIO_TRACK_SWITCHED="hlsAudioTrackSwitched",e.AUDIO_TRACK_LOADING="hlsAudioTrackLoading",e.AUDIO_TRACK_LOADED="hlsAudioTrackLoaded",e.SUBTITLE_TRACKS_UPDATED="hlsSubtitleTracksUpdated",e.SUBTITLE_TRACKS_CLEARED="hlsSubtitleTracksCleared",e.SUBTITLE_TRACK_SWITCH="hlsSubtitleTrackSwitch",e.SUBTITLE_TRACK_LOADING="hlsSubtitleTrackLoading",e.SUBTITLE_TRACK_LOADED="hlsSubtitleTrackLoaded",e.SUBTITLE_FRAG_PROCESSED="hlsSubtitleFragProcessed",e.CUES_PARSED="hlsCuesParsed",e.NON_NATIVE_TEXT_TRACKS_FOUND="hlsNonNativeTextTracksFound",e.INIT_PTS_FOUND="hlsInitPtsFound",e.FRAG_LOADING="hlsFragLoading",e.FRAG_LOAD_EMERGENCY_ABORTED="hlsFragLoadEmergencyAborted",e.FRAG_LOADED="hlsFragLoaded",e.FRAG_DECRYPTED="hlsFragDecrypted",e.FRAG_PARSING_INIT_SEGMENT="hlsFragParsingInitSegment",e.FRAG_PARSING_USERDATA="hlsFragParsingUserdata",e.FRAG_PARSING_METADATA="hlsFragParsingMetadata",e.FRAG_PARSED="hlsFragParsed",e.FRAG_BUFFERED="hlsFragBuffered",e.FRAG_CHANGED="hlsFragChanged",e.FPS_DROP="hlsFpsDrop",e.FPS_DROP_LEVEL_CAPPING="hlsFpsDropLevelCapping",e.ERROR="hlsError",e.DESTROYING="hlsDestroying",e.KEY_LOADING="hlsKeyLoading",e.KEY_LOADED="hlsKeyLoaded",e.LIVE_BACK_BUFFER_REACHED="hlsLiveBackBufferReached",e.BACK_BUFFER_REACHED="hlsBackBufferReached"}(i||(i={}))},392:(e,t,r)=>{"use strict";r.r(t),r.d(t,{default:()=>Ht});var i,n=r(945),a=r(965),s=r(851),o=r(973),l=r(93),u=r(63),d=/^(\d+)x(\d+)$/,h=/\s*(.+?)\s*=((?:\".*?\")|.*?)(?:,|$)/g,f=function(){function e(t){for(var r in"string"==typeof t&&(t=e.parseAttrList(t)),t)t.hasOwnProperty(r)&&(this[r]=t[r])}var t=e.prototype;return t.decimalInteger=function(e){var t=parseInt(this[e],10);return t>Number.MAX_SAFE_INTEGER?1/0:t},t.hexadecimalInteger=function(e){if(this[e]){var t=(this[e]||"0x").slice(2);t=(1&t.length?"0":"")+t;for(var r=new Uint8Array(t.length/2),i=0;i<t.length/2;i++)r[i]=parseInt(t.slice(2*i,2*i+2),16);return r}return null},t.hexadecimalIntegerAsNumber=function(e){var t=parseInt(this[e],16);return t>Number.MAX_SAFE_INTEGER?1/0:t},t.decimalFloatingPoint=function(e){return parseFloat(this[e])},t.optionalFloat=function(e,t){var r=this[e];return r?parseFloat(r):t},t.enumeratedString=function(e){return this[e]},t.bool=function(e){return"YES"===this[e]},t.decimalResolution=function(e){var t=d.exec(this[e]);if(null!==t)return{width:parseInt(t[1],10),height:parseInt(t[2],10)}},e.parseAttrList=function(e){var t,r={};for(h.lastIndex=0;null!==(t=h.exec(e));){var i=t[2];0===i.indexOf('"')&&i.lastIndexOf('"')===i.length-1&&(i=i.slice(1,-1)),r[t[1]]=i}return r},e}();function c(){return c=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var i in r)Object.prototype.hasOwnProperty.call(r,i)&&(e[i]=r[i])}return e},c.apply(this,arguments)}function v(e,t){for(var r=0;r<t.length;r++){var i=t[r];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}!function(e){e.ID="ID",e.CLASS="CLASS",e.START_DATE="START-DATE",e.DURATION="DURATION",e.END_DATE="END-DATE",e.END_ON_NEXT="END-ON-NEXT",e.PLANNED_DURATION="PLANNED-DURATION",e.SCTE35_OUT="SCTE35-OUT",e.SCTE35_IN="SCTE35-IN"}(i||(i={}));var g=function(){function e(e,t){if(this.attr=void 0,this._startDate=void 0,this._endDate=void 0,this._badValueForSameId=void 0,t){var r=t.attr;for(var n in r)if(Object.prototype.hasOwnProperty.call(e,n)&&e[n]!==r[n]){l.logger.warn('DATERANGE tag attribute: "'+n+'" does not match for tags with ID: "'+e.ID+'"'),this._badValueForSameId=n;break}e=c(new f({}),r,e)}if(this.attr=e,this._startDate=new Date(e[i.START_DATE]),i.END_DATE in this.attr){var s=new Date(this.attr[i.END_DATE]);(0,a.isFiniteNumber)(s.getTime())&&(this._endDate=s)}}var t,r;return t=e,(r=[{key:"id",get:function(){return this.attr.ID}},{key:"class",get:function(){return this.attr.CLASS}},{key:"startDate",get:function(){return this._startDate}},{key:"endDate",get:function(){if(this._endDate)return this._endDate;var e=this.duration;return null!==e?new Date(this._startDate.getTime()+1e3*e):null}},{key:"duration",get:function(){if(i.DURATION in this.attr){var e=this.attr.decimalFloatingPoint(i.DURATION);if((0,a.isFiniteNumber)(e))return e}else if(this._endDate)return(this._endDate.getTime()-this._startDate.getTime())/1e3;return null}},{key:"plannedDuration",get:function(){return i.PLANNED_DURATION in this.attr?this.attr.decimalFloatingPoint(i.PLANNED_DURATION):null}},{key:"endOnNext",get:function(){return this.attr.bool(i.END_ON_NEXT)}},{key:"isValid",get:function(){return!!this.id&&!this._badValueForSameId&&(0,a.isFiniteNumber)(this.startDate.getTime())&&(null===this.duration||this.duration>=0)&&(!this.endOnNext||!!this.class)}}])&&v(t.prototype,r),Object.defineProperty(t,"prototype",{writable:!1}),e}(),p=r(923);function m(e,t){for(var r=0;r<t.length;r++){var i=t[r];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}var y=function(){function e(e){this.PTSKnown=!1,this.alignedSliding=!1,this.averagetargetduration=void 0,this.endCC=0,this.endSN=0,this.fragments=void 0,this.fragmentHint=void 0,this.partList=null,this.dateRanges=void 0,this.live=!0,this.ageHeader=0,this.advancedDateTime=void 0,this.updated=!0,this.advanced=!0,this.availabilityDelay=void 0,this.misses=0,this.needSidxRanges=!1,this.startCC=0,this.startSN=0,this.startTimeOffset=null,this.targetduration=0,this.totalduration=0,this.type=null,this.url=void 0,this.m3u8="",this.version=null,this.canBlockReload=!1,this.canSkipUntil=0,this.canSkipDateRanges=!1,this.skippedSegments=0,this.recentlyRemovedDateranges=void 0,this.partHoldBack=0,this.holdBack=0,this.partTarget=0,this.preloadHint=void 0,this.renditionReports=void 0,this.tuneInGoal=0,this.deltaUpdateFailed=void 0,this.driftStartTime=0,this.driftEndTime=0,this.driftStart=0,this.driftEnd=0,this.fragments=[],this.dateRanges={},this.url=e}var t,r;return e.prototype.reloaded=function(e){if(!e)return this.advanced=!0,void(this.updated=!0);var t=this.lastPartSn-e.lastPartSn,r=this.lastPartIndex-e.lastPartIndex;this.updated=this.endSN!==e.endSN||!!r||!!t,this.advanced=this.endSN>e.endSN||t>0||0===t&&r>0,this.updated||this.advanced?this.misses=Math.floor(.6*e.misses):this.misses=e.misses+1,this.availabilityDelay=e.availabilityDelay},t=e,(r=[{key:"hasProgramDateTime",get:function(){return!!this.fragments.length&&(0,a.isFiniteNumber)(this.fragments[this.fragments.length-1].programDateTime)}},{key:"levelTargetDuration",get:function(){return this.averagetargetduration||this.targetduration||10}},{key:"drift",get:function(){var e=this.driftEndTime-this.driftStartTime;return e>0?1e3*(this.driftEnd-this.driftStart)/e:1}},{key:"edge",get:function(){return this.partEnd||this.fragmentEnd}},{key:"partEnd",get:function(){var e;return null!==(e=this.partList)&&void 0!==e&&e.length?this.partList[this.partList.length-1].end:this.fragmentEnd}},{key:"fragmentEnd",get:function(){var e;return null!==(e=this.fragments)&&void 0!==e&&e.length?this.fragments[this.fragments.length-1].end:0}},{key:"age",get:function(){return this.advancedDateTime?Math.max(Date.now()-this.advancedDateTime,0)/1e3:0}},{key:"lastPartIndex",get:function(){var e;return null!==(e=this.partList)&&void 0!==e&&e.length?this.partList[this.partList.length-1].index:-1}},{key:"lastPartSn",get:function(){var e;return null!==(e=this.partList)&&void 0!==e&&e.length?this.partList[this.partList.length-1].fragment.sn:this.endSN}}])&&m(t.prototype,r),Object.defineProperty(t,"prototype",{writable:!1}),e}(),E=r(960),T={audio:{a3ds:!0,"ac-3":!0,"ac-4":!0,alac:!0,alaw:!0,dra1:!0,"dts+":!0,"dts-":!0,dtsc:!0,dtse:!0,dtsh:!0,"ec-3":!0,enca:!0,g719:!0,g726:!0,m4ae:!0,mha1:!0,mha2:!0,mhm1:!0,mhm2:!0,mlpa:!0,mp4a:!0,"raw ":!0,Opus:!0,opus:!0,samr:!0,sawb:!0,sawp:!0,sevc:!0,sqcp:!0,ssmv:!0,twos:!0,ulaw:!0},video:{avc1:!0,avc2:!0,avc3:!0,avc4:!0,avcp:!0,av01:!0,drac:!0,dva1:!0,dvav:!0,dvh1:!0,dvhe:!0,encv:!0,hev1:!0,hvc1:!0,mjp2:!0,mp4v:!0,mvc1:!0,mvc2:!0,mvc3:!0,mvc4:!0,resv:!0,rv60:!0,s263:!0,svc1:!0,svc2:!0,"vc-1":!0,vp08:!0,vp09:!0},text:{stpp:!0,wvtt:!0}};function S(e,t){return MediaSource.isTypeSupported((t||"video")+'/mp4;codecs="'+e+'"')}var b=/#EXT-X-STREAM-INF:([^\r\n]*)(?:[\r\n](?:#[^\r\n]*)?)*([^\r\n]+)|#EXT-X-SESSION-DATA:([^\r\n]*)[\r\n]+/g,L=/#EXT-X-MEDIA:(.*)/g,A=new RegExp([/#EXTINF:\s*(\d*(?:\.\d+)?)(?:,(.*)\s+)?/.source,/(?!#) *(\S[\S ]*)/.source,/#EXT-X-BYTERANGE:*(.+)/.source,/#EXT-X-PROGRAM-DATE-TIME:(.+)/.source,/#.*/.source].join("|"),"g"),D=new RegExp([/#(EXTM3U)/.source,/#EXT-X-(DATERANGE|KEY|MAP|PART|PART-INF|PLAYLIST-TYPE|PRELOAD-HINT|RENDITION-REPORT|SERVER-CONTROL|SKIP|START):(.+)/.source,/#EXT-X-(BITRATE|DISCONTINUITY-SEQUENCE|MEDIA-SEQUENCE|TARGETDURATION|VERSION): *(\d+)/.source,/#EXT-X-(DISCONTINUITY|ENDLIST|GAP)/.source,/(#)([^:]*):(.*)/.source,/(#)(.*)(?:.*)\r?\n?/.source].join("|")),R=/\.(mp4|m4s|m4v|m4a)$/i,k=function(){function e(){}return e.findGroup=function(e,t){for(var r=0;r<e.length;r++){var i=e[r];if(i.id===t)return i}},e.convertAVC1ToAVCOTI=function(e){var t=e.split(".");if(t.length>2){var r=t.shift()+".";return(r+=parseInt(t.shift()).toString(16))+("000"+parseInt(t.shift()).toString(16)).slice(-4)}return e},e.resolve=function(e,t){return n.buildAbsoluteURL(t,e,{alwaysNormalize:!0})},e.parseMasterPlaylist=function(t,r){var i,n=[],a=[],s={},o=!1;for(b.lastIndex=0;null!=(i=b.exec(t));)if(i[1]){var l,u=new f(i[1]),d={attrs:u,bitrate:u.decimalInteger("AVERAGE-BANDWIDTH")||u.decimalInteger("BANDWIDTH"),name:u.NAME,url:e.resolve(i[2],r)},h=u.decimalResolution("RESOLUTION");h&&(d.width=h.width,d.height=h.height),_((u.CODECS||"").split(/[ ,]+/).filter((function(e){return e})),d),d.videoCodec&&-1!==d.videoCodec.indexOf("avc1")&&(d.videoCodec=e.convertAVC1ToAVCOTI(d.videoCodec)),null!==(l=d.unknownCodecs)&&void 0!==l&&l.length||a.push(d),n.push(d)}else if(i[3]){var c=new f(i[3]);c["DATA-ID"]&&(o=!0,s[c["DATA-ID"]]=c)}return{levels:a.length>0&&a.length<n.length?a:n,sessionData:o?s:null}},e.parseMasterPlaylistMedia=function(t,r,i,n){var a;void 0===n&&(n=[]);var s=[],o=0;for(L.lastIndex=0;null!==(a=L.exec(t));){var l=new f(a[1]);if(l.TYPE===i){var u={attrs:l,bitrate:0,id:o++,groupId:l["GROUP-ID"],instreamId:l["INSTREAM-ID"],name:l.NAME||l.LANGUAGE||"",type:i,default:l.bool("DEFAULT"),autoselect:l.bool("AUTOSELECT"),forced:l.bool("FORCED"),lang:l.LANGUAGE,url:l.URI?e.resolve(l.URI,r):""};if(n.length){var d=e.findGroup(n,u.groupId)||n[0];x(u,d,"audioCodec"),x(u,d,"textCodec")}s.push(u)}}return s},e.parseLevelPlaylist=function(e,t,r,i,s){var o,u,d,h=new y(t),c=h.fragments,v=null,m=0,T=0,S=0,b=0,L=null,k=new p.Fragment(i,t),_=-1,x=!1;for(A.lastIndex=0,h.m3u8=e;null!==(o=A.exec(e));){x&&(x=!1,(k=new p.Fragment(i,t)).start=S,k.sn=m,k.cc=b,k.level=r,v&&(k.initSegment=v,k.rawProgramDateTime=v.rawProgramDateTime,v.rawProgramDateTime=null));var C=o[1];if(C){k.duration=parseFloat(C);var P=(" "+o[2]).slice(1);k.title=P||null,k.tagList.push(P?["INF",C,P]:["INF",C])}else if(o[3])(0,a.isFiniteNumber)(k.duration)&&(k.start=S,d&&(k.levelkey=d),k.sn=m,k.level=r,k.cc=b,k.urlId=s,c.push(k),k.relurl=(" "+o[3]).slice(1),I(k,L),L=k,S+=k.duration,m++,T=0,x=!0);else if(o[4]){var O=(" "+o[4]).slice(1);L?k.setByteRange(O,L):k.setByteRange(O)}else if(o[5])k.rawProgramDateTime=(" "+o[5]).slice(1),k.tagList.push(["PROGRAM-DATE-TIME",k.rawProgramDateTime]),-1===_&&(_=c.length);else{if(!(o=o[0].match(D))){l.logger.warn("No matches on slow regex match for level playlist!");continue}for(u=1;u<o.length&&void 0===o[u];u++);var F=(" "+o[u]).slice(1),M=(" "+o[u+1]).slice(1),N=o[u+2]?(" "+o[u+2]).slice(1):"";switch(F){case"PLAYLIST-TYPE":h.type=M.toUpperCase();break;case"MEDIA-SEQUENCE":m=h.startSN=parseInt(M);break;case"SKIP":var U=new f(M),B=U.decimalInteger("SKIPPED-SEGMENTS");if((0,a.isFiniteNumber)(B)){h.skippedSegments=B;for(var G=B;G--;)c.unshift(null);m+=B}var V=U.enumeratedString("RECENTLY-REMOVED-DATERANGES");V&&(h.recentlyRemovedDateranges=V.split("\t"));break;case"TARGETDURATION":h.targetduration=parseFloat(M);break;case"VERSION":h.version=parseInt(M);break;case"EXTM3U":break;case"ENDLIST":h.live=!1;break;case"#":(M||N)&&k.tagList.push(N?[M,N]:[M]);break;case"DISCONTINUITY":b++,k.tagList.push(["DIS"]);break;case"GAP":k.tagList.push([F]);break;case"BITRATE":k.tagList.push([F,M]);break;case"DATERANGE":var H=new f(M),j=new g(H,h.dateRanges[H.ID]);j.isValid||h.skippedSegments?h.dateRanges[j.id]=j:l.logger.warn('Ignoring invalid DATERANGE tag: "'+M+'"'),k.tagList.push(["EXT-X-DATERANGE",M]);break;case"DISCONTINUITY-SEQUENCE":b=parseInt(M);break;case"KEY":var K,W=new f(M),Y=W.enumeratedString("METHOD"),q=W.URI,z=W.hexadecimalInteger("IV"),X=W.enumeratedString("KEYFORMATVERSIONS"),Q=W.enumeratedString("KEYID"),$=null!=(K=W.enumeratedString("KEYFORMAT"))?K:"identity";if(["com.apple.streamingkeydelivery","com.microsoft.playready","urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed","com.widevine"].indexOf($)>-1){l.logger.warn("Keyformat "+$+" is not supported from the manifest");continue}if("identity"!==$)continue;Y&&(d=E.LevelKey.fromURL(t,q),q&&["AES-128","SAMPLE-AES","SAMPLE-AES-CENC"].indexOf(Y)>=0&&(d.method=Y,d.keyFormat=$,Q&&(d.keyID=Q),X&&(d.keyFormatVersions=X),d.iv=z));break;case"START":var Z=new f(M).decimalFloatingPoint("TIME-OFFSET");(0,a.isFiniteNumber)(Z)&&(h.startTimeOffset=Z);break;case"MAP":var J=new f(M);if(k.duration){var ee=new p.Fragment(i,t);w(ee,J,r,d),v=ee,k.initSegment=v,v.rawProgramDateTime&&!k.rawProgramDateTime&&(k.rawProgramDateTime=v.rawProgramDateTime)}else w(k,J,r,d),v=k,x=!0;break;case"SERVER-CONTROL":var te=new f(M);h.canBlockReload=te.bool("CAN-BLOCK-RELOAD"),h.canSkipUntil=te.optionalFloat("CAN-SKIP-UNTIL",0),h.canSkipDateRanges=h.canSkipUntil>0&&te.bool("CAN-SKIP-DATERANGES"),h.partHoldBack=te.optionalFloat("PART-HOLD-BACK",0),h.holdBack=te.optionalFloat("HOLD-BACK",0);break;case"PART-INF":var re=new f(M);h.partTarget=re.decimalFloatingPoint("PART-TARGET");break;case"PART":var ie=h.partList;ie||(ie=h.partList=[]);var ne=T>0?ie[ie.length-1]:void 0,ae=T++,se=new p.Part(new f(M),k,t,ae,ne);ie.push(se),k.duration+=se.duration;break;case"PRELOAD-HINT":var oe=new f(M);h.preloadHint=oe;break;case"RENDITION-REPORT":var le=new f(M);h.renditionReports=h.renditionReports||[],h.renditionReports.push(le);break;default:l.logger.warn("line parsed but not handled: "+o)}}}L&&!L.relurl?(c.pop(),S-=L.duration,h.partList&&(h.fragmentHint=L)):h.partList&&(I(k,L),k.cc=b,h.fragmentHint=k);var ue=c.length,de=c[0],he=c[ue-1];if((S+=h.skippedSegments*h.targetduration)>0&&ue&&he){h.averagetargetduration=S/ue;var fe=he.sn;h.endSN="initSegment"!==fe?fe:0,de&&(h.startCC=de.cc,de.initSegment||h.fragments.every((function(e){return e.relurl&&(t=e.relurl,R.test(null!=(r=null===(i=n.parseURL(t))||void 0===i?void 0:i.path)?r:""));var t,r,i}))&&(l.logger.warn("MP4 fragments found but no init segment (probably no MAP, incomplete M3U8), trying to fetch SIDX"),(k=new p.Fragment(i,t)).relurl=he.relurl,k.level=r,k.sn="initSegment",de.initSegment=k,h.needSidxRanges=!0))}else h.endSN=0,h.startCC=0;return h.fragmentHint&&(S+=h.fragmentHint.duration),h.totalduration=S,h.endCC=b,_>0&&function(e,t){for(var r=e[t],i=t;i--;){var n=e[i];if(!n)return;n.programDateTime=r.programDateTime-1e3*n.duration,r=n}}(c,_),h},e}();function _(e,t){["video","audio","text"].forEach((function(r){var i=e.filter((function(e){return function(e,t){var r=T[t];return!!r&&!0===r[e.slice(0,4)]}(e,r)}));if(i.length){var n=i.filter((function(e){return 0===e.lastIndexOf("avc1",0)||0===e.lastIndexOf("mp4a",0)}));t[r+"Codec"]=n.length>0?n[0]:i[0],e=e.filter((function(e){return-1===i.indexOf(e)}))}})),t.unknownCodecs=e}function x(e,t,r){var i=t[r];i&&(e[r]=i)}function I(e,t){e.rawProgramDateTime?e.programDateTime=Date.parse(e.rawProgramDateTime):null!=t&&t.programDateTime&&(e.programDateTime=t.endProgramDateTime),(0,a.isFiniteNumber)(e.programDateTime)||(e.programDateTime=null,e.rawProgramDateTime=null)}function w(e,t,r,i){e.relurl=t.URI,t.BYTERANGE&&e.setByteRange(t.BYTERANGE),e.level=r,e.sn="initSegment",i&&(e.levelkey=i),e.initSegment=null}var C=r(308);function P(e,t){var r=e.url;return void 0!==r&&0!==r.indexOf("data:")||(r=t.url),r}const O=function(){function e(e){this.hls=void 0,this.loaders=Object.create(null),this.hls=e,this.registerListeners()}var t=e.prototype;return t.startLoad=function(e){},t.stopLoad=function(){this.destroyInternalLoaders()},t.registerListeners=function(){var e=this.hls;e.on(s.Events.MANIFEST_LOADING,this.onManifestLoading,this),e.on(s.Events.LEVEL_LOADING,this.onLevelLoading,this),e.on(s.Events.AUDIO_TRACK_LOADING,this.onAudioTrackLoading,this),e.on(s.Events.SUBTITLE_TRACK_LOADING,this.onSubtitleTrackLoading,this)},t.unregisterListeners=function(){var e=this.hls;e.off(s.Events.MANIFEST_LOADING,this.onManifestLoading,this),e.off(s.Events.LEVEL_LOADING,this.onLevelLoading,this),e.off(s.Events.AUDIO_TRACK_LOADING,this.onAudioTrackLoading,this),e.off(s.Events.SUBTITLE_TRACK_LOADING,this.onSubtitleTrackLoading,this)},t.createInternalLoader=function(e){var t=this.hls.config,r=t.pLoader,i=t.loader,n=new(r||i)(t);return e.loader=n,this.loaders[e.type]=n,n},t.getInternalLoader=function(e){return this.loaders[e.type]},t.resetInternalLoader=function(e){this.loaders[e]&&delete this.loaders[e]},t.destroyInternalLoaders=function(){for(var e in this.loaders){var t=this.loaders[e];t&&t.destroy(),this.resetInternalLoader(e)}},t.destroy=function(){this.unregisterListeners(),this.destroyInternalLoaders()},t.onManifestLoading=function(e,t){var r=t.url;this.load({id:null,groupId:null,level:0,responseType:"text",type:C.PlaylistContextType.MANIFEST,url:r,deliveryDirectives:null})},t.onLevelLoading=function(e,t){var r=t.id,i=t.level,n=t.url,a=t.deliveryDirectives;this.load({id:r,groupId:null,level:i,responseType:"text",type:C.PlaylistContextType.LEVEL,url:n,deliveryDirectives:a})},t.onAudioTrackLoading=function(e,t){var r=t.id,i=t.groupId,n=t.url,a=t.deliveryDirectives;this.load({id:r,groupId:i,level:null,responseType:"text",type:C.PlaylistContextType.AUDIO_TRACK,url:n,deliveryDirectives:a})},t.onSubtitleTrackLoading=function(e,t){var r=t.id,i=t.groupId,n=t.url,a=t.deliveryDirectives;this.load({id:r,groupId:i,level:null,responseType:"text",type:C.PlaylistContextType.SUBTITLE_TRACK,url:n,deliveryDirectives:a})},t.load=function(e){var t,r,i,n,a,s,o=this.hls.config,u=this.getInternalLoader(e);if(u){var d=u.context;if(d&&d.url===e.url)return void l.logger.trace("[playlist-loader]: playlist request ongoing");l.logger.log("[playlist-loader]: aborting previous loader for type: "+e.type),u.abort()}switch(e.type){case C.PlaylistContextType.MANIFEST:r=o.manifestLoadingMaxRetry,i=o.manifestLoadingTimeOut,n=o.manifestLoadingRetryDelay,a=o.manifestLoadingMaxRetryTimeout;break;case C.PlaylistContextType.LEVEL:case C.PlaylistContextType.AUDIO_TRACK:case C.PlaylistContextType.SUBTITLE_TRACK:r=0,i=o.levelLoadingTimeOut;break;default:r=o.levelLoadingMaxRetry,i=o.levelLoadingTimeOut,n=o.levelLoadingRetryDelay,a=o.levelLoadingMaxRetryTimeout}if(u=this.createInternalLoader(e),null!==(t=e.deliveryDirectives)&&void 0!==t&&t.part&&(e.type===C.PlaylistContextType.LEVEL&&null!==e.level?s=this.hls.levels[e.level].details:e.type===C.PlaylistContextType.AUDIO_TRACK&&null!==e.id?s=this.hls.audioTracks[e.id].details:e.type===C.PlaylistContextType.SUBTITLE_TRACK&&null!==e.id&&(s=this.hls.subtitleTracks[e.id].details),s)){var h=s.partTarget,f=s.targetduration;h&&f&&(i=Math.min(1e3*Math.max(3*h,.8*f),i))}var c={timeout:i,maxRetry:r,retryDelay:n,maxRetryDelay:a,highWaterMark:0},v={onSuccess:this.loadsuccess.bind(this),onError:this.loaderror.bind(this),onTimeout:this.loadtimeout.bind(this)};u.load(e,c,v)},t.loadsuccess=function(e,t,r,i){if(void 0===i&&(i=null),r.isSidxRequest)return this.handleSidxRequest(e,r),void this.handlePlaylistLoaded(e,t,r,i);this.resetInternalLoader(r.type);var n=e.data;0===n.indexOf("#EXTM3U")?(t.parsing.start=performance.now(),n.indexOf("#EXTINF:")>0||n.indexOf("#EXT-X-TARGETDURATION:")>0?this.handleTrackOrLevelPlaylist(e,t,r,i):this.handleMasterPlaylist(e,t,r,i)):this.handleManifestParsingError(e,r,"no EXTM3U delimiter",i)},t.loaderror=function(e,t,r){void 0===r&&(r=null),this.handleNetworkError(t,r,!1,e)},t.loadtimeout=function(e,t,r){void 0===r&&(r=null),this.handleNetworkError(t,r,!0)},t.handleMasterPlaylist=function(e,t,r,i){var n=this.hls,a=e.data,o=P(e,r),u=k.parseMasterPlaylist(a,o),d=u.levels,h=u.sessionData;if(d.length){var c=d.map((function(e){return{id:e.attrs.AUDIO,audioCodec:e.audioCodec}})),v=d.map((function(e){return{id:e.attrs.SUBTITLES,textCodec:e.textCodec}})),g=k.parseMasterPlaylistMedia(a,o,"AUDIO",c),p=k.parseMasterPlaylistMedia(a,o,"SUBTITLES",v),m=k.parseMasterPlaylistMedia(a,o,"CLOSED-CAPTIONS");g.length&&(g.some((function(e){return!e.url}))||!d[0].audioCodec||d[0].attrs.AUDIO||(l.logger.log("[playlist-loader]: audio codec signaled in quality level, but no embedded audio track signaled, create one"),g.unshift({type:"main",name:"main",default:!1,autoselect:!1,forced:!1,id:-1,attrs:new f({}),bitrate:0,url:""}))),n.trigger(s.Events.MANIFEST_LOADED,{levels:d,audioTracks:g,subtitles:p,captions:m,url:o,stats:t,networkDetails:i,sessionData:h})}else this.handleManifestParsingError(e,r,"no level found in manifest",i)},t.handleTrackOrLevelPlaylist=function(e,t,r,i){var n=this.hls,l=r.id,u=r.level,d=r.type,h=P(e,r),c=(0,a.isFiniteNumber)(l)?l:0,v=(0,a.isFiniteNumber)(u)?u:c,g=function(e){switch(e.type){case C.PlaylistContextType.AUDIO_TRACK:return C.PlaylistLevelType.AUDIO;case C.PlaylistContextType.SUBTITLE_TRACK:return C.PlaylistLevelType.SUBTITLE;default:return C.PlaylistLevelType.MAIN}}(r),p=k.parseLevelPlaylist(e.data,h,v,g,c);if(p.fragments.length){if(d===C.PlaylistContextType.MANIFEST){var m={attrs:new f({}),bitrate:0,details:p,name:"",url:h};n.trigger(s.Events.MANIFEST_LOADED,{levels:[m],audioTracks:[],url:h,stats:t,networkDetails:i,sessionData:null})}if(t.parsing.end=performance.now(),p.needSidxRanges){var y,E=null===(y=p.fragments[0].initSegment)||void 0===y?void 0:y.url;this.load({url:E,isSidxRequest:!0,type:d,level:u,levelDetails:p,id:l,groupId:null,rangeStart:0,rangeEnd:2048,responseType:"arraybuffer",deliveryDirectives:null})}else r.levelDetails=p,this.handlePlaylistLoaded(e,t,r,i)}else n.trigger(s.Events.ERROR,{type:o.ErrorTypes.NETWORK_ERROR,details:o.ErrorDetails.LEVEL_EMPTY_ERROR,fatal:!1,url:h,reason:"no fragments found in level",level:"number"==typeof r.level?r.level:void 0})},t.handleSidxRequest=function(e,t){var r=new Uint8Array(e.data),i=(0,u.findBox)(r,["sidx"])[0];if(i){var n=(0,u.parseSegmentIndex)(i);if(n){var a=n.references,s=t.levelDetails;a.forEach((function(e,t){var i=e.info,n=s.fragments[t];if(0===n.byteRange.length&&n.setByteRange(String(1+i.end-i.start)+"@"+String(i.start)),n.initSegment){var a=(0,u.findBox)(r,["moov"])[0],o=a?a.length:null;n.initSegment.setByteRange(String(o)+"@0")}}))}}},t.handleManifestParsingError=function(e,t,r,i){this.hls.trigger(s.Events.ERROR,{type:o.ErrorTypes.NETWORK_ERROR,details:o.ErrorDetails.MANIFEST_PARSING_ERROR,fatal:t.type===C.PlaylistContextType.MANIFEST,url:e.url,reason:r,response:e,context:t,networkDetails:i})},t.handleNetworkError=function(e,t,r,i){void 0===r&&(r=!1),l.logger.warn("[playlist-loader]: A network "+(r?"timeout":"error")+" occurred while loading "+e.type+" level: "+e.level+" id: "+e.id+' group-id: "'+e.groupId+'"');var n=o.ErrorDetails.UNKNOWN,a=!1,u=this.getInternalLoader(e);switch(e.type){case C.PlaylistContextType.MANIFEST:n=r?o.ErrorDetails.MANIFEST_LOAD_TIMEOUT:o.ErrorDetails.MANIFEST_LOAD_ERROR,a=!0;break;case C.PlaylistContextType.LEVEL:n=r?o.ErrorDetails.LEVEL_LOAD_TIMEOUT:o.ErrorDetails.LEVEL_LOAD_ERROR,a=!1;break;case C.PlaylistContextType.AUDIO_TRACK:n=r?o.ErrorDetails.AUDIO_TRACK_LOAD_TIMEOUT:o.ErrorDetails.AUDIO_TRACK_LOAD_ERROR,a=!1;break;case C.PlaylistContextType.SUBTITLE_TRACK:n=r?o.ErrorDetails.SUBTITLE_TRACK_LOAD_TIMEOUT:o.ErrorDetails.SUBTITLE_LOAD_ERROR,a=!1}u&&this.resetInternalLoader(e.type);var d={type:o.ErrorTypes.NETWORK_ERROR,details:n,fatal:a,url:e.url,loader:u,context:e,networkDetails:t};i&&(d.response=i),this.hls.trigger(s.Events.ERROR,d)},t.handlePlaylistLoaded=function(e,t,r,i){var n=r.type,a=r.level,o=r.id,l=r.groupId,u=r.loader,d=r.levelDetails,h=r.deliveryDirectives;if(null!=d&&d.targetduration){if(u)switch(d.live&&(u.getCacheAge&&(d.ageHeader=u.getCacheAge()||0),u.getCacheAge&&!isNaN(d.ageHeader)||(d.ageHeader=0)),n){case C.PlaylistContextType.MANIFEST:case C.PlaylistContextType.LEVEL:this.hls.trigger(s.Events.LEVEL_LOADED,{details:d,level:a||0,id:o||0,stats:t,networkDetails:i,deliveryDirectives:h});break;case C.PlaylistContextType.AUDIO_TRACK:this.hls.trigger(s.Events.AUDIO_TRACK_LOADED,{details:d,id:o||0,groupId:l||"",stats:t,networkDetails:i,deliveryDirectives:h});break;case C.PlaylistContextType.SUBTITLE_TRACK:this.hls.trigger(s.Events.SUBTITLE_TRACK_LOADED,{details:d,id:o||0,groupId:l||"",stats:t,networkDetails:i,deliveryDirectives:h})}}else this.handleManifestParsingError(e,r,"invalid target duration",i)},e}();var F=function(){function e(e){this.hls=void 0,this.loaders={},this.decryptkey=null,this.decrypturl=null,this.hls=e,this.registerListeners()}var t=e.prototype;return t.startLoad=function(e){},t.stopLoad=function(){this.destroyInternalLoaders()},t.registerListeners=function(){this.hls.on(s.Events.KEY_LOADING,this.onKeyLoading,this)},t.unregisterListeners=function(){this.hls.off(s.Events.KEY_LOADING,this.onKeyLoading)},t.destroyInternalLoaders=function(){for(var e in this.loaders){var t=this.loaders[e];t&&t.destroy()}this.loaders={}},t.destroy=function(){this.unregisterListeners(),this.destroyInternalLoaders()},t.onKeyLoading=function(e,t){var r=t.frag,i=r.type,n=this.loaders[i];if(r.decryptdata){var a=r.decryptdata.uri;if(a!==this.decrypturl||null===this.decryptkey){var o=this.hls.config;if(n&&(l.logger.warn("abort previous key loader for type:"+i),n.abort()),!a)return void l.logger.warn("key uri is falsy");var u=o.loader,d=r.loader=this.loaders[i]=new u(o);this.decrypturl=a,this.decryptkey=null;var h={url:a,frag:r,responseType:"arraybuffer"},f={timeout:o.fragLoadingTimeOut,maxRetry:0,retryDelay:o.fragLoadingRetryDelay,maxRetryDelay:o.fragLoadingMaxRetryTimeout,highWaterMark:0},c={onSuccess:this.loadsuccess.bind(this),onError:this.loaderror.bind(this),onTimeout:this.loadtimeout.bind(this)};d.load(h,f,c)}else this.decryptkey&&(r.decryptdata.key=this.decryptkey,this.hls.trigger(s.Events.KEY_LOADED,{frag:r}))}else l.logger.warn("Missing decryption data on fragment in onKeyLoading")},t.loadsuccess=function(e,t,r){var i=r.frag;i.decryptdata?(this.decryptkey=i.decryptdata.key=new Uint8Array(e.data),i.loader=null,delete this.loaders[i.type],this.hls.trigger(s.Events.KEY_LOADED,{frag:i})):l.logger.error("after key load, decryptdata unset")},t.loaderror=function(e,t){var r=t.frag,i=r.loader;i&&i.abort(),delete this.loaders[r.type],this.hls.trigger(s.Events.ERROR,{type:o.ErrorTypes.NETWORK_ERROR,details:o.ErrorDetails.KEY_LOAD_ERROR,fatal:!1,frag:r,response:e})},t.loadtimeout=function(e,t){var r=t.frag,i=r.loader;i&&i.abort(),delete this.loaders[r.type],this.hls.trigger(s.Events.ERROR,{type:o.ErrorTypes.NETWORK_ERROR,details:o.ErrorDetails.KEY_LOAD_TIMEOUT,fatal:!1,frag:r})},e}();function M(e,t){var r;try{r=new Event("addtrack")}catch(e){(r=document.createEvent("Event")).initEvent("addtrack",!1,!1)}r.track=e,t.dispatchEvent(r)}var N=r(181),U=r(856);function B(){return self.WebKitDataCue||self.VTTCue||self.TextTrackCue}var G=function(){var e=B();try{new e(0,Number.POSITIVE_INFINITY,"")}catch(e){return Number.MAX_VALUE}return Number.POSITIVE_INFINITY}();function V(e,t){return e.getTime()/1e3-t}const H=function(){function e(e){this.hls=void 0,this.id3Track=null,this.media=null,this.dateRangeCuesAppended={},this.hls=e,this._registerListeners()}var t=e.prototype;return t.destroy=function(){this._unregisterListeners(),this.id3Track=null,this.media=null,this.dateRangeCuesAppended={},this.hls=null},t._registerListeners=function(){var e=this.hls;e.on(s.Events.MEDIA_ATTACHED,this.onMediaAttached,this),e.on(s.Events.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(s.Events.MANIFEST_LOADING,this.onManifestLoading,this),e.on(s.Events.FRAG_PARSING_METADATA,this.onFragParsingMetadata,this),e.on(s.Events.BUFFER_FLUSHING,this.onBufferFlushing,this),e.on(s.Events.LEVEL_UPDATED,this.onLevelUpdated,this)},t._unregisterListeners=function(){var e=this.hls;e.off(s.Events.MEDIA_ATTACHED,this.onMediaAttached,this),e.off(s.Events.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(s.Events.MANIFEST_LOADING,this.onManifestLoading,this),e.off(s.Events.FRAG_PARSING_METADATA,this.onFragParsingMetadata,this),e.off(s.Events.BUFFER_FLUSHING,this.onBufferFlushing,this),e.off(s.Events.LEVEL_UPDATED,this.onLevelUpdated,this)},t.onMediaAttached=function(e,t){this.media=t.media},t.onMediaDetaching=function(){this.id3Track&&(function(e){var t=e.mode;if("disabled"===t&&(e.mode="hidden"),e.cues)for(var r=e.cues.length;r--;)e.removeCue(e.cues[r]);"disabled"===t&&(e.mode=t)}(this.id3Track),this.id3Track=null,this.media=null,this.dateRangeCuesAppended={})},t.onManifestLoading=function(){this.dateRangeCuesAppended={}},t.createTrack=function(e){var t=this.getID3Track(e.textTracks);return t.mode="hidden",t},t.getID3Track=function(e){if(this.media){for(var t=0;t<e.length;t++){var r=e[t];if("metadata"===r.kind&&"id3"===r.label)return M(r,this.media),r}return this.media.addTextTrack("metadata","id3")}},t.onFragParsingMetadata=function(e,t){if(this.media){var r=this.hls.config,i=r.enableEmsgMetadataCues,n=r.enableID3MetadataCues;if(i||n){t.frag;var a=t.samples;t.details,this.id3Track||(this.id3Track=this.createTrack(this.media));for(var s=B(),o=0;o<a.length;o++){var l=a[o].type;if((l!==U.MetadataSchema.emsg||i)&&n){var u=N.getID3Frames(a[o].data);if(u){var d=a[o].pts,h=d+a[o].duration;h>G&&(h=G),h-d<=0&&(h=d+.25);for(var f=0;f<u.length;f++){var c=u[f];if(!N.isTimeStampFrame(c)){this.updateId3CueEnds(d);var v=new s(d,h,"");v.value=c,l&&(v.type=l),this.id3Track.addCue(v)}}}}}}}},t.updateId3CueEnds=function(e){var t,r=null===(t=this.id3Track)||void 0===t?void 0:t.cues;if(r)for(var i=r.length;i--;){var n=r[i];n.startTime<e&&n.endTime===G&&(n.endTime=e)}},t.onBufferFlushing=function(e,t){var r=t.startOffset,i=t.endOffset,n=t.type,a=this.id3Track,s=this.hls;if(s){var o=s.config,l=o.enableEmsgMetadataCues,u=o.enableID3MetadataCues;a&&(l||u)&&function(e,t,r,i){var n=e.mode;if("disabled"===n&&(e.mode="hidden"),e.cues&&e.cues.length>0)for(var a=function(e,t,r){var i=[],n=function(e,t){if(t<e[0].startTime)return 0;var r=e.length-1;if(t>e[r].endTime)return-1;for(var i=0,n=r;i<=n;){var a=Math.floor((n+i)/2);if(t<e[a].startTime)n=a-1;else{if(!(t>e[a].startTime&&i<r))return a;i=a+1}}return e[i].startTime-t<t-e[n].startTime?i:n}(e,t);if(n>-1)for(var a=n,s=e.length;a<s;a++){var o=e[a];if(o.startTime>=t&&o.endTime<=r)i.push(o);else if(o.startTime>r)return i}return i}(e.cues,t,r),s=0;s<a.length;s++)i&&!i(a[s])||e.removeCue(a[s]);"disabled"===n&&(e.mode=n)}(a,r,i,"audio"===n?function(e){return e.type===U.MetadataSchema.audioId3&&u}:"video"===n?function(e){return e.type===U.MetadataSchema.emsg&&l}:function(e){return e.type===U.MetadataSchema.audioId3&&u||e.type===U.MetadataSchema.emsg&&l})}},t.onLevelUpdated=function(e,t){var r=this,n=t.details;if(this.media&&n.hasProgramDateTime&&this.hls.config.enableDateRangeMetadataCues){var s=this.dateRangeCuesAppended,o=this.id3Track,l=n.dateRanges,u=Object.keys(l);if(o)for(var d=Object.keys(s).filter((function(e){return!u.includes(e)})),h=function(e){var t=d[e];Object.keys(s[t].cues).forEach((function(e){o.removeCue(s[t].cues[e])})),delete s[t]},f=d.length;f--;)h(f);var c=n.fragments[n.fragments.length-1];if(0!==u.length&&(0,a.isFiniteNumber)(null==c?void 0:c.programDateTime)){this.id3Track||(this.id3Track=this.createTrack(this.media));for(var v=c.programDateTime/1e3-c.start,g=B(),p=function(e){var t=u[e],n=l[t],a=s[t],o=(null==a?void 0:a.cues)||{},d=(null==a?void 0:a.durationKnown)||!1,h=V(n.startDate,v),f=G,c=n.endDate;if(c)f=V(c,v),d=!0;else if(n.endOnNext&&!d){var p=u.reduce((function(e,t){var r=l[t];return r.class===n.class&&r.id!==t&&r.startDate>n.startDate&&e.push(r),e}),[]).sort((function(e,t){return e.startDate.getTime()-t.startDate.getTime()}))[0];p&&(f=V(p.startDate,v),d=!0)}for(var m,y=Object.keys(n.attr),E=0;E<y.length;E++){var T=y[E];if(T!==i.ID&&T!==i.CLASS&&T!==i.START_DATE&&T!==i.DURATION&&T!==i.END_DATE&&T!==i.END_ON_NEXT){var S=o[T];if(S)d&&!a.durationKnown&&(S.endTime=f);else{var b=n.attr[T];S=new g(h,f,""),T!==i.SCTE35_OUT&&T!==i.SCTE35_IN||(m=b,b=Uint8Array.from(m.replace(/^0x/,"").replace(/([\da-fA-F]{2}) ?/g,"0x$1 ").replace(/ +$/,"").split(" ")).buffer),S.value={key:T,data:b},S.type=U.MetadataSchema.dateRange,r.id3Track.addCue(S),o[T]=S}}}s[t]={cues:o,dateRange:n,durationKnown:d}},m=0;m<u.length;m++)p(m)}}},e}();function j(e,t){for(var r=0;r<t.length;r++){var i=t[r];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}var K,W=function(){function e(e){var t=this;this.hls=void 0,this.config=void 0,this.media=null,this.levelDetails=null,this.currentTime=0,this.stallCount=0,this._latency=null,this.timeupdateHandler=function(){return t.timeupdate()},this.hls=e,this.config=e.config,this.registerListeners()}var t,r,i=e.prototype;return i.destroy=function(){this.unregisterListeners(),this.onMediaDetaching(),this.levelDetails=null,this.hls=this.timeupdateHandler=null},i.registerListeners=function(){this.hls.on(s.Events.MEDIA_ATTACHED,this.onMediaAttached,this),this.hls.on(s.Events.MEDIA_DETACHING,this.onMediaDetaching,this),this.hls.on(s.Events.MANIFEST_LOADING,this.onManifestLoading,this),this.hls.on(s.Events.LEVEL_UPDATED,this.onLevelUpdated,this),this.hls.on(s.Events.ERROR,this.onError,this)},i.unregisterListeners=function(){this.hls.off(s.Events.MEDIA_ATTACHED,this.onMediaAttached),this.hls.off(s.Events.MEDIA_DETACHING,this.onMediaDetaching),this.hls.off(s.Events.MANIFEST_LOADING,this.onManifestLoading),this.hls.off(s.Events.LEVEL_UPDATED,this.onLevelUpdated),this.hls.off(s.Events.ERROR,this.onError)},i.onMediaAttached=function(e,t){this.media=t.media,this.media.addEventListener("timeupdate",this.timeupdateHandler)},i.onMediaDetaching=function(){this.media&&(this.media.removeEventListener("timeupdate",this.timeupdateHandler),this.media=null)},i.onManifestLoading=function(){this.levelDetails=null,this._latency=null,this.stallCount=0},i.onLevelUpdated=function(e,t){var r=t.details;this.levelDetails=r,r.advanced&&this.timeupdate(),!r.live&&this.media&&this.media.removeEventListener("timeupdate",this.timeupdateHandler)},i.onError=function(e,t){t.details===o.ErrorDetails.BUFFER_STALLED_ERROR&&(this.stallCount++,l.logger.warn("[playback-rate-controller]: Stall detected, adjusting target latency"))},i.timeupdate=function(){var e=this.media,t=this.levelDetails;if(e&&t){this.currentTime=e.currentTime;var r=this.computeLatency();if(null!==r){this._latency=r;var i=this.config,n=i.lowLatencyMode,a=i.maxLiveSyncPlaybackRate;if(n&&1!==a){var s=this.targetLatency;if(null!==s){var o=r-s,l=o<Math.min(this.maxLatency,s+t.targetduration);if(t.live&&l&&o>.05&&this.forwardBufferLength>1){var u=Math.min(2,Math.max(1,a)),d=Math.round(2/(1+Math.exp(-.75*o-this.edgeStalled))*20)/20;e.playbackRate=Math.min(u,Math.max(1,d))}else 1!==e.playbackRate&&0!==e.playbackRate&&(e.playbackRate=1)}}}}},i.estimateLiveEdge=function(){var e=this.levelDetails;return null===e?null:e.edge+e.age},i.computeLatency=function(){var e=this.estimateLiveEdge();return null===e?null:e-this.currentTime},t=e,(r=[{key:"latency",get:function(){return this._latency||0}},{key:"maxLatency",get:function(){var e=this.config,t=this.levelDetails;return void 0!==e.liveMaxLatencyDuration?e.liveMaxLatencyDuration:t?e.liveMaxLatencyDurationCount*t.targetduration:0}},{key:"targetLatency",get:function(){var e=this.levelDetails;if(null===e)return null;var t=e.holdBack,r=e.partHoldBack,i=e.targetduration,n=this.config,a=n.liveSyncDuration,s=n.liveSyncDurationCount,o=n.lowLatencyMode,l=this.hls.userConfig,u=o&&r||t;(l.liveSyncDuration||l.liveSyncDurationCount||0===u)&&(u=void 0!==a?a:s*i);var d=i;return u+Math.min(1*this.stallCount,d)}},{key:"liveSyncPosition",get:function(){var e=this.estimateLiveEdge(),t=this.targetLatency,r=this.levelDetails;if(null===e||null===t||null===r)return null;var i=r.edge,n=e-t-this.edgeStalled,a=i-r.totalduration,s=i-(this.config.lowLatencyMode&&r.partTarget||r.targetduration);return Math.min(Math.max(a,n),s)}},{key:"drift",get:function(){var e=this.levelDetails;return null===e?1:e.drift}},{key:"edgeStalled",get:function(){var e=this.levelDetails;if(null===e)return 0;var t=3*(this.config.lowLatencyMode&&e.partTarget||e.targetduration);return Math.max(e.age-t,0)}},{key:"forwardBufferLength",get:function(){var e=this.media,t=this.levelDetails;if(!e||!t)return 0;var r=e.buffered.length;return(r?e.buffered.end(r-1):t.edge)-this.currentTime}}])&&j(t.prototype,r),Object.defineProperty(t,"prototype",{writable:!1}),e}();function Y(e,t){for(var r=0;r<t.length;r++){var i=t[r];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}!function(e){e.No="",e.Yes="YES",e.v2="v2"}(K||(K={}));var q=function(){function e(e,t,r){this.msn=void 0,this.part=void 0,this.skip=void 0,this.msn=e,this.part=t,this.skip=r}return e.prototype.addDirectives=function(e){var t=new self.URL(e);return void 0!==this.msn&&t.searchParams.set("_HLS_msn",this.msn.toString()),void 0!==this.part&&t.searchParams.set("_HLS_part",this.part.toString()),this.skip&&t.searchParams.set("_HLS_skip",this.skip),t.toString()},e}(),z=function(){function e(e){this.attrs=void 0,this.audioCodec=void 0,this.bitrate=void 0,this.codecSet=void 0,this.height=void 0,this.id=void 0,this.name=void 0,this.videoCodec=void 0,this.width=void 0,this.unknownCodecs=void 0,this.audioGroupIds=void 0,this.details=void 0,this.fragmentError=0,this.loadError=0,this.loaded=void 0,this.realBitrate=0,this.textGroupIds=void 0,this.url=void 0,this._urlId=0,this.url=[e.url],this.attrs=e.attrs,this.bitrate=e.bitrate,e.details&&(this.details=e.details),this.id=e.id||0,this.name=e.name,this.width=e.width||0,this.height=e.height||0,this.audioCodec=e.audioCodec,this.videoCodec=e.videoCodec,this.unknownCodecs=e.unknownCodecs,this.codecSet=[e.videoCodec,e.audioCodec].filter((function(e){return e})).join(",").replace(/\.[^.,]+/g,"")}var t,r;return t=e,(r=[{key:"maxBitrate",get:function(){return Math.max(this.realBitrate,this.bitrate)}},{key:"uri",get:function(){return this.url[this._urlId]||""}},{key:"urlId",get:function(){return this._urlId},set:function(e){var t=e%this.url.length;this._urlId!==t&&(this.details=void 0,this._urlId=t)}}])&&Y(t.prototype,r),Object.defineProperty(t,"prototype",{writable:!1}),e}();function X(){return X=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var i in r)Object.prototype.hasOwnProperty.call(r,i)&&(e[i]=r[i])}return e},X.apply(this,arguments)}function Q(e,t,r){switch(t){case"audio":e.audioGroupIds||(e.audioGroupIds=[]),e.audioGroupIds.push(r);break;case"text":e.textGroupIds||(e.textGroupIds=[]),e.textGroupIds.push(r)}}function $(e){var t={};e.forEach((function(e){var r=e.groupId||"";e.id=t[r]=t[r]||0,t[r]++}))}function Z(e,t){var r=t.startPTS;if((0,a.isFiniteNumber)(r)){var i,n=0;t.sn>e.sn?(n=r-e.start,i=e):(n=e.start-r,i=t),i.duration!==n&&(i.duration=n)}else t.sn>e.sn?e.cc===t.cc&&e.minEndPTS?t.start=e.start+(e.minEndPTS-e.start):t.start=e.start+e.duration:t.start=Math.max(e.start-t.duration,0)}function J(e,t,r,i,n,s){i-r<=0&&(l.logger.warn("Fragment should have a positive duration",t),i=r+t.duration,s=n+t.duration);var o=r,u=i,d=t.startPTS,h=t.endPTS;if((0,a.isFiniteNumber)(d)){var f=Math.abs(d-r);(0,a.isFiniteNumber)(t.deltaPTS)?t.deltaPTS=Math.max(f,t.deltaPTS):t.deltaPTS=f,o=Math.max(r,d),r=Math.min(r,d),n=Math.min(n,t.startDTS),u=Math.min(i,h),i=Math.max(i,h),s=Math.max(s,t.endDTS)}t.duration=i-r;var c=r-t.start;t.appendedPTS=i,t.start=t.startPTS=r,t.maxStartPTS=o,t.startDTS=n,t.endPTS=i,t.minEndPTS=u,t.endDTS=s;var v,g=t.sn;if(!e||g<e.startSN||g>e.endSN)return 0;var p=g-e.startSN,m=e.fragments;for(m[p]=t,v=p;v>0;v--)Z(m[v],m[v-1]);for(v=p;v<m.length-1;v++)Z(m[v],m[v+1]);return e.fragmentHint&&Z(m[m.length-1],e.fragmentHint),e.PTSKnown=e.alignedSliding=!0,c}function ee(e,t){var r=t.startSN+t.skippedSegments-e.startSN,i=e.fragments;r<0||r>=i.length||function(e,t){if(t){for(var r=e.fragments,i=e.skippedSegments;i<r.length;i++)r[i].start+=t;e.fragmentHint&&(e.fragmentHint.start+=t)}}(t,i[r].start)}var te=function(){function e(e,t){this.hls=void 0,this.timer=-1,this.canLoad=!1,this.retryCount=0,this.log=void 0,this.warn=void 0,this.log=l.logger.log.bind(l.logger,t+":"),this.warn=l.logger.warn.bind(l.logger,t+":"),this.hls=e}var t=e.prototype;return t.destroy=function(){this.clearTimer(),this.hls=this.log=this.warn=null},t.onError=function(e,t){t.fatal&&t.type===o.ErrorTypes.NETWORK_ERROR&&this.clearTimer()},t.clearTimer=function(){clearTimeout(this.timer),this.timer=-1},t.startLoad=function(){this.canLoad=!0,this.retryCount=0,this.loadPlaylist()},t.stopLoad=function(){this.canLoad=!1,this.clearTimer()},t.switchParams=function(e,t){var r=null==t?void 0:t.renditionReports;if(r)for(var i=0;i<r.length;i++){var n=r[i],s=""+n.URI;if(s===e.slice(-s.length)){var o=parseInt(n["LAST-MSN"]),l=parseInt(n["LAST-PART"]);if(t&&this.hls.config.lowLatencyMode){var u=Math.min(t.age-t.partTarget,t.targetduration);void 0!==l&&u>t.partTarget&&(l+=1)}if((0,a.isFiniteNumber)(o))return new q(o,(0,a.isFiniteNumber)(l)?l:void 0,K.No)}}},t.loadPlaylist=function(e){},t.shouldLoadTrack=function(e){return this.canLoad&&e&&!!e.url&&(!e.details||e.details.live)},t.playlistLoaded=function(e,t,r){var i=this,n=t.details,s=t.stats,o=s.loading.end?Math.max(0,self.performance.now()-s.loading.end):0;if(n.advancedDateTime=Date.now()-o,n.live||null!=r&&r.live){if(n.reloaded(r),r&&this.log("live playlist "+e+" "+(n.advanced?"REFRESHED "+n.lastPartSn+"-"+n.lastPartIndex:"MISSED")),r&&n.fragments.length>0&&function(e,t){for(var r=null,i=e.fragments,n=i.length-1;n>=0;n--){var s=i[n].initSegment;if(s){r=s;break}}e.fragmentHint&&delete e.fragmentHint.endPTS;var o,u,d,h,f,c=0;if(function(e,t,r){for(var i=t.skippedSegments,n=Math.max(e.startSN,t.startSN)-t.startSN,a=(e.fragmentHint?1:0)+(i?t.endSN:Math.min(e.endSN,t.endSN))-t.startSN,s=t.startSN-e.startSN,o=t.fragmentHint?t.fragments.concat(t.fragmentHint):t.fragments,l=e.fragmentHint?e.fragments.concat(e.fragmentHint):e.fragments,u=n;u<=a;u++){var d=l[s+u],h=o[u];i&&!h&&u<i&&(h=t.fragments[u]=d),d&&h&&r(d,h)}}(e,t,(function(e,i){e.relurl&&(c=e.cc-i.cc),(0,a.isFiniteNumber)(e.startPTS)&&(0,a.isFiniteNumber)(e.endPTS)&&(i.start=i.startPTS=e.startPTS,i.startDTS=e.startDTS,i.appendedPTS=e.appendedPTS,i.maxStartPTS=e.maxStartPTS,i.endPTS=e.endPTS,i.endDTS=e.endDTS,i.minEndPTS=e.minEndPTS,i.duration=e.endPTS-e.startPTS,i.duration&&(o=i),t.PTSKnown=t.alignedSliding=!0),i.elementaryStreams=e.elementaryStreams,i.loader=e.loader,i.stats=e.stats,i.urlId=e.urlId,e.initSegment&&(i.initSegment=e.initSegment,r=e.initSegment)})),r&&(t.fragmentHint?t.fragments.concat(t.fragmentHint):t.fragments).forEach((function(e){var t;e.initSegment&&e.initSegment.relurl!==(null===(t=r)||void 0===t?void 0:t.relurl)||(e.initSegment=r)})),t.skippedSegments)if(t.deltaUpdateFailed=t.fragments.some((function(e){return!e})),t.deltaUpdateFailed){l.logger.warn("[level-helper] Previous playlist missing segments skipped in delta playlist");for(var v=t.skippedSegments;v--;)t.fragments.shift();t.startSN=t.fragments[0].sn,t.startCC=t.fragments[0].cc}else t.canSkipDateRanges&&(t.dateRanges=(u=e.dateRanges,d=t.dateRanges,h=t.recentlyRemovedDateranges,f=X({},u),h&&h.forEach((function(e){delete f[e]})),Object.keys(d).forEach((function(e){var t=new g(d[e].attr,f[e]);t.isValid?f[e]=t:l.logger.warn('Ignoring invalid Playlist Delta Update DATERANGE tag: "'+JSON.stringify(d[e].attr)+'"')})),f));var p=t.fragments;if(c){l.logger.warn("discontinuity sliding from playlist, take drift into account");for(var m=0;m<p.length;m++)p[m].cc+=c}t.skippedSegments&&(t.startCC=t.fragments[0].cc),function(e,t,r){if(e&&t)for(var i=0,n=0,a=e.length;n<=a;n++){var s=e[n],o=t[n+i];s&&o&&s.index===o.index&&s.fragment.sn===o.fragment.sn?(l=s,(u=o).elementaryStreams=l.elementaryStreams,u.stats=l.stats):i--}var l,u}(e.partList,t.partList),o?J(t,o,o.startPTS,o.endPTS,o.startDTS,o.endDTS):ee(e,t),p.length&&(t.totalduration=t.edge-p[0].start),t.driftStartTime=e.driftStartTime,t.driftStart=e.driftStart;var y=t.advancedDateTime;if(t.advanced&&y){var E=t.edge;t.driftStart||(t.driftStartTime=y,t.driftStart=E),t.driftEndTime=y,t.driftEnd=E}else t.driftEndTime=e.driftEndTime,t.driftEnd=e.driftEnd,t.advancedDateTime=e.advancedDateTime}(r,n),!this.canLoad||!n.live)return;var u,d=void 0,h=void 0;if(n.canBlockReload&&n.endSN&&n.advanced){var f=this.hls.config.lowLatencyMode,c=n.lastPartSn,v=n.endSN,p=n.lastPartIndex,m=c===v;-1!==p?(d=m?v+1:c,h=m?f?0:p:p+1):d=v+1;var y=n.age,E=y+n.ageHeader,T=Math.min(E-n.partTarget,1.5*n.targetduration);if(T>0){if(r&&T>r.tuneInGoal)this.warn("CDN Tune-in goal increased from: "+r.tuneInGoal+" to: "+T+" with playlist age: "+n.age),T=0;else{var S=Math.floor(T/n.targetduration);d+=S,void 0!==h&&(h+=Math.round(T%n.targetduration/n.partTarget)),this.log("CDN Tune-in age: "+n.ageHeader+"s last advanced "+y.toFixed(2)+"s goal: "+T+" skip sn "+S+" to part "+h)}n.tuneInGoal=T}if(u=this.getDeliveryDirectives(n,t.deliveryDirectives,d,h),f||!m)return void this.loadPlaylist(u)}else u=this.getDeliveryDirectives(n,t.deliveryDirectives,d,h);var b=function(e,t){var r,i=1e3*e.levelTargetDuration,n=i/2,a=e.age,s=a>0&&a<3*i,o=t.loading.end-t.loading.start,l=e.availabilityDelay;if(!1===e.updated)if(s){var u=333*e.misses;r=Math.max(Math.min(n,2*o),u),e.availabilityDelay=(e.availabilityDelay||0)+r}else r=n;else s?(l=Math.min(l||i/2,a),e.availabilityDelay=l,r=l+i-a):r=i-o;return Math.round(r)}(n,s);void 0!==d&&n.canBlockReload&&(b-=n.partTarget||1),this.log("reload live playlist "+e+" in "+Math.round(b)+" ms"),this.timer=self.setTimeout((function(){return i.loadPlaylist(u)}),b)}else this.clearTimer()},t.getDeliveryDirectives=function(e,t,r,i){var n=function(e,t){var r=e.canSkipUntil,i=e.canSkipDateRanges,n=e.endSN;return r&&(void 0!==t?t-n:0)<r?i?K.v2:K.Yes:K.No}(e,r);return null!=t&&t.skip&&e.deltaUpdateFailed&&(r=t.msn,i=t.part,n=K.No),new q(r,i,n)},t.retryLoadingOrFail=function(e){var t,r=this,i=this.hls.config,n=this.retryCount<i.levelLoadingMaxRetry;if(n)if(this.retryCount++,e.details.indexOf("LoadTimeOut")>-1&&null!==(t=e.context)&&void 0!==t&&t.deliveryDirectives)this.warn("retry playlist loading #"+this.retryCount+' after "'+e.details+'"'),this.loadPlaylist();else{var a=Math.min(Math.pow(2,this.retryCount)*i.levelLoadingRetryDelay,i.levelLoadingMaxRetryTimeout);this.timer=self.setTimeout((function(){return r.loadPlaylist()}),a),this.warn("retry playlist loading #"+this.retryCount+" in "+a+' ms after "'+e.details+'"')}else this.warn('cannot recover from error "'+e.details+'"'),this.clearTimer(),e.fatal=!0;return n},e}();function re(){return re=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var i in r)Object.prototype.hasOwnProperty.call(r,i)&&(e[i]=r[i])}return e},re.apply(this,arguments)}function ie(e,t){for(var r=0;r<t.length;r++){var i=t[r];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}function ne(e,t){return ne=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},ne(e,t)}var ae,se=/chrome|firefox/.test(navigator.userAgent.toLowerCase()),oe=function(e){var t,r;function i(t){var r;return(r=e.call(this,t,"[level-controller]")||this)._levels=[],r._firstLevel=-1,r._startLevel=void 0,r.currentLevelIndex=-1,r.manualLevelIndex=-1,r.onParsedComplete=void 0,r._registerListeners(),r}r=e,(t=i).prototype=Object.create(r.prototype),t.prototype.constructor=t,ne(t,r);var n,a,l=i.prototype;return l._registerListeners=function(){var e=this.hls;e.on(s.Events.MANIFEST_LOADED,this.onManifestLoaded,this),e.on(s.Events.LEVEL_LOADED,this.onLevelLoaded,this),e.on(s.Events.AUDIO_TRACK_SWITCHED,this.onAudioTrackSwitched,this),e.on(s.Events.FRAG_LOADED,this.onFragLoaded,this),e.on(s.Events.ERROR,this.onError,this)},l._unregisterListeners=function(){var e=this.hls;e.off(s.Events.MANIFEST_LOADED,this.onManifestLoaded,this),e.off(s.Events.LEVEL_LOADED,this.onLevelLoaded,this),e.off(s.Events.AUDIO_TRACK_SWITCHED,this.onAudioTrackSwitched,this),e.off(s.Events.FRAG_LOADED,this.onFragLoaded,this),e.off(s.Events.ERROR,this.onError,this)},l.destroy=function(){this._unregisterListeners(),this.manualLevelIndex=-1,this._levels.length=0,e.prototype.destroy.call(this)},l.startLoad=function(){this._levels.forEach((function(e){e.loadError=0})),e.prototype.startLoad.call(this)},l.onManifestLoaded=function(e,t){var r,i,n=[],a=[],l=[],u={},d=!1,h=!1,f=!1;if(t.levels.forEach((function(e){var t=e.attrs;d=d||!(!e.width||!e.height),h=h||!!e.videoCodec,f=f||!!e.audioCodec,se&&e.audioCodec&&-1!==e.audioCodec.indexOf("mp4a.40.34")&&(e.audioCodec=void 0);var r=e.bitrate+"-"+e.attrs.RESOLUTION+"-"+e.attrs.CODECS;(i=u[r])?i.url.push(e.url):(i=new z(e),u[r]=i,n.push(i)),t&&(t.AUDIO&&Q(i,"audio",t.AUDIO),t.SUBTITLES&&Q(i,"text",t.SUBTITLES))})),(d||h)&&f&&(n=n.filter((function(e){var t=e.videoCodec,r=e.width,i=e.height;return!!t||!(!r||!i)}))),n=n.filter((function(e){var t=e.audioCodec,r=e.videoCodec;return(!t||S(t,"audio"))&&(!r||S(r,"video"))})),t.audioTracks&&$(a=t.audioTracks.filter((function(e){return!e.audioCodec||S(e.audioCodec,"audio")}))),t.subtitles&&$(l=t.subtitles),n.length>0){r=n[0].bitrate,n.sort((function(e,t){return e.bitrate-t.bitrate})),this._levels=n;for(var c=0;c<n.length;c++)if(n[c].bitrate===r){this._firstLevel=c,this.log("manifest loaded, "+n.length+" level(s) found, first bitrate: "+r);break}var v=f&&!h,g={levels:n,audioTracks:a,subtitleTracks:l,firstLevel:this._firstLevel,stats:t.stats,audio:f,video:h,altAudio:!v&&a.some((function(e){return!!e.url}))};this.hls.trigger(s.Events.MANIFEST_PARSED,g),(this.hls.config.autoStartLoad||this.hls.forceStartLoad)&&this.hls.startLoad(this.hls.config.startPosition)}else this.hls.trigger(s.Events.ERROR,{type:o.ErrorTypes.MEDIA_ERROR,details:o.ErrorDetails.MANIFEST_INCOMPATIBLE_CODECS_ERROR,fatal:!0,url:t.url,reason:"no level with compatible codecs found in manifest"})},l.onError=function(t,r){var i;if(e.prototype.onError.call(this,t,r),!r.fatal){var n=r.context,a=this._levels[this.currentLevelIndex];if(n&&(n.type===C.PlaylistContextType.AUDIO_TRACK&&a.audioGroupIds&&n.groupId===a.audioGroupIds[a.urlId]||n.type===C.PlaylistContextType.SUBTITLE_TRACK&&a.textGroupIds&&n.groupId===a.textGroupIds[a.urlId]))this.redundantFailover(this.currentLevelIndex);else{var s,l=!1,u=!0;switch(r.details){case o.ErrorDetails.FRAG_LOAD_ERROR:case o.ErrorDetails.FRAG_LOAD_TIMEOUT:case o.ErrorDetails.KEY_LOAD_ERROR:case o.ErrorDetails.KEY_LOAD_TIMEOUT:if(r.frag){var d=r.frag.type===C.PlaylistLevelType.MAIN?r.frag.level:this.currentLevelIndex,h=this._levels[d];h?(h.fragmentError++,h.fragmentError>this.hls.config.fragLoadingMaxRetry&&(s=d)):s=d}break;case o.ErrorDetails.LEVEL_LOAD_ERROR:case o.ErrorDetails.LEVEL_LOAD_TIMEOUT:n&&(n.deliveryDirectives&&(u=!1),s=n.level),l=!0;break;case o.ErrorDetails.REMUX_ALLOC_ERROR:s=null!=(i=r.level)?i:this.currentLevelIndex,l=!0}void 0!==s&&this.recoverLevel(r,s,l,u)}}},l.recoverLevel=function(e,t,r,i){var n=e.details,a=this._levels[t];if(a.loadError++,r){if(!this.retryLoadingOrFail(e))return void(this.currentLevelIndex=-1);e.levelRetry=!0}if(i){var s=a.url.length;if(s>1&&a.loadError<s)e.levelRetry=!0,this.redundantFailover(t);else if(-1===this.manualLevelIndex){for(var o=-1,l=this._levels,u=l.length;u--;){var d=(u+this.currentLevelIndex)%l.length;if(d!==this.currentLevelIndex&&0===l[d].loadError){o=d;break}}o>-1&&this.currentLevelIndex!==o&&(this.warn(n+": switch to "+o),e.levelRetry=!0,this.hls.nextAutoLevel=o)}}},l.redundantFailover=function(e){var t=this._levels[e],r=t.url.length;if(r>1){var i=(t.urlId+1)%r;this.warn("Switching to redundant URL-id "+i),this._levels.forEach((function(e){e.urlId=i})),this.level=e}},l.onFragLoaded=function(e,t){var r=t.frag;if(void 0!==r&&r.type===C.PlaylistLevelType.MAIN){var i=this._levels[r.level];void 0!==i&&(i.fragmentError=0,i.loadError=0)}},l.onLevelLoaded=function(e,t){var r,i,n=t.level,a=t.details,s=this._levels[n];if(!s)return this.warn("Invalid level index "+n),void(null!==(i=t.deliveryDirectives)&&void 0!==i&&i.skip&&(a.deltaUpdateFailed=!0));n===this.currentLevelIndex?(0===s.fragmentError&&(s.loadError=0,this.retryCount=0),this.playlistLoaded(n,t,s.details)):null!==(r=t.deliveryDirectives)&&void 0!==r&&r.skip&&(a.deltaUpdateFailed=!0)},l.onAudioTrackSwitched=function(e,t){var r=this.hls.levels[this.currentLevelIndex];if(r&&r.audioGroupIds){for(var i=-1,n=this.hls.audioTracks[t.id].groupId,a=0;a<r.audioGroupIds.length;a++)if(r.audioGroupIds[a]===n){i=a;break}i!==r.urlId&&(r.urlId=i,this.startLoad())}},l.loadPlaylist=function(e){var t=this.currentLevelIndex,r=this._levels[t];if(this.canLoad&&r&&r.url.length>0){var i=r.urlId,n=r.url[i];if(e)try{n=e.addDirectives(n)}catch(e){this.warn("Could not construct new URL with HLS Delivery Directives: "+e)}this.log("Attempt loading level index "+t+(e?" at sn "+e.msn+" part "+e.part:"")+" with URL-id "+i+" "+n),this.clearTimer(),this.hls.trigger(s.Events.LEVEL_LOADING,{url:n,level:t,id:i,deliveryDirectives:e||null})}},l.removeLevel=function(e,t){var r=function(e,r){return r!==t},i=this._levels.filter((function(i,n){return n!==e||i.url.length>1&&void 0!==t&&(i.url=i.url.filter(r),i.audioGroupIds&&(i.audioGroupIds=i.audioGroupIds.filter(r)),i.textGroupIds&&(i.textGroupIds=i.textGroupIds.filter(r)),i.urlId=0,!0)})).map((function(e,t){var r=e.details;return null!=r&&r.fragments&&r.fragments.forEach((function(e){e.level=t})),e}));this._levels=i,this.hls.trigger(s.Events.LEVELS_UPDATED,{levels:i})},n=i,(a=[{key:"levels",get:function(){return 0===this._levels.length?null:this._levels}},{key:"level",get:function(){return this.currentLevelIndex},set:function(e){var t,r=this._levels;if(0!==r.length&&(this.currentLevelIndex!==e||null===(t=r[e])||void 0===t||!t.details)){if(e<0||e>=r.length){var i=e<0;if(this.hls.trigger(s.Events.ERROR,{type:o.ErrorTypes.OTHER_ERROR,details:o.ErrorDetails.LEVEL_SWITCH_ERROR,level:e,fatal:i,reason:"invalid level idx"}),i)return;e=Math.min(e,r.length-1)}this.clearTimer();var n=this.currentLevelIndex,a=r[n],l=r[e];this.log("switching to level "+e+" from "+n),this.currentLevelIndex=e;var u=re({},l,{level:e,maxBitrate:l.maxBitrate,uri:l.uri,urlId:l.urlId});delete u._urlId,this.hls.trigger(s.Events.LEVEL_SWITCHING,u);var d=l.details;if(!d||d.live){var h=this.switchParams(l.uri,null==a?void 0:a.details);this.loadPlaylist(h)}}}},{key:"manualLevel",get:function(){return this.manualLevelIndex},set:function(e){this.manualLevelIndex=e,void 0===this._startLevel&&(this._startLevel=e),-1!==e&&(this.level=e)}},{key:"firstLevel",get:function(){return this._firstLevel},set:function(e){this._firstLevel=e}},{key:"startLevel",get:function(){if(void 0===this._startLevel){var e=this.hls.config.startLevel;return void 0!==e?e:this._firstLevel}return this._startLevel},set:function(e){this._startLevel=e}},{key:"nextLoadLevel",get:function(){return-1!==this.manualLevelIndex?this.manualLevelIndex:this.hls.nextAutoLevel},set:function(e){this.level=e,-1===this.manualLevelIndex&&(this.hls.nextAutoLevel=e)}}])&&ie(n.prototype,a),Object.defineProperty(n,"prototype",{writable:!1}),i}(te);!function(e){e.NOT_LOADED="NOT_LOADED",e.APPENDING="APPENDING",e.PARTIAL="PARTIAL",e.OK="OK"}(ae||(ae={}));var le=function(){function e(e){this.activeFragment=null,this.activeParts=null,this.fragments=Object.create(null),this.timeRanges=Object.create(null),this.bufferPadding=.2,this.hls=void 0,this.hls=e,this._registerListeners()}var t=e.prototype;return t._registerListeners=function(){var e=this.hls;e.on(s.Events.BUFFER_APPENDED,this.onBufferAppended,this),e.on(s.Events.FRAG_BUFFERED,this.onFragBuffered,this),e.on(s.Events.FRAG_LOADED,this.onFragLoaded,this)},t._unregisterListeners=function(){var e=this.hls;e.off(s.Events.BUFFER_APPENDED,this.onBufferAppended,this),e.off(s.Events.FRAG_BUFFERED,this.onFragBuffered,this),e.off(s.Events.FRAG_LOADED,this.onFragLoaded,this)},t.destroy=function(){this._unregisterListeners(),this.fragments=this.timeRanges=null},t.getAppendedFrag=function(e,t){if(t===C.PlaylistLevelType.MAIN){var r=this.activeFragment,i=this.activeParts;if(!r)return null;if(i)for(var n=i.length;n--;){var a=i[n],s=a?a.end:r.appendedPTS;if(a.start<=e&&void 0!==s&&e<=s)return n>9&&(this.activeParts=i.slice(n-9)),a}else if(r.start<=e&&void 0!==r.appendedPTS&&e<=r.appendedPTS)return r}return this.getBufferedFrag(e,t)},t.getBufferedFrag=function(e,t){for(var r=this.fragments,i=Object.keys(r),n=i.length;n--;){var a=r[i[n]];if((null==a?void 0:a.body.type)===t&&a.buffered){var s=a.body;if(s.start<=e&&e<=s.end)return s}}return null},t.detectEvictedFragments=function(e,t,r){var i=this;Object.keys(this.fragments).forEach((function(n){var a=i.fragments[n];if(a)if(a.buffered){var s=a.range[e];s&&s.time.some((function(e){var r=!i.isTimeBuffered(e.startPTS,e.endPTS,t);return r&&i.removeFragment(a.body),r}))}else a.body.type===r&&i.removeFragment(a.body)}))},t.detectPartialFragments=function(e){var t=this,r=this.timeRanges,i=e.frag,n=e.part;if(r&&"initSegment"!==i.sn){var a=de(i),s=this.fragments[a];s&&(Object.keys(r).forEach((function(e){var a=i.elementaryStreams[e];if(a){var o=r[e],l=null!==n||!0===a.partial;s.range[e]=t.getBufferedTimes(i,n,l,o)}})),s.loaded=null,Object.keys(s.range).length?s.buffered=!0:this.removeFragment(s.body))}},t.fragBuffered=function(e){var t=de(e),r=this.fragments[t];r&&(r.loaded=null,r.buffered=!0)},t.getBufferedTimes=function(e,t,r,i){for(var n={time:[],partial:r},a=t?t.start:e.start,s=t?t.end:e.end,o=e.minEndPTS||s,l=e.maxStartPTS||a,u=0;u<i.length;u++){var d=i.start(u)-this.bufferPadding,h=i.end(u)+this.bufferPadding;if(l>=d&&o<=h){n.time.push({startPTS:Math.max(a,i.start(u)),endPTS:Math.min(s,i.end(u))});break}if(a<h&&s>d)n.partial=!0,n.time.push({startPTS:Math.max(a,i.start(u)),endPTS:Math.min(s,i.end(u))});else if(s<=d)break}return n},t.getPartialFragment=function(e){var t,r,i,n=null,a=0,s=this.bufferPadding,o=this.fragments;return Object.keys(o).forEach((function(l){var u=o[l];u&&ue(u)&&(r=u.body.start-s,i=u.body.end+s,e>=r&&e<=i&&(t=Math.min(e-r,i-e),a<=t&&(n=u.body,a=t)))})),n},t.getState=function(e){var t=de(e),r=this.fragments[t];return r?r.buffered?ue(r)?ae.PARTIAL:ae.OK:ae.APPENDING:ae.NOT_LOADED},t.isTimeBuffered=function(e,t,r){for(var i,n,a=0;a<r.length;a++){if(i=r.start(a)-this.bufferPadding,n=r.end(a)+this.bufferPadding,e>=i&&t<=n)return!0;if(t<=i)return!1}return!1},t.onFragLoaded=function(e,t){var r=t.frag,i=t.part;if("initSegment"!==r.sn&&!r.bitrateTest&&!i){var n=de(r);this.fragments[n]={body:r,loaded:t,buffered:!1,range:Object.create(null)}}},t.onBufferAppended=function(e,t){var r=this,i=t.frag,n=t.part,a=t.timeRanges;if(i.type===C.PlaylistLevelType.MAIN)if(this.activeFragment=i,n){var s=this.activeParts;s||(this.activeParts=s=[]),s.push(n)}else this.activeParts=null;this.timeRanges=a,Object.keys(a).forEach((function(e){var t=a[e];if(r.detectEvictedFragments(e,t),!n)for(var s=0;s<t.length;s++)i.appendedPTS=Math.max(t.end(s),i.appendedPTS||0)}))},t.onFragBuffered=function(e,t){this.detectPartialFragments(t)},t.hasFragment=function(e){var t=de(e);return!!this.fragments[t]},t.removeFragmentsInRange=function(e,t,r){var i=this;Object.keys(this.fragments).forEach((function(n){var a=i.fragments[n];if(a&&a.buffered){var s=a.body;s.type===r&&s.start<t&&s.end>e&&i.removeFragment(s)}}))},t.removeFragment=function(e){var t=de(e);e.stats.loaded=0,e.clearElementaryStreamInfo(),delete this.fragments[t]},t.removeAllFragments=function(){this.fragments=Object.create(null),this.activeFragment=null,this.activeParts=null},e}();function ue(e){var t,r;return e.buffered&&((null===(t=e.range.video)||void 0===t?void 0:t.partial)||(null===(r=e.range.audio)||void 0===r?void 0:r.partial))}function de(e){return e.type+"_"+e.level+"_"+e.urlId+"_"+e.sn}var he=function(){function e(){this._boundTick=void 0,this._tickTimer=null,this._tickInterval=null,this._tickCallCount=0,this._boundTick=this.tick.bind(this)}var t=e.prototype;return t.destroy=function(){this.onHandlerDestroying(),this.onHandlerDestroyed()},t.onHandlerDestroying=function(){this.clearNextTick(),this.clearInterval()},t.onHandlerDestroyed=function(){},t.hasInterval=function(){return!!this._tickInterval},t.hasNextTick=function(){return!!this._tickTimer},t.setInterval=function(e){return!this._tickInterval&&(this._tickInterval=self.setInterval(this._boundTick,e),!0)},t.clearInterval=function(){return!!this._tickInterval&&(self.clearInterval(this._tickInterval),this._tickInterval=null,!0)},t.clearNextTick=function(){return!!this._tickTimer&&(self.clearTimeout(this._tickTimer),this._tickTimer=null,!0)},t.tick=function(){this._tickCallCount++,1===this._tickCallCount&&(this.doTick(),this._tickCallCount>1&&this.tickImmediate(),this._tickCallCount=0)},t.tickImmediate=function(){this.clearNextTick(),this._tickTimer=self.setTimeout(this._boundTick,0)},t.doTick=function(){},e}(),fe={length:0,start:function(){return 0},end:function(){return 0}},ce=function(){function e(){}return e.isBuffered=function(t,r){try{if(t)for(var i=e.getBuffered(t),n=0;n<i.length;n++)if(r>=i.start(n)&&r<=i.end(n))return!0}catch(e){}return!1},e.bufferInfo=function(t,r,i){try{if(t){var n,a=e.getBuffered(t),s=[];for(n=0;n<a.length;n++)s.push({start:a.start(n),end:a.end(n)});return this.bufferedInfo(s,r,i)}}catch(e){}return{len:0,start:r,end:r,nextStart:void 0}},e.bufferedInfo=function(e,t,r){t=Math.max(0,t),e.sort((function(e,t){return e.start-t.start||t.end-e.end}));var i=[];if(r)for(var n=0;n<e.length;n++){var a=i.length;if(a){var s=i[a-1].end;e[n].start-s<r?e[n].end>s&&(i[a-1].end=e[n].end):i.push(e[n])}else i.push(e[n])}else i=e;for(var o,l=0,u=t,d=t,h=0;h<i.length;h++){var f=i[h].start,c=i[h].end;if(t+r>=f&&t<c)u=f,l=(d=c)-t;else if(t+r<f){o=f;break}}return{len:l,start:u||0,end:d||0,nextStart:o}},e.getBuffered=function(e){try{return e.buffered}catch(e){return l.logger.log("failed to get media.buffered",e),fe}},e}(),ve=function(e,t,r,i,n,a){void 0===i&&(i=0),void 0===n&&(n=-1),void 0===a&&(a=!1),this.level=void 0,this.sn=void 0,this.part=void 0,this.id=void 0,this.size=void 0,this.partial=void 0,this.transmuxing={start:0,executeStart:0,executeEnd:0,end:0},this.buffering={audio:{start:0,executeStart:0,executeEnd:0,end:0},video:{start:0,executeStart:0,executeEnd:0,end:0},audiovideo:{start:0,executeStart:0,executeEnd:0,end:0}},this.level=e,this.sn=t,this.id=r,this.size=i,this.part=n,this.partial=a};function ge(e,t){if(e){var r=e.start+t;e.start=e.startPTS=r,e.endPTS=r+e.duration}}function pe(e,t){for(var r=t.fragments,i=0,n=r.length;i<n;i++)ge(r[i],e);t.fragmentHint&&ge(t.fragmentHint,e),t.alignedSliding=!0}const me=function(e,t){for(var r=0,i=e.length-1,n=null,a=null;r<=i;){var s=t(a=e[n=(r+i)/2|0]);if(s>0)r=n+1;else{if(!(s<0))return a;i=n-1}}return null};function ye(e,t,r){void 0===e&&(e=0),void 0===t&&(t=0);var i=Math.min(t,r.duration+(r.deltaPTS?r.deltaPTS:0));return r.start+r.duration-i<=e?1:r.start-i>e&&r.start?-1:0}function Ee(e,t,r){var i=1e3*Math.min(t,r.duration+(r.deltaPTS?r.deltaPTS:0));return(r.endProgramDateTime||0)-i>e}function Te(e){var t="function"==typeof Map?new Map:void 0;return Te=function(e){if(null===e||(r=e,-1===Function.toString.call(r).indexOf("[native code]")))return e;var r;if("function"!=typeof e)throw new TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,i)}function i(){return Se(e,arguments,Ae(this).constructor)}return i.prototype=Object.create(e.prototype,{constructor:{value:i,enumerable:!1,writable:!0,configurable:!0}}),Le(i,e)},Te(e)}function Se(e,t,r){return Se=be()?Reflect.construct.bind():function(e,t,r){var i=[null];i.push.apply(i,t);var n=new(Function.bind.apply(e,i));return r&&Le(n,r.prototype),n},Se.apply(null,arguments)}function be(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}function Le(e,t){return Le=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},Le(e,t)}function Ae(e){return Ae=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(e){return e.__proto__||Object.getPrototypeOf(e)},Ae(e)}var De=Math.pow(2,17),Re=function(){function e(e){this.config=void 0,this.loader=null,this.partLoadTimeout=-1,this.config=e}var t=e.prototype;return t.destroy=function(){this.loader&&(this.loader.destroy(),this.loader=null)},t.abort=function(){this.loader&&this.loader.abort()},t.load=function(e,t){var r=this,i=e.url;if(!i)return Promise.reject(new _e({type:o.ErrorTypes.NETWORK_ERROR,details:o.ErrorDetails.FRAG_LOAD_ERROR,fatal:!1,frag:e,networkDetails:null},"Fragment does not have a "+(i?"part list":"url")));this.abort();var n=this.config,a=n.fLoader,s=n.loader;return new Promise((function(i,l){r.loader&&r.loader.destroy();var u=r.loader=e.loader=a?new a(n):new s(n),d=ke(e),h={timeout:n.fragLoadingTimeOut,maxRetry:0,retryDelay:0,maxRetryDelay:n.fragLoadingMaxRetryTimeout,highWaterMark:"initSegment"===e.sn?1/0:De};e.stats=u.stats,u.load(d,h,{onSuccess:function(t,n,a,s){r.resetLoader(e,u),i({frag:e,part:null,payload:t.data,networkDetails:s})},onError:function(t,i,n){r.resetLoader(e,u),l(new _e({type:o.ErrorTypes.NETWORK_ERROR,details:o.ErrorDetails.FRAG_LOAD_ERROR,fatal:!1,frag:e,response:t,networkDetails:n}))},onAbort:function(t,i,n){r.resetLoader(e,u),l(new _e({type:o.ErrorTypes.NETWORK_ERROR,details:o.ErrorDetails.INTERNAL_ABORTED,fatal:!1,frag:e,networkDetails:n}))},onTimeout:function(t,i,n){r.resetLoader(e,u),l(new _e({type:o.ErrorTypes.NETWORK_ERROR,details:o.ErrorDetails.FRAG_LOAD_TIMEOUT,fatal:!1,frag:e,networkDetails:n}))},onProgress:function(r,i,n,a){t&&t({frag:e,part:null,payload:n,networkDetails:a})}})}))},t.loadPart=function(e,t,r){var i=this;this.abort();var n=this.config,a=n.fLoader,s=n.loader;return new Promise((function(l,u){i.loader&&i.loader.destroy();var d=i.loader=e.loader=a?new a(n):new s(n),h=ke(e,t),f={timeout:n.fragLoadingTimeOut,maxRetry:0,retryDelay:0,maxRetryDelay:n.fragLoadingMaxRetryTimeout,highWaterMark:De};t.stats=d.stats,d.load(h,f,{onSuccess:function(n,a,s,o){i.resetLoader(e,d),i.updateStatsFromPart(e,t);var u={frag:e,part:t,payload:n.data,networkDetails:o};r(u),l(u)},onError:function(r,n,a){i.resetLoader(e,d),u(new _e({type:o.ErrorTypes.NETWORK_ERROR,details:o.ErrorDetails.FRAG_LOAD_ERROR,fatal:!1,frag:e,part:t,response:r,networkDetails:a}))},onAbort:function(r,n,a){e.stats.aborted=t.stats.aborted,i.resetLoader(e,d),u(new _e({type:o.ErrorTypes.NETWORK_ERROR,details:o.ErrorDetails.INTERNAL_ABORTED,fatal:!1,frag:e,part:t,networkDetails:a}))},onTimeout:function(r,n,a){i.resetLoader(e,d),u(new _e({type:o.ErrorTypes.NETWORK_ERROR,details:o.ErrorDetails.FRAG_LOAD_TIMEOUT,fatal:!1,frag:e,part:t,networkDetails:a}))}})}))},t.updateStatsFromPart=function(e,t){var r=e.stats,i=t.stats,n=i.total;if(r.loaded+=i.loaded,n){var a=Math.round(e.duration/t.duration),s=Math.min(Math.round(r.loaded/n),a),o=(a-s)*Math.round(r.loaded/s);r.total=r.loaded+o}else r.total=Math.max(r.loaded,r.total);var l=r.loading,u=i.loading;l.start?l.first+=u.first-u.start:(l.start=u.start,l.first=u.first),l.end=u.end},t.resetLoader=function(e,t){e.loader=null,this.loader===t&&(self.clearTimeout(this.partLoadTimeout),this.loader=null),t.destroy()},e}();function ke(e,t){void 0===t&&(t=null);var r=t||e,i={frag:e,part:t,responseType:"arraybuffer",url:r.url,headers:{},rangeStart:0,rangeEnd:0},n=r.byteRangeStartOffset,s=r.byteRangeEndOffset;return(0,a.isFiniteNumber)(n)&&(0,a.isFiniteNumber)(s)&&(i.rangeStart=n,i.rangeEnd=s),i}var _e=function(e){var t,r;function i(t){for(var r,i=arguments.length,n=new Array(i>1?i-1:0),a=1;a<i;a++)n[a-1]=arguments[a];return(r=e.call.apply(e,[this].concat(n))||this).data=void 0,r.data=t,r}return r=e,(t=i).prototype=Object.create(r.prototype),t.prototype.constructor=t,Le(t,r),i}(Te(Error)),xe=r(21);const Ie=function(e){for(var t="",r=e.length,i=0;i<r;i++)t+="["+e.start(i).toFixed(3)+","+e.end(i).toFixed(3)+"]";return t};function we(e,t){for(var r=0;r<t.length;r++){var i=t[r];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}function Ce(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function Pe(e,t){return Pe=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},Pe(e,t)}var Oe="STOPPED",Fe="IDLE",Me="KEY_LOADING",Ne="FRAG_LOADING",Ue="FRAG_LOADING_WAITING_RETRY",Be="PARSING",Ge="PARSED",Ve="ENDED",He="ERROR",je="WAITING_LEVEL",Ke=function(e){var t,r;function i(t,r,i){var n;return(n=e.call(this)||this).hls=void 0,n.fragPrevious=null,n.fragCurrent=null,n.fragmentTracker=void 0,n.transmuxer=null,n._state=Oe,n.media=null,n.mediaBuffer=null,n.config=void 0,n.bitrateTest=!1,n.lastCurrentTime=0,n.nextLoadPosition=0,n.startPosition=0,n.loadedmetadata=!1,n.fragLoadError=0,n.retryDate=0,n.levels=null,n.fragmentLoader=void 0,n.levelLastLoaded=null,n.startFragRequested=!1,n.decrypter=void 0,n.initPTS=[],n.onvseeking=null,n.onvended=null,n.logPrefix="",n.log=void 0,n.warn=void 0,n.logPrefix=i,n.log=l.logger.log.bind(l.logger,i+":"),n.warn=l.logger.warn.bind(l.logger,i+":"),n.hls=t,n.fragmentLoader=new Re(t.config),n.fragmentTracker=r,n.config=t.config,n.decrypter=new xe.default(t,t.config),t.on(s.Events.KEY_LOADED,n.onKeyLoaded,Ce(n)),t.on(s.Events.LEVEL_SWITCHING,n.onLevelSwitching,Ce(n)),n}r=e,(t=i).prototype=Object.create(r.prototype),t.prototype.constructor=t,Pe(t,r);var n,d,h=i.prototype;return h.doTick=function(){this.onTickEnd()},h.onTickEnd=function(){},h.startLoad=function(e){},h.stopLoad=function(){this.fragmentLoader.abort();var e=this.fragCurrent;e&&this.fragmentTracker.removeFragment(e),this.resetTransmuxer(),this.fragCurrent=null,this.fragPrevious=null,this.clearInterval(),this.clearNextTick(),this.state=Oe},h._streamEnded=function(e,t){var r=this.fragCurrent,i=this.fragmentTracker;if(!t.live&&r&&this.media&&r.sn>=t.endSN&&!e.nextStart){var n=t.partList;if(null!=n&&n.length){var a=n[n.length-1];return ce.isBuffered(this.media,a.start+a.duration/2)}var s=i.getState(r);return s===ae.PARTIAL||s===ae.OK}return!1},h.onMediaAttached=function(e,t){var r=this.media=this.mediaBuffer=t.media;this.onvseeking=this.onMediaSeeking.bind(this),this.onvended=this.onMediaEnded.bind(this),r.addEventListener("seeking",this.onvseeking),r.addEventListener("ended",this.onvended);var i=this.config;this.levels&&i.autoStartLoad&&this.state===Oe&&this.startLoad(i.startPosition)},h.onMediaDetaching=function(){var e=this.media;null!=e&&e.ended&&(this.log("MSE detaching and video ended, reset startPosition"),this.startPosition=this.lastCurrentTime=0),e&&this.onvseeking&&this.onvended&&(e.removeEventListener("seeking",this.onvseeking),e.removeEventListener("ended",this.onvended),this.onvseeking=this.onvended=null),this.media=this.mediaBuffer=null,this.loadedmetadata=!1,this.fragmentTracker.removeAllFragments(),this.stopLoad()},h.onMediaSeeking=function(){var e=this.config,t=this.fragCurrent,r=this.media,i=this.mediaBuffer,n=this.state,s=r?r.currentTime:0,o=ce.bufferInfo(i||r,s,e.maxBufferHole);if(this.log("media seeking to "+((0,a.isFiniteNumber)(s)?s.toFixed(3):s)+", state: "+n),n===Ve)this.resetLoadingState();else if(t){var l=e.maxFragLookUpTolerance,u=t.start-l,d=t.start+t.duration+l;if(!o.len||d<o.start||u>o.end){var h=s>d;(s<u||h)&&(h&&t.loader&&(this.log("seeking outside of buffer while fragment load in progress, cancel fragment load"),t.loader.abort()),this.resetLoadingState())}}r&&(this.lastCurrentTime=s),this.loadedmetadata||o.len||(this.nextLoadPosition=this.startPosition=s),this.tickImmediate()},h.onMediaEnded=function(){this.startPosition=this.lastCurrentTime=0},h.onKeyLoaded=function(e,t){if(this.state===Me&&t.frag===this.fragCurrent&&this.levels){this.state=Fe;var r=this.levels[t.frag.level].details;r&&this.loadFragment(t.frag,r,t.frag.start)}},h.onLevelSwitching=function(e,t){this.fragLoadError=0},h.onHandlerDestroying=function(){this.stopLoad(),e.prototype.onHandlerDestroying.call(this)},h.onHandlerDestroyed=function(){this.state=Oe,this.hls.off(s.Events.KEY_LOADED,this.onKeyLoaded,this),this.hls.off(s.Events.LEVEL_SWITCHING,this.onLevelSwitching,this),this.fragmentLoader&&this.fragmentLoader.destroy(),this.decrypter&&this.decrypter.destroy(),this.hls=this.log=this.warn=this.decrypter=this.fragmentLoader=this.fragmentTracker=null,e.prototype.onHandlerDestroyed.call(this)},h.loadKey=function(e,t){this.log("Loading key for "+e.sn+" of ["+t.startSN+"-"+t.endSN+"], "+("[stream-controller]"===this.logPrefix?"level":"track")+" "+e.level),this.state=Me,this.fragCurrent=e,this.hls.trigger(s.Events.KEY_LOADING,{frag:e})},h.loadFragment=function(e,t,r){this._loadFragForPlayback(e,t,r)},h._loadFragForPlayback=function(e,t,r){var i=this;this._doFragLoad(e,t,r,(function(t){if(i.fragContextChanged(e))return i.warn("Fragment "+e.sn+(t.part?" p: "+t.part.index:"")+" of level "+e.level+" was dropped during download."),void i.fragmentTracker.removeFragment(e);e.stats.chunkCount++,i._handleFragmentLoadProgress(t)})).then((function(t){if(t){i.fragLoadError=0;var r=i.state;i.fragContextChanged(e)?(r===Ne||!i.fragCurrent&&r===Be)&&(i.fragmentTracker.removeFragment(e),i.state=Fe):("payload"in t&&(i.log("Loaded fragment "+e.sn+" of level "+e.level),i.hls.trigger(s.Events.FRAG_LOADED,t)),i._handleFragmentLoadComplete(t))}})).catch((function(t){i.state!==Oe&&i.state!==He&&(i.warn(t),i.resetFragmentLoading(e))}))},h.flushMainBuffer=function(e,t,r){if(void 0===r&&(r=null),e-t){var i={startOffset:e,endOffset:t,type:r};this.fragLoadError=0,this.hls.trigger(s.Events.BUFFER_FLUSHING,i)}},h._loadInitSegment=function(e){var t=this;this._doFragLoad(e).then((function(r){if(!r||t.fragContextChanged(e)||!t.levels)throw new Error("init load aborted");return r})).then((function(r){var i=t.hls,n=r.payload,a=e.decryptdata;if(n&&n.byteLength>0&&a&&a.key&&a.iv&&"AES-128"===a.method){var o=self.performance.now();return t.decrypter.webCryptoDecrypt(new Uint8Array(n),a.key.buffer,a.iv.buffer).then((function(t){var n=self.performance.now();return i.trigger(s.Events.FRAG_DECRYPTED,{frag:e,payload:t,stats:{tstart:o,tdecrypt:n}}),r.payload=t,r}))}return r})).then((function(r){var i=t.fragCurrent,n=t.hls,a=t.levels;if(!a)throw new Error("init load aborted, missing levels");a[e.level].details;var o=e.stats;t.state=Fe,t.fragLoadError=0,e.data=new Uint8Array(r.payload),o.parsing.start=o.buffering.start=self.performance.now(),o.parsing.end=o.buffering.end=self.performance.now(),r.frag===i&&n.trigger(s.Events.FRAG_BUFFERED,{stats:o,frag:i,part:null,id:e.type}),t.tick()})).catch((function(r){t.state!==Oe&&t.state!==He&&(t.warn(r),t.resetFragmentLoading(e))}))},h.fragContextChanged=function(e){var t=this.fragCurrent;return!e||!t||e.level!==t.level||e.sn!==t.sn||e.urlId!==t.urlId},h.fragBufferedComplete=function(e,t){var r,i,n=this.mediaBuffer?this.mediaBuffer:this.media;this.log("Buffered "+e.type+" sn: "+e.sn+(t?" part: "+t.index:"")+" of "+("[stream-controller]"===this.logPrefix?"level":"track")+" "+e.level+" "+(n?Ie(ce.getBuffered(n)):"(detached)")),this.state=Fe,n&&(!this.loadedmetadata&&e.type==C.PlaylistLevelType.MAIN&&n.buffered.length&&(null===(r=this.fragCurrent)||void 0===r?void 0:r.sn)===(null===(i=this.fragPrevious)||void 0===i?void 0:i.sn)&&(this.loadedmetadata=!0,this.seekToStartPos()),this.tick())},h.seekToStartPos=function(){},h._handleFragmentLoadComplete=function(e){var t=this.transmuxer;if(t){var r=e.frag,i=e.part,n=e.partsLoaded,a=!n||0===n.length||n.some((function(e){return!e})),s=new ve(r.level,r.sn,r.stats.chunkCount+1,0,i?i.index:-1,!a);t.flush(s)}},h._handleFragmentLoadProgress=function(e){},h._doFragLoad=function(e,t,r,i){var n=this;if(void 0===r&&(r=null),!this.levels)throw new Error("frag load aborted, missing levels");if(r=Math.max(e.start,r||0),this.config.lowLatencyMode&&t){var o=t.partList;if(o&&i){r>e.end&&t.fragmentHint&&(e=t.fragmentHint);var l=this.getNextPart(o,e,r);if(l>-1){var u=o[l];return this.log("Loading part sn: "+e.sn+" p: "+u.index+" cc: "+e.cc+" of playlist ["+t.startSN+"-"+t.endSN+"] parts [0-"+l+"-"+(o.length-1)+"] "+("[stream-controller]"===this.logPrefix?"level":"track")+": "+e.level+", target: "+parseFloat(r.toFixed(3))),this.nextLoadPosition=u.start+u.duration,this.state=Ne,this.hls.trigger(s.Events.FRAG_LOADING,{frag:e,part:o[l],targetBufferTime:r}),this.doFragPartsLoad(e,o,l,i).catch((function(e){return n.handleFragLoadError(e)}))}if(!e.url||this.loadedEndOfParts(o,r))return Promise.resolve(null)}}return this.log("Loading fragment "+e.sn+" cc: "+e.cc+" "+(t?"of ["+t.startSN+"-"+t.endSN+"] ":"")+("[stream-controller]"===this.logPrefix?"level":"track")+": "+e.level+", target: "+parseFloat(r.toFixed(3))),(0,a.isFiniteNumber)(e.sn)&&!this.bitrateTest&&(this.nextLoadPosition=e.start+e.duration),this.state=Ne,this.hls.trigger(s.Events.FRAG_LOADING,{frag:e,targetBufferTime:r}),this.fragmentLoader.load(e,i).catch((function(e){return n.handleFragLoadError(e)}))},h.doFragPartsLoad=function(e,t,r,i){var n=this;return new Promise((function(a,o){var l=[];!function r(u){var d=t[u];n.fragmentLoader.loadPart(e,d,i).then((function(i){l[d.index]=i;var o=i.part;n.hls.trigger(s.Events.FRAG_LOADED,i);var h=t[u+1];if(!h||h.fragment!==e)return a({frag:e,part:o,partsLoaded:l});r(u+1)})).catch(o)}(r)}))},h.handleFragLoadError=function(e){var t=e.data;return t&&t.details===o.ErrorDetails.INTERNAL_ABORTED?this.handleFragLoadAborted(t.frag,t.part):this.hls.trigger(s.Events.ERROR,t),null},h._handleTransmuxerFlush=function(e){var t=this.getCurrentContext(e);if(t&&this.state===Be){var r=t.frag,i=t.part,n=t.level,a=self.performance.now();r.stats.parsing.end=a,i&&(i.stats.parsing.end=a),this.updateLevelTiming(r,i,n,e.partial)}else this.fragCurrent||(this.state=Fe)},h.getCurrentContext=function(e){var t=this.levels,r=e.level,i=e.sn,n=e.part;if(!t||!t[r])return this.warn("Levels object was unset while buffering fragment "+i+" of level "+r+". The current chunk will not be buffered."),null;var a=t[r],s=n>-1?function(e,t,r){if(!e||!e.details)return null;var i=e.details.partList;if(i)for(var n=i.length;n--;){var a=i[n];if(a.index===r&&a.fragment.sn===t)return a}return null}(a,i,n):null,o=s?s.fragment:function(e,t,r){if(!e||!e.details)return null;var i=e.details,n=i.fragments[t-i.startSN];return n||((n=i.fragmentHint)&&n.sn===t?n:t<i.startSN&&r&&r.sn===t?r:null)}(a,i,this.fragCurrent);return o?{frag:o,part:s,level:a}:null},h.bufferFragmentData=function(e,t,r,i){if(e&&this.state===Be){var n=e.data1,a=e.data2,o=n;if(n&&a&&(o=(0,u.appendUint8Array)(n,a)),o&&o.length){var l={type:e.type,frag:t,part:r,chunkMeta:i,parent:t.type,data:o};this.hls.trigger(s.Events.BUFFER_APPENDING,l),e.dropped&&e.independent&&!r&&this.flushBufferGap(t)}}},h.flushBufferGap=function(e){var t=this.media;if(t)if(ce.isBuffered(t,t.currentTime)){var r=t.currentTime,i=ce.bufferInfo(t,r,0),n=e.duration,a=Math.min(2*this.config.maxFragLookUpTolerance,.25*n),s=Math.max(Math.min(e.start-a,i.end-a),r+a);e.start-s>a&&this.flushMainBuffer(s,e.start)}else this.flushMainBuffer(0,e.start)},h.getFwdBufferInfo=function(e,t){var r=this.config,i=this.getLoadPosition();if(!(0,a.isFiniteNumber)(i))return null;var n=ce.bufferInfo(e,i,r.maxBufferHole);if(0===n.len&&void 0!==n.nextStart){var s=this.fragmentTracker.getBufferedFrag(i,t);if(s&&n.nextStart<s.end)return ce.bufferInfo(e,i,Math.max(n.nextStart,r.maxBufferHole))}return n},h.getMaxBufferLength=function(e){var t,r=this.config;return t=e?Math.max(8*r.maxBufferSize/e,r.maxBufferLength):r.maxBufferLength,Math.min(t,r.maxMaxBufferLength)},h.reduceMaxBufferLength=function(e){var t=this.config,r=e||t.maxBufferLength;return t.maxMaxBufferLength>=r&&(t.maxMaxBufferLength/=2,this.warn("Reduce max buffer length to "+t.maxMaxBufferLength+"s"),!0)},h.getNextFragment=function(e,t){var r=t.fragments,i=r.length;if(!i)return null;var n,a=this.config,s=r[0].start;if(t.live){var o=a.initialLiveManifestSize;if(i<o)return this.warn("Not enough fragments to start playback (have: "+i+", need: "+o+")"),null;t.PTSKnown||this.startFragRequested||-1!==this.startPosition||(n=this.getInitialLiveFragment(t,r),this.startPosition=n?this.hls.liveSyncPosition||n.start:e)}else e<=s&&(n=r[0]);if(!n){var l=a.lowLatencyMode?t.partEnd:t.fragmentEnd;n=this.getFragmentAtPosition(e,l,t)}return this.mapToInitFragWhenRequired(n)},h.mapToInitFragWhenRequired=function(e){return null==e||!e.initSegment||null!=e&&e.initSegment.data||this.bitrateTest?e:e.initSegment},h.getNextPart=function(e,t,r){for(var i=-1,n=!1,a=!0,s=0,o=e.length;s<o;s++){var l=e[s];if(a=a&&!l.independent,i>-1&&r<l.start)break;var u=l.loaded;!u&&(n||l.independent||a)&&l.fragment===t&&(i=s),n=u}return i},h.loadedEndOfParts=function(e,t){var r=e[e.length-1];return r&&t>r.start&&r.loaded},h.getInitialLiveFragment=function(e,t){var r=this.fragPrevious,i=null;if(r){if(e.hasProgramDateTime&&(this.log("Live playlist, switching playlist, load frag with same PDT: "+r.programDateTime),i=function(e,t,r){if(null===t||!Array.isArray(e)||!e.length||!(0,a.isFiniteNumber)(t))return null;if(t<(e[0].programDateTime||0))return null;if(t>=(e[e.length-1].endProgramDateTime||0))return null;r=r||0;for(var i=0;i<e.length;++i){var n=e[i];if(Ee(t,r,n))return n}return null}(t,r.endProgramDateTime,this.config.maxFragLookUpTolerance)),!i){var n=r.sn+1;if(n>=e.startSN&&n<=e.endSN){var s=t[n-e.startSN];r.cc===s.cc&&(i=s,this.log("Live playlist, switching playlist, load frag with next SN: "+i.sn))}i||(i=function(e,t){return me(e,(function(e){return e.cc<t?1:e.cc>t?-1:0}))}(t,r.cc),i&&this.log("Live playlist, switching playlist, load frag with same CC: "+i.sn))}}else{var o=this.hls.liveSyncPosition;null!==o&&(i=this.getFragmentAtPosition(o,this.bitrateTest?e.fragmentEnd:e.edge,e))}return i},h.getFragmentAtPosition=function(e,t,r){var i,n=this.config,a=this.fragPrevious,s=r.fragments,o=r.endSN,l=r.fragmentHint,u=n.maxFragLookUpTolerance,d=!!(n.lowLatencyMode&&r.partList&&l);if(d&&l&&!this.bitrateTest&&(s=s.concat(l),o=l.sn),i=e<t?function(e,t,r,i){void 0===r&&(r=0),void 0===i&&(i=0);var n=null;return e?n=t[e.sn-t[0].sn+1]||null:0===r&&0===t[0].start&&(n=t[0]),n&&0===ye(r,i,n)?n:me(t,ye.bind(null,r,i))||n}(a,s,e,e>t-u?0:u):s[s.length-1],i){var h=i.sn-r.startSN;if(this.fragmentTracker.getState(i)===ae.OK&&(a=i),a&&i.sn===a.sn&&!d&&a&&i.level===a.level){var f=s[h+1];i.sn<o&&this.fragmentTracker.getState(f)!==ae.OK?(this.log("SN "+i.sn+" just loaded, load next one: "+f.sn),i=f):i=null}}return i},h.synchronizeToLiveEdge=function(e){var t=this.config,r=this.media;if(r){var i=this.hls.liveSyncPosition,n=r.currentTime,a=e.fragments[0].start,s=e.edge,o=n>=a-t.maxFragLookUpTolerance&&n<=s;if(null!==i&&r.duration>i&&(n<i||!o)){var l=void 0!==t.liveMaxLatencyDuration?t.liveMaxLatencyDuration:t.liveMaxLatencyDurationCount*e.targetduration;(!o&&r.readyState<4||n<s-l)&&(this.loadedmetadata||(this.nextLoadPosition=i),r.readyState&&(this.warn("Playback: "+n.toFixed(3)+" is located too far from the end of live sliding playlist: "+s+", reset currentTime to : "+i.toFixed(3)),r.currentTime=i))}}},h.alignPlaylists=function(e,t){var r=this.levels,i=this.levelLastLoaded,n=this.fragPrevious,s=null!==i?r[i]:null,o=e.fragments.length;if(!o)return this.warn("No fragments in live playlist"),0;var u=e.fragments[0].start,d=!t,h=e.alignedSliding&&(0,a.isFiniteNumber)(u);if(d||!h&&!u){!function(e,t,r){t&&(function(e,t,r){if(function(e,t,r){return!(!t.details||!(r.endCC>r.startCC||e&&e.cc<r.startCC))}(e,r,t)){var i=function(e,t,r){void 0===r&&(r=0);var i=e.fragments,n=t.fragments;if(n.length&&i.length){var a=function(e,t){for(var r=null,i=0,n=e.length;i<n;i++){var a=e[i];if(a&&a.cc===t){r=a;break}}return r}(i,n[0].cc);if(a&&(!a||a.startPTS))return a;l.logger.log("No frag in previous level to align on")}else l.logger.log("No fragments to align")}(r.details,t);i&&(0,a.isFiniteNumber)(i.start)&&(l.logger.log("Adjusting PTS using last level due to CC increase within current level "+t.url),pe(i.start,t))}}(e,r,t),!r.alignedSliding&&t.details&&function(e,t){if(t.fragments.length&&e.hasProgramDateTime&&t.hasProgramDateTime){var r=t.fragments[0].programDateTime,i=e.fragments[0].programDateTime,n=(i-r)/1e3+t.fragments[0].start;n&&(0,a.isFiniteNumber)(n)&&(l.logger.log("Adjusting PTS using programDateTime delta "+(i-r)+"ms, sliding:"+n.toFixed(3)+" "+e.url+" "),pe(n,e))}}(r,t.details),r.alignedSliding||!t.details||r.skippedSegments||ee(t.details,r))}(n,s,e);var f=e.fragments[0].start;return this.log("Live playlist sliding: "+f.toFixed(2)+" start-sn: "+(t?t.startSN:"na")+"->"+e.startSN+" prev-sn: "+(n?n.sn:"na")+" fragments: "+o),f}return u},h.waitForCdnTuneIn=function(e){return e.live&&e.canBlockReload&&e.partTarget&&e.tuneInGoal>Math.max(e.partHoldBack,3*e.partTarget)},h.setStartPosition=function(e,t){var r=this.startPosition;if(r<t&&(r=-1),-1===r||-1===this.lastCurrentTime){var i=e.startTimeOffset;(0,a.isFiniteNumber)(i)?(r=t+i,i<0&&(r+=e.totalduration),r=Math.min(Math.max(t,r),t+e.totalduration),this.log("Start time offset "+i+" found in playlist, adjust startPosition to "+r),this.startPosition=r):e.live?r=this.hls.liveSyncPosition||t:this.startPosition=r=0,this.lastCurrentTime=r}this.nextLoadPosition=r},h.getLoadPosition=function(){var e=this.media,t=0;return this.loadedmetadata&&e?t=e.currentTime:this.nextLoadPosition&&(t=this.nextLoadPosition),t},h.handleFragLoadAborted=function(e,t){this.transmuxer&&"initSegment"!==e.sn&&e.stats.aborted&&(this.warn("Fragment "+e.sn+(t?" part"+t.index:"")+" of level "+e.level+" was aborted"),this.resetFragmentLoading(e))},h.resetFragmentLoading=function(e){this.fragCurrent&&(this.fragContextChanged(e)||this.state===Ue)||(this.state=Fe)},h.onFragmentOrKeyLoadError=function(e,t){if(!t.fatal){var r=t.frag;if(r&&r.type===e){this.fragCurrent;var i=this.config;if(this.fragLoadError+1<=i.fragLoadingMaxRetry){this.loadedmetadata||(this.startFragRequested=!1,this.nextLoadPosition=this.startPosition);var n=Math.min(Math.pow(2,this.fragLoadError)*i.fragLoadingRetryDelay,i.fragLoadingMaxRetryTimeout);this.warn("Fragment "+r.sn+" of "+e+" "+r.level+" failed to load, retrying in "+n+"ms"),this.retryDate=self.performance.now()+n,this.fragLoadError++,this.state=Ue}else t.levelRetry?(e===C.PlaylistLevelType.AUDIO&&(this.fragCurrent=null),this.fragLoadError=0,this.state=Fe):(l.logger.error(t.details+" reaches max retry, redispatch as fatal ..."),t.fatal=!0,this.hls.stopLoad(),this.state=He)}}},h.afterBufferFlushed=function(e,t,r){if(e){var i=ce.getBuffered(e);this.fragmentTracker.detectEvictedFragments(t,i,r),this.state===Ve&&this.resetLoadingState()}},h.resetLoadingState=function(){this.fragCurrent=null,this.fragPrevious=null,this.state=Fe},h.resetStartWhenNotLoaded=function(e){if(!this.loadedmetadata){this.startFragRequested=!1;var t=this.levels?this.levels[e].details:null;null!=t&&t.live?(this.startPosition=-1,this.setStartPosition(t,0),this.resetLoadingState()):this.nextLoadPosition=this.startPosition}},h.updateLevelTiming=function(e,t,r,i){var n=this,a=r.details;Object.keys(e.elementaryStreams).reduce((function(t,o){var l=e.elementaryStreams[o];if(l){var u=l.endPTS-l.startPTS;if(u<=0)return n.warn("Could not parse fragment "+e.sn+" "+o+" duration reliably ("+u+")"),t||!1;var d=i?0:J(a,e,l.startPTS,l.endPTS,l.startDTS,l.endDTS);return n.hls.trigger(s.Events.LEVEL_PTS_UPDATED,{details:a,level:r,drift:d,type:o,frag:e,start:l.startPTS,end:l.endPTS}),!0}return t}),!1)||(this.warn("Found no media in fragment "+e.sn+" of level "+r.id+" resetting transmuxer to fallback to playlist timing"),this.resetTransmuxer()),this.state=Ge,this.hls.trigger(s.Events.FRAG_PARSED,{frag:e,part:t})},h.resetTransmuxer=function(){this.transmuxer&&(this.transmuxer.destroy(),this.transmuxer=null)},n=i,(d=[{key:"state",get:function(){return this._state},set:function(e){var t=this._state;t!==e&&(this._state=e,this.log(t+"->"+e))}}])&&we(n.prototype,d),Object.defineProperty(n,"prototype",{writable:!1}),i}(he);function We(){return self.MediaSource||self.WebKitMediaSource}function Ye(){return self.SourceBuffer||self.WebKitSourceBuffer}var qe=function(){var e=ENTRY_MODULE,t={},r=function r(i){var n=t[i];if(void 0!==n)return n.exports;var a=t[i]={exports:{}};return e[i].call(a.exports,a,a.exports,r),a.exports};r.m=e,r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,{a:t}),t},r.d=function(e,t){for(var i in t)r.o(t,i)&&!r.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var i=r(ENTRY_MODULE);return i.default||i}.toString().split("ENTRY_MODULE"),ze="\\(\\s*(/\\*.*?\\*/)?\\s*.*?([\\.|\\-|\\+|\\w|/|@]+).*?\\)";function Xe(e){return(e+"").replace(/[.?*+^$[\]\\(){}|-]/g,"\\$&")}function Qe(e,t,i){var n={};n[i]=[];var a=t.toString().replace(/^"[^"]+"/,"function"),s=a.match(/^function\s?\w*\(\w+,\s*\w+,\s*(\w+)\)/)||a.match(/^\(\w+,\s*\w+,\s*(\w+)\)\s?\=\s?\>/);if(!s)return n;for(var o,l=s[1],u=new RegExp("(\\\\n|\\W)"+Xe(l)+ze,"g");o=u.exec(a);)"dll-reference"!==o[3]&&n[i].push(o[3]);for(u=new RegExp("\\("+Xe(l)+'\\("(dll-reference\\s([\\.|\\-|\\+|\\w|/|@]+))"\\)\\)'+ze,"g");o=u.exec(a);)e[o[2]]||(n[i].push(o[1]),e[o[2]]=r(o[1]).m),n[o[2]]=n[o[2]]||[],n[o[2]].push(o[4]);for(var d,h=Object.keys(n),f=0;f<h.length;f++)for(var c=0;c<n[h[f]].length;c++)d=n[h[f]][c],isNaN(1*d)||(n[h[f]][c]=1*n[h[f]][c]);return n}function $e(e){return Object.keys(e).reduce((function(t,r){return t||e[r].length>0}),!1)}function Ze(e,t,r,i){var n=e[i].map((function(e){return'"'+e+'": '+t[i][e].toString().replace(/^"[^"]+"/,"function")})).join(",");return qe[0]+"{"+n+"}"+qe[1]+'"'+r+'"'+qe[2]}var Je=r(764),et=r(729),tt=We()||{isTypeSupported:function(){return!1}},rt=function(){function e(e,t,i,n){var a=this;this.hls=void 0,this.id=void 0,this.observer=void 0,this.frag=null,this.part=null,this.useWorker=void 0,this.worker=void 0,this.onwmsg=void 0,this.transmuxer=null,this.onTransmuxComplete=void 0,this.onFlush=void 0;var u=e.config;this.hls=e,this.id=t,this.useWorker=!!u.enableWorker,this.onTransmuxComplete=i,this.onFlush=n;var d=function(e,t){(t=t||{}).frag=a.frag,t.id=a.id,a.hls.trigger(e,t)};this.observer=new et.EventEmitter,this.observer.on(s.Events.FRAG_DECRYPTED,d),this.observer.on(s.Events.ERROR,d);var h={mp4:tt.isTypeSupported("video/mp4"),mpeg:tt.isTypeSupported("audio/mpeg"),mp3:tt.isTypeSupported('audio/mp4; codecs="mp3"')},f=navigator.vendor;if(this.useWorker&&"undefined"!=typeof Worker){var c;l.logger.log("demuxing in webworker");try{c=this.worker=function(e,t){t=t||{};var i={main:r.m},n=t.all?{main:Object.keys(i.main)}:function(e,t){for(var r={main:[t]},i={main:[]},n={main:{}};$e(r);)for(var a=Object.keys(r),s=0;s<a.length;s++){var o=a[s],l=r[o].pop();if(n[o]=n[o]||{},!n[o][l]&&e[o][l]){n[o][l]=!0,i[o]=i[o]||[],i[o].push(l);for(var u=Qe(e,e[o][l],o),d=Object.keys(u),h=0;h<d.length;h++)r[d[h]]=r[d[h]]||[],r[d[h]]=r[d[h]].concat(u[d[h]])}}return i}(i,e),a="";Object.keys(n).filter((function(e){return"main"!==e})).forEach((function(e){for(var t=0;n[e][t];)t++;n[e].push(t),i[e][t]="(function(module, exports, __webpack_require__) { module.exports = __webpack_require__; })",a=a+"var "+e+" = ("+Ze(n,i,t,modules)+")();\n"})),a=a+"new (("+Ze(n,i,e,"main")+")())(self);";var s=new window.Blob([a],{type:"text/javascript"}),o=(window.URL||window.webkitURL||window.mozURL||window.msURL).createObjectURL(s),l=new window.Worker(o);return l.objectURL=o,l}(182),this.onwmsg=this.onWorkerMessage.bind(this),c.addEventListener("message",this.onwmsg),c.onerror=function(e){a.useWorker=!1,l.logger.warn("Exception in webworker, fallback to inline"),a.hls.trigger(s.Events.ERROR,{type:o.ErrorTypes.OTHER_ERROR,details:o.ErrorDetails.INTERNAL_EXCEPTION,fatal:!1,event:"demuxerWorker",error:new Error(e.message+" ("+e.filename+":"+e.lineno+")")})},c.postMessage({cmd:"init",typeSupported:h,vendor:f,id:t,config:JSON.stringify(u)})}catch(e){l.logger.warn("Error in worker:",e),l.logger.error("Error while initializing DemuxerWorker, fallback to inline"),c&&self.URL.revokeObjectURL(c.objectURL),this.transmuxer=new Je.default(this.observer,h,u,f,t),this.worker=null}}else this.transmuxer=new Je.default(this.observer,h,u,f,t)}var t=e.prototype;return t.destroy=function(){var e=this.worker;if(e)e.removeEventListener("message",this.onwmsg),e.terminate(),this.worker=null,this.onwmsg=void 0;else{var t=this.transmuxer;t&&(t.destroy(),this.transmuxer=null)}var r=this.observer;r&&r.removeAllListeners(),this.frag=null,this.observer=null,this.hls=null},t.push=function(e,t,r,i,n,a,s,o,u,d){var h,f,c=this;u.transmuxing.start=self.performance.now();var v=this.transmuxer,g=this.worker,p=a?a.start:n.start,m=n.decryptdata,y=this.frag,E=!(y&&n.cc===y.cc),T=!(y&&u.level===y.level),S=y?u.sn-y.sn:-1,b=this.part?u.part-this.part.index:-1,L=0===S&&u.id>1&&u.id===(null==y?void 0:y.stats.chunkCount),A=!T&&(1===S||0===S&&(1===b||L&&b<=0)),D=self.performance.now();(T||S||0===n.stats.parsing.start)&&(n.stats.parsing.start=D),!a||!b&&A||(a.stats.parsing.start=D);var R=!(y&&(null===(h=n.initSegment)||void 0===h?void 0:h.url)===(null===(f=y.initSegment)||void 0===f?void 0:f.url)),k=new Je.TransmuxState(E,A,o,T,p,R);if(!A||E||R){l.logger.log("[transmuxer-interface, "+n.type+"]: Starting new transmux session for sn: "+u.sn+" p: "+u.part+" level: "+u.level+" id: "+u.id+"\n discontinuity: "+E+"\n trackSwitch: "+T+"\n contiguous: "+A+"\n accurateTimeOffset: "+o+"\n timeOffset: "+p+"\n initSegmentChange: "+R);var _=new Je.TransmuxConfig(r,i,t,s,d);this.configureTransmuxer(_)}if(this.frag=n,this.part=a,g)g.postMessage({cmd:"demux",data:e,decryptdata:m,chunkMeta:u,state:k},e instanceof ArrayBuffer?[e]:[]);else if(v){var x=v.push(e,m,u,k);(0,Je.isPromise)(x)?x.then((function(e){c.handleTransmuxComplete(e)})):this.handleTransmuxComplete(x)}},t.flush=function(e){var t=this;e.transmuxing.start=self.performance.now();var r=this.transmuxer,i=this.worker;if(i)i.postMessage({cmd:"flush",chunkMeta:e});else if(r){var n=r.flush(e);(0,Je.isPromise)(n)?n.then((function(r){t.handleFlushResult(r,e)})):this.handleFlushResult(n,e)}},t.handleFlushResult=function(e,t){var r=this;e.forEach((function(e){r.handleTransmuxComplete(e)})),this.onFlush(t)},t.onWorkerMessage=function(e){var t=e.data,r=this.hls;switch(t.event){case"init":self.URL.revokeObjectURL(this.worker.objectURL);break;case"transmuxComplete":this.handleTransmuxComplete(t.data);break;case"flush":this.onFlush(t.data);break;case"workerLog":l.logger[t.data.logType]&&l.logger[t.data.logType](t.data.message);break;default:t.data=t.data||{},t.data.frag=this.frag,t.data.id=this.id,r.trigger(t.event,t.data)}},t.configureTransmuxer=function(e){var t=this.worker,r=this.transmuxer;t?t.postMessage({cmd:"configure",config:e}):r&&r.configure(e)},t.handleTransmuxComplete=function(e){e.chunkMeta.transmuxing.end=self.performance.now(),this.onTransmuxComplete(e)},e}(),it=function(){function e(e,t,r,i){this.config=void 0,this.media=null,this.fragmentTracker=void 0,this.hls=void 0,this.nudgeRetry=0,this.stallReported=!1,this.stalled=null,this.moved=!1,this.seeking=!1,this.config=e,this.media=t,this.fragmentTracker=r,this.hls=i}var t=e.prototype;return t.destroy=function(){this.media=null,this.hls=this.fragmentTracker=null},t.poll=function(e,t){var r=this.config,i=this.media,n=this.stalled;if(null!==i){var a=i.currentTime,s=i.seeking,o=this.seeking&&!s,u=!this.seeking&&s;if(this.seeking=s,a===e){if((u||o)&&(this.stalled=null),!(i.paused&&!s||i.ended||0===i.playbackRate)&&ce.getBuffered(i).length){var d=ce.bufferInfo(i,a,0),h=d.len>0,f=d.nextStart||0;if(h||f){if(s){var c=d.len>2,v=!f||t&&t.start<=a||f-a>2&&!this.fragmentTracker.getPartialFragment(a);if(c||v)return;this.moved=!1}if(!this.moved&&null!==this.stalled){var g,p=Math.max(f,d.start||0)-a,m=this.hls.levels?this.hls.levels[this.hls.currentLevel]:null,y=(null==m||null===(g=m.details)||void 0===g?void 0:g.live)?2*m.details.targetduration:2;if(p>0&&p<=y)return void this._trySkipBufferHole(null)}var E=self.performance.now();if(null!==n){var T=E-n;if(s||!(T>=250)||(this._reportStall(d),this.media)){var S=ce.bufferInfo(i,a,r.maxBufferHole);this._tryFixBufferStall(S,T)}}else this.stalled=E}}}else if(this.moved=!0,null!==n){if(this.stallReported){var b=self.performance.now()-n;l.logger.warn("playback not stuck anymore @"+a+", after "+Math.round(b)+"ms"),this.stallReported=!1}this.stalled=null,this.nudgeRetry=0}}},t._tryFixBufferStall=function(e,t){var r=this.config,i=this.fragmentTracker,n=this.media;if(null!==n){var a=n.currentTime,s=i.getPartialFragment(a);if(s&&(this._trySkipBufferHole(s)||!this.media))return;e.len>r.maxBufferHole&&t>1e3*r.highBufferWatchdogPeriod&&(l.logger.warn("Trying to nudge playhead over buffer-hole"),this.stalled=null,this._tryNudgeBuffer())}},t._reportStall=function(e){var t=this.hls,r=this.media;!this.stallReported&&r&&(this.stallReported=!0,l.logger.warn("Playback stalling at @"+r.currentTime+" due to low buffer ("+JSON.stringify(e)+")"),t.trigger(s.Events.ERROR,{type:o.ErrorTypes.MEDIA_ERROR,details:o.ErrorDetails.BUFFER_STALLED_ERROR,fatal:!1,buffer:e.len}))},t._trySkipBufferHole=function(e){var t=this.config,r=this.hls,i=this.media;if(null===i)return 0;for(var n=i.currentTime,a=0,u=ce.getBuffered(i),d=0;d<u.length;d++){var h=u.start(d);if(n+t.maxBufferHole>=a&&n<h){var f=Math.max(h+.05,i.currentTime+.1);return l.logger.warn("skipping hole, adjusting currentTime from "+n+" to "+f),this.moved=!0,this.stalled=null,i.currentTime=f,e&&r.trigger(s.Events.ERROR,{type:o.ErrorTypes.MEDIA_ERROR,details:o.ErrorDetails.BUFFER_SEEK_OVER_HOLE,fatal:!1,reason:"fragment loaded with buffer holes, seeking from "+n+" to "+f,frag:e}),f}a=u.end(d)}return 0},t._tryNudgeBuffer=function(){var e=this.config,t=this.hls,r=this.media,i=this.nudgeRetry;if(null!==r){var n=r.currentTime;if(this.nudgeRetry++,i<e.nudgeMaxRetry){var a=n+(i+1)*e.nudgeOffset;l.logger.warn("Nudging 'currentTime' from "+n+" to "+a),r.currentTime=a,t.trigger(s.Events.ERROR,{type:o.ErrorTypes.MEDIA_ERROR,details:o.ErrorDetails.BUFFER_NUDGE_ON_STALL,fatal:!1})}else l.logger.error("Playhead still not moving while enough data buffered @"+n+" after "+e.nudgeMaxRetry+" nudges"),t.trigger(s.Events.ERROR,{type:o.ErrorTypes.MEDIA_ERROR,details:o.ErrorDetails.BUFFER_STALLED_ERROR,fatal:!0})}},e}();function nt(e,t){for(var r=0;r<t.length;r++){var i=t[r];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}function at(e,t){return at=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},at(e,t)}var st=function(e){var t,r;function i(t,r){var i;return(i=e.call(this,t,r,"[stream-controller]")||this).audioCodecSwap=!1,i.gapController=null,i.level=-1,i._forceStartLoad=!1,i.altAudio=!1,i.audioOnly=!1,i.fragPlaying=null,i.onvplaying=null,i.onvseeked=null,i.fragLastKbps=0,i.couldBacktrack=!1,i.backtrackFragment=null,i.audioCodecSwitch=!1,i.videoBuffer=null,i._registerListeners(),i}r=e,(t=i).prototype=Object.create(r.prototype),t.prototype.constructor=t,at(t,r);var n,l,u=i.prototype;return u._registerListeners=function(){var e=this.hls;e.on(s.Events.MEDIA_ATTACHED,this.onMediaAttached,this),e.on(s.Events.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(s.Events.MANIFEST_LOADING,this.onManifestLoading,this),e.on(s.Events.MANIFEST_PARSED,this.onManifestParsed,this),e.on(s.Events.LEVEL_LOADING,this.onLevelLoading,this),e.on(s.Events.LEVEL_LOADED,this.onLevelLoaded,this),e.on(s.Events.FRAG_LOAD_EMERGENCY_ABORTED,this.onFragLoadEmergencyAborted,this),e.on(s.Events.ERROR,this.onError,this),e.on(s.Events.AUDIO_TRACK_SWITCHING,this.onAudioTrackSwitching,this),e.on(s.Events.AUDIO_TRACK_SWITCHED,this.onAudioTrackSwitched,this),e.on(s.Events.BUFFER_CREATED,this.onBufferCreated,this),e.on(s.Events.BUFFER_FLUSHED,this.onBufferFlushed,this),e.on(s.Events.LEVELS_UPDATED,this.onLevelsUpdated,this),e.on(s.Events.FRAG_BUFFERED,this.onFragBuffered,this)},u._unregisterListeners=function(){var e=this.hls;e.off(s.Events.MEDIA_ATTACHED,this.onMediaAttached,this),e.off(s.Events.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(s.Events.MANIFEST_LOADING,this.onManifestLoading,this),e.off(s.Events.MANIFEST_PARSED,this.onManifestParsed,this),e.off(s.Events.LEVEL_LOADED,this.onLevelLoaded,this),e.off(s.Events.FRAG_LOAD_EMERGENCY_ABORTED,this.onFragLoadEmergencyAborted,this),e.off(s.Events.ERROR,this.onError,this),e.off(s.Events.AUDIO_TRACK_SWITCHING,this.onAudioTrackSwitching,this),e.off(s.Events.AUDIO_TRACK_SWITCHED,this.onAudioTrackSwitched,this),e.off(s.Events.BUFFER_CREATED,this.onBufferCreated,this),e.off(s.Events.BUFFER_FLUSHED,this.onBufferFlushed,this),e.off(s.Events.LEVELS_UPDATED,this.onLevelsUpdated,this),e.off(s.Events.FRAG_BUFFERED,this.onFragBuffered,this)},u.onHandlerDestroying=function(){this._unregisterListeners(),this.onMediaDetaching()},u.startLoad=function(e){if(this.levels){var t=this.lastCurrentTime,r=this.hls;if(this.stopLoad(),this.setInterval(100),this.level=-1,this.fragLoadError=0,!this.startFragRequested){var i=r.startLevel;-1===i&&(r.config.testBandwidth&&this.levels.length>1?(i=0,this.bitrateTest=!0):i=r.nextAutoLevel),this.level=r.nextLoadLevel=i,this.loadedmetadata=!1}t>0&&-1===e&&(this.log("Override startPosition with lastCurrentTime @"+t.toFixed(3)),e=t),this.state=Fe,this.nextLoadPosition=this.startPosition=this.lastCurrentTime=e,this.tick()}else this._forceStartLoad=!0,this.state=Oe},u.stopLoad=function(){this._forceStartLoad=!1,e.prototype.stopLoad.call(this)},u.doTick=function(){switch(this.state){case Fe:this.doTickIdle();break;case je:var e,t=this.levels,r=this.level,i=null==t||null===(e=t[r])||void 0===e?void 0:e.details;if(i&&(!i.live||this.levelLastLoaded===this.level)){if(this.waitForCdnTuneIn(i))break;this.state=Fe;break}break;case Ue:var n,a=self.performance.now(),s=this.retryDate;(!s||a>=s||null!==(n=this.media)&&void 0!==n&&n.seeking)&&(this.log("retryDate reached, switch back to IDLE state"),this.resetStartWhenNotLoaded(this.level),this.state=Fe)}this.onTickEnd()},u.onTickEnd=function(){e.prototype.onTickEnd.call(this),this.checkBuffer(),this.checkFragmentChanged()},u.doTickIdle=function(){var e,t,r=this.hls,i=this.levelLastLoaded,n=this.levels,a=this.media,o=r.config,l=r.nextLoadLevel;if(null!==i&&(a||!this.startFragRequested&&o.startFragPrefetch)&&(!this.altAudio||!this.audioOnly)&&n&&n[l]){var u=n[l];this.level=r.nextLoadLevel=l;var d=u.details;if(!d||this.state===je||d.live&&this.levelLastLoaded!==l)this.state=je;else{var h=this.getMainFwdBufferInfo();if(null!==h&&!(h.len>=this.getMaxBufferLength(u.maxBitrate))){if(this._streamEnded(h,d)){var f={};return this.altAudio&&(f.type="video"),this.hls.trigger(s.Events.BUFFER_EOS,f),void(this.state=Ve)}this.backtrackFragment&&this.backtrackFragment.start>h.end&&(this.backtrackFragment=null);var c=this.backtrackFragment?this.backtrackFragment.start:h.end,v=this.getNextFragment(c,d);if(this.couldBacktrack&&!this.fragPrevious&&v&&"initSegment"!==v.sn&&this.fragmentTracker.getState(v)!==ae.OK){var g,m=(null!=(g=this.backtrackFragment)?g:v).sn-d.startSN,y=d.fragments[m-1];y&&v.cc===y.cc&&(v=y,this.fragmentTracker.removeFragment(y))}else this.backtrackFragment&&h.len&&(this.backtrackFragment=null);if(v&&this.fragmentTracker.getState(v)===ae.OK&&this.nextLoadPosition>c){var E=this.audioOnly&&!this.altAudio?p.ElementaryStreamTypes.AUDIO:p.ElementaryStreamTypes.VIDEO;a&&this.afterBufferFlushed(a,E,C.PlaylistLevelType.MAIN),v=this.getNextFragment(this.nextLoadPosition,d)}v&&(!v.initSegment||v.initSegment.data||this.bitrateTest||(v=v.initSegment),"identity"!==(null===(e=v.decryptdata)||void 0===e?void 0:e.keyFormat)||null!==(t=v.decryptdata)&&void 0!==t&&t.key?this.loadFragment(v,d,c):this.loadKey(v,d))}}}},u.loadFragment=function(t,r,i){var n,a=this.fragmentTracker.getState(t);this.fragCurrent=t,a===ae.NOT_LOADED?"initSegment"===t.sn?this._loadInitSegment(t):this.bitrateTest?(this.log("Fragment "+t.sn+" of level "+t.level+" is being downloaded to test bitrate and will not be buffered"),this._loadBitrateTestFrag(t)):(this.startFragRequested=!0,e.prototype.loadFragment.call(this,t,r,i)):a===ae.APPENDING?this.reduceMaxBufferLength(t.duration)&&this.fragmentTracker.removeFragment(t):0===(null===(n=this.media)||void 0===n?void 0:n.buffered.length)&&this.fragmentTracker.removeAllFragments()},u.getAppendedFrag=function(e){var t=this.fragmentTracker.getAppendedFrag(e,C.PlaylistLevelType.MAIN);return t&&"fragment"in t?t.fragment:t},u.getBufferedFrag=function(e){return this.fragmentTracker.getBufferedFrag(e,C.PlaylistLevelType.MAIN)},u.followingBufferedFrag=function(e){return e?this.getBufferedFrag(e.end+.5):null},u.immediateLevelSwitch=function(){this.abortCurrentFrag(),this.flushMainBuffer(0,Number.POSITIVE_INFINITY)},u.nextLevelSwitch=function(){var e=this.levels,t=this.media;if(null!=t&&t.readyState){var r,i=this.getAppendedFrag(t.currentTime);if(i&&i.start>1&&this.flushMainBuffer(0,i.start-1),!t.paused&&e){var n=e[this.hls.nextLoadLevel],a=this.fragLastKbps;r=a&&this.fragCurrent?this.fragCurrent.duration*n.maxBitrate/(1e3*a)+1:0}else r=0;var s=this.getBufferedFrag(t.currentTime+r);if(s){var o=this.followingBufferedFrag(s);if(o){this.abortCurrentFrag();var l=o.maxStartPTS?o.maxStartPTS:o.start,u=o.duration,d=Math.max(s.end,l+Math.min(Math.max(u-this.config.maxFragLookUpTolerance,.5*u),.75*u));this.flushMainBuffer(d,Number.POSITIVE_INFINITY)}}}},u.abortCurrentFrag=function(){var e=this.fragCurrent;switch(this.fragCurrent=null,this.backtrackFragment=null,null!=e&&e.loader&&e.loader.abort(),this.state){case Me:case Ne:case Ue:case Be:case Ge:this.state=Fe}this.nextLoadPosition=this.getLoadPosition()},u.flushMainBuffer=function(t,r){e.prototype.flushMainBuffer.call(this,t,r,this.altAudio?"video":null)},u.onMediaAttached=function(t,r){e.prototype.onMediaAttached.call(this,t,r);var i=r.media;this.onvplaying=this.onMediaPlaying.bind(this),this.onvseeked=this.onMediaSeeked.bind(this),i.addEventListener("playing",this.onvplaying),i.addEventListener("seeked",this.onvseeked),this.gapController=new it(this.config,i,this.fragmentTracker,this.hls)},u.onMediaDetaching=function(){var t=this.media;t&&this.onvplaying&&this.onvseeked&&(t.removeEventListener("playing",this.onvplaying),t.removeEventListener("seeked",this.onvseeked),this.onvplaying=this.onvseeked=null,this.videoBuffer=null),this.fragPlaying=null,this.gapController&&(this.gapController.destroy(),this.gapController=null),e.prototype.onMediaDetaching.call(this)},u.onMediaPlaying=function(){this.tick()},u.onMediaSeeked=function(){var e=this.media,t=e?e.currentTime:null;(0,a.isFiniteNumber)(t)&&this.log("Media seeked to "+t.toFixed(3)),this.tick()},u.onManifestLoading=function(){this.log("Trigger BUFFER_RESET"),this.hls.trigger(s.Events.BUFFER_RESET,void 0),this.fragmentTracker.removeAllFragments(),this.couldBacktrack=!1,this.startPosition=this.lastCurrentTime=0,this.fragPlaying=null,this.backtrackFragment=null},u.onManifestParsed=function(e,t){var r,i,n,a=!1,s=!1;t.levels.forEach((function(e){(r=e.audioCodec)&&(-1!==r.indexOf("mp4a.40.2")&&(a=!0),-1!==r.indexOf("mp4a.40.5")&&(s=!0))})),this.audioCodecSwitch=a&&s&&!("function"==typeof(null==(n=Ye())||null===(i=n.prototype)||void 0===i?void 0:i.changeType)),this.audioCodecSwitch&&this.log("Both AAC/HE-AAC audio found in levels; declaring level codec as HE-AAC"),this.levels=t.levels,this.startFragRequested=!1},u.onLevelLoading=function(e,t){var r=this.levels;if(r&&this.state===Fe){var i=r[t.level];(!i.details||i.details.live&&this.levelLastLoaded!==t.level||this.waitForCdnTuneIn(i.details))&&(this.state=je)}},u.onLevelLoaded=function(e,t){var r,i=this.levels,n=t.level,a=t.details,o=a.totalduration;if(i){this.log("Level "+n+" loaded ["+a.startSN+","+a.endSN+"], cc ["+a.startCC+", "+a.endCC+"] duration:"+o);var l=this.fragCurrent;!l||this.state!==Ne&&this.state!==Ue||l.level!==t.level&&l.loader&&(this.state=Fe,this.backtrackFragment=null,l.loader.abort());var u=i[n],d=0;if(a.live||null!==(r=u.details)&&void 0!==r&&r.live){if(a.fragments[0]||(a.deltaUpdateFailed=!0),a.deltaUpdateFailed)return;d=this.alignPlaylists(a,u.details)}if(u.details=a,this.levelLastLoaded=n,this.hls.trigger(s.Events.LEVEL_UPDATED,{details:a,level:n}),this.state===je){if(this.waitForCdnTuneIn(a))return;this.state=Fe}this.startFragRequested?a.live&&this.synchronizeToLiveEdge(a):this.setStartPosition(a,d),this.tick()}else this.warn("Levels were reset while loading level "+n)},u._handleFragmentLoadProgress=function(e){var t,r=e.frag,i=e.part,n=e.payload,a=this.levels;if(a){var s=a[r.level],o=s.details;if(o){var l=s.videoCodec,u=o.PTSKnown||!o.live,d=null===(t=r.initSegment)||void 0===t?void 0:t.data,h=this._getAudioCodec(s),f=this.transmuxer=this.transmuxer||new rt(this.hls,C.PlaylistLevelType.MAIN,this._handleTransmuxComplete.bind(this),this._handleTransmuxerFlush.bind(this)),c=i?i.index:-1,v=-1!==c,g=new ve(r.level,r.sn,r.stats.chunkCount,n.byteLength,c,v),p=this.initPTS[r.cc];f.push(n,d,h,l,r,i,o.totalduration,u,g,p)}else this.warn("Dropping fragment "+r.sn+" of level "+r.level+" after level details were reset")}else this.warn("Levels were reset while fragment load was in progress. Fragment "+r.sn+" of level "+r.level+" will not be buffered")},u.onAudioTrackSwitching=function(e,t){var r=this.altAudio,i=!!t.url,n=t.id;if(!i){if(this.mediaBuffer!==this.media){this.log("Switching on main audio, use media.buffered to schedule main fragment loading"),this.mediaBuffer=this.media;var a=this.fragCurrent;null!=a&&a.loader&&(this.log("Switching to main audio track, cancel main fragment load"),a.loader.abort()),this.resetTransmuxer(),this.resetLoadingState()}else this.audioOnly&&this.resetTransmuxer();var o=this.hls;r&&o.trigger(s.Events.BUFFER_FLUSHING,{startOffset:0,endOffset:Number.POSITIVE_INFINITY,type:"audio"}),o.trigger(s.Events.AUDIO_TRACK_SWITCHED,{id:n})}},u.onAudioTrackSwitched=function(e,t){var r=t.id,i=!!this.hls.audioTracks[r].url;if(i){var n=this.videoBuffer;n&&this.mediaBuffer!==n&&(this.log("Switching on alternate audio, use video.buffered to schedule main fragment loading"),this.mediaBuffer=n)}this.altAudio=i,this.tick()},u.onBufferCreated=function(e,t){var r,i,n=t.tracks,a=!1;for(var s in n){var o=n[s];if("main"===o.id){if(i=s,r=o,"video"===s){var l=n[s];l&&(this.videoBuffer=l.buffer)}}else a=!0}a&&r?(this.log("Alternate track found, use "+i+".buffered to schedule main fragment loading"),this.mediaBuffer=r.buffer):this.mediaBuffer=this.media},u.onFragBuffered=function(e,t){var r=t.frag,i=t.part;if(!r||r.type===C.PlaylistLevelType.MAIN){if(this.fragContextChanged(r))return this.warn("Fragment "+r.sn+(i?" p: "+i.index:"")+" of level "+r.level+" finished buffering, but was aborted. state: "+this.state),void(this.state===Ge&&(this.state=Fe));var n=i?i.stats:r.stats;this.fragLastKbps=Math.round(8*n.total/(n.buffering.end-n.loading.first)),"initSegment"!==r.sn&&(this.fragPrevious=r),this.fragBufferedComplete(r,i)}},u.onError=function(e,t){switch(t.details){case o.ErrorDetails.FRAG_LOAD_ERROR:case o.ErrorDetails.FRAG_LOAD_TIMEOUT:case o.ErrorDetails.KEY_LOAD_ERROR:case o.ErrorDetails.KEY_LOAD_TIMEOUT:this.onFragmentOrKeyLoadError(C.PlaylistLevelType.MAIN,t);break;case o.ErrorDetails.LEVEL_LOAD_ERROR:case o.ErrorDetails.LEVEL_LOAD_TIMEOUT:this.state!==He&&(t.fatal?(this.warn(""+t.details),this.state=He):t.levelRetry||this.state!==je||(this.state=Fe));break;case o.ErrorDetails.BUFFER_FULL_ERROR:if("main"===t.parent&&(this.state===Be||this.state===Ge)){var r=!0,i=this.getFwdBufferInfo(this.media,C.PlaylistLevelType.MAIN);i&&i.len>.5&&(r=!this.reduceMaxBufferLength(i.len)),r&&(this.warn("buffer full error also media.currentTime is not buffered, flush main"),this.immediateLevelSwitch()),this.resetLoadingState()}}},u.checkBuffer=function(){var e=this.media,t=this.gapController;if(e&&t&&e.readyState){if(this.loadedmetadata||!ce.getBuffered(e).length){var r=this.state!==Fe?this.fragCurrent:null;t.poll(this.lastCurrentTime,r)}this.lastCurrentTime=e.currentTime}},u.onFragLoadEmergencyAborted=function(){this.state=Fe,this.loadedmetadata||(this.startFragRequested=!1,this.nextLoadPosition=this.startPosition),this.tickImmediate()},u.onBufferFlushed=function(e,t){var r=t.type;if(r!==p.ElementaryStreamTypes.AUDIO||this.audioOnly&&!this.altAudio){var i=(r===p.ElementaryStreamTypes.VIDEO?this.videoBuffer:this.mediaBuffer)||this.media;this.afterBufferFlushed(i,r,C.PlaylistLevelType.MAIN)}},u.onLevelsUpdated=function(e,t){this.levels=t.levels},u.swapAudioCodec=function(){this.audioCodecSwap=!this.audioCodecSwap},u.seekToStartPos=function(){var e=this.media;if(e){var t=e.currentTime,r=this.startPosition;if(r>=0&&t<r){if(e.seeking)return void this.log("could not seek to "+r+", already seeking at "+t);var i=ce.getBuffered(e),n=(i.length?i.start(0):0)-r;n>0&&(n<this.config.maxBufferHole||n<this.config.maxFragLookUpTolerance)&&(this.log("adjusting start position by "+n+" to match buffer start"),r+=n,this.startPosition=r),this.log("seek to target start position "+r+" from current time "+t),e.currentTime=r}}},u._getAudioCodec=function(e){var t=this.config.defaultAudioCodec||e.audioCodec;return this.audioCodecSwap&&t&&(this.log("Swapping audio codec"),t=-1!==t.indexOf("mp4a.40.5")?"mp4a.40.2":"mp4a.40.5"),t},u._loadBitrateTestFrag=function(e){var t=this;e.bitrateTest=!0,this._doFragLoad(e).then((function(r){var i=t.hls;if(r&&!i.nextLoadLevel&&!t.fragContextChanged(e)){t.fragLoadError=0,t.state=Fe,t.startFragRequested=!1,t.bitrateTest=!1;var n=e.stats;n.parsing.start=n.parsing.end=n.buffering.start=n.buffering.end=self.performance.now(),i.trigger(s.Events.FRAG_LOADED,r),e.bitrateTest=!1}}))},u._handleTransmuxComplete=function(e){var t,r="main",i=this.hls,n=e.remuxResult,o=e.chunkMeta,l=this.getCurrentContext(o);if(!l)return this.warn("The loading context changed while buffering fragment "+o.sn+" of level "+o.level+". This chunk will not be buffered."),void this.resetStartWhenNotLoaded(o.level);var u=l.frag,d=l.part,h=l.level,f=n.video,c=n.text,v=n.id3,g=n.initSegment,m=h.details,y=this.altAudio?void 0:n.audio;if(!this.fragContextChanged(u)){if(this.state=Be,g){g.tracks&&(this._bufferInitSegment(h,g.tracks,u,o),i.trigger(s.Events.FRAG_PARSING_INIT_SEGMENT,{frag:u,id:r,tracks:g.tracks}));var E=g.initPTS,T=g.timescale;(0,a.isFiniteNumber)(E)&&(this.initPTS[u.cc]=E,i.trigger(s.Events.INIT_PTS_FOUND,{frag:u,id:r,initPTS:E,timescale:T}))}if(f&&!1!==n.independent){if(m){var S=f.startPTS,b=f.endPTS,L=f.startDTS,A=f.endDTS;if(d)d.elementaryStreams[f.type]={startPTS:S,endPTS:b,startDTS:L,endDTS:A};else if(f.firstKeyFrame&&f.independent&&(this.couldBacktrack=!0),f.dropped&&f.independent){var D=this.getMainFwdBufferInfo();if((D?D.end:this.getLoadPosition())+this.config.maxBufferHole<(f.firstKeyFramePTS?f.firstKeyFramePTS:S)-this.config.maxBufferHole)return void this.backtrack(u);u.setElementaryStreamInfo(f.type,u.start,b,u.start,A,!0)}u.setElementaryStreamInfo(f.type,S,b,L,A),this.backtrackFragment&&(this.backtrackFragment=u),this.bufferFragmentData(f,u,d,o)}}else if(!1===n.independent)return void this.backtrack(u);if(y){var R=y.startPTS,k=y.endPTS,_=y.startDTS,x=y.endDTS;d&&(d.elementaryStreams[p.ElementaryStreamTypes.AUDIO]={startPTS:R,endPTS:k,startDTS:_,endDTS:x}),u.setElementaryStreamInfo(p.ElementaryStreamTypes.AUDIO,R,k,_,x),this.bufferFragmentData(y,u,d,o)}if(m&&null!=v&&null!==(t=v.samples)&&void 0!==t&&t.length){var I={id:r,frag:u,details:m,samples:v.samples};i.trigger(s.Events.FRAG_PARSING_METADATA,I)}if(m&&c){var w={id:r,frag:u,details:m,samples:c.samples};i.trigger(s.Events.FRAG_PARSING_USERDATA,w)}}},u._bufferInitSegment=function(e,t,r,i){var n=this;if(this.state===Be){this.audioOnly=!!t.audio&&!t.video,this.altAudio&&!this.audioOnly&&delete t.audio;var a=t.audio,o=t.video,l=t.audiovideo;if(a){var u=e.audioCodec,d=navigator.userAgent.toLowerCase();this.audioCodecSwitch&&(u&&(u=-1!==u.indexOf("mp4a.40.5")?"mp4a.40.2":"mp4a.40.5"),1!==a.metadata.channelCount&&-1===d.indexOf("firefox")&&(u="mp4a.40.5")),-1!==d.indexOf("android")&&"audio/mpeg"!==a.container&&(u="mp4a.40.2",this.log("Android: force audio codec to "+u)),e.audioCodec&&e.audioCodec!==u&&this.log('Swapping manifest audio codec "'+e.audioCodec+'" for "'+u+'"'),a.levelCodec=u,a.id="main",this.log("Init audio buffer, container:"+a.container+", codecs[selected/level/parsed]=["+(u||"")+"/"+(e.audioCodec||"")+"/"+a.codec+"]")}o&&(o.levelCodec=e.videoCodec,o.id="main",this.log("Init video buffer, container:"+o.container+", codecs[level/parsed]=["+(e.videoCodec||"")+"/"+o.codec+"]")),l&&this.log("Init audiovideo buffer, container:"+l.container+", codecs[level/parsed]=["+(e.attrs.CODECS||"")+"/"+l.codec+"]"),this.hls.trigger(s.Events.BUFFER_CODECS,t),Object.keys(t).forEach((function(e){var a=t[e].initSegment;null!=a&&a.byteLength&&n.hls.trigger(s.Events.BUFFER_APPENDING,{type:e,data:a,frag:r,part:null,chunkMeta:i,parent:r.type})})),this.tick()}},u.getMainFwdBufferInfo=function(){return this.getFwdBufferInfo(this.mediaBuffer?this.mediaBuffer:this.media,C.PlaylistLevelType.MAIN)},u.backtrack=function(e){this.couldBacktrack=!0,this.backtrackFragment=e,this.resetTransmuxer(),this.flushBufferGap(e),this.fragmentTracker.removeFragment(e),this.fragPrevious=null,this.nextLoadPosition=e.start,this.state=Fe},u.checkFragmentChanged=function(){var e=this.media,t=null;if(e&&e.readyState>1&&!1===e.seeking){var r=e.currentTime;if(ce.isBuffered(e,r)?t=this.getAppendedFrag(r):ce.isBuffered(e,r+.1)&&(t=this.getAppendedFrag(r+.1)),t){this.backtrackFragment=null;var i=this.fragPlaying,n=t.level;i&&t.sn===i.sn&&i.level===n&&t.urlId===i.urlId||(this.hls.trigger(s.Events.FRAG_CHANGED,{frag:t}),i&&i.level===n||this.hls.trigger(s.Events.LEVEL_SWITCHED,{level:n}),this.fragPlaying=t)}}},n=i,(l=[{key:"nextLevel",get:function(){var e=this.nextBufferedFrag;return e?e.level:-1}},{key:"currentFrag",get:function(){var e=this.media;return e?this.fragPlaying||this.getAppendedFrag(e.currentTime):null}},{key:"currentProgramDateTime",get:function(){var e=this.media;if(e){var t=e.currentTime,r=this.currentFrag;if(r&&(0,a.isFiniteNumber)(t)&&(0,a.isFiniteNumber)(r.programDateTime)){var i=r.programDateTime+1e3*(t-r.start);return new Date(i)}}return null}},{key:"currentLevel",get:function(){var e=this.currentFrag;return e?e.level:-1}},{key:"nextBufferedFrag",get:function(){var e=this.currentFrag;return e?this.followingBufferedFrag(e):null}},{key:"forceStartLoad",get:function(){return this._forceStartLoad}}])&&nt(n.prototype,l),Object.defineProperty(n,"prototype",{writable:!1}),i}(Ke);const ot=function(){function e(e,t,r){void 0===t&&(t=0),void 0===r&&(r=0),this.halfLife=void 0,this.alpha_=void 0,this.estimate_=void 0,this.totalWeight_=void 0,this.halfLife=e,this.alpha_=e?Math.exp(Math.log(.5)/e):0,this.estimate_=t,this.totalWeight_=r}var t=e.prototype;return t.sample=function(e,t){var r=Math.pow(this.alpha_,e);this.estimate_=t*(1-r)+r*this.estimate_,this.totalWeight_+=e},t.getTotalWeight=function(){return this.totalWeight_},t.getEstimate=function(){if(this.alpha_){var e=1-Math.pow(this.alpha_,this.totalWeight_);if(e)return this.estimate_/e}return this.estimate_},e}(),lt=function(){function e(e,t,r){this.defaultEstimate_=void 0,this.minWeight_=void 0,this.minDelayMs_=void 0,this.slow_=void 0,this.fast_=void 0,this.defaultEstimate_=r,this.minWeight_=.001,this.minDelayMs_=50,this.slow_=new ot(e),this.fast_=new ot(t)}var t=e.prototype;return t.update=function(e,t){var r=this.slow_,i=this.fast_;this.slow_.halfLife!==e&&(this.slow_=new ot(e,r.getEstimate(),r.getTotalWeight())),this.fast_.halfLife!==t&&(this.fast_=new ot(t,i.getEstimate(),i.getTotalWeight()))},t.sample=function(e,t){var r=(e=Math.max(e,this.minDelayMs_))/1e3,i=8*t/r;this.fast_.sample(r,i),this.slow_.sample(r,i)},t.canEstimate=function(){var e=this.fast_;return e&&e.getTotalWeight()>=this.minWeight_},t.getEstimate=function(){return this.canEstimate()?Math.min(this.fast_.getEstimate(),this.slow_.getEstimate()):this.defaultEstimate_},t.destroy=function(){},e}();function ut(e,t){for(var r=0;r<t.length;r++){var i=t[r];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}const dt=function(){function e(e){this.hls=void 0,this.lastLoadedFragLevel=0,this._nextAutoLevel=-1,this.timer=void 0,this.onCheck=this._abandonRulesCheck.bind(this),this.fragCurrent=null,this.partCurrent=null,this.bitrateTestDelay=0,this.bwEstimator=void 0,this.hls=e;var t=e.config;this.bwEstimator=new lt(t.abrEwmaSlowVoD,t.abrEwmaFastVoD,t.abrEwmaDefaultEstimate),this.registerListeners()}var t,r,i=e.prototype;return i.registerListeners=function(){var e=this.hls;e.on(s.Events.FRAG_LOADING,this.onFragLoading,this),e.on(s.Events.FRAG_LOADED,this.onFragLoaded,this),e.on(s.Events.FRAG_BUFFERED,this.onFragBuffered,this),e.on(s.Events.LEVEL_LOADED,this.onLevelLoaded,this),e.on(s.Events.ERROR,this.onError,this)},i.unregisterListeners=function(){var e=this.hls;e.off(s.Events.FRAG_LOADING,this.onFragLoading,this),e.off(s.Events.FRAG_LOADED,this.onFragLoaded,this),e.off(s.Events.FRAG_BUFFERED,this.onFragBuffered,this),e.off(s.Events.LEVEL_LOADED,this.onLevelLoaded,this),e.off(s.Events.ERROR,this.onError,this)},i.destroy=function(){this.unregisterListeners(),this.clearTimer(),this.hls=this.onCheck=null,this.fragCurrent=this.partCurrent=null},i.onFragLoading=function(e,t){var r,i=t.frag;i.type===C.PlaylistLevelType.MAIN&&(this.timer||(this.fragCurrent=i,this.partCurrent=null!=(r=t.part)?r:null,this.timer=self.setInterval(this.onCheck,100)))},i.onLevelLoaded=function(e,t){var r=this.hls.config;t.details.live?this.bwEstimator.update(r.abrEwmaSlowLive,r.abrEwmaFastLive):this.bwEstimator.update(r.abrEwmaSlowVoD,r.abrEwmaFastVoD)},i._abandonRulesCheck=function(){var e=this.fragCurrent,t=this.partCurrent,r=this.hls,i=r.autoLevelEnabled,n=(r.config,r.media);if(e&&n){var o=t?t.stats:e.stats,u=t?t.duration:e.duration;if(o.aborted||o.loaded&&o.loaded===o.total||0===e.level)return this.clearTimer(),void(this._nextAutoLevel=-1);if(i&&!n.paused&&n.playbackRate&&n.readyState){var d=r.mainForwardBufferInfo;if(null!==d){var h=performance.now()-o.loading.start,f=Math.abs(n.playbackRate);if(!(h<=500*u/f)){var c=o.loaded&&o.loading.first,v=this.bwEstimator.getEstimate(),g=r.levels,p=r.minAutoLevel,m=g[e.level],y=o.total||Math.max(o.loaded,Math.round(u*m.maxBitrate/8)),E=c?1e3*o.loaded/h:0,T=E?(y-o.loaded)/E:8*y/v,S=d.len/f;if(!(T<=S)){var b,L=Number.POSITIVE_INFINITY;for(b=e.level-1;b>p;b--){var A=g[b].maxBitrate;if((L=E?u*A/(6.4*E):u*A/v)<S)break}L>=T||(l.logger.warn("Fragment "+e.sn+(t?" part "+t.index:"")+" of level "+e.level+" is loading too slowly and will cause an underbuffer; aborting and switching to level "+b+"\n Current BW estimate: "+((0,a.isFiniteNumber)(v)?(v/1024).toFixed(3):"Unknown")+" Kb/s\n Estimated load time for current fragment: "+T.toFixed(3)+" s\n Estimated load time for the next fragment: "+L.toFixed(3)+" s\n Time to underbuffer: "+S.toFixed(3)+" s"),r.nextLoadLevel=b,c&&this.bwEstimator.sample(h,o.loaded),this.clearTimer(),e.loader&&(this.fragCurrent=this.partCurrent=null,e.loader.abort()),r.trigger(s.Events.FRAG_LOAD_EMERGENCY_ABORTED,{frag:e,part:t,stats:o}))}}}}}},i.onFragLoaded=function(e,t){var r=t.frag,i=t.part;if(r.type===C.PlaylistLevelType.MAIN&&(0,a.isFiniteNumber)(r.sn)){var n=i?i.stats:r.stats,o=i?i.duration:r.duration;if(this.clearTimer(),this.lastLoadedFragLevel=r.level,this._nextAutoLevel=-1,this.hls.config.abrMaxWithRealBitrate){var l=this.hls.levels[r.level],u=(l.loaded?l.loaded.bytes:0)+n.loaded,d=(l.loaded?l.loaded.duration:0)+o;l.loaded={bytes:u,duration:d},l.realBitrate=Math.round(8*u/d)}if(r.bitrateTest){var h={stats:n,frag:r,part:i,id:r.type};this.onFragBuffered(s.Events.FRAG_BUFFERED,h)}}},i.onFragBuffered=function(e,t){var r=t.frag,i=t.part,n=i?i.stats:r.stats;if(!n.aborted&&r.type===C.PlaylistLevelType.MAIN&&"initSegment"!==r.sn){var a=n.parsing.end-n.loading.start;this.bwEstimator.sample(a,n.loaded),n.bwEstimate=this.bwEstimator.getEstimate(),r.bitrateTest?this.bitrateTestDelay=a/1e3:this.bitrateTestDelay=0}},i.onError=function(e,t){switch(t.details){case o.ErrorDetails.FRAG_LOAD_ERROR:case o.ErrorDetails.FRAG_LOAD_TIMEOUT:this.clearTimer()}},i.clearTimer=function(){self.clearInterval(this.timer),this.timer=void 0},i.getNextABRAutoLevel=function(){var e=this.fragCurrent,t=this.partCurrent,r=this.hls,i=r.maxAutoLevel,n=r.config,a=r.minAutoLevel,s=r.media,o=t?t.duration:e?e.duration:0,u=(s&&s.currentTime,s&&0!==s.playbackRate?Math.abs(s.playbackRate):1),d=this.bwEstimator?this.bwEstimator.getEstimate():n.abrEwmaDefaultEstimate,h=r.mainForwardBufferInfo,f=(h?h.len:0)/u,c=this.findBestLevel(d,a,i,f,n.abrBandWidthFactor,n.abrBandWidthUpFactor);if(c>=0)return c;l.logger.trace((f?"rebuffering expected":"buffer is empty")+", finding optimal quality level");var v=o?Math.min(o,n.maxStarvationDelay):n.maxStarvationDelay,g=n.abrBandWidthFactor,p=n.abrBandWidthUpFactor;if(!f){var m=this.bitrateTestDelay;m&&(v=(o?Math.min(o,n.maxLoadingDelay):n.maxLoadingDelay)-m,l.logger.trace("bitrate test took "+Math.round(1e3*m)+"ms, set first fragment max fetchDuration to "+Math.round(1e3*v)+" ms"),g=p=1)}return c=this.findBestLevel(d,a,i,f+v,g,p),Math.max(c,0)},i.findBestLevel=function(e,t,r,i,n,s){for(var o,u=this.fragCurrent,d=this.partCurrent,h=this.lastLoadedFragLevel,f=this.hls.levels,c=f[h],v=!(null==c||null===(o=c.details)||void 0===o||!o.live),g=null==c?void 0:c.codecSet,p=d?d.duration:u?u.duration:0,m=r;m>=t;m--){var y=f[m];if(y&&(!g||y.codecSet===g)){var E,T=y.details,S=(d?null==T?void 0:T.partTarget:null==T?void 0:T.averagetargetduration)||p;E=m<=h?n*e:s*e;var b=f[m].maxBitrate,L=b*S/E;if(l.logger.trace("level/adjustedbw/bitrate/avgDuration/maxFetchDuration/fetchDuration: "+m+"/"+Math.round(E)+"/"+b+"/"+S+"/"+i+"/"+L),E>b&&(0===L||!(0,a.isFiniteNumber)(L)||v&&!this.bitrateTestDelay||L<i))return m}}return-1},t=e,(r=[{key:"nextAutoLevel",get:function(){var e=this._nextAutoLevel,t=this.bwEstimator;if(-1!==e&&!t.canEstimate())return e;var r=this.getNextABRAutoLevel();return-1!==e&&this.hls.levels[r].loadError?e:(-1!==e&&(r=Math.min(e,r)),r)},set:function(e){this._nextAutoLevel=e}}])&&ut(t.prototype,r),Object.defineProperty(t,"prototype",{writable:!1}),e}();var ht=r(514),ft=r.n(ht),ct=function(){function e(e){this.buffers=void 0,this.queues={video:[],audio:[],audiovideo:[]},this.buffers=e}var t=e.prototype;return t.append=function(e,t){var r=this.queues[t];r.push(e),1===r.length&&this.buffers[t]&&this.executeNext(t)},t.insertAbort=function(e,t){this.queues[t].unshift(e),this.executeNext(t)},t.appendBlocker=function(e){var t,r=new Promise((function(e){t=e})),i={execute:t,onStart:function(){},onComplete:function(){},onError:function(){}};return this.append(i,e),r},t.executeNext=function(e){var t=this.buffers,r=this.queues,i=t[e],n=r[e];if(n.length){var a=n[0];try{a.execute()}catch(t){l.logger.warn("[buffer-operation-queue]: Unhandled exception executing the current operation"),a.onError(t),i&&i.updating||(n.shift(),this.executeNext(e))}}},t.shiftAndExecuteNext=function(e){this.queues[e].shift(),this.executeNext(e)},t.current=function(e){return this.queues[e][0]},e}(),vt=We(),gt=/([ha]vc.)(?:\.[^.,]+)+/,pt=function(){function e(e){var t=this;this.details=null,this._objectUrl=null,this.operationQueue=void 0,this.listeners=void 0,this.hls=void 0,this.bufferCodecEventsExpected=0,this._bufferCodecEventsTotal=0,this.media=null,this.mediaSource=null,this.appendError=0,this.tracks={},this.pendingTracks={},this.sourceBuffer=void 0,this._onMediaSourceOpen=function(){var e=t.hls,r=t.media,i=t.mediaSource;l.logger.log("[buffer-controller]: Media source opened"),r&&(t.updateMediaElementDuration(),e.trigger(s.Events.MEDIA_ATTACHED,{media:r})),i&&i.removeEventListener("sourceopen",t._onMediaSourceOpen),t.checkPendingTracks()},this._onMediaSourceClose=function(){l.logger.log("[buffer-controller]: Media source closed")},this._onMediaSourceEnded=function(){l.logger.log("[buffer-controller]: Media source ended")},this.hls=e,this._initSourceBuffer(),this.registerListeners()}var t=e.prototype;return t.hasSourceTypes=function(){return this.getSourceBufferTypes().length>0||Object.keys(this.pendingTracks).length>0},t.destroy=function(){this.unregisterListeners(),this.details=null},t.registerListeners=function(){var e=this.hls;e.on(s.Events.MEDIA_ATTACHING,this.onMediaAttaching,this),e.on(s.Events.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(s.Events.MANIFEST_PARSED,this.onManifestParsed,this),e.on(s.Events.BUFFER_RESET,this.onBufferReset,this),e.on(s.Events.BUFFER_APPENDING,this.onBufferAppending,this),e.on(s.Events.BUFFER_CODECS,this.onBufferCodecs,this),e.on(s.Events.BUFFER_EOS,this.onBufferEos,this),e.on(s.Events.BUFFER_FLUSHING,this.onBufferFlushing,this),e.on(s.Events.LEVEL_UPDATED,this.onLevelUpdated,this),e.on(s.Events.FRAG_PARSED,this.onFragParsed,this),e.on(s.Events.FRAG_CHANGED,this.onFragChanged,this)},t.unregisterListeners=function(){var e=this.hls;e.off(s.Events.MEDIA_ATTACHING,this.onMediaAttaching,this),e.off(s.Events.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(s.Events.MANIFEST_PARSED,this.onManifestParsed,this),e.off(s.Events.BUFFER_RESET,this.onBufferReset,this),e.off(s.Events.BUFFER_APPENDING,this.onBufferAppending,this),e.off(s.Events.BUFFER_CODECS,this.onBufferCodecs,this),e.off(s.Events.BUFFER_EOS,this.onBufferEos,this),e.off(s.Events.BUFFER_FLUSHING,this.onBufferFlushing,this),e.off(s.Events.LEVEL_UPDATED,this.onLevelUpdated,this),e.off(s.Events.FRAG_PARSED,this.onFragParsed,this),e.off(s.Events.FRAG_CHANGED,this.onFragChanged,this)},t._initSourceBuffer=function(){this.sourceBuffer={},this.operationQueue=new ct(this.sourceBuffer),this.listeners={audio:[],video:[],audiovideo:[]}},t.onManifestParsed=function(e,t){var r=2;(t.audio&&!t.video||!t.altAudio)&&(r=1),this.bufferCodecEventsExpected=this._bufferCodecEventsTotal=r,this.details=null,l.logger.log(this.bufferCodecEventsExpected+" bufferCodec event(s) expected")},t.onMediaAttaching=function(e,t){var r=this.media=t.media;if(r&&vt){var i=this.mediaSource=new vt;i.addEventListener("sourceopen",this._onMediaSourceOpen),i.addEventListener("sourceended",this._onMediaSourceEnded),i.addEventListener("sourceclose",this._onMediaSourceClose),r.src=self.URL.createObjectURL(i),this._objectUrl=r.src}},t.onMediaDetaching=function(){var e=this.media,t=this.mediaSource,r=this._objectUrl;if(t){if(l.logger.log("[buffer-controller]: media source detaching"),"open"===t.readyState)try{t.endOfStream()}catch(e){l.logger.warn("[buffer-controller]: onMediaDetaching: "+e.message+" while calling endOfStream")}this.onBufferReset(),t.removeEventListener("sourceopen",this._onMediaSourceOpen),t.removeEventListener("sourceended",this._onMediaSourceEnded),t.removeEventListener("sourceclose",this._onMediaSourceClose),e&&(r&&self.URL.revokeObjectURL(r),e.src===r?(e.removeAttribute("src"),e.load()):l.logger.warn("[buffer-controller]: media.src was changed by a third party - skip cleanup")),this.mediaSource=null,this.media=null,this._objectUrl=null,this.bufferCodecEventsExpected=this._bufferCodecEventsTotal,this.pendingTracks={},this.tracks={}}this.hls.trigger(s.Events.MEDIA_DETACHED,void 0)},t.onBufferReset=function(){var e=this;this.getSourceBufferTypes().forEach((function(t){var r=e.sourceBuffer[t];try{r&&(e.removeBufferListeners(t),e.mediaSource&&e.mediaSource.removeSourceBuffer(r),e.sourceBuffer[t]=void 0)}catch(e){l.logger.warn("[buffer-controller]: Failed to reset the "+t+" buffer",e)}})),this._initSourceBuffer()},t.onBufferCodecs=function(e,t){var r=this,i=this.getSourceBufferTypes().length;Object.keys(t).forEach((function(e){if(i){var n=r.tracks[e];if(n&&"function"==typeof n.buffer.changeType){var a=t[e],s=a.id,o=a.codec,u=a.levelCodec,d=a.container,h=a.metadata,f=(n.levelCodec||n.codec).replace(gt,"$1"),c=(u||o).replace(gt,"$1");if(f!==c){var v=d+";codecs="+(u||o);r.appendChangeType(e,v),l.logger.log("[buffer-controller]: switching codec "+f+" to "+c),r.tracks[e]={buffer:n.buffer,codec:o,container:d,levelCodec:u,metadata:h,id:s}}}}else r.pendingTracks[e]=t[e]})),i||(this.bufferCodecEventsExpected=Math.max(this.bufferCodecEventsExpected-1,0),this.mediaSource&&"open"===this.mediaSource.readyState&&this.checkPendingTracks())},t.appendChangeType=function(e,t){var r=this,i=this.operationQueue,n={execute:function(){var n=r.sourceBuffer[e];n&&(l.logger.log("[buffer-controller]: changing "+e+" sourceBuffer type to "+t),n.changeType(t)),i.shiftAndExecuteNext(e)},onStart:function(){},onComplete:function(){},onError:function(t){l.logger.warn("[buffer-controller]: Failed to change "+e+" SourceBuffer type",t)}};i.append(n,e)},t.onBufferAppending=function(e,t){var r=this,i=this.hls,n=this.operationQueue,a=this.tracks,u=t.data,d=t.type,h=t.frag,f=t.part,c=t.chunkMeta,v=c.buffering[d],g=self.performance.now();v.start=g;var p=h.stats.buffering,m=f?f.stats.buffering:null;0===p.start&&(p.start=g),m&&0===m.start&&(m.start=g);var y=a.audio,E="audio"===d&&1===c.id&&"audio/mpeg"===(null==y?void 0:y.container),T={execute:function(){if(v.executeStart=self.performance.now(),E){var e=r.sourceBuffer[d];if(e){var t=h.start-e.timestampOffset;Math.abs(t)>=.1&&(l.logger.log("[buffer-controller]: Updating audio SourceBuffer timestampOffset to "+h.start+" (delta: "+t+") sn: "+h.sn+")"),e.timestampOffset=h.start)}}r.appendExecutor(u,d)},onStart:function(){},onComplete:function(){var e=self.performance.now();v.executeEnd=v.end=e,0===p.first&&(p.first=e),m&&0===m.first&&(m.first=e);var t=r.sourceBuffer,i={};for(var n in t)i[n]=ce.getBuffered(t[n]);r.appendError=0,r.hls.trigger(s.Events.BUFFER_APPENDED,{type:d,frag:h,part:f,chunkMeta:c,parent:h.type,timeRanges:i})},onError:function(e){l.logger.error("[buffer-controller]: Error encountered while trying to append to the "+d+" SourceBuffer",e);var t={type:o.ErrorTypes.MEDIA_ERROR,parent:h.type,details:o.ErrorDetails.BUFFER_APPEND_ERROR,err:e,fatal:!1};e.code===DOMException.QUOTA_EXCEEDED_ERR?t.details=o.ErrorDetails.BUFFER_FULL_ERROR:(r.appendError++,t.details=o.ErrorDetails.BUFFER_APPEND_ERROR,r.appendError>i.config.appendErrorMaxRetry&&(l.logger.error("[buffer-controller]: Failed "+i.config.appendErrorMaxRetry+" times to append segment in sourceBuffer"),t.fatal=!0,i.stopLoad())),i.trigger(s.Events.ERROR,t)}};n.append(T,d)},t.onBufferFlushing=function(e,t){var r=this,i=this.operationQueue,n=function(e){return{execute:r.removeExecutor.bind(r,e,t.startOffset,t.endOffset),onStart:function(){},onComplete:function(){r.hls.trigger(s.Events.BUFFER_FLUSHED,{type:e})},onError:function(t){l.logger.warn("[buffer-controller]: Failed to remove from "+e+" SourceBuffer",t)}}};t.type?i.append(n(t.type),t.type):this.getSourceBufferTypes().forEach((function(e){i.append(n(e),e)}))},t.onFragParsed=function(e,t){var r=this,i=t.frag,n=t.part,a=[],o=n?n.elementaryStreams:i.elementaryStreams;o[p.ElementaryStreamTypes.AUDIOVIDEO]?a.push("audiovideo"):(o[p.ElementaryStreamTypes.AUDIO]&&a.push("audio"),o[p.ElementaryStreamTypes.VIDEO]&&a.push("video")),0===a.length&&l.logger.warn("Fragments must have at least one ElementaryStreamType set. type: "+i.type+" level: "+i.level+" sn: "+i.sn),this.blockBuffers((function(){var e=self.performance.now();i.stats.buffering.end=e,n&&(n.stats.buffering.end=e);var t=n?n.stats:i.stats;r.hls.trigger(s.Events.FRAG_BUFFERED,{frag:i,part:n,stats:t,id:i.type})}),a)},t.onFragChanged=function(e,t){this.flushBackBuffer()},t.onBufferEos=function(e,t){var r=this;this.getSourceBufferTypes().reduce((function(e,i){var n=r.sourceBuffer[i];return t.type&&t.type!==i||n&&!n.ended&&(n.ended=!0,l.logger.log("[buffer-controller]: "+i+" sourceBuffer now EOS")),e&&!(n&&!n.ended)}),!0)&&this.blockBuffers((function(){var e=r.mediaSource;e&&"open"===e.readyState&&e.endOfStream()}))},t.onLevelUpdated=function(e,t){var r=t.details;r.fragments.length&&(this.details=r,this.getSourceBufferTypes().length?this.blockBuffers(this.updateMediaElementDuration.bind(this)):this.updateMediaElementDuration())},t.flushBackBuffer=function(){var e=this.hls,t=this.details,r=this.media,i=this.sourceBuffer;if(r&&null!==t){var n=this.getSourceBufferTypes();if(n.length){var o=t.live&&null!==e.config.liveBackBufferLength?e.config.liveBackBufferLength:e.config.backBufferLength;if((0,a.isFiniteNumber)(o)&&!(o<0)){var l=r.currentTime,u=t.levelTargetDuration,d=Math.max(o,u),h=Math.floor(l/u)*u-d;n.forEach((function(r){var n=i[r];if(n){var a=ce.getBuffered(n);a.length>0&&h>a.start(0)&&(e.trigger(s.Events.BACK_BUFFER_REACHED,{bufferEnd:h}),t.live&&e.trigger(s.Events.LIVE_BACK_BUFFER_REACHED,{bufferEnd:h}),e.trigger(s.Events.BUFFER_FLUSHING,{startOffset:0,endOffset:h,type:r}))}}))}}}},t.updateMediaElementDuration=function(){if(this.details&&this.media&&this.mediaSource&&"open"===this.mediaSource.readyState){var e=this.details,t=this.hls,r=this.media,i=this.mediaSource,n=e.fragments[0].start+e.totalduration,s=r.duration,o=(0,a.isFiniteNumber)(i.duration)?i.duration:0;e.live&&t.config.liveDurationInfinity?(l.logger.log("[buffer-controller]: Media Source duration is set to Infinity"),i.duration=1/0,this.updateSeekableRange(e)):(n>o&&n>s||!(0,a.isFiniteNumber)(s))&&(l.logger.log("[buffer-controller]: Updating Media Source duration to "+n.toFixed(3)),i.duration=n)}},t.updateSeekableRange=function(e){var t=this.mediaSource,r=e.fragments;if(r.length&&e.live&&null!=t&&t.setLiveSeekableRange){var i=Math.max(0,r[0].start),n=Math.max(i,i+e.totalduration);t.setLiveSeekableRange(i,n)}},t.checkPendingTracks=function(){var e=this.bufferCodecEventsExpected,t=this.operationQueue,r=this.pendingTracks,i=Object.keys(r).length;if(i&&!e||2===i){this.createSourceBuffers(r),this.pendingTracks={};var n=this.getSourceBufferTypes();if(0===n.length)return void this.hls.trigger(s.Events.ERROR,{type:o.ErrorTypes.MEDIA_ERROR,details:o.ErrorDetails.BUFFER_INCOMPATIBLE_CODECS_ERROR,fatal:!0,reason:"could not create source buffer for media codec(s)"});n.forEach((function(e){t.executeNext(e)}))}},t.createSourceBuffers=function(e){var t=this.sourceBuffer,r=this.mediaSource;if(!r)throw Error("createSourceBuffers called when mediaSource was null");var i=0;for(var n in e)if(!t[n]){var a=e[n];if(!a)throw Error("source buffer exists for track "+n+", however track does not");var u=a.levelCodec||a.codec,d=a.container+";codecs="+u;l.logger.log("[buffer-controller]: creating sourceBuffer("+d+")");try{var h=t[n]=r.addSourceBuffer(d),f=n;this.addBufferListener(f,"updatestart",this._onSBUpdateStart),this.addBufferListener(f,"updateend",this._onSBUpdateEnd),this.addBufferListener(f,"error",this._onSBUpdateError),this.tracks[n]={buffer:h,codec:u,container:a.container,levelCodec:a.levelCodec,metadata:a.metadata,id:a.id},i++}catch(e){l.logger.error("[buffer-controller]: error while trying to add sourceBuffer: "+e.message),this.hls.trigger(s.Events.ERROR,{type:o.ErrorTypes.MEDIA_ERROR,details:o.ErrorDetails.BUFFER_ADD_CODEC_ERROR,fatal:!1,error:e,mimeType:d})}}i&&this.hls.trigger(s.Events.BUFFER_CREATED,{tracks:this.tracks})},t._onSBUpdateStart=function(e){this.operationQueue.current(e).onStart()},t._onSBUpdateEnd=function(e){var t=this.operationQueue;t.current(e).onComplete(),t.shiftAndExecuteNext(e)},t._onSBUpdateError=function(e,t){l.logger.error("[buffer-controller]: "+e+" SourceBuffer error",t),this.hls.trigger(s.Events.ERROR,{type:o.ErrorTypes.MEDIA_ERROR,details:o.ErrorDetails.BUFFER_APPENDING_ERROR,fatal:!1});var r=this.operationQueue.current(e);r&&r.onError(t)},t.removeExecutor=function(e,t,r){var i=this.media,n=this.mediaSource,s=this.operationQueue,o=this.sourceBuffer[e];if(!i||!n||!o)return l.logger.warn("[buffer-controller]: Attempting to remove from the "+e+" SourceBuffer, but it does not exist"),void s.shiftAndExecuteNext(e);var u=(0,a.isFiniteNumber)(i.duration)?i.duration:1/0,d=(0,a.isFiniteNumber)(n.duration)?n.duration:1/0,h=Math.max(0,t),f=Math.min(r,u,d);f>h?(l.logger.log("[buffer-controller]: Removing ["+h+","+f+"] from the "+e+" SourceBuffer"),o.remove(h,f)):s.shiftAndExecuteNext(e)},t.appendExecutor=function(e,t){var r=this.operationQueue,i=this.sourceBuffer[t];if(!i)return l.logger.warn("[buffer-controller]: Attempting to append to the "+t+" SourceBuffer, but it does not exist"),void r.shiftAndExecuteNext(t);i.ended=!1,i.appendBuffer(e)},t.blockBuffers=function(e,t){var r=this;if(void 0===t&&(t=this.getSourceBufferTypes()),!t.length)return l.logger.log("[buffer-controller]: Blocking operation requested, but no SourceBuffers exist"),void Promise.resolve().then(e);var i=this.operationQueue,n=t.map((function(e){return i.appendBlocker(e)}));Promise.all(n).then((function(){e(),t.forEach((function(e){var t=r.sourceBuffer[e];t&&t.updating||i.shiftAndExecuteNext(e)}))}))},t.getSourceBufferTypes=function(){return Object.keys(this.sourceBuffer)},t.addBufferListener=function(e,t,r){var i=this.sourceBuffer[e];if(i){var n=r.bind(this,e);this.listeners[e].push({event:t,listener:n}),i.addEventListener(t,n)}},t.removeBufferListeners=function(e){var t=this.sourceBuffer[e];t&&this.listeners[e].forEach((function(e){t.removeEventListener(e.event,e.listener)}))},e}();function mt(e,t){for(var r=0;r<t.length;r++){var i=t[r];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}const yt=function(){function e(e){this.autoLevelCapping=void 0,this.firstLevel=void 0,this.media=void 0,this.restrictedLevels=void 0,this.timer=void 0,this.hls=void 0,this.streamController=void 0,this.clientRect=void 0,this.hls=e,this.autoLevelCapping=Number.POSITIVE_INFINITY,this.firstLevel=-1,this.media=null,this.restrictedLevels=[],this.timer=void 0,this.clientRect=null,this.registerListeners()}var t,r,i=e.prototype;return i.setStreamController=function(e){this.streamController=e},i.destroy=function(){this.unregisterListener(),this.hls.config.capLevelToPlayerSize&&this.stopCapping(),this.media=null,this.clientRect=null,this.hls=this.streamController=null},i.registerListeners=function(){var e=this.hls;e.on(s.Events.FPS_DROP_LEVEL_CAPPING,this.onFpsDropLevelCapping,this),e.on(s.Events.MEDIA_ATTACHING,this.onMediaAttaching,this),e.on(s.Events.MANIFEST_PARSED,this.onManifestParsed,this),e.on(s.Events.BUFFER_CODECS,this.onBufferCodecs,this),e.on(s.Events.MEDIA_DETACHING,this.onMediaDetaching,this)},i.unregisterListener=function(){var e=this.hls;e.off(s.Events.FPS_DROP_LEVEL_CAPPING,this.onFpsDropLevelCapping,this),e.off(s.Events.MEDIA_ATTACHING,this.onMediaAttaching,this),e.off(s.Events.MANIFEST_PARSED,this.onManifestParsed,this),e.off(s.Events.BUFFER_CODECS,this.onBufferCodecs,this),e.off(s.Events.MEDIA_DETACHING,this.onMediaDetaching,this)},i.onFpsDropLevelCapping=function(t,r){e.isLevelAllowed(r.droppedLevel,this.restrictedLevels)&&this.restrictedLevels.push(r.droppedLevel)},i.onMediaAttaching=function(e,t){this.media=t.media instanceof HTMLVideoElement?t.media:null},i.onManifestParsed=function(e,t){var r=this.hls;this.restrictedLevels=[],this.firstLevel=t.firstLevel,r.config.capLevelToPlayerSize&&t.video&&this.startCapping()},i.onBufferCodecs=function(e,t){this.hls.config.capLevelToPlayerSize&&t.video&&this.startCapping()},i.onMediaDetaching=function(){this.stopCapping()},i.detectPlayerSize=function(){if(this.media&&this.mediaHeight>0&&this.mediaWidth>0){var e=this.hls.levels;if(e.length){var t=this.hls;t.autoLevelCapping=this.getMaxLevel(e.length-1),t.autoLevelCapping>this.autoLevelCapping&&this.streamController&&this.streamController.nextLevelSwitch(),this.autoLevelCapping=t.autoLevelCapping}}},i.getMaxLevel=function(t){var r=this,i=this.hls.levels;if(!i.length)return-1;var n=i.filter((function(i,n){return e.isLevelAllowed(n,r.restrictedLevels)&&n<=t}));return this.clientRect=null,e.getMaxLevelByMediaSize(n,this.mediaWidth,this.mediaHeight)},i.startCapping=function(){this.timer||(this.autoLevelCapping=Number.POSITIVE_INFINITY,this.hls.firstLevel=this.getMaxLevel(this.firstLevel),self.clearInterval(this.timer),this.timer=self.setInterval(this.detectPlayerSize.bind(this),1e3),this.detectPlayerSize())},i.stopCapping=function(){this.restrictedLevels=[],this.firstLevel=-1,this.autoLevelCapping=Number.POSITIVE_INFINITY,this.timer&&(self.clearInterval(this.timer),this.timer=void 0)},i.getDimensions=function(){if(this.clientRect)return this.clientRect;var e=this.media,t={width:0,height:0};if(e){var r=e.getBoundingClientRect();t.width=r.width,t.height=r.height,t.width||t.height||(t.width=r.right-r.left||e.width||0,t.height=r.bottom-r.top||e.height||0)}return this.clientRect=t,t},e.isLevelAllowed=function(e,t){return void 0===t&&(t=[]),-1===t.indexOf(e)},e.getMaxLevelByMediaSize=function(e,t,r){if(!e||!e.length)return-1;for(var i,n,a=e.length-1,s=0;s<e.length;s+=1){var o=e[s];if((o.width>=t||o.height>=r)&&(i=o,!(n=e[s+1])||i.width!==n.width||i.height!==n.height)){a=s;break}}return a},t=e,(r=[{key:"mediaWidth",get:function(){return this.getDimensions().width*this.contentScaleFactor}},{key:"mediaHeight",get:function(){return this.getDimensions().height*this.contentScaleFactor}},{key:"contentScaleFactor",get:function(){var e=1;if(!this.hls.config.ignoreDevicePixelRatio)try{e=self.devicePixelRatio}catch(e){}return e}}])&&mt(t.prototype,r),Object.defineProperty(t,"prototype",{writable:!1}),e}(),Et=function(){function e(e){this.hls=void 0,this.isVideoPlaybackQualityAvailable=!1,this.timer=void 0,this.media=null,this.lastTime=void 0,this.lastDroppedFrames=0,this.lastDecodedFrames=0,this.streamController=void 0,this.hls=e,this.registerListeners()}var t=e.prototype;return t.setStreamController=function(e){this.streamController=e},t.registerListeners=function(){this.hls.on(s.Events.MEDIA_ATTACHING,this.onMediaAttaching,this)},t.unregisterListeners=function(){this.hls.off(s.Events.MEDIA_ATTACHING,this.onMediaAttaching)},t.destroy=function(){this.timer&&clearInterval(this.timer),this.unregisterListeners(),this.isVideoPlaybackQualityAvailable=!1,this.media=null},t.onMediaAttaching=function(e,t){var r=this.hls.config;if(r.capLevelOnFPSDrop){var i=t.media instanceof self.HTMLVideoElement?t.media:null;this.media=i,i&&"function"==typeof i.getVideoPlaybackQuality&&(this.isVideoPlaybackQualityAvailable=!0),self.clearInterval(this.timer),this.timer=self.setInterval(this.checkFPSInterval.bind(this),r.fpsDroppedMonitoringPeriod)}},t.checkFPS=function(e,t,r){var i=performance.now();if(t){if(this.lastTime){var n=i-this.lastTime,a=r-this.lastDroppedFrames,o=t-this.lastDecodedFrames,u=1e3*a/n,d=this.hls;if(d.trigger(s.Events.FPS_DROP,{currentDropped:a,currentDecoded:o,totalDroppedFrames:r}),u>0&&a>d.config.fpsDroppedMonitoringThreshold*o){var h=d.currentLevel;l.logger.warn("drop FPS ratio greater than max allowed value for currentLevel: "+h),h>0&&(-1===d.autoLevelCapping||d.autoLevelCapping>=h)&&(h-=1,d.trigger(s.Events.FPS_DROP_LEVEL_CAPPING,{level:h,droppedLevel:d.currentLevel}),d.autoLevelCapping=h,this.streamController.nextLevelSwitch())}}this.lastTime=i,this.lastDroppedFrames=r,this.lastDecodedFrames=t}},t.checkFPSInterval=function(){var e=this.media;if(e)if(this.isVideoPlaybackQualityAvailable){var t=e.getVideoPlaybackQuality();this.checkFPS(e,t.totalVideoFrames,t.droppedVideoFrames)}else this.checkFPS(e,e.webkitDecodedFrameCount,e.webkitDroppedFrameCount)},e}();var Tt=r(408),St=/^age:\s*[\d.]+\s*$/m;const bt=function(){function e(e){this.xhrSetup=void 0,this.requestTimeout=void 0,this.retryTimeout=void 0,this.retryDelay=void 0,this.config=null,this.callbacks=null,this.context=void 0,this.loader=null,this.stats=void 0,this.xhrSetup=e?e.xhrSetup:null,this.stats=new Tt.LoadStats,this.retryDelay=0}var t=e.prototype;return t.destroy=function(){this.callbacks=null,this.abortInternal(),this.loader=null,this.config=null},t.abortInternal=function(){var e=this.loader;self.clearTimeout(this.requestTimeout),self.clearTimeout(this.retryTimeout),e&&(e.onreadystatechange=null,e.onprogress=null,4!==e.readyState&&(this.stats.aborted=!0,e.abort()))},t.abort=function(){var e;this.abortInternal(),null!==(e=this.callbacks)&&void 0!==e&&e.onAbort&&this.callbacks.onAbort(this.stats,this.context,this.loader)},t.load=function(e,t,r){if(this.stats.loading.start)throw new Error("Loader can only be used once.");this.stats.loading.start=self.performance.now(),this.context=e,this.config=t,this.callbacks=r,this.retryDelay=t.retryDelay,this.loadInternal()},t.loadInternal=function(){var e=this.config,t=this.context;if(e){var r=this.loader=new self.XMLHttpRequest,i=this.stats;i.loading.first=0,i.loaded=0;var n=this.xhrSetup;try{if(n)try{n(r,t.url)}catch(e){r.open("GET",t.url,!0),n(r,t.url)}r.readyState||r.open("GET",t.url,!0);var a=this.context.headers;if(a)for(var s in a)r.setRequestHeader(s,a[s])}catch(e){return void this.callbacks.onError({code:r.status,text:e.message},t,r)}t.rangeEnd&&r.setRequestHeader("Range","bytes="+t.rangeStart+"-"+(t.rangeEnd-1)),r.onreadystatechange=this.readystatechange.bind(this),r.onprogress=this.loadprogress.bind(this),r.responseType=t.responseType,self.clearTimeout(this.requestTimeout),this.requestTimeout=self.setTimeout(this.loadtimeout.bind(this),e.timeout),r.send()}},t.readystatechange=function(){var e=this.context,t=this.loader,r=this.stats;if(e&&t){var i=t.readyState,n=this.config;if(!r.aborted&&i>=2)if(self.clearTimeout(this.requestTimeout),0===r.loading.first&&(r.loading.first=Math.max(self.performance.now(),r.loading.start)),4===i){t.onreadystatechange=null,t.onprogress=null;var a=t.status,s="arraybuffer"===t.responseType;if(a>=200&&a<300&&(s&&t.response||null!==t.responseText)){var o,u;if(r.loading.end=Math.max(self.performance.now(),r.loading.first),u=s?(o=t.response).byteLength:(o=t.responseText).length,r.loaded=r.total=u,!this.callbacks)return;var d=this.callbacks.onProgress;if(d&&d(r,e,o,t),!this.callbacks)return;var h={url:t.responseURL,data:o};this.callbacks.onSuccess(h,r,e,t)}else r.retry>=n.maxRetry||a>=400&&a<499?(l.logger.error(a+" while loading "+e.url),this.callbacks.onError({code:a,text:t.statusText},e,t)):(l.logger.warn(a+" while loading "+e.url+", retrying in "+this.retryDelay+"..."),this.abortInternal(),this.loader=null,self.clearTimeout(this.retryTimeout),this.retryTimeout=self.setTimeout(this.loadInternal.bind(this),this.retryDelay),this.retryDelay=Math.min(2*this.retryDelay,n.maxRetryDelay),r.retry++)}else self.clearTimeout(this.requestTimeout),this.requestTimeout=self.setTimeout(this.loadtimeout.bind(this),n.timeout)}},t.loadtimeout=function(){l.logger.warn("timeout while loading "+this.context.url);var e=this.callbacks;e&&(this.abortInternal(),e.onTimeout(this.stats,this.context,this.loader))},t.loadprogress=function(e){var t=this.stats;t.loaded=e.loaded,e.lengthComputable&&(t.total=e.total)},t.getCacheAge=function(){var e=null;if(this.loader&&St.test(this.loader.getAllResponseHeaders())){var t=this.loader.getResponseHeader("age");e=t?parseFloat(t):null}return e},e}();var Lt=function(){function e(){this.chunks=[],this.dataLength=0}var t=e.prototype;return t.push=function(e){this.chunks.push(e),this.dataLength+=e.length},t.flush=function(){var e,t=this.chunks,r=this.dataLength;return t.length?(e=1===t.length?t[0]:function(e,t){for(var r=new Uint8Array(t),i=0,n=0;n<e.length;n++){var a=e[n];r.set(a,i),i+=a.length}return r}(t,r),this.reset(),e):new Uint8Array(0)},t.reset=function(){this.chunks.length=0,this.dataLength=0},e}();function At(e){var t="function"==typeof Map?new Map:void 0;return At=function(e){if(null===e||(r=e,-1===Function.toString.call(r).indexOf("[native code]")))return e;var r;if("function"!=typeof e)throw new TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,i)}function i(){return Dt(e,arguments,_t(this).constructor)}return i.prototype=Object.create(e.prototype,{constructor:{value:i,enumerable:!1,writable:!0,configurable:!0}}),kt(i,e)},At(e)}function Dt(e,t,r){return Dt=Rt()?Reflect.construct.bind():function(e,t,r){var i=[null];i.push.apply(i,t);var n=new(Function.bind.apply(e,i));return r&&kt(n,r.prototype),n},Dt.apply(null,arguments)}function Rt(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}function kt(e,t){return kt=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},kt(e,t)}function _t(e){return _t=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(e){return e.__proto__||Object.getPrototypeOf(e)},_t(e)}function xt(){return xt=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var i in r)Object.prototype.hasOwnProperty.call(r,i)&&(e[i]=r[i])}return e},xt.apply(this,arguments)}var It=function(){function e(e){this.fetchSetup=void 0,this.requestTimeout=void 0,this.request=void 0,this.response=void 0,this.controller=void 0,this.context=void 0,this.config=null,this.callbacks=null,this.stats=void 0,this.loader=null,this.fetchSetup=e.fetchSetup||wt,this.controller=new self.AbortController,this.stats=new Tt.LoadStats}var t=e.prototype;return t.destroy=function(){this.loader=this.callbacks=null,this.abortInternal()},t.abortInternal=function(){var e=this.response;e&&e.ok||(this.stats.aborted=!0,this.controller.abort())},t.abort=function(){var e;this.abortInternal(),null!==(e=this.callbacks)&&void 0!==e&&e.onAbort&&this.callbacks.onAbort(this.stats,this.context,this.response)},t.load=function(e,t,r){var i=this,n=this.stats;if(n.loading.start)throw new Error("Loader can only be used once.");n.loading.start=self.performance.now();var s=function(e,t){var r={method:"GET",mode:"cors",credentials:"same-origin",signal:t,headers:new self.Headers(xt({},e.headers))};return e.rangeEnd&&r.headers.set("Range","bytes="+e.rangeStart+"-"+String(e.rangeEnd-1)),r}(e,this.controller.signal),o=r.onProgress,l="arraybuffer"===e.responseType,u=l?"byteLength":"length";this.context=e,this.config=t,this.callbacks=r,this.request=this.fetchSetup(e,s),self.clearTimeout(this.requestTimeout),this.requestTimeout=self.setTimeout((function(){i.abortInternal(),r.onTimeout(n,e,i.response)}),t.timeout),self.fetch(this.request).then((function(r){if(i.response=i.loader=r,!r.ok){var s=r.status,u=r.statusText;throw new Ct(u||"fetch, bad network response",s,r)}return n.loading.first=Math.max(self.performance.now(),n.loading.start),n.total=parseInt(r.headers.get("Content-Length")||"0"),o&&(0,a.isFiniteNumber)(t.highWaterMark)?i.loadProgressively(r,n,e,t.highWaterMark,o):l?r.arrayBuffer():r.text()})).then((function(s){var l=i.response;self.clearTimeout(i.requestTimeout),n.loading.end=Math.max(self.performance.now(),n.loading.first);var d=s[u];d&&(n.loaded=n.total=d);var h={url:l.url,data:s};o&&!(0,a.isFiniteNumber)(t.highWaterMark)&&o(n,e,s,l),r.onSuccess(h,n,e,l)})).catch((function(t){if(self.clearTimeout(i.requestTimeout),!n.aborted){var a=t&&t.code||0,s=t?t.message:null;r.onError({code:a,text:s},e,t?t.details:null)}}))},t.getCacheAge=function(){var e=null;if(this.response){var t=this.response.headers.get("age");e=t?parseFloat(t):null}return e},t.loadProgressively=function(e,t,r,i,n){void 0===i&&(i=0);var a=new Lt,s=e.body.getReader();return function o(){return s.read().then((function(s){if(s.done)return a.dataLength&&n(t,r,a.flush(),e),Promise.resolve(new ArrayBuffer(0));var l=s.value,u=l.length;return t.loaded+=u,u<i||a.dataLength?(a.push(l),a.dataLength>=i&&n(t,r,a.flush(),e)):n(t,r,l,e),o()})).catch((function(){return Promise.reject()}))}()},e}();function wt(e,t){return new self.Request(e.url,t)}var Ct=function(e){var t,r;function i(t,r,i){var n;return(n=e.call(this,t)||this).code=void 0,n.details=void 0,n.code=r,n.details=i,n}return r=e,(t=i).prototype=Object.create(r.prototype),t.prototype.constructor=t,kt(t,r),i}(At(Error));const Pt=It;var Ot;!function(e){e.WIDEVINE="com.widevine.alpha",e.PLAYREADY="com.microsoft.playready"}(Ot||(Ot={}));var Ft="undefined"!=typeof self&&self.navigator&&self.navigator.requestMediaKeySystemAccess?self.navigator.requestMediaKeySystemAccess.bind(self.navigator):null;function Mt(){return Mt=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var i in r)Object.prototype.hasOwnProperty.call(r,i)&&(e[i]=r[i])}return e},Mt.apply(this,arguments)}function Nt(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,i)}return r}function Ut(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?Nt(Object(r),!0).forEach((function(t){Bt(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):Nt(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}function Bt(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}var Gt=Ut(Ut({autoStartLoad:!0,startPosition:-1,defaultAudioCodec:void 0,debug:!1,capLevelOnFPSDrop:!1,capLevelToPlayerSize:!1,ignoreDevicePixelRatio:!1,initialLiveManifestSize:1,maxBufferLength:30,backBufferLength:1/0,maxBufferSize:6e7,maxBufferHole:.1,highBufferWatchdogPeriod:2,nudgeOffset:.1,nudgeMaxRetry:3,maxFragLookUpTolerance:.25,liveSyncDurationCount:3,liveMaxLatencyDurationCount:1/0,liveSyncDuration:void 0,liveMaxLatencyDuration:void 0,maxLiveSyncPlaybackRate:1,liveDurationInfinity:!1,liveBackBufferLength:null,maxMaxBufferLength:600,enableWorker:!0,enableSoftwareAES:!0,manifestLoadingTimeOut:1e4,manifestLoadingMaxRetry:1,manifestLoadingRetryDelay:1e3,manifestLoadingMaxRetryTimeout:64e3,startLevel:void 0,levelLoadingTimeOut:1e4,levelLoadingMaxRetry:4,levelLoadingRetryDelay:1e3,levelLoadingMaxRetryTimeout:64e3,fragLoadingTimeOut:2e4,fragLoadingMaxRetry:6,fragLoadingRetryDelay:1e3,fragLoadingMaxRetryTimeout:64e3,startFragPrefetch:!1,fpsDroppedMonitoringPeriod:5e3,fpsDroppedMonitoringThreshold:.2,appendErrorMaxRetry:3,loader:bt,fLoader:void 0,pLoader:void 0,xhrSetup:void 0,licenseXhrSetup:void 0,licenseResponseCallback:void 0,abrController:dt,bufferController:pt,capLevelController:yt,fpsController:Et,stretchShortVideoTrack:!1,maxAudioFramesDrift:1,forceKeyFrameOnDiscontinuity:!0,abrEwmaFastLive:3,abrEwmaSlowLive:9,abrEwmaFastVoD:3,abrEwmaSlowVoD:9,abrEwmaDefaultEstimate:5e5,abrBandWidthFactor:.95,abrBandWidthUpFactor:.7,abrMaxWithRealBitrate:!1,maxStarvationDelay:4,maxLoadingDelay:4,minAutoBitrate:0,emeEnabled:!1,widevineLicenseUrl:void 0,drmSystemOptions:{},requestMediaKeySystemAccessFunc:Ft,testBandwidth:!0,progressive:!1,lowLatencyMode:!0,cmcd:void 0,enableDateRangeMetadataCues:!0,enableEmsgMetadataCues:!0,enableID3MetadataCues:!0},{cueHandler:ft(),enableWebVTT:!1,enableIMSC1:!1,enableCEA708Captions:!1,captionsTextTrack1Label:"English",captionsTextTrack1LanguageCode:"en",captionsTextTrack2Label:"Spanish",captionsTextTrack2LanguageCode:"es",captionsTextTrack3Label:"Unknown CC",captionsTextTrack3LanguageCode:"",captionsTextTrack4Label:"Unknown CC",captionsTextTrack4LanguageCode:"",renderTextTracksNatively:!0}),{},{subtitleStreamController:void 0,subtitleTrackController:void 0,timelineController:void 0,audioStreamController:void 0,audioTrackController:void 0,emeController:void 0,cmcdController:void 0});function Vt(e,t){for(var r=0;r<t.length;r++){var i=t[r];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}var Ht=function(){function e(t){void 0===t&&(t={}),this.config=void 0,this.userConfig=void 0,this.coreComponents=void 0,this.networkControllers=void 0,this._emitter=new et.EventEmitter,this._autoLevelCapping=void 0,this.abrController=void 0,this.bufferController=void 0,this.capLevelController=void 0,this.latencyController=void 0,this.levelController=void 0,this.streamController=void 0,this.audioTrackController=void 0,this.subtitleTrackController=void 0,this.emeController=void 0,this.cmcdController=void 0,this._media=null,this.url=null;var r=this.config=function(e,t){if((t.liveSyncDurationCount||t.liveMaxLatencyDurationCount)&&(t.liveSyncDuration||t.liveMaxLatencyDuration))throw new Error("Illegal hls.js config: don't mix up liveSyncDurationCount/liveMaxLatencyDurationCount and liveSyncDuration/liveMaxLatencyDuration");if(void 0!==t.liveMaxLatencyDurationCount&&(void 0===t.liveSyncDurationCount||t.liveMaxLatencyDurationCount<=t.liveSyncDurationCount))throw new Error('Illegal hls.js config: "liveMaxLatencyDurationCount" must be greater than "liveSyncDurationCount"');if(void 0!==t.liveMaxLatencyDuration&&(void 0===t.liveSyncDuration||t.liveMaxLatencyDuration<=t.liveSyncDuration))throw new Error('Illegal hls.js config: "liveMaxLatencyDuration" must be greater than "liveSyncDuration"');return Mt({},e,t)}(e.DefaultConfig,t);this.userConfig=t,(0,l.enableLogs)(r.debug,"Hls instance"),this._autoLevelCapping=-1,r.progressive&&function(e){var t=e.loader;t!==Pt&&t!==bt?(l.logger.log("[config]: Custom loader detected, cannot enable progressive streaming"),e.progressive=!1):function(){if(self.fetch&&self.AbortController&&self.ReadableStream&&self.Request)try{return new self.ReadableStream({}),!0}catch(e){}return!1}()&&(e.loader=Pt,e.progressive=!0,e.enableSoftwareAES=!0,l.logger.log("[config]: Progressive streaming enabled, using FetchLoader"))}(r);var i=r.abrController,n=r.bufferController,a=r.capLevelController,s=r.fpsController,o=this.abrController=new i(this),u=this.bufferController=new n(this),d=this.capLevelController=new a(this),h=new s(this),f=new O(this),c=new F(this),v=new H(this),g=this.levelController=new oe(this),p=new le(this),m=this.streamController=new st(this,p);d.setStreamController(m),h.setStreamController(m);var y=[f,c,g,m];this.networkControllers=y;var E=[o,u,d,h,v,p];this.audioTrackController=this.createController(r.audioTrackController,null,y),this.createController(r.audioStreamController,p,y),this.subtitleTrackController=this.createController(r.subtitleTrackController,null,y),this.createController(r.subtitleStreamController,p,y),this.createController(r.timelineController,null,E),this.emeController=this.createController(r.emeController,null,E),this.cmcdController=this.createController(r.cmcdController,null,E),this.latencyController=this.createController(W,null,E),this.coreComponents=E}e.isSupported=function(){return function(){var e=We();if(!e)return!1;var t=Ye(),r=e&&"function"==typeof e.isTypeSupported&&e.isTypeSupported('video/mp4; codecs="avc1.42E01E,mp4a.40.2"'),i=!t||t.prototype&&"function"==typeof t.prototype.appendBuffer&&"function"==typeof t.prototype.remove;return!!r&&!!i}()};var t,r,i,a=e.prototype;return a.createController=function(e,t,r){if(e){var i=t?new e(this,t):new e(this);return r&&r.push(i),i}return null},a.on=function(e,t,r){void 0===r&&(r=this),this._emitter.on(e,t,r)},a.once=function(e,t,r){void 0===r&&(r=this),this._emitter.once(e,t,r)},a.removeAllListeners=function(e){this._emitter.removeAllListeners(e)},a.off=function(e,t,r,i){void 0===r&&(r=this),this._emitter.off(e,t,r,i)},a.listeners=function(e){return this._emitter.listeners(e)},a.emit=function(e,t,r){return this._emitter.emit(e,t,r)},a.trigger=function(e,t){if(this.config.debug)return this.emit(e,e,t);try{return this.emit(e,e,t)}catch(t){l.logger.error("An internal error happened while handling event "+e+'. Error message: "'+t.message+'". Here is a stacktrace:',t),this.trigger(s.Events.ERROR,{type:o.ErrorTypes.OTHER_ERROR,details:o.ErrorDetails.INTERNAL_EXCEPTION,fatal:!1,event:e,error:t})}return!1},a.listenerCount=function(e){return this._emitter.listenerCount(e)},a.destroy=function(){l.logger.log("destroy"),this.trigger(s.Events.DESTROYING,void 0),this.detachMedia(),this.removeAllListeners(),this._autoLevelCapping=-1,this.url=null,this.networkControllers.forEach((function(e){return e.destroy()})),this.networkControllers.length=0,this.coreComponents.forEach((function(e){return e.destroy()})),this.coreComponents.length=0},a.attachMedia=function(e){l.logger.log("attachMedia"),this._media=e,this.trigger(s.Events.MEDIA_ATTACHING,{media:e})},a.detachMedia=function(){l.logger.log("detachMedia"),this.trigger(s.Events.MEDIA_DETACHING,void 0),this._media=null},a.loadSource=function(e){this.stopLoad();var t=this.media,r=this.url,i=this.url=n.buildAbsoluteURL(self.location.href,e,{alwaysNormalize:!0});l.logger.log("loadSource:"+i),t&&r&&r!==i&&this.bufferController.hasSourceTypes()&&(this.detachMedia(),this.attachMedia(t)),this.trigger(s.Events.MANIFEST_LOADING,{url:e})},a.startLoad=function(e){void 0===e&&(e=-1),l.logger.log("startLoad("+e+")"),this.networkControllers.forEach((function(t){t.startLoad(e)}))},a.stopLoad=function(){l.logger.log("stopLoad"),this.networkControllers.forEach((function(e){e.stopLoad()}))},a.swapAudioCodec=function(){l.logger.log("swapAudioCodec"),this.streamController.swapAudioCodec()},a.recoverMediaError=function(){l.logger.log("recoverMediaError");var e=this._media;this.detachMedia(),e&&this.attachMedia(e)},a.removeLevel=function(e,t){void 0===t&&(t=0),this.levelController.removeLevel(e,t)},t=e,i=[{key:"version",get:function(){return"1.2.9"}},{key:"Events",get:function(){return s.Events}},{key:"ErrorTypes",get:function(){return o.ErrorTypes}},{key:"ErrorDetails",get:function(){return o.ErrorDetails}},{key:"DefaultConfig",get:function(){return e.defaultConfig?e.defaultConfig:Gt},set:function(t){e.defaultConfig=t}}],(r=[{key:"levels",get:function(){return this.levelController.levels||[]}},{key:"currentLevel",get:function(){return this.streamController.currentLevel},set:function(e){l.logger.log("set currentLevel:"+e),this.loadLevel=e,this.abrController.clearTimer(),this.streamController.immediateLevelSwitch()}},{key:"nextLevel",get:function(){return this.streamController.nextLevel},set:function(e){l.logger.log("set nextLevel:"+e),this.levelController.manualLevel=e,this.streamController.nextLevelSwitch()}},{key:"loadLevel",get:function(){return this.levelController.level},set:function(e){l.logger.log("set loadLevel:"+e),this.levelController.manualLevel=e}},{key:"nextLoadLevel",get:function(){return this.levelController.nextLoadLevel},set:function(e){this.levelController.nextLoadLevel=e}},{key:"firstLevel",get:function(){return Math.max(this.levelController.firstLevel,this.minAutoLevel)},set:function(e){l.logger.log("set firstLevel:"+e),this.levelController.firstLevel=e}},{key:"startLevel",get:function(){return this.levelController.startLevel},set:function(e){l.logger.log("set startLevel:"+e),-1!==e&&(e=Math.max(e,this.minAutoLevel)),this.levelController.startLevel=e}},{key:"capLevelToPlayerSize",get:function(){return this.config.capLevelToPlayerSize},set:function(e){var t=!!e;t!==this.config.capLevelToPlayerSize&&(t?this.capLevelController.startCapping():(this.capLevelController.stopCapping(),this.autoLevelCapping=-1,this.streamController.nextLevelSwitch()),this.config.capLevelToPlayerSize=t)}},{key:"autoLevelCapping",get:function(){return this._autoLevelCapping},set:function(e){this._autoLevelCapping!==e&&(l.logger.log("set autoLevelCapping:"+e),this._autoLevelCapping=e)}},{key:"bandwidthEstimate",get:function(){var e=this.abrController.bwEstimator;return e?e.getEstimate():NaN}},{key:"autoLevelEnabled",get:function(){return-1===this.levelController.manualLevel}},{key:"manualLevel",get:function(){return this.levelController.manualLevel}},{key:"minAutoLevel",get:function(){var e=this.levels,t=this.config.minAutoBitrate;if(!e)return 0;for(var r=e.length,i=0;i<r;i++)if(e[i].maxBitrate>=t)return i;return 0}},{key:"maxAutoLevel",get:function(){var e=this.levels,t=this.autoLevelCapping;return-1===t&&e&&e.length?e.length-1:t}},{key:"nextAutoLevel",get:function(){return Math.min(Math.max(this.abrController.nextAutoLevel,this.minAutoLevel),this.maxAutoLevel)},set:function(e){this.abrController.nextAutoLevel=Math.max(this.minAutoLevel,e)}},{key:"playingDate",get:function(){return this.streamController.currentProgramDateTime}},{key:"mainForwardBufferInfo",get:function(){return this.streamController.getMainFwdBufferInfo()}},{key:"audioTracks",get:function(){var e=this.audioTrackController;return e?e.audioTracks:[]}},{key:"audioTrack",get:function(){var e=this.audioTrackController;return e?e.audioTrack:-1},set:function(e){var t=this.audioTrackController;t&&(t.audioTrack=e)}},{key:"subtitleTracks",get:function(){var e=this.subtitleTrackController;return e?e.subtitleTracks:[]}},{key:"subtitleTrack",get:function(){var e=this.subtitleTrackController;return e?e.subtitleTrack:-1},set:function(e){var t=this.subtitleTrackController;t&&(t.subtitleTrack=e)}},{key:"media",get:function(){return this._media}},{key:"subtitleDisplay",get:function(){var e=this.subtitleTrackController;return!!e&&e.subtitleDisplay},set:function(e){var t=this.subtitleTrackController;t&&(t.subtitleDisplay=e)}},{key:"lowLatencyMode",get:function(){return this.config.lowLatencyMode},set:function(e){this.config.lowLatencyMode=e}},{key:"liveSyncPosition",get:function(){return this.latencyController.liveSyncPosition}},{key:"latency",get:function(){return this.latencyController.latency}},{key:"maxLatency",get:function(){return this.latencyController.maxLatency}},{key:"targetLatency",get:function(){return this.latencyController.targetLatency}},{key:"drift",get:function(){return this.latencyController.drift}},{key:"forceStartLoad",get:function(){return this.streamController.forceStartLoad}}])&&Vt(t.prototype,r),i&&Vt(t,i),Object.defineProperty(t,"prototype",{writable:!1}),e}();Ht.defaultConfig=void 0},923:(e,t,r)=>{"use strict";r.r(t),r.d(t,{BaseSegment:()=>c,ElementaryStreamTypes:()=>i,Fragment:()=>v,Part:()=>g});var i,n=r(965),a=r(945),s=r(93),o=r(960),l=r(408);function u(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,d(e,t)}function d(e,t){return d=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},d(e,t)}function h(e,t){for(var r=0;r<t.length;r++){var i=t[r];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}function f(e,t,r){return t&&h(e.prototype,t),r&&h(e,r),Object.defineProperty(e,"prototype",{writable:!1}),e}!function(e){e.AUDIO="audio",e.VIDEO="video",e.AUDIOVIDEO="audiovideo"}(i||(i={}));var c=function(){function e(e){var t;this._byteRange=null,this._url=null,this.baseurl=void 0,this.relurl=void 0,this.elementaryStreams=((t={})[i.AUDIO]=null,t[i.VIDEO]=null,t[i.AUDIOVIDEO]=null,t),this.baseurl=e}return e.prototype.setByteRange=function(e,t){var r=e.split("@",2),i=[];1===r.length?i[0]=t?t.byteRangeEndOffset:0:i[0]=parseInt(r[1]),i[1]=parseInt(r[0])+i[0],this._byteRange=i},f(e,[{key:"byteRange",get:function(){return this._byteRange?this._byteRange:[]}},{key:"byteRangeStartOffset",get:function(){return this.byteRange[0]}},{key:"byteRangeEndOffset",get:function(){return this.byteRange[1]}},{key:"url",get:function(){return!this._url&&this.baseurl&&this.relurl&&(this._url=(0,a.buildAbsoluteURL)(this.baseurl,this.relurl,{alwaysNormalize:!0})),this._url||""},set:function(e){this._url=e}}]),e}(),v=function(e){function t(t,r){var i;return(i=e.call(this,r)||this)._decryptdata=null,i.rawProgramDateTime=null,i.programDateTime=null,i.tagList=[],i.duration=0,i.sn=0,i.levelkey=void 0,i.type=void 0,i.loader=null,i.level=-1,i.cc=0,i.startPTS=void 0,i.endPTS=void 0,i.appendedPTS=void 0,i.startDTS=void 0,i.endDTS=void 0,i.start=0,i.deltaPTS=void 0,i.maxStartPTS=void 0,i.minEndPTS=void 0,i.stats=new l.LoadStats,i.urlId=0,i.data=void 0,i.bitrateTest=!1,i.title=null,i.initSegment=null,i.type=t,i}u(t,e);var r=t.prototype;return r.createInitializationVector=function(e){for(var t=new Uint8Array(16),r=12;r<16;r++)t[r]=e>>8*(15-r)&255;return t},r.setDecryptDataFromLevelKey=function(e,t){var r=e;return"AES-128"===(null==e?void 0:e.method)&&e.uri&&!e.iv&&((r=o.LevelKey.fromURI(e.uri)).method=e.method,r.iv=this.createInitializationVector(t),r.keyFormat="identity"),r},r.setElementaryStreamInfo=function(e,t,r,i,n,a){void 0===a&&(a=!1);var s=this.elementaryStreams,o=s[e];o?(o.startPTS=Math.min(o.startPTS,t),o.endPTS=Math.max(o.endPTS,r),o.startDTS=Math.min(o.startDTS,i),o.endDTS=Math.max(o.endDTS,n)):s[e]={startPTS:t,endPTS:r,startDTS:i,endDTS:n,partial:a}},r.clearElementaryStreamInfo=function(){var e=this.elementaryStreams;e[i.AUDIO]=null,e[i.VIDEO]=null,e[i.AUDIOVIDEO]=null},f(t,[{key:"decryptdata",get:function(){if(!this.levelkey&&!this._decryptdata)return null;if(!this._decryptdata&&this.levelkey){var e=this.sn;"number"!=typeof e&&(this.levelkey&&"AES-128"===this.levelkey.method&&!this.levelkey.iv&&s.logger.warn('missing IV for initialization segment with method="'+this.levelkey.method+'" - compliance issue'),e=0),this._decryptdata=this.setDecryptDataFromLevelKey(this.levelkey,e)}return this._decryptdata}},{key:"end",get:function(){return this.start+this.duration}},{key:"endProgramDateTime",get:function(){if(null===this.programDateTime)return null;if(!(0,n.isFiniteNumber)(this.programDateTime))return null;var e=(0,n.isFiniteNumber)(this.duration)?this.duration:0;return this.programDateTime+1e3*e}},{key:"encrypted",get:function(){var e;return!(null===(e=this.decryptdata)||void 0===e||!e.keyFormat||!this.decryptdata.uri)}}]),t}(c),g=function(e){function t(t,r,i,n,a){var s;(s=e.call(this,i)||this).fragOffset=0,s.duration=0,s.gap=!1,s.independent=!1,s.relurl=void 0,s.fragment=void 0,s.index=void 0,s.stats=new l.LoadStats,s.duration=t.decimalFloatingPoint("DURATION"),s.gap=t.bool("GAP"),s.independent=t.bool("INDEPENDENT"),s.relurl=t.enumeratedString("URI"),s.fragment=r,s.index=n;var o=t.enumeratedString("BYTERANGE");return o&&s.setByteRange(o,a),a&&(s.fragOffset=a.fragOffset+a.duration),s}return u(t,e),f(t,[{key:"start",get:function(){return this.fragment.start+this.fragOffset}},{key:"end",get:function(){return this.start+this.duration}},{key:"loaded",get:function(){var e=this.elementaryStreams;return!!(e.audio||e.video||e.audiovideo)}}]),t}(c)},960:(e,t,r)=>{"use strict";r.r(t),r.d(t,{LevelKey:()=>a});var i=r(945);function n(e,t){for(var r=0;r<t.length;r++){var i=t[r];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}var a=function(){function e(e,t){this._uri=null,this.method=null,this.keyFormat=null,this.keyFormatVersions=null,this.keyID=null,this.key=null,this.iv=null,this._uri=t?(0,i.buildAbsoluteURL)(e,t,{alwaysNormalize:!0}):e}var t,r;return e.fromURL=function(t,r){return new e(t,r)},e.fromURI=function(t){return new e(t)},t=e,(r=[{key:"uri",get:function(){return this._uri}}])&&n(t.prototype,r),Object.defineProperty(t,"prototype",{writable:!1}),e}()},408:(e,t,r)=>{"use strict";r.r(t),r.d(t,{LoadStats:()=>i});var i=function(){this.aborted=!1,this.loaded=0,this.retry=0,this.total=0,this.chunkCount=0,this.bwEstimate=0,this.loading={start:0,first:0,end:0},this.parsing={start:0,end:0},this.buffering={start:0,first:0,end:0}}},965:(e,t,r)=>{"use strict";r.r(t),r.d(t,{MAX_SAFE_INTEGER:()=>n,isFiniteNumber:()=>i});var i=Number.isFinite||function(e){return"number"==typeof e&&isFinite(e)},n=Number.MAX_SAFE_INTEGER||9007199254740991},856:(e,t,r)=>{"use strict";var i;r.r(t),r.d(t,{MetadataSchema:()=>i}),function(e){e.audioId3="org.id3",e.dateRange="com.apple.quicktime.HLS",e.emsg="https://aomedia.org/emsg/ID3"}(i||(i={}))},308:(e,t,r)=>{"use strict";var i,n;r.r(t),r.d(t,{PlaylistContextType:()=>i,PlaylistLevelType:()=>n}),function(e){e.MANIFEST="manifest",e.LEVEL="level",e.AUDIO_TRACK="audioTrack",e.SUBTITLE_TRACK="subtitleTrack"}(i||(i={})),function(e){e.MAIN="main",e.AUDIO="audio",e.SUBTITLE="subtitle"}(n||(n={}))},93:(e,t,r)=>{"use strict";r.r(t),r.d(t,{enableLogs:()=>o,logger:()=>l});var i=function(){},n={trace:i,debug:i,log:i,warn:i,info:i,error:i},a=n;function s(e){var t=self.console[e];return t?t.bind(self.console,"["+e+"] >"):i}function o(e,t){if(self.console&&!0===e||"object"==typeof e){!function(e){for(var t=arguments.length,r=new Array(t>1?t-1:0),i=1;i<t;i++)r[i-1]=arguments[i];r.forEach((function(t){a[t]=e[t]?e[t].bind(e):s(t)}))}(e,"debug","log","info","warn","error");try{a.log('Debug logs enabled for "'+t+'"')}catch(e){a=n}}else a=n}var l=n},63:(e,t,r)=>{"use strict";r.r(t),r.d(t,{RemuxerTrackIdConfig:()=>l,appendUint8Array:()=>b,bin2str:()=>u,computeRawDurationFromSamples:()=>E,discardEPB:()=>R,findBox:()=>v,getDuration:()=>y,getStartDTS:()=>m,offsetStartDTS:()=>T,parseEmsg:()=>k,parseInitSegment:()=>p,parseSEIMessageFromNALu:()=>D,parseSamples:()=>L,parseSegmentIndex:()=>g,readSint32:()=>f,readUint16:()=>d,readUint32:()=>h,segmentValidRange:()=>S,writeUint32:()=>c});var i=r(145),n=r(923),a=r(181),s=Math.pow(2,32)-1,o=[].push,l={video:1,audio:2,id3:3,text:4};function u(e){return String.fromCharCode.apply(null,e)}function d(e,t){var r=e[t]<<8|e[t+1];return r<0?65536+r:r}function h(e,t){var r=f(e,t);return r<0?4294967296+r:r}function f(e,t){return e[t]<<24|e[t+1]<<16|e[t+2]<<8|e[t+3]}function c(e,t,r){e[t]=r>>24,e[t+1]=r>>16&255,e[t+2]=r>>8&255,e[t+3]=255&r}function v(e,t){var r=[];if(!t.length)return r;for(var i=e.byteLength,n=0;n<i;){var a=h(e,n),s=a>1?n+a:i;if(u(e.subarray(n+4,n+8))===t[0])if(1===t.length)r.push(e.subarray(n+8,s));else{var l=v(e.subarray(n+8,s),t.slice(1));l.length&&o.apply(r,l)}n=s}return r}function g(e){var t=[],r=e[0],i=8,n=h(e,i);i+=4,i+=0===r?8:16,i+=2;var a=e.length+0,s=d(e,i);i+=2;for(var o=0;o<s;o++){var l=i,u=h(e,l);l+=4;var f=2147483647&u;if(1==(2147483648&u)>>>31)return console.warn("SIDX has hierarchical references (not supported)"),null;var c=h(e,l);l+=4,t.push({referenceSize:f,subsegmentDuration:c,info:{duration:c/n,start:a,end:a+f-1}}),a+=f,i=l+=4}return{earliestPresentationTime:0,timescale:n,version:r,referencesCount:s,references:t}}function p(e){for(var t=[],r=v(e,["moov","trak"]),i=0;i<r.length;i++){var a=r[i],s=v(a,["tkhd"])[0];if(s){var o=s[0],l=0===o?12:20,d=h(s,l),f=v(a,["mdia","mdhd"])[0];if(f){var c=h(f,l=0===(o=f[0])?12:20),g=v(a,["mdia","hdlr"])[0];if(g){var p=u(g.subarray(8,12)),m={soun:n.ElementaryStreamTypes.AUDIO,vide:n.ElementaryStreamTypes.VIDEO}[p];if(m){var y=v(a,["mdia","minf","stbl","stsd"])[0],E=void 0;y&&(E=u(y.subarray(12,16))),t[d]={timescale:c,type:m},t[m]={timescale:c,id:d,codec:E}}}}}}return v(e,["moov","mvex","trex"]).forEach((function(e){var r=h(e,4),i=t[r];i&&(i.default={duration:h(e,12),flags:h(e,20)})})),t}function m(e,t){return v(t,["moof","traf"]).reduce((function(t,r){var i=v(r,["tfdt"])[0],n=i[0],a=v(r,["tfhd"]).reduce((function(t,r){var a=h(r,4),s=e[a];if(s){var o=h(i,4);1===n&&(o*=Math.pow(2,32),o+=h(i,8));var l=o/(s.timescale||9e4);if(isFinite(l)&&(null===t||l<t))return l}return t}),null);return null!==a&&isFinite(a)&&(null===t||a<t)?a:t}),null)||0}function y(e,t){for(var r=0,i=0,a=0,s=v(e,["moof","traf"]),o=0;o<s.length;o++){var l=s[o],u=v(l,["tfhd"])[0],d=t[h(u,4)];if(d){var f=d.default,c=h(u,0)|(null==f?void 0:f.flags),p=null==f?void 0:f.duration;8&c&&(p=h(u,2&c?12:8));for(var m=d.timescale||9e4,y=v(l,["trun"]),T=0;T<y.length;T++)!(r=E(y[T]))&&p&&(r=p*h(y[T],4)),d.type===n.ElementaryStreamTypes.VIDEO?i+=r/m:d.type===n.ElementaryStreamTypes.AUDIO&&(a+=r/m)}}if(0===i&&0===a){for(var S=0,b=v(e,["sidx"]),L=0;L<b.length;L++){var A=g(b[L]);null!=A&&A.references&&(S+=A.references.reduce((function(e,t){return e+t.info.duration||0}),0))}return S}return i||a}function E(e){var t=h(e,0),r=8;1&t&&(r+=4),4&t&&(r+=4);for(var i=0,n=h(e,4),a=0;a<n;a++)256&t&&(i+=h(e,r),r+=4),512&t&&(r+=4),1024&t&&(r+=4),2048&t&&(r+=4);return i}function T(e,t,r){v(t,["moof","traf"]).forEach((function(t){v(t,["tfhd"]).forEach((function(i){var n=h(i,4),a=e[n];if(a){var o=a.timescale||9e4;v(t,["tfdt"]).forEach((function(e){var t=e[0],i=h(e,4);if(0===t)i-=r*o,c(e,4,i=Math.max(i,0));else{i*=Math.pow(2,32),i+=h(e,8),i-=r*o,i=Math.max(i,0);var n=Math.floor(i/(s+1)),a=Math.floor(i%(s+1));c(e,4,n),c(e,8,a)}}))}}))}))}function S(e){var t={valid:null,remainder:null},r=v(e,["moof"]);if(!r)return t;if(r.length<2)return t.remainder=e,t;var n=r[r.length-1];return t.valid=(0,i.sliceUint8)(e,0,n.byteOffset-8),t.remainder=(0,i.sliceUint8)(e,n.byteOffset-8),t}function b(e,t){var r=new Uint8Array(e.length+t.length);return r.set(e),r.set(t,e.length),r}function L(e,t){var r=[],i=t.samples,a=t.timescale,s=t.id,o=!1;return v(i,["moof"]).map((function(l){var u=l.byteOffset-8;v(l,["traf"]).map((function(l){var d=v(l,["tfdt"]).map((function(e){var t=e[0],r=h(e,4);return 1===t&&(r*=Math.pow(2,32),r+=h(e,8)),r/a}))[0];return void 0!==d&&(e=d),v(l,["tfhd"]).map((function(d){var c=h(d,4),g=16777215&h(d,0),p=0,m=0!=(16&g),y=0,E=0!=(32&g),T=8;c===s&&(0!=(1&g)&&(T+=8),0!=(2&g)&&(T+=4),0!=(8&g)&&(p=h(d,T),T+=4),m&&(y=h(d,T),T+=4),E&&(T+=4),"video"===t.type&&(o=function(e){if(!e)return!1;var t=e.indexOf("."),r=t<0?e:e.substring(0,t);return"hvc1"===r||"hev1"===r||"dvh1"===r||"dvhe"===r}(t.codec)),v(l,["trun"]).map((function(s){var l=s[0],d=16777215&h(s,0),c=0!=(1&d),v=0,g=0!=(4&d),m=0!=(256&d),E=0,T=0!=(512&d),S=0,b=0!=(1024&d),L=0!=(2048&d),R=0,k=h(s,4),_=8;c&&(v=h(s,_),_+=4),g&&(_+=4);for(var x=v+u,I=0;I<k;I++){if(m?(E=h(s,_),_+=4):E=p,T?(S=h(s,_),_+=4):S=y,b&&(_+=4),L&&(R=0===l?h(s,_):f(s,_),_+=4),t.type===n.ElementaryStreamTypes.VIDEO)for(var w=0;w<S;){var C=h(i,x);A(o,i[x+=4])&&D(i.subarray(x,x+C),o?2:1,e+R/a,r),x+=C,w+=C+4}e+=E/a}})))}))}))})),r}function A(e,t){if(e){var r=t>>1&63;return 39===r||40===r}return 6==(31&t)}function D(e,t,r,i){var n=R(e),s=0;s+=t;for(var o=0,l=0,u=!1,f=0;s<n.length;){o=0;do{if(s>=n.length)break;o+=f=n[s++]}while(255===f);l=0;do{if(s>=n.length)break;l+=f=n[s++]}while(255===f);var c=n.length-s;if(!u&&4===o&&s<n.length){if(u=!0,181===n[s++]){var v=d(n,s);if(s+=2,49===v){var g=h(n,s);if(s+=4,1195456820===g){var p=n[s++];if(3===p){var m=n[s++],y=64&m,E=y?2+3*(31&m):0,T=new Uint8Array(E);if(y){T[0]=m;for(var S=1;S<E;S++)T[S]=n[s++]}i.push({type:p,payloadType:o,pts:r,bytes:T})}}}}}else if(5===o&&l<c){if(u=!0,l>16){for(var b=[],L=0;L<16;L++){var A=n[s++].toString(16);b.push(1==A.length?"0"+A:A),3!==L&&5!==L&&7!==L&&9!==L||b.push("-")}for(var D=l-16,k=new Uint8Array(D),_=0;_<D;_++)k[_]=n[s++];i.push({payloadType:o,pts:r,uuid:b.join(""),userData:(0,a.utf8ArrayToStr)(k),userDataBytes:k})}}else if(l<c)s+=l;else if(l>c)break}}function R(e){for(var t=e.byteLength,r=[],i=1;i<t-2;)0===e[i]&&0===e[i+1]&&3===e[i+2]?(r.push(i+2),i+=2):i++;if(0===r.length)return e;var n=t-r.length,a=new Uint8Array(n),s=0;for(i=0;i<n;s++,i++)s===r[0]&&(s++,r.shift()),a[i]=e[s];return a}function k(e){var t=e[0],r="",i="",n=0,a=0,s=0,o=0,l=0,d=0;if(0===t){for(;"\0"!==u(e.subarray(d,d+1));)r+=u(e.subarray(d,d+1)),d+=1;for(r+=u(e.subarray(d,d+1)),d+=1;"\0"!==u(e.subarray(d,d+1));)i+=u(e.subarray(d,d+1)),d+=1;i+=u(e.subarray(d,d+1)),d+=1,n=h(e,12),a=h(e,16),o=h(e,20),l=h(e,24),d=28}else if(1===t){n=h(e,d+=4);var f=h(e,d+=4),c=h(e,d+=4);for(d+=4,s=Math.pow(2,32)*f+c,Number.isSafeInteger(s)||(s=Number.MAX_SAFE_INTEGER,console.warn("Presentation time exceeds safe integer limit and wrapped to max safe integer in parsing emsg box")),o=h(e,d),l=h(e,d+=4),d+=4;"\0"!==u(e.subarray(d,d+1));)r+=u(e.subarray(d,d+1)),d+=1;for(r+=u(e.subarray(d,d+1)),d+=1;"\0"!==u(e.subarray(d,d+1));)i+=u(e.subarray(d,d+1)),d+=1;i+=u(e.subarray(d,d+1)),d+=1}return{schemeIdUri:r,value:i,timeScale:n,presentationTime:s,presentationTimeDelta:a,eventDuration:o,id:l,payload:e.subarray(d,e.byteLength)}}},145:(e,t,r)=>{"use strict";function i(e,t,r){return Uint8Array.prototype.slice?e.slice(t,r):new Uint8Array(Array.prototype.slice.call(e,t,r))}r.r(t),r.d(t,{sliceUint8:()=>i})},729:e=>{"use strict";var t=Object.prototype.hasOwnProperty,r="~";function i(){}function n(e,t,r){this.fn=e,this.context=t,this.once=r||!1}function a(e,t,i,a,s){if("function"!=typeof i)throw new TypeError("The listener must be a function");var o=new n(i,a||e,s),l=r?r+t:t;return e._events[l]?e._events[l].fn?e._events[l]=[e._events[l],o]:e._events[l].push(o):(e._events[l]=o,e._eventsCount++),e}function s(e,t){0==--e._eventsCount?e._events=new i:delete e._events[t]}function o(){this._events=new i,this._eventsCount=0}Object.create&&(i.prototype=Object.create(null),(new i).__proto__||(r=!1)),o.prototype.eventNames=function(){var e,i,n=[];if(0===this._eventsCount)return n;for(i in e=this._events)t.call(e,i)&&n.push(r?i.slice(1):i);return Object.getOwnPropertySymbols?n.concat(Object.getOwnPropertySymbols(e)):n},o.prototype.listeners=function(e){var t=r?r+e:e,i=this._events[t];if(!i)return[];if(i.fn)return[i.fn];for(var n=0,a=i.length,s=new Array(a);n<a;n++)s[n]=i[n].fn;return s},o.prototype.listenerCount=function(e){var t=r?r+e:e,i=this._events[t];return i?i.fn?1:i.length:0},o.prototype.emit=function(e,t,i,n,a,s){var o=r?r+e:e;if(!this._events[o])return!1;var l,u,d=this._events[o],h=arguments.length;if(d.fn){switch(d.once&&this.removeListener(e,d.fn,void 0,!0),h){case 1:return d.fn.call(d.context),!0;case 2:return d.fn.call(d.context,t),!0;case 3:return d.fn.call(d.context,t,i),!0;case 4:return d.fn.call(d.context,t,i,n),!0;case 5:return d.fn.call(d.context,t,i,n,a),!0;case 6:return d.fn.call(d.context,t,i,n,a,s),!0}for(u=1,l=new Array(h-1);u<h;u++)l[u-1]=arguments[u];d.fn.apply(d.context,l)}else{var f,c=d.length;for(u=0;u<c;u++)switch(d[u].once&&this.removeListener(e,d[u].fn,void 0,!0),h){case 1:d[u].fn.call(d[u].context);break;case 2:d[u].fn.call(d[u].context,t);break;case 3:d[u].fn.call(d[u].context,t,i);break;case 4:d[u].fn.call(d[u].context,t,i,n);break;default:if(!l)for(f=1,l=new Array(h-1);f<h;f++)l[f-1]=arguments[f];d[u].fn.apply(d[u].context,l)}}return!0},o.prototype.on=function(e,t,r){return a(this,e,t,r,!1)},o.prototype.once=function(e,t,r){return a(this,e,t,r,!0)},o.prototype.removeListener=function(e,t,i,n){var a=r?r+e:e;if(!this._events[a])return this;if(!t)return s(this,a),this;var o=this._events[a];if(o.fn)o.fn!==t||n&&!o.once||i&&o.context!==i||s(this,a);else{for(var l=0,u=[],d=o.length;l<d;l++)(o[l].fn!==t||n&&!o[l].once||i&&o[l].context!==i)&&u.push(o[l]);u.length?this._events[a]=1===u.length?u[0]:u:s(this,a)}return this},o.prototype.removeAllListeners=function(e){var t;return e?(t=r?r+e:e,this._events[t]&&s(this,t)):(this._events=new i,this._eventsCount=0),this},o.prototype.off=o.prototype.removeListener,o.prototype.addListener=o.prototype.on,o.prefixed=r,o.EventEmitter=o,e.exports=o},945:function(e){var t,r,i,n,a;t=/^(?=((?:[a-zA-Z0-9+\-.]+:)?))\1(?=((?:\/\/[^\/?#]*)?))\2(?=((?:(?:[^?#\/]*\/)*[^;?#\/]*)?))\3((?:;[^?#]*)?)(\?[^#]*)?(#[^]*)?$/,r=/^(?=([^\/?#]*))\1([^]*)$/,i=/(?:\/|^)\.(?=\/)/g,n=/(?:\/|^)\.\.\/(?!\.\.\/)[^\/]*(?=\/)/g,a={buildAbsoluteURL:function(e,t,i){if(i=i||{},e=e.trim(),!(t=t.trim())){if(!i.alwaysNormalize)return e;var n=a.parseURL(e);if(!n)throw new Error("Error trying to parse base URL.");return n.path=a.normalizePath(n.path),a.buildURLFromParts(n)}var s=a.parseURL(t);if(!s)throw new Error("Error trying to parse relative URL.");if(s.scheme)return i.alwaysNormalize?(s.path=a.normalizePath(s.path),a.buildURLFromParts(s)):t;var o=a.parseURL(e);if(!o)throw new Error("Error trying to parse base URL.");if(!o.netLoc&&o.path&&"/"!==o.path[0]){var l=r.exec(o.path);o.netLoc=l[1],o.path=l[2]}o.netLoc&&!o.path&&(o.path="/");var u={scheme:o.scheme,netLoc:s.netLoc,path:null,params:s.params,query:s.query,fragment:s.fragment};if(!s.netLoc&&(u.netLoc=o.netLoc,"/"!==s.path[0]))if(s.path){var d=o.path,h=d.substring(0,d.lastIndexOf("/")+1)+s.path;u.path=a.normalizePath(h)}else u.path=o.path,s.params||(u.params=o.params,s.query||(u.query=o.query));return null===u.path&&(u.path=i.alwaysNormalize?a.normalizePath(s.path):s.path),a.buildURLFromParts(u)},parseURL:function(e){var r=t.exec(e);return r?{scheme:r[1]||"",netLoc:r[2]||"",path:r[3]||"",params:r[4]||"",query:r[5]||"",fragment:r[6]||""}:null},normalizePath:function(e){for(e=e.split("").reverse().join("").replace(i,"");e.length!==(e=e.replace(n,"")).length;);return e.split("").reverse().join("")},buildURLFromParts:function(e){return e.scheme+e.netLoc+e.path+e.params+e.query+e.fragment}},e.exports=a}},t={};function r(i){var n=t[i];if(void 0!==n)return n.exports;var a=t[i]={exports:{}};return e[i].call(a.exports,a,a.exports,r),a.exports}r.m=e,r.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return r.d(t,{a:t}),t},r.d=(e,t)=>{for(var i in t)r.o(t,i)&&!r.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var i=r(392);return i.default})())); -// @license-end diff --git a/public/js/hls.min.js b/public/js/hls.min.js new file mode 100644 index 0000000..b9098c5 --- /dev/null +++ b/public/js/hls.min.js @@ -0,0 +1,5 @@ +// @license http://www.apache.org/licenses/LICENSE-2.0 Apache-2.0 +// @source https://github.com/video-dev/hls.js +// @version v1.5.1 +!function t(e){var r,i;r=this,i=function(){"use strict";function r(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(t);e&&(i=i.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),r.push.apply(r,i)}return r}function i(t){for(var e=1;e<arguments.length;e++){var i=null!=arguments[e]?arguments[e]:{};e%2?r(Object(i),!0).forEach((function(e){var r,a,s;r=t,a=e,s=i[e],(a=n(a))in r?Object.defineProperty(r,a,{value:s,enumerable:!0,configurable:!0,writable:!0}):r[a]=s})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(i)):r(Object(i)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(i,e))}))}return t}function n(t){var e=function(t,e){if("object"!=typeof t||!t)return t;var r=t[Symbol.toPrimitive];if(void 0!==r){var i=r.call(t,e||"default");if("object"!=typeof i)return i;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===e?String:Number)(t)}(t,"string");return"symbol"==typeof e?e:String(e)}function a(t,e){for(var r=0;r<e.length;r++){var i=e[r];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,n(i.key),i)}}function s(t,e,r){return e&&a(t.prototype,e),r&&a(t,r),Object.defineProperty(t,"prototype",{writable:!1}),t}function o(){return o=Object.assign?Object.assign.bind():function(t){for(var e=1;e<arguments.length;e++){var r=arguments[e];for(var i in r)Object.prototype.hasOwnProperty.call(r,i)&&(t[i]=r[i])}return t},o.apply(this,arguments)}function l(t,e){t.prototype=Object.create(e.prototype),t.prototype.constructor=t,h(t,e)}function u(t){return u=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(t){return t.__proto__||Object.getPrototypeOf(t)},u(t)}function h(t,e){return h=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(t,e){return t.__proto__=e,t},h(t,e)}function d(t,e,r){return d=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(t){return!1}}()?Reflect.construct.bind():function(t,e,r){var i=[null];i.push.apply(i,e);var n=new(Function.bind.apply(t,i));return r&&h(n,r.prototype),n},d.apply(null,arguments)}function c(t){var e="function"==typeof Map?new Map:void 0;return c=function(t){if(null===t||!function(t){try{return-1!==Function.toString.call(t).indexOf("[native code]")}catch(e){return"function"==typeof t}}(t))return t;if("function"!=typeof t)throw new TypeError("Super expression must either be null or a function");if(void 0!==e){if(e.has(t))return e.get(t);e.set(t,r)}function r(){return d(t,arguments,u(this).constructor)}return r.prototype=Object.create(t.prototype,{constructor:{value:r,enumerable:!1,writable:!0,configurable:!0}}),h(r,t)},c(t)}function f(t,e){(null==e||e>t.length)&&(e=t.length);for(var r=0,i=new Array(e);r<e;r++)i[r]=t[r];return i}function g(t,e){var r="undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(r)return(r=r.call(t)).next.bind(r);if(Array.isArray(t)||(r=function(t,e){if(t){if("string"==typeof t)return f(t,e);var r=Object.prototype.toString.call(t).slice(8,-1);return"Object"===r&&t.constructor&&(r=t.constructor.name),"Map"===r||"Set"===r?Array.from(t):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?f(t,e):void 0}}(t))||e&&t&&"number"==typeof t.length){r&&(t=r);var i=0;return function(){return i>=t.length?{done:!0}:{done:!1,value:t[i++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function v(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var m={exports:{}};!function(t,e){var r,i,n,a,s;r=/^(?=((?:[a-zA-Z0-9+\-.]+:)?))\1(?=((?:\/\/[^\/?#]*)?))\2(?=((?:(?:[^?#\/]*\/)*[^;?#\/]*)?))\3((?:;[^?#]*)?)(\?[^#]*)?(#[^]*)?$/,i=/^(?=([^\/?#]*))\1([^]*)$/,n=/(?:\/|^)\.(?=\/)/g,a=/(?:\/|^)\.\.\/(?!\.\.\/)[^\/]*(?=\/)/g,s={buildAbsoluteURL:function(t,e,r){if(r=r||{},t=t.trim(),!(e=e.trim())){if(!r.alwaysNormalize)return t;var n=s.parseURL(t);if(!n)throw new Error("Error trying to parse base URL.");return n.path=s.normalizePath(n.path),s.buildURLFromParts(n)}var a=s.parseURL(e);if(!a)throw new Error("Error trying to parse relative URL.");if(a.scheme)return r.alwaysNormalize?(a.path=s.normalizePath(a.path),s.buildURLFromParts(a)):e;var o=s.parseURL(t);if(!o)throw new Error("Error trying to parse base URL.");if(!o.netLoc&&o.path&&"/"!==o.path[0]){var l=i.exec(o.path);o.netLoc=l[1],o.path=l[2]}o.netLoc&&!o.path&&(o.path="/");var u={scheme:o.scheme,netLoc:a.netLoc,path:null,params:a.params,query:a.query,fragment:a.fragment};if(!a.netLoc&&(u.netLoc=o.netLoc,"/"!==a.path[0]))if(a.path){var h=o.path,d=h.substring(0,h.lastIndexOf("/")+1)+a.path;u.path=s.normalizePath(d)}else u.path=o.path,a.params||(u.params=o.params,a.query||(u.query=o.query));return null===u.path&&(u.path=r.alwaysNormalize?s.normalizePath(a.path):a.path),s.buildURLFromParts(u)},parseURL:function(t){var e=r.exec(t);return e?{scheme:e[1]||"",netLoc:e[2]||"",path:e[3]||"",params:e[4]||"",query:e[5]||"",fragment:e[6]||""}:null},normalizePath:function(t){for(t=t.split("").reverse().join("").replace(n,"");t.length!==(t=t.replace(a,"")).length;);return t.split("").reverse().join("")},buildURLFromParts:function(t){return t.scheme+t.netLoc+t.path+t.params+t.query+t.fragment}},t.exports=s}(m);var p=m.exports,y=Number.isFinite||function(t){return"number"==typeof t&&isFinite(t)},E=Number.isSafeInteger||function(t){return"number"==typeof t&&Math.abs(t)<=T},T=Number.MAX_SAFE_INTEGER||9007199254740991,S=function(t){return t.MEDIA_ATTACHING="hlsMediaAttaching",t.MEDIA_ATTACHED="hlsMediaAttached",t.MEDIA_DETACHING="hlsMediaDetaching",t.MEDIA_DETACHED="hlsMediaDetached",t.BUFFER_RESET="hlsBufferReset",t.BUFFER_CODECS="hlsBufferCodecs",t.BUFFER_CREATED="hlsBufferCreated",t.BUFFER_APPENDING="hlsBufferAppending",t.BUFFER_APPENDED="hlsBufferAppended",t.BUFFER_EOS="hlsBufferEos",t.BUFFER_FLUSHING="hlsBufferFlushing",t.BUFFER_FLUSHED="hlsBufferFlushed",t.MANIFEST_LOADING="hlsManifestLoading",t.MANIFEST_LOADED="hlsManifestLoaded",t.MANIFEST_PARSED="hlsManifestParsed",t.LEVEL_SWITCHING="hlsLevelSwitching",t.LEVEL_SWITCHED="hlsLevelSwitched",t.LEVEL_LOADING="hlsLevelLoading",t.LEVEL_LOADED="hlsLevelLoaded",t.LEVEL_UPDATED="hlsLevelUpdated",t.LEVEL_PTS_UPDATED="hlsLevelPtsUpdated",t.LEVELS_UPDATED="hlsLevelsUpdated",t.AUDIO_TRACKS_UPDATED="hlsAudioTracksUpdated",t.AUDIO_TRACK_SWITCHING="hlsAudioTrackSwitching",t.AUDIO_TRACK_SWITCHED="hlsAudioTrackSwitched",t.AUDIO_TRACK_LOADING="hlsAudioTrackLoading",t.AUDIO_TRACK_LOADED="hlsAudioTrackLoaded",t.SUBTITLE_TRACKS_UPDATED="hlsSubtitleTracksUpdated",t.SUBTITLE_TRACKS_CLEARED="hlsSubtitleTracksCleared",t.SUBTITLE_TRACK_SWITCH="hlsSubtitleTrackSwitch",t.SUBTITLE_TRACK_LOADING="hlsSubtitleTrackLoading",t.SUBTITLE_TRACK_LOADED="hlsSubtitleTrackLoaded",t.SUBTITLE_FRAG_PROCESSED="hlsSubtitleFragProcessed",t.CUES_PARSED="hlsCuesParsed",t.NON_NATIVE_TEXT_TRACKS_FOUND="hlsNonNativeTextTracksFound",t.INIT_PTS_FOUND="hlsInitPtsFound",t.FRAG_LOADING="hlsFragLoading",t.FRAG_LOAD_EMERGENCY_ABORTED="hlsFragLoadEmergencyAborted",t.FRAG_LOADED="hlsFragLoaded",t.FRAG_DECRYPTED="hlsFragDecrypted",t.FRAG_PARSING_INIT_SEGMENT="hlsFragParsingInitSegment",t.FRAG_PARSING_USERDATA="hlsFragParsingUserdata",t.FRAG_PARSING_METADATA="hlsFragParsingMetadata",t.FRAG_PARSED="hlsFragParsed",t.FRAG_BUFFERED="hlsFragBuffered",t.FRAG_CHANGED="hlsFragChanged",t.FPS_DROP="hlsFpsDrop",t.FPS_DROP_LEVEL_CAPPING="hlsFpsDropLevelCapping",t.MAX_AUTO_LEVEL_UPDATED="hlsMaxAutoLevelUpdated",t.ERROR="hlsError",t.DESTROYING="hlsDestroying",t.KEY_LOADING="hlsKeyLoading",t.KEY_LOADED="hlsKeyLoaded",t.LIVE_BACK_BUFFER_REACHED="hlsLiveBackBufferReached",t.BACK_BUFFER_REACHED="hlsBackBufferReached",t.STEERING_MANIFEST_LOADED="hlsSteeringManifestLoaded",t}({}),L=function(t){return t.NETWORK_ERROR="networkError",t.MEDIA_ERROR="mediaError",t.KEY_SYSTEM_ERROR="keySystemError",t.MUX_ERROR="muxError",t.OTHER_ERROR="otherError",t}({}),A=function(t){return t.KEY_SYSTEM_NO_KEYS="keySystemNoKeys",t.KEY_SYSTEM_NO_ACCESS="keySystemNoAccess",t.KEY_SYSTEM_NO_SESSION="keySystemNoSession",t.KEY_SYSTEM_NO_CONFIGURED_LICENSE="keySystemNoConfiguredLicense",t.KEY_SYSTEM_LICENSE_REQUEST_FAILED="keySystemLicenseRequestFailed",t.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED="keySystemServerCertificateRequestFailed",t.KEY_SYSTEM_SERVER_CERTIFICATE_UPDATE_FAILED="keySystemServerCertificateUpdateFailed",t.KEY_SYSTEM_SESSION_UPDATE_FAILED="keySystemSessionUpdateFailed",t.KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED="keySystemStatusOutputRestricted",t.KEY_SYSTEM_STATUS_INTERNAL_ERROR="keySystemStatusInternalError",t.MANIFEST_LOAD_ERROR="manifestLoadError",t.MANIFEST_LOAD_TIMEOUT="manifestLoadTimeOut",t.MANIFEST_PARSING_ERROR="manifestParsingError",t.MANIFEST_INCOMPATIBLE_CODECS_ERROR="manifestIncompatibleCodecsError",t.LEVEL_EMPTY_ERROR="levelEmptyError",t.LEVEL_LOAD_ERROR="levelLoadError",t.LEVEL_LOAD_TIMEOUT="levelLoadTimeOut",t.LEVEL_PARSING_ERROR="levelParsingError",t.LEVEL_SWITCH_ERROR="levelSwitchError",t.AUDIO_TRACK_LOAD_ERROR="audioTrackLoadError",t.AUDIO_TRACK_LOAD_TIMEOUT="audioTrackLoadTimeOut",t.SUBTITLE_LOAD_ERROR="subtitleTrackLoadError",t.SUBTITLE_TRACK_LOAD_TIMEOUT="subtitleTrackLoadTimeOut",t.FRAG_LOAD_ERROR="fragLoadError",t.FRAG_LOAD_TIMEOUT="fragLoadTimeOut",t.FRAG_DECRYPT_ERROR="fragDecryptError",t.FRAG_PARSING_ERROR="fragParsingError",t.FRAG_GAP="fragGap",t.REMUX_ALLOC_ERROR="remuxAllocError",t.KEY_LOAD_ERROR="keyLoadError",t.KEY_LOAD_TIMEOUT="keyLoadTimeOut",t.BUFFER_ADD_CODEC_ERROR="bufferAddCodecError",t.BUFFER_INCOMPATIBLE_CODECS_ERROR="bufferIncompatibleCodecsError",t.BUFFER_APPEND_ERROR="bufferAppendError",t.BUFFER_APPENDING_ERROR="bufferAppendingError",t.BUFFER_STALLED_ERROR="bufferStalledError",t.BUFFER_FULL_ERROR="bufferFullError",t.BUFFER_SEEK_OVER_HOLE="bufferSeekOverHole",t.BUFFER_NUDGE_ON_STALL="bufferNudgeOnStall",t.INTERNAL_EXCEPTION="internalException",t.INTERNAL_ABORTED="aborted",t.UNKNOWN="unknown",t}({}),R=function(){},k={trace:R,debug:R,log:R,warn:R,info:R,error:R},b=k;function D(t){for(var e=arguments.length,r=new Array(e>1?e-1:0),i=1;i<e;i++)r[i-1]=arguments[i];r.forEach((function(e){b[e]=t[e]?t[e].bind(t):function(t){var e=self.console[t];return e?e.bind(self.console,"["+t+"] >"):R}(e)}))}function I(t,e){if("object"==typeof console&&!0===t||"object"==typeof t){D(t,"debug","log","info","warn","error");try{b.log('Debug logs enabled for "'+e+'" in hls.js version 1.5.1')}catch(t){b=k}}else b=k}var w=b,C=/^(\d+)x(\d+)$/,_=/(.+?)=(".*?"|.*?)(?:,|$)/g,x=function(){function t(e){"string"==typeof e&&(e=t.parseAttrList(e)),o(this,e)}var e=t.prototype;return e.decimalInteger=function(t){var e=parseInt(this[t],10);return e>Number.MAX_SAFE_INTEGER?1/0:e},e.hexadecimalInteger=function(t){if(this[t]){var e=(this[t]||"0x").slice(2);e=(1&e.length?"0":"")+e;for(var r=new Uint8Array(e.length/2),i=0;i<e.length/2;i++)r[i]=parseInt(e.slice(2*i,2*i+2),16);return r}return null},e.hexadecimalIntegerAsNumber=function(t){var e=parseInt(this[t],16);return e>Number.MAX_SAFE_INTEGER?1/0:e},e.decimalFloatingPoint=function(t){return parseFloat(this[t])},e.optionalFloat=function(t,e){var r=this[t];return r?parseFloat(r):e},e.enumeratedString=function(t){return this[t]},e.bool=function(t){return"YES"===this[t]},e.decimalResolution=function(t){var e=C.exec(this[t]);if(null!==e)return{width:parseInt(e[1],10),height:parseInt(e[2],10)}},t.parseAttrList=function(t){var e,r={};for(_.lastIndex=0;null!==(e=_.exec(t));){var i=e[2];0===i.indexOf('"')&&i.lastIndexOf('"')===i.length-1&&(i=i.slice(1,-1)),r[e[1].trim()]=i}return r},s(t,[{key:"clientAttrs",get:function(){return Object.keys(this).filter((function(t){return"X-"===t.substring(0,2)}))}}]),t}();function P(t){return"SCTE35-OUT"===t||"SCTE35-IN"===t}var F=function(){function t(t,e){if(this.attr=void 0,this._startDate=void 0,this._endDate=void 0,this._badValueForSameId=void 0,e){var r=e.attr;for(var i in r)if(Object.prototype.hasOwnProperty.call(t,i)&&t[i]!==r[i]){w.warn('DATERANGE tag attribute: "'+i+'" does not match for tags with ID: "'+t.ID+'"'),this._badValueForSameId=i;break}t=o(new x({}),r,t)}if(this.attr=t,this._startDate=new Date(t["START-DATE"]),"END-DATE"in this.attr){var n=new Date(this.attr["END-DATE"]);y(n.getTime())&&(this._endDate=n)}}return s(t,[{key:"id",get:function(){return this.attr.ID}},{key:"class",get:function(){return this.attr.CLASS}},{key:"startDate",get:function(){return this._startDate}},{key:"endDate",get:function(){if(this._endDate)return this._endDate;var t=this.duration;return null!==t?new Date(this._startDate.getTime()+1e3*t):null}},{key:"duration",get:function(){if("DURATION"in this.attr){var t=this.attr.decimalFloatingPoint("DURATION");if(y(t))return t}else if(this._endDate)return(this._endDate.getTime()-this._startDate.getTime())/1e3;return null}},{key:"plannedDuration",get:function(){return"PLANNED-DURATION"in this.attr?this.attr.decimalFloatingPoint("PLANNED-DURATION"):null}},{key:"endOnNext",get:function(){return this.attr.bool("END-ON-NEXT")}},{key:"isValid",get:function(){return!!this.id&&!this._badValueForSameId&&y(this.startDate.getTime())&&(null===this.duration||this.duration>=0)&&(!this.endOnNext||!!this.class)}}]),t}(),M=function(){this.aborted=!1,this.loaded=0,this.retry=0,this.total=0,this.chunkCount=0,this.bwEstimate=0,this.loading={start:0,first:0,end:0},this.parsing={start:0,end:0},this.buffering={start:0,first:0,end:0}},O="audio",N="video",U="audiovideo",B=function(){function t(t){var e;this._byteRange=null,this._url=null,this.baseurl=void 0,this.relurl=void 0,this.elementaryStreams=((e={})[O]=null,e[N]=null,e[U]=null,e),this.baseurl=t}return t.prototype.setByteRange=function(t,e){var r,i=t.split("@",2);r=1===i.length?(null==e?void 0:e.byteRangeEndOffset)||0:parseInt(i[1]),this._byteRange=[r,parseInt(i[0])+r]},s(t,[{key:"byteRange",get:function(){return this._byteRange?this._byteRange:[]}},{key:"byteRangeStartOffset",get:function(){return this.byteRange[0]}},{key:"byteRangeEndOffset",get:function(){return this.byteRange[1]}},{key:"url",get:function(){return!this._url&&this.baseurl&&this.relurl&&(this._url=p.buildAbsoluteURL(this.baseurl,this.relurl,{alwaysNormalize:!0})),this._url||""},set:function(t){this._url=t}}]),t}(),G=function(t){function e(e,r){var i;return(i=t.call(this,r)||this)._decryptdata=null,i.rawProgramDateTime=null,i.programDateTime=null,i.tagList=[],i.duration=0,i.sn=0,i.levelkeys=void 0,i.type=void 0,i.loader=null,i.keyLoader=null,i.level=-1,i.cc=0,i.startPTS=void 0,i.endPTS=void 0,i.startDTS=void 0,i.endDTS=void 0,i.start=0,i.deltaPTS=void 0,i.maxStartPTS=void 0,i.minEndPTS=void 0,i.stats=new M,i.data=void 0,i.bitrateTest=!1,i.title=null,i.initSegment=null,i.endList=void 0,i.gap=void 0,i.urlId=0,i.type=e,i}l(e,t);var r=e.prototype;return r.setKeyFormat=function(t){if(this.levelkeys){var e=this.levelkeys[t];e&&!this._decryptdata&&(this._decryptdata=e.getDecryptData(this.sn))}},r.abortRequests=function(){var t,e;null==(t=this.loader)||t.abort(),null==(e=this.keyLoader)||e.abort()},r.setElementaryStreamInfo=function(t,e,r,i,n,a){void 0===a&&(a=!1);var s=this.elementaryStreams,o=s[t];o?(o.startPTS=Math.min(o.startPTS,e),o.endPTS=Math.max(o.endPTS,r),o.startDTS=Math.min(o.startDTS,i),o.endDTS=Math.max(o.endDTS,n)):s[t]={startPTS:e,endPTS:r,startDTS:i,endDTS:n,partial:a}},r.clearElementaryStreamInfo=function(){var t=this.elementaryStreams;t[O]=null,t[N]=null,t[U]=null},s(e,[{key:"decryptdata",get:function(){if(!this.levelkeys&&!this._decryptdata)return null;if(!this._decryptdata&&this.levelkeys&&!this.levelkeys.NONE){var t=this.levelkeys.identity;if(t)this._decryptdata=t.getDecryptData(this.sn);else{var e=Object.keys(this.levelkeys);if(1===e.length)return this._decryptdata=this.levelkeys[e[0]].getDecryptData(this.sn)}}return this._decryptdata}},{key:"end",get:function(){return this.start+this.duration}},{key:"endProgramDateTime",get:function(){if(null===this.programDateTime)return null;if(!y(this.programDateTime))return null;var t=y(this.duration)?this.duration:0;return this.programDateTime+1e3*t}},{key:"encrypted",get:function(){var t;if(null!=(t=this._decryptdata)&&t.encrypted)return!0;if(this.levelkeys){var e=Object.keys(this.levelkeys),r=e.length;if(r>1||1===r&&this.levelkeys[e[0]].encrypted)return!0}return!1}}]),e}(B),K=function(t){function e(e,r,i,n,a){var s;(s=t.call(this,i)||this).fragOffset=0,s.duration=0,s.gap=!1,s.independent=!1,s.relurl=void 0,s.fragment=void 0,s.index=void 0,s.stats=new M,s.duration=e.decimalFloatingPoint("DURATION"),s.gap=e.bool("GAP"),s.independent=e.bool("INDEPENDENT"),s.relurl=e.enumeratedString("URI"),s.fragment=r,s.index=n;var o=e.enumeratedString("BYTERANGE");return o&&s.setByteRange(o,a),a&&(s.fragOffset=a.fragOffset+a.duration),s}return l(e,t),s(e,[{key:"start",get:function(){return this.fragment.start+this.fragOffset}},{key:"end",get:function(){return this.start+this.duration}},{key:"loaded",get:function(){var t=this.elementaryStreams;return!!(t.audio||t.video||t.audiovideo)}}]),e}(B),H=function(){function t(t){this.PTSKnown=!1,this.alignedSliding=!1,this.averagetargetduration=void 0,this.endCC=0,this.endSN=0,this.fragments=void 0,this.fragmentHint=void 0,this.partList=null,this.dateRanges=void 0,this.live=!0,this.ageHeader=0,this.advancedDateTime=void 0,this.updated=!0,this.advanced=!0,this.availabilityDelay=void 0,this.misses=0,this.startCC=0,this.startSN=0,this.startTimeOffset=null,this.targetduration=0,this.totalduration=0,this.type=null,this.url=void 0,this.m3u8="",this.version=null,this.canBlockReload=!1,this.canSkipUntil=0,this.canSkipDateRanges=!1,this.skippedSegments=0,this.recentlyRemovedDateranges=void 0,this.partHoldBack=0,this.holdBack=0,this.partTarget=0,this.preloadHint=void 0,this.renditionReports=void 0,this.tuneInGoal=0,this.deltaUpdateFailed=void 0,this.driftStartTime=0,this.driftEndTime=0,this.driftStart=0,this.driftEnd=0,this.encryptedFragments=void 0,this.playlistParsingError=null,this.variableList=null,this.hasVariableRefs=!1,this.fragments=[],this.encryptedFragments=[],this.dateRanges={},this.url=t}return t.prototype.reloaded=function(t){if(!t)return this.advanced=!0,void(this.updated=!0);var e=this.lastPartSn-t.lastPartSn,r=this.lastPartIndex-t.lastPartIndex;this.updated=this.endSN!==t.endSN||!!r||!!e||!this.live,this.advanced=this.endSN>t.endSN||e>0||0===e&&r>0,this.updated||this.advanced?this.misses=Math.floor(.6*t.misses):this.misses=t.misses+1,this.availabilityDelay=t.availabilityDelay},s(t,[{key:"hasProgramDateTime",get:function(){return!!this.fragments.length&&y(this.fragments[this.fragments.length-1].programDateTime)}},{key:"levelTargetDuration",get:function(){return this.averagetargetduration||this.targetduration||10}},{key:"drift",get:function(){var t=this.driftEndTime-this.driftStartTime;return t>0?1e3*(this.driftEnd-this.driftStart)/t:1}},{key:"edge",get:function(){return this.partEnd||this.fragmentEnd}},{key:"partEnd",get:function(){var t;return null!=(t=this.partList)&&t.length?this.partList[this.partList.length-1].end:this.fragmentEnd}},{key:"fragmentEnd",get:function(){var t;return null!=(t=this.fragments)&&t.length?this.fragments[this.fragments.length-1].end:0}},{key:"age",get:function(){return this.advancedDateTime?Math.max(Date.now()-this.advancedDateTime,0)/1e3:0}},{key:"lastPartIndex",get:function(){var t;return null!=(t=this.partList)&&t.length?this.partList[this.partList.length-1].index:-1}},{key:"lastPartSn",get:function(){var t;return null!=(t=this.partList)&&t.length?this.partList[this.partList.length-1].fragment.sn:this.endSN}}]),t}();function V(t){return Uint8Array.from(atob(t),(function(t){return t.charCodeAt(0)}))}function Y(t){var e,r,i=t.split(":"),n=null;if("data"===i[0]&&2===i.length){var a=i[1].split(";"),s=a[a.length-1].split(",");if(2===s.length){var o="base64"===s[0],l=s[1];o?(a.splice(-1,1),n=V(l)):(e=W(l).subarray(0,16),(r=new Uint8Array(16)).set(e,16-e.length),n=r)}}return n}function W(t){return Uint8Array.from(unescape(encodeURIComponent(t)),(function(t){return t.charCodeAt(0)}))}var j="undefined"!=typeof self?self:void 0,q={CLEARKEY:"org.w3.clearkey",FAIRPLAY:"com.apple.fps",PLAYREADY:"com.microsoft.playready",WIDEVINE:"com.widevine.alpha"},X="org.w3.clearkey",z="com.apple.streamingkeydelivery",Q="com.microsoft.playready",J="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed";function $(t){switch(t){case z:return q.FAIRPLAY;case Q:return q.PLAYREADY;case J:return q.WIDEVINE;case X:return q.CLEARKEY}}var Z="edef8ba979d64acea3c827dcd51d21ed";function tt(t){switch(t){case q.FAIRPLAY:return z;case q.PLAYREADY:return Q;case q.WIDEVINE:return J;case q.CLEARKEY:return X}}function et(t){var e=t.drmSystems,r=t.widevineLicenseUrl,i=e?[q.FAIRPLAY,q.WIDEVINE,q.PLAYREADY,q.CLEARKEY].filter((function(t){return!!e[t]})):[];return!i[q.WIDEVINE]&&r&&i.push(q.WIDEVINE),i}var rt,it=null!=j&&null!=(rt=j.navigator)&&rt.requestMediaKeySystemAccess?self.navigator.requestMediaKeySystemAccess.bind(self.navigator):null;function nt(t,e,r){return Uint8Array.prototype.slice?t.slice(e,r):new Uint8Array(Array.prototype.slice.call(t,e,r))}var at,st=function(t,e){return e+10<=t.length&&73===t[e]&&68===t[e+1]&&51===t[e+2]&&t[e+3]<255&&t[e+4]<255&&t[e+6]<128&&t[e+7]<128&&t[e+8]<128&&t[e+9]<128},ot=function(t,e){return e+10<=t.length&&51===t[e]&&68===t[e+1]&&73===t[e+2]&&t[e+3]<255&&t[e+4]<255&&t[e+6]<128&&t[e+7]<128&&t[e+8]<128&&t[e+9]<128},lt=function(t,e){for(var r=e,i=0;st(t,e);)i+=10,i+=ut(t,e+6),ot(t,e+10)&&(i+=10),e+=i;if(i>0)return t.subarray(r,r+i)},ut=function(t,e){var r=0;return r=(127&t[e])<<21,r|=(127&t[e+1])<<14,r|=(127&t[e+2])<<7,r|=127&t[e+3]},ht=function(t,e){return st(t,e)&&ut(t,e+6)+10<=t.length-e},dt=function(t){for(var e=gt(t),r=0;r<e.length;r++){var i=e[r];if(ct(i))return Et(i)}},ct=function(t){return t&&"PRIV"===t.key&&"com.apple.streaming.transportStreamTimestamp"===t.info},ft=function(t){var e=String.fromCharCode(t[0],t[1],t[2],t[3]),r=ut(t,4);return{type:e,size:r,data:t.subarray(10,10+r)}},gt=function(t){for(var e=0,r=[];st(t,e);){for(var i=ut(t,e+6),n=(e+=10)+i;e+8<n;){var a=ft(t.subarray(e)),s=vt(a);s&&r.push(s),e+=a.size+10}ot(t,e)&&(e+=10)}return r},vt=function(t){return"PRIV"===t.type?mt(t):"W"===t.type[0]?yt(t):pt(t)},mt=function(t){if(!(t.size<2)){var e=Tt(t.data,!0),r=new Uint8Array(t.data.subarray(e.length+1));return{key:t.type,info:e,data:r.buffer}}},pt=function(t){if(!(t.size<2)){if("TXXX"===t.type){var e=1,r=Tt(t.data.subarray(e),!0);e+=r.length+1;var i=Tt(t.data.subarray(e));return{key:t.type,info:r,data:i}}var n=Tt(t.data.subarray(1));return{key:t.type,data:n}}},yt=function(t){if("WXXX"===t.type){if(t.size<2)return;var e=1,r=Tt(t.data.subarray(e),!0);e+=r.length+1;var i=Tt(t.data.subarray(e));return{key:t.type,info:r,data:i}}var n=Tt(t.data);return{key:t.type,data:n}},Et=function(t){if(8===t.data.byteLength){var e=new Uint8Array(t.data),r=1&e[3],i=(e[4]<<23)+(e[5]<<15)+(e[6]<<7)+e[7];return i/=45,r&&(i+=47721858.84),Math.round(i)}},Tt=function(t,e){void 0===e&&(e=!1);var r=St();if(r){var i=r.decode(t);if(e){var n=i.indexOf("\0");return-1!==n?i.substring(0,n):i}return i.replace(/\0/g,"")}for(var a,s,o,l=t.length,u="",h=0;h<l;){if(0===(a=t[h++])&&e)return u;if(0!==a&&3!==a)switch(a>>4){case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:u+=String.fromCharCode(a);break;case 12:case 13:s=t[h++],u+=String.fromCharCode((31&a)<<6|63&s);break;case 14:s=t[h++],o=t[h++],u+=String.fromCharCode((15&a)<<12|(63&s)<<6|(63&o)<<0)}}return u};function St(){if(!navigator.userAgent.includes("PlayStation 4"))return at||void 0===self.TextDecoder||(at=new self.TextDecoder("utf-8")),at}var Lt=function(t){for(var e="",r=0;r<t.length;r++){var i=t[r].toString(16);i.length<2&&(i="0"+i),e+=i}return e},At=Math.pow(2,32)-1,Rt=[].push,kt={video:1,audio:2,id3:3,text:4};function bt(t){return String.fromCharCode.apply(null,t)}function Dt(t,e){var r=t[e]<<8|t[e+1];return r<0?65536+r:r}function It(t,e){var r=wt(t,e);return r<0?4294967296+r:r}function wt(t,e){return t[e]<<24|t[e+1]<<16|t[e+2]<<8|t[e+3]}function Ct(t,e,r){t[e]=r>>24,t[e+1]=r>>16&255,t[e+2]=r>>8&255,t[e+3]=255&r}function _t(t,e){var r=[];if(!e.length)return r;for(var i=t.byteLength,n=0;n<i;){var a=It(t,n),s=a>1?n+a:i;if(bt(t.subarray(n+4,n+8))===e[0])if(1===e.length)r.push(t.subarray(n+8,s));else{var o=_t(t.subarray(n+8,s),e.slice(1));o.length&&Rt.apply(r,o)}n=s}return r}function xt(t){var e=[],r=t[0],i=8,n=It(t,i);i+=4,i+=0===r?8:16,i+=2;var a=t.length+0,s=Dt(t,i);i+=2;for(var o=0;o<s;o++){var l=i,u=It(t,l);l+=4;var h=2147483647&u;if(1==(2147483648&u)>>>31)return w.warn("SIDX has hierarchical references (not supported)"),null;var d=It(t,l);l+=4,e.push({referenceSize:h,subsegmentDuration:d,info:{duration:d/n,start:a,end:a+h-1}}),a+=h,i=l+=4}return{earliestPresentationTime:0,timescale:n,version:r,referencesCount:s,references:e}}function Pt(t){for(var e=[],r=_t(t,["moov","trak"]),n=0;n<r.length;n++){var a=r[n],s=_t(a,["tkhd"])[0];if(s){var o=s[0],l=It(s,0===o?12:20),u=_t(a,["mdia","mdhd"])[0];if(u){var h=It(u,0===(o=u[0])?12:20),d=_t(a,["mdia","hdlr"])[0];if(d){var c=bt(d.subarray(8,12)),f={soun:O,vide:N}[c];if(f){var g=Ft(_t(a,["mdia","minf","stbl","stsd"])[0]);e[l]={timescale:h,type:f},e[f]=i({timescale:h,id:l},g)}}}}}return _t(t,["moov","mvex","trex"]).forEach((function(t){var r=It(t,4),i=e[r];i&&(i.default={duration:It(t,12),flags:It(t,20)})})),e}function Ft(t){var e=t.subarray(8),r=e.subarray(86),i=bt(e.subarray(4,8)),n=i,a="enca"===i||"encv"===i;if(a){var s=_t(e,[i])[0];_t(s.subarray("enca"===i?28:78),["sinf"]).forEach((function(t){var e=_t(t,["schm"])[0];if(e){var r=bt(e.subarray(4,8));if("cbcs"===r||"cenc"===r){var i=_t(t,["frma"])[0];i&&(n=bt(i))}}}))}switch(n){case"avc1":case"avc2":case"avc3":case"avc4":var o=_t(r,["avcC"])[0];n+="."+Ot(o[1])+Ot(o[2])+Ot(o[3]);break;case"mp4a":var l=_t(e,[i])[0],u=_t(l.subarray(28),["esds"])[0];if(u&&u.length>12){var h=4;if(3!==u[h++])break;h=Mt(u,h),h+=2;var d=u[h++];if(128&d&&(h+=2),64&d&&(h+=u[h++]),4!==u[h++])break;h=Mt(u,h);var c=u[h++];if(64!==c)break;if(n+="."+Ot(c),h+=12,5!==u[h++])break;h=Mt(u,h);var f=u[h++],g=(248&f)>>3;31===g&&(g+=1+((7&f)<<3)+((224&u[h])>>5)),n+="."+g}break;case"hvc1":case"hev1":var v=_t(r,["hvcC"])[0],m=v[1],p=["","A","B","C"][m>>6],y=31&m,E=It(v,2),T=(32&m)>>5?"H":"L",S=v[12],L=v.subarray(6,12);n+="."+p+y,n+="."+E.toString(16).toUpperCase(),n+="."+T+S;for(var A="",R=L.length;R--;){var k=L[R];(k||A)&&(A="."+k.toString(16).toUpperCase()+A)}n+=A;break;case"dvh1":case"dvhe":var b=_t(r,["dvcC"])[0],D=b[2]>>1&127,I=b[2]<<5&32|b[3]>>3&31;n+="."+Nt(D)+"."+Nt(I);break;case"vp09":var w=_t(r,["vpcC"])[0],C=w[4],_=w[5],x=w[6]>>4&15;n+="."+Nt(C)+"."+Nt(_)+"."+Nt(x);break;case"av01":var P=_t(r,["av1C"])[0],F=P[1]>>>5,M=31&P[1],O=P[2]>>>7?"H":"M",N=(64&P[2])>>6,U=(32&P[2])>>5,B=2===F&&N?U?12:10:N?10:8,G=(16&P[2])>>4,K=(8&P[2])>>3,H=(4&P[2])>>2,V=3&P[2];n+="."+F+"."+Nt(M)+O+"."+Nt(B)+"."+G+"."+K+H+V+"."+Nt(1)+"."+Nt(1)+"."+Nt(1)+".0"}return{codec:n,encrypted:a}}function Mt(t,e){for(var r=e+5;128&t[e++]&&e<r;);return e}function Ot(t){return("0"+t.toString(16).toUpperCase()).slice(-2)}function Nt(t){return(t<10?"0":"")+t}function Ut(t){var e=_t(t,["schm"])[0];if(e){var r=bt(e.subarray(4,8));if("cbcs"===r||"cenc"===r)return _t(t,["schi","tenc"])[0]}return w.error("[eme] missing 'schm' box"),null}function Bt(t){var e=It(t,0),r=8;1&e&&(r+=4),4&e&&(r+=4);for(var i=0,n=It(t,4),a=0;a<n;a++)256&e&&(i+=It(t,r),r+=4),512&e&&(r+=4),1024&e&&(r+=4),2048&e&&(r+=4);return i}function Gt(t,e){var r=new Uint8Array(t.length+e.length);return r.set(t),r.set(e,t.length),r}function Kt(t,e){var r=[],i=e.samples,n=e.timescale,a=e.id,s=!1;return _t(i,["moof"]).map((function(o){var l=o.byteOffset-8;_t(o,["traf"]).map((function(o){var u=_t(o,["tfdt"]).map((function(t){var e=t[0],r=It(t,4);return 1===e&&(r*=Math.pow(2,32),r+=It(t,8)),r/n}))[0];return void 0!==u&&(t=u),_t(o,["tfhd"]).map((function(u){var h=It(u,4),d=16777215&It(u,0),c=0,f=0!=(16&d),g=0,v=0!=(32&d),m=8;h===a&&(0!=(1&d)&&(m+=8),0!=(2&d)&&(m+=4),0!=(8&d)&&(c=It(u,m),m+=4),f&&(g=It(u,m),m+=4),v&&(m+=4),"video"===e.type&&(s=function(t){if(!t)return!1;var e=t.indexOf("."),r=e<0?t:t.substring(0,e);return"hvc1"===r||"hev1"===r||"dvh1"===r||"dvhe"===r}(e.codec)),_t(o,["trun"]).map((function(a){var o=a[0],u=16777215&It(a,0),h=0!=(1&u),d=0,f=0!=(4&u),v=0!=(256&u),m=0,p=0!=(512&u),y=0,E=0!=(1024&u),T=0!=(2048&u),S=0,L=It(a,4),A=8;h&&(d=It(a,A),A+=4),f&&(A+=4);for(var R=d+l,k=0;k<L;k++){if(v?(m=It(a,A),A+=4):m=c,p?(y=It(a,A),A+=4):y=g,E&&(A+=4),T&&(S=0===o?It(a,A):wt(a,A),A+=4),e.type===N)for(var b=0;b<y;){var D=It(i,R);Ht(s,i[R+=4])&&Vt(i.subarray(R,R+D),s?2:1,t+S/n,r),R+=D,b+=D+4}t+=m/n}})))}))}))})),r}function Ht(t,e){if(t){var r=e>>1&63;return 39===r||40===r}return 6==(31&e)}function Vt(t,e,r,i){var n=Yt(t),a=0;a+=e;for(var s=0,o=0,l=0;a<n.length;){s=0;do{if(a>=n.length)break;s+=l=n[a++]}while(255===l);o=0;do{if(a>=n.length)break;o+=l=n[a++]}while(255===l);var u=n.length-a,h=a;if(o<u)a+=o;else if(o>u){w.error("Malformed SEI payload. "+o+" is too small, only "+u+" bytes left to parse.");break}if(4===s){if(181===n[h++]){var d=Dt(n,h);if(h+=2,49===d){var c=It(n,h);if(h+=4,1195456820===c){var f=n[h++];if(3===f){var g=n[h++],v=64&g,m=v?2+3*(31&g):0,p=new Uint8Array(m);if(v){p[0]=g;for(var y=1;y<m;y++)p[y]=n[h++]}i.push({type:f,payloadType:s,pts:r,bytes:p})}}}}}else if(5===s&&o>16){for(var E=[],T=0;T<16;T++){var S=n[h++].toString(16);E.push(1==S.length?"0"+S:S),3!==T&&5!==T&&7!==T&&9!==T||E.push("-")}for(var L=o-16,A=new Uint8Array(L),R=0;R<L;R++)A[R]=n[h++];i.push({payloadType:s,pts:r,uuid:E.join(""),userData:Tt(A),userDataBytes:A})}}}function Yt(t){for(var e=t.byteLength,r=[],i=1;i<e-2;)0===t[i]&&0===t[i+1]&&3===t[i+2]?(r.push(i+2),i+=2):i++;if(0===r.length)return t;var n=e-r.length,a=new Uint8Array(n),s=0;for(i=0;i<n;s++,i++)s===r[0]&&(s++,r.shift()),a[i]=t[s];return a}function Wt(t,e,r){if(16!==t.byteLength)throw new RangeError("Invalid system id");var i,n,a;if(e){i=1,n=new Uint8Array(16*e.length);for(var s=0;s<e.length;s++){var o=e[s];if(16!==o.byteLength)throw new RangeError("Invalid key");n.set(o,16*s)}}else i=0,n=new Uint8Array;i>0?(a=new Uint8Array(4),e.length>0&&new DataView(a.buffer).setUint32(0,e.length,!1)):a=new Uint8Array;var l=new Uint8Array(4);return r&&r.byteLength>0&&new DataView(l.buffer).setUint32(0,r.byteLength,!1),function(t){for(var e=arguments.length,r=new Array(e>1?e-1:0),i=1;i<e;i++)r[i-1]=arguments[i];for(var n=r.length,a=8,s=n;s--;)a+=r[s].byteLength;var o=new Uint8Array(a);for(o[0]=a>>24&255,o[1]=a>>16&255,o[2]=a>>8&255,o[3]=255&a,o.set(t,4),s=0,a=8;s<n;s++)o.set(r[s],a),a+=r[s].byteLength;return o}([112,115,115,104],new Uint8Array([i,0,0,0]),t,a,n,l,r||new Uint8Array)}var jt={},qt=function(){function t(t,e,r,i,n){void 0===i&&(i=[1]),void 0===n&&(n=null),this.uri=void 0,this.method=void 0,this.keyFormat=void 0,this.keyFormatVersions=void 0,this.encrypted=void 0,this.isCommonEncryption=void 0,this.iv=null,this.key=null,this.keyId=null,this.pssh=null,this.method=t,this.uri=e,this.keyFormat=r,this.keyFormatVersions=i,this.iv=n,this.encrypted=!!t&&"NONE"!==t,this.isCommonEncryption=this.encrypted&&"AES-128"!==t}t.clearKeyUriToKeyIdMap=function(){jt={}};var e=t.prototype;return e.isSupported=function(){if(this.method){if("AES-128"===this.method||"NONE"===this.method)return!0;if("identity"===this.keyFormat)return"SAMPLE-AES"===this.method;switch(this.keyFormat){case z:case J:case Q:case X:return-1!==["ISO-23001-7","SAMPLE-AES","SAMPLE-AES-CENC","SAMPLE-AES-CTR"].indexOf(this.method)}}return!1},e.getDecryptData=function(e){if(!this.encrypted||!this.uri)return null;if("AES-128"===this.method&&this.uri&&!this.iv){"number"!=typeof e&&("AES-128"!==this.method||this.iv||w.warn('missing IV for initialization segment with method="'+this.method+'" - compliance issue'),e=0);var r=function(t){for(var e=new Uint8Array(16),r=12;r<16;r++)e[r]=t>>8*(15-r)&255;return e}(e);return new t(this.method,this.uri,"identity",this.keyFormatVersions,r)}var i=Y(this.uri);if(i)switch(this.keyFormat){case J:this.pssh=i,i.length>=22&&(this.keyId=i.subarray(i.length-22,i.length-6));break;case Q:var n=new Uint8Array([154,4,240,121,152,64,66,134,171,146,230,91,224,136,95,149]);this.pssh=Wt(n,null,i);var a=new Uint16Array(i.buffer,i.byteOffset,i.byteLength/2),s=String.fromCharCode.apply(null,Array.from(a)),o=s.substring(s.indexOf("<"),s.length),l=(new DOMParser).parseFromString(o,"text/xml").getElementsByTagName("KID")[0];if(l){var u=l.childNodes[0]?l.childNodes[0].nodeValue:l.getAttribute("VALUE");if(u){var h=V(u).subarray(0,16);!function(t){var e=function(t,e,r){var i=t[e];t[e]=t[r],t[r]=i};e(t,0,3),e(t,1,2),e(t,4,5),e(t,6,7)}(h),this.keyId=h}}break;default:var d=i.subarray(0,16);if(16!==d.length){var c=new Uint8Array(16);c.set(d,16-d.length),d=c}this.keyId=d}if(!this.keyId||16!==this.keyId.byteLength){var f=jt[this.uri];if(!f){var g=Object.keys(jt).length%Number.MAX_SAFE_INTEGER;f=new Uint8Array(16),new DataView(f.buffer,12,4).setUint32(0,g),jt[this.uri]=f}this.keyId=f}return this},t}(),Xt=/\{\$([a-zA-Z0-9-_]+)\}/g;function zt(t){return Xt.test(t)}function Qt(t,e,r){if(null!==t.variableList||t.hasVariableRefs)for(var i=r.length;i--;){var n=r[i],a=e[n];a&&(e[n]=Jt(t,a))}}function Jt(t,e){if(null!==t.variableList||t.hasVariableRefs){var r=t.variableList;return e.replace(Xt,(function(e){var i=e.substring(2,e.length-1),n=null==r?void 0:r[i];return void 0===n?(t.playlistParsingError||(t.playlistParsingError=new Error('Missing preceding EXT-X-DEFINE tag for Variable Reference: "'+i+'"')),e):n}))}return e}function $t(t,e,r){var i,n,a=t.variableList;if(a||(t.variableList=a={}),"QUERYPARAM"in e){i=e.QUERYPARAM;try{var s=new self.URL(r).searchParams;if(!s.has(i))throw new Error('"'+i+'" does not match any query parameter in URI: "'+r+'"');n=s.get(i)}catch(e){t.playlistParsingError||(t.playlistParsingError=new Error("EXT-X-DEFINE QUERYPARAM: "+e.message))}}else i=e.NAME,n=e.VALUE;i in a?t.playlistParsingError||(t.playlistParsingError=new Error('EXT-X-DEFINE duplicate Variable Name declarations: "'+i+'"')):a[i]=n||""}function Zt(t,e,r){var i=e.IMPORT;if(r&&i in r){var n=t.variableList;n||(t.variableList=n={}),n[i]=r[i]}else t.playlistParsingError||(t.playlistParsingError=new Error('EXT-X-DEFINE IMPORT attribute not found in Multivariant Playlist: "'+i+'"'))}function te(t){if(void 0===t&&(t=!0),"undefined"!=typeof self)return(t||!self.MediaSource)&&self.ManagedMediaSource||self.MediaSource||self.WebKitMediaSource}var ee={audio:{a3ds:1,"ac-3":.95,"ac-4":1,alac:.9,alaw:1,dra1:1,"dts+":1,"dts-":1,dtsc:1,dtse:1,dtsh:1,"ec-3":.9,enca:1,fLaC:.9,flac:.9,FLAC:.9,g719:1,g726:1,m4ae:1,mha1:1,mha2:1,mhm1:1,mhm2:1,mlpa:1,mp4a:1,"raw ":1,Opus:1,opus:1,samr:1,sawb:1,sawp:1,sevc:1,sqcp:1,ssmv:1,twos:1,ulaw:1},video:{avc1:1,avc2:1,avc3:1,avc4:1,avcp:1,av01:.8,drac:1,dva1:1,dvav:1,dvh1:.7,dvhe:.7,encv:1,hev1:.75,hvc1:.75,mjp2:1,mp4v:1,mvc1:1,mvc2:1,mvc3:1,mvc4:1,resv:1,rv60:1,s263:1,svc1:1,svc2:1,"vc-1":1,vp08:1,vp09:.9},text:{stpp:1,wvtt:1}};function re(t,e,r){return void 0===r&&(r=!0),!t.split(",").some((function(t){return!ie(t,e,r)}))}function ie(t,e,r){var i;void 0===r&&(r=!0);var n=te(r);return null!=(i=null==n?void 0:n.isTypeSupported(ne(t,e)))&&i}function ne(t,e){return e+'/mp4;codecs="'+t+'"'}function ae(t){if(t){var e=t.substring(0,4);return ee.video[e]}return 2}function se(t){return t.split(",").reduce((function(t,e){var r=ee.video[e];return r?(2*r+t)/(t?3:2):(ee.audio[e]+t)/(t?2:1)}),0)}var oe={},le=/flac|opus/i;function ue(t,e){return void 0===e&&(e=!0),t.replace(le,(function(t){return function(t,e){if(void 0===e&&(e=!0),oe[t])return oe[t];for(var r={flac:["flac","fLaC","FLAC"],opus:["opus","Opus"]}[t],i=0;i<r.length;i++)if(ie(r[i],"audio",e))return oe[t]=r[i],r[i];return t}(t.toLowerCase(),e)}))}function he(t,e){return t&&"mp4a"!==t?t:e}var de=/#EXT-X-STREAM-INF:([^\r\n]*)(?:[\r\n](?:#[^\r\n]*)?)*([^\r\n]+)|#EXT-X-(SESSION-DATA|SESSION-KEY|DEFINE|CONTENT-STEERING|START):([^\r\n]*)[\r\n]+/g,ce=/#EXT-X-MEDIA:(.*)/g,fe=/^#EXT(?:INF|-X-TARGETDURATION):/m,ge=new RegExp([/#EXTINF:\s*(\d*(?:\.\d+)?)(?:,(.*)\s+)?/.source,/(?!#) *(\S[\S ]*)/.source,/#EXT-X-BYTERANGE:*(.+)/.source,/#EXT-X-PROGRAM-DATE-TIME:(.+)/.source,/#.*/.source].join("|"),"g"),ve=new RegExp([/#(EXTM3U)/.source,/#EXT-X-(DATERANGE|DEFINE|KEY|MAP|PART|PART-INF|PLAYLIST-TYPE|PRELOAD-HINT|RENDITION-REPORT|SERVER-CONTROL|SKIP|START):(.+)/.source,/#EXT-X-(BITRATE|DISCONTINUITY-SEQUENCE|MEDIA-SEQUENCE|TARGETDURATION|VERSION): *(\d+)/.source,/#EXT-X-(DISCONTINUITY|ENDLIST|GAP|INDEPENDENT-SEGMENTS)/.source,/(#)([^:]*):(.*)/.source,/(#)(.*)(?:.*)\r?\n?/.source].join("|")),me=function(){function t(){}return t.findGroup=function(t,e){for(var r=0;r<t.length;r++){var i=t[r];if(i.id===e)return i}},t.resolve=function(t,e){return p.buildAbsoluteURL(e,t,{alwaysNormalize:!0})},t.isMediaPlaylist=function(t){return fe.test(t)},t.parseMasterPlaylist=function(e,r){var i,n={contentSteering:null,levels:[],playlistParsingError:null,sessionData:null,sessionKeys:null,startTimeOffset:null,variableList:null,hasVariableRefs:zt(e)},a=[];for(de.lastIndex=0;null!=(i=de.exec(e));)if(i[1]){var s,o=new x(i[1]);Qt(n,o,["CODECS","SUPPLEMENTAL-CODECS","ALLOWED-CPC","PATHWAY-ID","STABLE-VARIANT-ID","AUDIO","VIDEO","SUBTITLES","CLOSED-CAPTIONS","NAME"]);var l=Jt(n,i[2]),u={attrs:o,bitrate:o.decimalInteger("BANDWIDTH")||o.decimalInteger("AVERAGE-BANDWIDTH"),name:o.NAME,url:t.resolve(l,r)},h=o.decimalResolution("RESOLUTION");h&&(u.width=h.width,u.height=h.height),Ee(o.CODECS,u),null!=(s=u.unknownCodecs)&&s.length||a.push(u),n.levels.push(u)}else if(i[3]){var d=i[3],c=i[4];switch(d){case"SESSION-DATA":var f=new x(c);Qt(n,f,["DATA-ID","LANGUAGE","VALUE","URI"]);var g=f["DATA-ID"];g&&(null===n.sessionData&&(n.sessionData={}),n.sessionData[g]=f);break;case"SESSION-KEY":var v=pe(c,r,n);v.encrypted&&v.isSupported()?(null===n.sessionKeys&&(n.sessionKeys=[]),n.sessionKeys.push(v)):w.warn('[Keys] Ignoring invalid EXT-X-SESSION-KEY tag: "'+c+'"');break;case"DEFINE":var m=new x(c);Qt(n,m,["NAME","VALUE","QUERYPARAM"]),$t(n,m,r);break;case"CONTENT-STEERING":var p=new x(c);Qt(n,p,["SERVER-URI","PATHWAY-ID"]),n.contentSteering={uri:t.resolve(p["SERVER-URI"],r),pathwayId:p["PATHWAY-ID"]||"."};break;case"START":n.startTimeOffset=ye(c)}}var y=a.length>0&&a.length<n.levels.length;return n.levels=y?a:n.levels,0===n.levels.length&&(n.playlistParsingError=new Error("no levels found in manifest")),n},t.parseMasterPlaylistMedia=function(e,r,i){var n,a={},s=i.levels,o={AUDIO:s.map((function(t){return{id:t.attrs.AUDIO,audioCodec:t.audioCodec}})),SUBTITLES:s.map((function(t){return{id:t.attrs.SUBTITLES,textCodec:t.textCodec}})),"CLOSED-CAPTIONS":[]},l=0;for(ce.lastIndex=0;null!==(n=ce.exec(e));){var u=new x(n[1]),h=u.TYPE;if(h){var d=o[h],c=a[h]||[];a[h]=c,Qt(i,u,["URI","GROUP-ID","LANGUAGE","ASSOC-LANGUAGE","STABLE-RENDITION-ID","NAME","INSTREAM-ID","CHARACTERISTICS","CHANNELS"]);var f=u.LANGUAGE,g=u["ASSOC-LANGUAGE"],v=u.CHANNELS,m=u.CHARACTERISTICS,p=u["INSTREAM-ID"],y={attrs:u,bitrate:0,id:l++,groupId:u["GROUP-ID"]||"",name:u.NAME||f||"",type:h,default:u.bool("DEFAULT"),autoselect:u.bool("AUTOSELECT"),forced:u.bool("FORCED"),lang:f,url:u.URI?t.resolve(u.URI,r):""};if(g&&(y.assocLang=g),v&&(y.channels=v),m&&(y.characteristics=m),p&&(y.instreamId=p),null!=d&&d.length){var E=t.findGroup(d,y.groupId)||d[0];Te(y,E,"audioCodec"),Te(y,E,"textCodec")}c.push(y)}}return a},t.parseLevelPlaylist=function(t,e,r,i,n,a){var s,l,u,h=new H(e),d=h.fragments,c=null,f=0,g=0,v=0,m=0,p=null,E=new G(i,e),T=-1,S=!1,L=null;for(ge.lastIndex=0,h.m3u8=t,h.hasVariableRefs=zt(t);null!==(s=ge.exec(t));){S&&(S=!1,(E=new G(i,e)).start=v,E.sn=f,E.cc=m,E.level=r,c&&(E.initSegment=c,E.rawProgramDateTime=c.rawProgramDateTime,c.rawProgramDateTime=null,L&&(E.setByteRange(L),L=null)));var A=s[1];if(A){E.duration=parseFloat(A);var R=(" "+s[2]).slice(1);E.title=R||null,E.tagList.push(R?["INF",A,R]:["INF",A])}else if(s[3]){if(y(E.duration)){E.start=v,u&&Ae(E,u,h),E.sn=f,E.level=r,E.cc=m,d.push(E);var k=(" "+s[3]).slice(1);E.relurl=Jt(h,k),Se(E,p),p=E,v+=E.duration,f++,g=0,S=!0}}else if(s[4]){var b=(" "+s[4]).slice(1);p?E.setByteRange(b,p):E.setByteRange(b)}else if(s[5])E.rawProgramDateTime=(" "+s[5]).slice(1),E.tagList.push(["PROGRAM-DATE-TIME",E.rawProgramDateTime]),-1===T&&(T=d.length);else{if(!(s=s[0].match(ve))){w.warn("No matches on slow regex match for level playlist!");continue}for(l=1;l<s.length&&void 0===s[l];l++);var D=(" "+s[l]).slice(1),I=(" "+s[l+1]).slice(1),C=s[l+2]?(" "+s[l+2]).slice(1):"";switch(D){case"PLAYLIST-TYPE":h.type=I.toUpperCase();break;case"MEDIA-SEQUENCE":f=h.startSN=parseInt(I);break;case"SKIP":var _=new x(I);Qt(h,_,["RECENTLY-REMOVED-DATERANGES"]);var P=_.decimalInteger("SKIPPED-SEGMENTS");if(y(P)){h.skippedSegments=P;for(var M=P;M--;)d.unshift(null);f+=P}var O=_.enumeratedString("RECENTLY-REMOVED-DATERANGES");O&&(h.recentlyRemovedDateranges=O.split("\t"));break;case"TARGETDURATION":h.targetduration=Math.max(parseInt(I),1);break;case"VERSION":h.version=parseInt(I);break;case"INDEPENDENT-SEGMENTS":case"EXTM3U":break;case"ENDLIST":h.live=!1;break;case"#":(I||C)&&E.tagList.push(C?[I,C]:[I]);break;case"DISCONTINUITY":m++,E.tagList.push(["DIS"]);break;case"GAP":E.gap=!0,E.tagList.push([D]);break;case"BITRATE":E.tagList.push([D,I]);break;case"DATERANGE":var N=new x(I);Qt(h,N,["ID","CLASS","START-DATE","END-DATE","SCTE35-CMD","SCTE35-OUT","SCTE35-IN"]),Qt(h,N,N.clientAttrs);var U=new F(N,h.dateRanges[N.ID]);U.isValid||h.skippedSegments?h.dateRanges[U.id]=U:w.warn('Ignoring invalid DATERANGE tag: "'+I+'"'),E.tagList.push(["EXT-X-DATERANGE",I]);break;case"DEFINE":var B=new x(I);Qt(h,B,["NAME","VALUE","IMPORT","QUERYPARAM"]),"IMPORT"in B?Zt(h,B,a):$t(h,B,e);break;case"DISCONTINUITY-SEQUENCE":m=parseInt(I);break;case"KEY":var V=pe(I,e,h);if(V.isSupported()){if("NONE"===V.method){u=void 0;break}u||(u={}),u[V.keyFormat]&&(u=o({},u)),u[V.keyFormat]=V}else w.warn('[Keys] Ignoring invalid EXT-X-KEY tag: "'+I+'"');break;case"START":h.startTimeOffset=ye(I);break;case"MAP":var Y=new x(I);if(Qt(h,Y,["BYTERANGE","URI"]),E.duration){var W=new G(i,e);Le(W,Y,r,u),c=W,E.initSegment=c,c.rawProgramDateTime&&!E.rawProgramDateTime&&(E.rawProgramDateTime=c.rawProgramDateTime)}else{var j=E.byteRangeEndOffset;if(j){var q=E.byteRangeStartOffset;L=j-q+"@"+q}else L=null;Le(E,Y,r,u),c=E,S=!0}break;case"SERVER-CONTROL":var X=new x(I);h.canBlockReload=X.bool("CAN-BLOCK-RELOAD"),h.canSkipUntil=X.optionalFloat("CAN-SKIP-UNTIL",0),h.canSkipDateRanges=h.canSkipUntil>0&&X.bool("CAN-SKIP-DATERANGES"),h.partHoldBack=X.optionalFloat("PART-HOLD-BACK",0),h.holdBack=X.optionalFloat("HOLD-BACK",0);break;case"PART-INF":var z=new x(I);h.partTarget=z.decimalFloatingPoint("PART-TARGET");break;case"PART":var Q=h.partList;Q||(Q=h.partList=[]);var J=g>0?Q[Q.length-1]:void 0,$=g++,Z=new x(I);Qt(h,Z,["BYTERANGE","URI"]);var tt=new K(Z,E,e,$,J);Q.push(tt),E.duration+=tt.duration;break;case"PRELOAD-HINT":var et=new x(I);Qt(h,et,["URI"]),h.preloadHint=et;break;case"RENDITION-REPORT":var rt=new x(I);Qt(h,rt,["URI"]),h.renditionReports=h.renditionReports||[],h.renditionReports.push(rt);break;default:w.warn("line parsed but not handled: "+s)}}}p&&!p.relurl?(d.pop(),v-=p.duration,h.partList&&(h.fragmentHint=p)):h.partList&&(Se(E,p),E.cc=m,h.fragmentHint=E,u&&Ae(E,u,h));var it=d.length,nt=d[0],at=d[it-1];if((v+=h.skippedSegments*h.targetduration)>0&&it&&at){h.averagetargetduration=v/it;var st=at.sn;h.endSN="initSegment"!==st?st:0,h.live||(at.endList=!0),nt&&(h.startCC=nt.cc)}else h.endSN=0,h.startCC=0;return h.fragmentHint&&(v+=h.fragmentHint.duration),h.totalduration=v,h.endCC=m,T>0&&function(t,e){for(var r=t[e],i=e;i--;){var n=t[i];if(!n)return;n.programDateTime=r.programDateTime-1e3*n.duration,r=n}}(d,T),h},t}();function pe(t,e,r){var i,n,a=new x(t);Qt(r,a,["KEYFORMAT","KEYFORMATVERSIONS","URI","IV","URI"]);var s=null!=(i=a.METHOD)?i:"",o=a.URI,l=a.hexadecimalInteger("IV"),u=a.KEYFORMATVERSIONS,h=null!=(n=a.KEYFORMAT)?n:"identity";o&&a.IV&&!l&&w.error("Invalid IV: "+a.IV);var d=o?me.resolve(o,e):"",c=(u||"1").split("/").map(Number).filter(Number.isFinite);return new qt(s,d,h,c,l)}function ye(t){var e=new x(t).decimalFloatingPoint("TIME-OFFSET");return y(e)?e:null}function Ee(t,e){var r=(t||"").split(/[ ,]+/).filter((function(t){return t}));["video","audio","text"].forEach((function(t){var i=r.filter((function(e){return function(t,e){var r=ee[e];return!!r&&!!r[t.slice(0,4)]}(e,t)}));i.length&&(e[t+"Codec"]=i.join(","),r=r.filter((function(t){return-1===i.indexOf(t)})))})),e.unknownCodecs=r}function Te(t,e,r){var i=e[r];i&&(t[r]=i)}function Se(t,e){t.rawProgramDateTime?t.programDateTime=Date.parse(t.rawProgramDateTime):null!=e&&e.programDateTime&&(t.programDateTime=e.endProgramDateTime),y(t.programDateTime)||(t.programDateTime=null,t.rawProgramDateTime=null)}function Le(t,e,r,i){t.relurl=e.URI,e.BYTERANGE&&t.setByteRange(e.BYTERANGE),t.level=r,t.sn="initSegment",i&&(t.levelkeys=i),t.initSegment=null}function Ae(t,e,r){t.levelkeys=e;var i=r.encryptedFragments;i.length&&i[i.length-1].levelkeys===e||!Object.keys(e).some((function(t){return e[t].isCommonEncryption}))||i.push(t)}var Re="manifest",ke="level",be="audioTrack",De="subtitleTrack",Ie="main",we="audio",Ce="subtitle";function _e(t){switch(t.type){case be:return we;case De:return Ce;default:return Ie}}function xe(t,e){var r=t.url;return void 0!==r&&0!==r.indexOf("data:")||(r=e.url),r}var Pe=function(){function t(t){this.hls=void 0,this.loaders=Object.create(null),this.variableList=null,this.hls=t,this.registerListeners()}var e=t.prototype;return e.startLoad=function(t){},e.stopLoad=function(){this.destroyInternalLoaders()},e.registerListeners=function(){var t=this.hls;t.on(S.MANIFEST_LOADING,this.onManifestLoading,this),t.on(S.LEVEL_LOADING,this.onLevelLoading,this),t.on(S.AUDIO_TRACK_LOADING,this.onAudioTrackLoading,this),t.on(S.SUBTITLE_TRACK_LOADING,this.onSubtitleTrackLoading,this)},e.unregisterListeners=function(){var t=this.hls;t.off(S.MANIFEST_LOADING,this.onManifestLoading,this),t.off(S.LEVEL_LOADING,this.onLevelLoading,this),t.off(S.AUDIO_TRACK_LOADING,this.onAudioTrackLoading,this),t.off(S.SUBTITLE_TRACK_LOADING,this.onSubtitleTrackLoading,this)},e.createInternalLoader=function(t){var e=this.hls.config,r=e.pLoader,i=e.loader,n=new(r||i)(e);return this.loaders[t.type]=n,n},e.getInternalLoader=function(t){return this.loaders[t.type]},e.resetInternalLoader=function(t){this.loaders[t]&&delete this.loaders[t]},e.destroyInternalLoaders=function(){for(var t in this.loaders){var e=this.loaders[t];e&&e.destroy(),this.resetInternalLoader(t)}},e.destroy=function(){this.variableList=null,this.unregisterListeners(),this.destroyInternalLoaders()},e.onManifestLoading=function(t,e){var r=e.url;this.variableList=null,this.load({id:null,level:0,responseType:"text",type:Re,url:r,deliveryDirectives:null})},e.onLevelLoading=function(t,e){var r=e.id,i=e.level,n=e.pathwayId,a=e.url,s=e.deliveryDirectives;this.load({id:r,level:i,pathwayId:n,responseType:"text",type:ke,url:a,deliveryDirectives:s})},e.onAudioTrackLoading=function(t,e){var r=e.id,i=e.groupId,n=e.url,a=e.deliveryDirectives;this.load({id:r,groupId:i,level:null,responseType:"text",type:be,url:n,deliveryDirectives:a})},e.onSubtitleTrackLoading=function(t,e){var r=e.id,i=e.groupId,n=e.url,a=e.deliveryDirectives;this.load({id:r,groupId:i,level:null,responseType:"text",type:De,url:n,deliveryDirectives:a})},e.load=function(t){var e,r,i,n=this,a=this.hls.config,s=this.getInternalLoader(t);if(s){var l=s.context;if(l&&l.url===t.url&&l.level===t.level)return void w.trace("[playlist-loader]: playlist request ongoing");w.log("[playlist-loader]: aborting previous loader for type: "+t.type),s.abort()}if(r=t.type===Re?a.manifestLoadPolicy.default:o({},a.playlistLoadPolicy.default,{timeoutRetry:null,errorRetry:null}),s=this.createInternalLoader(t),y(null==(e=t.deliveryDirectives)?void 0:e.part)&&(t.type===ke&&null!==t.level?i=this.hls.levels[t.level].details:t.type===be&&null!==t.id?i=this.hls.audioTracks[t.id].details:t.type===De&&null!==t.id&&(i=this.hls.subtitleTracks[t.id].details),i)){var u=i.partTarget,h=i.targetduration;if(u&&h){var d=1e3*Math.max(3*u,.8*h);r=o({},r,{maxTimeToFirstByteMs:Math.min(d,r.maxTimeToFirstByteMs),maxLoadTimeMs:Math.min(d,r.maxTimeToFirstByteMs)})}}var c=r.errorRetry||r.timeoutRetry||{},f={loadPolicy:r,timeout:r.maxLoadTimeMs,maxRetry:c.maxNumRetry||0,retryDelay:c.retryDelayMs||0,maxRetryDelay:c.maxRetryDelayMs||0},g={onSuccess:function(t,e,r,i){var a=n.getInternalLoader(r);n.resetInternalLoader(r.type);var s=t.data;0===s.indexOf("#EXTM3U")?(e.parsing.start=performance.now(),me.isMediaPlaylist(s)?n.handleTrackOrLevelPlaylist(t,e,r,i||null,a):n.handleMasterPlaylist(t,e,r,i)):n.handleManifestParsingError(t,r,new Error("no EXTM3U delimiter"),i||null,e)},onError:function(t,e,r,i){n.handleNetworkError(e,r,!1,t,i)},onTimeout:function(t,e,r){n.handleNetworkError(e,r,!0,void 0,t)}};s.load(t,f,g)},e.handleMasterPlaylist=function(t,e,r,i){var n=this.hls,a=t.data,s=xe(t,r),o=me.parseMasterPlaylist(a,s);if(o.playlistParsingError)this.handleManifestParsingError(t,r,o.playlistParsingError,i,e);else{var l=o.contentSteering,u=o.levels,h=o.sessionData,d=o.sessionKeys,c=o.startTimeOffset,f=o.variableList;this.variableList=f;var g=me.parseMasterPlaylistMedia(a,s,o),v=g.AUDIO,m=void 0===v?[]:v,p=g.SUBTITLES,y=g["CLOSED-CAPTIONS"];m.length&&(m.some((function(t){return!t.url}))||!u[0].audioCodec||u[0].attrs.AUDIO||(w.log("[playlist-loader]: audio codec signaled in quality level, but no embedded audio track signaled, create one"),m.unshift({type:"main",name:"main",groupId:"main",default:!1,autoselect:!1,forced:!1,id:-1,attrs:new x({}),bitrate:0,url:""}))),n.trigger(S.MANIFEST_LOADED,{levels:u,audioTracks:m,subtitles:p,captions:y,contentSteering:l,url:s,stats:e,networkDetails:i,sessionData:h,sessionKeys:d,startTimeOffset:c,variableList:f})}},e.handleTrackOrLevelPlaylist=function(t,e,r,i,n){var a=this.hls,s=r.id,o=r.level,l=r.type,u=xe(t,r),h=y(o)?o:y(s)?s:0,d=_e(r),c=me.parseLevelPlaylist(t.data,u,h,d,0,this.variableList);if(l===Re){var f={attrs:new x({}),bitrate:0,details:c,name:"",url:u};a.trigger(S.MANIFEST_LOADED,{levels:[f],audioTracks:[],url:u,stats:e,networkDetails:i,sessionData:null,sessionKeys:null,contentSteering:null,startTimeOffset:null,variableList:null})}e.parsing.end=performance.now(),r.levelDetails=c,this.handlePlaylistLoaded(c,t,e,r,i,n)},e.handleManifestParsingError=function(t,e,r,i,n){this.hls.trigger(S.ERROR,{type:L.NETWORK_ERROR,details:A.MANIFEST_PARSING_ERROR,fatal:e.type===Re,url:t.url,err:r,error:r,reason:r.message,response:t,context:e,networkDetails:i,stats:n})},e.handleNetworkError=function(t,e,r,n,a){void 0===r&&(r=!1);var s="A network "+(r?"timeout":"error"+(n?" (status "+n.code+")":""))+" occurred while loading "+t.type;t.type===ke?s+=": "+t.level+" id: "+t.id:t.type!==be&&t.type!==De||(s+=" id: "+t.id+' group-id: "'+t.groupId+'"');var o=new Error(s);w.warn("[playlist-loader]: "+s);var l=A.UNKNOWN,u=!1,h=this.getInternalLoader(t);switch(t.type){case Re:l=r?A.MANIFEST_LOAD_TIMEOUT:A.MANIFEST_LOAD_ERROR,u=!0;break;case ke:l=r?A.LEVEL_LOAD_TIMEOUT:A.LEVEL_LOAD_ERROR,u=!1;break;case be:l=r?A.AUDIO_TRACK_LOAD_TIMEOUT:A.AUDIO_TRACK_LOAD_ERROR,u=!1;break;case De:l=r?A.SUBTITLE_TRACK_LOAD_TIMEOUT:A.SUBTITLE_LOAD_ERROR,u=!1}h&&this.resetInternalLoader(t.type);var d={type:L.NETWORK_ERROR,details:l,fatal:u,url:t.url,loader:h,context:t,error:o,networkDetails:e,stats:a};if(n){var c=(null==e?void 0:e.url)||t.url;d.response=i({url:c,data:void 0},n)}this.hls.trigger(S.ERROR,d)},e.handlePlaylistLoaded=function(t,e,r,i,n,a){var s=this.hls,o=i.type,l=i.level,u=i.id,h=i.groupId,d=i.deliveryDirectives,c=xe(e,i),f=_e(i),g="number"==typeof i.level&&f===Ie?l:void 0;if(t.fragments.length){t.targetduration||(t.playlistParsingError=new Error("Missing Target Duration"));var v=t.playlistParsingError;if(v)s.trigger(S.ERROR,{type:L.NETWORK_ERROR,details:A.LEVEL_PARSING_ERROR,fatal:!1,url:c,error:v,reason:v.message,response:e,context:i,level:g,parent:f,networkDetails:n,stats:r});else switch(t.live&&a&&(a.getCacheAge&&(t.ageHeader=a.getCacheAge()||0),a.getCacheAge&&!isNaN(t.ageHeader)||(t.ageHeader=0)),o){case Re:case ke:s.trigger(S.LEVEL_LOADED,{details:t,level:g||0,id:u||0,stats:r,networkDetails:n,deliveryDirectives:d});break;case be:s.trigger(S.AUDIO_TRACK_LOADED,{details:t,id:u||0,groupId:h||"",stats:r,networkDetails:n,deliveryDirectives:d});break;case De:s.trigger(S.SUBTITLE_TRACK_LOADED,{details:t,id:u||0,groupId:h||"",stats:r,networkDetails:n,deliveryDirectives:d})}}else{var m=new Error("No Segments found in Playlist");s.trigger(S.ERROR,{type:L.NETWORK_ERROR,details:A.LEVEL_EMPTY_ERROR,fatal:!1,url:c,error:m,reason:m.message,response:e,context:i,level:g,parent:f,networkDetails:n,stats:r})}},t}();function Fe(t,e){var r;try{r=new Event("addtrack")}catch(t){(r=document.createEvent("Event")).initEvent("addtrack",!1,!1)}r.track=t,e.dispatchEvent(r)}function Me(t,e){var r=t.mode;if("disabled"===r&&(t.mode="hidden"),t.cues&&!t.cues.getCueById(e.id))try{if(t.addCue(e),!t.cues.getCueById(e.id))throw new Error("addCue is failed for: "+e)}catch(r){w.debug("[texttrack-utils]: "+r);try{var i=new self.TextTrackCue(e.startTime,e.endTime,e.text);i.id=e.id,t.addCue(i)}catch(t){w.debug("[texttrack-utils]: Legacy TextTrackCue fallback failed: "+t)}}"disabled"===r&&(t.mode=r)}function Oe(t){var e=t.mode;if("disabled"===e&&(t.mode="hidden"),t.cues)for(var r=t.cues.length;r--;)t.removeCue(t.cues[r]);"disabled"===e&&(t.mode=e)}function Ne(t,e,r,i){var n=t.mode;if("disabled"===n&&(t.mode="hidden"),t.cues&&t.cues.length>0)for(var a=function(t,e,r){var i=[],n=function(t,e){if(e<t[0].startTime)return 0;var r=t.length-1;if(e>t[r].endTime)return-1;for(var i=0,n=r;i<=n;){var a=Math.floor((n+i)/2);if(e<t[a].startTime)n=a-1;else{if(!(e>t[a].startTime&&i<r))return a;i=a+1}}return t[i].startTime-e<e-t[n].startTime?i:n}(t,e);if(n>-1)for(var a=n,s=t.length;a<s;a++){var o=t[a];if(o.startTime>=e&&o.endTime<=r)i.push(o);else if(o.startTime>r)return i}return i}(t.cues,e,r),s=0;s<a.length;s++)i&&!i(a[s])||t.removeCue(a[s]);"disabled"===n&&(t.mode=n)}function Ue(t){for(var e=[],r=0;r<t.length;r++){var i=t[r];"subtitles"!==i.kind&&"captions"!==i.kind||!i.label||e.push(t[r])}return e}var Be="org.id3",Ge="com.apple.quicktime.HLS",Ke="https://aomedia.org/emsg/ID3";function He(){if("undefined"!=typeof self)return self.VTTCue||self.TextTrackCue}function Ve(t,e,r,n,a){var s=new t(e,r,"");try{s.value=n,a&&(s.type=a)}catch(o){s=new t(e,r,JSON.stringify(a?i({type:a},n):n))}return s}var Ye=function(){var t=He();try{t&&new t(0,Number.POSITIVE_INFINITY,"")}catch(t){return Number.MAX_VALUE}return Number.POSITIVE_INFINITY}();function We(t,e){return t.getTime()/1e3-e}var je=function(){function t(t){this.hls=void 0,this.id3Track=null,this.media=null,this.dateRangeCuesAppended={},this.hls=t,this._registerListeners()}var e=t.prototype;return e.destroy=function(){this._unregisterListeners(),this.id3Track=null,this.media=null,this.dateRangeCuesAppended={},this.hls=null},e._registerListeners=function(){var t=this.hls;t.on(S.MEDIA_ATTACHED,this.onMediaAttached,this),t.on(S.MEDIA_DETACHING,this.onMediaDetaching,this),t.on(S.MANIFEST_LOADING,this.onManifestLoading,this),t.on(S.FRAG_PARSING_METADATA,this.onFragParsingMetadata,this),t.on(S.BUFFER_FLUSHING,this.onBufferFlushing,this),t.on(S.LEVEL_UPDATED,this.onLevelUpdated,this)},e._unregisterListeners=function(){var t=this.hls;t.off(S.MEDIA_ATTACHED,this.onMediaAttached,this),t.off(S.MEDIA_DETACHING,this.onMediaDetaching,this),t.off(S.MANIFEST_LOADING,this.onManifestLoading,this),t.off(S.FRAG_PARSING_METADATA,this.onFragParsingMetadata,this),t.off(S.BUFFER_FLUSHING,this.onBufferFlushing,this),t.off(S.LEVEL_UPDATED,this.onLevelUpdated,this)},e.onMediaAttached=function(t,e){this.media=e.media},e.onMediaDetaching=function(){this.id3Track&&(Oe(this.id3Track),this.id3Track=null,this.media=null,this.dateRangeCuesAppended={})},e.onManifestLoading=function(){this.dateRangeCuesAppended={}},e.createTrack=function(t){var e=this.getID3Track(t.textTracks);return e.mode="hidden",e},e.getID3Track=function(t){if(this.media){for(var e=0;e<t.length;e++){var r=t[e];if("metadata"===r.kind&&"id3"===r.label)return Fe(r,this.media),r}return this.media.addTextTrack("metadata","id3")}},e.onFragParsingMetadata=function(t,e){if(this.media){var r=this.hls.config,i=r.enableEmsgMetadataCues,n=r.enableID3MetadataCues;if(i||n){var a=e.samples;this.id3Track||(this.id3Track=this.createTrack(this.media));var s=He();if(s)for(var o=0;o<a.length;o++){var l=a[o].type;if((l!==Ke||i)&&n){var u=gt(a[o].data);if(u){var h=a[o].pts,d=h+a[o].duration;d>Ye&&(d=Ye),d-h<=0&&(d=h+.25);for(var c=0;c<u.length;c++){var f=u[c];if(!ct(f)){this.updateId3CueEnds(h,l);var g=Ve(s,h,d,f,l);g&&this.id3Track.addCue(g)}}}}}}}},e.updateId3CueEnds=function(t,e){var r,i=null==(r=this.id3Track)?void 0:r.cues;if(i)for(var n=i.length;n--;){var a=i[n];a.type===e&&a.startTime<t&&a.endTime===Ye&&(a.endTime=t)}},e.onBufferFlushing=function(t,e){var r=e.startOffset,i=e.endOffset,n=e.type,a=this.id3Track,s=this.hls;if(s){var o=s.config,l=o.enableEmsgMetadataCues,u=o.enableID3MetadataCues;a&&(l||u)&&Ne(a,r,i,"audio"===n?function(t){return t.type===Be&&u}:"video"===n?function(t){return t.type===Ke&&l}:function(t){return t.type===Be&&u||t.type===Ke&&l})}},e.onLevelUpdated=function(t,e){var r=this,i=e.details;if(this.media&&i.hasProgramDateTime&&this.hls.config.enableDateRangeMetadataCues){var n=this.dateRangeCuesAppended,a=this.id3Track,s=i.dateRanges,o=Object.keys(s);if(a)for(var l=Object.keys(n).filter((function(t){return!o.includes(t)})),u=function(){var t=l[h];Object.keys(n[t].cues).forEach((function(e){a.removeCue(n[t].cues[e])})),delete n[t]},h=l.length;h--;)u();var d=i.fragments[i.fragments.length-1];if(0!==o.length&&y(null==d?void 0:d.programDateTime)){this.id3Track||(this.id3Track=this.createTrack(this.media));for(var c=d.programDateTime/1e3-d.start,f=He(),g=function(){var t=o[v],e=s[t],i=We(e.startDate,c),a=n[t],l=(null==a?void 0:a.cues)||{},u=(null==a?void 0:a.durationKnown)||!1,h=Ye,d=e.endDate;if(d)h=We(d,c),u=!0;else if(e.endOnNext&&!u){var g=o.reduce((function(t,r){if(r!==e.id){var i=s[r];if(i.class===e.class&&i.startDate>e.startDate&&(!t||e.startDate<t.startDate))return i}return t}),null);g&&(h=We(g.startDate,c),u=!0)}for(var m,p,y=Object.keys(e.attr),E=0;E<y.length;E++){var T=y[E];if("ID"!==(p=T)&&"CLASS"!==p&&"START-DATE"!==p&&"DURATION"!==p&&"END-DATE"!==p&&"END-ON-NEXT"!==p){var S=l[T];if(S)u&&!a.durationKnown&&(S.endTime=h);else if(f){var L=e.attr[T];P(T)&&(m=L,L=Uint8Array.from(m.replace(/^0x/,"").replace(/([\da-fA-F]{2}) ?/g,"0x$1 ").replace(/ +$/,"").split(" ")).buffer);var A=Ve(f,i,h,{key:T,data:L},Ge);A&&(A.id=t,r.id3Track.addCue(A),l[T]=A)}}}n[t]={cues:l,dateRange:e,durationKnown:u}},v=0;v<o.length;v++)g()}}},t}(),qe=function(){function t(t){var e=this;this.hls=void 0,this.config=void 0,this.media=null,this.levelDetails=null,this.currentTime=0,this.stallCount=0,this._latency=null,this.timeupdateHandler=function(){return e.timeupdate()},this.hls=t,this.config=t.config,this.registerListeners()}var e=t.prototype;return e.destroy=function(){this.unregisterListeners(),this.onMediaDetaching(),this.levelDetails=null,this.hls=this.timeupdateHandler=null},e.registerListeners=function(){this.hls.on(S.MEDIA_ATTACHED,this.onMediaAttached,this),this.hls.on(S.MEDIA_DETACHING,this.onMediaDetaching,this),this.hls.on(S.MANIFEST_LOADING,this.onManifestLoading,this),this.hls.on(S.LEVEL_UPDATED,this.onLevelUpdated,this),this.hls.on(S.ERROR,this.onError,this)},e.unregisterListeners=function(){this.hls.off(S.MEDIA_ATTACHED,this.onMediaAttached,this),this.hls.off(S.MEDIA_DETACHING,this.onMediaDetaching,this),this.hls.off(S.MANIFEST_LOADING,this.onManifestLoading,this),this.hls.off(S.LEVEL_UPDATED,this.onLevelUpdated,this),this.hls.off(S.ERROR,this.onError,this)},e.onMediaAttached=function(t,e){this.media=e.media,this.media.addEventListener("timeupdate",this.timeupdateHandler)},e.onMediaDetaching=function(){this.media&&(this.media.removeEventListener("timeupdate",this.timeupdateHandler),this.media=null)},e.onManifestLoading=function(){this.levelDetails=null,this._latency=null,this.stallCount=0},e.onLevelUpdated=function(t,e){var r=e.details;this.levelDetails=r,r.advanced&&this.timeupdate(),!r.live&&this.media&&this.media.removeEventListener("timeupdate",this.timeupdateHandler)},e.onError=function(t,e){var r;e.details===A.BUFFER_STALLED_ERROR&&(this.stallCount++,null!=(r=this.levelDetails)&&r.live&&w.warn("[playback-rate-controller]: Stall detected, adjusting target latency"))},e.timeupdate=function(){var t=this.media,e=this.levelDetails;if(t&&e){this.currentTime=t.currentTime;var r=this.computeLatency();if(null!==r){this._latency=r;var i=this.config,n=i.lowLatencyMode,a=i.maxLiveSyncPlaybackRate;if(n&&1!==a&&e.live){var s=this.targetLatency;if(null!==s){var o=r-s;if(o<Math.min(this.maxLatency,s+e.targetduration)&&o>.05&&this.forwardBufferLength>1){var l=Math.min(2,Math.max(1,a)),u=Math.round(2/(1+Math.exp(-.75*o-this.edgeStalled))*20)/20;t.playbackRate=Math.min(l,Math.max(1,u))}else 1!==t.playbackRate&&0!==t.playbackRate&&(t.playbackRate=1)}}}}},e.estimateLiveEdge=function(){var t=this.levelDetails;return null===t?null:t.edge+t.age},e.computeLatency=function(){var t=this.estimateLiveEdge();return null===t?null:t-this.currentTime},s(t,[{key:"latency",get:function(){return this._latency||0}},{key:"maxLatency",get:function(){var t=this.config,e=this.levelDetails;return void 0!==t.liveMaxLatencyDuration?t.liveMaxLatencyDuration:e?t.liveMaxLatencyDurationCount*e.targetduration:0}},{key:"targetLatency",get:function(){var t=this.levelDetails;if(null===t)return null;var e=t.holdBack,r=t.partHoldBack,i=t.targetduration,n=this.config,a=n.liveSyncDuration,s=n.liveSyncDurationCount,o=n.lowLatencyMode,l=this.hls.userConfig,u=o&&r||e;(l.liveSyncDuration||l.liveSyncDurationCount||0===u)&&(u=void 0!==a?a:s*i);var h=i;return u+Math.min(1*this.stallCount,h)}},{key:"liveSyncPosition",get:function(){var t=this.estimateLiveEdge(),e=this.targetLatency,r=this.levelDetails;if(null===t||null===e||null===r)return null;var i=r.edge,n=t-e-this.edgeStalled,a=i-r.totalduration,s=i-(this.config.lowLatencyMode&&r.partTarget||r.targetduration);return Math.min(Math.max(a,n),s)}},{key:"drift",get:function(){var t=this.levelDetails;return null===t?1:t.drift}},{key:"edgeStalled",get:function(){var t=this.levelDetails;if(null===t)return 0;var e=3*(this.config.lowLatencyMode&&t.partTarget||t.targetduration);return Math.max(t.age-e,0)}},{key:"forwardBufferLength",get:function(){var t=this.media,e=this.levelDetails;if(!t||!e)return 0;var r=t.buffered.length;return(r?t.buffered.end(r-1):e.edge)-this.currentTime}}]),t}(),Xe=["NONE","TYPE-0","TYPE-1",null],ze=["SDR","PQ","HLG"],Qe="",Je="YES",$e="v2",Ze=function(){function t(t,e,r){this.msn=void 0,this.part=void 0,this.skip=void 0,this.msn=t,this.part=e,this.skip=r}return t.prototype.addDirectives=function(t){var e=new self.URL(t);return void 0!==this.msn&&e.searchParams.set("_HLS_msn",this.msn.toString()),void 0!==this.part&&e.searchParams.set("_HLS_part",this.part.toString()),this.skip&&e.searchParams.set("_HLS_skip",this.skip),e.href},t}(),tr=function(){function t(t){this._attrs=void 0,this.audioCodec=void 0,this.bitrate=void 0,this.codecSet=void 0,this.url=void 0,this.frameRate=void 0,this.height=void 0,this.id=void 0,this.name=void 0,this.videoCodec=void 0,this.width=void 0,this.details=void 0,this.fragmentError=0,this.loadError=0,this.loaded=void 0,this.realBitrate=0,this.supportedPromise=void 0,this.supportedResult=void 0,this._avgBitrate=0,this._audioGroups=void 0,this._subtitleGroups=void 0,this._urlId=0,this.url=[t.url],this._attrs=[t.attrs],this.bitrate=t.bitrate,t.details&&(this.details=t.details),this.id=t.id||0,this.name=t.name,this.width=t.width||0,this.height=t.height||0,this.frameRate=t.attrs.optionalFloat("FRAME-RATE",0),this._avgBitrate=t.attrs.decimalInteger("AVERAGE-BANDWIDTH"),this.audioCodec=t.audioCodec,this.videoCodec=t.videoCodec,this.codecSet=[t.videoCodec,t.audioCodec].filter((function(t){return!!t})).map((function(t){return t.substring(0,4)})).join(","),this.addGroupId("audio",t.attrs.AUDIO),this.addGroupId("text",t.attrs.SUBTITLES)}var e=t.prototype;return e.hasAudioGroup=function(t){return er(this._audioGroups,t)},e.hasSubtitleGroup=function(t){return er(this._subtitleGroups,t)},e.addGroupId=function(t,e){if(e)if("audio"===t){var r=this._audioGroups;r||(r=this._audioGroups=[]),-1===r.indexOf(e)&&r.push(e)}else if("text"===t){var i=this._subtitleGroups;i||(i=this._subtitleGroups=[]),-1===i.indexOf(e)&&i.push(e)}},e.addFallback=function(){},s(t,[{key:"maxBitrate",get:function(){return Math.max(this.realBitrate,this.bitrate)}},{key:"averageBitrate",get:function(){return this._avgBitrate||this.realBitrate||this.bitrate}},{key:"attrs",get:function(){return this._attrs[0]}},{key:"codecs",get:function(){return this.attrs.CODECS||""}},{key:"pathwayId",get:function(){return this.attrs["PATHWAY-ID"]||"."}},{key:"videoRange",get:function(){return this.attrs["VIDEO-RANGE"]||"SDR"}},{key:"score",get:function(){return this.attrs.optionalFloat("SCORE",0)}},{key:"uri",get:function(){return this.url[0]||""}},{key:"audioGroups",get:function(){return this._audioGroups}},{key:"subtitleGroups",get:function(){return this._subtitleGroups}},{key:"urlId",get:function(){return 0},set:function(t){}},{key:"audioGroupIds",get:function(){return this.audioGroups?[this.audioGroupId]:void 0}},{key:"textGroupIds",get:function(){return this.subtitleGroups?[this.textGroupId]:void 0}},{key:"audioGroupId",get:function(){var t;return null==(t=this.audioGroups)?void 0:t[0]}},{key:"textGroupId",get:function(){var t;return null==(t=this.subtitleGroups)?void 0:t[0]}}]),t}();function er(t,e){return!(!e||!t)&&-1!==t.indexOf(e)}function rr(t,e){var r=e.startPTS;if(y(r)){var i,n=0;e.sn>t.sn?(n=r-t.start,i=t):(n=t.start-r,i=e),i.duration!==n&&(i.duration=n)}else e.sn>t.sn?t.cc===e.cc&&t.minEndPTS?e.start=t.start+(t.minEndPTS-t.start):e.start=t.start+t.duration:e.start=Math.max(t.start-e.duration,0)}function ir(t,e,r,i,n,a){i-r<=0&&(w.warn("Fragment should have a positive duration",e),i=r+e.duration,a=n+e.duration);var s=r,o=i,l=e.startPTS,u=e.endPTS;if(y(l)){var h=Math.abs(l-r);y(e.deltaPTS)?e.deltaPTS=Math.max(h,e.deltaPTS):e.deltaPTS=h,s=Math.max(r,l),r=Math.min(r,l),n=Math.min(n,e.startDTS),o=Math.min(i,u),i=Math.max(i,u),a=Math.max(a,e.endDTS)}var d=r-e.start;0!==e.start&&(e.start=r),e.duration=i-e.start,e.startPTS=r,e.maxStartPTS=s,e.startDTS=n,e.endPTS=i,e.minEndPTS=o,e.endDTS=a;var c,f=e.sn;if(!t||f<t.startSN||f>t.endSN)return 0;var g=f-t.startSN,v=t.fragments;for(v[g]=e,c=g;c>0;c--)rr(v[c],v[c-1]);for(c=g;c<v.length-1;c++)rr(v[c],v[c+1]);return t.fragmentHint&&rr(v[v.length-1],t.fragmentHint),t.PTSKnown=t.alignedSliding=!0,d}function nr(t,e){for(var r=null,i=t.fragments,n=i.length-1;n>=0;n--){var a=i[n].initSegment;if(a){r=a;break}}t.fragmentHint&&delete t.fragmentHint.endPTS;var s,l,u,h,d,c=0;if(function(t,e,r){for(var i=e.skippedSegments,n=Math.max(t.startSN,e.startSN)-e.startSN,a=(t.fragmentHint?1:0)+(i?e.endSN:Math.min(t.endSN,e.endSN))-e.startSN,s=e.startSN-t.startSN,o=e.fragmentHint?e.fragments.concat(e.fragmentHint):e.fragments,l=t.fragmentHint?t.fragments.concat(t.fragmentHint):t.fragments,u=n;u<=a;u++){var h=l[s+u],d=o[u];i&&!d&&u<i&&(d=e.fragments[u]=h),h&&d&&r(h,d)}}(t,e,(function(t,i){t.relurl&&(c=t.cc-i.cc),y(t.startPTS)&&y(t.endPTS)&&(i.start=i.startPTS=t.startPTS,i.startDTS=t.startDTS,i.maxStartPTS=t.maxStartPTS,i.endPTS=t.endPTS,i.endDTS=t.endDTS,i.minEndPTS=t.minEndPTS,i.duration=t.endPTS-t.startPTS,i.duration&&(s=i),e.PTSKnown=e.alignedSliding=!0),i.elementaryStreams=t.elementaryStreams,i.loader=t.loader,i.stats=t.stats,t.initSegment&&(i.initSegment=t.initSegment,r=t.initSegment)})),r&&(e.fragmentHint?e.fragments.concat(e.fragmentHint):e.fragments).forEach((function(t){var e;!t||t.initSegment&&t.initSegment.relurl!==(null==(e=r)?void 0:e.relurl)||(t.initSegment=r)})),e.skippedSegments)if(e.deltaUpdateFailed=e.fragments.some((function(t){return!t})),e.deltaUpdateFailed){w.warn("[level-helper] Previous playlist missing segments skipped in delta playlist");for(var f=e.skippedSegments;f--;)e.fragments.shift();e.startSN=e.fragments[0].sn,e.startCC=e.fragments[0].cc}else e.canSkipDateRanges&&(e.dateRanges=(l=t.dateRanges,u=e.dateRanges,h=e.recentlyRemovedDateranges,d=o({},l),h&&h.forEach((function(t){delete d[t]})),Object.keys(u).forEach((function(t){var e=new F(u[t].attr,d[t]);e.isValid?d[t]=e:w.warn('Ignoring invalid Playlist Delta Update DATERANGE tag: "'+JSON.stringify(u[t].attr)+'"')})),d));var g=e.fragments;if(c){w.warn("discontinuity sliding from playlist, take drift into account");for(var v=0;v<g.length;v++)g[v].cc+=c}e.skippedSegments&&(e.startCC=e.fragments[0].cc),function(t,e,r){if(t&&e)for(var i=0,n=0,a=t.length;n<=a;n++){var s=t[n],o=e[n+i];s&&o&&s.index===o.index&&s.fragment.sn===o.fragment.sn?r(s,o):i--}}(t.partList,e.partList,(function(t,e){e.elementaryStreams=t.elementaryStreams,e.stats=t.stats})),s?ir(e,s,s.startPTS,s.endPTS,s.startDTS,s.endDTS):ar(t,e),g.length&&(e.totalduration=e.edge-g[0].start),e.driftStartTime=t.driftStartTime,e.driftStart=t.driftStart;var m=e.advancedDateTime;if(e.advanced&&m){var p=e.edge;e.driftStart||(e.driftStartTime=m,e.driftStart=p),e.driftEndTime=m,e.driftEnd=p}else e.driftEndTime=t.driftEndTime,e.driftEnd=t.driftEnd,e.advancedDateTime=t.advancedDateTime}function ar(t,e){var r=e.startSN+e.skippedSegments-t.startSN,i=t.fragments;r<0||r>=i.length||sr(e,i[r].start)}function sr(t,e){if(e){for(var r=t.fragments,i=t.skippedSegments;i<r.length;i++)r[i].start+=e;t.fragmentHint&&(t.fragmentHint.start+=e)}}function or(t,e,r){var i;return null!=t&&t.details?lr(null==(i=t.details)?void 0:i.partList,e,r):null}function lr(t,e,r){if(t)for(var i=t.length;i--;){var n=t[i];if(n.index===r&&n.fragment.sn===e)return n}return null}function ur(t){t.forEach((function(t,e){var r=t.details;null!=r&&r.fragments&&r.fragments.forEach((function(t){t.level=e}))}))}function hr(t){switch(t.details){case A.FRAG_LOAD_TIMEOUT:case A.KEY_LOAD_TIMEOUT:case A.LEVEL_LOAD_TIMEOUT:case A.MANIFEST_LOAD_TIMEOUT:return!0}return!1}function dr(t,e){var r=hr(e);return t.default[(r?"timeout":"error")+"Retry"]}function cr(t,e){var r="linear"===t.backoff?1:Math.pow(2,e);return Math.min(r*t.retryDelayMs,t.maxRetryDelayMs)}function fr(t){return i(i({},t),{errorRetry:null,timeoutRetry:null})}function gr(t,e,r,i){if(!t)return!1;var n=null==i?void 0:i.code,a=e<t.maxNumRetry&&(function(t){return 0===t&&!1===navigator.onLine||!!t&&(t<400||t>499)}(n)||!!r);return t.shouldRetry?t.shouldRetry(t,e,r,i,a):a}var vr=function(t,e){for(var r=0,i=t.length-1,n=null,a=null;r<=i;){var s=e(a=t[n=(r+i)/2|0]);if(s>0)r=n+1;else{if(!(s<0))return a;i=n-1}}return null};function mr(t,e,r,i){void 0===r&&(r=0),void 0===i&&(i=0);var n=null;if(t){n=e[t.sn-e[0].sn+1]||null;var a=t.endDTS-r;a>0&&a<15e-7&&(r+=15e-7)}else 0===r&&0===e[0].start&&(n=e[0]);if(n&&(!t||t.level===n.level)&&0===pr(r,i,n))return n;var s=vr(e,pr.bind(null,r,i));return!s||s===t&&n?n:s}function pr(t,e,r){if(void 0===t&&(t=0),void 0===e&&(e=0),r.start<=t&&r.start+r.duration>t)return 0;var i=Math.min(e,r.duration+(r.deltaPTS?r.deltaPTS:0));return r.start+r.duration-i<=t?1:r.start-i>t&&r.start?-1:0}function yr(t,e,r){var i=1e3*Math.min(e,r.duration+(r.deltaPTS?r.deltaPTS:0));return(r.endProgramDateTime||0)-i>t}var Er=0,Tr=2,Sr=3,Lr=5,Ar=0,Rr=1,kr=2,br=function(){function t(t){this.hls=void 0,this.playlistError=0,this.penalizedRenditions={},this.log=void 0,this.warn=void 0,this.error=void 0,this.hls=t,this.log=w.log.bind(w,"[info]:"),this.warn=w.warn.bind(w,"[warning]:"),this.error=w.error.bind(w,"[error]:"),this.registerListeners()}var e=t.prototype;return e.registerListeners=function(){var t=this.hls;t.on(S.ERROR,this.onError,this),t.on(S.MANIFEST_LOADING,this.onManifestLoading,this),t.on(S.LEVEL_UPDATED,this.onLevelUpdated,this)},e.unregisterListeners=function(){var t=this.hls;t&&(t.off(S.ERROR,this.onError,this),t.off(S.ERROR,this.onErrorOut,this),t.off(S.MANIFEST_LOADING,this.onManifestLoading,this),t.off(S.LEVEL_UPDATED,this.onLevelUpdated,this))},e.destroy=function(){this.unregisterListeners(),this.hls=null,this.penalizedRenditions={}},e.startLoad=function(t){},e.stopLoad=function(){this.playlistError=0},e.getVariantLevelIndex=function(t){return(null==t?void 0:t.type)===Ie?t.level:this.hls.loadLevel},e.onManifestLoading=function(){this.playlistError=0,this.penalizedRenditions={}},e.onLevelUpdated=function(){this.playlistError=0},e.onError=function(t,e){var r,i;if(!e.fatal){var n=this.hls,a=e.context;switch(e.details){case A.FRAG_LOAD_ERROR:case A.FRAG_LOAD_TIMEOUT:case A.KEY_LOAD_ERROR:case A.KEY_LOAD_TIMEOUT:return void(e.errorAction=this.getFragRetryOrSwitchAction(e));case A.FRAG_PARSING_ERROR:if(null!=(r=e.frag)&&r.gap)return void(e.errorAction={action:Er,flags:Ar});case A.FRAG_GAP:case A.FRAG_DECRYPT_ERROR:return e.errorAction=this.getFragRetryOrSwitchAction(e),void(e.errorAction.action=Tr);case A.LEVEL_EMPTY_ERROR:case A.LEVEL_PARSING_ERROR:var s,o,l=e.parent===Ie?e.level:n.loadLevel;return void(e.details===A.LEVEL_EMPTY_ERROR&&null!=(s=e.context)&&null!=(o=s.levelDetails)&&o.live?e.errorAction=this.getPlaylistRetryOrSwitchAction(e,l):(e.levelRetry=!1,e.errorAction=this.getLevelSwitchAction(e,l)));case A.LEVEL_LOAD_ERROR:case A.LEVEL_LOAD_TIMEOUT:return void("number"==typeof(null==a?void 0:a.level)&&(e.errorAction=this.getPlaylistRetryOrSwitchAction(e,a.level)));case A.AUDIO_TRACK_LOAD_ERROR:case A.AUDIO_TRACK_LOAD_TIMEOUT:case A.SUBTITLE_LOAD_ERROR:case A.SUBTITLE_TRACK_LOAD_TIMEOUT:if(a){var u=n.levels[n.loadLevel];if(u&&(a.type===be&&u.hasAudioGroup(a.groupId)||a.type===De&&u.hasSubtitleGroup(a.groupId)))return e.errorAction=this.getPlaylistRetryOrSwitchAction(e,n.loadLevel),e.errorAction.action=Tr,void(e.errorAction.flags=Rr)}return;case A.KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED:var h=n.levels[n.loadLevel],d=null==h?void 0:h.attrs["HDCP-LEVEL"];return void(d?e.errorAction={action:Tr,flags:kr,hdcpLevel:d}:this.keySystemError(e));case A.BUFFER_ADD_CODEC_ERROR:case A.REMUX_ALLOC_ERROR:case A.BUFFER_APPEND_ERROR:return void(e.errorAction=this.getLevelSwitchAction(e,null!=(i=e.level)?i:n.loadLevel));case A.INTERNAL_EXCEPTION:case A.BUFFER_APPENDING_ERROR:case A.BUFFER_FULL_ERROR:case A.LEVEL_SWITCH_ERROR:case A.BUFFER_STALLED_ERROR:case A.BUFFER_SEEK_OVER_HOLE:case A.BUFFER_NUDGE_ON_STALL:return void(e.errorAction={action:Er,flags:Ar})}e.type===L.KEY_SYSTEM_ERROR&&this.keySystemError(e)}},e.keySystemError=function(t){var e=this.getVariantLevelIndex(t.frag);t.levelRetry=!1,t.errorAction=this.getLevelSwitchAction(t,e)},e.getPlaylistRetryOrSwitchAction=function(t,e){var r=dr(this.hls.config.playlistLoadPolicy,t),i=this.playlistError++;if(gr(r,i,hr(t),t.response))return{action:Lr,flags:Ar,retryConfig:r,retryCount:i};var n=this.getLevelSwitchAction(t,e);return r&&(n.retryConfig=r,n.retryCount=i),n},e.getFragRetryOrSwitchAction=function(t){var e=this.hls,r=this.getVariantLevelIndex(t.frag),i=e.levels[r],n=e.config,a=n.fragLoadPolicy,s=n.keyLoadPolicy,o=dr(t.details.startsWith("key")?s:a,t),l=e.levels.reduce((function(t,e){return t+e.fragmentError}),0);if(i&&(t.details!==A.FRAG_GAP&&i.fragmentError++,gr(o,l,hr(t),t.response)))return{action:Lr,flags:Ar,retryConfig:o,retryCount:l};var u=this.getLevelSwitchAction(t,r);return o&&(u.retryConfig=o,u.retryCount=l),u},e.getLevelSwitchAction=function(t,e){var r=this.hls;null==e&&(e=r.loadLevel);var i=this.hls.levels[e];if(i){var n,a,s=t.details;i.loadError++,s===A.BUFFER_APPEND_ERROR&&i.fragmentError++;var o=-1,l=r.levels,u=r.loadLevel,h=r.minAutoLevel,d=r.maxAutoLevel;r.autoLevelEnabled||(r.loadLevel=-1);for(var c,f=null==(n=t.frag)?void 0:n.type,g=(f===we&&s===A.FRAG_PARSING_ERROR||"audio"===t.sourceBufferName&&(s===A.BUFFER_ADD_CODEC_ERROR||s===A.BUFFER_APPEND_ERROR))&&l.some((function(t){var e=t.audioCodec;return i.audioCodec!==e})),v="video"===t.sourceBufferName&&(s===A.BUFFER_ADD_CODEC_ERROR||s===A.BUFFER_APPEND_ERROR)&&l.some((function(t){var e=t.codecSet,r=t.audioCodec;return i.codecSet!==e&&i.audioCodec===r})),m=null!=(a=t.context)?a:{},p=m.type,y=m.groupId,E=function(){var e=(T+u)%l.length;if(e!==u&&e>=h&&e<=d&&0===l[e].loadError){var r,n,a=l[e];if(s===A.FRAG_GAP&&t.frag){var c=l[e].details;if(c){var m=mr(t.frag,c.fragments,t.frag.start);if(null!=m&&m.gap)return 0}}else{if(p===be&&a.hasAudioGroup(y)||p===De&&a.hasSubtitleGroup(y))return 0;if(f===we&&null!=(r=i.audioGroups)&&r.some((function(t){return a.hasAudioGroup(t)}))||f===Ce&&null!=(n=i.subtitleGroups)&&n.some((function(t){return a.hasSubtitleGroup(t)}))||g&&i.audioCodec===a.audioCodec||!g&&i.audioCodec!==a.audioCodec||v&&i.codecSet===a.codecSet)return 0}return o=e,1}},T=l.length;T--&&(0===(c=E())||1!==c););if(o>-1&&r.loadLevel!==o)return t.levelRetry=!0,this.playlistError=0,{action:Tr,flags:Ar,nextAutoLevel:o}}return{action:Tr,flags:Rr}},e.onErrorOut=function(t,e){var r;switch(null==(r=e.errorAction)?void 0:r.action){case Er:break;case Tr:this.sendAlternateToPenaltyBox(e),e.errorAction.resolved||e.details===A.FRAG_GAP?/MediaSource readyState: ended/.test(e.error.message)&&(this.warn('MediaSource ended after "'+e.sourceBufferName+'" sourceBuffer append error. Attempting to recover from media error.'),this.hls.recoverMediaError()):e.fatal=!0}e.fatal&&this.hls.stopLoad()},e.sendAlternateToPenaltyBox=function(t){var e=this.hls,r=t.errorAction;if(r){var i=r.flags,n=r.hdcpLevel,a=r.nextAutoLevel;switch(i){case Ar:this.switchLevel(t,a);break;case kr:n&&(e.maxHdcpLevel=Xe[Xe.indexOf(n)-1],r.resolved=!0),this.warn('Restricting playback to HDCP-LEVEL of "'+e.maxHdcpLevel+'" or lower')}r.resolved||this.switchLevel(t,a)}},e.switchLevel=function(t,e){void 0!==e&&t.errorAction&&(this.warn("switching to level "+e+" after "+t.details),this.hls.nextAutoLevel=e,t.errorAction.resolved=!0,this.hls.nextLoadLevel=this.hls.nextAutoLevel)},t}(),Dr=function(){function t(t,e){this.hls=void 0,this.timer=-1,this.requestScheduled=-1,this.canLoad=!1,this.log=void 0,this.warn=void 0,this.log=w.log.bind(w,e+":"),this.warn=w.warn.bind(w,e+":"),this.hls=t}var e=t.prototype;return e.destroy=function(){this.clearTimer(),this.hls=this.log=this.warn=null},e.clearTimer=function(){-1!==this.timer&&(self.clearTimeout(this.timer),this.timer=-1)},e.startLoad=function(){this.canLoad=!0,this.requestScheduled=-1,this.loadPlaylist()},e.stopLoad=function(){this.canLoad=!1,this.clearTimer()},e.switchParams=function(t,e){var r=null==e?void 0:e.renditionReports;if(r){for(var i=-1,n=0;n<r.length;n++){var a=r[n],s=void 0;try{s=new self.URL(a.URI,e.url).href}catch(t){w.warn("Could not construct new URL for Rendition Report: "+t),s=a.URI||""}if(s===t){i=n;break}s===t.substring(0,s.length)&&(i=n)}if(-1!==i){var o=r[i],l=parseInt(o["LAST-MSN"])||(null==e?void 0:e.lastPartSn),u=parseInt(o["LAST-PART"])||(null==e?void 0:e.lastPartIndex);if(this.hls.config.lowLatencyMode){var h=Math.min(e.age-e.partTarget,e.targetduration);u>=0&&h>e.partTarget&&(u+=1)}return new Ze(l,u>=0?u:void 0,Qe)}}},e.loadPlaylist=function(t){-1===this.requestScheduled&&(this.requestScheduled=self.performance.now())},e.shouldLoadPlaylist=function(t){return this.canLoad&&!!t&&!!t.url&&(!t.details||t.details.live)},e.shouldReloadPlaylist=function(t){return-1===this.timer&&-1===this.requestScheduled&&this.shouldLoadPlaylist(t)},e.playlistLoaded=function(t,e,r){var i=this,n=e.details,a=e.stats,s=self.performance.now(),o=a.loading.first?Math.max(0,s-a.loading.first):0;if(n.advancedDateTime=Date.now()-o,n.live||null!=r&&r.live){if(n.reloaded(r),r&&this.log("live playlist "+t+" "+(n.advanced?"REFRESHED "+n.lastPartSn+"-"+n.lastPartIndex:n.updated?"UPDATED":"MISSED")),r&&n.fragments.length>0&&nr(r,n),!this.canLoad||!n.live)return;var l,u=void 0,h=void 0;if(n.canBlockReload&&n.endSN&&n.advanced){var d=this.hls.config.lowLatencyMode,c=n.lastPartSn,f=n.endSN,g=n.lastPartIndex,v=c===f;-1!==g?(u=v?f+1:c,h=v?d?0:g:g+1):u=f+1;var m=n.age,p=m+n.ageHeader,y=Math.min(p-n.partTarget,1.5*n.targetduration);if(y>0){if(r&&y>r.tuneInGoal)this.warn("CDN Tune-in goal increased from: "+r.tuneInGoal+" to: "+y+" with playlist age: "+n.age),y=0;else{var E=Math.floor(y/n.targetduration);u+=E,void 0!==h&&(h+=Math.round(y%n.targetduration/n.partTarget)),this.log("CDN Tune-in age: "+n.ageHeader+"s last advanced "+m.toFixed(2)+"s goal: "+y+" skip sn "+E+" to part "+h)}n.tuneInGoal=y}if(l=this.getDeliveryDirectives(n,e.deliveryDirectives,u,h),d||!v)return void this.loadPlaylist(l)}else(n.canBlockReload||n.canSkipUntil)&&(l=this.getDeliveryDirectives(n,e.deliveryDirectives,u,h));var T=this.hls.mainForwardBufferInfo,S=T?T.end-T.len:0,L=function(t,e){void 0===e&&(e=1/0);var r=1e3*t.targetduration;if(t.updated){var i=t.fragments;if(i.length&&4*r>e){var n=1e3*i[i.length-1].duration;n<r&&(r=n)}}else r/=2;return Math.round(r)}(n,1e3*(n.edge-S));n.updated&&s>this.requestScheduled+L&&(this.requestScheduled=a.loading.start),void 0!==u&&n.canBlockReload?this.requestScheduled=a.loading.first+L-(1e3*n.partTarget||1e3):-1===this.requestScheduled||this.requestScheduled+L<s?this.requestScheduled=s:this.requestScheduled-s<=0&&(this.requestScheduled+=L);var A=this.requestScheduled-s;A=Math.max(0,A),this.log("reload live playlist "+t+" in "+Math.round(A)+" ms"),this.timer=self.setTimeout((function(){return i.loadPlaylist(l)}),A)}else this.clearTimer()},e.getDeliveryDirectives=function(t,e,r,i){var n=function(t,e){var r=t.canSkipUntil,i=t.canSkipDateRanges,n=t.endSN;return r&&(void 0!==e?e-n:0)<r?i?$e:Je:Qe}(t,r);return null!=e&&e.skip&&t.deltaUpdateFailed&&(r=e.msn,i=e.part,n=Qe),new Ze(r,i,n)},e.checkRetry=function(t){var e=this,r=t.details,i=hr(t),n=t.errorAction,a=n||{},s=a.action,o=a.retryCount,l=void 0===o?0:o,u=a.retryConfig,h=!!n&&!!u&&(s===Lr||!n.resolved&&s===Tr);if(h){var d;if(this.requestScheduled=-1,l>=u.maxNumRetry)return!1;if(i&&null!=(d=t.context)&&d.deliveryDirectives)this.warn("Retrying playlist loading "+(l+1)+"/"+u.maxNumRetry+' after "'+r+'" without delivery-directives'),this.loadPlaylist();else{var c=cr(u,l);this.timer=self.setTimeout((function(){return e.loadPlaylist()}),c),this.warn("Retrying playlist loading "+(l+1)+"/"+u.maxNumRetry+' after "'+r+'" in '+c+"ms")}t.levelRetry=!0,n.resolved=!0}return h},t}(),Ir=function(){function t(t,e,r){void 0===e&&(e=0),void 0===r&&(r=0),this.halfLife=void 0,this.alpha_=void 0,this.estimate_=void 0,this.totalWeight_=void 0,this.halfLife=t,this.alpha_=t?Math.exp(Math.log(.5)/t):0,this.estimate_=e,this.totalWeight_=r}var e=t.prototype;return e.sample=function(t,e){var r=Math.pow(this.alpha_,t);this.estimate_=e*(1-r)+r*this.estimate_,this.totalWeight_+=t},e.getTotalWeight=function(){return this.totalWeight_},e.getEstimate=function(){if(this.alpha_){var t=1-Math.pow(this.alpha_,this.totalWeight_);if(t)return this.estimate_/t}return this.estimate_},t}(),wr=function(){function t(t,e,r,i){void 0===i&&(i=100),this.defaultEstimate_=void 0,this.minWeight_=void 0,this.minDelayMs_=void 0,this.slow_=void 0,this.fast_=void 0,this.defaultTTFB_=void 0,this.ttfb_=void 0,this.defaultEstimate_=r,this.minWeight_=.001,this.minDelayMs_=50,this.slow_=new Ir(t),this.fast_=new Ir(e),this.defaultTTFB_=i,this.ttfb_=new Ir(t)}var e=t.prototype;return e.update=function(t,e){var r=this.slow_,i=this.fast_,n=this.ttfb_;r.halfLife!==t&&(this.slow_=new Ir(t,r.getEstimate(),r.getTotalWeight())),i.halfLife!==e&&(this.fast_=new Ir(e,i.getEstimate(),i.getTotalWeight())),n.halfLife!==t&&(this.ttfb_=new Ir(t,n.getEstimate(),n.getTotalWeight()))},e.sample=function(t,e){var r=(t=Math.max(t,this.minDelayMs_))/1e3,i=8*e/r;this.fast_.sample(r,i),this.slow_.sample(r,i)},e.sampleTTFB=function(t){var e=t/1e3,r=Math.sqrt(2)*Math.exp(-Math.pow(e,2)/2);this.ttfb_.sample(r,Math.max(t,5))},e.canEstimate=function(){return this.fast_.getTotalWeight()>=this.minWeight_},e.getEstimate=function(){return this.canEstimate()?Math.min(this.fast_.getEstimate(),this.slow_.getEstimate()):this.defaultEstimate_},e.getEstimateTTFB=function(){return this.ttfb_.getTotalWeight()>=this.minWeight_?this.ttfb_.getEstimate():this.defaultTTFB_},e.destroy=function(){},t}(),Cr={supported:!0,configurations:[],decodingInfoResults:[{supported:!0,powerEfficient:!0,smooth:!0}]},_r={};function xr(t,e,r){var n=t.videoCodec,a=t.audioCodec;if(!n||!a||!r)return Promise.resolve(Cr);var s={width:t.width,height:t.height,bitrate:Math.ceil(Math.max(.9*t.bitrate,t.averageBitrate)),framerate:t.frameRate||30},o=t.videoRange;"SDR"!==o&&(s.transferFunction=o.toLowerCase());var l=n.split(",").map((function(t){return{type:"media-source",video:i(i({},s),{},{contentType:ne(t,"video")})}}));return a&&t.audioGroups&&t.audioGroups.forEach((function(t){var r;t&&(null==(r=e.groups[t])||r.tracks.forEach((function(e){if(e.groupId===t){var r=e.channels||"",i=parseFloat(r);y(i)&&i>2&&l.push.apply(l,a.split(",").map((function(t){return{type:"media-source",audio:{contentType:ne(t,"audio"),channels:""+i}}})))}})))})),Promise.all(l.map((function(t){var e=function(t){var e=t.audio,r=t.video,i=r||e;if(i){var n=i.contentType.split('"')[1];if(r)return"r"+r.height+"x"+r.width+"f"+Math.ceil(r.framerate)+(r.transferFunction||"sd")+"_"+n+"_"+Math.ceil(r.bitrate/1e5);if(e)return"c"+e.channels+(e.spatialRendering?"s":"n")+"_"+n}return""}(t);return _r[e]||(_r[e]=r.decodingInfo(t))}))).then((function(t){return{supported:!t.some((function(t){return!t.supported})),configurations:l,decodingInfoResults:t}})).catch((function(t){return{supported:!1,configurations:l,decodingInfoResults:[],error:t}}))}function Pr(t,e){var r=!1,i=[];return t&&(r="SDR"!==t,i=[t]),e&&(i=e.allowedVideoRanges||ze.slice(0),i=(r=void 0!==e.preferHDR?e.preferHDR:function(){if("function"==typeof matchMedia){var t=matchMedia("(dynamic-range: high)"),e=matchMedia("bad query");if(t.media!==e.media)return!0===t.matches}return!1}())?i.filter((function(t){return"SDR"!==t})):["SDR"]),{preferHDR:r,allowedVideoRanges:i}}function Fr(t,e){w.log('[abr] start candidates with "'+t+'" ignored because '+e)}function Mr(t,e,r){if("attrs"in t){var i=e.indexOf(t);if(-1!==i)return i}for(var n=0;n<e.length;n++)if(Or(t,e[n],r))return n;return-1}function Or(t,e,r){var i=t.groupId,n=t.name,a=t.lang,s=t.assocLang,o=t.characteristics,l=t.default,u=t.forced;return(void 0===i||e.groupId===i)&&(void 0===n||e.name===n)&&(void 0===a||e.lang===a)&&(void 0===a||e.assocLang===s)&&(void 0===l||e.default===l)&&(void 0===u||e.forced===u)&&(void 0===o||function(t,e){void 0===e&&(e="");var r=t.split(","),i=e.split(",");return r.length===i.length&&!r.some((function(t){return-1===i.indexOf(t)}))}(o,e.characteristics))&&(void 0===r||r(t,e))}function Nr(t,e){var r=t.audioCodec,i=t.channels;return!(void 0!==r&&(e.audioCodec||"").substring(0,4)!==r.substring(0,4)||void 0!==i&&i!==(e.channels||"2"))}function Ur(t,e,r){for(var i=e;i;i--)if(r(t[i]))return i;for(var n=e+1;n<t.length;n++)if(r(t[n]))return n;return-1}var Br=function(){function t(t){var e=this;this.hls=void 0,this.lastLevelLoadSec=0,this.lastLoadedFragLevel=-1,this.firstSelection=-1,this._nextAutoLevel=-1,this.nextAutoLevelKey="",this.audioTracksByGroup=null,this.codecTiers=null,this.timer=-1,this.fragCurrent=null,this.partCurrent=null,this.bitrateTestDelay=0,this.bwEstimator=void 0,this._abandonRulesCheck=function(){var t=e.fragCurrent,r=e.partCurrent,i=e.hls,n=i.autoLevelEnabled,a=i.media;if(t&&a){var s=performance.now(),o=r?r.stats:t.stats,l=r?r.duration:t.duration,u=s-o.loading.start,h=i.minAutoLevel;if(o.aborted||o.loaded&&o.loaded===o.total||t.level<=h)return e.clearTimer(),void(e._nextAutoLevel=-1);if(n&&!a.paused&&a.playbackRate&&a.readyState){var d=i.mainForwardBufferInfo;if(null!==d){var c=e.bwEstimator.getEstimateTTFB(),f=Math.abs(a.playbackRate);if(!(u<=Math.max(c,l/(2*f)*1e3))){var g=d.len/f,v=o.loading.first?o.loading.first-o.loading.start:-1,m=o.loaded&&v>-1,p=e.getBwEstimate(),E=i.levels,T=E[t.level],L=o.total||Math.max(o.loaded,Math.round(l*T.maxBitrate/8)),A=m?u-v:u;A<1&&m&&(A=Math.min(u,8*o.loaded/p));var R=m?1e3*o.loaded/A:0,k=R?(L-o.loaded)/R:8*L/p+c/1e3;if(!(k<=g)){var b,D=R?8*R:p,I=Number.POSITIVE_INFINITY;for(b=t.level-1;b>h;b--){var C=E[b].maxBitrate;if((I=e.getTimeToLoadFrag(c/1e3,D,l*C,!E[b].details))<g)break}if(!(I>=k||I>10*l)){i.nextLoadLevel=i.nextAutoLevel=b,m?e.bwEstimator.sample(u-Math.min(c,v),o.loaded):e.bwEstimator.sampleTTFB(u);var _=E[b].bitrate;e.getBwEstimate()*e.hls.config.abrBandWidthUpFactor>_&&e.resetEstimator(_),e.clearTimer(),w.warn("[abr] Fragment "+t.sn+(r?" part "+r.index:"")+" of level "+t.level+" is loading too slowly;\n Time to underbuffer: "+g.toFixed(3)+" s\n Estimated load time for current fragment: "+k.toFixed(3)+" s\n Estimated load time for down switch fragment: "+I.toFixed(3)+" s\n TTFB estimate: "+(0|v)+" ms\n Current BW estimate: "+(y(p)?0|p:"Unknown")+" bps\n New BW estimate: "+(0|e.getBwEstimate())+" bps\n Switching to level "+b+" @ "+(0|_)+" bps"),i.trigger(S.FRAG_LOAD_EMERGENCY_ABORTED,{frag:t,part:r,stats:o})}}}}}}},this.hls=t,this.bwEstimator=this.initEstimator(),this.registerListeners()}var e=t.prototype;return e.resetEstimator=function(t){t&&(w.log("setting initial bwe to "+t),this.hls.config.abrEwmaDefaultEstimate=t),this.firstSelection=-1,this.bwEstimator=this.initEstimator()},e.initEstimator=function(){var t=this.hls.config;return new wr(t.abrEwmaSlowVoD,t.abrEwmaFastVoD,t.abrEwmaDefaultEstimate)},e.registerListeners=function(){var t=this.hls;t.on(S.MANIFEST_LOADING,this.onManifestLoading,this),t.on(S.FRAG_LOADING,this.onFragLoading,this),t.on(S.FRAG_LOADED,this.onFragLoaded,this),t.on(S.FRAG_BUFFERED,this.onFragBuffered,this),t.on(S.LEVEL_SWITCHING,this.onLevelSwitching,this),t.on(S.LEVEL_LOADED,this.onLevelLoaded,this),t.on(S.LEVELS_UPDATED,this.onLevelsUpdated,this),t.on(S.MAX_AUTO_LEVEL_UPDATED,this.onMaxAutoLevelUpdated,this),t.on(S.ERROR,this.onError,this)},e.unregisterListeners=function(){var t=this.hls;t&&(t.off(S.MANIFEST_LOADING,this.onManifestLoading,this),t.off(S.FRAG_LOADING,this.onFragLoading,this),t.off(S.FRAG_LOADED,this.onFragLoaded,this),t.off(S.FRAG_BUFFERED,this.onFragBuffered,this),t.off(S.LEVEL_SWITCHING,this.onLevelSwitching,this),t.off(S.LEVEL_LOADED,this.onLevelLoaded,this),t.off(S.LEVELS_UPDATED,this.onLevelsUpdated,this),t.off(S.MAX_AUTO_LEVEL_UPDATED,this.onMaxAutoLevelUpdated,this),t.off(S.ERROR,this.onError,this))},e.destroy=function(){this.unregisterListeners(),this.clearTimer(),this.hls=this._abandonRulesCheck=null,this.fragCurrent=this.partCurrent=null},e.onManifestLoading=function(t,e){this.lastLoadedFragLevel=-1,this.firstSelection=-1,this.lastLevelLoadSec=0,this.fragCurrent=this.partCurrent=null,this.onLevelsUpdated(),this.clearTimer()},e.onLevelsUpdated=function(){this.lastLoadedFragLevel>-1&&this.fragCurrent&&(this.lastLoadedFragLevel=this.fragCurrent.level),this._nextAutoLevel=-1,this.onMaxAutoLevelUpdated(),this.codecTiers=null,this.audioTracksByGroup=null},e.onMaxAutoLevelUpdated=function(){this.firstSelection=-1,this.nextAutoLevelKey=""},e.onFragLoading=function(t,e){var r,i=e.frag;this.ignoreFragment(i)||(i.bitrateTest||(this.fragCurrent=i,this.partCurrent=null!=(r=e.part)?r:null),this.clearTimer(),this.timer=self.setInterval(this._abandonRulesCheck,100))},e.onLevelSwitching=function(t,e){this.clearTimer()},e.onError=function(t,e){if(!e.fatal)switch(e.details){case A.BUFFER_ADD_CODEC_ERROR:case A.BUFFER_APPEND_ERROR:this.lastLoadedFragLevel=-1,this.firstSelection=-1;break;case A.FRAG_LOAD_TIMEOUT:var r=e.frag,i=this.fragCurrent,n=this.partCurrent;if(r&&i&&r.sn===i.sn&&r.level===i.level){var a=performance.now(),s=n?n.stats:r.stats,o=a-s.loading.start,l=s.loading.first?s.loading.first-s.loading.start:-1;if(s.loaded&&l>-1){var u=this.bwEstimator.getEstimateTTFB();this.bwEstimator.sample(o-Math.min(u,l),s.loaded)}else this.bwEstimator.sampleTTFB(o)}}},e.getTimeToLoadFrag=function(t,e,r,i){return t+r/e+(i?this.lastLevelLoadSec:0)},e.onLevelLoaded=function(t,e){var r=this.hls.config,i=e.stats.loading,n=i.end-i.start;y(n)&&(this.lastLevelLoadSec=n/1e3),e.details.live?this.bwEstimator.update(r.abrEwmaSlowLive,r.abrEwmaFastLive):this.bwEstimator.update(r.abrEwmaSlowVoD,r.abrEwmaFastVoD)},e.onFragLoaded=function(t,e){var r=e.frag,i=e.part,n=i?i.stats:r.stats;if(r.type===Ie&&this.bwEstimator.sampleTTFB(n.loading.first-n.loading.start),!this.ignoreFragment(r)){if(this.clearTimer(),r.level===this._nextAutoLevel&&(this._nextAutoLevel=-1),this.firstSelection=-1,this.hls.config.abrMaxWithRealBitrate){var a=i?i.duration:r.duration,s=this.hls.levels[r.level],o=(s.loaded?s.loaded.bytes:0)+n.loaded,l=(s.loaded?s.loaded.duration:0)+a;s.loaded={bytes:o,duration:l},s.realBitrate=Math.round(8*o/l)}if(r.bitrateTest){var u={stats:n,frag:r,part:i,id:r.type};this.onFragBuffered(S.FRAG_BUFFERED,u),r.bitrateTest=!1}else this.lastLoadedFragLevel=r.level}},e.onFragBuffered=function(t,e){var r=e.frag,i=e.part,n=null!=i&&i.stats.loaded?i.stats:r.stats;if(!n.aborted&&!this.ignoreFragment(r)){var a=n.parsing.end-n.loading.start-Math.min(n.loading.first-n.loading.start,this.bwEstimator.getEstimateTTFB());this.bwEstimator.sample(a,n.loaded),n.bwEstimate=this.getBwEstimate(),r.bitrateTest?this.bitrateTestDelay=a/1e3:this.bitrateTestDelay=0}},e.ignoreFragment=function(t){return t.type!==Ie||"initSegment"===t.sn},e.clearTimer=function(){this.timer>-1&&(self.clearInterval(this.timer),this.timer=-1)},e.getAutoLevelKey=function(){var t;return this.getBwEstimate()+"_"+(null==(t=this.hls.mainForwardBufferInfo)?void 0:t.len)},e.getNextABRAutoLevel=function(){var t=this.fragCurrent,e=this.partCurrent,r=this.hls,i=r.maxAutoLevel,n=r.config,a=r.minAutoLevel,s=r.media,o=e?e.duration:t?t.duration:0,l=s&&0!==s.playbackRate?Math.abs(s.playbackRate):1,u=this.getBwEstimate(),h=r.mainForwardBufferInfo,d=(h?h.len:0)/l,c=n.abrBandWidthFactor,f=n.abrBandWidthUpFactor;if(d){var g=this.findBestLevel(u,a,i,d,0,c,f);if(g>=0)return g}var v=o?Math.min(o,n.maxStarvationDelay):n.maxStarvationDelay;if(!d){var m=this.bitrateTestDelay;m&&(v=(o?Math.min(o,n.maxLoadingDelay):n.maxLoadingDelay)-m,w.info("[abr] bitrate test took "+Math.round(1e3*m)+"ms, set first fragment max fetchDuration to "+Math.round(1e3*v)+" ms"),c=f=1)}var p=this.findBestLevel(u,a,i,d,v,c,f);if(w.info("[abr] "+(d?"rebuffering expected":"buffer is empty")+", optimal quality level "+p),p>-1)return p;var y=r.levels[a],E=r.levels[r.loadLevel];return(null==y?void 0:y.bitrate)<(null==E?void 0:E.bitrate)?a:r.loadLevel},e.getBwEstimate=function(){return this.bwEstimator.canEstimate()?this.bwEstimator.getEstimate():this.hls.config.abrEwmaDefaultEstimate},e.findBestLevel=function(t,e,r,i,n,a,s){var o,l=this,u=i+n,h=this.lastLoadedFragLevel,d=-1===h?this.hls.firstLevel:h,c=this.fragCurrent,f=this.partCurrent,g=this.hls,v=g.levels,m=g.allAudioTracks,p=g.loadLevel,E=g.config;if(1===v.length)return 0;var T,S=v[d],L=!(null==S||null==(o=S.details)||!o.live),A=-1===p||-1===h,R="SDR",k=(null==S?void 0:S.frameRate)||0,b=E.audioPreference,D=E.videoPreference,I=this.audioTracksByGroup||(this.audioTracksByGroup=function(t){return t.reduce((function(t,e){var r=t.groups[e.groupId];r||(r=t.groups[e.groupId]={tracks:[],channels:{2:0},hasDefault:!1,hasAutoSelect:!1}),r.tracks.push(e);var i=e.channels||"2";return r.channels[i]=(r.channels[i]||0)+1,r.hasDefault=r.hasDefault||e.default,r.hasAutoSelect=r.hasAutoSelect||e.autoselect,r.hasDefault&&(t.hasDefaultAudio=!0),r.hasAutoSelect&&(t.hasAutoSelectAudio=!0),t}),{hasDefaultAudio:!1,hasAutoSelectAudio:!1,groups:{}})}(m));if(A){if(-1!==this.firstSelection)return this.firstSelection;var C=this.codecTiers||(this.codecTiers=function(t,e,r,i){return t.slice(r,i+1).reduce((function(t,r){if(!r.codecSet)return t;var i=r.audioGroups,n=t[r.codecSet];n||(t[r.codecSet]=n={minBitrate:1/0,minHeight:1/0,minFramerate:1/0,maxScore:0,videoRanges:{SDR:0},channels:{2:0},hasDefaultAudio:!i,fragmentError:0}),n.minBitrate=Math.min(n.minBitrate,r.bitrate);var a=Math.min(r.height,r.width);return n.minHeight=Math.min(n.minHeight,a),n.minFramerate=Math.min(n.minFramerate,r.frameRate),n.maxScore=Math.max(n.maxScore,r.score),n.fragmentError+=r.fragmentError,n.videoRanges[r.videoRange]=(n.videoRanges[r.videoRange]||0)+1,i&&i.forEach((function(t){if(t){var r=e.groups[t];n.hasDefaultAudio=n.hasDefaultAudio||e.hasDefaultAudio?r.hasDefault:r.hasAutoSelect||!e.hasDefaultAudio&&!e.hasAutoSelectAudio,Object.keys(r.channels).forEach((function(t){n.channels[t]=(n.channels[t]||0)+r.channels[t]}))}})),t}),{})}(v,I,e,r)),_=function(t,e,r,i,n){for(var a=Object.keys(t),s=null==i?void 0:i.channels,o=null==i?void 0:i.audioCodec,l=s&&2===parseInt(s),u=!0,h=!1,d=1/0,c=1/0,f=1/0,g=0,v=[],m=Pr(e,n),p=m.preferHDR,E=m.allowedVideoRanges,T=function(){var e=t[a[S]];u=e.channels[2]>0,d=Math.min(d,e.minHeight),c=Math.min(c,e.minFramerate),f=Math.min(f,e.minBitrate);var r=E.filter((function(t){return e.videoRanges[t]>0}));r.length>0&&(h=!0,v=r)},S=a.length;S--;)T();d=y(d)?d:0,c=y(c)?c:0;var L=Math.max(1080,d),A=Math.max(30,c);return f=y(f)?f:r,r=Math.max(f,r),h||(e=void 0,v=[]),{codecSet:a.reduce((function(e,i){var n=t[i];if(i===e)return e;if(n.minBitrate>r)return Fr(i,"min bitrate of "+n.minBitrate+" > current estimate of "+r),e;if(!n.hasDefaultAudio)return Fr(i,"no renditions with default or auto-select sound found"),e;if(o&&i.indexOf(o.substring(0,4))%5!=0)return Fr(i,'audio codec preference "'+o+'" not found'),e;if(s&&!l){if(!n.channels[s])return Fr(i,"no renditions with "+s+" channel sound found (channels options: "+Object.keys(n.channels)+")"),e}else if((!o||l)&&u&&0===n.channels[2])return Fr(i,"no renditions with stereo sound found"),e;return n.minHeight>L?(Fr(i,"min resolution of "+n.minHeight+" > maximum of "+L),e):n.minFramerate>A?(Fr(i,"min framerate of "+n.minFramerate+" > maximum of "+A),e):v.some((function(t){return n.videoRanges[t]>0}))?n.maxScore<g?(Fr(i,"max score of "+n.maxScore+" < selected max of "+g),e):e&&(se(i)>=se(e)||n.fragmentError>t[e].fragmentError)?e:(g=n.maxScore,i):(Fr(i,"no variants with VIDEO-RANGE of "+JSON.stringify(v)+" found"),e)}),void 0),videoRanges:v,preferHDR:p,minFramerate:c,minBitrate:f}}(C,R,t,b,D),x=_.codecSet,P=_.videoRanges,F=_.minFramerate,M=_.minBitrate,O=_.preferHDR;T=x,R=O?P[P.length-1]:P[0],k=F,t=Math.max(t,M),w.log("[abr] picked start tier "+JSON.stringify(_))}else T=null==S?void 0:S.codecSet,R=null==S?void 0:S.videoRange;for(var N,U=f?f.duration:c?c.duration:0,B=this.bwEstimator.getEstimateTTFB()/1e3,G=[],K=function(){var e,o,c=v[H],g=H>d;if(!c)return 0;if(E.useMediaCapabilities&&!c.supportedResult&&!c.supportedPromise){var m=navigator.mediaCapabilities;"function"==typeof(null==m?void 0:m.decodingInfo)&&function(t,e,r,i,n,a){var s=t.audioCodec?t.audioGroups:null,o=null==a?void 0:a.audioCodec,l=null==a?void 0:a.channels,u=l?parseInt(l):o?1/0:2,h=null;if(null!=s&&s.length)try{h=1===s.length&&s[0]?e.groups[s[0]].channels:s.reduce((function(t,r){if(r){var i=e.groups[r];if(!i)throw new Error("Audio track group "+r+" not found");Object.keys(i.channels).forEach((function(e){t[e]=(t[e]||0)+i.channels[e]}))}return t}),{2:0})}catch(t){return!0}return void 0!==t.videoCodec&&(t.width>1920&&t.height>1088||t.height>1920&&t.width>1088||t.frameRate>Math.max(i,30)||"SDR"!==t.videoRange&&t.videoRange!==r||t.bitrate>Math.max(n,8e6))||!!h&&y(u)&&Object.keys(h).some((function(t){return parseInt(t)>u}))}(c,I,R,k,t,b)?(c.supportedPromise=xr(c,I,m),c.supportedPromise.then((function(t){c.supportedResult=t;var e=l.hls.levels,r=e.indexOf(c);t.error?w.warn('[abr] MediaCapabilities decodingInfo error: "'+t.error+'" for level '+r+" "+JSON.stringify(t)):t.supported||(w.warn("[abr] Unsupported MediaCapabilities decodingInfo result for level "+r+" "+JSON.stringify(t)),r>-1&&e.length>1&&(w.log("[abr] Removing unsupported level "+r),l.hls.removeLevel(r)))}))):c.supportedResult=Cr}if(T&&c.codecSet!==T||R&&c.videoRange!==R||g&&k>c.frameRate||!g&&k>0&&k<c.frameRate||null==(e=c.supportedResult)||null==(o=e.decodingInfoResults)||!o[0].smooth)return G.push(H),0;var D,C=c.details,_=(f?null==C?void 0:C.partTarget:null==C?void 0:C.averagetargetduration)||U;D=g?s*t:a*t;var x=U&&i>=2*U&&0===n?v[H].averageBitrate:v[H].maxBitrate,P=l.getTimeToLoadFrag(B,D,x*_,void 0===C);if(D>=x&&(H===h||0===c.loadError&&0===c.fragmentError)&&(P<=B||!y(P)||L&&!l.bitrateTestDelay||P<u)){var F=l.forcedAutoLevel;return H===p||-1!==F&&F===p||(G.length&&w.trace("[abr] Skipped level(s) "+G.join(",")+" of "+r+' max with CODECS and VIDEO-RANGE:"'+v[G[0]].codecs+'" '+v[G[0]].videoRange+'; not compatible with "'+S.codecs+'" '+R),w.info("[abr] switch candidate:"+d+"->"+H+" adjustedbw("+Math.round(D)+")-bitrate="+Math.round(D-x)+" ttfb:"+B.toFixed(1)+" avgDuration:"+_.toFixed(1)+" maxFetchDuration:"+u.toFixed(1)+" fetchDuration:"+P.toFixed(1)+" firstSelection:"+A+" codecSet:"+T+" videoRange:"+R+" hls.loadLevel:"+p)),A&&(l.firstSelection=H),{v:H}}},H=r;H>=e;H--)if(0!==(N=K())&&N)return N.v;return-1},s(t,[{key:"firstAutoLevel",get:function(){var t=this.hls,e=t.maxAutoLevel,r=t.minAutoLevel,i=this.getBwEstimate(),n=this.hls.config.maxStarvationDelay,a=this.findBestLevel(i,r,e,0,n,1,1);if(a>-1)return a;var s=this.hls.firstLevel,o=Math.min(Math.max(s,r),e);return w.warn("[abr] Could not find best starting auto level. Defaulting to first in playlist "+s+" clamped to "+o),o}},{key:"forcedAutoLevel",get:function(){return this.nextAutoLevelKey?-1:this._nextAutoLevel}},{key:"nextAutoLevel",get:function(){var t=this.forcedAutoLevel,e=this.bwEstimator.canEstimate(),r=this.lastLoadedFragLevel>-1;if(!(-1===t||e&&r&&this.nextAutoLevelKey!==this.getAutoLevelKey()))return t;var i=e&&r?this.getNextABRAutoLevel():this.firstAutoLevel;if(-1!==t){var n=this.hls.levels;if(n.length>Math.max(t,i)&&n[t].loadError<=n[i].loadError)return t}return this._nextAutoLevel=i,this.nextAutoLevelKey=this.getAutoLevelKey(),i},set:function(t){var e=Math.max(this.hls.minAutoLevel,t);this._nextAutoLevel!=e&&(this.nextAutoLevelKey="",this._nextAutoLevel=e)}}]),t}(),Gr=function(){function t(){this._boundTick=void 0,this._tickTimer=null,this._tickInterval=null,this._tickCallCount=0,this._boundTick=this.tick.bind(this)}var e=t.prototype;return e.destroy=function(){this.onHandlerDestroying(),this.onHandlerDestroyed()},e.onHandlerDestroying=function(){this.clearNextTick(),this.clearInterval()},e.onHandlerDestroyed=function(){},e.hasInterval=function(){return!!this._tickInterval},e.hasNextTick=function(){return!!this._tickTimer},e.setInterval=function(t){return!this._tickInterval&&(this._tickCallCount=0,this._tickInterval=self.setInterval(this._boundTick,t),!0)},e.clearInterval=function(){return!!this._tickInterval&&(self.clearInterval(this._tickInterval),this._tickInterval=null,!0)},e.clearNextTick=function(){return!!this._tickTimer&&(self.clearTimeout(this._tickTimer),this._tickTimer=null,!0)},e.tick=function(){this._tickCallCount++,1===this._tickCallCount&&(this.doTick(),this._tickCallCount>1&&this.tickImmediate(),this._tickCallCount=0)},e.tickImmediate=function(){this.clearNextTick(),this._tickTimer=self.setTimeout(this._boundTick,0)},e.doTick=function(){},t}(),Kr="NOT_LOADED",Hr="APPENDING",Vr="PARTIAL",Yr="OK",Wr=function(){function t(t){this.activePartLists=Object.create(null),this.endListFragments=Object.create(null),this.fragments=Object.create(null),this.timeRanges=Object.create(null),this.bufferPadding=.2,this.hls=void 0,this.hasGaps=!1,this.hls=t,this._registerListeners()}var e=t.prototype;return e._registerListeners=function(){var t=this.hls;t.on(S.BUFFER_APPENDED,this.onBufferAppended,this),t.on(S.FRAG_BUFFERED,this.onFragBuffered,this),t.on(S.FRAG_LOADED,this.onFragLoaded,this)},e._unregisterListeners=function(){var t=this.hls;t.off(S.BUFFER_APPENDED,this.onBufferAppended,this),t.off(S.FRAG_BUFFERED,this.onFragBuffered,this),t.off(S.FRAG_LOADED,this.onFragLoaded,this)},e.destroy=function(){this._unregisterListeners(),this.fragments=this.activePartLists=this.endListFragments=this.timeRanges=null},e.getAppendedFrag=function(t,e){var r=this.activePartLists[e];if(r)for(var i=r.length;i--;){var n=r[i];if(!n)break;var a=n.end;if(n.start<=t&&null!==a&&t<=a)return n}return this.getBufferedFrag(t,e)},e.getBufferedFrag=function(t,e){for(var r=this.fragments,i=Object.keys(r),n=i.length;n--;){var a=r[i[n]];if((null==a?void 0:a.body.type)===e&&a.buffered){var s=a.body;if(s.start<=t&&t<=s.end)return s}}return null},e.detectEvictedFragments=function(t,e,r,i){var n=this;this.timeRanges&&(this.timeRanges[t]=e);var a=(null==i?void 0:i.fragment.sn)||-1;Object.keys(this.fragments).forEach((function(i){var s=n.fragments[i];if(s&&!(a>=s.body.sn))if(s.buffered||s.loaded){var o=s.range[t];o&&o.time.some((function(t){var r=!n.isTimeBuffered(t.startPTS,t.endPTS,e);return r&&n.removeFragment(s.body),r}))}else s.body.type===r&&n.removeFragment(s.body)}))},e.detectPartialFragments=function(t){var e=this,r=this.timeRanges,i=t.frag,n=t.part;if(r&&"initSegment"!==i.sn){var a=qr(i),s=this.fragments[a];if(!(!s||s.buffered&&i.gap)){var o=!i.relurl;Object.keys(r).forEach((function(t){var a=i.elementaryStreams[t];if(a){var l=r[t],u=o||!0===a.partial;s.range[t]=e.getBufferedTimes(i,n,u,l)}})),s.loaded=null,Object.keys(s.range).length?(s.buffered=!0,(s.body.endList=i.endList||s.body.endList)&&(this.endListFragments[s.body.type]=s),jr(s)||this.removeParts(i.sn-1,i.type)):this.removeFragment(s.body)}}},e.removeParts=function(t,e){var r=this.activePartLists[e];r&&(this.activePartLists[e]=r.filter((function(e){return e.fragment.sn>=t})))},e.fragBuffered=function(t,e){var r=qr(t),i=this.fragments[r];!i&&e&&(i=this.fragments[r]={body:t,appendedPTS:null,loaded:null,buffered:!1,range:Object.create(null)},t.gap&&(this.hasGaps=!0)),i&&(i.loaded=null,i.buffered=!0)},e.getBufferedTimes=function(t,e,r,i){for(var n={time:[],partial:r},a=t.start,s=t.end,o=t.minEndPTS||s,l=t.maxStartPTS||a,u=0;u<i.length;u++){var h=i.start(u)-this.bufferPadding,d=i.end(u)+this.bufferPadding;if(l>=h&&o<=d){n.time.push({startPTS:Math.max(a,i.start(u)),endPTS:Math.min(s,i.end(u))});break}if(a<d&&s>h){var c=Math.max(a,i.start(u)),f=Math.min(s,i.end(u));f>c&&(n.partial=!0,n.time.push({startPTS:c,endPTS:f}))}else if(s<=h)break}return n},e.getPartialFragment=function(t){var e,r,i,n=null,a=0,s=this.bufferPadding,o=this.fragments;return Object.keys(o).forEach((function(l){var u=o[l];u&&jr(u)&&(r=u.body.start-s,i=u.body.end+s,t>=r&&t<=i&&(e=Math.min(t-r,i-t),a<=e&&(n=u.body,a=e)))})),n},e.isEndListAppended=function(t){var e=this.endListFragments[t];return void 0!==e&&(e.buffered||jr(e))},e.getState=function(t){var e=qr(t),r=this.fragments[e];return r?r.buffered?jr(r)?Vr:Yr:Hr:Kr},e.isTimeBuffered=function(t,e,r){for(var i,n,a=0;a<r.length;a++){if(i=r.start(a)-this.bufferPadding,n=r.end(a)+this.bufferPadding,t>=i&&e<=n)return!0;if(e<=i)return!1}return!1},e.onFragLoaded=function(t,e){var r=e.frag,i=e.part;if("initSegment"!==r.sn&&!r.bitrateTest){var n=i?null:e,a=qr(r);this.fragments[a]={body:r,appendedPTS:null,loaded:n,buffered:!1,range:Object.create(null)}}},e.onBufferAppended=function(t,e){var r=this,i=e.frag,n=e.part,a=e.timeRanges;if("initSegment"!==i.sn){var s=i.type;if(n){var o=this.activePartLists[s];o||(this.activePartLists[s]=o=[]),o.push(n)}this.timeRanges=a,Object.keys(a).forEach((function(t){var e=a[t];r.detectEvictedFragments(t,e,s,n)}))}},e.onFragBuffered=function(t,e){this.detectPartialFragments(e)},e.hasFragment=function(t){var e=qr(t);return!!this.fragments[e]},e.hasParts=function(t){var e;return!(null==(e=this.activePartLists[t])||!e.length)},e.removeFragmentsInRange=function(t,e,r,i,n){var a=this;i&&!this.hasGaps||Object.keys(this.fragments).forEach((function(s){var o=a.fragments[s];if(o){var l=o.body;l.type!==r||i&&!l.gap||l.start<e&&l.end>t&&(o.buffered||n)&&a.removeFragment(l)}}))},e.removeFragment=function(t){var e=qr(t);t.stats.loaded=0,t.clearElementaryStreamInfo();var r=this.activePartLists[t.type];if(r){var i=t.sn;this.activePartLists[t.type]=r.filter((function(t){return t.fragment.sn!==i}))}delete this.fragments[e],t.endList&&delete this.endListFragments[t.type]},e.removeAllFragments=function(){this.fragments=Object.create(null),this.endListFragments=Object.create(null),this.activePartLists=Object.create(null),this.hasGaps=!1},t}();function jr(t){var e,r,i;return t.buffered&&(t.body.gap||(null==(e=t.range.video)?void 0:e.partial)||(null==(r=t.range.audio)?void 0:r.partial)||(null==(i=t.range.audiovideo)?void 0:i.partial))}function qr(t){return t.type+"_"+t.level+"_"+t.sn}var Xr={length:0,start:function(){return 0},end:function(){return 0}},zr=function(){function t(){}return t.isBuffered=function(e,r){try{if(e)for(var i=t.getBuffered(e),n=0;n<i.length;n++)if(r>=i.start(n)&&r<=i.end(n))return!0}catch(t){}return!1},t.bufferInfo=function(e,r,i){try{if(e){var n,a=t.getBuffered(e),s=[];for(n=0;n<a.length;n++)s.push({start:a.start(n),end:a.end(n)});return this.bufferedInfo(s,r,i)}}catch(t){}return{len:0,start:r,end:r,nextStart:void 0}},t.bufferedInfo=function(t,e,r){e=Math.max(0,e),t.sort((function(t,e){var r=t.start-e.start;return r||e.end-t.end}));var i=[];if(r)for(var n=0;n<t.length;n++){var a=i.length;if(a){var s=i[a-1].end;t[n].start-s<r?t[n].end>s&&(i[a-1].end=t[n].end):i.push(t[n])}else i.push(t[n])}else i=t;for(var o,l=0,u=e,h=e,d=0;d<i.length;d++){var c=i[d].start,f=i[d].end;if(e+r>=c&&e<f)u=c,l=(h=f)-e;else if(e+r<c){o=c;break}}return{len:l,start:u||0,end:h||0,nextStart:o}},t.getBuffered=function(t){try{return t.buffered}catch(t){return w.log("failed to get media.buffered",t),Xr}},t}(),Qr=function(t,e,r,i,n,a){void 0===i&&(i=0),void 0===n&&(n=-1),void 0===a&&(a=!1),this.level=void 0,this.sn=void 0,this.part=void 0,this.id=void 0,this.size=void 0,this.partial=void 0,this.transmuxing={start:0,executeStart:0,executeEnd:0,end:0},this.buffering={audio:{start:0,executeStart:0,executeEnd:0,end:0},video:{start:0,executeStart:0,executeEnd:0,end:0},audiovideo:{start:0,executeStart:0,executeEnd:0,end:0}},this.level=t,this.sn=e,this.id=r,this.size=i,this.part=n,this.partial=a};function Jr(t,e){for(var r=0,i=t.length;r<i;r++){var n;if((null==(n=t[r])?void 0:n.cc)===e)return t[r]}return null}function $r(t,e){if(t){var r=t.start+e;t.start=t.startPTS=r,t.endPTS=r+t.duration}}function Zr(t,e){for(var r=e.fragments,i=0,n=r.length;i<n;i++)$r(r[i],t);e.fragmentHint&&$r(e.fragmentHint,t),e.alignedSliding=!0}function ti(t,e,r){e&&(function(t,e,r){if(function(t,e,r){return!(!e||!(r.endCC>r.startCC||t&&t.cc<r.startCC))}(t,r,e)){var i=function(t,e){var r=t.fragments,i=e.fragments;if(i.length&&r.length){var n=Jr(r,i[0].cc);if(n&&(!n||n.startPTS))return n;w.log("No frag in previous level to align on")}else w.log("No fragments to align")}(r,e);i&&y(i.start)&&(w.log("Adjusting PTS using last level due to CC increase within current level "+e.url),Zr(i.start,e))}}(t,r,e),!r.alignedSliding&&e&&ei(r,e),r.alignedSliding||!e||r.skippedSegments||ar(e,r))}function ei(t,e){if(t.hasProgramDateTime&&e.hasProgramDateTime){var r=t.fragments,i=e.fragments;if(r.length&&i.length){var n,a,s=Math.min(e.endCC,t.endCC);e.startCC<s&&t.startCC<s&&(n=Jr(i,s),a=Jr(r,s)),n&&a||(a=Jr(r,(n=i[Math.floor(i.length/2)]).cc)||r[Math.floor(r.length/2)]);var o=n.programDateTime,l=a.programDateTime;o&&l&&Zr((l-o)/1e3-(a.start-n.start),t)}}}var ri=Math.pow(2,17),ii=function(){function t(t){this.config=void 0,this.loader=null,this.partLoadTimeout=-1,this.config=t}var e=t.prototype;return e.destroy=function(){this.loader&&(this.loader.destroy(),this.loader=null)},e.abort=function(){this.loader&&this.loader.abort()},e.load=function(t,e){var r=this,n=t.url;if(!n)return Promise.reject(new si({type:L.NETWORK_ERROR,details:A.FRAG_LOAD_ERROR,fatal:!1,frag:t,error:new Error("Fragment does not have a "+(n?"part list":"url")),networkDetails:null}));this.abort();var a=this.config,s=a.fLoader,o=a.loader;return new Promise((function(l,u){if(r.loader&&r.loader.destroy(),t.gap){if(t.tagList.some((function(t){return"GAP"===t[0]})))return void u(ai(t));t.gap=!1}var h=r.loader=t.loader=s?new s(a):new o(a),d=ni(t),c=fr(a.fragLoadPolicy.default),f={loadPolicy:c,timeout:c.maxLoadTimeMs,maxRetry:0,retryDelay:0,maxRetryDelay:0,highWaterMark:"initSegment"===t.sn?1/0:ri};t.stats=h.stats,h.load(d,f,{onSuccess:function(e,i,n,a){r.resetLoader(t,h);var s=e.data;n.resetIV&&t.decryptdata&&(t.decryptdata.iv=new Uint8Array(s.slice(0,16)),s=s.slice(16)),l({frag:t,part:null,payload:s,networkDetails:a})},onError:function(e,a,s,o){r.resetLoader(t,h),u(new si({type:L.NETWORK_ERROR,details:A.FRAG_LOAD_ERROR,fatal:!1,frag:t,response:i({url:n,data:void 0},e),error:new Error("HTTP Error "+e.code+" "+e.text),networkDetails:s,stats:o}))},onAbort:function(e,i,n){r.resetLoader(t,h),u(new si({type:L.NETWORK_ERROR,details:A.INTERNAL_ABORTED,fatal:!1,frag:t,error:new Error("Aborted"),networkDetails:n,stats:e}))},onTimeout:function(e,i,n){r.resetLoader(t,h),u(new si({type:L.NETWORK_ERROR,details:A.FRAG_LOAD_TIMEOUT,fatal:!1,frag:t,error:new Error("Timeout after "+f.timeout+"ms"),networkDetails:n,stats:e}))},onProgress:function(r,i,n,a){e&&e({frag:t,part:null,payload:n,networkDetails:a})}})}))},e.loadPart=function(t,e,r){var n=this;this.abort();var a=this.config,s=a.fLoader,o=a.loader;return new Promise((function(l,u){if(n.loader&&n.loader.destroy(),t.gap||e.gap)u(ai(t,e));else{var h=n.loader=t.loader=s?new s(a):new o(a),d=ni(t,e),c=fr(a.fragLoadPolicy.default),f={loadPolicy:c,timeout:c.maxLoadTimeMs,maxRetry:0,retryDelay:0,maxRetryDelay:0,highWaterMark:ri};e.stats=h.stats,h.load(d,f,{onSuccess:function(i,a,s,o){n.resetLoader(t,h),n.updateStatsFromPart(t,e);var u={frag:t,part:e,payload:i.data,networkDetails:o};r(u),l(u)},onError:function(r,a,s,o){n.resetLoader(t,h),u(new si({type:L.NETWORK_ERROR,details:A.FRAG_LOAD_ERROR,fatal:!1,frag:t,part:e,response:i({url:d.url,data:void 0},r),error:new Error("HTTP Error "+r.code+" "+r.text),networkDetails:s,stats:o}))},onAbort:function(r,i,a){t.stats.aborted=e.stats.aborted,n.resetLoader(t,h),u(new si({type:L.NETWORK_ERROR,details:A.INTERNAL_ABORTED,fatal:!1,frag:t,part:e,error:new Error("Aborted"),networkDetails:a,stats:r}))},onTimeout:function(r,i,a){n.resetLoader(t,h),u(new si({type:L.NETWORK_ERROR,details:A.FRAG_LOAD_TIMEOUT,fatal:!1,frag:t,part:e,error:new Error("Timeout after "+f.timeout+"ms"),networkDetails:a,stats:r}))}})}}))},e.updateStatsFromPart=function(t,e){var r=t.stats,i=e.stats,n=i.total;if(r.loaded+=i.loaded,n){var a=Math.round(t.duration/e.duration),s=Math.min(Math.round(r.loaded/n),a),o=(a-s)*Math.round(r.loaded/s);r.total=r.loaded+o}else r.total=Math.max(r.loaded,r.total);var l=r.loading,u=i.loading;l.start?l.first+=u.first-u.start:(l.start=u.start,l.first=u.first),l.end=u.end},e.resetLoader=function(t,e){t.loader=null,this.loader===e&&(self.clearTimeout(this.partLoadTimeout),this.loader=null),e.destroy()},t}();function ni(t,e){void 0===e&&(e=null);var r=e||t,i={frag:t,part:e,responseType:"arraybuffer",url:r.url,headers:{},rangeStart:0,rangeEnd:0},n=r.byteRangeStartOffset,a=r.byteRangeEndOffset;if(y(n)&&y(a)){var s,o=n,l=a;if("initSegment"===t.sn&&"AES-128"===(null==(s=t.decryptdata)?void 0:s.method)){var u=a-n;u%16&&(l=a+(16-u%16)),0!==n&&(i.resetIV=!0,o=n-16)}i.rangeStart=o,i.rangeEnd=l}return i}function ai(t,e){var r=new Error("GAP "+(t.gap?"tag":"attribute")+" found"),i={type:L.MEDIA_ERROR,details:A.FRAG_GAP,fatal:!1,frag:t,error:r,networkDetails:null};return e&&(i.part=e),(e||t).stats.aborted=!0,new si(i)}var si=function(t){function e(e){var r;return(r=t.call(this,e.error.message)||this).data=void 0,r.data=e,r}return l(e,t),e}(c(Error)),oi=function(){function t(t,e){this.subtle=void 0,this.aesIV=void 0,this.subtle=t,this.aesIV=e}return t.prototype.decrypt=function(t,e){return this.subtle.decrypt({name:"AES-CBC",iv:this.aesIV},e,t)},t}(),li=function(){function t(t,e){this.subtle=void 0,this.key=void 0,this.subtle=t,this.key=e}return t.prototype.expandKey=function(){return this.subtle.importKey("raw",this.key,{name:"AES-CBC"},!1,["encrypt","decrypt"])},t}(),ui=function(){function t(){this.rcon=[0,1,2,4,8,16,32,64,128,27,54],this.subMix=[new Uint32Array(256),new Uint32Array(256),new Uint32Array(256),new Uint32Array(256)],this.invSubMix=[new Uint32Array(256),new Uint32Array(256),new Uint32Array(256),new Uint32Array(256)],this.sBox=new Uint32Array(256),this.invSBox=new Uint32Array(256),this.key=new Uint32Array(0),this.ksRows=0,this.keySize=0,this.keySchedule=void 0,this.invKeySchedule=void 0,this.initTable()}var e=t.prototype;return e.uint8ArrayToUint32Array_=function(t){for(var e=new DataView(t),r=new Uint32Array(4),i=0;i<4;i++)r[i]=e.getUint32(4*i);return r},e.initTable=function(){var t=this.sBox,e=this.invSBox,r=this.subMix,i=r[0],n=r[1],a=r[2],s=r[3],o=this.invSubMix,l=o[0],u=o[1],h=o[2],d=o[3],c=new Uint32Array(256),f=0,g=0,v=0;for(v=0;v<256;v++)c[v]=v<128?v<<1:v<<1^283;for(v=0;v<256;v++){var m=g^g<<1^g<<2^g<<3^g<<4;m=m>>>8^255&m^99,t[f]=m,e[m]=f;var p=c[f],y=c[p],E=c[y],T=257*c[m]^16843008*m;i[f]=T<<24|T>>>8,n[f]=T<<16|T>>>16,a[f]=T<<8|T>>>24,s[f]=T,T=16843009*E^65537*y^257*p^16843008*f,l[m]=T<<24|T>>>8,u[m]=T<<16|T>>>16,h[m]=T<<8|T>>>24,d[m]=T,f?(f=p^c[c[c[E^p]]],g^=c[c[g]]):f=g=1}},e.expandKey=function(t){for(var e=this.uint8ArrayToUint32Array_(t),r=!0,i=0;i<e.length&&r;)r=e[i]===this.key[i],i++;if(!r){this.key=e;var n=this.keySize=e.length;if(4!==n&&6!==n&&8!==n)throw new Error("Invalid aes key size="+n);var a,s,o,l,u=this.ksRows=4*(n+6+1),h=this.keySchedule=new Uint32Array(u),d=this.invKeySchedule=new Uint32Array(u),c=this.sBox,f=this.rcon,g=this.invSubMix,v=g[0],m=g[1],p=g[2],y=g[3];for(a=0;a<u;a++)a<n?o=h[a]=e[a]:(l=o,a%n==0?(l=c[(l=l<<8|l>>>24)>>>24]<<24|c[l>>>16&255]<<16|c[l>>>8&255]<<8|c[255&l],l^=f[a/n|0]<<24):n>6&&a%n==4&&(l=c[l>>>24]<<24|c[l>>>16&255]<<16|c[l>>>8&255]<<8|c[255&l]),h[a]=o=(h[a-n]^l)>>>0);for(s=0;s<u;s++)a=u-s,l=3&s?h[a]:h[a-4],d[s]=s<4||a<=4?l:v[c[l>>>24]]^m[c[l>>>16&255]]^p[c[l>>>8&255]]^y[c[255&l]],d[s]=d[s]>>>0}},e.networkToHostOrderSwap=function(t){return t<<24|(65280&t)<<8|(16711680&t)>>8|t>>>24},e.decrypt=function(t,e,r){for(var i,n,a,s,o,l,u,h,d,c,f,g,v,m,p=this.keySize+6,y=this.invKeySchedule,E=this.invSBox,T=this.invSubMix,S=T[0],L=T[1],A=T[2],R=T[3],k=this.uint8ArrayToUint32Array_(r),b=k[0],D=k[1],I=k[2],w=k[3],C=new Int32Array(t),_=new Int32Array(C.length),x=this.networkToHostOrderSwap;e<C.length;){for(d=x(C[e]),c=x(C[e+1]),f=x(C[e+2]),g=x(C[e+3]),o=d^y[0],l=g^y[1],u=f^y[2],h=c^y[3],v=4,m=1;m<p;m++)i=S[o>>>24]^L[l>>16&255]^A[u>>8&255]^R[255&h]^y[v],n=S[l>>>24]^L[u>>16&255]^A[h>>8&255]^R[255&o]^y[v+1],a=S[u>>>24]^L[h>>16&255]^A[o>>8&255]^R[255&l]^y[v+2],s=S[h>>>24]^L[o>>16&255]^A[l>>8&255]^R[255&u]^y[v+3],o=i,l=n,u=a,h=s,v+=4;i=E[o>>>24]<<24^E[l>>16&255]<<16^E[u>>8&255]<<8^E[255&h]^y[v],n=E[l>>>24]<<24^E[u>>16&255]<<16^E[h>>8&255]<<8^E[255&o]^y[v+1],a=E[u>>>24]<<24^E[h>>16&255]<<16^E[o>>8&255]<<8^E[255&l]^y[v+2],s=E[h>>>24]<<24^E[o>>16&255]<<16^E[l>>8&255]<<8^E[255&u]^y[v+3],_[e]=x(i^b),_[e+1]=x(s^D),_[e+2]=x(a^I),_[e+3]=x(n^w),b=d,D=c,I=f,w=g,e+=4}return _.buffer},t}(),hi=function(){function t(t,e){var r=(void 0===e?{}:e).removePKCS7Padding,i=void 0===r||r;if(this.logEnabled=!0,this.removePKCS7Padding=void 0,this.subtle=null,this.softwareDecrypter=null,this.key=null,this.fastAesKey=null,this.remainderData=null,this.currentIV=null,this.currentResult=null,this.useSoftware=void 0,this.useSoftware=t.enableSoftwareAES,this.removePKCS7Padding=i,i)try{var n=self.crypto;n&&(this.subtle=n.subtle||n.webkitSubtle)}catch(t){}null===this.subtle&&(this.useSoftware=!0)}var e=t.prototype;return e.destroy=function(){this.subtle=null,this.softwareDecrypter=null,this.key=null,this.fastAesKey=null,this.remainderData=null,this.currentIV=null,this.currentResult=null},e.isSync=function(){return this.useSoftware},e.flush=function(){var t=this.currentResult,e=this.remainderData;if(!t||e)return this.reset(),null;var r,i,n,a=new Uint8Array(t);return this.reset(),this.removePKCS7Padding?(i=(r=a).byteLength,(n=i&&new DataView(r.buffer).getUint8(i-1))?nt(r,0,i-n):r):a},e.reset=function(){this.currentResult=null,this.currentIV=null,this.remainderData=null,this.softwareDecrypter&&(this.softwareDecrypter=null)},e.decrypt=function(t,e,r){var i=this;return this.useSoftware?new Promise((function(n,a){i.softwareDecrypt(new Uint8Array(t),e,r);var s=i.flush();s?n(s.buffer):a(new Error("[softwareDecrypt] Failed to decrypt data"))})):this.webCryptoDecrypt(new Uint8Array(t),e,r)},e.softwareDecrypt=function(t,e,r){var i=this.currentIV,n=this.currentResult,a=this.remainderData;this.logOnce("JS AES decrypt"),a&&(t=Gt(a,t),this.remainderData=null);var s=this.getValidChunk(t);if(!s.length)return null;i&&(r=i);var o=this.softwareDecrypter;o||(o=this.softwareDecrypter=new ui),o.expandKey(e);var l=n;return this.currentResult=o.decrypt(s.buffer,0,r),this.currentIV=nt(s,-16).buffer,l||null},e.webCryptoDecrypt=function(t,e,r){var i=this,n=this.subtle;return this.key===e&&this.fastAesKey||(this.key=e,this.fastAesKey=new li(n,e)),this.fastAesKey.expandKey().then((function(e){return n?(i.logOnce("WebCrypto AES decrypt"),new oi(n,new Uint8Array(r)).decrypt(t.buffer,e)):Promise.reject(new Error("web crypto not initialized"))})).catch((function(n){return w.warn("[decrypter]: WebCrypto Error, disable WebCrypto API, "+n.name+": "+n.message),i.onWebCryptoError(t,e,r)}))},e.onWebCryptoError=function(t,e,r){this.useSoftware=!0,this.logEnabled=!0,this.softwareDecrypt(t,e,r);var i=this.flush();if(i)return i.buffer;throw new Error("WebCrypto and softwareDecrypt: failed to decrypt data")},e.getValidChunk=function(t){var e=t,r=t.length-t.length%16;return r!==t.length&&(e=nt(t,0,r),this.remainderData=nt(t,r)),e},e.logOnce=function(t){this.logEnabled&&(w.log("[decrypter]: "+t),this.logEnabled=!1)},t}(),di=function(t){for(var e="",r=t.length,i=0;i<r;i++)e+="["+t.start(i).toFixed(3)+"-"+t.end(i).toFixed(3)+"]";return e},ci="STOPPED",fi="IDLE",gi="KEY_LOADING",vi="FRAG_LOADING",mi="FRAG_LOADING_WAITING_RETRY",pi="WAITING_TRACK",yi="PARSING",Ei="PARSED",Ti="ENDED",Si="ERROR",Li="WAITING_INIT_PTS",Ai="WAITING_LEVEL",Ri=function(t){function e(e,r,i,n,a){var s;return(s=t.call(this)||this).hls=void 0,s.fragPrevious=null,s.fragCurrent=null,s.fragmentTracker=void 0,s.transmuxer=null,s._state=ci,s.playlistType=void 0,s.media=null,s.mediaBuffer=null,s.config=void 0,s.bitrateTest=!1,s.lastCurrentTime=0,s.nextLoadPosition=0,s.startPosition=0,s.startTimeOffset=null,s.loadedmetadata=!1,s.retryDate=0,s.levels=null,s.fragmentLoader=void 0,s.keyLoader=void 0,s.levelLastLoaded=null,s.startFragRequested=!1,s.decrypter=void 0,s.initPTS=[],s.onvseeking=null,s.onvended=null,s.logPrefix="",s.log=void 0,s.warn=void 0,s.playlistType=a,s.logPrefix=n,s.log=w.log.bind(w,n+":"),s.warn=w.warn.bind(w,n+":"),s.hls=e,s.fragmentLoader=new ii(e.config),s.keyLoader=i,s.fragmentTracker=r,s.config=e.config,s.decrypter=new hi(e.config),e.on(S.MANIFEST_LOADED,s.onManifestLoaded,function(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}(s)),s}l(e,t);var r=e.prototype;return r.doTick=function(){this.onTickEnd()},r.onTickEnd=function(){},r.startLoad=function(t){},r.stopLoad=function(){this.fragmentLoader.abort(),this.keyLoader.abort(this.playlistType);var t=this.fragCurrent;null!=t&&t.loader&&(t.abortRequests(),this.fragmentTracker.removeFragment(t)),this.resetTransmuxer(),this.fragCurrent=null,this.fragPrevious=null,this.clearInterval(),this.clearNextTick(),this.state=ci},r._streamEnded=function(t,e){if(e.live||t.nextStart||!t.end||!this.media)return!1;var r=e.partList;if(null!=r&&r.length){var i=r[r.length-1];return zr.isBuffered(this.media,i.start+i.duration/2)}var n=e.fragments[e.fragments.length-1].type;return this.fragmentTracker.isEndListAppended(n)},r.getLevelDetails=function(){var t;if(this.levels&&null!==this.levelLastLoaded)return null==(t=this.levelLastLoaded)?void 0:t.details},r.onMediaAttached=function(t,e){var r=this.media=this.mediaBuffer=e.media;this.onvseeking=this.onMediaSeeking.bind(this),this.onvended=this.onMediaEnded.bind(this),r.addEventListener("seeking",this.onvseeking),r.addEventListener("ended",this.onvended);var i=this.config;this.levels&&i.autoStartLoad&&this.state===ci&&this.startLoad(i.startPosition)},r.onMediaDetaching=function(){var t=this.media;null!=t&&t.ended&&(this.log("MSE detaching and video ended, reset startPosition"),this.startPosition=this.lastCurrentTime=0),t&&this.onvseeking&&this.onvended&&(t.removeEventListener("seeking",this.onvseeking),t.removeEventListener("ended",this.onvended),this.onvseeking=this.onvended=null),this.keyLoader&&this.keyLoader.detach(),this.media=this.mediaBuffer=null,this.loadedmetadata=!1,this.fragmentTracker.removeAllFragments(),this.stopLoad()},r.onMediaSeeking=function(){var t=this.config,e=this.fragCurrent,r=this.media,i=this.mediaBuffer,n=this.state,a=r?r.currentTime:0,s=zr.bufferInfo(i||r,a,t.maxBufferHole);if(this.log("media seeking to "+(y(a)?a.toFixed(3):a)+", state: "+n),this.state===Ti)this.resetLoadingState();else if(e){var o=t.maxFragLookUpTolerance,l=e.start-o,u=e.start+e.duration+o;if(!s.len||u<s.start||l>s.end){var h=a>u;(a<l||h)&&(h&&e.loader&&(this.log("seeking outside of buffer while fragment load in progress, cancel fragment load"),e.abortRequests(),this.resetLoadingState()),this.fragPrevious=null)}}r&&(this.fragmentTracker.removeFragmentsInRange(a,1/0,this.playlistType,!0),this.lastCurrentTime=a),this.loadedmetadata||s.len||(this.nextLoadPosition=this.startPosition=a),this.tickImmediate()},r.onMediaEnded=function(){this.startPosition=this.lastCurrentTime=0},r.onManifestLoaded=function(t,e){this.startTimeOffset=e.startTimeOffset,this.initPTS=[]},r.onHandlerDestroying=function(){this.hls.off(S.MANIFEST_LOADED,this.onManifestLoaded,this),this.stopLoad(),t.prototype.onHandlerDestroying.call(this),this.hls=null},r.onHandlerDestroyed=function(){this.state=ci,this.fragmentLoader&&this.fragmentLoader.destroy(),this.keyLoader&&this.keyLoader.destroy(),this.decrypter&&this.decrypter.destroy(),this.hls=this.log=this.warn=this.decrypter=this.keyLoader=this.fragmentLoader=this.fragmentTracker=null,t.prototype.onHandlerDestroyed.call(this)},r.loadFragment=function(t,e,r){this._loadFragForPlayback(t,e,r)},r._loadFragForPlayback=function(t,e,r){var i=this;this._doFragLoad(t,e,r,(function(e){if(i.fragContextChanged(t))return i.warn("Fragment "+t.sn+(e.part?" p: "+e.part.index:"")+" of level "+t.level+" was dropped during download."),void i.fragmentTracker.removeFragment(t);t.stats.chunkCount++,i._handleFragmentLoadProgress(e)})).then((function(e){if(e){var r=i.state;i.fragContextChanged(t)?(r===vi||!i.fragCurrent&&r===yi)&&(i.fragmentTracker.removeFragment(t),i.state=fi):("payload"in e&&(i.log("Loaded fragment "+t.sn+" of level "+t.level),i.hls.trigger(S.FRAG_LOADED,e)),i._handleFragmentLoadComplete(e))}})).catch((function(e){i.state!==ci&&i.state!==Si&&(i.warn(e),i.resetFragmentLoading(t))}))},r.clearTrackerIfNeeded=function(t){var e,r=this.fragmentTracker;if(r.getState(t)===Hr){var i=t.type,n=this.getFwdBufferInfo(this.mediaBuffer,i),a=Math.max(t.duration,n?n.len:this.config.maxBufferLength);this.reduceMaxBufferLength(a)&&r.removeFragment(t)}else 0===(null==(e=this.mediaBuffer)?void 0:e.buffered.length)?r.removeAllFragments():r.hasParts(t.type)&&(r.detectPartialFragments({frag:t,part:null,stats:t.stats,id:t.type}),r.getState(t)===Vr&&r.removeFragment(t))},r.checkLiveUpdate=function(t){if(t.updated&&!t.live){var e=t.fragments[t.fragments.length-1];this.fragmentTracker.detectPartialFragments({frag:e,part:null,stats:e.stats,id:e.type})}t.fragments[0]||(t.deltaUpdateFailed=!0)},r.flushMainBuffer=function(t,e,r){if(void 0===r&&(r=null),t-e){var i={startOffset:t,endOffset:e,type:r};this.hls.trigger(S.BUFFER_FLUSHING,i)}},r._loadInitSegment=function(t,e){var r=this;this._doFragLoad(t,e).then((function(e){if(!e||r.fragContextChanged(t)||!r.levels)throw new Error("init load aborted");return e})).then((function(e){var i=r.hls,n=e.payload,a=t.decryptdata;if(n&&n.byteLength>0&&null!=a&&a.key&&a.iv&&"AES-128"===a.method){var s=self.performance.now();return r.decrypter.decrypt(new Uint8Array(n),a.key.buffer,a.iv.buffer).catch((function(e){throw i.trigger(S.ERROR,{type:L.MEDIA_ERROR,details:A.FRAG_DECRYPT_ERROR,fatal:!1,error:e,reason:e.message,frag:t}),e})).then((function(n){var a=self.performance.now();return i.trigger(S.FRAG_DECRYPTED,{frag:t,payload:n,stats:{tstart:s,tdecrypt:a}}),e.payload=n,r.completeInitSegmentLoad(e)}))}return r.completeInitSegmentLoad(e)})).catch((function(e){r.state!==ci&&r.state!==Si&&(r.warn(e),r.resetFragmentLoading(t))}))},r.completeInitSegmentLoad=function(t){if(!this.levels)throw new Error("init load aborted, missing levels");var e=t.frag.stats;this.state=fi,t.frag.data=new Uint8Array(t.payload),e.parsing.start=e.buffering.start=self.performance.now(),e.parsing.end=e.buffering.end=self.performance.now(),this.tick()},r.fragContextChanged=function(t){var e=this.fragCurrent;return!t||!e||t.sn!==e.sn||t.level!==e.level},r.fragBufferedComplete=function(t,e){var r,i,n,a,s=this.mediaBuffer?this.mediaBuffer:this.media;if(this.log("Buffered "+t.type+" sn: "+t.sn+(e?" part: "+e.index:"")+" of "+(this.playlistType===Ie?"level":"track")+" "+t.level+" (frag:["+(null!=(r=t.startPTS)?r:NaN).toFixed(3)+"-"+(null!=(i=t.endPTS)?i:NaN).toFixed(3)+"] > buffer:"+(s?di(zr.getBuffered(s)):"(detached)")+")"),"initSegment"!==t.sn){var o;if(t.type!==Ce){var l=t.elementaryStreams;if(!Object.keys(l).some((function(t){return!!l[t]})))return void(this.state=fi)}var u=null==(o=this.levels)?void 0:o[t.level];null!=u&&u.fragmentError&&(this.log("Resetting level fragment error count of "+u.fragmentError+" on frag buffered"),u.fragmentError=0)}this.state=fi,s&&(!this.loadedmetadata&&t.type==Ie&&s.buffered.length&&(null==(n=this.fragCurrent)?void 0:n.sn)===(null==(a=this.fragPrevious)?void 0:a.sn)&&(this.loadedmetadata=!0,this.seekToStartPos()),this.tick())},r.seekToStartPos=function(){},r._handleFragmentLoadComplete=function(t){var e=this.transmuxer;if(e){var r=t.frag,i=t.part,n=t.partsLoaded,a=!n||0===n.length||n.some((function(t){return!t})),s=new Qr(r.level,r.sn,r.stats.chunkCount+1,0,i?i.index:-1,!a);e.flush(s)}},r._handleFragmentLoadProgress=function(t){},r._doFragLoad=function(t,e,r,i){var n,a=this;void 0===r&&(r=null);var s=null==e?void 0:e.details;if(!this.levels||!s)throw new Error("frag load aborted, missing level"+(s?"":" detail")+"s");var o=null;if(!t.encrypted||null!=(n=t.decryptdata)&&n.key?!t.encrypted&&s.encryptedFragments.length&&this.keyLoader.loadClear(t,s.encryptedFragments):(this.log("Loading key for "+t.sn+" of ["+s.startSN+"-"+s.endSN+"], "+("[stream-controller]"===this.logPrefix?"level":"track")+" "+t.level),this.state=gi,this.fragCurrent=t,o=this.keyLoader.load(t).then((function(t){if(!a.fragContextChanged(t.frag))return a.hls.trigger(S.KEY_LOADED,t),a.state===gi&&(a.state=fi),t})),this.hls.trigger(S.KEY_LOADING,{frag:t}),null===this.fragCurrent&&(o=Promise.reject(new Error("frag load aborted, context changed in KEY_LOADING")))),r=Math.max(t.start,r||0),this.config.lowLatencyMode&&"initSegment"!==t.sn){var l=s.partList;if(l&&i){r>t.end&&s.fragmentHint&&(t=s.fragmentHint);var u=this.getNextPart(l,t,r);if(u>-1){var h,d=l[u];return this.log("Loading part sn: "+t.sn+" p: "+d.index+" cc: "+t.cc+" of playlist ["+s.startSN+"-"+s.endSN+"] parts [0-"+u+"-"+(l.length-1)+"] "+("[stream-controller]"===this.logPrefix?"level":"track")+": "+t.level+", target: "+parseFloat(r.toFixed(3))),this.nextLoadPosition=d.start+d.duration,this.state=vi,h=o?o.then((function(r){return!r||a.fragContextChanged(r.frag)?null:a.doFragPartsLoad(t,d,e,i)})).catch((function(t){return a.handleFragLoadError(t)})):this.doFragPartsLoad(t,d,e,i).catch((function(t){return a.handleFragLoadError(t)})),this.hls.trigger(S.FRAG_LOADING,{frag:t,part:d,targetBufferTime:r}),null===this.fragCurrent?Promise.reject(new Error("frag load aborted, context changed in FRAG_LOADING parts")):h}if(!t.url||this.loadedEndOfParts(l,r))return Promise.resolve(null)}}this.log("Loading fragment "+t.sn+" cc: "+t.cc+" "+(s?"of ["+s.startSN+"-"+s.endSN+"] ":"")+("[stream-controller]"===this.logPrefix?"level":"track")+": "+t.level+", target: "+parseFloat(r.toFixed(3))),y(t.sn)&&!this.bitrateTest&&(this.nextLoadPosition=t.start+t.duration),this.state=vi;var c,f=this.config.progressive;return c=f&&o?o.then((function(e){return!e||a.fragContextChanged(null==e?void 0:e.frag)?null:a.fragmentLoader.load(t,i)})).catch((function(t){return a.handleFragLoadError(t)})):Promise.all([this.fragmentLoader.load(t,f?i:void 0),o]).then((function(t){var e=t[0];return!f&&e&&i&&i(e),e})).catch((function(t){return a.handleFragLoadError(t)})),this.hls.trigger(S.FRAG_LOADING,{frag:t,targetBufferTime:r}),null===this.fragCurrent?Promise.reject(new Error("frag load aborted, context changed in FRAG_LOADING")):c},r.doFragPartsLoad=function(t,e,r,i){var n=this;return new Promise((function(a,s){var o,l=[],u=null==(o=r.details)?void 0:o.partList;!function e(o){n.fragmentLoader.loadPart(t,o,i).then((function(i){l[o.index]=i;var s=i.part;n.hls.trigger(S.FRAG_LOADED,i);var h=or(r,t.sn,o.index+1)||lr(u,t.sn,o.index+1);if(!h)return a({frag:t,part:s,partsLoaded:l});e(h)})).catch(s)}(e)}))},r.handleFragLoadError=function(t){if("data"in t){var e=t.data;t.data&&e.details===A.INTERNAL_ABORTED?this.handleFragLoadAborted(e.frag,e.part):this.hls.trigger(S.ERROR,e)}else this.hls.trigger(S.ERROR,{type:L.OTHER_ERROR,details:A.INTERNAL_EXCEPTION,err:t,error:t,fatal:!0});return null},r._handleTransmuxerFlush=function(t){var e=this.getCurrentContext(t);if(e&&this.state===yi){var r=e.frag,i=e.part,n=e.level,a=self.performance.now();r.stats.parsing.end=a,i&&(i.stats.parsing.end=a),this.updateLevelTiming(r,i,n,t.partial)}else this.fragCurrent||this.state===ci||this.state===Si||(this.state=fi)},r.getCurrentContext=function(t){var e=this.levels,r=this.fragCurrent,i=t.level,n=t.sn,a=t.part;if(null==e||!e[i])return this.warn("Levels object was unset while buffering fragment "+n+" of level "+i+". The current chunk will not be buffered."),null;var s=e[i],o=a>-1?or(s,n,a):null,l=o?o.fragment:function(t,e,r){if(null==t||!t.details)return null;var i=t.details,n=i.fragments[e-i.startSN];return n||((n=i.fragmentHint)&&n.sn===e?n:e<i.startSN&&r&&r.sn===e?r:null)}(s,n,r);return l?(r&&r!==l&&(l.stats=r.stats),{frag:l,part:o,level:s}):null},r.bufferFragmentData=function(t,e,r,i,n){var a;if(t&&this.state===yi){var s=t.data1,o=t.data2,l=s;if(s&&o&&(l=Gt(s,o)),null!=(a=l)&&a.length){var u={type:t.type,frag:e,part:r,chunkMeta:i,parent:e.type,data:l};if(this.hls.trigger(S.BUFFER_APPENDING,u),t.dropped&&t.independent&&!r){if(n)return;this.flushBufferGap(e)}}}},r.flushBufferGap=function(t){var e=this.media;if(e)if(zr.isBuffered(e,e.currentTime)){var r=e.currentTime,i=zr.bufferInfo(e,r,0),n=t.duration,a=Math.min(2*this.config.maxFragLookUpTolerance,.25*n),s=Math.max(Math.min(t.start-a,i.end-a),r+a);t.start-s>a&&this.flushMainBuffer(s,t.start)}else this.flushMainBuffer(0,t.start)},r.getFwdBufferInfo=function(t,e){var r=this.getLoadPosition();return y(r)?this.getFwdBufferInfoAtPos(t,r,e):null},r.getFwdBufferInfoAtPos=function(t,e,r){var i=this.config.maxBufferHole,n=zr.bufferInfo(t,e,i);if(0===n.len&&void 0!==n.nextStart){var a=this.fragmentTracker.getBufferedFrag(e,r);if(a&&n.nextStart<a.end)return zr.bufferInfo(t,e,Math.max(n.nextStart,i))}return n},r.getMaxBufferLength=function(t){var e,r=this.config;return e=t?Math.max(8*r.maxBufferSize/t,r.maxBufferLength):r.maxBufferLength,Math.min(e,r.maxMaxBufferLength)},r.reduceMaxBufferLength=function(t){var e=this.config,r=t||e.maxBufferLength;return e.maxMaxBufferLength>=r&&(e.maxMaxBufferLength/=2,this.warn("Reduce max buffer length to "+e.maxMaxBufferLength+"s"),!0)},r.getAppendedFrag=function(t,e){var r=this.fragmentTracker.getAppendedFrag(t,Ie);return r&&"fragment"in r?r.fragment:r},r.getNextFragment=function(t,e){var r=e.fragments,i=r.length;if(!i)return null;var n,a=this.config,s=r[0].start;if(e.live){var o=a.initialLiveManifestSize;if(i<o)return this.warn("Not enough fragments to start playback (have: "+i+", need: "+o+")"),null;(!e.PTSKnown&&!this.startFragRequested&&-1===this.startPosition||t<s)&&(n=this.getInitialLiveFragment(e,r),this.startPosition=this.nextLoadPosition=n?this.hls.liveSyncPosition||n.start:t)}else t<=s&&(n=r[0]);if(!n){var l=a.lowLatencyMode?e.partEnd:e.fragmentEnd;n=this.getFragmentAtPosition(t,l,e)}return this.mapToInitFragWhenRequired(n)},r.isLoopLoading=function(t,e){var r=this.fragmentTracker.getState(t);return(r===Yr||r===Vr&&!!t.gap)&&this.nextLoadPosition>e},r.getNextFragmentLoopLoading=function(t,e,r,i,n){var a=t.gap,s=this.getNextFragment(this.nextLoadPosition,e);if(null===s)return s;if(t=s,a&&t&&!t.gap&&r.nextStart){var o=this.getFwdBufferInfoAtPos(this.mediaBuffer?this.mediaBuffer:this.media,r.nextStart,i);if(null!==o&&r.len+o.len>=n)return this.log('buffer full after gaps in "'+i+'" playlist starting at sn: '+t.sn),null}return t},r.mapToInitFragWhenRequired=function(t){return null==t||!t.initSegment||null!=t&&t.initSegment.data||this.bitrateTest?t:t.initSegment},r.getNextPart=function(t,e,r){for(var i=-1,n=!1,a=!0,s=0,o=t.length;s<o;s++){var l=t[s];if(a=a&&!l.independent,i>-1&&r<l.start)break;var u=l.loaded;u?i=-1:(n||l.independent||a)&&l.fragment===e&&(i=s),n=u}return i},r.loadedEndOfParts=function(t,e){var r=t[t.length-1];return r&&e>r.start&&r.loaded},r.getInitialLiveFragment=function(t,e){var r=this.fragPrevious,i=null;if(r){if(t.hasProgramDateTime&&(this.log("Live playlist, switching playlist, load frag with same PDT: "+r.programDateTime),i=function(t,e,r){if(null===e||!Array.isArray(t)||!t.length||!y(e))return null;if(e<(t[0].programDateTime||0))return null;if(e>=(t[t.length-1].endProgramDateTime||0))return null;r=r||0;for(var i=0;i<t.length;++i){var n=t[i];if(yr(e,r,n))return n}return null}(e,r.endProgramDateTime,this.config.maxFragLookUpTolerance)),!i){var n=r.sn+1;if(n>=t.startSN&&n<=t.endSN){var a=e[n-t.startSN];r.cc===a.cc&&(i=a,this.log("Live playlist, switching playlist, load frag with next SN: "+i.sn))}i||(i=function(t,e){return vr(t,(function(t){return t.cc<e?1:t.cc>e?-1:0}))}(e,r.cc),i&&this.log("Live playlist, switching playlist, load frag with same CC: "+i.sn))}}else{var s=this.hls.liveSyncPosition;null!==s&&(i=this.getFragmentAtPosition(s,this.bitrateTest?t.fragmentEnd:t.edge,t))}return i},r.getFragmentAtPosition=function(t,e,r){var i,n=this.config,a=this.fragPrevious,s=r.fragments,o=r.endSN,l=r.fragmentHint,u=n.maxFragLookUpTolerance,h=r.partList,d=!!(n.lowLatencyMode&&null!=h&&h.length&&l);if(d&&l&&!this.bitrateTest&&(s=s.concat(l),o=l.sn),i=t<e?mr(a,s,t,t>e-u?0:u):s[s.length-1]){var c=i.sn-r.startSN,f=this.fragmentTracker.getState(i);if((f===Yr||f===Vr&&i.gap)&&(a=i),a&&i.sn===a.sn&&(!d||h[0].fragment.sn>i.sn)&&a&&i.level===a.level){var g=s[c+1];i=i.sn<o&&this.fragmentTracker.getState(g)!==Yr?g:null}}return i},r.synchronizeToLiveEdge=function(t){var e=this.config,r=this.media;if(r){var i=this.hls.liveSyncPosition,n=r.currentTime,a=t.fragments[0].start,s=t.edge,o=n>=a-e.maxFragLookUpTolerance&&n<=s;if(null!==i&&r.duration>i&&(n<i||!o)){var l=void 0!==e.liveMaxLatencyDuration?e.liveMaxLatencyDuration:e.liveMaxLatencyDurationCount*t.targetduration;(!o&&r.readyState<4||n<s-l)&&(this.loadedmetadata||(this.nextLoadPosition=i),r.readyState&&(this.warn("Playback: "+n.toFixed(3)+" is located too far from the end of live sliding playlist: "+s+", reset currentTime to : "+i.toFixed(3)),r.currentTime=i))}}},r.alignPlaylists=function(t,e,r){var i=t.fragments.length;if(!i)return this.warn("No fragments in live playlist"),0;var n=t.fragments[0].start,a=!e,s=t.alignedSliding&&y(n);if(a||!s&&!n){var o=this.fragPrevious;ti(o,r,t);var l=t.fragments[0].start;return this.log("Live playlist sliding: "+l.toFixed(2)+" start-sn: "+(e?e.startSN:"na")+"->"+t.startSN+" prev-sn: "+(o?o.sn:"na")+" fragments: "+i),l}return n},r.waitForCdnTuneIn=function(t){return t.live&&t.canBlockReload&&t.partTarget&&t.tuneInGoal>Math.max(t.partHoldBack,3*t.partTarget)},r.setStartPosition=function(t,e){var r=this.startPosition;if(r<e&&(r=-1),-1===r||-1===this.lastCurrentTime){var i=null!==this.startTimeOffset,n=i?this.startTimeOffset:t.startTimeOffset;null!==n&&y(n)?(r=e+n,n<0&&(r+=t.totalduration),r=Math.min(Math.max(e,r),e+t.totalduration),this.log("Start time offset "+n+" found in "+(i?"multivariant":"media")+" playlist, adjust startPosition to "+r),this.startPosition=r):t.live?r=this.hls.liveSyncPosition||e:this.startPosition=r=0,this.lastCurrentTime=r}this.nextLoadPosition=r},r.getLoadPosition=function(){var t=this.media,e=0;return this.loadedmetadata&&t?e=t.currentTime:this.nextLoadPosition&&(e=this.nextLoadPosition),e},r.handleFragLoadAborted=function(t,e){this.transmuxer&&"initSegment"!==t.sn&&t.stats.aborted&&(this.warn("Fragment "+t.sn+(e?" part "+e.index:"")+" of level "+t.level+" was aborted"),this.resetFragmentLoading(t))},r.resetFragmentLoading=function(t){this.fragCurrent&&(this.fragContextChanged(t)||this.state===mi)||(this.state=fi)},r.onFragmentOrKeyLoadError=function(t,e){if(e.chunkMeta&&!e.frag){var r=this.getCurrentContext(e.chunkMeta);r&&(e.frag=r.frag)}var i=e.frag;if(i&&i.type===t&&this.levels)if(this.fragContextChanged(i)){var n;this.warn("Frag load error must match current frag to retry "+i.url+" > "+(null==(n=this.fragCurrent)?void 0:n.url))}else{var a=e.details===A.FRAG_GAP;a&&this.fragmentTracker.fragBuffered(i,!0);var s=e.errorAction,o=s||{},l=o.action,u=o.retryCount,h=void 0===u?0:u,d=o.retryConfig;if(s&&l===Lr&&d){this.resetStartWhenNotLoaded(this.levelLastLoaded);var c=cr(d,h);this.warn("Fragment "+i.sn+" of "+t+" "+i.level+" errored with "+e.details+", retrying loading "+(h+1)+"/"+d.maxNumRetry+" in "+c+"ms"),s.resolved=!0,this.retryDate=self.performance.now()+c,this.state=mi}else if(d&&s){if(this.resetFragmentErrors(t),!(h<d.maxNumRetry))return void w.warn(e.details+" reached or exceeded max retry ("+h+")");a||l===Sr||(s.resolved=!0)}else(null==s?void 0:s.action)===Tr?this.state=Ai:this.state=Si;this.tickImmediate()}},r.reduceLengthAndFlushBuffer=function(t){if(this.state===yi||this.state===Ei){var e=t.parent,r=this.getFwdBufferInfo(this.mediaBuffer,e),i=r&&r.len>.5;i&&this.reduceMaxBufferLength(r.len);var n=!i;return n&&this.warn("Buffer full error while media.currentTime is not buffered, flush "+e+" buffer"),t.frag&&(this.fragmentTracker.removeFragment(t.frag),this.nextLoadPosition=t.frag.start),this.resetLoadingState(),n}return!1},r.resetFragmentErrors=function(t){t===we&&(this.fragCurrent=null),this.loadedmetadata||(this.startFragRequested=!1),this.state!==ci&&(this.state=fi)},r.afterBufferFlushed=function(t,e,r){if(t){var i=zr.getBuffered(t);this.fragmentTracker.detectEvictedFragments(e,i,r),this.state===Ti&&this.resetLoadingState()}},r.resetLoadingState=function(){this.log("Reset loading state"),this.fragCurrent=null,this.fragPrevious=null,this.state=fi},r.resetStartWhenNotLoaded=function(t){if(!this.loadedmetadata){this.startFragRequested=!1;var e=t?t.details:null;null!=e&&e.live?(this.startPosition=-1,this.setStartPosition(e,0),this.resetLoadingState()):this.nextLoadPosition=this.startPosition}},r.resetWhenMissingContext=function(t){this.warn("The loading context changed while buffering fragment "+t.sn+" of level "+t.level+". This chunk will not be buffered."),this.removeUnbufferedFrags(),this.resetStartWhenNotLoaded(this.levelLastLoaded),this.resetLoadingState()},r.removeUnbufferedFrags=function(t){void 0===t&&(t=0),this.fragmentTracker.removeFragmentsInRange(t,1/0,this.playlistType,!1,!0)},r.updateLevelTiming=function(t,e,r,i){var n,a=this,s=r.details;if(s){if(!Object.keys(t.elementaryStreams).reduce((function(e,n){var o=t.elementaryStreams[n];if(o){var l=o.endPTS-o.startPTS;if(l<=0)return a.warn("Could not parse fragment "+t.sn+" "+n+" duration reliably ("+l+")"),e||!1;var u=i?0:ir(s,t,o.startPTS,o.endPTS,o.startDTS,o.endDTS);return a.hls.trigger(S.LEVEL_PTS_UPDATED,{details:s,level:r,drift:u,type:n,frag:t,start:o.startPTS,end:o.endPTS}),!0}return e}),!1)&&null===(null==(n=this.transmuxer)?void 0:n.error)){var o=new Error("Found no media in fragment "+t.sn+" of level "+t.level+" resetting transmuxer to fallback to playlist timing");if(0===r.fragmentError&&(r.fragmentError++,t.gap=!0,this.fragmentTracker.removeFragment(t),this.fragmentTracker.fragBuffered(t,!0)),this.warn(o.message),this.hls.trigger(S.ERROR,{type:L.MEDIA_ERROR,details:A.FRAG_PARSING_ERROR,fatal:!1,error:o,frag:t,reason:"Found no media in msn "+t.sn+' of level "'+r.url+'"'}),!this.hls)return;this.resetTransmuxer()}this.state=Ei,this.hls.trigger(S.FRAG_PARSED,{frag:t,part:e})}else this.warn("level.details undefined")},r.resetTransmuxer=function(){this.transmuxer&&(this.transmuxer.destroy(),this.transmuxer=null)},r.recoverWorkerError=function(t){"demuxerWorker"===t.event&&(this.fragmentTracker.removeAllFragments(),this.resetTransmuxer(),this.resetStartWhenNotLoaded(this.levelLastLoaded),this.resetLoadingState())},s(e,[{key:"state",get:function(){return this._state},set:function(t){var e=this._state;e!==t&&(this._state=t,this.log(e+"->"+t))}}]),e}(Gr),ki=function(){function t(){this.chunks=[],this.dataLength=0}var e=t.prototype;return e.push=function(t){this.chunks.push(t),this.dataLength+=t.length},e.flush=function(){var t,e=this.chunks,r=this.dataLength;return e.length?(t=1===e.length?e[0]:function(t,e){for(var r=new Uint8Array(e),i=0,n=0;n<t.length;n++){var a=t[n];r.set(a,i),i+=a.length}return r}(e,r),this.reset(),t):new Uint8Array(0)},e.reset=function(){this.chunks.length=0,this.dataLength=0},t}();function bi(t,e){return void 0===t&&(t=""),void 0===e&&(e=9e4),{type:t,id:-1,pid:-1,inputTimeScale:e,sequenceNumber:-1,samples:[],dropped:0}}var Di=function(){function t(){this._audioTrack=void 0,this._id3Track=void 0,this.frameIndex=0,this.cachedData=null,this.basePTS=null,this.initPTS=null,this.lastPTS=null}var e=t.prototype;return e.resetInitSegment=function(t,e,r,i){this._id3Track={type:"id3",id:3,pid:-1,inputTimeScale:9e4,sequenceNumber:0,samples:[],dropped:0}},e.resetTimeStamp=function(t){this.initPTS=t,this.resetContiguity()},e.resetContiguity=function(){this.basePTS=null,this.lastPTS=null,this.frameIndex=0},e.canParse=function(t,e){return!1},e.appendFrame=function(t,e,r){},e.demux=function(t,e){this.cachedData&&(t=Gt(this.cachedData,t),this.cachedData=null);var r,i=lt(t,0),n=i?i.length:0,a=this._audioTrack,s=this._id3Track,o=i?dt(i):void 0,l=t.length;for((null===this.basePTS||0===this.frameIndex&&y(o))&&(this.basePTS=Ii(o,e,this.initPTS),this.lastPTS=this.basePTS),null===this.lastPTS&&(this.lastPTS=this.basePTS),i&&i.length>0&&s.samples.push({pts:this.lastPTS,dts:this.lastPTS,data:i,type:Be,duration:Number.POSITIVE_INFINITY});n<l;){if(this.canParse(t,n)){var u=this.appendFrame(a,t,n);u?(this.frameIndex++,this.lastPTS=u.sample.pts,r=n+=u.length):n=l}else ht(t,n)?(i=lt(t,n),s.samples.push({pts:this.lastPTS,dts:this.lastPTS,data:i,type:Be,duration:Number.POSITIVE_INFINITY}),r=n+=i.length):n++;if(n===l&&r!==l){var h=nt(t,r);this.cachedData?this.cachedData=Gt(this.cachedData,h):this.cachedData=h}}return{audioTrack:a,videoTrack:bi(),id3Track:s,textTrack:bi()}},e.demuxSampleAes=function(t,e,r){return Promise.reject(new Error("["+this+"] This demuxer does not support Sample-AES decryption"))},e.flush=function(t){var e=this.cachedData;return e&&(this.cachedData=null,this.demux(e,0)),{audioTrack:this._audioTrack,videoTrack:bi(),id3Track:this._id3Track,textTrack:bi()}},e.destroy=function(){},t}(),Ii=function(t,e,r){return y(t)?90*t:9e4*e+(r?9e4*r.baseTime/r.timescale:0)};function wi(t,e){return 255===t[e]&&240==(246&t[e+1])}function Ci(t,e){return 1&t[e+1]?7:9}function _i(t,e){return(3&t[e+3])<<11|t[e+4]<<3|(224&t[e+5])>>>5}function xi(t,e){return e+1<t.length&&wi(t,e)}function Pi(t,e){if(xi(t,e)){var r=Ci(t,e);if(e+r>=t.length)return!1;var i=_i(t,e);if(i<=r)return!1;var n=e+i;return n===t.length||xi(t,n)}return!1}function Fi(t,e,r,i,n){if(!t.samplerate){var a=function(t,e,r,i){var n,a,s,o,l=navigator.userAgent.toLowerCase(),u=i,h=[96e3,88200,64e3,48e3,44100,32e3,24e3,22050,16e3,12e3,11025,8e3,7350];n=1+((192&e[r+2])>>>6);var d=(60&e[r+2])>>>2;if(!(d>h.length-1))return s=(1&e[r+2])<<2,s|=(192&e[r+3])>>>6,w.log("manifest codec:"+i+", ADTS type:"+n+", samplingIndex:"+d),/firefox/i.test(l)?d>=6?(n=5,o=new Array(4),a=d-3):(n=2,o=new Array(2),a=d):-1!==l.indexOf("android")?(n=2,o=new Array(2),a=d):(n=5,o=new Array(4),i&&(-1!==i.indexOf("mp4a.40.29")||-1!==i.indexOf("mp4a.40.5"))||!i&&d>=6?a=d-3:((i&&-1!==i.indexOf("mp4a.40.2")&&(d>=6&&1===s||/vivaldi/i.test(l))||!i&&1===s)&&(n=2,o=new Array(2)),a=d)),o[0]=n<<3,o[0]|=(14&d)>>1,o[1]|=(1&d)<<7,o[1]|=s<<3,5===n&&(o[1]|=(14&a)>>1,o[2]=(1&a)<<7,o[2]|=8,o[3]=0),{config:o,samplerate:h[d],channelCount:s,codec:"mp4a.40."+n,manifestCodec:u};var c=new Error("invalid ADTS sampling index:"+d);t.emit(S.ERROR,S.ERROR,{type:L.MEDIA_ERROR,details:A.FRAG_PARSING_ERROR,fatal:!0,error:c,reason:c.message})}(e,r,i,n);if(!a)return;t.config=a.config,t.samplerate=a.samplerate,t.channelCount=a.channelCount,t.codec=a.codec,t.manifestCodec=a.manifestCodec,w.log("parsed codec:"+t.codec+", rate:"+a.samplerate+", channels:"+a.channelCount)}}function Mi(t){return 9216e4/t}function Oi(t,e,r,i,n){var a,s=i+n*Mi(t.samplerate),o=function(t,e){var r=Ci(t,e);if(e+r<=t.length){var i=_i(t,e)-r;if(i>0)return{headerLength:r,frameLength:i}}}(e,r);if(o){var l=o.frameLength,u=o.headerLength,h=u+l,d=Math.max(0,r+h-e.length);d?(a=new Uint8Array(h-u)).set(e.subarray(r+u,e.length),0):a=e.subarray(r+u,r+h);var c={unit:a,pts:s};return d||t.samples.push(c),{sample:c,length:h,missing:d}}var f=e.length-r;return(a=new Uint8Array(f)).set(e.subarray(r,e.length),0),{sample:{unit:a,pts:s},length:f,missing:-1}}var Ni=null,Ui=[32,64,96,128,160,192,224,256,288,320,352,384,416,448,32,48,56,64,80,96,112,128,160,192,224,256,320,384,32,40,48,56,64,80,96,112,128,160,192,224,256,320,32,48,56,64,80,96,112,128,144,160,176,192,224,256,8,16,24,32,40,48,56,64,80,96,112,128,144,160],Bi=[44100,48e3,32e3,22050,24e3,16e3,11025,12e3,8e3],Gi=[[0,72,144,12],[0,0,0,0],[0,72,144,12],[0,144,144,12]],Ki=[0,1,1,4];function Hi(t,e,r,i,n){if(!(r+24>e.length)){var a=Vi(e,r);if(a&&r+a.frameLength<=e.length){var s=i+n*(9e4*a.samplesPerFrame/a.sampleRate),o={unit:e.subarray(r,r+a.frameLength),pts:s,dts:s};return t.config=[],t.channelCount=a.channelCount,t.samplerate=a.sampleRate,t.samples.push(o),{sample:o,length:a.frameLength,missing:0}}}}function Vi(t,e){var r=t[e+1]>>3&3,i=t[e+1]>>1&3,n=t[e+2]>>4&15,a=t[e+2]>>2&3;if(1!==r&&0!==n&&15!==n&&3!==a){var s=t[e+2]>>1&1,o=t[e+3]>>6,l=1e3*Ui[14*(3===r?3-i:3===i?3:4)+n-1],u=Bi[3*(3===r?0:2===r?1:2)+a],h=3===o?1:2,d=Gi[r][i],c=Ki[i],f=8*d*c,g=Math.floor(d*l/u+s)*c;if(null===Ni){var v=(navigator.userAgent||"").match(/Chrome\/(\d+)/i);Ni=v?parseInt(v[1]):0}return!!Ni&&Ni<=87&&2===i&&l>=224e3&&0===o&&(t[e+3]=128|t[e+3]),{sampleRate:u,channelCount:h,frameLength:g,samplesPerFrame:f}}}function Yi(t,e){return 255===t[e]&&224==(224&t[e+1])&&0!=(6&t[e+1])}function Wi(t,e){return e+1<t.length&&Yi(t,e)}function ji(t,e){if(e+1<t.length&&Yi(t,e)){var r=Vi(t,e),i=4;null!=r&&r.frameLength&&(i=r.frameLength);var n=e+i;return n===t.length||Wi(t,n)}return!1}var qi=function(t){function e(e,r){var i;return(i=t.call(this)||this).observer=void 0,i.config=void 0,i.observer=e,i.config=r,i}l(e,t);var r=e.prototype;return r.resetInitSegment=function(e,r,i,n){t.prototype.resetInitSegment.call(this,e,r,i,n),this._audioTrack={container:"audio/adts",type:"audio",id:2,pid:-1,sequenceNumber:0,segmentCodec:"aac",samples:[],manifestCodec:r,duration:n,inputTimeScale:9e4,dropped:0}},e.probe=function(t){if(!t)return!1;var e=lt(t,0),r=(null==e?void 0:e.length)||0;if(ji(t,r))return!1;for(var i=t.length;r<i;r++)if(Pi(t,r))return w.log("ADTS sync word found !"),!0;return!1},r.canParse=function(t,e){return function(t,e){return function(t,e){return e+5<t.length}(t,e)&&wi(t,e)&&_i(t,e)<=t.length-e}(t,e)},r.appendFrame=function(t,e,r){Fi(t,this.observer,e,r,t.manifestCodec);var i=Oi(t,e,r,this.basePTS,this.frameIndex);if(i&&0===i.missing)return i},e}(Di),Xi=/\/emsg[-/]ID3/i,zi=function(){function t(t,e){this.remainderData=null,this.timeOffset=0,this.config=void 0,this.videoTrack=void 0,this.audioTrack=void 0,this.id3Track=void 0,this.txtTrack=void 0,this.config=e}var e=t.prototype;return e.resetTimeStamp=function(){},e.resetInitSegment=function(t,e,r,i){var n=this.videoTrack=bi("video",1),a=this.audioTrack=bi("audio",1),s=this.txtTrack=bi("text",1);if(this.id3Track=bi("id3",1),this.timeOffset=0,null!=t&&t.byteLength){var o=Pt(t);if(o.video){var l=o.video,u=l.id,h=l.timescale,d=l.codec;n.id=u,n.timescale=s.timescale=h,n.codec=d}if(o.audio){var c=o.audio,f=c.id,g=c.timescale,v=c.codec;a.id=f,a.timescale=g,a.codec=v}s.id=kt.text,n.sampleDuration=0,n.duration=a.duration=i}},e.resetContiguity=function(){this.remainderData=null},t.probe=function(t){return function(t){for(var e=t.byteLength,r=0;r<e;){var i=It(t,r);if(i>8&&109===t[r+4]&&111===t[r+5]&&111===t[r+6]&&102===t[r+7])return!0;r=i>1?r+i:e}return!1}(t)},e.demux=function(t,e){this.timeOffset=e;var r=t,i=this.videoTrack,n=this.txtTrack;if(this.config.progressive){this.remainderData&&(r=Gt(this.remainderData,t));var a=function(t){var e={valid:null,remainder:null},r=_t(t,["moof"]);if(r.length<2)return e.remainder=t,e;var i=r[r.length-1];return e.valid=nt(t,0,i.byteOffset-8),e.remainder=nt(t,i.byteOffset-8),e}(r);this.remainderData=a.remainder,i.samples=a.valid||new Uint8Array}else i.samples=r;var s=this.extractID3Track(i,e);return n.samples=Kt(e,i),{videoTrack:i,audioTrack:this.audioTrack,id3Track:s,textTrack:this.txtTrack}},e.flush=function(){var t=this.timeOffset,e=this.videoTrack,r=this.txtTrack;e.samples=this.remainderData||new Uint8Array,this.remainderData=null;var i=this.extractID3Track(e,this.timeOffset);return r.samples=Kt(t,e),{videoTrack:e,audioTrack:bi(),id3Track:i,textTrack:bi()}},e.extractID3Track=function(t,e){var r=this.id3Track;if(t.samples.length){var i=_t(t.samples,["emsg"]);i&&i.forEach((function(t){var i=function(t){var e=t[0],r="",i="",n=0,a=0,s=0,o=0,l=0,u=0;if(0===e){for(;"\0"!==bt(t.subarray(u,u+1));)r+=bt(t.subarray(u,u+1)),u+=1;for(r+=bt(t.subarray(u,u+1)),u+=1;"\0"!==bt(t.subarray(u,u+1));)i+=bt(t.subarray(u,u+1)),u+=1;i+=bt(t.subarray(u,u+1)),u+=1,n=It(t,12),a=It(t,16),o=It(t,20),l=It(t,24),u=28}else if(1===e){n=It(t,u+=4);var h=It(t,u+=4),d=It(t,u+=4);for(u+=4,s=Math.pow(2,32)*h+d,E(s)||(s=Number.MAX_SAFE_INTEGER,w.warn("Presentation time exceeds safe integer limit and wrapped to max safe integer in parsing emsg box")),o=It(t,u),l=It(t,u+=4),u+=4;"\0"!==bt(t.subarray(u,u+1));)r+=bt(t.subarray(u,u+1)),u+=1;for(r+=bt(t.subarray(u,u+1)),u+=1;"\0"!==bt(t.subarray(u,u+1));)i+=bt(t.subarray(u,u+1)),u+=1;i+=bt(t.subarray(u,u+1)),u+=1}return{schemeIdUri:r,value:i,timeScale:n,presentationTime:s,presentationTimeDelta:a,eventDuration:o,id:l,payload:t.subarray(u,t.byteLength)}}(t);if(Xi.test(i.schemeIdUri)){var n=y(i.presentationTime)?i.presentationTime/i.timeScale:e+i.presentationTimeDelta/i.timeScale,a=4294967295===i.eventDuration?Number.POSITIVE_INFINITY:i.eventDuration/i.timeScale;a<=.001&&(a=Number.POSITIVE_INFINITY);var s=i.payload;r.samples.push({data:s,len:s.byteLength,dts:n,pts:n,type:Ke,duration:a})}}))}return r},e.demuxSampleAes=function(t,e,r){return Promise.reject(new Error("The MP4 demuxer does not support SAMPLE-AES decryption"))},e.destroy=function(){},t}(),Qi=function(t,e){var r=0,i=5;e+=i;for(var n=new Uint32Array(1),a=new Uint32Array(1),s=new Uint8Array(1);i>0;){s[0]=t[e];var o=Math.min(i,8),l=8-o;a[0]=4278190080>>>24+l<<l,n[0]=(s[0]&a[0])>>l,r=r?r<<o|n[0]:n[0],e+=1,i-=o}return r},Ji=function(t){function e(e){var r;return(r=t.call(this)||this).observer=void 0,r.observer=e,r}l(e,t);var r=e.prototype;return r.resetInitSegment=function(e,r,i,n){t.prototype.resetInitSegment.call(this,e,r,i,n),this._audioTrack={container:"audio/ac-3",type:"audio",id:2,pid:-1,sequenceNumber:0,segmentCodec:"ac3",samples:[],manifestCodec:r,duration:n,inputTimeScale:9e4,dropped:0}},r.canParse=function(t,e){return e+64<t.length},r.appendFrame=function(t,e,r){var i=$i(t,e,r,this.basePTS,this.frameIndex);if(-1!==i)return{sample:t.samples[t.samples.length-1],length:i,missing:0}},e.probe=function(t){if(!t)return!1;var e=lt(t,0);if(!e)return!1;var r=e.length;return 11===t[r]&&119===t[r+1]&&void 0!==dt(e)&&Qi(t,r)<16},e}(Di);function $i(t,e,r,i,n){if(r+8>e.length)return-1;if(11!==e[r]||119!==e[r+1])return-1;var a=e[r+4]>>6;if(a>=3)return-1;var s=[48e3,44100,32e3][a],o=63&e[r+4],l=2*[64,69,96,64,70,96,80,87,120,80,88,120,96,104,144,96,105,144,112,121,168,112,122,168,128,139,192,128,140,192,160,174,240,160,175,240,192,208,288,192,209,288,224,243,336,224,244,336,256,278,384,256,279,384,320,348,480,320,349,480,384,417,576,384,418,576,448,487,672,448,488,672,512,557,768,512,558,768,640,696,960,640,697,960,768,835,1152,768,836,1152,896,975,1344,896,976,1344,1024,1114,1536,1024,1115,1536,1152,1253,1728,1152,1254,1728,1280,1393,1920,1280,1394,1920][3*o+a];if(r+l>e.length)return-1;var u=e[r+6]>>5,h=0;2===u?h+=2:(1&u&&1!==u&&(h+=2),4&u&&(h+=2));var d=(e[r+6]<<8|e[r+7])>>12-h&1,c=[2,1,2,3,3,4,4,5][u]+d,f=e[r+5]>>3,g=7&e[r+5],v=new Uint8Array([a<<6|f<<1|g>>2,(3&g)<<6|u<<3|d<<2|o>>4,o<<4&224]),m=i+n*(1536/s*9e4),p=e.subarray(r,r+l);return t.config=v,t.channelCount=c,t.samplerate=s,t.samples.push({unit:p,pts:m}),l}var Zi=function(){function t(){this.VideoSample=null}var e=t.prototype;return e.createVideoSample=function(t,e,r,i){return{key:t,frame:!1,pts:e,dts:r,units:[],debug:i,length:0}},e.getLastNalUnit=function(t){var e,r,i=this.VideoSample;if(i&&0!==i.units.length||(i=t[t.length-1]),null!=(e=i)&&e.units){var n=i.units;r=n[n.length-1]}return r},e.pushAccessUnit=function(t,e){if(t.units.length&&t.frame){if(void 0===t.pts){var r=e.samples,i=r.length;if(!i)return void e.dropped++;var n=r[i-1];t.pts=n.pts,t.dts=n.dts}e.samples.push(t)}t.debug.length&&w.log(t.pts+"/"+t.dts+":"+t.debug)},t}(),tn=function(){function t(t){this.data=void 0,this.bytesAvailable=void 0,this.word=void 0,this.bitsAvailable=void 0,this.data=t,this.bytesAvailable=t.byteLength,this.word=0,this.bitsAvailable=0}var e=t.prototype;return e.loadWord=function(){var t=this.data,e=this.bytesAvailable,r=t.byteLength-e,i=new Uint8Array(4),n=Math.min(4,e);if(0===n)throw new Error("no bytes available");i.set(t.subarray(r,r+n)),this.word=new DataView(i.buffer).getUint32(0),this.bitsAvailable=8*n,this.bytesAvailable-=n},e.skipBits=function(t){var e;t=Math.min(t,8*this.bytesAvailable+this.bitsAvailable),this.bitsAvailable>t?(this.word<<=t,this.bitsAvailable-=t):(t-=this.bitsAvailable,t-=(e=t>>3)<<3,this.bytesAvailable-=e,this.loadWord(),this.word<<=t,this.bitsAvailable-=t)},e.readBits=function(t){var e=Math.min(this.bitsAvailable,t),r=this.word>>>32-e;if(t>32&&w.error("Cannot read more than 32 bits at a time"),this.bitsAvailable-=e,this.bitsAvailable>0)this.word<<=e;else{if(!(this.bytesAvailable>0))throw new Error("no bits available");this.loadWord()}return(e=t-e)>0&&this.bitsAvailable?r<<e|this.readBits(e):r},e.skipLZ=function(){var t;for(t=0;t<this.bitsAvailable;++t)if(0!=(this.word&2147483648>>>t))return this.word<<=t,this.bitsAvailable-=t,t;return this.loadWord(),t+this.skipLZ()},e.skipUEG=function(){this.skipBits(1+this.skipLZ())},e.skipEG=function(){this.skipBits(1+this.skipLZ())},e.readUEG=function(){var t=this.skipLZ();return this.readBits(t+1)-1},e.readEG=function(){var t=this.readUEG();return 1&t?1+t>>>1:-1*(t>>>1)},e.readBoolean=function(){return 1===this.readBits(1)},e.readUByte=function(){return this.readBits(8)},e.readUShort=function(){return this.readBits(16)},e.readUInt=function(){return this.readBits(32)},e.skipScalingList=function(t){for(var e=8,r=8,i=0;i<t;i++)0!==r&&(r=(e+this.readEG()+256)%256),e=0===r?e:r},e.readSPS=function(){var t,e,r,i=0,n=0,a=0,s=0,o=this.readUByte.bind(this),l=this.readBits.bind(this),u=this.readUEG.bind(this),h=this.readBoolean.bind(this),d=this.skipBits.bind(this),c=this.skipEG.bind(this),f=this.skipUEG.bind(this),g=this.skipScalingList.bind(this);o();var v=o();if(l(5),d(3),o(),f(),100===v||110===v||122===v||244===v||44===v||83===v||86===v||118===v||128===v){var m=u();if(3===m&&d(1),f(),f(),d(1),h())for(e=3!==m?8:12,r=0;r<e;r++)h()&&g(r<6?16:64)}f();var p=u();if(0===p)u();else if(1===p)for(d(1),c(),c(),t=u(),r=0;r<t;r++)c();f(),d(1);var y=u(),E=u(),T=l(1);0===T&&d(1),d(1),h()&&(i=u(),n=u(),a=u(),s=u());var S=[1,1];if(h()&&h())switch(o()){case 1:S=[1,1];break;case 2:S=[12,11];break;case 3:S=[10,11];break;case 4:S=[16,11];break;case 5:S=[40,33];break;case 6:S=[24,11];break;case 7:S=[20,11];break;case 8:S=[32,11];break;case 9:S=[80,33];break;case 10:S=[18,11];break;case 11:S=[15,11];break;case 12:S=[64,33];break;case 13:S=[160,99];break;case 14:S=[4,3];break;case 15:S=[3,2];break;case 16:S=[2,1];break;case 255:S=[o()<<8|o(),o()<<8|o()]}return{width:Math.ceil(16*(y+1)-2*i-2*n),height:(2-T)*(E+1)*16-(T?2:4)*(a+s),pixelRatio:S}},e.readSliceType=function(){return this.readUByte(),this.readUEG(),this.readUEG()},t}(),en=function(t){function e(){return t.apply(this,arguments)||this}l(e,t);var r=e.prototype;return r.parseAVCPES=function(t,e,r,i,n){var a,s=this,o=this.parseAVCNALu(t,r.data),l=this.VideoSample,u=!1;r.data=null,l&&o.length&&!t.audFound&&(this.pushAccessUnit(l,t),l=this.VideoSample=this.createVideoSample(!1,r.pts,r.dts,"")),o.forEach((function(i){var o;switch(i.type){case 1:var h=!1;a=!0;var d,c=i.data;if(u&&c.length>4){var f=new tn(c).readSliceType();2!==f&&4!==f&&7!==f&&9!==f||(h=!0)}h&&null!=(d=l)&&d.frame&&!l.key&&(s.pushAccessUnit(l,t),l=s.VideoSample=null),l||(l=s.VideoSample=s.createVideoSample(!0,r.pts,r.dts,"")),l.frame=!0,l.key=h;break;case 5:a=!0,null!=(o=l)&&o.frame&&!l.key&&(s.pushAccessUnit(l,t),l=s.VideoSample=null),l||(l=s.VideoSample=s.createVideoSample(!0,r.pts,r.dts,"")),l.key=!0,l.frame=!0;break;case 6:a=!0,Vt(i.data,1,r.pts,e.samples);break;case 7:var g,v;a=!0,u=!0;var m=i.data,p=new tn(m).readSPS();if(!t.sps||t.width!==p.width||t.height!==p.height||(null==(g=t.pixelRatio)?void 0:g[0])!==p.pixelRatio[0]||(null==(v=t.pixelRatio)?void 0:v[1])!==p.pixelRatio[1]){t.width=p.width,t.height=p.height,t.pixelRatio=p.pixelRatio,t.sps=[m],t.duration=n;for(var y=m.subarray(1,4),E="avc1.",T=0;T<3;T++){var S=y[T].toString(16);S.length<2&&(S="0"+S),E+=S}t.codec=E}break;case 8:a=!0,t.pps=[i.data];break;case 9:a=!0,t.audFound=!0,l&&s.pushAccessUnit(l,t),l=s.VideoSample=s.createVideoSample(!1,r.pts,r.dts,"");break;case 12:a=!0;break;default:a=!1,l&&(l.debug+="unknown NAL "+i.type+" ")}l&&a&&l.units.push(i)})),i&&l&&(this.pushAccessUnit(l,t),this.VideoSample=null)},r.parseAVCNALu=function(t,e){var r,i,n=e.byteLength,a=t.naluState||0,s=a,o=[],l=0,u=-1,h=0;for(-1===a&&(u=0,h=31&e[0],a=0,l=1);l<n;)if(r=e[l++],a)if(1!==a)if(r)if(1===r){if(i=l-a-1,u>=0){var d={data:e.subarray(u,i),type:h};o.push(d)}else{var c=this.getLastNalUnit(t.samples);c&&(s&&l<=4-s&&c.state&&(c.data=c.data.subarray(0,c.data.byteLength-s)),i>0&&(c.data=Gt(c.data,e.subarray(0,i)),c.state=0))}l<n?(u=l,h=31&e[l],a=0):a=-1}else a=0;else a=3;else a=r?0:2;else a=r?0:1;if(u>=0&&a>=0){var f={data:e.subarray(u,n),type:h,state:a};o.push(f)}if(0===o.length){var g=this.getLastNalUnit(t.samples);g&&(g.data=Gt(g.data,e))}return t.naluState=a,o},e}(Zi),rn=function(){function t(t,e,r){this.keyData=void 0,this.decrypter=void 0,this.keyData=r,this.decrypter=new hi(e,{removePKCS7Padding:!1})}var e=t.prototype;return e.decryptBuffer=function(t){return this.decrypter.decrypt(t,this.keyData.key.buffer,this.keyData.iv.buffer)},e.decryptAacSample=function(t,e,r){var i=this,n=t[e].unit;if(!(n.length<=16)){var a=n.subarray(16,n.length-n.length%16),s=a.buffer.slice(a.byteOffset,a.byteOffset+a.length);this.decryptBuffer(s).then((function(a){var s=new Uint8Array(a);n.set(s,16),i.decrypter.isSync()||i.decryptAacSamples(t,e+1,r)}))}},e.decryptAacSamples=function(t,e,r){for(;;e++){if(e>=t.length)return void r();if(!(t[e].unit.length<32||(this.decryptAacSample(t,e,r),this.decrypter.isSync())))return}},e.getAvcEncryptedData=function(t){for(var e=16*Math.floor((t.length-48)/160)+16,r=new Int8Array(e),i=0,n=32;n<t.length-16;n+=160,i+=16)r.set(t.subarray(n,n+16),i);return r},e.getAvcDecryptedUnit=function(t,e){for(var r=new Uint8Array(e),i=0,n=32;n<t.length-16;n+=160,i+=16)t.set(r.subarray(i,i+16),n);return t},e.decryptAvcSample=function(t,e,r,i,n){var a=this,s=Yt(n.data),o=this.getAvcEncryptedData(s);this.decryptBuffer(o.buffer).then((function(o){n.data=a.getAvcDecryptedUnit(s,o),a.decrypter.isSync()||a.decryptAvcSamples(t,e,r+1,i)}))},e.decryptAvcSamples=function(t,e,r,i){if(t instanceof Uint8Array)throw new Error("Cannot decrypt samples of type Uint8Array");for(;;e++,r=0){if(e>=t.length)return void i();for(var n=t[e].units;!(r>=n.length);r++){var a=n[r];if(!(a.data.length<=48||1!==a.type&&5!==a.type||(this.decryptAvcSample(t,e,r,i,a),this.decrypter.isSync())))return}}},t}(),nn=188,an=function(){function t(t,e,r){this.observer=void 0,this.config=void 0,this.typeSupported=void 0,this.sampleAes=null,this.pmtParsed=!1,this.audioCodec=void 0,this.videoCodec=void 0,this._duration=0,this._pmtId=-1,this._videoTrack=void 0,this._audioTrack=void 0,this._id3Track=void 0,this._txtTrack=void 0,this.aacOverFlow=null,this.remainderData=null,this.videoParser=void 0,this.observer=t,this.config=e,this.typeSupported=r,this.videoParser=new en}t.probe=function(e){var r=t.syncOffset(e);return r>0&&w.warn("MPEG2-TS detected but first sync word found @ offset "+r),-1!==r},t.syncOffset=function(t){for(var e=t.length,r=Math.min(940,e-nn)+1,i=0;i<r;){for(var n=!1,a=-1,s=0,o=i;o<e;o+=nn){if(71!==t[o]||e-o!==nn&&71!==t[o+nn]){if(s)return-1;break}if(s++,-1===a&&0!==(a=o)&&(r=Math.min(a+18612,t.length-nn)+1),n||(n=0===sn(t,o)),n&&s>1&&(0===a&&s>2||o+nn>r))return a}i++}return-1},t.createTrack=function(t,e){return{container:"video"===t||"audio"===t?"video/mp2t":void 0,type:t,id:kt[t],pid:-1,inputTimeScale:9e4,sequenceNumber:0,samples:[],dropped:0,duration:"audio"===t?e:void 0}};var e=t.prototype;return e.resetInitSegment=function(e,r,i,n){this.pmtParsed=!1,this._pmtId=-1,this._videoTrack=t.createTrack("video"),this._audioTrack=t.createTrack("audio",n),this._id3Track=t.createTrack("id3"),this._txtTrack=t.createTrack("text"),this._audioTrack.segmentCodec="aac",this.aacOverFlow=null,this.remainderData=null,this.audioCodec=r,this.videoCodec=i,this._duration=n},e.resetTimeStamp=function(){},e.resetContiguity=function(){var t=this._audioTrack,e=this._videoTrack,r=this._id3Track;t&&(t.pesData=null),e&&(e.pesData=null),r&&(r.pesData=null),this.aacOverFlow=null,this.remainderData=null},e.demux=function(e,r,i,n){var a;void 0===i&&(i=!1),void 0===n&&(n=!1),i||(this.sampleAes=null);var s=this._videoTrack,o=this._audioTrack,l=this._id3Track,u=this._txtTrack,h=s.pid,d=s.pesData,c=o.pid,f=l.pid,g=o.pesData,v=l.pesData,m=null,p=this.pmtParsed,y=this._pmtId,E=e.length;if(this.remainderData&&(E=(e=Gt(this.remainderData,e)).length,this.remainderData=null),E<nn&&!n)return this.remainderData=e,{audioTrack:o,videoTrack:s,id3Track:l,textTrack:u};var T=Math.max(0,t.syncOffset(e));(E-=(E-T)%nn)<e.byteLength&&!n&&(this.remainderData=new Uint8Array(e.buffer,E,e.buffer.byteLength-E));for(var R=0,k=T;k<E;k+=nn)if(71===e[k]){var b=!!(64&e[k+1]),D=sn(e,k),I=void 0;if((48&e[k+3])>>4>1){if((I=k+5+e[k+4])===k+nn)continue}else I=k+4;switch(D){case h:b&&(d&&(a=hn(d))&&this.videoParser.parseAVCPES(s,u,a,!1,this._duration),d={data:[],size:0}),d&&(d.data.push(e.subarray(I,k+nn)),d.size+=k+nn-I);break;case c:if(b){if(g&&(a=hn(g)))switch(o.segmentCodec){case"aac":this.parseAACPES(o,a);break;case"mp3":this.parseMPEGPES(o,a);break;case"ac3":this.parseAC3PES(o,a)}g={data:[],size:0}}g&&(g.data.push(e.subarray(I,k+nn)),g.size+=k+nn-I);break;case f:b&&(v&&(a=hn(v))&&this.parseID3PES(l,a),v={data:[],size:0}),v&&(v.data.push(e.subarray(I,k+nn)),v.size+=k+nn-I);break;case 0:b&&(I+=e[I]+1),y=this._pmtId=on(e,I);break;case y:b&&(I+=e[I]+1);var C=ln(e,I,this.typeSupported,i);(h=C.videoPid)>0&&(s.pid=h,s.segmentCodec=C.segmentVideoCodec),(c=C.audioPid)>0&&(o.pid=c,o.segmentCodec=C.segmentAudioCodec),(f=C.id3Pid)>0&&(l.pid=f),null===m||p||(w.warn("MPEG-TS PMT found at "+k+" after unknown PID '"+m+"'. Backtracking to sync byte @"+T+" to parse all TS packets."),m=null,k=T-188),p=this.pmtParsed=!0;break;case 17:case 8191:break;default:m=D}}else R++;if(R>0){var _=new Error("Found "+R+" TS packet/s that do not start with 0x47");this.observer.emit(S.ERROR,S.ERROR,{type:L.MEDIA_ERROR,details:A.FRAG_PARSING_ERROR,fatal:!1,error:_,reason:_.message})}s.pesData=d,o.pesData=g,l.pesData=v;var x={audioTrack:o,videoTrack:s,id3Track:l,textTrack:u};return n&&this.extractRemainingSamples(x),x},e.flush=function(){var t,e=this.remainderData;return this.remainderData=null,t=e?this.demux(e,-1,!1,!0):{videoTrack:this._videoTrack,audioTrack:this._audioTrack,id3Track:this._id3Track,textTrack:this._txtTrack},this.extractRemainingSamples(t),this.sampleAes?this.decrypt(t,this.sampleAes):t},e.extractRemainingSamples=function(t){var e,r=t.audioTrack,i=t.videoTrack,n=t.id3Track,a=t.textTrack,s=i.pesData,o=r.pesData,l=n.pesData;if(s&&(e=hn(s))?(this.videoParser.parseAVCPES(i,a,e,!0,this._duration),i.pesData=null):i.pesData=s,o&&(e=hn(o))){switch(r.segmentCodec){case"aac":this.parseAACPES(r,e);break;case"mp3":this.parseMPEGPES(r,e);break;case"ac3":this.parseAC3PES(r,e)}r.pesData=null}else null!=o&&o.size&&w.log("last AAC PES packet truncated,might overlap between fragments"),r.pesData=o;l&&(e=hn(l))?(this.parseID3PES(n,e),n.pesData=null):n.pesData=l},e.demuxSampleAes=function(t,e,r){var i=this.demux(t,r,!0,!this.config.progressive),n=this.sampleAes=new rn(this.observer,this.config,e);return this.decrypt(i,n)},e.decrypt=function(t,e){return new Promise((function(r){var i=t.audioTrack,n=t.videoTrack;i.samples&&"aac"===i.segmentCodec?e.decryptAacSamples(i.samples,0,(function(){n.samples?e.decryptAvcSamples(n.samples,0,0,(function(){r(t)})):r(t)})):n.samples&&e.decryptAvcSamples(n.samples,0,0,(function(){r(t)}))}))},e.destroy=function(){this._duration=0},e.parseAACPES=function(t,e){var r,i,n,a=0,s=this.aacOverFlow,o=e.data;if(s){this.aacOverFlow=null;var l=s.missing,u=s.sample.unit.byteLength;if(-1===l)o=Gt(s.sample.unit,o);else{var h=u-l;s.sample.unit.set(o.subarray(0,l),h),t.samples.push(s.sample),a=s.missing}}for(r=a,i=o.length;r<i-1&&!xi(o,r);r++);if(r!==a){var d,c=r<i-1;d=c?"AAC PES did not start with ADTS header,offset:"+r:"No ADTS header found in AAC PES";var f=new Error(d);if(w.warn("parsing error: "+d),this.observer.emit(S.ERROR,S.ERROR,{type:L.MEDIA_ERROR,details:A.FRAG_PARSING_ERROR,fatal:!1,levelRetry:c,error:f,reason:d}),!c)return}if(Fi(t,this.observer,o,r,this.audioCodec),void 0!==e.pts)n=e.pts;else{if(!s)return void w.warn("[tsdemuxer]: AAC PES unknown PTS");var g=Mi(t.samplerate);n=s.sample.pts+g}for(var v,m=0;r<i;){if(r+=(v=Oi(t,o,r,n,m)).length,v.missing){this.aacOverFlow=v;break}for(m++;r<i-1&&!xi(o,r);r++);}},e.parseMPEGPES=function(t,e){var r=e.data,i=r.length,n=0,a=0,s=e.pts;if(void 0!==s)for(;a<i;)if(Wi(r,a)){var o=Hi(t,r,a,s,n);if(!o)break;a+=o.length,n++}else a++;else w.warn("[tsdemuxer]: MPEG PES unknown PTS")},e.parseAC3PES=function(t,e){var r=e.data,i=e.pts;if(void 0!==i)for(var n,a=r.length,s=0,o=0;o<a&&(n=$i(t,r,o,i,s++))>0;)o+=n;else w.warn("[tsdemuxer]: AC3 PES unknown PTS")},e.parseID3PES=function(t,e){if(void 0!==e.pts){var r=o({},e,{type:this._videoTrack?Ke:Be,duration:Number.POSITIVE_INFINITY});t.samples.push(r)}else w.warn("[tsdemuxer]: ID3 PES unknown PTS")},t}();function sn(t,e){return((31&t[e+1])<<8)+t[e+2]}function on(t,e){return(31&t[e+10])<<8|t[e+11]}function ln(t,e,r,i){var n={audioPid:-1,videoPid:-1,id3Pid:-1,segmentVideoCodec:"avc",segmentAudioCodec:"aac"},a=e+3+((15&t[e+1])<<8|t[e+2])-4;for(e+=12+((15&t[e+10])<<8|t[e+11]);e<a;){var s=sn(t,e),o=(15&t[e+3])<<8|t[e+4];switch(t[e]){case 207:if(!i){un("ADTS AAC");break}case 15:-1===n.audioPid&&(n.audioPid=s);break;case 21:-1===n.id3Pid&&(n.id3Pid=s);break;case 219:if(!i){un("H.264");break}case 27:-1===n.videoPid&&(n.videoPid=s,n.segmentVideoCodec="avc");break;case 3:case 4:r.mpeg||r.mp3?-1===n.audioPid&&(n.audioPid=s,n.segmentAudioCodec="mp3"):w.log("MPEG audio found, not supported in this browser");break;case 193:if(!i){un("AC-3");break}case 129:r.ac3?-1===n.audioPid&&(n.audioPid=s,n.segmentAudioCodec="ac3"):w.log("AC-3 audio found, not supported in this browser");break;case 6:if(-1===n.audioPid&&o>0)for(var l=e+5,u=o;u>2;){106===t[l]&&(!0!==r.ac3?w.log("AC-3 audio found, not supported in this browser for now"):(n.audioPid=s,n.segmentAudioCodec="ac3"));var h=t[l+1]+2;l+=h,u-=h}break;case 194:case 135:w.warn("Unsupported EC-3 in M2TS found");break;case 36:w.warn("Unsupported HEVC in M2TS found")}e+=o+5}return n}function un(t){w.log(t+" with AES-128-CBC encryption found in unencrypted stream")}function hn(t){var e,r,i,n,a,s=0,o=t.data;if(!t||0===t.size)return null;for(;o[0].length<19&&o.length>1;)o[0]=Gt(o[0],o[1]),o.splice(1,1);if(1===((e=o[0])[0]<<16)+(e[1]<<8)+e[2]){if((r=(e[4]<<8)+e[5])&&r>t.size-6)return null;var l=e[7];192&l&&(n=536870912*(14&e[9])+4194304*(255&e[10])+16384*(254&e[11])+128*(255&e[12])+(254&e[13])/2,64&l?n-(a=536870912*(14&e[14])+4194304*(255&e[15])+16384*(254&e[16])+128*(255&e[17])+(254&e[18])/2)>54e5&&(w.warn(Math.round((n-a)/9e4)+"s delta between PTS and DTS, align them"),n=a):a=n);var u=(i=e[8])+9;if(t.size<=u)return null;t.size-=u;for(var h=new Uint8Array(t.size),d=0,c=o.length;d<c;d++){var f=(e=o[d]).byteLength;if(u){if(u>f){u-=f;continue}e=e.subarray(u),f-=u,u=0}h.set(e,s),s+=f}return r&&(r-=i+3),{data:h,pts:n,dts:a,len:r}}return null}var dn=function(t){function e(){return t.apply(this,arguments)||this}l(e,t);var r=e.prototype;return r.resetInitSegment=function(e,r,i,n){t.prototype.resetInitSegment.call(this,e,r,i,n),this._audioTrack={container:"audio/mpeg",type:"audio",id:2,pid:-1,sequenceNumber:0,segmentCodec:"mp3",samples:[],manifestCodec:r,duration:n,inputTimeScale:9e4,dropped:0}},e.probe=function(t){if(!t)return!1;var e=lt(t,0),r=(null==e?void 0:e.length)||0;if(e&&11===t[r]&&119===t[r+1]&&void 0!==dt(e)&&Qi(t,r)<=16)return!1;for(var i=t.length;r<i;r++)if(ji(t,r))return w.log("MPEG Audio sync word found !"),!0;return!1},r.canParse=function(t,e){return function(t,e){return Yi(t,e)&&4<=t.length-e}(t,e)},r.appendFrame=function(t,e,r){if(null!==this.basePTS)return Hi(t,e,r,this.basePTS,this.frameIndex)},e}(Di),cn=function(){function t(){}return t.getSilentFrame=function(t,e){if("mp4a.40.2"===t){if(1===e)return new Uint8Array([0,200,0,128,35,128]);if(2===e)return new Uint8Array([33,0,73,144,2,25,0,35,128]);if(3===e)return new Uint8Array([0,200,0,128,32,132,1,38,64,8,100,0,142]);if(4===e)return new Uint8Array([0,200,0,128,32,132,1,38,64,8,100,0,128,44,128,8,2,56]);if(5===e)return new Uint8Array([0,200,0,128,32,132,1,38,64,8,100,0,130,48,4,153,0,33,144,2,56]);if(6===e)return new Uint8Array([0,200,0,128,32,132,1,38,64,8,100,0,130,48,4,153,0,33,144,2,0,178,0,32,8,224])}else{if(1===e)return new Uint8Array([1,64,34,128,163,78,230,128,186,8,0,0,0,28,6,241,193,10,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,94]);if(2===e)return new Uint8Array([1,64,34,128,163,94,230,128,186,8,0,0,0,0,149,0,6,241,161,10,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,94]);if(3===e)return new Uint8Array([1,64,34,128,163,94,230,128,186,8,0,0,0,0,149,0,6,241,161,10,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,94])}},t}(),fn=Math.pow(2,32)-1,gn=function(){function t(){}return t.init=function(){var e;for(e in t.types={avc1:[],avcC:[],btrt:[],dinf:[],dref:[],esds:[],ftyp:[],hdlr:[],mdat:[],mdhd:[],mdia:[],mfhd:[],minf:[],moof:[],moov:[],mp4a:[],".mp3":[],dac3:[],"ac-3":[],mvex:[],mvhd:[],pasp:[],sdtp:[],stbl:[],stco:[],stsc:[],stsd:[],stsz:[],stts:[],tfdt:[],tfhd:[],traf:[],trak:[],trun:[],trex:[],tkhd:[],vmhd:[],smhd:[]},t.types)t.types.hasOwnProperty(e)&&(t.types[e]=[e.charCodeAt(0),e.charCodeAt(1),e.charCodeAt(2),e.charCodeAt(3)]);var r=new Uint8Array([0,0,0,0,0,0,0,0,118,105,100,101,0,0,0,0,0,0,0,0,0,0,0,0,86,105,100,101,111,72,97,110,100,108,101,114,0]),i=new Uint8Array([0,0,0,0,0,0,0,0,115,111,117,110,0,0,0,0,0,0,0,0,0,0,0,0,83,111,117,110,100,72,97,110,100,108,101,114,0]);t.HDLR_TYPES={video:r,audio:i};var n=new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,12,117,114,108,32,0,0,0,1]),a=new Uint8Array([0,0,0,0,0,0,0,0]);t.STTS=t.STSC=t.STCO=a,t.STSZ=new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0]),t.VMHD=new Uint8Array([0,0,0,1,0,0,0,0,0,0,0,0]),t.SMHD=new Uint8Array([0,0,0,0,0,0,0,0]),t.STSD=new Uint8Array([0,0,0,0,0,0,0,1]);var s=new Uint8Array([105,115,111,109]),o=new Uint8Array([97,118,99,49]),l=new Uint8Array([0,0,0,1]);t.FTYP=t.box(t.types.ftyp,s,l,s,o),t.DINF=t.box(t.types.dinf,t.box(t.types.dref,n))},t.box=function(t){for(var e=8,r=arguments.length,i=new Array(r>1?r-1:0),n=1;n<r;n++)i[n-1]=arguments[n];for(var a=i.length,s=a;a--;)e+=i[a].byteLength;var o=new Uint8Array(e);for(o[0]=e>>24&255,o[1]=e>>16&255,o[2]=e>>8&255,o[3]=255&e,o.set(t,4),a=0,e=8;a<s;a++)o.set(i[a],e),e+=i[a].byteLength;return o},t.hdlr=function(e){return t.box(t.types.hdlr,t.HDLR_TYPES[e])},t.mdat=function(e){return t.box(t.types.mdat,e)},t.mdhd=function(e,r){r*=e;var i=Math.floor(r/(fn+1)),n=Math.floor(r%(fn+1));return t.box(t.types.mdhd,new Uint8Array([1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,3,e>>24&255,e>>16&255,e>>8&255,255&e,i>>24,i>>16&255,i>>8&255,255&i,n>>24,n>>16&255,n>>8&255,255&n,85,196,0,0]))},t.mdia=function(e){return t.box(t.types.mdia,t.mdhd(e.timescale,e.duration),t.hdlr(e.type),t.minf(e))},t.mfhd=function(e){return t.box(t.types.mfhd,new Uint8Array([0,0,0,0,e>>24,e>>16&255,e>>8&255,255&e]))},t.minf=function(e){return"audio"===e.type?t.box(t.types.minf,t.box(t.types.smhd,t.SMHD),t.DINF,t.stbl(e)):t.box(t.types.minf,t.box(t.types.vmhd,t.VMHD),t.DINF,t.stbl(e))},t.moof=function(e,r,i){return t.box(t.types.moof,t.mfhd(e),t.traf(i,r))},t.moov=function(e){for(var r=e.length,i=[];r--;)i[r]=t.trak(e[r]);return t.box.apply(null,[t.types.moov,t.mvhd(e[0].timescale,e[0].duration)].concat(i).concat(t.mvex(e)))},t.mvex=function(e){for(var r=e.length,i=[];r--;)i[r]=t.trex(e[r]);return t.box.apply(null,[t.types.mvex].concat(i))},t.mvhd=function(e,r){r*=e;var i=Math.floor(r/(fn+1)),n=Math.floor(r%(fn+1)),a=new Uint8Array([1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,3,e>>24&255,e>>16&255,e>>8&255,255&e,i>>24,i>>16&255,i>>8&255,255&i,n>>24,n>>16&255,n>>8&255,255&n,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255]);return t.box(t.types.mvhd,a)},t.sdtp=function(e){var r,i,n=e.samples||[],a=new Uint8Array(4+n.length);for(r=0;r<n.length;r++)i=n[r].flags,a[r+4]=i.dependsOn<<4|i.isDependedOn<<2|i.hasRedundancy;return t.box(t.types.sdtp,a)},t.stbl=function(e){return t.box(t.types.stbl,t.stsd(e),t.box(t.types.stts,t.STTS),t.box(t.types.stsc,t.STSC),t.box(t.types.stsz,t.STSZ),t.box(t.types.stco,t.STCO))},t.avc1=function(e){var r,i,n,a=[],s=[];for(r=0;r<e.sps.length;r++)n=(i=e.sps[r]).byteLength,a.push(n>>>8&255),a.push(255&n),a=a.concat(Array.prototype.slice.call(i));for(r=0;r<e.pps.length;r++)n=(i=e.pps[r]).byteLength,s.push(n>>>8&255),s.push(255&n),s=s.concat(Array.prototype.slice.call(i));var o=t.box(t.types.avcC,new Uint8Array([1,a[3],a[4],a[5],255,224|e.sps.length].concat(a).concat([e.pps.length]).concat(s))),l=e.width,u=e.height,h=e.pixelRatio[0],d=e.pixelRatio[1];return t.box(t.types.avc1,new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,l>>8&255,255&l,u>>8&255,255&u,0,72,0,0,0,72,0,0,0,0,0,0,0,1,18,100,97,105,108,121,109,111,116,105,111,110,47,104,108,115,46,106,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,17,17]),o,t.box(t.types.btrt,new Uint8Array([0,28,156,128,0,45,198,192,0,45,198,192])),t.box(t.types.pasp,new Uint8Array([h>>24,h>>16&255,h>>8&255,255&h,d>>24,d>>16&255,d>>8&255,255&d])))},t.esds=function(t){var e=t.config.length;return new Uint8Array([0,0,0,0,3,23+e,0,1,0,4,15+e,64,21,0,0,0,0,0,0,0,0,0,0,0,5].concat([e]).concat(t.config).concat([6,1,2]))},t.audioStsd=function(t){var e=t.samplerate;return new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,t.channelCount,0,16,0,0,0,0,e>>8&255,255&e,0,0])},t.mp4a=function(e){return t.box(t.types.mp4a,t.audioStsd(e),t.box(t.types.esds,t.esds(e)))},t.mp3=function(e){return t.box(t.types[".mp3"],t.audioStsd(e))},t.ac3=function(e){return t.box(t.types["ac-3"],t.audioStsd(e),t.box(t.types.dac3,e.config))},t.stsd=function(e){return"audio"===e.type?"mp3"===e.segmentCodec&&"mp3"===e.codec?t.box(t.types.stsd,t.STSD,t.mp3(e)):"ac3"===e.segmentCodec?t.box(t.types.stsd,t.STSD,t.ac3(e)):t.box(t.types.stsd,t.STSD,t.mp4a(e)):t.box(t.types.stsd,t.STSD,t.avc1(e))},t.tkhd=function(e){var r=e.id,i=e.duration*e.timescale,n=e.width,a=e.height,s=Math.floor(i/(fn+1)),o=Math.floor(i%(fn+1));return t.box(t.types.tkhd,new Uint8Array([1,0,0,7,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,3,r>>24&255,r>>16&255,r>>8&255,255&r,0,0,0,0,s>>24,s>>16&255,s>>8&255,255&s,o>>24,o>>16&255,o>>8&255,255&o,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,n>>8&255,255&n,0,0,a>>8&255,255&a,0,0]))},t.traf=function(e,r){var i=t.sdtp(e),n=e.id,a=Math.floor(r/(fn+1)),s=Math.floor(r%(fn+1));return t.box(t.types.traf,t.box(t.types.tfhd,new Uint8Array([0,0,0,0,n>>24,n>>16&255,n>>8&255,255&n])),t.box(t.types.tfdt,new Uint8Array([1,0,0,0,a>>24,a>>16&255,a>>8&255,255&a,s>>24,s>>16&255,s>>8&255,255&s])),t.trun(e,i.length+16+20+8+16+8+8),i)},t.trak=function(e){return e.duration=e.duration||4294967295,t.box(t.types.trak,t.tkhd(e),t.mdia(e))},t.trex=function(e){var r=e.id;return t.box(t.types.trex,new Uint8Array([0,0,0,0,r>>24,r>>16&255,r>>8&255,255&r,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1]))},t.trun=function(e,r){var i,n,a,s,o,l,u=e.samples||[],h=u.length,d=12+16*h,c=new Uint8Array(d);for(r+=8+d,c.set(["video"===e.type?1:0,0,15,1,h>>>24&255,h>>>16&255,h>>>8&255,255&h,r>>>24&255,r>>>16&255,r>>>8&255,255&r],0),i=0;i<h;i++)a=(n=u[i]).duration,s=n.size,o=n.flags,l=n.cts,c.set([a>>>24&255,a>>>16&255,a>>>8&255,255&a,s>>>24&255,s>>>16&255,s>>>8&255,255&s,o.isLeading<<2|o.dependsOn,o.isDependedOn<<6|o.hasRedundancy<<4|o.paddingValue<<1|o.isNonSync,61440&o.degradPrio,15&o.degradPrio,l>>>24&255,l>>>16&255,l>>>8&255,255&l],12+16*i);return t.box(t.types.trun,c)},t.initSegment=function(e){t.types||t.init();var r=t.moov(e);return Gt(t.FTYP,r)},t}();gn.types=void 0,gn.HDLR_TYPES=void 0,gn.STTS=void 0,gn.STSC=void 0,gn.STCO=void 0,gn.STSZ=void 0,gn.VMHD=void 0,gn.SMHD=void 0,gn.STSD=void 0,gn.FTYP=void 0,gn.DINF=void 0;var vn=9e4;function mn(t,e,r,i){void 0===r&&(r=1),void 0===i&&(i=!1);var n=t*e*r;return i?Math.round(n):n}function pn(t,e){return void 0===e&&(e=!1),mn(t,1e3,1/vn,e)}var yn=null,En=null,Tn=function(){function t(t,e,r,i){if(this.observer=void 0,this.config=void 0,this.typeSupported=void 0,this.ISGenerated=!1,this._initPTS=null,this._initDTS=null,this.nextAvcDts=null,this.nextAudioPts=null,this.videoSampleDuration=null,this.isAudioContiguous=!1,this.isVideoContiguous=!1,this.videoTrackConfig=void 0,this.observer=t,this.config=e,this.typeSupported=r,this.ISGenerated=!1,null===yn){var n=(navigator.userAgent||"").match(/Chrome\/(\d+)/i);yn=n?parseInt(n[1]):0}if(null===En){var a=navigator.userAgent.match(/Safari\/(\d+)/i);En=a?parseInt(a[1]):0}}var e=t.prototype;return e.destroy=function(){this.config=this.videoTrackConfig=this._initPTS=this._initDTS=null},e.resetTimeStamp=function(t){w.log("[mp4-remuxer]: initPTS & initDTS reset"),this._initPTS=this._initDTS=t},e.resetNextTimestamp=function(){w.log("[mp4-remuxer]: reset next timestamp"),this.isVideoContiguous=!1,this.isAudioContiguous=!1},e.resetInitSegment=function(){w.log("[mp4-remuxer]: ISGenerated flag reset"),this.ISGenerated=!1,this.videoTrackConfig=void 0},e.getVideoStartPts=function(t){var e=!1,r=t.reduce((function(t,r){var i=r.pts-t;return i<-4294967296?(e=!0,Sn(t,r.pts)):i>0?t:r.pts}),t[0].pts);return e&&w.debug("PTS rollover detected"),r},e.remux=function(t,e,r,i,n,a,s,o){var l,u,h,d,c,f,g=n,v=n,m=t.pid>-1,p=e.pid>-1,y=e.samples.length,E=t.samples.length>0,T=s&&y>0||y>1;if((!m||E)&&(!p||T)||this.ISGenerated||s){if(this.ISGenerated){var S,L,A,R,k=this.videoTrackConfig;!k||e.width===k.width&&e.height===k.height&&(null==(S=e.pixelRatio)?void 0:S[0])===(null==(L=k.pixelRatio)?void 0:L[0])&&(null==(A=e.pixelRatio)?void 0:A[1])===(null==(R=k.pixelRatio)?void 0:R[1])||this.resetInitSegment()}else h=this.generateIS(t,e,n,a);var b,D=this.isVideoContiguous,I=-1;if(T&&(I=function(t){for(var e=0;e<t.length;e++)if(t[e].key)return e;return-1}(e.samples),!D&&this.config.forceKeyFrameOnDiscontinuity))if(f=!0,I>0){w.warn("[mp4-remuxer]: Dropped "+I+" out of "+y+" video samples due to a missing keyframe");var C=this.getVideoStartPts(e.samples);e.samples=e.samples.slice(I),e.dropped+=I,b=v+=(e.samples[0].pts-C)/e.inputTimeScale}else-1===I&&(w.warn("[mp4-remuxer]: No keyframe found out of "+y+" video samples"),f=!1);if(this.ISGenerated){if(E&&T){var _=this.getVideoStartPts(e.samples),x=(Sn(t.samples[0].pts,_)-_)/e.inputTimeScale;g+=Math.max(0,x),v+=Math.max(0,-x)}if(E){if(t.samplerate||(w.warn("[mp4-remuxer]: regenerate InitSegment as audio detected"),h=this.generateIS(t,e,n,a)),u=this.remuxAudio(t,g,this.isAudioContiguous,a,p||T||o===we?v:void 0),T){var P=u?u.endPTS-u.startPTS:0;e.inputTimeScale||(w.warn("[mp4-remuxer]: regenerate InitSegment as video detected"),h=this.generateIS(t,e,n,a)),l=this.remuxVideo(e,v,D,P)}}else T&&(l=this.remuxVideo(e,v,D,0));l&&(l.firstKeyFrame=I,l.independent=-1!==I,l.firstKeyFramePTS=b)}}return this.ISGenerated&&this._initPTS&&this._initDTS&&(r.samples.length&&(c=Ln(r,n,this._initPTS,this._initDTS)),i.samples.length&&(d=An(i,n,this._initPTS))),{audio:u,video:l,initSegment:h,independent:f,text:d,id3:c}},e.generateIS=function(t,e,r,i){var n,a,s,o=t.samples,l=e.samples,u=this.typeSupported,h={},d=this._initPTS,c=!d||i,f="audio/mp4";if(c&&(n=a=1/0),t.config&&o.length){switch(t.timescale=t.samplerate,t.segmentCodec){case"mp3":u.mpeg?(f="audio/mpeg",t.codec=""):u.mp3&&(t.codec="mp3");break;case"ac3":t.codec="ac-3"}h.audio={id:"audio",container:f,codec:t.codec,initSegment:"mp3"===t.segmentCodec&&u.mpeg?new Uint8Array(0):gn.initSegment([t]),metadata:{channelCount:t.channelCount}},c&&(s=t.inputTimeScale,d&&s===d.timescale?c=!1:n=a=o[0].pts-Math.round(s*r))}if(e.sps&&e.pps&&l.length){if(e.timescale=e.inputTimeScale,h.video={id:"main",container:"video/mp4",codec:e.codec,initSegment:gn.initSegment([e]),metadata:{width:e.width,height:e.height}},c)if(s=e.inputTimeScale,d&&s===d.timescale)c=!1;else{var g=this.getVideoStartPts(l),v=Math.round(s*r);a=Math.min(a,Sn(l[0].dts,g)-v),n=Math.min(n,g-v)}this.videoTrackConfig={width:e.width,height:e.height,pixelRatio:e.pixelRatio}}if(Object.keys(h).length)return this.ISGenerated=!0,c?(this._initPTS={baseTime:n,timescale:s},this._initDTS={baseTime:a,timescale:s}):n=s=void 0,{tracks:h,initPTS:n,timescale:s}},e.remuxVideo=function(t,e,r,i){var n,a,s=t.inputTimeScale,l=t.samples,u=[],h=l.length,d=this._initPTS,c=this.nextAvcDts,f=8,g=this.videoSampleDuration,v=Number.POSITIVE_INFINITY,m=Number.NEGATIVE_INFINITY,p=!1;if(!r||null===c){var y=e*s,E=l[0].pts-Sn(l[0].dts,l[0].pts);yn&&null!==c&&Math.abs(y-E-c)<15e3?r=!0:c=y-E}for(var T=d.baseTime*s/d.timescale,R=0;R<h;R++){var k=l[R];k.pts=Sn(k.pts-T,c),k.dts=Sn(k.dts-T,c),k.dts<l[R>0?R-1:R].dts&&(p=!0)}p&&l.sort((function(t,e){var r=t.dts-e.dts,i=t.pts-e.pts;return r||i})),n=l[0].dts;var b=(a=l[l.length-1].dts)-n,D=b?Math.round(b/(h-1)):g||t.inputTimeScale/30;if(r){var I=n-c,C=I>D,_=I<-1;if((C||_)&&(C?w.warn("AVC: "+pn(I,!0)+" ms ("+I+"dts) hole between fragments detected at "+e.toFixed(3)):w.warn("AVC: "+pn(-I,!0)+" ms ("+I+"dts) overlapping between fragments detected at "+e.toFixed(3)),!_||c>=l[0].pts||yn)){n=c;var x=l[0].pts-I;if(C)l[0].dts=n,l[0].pts=x;else for(var P=0;P<l.length&&!(l[P].dts>x);P++)l[P].dts-=I,l[P].pts-=I;w.log("Video: Initial PTS/DTS adjusted: "+pn(x,!0)+"/"+pn(n,!0)+", delta: "+pn(I,!0)+" ms")}}for(var F=0,M=0,O=n=Math.max(0,n),N=0;N<h;N++){for(var U=l[N],B=U.units,G=B.length,K=0,H=0;H<G;H++)K+=B[H].data.length;M+=K,F+=G,U.length=K,U.dts<O?(U.dts=O,O+=D/4|0||1):O=U.dts,v=Math.min(U.pts,v),m=Math.max(U.pts,m)}a=l[h-1].dts;var V,Y=M+4*F+8;try{V=new Uint8Array(Y)}catch(t){return void this.observer.emit(S.ERROR,S.ERROR,{type:L.MUX_ERROR,details:A.REMUX_ALLOC_ERROR,fatal:!1,error:t,bytes:Y,reason:"fail allocating video mdat "+Y})}var W=new DataView(V.buffer);W.setUint32(0,Y),V.set(gn.types.mdat,4);for(var j=!1,q=Number.POSITIVE_INFINITY,X=Number.POSITIVE_INFINITY,z=Number.NEGATIVE_INFINITY,Q=Number.NEGATIVE_INFINITY,J=0;J<h;J++){for(var $=l[J],Z=$.units,tt=0,et=0,rt=Z.length;et<rt;et++){var it=Z[et],nt=it.data,at=it.data.byteLength;W.setUint32(f,at),f+=4,V.set(nt,f),f+=at,tt+=4+at}var st=void 0;if(J<h-1)g=l[J+1].dts-$.dts,st=l[J+1].pts-$.pts;else{var ot=this.config,lt=J>0?$.dts-l[J-1].dts:D;if(st=J>0?$.pts-l[J-1].pts:D,ot.stretchShortVideoTrack&&null!==this.nextAudioPts){var ut=Math.floor(ot.maxBufferHole*s),ht=(i?v+i*s:this.nextAudioPts)-$.pts;ht>ut?((g=ht-lt)<0?g=lt:j=!0,w.log("[mp4-remuxer]: It is approximately "+ht/90+" ms to the next segment; using duration "+g/90+" ms for the last video frame.")):g=lt}else g=lt}var dt=Math.round($.pts-$.dts);q=Math.min(q,g),z=Math.max(z,g),X=Math.min(X,st),Q=Math.max(Q,st),u.push(new kn($.key,g,tt,dt))}if(u.length)if(yn){if(yn<70){var ct=u[0].flags;ct.dependsOn=2,ct.isNonSync=0}}else if(En&&Q-X<z-q&&D/z<.025&&0===u[0].cts){w.warn("Found irregular gaps in sample duration. Using PTS instead of DTS to determine MP4 sample duration.");for(var ft=n,gt=0,vt=u.length;gt<vt;gt++){var mt=ft+u[gt].duration,pt=ft+u[gt].cts;if(gt<vt-1){var yt=mt+u[gt+1].cts;u[gt].duration=yt-pt}else u[gt].duration=gt?u[gt-1].duration:D;u[gt].cts=0,ft=mt}}g=j||!g?D:g,this.nextAvcDts=c=a+g,this.videoSampleDuration=g,this.isVideoContiguous=!0;var Et={data1:gn.moof(t.sequenceNumber++,n,o({},t,{samples:u})),data2:V,startPTS:v/s,endPTS:(m+g)/s,startDTS:n/s,endDTS:c/s,type:"video",hasAudio:!1,hasVideo:!0,nb:u.length,dropped:t.dropped};return t.samples=[],t.dropped=0,Et},e.getSamplesPerFrame=function(t){switch(t.segmentCodec){case"mp3":return 1152;case"ac3":return 1536;default:return 1024}},e.remuxAudio=function(t,e,r,i,n){var a=t.inputTimeScale,s=a/(t.samplerate?t.samplerate:a),l=this.getSamplesPerFrame(t),u=l*s,h=this._initPTS,d="mp3"===t.segmentCodec&&this.typeSupported.mpeg,c=[],f=void 0!==n,g=t.samples,v=d?0:8,m=this.nextAudioPts||-1,p=e*a,y=h.baseTime*a/h.timescale;if(this.isAudioContiguous=r=r||g.length&&m>0&&(i&&Math.abs(p-m)<9e3||Math.abs(Sn(g[0].pts-y,p)-m)<20*u),g.forEach((function(t){t.pts=Sn(t.pts-y,p)})),!r||m<0){if(g=g.filter((function(t){return t.pts>=0})),!g.length)return;m=0===n?0:i&&!f?Math.max(0,p):g[0].pts}if("aac"===t.segmentCodec)for(var E=this.config.maxAudioFramesDrift,T=0,R=m;T<g.length;T++){var k=g[T],b=k.pts,D=b-R,I=Math.abs(1e3*D/a);if(D<=-E*u&&f)0===T&&(w.warn("Audio frame @ "+(b/a).toFixed(3)+"s overlaps nextAudioPts by "+Math.round(1e3*D/a)+" ms."),this.nextAudioPts=m=R=b);else if(D>=E*u&&I<1e4&&f){var C=Math.round(D/u);(R=b-C*u)<0&&(C--,R+=u),0===T&&(this.nextAudioPts=m=R),w.warn("[mp4-remuxer]: Injecting "+C+" audio frame @ "+(R/a).toFixed(3)+"s due to "+Math.round(1e3*D/a)+" ms gap.");for(var _=0;_<C;_++){var x=Math.max(R,0),P=cn.getSilentFrame(t.manifestCodec||t.codec,t.channelCount);P||(w.log("[mp4-remuxer]: Unable to get silent frame for given audio codec; duplicating last frame instead."),P=k.unit.subarray()),g.splice(T,0,{unit:P,pts:x}),R+=u,T++}}k.pts=R,R+=u}for(var F,M=null,O=null,N=0,U=g.length;U--;)N+=g[U].unit.byteLength;for(var B=0,G=g.length;B<G;B++){var K=g[B],H=K.unit,V=K.pts;if(null!==O)c[B-1].duration=Math.round((V-O)/s);else{if(r&&"aac"===t.segmentCodec&&(V=m),M=V,!(N>0))return;N+=v;try{F=new Uint8Array(N)}catch(t){return void this.observer.emit(S.ERROR,S.ERROR,{type:L.MUX_ERROR,details:A.REMUX_ALLOC_ERROR,fatal:!1,error:t,bytes:N,reason:"fail allocating audio mdat "+N})}d||(new DataView(F.buffer).setUint32(0,N),F.set(gn.types.mdat,4))}F.set(H,v);var Y=H.byteLength;v+=Y,c.push(new kn(!0,l,Y,0)),O=V}var W=c.length;if(W){var j=c[c.length-1];this.nextAudioPts=m=O+s*j.duration;var q=d?new Uint8Array(0):gn.moof(t.sequenceNumber++,M/s,o({},t,{samples:c}));t.samples=[];var X=M/a,z=m/a,Q={data1:q,data2:F,startPTS:X,endPTS:z,startDTS:X,endDTS:z,type:"audio",hasAudio:!0,hasVideo:!1,nb:W};return this.isAudioContiguous=!0,Q}},e.remuxEmptyAudio=function(t,e,r,i){var n=t.inputTimeScale,a=n/(t.samplerate?t.samplerate:n),s=this.nextAudioPts,o=this._initDTS,l=9e4*o.baseTime/o.timescale,u=(null!==s?s:i.startDTS*n)+l,h=i.endDTS*n+l,d=1024*a,c=Math.ceil((h-u)/d),f=cn.getSilentFrame(t.manifestCodec||t.codec,t.channelCount);if(w.warn("[mp4-remuxer]: remux empty Audio"),f){for(var g=[],v=0;v<c;v++){var m=u+v*d;g.push({unit:f,pts:m,dts:m})}return t.samples=g,this.remuxAudio(t,e,r,!1)}w.trace("[mp4-remuxer]: Unable to remuxEmptyAudio since we were unable to get a silent frame for given audio codec")},t}();function Sn(t,e){var r;if(null===e)return t;for(r=e<t?-8589934592:8589934592;Math.abs(t-e)>4294967296;)t+=r;return t}function Ln(t,e,r,i){var n=t.samples.length;if(n){for(var a=t.inputTimeScale,s=0;s<n;s++){var o=t.samples[s];o.pts=Sn(o.pts-r.baseTime*a/r.timescale,e*a)/a,o.dts=Sn(o.dts-i.baseTime*a/i.timescale,e*a)/a}var l=t.samples;return t.samples=[],{samples:l}}}function An(t,e,r){var i=t.samples.length;if(i){for(var n=t.inputTimeScale,a=0;a<i;a++){var s=t.samples[a];s.pts=Sn(s.pts-r.baseTime*n/r.timescale,e*n)/n}t.samples.sort((function(t,e){return t.pts-e.pts}));var o=t.samples;return t.samples=[],{samples:o}}}var Rn,kn=function(t,e,r,i){this.size=void 0,this.duration=void 0,this.cts=void 0,this.flags=void 0,this.duration=e,this.size=r,this.cts=i,this.flags={isLeading:0,isDependedOn:0,hasRedundancy:0,degradPrio:0,dependsOn:t?2:1,isNonSync:t?0:1}},bn=function(){function t(){this.emitInitSegment=!1,this.audioCodec=void 0,this.videoCodec=void 0,this.initData=void 0,this.initPTS=null,this.initTracks=void 0,this.lastEndTime=null}var e=t.prototype;return e.destroy=function(){},e.resetTimeStamp=function(t){this.initPTS=t,this.lastEndTime=null},e.resetNextTimestamp=function(){this.lastEndTime=null},e.resetInitSegment=function(t,e,r,i){this.audioCodec=e,this.videoCodec=r,this.generateInitSegment(function(t,e){if(!t||!e)return t;var r=e.keyId;return r&&e.isCommonEncryption&&_t(t,["moov","trak"]).forEach((function(t){var e=_t(t,["mdia","minf","stbl","stsd"])[0].subarray(8),i=_t(e,["enca"]),n=i.length>0;n||(i=_t(e,["encv"])),i.forEach((function(t){_t(n?t.subarray(28):t.subarray(78),["sinf"]).forEach((function(t){var e=Ut(t);if(e){var i=e.subarray(8,24);i.some((function(t){return 0!==t}))||(w.log("[eme] Patching keyId in 'enc"+(n?"a":"v")+">sinf>>tenc' box: "+Lt(i)+" -> "+Lt(r)),e.set(r,8))}}))}))})),t}(t,i)),this.emitInitSegment=!0},e.generateInitSegment=function(t){var e=this.audioCodec,r=this.videoCodec;if(null==t||!t.byteLength)return this.initTracks=void 0,void(this.initData=void 0);var i=this.initData=Pt(t);i.audio&&(e=Dn(i.audio,O)),i.video&&(r=Dn(i.video,N));var n={};i.audio&&i.video?n.audiovideo={container:"video/mp4",codec:e+","+r,initSegment:t,id:"main"}:i.audio?n.audio={container:"audio/mp4",codec:e,initSegment:t,id:"audio"}:i.video?n.video={container:"video/mp4",codec:r,initSegment:t,id:"main"}:w.warn("[passthrough-remuxer.ts]: initSegment does not contain moov or trak boxes."),this.initTracks=n},e.remux=function(t,e,r,i,n,a){var s,o,l=this.initPTS,u=this.lastEndTime,h={audio:void 0,video:void 0,text:i,id3:r,initSegment:void 0};y(u)||(u=this.lastEndTime=n||0);var d=e.samples;if(null==d||!d.length)return h;var c={initPTS:void 0,timescale:1},f=this.initData;if(null!=(s=f)&&s.length||(this.generateInitSegment(d),f=this.initData),null==(o=f)||!o.length)return w.warn("[passthrough-remuxer.ts]: Failed to generate initSegment."),h;this.emitInitSegment&&(c.tracks=this.initTracks,this.emitInitSegment=!1);var g=function(t,e){for(var r=0,i=0,n=0,a=_t(t,["moof","traf"]),s=0;s<a.length;s++){var o=a[s],l=_t(o,["tfhd"])[0],u=e[It(l,4)];if(u){var h=u.default,d=It(l,0)|(null==h?void 0:h.flags),c=null==h?void 0:h.duration;8&d&&(c=It(l,2&d?12:8));for(var f=u.timescale||9e4,g=_t(o,["trun"]),v=0;v<g.length;v++)!(r=Bt(g[v]))&&c&&(r=c*It(g[v],4)),u.type===N?i+=r/f:u.type===O&&(n+=r/f)}}if(0===i&&0===n){for(var m=0,p=_t(t,["sidx"]),y=0;y<p.length;y++){var E=xt(p[y]);null!=E&&E.references&&(m+=E.references.reduce((function(t,e){return t+e.info.duration||0}),0))}return m}return i||n}(d,f),v=function(t,e){return _t(e,["moof","traf"]).reduce((function(e,r){var i=_t(r,["tfdt"])[0],n=i[0],a=_t(r,["tfhd"]).reduce((function(e,r){var a=It(r,4),s=t[a];if(s){var o=It(i,4);if(1===n){if(o===At)return w.warn("[mp4-demuxer]: Ignoring assumed invalid signed 64-bit track fragment decode time"),e;o*=At+1,o+=It(i,8)}var l=o/(s.timescale||9e4);if(y(l)&&(null===e||l<e))return l}return e}),null);return null!==a&&y(a)&&(null===e||a<e)?a:e}),null)}(f,d),m=null===v?n:v;(function(t,e,r,i){if(null===t)return!0;var n=Math.max(i,1),a=e-t.baseTime/t.timescale;return Math.abs(a-r)>n}(l,m,n,g)||c.timescale!==l.timescale&&a)&&(c.initPTS=m-n,l&&1===l.timescale&&w.warn("Adjusting initPTS by "+(c.initPTS-l.baseTime)),this.initPTS=l={baseTime:c.initPTS,timescale:1});var p=t?m-l.baseTime/l.timescale:u,E=p+g;!function(t,e,r){_t(e,["moof","traf"]).forEach((function(e){_t(e,["tfhd"]).forEach((function(i){var n=It(i,4),a=t[n];if(a){var s=a.timescale||9e4;_t(e,["tfdt"]).forEach((function(t){var e=t[0],i=r*s;if(i){var n=It(t,4);if(0===e)n-=i,Ct(t,4,n=Math.max(n,0));else{n*=Math.pow(2,32),n+=It(t,8),n-=i,n=Math.max(n,0);var a=Math.floor(n/(At+1)),o=Math.floor(n%(At+1));Ct(t,4,a),Ct(t,8,o)}}}))}}))}))}(f,d,l.baseTime/l.timescale),g>0?this.lastEndTime=E:(w.warn("Duration parsed from mp4 should be greater than zero"),this.resetNextTimestamp());var T=!!f.audio,S=!!f.video,L="";T&&(L+="audio"),S&&(L+="video");var A={data1:d,startPTS:p,startDTS:p,endPTS:E,endDTS:E,type:L,hasAudio:T,hasVideo:S,nb:1,dropped:0};return h.audio="audio"===A.type?A:void 0,h.video="audio"!==A.type?A:void 0,h.initSegment=c,h.id3=Ln(r,n,l,l),i.samples.length&&(h.text=An(i,n,l)),h},t}();function Dn(t,e){var r=null==t?void 0:t.codec;if(r&&r.length>4)return r;if(e===O){if("ec-3"===r||"ac-3"===r||"alac"===r)return r;if("fLaC"===r||"Opus"===r)return ue(r,!1);var i="mp4a.40.5";return w.info('Parsed audio codec "'+r+'" or audio object type not handled. Using "'+i+'"'),i}return w.warn('Unhandled video codec "'+r+'"'),"hvc1"===r||"hev1"===r?"hvc1.1.6.L120.90":"av01"===r?"av01.0.04M.08":"avc1.42e01e"}try{Rn=self.performance.now.bind(self.performance)}catch(t){w.debug("Unable to use Performance API on this environment"),Rn=null==j?void 0:j.Date.now}var In=[{demux:zi,remux:bn},{demux:an,remux:Tn},{demux:qi,remux:Tn},{demux:dn,remux:Tn}];In.splice(2,0,{demux:Ji,remux:Tn});var wn=function(){function t(t,e,r,i,n){this.async=!1,this.observer=void 0,this.typeSupported=void 0,this.config=void 0,this.vendor=void 0,this.id=void 0,this.demuxer=void 0,this.remuxer=void 0,this.decrypter=void 0,this.probe=void 0,this.decryptionPromise=null,this.transmuxConfig=void 0,this.currentTransmuxState=void 0,this.observer=t,this.typeSupported=e,this.config=r,this.vendor=i,this.id=n}var e=t.prototype;return e.configure=function(t){this.transmuxConfig=t,this.decrypter&&this.decrypter.reset()},e.push=function(t,e,r,i){var n=this,a=r.transmuxing;a.executeStart=Rn();var s=new Uint8Array(t),o=this.currentTransmuxState,l=this.transmuxConfig;i&&(this.currentTransmuxState=i);var u=i||o,h=u.contiguous,d=u.discontinuity,c=u.trackSwitch,f=u.accurateTimeOffset,g=u.timeOffset,v=u.initSegmentChange,m=l.audioCodec,p=l.videoCodec,y=l.defaultInitPts,E=l.duration,T=l.initSegmentData,R=function(t,e){var r=null;return t.byteLength>0&&null!=(null==e?void 0:e.key)&&null!==e.iv&&null!=e.method&&(r=e),r}(s,e);if(R&&"AES-128"===R.method){var k=this.getDecrypter();if(!k.isSync())return this.decryptionPromise=k.webCryptoDecrypt(s,R.key.buffer,R.iv.buffer).then((function(t){var e=n.push(t,null,r);return n.decryptionPromise=null,e})),this.decryptionPromise;var b=k.softwareDecrypt(s,R.key.buffer,R.iv.buffer);if(r.part>-1&&(b=k.flush()),!b)return a.executeEnd=Rn(),Cn(r);s=new Uint8Array(b)}var D=this.needsProbing(d,c);if(D){var I=this.configureTransmuxer(s);if(I)return w.warn("[transmuxer] "+I.message),this.observer.emit(S.ERROR,S.ERROR,{type:L.MEDIA_ERROR,details:A.FRAG_PARSING_ERROR,fatal:!1,error:I,reason:I.message}),a.executeEnd=Rn(),Cn(r)}(d||c||v||D)&&this.resetInitSegment(T,m,p,E,e),(d||v||D)&&this.resetInitialTimestamp(y),h||this.resetContiguity();var C=this.transmux(s,R,g,f,r),_=this.currentTransmuxState;return _.contiguous=!0,_.discontinuity=!1,_.trackSwitch=!1,a.executeEnd=Rn(),C},e.flush=function(t){var e=this,r=t.transmuxing;r.executeStart=Rn();var i=this.decrypter,n=this.currentTransmuxState,a=this.decryptionPromise;if(a)return a.then((function(){return e.flush(t)}));var s=[],o=n.timeOffset;if(i){var l=i.flush();l&&s.push(this.push(l,null,t))}var u=this.demuxer,h=this.remuxer;if(!u||!h)return r.executeEnd=Rn(),[Cn(t)];var d=u.flush(o);return _n(d)?d.then((function(r){return e.flushRemux(s,r,t),s})):(this.flushRemux(s,d,t),s)},e.flushRemux=function(t,e,r){var i=e.audioTrack,n=e.videoTrack,a=e.id3Track,s=e.textTrack,o=this.currentTransmuxState,l=o.accurateTimeOffset,u=o.timeOffset;w.log("[transmuxer.ts]: Flushed fragment "+r.sn+(r.part>-1?" p: "+r.part:"")+" of level "+r.level);var h=this.remuxer.remux(i,n,a,s,u,l,!0,this.id);t.push({remuxResult:h,chunkMeta:r}),r.transmuxing.executeEnd=Rn()},e.resetInitialTimestamp=function(t){var e=this.demuxer,r=this.remuxer;e&&r&&(e.resetTimeStamp(t),r.resetTimeStamp(t))},e.resetContiguity=function(){var t=this.demuxer,e=this.remuxer;t&&e&&(t.resetContiguity(),e.resetNextTimestamp())},e.resetInitSegment=function(t,e,r,i,n){var a=this.demuxer,s=this.remuxer;a&&s&&(a.resetInitSegment(t,e,r,i),s.resetInitSegment(t,e,r,n))},e.destroy=function(){this.demuxer&&(this.demuxer.destroy(),this.demuxer=void 0),this.remuxer&&(this.remuxer.destroy(),this.remuxer=void 0)},e.transmux=function(t,e,r,i,n){return e&&"SAMPLE-AES"===e.method?this.transmuxSampleAes(t,e,r,i,n):this.transmuxUnencrypted(t,r,i,n)},e.transmuxUnencrypted=function(t,e,r,i){var n=this.demuxer.demux(t,e,!1,!this.config.progressive),a=n.audioTrack,s=n.videoTrack,o=n.id3Track,l=n.textTrack;return{remuxResult:this.remuxer.remux(a,s,o,l,e,r,!1,this.id),chunkMeta:i}},e.transmuxSampleAes=function(t,e,r,i,n){var a=this;return this.demuxer.demuxSampleAes(t,e,r).then((function(t){return{remuxResult:a.remuxer.remux(t.audioTrack,t.videoTrack,t.id3Track,t.textTrack,r,i,!1,a.id),chunkMeta:n}}))},e.configureTransmuxer=function(t){for(var e,r=this.config,i=this.observer,n=this.typeSupported,a=this.vendor,s=0,o=In.length;s<o;s++){var l;if(null!=(l=In[s].demux)&&l.probe(t)){e=In[s];break}}if(!e)return new Error("Failed to find demuxer by probing fragment data");var u=this.demuxer,h=this.remuxer,d=e.remux,c=e.demux;h&&h instanceof d||(this.remuxer=new d(i,r,n,a)),u&&u instanceof c||(this.demuxer=new c(i,r,n),this.probe=c.probe)},e.needsProbing=function(t,e){return!this.demuxer||!this.remuxer||t||e},e.getDecrypter=function(){var t=this.decrypter;return t||(t=this.decrypter=new hi(this.config)),t},t}(),Cn=function(t){return{remuxResult:{},chunkMeta:t}};function _n(t){return"then"in t&&t.then instanceof Function}var xn=function(t,e,r,i,n){this.audioCodec=void 0,this.videoCodec=void 0,this.initSegmentData=void 0,this.duration=void 0,this.defaultInitPts=void 0,this.audioCodec=t,this.videoCodec=e,this.initSegmentData=r,this.duration=i,this.defaultInitPts=n||null},Pn=function(t,e,r,i,n,a){this.discontinuity=void 0,this.contiguous=void 0,this.accurateTimeOffset=void 0,this.trackSwitch=void 0,this.timeOffset=void 0,this.initSegmentChange=void 0,this.discontinuity=t,this.contiguous=e,this.accurateTimeOffset=r,this.trackSwitch=i,this.timeOffset=n,this.initSegmentChange=a},Fn={exports:{}};!function(t){var e=Object.prototype.hasOwnProperty,r="~";function i(){}function n(t,e,r){this.fn=t,this.context=e,this.once=r||!1}function a(t,e,i,a,s){if("function"!=typeof i)throw new TypeError("The listener must be a function");var o=new n(i,a||t,s),l=r?r+e:e;return t._events[l]?t._events[l].fn?t._events[l]=[t._events[l],o]:t._events[l].push(o):(t._events[l]=o,t._eventsCount++),t}function s(t,e){0==--t._eventsCount?t._events=new i:delete t._events[e]}function o(){this._events=new i,this._eventsCount=0}Object.create&&(i.prototype=Object.create(null),(new i).__proto__||(r=!1)),o.prototype.eventNames=function(){var t,i,n=[];if(0===this._eventsCount)return n;for(i in t=this._events)e.call(t,i)&&n.push(r?i.slice(1):i);return Object.getOwnPropertySymbols?n.concat(Object.getOwnPropertySymbols(t)):n},o.prototype.listeners=function(t){var e=r?r+t:t,i=this._events[e];if(!i)return[];if(i.fn)return[i.fn];for(var n=0,a=i.length,s=new Array(a);n<a;n++)s[n]=i[n].fn;return s},o.prototype.listenerCount=function(t){var e=r?r+t:t,i=this._events[e];return i?i.fn?1:i.length:0},o.prototype.emit=function(t,e,i,n,a,s){var o=r?r+t:t;if(!this._events[o])return!1;var l,u,h=this._events[o],d=arguments.length;if(h.fn){switch(h.once&&this.removeListener(t,h.fn,void 0,!0),d){case 1:return h.fn.call(h.context),!0;case 2:return h.fn.call(h.context,e),!0;case 3:return h.fn.call(h.context,e,i),!0;case 4:return h.fn.call(h.context,e,i,n),!0;case 5:return h.fn.call(h.context,e,i,n,a),!0;case 6:return h.fn.call(h.context,e,i,n,a,s),!0}for(u=1,l=new Array(d-1);u<d;u++)l[u-1]=arguments[u];h.fn.apply(h.context,l)}else{var c,f=h.length;for(u=0;u<f;u++)switch(h[u].once&&this.removeListener(t,h[u].fn,void 0,!0),d){case 1:h[u].fn.call(h[u].context);break;case 2:h[u].fn.call(h[u].context,e);break;case 3:h[u].fn.call(h[u].context,e,i);break;case 4:h[u].fn.call(h[u].context,e,i,n);break;default:if(!l)for(c=1,l=new Array(d-1);c<d;c++)l[c-1]=arguments[c];h[u].fn.apply(h[u].context,l)}}return!0},o.prototype.on=function(t,e,r){return a(this,t,e,r,!1)},o.prototype.once=function(t,e,r){return a(this,t,e,r,!0)},o.prototype.removeListener=function(t,e,i,n){var a=r?r+t:t;if(!this._events[a])return this;if(!e)return s(this,a),this;var o=this._events[a];if(o.fn)o.fn!==e||n&&!o.once||i&&o.context!==i||s(this,a);else{for(var l=0,u=[],h=o.length;l<h;l++)(o[l].fn!==e||n&&!o[l].once||i&&o[l].context!==i)&&u.push(o[l]);u.length?this._events[a]=1===u.length?u[0]:u:s(this,a)}return this},o.prototype.removeAllListeners=function(t){var e;return t?(e=r?r+t:t,this._events[e]&&s(this,e)):(this._events=new i,this._eventsCount=0),this},o.prototype.off=o.prototype.removeListener,o.prototype.addListener=o.prototype.on,o.prefixed=r,o.EventEmitter=o,t.exports=o}(Fn);var Mn=v(Fn.exports);function On(t,e){if(!((r=e.remuxResult).audio||r.video||r.text||r.id3||r.initSegment))return!1;var r,i=[],n=e.remuxResult,a=n.audio,s=n.video;return a&&Nn(i,a),s&&Nn(i,s),t.postMessage({event:"transmuxComplete",data:e},i),!0}function Nn(t,e){e.data1&&t.push(e.data1.buffer),e.data2&&t.push(e.data2.buffer)}function Un(t,e,r){e.reduce((function(e,r){return On(t,r)||e}),!1)||t.postMessage({event:"transmuxComplete",data:e[0]}),t.postMessage({event:"flush",data:r})}void 0!==e&&e&&function(t){var e=new Mn,r=function(e,r){t.postMessage({event:e,data:r})};e.on(S.FRAG_DECRYPTED,r),e.on(S.ERROR,r);var i=function(){var t=function(t){var e=function(e){r("workerLog",{logType:t,message:e})};w[t]=e};for(var e in w)t(e)};t.addEventListener("message",(function(n){var a=n.data;switch(a.cmd){case"init":var s=JSON.parse(a.config);t.transmuxer=new wn(e,a.typeSupported,s,a.vendor,a.id),I(s.debug,a.id),i(),r("init",null);break;case"configure":t.transmuxer.configure(a.config);break;case"demux":var o=t.transmuxer.push(a.data,a.decryptdata,a.chunkMeta,a.state);_n(o)?(t.transmuxer.async=!0,o.then((function(e){On(t,e)})).catch((function(t){r(S.ERROR,{type:L.MEDIA_ERROR,details:A.FRAG_PARSING_ERROR,chunkMeta:a.chunkMeta,fatal:!1,error:t,err:t,reason:"transmuxer-worker push error"})}))):(t.transmuxer.async=!1,On(t,o));break;case"flush":var l=a.chunkMeta,u=t.transmuxer.flush(l);_n(u)||t.transmuxer.async?(_n(u)||(u=Promise.resolve(u)),u.then((function(e){Un(t,e,l)})).catch((function(t){r(S.ERROR,{type:L.MEDIA_ERROR,details:A.FRAG_PARSING_ERROR,chunkMeta:a.chunkMeta,fatal:!1,error:t,err:t,reason:"transmuxer-worker flush error"})}))):Un(t,u,l)}}))}(self);var Bn=function(){function e(e,r,i,n){var a=this;this.error=null,this.hls=void 0,this.id=void 0,this.observer=void 0,this.frag=null,this.part=null,this.useWorker=void 0,this.workerContext=null,this.onwmsg=void 0,this.transmuxer=null,this.onTransmuxComplete=void 0,this.onFlush=void 0;var s=e.config;this.hls=e,this.id=r,this.useWorker=!!s.enableWorker,this.onTransmuxComplete=i,this.onFlush=n;var o=function(t,e){(e=e||{}).frag=a.frag,e.id=a.id,t===S.ERROR&&(a.error=e.error),a.hls.trigger(t,e)};this.observer=new Mn,this.observer.on(S.FRAG_DECRYPTED,o),this.observer.on(S.ERROR,o);var l,u,h,d,c=te(s.preferManagedMediaSource)||{isTypeSupported:function(){return!1}},f={mpeg:c.isTypeSupported("audio/mpeg"),mp3:c.isTypeSupported('audio/mp4; codecs="mp3"'),ac3:c.isTypeSupported('audio/mp4; codecs="ac-3"')},g=navigator.vendor;if(!this.useWorker||"undefined"==typeof Worker||(s.workerPath,0))this.transmuxer=new wn(this.observer,f,s,g,r);else try{s.workerPath?(w.log("loading Web Worker "+s.workerPath+' for "'+r+'"'),this.workerContext=(h=s.workerPath,d=new self.URL(h,self.location.href).href,{worker:new self.Worker(d),scriptURL:d})):(w.log('injecting Web Worker for "'+r+'"'),this.workerContext=(l=new self.Blob(["var exports={};var module={exports:exports};function define(f){f()};define.amd=true;("+t.toString()+")(true);"],{type:"text/javascript"}),u=self.URL.createObjectURL(l),{worker:new self.Worker(u),objectURL:u})),this.onwmsg=function(t){return a.onWorkerMessage(t)};var v=this.workerContext.worker;v.addEventListener("message",this.onwmsg),v.onerror=function(t){var e=new Error(t.message+" ("+t.filename+":"+t.lineno+")");s.enableWorker=!1,w.warn('Error in "'+r+'" Web Worker, fallback to inline'),a.hls.trigger(S.ERROR,{type:L.OTHER_ERROR,details:A.INTERNAL_EXCEPTION,fatal:!1,event:"demuxerWorker",error:e})},v.postMessage({cmd:"init",typeSupported:f,vendor:g,id:r,config:JSON.stringify(s)})}catch(t){w.warn('Error setting up "'+r+'" Web Worker, fallback to inline',t),this.resetWorker(),this.error=null,this.transmuxer=new wn(this.observer,f,s,g,r)}}var r=e.prototype;return r.resetWorker=function(){if(this.workerContext){var t=this.workerContext,e=t.worker,r=t.objectURL;r&&self.URL.revokeObjectURL(r),e.removeEventListener("message",this.onwmsg),e.onerror=null,e.terminate(),this.workerContext=null}},r.destroy=function(){if(this.workerContext)this.resetWorker(),this.onwmsg=void 0;else{var t=this.transmuxer;t&&(t.destroy(),this.transmuxer=null)}var e=this.observer;e&&e.removeAllListeners(),this.frag=null,this.observer=null,this.hls=null},r.push=function(t,e,r,i,n,a,s,o,l,u){var h,d,c=this;l.transmuxing.start=self.performance.now();var f=this.transmuxer,g=a?a.start:n.start,v=n.decryptdata,m=this.frag,p=!(m&&n.cc===m.cc),y=!(m&&l.level===m.level),E=m?l.sn-m.sn:-1,T=this.part?l.part-this.part.index:-1,S=0===E&&l.id>1&&l.id===(null==m?void 0:m.stats.chunkCount),L=!y&&(1===E||0===E&&(1===T||S&&T<=0)),A=self.performance.now();(y||E||0===n.stats.parsing.start)&&(n.stats.parsing.start=A),!a||!T&&L||(a.stats.parsing.start=A);var R=!(m&&(null==(h=n.initSegment)?void 0:h.url)===(null==(d=m.initSegment)?void 0:d.url)),k=new Pn(p,L,o,y,g,R);if(!L||p||R){w.log("[transmuxer-interface, "+n.type+"]: Starting new transmux session for sn: "+l.sn+" p: "+l.part+" level: "+l.level+" id: "+l.id+"\n discontinuity: "+p+"\n trackSwitch: "+y+"\n contiguous: "+L+"\n accurateTimeOffset: "+o+"\n timeOffset: "+g+"\n initSegmentChange: "+R);var b=new xn(r,i,e,s,u);this.configureTransmuxer(b)}if(this.frag=n,this.part=a,this.workerContext)this.workerContext.worker.postMessage({cmd:"demux",data:t,decryptdata:v,chunkMeta:l,state:k},t instanceof ArrayBuffer?[t]:[]);else if(f){var D=f.push(t,v,l,k);_n(D)?(f.async=!0,D.then((function(t){c.handleTransmuxComplete(t)})).catch((function(t){c.transmuxerError(t,l,"transmuxer-interface push error")}))):(f.async=!1,this.handleTransmuxComplete(D))}},r.flush=function(t){var e=this;t.transmuxing.start=self.performance.now();var r=this.transmuxer;if(this.workerContext)this.workerContext.worker.postMessage({cmd:"flush",chunkMeta:t});else if(r){var i=r.flush(t);_n(i)||r.async?(_n(i)||(i=Promise.resolve(i)),i.then((function(r){e.handleFlushResult(r,t)})).catch((function(r){e.transmuxerError(r,t,"transmuxer-interface flush error")}))):this.handleFlushResult(i,t)}},r.transmuxerError=function(t,e,r){this.hls&&(this.error=t,this.hls.trigger(S.ERROR,{type:L.MEDIA_ERROR,details:A.FRAG_PARSING_ERROR,chunkMeta:e,fatal:!1,error:t,err:t,reason:r}))},r.handleFlushResult=function(t,e){var r=this;t.forEach((function(t){r.handleTransmuxComplete(t)})),this.onFlush(e)},r.onWorkerMessage=function(t){var e=t.data,r=this.hls;switch(e.event){case"init":var i,n=null==(i=this.workerContext)?void 0:i.objectURL;n&&self.URL.revokeObjectURL(n);break;case"transmuxComplete":this.handleTransmuxComplete(e.data);break;case"flush":this.onFlush(e.data);break;case"workerLog":w[e.data.logType]&&w[e.data.logType](e.data.message);break;default:e.data=e.data||{},e.data.frag=this.frag,e.data.id=this.id,r.trigger(e.event,e.data)}},r.configureTransmuxer=function(t){var e=this.transmuxer;this.workerContext?this.workerContext.worker.postMessage({cmd:"configure",config:t}):e&&e.configure(t)},r.handleTransmuxComplete=function(t){t.chunkMeta.transmuxing.end=self.performance.now(),this.onTransmuxComplete(t)},e}();function Gn(t,e){if(t.length!==e.length)return!1;for(var r=0;r<t.length;r++)if(!Kn(t[r].attrs,e[r].attrs))return!1;return!0}function Kn(t,e,r){var i=t["STABLE-RENDITION-ID"];return i&&!r?i===e["STABLE-RENDITION-ID"]:!(r||["LANGUAGE","NAME","CHARACTERISTICS","AUTOSELECT","DEFAULT","FORCED","ASSOC-LANGUAGE"]).some((function(r){return t[r]!==e[r]}))}function Hn(t,e){return e.label.toLowerCase()===t.name.toLowerCase()&&(!e.language||e.language.toLowerCase()===(t.lang||"").toLowerCase())}var Vn=function(t){function e(e,r,i){var n;return(n=t.call(this,e,r,i,"[audio-stream-controller]",we)||this).videoBuffer=null,n.videoTrackCC=-1,n.waitingVideoCC=-1,n.bufferedTrack=null,n.switchingTrack=null,n.trackId=-1,n.waitingData=null,n.mainDetails=null,n.flushing=!1,n.bufferFlushed=!1,n.cachedTrackLoadedData=null,n._registerListeners(),n}l(e,t);var r=e.prototype;return r.onHandlerDestroying=function(){this._unregisterListeners(),t.prototype.onHandlerDestroying.call(this),this.mainDetails=null,this.bufferedTrack=null,this.switchingTrack=null},r._registerListeners=function(){var t=this.hls;t.on(S.MEDIA_ATTACHED,this.onMediaAttached,this),t.on(S.MEDIA_DETACHING,this.onMediaDetaching,this),t.on(S.MANIFEST_LOADING,this.onManifestLoading,this),t.on(S.LEVEL_LOADED,this.onLevelLoaded,this),t.on(S.AUDIO_TRACKS_UPDATED,this.onAudioTracksUpdated,this),t.on(S.AUDIO_TRACK_SWITCHING,this.onAudioTrackSwitching,this),t.on(S.AUDIO_TRACK_LOADED,this.onAudioTrackLoaded,this),t.on(S.ERROR,this.onError,this),t.on(S.BUFFER_RESET,this.onBufferReset,this),t.on(S.BUFFER_CREATED,this.onBufferCreated,this),t.on(S.BUFFER_FLUSHING,this.onBufferFlushing,this),t.on(S.BUFFER_FLUSHED,this.onBufferFlushed,this),t.on(S.INIT_PTS_FOUND,this.onInitPtsFound,this),t.on(S.FRAG_BUFFERED,this.onFragBuffered,this)},r._unregisterListeners=function(){var t=this.hls;t.off(S.MEDIA_ATTACHED,this.onMediaAttached,this),t.off(S.MEDIA_DETACHING,this.onMediaDetaching,this),t.off(S.MANIFEST_LOADING,this.onManifestLoading,this),t.off(S.LEVEL_LOADED,this.onLevelLoaded,this),t.off(S.AUDIO_TRACKS_UPDATED,this.onAudioTracksUpdated,this),t.off(S.AUDIO_TRACK_SWITCHING,this.onAudioTrackSwitching,this),t.off(S.AUDIO_TRACK_LOADED,this.onAudioTrackLoaded,this),t.off(S.ERROR,this.onError,this),t.off(S.BUFFER_RESET,this.onBufferReset,this),t.off(S.BUFFER_CREATED,this.onBufferCreated,this),t.off(S.BUFFER_FLUSHING,this.onBufferFlushing,this),t.off(S.BUFFER_FLUSHED,this.onBufferFlushed,this),t.off(S.INIT_PTS_FOUND,this.onInitPtsFound,this),t.off(S.FRAG_BUFFERED,this.onFragBuffered,this)},r.onInitPtsFound=function(t,e){var r=e.frag,i=e.id,n=e.initPTS,a=e.timescale;if("main"===i){var s=r.cc;this.initPTS[r.cc]={baseTime:n,timescale:a},this.log("InitPTS for cc: "+s+" found from main: "+n),this.videoTrackCC=s,this.state===Li&&this.tick()}},r.startLoad=function(t){if(!this.levels)return this.startPosition=t,void(this.state=ci);var e=this.lastCurrentTime;this.stopLoad(),this.setInterval(100),e>0&&-1===t?(this.log("Override startPosition with lastCurrentTime @"+e.toFixed(3)),t=e,this.state=fi):(this.loadedmetadata=!1,this.state=pi),this.nextLoadPosition=this.startPosition=this.lastCurrentTime=t,this.tick()},r.doTick=function(){switch(this.state){case fi:this.doTickIdle();break;case pi:var e,r=this.levels,i=this.trackId,n=null==r||null==(e=r[i])?void 0:e.details;if(n){if(this.waitForCdnTuneIn(n))break;this.state=Li}break;case mi:var a,s=performance.now(),o=this.retryDate;if(!o||s>=o||null!=(a=this.media)&&a.seeking){var l=this.levels,u=this.trackId;this.log("RetryDate reached, switch back to IDLE state"),this.resetStartWhenNotLoaded((null==l?void 0:l[u])||null),this.state=fi}break;case Li:var h=this.waitingData;if(h){var d=h.frag,c=h.part,f=h.cache,g=h.complete;if(void 0!==this.initPTS[d.cc]){this.waitingData=null,this.waitingVideoCC=-1,this.state=vi;var v={frag:d,part:c,payload:f.flush(),networkDetails:null};this._handleFragmentLoadProgress(v),g&&t.prototype._handleFragmentLoadComplete.call(this,v)}else if(this.videoTrackCC!==this.waitingVideoCC)this.log("Waiting fragment cc ("+d.cc+") cancelled because video is at cc "+this.videoTrackCC),this.clearWaitingFragment();else{var m=this.getLoadPosition(),p=zr.bufferInfo(this.mediaBuffer,m,this.config.maxBufferHole);pr(p.end,this.config.maxFragLookUpTolerance,d)<0&&(this.log("Waiting fragment cc ("+d.cc+") @ "+d.start+" cancelled because another fragment at "+p.end+" is needed"),this.clearWaitingFragment())}}else this.state=fi}this.onTickEnd()},r.clearWaitingFragment=function(){var t=this.waitingData;t&&(this.fragmentTracker.removeFragment(t.frag),this.waitingData=null,this.waitingVideoCC=-1,this.state=fi)},r.resetLoadingState=function(){this.clearWaitingFragment(),t.prototype.resetLoadingState.call(this)},r.onTickEnd=function(){var t=this.media;null!=t&&t.readyState&&(this.lastCurrentTime=t.currentTime)},r.doTickIdle=function(){var t=this.hls,e=this.levels,r=this.media,i=this.trackId,n=t.config;if((r||!this.startFragRequested&&n.startFragPrefetch)&&null!=e&&e[i]){var a=e[i],s=a.details;if(!s||s.live&&this.levelLastLoaded!==a||this.waitForCdnTuneIn(s))this.state=pi;else{var o=this.mediaBuffer?this.mediaBuffer:this.media;this.bufferFlushed&&o&&(this.bufferFlushed=!1,this.afterBufferFlushed(o,O,we));var l=this.getFwdBufferInfo(o,we);if(null!==l){var u=this.bufferedTrack,h=this.switchingTrack;if(!h&&this._streamEnded(l,s))return t.trigger(S.BUFFER_EOS,{type:"audio"}),void(this.state=Ti);var d=this.getFwdBufferInfo(this.videoBuffer?this.videoBuffer:this.media,Ie),c=l.len,f=this.getMaxBufferLength(null==d?void 0:d.len),g=s.fragments,v=g[0].start,m=this.flushing?this.getLoadPosition():l.end;if(h&&r){var p=this.getLoadPosition();u&&!Kn(h.attrs,u.attrs)&&(m=p),s.PTSKnown&&p<v&&(l.end>v||l.nextStart)&&(this.log("Alt audio track ahead of main track, seek to start of alt audio track"),r.currentTime=v+.05)}if(!(c>=f&&!h&&m<g[g.length-1].start)){var y=this.getNextFragment(m,s),E=!1;if(y&&this.isLoopLoading(y,m)&&(E=!!y.gap,y=this.getNextFragmentLoopLoading(y,s,l,Ie,f)),y){var T=d&&y.start>d.end+s.targetduration;if(T||(null==d||!d.len)&&l.len){var L=this.getAppendedFrag(y.start,Ie);if(null===L)return;if(E||(E=!!L.gap||!!T&&0===d.len),T&&!E||E&&l.nextStart&&l.nextStart<L.end)return}this.loadFragment(y,a,m)}else this.bufferFlushed=!0}}}}},r.getMaxBufferLength=function(e){var r=t.prototype.getMaxBufferLength.call(this);return e?Math.min(Math.max(r,e),this.config.maxMaxBufferLength):r},r.onMediaDetaching=function(){this.videoBuffer=null,this.bufferFlushed=this.flushing=!1,t.prototype.onMediaDetaching.call(this)},r.onAudioTracksUpdated=function(t,e){var r=e.audioTracks;this.resetTransmuxer(),this.levels=r.map((function(t){return new tr(t)}))},r.onAudioTrackSwitching=function(t,e){var r=!!e.url;this.trackId=e.id;var i=this.fragCurrent;i&&(i.abortRequests(),this.removeUnbufferedFrags(i.start)),this.resetLoadingState(),r?this.setInterval(100):this.resetTransmuxer(),r?(this.switchingTrack=e,this.state=fi,this.flushAudioIfNeeded(e)):(this.switchingTrack=null,this.bufferedTrack=e,this.state=ci),this.tick()},r.onManifestLoading=function(){this.fragmentTracker.removeAllFragments(),this.startPosition=this.lastCurrentTime=0,this.bufferFlushed=this.flushing=!1,this.levels=this.mainDetails=this.waitingData=this.bufferedTrack=this.cachedTrackLoadedData=this.switchingTrack=null,this.startFragRequested=!1,this.trackId=this.videoTrackCC=this.waitingVideoCC=-1},r.onLevelLoaded=function(t,e){this.mainDetails=e.details,null!==this.cachedTrackLoadedData&&(this.hls.trigger(S.AUDIO_TRACK_LOADED,this.cachedTrackLoadedData),this.cachedTrackLoadedData=null)},r.onAudioTrackLoaded=function(t,e){var r;if(null!=this.mainDetails){var i=this.levels,n=e.details,a=e.id;if(i){this.log("Audio track "+a+" loaded ["+n.startSN+","+n.endSN+"]"+(n.lastPartSn?"[part-"+n.lastPartSn+"-"+n.lastPartIndex+"]":"")+",duration:"+n.totalduration);var s=i[a],o=0;if(n.live||null!=(r=s.details)&&r.live){this.checkLiveUpdate(n);var l,u=this.mainDetails;if(n.deltaUpdateFailed||!u)return;!s.details&&n.hasProgramDateTime&&u.hasProgramDateTime?(ei(n,u),o=n.fragments[0].start):o=this.alignPlaylists(n,s.details,null==(l=this.levelLastLoaded)?void 0:l.details)}s.details=n,this.levelLastLoaded=s,this.startFragRequested||!this.mainDetails&&n.live||this.setStartPosition(s.details,o),this.state!==pi||this.waitForCdnTuneIn(n)||(this.state=fi),this.tick()}else this.warn("Audio tracks were reset while loading level "+a)}else this.cachedTrackLoadedData=e},r._handleFragmentLoadProgress=function(t){var e,r=t.frag,i=t.part,n=t.payload,a=this.config,s=this.trackId,o=this.levels;if(o){var l=o[s];if(l){var u=l.details;if(!u)return this.warn("Audio track details undefined on fragment load progress"),void this.removeUnbufferedFrags(r.start);var h=a.defaultAudioCodec||l.audioCodec||"mp4a.40.2",d=this.transmuxer;d||(d=this.transmuxer=new Bn(this.hls,we,this._handleTransmuxComplete.bind(this),this._handleTransmuxerFlush.bind(this)));var c=this.initPTS[r.cc],f=null==(e=r.initSegment)?void 0:e.data;if(void 0!==c){var g=i?i.index:-1,v=-1!==g,m=new Qr(r.level,r.sn,r.stats.chunkCount,n.byteLength,g,v);d.push(n,f,h,"",r,i,u.totalduration,!1,m,c)}else this.log("Unknown video PTS for cc "+r.cc+", waiting for video PTS before demuxing audio frag "+r.sn+" of ["+u.startSN+" ,"+u.endSN+"],track "+s),(this.waitingData=this.waitingData||{frag:r,part:i,cache:new ki,complete:!1}).cache.push(new Uint8Array(n)),this.waitingVideoCC=this.videoTrackCC,this.state=Li}else this.warn("Audio track is undefined on fragment load progress")}else this.warn("Audio tracks were reset while fragment load was in progress. Fragment "+r.sn+" of level "+r.level+" will not be buffered")},r._handleFragmentLoadComplete=function(e){this.waitingData?this.waitingData.complete=!0:t.prototype._handleFragmentLoadComplete.call(this,e)},r.onBufferReset=function(){this.mediaBuffer=this.videoBuffer=null,this.loadedmetadata=!1},r.onBufferCreated=function(t,e){var r=e.tracks.audio;r&&(this.mediaBuffer=r.buffer||null),e.tracks.video&&(this.videoBuffer=e.tracks.video.buffer||null)},r.onFragBuffered=function(t,e){var r=e.frag,n=e.part;if(r.type===we)if(this.fragContextChanged(r))this.warn("Fragment "+r.sn+(n?" p: "+n.index:"")+" of level "+r.level+" finished buffering, but was aborted. state: "+this.state+", audioSwitch: "+(this.switchingTrack?this.switchingTrack.name:"false"));else{if("initSegment"!==r.sn){this.fragPrevious=r;var a=this.switchingTrack;a&&(this.bufferedTrack=a,this.switchingTrack=null,this.hls.trigger(S.AUDIO_TRACK_SWITCHED,i({},a)))}this.fragBufferedComplete(r,n)}else if(!this.loadedmetadata&&r.type===Ie){var s=this.videoBuffer||this.media;s&&zr.getBuffered(s).length&&(this.loadedmetadata=!0)}},r.onError=function(e,r){var i;if(r.fatal)this.state=Si;else switch(r.details){case A.FRAG_GAP:case A.FRAG_PARSING_ERROR:case A.FRAG_DECRYPT_ERROR:case A.FRAG_LOAD_ERROR:case A.FRAG_LOAD_TIMEOUT:case A.KEY_LOAD_ERROR:case A.KEY_LOAD_TIMEOUT:this.onFragmentOrKeyLoadError(we,r);break;case A.AUDIO_TRACK_LOAD_ERROR:case A.AUDIO_TRACK_LOAD_TIMEOUT:case A.LEVEL_PARSING_ERROR:r.levelRetry||this.state!==pi||(null==(i=r.context)?void 0:i.type)!==be||(this.state=fi);break;case A.BUFFER_APPEND_ERROR:case A.BUFFER_FULL_ERROR:if(!r.parent||"audio"!==r.parent)return;if(r.details===A.BUFFER_APPEND_ERROR)return void this.resetLoadingState();this.reduceLengthAndFlushBuffer(r)&&(this.bufferedTrack=null,t.prototype.flushMainBuffer.call(this,0,Number.POSITIVE_INFINITY,"audio"));break;case A.INTERNAL_EXCEPTION:this.recoverWorkerError(r)}},r.onBufferFlushing=function(t,e){e.type!==N&&(this.flushing=!0)},r.onBufferFlushed=function(t,e){var r=e.type;if(r!==N){this.flushing=!1,this.bufferFlushed=!0,this.state===Ti&&(this.state=fi);var i=this.mediaBuffer||this.media;i&&(this.afterBufferFlushed(i,r,we),this.tick())}},r._handleTransmuxComplete=function(t){var e,r="audio",i=this.hls,n=t.remuxResult,a=t.chunkMeta,s=this.getCurrentContext(a);if(s){var l=s.frag,u=s.part,h=s.level,d=h.details,c=n.audio,f=n.text,g=n.id3,v=n.initSegment;if(!this.fragContextChanged(l)&&d){if(this.state=yi,this.switchingTrack&&c&&this.completeAudioSwitch(this.switchingTrack),null!=v&&v.tracks){var m=l.initSegment||l;this._bufferInitSegment(h,v.tracks,m,a),i.trigger(S.FRAG_PARSING_INIT_SEGMENT,{frag:m,id:r,tracks:v.tracks})}if(c){var p=c.startPTS,y=c.endPTS,E=c.startDTS,T=c.endDTS;u&&(u.elementaryStreams[O]={startPTS:p,endPTS:y,startDTS:E,endDTS:T}),l.setElementaryStreamInfo(O,p,y,E,T),this.bufferFragmentData(c,l,u,a)}if(null!=g&&null!=(e=g.samples)&&e.length){var L=o({id:r,frag:l,details:d},g);i.trigger(S.FRAG_PARSING_METADATA,L)}if(f){var A=o({id:r,frag:l,details:d},f);i.trigger(S.FRAG_PARSING_USERDATA,A)}}else this.fragmentTracker.removeFragment(l)}else this.resetWhenMissingContext(a)},r._bufferInitSegment=function(t,e,r,i){if(this.state===yi){e.video&&delete e.video;var n=e.audio;if(n){n.id="audio";var a=t.audioCodec;this.log("Init audio buffer, container:"+n.container+", codecs[level/parsed]=["+a+"/"+n.codec+"]"),a&&1===a.split(",").length&&(n.levelCodec=a),this.hls.trigger(S.BUFFER_CODECS,e);var s=n.initSegment;if(null!=s&&s.byteLength){var o={type:"audio",frag:r,part:null,chunkMeta:i,parent:r.type,data:s};this.hls.trigger(S.BUFFER_APPENDING,o)}this.tickImmediate()}}},r.loadFragment=function(e,r,i){var n,a=this.fragmentTracker.getState(e);if(this.fragCurrent=e,this.switchingTrack||a===Kr||a===Vr)if("initSegment"===e.sn)this._loadInitSegment(e,r);else if(null!=(n=r.details)&&n.live&&!this.initPTS[e.cc]){this.log("Waiting for video PTS in continuity counter "+e.cc+" of live stream before loading audio fragment "+e.sn+" of level "+this.trackId),this.state=Li;var s=this.mainDetails;s&&s.fragments[0].start!==r.details.fragments[0].start&&ei(r.details,s)}else this.startFragRequested=!0,t.prototype.loadFragment.call(this,e,r,i);else this.clearTrackerIfNeeded(e)},r.flushAudioIfNeeded=function(e){var r=this.media,i=this.bufferedTrack,n=null==i?void 0:i.attrs,a=e.attrs;r&&n&&(n.CHANNELS!==a.CHANNELS||i.name!==e.name||i.lang!==e.lang)&&(this.log("Switching audio track : flushing all audio"),t.prototype.flushMainBuffer.call(this,0,Number.POSITIVE_INFINITY,"audio"),this.bufferedTrack=null)},r.completeAudioSwitch=function(t){var e=this.hls;this.flushAudioIfNeeded(t),this.bufferedTrack=t,this.switchingTrack=null,e.trigger(S.AUDIO_TRACK_SWITCHED,i({},t))},e}(Ri),Yn=function(t){function e(e){var r;return(r=t.call(this,e,"[audio-track-controller]")||this).tracks=[],r.groupIds=null,r.tracksInGroup=[],r.trackId=-1,r.currentTrack=null,r.selectDefaultTrack=!0,r.registerListeners(),r}l(e,t);var r=e.prototype;return r.registerListeners=function(){var t=this.hls;t.on(S.MANIFEST_LOADING,this.onManifestLoading,this),t.on(S.MANIFEST_PARSED,this.onManifestParsed,this),t.on(S.LEVEL_LOADING,this.onLevelLoading,this),t.on(S.LEVEL_SWITCHING,this.onLevelSwitching,this),t.on(S.AUDIO_TRACK_LOADED,this.onAudioTrackLoaded,this),t.on(S.ERROR,this.onError,this)},r.unregisterListeners=function(){var t=this.hls;t.off(S.MANIFEST_LOADING,this.onManifestLoading,this),t.off(S.MANIFEST_PARSED,this.onManifestParsed,this),t.off(S.LEVEL_LOADING,this.onLevelLoading,this),t.off(S.LEVEL_SWITCHING,this.onLevelSwitching,this),t.off(S.AUDIO_TRACK_LOADED,this.onAudioTrackLoaded,this),t.off(S.ERROR,this.onError,this)},r.destroy=function(){this.unregisterListeners(),this.tracks.length=0,this.tracksInGroup.length=0,this.currentTrack=null,t.prototype.destroy.call(this)},r.onManifestLoading=function(){this.tracks=[],this.tracksInGroup=[],this.groupIds=null,this.currentTrack=null,this.trackId=-1,this.selectDefaultTrack=!0},r.onManifestParsed=function(t,e){this.tracks=e.audioTracks||[]},r.onAudioTrackLoaded=function(t,e){var r=e.id,i=e.groupId,n=e.details,a=this.tracksInGroup[r];if(a&&a.groupId===i){var s=a.details;a.details=e.details,this.log("Audio track "+r+' "'+a.name+'" lang:'+a.lang+" group:"+i+" loaded ["+n.startSN+"-"+n.endSN+"]"),r===this.trackId&&this.playlistLoaded(r,e,s)}else this.warn("Audio track with id:"+r+" and group:"+i+" not found in active group "+(null==a?void 0:a.groupId))},r.onLevelLoading=function(t,e){this.switchLevel(e.level)},r.onLevelSwitching=function(t,e){this.switchLevel(e.level)},r.switchLevel=function(t){var e=this.hls.levels[t];if(e){var r=e.audioGroups||null,i=this.groupIds,n=this.currentTrack;if(!r||(null==i?void 0:i.length)!==(null==r?void 0:r.length)||null!=r&&r.some((function(t){return-1===(null==i?void 0:i.indexOf(t))}))){this.groupIds=r,this.trackId=-1,this.currentTrack=null;var a=this.tracks.filter((function(t){return!r||-1!==r.indexOf(t.groupId)}));if(a.length)this.selectDefaultTrack&&!a.some((function(t){return t.default}))&&(this.selectDefaultTrack=!1),a.forEach((function(t,e){t.id=e}));else if(!n&&!this.tracksInGroup.length)return;this.tracksInGroup=a;var s=this.hls.config.audioPreference;if(!n&&s){var o=Mr(s,a,Nr);if(o>-1)n=a[o];else{var l=Mr(s,this.tracks);n=this.tracks[l]}}var u=this.findTrackId(n);-1===u&&n&&(u=this.findTrackId(null));var h={audioTracks:a};this.log("Updating audio tracks, "+a.length+" track(s) found in group(s): "+(null==r?void 0:r.join(","))),this.hls.trigger(S.AUDIO_TRACKS_UPDATED,h);var d=this.trackId;if(-1!==u&&-1===d)this.setAudioTrack(u);else if(a.length&&-1===d){var c,f=new Error("No audio track selected for current audio group-ID(s): "+(null==(c=this.groupIds)?void 0:c.join(","))+" track count: "+a.length);this.warn(f.message),this.hls.trigger(S.ERROR,{type:L.MEDIA_ERROR,details:A.AUDIO_TRACK_LOAD_ERROR,fatal:!0,error:f})}}else this.shouldReloadPlaylist(n)&&this.setAudioTrack(this.trackId)}},r.onError=function(t,e){!e.fatal&&e.context&&(e.context.type!==be||e.context.id!==this.trackId||this.groupIds&&-1===this.groupIds.indexOf(e.context.groupId)||(this.requestScheduled=-1,this.checkRetry(e)))},r.setAudioOption=function(t){var e=this.hls;if(e.config.audioPreference=t,t){var r=this.allAudioTracks;if(this.selectDefaultTrack=!1,r.length){var i=this.currentTrack;if(i&&Or(t,i,Nr))return i;var n=Mr(t,this.tracksInGroup,Nr);if(n>-1){var a=this.tracksInGroup[n];return this.setAudioTrack(n),a}if(i){var s=e.loadLevel;-1===s&&(s=e.firstAutoLevel);var o=function(t,e,r,i,n){var a=e[i],s=e.reduce((function(t,e,r){var i=e.uri;return(t[i]||(t[i]=[])).push(r),t}),{})[a.uri];s.length>1&&(i=Math.max.apply(Math,s));var o=a.videoRange,l=a.frameRate,u=a.codecSet.substring(0,4),h=Ur(e,i,(function(e){if(e.videoRange!==o||e.frameRate!==l||e.codecSet.substring(0,4)!==u)return!1;var i=e.audioGroups,a=r.filter((function(t){return!i||-1!==i.indexOf(t.groupId)}));return Mr(t,a,n)>-1}));return h>-1?h:Ur(e,i,(function(e){var i=e.audioGroups,a=r.filter((function(t){return!i||-1!==i.indexOf(t.groupId)}));return Mr(t,a,n)>-1}))}(t,e.levels,r,s,Nr);if(-1===o)return null;e.nextLoadLevel=o}if(t.channels||t.audioCodec){var l=Mr(t,r);if(l>-1)return r[l]}}}return null},r.setAudioTrack=function(t){var e=this.tracksInGroup;if(t<0||t>=e.length)this.warn("Invalid audio track id: "+t);else{this.clearTimer(),this.selectDefaultTrack=!1;var r=this.currentTrack,n=e[t],a=n.details&&!n.details.live;if(!(t===this.trackId&&n===r&&a||(this.log("Switching to audio-track "+t+' "'+n.name+'" lang:'+n.lang+" group:"+n.groupId+" channels:"+n.channels),this.trackId=t,this.currentTrack=n,this.hls.trigger(S.AUDIO_TRACK_SWITCHING,i({},n)),a))){var s=this.switchParams(n.url,null==r?void 0:r.details);this.loadPlaylist(s)}}},r.findTrackId=function(t){for(var e=this.tracksInGroup,r=0;r<e.length;r++){var i=e[r];if((!this.selectDefaultTrack||i.default)&&(!t||Or(t,i,Nr)))return r}if(t){for(var n=t.name,a=t.lang,s=t.assocLang,o=t.characteristics,l=t.audioCodec,u=t.channels,h=0;h<e.length;h++)if(Or({name:n,lang:a,assocLang:s,characteristics:o,audioCodec:l,channels:u},e[h],Nr))return h;for(var d=0;d<e.length;d++){var c=e[d];if(Kn(t.attrs,c.attrs,["LANGUAGE","ASSOC-LANGUAGE","CHARACTERISTICS"]))return d}for(var f=0;f<e.length;f++){var g=e[f];if(Kn(t.attrs,g.attrs,["LANGUAGE"]))return f}}return-1},r.loadPlaylist=function(e){var r=this.currentTrack;if(this.shouldLoadPlaylist(r)&&r){t.prototype.loadPlaylist.call(this);var i=r.id,n=r.groupId,a=r.url;if(e)try{a=e.addDirectives(a)}catch(t){this.warn("Could not construct new URL with HLS Delivery Directives: "+t)}this.log("loading audio-track playlist "+i+' "'+r.name+'" lang:'+r.lang+" group:"+n),this.clearTimer(),this.hls.trigger(S.AUDIO_TRACK_LOADING,{url:a,id:i,groupId:n,deliveryDirectives:e||null})}},s(e,[{key:"allAudioTracks",get:function(){return this.tracks}},{key:"audioTracks",get:function(){return this.tracksInGroup}},{key:"audioTrack",get:function(){return this.trackId},set:function(t){this.selectDefaultTrack=!1,this.setAudioTrack(t)}}]),e}(Dr),Wn=function(t){function e(e,r,i){var n;return(n=t.call(this,e,r,i,"[subtitle-stream-controller]",Ce)||this).currentTrackId=-1,n.tracksBuffered=[],n.mainDetails=null,n._registerListeners(),n}l(e,t);var r=e.prototype;return r.onHandlerDestroying=function(){this._unregisterListeners(),t.prototype.onHandlerDestroying.call(this),this.mainDetails=null},r._registerListeners=function(){var t=this.hls;t.on(S.MEDIA_ATTACHED,this.onMediaAttached,this),t.on(S.MEDIA_DETACHING,this.onMediaDetaching,this),t.on(S.MANIFEST_LOADING,this.onManifestLoading,this),t.on(S.LEVEL_LOADED,this.onLevelLoaded,this),t.on(S.ERROR,this.onError,this),t.on(S.SUBTITLE_TRACKS_UPDATED,this.onSubtitleTracksUpdated,this),t.on(S.SUBTITLE_TRACK_SWITCH,this.onSubtitleTrackSwitch,this),t.on(S.SUBTITLE_TRACK_LOADED,this.onSubtitleTrackLoaded,this),t.on(S.SUBTITLE_FRAG_PROCESSED,this.onSubtitleFragProcessed,this),t.on(S.BUFFER_FLUSHING,this.onBufferFlushing,this),t.on(S.FRAG_BUFFERED,this.onFragBuffered,this)},r._unregisterListeners=function(){var t=this.hls;t.off(S.MEDIA_ATTACHED,this.onMediaAttached,this),t.off(S.MEDIA_DETACHING,this.onMediaDetaching,this),t.off(S.MANIFEST_LOADING,this.onManifestLoading,this),t.off(S.LEVEL_LOADED,this.onLevelLoaded,this),t.off(S.ERROR,this.onError,this),t.off(S.SUBTITLE_TRACKS_UPDATED,this.onSubtitleTracksUpdated,this),t.off(S.SUBTITLE_TRACK_SWITCH,this.onSubtitleTrackSwitch,this),t.off(S.SUBTITLE_TRACK_LOADED,this.onSubtitleTrackLoaded,this),t.off(S.SUBTITLE_FRAG_PROCESSED,this.onSubtitleFragProcessed,this),t.off(S.BUFFER_FLUSHING,this.onBufferFlushing,this),t.off(S.FRAG_BUFFERED,this.onFragBuffered,this)},r.startLoad=function(t){this.stopLoad(),this.state=fi,this.setInterval(500),this.nextLoadPosition=this.startPosition=this.lastCurrentTime=t,this.tick()},r.onManifestLoading=function(){this.mainDetails=null,this.fragmentTracker.removeAllFragments()},r.onMediaDetaching=function(){this.tracksBuffered=[],t.prototype.onMediaDetaching.call(this)},r.onLevelLoaded=function(t,e){this.mainDetails=e.details},r.onSubtitleFragProcessed=function(t,e){var r=e.frag,i=e.success;if(this.fragPrevious=r,this.state=fi,i){var n=this.tracksBuffered[this.currentTrackId];if(n){for(var a,s=r.start,o=0;o<n.length;o++)if(s>=n[o].start&&s<=n[o].end){a=n[o];break}var l=r.start+r.duration;a?a.end=l:(a={start:s,end:l},n.push(a)),this.fragmentTracker.fragBuffered(r),this.fragBufferedComplete(r,null)}}},r.onBufferFlushing=function(t,e){var r=e.startOffset,i=e.endOffset;if(0===r&&i!==Number.POSITIVE_INFINITY){var n=i-1;if(n<=0)return;e.endOffsetSubtitles=Math.max(0,n),this.tracksBuffered.forEach((function(t){for(var e=0;e<t.length;)if(t[e].end<=n)t.shift();else{if(!(t[e].start<n))break;t[e].start=n,e++}})),this.fragmentTracker.removeFragmentsInRange(r,n,Ce)}},r.onFragBuffered=function(t,e){var r;this.loadedmetadata||e.frag.type!==Ie||null!=(r=this.media)&&r.buffered.length&&(this.loadedmetadata=!0)},r.onError=function(t,e){var r=e.frag;(null==r?void 0:r.type)===Ce&&(this.fragCurrent&&this.fragCurrent.abortRequests(),this.state!==ci&&(this.state=fi))},r.onSubtitleTracksUpdated=function(t,e){var r=this,i=e.subtitleTracks;this.levels&&!Gn(this.levels,i)?(this.tracksBuffered=[],this.levels=i.map((function(t){var e=new tr(t);return r.tracksBuffered[e.id]=[],e})),this.fragmentTracker.removeFragmentsInRange(0,Number.POSITIVE_INFINITY,Ce),this.fragPrevious=null,this.mediaBuffer=null):this.levels=i.map((function(t){return new tr(t)}))},r.onSubtitleTrackSwitch=function(t,e){var r;if(this.currentTrackId=e.id,null!=(r=this.levels)&&r.length&&-1!==this.currentTrackId){var i=this.levels[this.currentTrackId];null!=i&&i.details?this.mediaBuffer=this.mediaBufferTimeRanges:this.mediaBuffer=null,i&&this.setInterval(500)}else this.clearInterval()},r.onSubtitleTrackLoaded=function(t,e){var r,i=this.currentTrackId,n=this.levels,a=e.details,s=e.id;if(n){var o=n[i];if(!(s>=n.length||s!==i)&&o){this.log("Subtitle track "+s+" loaded ["+a.startSN+","+a.endSN+"]"+(a.lastPartSn?"[part-"+a.lastPartSn+"-"+a.lastPartIndex+"]":"")+",duration:"+a.totalduration),this.mediaBuffer=this.mediaBufferTimeRanges;var l=0;if(a.live||null!=(r=o.details)&&r.live){var u=this.mainDetails;if(a.deltaUpdateFailed||!u)return;var h,d=u.fragments[0];o.details?0===(l=this.alignPlaylists(a,o.details,null==(h=this.levelLastLoaded)?void 0:h.details))&&d&&sr(a,l=d.start):a.hasProgramDateTime&&u.hasProgramDateTime?(ei(a,u),l=a.fragments[0].start):d&&sr(a,l=d.start)}o.details=a,this.levelLastLoaded=o,this.startFragRequested||!this.mainDetails&&a.live||this.setStartPosition(o.details,l),this.tick(),a.live&&!this.fragCurrent&&this.media&&this.state===fi&&(mr(null,a.fragments,this.media.currentTime,0)||(this.warn("Subtitle playlist not aligned with playback"),o.details=void 0))}}else this.warn("Subtitle tracks were reset while loading level "+s)},r._handleFragmentLoadComplete=function(t){var e=this,r=t.frag,i=t.payload,n=r.decryptdata,a=this.hls;if(!this.fragContextChanged(r)&&i&&i.byteLength>0&&null!=n&&n.key&&n.iv&&"AES-128"===n.method){var s=performance.now();this.decrypter.decrypt(new Uint8Array(i),n.key.buffer,n.iv.buffer).catch((function(t){throw a.trigger(S.ERROR,{type:L.MEDIA_ERROR,details:A.FRAG_DECRYPT_ERROR,fatal:!1,error:t,reason:t.message,frag:r}),t})).then((function(t){var e=performance.now();a.trigger(S.FRAG_DECRYPTED,{frag:r,payload:t,stats:{tstart:s,tdecrypt:e}})})).catch((function(t){e.warn(t.name+": "+t.message),e.state=fi}))}},r.doTick=function(){if(this.media){if(this.state===fi){var t=this.currentTrackId,e=this.levels,r=null==e?void 0:e[t];if(!r||!e.length||!r.details)return;var i=this.config,n=this.getLoadPosition(),a=zr.bufferedInfo(this.tracksBuffered[this.currentTrackId]||[],n,i.maxBufferHole),s=a.end,o=a.len,l=this.getFwdBufferInfo(this.media,Ie),u=r.details;if(o>this.getMaxBufferLength(null==l?void 0:l.len)+u.levelTargetDuration)return;var h=u.fragments,d=h.length,c=u.edge,f=null,g=this.fragPrevious;if(s<c){var v=i.maxFragLookUpTolerance,m=s>c-v?0:v;!(f=mr(g,h,Math.max(h[0].start,s),m))&&g&&g.start<h[0].start&&(f=h[0])}else f=h[d-1];if(!f)return;if("initSegment"!==(f=this.mapToInitFragWhenRequired(f)).sn){var p=h[f.sn-u.startSN-1];p&&p.cc===f.cc&&this.fragmentTracker.getState(p)===Kr&&(f=p)}this.fragmentTracker.getState(f)===Kr&&this.loadFragment(f,r,s)}}else this.state=fi},r.getMaxBufferLength=function(e){var r=t.prototype.getMaxBufferLength.call(this);return e?Math.max(r,e):r},r.loadFragment=function(e,r,i){this.fragCurrent=e,"initSegment"===e.sn?this._loadInitSegment(e,r):(this.startFragRequested=!0,t.prototype.loadFragment.call(this,e,r,i))},s(e,[{key:"mediaBufferTimeRanges",get:function(){return new jn(this.tracksBuffered[this.currentTrackId]||[])}}]),e}(Ri),jn=function(t){this.buffered=void 0;var e=function(e,r,i){if((r>>>=0)>i-1)throw new DOMException("Failed to execute '"+e+"' on 'TimeRanges': The index provided ("+r+") is greater than the maximum bound ("+i+")");return t[r][e]};this.buffered={get length(){return t.length},end:function(r){return e("end",r,t.length)},start:function(r){return e("start",r,t.length)}}},qn=function(t){function e(e){var r;return(r=t.call(this,e,"[subtitle-track-controller]")||this).media=null,r.tracks=[],r.groupIds=null,r.tracksInGroup=[],r.trackId=-1,r.currentTrack=null,r.selectDefaultTrack=!0,r.queuedDefaultTrack=-1,r.asyncPollTrackChange=function(){return r.pollTrackChange(0)},r.useTextTrackPolling=!1,r.subtitlePollingInterval=-1,r._subtitleDisplay=!0,r.onTextTracksChanged=function(){if(r.useTextTrackPolling||self.clearInterval(r.subtitlePollingInterval),r.media&&r.hls.config.renderTextTracksNatively){for(var t=null,e=Ue(r.media.textTracks),i=0;i<e.length;i++)if("hidden"===e[i].mode)t=e[i];else if("showing"===e[i].mode){t=e[i];break}var n=r.findTrackForTextTrack(t);r.subtitleTrack!==n&&r.setSubtitleTrack(n)}},r.registerListeners(),r}l(e,t);var r=e.prototype;return r.destroy=function(){this.unregisterListeners(),this.tracks.length=0,this.tracksInGroup.length=0,this.currentTrack=null,this.onTextTracksChanged=this.asyncPollTrackChange=null,t.prototype.destroy.call(this)},r.registerListeners=function(){var t=this.hls;t.on(S.MEDIA_ATTACHED,this.onMediaAttached,this),t.on(S.MEDIA_DETACHING,this.onMediaDetaching,this),t.on(S.MANIFEST_LOADING,this.onManifestLoading,this),t.on(S.MANIFEST_PARSED,this.onManifestParsed,this),t.on(S.LEVEL_LOADING,this.onLevelLoading,this),t.on(S.LEVEL_SWITCHING,this.onLevelSwitching,this),t.on(S.SUBTITLE_TRACK_LOADED,this.onSubtitleTrackLoaded,this),t.on(S.ERROR,this.onError,this)},r.unregisterListeners=function(){var t=this.hls;t.off(S.MEDIA_ATTACHED,this.onMediaAttached,this),t.off(S.MEDIA_DETACHING,this.onMediaDetaching,this),t.off(S.MANIFEST_LOADING,this.onManifestLoading,this),t.off(S.MANIFEST_PARSED,this.onManifestParsed,this),t.off(S.LEVEL_LOADING,this.onLevelLoading,this),t.off(S.LEVEL_SWITCHING,this.onLevelSwitching,this),t.off(S.SUBTITLE_TRACK_LOADED,this.onSubtitleTrackLoaded,this),t.off(S.ERROR,this.onError,this)},r.onMediaAttached=function(t,e){this.media=e.media,this.media&&(this.queuedDefaultTrack>-1&&(this.subtitleTrack=this.queuedDefaultTrack,this.queuedDefaultTrack=-1),this.useTextTrackPolling=!(this.media.textTracks&&"onchange"in this.media.textTracks),this.useTextTrackPolling?this.pollTrackChange(500):this.media.textTracks.addEventListener("change",this.asyncPollTrackChange))},r.pollTrackChange=function(t){self.clearInterval(this.subtitlePollingInterval),this.subtitlePollingInterval=self.setInterval(this.onTextTracksChanged,t)},r.onMediaDetaching=function(){this.media&&(self.clearInterval(this.subtitlePollingInterval),this.useTextTrackPolling||this.media.textTracks.removeEventListener("change",this.asyncPollTrackChange),this.trackId>-1&&(this.queuedDefaultTrack=this.trackId),Ue(this.media.textTracks).forEach((function(t){Oe(t)})),this.subtitleTrack=-1,this.media=null)},r.onManifestLoading=function(){this.tracks=[],this.groupIds=null,this.tracksInGroup=[],this.trackId=-1,this.currentTrack=null,this.selectDefaultTrack=!0},r.onManifestParsed=function(t,e){this.tracks=e.subtitleTracks},r.onSubtitleTrackLoaded=function(t,e){var r=e.id,i=e.groupId,n=e.details,a=this.tracksInGroup[r];if(a&&a.groupId===i){var s=a.details;a.details=e.details,this.log("Subtitle track "+r+' "'+a.name+'" lang:'+a.lang+" group:"+i+" loaded ["+n.startSN+"-"+n.endSN+"]"),r===this.trackId&&this.playlistLoaded(r,e,s)}else this.warn("Subtitle track with id:"+r+" and group:"+i+" not found in active group "+(null==a?void 0:a.groupId))},r.onLevelLoading=function(t,e){this.switchLevel(e.level)},r.onLevelSwitching=function(t,e){this.switchLevel(e.level)},r.switchLevel=function(t){var e=this.hls.levels[t];if(e){var r=e.subtitleGroups||null,i=this.groupIds,n=this.currentTrack;if(!r||(null==i?void 0:i.length)!==(null==r?void 0:r.length)||null!=r&&r.some((function(t){return-1===(null==i?void 0:i.indexOf(t))}))){this.groupIds=r,this.trackId=-1,this.currentTrack=null;var a=this.tracks.filter((function(t){return!r||-1!==r.indexOf(t.groupId)}));if(a.length)this.selectDefaultTrack&&!a.some((function(t){return t.default}))&&(this.selectDefaultTrack=!1),a.forEach((function(t,e){t.id=e}));else if(!n&&!this.tracksInGroup.length)return;this.tracksInGroup=a;var s=this.hls.config.subtitlePreference;if(!n&&s){this.selectDefaultTrack=!1;var o=Mr(s,a);if(o>-1)n=a[o];else{var l=Mr(s,this.tracks);n=this.tracks[l]}}var u=this.findTrackId(n);-1===u&&n&&(u=this.findTrackId(null));var h={subtitleTracks:a};this.log("Updating subtitle tracks, "+a.length+' track(s) found in "'+(null==r?void 0:r.join(","))+'" group-id'),this.hls.trigger(S.SUBTITLE_TRACKS_UPDATED,h),-1!==u&&-1===this.trackId&&this.setSubtitleTrack(u)}else this.shouldReloadPlaylist(n)&&this.setSubtitleTrack(this.trackId)}},r.findTrackId=function(t){for(var e=this.tracksInGroup,r=this.selectDefaultTrack,i=0;i<e.length;i++){var n=e[i];if((!r||n.default)&&(r||t)&&(!t||Or(n,t)))return i}if(t){for(var a=0;a<e.length;a++){var s=e[a];if(Kn(t.attrs,s.attrs,["LANGUAGE","ASSOC-LANGUAGE","CHARACTERISTICS"]))return a}for(var o=0;o<e.length;o++){var l=e[o];if(Kn(t.attrs,l.attrs,["LANGUAGE"]))return o}}return-1},r.findTrackForTextTrack=function(t){if(t)for(var e=this.tracksInGroup,r=0;r<e.length;r++)if(Hn(e[r],t))return r;return-1},r.onError=function(t,e){!e.fatal&&e.context&&(e.context.type!==De||e.context.id!==this.trackId||this.groupIds&&-1===this.groupIds.indexOf(e.context.groupId)||this.checkRetry(e))},r.setSubtitleOption=function(t){if(this.hls.config.subtitlePreference=t,t){var e=this.allSubtitleTracks;if(this.selectDefaultTrack=!1,e.length){var r=this.currentTrack;if(r&&Or(t,r))return r;var i=Mr(t,this.tracksInGroup);if(i>-1){var n=this.tracksInGroup[i];return this.setSubtitleTrack(i),n}if(r)return null;var a=Mr(t,e);if(a>-1)return e[a]}}return null},r.loadPlaylist=function(e){t.prototype.loadPlaylist.call(this);var r=this.currentTrack;if(this.shouldLoadPlaylist(r)&&r){var i=r.id,n=r.groupId,a=r.url;if(e)try{a=e.addDirectives(a)}catch(t){this.warn("Could not construct new URL with HLS Delivery Directives: "+t)}this.log("Loading subtitle playlist for id "+i),this.hls.trigger(S.SUBTITLE_TRACK_LOADING,{url:a,id:i,groupId:n,deliveryDirectives:e||null})}},r.toggleTrackModes=function(){var t=this.media;if(t){var e,r=Ue(t.textTracks),i=this.currentTrack;if(i&&((e=r.filter((function(t){return Hn(i,t)}))[0])||this.warn('Unable to find subtitle TextTrack with name "'+i.name+'" and language "'+i.lang+'"')),[].slice.call(r).forEach((function(t){"disabled"!==t.mode&&t!==e&&(t.mode="disabled")})),e){var n=this.subtitleDisplay?"showing":"hidden";e.mode!==n&&(e.mode=n)}}},r.setSubtitleTrack=function(t){var e=this.tracksInGroup;if(this.media)if(t<-1||t>=e.length||!y(t))this.warn("Invalid subtitle track id: "+t);else{this.clearTimer(),this.selectDefaultTrack=!1;var r=this.currentTrack,i=e[t]||null;if(this.trackId=t,this.currentTrack=i,this.toggleTrackModes(),i){var n=!!i.details&&!i.details.live;if(t!==this.trackId||i!==r||!n){this.log("Switching to subtitle-track "+t+(i?' "'+i.name+'" lang:'+i.lang+" group:"+i.groupId:""));var a=i.id,s=i.groupId,o=void 0===s?"":s,l=i.name,u=i.type,h=i.url;this.hls.trigger(S.SUBTITLE_TRACK_SWITCH,{id:a,groupId:o,name:l,type:u,url:h});var d=this.switchParams(i.url,null==r?void 0:r.details);this.loadPlaylist(d)}}else this.hls.trigger(S.SUBTITLE_TRACK_SWITCH,{id:t})}else this.queuedDefaultTrack=t},s(e,[{key:"subtitleDisplay",get:function(){return this._subtitleDisplay},set:function(t){this._subtitleDisplay=t,this.trackId>-1&&this.toggleTrackModes()}},{key:"allSubtitleTracks",get:function(){return this.tracks}},{key:"subtitleTracks",get:function(){return this.tracksInGroup}},{key:"subtitleTrack",get:function(){return this.trackId},set:function(t){this.selectDefaultTrack=!1,this.setSubtitleTrack(t)}}]),e}(Dr),Xn=function(){function t(t){this.buffers=void 0,this.queues={video:[],audio:[],audiovideo:[]},this.buffers=t}var e=t.prototype;return e.append=function(t,e,r){var i=this.queues[e];i.push(t),1!==i.length||r||this.executeNext(e)},e.insertAbort=function(t,e){this.queues[e].unshift(t),this.executeNext(e)},e.appendBlocker=function(t){var e,r=new Promise((function(t){e=t})),i={execute:e,onStart:function(){},onComplete:function(){},onError:function(){}};return this.append(i,t),r},e.executeNext=function(t){var e=this.queues[t];if(e.length){var r=e[0];try{r.execute()}catch(e){w.warn('[buffer-operation-queue]: Exception executing "'+t+'" SourceBuffer operation: '+e),r.onError(e);var i=this.buffers[t];null!=i&&i.updating||this.shiftAndExecuteNext(t)}}},e.shiftAndExecuteNext=function(t){this.queues[t].shift(),this.executeNext(t)},e.current=function(t){return this.queues[t][0]},t}(),zn=/(avc[1234]|hvc1|hev1|dvh[1e]|vp09|av01)(?:\.[^.,]+)+/,Qn=function(){function t(t){var e=this;this.details=null,this._objectUrl=null,this.operationQueue=void 0,this.listeners=void 0,this.hls=void 0,this.bufferCodecEventsExpected=0,this._bufferCodecEventsTotal=0,this.media=null,this.mediaSource=null,this.lastMpegAudioChunk=null,this.appendSource=void 0,this.appendErrors={audio:0,video:0,audiovideo:0},this.tracks={},this.pendingTracks={},this.sourceBuffer=void 0,this.log=void 0,this.warn=void 0,this.error=void 0,this._onEndStreaming=function(t){e.hls&&e.hls.pauseBuffering()},this._onStartStreaming=function(t){e.hls&&e.hls.resumeBuffering()},this._onMediaSourceOpen=function(){var t=e.media,r=e.mediaSource;e.log("Media source opened"),t&&(t.removeEventListener("emptied",e._onMediaEmptied),e.updateMediaElementDuration(),e.hls.trigger(S.MEDIA_ATTACHED,{media:t,mediaSource:r})),r&&r.removeEventListener("sourceopen",e._onMediaSourceOpen),e.checkPendingTracks()},this._onMediaSourceClose=function(){e.log("Media source closed")},this._onMediaSourceEnded=function(){e.log("Media source ended")},this._onMediaEmptied=function(){var t=e.mediaSrc,r=e._objectUrl;t!==r&&w.error("Media element src was set while attaching MediaSource ("+r+" > "+t+")")},this.hls=t;var r="[buffer-controller]";this.appendSource=t.config.preferManagedMediaSource,this.log=w.log.bind(w,r),this.warn=w.warn.bind(w,r),this.error=w.error.bind(w,r),this._initSourceBuffer(),this.registerListeners()}var e=t.prototype;return e.hasSourceTypes=function(){return this.getSourceBufferTypes().length>0||Object.keys(this.pendingTracks).length>0},e.destroy=function(){this.unregisterListeners(),this.details=null,this.lastMpegAudioChunk=null,this.hls=null},e.registerListeners=function(){var t=this.hls;t.on(S.MEDIA_ATTACHING,this.onMediaAttaching,this),t.on(S.MEDIA_DETACHING,this.onMediaDetaching,this),t.on(S.MANIFEST_LOADING,this.onManifestLoading,this),t.on(S.MANIFEST_PARSED,this.onManifestParsed,this),t.on(S.BUFFER_RESET,this.onBufferReset,this),t.on(S.BUFFER_APPENDING,this.onBufferAppending,this),t.on(S.BUFFER_CODECS,this.onBufferCodecs,this),t.on(S.BUFFER_EOS,this.onBufferEos,this),t.on(S.BUFFER_FLUSHING,this.onBufferFlushing,this),t.on(S.LEVEL_UPDATED,this.onLevelUpdated,this),t.on(S.FRAG_PARSED,this.onFragParsed,this),t.on(S.FRAG_CHANGED,this.onFragChanged,this)},e.unregisterListeners=function(){var t=this.hls;t.off(S.MEDIA_ATTACHING,this.onMediaAttaching,this),t.off(S.MEDIA_DETACHING,this.onMediaDetaching,this),t.off(S.MANIFEST_LOADING,this.onManifestLoading,this),t.off(S.MANIFEST_PARSED,this.onManifestParsed,this),t.off(S.BUFFER_RESET,this.onBufferReset,this),t.off(S.BUFFER_APPENDING,this.onBufferAppending,this),t.off(S.BUFFER_CODECS,this.onBufferCodecs,this),t.off(S.BUFFER_EOS,this.onBufferEos,this),t.off(S.BUFFER_FLUSHING,this.onBufferFlushing,this),t.off(S.LEVEL_UPDATED,this.onLevelUpdated,this),t.off(S.FRAG_PARSED,this.onFragParsed,this),t.off(S.FRAG_CHANGED,this.onFragChanged,this)},e._initSourceBuffer=function(){this.sourceBuffer={},this.operationQueue=new Xn(this.sourceBuffer),this.listeners={audio:[],video:[],audiovideo:[]},this.appendErrors={audio:0,video:0,audiovideo:0},this.lastMpegAudioChunk=null},e.onManifestLoading=function(){this.bufferCodecEventsExpected=this._bufferCodecEventsTotal=0,this.details=null},e.onManifestParsed=function(t,e){var r=2;(e.audio&&!e.video||!e.altAudio)&&(r=1),this.bufferCodecEventsExpected=this._bufferCodecEventsTotal=r,this.log(this.bufferCodecEventsExpected+" bufferCodec event(s) expected")},e.onMediaAttaching=function(t,e){var r=this.media=e.media,i=te(this.appendSource);if(r&&i){var n,a=this.mediaSource=new i;this.log("created media source: "+(null==(n=a.constructor)?void 0:n.name)),a.addEventListener("sourceopen",this._onMediaSourceOpen),a.addEventListener("sourceended",this._onMediaSourceEnded),a.addEventListener("sourceclose",this._onMediaSourceClose),a.addEventListener("startstreaming",this._onStartStreaming),a.addEventListener("endstreaming",this._onEndStreaming);var s=this._objectUrl=self.URL.createObjectURL(a);if(this.appendSource)try{r.removeAttribute("src");var o=self.ManagedMediaSource;r.disableRemotePlayback=r.disableRemotePlayback||o&&a instanceof o,Jn(r),function(t,e){var r=self.document.createElement("source");r.type="video/mp4",r.src=e,t.appendChild(r)}(r,s),r.load()}catch(t){r.src=s}else r.src=s;r.addEventListener("emptied",this._onMediaEmptied)}},e.onMediaDetaching=function(){var t=this.media,e=this.mediaSource,r=this._objectUrl;if(e){if(this.log("media source detaching"),"open"===e.readyState)try{e.endOfStream()}catch(t){this.warn("onMediaDetaching: "+t.message+" while calling endOfStream")}this.onBufferReset(),e.removeEventListener("sourceopen",this._onMediaSourceOpen),e.removeEventListener("sourceended",this._onMediaSourceEnded),e.removeEventListener("sourceclose",this._onMediaSourceClose),e.removeEventListener("startstreaming",this._onStartStreaming),e.removeEventListener("endstreaming",this._onEndStreaming),t&&(t.removeEventListener("emptied",this._onMediaEmptied),r&&self.URL.revokeObjectURL(r),this.mediaSrc===r?(t.removeAttribute("src"),this.appendSource&&Jn(t),t.load()):this.warn("media|source.src was changed by a third party - skip cleanup")),this.mediaSource=null,this.media=null,this._objectUrl=null,this.bufferCodecEventsExpected=this._bufferCodecEventsTotal,this.pendingTracks={},this.tracks={}}this.hls.trigger(S.MEDIA_DETACHED,void 0)},e.onBufferReset=function(){var t=this;this.getSourceBufferTypes().forEach((function(e){t.resetBuffer(e)})),this._initSourceBuffer()},e.resetBuffer=function(t){var e=this.sourceBuffer[t];try{var r;e&&(this.removeBufferListeners(t),this.sourceBuffer[t]=void 0,null!=(r=this.mediaSource)&&r.sourceBuffers.length&&this.mediaSource.removeSourceBuffer(e))}catch(e){this.warn("onBufferReset "+t,e)}},e.onBufferCodecs=function(t,e){var r=this,i=this.getSourceBufferTypes().length,n=Object.keys(e);if(n.forEach((function(t){if(i){var n=r.tracks[t];if(n&&"function"==typeof n.buffer.changeType){var a,s=e[t],o=s.id,l=s.codec,u=s.levelCodec,h=s.container,d=s.metadata,c=he(n.codec,n.levelCodec),f=null==c?void 0:c.replace(zn,"$1"),g=he(l,u),v=null==(a=g)?void 0:a.replace(zn,"$1");if(g&&f!==v){"audio"===t.slice(0,5)&&(g=ue(g,r.hls.config.preferManagedMediaSource));var m=h+";codecs="+g;r.appendChangeType(t,m),r.log("switching codec "+c+" to "+g),r.tracks[t]={buffer:n.buffer,codec:l,container:h,levelCodec:u,metadata:d,id:o}}}}else r.pendingTracks[t]=e[t]})),!i){var a=Math.max(this.bufferCodecEventsExpected-1,0);this.bufferCodecEventsExpected!==a&&(this.log(a+" bufferCodec event(s) expected "+n.join(",")),this.bufferCodecEventsExpected=a),this.mediaSource&&"open"===this.mediaSource.readyState&&this.checkPendingTracks()}},e.appendChangeType=function(t,e){var r=this,i=this.operationQueue,n={execute:function(){var n=r.sourceBuffer[t];n&&(r.log("changing "+t+" sourceBuffer type to "+e),n.changeType(e)),i.shiftAndExecuteNext(t)},onStart:function(){},onComplete:function(){},onError:function(e){r.warn("Failed to change "+t+" SourceBuffer type",e)}};i.append(n,t,!!this.pendingTracks[t])},e.onBufferAppending=function(t,e){var r=this,i=this.hls,n=this.operationQueue,a=this.tracks,s=e.data,o=e.type,l=e.frag,u=e.part,h=e.chunkMeta,d=h.buffering[o],c=self.performance.now();d.start=c;var f=l.stats.buffering,g=u?u.stats.buffering:null;0===f.start&&(f.start=c),g&&0===g.start&&(g.start=c);var v=a.audio,m=!1;"audio"===o&&"audio/mpeg"===(null==v?void 0:v.container)&&(m=!this.lastMpegAudioChunk||1===h.id||this.lastMpegAudioChunk.sn!==h.sn,this.lastMpegAudioChunk=h);var p=l.start,y={execute:function(){if(d.executeStart=self.performance.now(),m){var t=r.sourceBuffer[o];if(t){var e=p-t.timestampOffset;Math.abs(e)>=.1&&(r.log("Updating audio SourceBuffer timestampOffset to "+p+" (delta: "+e+") sn: "+l.sn+")"),t.timestampOffset=p)}}r.appendExecutor(s,o)},onStart:function(){},onComplete:function(){var t=self.performance.now();d.executeEnd=d.end=t,0===f.first&&(f.first=t),g&&0===g.first&&(g.first=t);var e=r.sourceBuffer,i={};for(var n in e)i[n]=zr.getBuffered(e[n]);r.appendErrors[o]=0,"audio"===o||"video"===o?r.appendErrors.audiovideo=0:(r.appendErrors.audio=0,r.appendErrors.video=0),r.hls.trigger(S.BUFFER_APPENDED,{type:o,frag:l,part:u,chunkMeta:h,parent:l.type,timeRanges:i})},onError:function(t){var e={type:L.MEDIA_ERROR,parent:l.type,details:A.BUFFER_APPEND_ERROR,sourceBufferName:o,frag:l,part:u,chunkMeta:h,error:t,err:t,fatal:!1};if(t.code===DOMException.QUOTA_EXCEEDED_ERR)e.details=A.BUFFER_FULL_ERROR;else{var n=++r.appendErrors[o];e.details=A.BUFFER_APPEND_ERROR,r.warn("Failed "+n+"/"+i.config.appendErrorMaxRetry+' times to append segment in "'+o+'" sourceBuffer'),n>=i.config.appendErrorMaxRetry&&(e.fatal=!0)}i.trigger(S.ERROR,e)}};n.append(y,o,!!this.pendingTracks[o])},e.onBufferFlushing=function(t,e){var r=this,i=this.operationQueue,n=function(t){return{execute:r.removeExecutor.bind(r,t,e.startOffset,e.endOffset),onStart:function(){},onComplete:function(){r.hls.trigger(S.BUFFER_FLUSHED,{type:t})},onError:function(e){r.warn("Failed to remove from "+t+" SourceBuffer",e)}}};e.type?i.append(n(e.type),e.type):this.getSourceBufferTypes().forEach((function(t){i.append(n(t),t)}))},e.onFragParsed=function(t,e){var r=this,i=e.frag,n=e.part,a=[],s=n?n.elementaryStreams:i.elementaryStreams;s[U]?a.push("audiovideo"):(s[O]&&a.push("audio"),s[N]&&a.push("video")),0===a.length&&this.warn("Fragments must have at least one ElementaryStreamType set. type: "+i.type+" level: "+i.level+" sn: "+i.sn),this.blockBuffers((function(){var t=self.performance.now();i.stats.buffering.end=t,n&&(n.stats.buffering.end=t);var e=n?n.stats:i.stats;r.hls.trigger(S.FRAG_BUFFERED,{frag:i,part:n,stats:e,id:i.type})}),a)},e.onFragChanged=function(t,e){this.trimBuffers()},e.onBufferEos=function(t,e){var r=this;this.getSourceBufferTypes().reduce((function(t,i){var n=r.sourceBuffer[i];return!n||e.type&&e.type!==i||(n.ending=!0,n.ended||(n.ended=!0,r.log(i+" sourceBuffer now EOS"))),t&&!(n&&!n.ended)}),!0)&&(this.log("Queueing mediaSource.endOfStream()"),this.blockBuffers((function(){r.getSourceBufferTypes().forEach((function(t){var e=r.sourceBuffer[t];e&&(e.ending=!1)}));var t=r.mediaSource;t&&"open"===t.readyState?(r.log("Calling mediaSource.endOfStream()"),t.endOfStream()):t&&r.log("Could not call mediaSource.endOfStream(). mediaSource.readyState: "+t.readyState)})))},e.onLevelUpdated=function(t,e){var r=e.details;r.fragments.length&&(this.details=r,this.getSourceBufferTypes().length?this.blockBuffers(this.updateMediaElementDuration.bind(this)):this.updateMediaElementDuration())},e.trimBuffers=function(){var t=this.hls,e=this.details,r=this.media;if(r&&null!==e&&this.getSourceBufferTypes().length){var i=t.config,n=r.currentTime,a=e.levelTargetDuration,s=e.live&&null!==i.liveBackBufferLength?i.liveBackBufferLength:i.backBufferLength;if(y(s)&&s>0){var o=Math.max(s,a),l=Math.floor(n/a)*a-o;this.flushBackBuffer(n,a,l)}if(y(i.frontBufferFlushThreshold)&&i.frontBufferFlushThreshold>0){var u=Math.max(i.maxBufferLength,i.frontBufferFlushThreshold),h=Math.max(u,a),d=Math.floor(n/a)*a+h;this.flushFrontBuffer(n,a,d)}}},e.flushBackBuffer=function(t,e,r){var i=this,n=this.details,a=this.sourceBuffer;this.getSourceBufferTypes().forEach((function(s){var o=a[s];if(o){var l=zr.getBuffered(o);if(l.length>0&&r>l.start(0)){if(i.hls.trigger(S.BACK_BUFFER_REACHED,{bufferEnd:r}),null!=n&&n.live)i.hls.trigger(S.LIVE_BACK_BUFFER_REACHED,{bufferEnd:r});else if(o.ended&&l.end(l.length-1)-t<2*e)return void i.log("Cannot flush "+s+" back buffer while SourceBuffer is in ended state");i.hls.trigger(S.BUFFER_FLUSHING,{startOffset:0,endOffset:r,type:s})}}}))},e.flushFrontBuffer=function(t,e,r){var i=this,n=this.sourceBuffer;this.getSourceBufferTypes().forEach((function(a){var s=n[a];if(s){var o=zr.getBuffered(s),l=o.length;if(l<2)return;var u=o.start(l-1),h=o.end(l-1);if(r>u||t>=u&&t<=h)return;if(s.ended&&t-h<2*e)return void i.log("Cannot flush "+a+" front buffer while SourceBuffer is in ended state");i.hls.trigger(S.BUFFER_FLUSHING,{startOffset:u,endOffset:1/0,type:a})}}))},e.updateMediaElementDuration=function(){if(this.details&&this.media&&this.mediaSource&&"open"===this.mediaSource.readyState){var t=this.details,e=this.hls,r=this.media,i=this.mediaSource,n=t.fragments[0].start+t.totalduration,a=r.duration,s=y(i.duration)?i.duration:0;t.live&&e.config.liveDurationInfinity?(i.duration=1/0,this.updateSeekableRange(t)):(n>s&&n>a||!y(a))&&(this.log("Updating Media Source duration to "+n.toFixed(3)),i.duration=n)}},e.updateSeekableRange=function(t){var e=this.mediaSource,r=t.fragments;if(r.length&&t.live&&null!=e&&e.setLiveSeekableRange){var i=Math.max(0,r[0].start),n=Math.max(i,i+t.totalduration);this.log("Media Source duration is set to "+e.duration+". Setting seekable range to "+i+"-"+n+"."),e.setLiveSeekableRange(i,n)}},e.checkPendingTracks=function(){var t=this.bufferCodecEventsExpected,e=this.operationQueue,r=this.pendingTracks,i=Object.keys(r).length;if(i&&(!t||2===i||"audiovideo"in r)){this.createSourceBuffers(r),this.pendingTracks={};var n=this.getSourceBufferTypes();if(n.length)this.hls.trigger(S.BUFFER_CREATED,{tracks:this.tracks}),n.forEach((function(t){e.executeNext(t)}));else{var a=new Error("could not create source buffer for media codec(s)");this.hls.trigger(S.ERROR,{type:L.MEDIA_ERROR,details:A.BUFFER_INCOMPATIBLE_CODECS_ERROR,fatal:!0,error:a,reason:a.message})}}},e.createSourceBuffers=function(t){var e=this,r=this.sourceBuffer,i=this.mediaSource;if(!i)throw Error("createSourceBuffers called when mediaSource was null");var n=function(n){if(!r[n]){var a=t[n];if(!a)throw Error("source buffer exists for track "+n+", however track does not");var s=a.levelCodec||a.codec;s&&"audio"===n.slice(0,5)&&(s=ue(s,e.hls.config.preferManagedMediaSource));var o=a.container+";codecs="+s;e.log("creating sourceBuffer("+o+")");try{var l=r[n]=i.addSourceBuffer(o),u=n;e.addBufferListener(u,"updatestart",e._onSBUpdateStart),e.addBufferListener(u,"updateend",e._onSBUpdateEnd),e.addBufferListener(u,"error",e._onSBUpdateError),e.addBufferListener(u,"bufferedchange",(function(t,r){var i=r.removedRanges;null!=i&&i.length&&e.hls.trigger(S.BUFFER_FLUSHED,{type:n})})),e.tracks[n]={buffer:l,codec:s,container:a.container,levelCodec:a.levelCodec,metadata:a.metadata,id:a.id}}catch(t){e.error("error while trying to add sourceBuffer: "+t.message),e.hls.trigger(S.ERROR,{type:L.MEDIA_ERROR,details:A.BUFFER_ADD_CODEC_ERROR,fatal:!1,error:t,sourceBufferName:n,mimeType:o})}}};for(var a in t)n(a)},e._onSBUpdateStart=function(t){this.operationQueue.current(t).onStart()},e._onSBUpdateEnd=function(t){var e;if("closed"!==(null==(e=this.mediaSource)?void 0:e.readyState)){var r=this.operationQueue;r.current(t).onComplete(),r.shiftAndExecuteNext(t)}else this.resetBuffer(t)},e._onSBUpdateError=function(t,e){var r,i=new Error(t+" SourceBuffer error. MediaSource readyState: "+(null==(r=this.mediaSource)?void 0:r.readyState));this.error(""+i,e),this.hls.trigger(S.ERROR,{type:L.MEDIA_ERROR,details:A.BUFFER_APPENDING_ERROR,sourceBufferName:t,error:i,fatal:!1});var n=this.operationQueue.current(t);n&&n.onError(i)},e.removeExecutor=function(t,e,r){var i=this.media,n=this.mediaSource,a=this.operationQueue,s=this.sourceBuffer[t];if(!i||!n||!s)return this.warn("Attempting to remove from the "+t+" SourceBuffer, but it does not exist"),void a.shiftAndExecuteNext(t);var o=y(i.duration)?i.duration:1/0,l=y(n.duration)?n.duration:1/0,u=Math.max(0,e),h=Math.min(r,o,l);h>u&&(!s.ending||s.ended)?(s.ended=!1,this.log("Removing ["+u+","+h+"] from the "+t+" SourceBuffer"),s.remove(u,h)):a.shiftAndExecuteNext(t)},e.appendExecutor=function(t,e){var r=this.sourceBuffer[e];if(r)r.ended=!1,r.appendBuffer(t);else if(!this.pendingTracks[e])throw new Error("Attempting to append to the "+e+" SourceBuffer, but it does not exist")},e.blockBuffers=function(t,e){var r=this;if(void 0===e&&(e=this.getSourceBufferTypes()),!e.length)return this.log("Blocking operation requested, but no SourceBuffers exist"),void Promise.resolve().then(t);var i=this.operationQueue,n=e.map((function(t){return i.appendBlocker(t)}));Promise.all(n).then((function(){t(),e.forEach((function(t){var e=r.sourceBuffer[t];null!=e&&e.updating||i.shiftAndExecuteNext(t)}))}))},e.getSourceBufferTypes=function(){return Object.keys(this.sourceBuffer)},e.addBufferListener=function(t,e,r){var i=this.sourceBuffer[t];if(i){var n=r.bind(this,t);this.listeners[t].push({event:e,listener:n}),i.addEventListener(e,n)}},e.removeBufferListeners=function(t){var e=this.sourceBuffer[t];e&&this.listeners[t].forEach((function(t){e.removeEventListener(t.event,t.listener)}))},s(t,[{key:"mediaSrc",get:function(){var t,e=(null==(t=this.media)?void 0:t.firstChild)||this.media;return null==e?void 0:e.src}}]),t}();function Jn(t){var e=t.querySelectorAll("source");[].slice.call(e).forEach((function(e){t.removeChild(e)}))}var $n={42:225,92:233,94:237,95:243,96:250,123:231,124:247,125:209,126:241,127:9608,128:174,129:176,130:189,131:191,132:8482,133:162,134:163,135:9834,136:224,137:32,138:232,139:226,140:234,141:238,142:244,143:251,144:193,145:201,146:211,147:218,148:220,149:252,150:8216,151:161,152:42,153:8217,154:9473,155:169,156:8480,157:8226,158:8220,159:8221,160:192,161:194,162:199,163:200,164:202,165:203,166:235,167:206,168:207,169:239,170:212,171:217,172:249,173:219,174:171,175:187,176:195,177:227,178:205,179:204,180:236,181:210,182:242,183:213,184:245,185:123,186:125,187:92,188:94,189:95,190:124,191:8764,192:196,193:228,194:214,195:246,196:223,197:165,198:164,199:9475,200:197,201:229,202:216,203:248,204:9487,205:9491,206:9495,207:9499},Zn=function(t){var e=t;return $n.hasOwnProperty(t)&&(e=$n[t]),String.fromCharCode(e)},ta=15,ea=100,ra={17:1,18:3,21:5,22:7,23:9,16:11,19:12,20:14},ia={17:2,18:4,21:6,22:8,23:10,19:13,20:15},na={25:1,26:3,29:5,30:7,31:9,24:11,27:12,28:14},aa={25:2,26:4,29:6,30:8,31:10,27:13,28:15},sa=["white","green","blue","cyan","red","yellow","magenta","black","transparent"],oa=function(){function t(){this.time=null,this.verboseLevel=0}return t.prototype.log=function(t,e){if(this.verboseLevel>=t){var r="function"==typeof e?e():e;w.log(this.time+" ["+t+"] "+r)}},t}(),la=function(t){for(var e=[],r=0;r<t.length;r++)e.push(t[r].toString(16));return e},ua=function(){function t(){this.foreground="white",this.underline=!1,this.italics=!1,this.background="black",this.flash=!1}var e=t.prototype;return e.reset=function(){this.foreground="white",this.underline=!1,this.italics=!1,this.background="black",this.flash=!1},e.setStyles=function(t){for(var e=["foreground","underline","italics","background","flash"],r=0;r<e.length;r++){var i=e[r];t.hasOwnProperty(i)&&(this[i]=t[i])}},e.isDefault=function(){return"white"===this.foreground&&!this.underline&&!this.italics&&"black"===this.background&&!this.flash},e.equals=function(t){return this.foreground===t.foreground&&this.underline===t.underline&&this.italics===t.italics&&this.background===t.background&&this.flash===t.flash},e.copy=function(t){this.foreground=t.foreground,this.underline=t.underline,this.italics=t.italics,this.background=t.background,this.flash=t.flash},e.toString=function(){return"color="+this.foreground+", underline="+this.underline+", italics="+this.italics+", background="+this.background+", flash="+this.flash},t}(),ha=function(){function t(){this.uchar=" ",this.penState=new ua}var e=t.prototype;return e.reset=function(){this.uchar=" ",this.penState.reset()},e.setChar=function(t,e){this.uchar=t,this.penState.copy(e)},e.setPenState=function(t){this.penState.copy(t)},e.equals=function(t){return this.uchar===t.uchar&&this.penState.equals(t.penState)},e.copy=function(t){this.uchar=t.uchar,this.penState.copy(t.penState)},e.isEmpty=function(){return" "===this.uchar&&this.penState.isDefault()},t}(),da=function(){function t(t){this.chars=[],this.pos=0,this.currPenState=new ua,this.cueStartTime=null,this.logger=void 0;for(var e=0;e<ea;e++)this.chars.push(new ha);this.logger=t}var e=t.prototype;return e.equals=function(t){for(var e=0;e<ea;e++)if(!this.chars[e].equals(t.chars[e]))return!1;return!0},e.copy=function(t){for(var e=0;e<ea;e++)this.chars[e].copy(t.chars[e])},e.isEmpty=function(){for(var t=!0,e=0;e<ea;e++)if(!this.chars[e].isEmpty()){t=!1;break}return t},e.setCursor=function(t){this.pos!==t&&(this.pos=t),this.pos<0?(this.logger.log(3,"Negative cursor position "+this.pos),this.pos=0):this.pos>ea&&(this.logger.log(3,"Too large cursor position "+this.pos),this.pos=ea)},e.moveCursor=function(t){var e=this.pos+t;if(t>1)for(var r=this.pos+1;r<e+1;r++)this.chars[r].setPenState(this.currPenState);this.setCursor(e)},e.backSpace=function(){this.moveCursor(-1),this.chars[this.pos].setChar(" ",this.currPenState)},e.insertChar=function(t){var e=this;t>=144&&this.backSpace();var r=Zn(t);this.pos>=ea?this.logger.log(0,(function(){return"Cannot insert "+t.toString(16)+" ("+r+") at position "+e.pos+". Skipping it!"})):(this.chars[this.pos].setChar(r,this.currPenState),this.moveCursor(1))},e.clearFromPos=function(t){var e;for(e=t;e<ea;e++)this.chars[e].reset()},e.clear=function(){this.clearFromPos(0),this.pos=0,this.currPenState.reset()},e.clearToEndOfRow=function(){this.clearFromPos(this.pos)},e.getTextString=function(){for(var t=[],e=!0,r=0;r<ea;r++){var i=this.chars[r].uchar;" "!==i&&(e=!1),t.push(i)}return e?"":t.join("")},e.setPenStyles=function(t){this.currPenState.setStyles(t),this.chars[this.pos].setPenState(this.currPenState)},t}(),ca=function(){function t(t){this.rows=[],this.currRow=14,this.nrRollUpRows=null,this.lastOutputScreen=null,this.logger=void 0;for(var e=0;e<ta;e++)this.rows.push(new da(t));this.logger=t}var e=t.prototype;return e.reset=function(){for(var t=0;t<ta;t++)this.rows[t].clear();this.currRow=14},e.equals=function(t){for(var e=!0,r=0;r<ta;r++)if(!this.rows[r].equals(t.rows[r])){e=!1;break}return e},e.copy=function(t){for(var e=0;e<ta;e++)this.rows[e].copy(t.rows[e])},e.isEmpty=function(){for(var t=!0,e=0;e<ta;e++)if(!this.rows[e].isEmpty()){t=!1;break}return t},e.backSpace=function(){this.rows[this.currRow].backSpace()},e.clearToEndOfRow=function(){this.rows[this.currRow].clearToEndOfRow()},e.insertChar=function(t){this.rows[this.currRow].insertChar(t)},e.setPen=function(t){this.rows[this.currRow].setPenStyles(t)},e.moveCursor=function(t){this.rows[this.currRow].moveCursor(t)},e.setCursor=function(t){this.logger.log(2,"setCursor: "+t),this.rows[this.currRow].setCursor(t)},e.setPAC=function(t){this.logger.log(2,(function(){return"pacData = "+JSON.stringify(t)}));var e=t.row-1;if(this.nrRollUpRows&&e<this.nrRollUpRows-1&&(e=this.nrRollUpRows-1),this.nrRollUpRows&&this.currRow!==e){for(var r=0;r<ta;r++)this.rows[r].clear();var i=this.currRow+1-this.nrRollUpRows,n=this.lastOutputScreen;if(n){var a=n.rows[i].cueStartTime,s=this.logger.time;if(null!==a&&null!==s&&a<s)for(var o=0;o<this.nrRollUpRows;o++)this.rows[e-this.nrRollUpRows+o+1].copy(n.rows[i+o])}}this.currRow=e;var l=this.rows[this.currRow];if(null!==t.indent){var u=t.indent,h=Math.max(u-1,0);l.setCursor(t.indent),t.color=l.chars[h].penState.foreground}var d={foreground:t.color,underline:t.underline,italics:t.italics,background:"black",flash:!1};this.setPen(d)},e.setBkgData=function(t){this.logger.log(2,(function(){return"bkgData = "+JSON.stringify(t)})),this.backSpace(),this.setPen(t),this.insertChar(32)},e.setRollUpRows=function(t){this.nrRollUpRows=t},e.rollUp=function(){var t=this;if(null!==this.nrRollUpRows){this.logger.log(1,(function(){return t.getDisplayText()}));var e=this.currRow+1-this.nrRollUpRows,r=this.rows.splice(e,1)[0];r.clear(),this.rows.splice(this.currRow,0,r),this.logger.log(2,"Rolling up")}else this.logger.log(3,"roll_up but nrRollUpRows not set yet")},e.getDisplayText=function(t){t=t||!1;for(var e=[],r="",i=-1,n=0;n<ta;n++){var a=this.rows[n].getTextString();a&&(i=n+1,t?e.push("Row "+i+": '"+a+"'"):e.push(a.trim()))}return e.length>0&&(r=t?"["+e.join(" | ")+"]":e.join("\n")),r},e.getTextAndFormat=function(){return this.rows},t}(),fa=function(){function t(t,e,r){this.chNr=void 0,this.outputFilter=void 0,this.mode=void 0,this.verbose=void 0,this.displayedMemory=void 0,this.nonDisplayedMemory=void 0,this.lastOutputScreen=void 0,this.currRollUpRow=void 0,this.writeScreen=void 0,this.cueStartTime=void 0,this.logger=void 0,this.chNr=t,this.outputFilter=e,this.mode=null,this.verbose=0,this.displayedMemory=new ca(r),this.nonDisplayedMemory=new ca(r),this.lastOutputScreen=new ca(r),this.currRollUpRow=this.displayedMemory.rows[14],this.writeScreen=this.displayedMemory,this.mode=null,this.cueStartTime=null,this.logger=r}var e=t.prototype;return e.reset=function(){this.mode=null,this.displayedMemory.reset(),this.nonDisplayedMemory.reset(),this.lastOutputScreen.reset(),this.outputFilter.reset(),this.currRollUpRow=this.displayedMemory.rows[14],this.writeScreen=this.displayedMemory,this.mode=null,this.cueStartTime=null},e.getHandler=function(){return this.outputFilter},e.setHandler=function(t){this.outputFilter=t},e.setPAC=function(t){this.writeScreen.setPAC(t)},e.setBkgData=function(t){this.writeScreen.setBkgData(t)},e.setMode=function(t){t!==this.mode&&(this.mode=t,this.logger.log(2,(function(){return"MODE="+t})),"MODE_POP-ON"===this.mode?this.writeScreen=this.nonDisplayedMemory:(this.writeScreen=this.displayedMemory,this.writeScreen.reset()),"MODE_ROLL-UP"!==this.mode&&(this.displayedMemory.nrRollUpRows=null,this.nonDisplayedMemory.nrRollUpRows=null),this.mode=t)},e.insertChars=function(t){for(var e=this,r=0;r<t.length;r++)this.writeScreen.insertChar(t[r]);var i=this.writeScreen===this.displayedMemory?"DISP":"NON_DISP";this.logger.log(2,(function(){return i+": "+e.writeScreen.getDisplayText(!0)})),"MODE_PAINT-ON"!==this.mode&&"MODE_ROLL-UP"!==this.mode||(this.logger.log(1,(function(){return"DISPLAYED: "+e.displayedMemory.getDisplayText(!0)})),this.outputDataUpdate())},e.ccRCL=function(){this.logger.log(2,"RCL - Resume Caption Loading"),this.setMode("MODE_POP-ON")},e.ccBS=function(){this.logger.log(2,"BS - BackSpace"),"MODE_TEXT"!==this.mode&&(this.writeScreen.backSpace(),this.writeScreen===this.displayedMemory&&this.outputDataUpdate())},e.ccAOF=function(){},e.ccAON=function(){},e.ccDER=function(){this.logger.log(2,"DER- Delete to End of Row"),this.writeScreen.clearToEndOfRow(),this.outputDataUpdate()},e.ccRU=function(t){this.logger.log(2,"RU("+t+") - Roll Up"),this.writeScreen=this.displayedMemory,this.setMode("MODE_ROLL-UP"),this.writeScreen.setRollUpRows(t)},e.ccFON=function(){this.logger.log(2,"FON - Flash On"),this.writeScreen.setPen({flash:!0})},e.ccRDC=function(){this.logger.log(2,"RDC - Resume Direct Captioning"),this.setMode("MODE_PAINT-ON")},e.ccTR=function(){this.logger.log(2,"TR"),this.setMode("MODE_TEXT")},e.ccRTD=function(){this.logger.log(2,"RTD"),this.setMode("MODE_TEXT")},e.ccEDM=function(){this.logger.log(2,"EDM - Erase Displayed Memory"),this.displayedMemory.reset(),this.outputDataUpdate(!0)},e.ccCR=function(){this.logger.log(2,"CR - Carriage Return"),this.writeScreen.rollUp(),this.outputDataUpdate(!0)},e.ccENM=function(){this.logger.log(2,"ENM - Erase Non-displayed Memory"),this.nonDisplayedMemory.reset()},e.ccEOC=function(){var t=this;if(this.logger.log(2,"EOC - End Of Caption"),"MODE_POP-ON"===this.mode){var e=this.displayedMemory;this.displayedMemory=this.nonDisplayedMemory,this.nonDisplayedMemory=e,this.writeScreen=this.nonDisplayedMemory,this.logger.log(1,(function(){return"DISP: "+t.displayedMemory.getDisplayText()}))}this.outputDataUpdate(!0)},e.ccTO=function(t){this.logger.log(2,"TO("+t+") - Tab Offset"),this.writeScreen.moveCursor(t)},e.ccMIDROW=function(t){var e={flash:!1};if(e.underline=t%2==1,e.italics=t>=46,e.italics)e.foreground="white";else{var r=Math.floor(t/2)-16;e.foreground=["white","green","blue","cyan","red","yellow","magenta"][r]}this.logger.log(2,"MIDROW: "+JSON.stringify(e)),this.writeScreen.setPen(e)},e.outputDataUpdate=function(t){void 0===t&&(t=!1);var e=this.logger.time;null!==e&&this.outputFilter&&(null!==this.cueStartTime||this.displayedMemory.isEmpty()?this.displayedMemory.equals(this.lastOutputScreen)||(this.outputFilter.newCue(this.cueStartTime,e,this.lastOutputScreen),t&&this.outputFilter.dispatchCue&&this.outputFilter.dispatchCue(),this.cueStartTime=this.displayedMemory.isEmpty()?null:e):this.cueStartTime=e,this.lastOutputScreen.copy(this.displayedMemory))},e.cueSplitAtTime=function(t){this.outputFilter&&(this.displayedMemory.isEmpty()||(this.outputFilter.newCue&&this.outputFilter.newCue(this.cueStartTime,t,this.displayedMemory),this.cueStartTime=t))},t}(),ga=function(){function t(t,e,r){this.channels=void 0,this.currentChannel=0,this.cmdHistory={a:null,b:null},this.logger=void 0;var i=this.logger=new oa;this.channels=[null,new fa(t,e,i),new fa(t+1,r,i)]}var e=t.prototype;return e.getHandler=function(t){return this.channels[t].getHandler()},e.setHandler=function(t,e){this.channels[t].setHandler(e)},e.addData=function(t,e){var r,i,n,a=!1;this.logger.time=t;for(var s=0;s<e.length;s+=2)if(i=127&e[s],n=127&e[s+1],0!==i||0!==n){if(this.logger.log(3,"["+la([e[s],e[s+1]])+"] -> ("+la([i,n])+")"),(r=this.parseCmd(i,n))||(r=this.parseMidrow(i,n)),r||(r=this.parsePAC(i,n)),r||(r=this.parseBackgroundAttributes(i,n)),!r&&(a=this.parseChars(i,n))){var o=this.currentChannel;o&&o>0?this.channels[o].insertChars(a):this.logger.log(2,"No channel found yet. TEXT-MODE?")}r||a||this.logger.log(2,"Couldn't parse cleaned data "+la([i,n])+" orig: "+la([e[s],e[s+1]]))}},e.parseCmd=function(t,e){var r=this.cmdHistory;if(!((20===t||28===t||21===t||29===t)&&e>=32&&e<=47||(23===t||31===t)&&e>=33&&e<=35))return!1;if(ma(t,e,r))return va(null,null,r),this.logger.log(3,"Repeated command ("+la([t,e])+") is dropped"),!0;var i=20===t||21===t||23===t?1:2,n=this.channels[i];return 20===t||21===t||28===t||29===t?32===e?n.ccRCL():33===e?n.ccBS():34===e?n.ccAOF():35===e?n.ccAON():36===e?n.ccDER():37===e?n.ccRU(2):38===e?n.ccRU(3):39===e?n.ccRU(4):40===e?n.ccFON():41===e?n.ccRDC():42===e?n.ccTR():43===e?n.ccRTD():44===e?n.ccEDM():45===e?n.ccCR():46===e?n.ccENM():47===e&&n.ccEOC():n.ccTO(e-32),va(t,e,r),this.currentChannel=i,!0},e.parseMidrow=function(t,e){var r=0;if((17===t||25===t)&&e>=32&&e<=47){if((r=17===t?1:2)!==this.currentChannel)return this.logger.log(0,"Mismatch channel in midrow parsing"),!1;var i=this.channels[r];return!!i&&(i.ccMIDROW(e),this.logger.log(3,"MIDROW ("+la([t,e])+")"),!0)}return!1},e.parsePAC=function(t,e){var r,i=this.cmdHistory;if(!((t>=17&&t<=23||t>=25&&t<=31)&&e>=64&&e<=127||(16===t||24===t)&&e>=64&&e<=95))return!1;if(ma(t,e,i))return va(null,null,i),!0;var n=t<=23?1:2;r=e>=64&&e<=95?1===n?ra[t]:na[t]:1===n?ia[t]:aa[t];var a=this.channels[n];return!!a&&(a.setPAC(this.interpretPAC(r,e)),va(t,e,i),this.currentChannel=n,!0)},e.interpretPAC=function(t,e){var r,i={color:null,italics:!1,indent:null,underline:!1,row:t};return r=e>95?e-96:e-64,i.underline=1==(1&r),r<=13?i.color=["white","green","blue","cyan","red","yellow","magenta","white"][Math.floor(r/2)]:r<=15?(i.italics=!0,i.color="white"):i.indent=4*Math.floor((r-16)/2),i},e.parseChars=function(t,e){var r,i,n=null,a=null;if(t>=25?(r=2,a=t-8):(r=1,a=t),a>=17&&a<=19?(i=17===a?e+80:18===a?e+112:e+144,this.logger.log(2,"Special char '"+Zn(i)+"' in channel "+r),n=[i]):t>=32&&t<=127&&(n=0===e?[t]:[t,e]),n){var s=la(n);this.logger.log(3,"Char codes = "+s.join(",")),va(t,e,this.cmdHistory)}return n},e.parseBackgroundAttributes=function(t,e){var r;if(!((16===t||24===t)&&e>=32&&e<=47||(23===t||31===t)&&e>=45&&e<=47))return!1;var i={};16===t||24===t?(r=Math.floor((e-32)/2),i.background=sa[r],e%2==1&&(i.background=i.background+"_semi")):45===e?i.background="transparent":(i.foreground="black",47===e&&(i.underline=!0));var n=t<=23?1:2;return this.channels[n].setBkgData(i),va(t,e,this.cmdHistory),!0},e.reset=function(){for(var t=0;t<Object.keys(this.channels).length;t++){var e=this.channels[t];e&&e.reset()}this.cmdHistory={a:null,b:null}},e.cueSplitAtTime=function(t){for(var e=0;e<this.channels.length;e++){var r=this.channels[e];r&&r.cueSplitAtTime(t)}},t}();function va(t,e,r){r.a=t,r.b=e}function ma(t,e,r){return r.a===t&&r.b===e}var pa=function(){function t(t,e){this.timelineController=void 0,this.cueRanges=[],this.trackName=void 0,this.startTime=null,this.endTime=null,this.screen=null,this.timelineController=t,this.trackName=e}var e=t.prototype;return e.dispatchCue=function(){null!==this.startTime&&(this.timelineController.addCues(this.trackName,this.startTime,this.endTime,this.screen,this.cueRanges),this.startTime=null)},e.newCue=function(t,e,r){(null===this.startTime||this.startTime>t)&&(this.startTime=t),this.endTime=e,this.screen=r,this.timelineController.createCaptionsTrack(this.trackName)},e.reset=function(){this.cueRanges=[],this.startTime=null},t}(),ya=function(){if(null!=j&&j.VTTCue)return self.VTTCue;var t=["","lr","rl"],e=["start","middle","end","left","right"];function r(t,e){if("string"!=typeof e)return!1;if(!Array.isArray(t))return!1;var r=e.toLowerCase();return!!~t.indexOf(r)&&r}function i(t){return r(e,t)}function n(t){for(var e=arguments.length,r=new Array(e>1?e-1:0),i=1;i<e;i++)r[i-1]=arguments[i];for(var n=1;n<arguments.length;n++){var a=arguments[n];for(var s in a)t[s]=a[s]}return t}function a(e,a,s){var o=this,l={enumerable:!0};o.hasBeenReset=!1;var u="",h=!1,d=e,c=a,f=s,g=null,v="",m=!0,p="auto",y="start",E=50,T="middle",S=50,L="middle";Object.defineProperty(o,"id",n({},l,{get:function(){return u},set:function(t){u=""+t}})),Object.defineProperty(o,"pauseOnExit",n({},l,{get:function(){return h},set:function(t){h=!!t}})),Object.defineProperty(o,"startTime",n({},l,{get:function(){return d},set:function(t){if("number"!=typeof t)throw new TypeError("Start time must be set to a number.");d=t,this.hasBeenReset=!0}})),Object.defineProperty(o,"endTime",n({},l,{get:function(){return c},set:function(t){if("number"!=typeof t)throw new TypeError("End time must be set to a number.");c=t,this.hasBeenReset=!0}})),Object.defineProperty(o,"text",n({},l,{get:function(){return f},set:function(t){f=""+t,this.hasBeenReset=!0}})),Object.defineProperty(o,"region",n({},l,{get:function(){return g},set:function(t){g=t,this.hasBeenReset=!0}})),Object.defineProperty(o,"vertical",n({},l,{get:function(){return v},set:function(e){var i=function(e){return r(t,e)}(e);if(!1===i)throw new SyntaxError("An invalid or illegal string was specified.");v=i,this.hasBeenReset=!0}})),Object.defineProperty(o,"snapToLines",n({},l,{get:function(){return m},set:function(t){m=!!t,this.hasBeenReset=!0}})),Object.defineProperty(o,"line",n({},l,{get:function(){return p},set:function(t){if("number"!=typeof t&&"auto"!==t)throw new SyntaxError("An invalid number or illegal string was specified.");p=t,this.hasBeenReset=!0}})),Object.defineProperty(o,"lineAlign",n({},l,{get:function(){return y},set:function(t){var e=i(t);if(!e)throw new SyntaxError("An invalid or illegal string was specified.");y=e,this.hasBeenReset=!0}})),Object.defineProperty(o,"position",n({},l,{get:function(){return E},set:function(t){if(t<0||t>100)throw new Error("Position must be between 0 and 100.");E=t,this.hasBeenReset=!0}})),Object.defineProperty(o,"positionAlign",n({},l,{get:function(){return T},set:function(t){var e=i(t);if(!e)throw new SyntaxError("An invalid or illegal string was specified.");T=e,this.hasBeenReset=!0}})),Object.defineProperty(o,"size",n({},l,{get:function(){return S},set:function(t){if(t<0||t>100)throw new Error("Size must be between 0 and 100.");S=t,this.hasBeenReset=!0}})),Object.defineProperty(o,"align",n({},l,{get:function(){return L},set:function(t){var e=i(t);if(!e)throw new SyntaxError("An invalid or illegal string was specified.");L=e,this.hasBeenReset=!0}})),o.displayState=void 0}return a.prototype.getCueAsHTML=function(){return self.WebVTT.convertCueToDOMTree(self,this.text)},a}(),Ea=function(){function t(){}return t.prototype.decode=function(t,e){if(!t)return"";if("string"!=typeof t)throw new Error("Error - expected string data.");return decodeURIComponent(encodeURIComponent(t))},t}();function Ta(t){function e(t,e,r,i){return 3600*(0|t)+60*(0|e)+(0|r)+parseFloat(i||0)}var r=t.match(/^(?:(\d+):)?(\d{2}):(\d{2})(\.\d+)?/);return r?parseFloat(r[2])>59?e(r[2],r[3],0,r[4]):e(r[1],r[2],r[3],r[4]):null}var Sa=function(){function t(){this.values=Object.create(null)}var e=t.prototype;return e.set=function(t,e){this.get(t)||""===e||(this.values[t]=e)},e.get=function(t,e,r){return r?this.has(t)?this.values[t]:e[r]:this.has(t)?this.values[t]:e},e.has=function(t){return t in this.values},e.alt=function(t,e,r){for(var i=0;i<r.length;++i)if(e===r[i]){this.set(t,e);break}},e.integer=function(t,e){/^-?\d+$/.test(e)&&this.set(t,parseInt(e,10))},e.percent=function(t,e){if(/^([\d]{1,3})(\.[\d]*)?%$/.test(e)){var r=parseFloat(e);if(r>=0&&r<=100)return this.set(t,r),!0}return!1},t}();function La(t,e,r,i){var n=i?t.split(i):[t];for(var a in n)if("string"==typeof n[a]){var s=n[a].split(r);2===s.length&&e(s[0],s[1])}}var Aa=new ya(0,0,""),Ra="middle"===Aa.align?"middle":"center";function ka(t,e,r){var i=t;function n(){var e=Ta(t);if(null===e)throw new Error("Malformed timestamp: "+i);return t=t.replace(/^[^\sa-zA-Z-]+/,""),e}function a(){t=t.replace(/^\s+/,"")}if(a(),e.startTime=n(),a(),"--\x3e"!==t.slice(0,3))throw new Error("Malformed time stamp (time stamps must be separated by '--\x3e'): "+i);t=t.slice(3),a(),e.endTime=n(),a(),function(t,e){var i=new Sa;La(t,(function(t,e){var n;switch(t){case"region":for(var a=r.length-1;a>=0;a--)if(r[a].id===e){i.set(t,r[a].region);break}break;case"vertical":i.alt(t,e,["rl","lr"]);break;case"line":n=e.split(","),i.integer(t,n[0]),i.percent(t,n[0])&&i.set("snapToLines",!1),i.alt(t,n[0],["auto"]),2===n.length&&i.alt("lineAlign",n[1],["start",Ra,"end"]);break;case"position":n=e.split(","),i.percent(t,n[0]),2===n.length&&i.alt("positionAlign",n[1],["start",Ra,"end","line-left","line-right","auto"]);break;case"size":i.percent(t,e);break;case"align":i.alt(t,e,["start",Ra,"end","left","right"])}}),/:/,/\s/),e.region=i.get("region",null),e.vertical=i.get("vertical","");var n=i.get("line","auto");"auto"===n&&-1===Aa.line&&(n=-1),e.line=n,e.lineAlign=i.get("lineAlign","start"),e.snapToLines=i.get("snapToLines",!0),e.size=i.get("size",100),e.align=i.get("align",Ra);var a=i.get("position","auto");"auto"===a&&50===Aa.position&&(a="start"===e.align||"left"===e.align?0:"end"===e.align||"right"===e.align?100:50),e.position=a}(t,e)}function ba(t){return t.replace(/<br(?: \/)?>/gi,"\n")}var Da=function(){function t(){this.state="INITIAL",this.buffer="",this.decoder=new Ea,this.regionList=[],this.cue=null,this.oncue=void 0,this.onparsingerror=void 0,this.onflush=void 0}var e=t.prototype;return e.parse=function(t){var e=this;function r(){var t=e.buffer,r=0;for(t=ba(t);r<t.length&&"\r"!==t[r]&&"\n"!==t[r];)++r;var i=t.slice(0,r);return"\r"===t[r]&&++r,"\n"===t[r]&&++r,e.buffer=t.slice(r),i}t&&(e.buffer+=e.decoder.decode(t,{stream:!0}));try{var i="";if("INITIAL"===e.state){if(!/\r\n|\n/.test(e.buffer))return this;var n=(i=r()).match(/^()?WEBVTT([ \t].*)?$/);if(null==n||!n[0])throw new Error("Malformed WebVTT signature.");e.state="HEADER"}for(var a=!1;e.buffer;){if(!/\r\n|\n/.test(e.buffer))return this;switch(a?a=!1:i=r(),e.state){case"HEADER":/:/.test(i)?La(i,(function(t,e){}),/:/):i||(e.state="ID");continue;case"NOTE":i||(e.state="ID");continue;case"ID":if(/^NOTE($|[ \t])/.test(i)){e.state="NOTE";break}if(!i)continue;if(e.cue=new ya(0,0,""),e.state="CUE",-1===i.indexOf("--\x3e")){e.cue.id=i;continue}case"CUE":if(!e.cue){e.state="BADCUE";continue}try{ka(i,e.cue,e.regionList)}catch(t){e.cue=null,e.state="BADCUE";continue}e.state="CUETEXT";continue;case"CUETEXT":var s=-1!==i.indexOf("--\x3e");if(!i||s&&(a=!0)){e.oncue&&e.cue&&e.oncue(e.cue),e.cue=null,e.state="ID";continue}if(null===e.cue)continue;e.cue.text&&(e.cue.text+="\n"),e.cue.text+=i;continue;case"BADCUE":i||(e.state="ID")}}}catch(t){"CUETEXT"===e.state&&e.cue&&e.oncue&&e.oncue(e.cue),e.cue=null,e.state="INITIAL"===e.state?"BADWEBVTT":"BADCUE"}return this},e.flush=function(){var t=this;try{if((t.cue||"HEADER"===t.state)&&(t.buffer+="\n\n",t.parse()),"INITIAL"===t.state||"BADWEBVTT"===t.state)throw new Error("Malformed WebVTT signature.")}catch(e){t.onparsingerror&&t.onparsingerror(e)}return t.onflush&&t.onflush(),this},t}(),Ia=/\r\n|\n\r|\n|\r/g,wa=function(t,e,r){return void 0===r&&(r=0),t.slice(r,r+e.length)===e},Ca=function(t){for(var e=5381,r=t.length;r;)e=33*e^t.charCodeAt(--r);return(e>>>0).toString()};function _a(t,e,r){return Ca(t.toString())+Ca(e.toString())+Ca(r)}function xa(t,e,r,i,n,a,s){var o,l,u,h=new Da,d=Tt(new Uint8Array(t)).trim().replace(Ia,"\n").split("\n"),c=[],f=e?(o=e.baseTime,void 0===(l=e.timescale)&&(l=1),mn(o,vn,1/l)):0,g="00:00.000",v=0,m=0,p=!0;h.oncue=function(t){var a=r[i],s=r.ccOffset,o=(v-f)/9e4;if(null!=a&&a.new&&(void 0!==m?s=r.ccOffset=a.start:function(t,e,r){var i=t[e],n=t[i.prevCC];if(!n||!n.new&&i.new)return t.ccOffset=t.presentationOffset=i.start,void(i.new=!1);for(;null!=(a=n)&&a.new;){var a;t.ccOffset+=i.start-n.start,i.new=!1,n=t[(i=n).prevCC]}t.presentationOffset=r}(r,i,o)),o){if(!e)return void(u=new Error("Missing initPTS for VTT MPEGTS"));s=o-r.presentationOffset}var l=t.endTime-t.startTime,h=Sn(9e4*(t.startTime+s-m),9e4*n)/9e4;t.startTime=Math.max(h,0),t.endTime=Math.max(h+l,0);var d=t.text.trim();t.text=decodeURIComponent(encodeURIComponent(d)),t.id||(t.id=_a(t.startTime,t.endTime,d)),t.endTime>0&&c.push(t)},h.onparsingerror=function(t){u=t},h.onflush=function(){u?s(u):a(c)},d.forEach((function(t){if(p){if(wa(t,"X-TIMESTAMP-MAP=")){p=!1,t.slice(16).split(",").forEach((function(t){wa(t,"LOCAL:")?g=t.slice(6):wa(t,"MPEGTS:")&&(v=parseInt(t.slice(7)))}));try{m=function(t){var e=parseInt(t.slice(-3)),r=parseInt(t.slice(-6,-4)),i=parseInt(t.slice(-9,-7)),n=t.length>9?parseInt(t.substring(0,t.indexOf(":"))):0;if(!(y(e)&&y(r)&&y(i)&&y(n)))throw Error("Malformed X-TIMESTAMP-MAP: Local:"+t);return e+=1e3*r,(e+=6e4*i)+36e5*n}(g)/1e3}catch(t){u=t}return}""===t&&(p=!1)}h.parse(t+"\n")})),h.flush()}var Pa="stpp.ttml.im1t",Fa=/^(\d{2,}):(\d{2}):(\d{2}):(\d{2})\.?(\d+)?$/,Ma=/^(\d*(?:\.\d*)?)(h|m|s|ms|f|t)$/,Oa={left:"start",center:"center",right:"end",start:"start",end:"end"};function Na(t,e,r,i){var n=_t(new Uint8Array(t),["mdat"]);if(0!==n.length){var a,s,l,u,h=n.map((function(t){return Tt(t)})),d=(a=e.baseTime,s=1,void 0===(l=e.timescale)&&(l=1),void 0===u&&(u=!1),mn(a,s,1/l,u));try{h.forEach((function(t){return r(function(t,e){var r=(new DOMParser).parseFromString(t,"text/xml"),i=r.getElementsByTagName("tt")[0];if(!i)throw new Error("Invalid ttml");var n={frameRate:30,subFrameRate:1,frameRateMultiplier:0,tickRate:0},a=Object.keys(n).reduce((function(t,e){return t[e]=i.getAttribute("ttp:"+e)||n[e],t}),{}),s="preserve"!==i.getAttribute("xml:space"),l=Ba(Ua(i,"styling","style")),u=Ba(Ua(i,"layout","region")),h=Ua(i,"body","[begin]");return[].map.call(h,(function(t){var r=Ga(t,s);if(!r||!t.hasAttribute("begin"))return null;var i=Va(t.getAttribute("begin"),a),n=Va(t.getAttribute("dur"),a),h=Va(t.getAttribute("end"),a);if(null===i)throw Ha(t);if(null===h){if(null===n)throw Ha(t);h=i+n}var d=new ya(i-e,h-e,r);d.id=_a(d.startTime,d.endTime,d.text);var c=function(t,e,r){var i="http://www.w3.org/ns/ttml#styling",n=null,a=["displayAlign","textAlign","color","backgroundColor","fontSize","fontFamily"],s=null!=t&&t.hasAttribute("style")?t.getAttribute("style"):null;return s&&r.hasOwnProperty(s)&&(n=r[s]),a.reduce((function(r,a){var s=Ka(e,i,a)||Ka(t,i,a)||Ka(n,i,a);return s&&(r[a]=s),r}),{})}(u[t.getAttribute("region")],l[t.getAttribute("style")],l),f=c.textAlign;if(f){var g=Oa[f];g&&(d.lineAlign=g),d.align=f}return o(d,c),d})).filter((function(t){return null!==t}))}(t,d))}))}catch(t){i(t)}}else i(new Error("Could not parse IMSC1 mdat"))}function Ua(t,e,r){var i=t.getElementsByTagName(e)[0];return i?[].slice.call(i.querySelectorAll(r)):[]}function Ba(t){return t.reduce((function(t,e){var r=e.getAttribute("xml:id");return r&&(t[r]=e),t}),{})}function Ga(t,e){return[].slice.call(t.childNodes).reduce((function(t,r,i){var n;return"br"===r.nodeName&&i?t+"\n":null!=(n=r.childNodes)&&n.length?Ga(r,e):e?t+r.textContent.trim().replace(/\s+/g," "):t+r.textContent}),"")}function Ka(t,e,r){return t&&t.hasAttributeNS(e,r)?t.getAttributeNS(e,r):null}function Ha(t){return new Error("Could not parse ttml timestamp "+t)}function Va(t,e){if(!t)return null;var r=Ta(t);return null===r&&(Fa.test(t)?r=function(t,e){var r=Fa.exec(t),i=(0|r[4])+(0|r[5])/e.subFrameRate;return 3600*(0|r[1])+60*(0|r[2])+(0|r[3])+i/e.frameRate}(t,e):Ma.test(t)&&(r=function(t,e){var r=Ma.exec(t),i=Number(r[1]);switch(r[2]){case"h":return 3600*i;case"m":return 60*i;case"ms":return 1e3*i;case"f":return i/e.frameRate;case"t":return i/e.tickRate}return i}(t,e))),r}var Ya=function(){function t(t){this.hls=void 0,this.media=null,this.config=void 0,this.enabled=!0,this.Cues=void 0,this.textTracks=[],this.tracks=[],this.initPTS=[],this.unparsedVttFrags=[],this.captionsTracks={},this.nonNativeCaptionsTracks={},this.cea608Parser1=void 0,this.cea608Parser2=void 0,this.lastCc=-1,this.lastSn=-1,this.lastPartIndex=-1,this.prevCC=-1,this.vttCCs={ccOffset:0,presentationOffset:0,0:{start:0,prevCC:-1,new:!0}},this.captionsProperties=void 0,this.hls=t,this.config=t.config,this.Cues=t.config.cueHandler,this.captionsProperties={textTrack1:{label:this.config.captionsTextTrack1Label,languageCode:this.config.captionsTextTrack1LanguageCode},textTrack2:{label:this.config.captionsTextTrack2Label,languageCode:this.config.captionsTextTrack2LanguageCode},textTrack3:{label:this.config.captionsTextTrack3Label,languageCode:this.config.captionsTextTrack3LanguageCode},textTrack4:{label:this.config.captionsTextTrack4Label,languageCode:this.config.captionsTextTrack4LanguageCode}},t.on(S.MEDIA_ATTACHING,this.onMediaAttaching,this),t.on(S.MEDIA_DETACHING,this.onMediaDetaching,this),t.on(S.MANIFEST_LOADING,this.onManifestLoading,this),t.on(S.MANIFEST_LOADED,this.onManifestLoaded,this),t.on(S.SUBTITLE_TRACKS_UPDATED,this.onSubtitleTracksUpdated,this),t.on(S.FRAG_LOADING,this.onFragLoading,this),t.on(S.FRAG_LOADED,this.onFragLoaded,this),t.on(S.FRAG_PARSING_USERDATA,this.onFragParsingUserdata,this),t.on(S.FRAG_DECRYPTED,this.onFragDecrypted,this),t.on(S.INIT_PTS_FOUND,this.onInitPtsFound,this),t.on(S.SUBTITLE_TRACKS_CLEARED,this.onSubtitleTracksCleared,this),t.on(S.BUFFER_FLUSHING,this.onBufferFlushing,this)}var e=t.prototype;return e.destroy=function(){var t=this.hls;t.off(S.MEDIA_ATTACHING,this.onMediaAttaching,this),t.off(S.MEDIA_DETACHING,this.onMediaDetaching,this),t.off(S.MANIFEST_LOADING,this.onManifestLoading,this),t.off(S.MANIFEST_LOADED,this.onManifestLoaded,this),t.off(S.SUBTITLE_TRACKS_UPDATED,this.onSubtitleTracksUpdated,this),t.off(S.FRAG_LOADING,this.onFragLoading,this),t.off(S.FRAG_LOADED,this.onFragLoaded,this),t.off(S.FRAG_PARSING_USERDATA,this.onFragParsingUserdata,this),t.off(S.FRAG_DECRYPTED,this.onFragDecrypted,this),t.off(S.INIT_PTS_FOUND,this.onInitPtsFound,this),t.off(S.SUBTITLE_TRACKS_CLEARED,this.onSubtitleTracksCleared,this),t.off(S.BUFFER_FLUSHING,this.onBufferFlushing,this),this.hls=this.config=null,this.cea608Parser1=this.cea608Parser2=void 0},e.initCea608Parsers=function(){if(this.config.enableCEA708Captions&&(!this.cea608Parser1||!this.cea608Parser2)){var t=new pa(this,"textTrack1"),e=new pa(this,"textTrack2"),r=new pa(this,"textTrack3"),i=new pa(this,"textTrack4");this.cea608Parser1=new ga(1,t,e),this.cea608Parser2=new ga(3,r,i)}},e.addCues=function(t,e,r,i,n){for(var a,s,o,l,u=!1,h=n.length;h--;){var d=n[h],c=(a=d[0],s=d[1],o=e,l=r,Math.min(s,l)-Math.max(a,o));if(c>=0&&(d[0]=Math.min(d[0],e),d[1]=Math.max(d[1],r),u=!0,c/(r-e)>.5))return}if(u||n.push([e,r]),this.config.renderTextTracksNatively){var f=this.captionsTracks[t];this.Cues.newCue(f,e,r,i)}else{var g=this.Cues.newCue(null,e,r,i);this.hls.trigger(S.CUES_PARSED,{type:"captions",cues:g,track:t})}},e.onInitPtsFound=function(t,e){var r=this,i=e.frag,n=e.id,a=e.initPTS,s=e.timescale,o=this.unparsedVttFrags;"main"===n&&(this.initPTS[i.cc]={baseTime:a,timescale:s}),o.length&&(this.unparsedVttFrags=[],o.forEach((function(t){r.onFragLoaded(S.FRAG_LOADED,t)})))},e.getExistingTrack=function(t,e){var r=this.media;if(r)for(var i=0;i<r.textTracks.length;i++){var n=r.textTracks[i];if(ja(n,{name:t,lang:e,attrs:{}}))return n}return null},e.createCaptionsTrack=function(t){this.config.renderTextTracksNatively?this.createNativeTrack(t):this.createNonNativeTrack(t)},e.createNativeTrack=function(t){if(!this.captionsTracks[t]){var e=this.captionsProperties,r=this.captionsTracks,i=this.media,n=e[t],a=n.label,s=n.languageCode,o=this.getExistingTrack(a,s);if(o)r[t]=o,Oe(r[t]),Fe(r[t],i);else{var l=this.createTextTrack("captions",a,s);l&&(l[t]=!0,r[t]=l)}}},e.createNonNativeTrack=function(t){if(!this.nonNativeCaptionsTracks[t]){var e=this.captionsProperties[t];if(e){var r={_id:t,label:e.label,kind:"captions",default:!!e.media&&!!e.media.default,closedCaptions:e.media};this.nonNativeCaptionsTracks[t]=r,this.hls.trigger(S.NON_NATIVE_TEXT_TRACKS_FOUND,{tracks:[r]})}}},e.createTextTrack=function(t,e,r){var i=this.media;if(i)return i.addTextTrack(t,e,r)},e.onMediaAttaching=function(t,e){this.media=e.media,this._cleanTracks()},e.onMediaDetaching=function(){var t=this.captionsTracks;Object.keys(t).forEach((function(e){Oe(t[e]),delete t[e]})),this.nonNativeCaptionsTracks={}},e.onManifestLoading=function(){this.lastCc=-1,this.lastSn=-1,this.lastPartIndex=-1,this.prevCC=-1,this.vttCCs={ccOffset:0,presentationOffset:0,0:{start:0,prevCC:-1,new:!0}},this._cleanTracks(),this.tracks=[],this.captionsTracks={},this.nonNativeCaptionsTracks={},this.textTracks=[],this.unparsedVttFrags=[],this.initPTS=[],this.cea608Parser1&&this.cea608Parser2&&(this.cea608Parser1.reset(),this.cea608Parser2.reset())},e._cleanTracks=function(){var t=this.media;if(t){var e=t.textTracks;if(e)for(var r=0;r<e.length;r++)Oe(e[r])}},e.onSubtitleTracksUpdated=function(t,e){var r=this,i=e.subtitleTracks||[],n=i.some((function(t){return t.textCodec===Pa}));if(this.config.enableWebVTT||n&&this.config.enableIMSC1){if(Gn(this.tracks,i))return void(this.tracks=i);if(this.textTracks=[],this.tracks=i,this.config.renderTextTracksNatively){var a=this.media,s=a?Ue(a.textTracks):null;if(this.tracks.forEach((function(t,e){var i;if(s){for(var n=null,a=0;a<s.length;a++)if(s[a]&&ja(s[a],t)){n=s[a],s[a]=null;break}n&&(i=n)}if(i)Oe(i);else{var o=Wa(t);(i=r.createTextTrack(o,t.name,t.lang))&&(i.mode="disabled")}i&&r.textTracks.push(i)})),null!=s&&s.length){var o=s.filter((function(t){return null!==t})).map((function(t){return t.label}));o.length&&w.warn("Media element contains unused subtitle tracks: "+o.join(", ")+". Replace media element for each source to clear TextTracks and captions menu.")}}else if(this.tracks.length){var l=this.tracks.map((function(t){return{label:t.name,kind:t.type.toLowerCase(),default:t.default,subtitleTrack:t}}));this.hls.trigger(S.NON_NATIVE_TEXT_TRACKS_FOUND,{tracks:l})}}},e.onManifestLoaded=function(t,e){var r=this;this.config.enableCEA708Captions&&e.captions&&e.captions.forEach((function(t){var e=/(?:CC|SERVICE)([1-4])/.exec(t.instreamId);if(e){var i="textTrack"+e[1],n=r.captionsProperties[i];n&&(n.label=t.name,t.lang&&(n.languageCode=t.lang),n.media=t)}}))},e.closedCaptionsForLevel=function(t){var e=this.hls.levels[t.level];return null==e?void 0:e.attrs["CLOSED-CAPTIONS"]},e.onFragLoading=function(t,e){this.initCea608Parsers();var r=this.cea608Parser1,i=this.cea608Parser2,n=this.lastCc,a=this.lastSn,s=this.lastPartIndex;if(this.enabled&&r&&i&&e.frag.type===Ie){var o,l,u=e.frag,h=u.cc,d=u.sn,c=null!=(o=null==e||null==(l=e.part)?void 0:l.index)?o:-1;d===a+1||d===a&&c===s+1||h===n||(r.reset(),i.reset()),this.lastCc=h,this.lastSn=d,this.lastPartIndex=c}},e.onFragLoaded=function(t,e){var r=e.frag,i=e.payload;if(r.type===Ce)if(i.byteLength){var n=r.decryptdata,a="stats"in e;if(null==n||!n.encrypted||a){var s=this.tracks[r.level],o=this.vttCCs;o[r.cc]||(o[r.cc]={start:r.start,prevCC:this.prevCC,new:!0},this.prevCC=r.cc),s&&s.textCodec===Pa?this._parseIMSC1(r,i):this._parseVTTs(e)}}else this.hls.trigger(S.SUBTITLE_FRAG_PROCESSED,{success:!1,frag:r,error:new Error("Empty subtitle payload")})},e._parseIMSC1=function(t,e){var r=this,i=this.hls;Na(e,this.initPTS[t.cc],(function(e){r._appendCues(e,t.level),i.trigger(S.SUBTITLE_FRAG_PROCESSED,{success:!0,frag:t})}),(function(e){w.log("Failed to parse IMSC1: "+e),i.trigger(S.SUBTITLE_FRAG_PROCESSED,{success:!1,frag:t,error:e})}))},e._parseVTTs=function(t){var e,r=this,i=t.frag,n=t.payload,a=this.initPTS,s=this.unparsedVttFrags,o=a.length-1;if(a[i.cc]||-1!==o){var l=this.hls;xa(null!=(e=i.initSegment)&&e.data?Gt(i.initSegment.data,new Uint8Array(n)):n,this.initPTS[i.cc],this.vttCCs,i.cc,i.start,(function(t){r._appendCues(t,i.level),l.trigger(S.SUBTITLE_FRAG_PROCESSED,{success:!0,frag:i})}),(function(e){var a="Missing initPTS for VTT MPEGTS"===e.message;a?s.push(t):r._fallbackToIMSC1(i,n),w.log("Failed to parse VTT cue: "+e),a&&o>i.cc||l.trigger(S.SUBTITLE_FRAG_PROCESSED,{success:!1,frag:i,error:e})}))}else s.push(t)},e._fallbackToIMSC1=function(t,e){var r=this,i=this.tracks[t.level];i.textCodec||Na(e,this.initPTS[t.cc],(function(){i.textCodec=Pa,r._parseIMSC1(t,e)}),(function(){i.textCodec="wvtt"}))},e._appendCues=function(t,e){var r=this.hls;if(this.config.renderTextTracksNatively){var i=this.textTracks[e];if(!i||"disabled"===i.mode)return;t.forEach((function(t){return Me(i,t)}))}else{var n=this.tracks[e];if(!n)return;var a=n.default?"default":"subtitles"+e;r.trigger(S.CUES_PARSED,{type:"subtitles",cues:t,track:a})}},e.onFragDecrypted=function(t,e){e.frag.type===Ce&&this.onFragLoaded(S.FRAG_LOADED,e)},e.onSubtitleTracksCleared=function(){this.tracks=[],this.captionsTracks={}},e.onFragParsingUserdata=function(t,e){this.initCea608Parsers();var r=this.cea608Parser1,i=this.cea608Parser2;if(this.enabled&&r&&i){var n=e.frag,a=e.samples;if(n.type!==Ie||"NONE"!==this.closedCaptionsForLevel(n))for(var s=0;s<a.length;s++){var o=a[s].bytes;if(o){var l=this.extractCea608Data(o);r.addData(a[s].pts,l[0]),i.addData(a[s].pts,l[1])}}}},e.onBufferFlushing=function(t,e){var r=e.startOffset,i=e.endOffset,n=e.endOffsetSubtitles,a=e.type,s=this.media;if(s&&!(s.currentTime<i)){if(!a||"video"===a){var o=this.captionsTracks;Object.keys(o).forEach((function(t){return Ne(o[t],r,i)}))}if(this.config.renderTextTracksNatively&&0===r&&void 0!==n){var l=this.textTracks;Object.keys(l).forEach((function(t){return Ne(l[t],r,n)}))}}},e.extractCea608Data=function(t){for(var e=[[],[]],r=31&t[0],i=2,n=0;n<r;n++){var a=t[i++],s=127&t[i++],o=127&t[i++];if((0!==s||0!==o)&&0!=(4&a)){var l=3&a;0!==l&&1!==l||(e[l].push(s),e[l].push(o))}}return e},t}();function Wa(t){return t.characteristics&&/transcribes-spoken-dialog/gi.test(t.characteristics)&&/describes-music-and-sound/gi.test(t.characteristics)?"captions":"subtitles"}function ja(t,e){return!!t&&t.kind===Wa(e)&&Hn(e,t)}var qa=function(){function t(t){this.hls=void 0,this.autoLevelCapping=void 0,this.firstLevel=void 0,this.media=void 0,this.restrictedLevels=void 0,this.timer=void 0,this.clientRect=void 0,this.streamController=void 0,this.hls=t,this.autoLevelCapping=Number.POSITIVE_INFINITY,this.firstLevel=-1,this.media=null,this.restrictedLevels=[],this.timer=void 0,this.clientRect=null,this.registerListeners()}var e=t.prototype;return e.setStreamController=function(t){this.streamController=t},e.destroy=function(){this.hls&&this.unregisterListener(),this.timer&&this.stopCapping(),this.media=null,this.clientRect=null,this.hls=this.streamController=null},e.registerListeners=function(){var t=this.hls;t.on(S.FPS_DROP_LEVEL_CAPPING,this.onFpsDropLevelCapping,this),t.on(S.MEDIA_ATTACHING,this.onMediaAttaching,this),t.on(S.MANIFEST_PARSED,this.onManifestParsed,this),t.on(S.LEVELS_UPDATED,this.onLevelsUpdated,this),t.on(S.BUFFER_CODECS,this.onBufferCodecs,this),t.on(S.MEDIA_DETACHING,this.onMediaDetaching,this)},e.unregisterListener=function(){var t=this.hls;t.off(S.FPS_DROP_LEVEL_CAPPING,this.onFpsDropLevelCapping,this),t.off(S.MEDIA_ATTACHING,this.onMediaAttaching,this),t.off(S.MANIFEST_PARSED,this.onManifestParsed,this),t.off(S.LEVELS_UPDATED,this.onLevelsUpdated,this),t.off(S.BUFFER_CODECS,this.onBufferCodecs,this),t.off(S.MEDIA_DETACHING,this.onMediaDetaching,this)},e.onFpsDropLevelCapping=function(t,e){var r=this.hls.levels[e.droppedLevel];this.isLevelAllowed(r)&&this.restrictedLevels.push({bitrate:r.bitrate,height:r.height,width:r.width})},e.onMediaAttaching=function(t,e){this.media=e.media instanceof HTMLVideoElement?e.media:null,this.clientRect=null,this.timer&&this.hls.levels.length&&this.detectPlayerSize()},e.onManifestParsed=function(t,e){var r=this.hls;this.restrictedLevels=[],this.firstLevel=e.firstLevel,r.config.capLevelToPlayerSize&&e.video&&this.startCapping()},e.onLevelsUpdated=function(t,e){this.timer&&y(this.autoLevelCapping)&&this.detectPlayerSize()},e.onBufferCodecs=function(t,e){this.hls.config.capLevelToPlayerSize&&e.video&&this.startCapping()},e.onMediaDetaching=function(){this.stopCapping()},e.detectPlayerSize=function(){if(this.media){if(this.mediaHeight<=0||this.mediaWidth<=0)return void(this.clientRect=null);var t=this.hls.levels;if(t.length){var e=this.hls,r=this.getMaxLevel(t.length-1);r!==this.autoLevelCapping&&w.log("Setting autoLevelCapping to "+r+": "+t[r].height+"p@"+t[r].bitrate+" for media "+this.mediaWidth+"x"+this.mediaHeight),e.autoLevelCapping=r,e.autoLevelCapping>this.autoLevelCapping&&this.streamController&&this.streamController.nextLevelSwitch(),this.autoLevelCapping=e.autoLevelCapping}}},e.getMaxLevel=function(e){var r=this,i=this.hls.levels;if(!i.length)return-1;var n=i.filter((function(t,i){return r.isLevelAllowed(t)&&i<=e}));return this.clientRect=null,t.getMaxLevelByMediaSize(n,this.mediaWidth,this.mediaHeight)},e.startCapping=function(){this.timer||(this.autoLevelCapping=Number.POSITIVE_INFINITY,self.clearInterval(this.timer),this.timer=self.setInterval(this.detectPlayerSize.bind(this),1e3),this.detectPlayerSize())},e.stopCapping=function(){this.restrictedLevels=[],this.firstLevel=-1,this.autoLevelCapping=Number.POSITIVE_INFINITY,this.timer&&(self.clearInterval(this.timer),this.timer=void 0)},e.getDimensions=function(){if(this.clientRect)return this.clientRect;var t=this.media,e={width:0,height:0};if(t){var r=t.getBoundingClientRect();e.width=r.width,e.height=r.height,e.width||e.height||(e.width=r.right-r.left||t.width||0,e.height=r.bottom-r.top||t.height||0)}return this.clientRect=e,e},e.isLevelAllowed=function(t){return!this.restrictedLevels.some((function(e){return t.bitrate===e.bitrate&&t.width===e.width&&t.height===e.height}))},t.getMaxLevelByMediaSize=function(t,e,r){if(null==t||!t.length)return-1;for(var i,n,a=t.length-1,s=Math.max(e,r),o=0;o<t.length;o+=1){var l=t[o];if((l.width>=s||l.height>=s)&&(i=l,!(n=t[o+1])||i.width!==n.width||i.height!==n.height)){a=o;break}}return a},s(t,[{key:"mediaWidth",get:function(){return this.getDimensions().width*this.contentScaleFactor}},{key:"mediaHeight",get:function(){return this.getDimensions().height*this.contentScaleFactor}},{key:"contentScaleFactor",get:function(){var t=1;if(!this.hls.config.ignoreDevicePixelRatio)try{t=self.devicePixelRatio}catch(t){}return t}}]),t}(),Xa=function(){function t(t){this.hls=void 0,this.isVideoPlaybackQualityAvailable=!1,this.timer=void 0,this.media=null,this.lastTime=void 0,this.lastDroppedFrames=0,this.lastDecodedFrames=0,this.streamController=void 0,this.hls=t,this.registerListeners()}var e=t.prototype;return e.setStreamController=function(t){this.streamController=t},e.registerListeners=function(){this.hls.on(S.MEDIA_ATTACHING,this.onMediaAttaching,this)},e.unregisterListeners=function(){this.hls.off(S.MEDIA_ATTACHING,this.onMediaAttaching,this)},e.destroy=function(){this.timer&&clearInterval(this.timer),this.unregisterListeners(),this.isVideoPlaybackQualityAvailable=!1,this.media=null},e.onMediaAttaching=function(t,e){var r=this.hls.config;if(r.capLevelOnFPSDrop){var i=e.media instanceof self.HTMLVideoElement?e.media:null;this.media=i,i&&"function"==typeof i.getVideoPlaybackQuality&&(this.isVideoPlaybackQualityAvailable=!0),self.clearInterval(this.timer),this.timer=self.setInterval(this.checkFPSInterval.bind(this),r.fpsDroppedMonitoringPeriod)}},e.checkFPS=function(t,e,r){var i=performance.now();if(e){if(this.lastTime){var n=i-this.lastTime,a=r-this.lastDroppedFrames,s=e-this.lastDecodedFrames,o=1e3*a/n,l=this.hls;if(l.trigger(S.FPS_DROP,{currentDropped:a,currentDecoded:s,totalDroppedFrames:r}),o>0&&a>l.config.fpsDroppedMonitoringThreshold*s){var u=l.currentLevel;w.warn("drop FPS ratio greater than max allowed value for currentLevel: "+u),u>0&&(-1===l.autoLevelCapping||l.autoLevelCapping>=u)&&(u-=1,l.trigger(S.FPS_DROP_LEVEL_CAPPING,{level:u,droppedLevel:l.currentLevel}),l.autoLevelCapping=u,this.streamController.nextLevelSwitch())}}this.lastTime=i,this.lastDroppedFrames=r,this.lastDecodedFrames=e}},e.checkFPSInterval=function(){var t=this.media;if(t)if(this.isVideoPlaybackQualityAvailable){var e=t.getVideoPlaybackQuality();this.checkFPS(t,e.totalVideoFrames,e.droppedVideoFrames)}else this.checkFPS(t,t.webkitDecodedFrameCount,t.webkitDroppedFrameCount)},t}(),za="[eme]",Qa=function(){function t(e){this.hls=void 0,this.config=void 0,this.media=null,this.keyFormatPromise=null,this.keySystemAccessPromises={},this._requestLicenseFailureCount=0,this.mediaKeySessions=[],this.keyIdToKeySessionPromise={},this.setMediaKeysQueue=t.CDMCleanupPromise?[t.CDMCleanupPromise]:[],this.onMediaEncrypted=this._onMediaEncrypted.bind(this),this.onWaitingForKey=this._onWaitingForKey.bind(this),this.debug=w.debug.bind(w,za),this.log=w.log.bind(w,za),this.warn=w.warn.bind(w,za),this.error=w.error.bind(w,za),this.hls=e,this.config=e.config,this.registerListeners()}var e=t.prototype;return e.destroy=function(){this.unregisterListeners(),this.onMediaDetached();var t=this.config;t.requestMediaKeySystemAccessFunc=null,t.licenseXhrSetup=t.licenseResponseCallback=void 0,t.drmSystems=t.drmSystemOptions={},this.hls=this.onMediaEncrypted=this.onWaitingForKey=this.keyIdToKeySessionPromise=null,this.config=null},e.registerListeners=function(){this.hls.on(S.MEDIA_ATTACHED,this.onMediaAttached,this),this.hls.on(S.MEDIA_DETACHED,this.onMediaDetached,this),this.hls.on(S.MANIFEST_LOADING,this.onManifestLoading,this),this.hls.on(S.MANIFEST_LOADED,this.onManifestLoaded,this)},e.unregisterListeners=function(){this.hls.off(S.MEDIA_ATTACHED,this.onMediaAttached,this),this.hls.off(S.MEDIA_DETACHED,this.onMediaDetached,this),this.hls.off(S.MANIFEST_LOADING,this.onManifestLoading,this),this.hls.off(S.MANIFEST_LOADED,this.onManifestLoaded,this)},e.getLicenseServerUrl=function(t){var e=this.config,r=e.drmSystems,i=e.widevineLicenseUrl,n=r[t];if(n)return n.licenseUrl;if(t===q.WIDEVINE&&i)return i;throw new Error('no license server URL configured for key-system "'+t+'"')},e.getServerCertificateUrl=function(t){var e=this.config.drmSystems[t];if(e)return e.serverCertificateUrl;this.log('No Server Certificate in config.drmSystems["'+t+'"]')},e.attemptKeySystemAccess=function(t){var e=this,r=this.hls.levels,i=function(t,e,r){return!!t&&r.indexOf(t)===e},n=r.map((function(t){return t.audioCodec})).filter(i),a=r.map((function(t){return t.videoCodec})).filter(i);return n.length+a.length===0&&a.push("avc1.42e01e"),new Promise((function(r,i){!function t(s){var o=s.shift();e.getMediaKeysPromise(o,n,a).then((function(t){return r({keySystem:o,mediaKeys:t})})).catch((function(e){s.length?t(s):i(e instanceof es?e:new es({type:L.KEY_SYSTEM_ERROR,details:A.KEY_SYSTEM_NO_ACCESS,error:e,fatal:!0},e.message))}))}(t)}))},e.requestMediaKeySystemAccess=function(t,e){var r=this.config.requestMediaKeySystemAccessFunc;if("function"!=typeof r){var i="Configured requestMediaKeySystemAccess is not a function "+r;return null===it&&"http:"===self.location.protocol&&(i="navigator.requestMediaKeySystemAccess is not available over insecure protocol "+location.protocol),Promise.reject(new Error(i))}return r(t,e)},e.getMediaKeysPromise=function(t,e,r){var i=this,n=function(t,e,r,i){var n;switch(t){case q.FAIRPLAY:n=["cenc","sinf"];break;case q.WIDEVINE:case q.PLAYREADY:n=["cenc"];break;case q.CLEARKEY:n=["cenc","keyids"];break;default:throw new Error("Unknown key-system: "+t)}return function(t,e,r,i){return[{initDataTypes:t,persistentState:i.persistentState||"optional",distinctiveIdentifier:i.distinctiveIdentifier||"optional",sessionTypes:i.sessionTypes||[i.sessionType||"temporary"],audioCapabilities:e.map((function(t){return{contentType:'audio/mp4; codecs="'+t+'"',robustness:i.audioRobustness||"",encryptionScheme:i.audioEncryptionScheme||null}})),videoCapabilities:r.map((function(t){return{contentType:'video/mp4; codecs="'+t+'"',robustness:i.videoRobustness||"",encryptionScheme:i.videoEncryptionScheme||null}}))}]}(n,e,r,i)}(t,e,r,this.config.drmSystemOptions),a=this.keySystemAccessPromises[t],s=null==a?void 0:a.keySystemAccess;if(!s){this.log('Requesting encrypted media "'+t+'" key-system access with config: '+JSON.stringify(n)),s=this.requestMediaKeySystemAccess(t,n);var o=this.keySystemAccessPromises[t]={keySystemAccess:s};return s.catch((function(e){i.log('Failed to obtain access to key-system "'+t+'": '+e)})),s.then((function(e){i.log('Access for key-system "'+e.keySystem+'" obtained');var r=i.fetchServerCertificate(t);return i.log('Create media-keys for "'+t+'"'),o.mediaKeys=e.createMediaKeys().then((function(e){return i.log('Media-keys created for "'+t+'"'),r.then((function(r){return r?i.setMediaKeysServerCertificate(e,t,r):e}))})),o.mediaKeys.catch((function(e){i.error('Failed to create media-keys for "'+t+'"}: '+e)})),o.mediaKeys}))}return s.then((function(){return a.mediaKeys}))},e.createMediaKeySessionContext=function(t){var e=t.decryptdata,r=t.keySystem,i=t.mediaKeys;this.log('Creating key-system session "'+r+'" keyId: '+Lt(e.keyId||[]));var n=i.createSession(),a={decryptdata:e,keySystem:r,mediaKeys:i,mediaKeysSession:n,keyStatus:"status-pending"};return this.mediaKeySessions.push(a),a},e.renewKeySession=function(t){var e=t.decryptdata;if(e.pssh){var r=this.createMediaKeySessionContext(t),i=this.getKeyIdString(e);this.keyIdToKeySessionPromise[i]=this.generateRequestWithPreferredKeySession(r,"cenc",e.pssh,"expired")}else this.warn("Could not renew expired session. Missing pssh initData.");this.removeSession(t)},e.getKeyIdString=function(t){if(!t)throw new Error("Could not read keyId of undefined decryptdata");if(null===t.keyId)throw new Error("keyId is null");return Lt(t.keyId)},e.updateKeySession=function(t,e){var r,i=t.mediaKeysSession;return this.log('Updating key-session "'+i.sessionId+'" for keyID '+Lt((null==(r=t.decryptdata)?void 0:r.keyId)||[])+"\n } (data length: "+(e?e.byteLength:e)+")"),i.update(e)},e.selectKeySystemFormat=function(t){var e=Object.keys(t.levelkeys||{});return this.keyFormatPromise||(this.log("Selecting key-system from fragment (sn: "+t.sn+" "+t.type+": "+t.level+") key formats "+e.join(", ")),this.keyFormatPromise=this.getKeyFormatPromise(e)),this.keyFormatPromise},e.getKeyFormatPromise=function(t){var e=this;return new Promise((function(r,i){var n=et(e.config),a=t.map($).filter((function(t){return!!t&&-1!==n.indexOf(t)}));return e.getKeySystemSelectionPromise(a).then((function(t){var e=t.keySystem,n=tt(e);n?r(n):i(new Error('Unable to find format for key-system "'+e+'"'))})).catch(i)}))},e.loadKey=function(t){var e=this,r=t.keyInfo.decryptdata,i=this.getKeyIdString(r),n="(keyId: "+i+' format: "'+r.keyFormat+'" method: '+r.method+" uri: "+r.uri+")";this.log("Starting session for key "+n);var a=this.keyIdToKeySessionPromise[i];return a||(a=this.keyIdToKeySessionPromise[i]=this.getKeySystemForKeyPromise(r).then((function(i){var a=i.keySystem,s=i.mediaKeys;return e.throwIfDestroyed(),e.log("Handle encrypted media sn: "+t.frag.sn+" "+t.frag.type+": "+t.frag.level+" using key "+n),e.attemptSetMediaKeys(a,s).then((function(){e.throwIfDestroyed();var t=e.createMediaKeySessionContext({keySystem:a,mediaKeys:s,decryptdata:r});return e.generateRequestWithPreferredKeySession(t,"cenc",r.pssh,"playlist-key")}))}))).catch((function(t){return e.handleError(t)})),a},e.throwIfDestroyed=function(t){if(!this.hls)throw new Error("invalid state")},e.handleError=function(t){this.hls&&(this.error(t.message),t instanceof es?this.hls.trigger(S.ERROR,t.data):this.hls.trigger(S.ERROR,{type:L.KEY_SYSTEM_ERROR,details:A.KEY_SYSTEM_NO_KEYS,error:t,fatal:!0}))},e.getKeySystemForKeyPromise=function(t){var e=this.getKeyIdString(t),r=this.keyIdToKeySessionPromise[e];if(!r){var i=$(t.keyFormat),n=i?[i]:et(this.config);return this.attemptKeySystemAccess(n)}return r},e.getKeySystemSelectionPromise=function(t){if(t.length||(t=et(this.config)),0===t.length)throw new es({type:L.KEY_SYSTEM_ERROR,details:A.KEY_SYSTEM_NO_CONFIGURED_LICENSE,fatal:!0},"Missing key-system license configuration options "+JSON.stringify({drmSystems:this.config.drmSystems}));return this.attemptKeySystemAccess(t)},e._onMediaEncrypted=function(t){var e=this,r=t.initDataType,i=t.initData;if(this.debug('"'+t.type+'" event: init data type: "'+r+'"'),null!==i){var n,a;if("sinf"===r&&this.config.drmSystems[q.FAIRPLAY]){var s=bt(new Uint8Array(i));try{var o=V(JSON.parse(s).sinf),l=Ut(new Uint8Array(o));if(!l)return;n=l.subarray(8,24),a=q.FAIRPLAY}catch(t){return void this.warn('Failed to parse sinf "encrypted" event message initData')}}else{var u=function(t){if(!(t instanceof ArrayBuffer)||t.byteLength<32)return null;var e={version:0,systemId:"",kids:null,data:null},r=new DataView(t),i=r.getUint32(0);if(t.byteLength!==i&&i>44)return null;if(1886614376!==r.getUint32(4))return null;if(e.version=r.getUint32(8)>>>24,e.version>1)return null;e.systemId=Lt(new Uint8Array(t,12,16));var n=r.getUint32(28);if(0===e.version){if(i-32<n)return null;e.data=new Uint8Array(t,32,n)}else if(1===e.version){e.kids=[];for(var a=0;a<n;a++)e.kids.push(new Uint8Array(t,32+16*a,16))}return e}(i);if(null===u)return;0===u.version&&u.systemId===Z&&u.data&&(n=u.data.subarray(8,24)),a=function(t){if(t===Z)return q.WIDEVINE}(u.systemId)}if(a&&n){for(var h,d=Lt(n),c=this.keyIdToKeySessionPromise,f=this.mediaKeySessions,g=c[d],v=function(){var t=f[m],a=t.decryptdata;if(a.pssh||!a.keyId)return 0;var s=Lt(a.keyId);return d===s||-1!==a.uri.replace(/-/g,"").indexOf(d)?(g=c[s],delete c[s],a.pssh=new Uint8Array(i),a.keyId=n,g=c[d]=g.then((function(){return e.generateRequestWithPreferredKeySession(t,r,i,"encrypted-event-key-match")})),1):void 0},m=0;m<f.length&&(0===(h=v())||1!==h);m++);g||(g=c[d]=this.getKeySystemSelectionPromise([a]).then((function(t){var a,s=t.keySystem,o=t.mediaKeys;e.throwIfDestroyed();var l=new qt("ISO-23001-7",d,null!=(a=tt(s))?a:"");return l.pssh=new Uint8Array(i),l.keyId=n,e.attemptSetMediaKeys(s,o).then((function(){e.throwIfDestroyed();var t=e.createMediaKeySessionContext({decryptdata:l,keySystem:s,mediaKeys:o});return e.generateRequestWithPreferredKeySession(t,r,i,"encrypted-event-no-match")}))}))),g.catch((function(t){return e.handleError(t)}))}}},e._onWaitingForKey=function(t){this.log('"'+t.type+'" event')},e.attemptSetMediaKeys=function(t,e){var r=this,i=this.setMediaKeysQueue.slice();this.log('Setting media-keys for "'+t+'"');var n=Promise.all(i).then((function(){if(!r.media)throw new Error("Attempted to set mediaKeys without media element attached");return r.media.setMediaKeys(e)}));return this.setMediaKeysQueue.push(n),n.then((function(){r.log('Media-keys set for "'+t+'"'),i.push(n),r.setMediaKeysQueue=r.setMediaKeysQueue.filter((function(t){return-1===i.indexOf(t)}))}))},e.generateRequestWithPreferredKeySession=function(t,e,r,i){var n,a,s=this,o=null==(n=this.config.drmSystems)||null==(a=n[t.keySystem])?void 0:a.generateRequest;if(o)try{var l=o.call(this.hls,e,r,t);if(!l)throw new Error("Invalid response from configured generateRequest filter");e=l.initDataType,r=t.decryptdata.pssh=l.initData?new Uint8Array(l.initData):null}catch(t){var u;if(this.warn(t.message),null!=(u=this.hls)&&u.config.debug)throw t}if(null===r)return this.log('Skipping key-session request for "'+i+'" (no initData)'),Promise.resolve(t);var h=this.getKeyIdString(t.decryptdata);this.log('Generating key-session request for "'+i+'": '+h+" (init data type: "+e+" length: "+(r?r.byteLength:null)+")");var d=new Mn,c=t._onmessage=function(e){var r=t.mediaKeysSession;if(r){var i=e.messageType,n=e.message;s.log('"'+i+'" message event for session "'+r.sessionId+'" message size: '+n.byteLength),"license-request"===i||"license-renewal"===i?s.renewLicense(t,n).catch((function(t){s.handleError(t),d.emit("error",t)})):"license-release"===i?t.keySystem===q.FAIRPLAY&&(s.updateKeySession(t,W("acknowledged")),s.removeSession(t)):s.warn('unhandled media key message type "'+i+'"')}else d.emit("error",new Error("invalid state"))},f=t._onkeystatuseschange=function(e){if(t.mediaKeysSession){s.onKeyStatusChange(t);var r=t.keyStatus;d.emit("keyStatus",r),"expired"===r&&(s.warn(t.keySystem+" expired for key "+h),s.renewKeySession(t))}else d.emit("error",new Error("invalid state"))};t.mediaKeysSession.addEventListener("message",c),t.mediaKeysSession.addEventListener("keystatuseschange",f);var g=new Promise((function(t,e){d.on("error",e),d.on("keyStatus",(function(r){r.startsWith("usable")?t():"output-restricted"===r?e(new es({type:L.KEY_SYSTEM_ERROR,details:A.KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED,fatal:!1},"HDCP level output restricted")):"internal-error"===r?e(new es({type:L.KEY_SYSTEM_ERROR,details:A.KEY_SYSTEM_STATUS_INTERNAL_ERROR,fatal:!0},'key status changed to "'+r+'"')):"expired"===r?e(new Error("key expired while generating request")):s.warn('unhandled key status change "'+r+'"')}))}));return t.mediaKeysSession.generateRequest(e,r).then((function(){var e;s.log('Request generated for key-session "'+(null==(e=t.mediaKeysSession)?void 0:e.sessionId)+'" keyId: '+h)})).catch((function(t){throw new es({type:L.KEY_SYSTEM_ERROR,details:A.KEY_SYSTEM_NO_SESSION,error:t,fatal:!1},"Error generating key-session request: "+t)})).then((function(){return g})).catch((function(e){throw d.removeAllListeners(),s.removeSession(t),e})).then((function(){return d.removeAllListeners(),t}))},e.onKeyStatusChange=function(t){var e=this;t.mediaKeysSession.keyStatuses.forEach((function(r,i){e.log('key status change "'+r+'" for keyStatuses keyId: '+Lt("buffer"in i?new Uint8Array(i.buffer,i.byteOffset,i.byteLength):new Uint8Array(i))+" session keyId: "+Lt(new Uint8Array(t.decryptdata.keyId||[]))+" uri: "+t.decryptdata.uri),t.keyStatus=r}))},e.fetchServerCertificate=function(t){var e=this.config,r=new(0,e.loader)(e),n=this.getServerCertificateUrl(t);return n?(this.log('Fetching server certificate for "'+t+'"'),new Promise((function(a,s){var o={responseType:"arraybuffer",url:n},l=e.certLoadPolicy.default,u={loadPolicy:l,timeout:l.maxLoadTimeMs,maxRetry:0,retryDelay:0,maxRetryDelay:0},h={onSuccess:function(t,e,r,i){a(t.data)},onError:function(e,r,a,l){s(new es({type:L.KEY_SYSTEM_ERROR,details:A.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED,fatal:!0,networkDetails:a,response:i({url:o.url,data:void 0},e)},'"'+t+'" certificate request failed ('+n+"). Status: "+e.code+" ("+e.text+")"))},onTimeout:function(e,r,i){s(new es({type:L.KEY_SYSTEM_ERROR,details:A.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED,fatal:!0,networkDetails:i,response:{url:o.url,data:void 0}},'"'+t+'" certificate request timed out ('+n+")"))},onAbort:function(t,e,r){s(new Error("aborted"))}};r.load(o,u,h)}))):Promise.resolve()},e.setMediaKeysServerCertificate=function(t,e,r){var i=this;return new Promise((function(n,a){t.setServerCertificate(r).then((function(a){i.log("setServerCertificate "+(a?"success":"not supported by CDM")+" ("+(null==r?void 0:r.byteLength)+') on "'+e+'"'),n(t)})).catch((function(t){a(new es({type:L.KEY_SYSTEM_ERROR,details:A.KEY_SYSTEM_SERVER_CERTIFICATE_UPDATE_FAILED,error:t,fatal:!0},t.message))}))}))},e.renewLicense=function(t,e){var r=this;return this.requestLicense(t,new Uint8Array(e)).then((function(e){return r.updateKeySession(t,new Uint8Array(e)).catch((function(t){throw new es({type:L.KEY_SYSTEM_ERROR,details:A.KEY_SYSTEM_SESSION_UPDATE_FAILED,error:t,fatal:!0},t.message)}))}))},e.unpackPlayReadyKeyMessage=function(t,e){var r=String.fromCharCode.apply(null,new Uint16Array(e.buffer));if(!r.includes("PlayReadyKeyMessage"))return t.setRequestHeader("Content-Type","text/xml; charset=utf-8"),e;var i=(new DOMParser).parseFromString(r,"application/xml"),n=i.querySelectorAll("HttpHeader");if(n.length>0)for(var a,s=0,o=n.length;s<o;s++){var l,u,h=null==(l=(a=n[s]).querySelector("name"))?void 0:l.textContent,d=null==(u=a.querySelector("value"))?void 0:u.textContent;h&&d&&t.setRequestHeader(h,d)}var c=i.querySelector("Challenge"),f=null==c?void 0:c.textContent;if(!f)throw new Error("Cannot find <Challenge> in key message");return W(atob(f))},e.setupLicenseXHR=function(t,e,r,i){var n=this,a=this.config.licenseXhrSetup;return a?Promise.resolve().then((function(){if(!r.decryptdata)throw new Error("Key removed");return a.call(n.hls,t,e,r,i)})).catch((function(s){if(!r.decryptdata)throw s;return t.open("POST",e,!0),a.call(n.hls,t,e,r,i)})).then((function(r){return t.readyState||t.open("POST",e,!0),{xhr:t,licenseChallenge:r||i}})):(t.open("POST",e,!0),Promise.resolve({xhr:t,licenseChallenge:i}))},e.requestLicense=function(t,e){var r=this,i=this.config.keyLoadPolicy.default;return new Promise((function(n,a){var s=r.getLicenseServerUrl(t.keySystem);r.log("Sending license request to URL: "+s);var o=new XMLHttpRequest;o.responseType="arraybuffer",o.onreadystatechange=function(){if(!r.hls||!t.mediaKeysSession)return a(new Error("invalid state"));if(4===o.readyState)if(200===o.status){r._requestLicenseFailureCount=0;var l=o.response;r.log("License received "+(l instanceof ArrayBuffer?l.byteLength:l));var u=r.config.licenseResponseCallback;if(u)try{l=u.call(r.hls,o,s,t)}catch(t){r.error(t)}n(l)}else{var h=i.errorRetry,d=h?h.maxNumRetry:0;if(r._requestLicenseFailureCount++,r._requestLicenseFailureCount>d||o.status>=400&&o.status<500)a(new es({type:L.KEY_SYSTEM_ERROR,details:A.KEY_SYSTEM_LICENSE_REQUEST_FAILED,fatal:!0,networkDetails:o,response:{url:s,data:void 0,code:o.status,text:o.statusText}},"License Request XHR failed ("+s+"). Status: "+o.status+" ("+o.statusText+")"));else{var c=d-r._requestLicenseFailureCount+1;r.warn("Retrying license request, "+c+" attempts left"),r.requestLicense(t,e).then(n,a)}}},t.licenseXhr&&t.licenseXhr.readyState!==XMLHttpRequest.DONE&&t.licenseXhr.abort(),t.licenseXhr=o,r.setupLicenseXHR(o,s,t,e).then((function(e){var i=e.xhr,n=e.licenseChallenge;t.keySystem==q.PLAYREADY&&(n=r.unpackPlayReadyKeyMessage(i,n)),i.send(n)}))}))},e.onMediaAttached=function(t,e){if(this.config.emeEnabled){var r=e.media;this.media=r,r.addEventListener("encrypted",this.onMediaEncrypted),r.addEventListener("waitingforkey",this.onWaitingForKey)}},e.onMediaDetached=function(){var e=this,r=this.media,i=this.mediaKeySessions;r&&(r.removeEventListener("encrypted",this.onMediaEncrypted),r.removeEventListener("waitingforkey",this.onWaitingForKey),this.media=null),this._requestLicenseFailureCount=0,this.setMediaKeysQueue=[],this.mediaKeySessions=[],this.keyIdToKeySessionPromise={},qt.clearKeyUriToKeyIdMap();var n=i.length;t.CDMCleanupPromise=Promise.all(i.map((function(t){return e.removeSession(t)})).concat(null==r?void 0:r.setMediaKeys(null).catch((function(t){e.log("Could not clear media keys: "+t)})))).then((function(){n&&(e.log("finished closing key sessions and clearing media keys"),i.length=0)})).catch((function(t){e.log("Could not close sessions and clear media keys: "+t)}))},e.onManifestLoading=function(){this.keyFormatPromise=null},e.onManifestLoaded=function(t,e){var r=e.sessionKeys;if(r&&this.config.emeEnabled&&!this.keyFormatPromise){var i=r.reduce((function(t,e){return-1===t.indexOf(e.keyFormat)&&t.push(e.keyFormat),t}),[]);this.log("Selecting key-system from session-keys "+i.join(", ")),this.keyFormatPromise=this.getKeyFormatPromise(i)}},e.removeSession=function(t){var e=this,r=t.mediaKeysSession,i=t.licenseXhr;if(r){this.log("Remove licenses and keys and close session "+r.sessionId),t._onmessage&&(r.removeEventListener("message",t._onmessage),t._onmessage=void 0),t._onkeystatuseschange&&(r.removeEventListener("keystatuseschange",t._onkeystatuseschange),t._onkeystatuseschange=void 0),i&&i.readyState!==XMLHttpRequest.DONE&&i.abort(),t.mediaKeysSession=t.decryptdata=t.licenseXhr=void 0;var n=this.mediaKeySessions.indexOf(t);return n>-1&&this.mediaKeySessions.splice(n,1),r.remove().catch((function(t){e.log("Could not remove session: "+t)})).then((function(){return r.close()})).catch((function(t){e.log("Could not close session: "+t)}))}},t}();Qa.CDMCleanupPromise=void 0;var Ja,$a,Za,ts,es=function(t){function e(e,r){var i;return(i=t.call(this,r)||this).data=void 0,e.error||(e.error=new Error(r)),i.data=e,e.err=e.error,i}return l(e,t),e}(c(Error));!function(t){t.MANIFEST="m",t.AUDIO="a",t.VIDEO="v",t.MUXED="av",t.INIT="i",t.CAPTION="c",t.TIMED_TEXT="tt",t.KEY="k",t.OTHER="o"}(Ja||(Ja={})),function(t){t.DASH="d",t.HLS="h",t.SMOOTH="s",t.OTHER="o"}($a||($a={})),function(t){t.OBJECT="CMCD-Object",t.REQUEST="CMCD-Request",t.SESSION="CMCD-Session",t.STATUS="CMCD-Status"}(Za||(Za={}));var rs=((ts={})[Za.OBJECT]=["br","d","ot","tb"],ts[Za.REQUEST]=["bl","dl","mtp","nor","nrr","su"],ts[Za.SESSION]=["cid","pr","sf","sid","st","v"],ts[Za.STATUS]=["bs","rtp"],ts),is=function t(e,r){this.value=void 0,this.params=void 0,Array.isArray(e)&&(e=e.map((function(e){return e instanceof t?e:new t(e)}))),this.value=e,this.params=r},ns=function(t){this.description=void 0,this.description=t},as="Dict";function ss(t,e,r,i){return new Error("failed to "+t+' "'+(n=e,(Array.isArray(n)?JSON.stringify(n):n instanceof Map?"Map{}":n instanceof Set?"Set{}":"object"==typeof n?JSON.stringify(n):String(n))+'" as ')+r,{cause:i});var n}var os="Bare Item",ls="Boolean",us="Byte Sequence",hs="Decimal",ds="Integer",cs=/[\x00-\x1f\x7f]+/,fs="Token",gs="Key";function vs(t,e,r){return ss("serialize",t,e,r)}function ms(t){if(!1===ArrayBuffer.isView(t))throw vs(t,us);return":"+(e=t,btoa(String.fromCharCode.apply(String,e))+":");var e}function ps(t){if(function(t){return t<-999999999999999||999999999999999<t}(t))throw vs(t,ds);return t.toString()}function ys(t,e){if(t<0)return-ys(-t,e);var r=Math.pow(10,e);if(Math.abs(t*r%1-.5)<Number.EPSILON){var i=Math.floor(t*r);return(i%2==0?i:i+1)/r}return Math.round(t*r)/r}function Es(t){var e=ys(t,3);if(Math.floor(Math.abs(e)).toString().length>12)throw vs(t,hs);var r=e.toString();return r.includes(".")?r:r+".0"}var Ts="String";function Ss(t){var e,r=(e=t).description||e.toString().slice(7,-1);if(!1===/^([a-zA-Z*])([!#$%&'*+\-.^_`|~\w:/]*)$/.test(r))throw vs(r,fs);return r}function Ls(t){switch(typeof t){case"number":if(!y(t))throw vs(t,os);return Number.isInteger(t)?ps(t):Es(t);case"string":return function(t){if(cs.test(t))throw vs(t,Ts);return'"'+t.replace(/\\/g,"\\\\").replace(/"/g,'\\"')+'"'}(t);case"symbol":return Ss(t);case"boolean":return function(t){if("boolean"!=typeof t)throw vs(t,ls);return t?"?1":"?0"}(t);case"object":if(t instanceof Date)return function(t){return"@"+ps(t.getTime()/1e3)}(t);if(t instanceof Uint8Array)return ms(t);if(t instanceof ns)return Ss(t);default:throw vs(t,os)}}function As(t){if(!1===/^[a-z*][a-z0-9\-_.*]*$/.test(t))throw vs(t,gs);return t}function Rs(t){return null==t?"":Object.entries(t).map((function(t){var e=t[0],r=t[1];return!0===r?";"+As(e):";"+As(e)+"="+Ls(r)})).join("")}function ks(t){return t instanceof is?""+Ls(t.value)+Rs(t.params):Ls(t)}function bs(t,e){var r;if(void 0===e&&(e={whitespace:!0}),"object"!=typeof t)throw vs(t,as);var i=t instanceof Map?t.entries():Object.entries(t),n=null!=(r=e)&&r.whitespace?" ":"";return Array.from(i).map((function(t){var e=t[0],r=t[1];r instanceof is==0&&(r=new is(r));var i,n=As(e);return!0===r.value?n+=Rs(r.params):(n+="=",Array.isArray(r.value)?n+="("+(i=r).value.map(ks).join(" ")+")"+Rs(i.params):n+=ks(r)),n})).join(","+n)}var Ds=function(t){return"ot"===t||"sf"===t||"st"===t},Is=function(t){return"number"==typeof t?y(t):null!=t&&""!==t&&!1!==t},ws=function(t){return Math.round(t)},Cs=function(t){return 100*ws(t/100)},_s={br:ws,d:ws,bl:Cs,dl:Cs,mtp:Cs,nor:function(t,e){return null!=e&&e.baseUrl&&(t=function(t,e){var r=new URL(t),i=new URL(e);if(r.origin!==i.origin)return t;for(var n=r.pathname.split("/").slice(1),a=i.pathname.split("/").slice(1,-1);n[0]===a[0];)n.shift(),a.shift();for(;a.length;)a.shift(),n.unshift("..");return n.join("/")}(t,e.baseUrl)),encodeURIComponent(t)},rtp:Cs,tb:ws};function xs(t,e){return void 0===e&&(e={}),t?function(t,e){return bs(t,e)}(function(t,e){var r={};if(null==t||"object"!=typeof t)return r;var i=Object.keys(t).sort(),n=o({},_s,null==e?void 0:e.formatters),a=null==e?void 0:e.filter;return i.forEach((function(i){if(null==a||!a(i)){var s=t[i],o=n[i];o&&(s=o(s,e)),"v"===i&&1===s||"pr"==i&&1===s||Is(s)&&(Ds(i)&&"string"==typeof s&&(s=new ns(s)),r[i]=s)}})),r}(t,e),o({whitespace:!1},e)):""}function Ps(t,e,r){return o(t,function(t,e){var r;if(void 0===e&&(e={}),!t)return{};var i=Object.entries(t),n=Object.entries(rs).concat(Object.entries((null==(r=e)?void 0:r.customHeaderMap)||{})),a=i.reduce((function(t,e){var r,i=e[0],a=e[1],s=(null==(r=n.find((function(t){return t[1].includes(i)})))?void 0:r[0])||Za.REQUEST;return null!=t[s]||(t[s]={}),t[s][i]=a,t}),{});return Object.entries(a).reduce((function(t,r){var i=r[0],n=r[1];return t[i]=xs(n,e),t}),{})}(e,r))}var Fs="CMCD",Ms=/CMCD=[^&#]+/;function Os(t,e,r){var i=function(t,e){if(void 0===e&&(e={}),!t)return"";var r=xs(t,e);return Fs+"="+encodeURIComponent(r)}(e,r);if(!i)return t;if(Ms.test(t))return t.replace(Ms,i);var n=t.includes("?")?"&":"?";return""+t+n+i}var Ns=function(){function t(t){var e=this;this.hls=void 0,this.config=void 0,this.media=void 0,this.sid=void 0,this.cid=void 0,this.useHeaders=!1,this.includeKeys=void 0,this.initialized=!1,this.starved=!1,this.buffering=!0,this.audioBuffer=void 0,this.videoBuffer=void 0,this.onWaiting=function(){e.initialized&&(e.starved=!0),e.buffering=!0},this.onPlaying=function(){e.initialized||(e.initialized=!0),e.buffering=!1},this.applyPlaylistData=function(t){try{e.apply(t,{ot:Ja.MANIFEST,su:!e.initialized})}catch(t){w.warn("Could not generate manifest CMCD data.",t)}},this.applyFragmentData=function(t){try{var r=t.frag,i=e.hls.levels[r.level],n=e.getObjectType(r),a={d:1e3*r.duration,ot:n};n!==Ja.VIDEO&&n!==Ja.AUDIO&&n!=Ja.MUXED||(a.br=i.bitrate/1e3,a.tb=e.getTopBandwidth(n)/1e3,a.bl=e.getBufferLength(n)),e.apply(t,a)}catch(t){w.warn("Could not generate segment CMCD data.",t)}},this.hls=t;var r=this.config=t.config,i=r.cmcd;null!=i&&(r.pLoader=this.createPlaylistLoader(),r.fLoader=this.createFragmentLoader(),this.sid=i.sessionId||function(){try{return crypto.randomUUID()}catch(i){try{var t=URL.createObjectURL(new Blob),e=t.toString();return URL.revokeObjectURL(t),e.slice(e.lastIndexOf("/")+1)}catch(t){var r=(new Date).getTime();return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(function(t){var e=(r+16*Math.random())%16|0;return r=Math.floor(r/16),("x"==t?e:3&e|8).toString(16)}))}}}(),this.cid=i.contentId,this.useHeaders=!0===i.useHeaders,this.includeKeys=i.includeKeys,this.registerListeners())}var e=t.prototype;return e.registerListeners=function(){var t=this.hls;t.on(S.MEDIA_ATTACHED,this.onMediaAttached,this),t.on(S.MEDIA_DETACHED,this.onMediaDetached,this),t.on(S.BUFFER_CREATED,this.onBufferCreated,this)},e.unregisterListeners=function(){var t=this.hls;t.off(S.MEDIA_ATTACHED,this.onMediaAttached,this),t.off(S.MEDIA_DETACHED,this.onMediaDetached,this),t.off(S.BUFFER_CREATED,this.onBufferCreated,this)},e.destroy=function(){this.unregisterListeners(),this.onMediaDetached(),this.hls=this.config=this.audioBuffer=this.videoBuffer=null,this.onWaiting=this.onPlaying=null},e.onMediaAttached=function(t,e){this.media=e.media,this.media.addEventListener("waiting",this.onWaiting),this.media.addEventListener("playing",this.onPlaying)},e.onMediaDetached=function(){this.media&&(this.media.removeEventListener("waiting",this.onWaiting),this.media.removeEventListener("playing",this.onPlaying),this.media=null)},e.onBufferCreated=function(t,e){var r,i;this.audioBuffer=null==(r=e.tracks.audio)?void 0:r.buffer,this.videoBuffer=null==(i=e.tracks.video)?void 0:i.buffer},e.createData=function(){var t;return{v:1,sf:$a.HLS,sid:this.sid,cid:this.cid,pr:null==(t=this.media)?void 0:t.playbackRate,mtp:this.hls.bandwidthEstimate/1e3}},e.apply=function(t,e){void 0===e&&(e={}),o(e,this.createData());var r=e.ot===Ja.INIT||e.ot===Ja.VIDEO||e.ot===Ja.MUXED;this.starved&&r&&(e.bs=!0,e.su=!0,this.starved=!1),null==e.su&&(e.su=this.buffering);var i=this.includeKeys;i&&(e=Object.keys(e).reduce((function(t,r){return i.includes(r)&&(t[r]=e[r]),t}),{})),this.useHeaders?(t.headers||(t.headers={}),Ps(t.headers,e)):t.url=Os(t.url,e)},e.getObjectType=function(t){var e=t.type;return"subtitle"===e?Ja.TIMED_TEXT:"initSegment"===t.sn?Ja.INIT:"audio"===e?Ja.AUDIO:"main"===e?this.hls.audioTracks.length?Ja.VIDEO:Ja.MUXED:void 0},e.getTopBandwidth=function(t){var e,r=0,i=this.hls;if(t===Ja.AUDIO)e=i.audioTracks;else{var n=i.maxAutoLevel,a=n>-1?n+1:i.levels.length;e=i.levels.slice(0,a)}for(var s,o=g(e);!(s=o()).done;){var l=s.value;l.bitrate>r&&(r=l.bitrate)}return r>0?r:NaN},e.getBufferLength=function(t){var e=this.hls.media,r=t===Ja.AUDIO?this.audioBuffer:this.videoBuffer;return r&&e?1e3*zr.bufferInfo(r,e.currentTime,this.config.maxBufferHole).len:NaN},e.createPlaylistLoader=function(){var t=this.config.pLoader,e=this.applyPlaylistData,r=t||this.config.loader;return function(){function t(t){this.loader=void 0,this.loader=new r(t)}var i=t.prototype;return i.destroy=function(){this.loader.destroy()},i.abort=function(){this.loader.abort()},i.load=function(t,r,i){e(t),this.loader.load(t,r,i)},s(t,[{key:"stats",get:function(){return this.loader.stats}},{key:"context",get:function(){return this.loader.context}}]),t}()},e.createFragmentLoader=function(){var t=this.config.fLoader,e=this.applyFragmentData,r=t||this.config.loader;return function(){function t(t){this.loader=void 0,this.loader=new r(t)}var i=t.prototype;return i.destroy=function(){this.loader.destroy()},i.abort=function(){this.loader.abort()},i.load=function(t,r,i){e(t),this.loader.load(t,r,i)},s(t,[{key:"stats",get:function(){return this.loader.stats}},{key:"context",get:function(){return this.loader.context}}]),t}()},t}(),Us=function(){function t(t){this.hls=void 0,this.log=void 0,this.loader=null,this.uri=null,this.pathwayId=".",this.pathwayPriority=null,this.timeToLoad=300,this.reloadTimer=-1,this.updated=0,this.started=!1,this.enabled=!0,this.levels=null,this.audioTracks=null,this.subtitleTracks=null,this.penalizedPathways={},this.hls=t,this.log=w.log.bind(w,"[content-steering]:"),this.registerListeners()}var e=t.prototype;return e.registerListeners=function(){var t=this.hls;t.on(S.MANIFEST_LOADING,this.onManifestLoading,this),t.on(S.MANIFEST_LOADED,this.onManifestLoaded,this),t.on(S.MANIFEST_PARSED,this.onManifestParsed,this),t.on(S.ERROR,this.onError,this)},e.unregisterListeners=function(){var t=this.hls;t&&(t.off(S.MANIFEST_LOADING,this.onManifestLoading,this),t.off(S.MANIFEST_LOADED,this.onManifestLoaded,this),t.off(S.MANIFEST_PARSED,this.onManifestParsed,this),t.off(S.ERROR,this.onError,this))},e.startLoad=function(){if(this.started=!0,this.clearTimeout(),this.enabled&&this.uri){if(this.updated){var t=1e3*this.timeToLoad-(performance.now()-this.updated);if(t>0)return void this.scheduleRefresh(this.uri,t)}this.loadSteeringManifest(this.uri)}},e.stopLoad=function(){this.started=!1,this.loader&&(this.loader.destroy(),this.loader=null),this.clearTimeout()},e.clearTimeout=function(){-1!==this.reloadTimer&&(self.clearTimeout(this.reloadTimer),this.reloadTimer=-1)},e.destroy=function(){this.unregisterListeners(),this.stopLoad(),this.hls=null,this.levels=this.audioTracks=this.subtitleTracks=null},e.removeLevel=function(t){var e=this.levels;e&&(this.levels=e.filter((function(e){return e!==t})))},e.onManifestLoading=function(){this.stopLoad(),this.enabled=!0,this.timeToLoad=300,this.updated=0,this.uri=null,this.pathwayId=".",this.levels=this.audioTracks=this.subtitleTracks=null},e.onManifestLoaded=function(t,e){var r=e.contentSteering;null!==r&&(this.pathwayId=r.pathwayId,this.uri=r.uri,this.started&&this.startLoad())},e.onManifestParsed=function(t,e){this.audioTracks=e.audioTracks,this.subtitleTracks=e.subtitleTracks},e.onError=function(t,e){var r=e.errorAction;if((null==r?void 0:r.action)===Tr&&r.flags===Rr){var i=this.levels,n=this.pathwayPriority,a=this.pathwayId;if(e.context){var s=e.context,o=s.groupId,l=s.pathwayId,u=s.type;o&&i?a=this.getPathwayForGroupId(o,u,a):l&&(a=l)}a in this.penalizedPathways||(this.penalizedPathways[a]=performance.now()),!n&&i&&(n=i.reduce((function(t,e){return-1===t.indexOf(e.pathwayId)&&t.push(e.pathwayId),t}),[])),n&&n.length>1&&(this.updatePathwayPriority(n),r.resolved=this.pathwayId!==a),r.resolved||w.warn("Could not resolve "+e.details+' ("'+e.error.message+'") with content-steering for Pathway: '+a+" levels: "+(i?i.length:i)+" priorities: "+JSON.stringify(n)+" penalized: "+JSON.stringify(this.penalizedPathways))}},e.filterParsedLevels=function(t){this.levels=t;var e=this.getLevelsForPathway(this.pathwayId);if(0===e.length){var r=t[0].pathwayId;this.log("No levels found in Pathway "+this.pathwayId+'. Setting initial Pathway to "'+r+'"'),e=this.getLevelsForPathway(r),this.pathwayId=r}return e.length!==t.length?(this.log("Found "+e.length+"/"+t.length+' levels in Pathway "'+this.pathwayId+'"'),e):t},e.getLevelsForPathway=function(t){return null===this.levels?[]:this.levels.filter((function(e){return t===e.pathwayId}))},e.updatePathwayPriority=function(t){var e;this.pathwayPriority=t;var r=this.penalizedPathways,i=performance.now();Object.keys(r).forEach((function(t){i-r[t]>3e5&&delete r[t]}));for(var n=0;n<t.length;n++){var a=t[n];if(!(a in r)){if(a===this.pathwayId)return;var s=this.hls.nextLoadLevel,o=this.hls.levels[s];if((e=this.getLevelsForPathway(a)).length>0){this.log('Setting Pathway to "'+a+'"'),this.pathwayId=a,ur(e),this.hls.trigger(S.LEVELS_UPDATED,{levels:e});var l=this.hls.levels[s];o&&l&&this.levels&&(l.attrs["STABLE-VARIANT-ID"]!==o.attrs["STABLE-VARIANT-ID"]&&l.bitrate!==o.bitrate&&this.log("Unstable Pathways change from bitrate "+o.bitrate+" to "+l.bitrate),this.hls.nextLoadLevel=s);break}}}},e.getPathwayForGroupId=function(t,e,r){for(var i=this.getLevelsForPathway(r).concat(this.levels||[]),n=0;n<i.length;n++)if(e===be&&i[n].hasAudioGroup(t)||e===De&&i[n].hasSubtitleGroup(t))return i[n].pathwayId;return r},e.clonePathways=function(t){var e=this,r=this.levels;if(r){var i={},n={};t.forEach((function(t){var a=t.ID,s=t["BASE-ID"],o=t["URI-REPLACEMENT"];if(!r.some((function(t){return t.pathwayId===a}))){var l=e.getLevelsForPathway(s).map((function(t){var e=new x(t.attrs);e["PATHWAY-ID"]=a;var r=e.AUDIO&&e.AUDIO+"_clone_"+a,s=e.SUBTITLES&&e.SUBTITLES+"_clone_"+a;r&&(i[e.AUDIO]=r,e.AUDIO=r),s&&(n[e.SUBTITLES]=s,e.SUBTITLES=s);var l=Gs(t.uri,e["STABLE-VARIANT-ID"],"PER-VARIANT-URIS",o),u=new tr({attrs:e,audioCodec:t.audioCodec,bitrate:t.bitrate,height:t.height,name:t.name,url:l,videoCodec:t.videoCodec,width:t.width});if(t.audioGroups)for(var h=1;h<t.audioGroups.length;h++)u.addGroupId("audio",t.audioGroups[h]+"_clone_"+a);if(t.subtitleGroups)for(var d=1;d<t.subtitleGroups.length;d++)u.addGroupId("text",t.subtitleGroups[d]+"_clone_"+a);return u}));r.push.apply(r,l),Bs(e.audioTracks,i,o,a),Bs(e.subtitleTracks,n,o,a)}}))}},e.loadSteeringManifest=function(t){var e,r=this,i=this.hls.config,n=i.loader;this.loader&&this.loader.destroy(),this.loader=new n(i);try{e=new self.URL(t)}catch(e){return this.enabled=!1,void this.log("Failed to parse Steering Manifest URI: "+t)}if("data:"!==e.protocol){var a=0|(this.hls.bandwidthEstimate||i.abrEwmaDefaultEstimate);e.searchParams.set("_HLS_pathway",this.pathwayId),e.searchParams.set("_HLS_throughput",""+a)}var s={responseType:"json",url:e.href},o=i.steeringManifestLoadPolicy.default,l=o.errorRetry||o.timeoutRetry||{},u={loadPolicy:o,timeout:o.maxLoadTimeMs,maxRetry:l.maxNumRetry||0,retryDelay:l.retryDelayMs||0,maxRetryDelay:l.maxRetryDelayMs||0},h={onSuccess:function(t,i,n,a){r.log('Loaded steering manifest: "'+e+'"');var s=t.data;if(1===s.VERSION){r.updated=performance.now(),r.timeToLoad=s.TTL;var o=s["RELOAD-URI"],l=s["PATHWAY-CLONES"],u=s["PATHWAY-PRIORITY"];if(o)try{r.uri=new self.URL(o,e).href}catch(t){return r.enabled=!1,void r.log("Failed to parse Steering Manifest RELOAD-URI: "+o)}r.scheduleRefresh(r.uri||n.url),l&&r.clonePathways(l);var h={steeringManifest:s,url:e.toString()};r.hls.trigger(S.STEERING_MANIFEST_LOADED,h),u&&r.updatePathwayPriority(u)}else r.log("Steering VERSION "+s.VERSION+" not supported!")},onError:function(t,e,i,n){if(r.log("Error loading steering manifest: "+t.code+" "+t.text+" ("+e.url+")"),r.stopLoad(),410===t.code)return r.enabled=!1,void r.log("Steering manifest "+e.url+" no longer available");var a=1e3*r.timeToLoad;if(429!==t.code)r.scheduleRefresh(r.uri||e.url,a);else{var s=r.loader;if("function"==typeof(null==s?void 0:s.getResponseHeader)){var o=s.getResponseHeader("Retry-After");o&&(a=1e3*parseFloat(o))}r.log("Steering manifest "+e.url+" rate limited")}},onTimeout:function(t,e,i){r.log("Timeout loading steering manifest ("+e.url+")"),r.scheduleRefresh(r.uri||e.url)}};this.log("Requesting steering manifest: "+e),this.loader.load(s,u,h)},e.scheduleRefresh=function(t,e){var r=this;void 0===e&&(e=1e3*this.timeToLoad),this.clearTimeout(),this.reloadTimer=self.setTimeout((function(){var e,i=null==(e=r.hls)?void 0:e.media;!i||i.ended?r.scheduleRefresh(t,1e3*r.timeToLoad):r.loadSteeringManifest(t)}),e)},t}();function Bs(t,e,r,i){t&&Object.keys(e).forEach((function(n){var a=t.filter((function(t){return t.groupId===n})).map((function(t){var a=o({},t);return a.details=void 0,a.attrs=new x(a.attrs),a.url=a.attrs.URI=Gs(t.url,t.attrs["STABLE-RENDITION-ID"],"PER-RENDITION-URIS",r),a.groupId=a.attrs["GROUP-ID"]=e[n],a.attrs["PATHWAY-ID"]=i,a}));t.push.apply(t,a)}))}function Gs(t,e,r,i){var n,a=i.HOST,s=i.PARAMS,o=i[r];e&&(n=null==o?void 0:o[e])&&(t=n);var l=new self.URL(t);return a&&!n&&(l.host=a),s&&Object.keys(s).sort().forEach((function(t){t&&l.searchParams.set(t,s[t])})),l.href}var Ks=/^age:\s*[\d.]+\s*$/im,Hs=function(){function t(t){this.xhrSetup=void 0,this.requestTimeout=void 0,this.retryTimeout=void 0,this.retryDelay=void 0,this.config=null,this.callbacks=null,this.context=null,this.loader=null,this.stats=void 0,this.xhrSetup=t&&t.xhrSetup||null,this.stats=new M,this.retryDelay=0}var e=t.prototype;return e.destroy=function(){this.callbacks=null,this.abortInternal(),this.loader=null,this.config=null,this.context=null,this.xhrSetup=null,this.stats=null},e.abortInternal=function(){var t=this.loader;self.clearTimeout(this.requestTimeout),self.clearTimeout(this.retryTimeout),t&&(t.onreadystatechange=null,t.onprogress=null,4!==t.readyState&&(this.stats.aborted=!0,t.abort()))},e.abort=function(){var t;this.abortInternal(),null!=(t=this.callbacks)&&t.onAbort&&this.callbacks.onAbort(this.stats,this.context,this.loader)},e.load=function(t,e,r){if(this.stats.loading.start)throw new Error("Loader can only be used once.");this.stats.loading.start=self.performance.now(),this.context=t,this.config=e,this.callbacks=r,this.loadInternal()},e.loadInternal=function(){var t=this,e=this.config,r=this.context;if(e&&r){var i=this.loader=new self.XMLHttpRequest,n=this.stats;n.loading.first=0,n.loaded=0,n.aborted=!1;var a=this.xhrSetup;a?Promise.resolve().then((function(){if(!t.stats.aborted)return a(i,r.url)})).catch((function(t){return i.open("GET",r.url,!0),a(i,r.url)})).then((function(){t.stats.aborted||t.openAndSendXhr(i,r,e)})).catch((function(e){t.callbacks.onError({code:i.status,text:e.message},r,i,n)})):this.openAndSendXhr(i,r,e)}},e.openAndSendXhr=function(t,e,r){t.readyState||t.open("GET",e.url,!0);var i=e.headers,n=r.loadPolicy,a=n.maxTimeToFirstByteMs,s=n.maxLoadTimeMs;if(i)for(var o in i)t.setRequestHeader(o,i[o]);e.rangeEnd&&t.setRequestHeader("Range","bytes="+e.rangeStart+"-"+(e.rangeEnd-1)),t.onreadystatechange=this.readystatechange.bind(this),t.onprogress=this.loadprogress.bind(this),t.responseType=e.responseType,self.clearTimeout(this.requestTimeout),r.timeout=a&&y(a)?a:s,this.requestTimeout=self.setTimeout(this.loadtimeout.bind(this),r.timeout),t.send()},e.readystatechange=function(){var t=this.context,e=this.loader,r=this.stats;if(t&&e){var i=e.readyState,n=this.config;if(!r.aborted&&i>=2&&(0===r.loading.first&&(r.loading.first=Math.max(self.performance.now(),r.loading.start),n.timeout!==n.loadPolicy.maxLoadTimeMs&&(self.clearTimeout(this.requestTimeout),n.timeout=n.loadPolicy.maxLoadTimeMs,this.requestTimeout=self.setTimeout(this.loadtimeout.bind(this),n.loadPolicy.maxLoadTimeMs-(r.loading.first-r.loading.start)))),4===i)){self.clearTimeout(this.requestTimeout),e.onreadystatechange=null,e.onprogress=null;var a=e.status,s="text"!==e.responseType;if(a>=200&&a<300&&(s&&e.response||null!==e.responseText)){r.loading.end=Math.max(self.performance.now(),r.loading.first);var o=s?e.response:e.responseText,l="arraybuffer"===e.responseType?o.byteLength:o.length;if(r.loaded=r.total=l,r.bwEstimate=8e3*r.total/(r.loading.end-r.loading.first),!this.callbacks)return;var u=this.callbacks.onProgress;if(u&&u(r,t,o,e),!this.callbacks)return;var h={url:e.responseURL,data:o,code:a};this.callbacks.onSuccess(h,r,t,e)}else{var d=n.loadPolicy.errorRetry;gr(d,r.retry,!1,{url:t.url,data:void 0,code:a})?this.retry(d):(w.error(a+" while loading "+t.url),this.callbacks.onError({code:a,text:e.statusText},t,e,r))}}}},e.loadtimeout=function(){var t,e=null==(t=this.config)?void 0:t.loadPolicy.timeoutRetry;if(gr(e,this.stats.retry,!0))this.retry(e);else{var r;w.warn("timeout while loading "+(null==(r=this.context)?void 0:r.url));var i=this.callbacks;i&&(this.abortInternal(),i.onTimeout(this.stats,this.context,this.loader))}},e.retry=function(t){var e=this.context,r=this.stats;this.retryDelay=cr(t,r.retry),r.retry++,w.warn((status?"HTTP Status "+status:"Timeout")+" while loading "+(null==e?void 0:e.url)+", retrying "+r.retry+"/"+t.maxNumRetry+" in "+this.retryDelay+"ms"),this.abortInternal(),this.loader=null,self.clearTimeout(this.retryTimeout),this.retryTimeout=self.setTimeout(this.loadInternal.bind(this),this.retryDelay)},e.loadprogress=function(t){var e=this.stats;e.loaded=t.loaded,t.lengthComputable&&(e.total=t.total)},e.getCacheAge=function(){var t=null;if(this.loader&&Ks.test(this.loader.getAllResponseHeaders())){var e=this.loader.getResponseHeader("age");t=e?parseFloat(e):null}return t},e.getResponseHeader=function(t){return this.loader&&new RegExp("^"+t+":\\s*[\\d.]+\\s*$","im").test(this.loader.getAllResponseHeaders())?this.loader.getResponseHeader(t):null},t}(),Vs=/(\d+)-(\d+)\/(\d+)/,Ys=function(){function t(t){this.fetchSetup=void 0,this.requestTimeout=void 0,this.request=null,this.response=null,this.controller=void 0,this.context=null,this.config=null,this.callbacks=null,this.stats=void 0,this.loader=null,this.fetchSetup=t.fetchSetup||Ws,this.controller=new self.AbortController,this.stats=new M}var e=t.prototype;return e.destroy=function(){this.loader=this.callbacks=this.context=this.config=this.request=null,this.abortInternal(),this.response=null,this.fetchSetup=this.controller=this.stats=null},e.abortInternal=function(){this.controller&&!this.stats.loading.end&&(this.stats.aborted=!0,this.controller.abort())},e.abort=function(){var t;this.abortInternal(),null!=(t=this.callbacks)&&t.onAbort&&this.callbacks.onAbort(this.stats,this.context,this.response)},e.load=function(t,e,r){var i=this,n=this.stats;if(n.loading.start)throw new Error("Loader can only be used once.");n.loading.start=self.performance.now();var a=function(t,e){var r={method:"GET",mode:"cors",credentials:"same-origin",signal:e,headers:new self.Headers(o({},t.headers))};return t.rangeEnd&&r.headers.set("Range","bytes="+t.rangeStart+"-"+String(t.rangeEnd-1)),r}(t,this.controller.signal),s=r.onProgress,l="arraybuffer"===t.responseType,u=l?"byteLength":"length",h=e.loadPolicy,d=h.maxTimeToFirstByteMs,c=h.maxLoadTimeMs;this.context=t,this.config=e,this.callbacks=r,this.request=this.fetchSetup(t,a),self.clearTimeout(this.requestTimeout),e.timeout=d&&y(d)?d:c,this.requestTimeout=self.setTimeout((function(){i.abortInternal(),r.onTimeout(n,t,i.response)}),e.timeout),self.fetch(this.request).then((function(a){i.response=i.loader=a;var o=Math.max(self.performance.now(),n.loading.start);if(self.clearTimeout(i.requestTimeout),e.timeout=c,i.requestTimeout=self.setTimeout((function(){i.abortInternal(),r.onTimeout(n,t,i.response)}),c-(o-n.loading.start)),!a.ok){var u=a.status,h=a.statusText;throw new qs(h||"fetch, bad network response",u,a)}return n.loading.first=o,n.total=function(t){var e=t.get("Content-Range");if(e){var r=function(t){var e=Vs.exec(t);if(e)return parseInt(e[2])-parseInt(e[1])+1}(e);if(y(r))return r}var i=t.get("Content-Length");if(i)return parseInt(i)}(a.headers)||n.total,s&&y(e.highWaterMark)?i.loadProgressively(a,n,t,e.highWaterMark,s):l?a.arrayBuffer():"json"===t.responseType?a.json():a.text()})).then((function(a){var o=i.response;if(!o)throw new Error("loader destroyed");self.clearTimeout(i.requestTimeout),n.loading.end=Math.max(self.performance.now(),n.loading.first);var l=a[u];l&&(n.loaded=n.total=l);var h={url:o.url,data:a,code:o.status};s&&!y(e.highWaterMark)&&s(n,t,a,o),r.onSuccess(h,n,t,o)})).catch((function(e){if(self.clearTimeout(i.requestTimeout),!n.aborted){var a=e&&e.code||0,s=e?e.message:null;r.onError({code:a,text:s},t,e?e.details:null,n)}}))},e.getCacheAge=function(){var t=null;if(this.response){var e=this.response.headers.get("age");t=e?parseFloat(e):null}return t},e.getResponseHeader=function(t){return this.response?this.response.headers.get(t):null},e.loadProgressively=function(t,e,r,i,n){void 0===i&&(i=0);var a=new ki,s=t.body.getReader();return function o(){return s.read().then((function(s){if(s.done)return a.dataLength&&n(e,r,a.flush(),t),Promise.resolve(new ArrayBuffer(0));var l=s.value,u=l.length;return e.loaded+=u,u<i||a.dataLength?(a.push(l),a.dataLength>=i&&n(e,r,a.flush(),t)):n(e,r,l,t),o()})).catch((function(){return Promise.reject()}))}()},t}();function Ws(t,e){return new self.Request(t.url,e)}var js,qs=function(t){function e(e,r,i){var n;return(n=t.call(this,e)||this).code=void 0,n.details=void 0,n.code=r,n.details=i,n}return l(e,t),e}(c(Error)),Xs=/\s/,zs=i(i({autoStartLoad:!0,startPosition:-1,defaultAudioCodec:void 0,debug:!1,capLevelOnFPSDrop:!1,capLevelToPlayerSize:!1,ignoreDevicePixelRatio:!1,preferManagedMediaSource:!0,initialLiveManifestSize:1,maxBufferLength:30,backBufferLength:1/0,frontBufferFlushThreshold:1/0,maxBufferSize:6e7,maxBufferHole:.1,highBufferWatchdogPeriod:2,nudgeOffset:.1,nudgeMaxRetry:3,maxFragLookUpTolerance:.25,liveSyncDurationCount:3,liveMaxLatencyDurationCount:1/0,liveSyncDuration:void 0,liveMaxLatencyDuration:void 0,maxLiveSyncPlaybackRate:1,liveDurationInfinity:!1,liveBackBufferLength:null,maxMaxBufferLength:600,enableWorker:!0,workerPath:null,enableSoftwareAES:!0,startLevel:void 0,startFragPrefetch:!1,fpsDroppedMonitoringPeriod:5e3,fpsDroppedMonitoringThreshold:.2,appendErrorMaxRetry:3,loader:Hs,fLoader:void 0,pLoader:void 0,xhrSetup:void 0,licenseXhrSetup:void 0,licenseResponseCallback:void 0,abrController:Br,bufferController:Qn,capLevelController:qa,errorController:br,fpsController:Xa,stretchShortVideoTrack:!1,maxAudioFramesDrift:1,forceKeyFrameOnDiscontinuity:!0,abrEwmaFastLive:3,abrEwmaSlowLive:9,abrEwmaFastVoD:3,abrEwmaSlowVoD:9,abrEwmaDefaultEstimate:5e5,abrEwmaDefaultEstimateMax:5e6,abrBandWidthFactor:.95,abrBandWidthUpFactor:.7,abrMaxWithRealBitrate:!1,maxStarvationDelay:4,maxLoadingDelay:4,minAutoBitrate:0,emeEnabled:!1,widevineLicenseUrl:void 0,drmSystems:{},drmSystemOptions:{},requestMediaKeySystemAccessFunc:it,testBandwidth:!0,progressive:!1,lowLatencyMode:!0,cmcd:void 0,enableDateRangeMetadataCues:!0,enableEmsgMetadataCues:!0,enableID3MetadataCues:!0,useMediaCapabilities:!0,certLoadPolicy:{default:{maxTimeToFirstByteMs:8e3,maxLoadTimeMs:2e4,timeoutRetry:null,errorRetry:null}},keyLoadPolicy:{default:{maxTimeToFirstByteMs:8e3,maxLoadTimeMs:2e4,timeoutRetry:{maxNumRetry:1,retryDelayMs:1e3,maxRetryDelayMs:2e4,backoff:"linear"},errorRetry:{maxNumRetry:8,retryDelayMs:1e3,maxRetryDelayMs:2e4,backoff:"linear"}}},manifestLoadPolicy:{default:{maxTimeToFirstByteMs:1/0,maxLoadTimeMs:2e4,timeoutRetry:{maxNumRetry:2,retryDelayMs:0,maxRetryDelayMs:0},errorRetry:{maxNumRetry:1,retryDelayMs:1e3,maxRetryDelayMs:8e3}}},playlistLoadPolicy:{default:{maxTimeToFirstByteMs:1e4,maxLoadTimeMs:2e4,timeoutRetry:{maxNumRetry:2,retryDelayMs:0,maxRetryDelayMs:0},errorRetry:{maxNumRetry:2,retryDelayMs:1e3,maxRetryDelayMs:8e3}}},fragLoadPolicy:{default:{maxTimeToFirstByteMs:1e4,maxLoadTimeMs:12e4,timeoutRetry:{maxNumRetry:4,retryDelayMs:0,maxRetryDelayMs:0},errorRetry:{maxNumRetry:6,retryDelayMs:1e3,maxRetryDelayMs:8e3}}},steeringManifestLoadPolicy:{default:{maxTimeToFirstByteMs:1e4,maxLoadTimeMs:2e4,timeoutRetry:{maxNumRetry:2,retryDelayMs:0,maxRetryDelayMs:0},errorRetry:{maxNumRetry:1,retryDelayMs:1e3,maxRetryDelayMs:8e3}}},manifestLoadingTimeOut:1e4,manifestLoadingMaxRetry:1,manifestLoadingRetryDelay:1e3,manifestLoadingMaxRetryTimeout:64e3,levelLoadingTimeOut:1e4,levelLoadingMaxRetry:4,levelLoadingRetryDelay:1e3,levelLoadingMaxRetryTimeout:64e3,fragLoadingTimeOut:2e4,fragLoadingMaxRetry:6,fragLoadingRetryDelay:1e3,fragLoadingMaxRetryTimeout:64e3},{cueHandler:{newCue:function(t,e,r,i){for(var n,a,s,o,l,u=[],h=self.VTTCue||self.TextTrackCue,d=0;d<i.rows.length;d++)if(s=!0,o=0,l="",!(n=i.rows[d]).isEmpty()){for(var c,f=0;f<n.chars.length;f++)Xs.test(n.chars[f].uchar)&&s?o++:(l+=n.chars[f].uchar,s=!1);n.cueStartTime=e,e===r&&(r+=1e-4),o>=16?o--:o++;var g=ba(l.trim()),v=_a(e,r,g);null!=t&&null!=(c=t.cues)&&c.getCueById(v)||((a=new h(e,r,g)).id=v,a.line=d+1,a.align="left",a.position=10+Math.min(80,10*Math.floor(8*o/32)),u.push(a))}return t&&u.length&&(u.sort((function(t,e){return"auto"===t.line||"auto"===e.line?0:t.line>8&&e.line>8?e.line-t.line:t.line-e.line})),u.forEach((function(e){return Me(t,e)}))),u}},enableWebVTT:!0,enableIMSC1:!0,enableCEA708Captions:!0,captionsTextTrack1Label:"English",captionsTextTrack1LanguageCode:"en",captionsTextTrack2Label:"Spanish",captionsTextTrack2LanguageCode:"es",captionsTextTrack3Label:"Unknown CC",captionsTextTrack3LanguageCode:"",captionsTextTrack4Label:"Unknown CC",captionsTextTrack4LanguageCode:"",renderTextTracksNatively:!0}),{},{subtitleStreamController:Wn,subtitleTrackController:qn,timelineController:Ya,audioStreamController:Vn,audioTrackController:Yn,emeController:Qa,cmcdController:Ns,contentSteeringController:Us});function Qs(t){return t&&"object"==typeof t?Array.isArray(t)?t.map(Qs):Object.keys(t).reduce((function(e,r){return e[r]=Qs(t[r]),e}),{}):t}function Js(t){var e=t.loader;e!==Ys&&e!==Hs?(w.log("[config]: Custom loader detected, cannot enable progressive streaming"),t.progressive=!1):function(){if(self.fetch&&self.AbortController&&self.ReadableStream&&self.Request)try{return new self.ReadableStream({}),!0}catch(t){}return!1}()&&(t.loader=Ys,t.progressive=!0,t.enableSoftwareAES=!0,w.log("[config]: Progressive streaming enabled, using FetchLoader"))}var $s=function(t){function e(e,r){var i;return(i=t.call(this,e,"[level-controller]")||this)._levels=[],i._firstLevel=-1,i._maxAutoLevel=-1,i._startLevel=void 0,i.currentLevel=null,i.currentLevelIndex=-1,i.manualLevelIndex=-1,i.steering=void 0,i.onParsedComplete=void 0,i.steering=r,i._registerListeners(),i}l(e,t);var r=e.prototype;return r._registerListeners=function(){var t=this.hls;t.on(S.MANIFEST_LOADING,this.onManifestLoading,this),t.on(S.MANIFEST_LOADED,this.onManifestLoaded,this),t.on(S.LEVEL_LOADED,this.onLevelLoaded,this),t.on(S.LEVELS_UPDATED,this.onLevelsUpdated,this),t.on(S.FRAG_BUFFERED,this.onFragBuffered,this),t.on(S.ERROR,this.onError,this)},r._unregisterListeners=function(){var t=this.hls;t.off(S.MANIFEST_LOADING,this.onManifestLoading,this),t.off(S.MANIFEST_LOADED,this.onManifestLoaded,this),t.off(S.LEVEL_LOADED,this.onLevelLoaded,this),t.off(S.LEVELS_UPDATED,this.onLevelsUpdated,this),t.off(S.FRAG_BUFFERED,this.onFragBuffered,this),t.off(S.ERROR,this.onError,this)},r.destroy=function(){this._unregisterListeners(),this.steering=null,this.resetLevels(),t.prototype.destroy.call(this)},r.stopLoad=function(){this._levels.forEach((function(t){t.loadError=0,t.fragmentError=0})),t.prototype.stopLoad.call(this)},r.resetLevels=function(){this._startLevel=void 0,this.manualLevelIndex=-1,this.currentLevelIndex=-1,this.currentLevel=null,this._levels=[],this._maxAutoLevel=-1},r.onManifestLoading=function(t,e){this.resetLevels()},r.onManifestLoaded=function(t,e){var r=this.hls.config.preferManagedMediaSource,i=[],n={},a={},s=!1,o=!1,l=!1;e.levels.forEach((function(t){var e,u,h=t.attrs,d=t.audioCodec,c=t.videoCodec;-1!==(null==(e=d)?void 0:e.indexOf("mp4a.40.34"))&&(js||(js=/chrome|firefox/i.test(navigator.userAgent)),js&&(t.audioCodec=d=void 0)),d&&(t.audioCodec=d=ue(d,r)),0===(null==(u=c)?void 0:u.indexOf("avc1"))&&(c=t.videoCodec=function(t){var e=t.split(".");if(e.length>2){var r=e.shift()+".";return(r+=parseInt(e.shift()).toString(16))+("000"+parseInt(e.shift()).toString(16)).slice(-4)}return t}(c));var f=t.width,g=t.height,v=t.unknownCodecs;if(s||(s=!(!f||!g)),o||(o=!!c),l||(l=!!d),!(null!=v&&v.length||d&&!re(d,"audio",r)||c&&!re(c,"video",r))){var m=h.CODECS,p=h["FRAME-RATE"],y=h["HDCP-LEVEL"],E=h["PATHWAY-ID"],T=h.RESOLUTION,S=h["VIDEO-RANGE"],L=(E||".")+"-"+t.bitrate+"-"+T+"-"+p+"-"+m+"-"+S+"-"+y;if(n[L])if(n[L].uri===t.url||t.attrs["PATHWAY-ID"])n[L].addGroupId("audio",h.AUDIO),n[L].addGroupId("text",h.SUBTITLES);else{var A=a[L]+=1;t.attrs["PATHWAY-ID"]=new Array(A+1).join(".");var R=new tr(t);n[L]=R,i.push(R)}else{var k=new tr(t);n[L]=k,a[L]=1,i.push(k)}}})),this.filterAndSortMediaOptions(i,e,s,o,l)},r.filterAndSortMediaOptions=function(t,e,r,i,n){var a=this,s=[],o=[],l=t;if((r||i)&&n&&(l=l.filter((function(t){var e,r=t.videoCodec,i=t.videoRange,n=t.width,a=t.height;return(!!r||!(!n||!a))&&!!(e=i)&&ze.indexOf(e)>-1}))),0!==l.length){if(e.audioTracks){var u=this.hls.config.preferManagedMediaSource;Zs(s=e.audioTracks.filter((function(t){return!t.audioCodec||re(t.audioCodec,"audio",u)})))}e.subtitles&&Zs(o=e.subtitles);var h=l.slice(0);l.sort((function(t,e){if(t.attrs["HDCP-LEVEL"]!==e.attrs["HDCP-LEVEL"])return(t.attrs["HDCP-LEVEL"]||"")>(e.attrs["HDCP-LEVEL"]||"")?1:-1;if(r&&t.height!==e.height)return t.height-e.height;if(t.frameRate!==e.frameRate)return t.frameRate-e.frameRate;if(t.videoRange!==e.videoRange)return ze.indexOf(t.videoRange)-ze.indexOf(e.videoRange);if(t.videoCodec!==e.videoCodec){var i=ae(t.videoCodec),n=ae(e.videoCodec);if(i!==n)return n-i}if(t.uri===e.uri&&t.codecSet!==e.codecSet){var a=se(t.codecSet),s=se(e.codecSet);if(a!==s)return s-a}return t.bitrate!==e.bitrate?t.bitrate-e.bitrate:0}));var d=h[0];if(this.steering&&(l=this.steering.filterParsedLevels(l)).length!==h.length)for(var c=0;c<h.length;c++)if(h[c].pathwayId===l[0].pathwayId){d=h[c];break}this._levels=l;for(var f=0;f<l.length;f++)if(l[f]===d){var g;this._firstLevel=f;var v=d.bitrate,m=this.hls.bandwidthEstimate;if(this.log("manifest loaded, "+l.length+" level(s) found, first bitrate: "+v),void 0===(null==(g=this.hls.userConfig)?void 0:g.abrEwmaDefaultEstimate)){var p=Math.min(v,this.hls.config.abrEwmaDefaultEstimateMax);p>m&&m===zs.abrEwmaDefaultEstimate&&(this.hls.bandwidthEstimate=p)}break}var y=n&&!i,E={levels:l,audioTracks:s,subtitleTracks:o,sessionData:e.sessionData,sessionKeys:e.sessionKeys,firstLevel:this._firstLevel,stats:e.stats,audio:n,video:i,altAudio:!y&&s.some((function(t){return!!t.url}))};this.hls.trigger(S.MANIFEST_PARSED,E),(this.hls.config.autoStartLoad||this.hls.forceStartLoad)&&this.hls.startLoad(this.hls.config.startPosition)}else Promise.resolve().then((function(){if(a.hls){e.levels.length&&a.warn("One or more CODECS in variant not supported: "+JSON.stringify(e.levels[0].attrs));var t=new Error("no level with compatible codecs found in manifest");a.hls.trigger(S.ERROR,{type:L.MEDIA_ERROR,details:A.MANIFEST_INCOMPATIBLE_CODECS_ERROR,fatal:!0,url:e.url,error:t,reason:t.message})}}))},r.onError=function(t,e){!e.fatal&&e.context&&e.context.type===ke&&e.context.level===this.level&&this.checkRetry(e)},r.onFragBuffered=function(t,e){var r=e.frag;if(void 0!==r&&r.type===Ie){var i=r.elementaryStreams;if(!Object.keys(i).some((function(t){return!!i[t]})))return;var n=this._levels[r.level];null!=n&&n.loadError&&(this.log("Resetting level error count of "+n.loadError+" on frag buffered"),n.loadError=0)}},r.onLevelLoaded=function(t,e){var r,i,n=e.level,a=e.details,s=this._levels[n];if(!s)return this.warn("Invalid level index "+n),void(null!=(i=e.deliveryDirectives)&&i.skip&&(a.deltaUpdateFailed=!0));n===this.currentLevelIndex?(0===s.fragmentError&&(s.loadError=0),this.playlistLoaded(n,e,s.details)):null!=(r=e.deliveryDirectives)&&r.skip&&(a.deltaUpdateFailed=!0)},r.loadPlaylist=function(e){t.prototype.loadPlaylist.call(this);var r=this.currentLevelIndex,i=this.currentLevel;if(i&&this.shouldLoadPlaylist(i)){var n=i.uri;if(e)try{n=e.addDirectives(n)}catch(t){this.warn("Could not construct new URL with HLS Delivery Directives: "+t)}var a=i.attrs["PATHWAY-ID"];this.log("Loading level index "+r+(void 0!==(null==e?void 0:e.msn)?" at sn "+e.msn+" part "+e.part:"")+" with"+(a?" Pathway "+a:"")+" "+n),this.clearTimer(),this.hls.trigger(S.LEVEL_LOADING,{url:n,level:r,pathwayId:i.attrs["PATHWAY-ID"],id:0,deliveryDirectives:e||null})}},r.removeLevel=function(t){var e,r=this,i=this._levels.filter((function(e,i){return i!==t||(r.steering&&r.steering.removeLevel(e),e===r.currentLevel&&(r.currentLevel=null,r.currentLevelIndex=-1,e.details&&e.details.fragments.forEach((function(t){return t.level=-1}))),!1)}));ur(i),this._levels=i,this.currentLevelIndex>-1&&null!=(e=this.currentLevel)&&e.details&&(this.currentLevelIndex=this.currentLevel.details.fragments[0].level),this.hls.trigger(S.LEVELS_UPDATED,{levels:i})},r.onLevelsUpdated=function(t,e){var r=e.levels;this._levels=r},r.checkMaxAutoUpdated=function(){var t=this.hls,e=t.autoLevelCapping,r=t.maxAutoLevel,i=t.maxHdcpLevel;this._maxAutoLevel!==r&&(this._maxAutoLevel=r,this.hls.trigger(S.MAX_AUTO_LEVEL_UPDATED,{autoLevelCapping:e,levels:this.levels,maxAutoLevel:r,minAutoLevel:this.hls.minAutoLevel,maxHdcpLevel:i}))},s(e,[{key:"levels",get:function(){return 0===this._levels.length?null:this._levels}},{key:"level",get:function(){return this.currentLevelIndex},set:function(t){var e=this._levels;if(0!==e.length){if(t<0||t>=e.length){var r=new Error("invalid level idx"),i=t<0;if(this.hls.trigger(S.ERROR,{type:L.OTHER_ERROR,details:A.LEVEL_SWITCH_ERROR,level:t,fatal:i,error:r,reason:r.message}),i)return;t=Math.min(t,e.length-1)}var n=this.currentLevelIndex,a=this.currentLevel,s=a?a.attrs["PATHWAY-ID"]:void 0,o=e[t],l=o.attrs["PATHWAY-ID"];if(this.currentLevelIndex=t,this.currentLevel=o,n!==t||!o.details||!a||s!==l){this.log("Switching to level "+t+" ("+(o.height?o.height+"p ":"")+(o.videoRange?o.videoRange+" ":"")+(o.codecSet?o.codecSet+" ":"")+"@"+o.bitrate+")"+(l?" with Pathway "+l:"")+" from level "+n+(s?" with Pathway "+s:""));var u={level:t,attrs:o.attrs,details:o.details,bitrate:o.bitrate,averageBitrate:o.averageBitrate,maxBitrate:o.maxBitrate,realBitrate:o.realBitrate,width:o.width,height:o.height,codecSet:o.codecSet,audioCodec:o.audioCodec,videoCodec:o.videoCodec,audioGroups:o.audioGroups,subtitleGroups:o.subtitleGroups,loaded:o.loaded,loadError:o.loadError,fragmentError:o.fragmentError,name:o.name,id:o.id,uri:o.uri,url:o.url,urlId:0,audioGroupIds:o.audioGroupIds,textGroupIds:o.textGroupIds};this.hls.trigger(S.LEVEL_SWITCHING,u);var h=o.details;if(!h||h.live){var d=this.switchParams(o.uri,null==a?void 0:a.details);this.loadPlaylist(d)}}}}},{key:"manualLevel",get:function(){return this.manualLevelIndex},set:function(t){this.manualLevelIndex=t,void 0===this._startLevel&&(this._startLevel=t),-1!==t&&(this.level=t)}},{key:"firstLevel",get:function(){return this._firstLevel},set:function(t){this._firstLevel=t}},{key:"startLevel",get:function(){if(void 0===this._startLevel){var t=this.hls.config.startLevel;return void 0!==t?t:this.hls.firstAutoLevel}return this._startLevel},set:function(t){this._startLevel=t}},{key:"nextLoadLevel",get:function(){return-1!==this.manualLevelIndex?this.manualLevelIndex:this.hls.nextAutoLevel},set:function(t){this.level=t,-1===this.manualLevelIndex&&(this.hls.nextAutoLevel=t)}}]),e}(Dr);function Zs(t){var e={};t.forEach((function(t){var r=t.groupId||"";t.id=e[r]=e[r]||0,e[r]++}))}var to=function(){function t(t){this.config=void 0,this.keyUriToKeyInfo={},this.emeController=null,this.config=t}var e=t.prototype;return e.abort=function(t){for(var e in this.keyUriToKeyInfo){var r=this.keyUriToKeyInfo[e].loader;if(r){var i;if(t&&t!==(null==(i=r.context)?void 0:i.frag.type))return;r.abort()}}},e.detach=function(){for(var t in this.keyUriToKeyInfo){var e=this.keyUriToKeyInfo[t];(e.mediaKeySessionContext||e.decryptdata.isCommonEncryption)&&delete this.keyUriToKeyInfo[t]}},e.destroy=function(){for(var t in this.detach(),this.keyUriToKeyInfo){var e=this.keyUriToKeyInfo[t].loader;e&&e.destroy()}this.keyUriToKeyInfo={}},e.createKeyLoadError=function(t,e,r,i,n){return void 0===e&&(e=A.KEY_LOAD_ERROR),new si({type:L.NETWORK_ERROR,details:e,fatal:!1,frag:t,response:n,error:r,networkDetails:i})},e.loadClear=function(t,e){var r=this;if(this.emeController&&this.config.emeEnabled)for(var i=t.sn,n=t.cc,a=function(){var t=e[s];if(n<=t.cc&&("initSegment"===i||"initSegment"===t.sn||i<t.sn))return r.emeController.selectKeySystemFormat(t).then((function(e){t.setKeyFormat(e)})),1},s=0;s<e.length&&!a();s++);},e.load=function(t){var e=this;return!t.decryptdata&&t.encrypted&&this.emeController?this.emeController.selectKeySystemFormat(t).then((function(r){return e.loadInternal(t,r)})):this.loadInternal(t)},e.loadInternal=function(t,e){var r,i;e&&t.setKeyFormat(e);var n=t.decryptdata;if(!n){var a=new Error(e?"Expected frag.decryptdata to be defined after setting format "+e:"Missing decryption data on fragment in onKeyLoading");return Promise.reject(this.createKeyLoadError(t,A.KEY_LOAD_ERROR,a))}var s=n.uri;if(!s)return Promise.reject(this.createKeyLoadError(t,A.KEY_LOAD_ERROR,new Error('Invalid key URI: "'+s+'"')));var o,l=this.keyUriToKeyInfo[s];if(null!=(r=l)&&r.decryptdata.key)return n.key=l.decryptdata.key,Promise.resolve({frag:t,keyInfo:l});if(null!=(i=l)&&i.keyLoadPromise)switch(null==(o=l.mediaKeySessionContext)?void 0:o.keyStatus){case void 0:case"status-pending":case"usable":case"usable-in-future":return l.keyLoadPromise.then((function(e){return n.key=e.keyInfo.decryptdata.key,{frag:t,keyInfo:l}}))}switch(l=this.keyUriToKeyInfo[s]={decryptdata:n,keyLoadPromise:null,loader:null,mediaKeySessionContext:null},n.method){case"ISO-23001-7":case"SAMPLE-AES":case"SAMPLE-AES-CENC":case"SAMPLE-AES-CTR":return"identity"===n.keyFormat?this.loadKeyHTTP(l,t):this.loadKeyEME(l,t);case"AES-128":return this.loadKeyHTTP(l,t);default:return Promise.reject(this.createKeyLoadError(t,A.KEY_LOAD_ERROR,new Error('Key supplied with unsupported METHOD: "'+n.method+'"')))}},e.loadKeyEME=function(t,e){var r={frag:e,keyInfo:t};if(this.emeController&&this.config.emeEnabled){var i=this.emeController.loadKey(r);if(i)return(t.keyLoadPromise=i.then((function(e){return t.mediaKeySessionContext=e,r}))).catch((function(e){throw t.keyLoadPromise=null,e}))}return Promise.resolve(r)},e.loadKeyHTTP=function(t,e){var r=this,n=this.config,a=new(0,n.loader)(n);return e.keyLoader=t.loader=a,t.keyLoadPromise=new Promise((function(s,o){var l={keyInfo:t,frag:e,responseType:"arraybuffer",url:t.decryptdata.uri},u=n.keyLoadPolicy.default,h={loadPolicy:u,timeout:u.maxLoadTimeMs,maxRetry:0,retryDelay:0,maxRetryDelay:0},d={onSuccess:function(t,e,i,n){var a=i.frag,l=i.keyInfo,u=i.url;if(!a.decryptdata||l!==r.keyUriToKeyInfo[u])return o(r.createKeyLoadError(a,A.KEY_LOAD_ERROR,new Error("after key load, decryptdata unset or changed"),n));l.decryptdata.key=a.decryptdata.key=new Uint8Array(t.data),a.keyLoader=null,l.loader=null,s({frag:a,keyInfo:l})},onError:function(t,n,a,s){r.resetLoader(n),o(r.createKeyLoadError(e,A.KEY_LOAD_ERROR,new Error("HTTP Error "+t.code+" loading key "+t.text),a,i({url:l.url,data:void 0},t)))},onTimeout:function(t,i,n){r.resetLoader(i),o(r.createKeyLoadError(e,A.KEY_LOAD_TIMEOUT,new Error("key loading timed out"),n))},onAbort:function(t,i,n){r.resetLoader(i),o(r.createKeyLoadError(e,A.INTERNAL_ABORTED,new Error("key loading aborted"),n))}};a.load(l,h,d)}))},e.resetLoader=function(t){var e=t.frag,r=t.keyInfo,i=t.url,n=r.loader;e.keyLoader===n&&(e.keyLoader=null,r.loader=null),delete this.keyUriToKeyInfo[i],n&&n.destroy()},t}();function eo(){return self.SourceBuffer||self.WebKitSourceBuffer}function ro(){if(!te())return!1;var t=eo();return!t||t.prototype&&"function"==typeof t.prototype.appendBuffer&&"function"==typeof t.prototype.remove}var io=function(){function t(t,e,r,i){this.config=void 0,this.media=null,this.fragmentTracker=void 0,this.hls=void 0,this.nudgeRetry=0,this.stallReported=!1,this.stalled=null,this.moved=!1,this.seeking=!1,this.config=t,this.media=e,this.fragmentTracker=r,this.hls=i}var e=t.prototype;return e.destroy=function(){this.media=null,this.hls=this.fragmentTracker=null},e.poll=function(t,e){var r=this.config,i=this.media,n=this.stalled;if(null!==i){var a=i.currentTime,s=i.seeking,o=this.seeking&&!s,l=!this.seeking&&s;if(this.seeking=s,a===t)if(l||o)this.stalled=null;else if(i.paused&&!s||i.ended||0===i.playbackRate||!zr.getBuffered(i).length)this.nudgeRetry=0;else{var u=zr.bufferInfo(i,a,0),h=u.nextStart||0;if(s){var d=u.len>2,c=!h||e&&e.start<=a||h-a>2&&!this.fragmentTracker.getPartialFragment(a);if(d||c)return;this.moved=!1}if(!this.moved&&null!==this.stalled){var f;if(!(u.len>0||h))return;var g=Math.max(h,u.start||0)-a,v=this.hls.levels?this.hls.levels[this.hls.currentLevel]:null,m=(null==v||null==(f=v.details)?void 0:f.live)?2*v.details.targetduration:2,p=this.fragmentTracker.getPartialFragment(a);if(g>0&&(g<=m||p))return void(i.paused||this._trySkipBufferHole(p))}var y=self.performance.now();if(null!==n){var E=y-n;if(s||!(E>=250)||(this._reportStall(u),this.media)){var T=zr.bufferInfo(i,a,r.maxBufferHole);this._tryFixBufferStall(T,E)}}else this.stalled=y}else if(this.moved=!0,s||(this.nudgeRetry=0),null!==n){if(this.stallReported){var S=self.performance.now()-n;w.warn("playback not stuck anymore @"+a+", after "+Math.round(S)+"ms"),this.stallReported=!1}this.stalled=null}}},e._tryFixBufferStall=function(t,e){var r=this.config,i=this.fragmentTracker,n=this.media;if(null!==n){var a=n.currentTime,s=i.getPartialFragment(a);if(s&&(this._trySkipBufferHole(s)||!this.media))return;(t.len>r.maxBufferHole||t.nextStart&&t.nextStart-a<r.maxBufferHole)&&e>1e3*r.highBufferWatchdogPeriod&&(w.warn("Trying to nudge playhead over buffer-hole"),this.stalled=null,this._tryNudgeBuffer())}},e._reportStall=function(t){var e=this.hls,r=this.media;if(!this.stallReported&&r){this.stallReported=!0;var i=new Error("Playback stalling at @"+r.currentTime+" due to low buffer ("+JSON.stringify(t)+")");w.warn(i.message),e.trigger(S.ERROR,{type:L.MEDIA_ERROR,details:A.BUFFER_STALLED_ERROR,fatal:!1,error:i,buffer:t.len})}},e._trySkipBufferHole=function(t){var e=this.config,r=this.hls,i=this.media;if(null===i)return 0;var n=i.currentTime,a=zr.bufferInfo(i,n,0),s=n<a.start?a.start:a.nextStart;if(s){var o=a.len<=e.maxBufferHole,l=a.len>0&&a.len<1&&i.readyState<3,u=s-n;if(u>0&&(o||l)){if(u>e.maxBufferHole){var h=this.fragmentTracker,d=!1;if(0===n){var c=h.getAppendedFrag(0,Ie);c&&s<c.end&&(d=!0)}if(!d){var f=t||h.getAppendedFrag(n,Ie);if(f){for(var g=!1,v=f.end;v<s;){var m=h.getPartialFragment(v);if(!m){g=!0;break}v+=m.duration}if(g)return 0}}}var p=Math.max(s+.05,n+.1);if(w.warn("skipping hole, adjusting currentTime from "+n+" to "+p),this.moved=!0,this.stalled=null,i.currentTime=p,t&&!t.gap){var y=new Error("fragment loaded with buffer holes, seeking from "+n+" to "+p);r.trigger(S.ERROR,{type:L.MEDIA_ERROR,details:A.BUFFER_SEEK_OVER_HOLE,fatal:!1,error:y,reason:y.message,frag:t})}return p}}return 0},e._tryNudgeBuffer=function(){var t=this.config,e=this.hls,r=this.media,i=this.nudgeRetry;if(null!==r){var n=r.currentTime;if(this.nudgeRetry++,i<t.nudgeMaxRetry){var a=n+(i+1)*t.nudgeOffset,s=new Error("Nudging 'currentTime' from "+n+" to "+a);w.warn(s.message),r.currentTime=a,e.trigger(S.ERROR,{type:L.MEDIA_ERROR,details:A.BUFFER_NUDGE_ON_STALL,error:s,fatal:!1})}else{var o=new Error("Playhead still not moving while enough data buffered @"+n+" after "+t.nudgeMaxRetry+" nudges");w.error(o.message),e.trigger(S.ERROR,{type:L.MEDIA_ERROR,details:A.BUFFER_STALLED_ERROR,error:o,fatal:!0})}}},t}(),no=function(t){function e(e,r,i){var n;return(n=t.call(this,e,r,i,"[stream-controller]",Ie)||this).audioCodecSwap=!1,n.gapController=null,n.level=-1,n._forceStartLoad=!1,n.altAudio=!1,n.audioOnly=!1,n.fragPlaying=null,n.onvplaying=null,n.onvseeked=null,n.fragLastKbps=0,n.couldBacktrack=!1,n.backtrackFragment=null,n.audioCodecSwitch=!1,n.videoBuffer=null,n._registerListeners(),n}l(e,t);var r=e.prototype;return r._registerListeners=function(){var t=this.hls;t.on(S.MEDIA_ATTACHED,this.onMediaAttached,this),t.on(S.MEDIA_DETACHING,this.onMediaDetaching,this),t.on(S.MANIFEST_LOADING,this.onManifestLoading,this),t.on(S.MANIFEST_PARSED,this.onManifestParsed,this),t.on(S.LEVEL_LOADING,this.onLevelLoading,this),t.on(S.LEVEL_LOADED,this.onLevelLoaded,this),t.on(S.FRAG_LOAD_EMERGENCY_ABORTED,this.onFragLoadEmergencyAborted,this),t.on(S.ERROR,this.onError,this),t.on(S.AUDIO_TRACK_SWITCHING,this.onAudioTrackSwitching,this),t.on(S.AUDIO_TRACK_SWITCHED,this.onAudioTrackSwitched,this),t.on(S.BUFFER_CREATED,this.onBufferCreated,this),t.on(S.BUFFER_FLUSHED,this.onBufferFlushed,this),t.on(S.LEVELS_UPDATED,this.onLevelsUpdated,this),t.on(S.FRAG_BUFFERED,this.onFragBuffered,this)},r._unregisterListeners=function(){var t=this.hls;t.off(S.MEDIA_ATTACHED,this.onMediaAttached,this),t.off(S.MEDIA_DETACHING,this.onMediaDetaching,this),t.off(S.MANIFEST_LOADING,this.onManifestLoading,this),t.off(S.MANIFEST_PARSED,this.onManifestParsed,this),t.off(S.LEVEL_LOADED,this.onLevelLoaded,this),t.off(S.FRAG_LOAD_EMERGENCY_ABORTED,this.onFragLoadEmergencyAborted,this),t.off(S.ERROR,this.onError,this),t.off(S.AUDIO_TRACK_SWITCHING,this.onAudioTrackSwitching,this),t.off(S.AUDIO_TRACK_SWITCHED,this.onAudioTrackSwitched,this),t.off(S.BUFFER_CREATED,this.onBufferCreated,this),t.off(S.BUFFER_FLUSHED,this.onBufferFlushed,this),t.off(S.LEVELS_UPDATED,this.onLevelsUpdated,this),t.off(S.FRAG_BUFFERED,this.onFragBuffered,this)},r.onHandlerDestroying=function(){this._unregisterListeners(),t.prototype.onHandlerDestroying.call(this)},r.startLoad=function(t){if(this.levels){var e=this.lastCurrentTime,r=this.hls;if(this.stopLoad(),this.setInterval(100),this.level=-1,!this.startFragRequested){var i=r.startLevel;-1===i&&(r.config.testBandwidth&&this.levels.length>1?(i=0,this.bitrateTest=!0):i=r.firstAutoLevel),this.level=r.nextLoadLevel=i,this.loadedmetadata=!1}e>0&&-1===t&&(this.log("Override startPosition with lastCurrentTime @"+e.toFixed(3)),t=e),this.state=fi,this.nextLoadPosition=this.startPosition=this.lastCurrentTime=t,this.tick()}else this._forceStartLoad=!0,this.state=ci},r.stopLoad=function(){this._forceStartLoad=!1,t.prototype.stopLoad.call(this)},r.doTick=function(){switch(this.state){case Ai:var t=this.levels,e=this.level,r=null==t?void 0:t[e],i=null==r?void 0:r.details;if(i&&(!i.live||this.levelLastLoaded===r)){if(this.waitForCdnTuneIn(i))break;this.state=fi;break}if(this.hls.nextLoadLevel!==this.level){this.state=fi;break}break;case mi:var n,a=self.performance.now(),s=this.retryDate;if(!s||a>=s||null!=(n=this.media)&&n.seeking){var o=this.levels,l=this.level,u=null==o?void 0:o[l];this.resetStartWhenNotLoaded(u||null),this.state=fi}}this.state===fi&&this.doTickIdle(),this.onTickEnd()},r.onTickEnd=function(){t.prototype.onTickEnd.call(this),this.checkBuffer(),this.checkFragmentChanged()},r.doTickIdle=function(){var t=this.hls,e=this.levelLastLoaded,r=this.levels,i=this.media,n=t.config,a=t.nextLoadLevel;if(null!==e&&(i||!this.startFragRequested&&n.startFragPrefetch)&&(!this.altAudio||!this.audioOnly)&&null!=r&&r[a]){var s=r[a],o=this.getMainFwdBufferInfo();if(null!==o){var l=this.getLevelDetails();if(l&&this._streamEnded(o,l)){var u={};return this.altAudio&&(u.type="video"),this.hls.trigger(S.BUFFER_EOS,u),void(this.state=Ti)}t.loadLevel!==a&&-1===t.manualLevel&&this.log("Adapting to level "+a+" from level "+this.level),this.level=t.nextLoadLevel=a;var h=s.details;if(!h||this.state===Ai||h.live&&this.levelLastLoaded!==s)return this.level=a,void(this.state=Ai);var d=o.len,c=this.getMaxBufferLength(s.maxBitrate);if(!(d>=c)){this.backtrackFragment&&this.backtrackFragment.start>o.end&&(this.backtrackFragment=null);var f=this.backtrackFragment?this.backtrackFragment.start:o.end,g=this.getNextFragment(f,h);if(this.couldBacktrack&&!this.fragPrevious&&g&&"initSegment"!==g.sn&&this.fragmentTracker.getState(g)!==Yr){var v,m=(null!=(v=this.backtrackFragment)?v:g).sn-h.startSN,p=h.fragments[m-1];p&&g.cc===p.cc&&(g=p,this.fragmentTracker.removeFragment(p))}else this.backtrackFragment&&o.len&&(this.backtrackFragment=null);if(g&&this.isLoopLoading(g,f)){if(!g.gap){var y=this.audioOnly&&!this.altAudio?O:N,E=(y===N?this.videoBuffer:this.mediaBuffer)||this.media;E&&this.afterBufferFlushed(E,y,Ie)}g=this.getNextFragmentLoopLoading(g,h,o,Ie,c)}g&&(!g.initSegment||g.initSegment.data||this.bitrateTest||(g=g.initSegment),this.loadFragment(g,s,f))}}}},r.loadFragment=function(e,r,i){var n=this.fragmentTracker.getState(e);this.fragCurrent=e,n===Kr||n===Vr?"initSegment"===e.sn?this._loadInitSegment(e,r):this.bitrateTest?(this.log("Fragment "+e.sn+" of level "+e.level+" is being downloaded to test bitrate and will not be buffered"),this._loadBitrateTestFrag(e,r)):(this.startFragRequested=!0,t.prototype.loadFragment.call(this,e,r,i)):this.clearTrackerIfNeeded(e)},r.getBufferedFrag=function(t){return this.fragmentTracker.getBufferedFrag(t,Ie)},r.followingBufferedFrag=function(t){return t?this.getBufferedFrag(t.end+.5):null},r.immediateLevelSwitch=function(){this.abortCurrentFrag(),this.flushMainBuffer(0,Number.POSITIVE_INFINITY)},r.nextLevelSwitch=function(){var t=this.levels,e=this.media;if(null!=e&&e.readyState){var r,i=this.getAppendedFrag(e.currentTime);i&&i.start>1&&this.flushMainBuffer(0,i.start-1);var n=this.getLevelDetails();if(null!=n&&n.live){var a=this.getMainFwdBufferInfo();if(!a||a.len<2*n.targetduration)return}if(!e.paused&&t){var s=t[this.hls.nextLoadLevel],o=this.fragLastKbps;r=o&&this.fragCurrent?this.fragCurrent.duration*s.maxBitrate/(1e3*o)+1:0}else r=0;var l=this.getBufferedFrag(e.currentTime+r);if(l){var u=this.followingBufferedFrag(l);if(u){this.abortCurrentFrag();var h=u.maxStartPTS?u.maxStartPTS:u.start,d=u.duration,c=Math.max(l.end,h+Math.min(Math.max(d-this.config.maxFragLookUpTolerance,d*(this.couldBacktrack?.5:.125)),d*(this.couldBacktrack?.75:.25)));this.flushMainBuffer(c,Number.POSITIVE_INFINITY)}}}},r.abortCurrentFrag=function(){var t=this.fragCurrent;switch(this.fragCurrent=null,this.backtrackFragment=null,t&&(t.abortRequests(),this.fragmentTracker.removeFragment(t)),this.state){case gi:case vi:case mi:case yi:case Ei:this.state=fi}this.nextLoadPosition=this.getLoadPosition()},r.flushMainBuffer=function(e,r){t.prototype.flushMainBuffer.call(this,e,r,this.altAudio?"video":null)},r.onMediaAttached=function(e,r){t.prototype.onMediaAttached.call(this,e,r);var i=r.media;this.onvplaying=this.onMediaPlaying.bind(this),this.onvseeked=this.onMediaSeeked.bind(this),i.addEventListener("playing",this.onvplaying),i.addEventListener("seeked",this.onvseeked),this.gapController=new io(this.config,i,this.fragmentTracker,this.hls)},r.onMediaDetaching=function(){var e=this.media;e&&this.onvplaying&&this.onvseeked&&(e.removeEventListener("playing",this.onvplaying),e.removeEventListener("seeked",this.onvseeked),this.onvplaying=this.onvseeked=null,this.videoBuffer=null),this.fragPlaying=null,this.gapController&&(this.gapController.destroy(),this.gapController=null),t.prototype.onMediaDetaching.call(this)},r.onMediaPlaying=function(){this.tick()},r.onMediaSeeked=function(){var t=this.media,e=t?t.currentTime:null;y(e)&&this.log("Media seeked to "+e.toFixed(3));var r=this.getMainFwdBufferInfo();null!==r&&0!==r.len?this.tick():this.warn('Main forward buffer length on "seeked" event '+(r?r.len:"empty")+")")},r.onManifestLoading=function(){this.log("Trigger BUFFER_RESET"),this.hls.trigger(S.BUFFER_RESET,void 0),this.fragmentTracker.removeAllFragments(),this.couldBacktrack=!1,this.startPosition=this.lastCurrentTime=this.fragLastKbps=0,this.levels=this.fragPlaying=this.backtrackFragment=this.levelLastLoaded=null,this.altAudio=this.audioOnly=this.startFragRequested=!1},r.onManifestParsed=function(t,e){var r,i,n=!1,a=!1;e.levels.forEach((function(t){var e=t.audioCodec;e&&(n=n||-1!==e.indexOf("mp4a.40.2"),a=a||-1!==e.indexOf("mp4a.40.5"))})),this.audioCodecSwitch=n&&a&&!("function"==typeof(null==(i=eo())||null==(r=i.prototype)?void 0:r.changeType)),this.audioCodecSwitch&&this.log("Both AAC/HE-AAC audio found in levels; declaring level codec as HE-AAC"),this.levels=e.levels,this.startFragRequested=!1},r.onLevelLoading=function(t,e){var r=this.levels;if(r&&this.state===fi){var i=r[e.level];(!i.details||i.details.live&&this.levelLastLoaded!==i||this.waitForCdnTuneIn(i.details))&&(this.state=Ai)}},r.onLevelLoaded=function(t,e){var r,i=this.levels,n=e.level,a=e.details,s=a.totalduration;if(i){this.log("Level "+n+" loaded ["+a.startSN+","+a.endSN+"]"+(a.lastPartSn?"[part-"+a.lastPartSn+"-"+a.lastPartIndex+"]":"")+", cc ["+a.startCC+", "+a.endCC+"] duration:"+s);var o=i[n],l=this.fragCurrent;!l||this.state!==vi&&this.state!==mi||l.level!==e.level&&l.loader&&this.abortCurrentFrag();var u=0;if(a.live||null!=(r=o.details)&&r.live){var h;if(this.checkLiveUpdate(a),a.deltaUpdateFailed)return;u=this.alignPlaylists(a,o.details,null==(h=this.levelLastLoaded)?void 0:h.details)}if(o.details=a,this.levelLastLoaded=o,this.hls.trigger(S.LEVEL_UPDATED,{details:a,level:n}),this.state===Ai){if(this.waitForCdnTuneIn(a))return;this.state=fi}this.startFragRequested?a.live&&this.synchronizeToLiveEdge(a):this.setStartPosition(a,u),this.tick()}else this.warn("Levels were reset while loading level "+n)},r._handleFragmentLoadProgress=function(t){var e,r=t.frag,i=t.part,n=t.payload,a=this.levels;if(a){var s=a[r.level],o=s.details;if(!o)return this.warn("Dropping fragment "+r.sn+" of level "+r.level+" after level details were reset"),void this.fragmentTracker.removeFragment(r);var l=s.videoCodec,u=o.PTSKnown||!o.live,h=null==(e=r.initSegment)?void 0:e.data,d=this._getAudioCodec(s),c=this.transmuxer=this.transmuxer||new Bn(this.hls,Ie,this._handleTransmuxComplete.bind(this),this._handleTransmuxerFlush.bind(this)),f=i?i.index:-1,g=-1!==f,v=new Qr(r.level,r.sn,r.stats.chunkCount,n.byteLength,f,g),m=this.initPTS[r.cc];c.push(n,h,d,l,r,i,o.totalduration,u,v,m)}else this.warn("Levels were reset while fragment load was in progress. Fragment "+r.sn+" of level "+r.level+" will not be buffered")},r.onAudioTrackSwitching=function(t,e){var r=this.altAudio;if(!e.url){if(this.mediaBuffer!==this.media){this.log("Switching on main audio, use media.buffered to schedule main fragment loading"),this.mediaBuffer=this.media;var i=this.fragCurrent;i&&(this.log("Switching to main audio track, cancel main fragment load"),i.abortRequests(),this.fragmentTracker.removeFragment(i)),this.resetTransmuxer(),this.resetLoadingState()}else this.audioOnly&&this.resetTransmuxer();var n=this.hls;r&&(n.trigger(S.BUFFER_FLUSHING,{startOffset:0,endOffset:Number.POSITIVE_INFINITY,type:null}),this.fragmentTracker.removeAllFragments()),n.trigger(S.AUDIO_TRACK_SWITCHED,e)}},r.onAudioTrackSwitched=function(t,e){var r=e.id,i=!!this.hls.audioTracks[r].url;if(i){var n=this.videoBuffer;n&&this.mediaBuffer!==n&&(this.log("Switching on alternate audio, use video.buffered to schedule main fragment loading"),this.mediaBuffer=n)}this.altAudio=i,this.tick()},r.onBufferCreated=function(t,e){var r,i,n=e.tracks,a=!1;for(var s in n){var o=n[s];if("main"===o.id){if(i=s,r=o,"video"===s){var l=n[s];l&&(this.videoBuffer=l.buffer)}}else a=!0}a&&r?(this.log("Alternate track found, use "+i+".buffered to schedule main fragment loading"),this.mediaBuffer=r.buffer):this.mediaBuffer=this.media},r.onFragBuffered=function(t,e){var r=e.frag,i=e.part;if(!r||r.type===Ie){if(this.fragContextChanged(r))return this.warn("Fragment "+r.sn+(i?" p: "+i.index:"")+" of level "+r.level+" finished buffering, but was aborted. state: "+this.state),void(this.state===Ei&&(this.state=fi));var n=i?i.stats:r.stats;this.fragLastKbps=Math.round(8*n.total/(n.buffering.end-n.loading.first)),"initSegment"!==r.sn&&(this.fragPrevious=r),this.fragBufferedComplete(r,i)}},r.onError=function(t,e){var r;if(e.fatal)this.state=Si;else switch(e.details){case A.FRAG_GAP:case A.FRAG_PARSING_ERROR:case A.FRAG_DECRYPT_ERROR:case A.FRAG_LOAD_ERROR:case A.FRAG_LOAD_TIMEOUT:case A.KEY_LOAD_ERROR:case A.KEY_LOAD_TIMEOUT:this.onFragmentOrKeyLoadError(Ie,e);break;case A.LEVEL_LOAD_ERROR:case A.LEVEL_LOAD_TIMEOUT:case A.LEVEL_PARSING_ERROR:e.levelRetry||this.state!==Ai||(null==(r=e.context)?void 0:r.type)!==ke||(this.state=fi);break;case A.BUFFER_APPEND_ERROR:case A.BUFFER_FULL_ERROR:if(!e.parent||"main"!==e.parent)return;if(e.details===A.BUFFER_APPEND_ERROR)return void this.resetLoadingState();this.reduceLengthAndFlushBuffer(e)&&this.flushMainBuffer(0,Number.POSITIVE_INFINITY);break;case A.INTERNAL_EXCEPTION:this.recoverWorkerError(e)}},r.checkBuffer=function(){var t=this.media,e=this.gapController;if(t&&e&&t.readyState){if(this.loadedmetadata||!zr.getBuffered(t).length){var r=this.state!==fi?this.fragCurrent:null;e.poll(this.lastCurrentTime,r)}this.lastCurrentTime=t.currentTime}},r.onFragLoadEmergencyAborted=function(){this.state=fi,this.loadedmetadata||(this.startFragRequested=!1,this.nextLoadPosition=this.startPosition),this.tickImmediate()},r.onBufferFlushed=function(t,e){var r=e.type;if(r!==O||this.audioOnly&&!this.altAudio){var i=(r===N?this.videoBuffer:this.mediaBuffer)||this.media;this.afterBufferFlushed(i,r,Ie),this.tick()}},r.onLevelsUpdated=function(t,e){this.level>-1&&this.fragCurrent&&(this.level=this.fragCurrent.level),this.levels=e.levels},r.swapAudioCodec=function(){this.audioCodecSwap=!this.audioCodecSwap},r.seekToStartPos=function(){var t=this.media;if(t){var e=t.currentTime,r=this.startPosition;if(r>=0&&e<r){if(t.seeking)return void this.log("could not seek to "+r+", already seeking at "+e);var i=zr.getBuffered(t),n=(i.length?i.start(0):0)-r;n>0&&(n<this.config.maxBufferHole||n<this.config.maxFragLookUpTolerance)&&(this.log("adjusting start position by "+n+" to match buffer start"),r+=n,this.startPosition=r),this.log("seek to target start position "+r+" from current time "+e),t.currentTime=r}}},r._getAudioCodec=function(t){var e=this.config.defaultAudioCodec||t.audioCodec;return this.audioCodecSwap&&e&&(this.log("Swapping audio codec"),e=-1!==e.indexOf("mp4a.40.5")?"mp4a.40.2":"mp4a.40.5"),e},r._loadBitrateTestFrag=function(t,e){var r=this;t.bitrateTest=!0,this._doFragLoad(t,e).then((function(i){var n=r.hls;if(i&&!r.fragContextChanged(t)){e.fragmentError=0,r.state=fi,r.startFragRequested=!1,r.bitrateTest=!1;var a=t.stats;a.parsing.start=a.parsing.end=a.buffering.start=a.buffering.end=self.performance.now(),n.trigger(S.FRAG_LOADED,i),t.bitrateTest=!1}}))},r._handleTransmuxComplete=function(t){var e,r="main",i=this.hls,n=t.remuxResult,a=t.chunkMeta,s=this.getCurrentContext(a);if(s){var o=s.frag,l=s.part,u=s.level,h=n.video,d=n.text,c=n.id3,f=n.initSegment,g=u.details,v=this.altAudio?void 0:n.audio;if(this.fragContextChanged(o))this.fragmentTracker.removeFragment(o);else{if(this.state=yi,f){if(null!=f&&f.tracks){var m=o.initSegment||o;this._bufferInitSegment(u,f.tracks,m,a),i.trigger(S.FRAG_PARSING_INIT_SEGMENT,{frag:m,id:r,tracks:f.tracks})}var p=f.initPTS,E=f.timescale;y(p)&&(this.initPTS[o.cc]={baseTime:p,timescale:E},i.trigger(S.INIT_PTS_FOUND,{frag:o,id:r,initPTS:p,timescale:E}))}if(h&&g&&"initSegment"!==o.sn){var T=g.fragments[o.sn-1-g.startSN],L=o.sn===g.startSN,A=!T||o.cc>T.cc;if(!1!==n.independent){var R=h.startPTS,k=h.endPTS,b=h.startDTS,D=h.endDTS;if(l)l.elementaryStreams[h.type]={startPTS:R,endPTS:k,startDTS:b,endDTS:D};else if(h.firstKeyFrame&&h.independent&&1===a.id&&!A&&(this.couldBacktrack=!0),h.dropped&&h.independent){var I=this.getMainFwdBufferInfo(),w=(I?I.end:this.getLoadPosition())+this.config.maxBufferHole,C=h.firstKeyFramePTS?h.firstKeyFramePTS:R;if(!L&&w<C-this.config.maxBufferHole&&!A)return void this.backtrack(o);A&&(o.gap=!0),o.setElementaryStreamInfo(h.type,o.start,k,o.start,D,!0)}else L&&R>2&&(o.gap=!0);o.setElementaryStreamInfo(h.type,R,k,b,D),this.backtrackFragment&&(this.backtrackFragment=o),this.bufferFragmentData(h,o,l,a,L||A)}else{if(!L&&!A)return void this.backtrack(o);o.gap=!0}}if(v){var _=v.startPTS,x=v.endPTS,P=v.startDTS,F=v.endDTS;l&&(l.elementaryStreams[O]={startPTS:_,endPTS:x,startDTS:P,endDTS:F}),o.setElementaryStreamInfo(O,_,x,P,F),this.bufferFragmentData(v,o,l,a)}if(g&&null!=c&&null!=(e=c.samples)&&e.length){var M={id:r,frag:o,details:g,samples:c.samples};i.trigger(S.FRAG_PARSING_METADATA,M)}if(g&&d){var N={id:r,frag:o,details:g,samples:d.samples};i.trigger(S.FRAG_PARSING_USERDATA,N)}}}else this.resetWhenMissingContext(a)},r._bufferInitSegment=function(t,e,r,i){var n=this;if(this.state===yi){this.audioOnly=!!e.audio&&!e.video,this.altAudio&&!this.audioOnly&&delete e.audio;var a=e.audio,s=e.video,o=e.audiovideo;if(a){var l=t.audioCodec,u=navigator.userAgent.toLowerCase();this.audioCodecSwitch&&(l&&(l=-1!==l.indexOf("mp4a.40.5")?"mp4a.40.2":"mp4a.40.5"),1!==a.metadata.channelCount&&-1===u.indexOf("firefox")&&(l="mp4a.40.5")),l&&-1!==l.indexOf("mp4a.40.5")&&-1!==u.indexOf("android")&&"audio/mpeg"!==a.container&&(l="mp4a.40.2",this.log("Android: force audio codec to "+l)),t.audioCodec&&t.audioCodec!==l&&this.log('Swapping manifest audio codec "'+t.audioCodec+'" for "'+l+'"'),a.levelCodec=l,a.id="main",this.log("Init audio buffer, container:"+a.container+", codecs[selected/level/parsed]=["+(l||"")+"/"+(t.audioCodec||"")+"/"+a.codec+"]")}s&&(s.levelCodec=t.videoCodec,s.id="main",this.log("Init video buffer, container:"+s.container+", codecs[level/parsed]=["+(t.videoCodec||"")+"/"+s.codec+"]")),o&&this.log("Init audiovideo buffer, container:"+o.container+", codecs[level/parsed]=["+t.codecs+"/"+o.codec+"]"),this.hls.trigger(S.BUFFER_CODECS,e),Object.keys(e).forEach((function(t){var a=e[t].initSegment;null!=a&&a.byteLength&&n.hls.trigger(S.BUFFER_APPENDING,{type:t,data:a,frag:r,part:null,chunkMeta:i,parent:r.type})})),this.tickImmediate()}},r.getMainFwdBufferInfo=function(){return this.getFwdBufferInfo(this.mediaBuffer?this.mediaBuffer:this.media,Ie)},r.backtrack=function(t){this.couldBacktrack=!0,this.backtrackFragment=t,this.resetTransmuxer(),this.flushBufferGap(t),this.fragmentTracker.removeFragment(t),this.fragPrevious=null,this.nextLoadPosition=t.start,this.state=fi},r.checkFragmentChanged=function(){var t=this.media,e=null;if(t&&t.readyState>1&&!1===t.seeking){var r=t.currentTime;if(zr.isBuffered(t,r)?e=this.getAppendedFrag(r):zr.isBuffered(t,r+.1)&&(e=this.getAppendedFrag(r+.1)),e){this.backtrackFragment=null;var i=this.fragPlaying,n=e.level;i&&e.sn===i.sn&&i.level===n||(this.fragPlaying=e,this.hls.trigger(S.FRAG_CHANGED,{frag:e}),i&&i.level===n||this.hls.trigger(S.LEVEL_SWITCHED,{level:n}))}}},s(e,[{key:"nextLevel",get:function(){var t=this.nextBufferedFrag;return t?t.level:-1}},{key:"currentFrag",get:function(){var t=this.media;return t?this.fragPlaying||this.getAppendedFrag(t.currentTime):null}},{key:"currentProgramDateTime",get:function(){var t=this.media;if(t){var e=t.currentTime,r=this.currentFrag;if(r&&y(e)&&y(r.programDateTime)){var i=r.programDateTime+1e3*(e-r.start);return new Date(i)}}return null}},{key:"currentLevel",get:function(){var t=this.currentFrag;return t?t.level:-1}},{key:"nextBufferedFrag",get:function(){var t=this.currentFrag;return t?this.followingBufferedFrag(t):null}},{key:"forceStartLoad",get:function(){return this._forceStartLoad}}]),e}(Ri),ao=function(){function t(e){void 0===e&&(e={}),this.config=void 0,this.userConfig=void 0,this.coreComponents=void 0,this.networkControllers=void 0,this.started=!1,this._emitter=new Mn,this._autoLevelCapping=-1,this._maxHdcpLevel=null,this.abrController=void 0,this.bufferController=void 0,this.capLevelController=void 0,this.latencyController=void 0,this.levelController=void 0,this.streamController=void 0,this.audioTrackController=void 0,this.subtitleTrackController=void 0,this.emeController=void 0,this.cmcdController=void 0,this._media=null,this.url=null,this.triggeringException=void 0,I(e.debug||!1,"Hls instance");var r=this.config=function(t,e){if((e.liveSyncDurationCount||e.liveMaxLatencyDurationCount)&&(e.liveSyncDuration||e.liveMaxLatencyDuration))throw new Error("Illegal hls.js config: don't mix up liveSyncDurationCount/liveMaxLatencyDurationCount and liveSyncDuration/liveMaxLatencyDuration");if(void 0!==e.liveMaxLatencyDurationCount&&(void 0===e.liveSyncDurationCount||e.liveMaxLatencyDurationCount<=e.liveSyncDurationCount))throw new Error('Illegal hls.js config: "liveMaxLatencyDurationCount" must be greater than "liveSyncDurationCount"');if(void 0!==e.liveMaxLatencyDuration&&(void 0===e.liveSyncDuration||e.liveMaxLatencyDuration<=e.liveSyncDuration))throw new Error('Illegal hls.js config: "liveMaxLatencyDuration" must be greater than "liveSyncDuration"');var r=Qs(t),n=["TimeOut","MaxRetry","RetryDelay","MaxRetryTimeout"];return["manifest","level","frag"].forEach((function(t){var i=("level"===t?"playlist":t)+"LoadPolicy",a=void 0===e[i],s=[];n.forEach((function(n){var o=t+"Loading"+n,l=e[o];if(void 0!==l&&a){s.push(o);var u=r[i].default;switch(e[i]={default:u},n){case"TimeOut":u.maxLoadTimeMs=l,u.maxTimeToFirstByteMs=l;break;case"MaxRetry":u.errorRetry.maxNumRetry=l,u.timeoutRetry.maxNumRetry=l;break;case"RetryDelay":u.errorRetry.retryDelayMs=l,u.timeoutRetry.retryDelayMs=l;break;case"MaxRetryTimeout":u.errorRetry.maxRetryDelayMs=l,u.timeoutRetry.maxRetryDelayMs=l}}})),s.length&&w.warn('hls.js config: "'+s.join('", "')+'" setting(s) are deprecated, use "'+i+'": '+JSON.stringify(e[i]))})),i(i({},r),e)}(t.DefaultConfig,e);this.userConfig=e,r.progressive&&Js(r);var n=r.abrController,a=r.bufferController,s=r.capLevelController,o=r.errorController,l=r.fpsController,u=new o(this),h=this.abrController=new n(this),d=this.bufferController=new a(this),c=this.capLevelController=new s(this),f=new l(this),g=new Pe(this),v=new je(this),m=r.contentSteeringController,p=m?new m(this):null,y=this.levelController=new $s(this,p),E=new Wr(this),T=new to(this.config),L=this.streamController=new no(this,E,T);c.setStreamController(L),f.setStreamController(L);var A=[g,y,L];p&&A.splice(1,0,p),this.networkControllers=A;var R=[h,d,c,f,v,E];this.audioTrackController=this.createController(r.audioTrackController,A);var k=r.audioStreamController;k&&A.push(new k(this,E,T)),this.subtitleTrackController=this.createController(r.subtitleTrackController,A);var b=r.subtitleStreamController;b&&A.push(new b(this,E,T)),this.createController(r.timelineController,R),T.emeController=this.emeController=this.createController(r.emeController,R),this.cmcdController=this.createController(r.cmcdController,R),this.latencyController=this.createController(qe,R),this.coreComponents=R,A.push(u);var D=u.onErrorOut;"function"==typeof D&&this.on(S.ERROR,D,u)}t.isMSESupported=function(){return ro()},t.isSupported=function(){return function(){if(!ro())return!1;var t=te();return"function"==typeof(null==t?void 0:t.isTypeSupported)&&(["avc1.42E01E,mp4a.40.2","av01.0.01M.08","vp09.00.50.08"].some((function(e){return t.isTypeSupported(ne(e,"video"))}))||["mp4a.40.2","fLaC"].some((function(e){return t.isTypeSupported(ne(e,"audio"))})))}()},t.getMediaSource=function(){return te()};var e=t.prototype;return e.createController=function(t,e){if(t){var r=new t(this);return e&&e.push(r),r}return null},e.on=function(t,e,r){void 0===r&&(r=this),this._emitter.on(t,e,r)},e.once=function(t,e,r){void 0===r&&(r=this),this._emitter.once(t,e,r)},e.removeAllListeners=function(t){this._emitter.removeAllListeners(t)},e.off=function(t,e,r,i){void 0===r&&(r=this),this._emitter.off(t,e,r,i)},e.listeners=function(t){return this._emitter.listeners(t)},e.emit=function(t,e,r){return this._emitter.emit(t,e,r)},e.trigger=function(t,e){if(this.config.debug)return this.emit(t,t,e);try{return this.emit(t,t,e)}catch(e){if(w.error("An internal error happened while handling event "+t+'. Error message: "'+e.message+'". Here is a stacktrace:',e),!this.triggeringException){this.triggeringException=!0;var r=t===S.ERROR;this.trigger(S.ERROR,{type:L.OTHER_ERROR,details:A.INTERNAL_EXCEPTION,fatal:r,event:t,error:e}),this.triggeringException=!1}}return!1},e.listenerCount=function(t){return this._emitter.listenerCount(t)},e.destroy=function(){w.log("destroy"),this.trigger(S.DESTROYING,void 0),this.detachMedia(),this.removeAllListeners(),this._autoLevelCapping=-1,this.url=null,this.networkControllers.forEach((function(t){return t.destroy()})),this.networkControllers.length=0,this.coreComponents.forEach((function(t){return t.destroy()})),this.coreComponents.length=0;var t=this.config;t.xhrSetup=t.fetchSetup=void 0,this.userConfig=null},e.attachMedia=function(t){w.log("attachMedia"),this._media=t,this.trigger(S.MEDIA_ATTACHING,{media:t})},e.detachMedia=function(){w.log("detachMedia"),this.trigger(S.MEDIA_DETACHING,void 0),this._media=null},e.loadSource=function(t){this.stopLoad();var e=this.media,r=this.url,i=this.url=p.buildAbsoluteURL(self.location.href,t,{alwaysNormalize:!0});this._autoLevelCapping=-1,this._maxHdcpLevel=null,w.log("loadSource:"+i),e&&r&&(r!==i||this.bufferController.hasSourceTypes())&&(this.detachMedia(),this.attachMedia(e)),this.trigger(S.MANIFEST_LOADING,{url:t})},e.startLoad=function(t){void 0===t&&(t=-1),w.log("startLoad("+t+")"),this.started=!0,this.networkControllers.forEach((function(e){e.startLoad(t)}))},e.stopLoad=function(){w.log("stopLoad"),this.started=!1,this.networkControllers.forEach((function(t){t.stopLoad()}))},e.resumeBuffering=function(){this.started&&this.networkControllers.forEach((function(t){"fragmentLoader"in t&&t.startLoad(-1)}))},e.pauseBuffering=function(){this.networkControllers.forEach((function(t){"fragmentLoader"in t&&t.stopLoad()}))},e.swapAudioCodec=function(){w.log("swapAudioCodec"),this.streamController.swapAudioCodec()},e.recoverMediaError=function(){w.log("recoverMediaError");var t=this._media;this.detachMedia(),t&&this.attachMedia(t)},e.removeLevel=function(t){this.levelController.removeLevel(t)},e.setAudioOption=function(t){var e;return null==(e=this.audioTrackController)?void 0:e.setAudioOption(t)},e.setSubtitleOption=function(t){var e;return null==(e=this.subtitleTrackController)||e.setSubtitleOption(t),null},s(t,[{key:"levels",get:function(){var t=this.levelController.levels;return t||[]}},{key:"currentLevel",get:function(){return this.streamController.currentLevel},set:function(t){w.log("set currentLevel:"+t),this.levelController.manualLevel=t,this.streamController.immediateLevelSwitch()}},{key:"nextLevel",get:function(){return this.streamController.nextLevel},set:function(t){w.log("set nextLevel:"+t),this.levelController.manualLevel=t,this.streamController.nextLevelSwitch()}},{key:"loadLevel",get:function(){return this.levelController.level},set:function(t){w.log("set loadLevel:"+t),this.levelController.manualLevel=t}},{key:"nextLoadLevel",get:function(){return this.levelController.nextLoadLevel},set:function(t){this.levelController.nextLoadLevel=t}},{key:"firstLevel",get:function(){return Math.max(this.levelController.firstLevel,this.minAutoLevel)},set:function(t){w.log("set firstLevel:"+t),this.levelController.firstLevel=t}},{key:"startLevel",get:function(){var t=this.levelController.startLevel;return-1===t&&this.abrController.forcedAutoLevel>-1?this.abrController.forcedAutoLevel:t},set:function(t){w.log("set startLevel:"+t),-1!==t&&(t=Math.max(t,this.minAutoLevel)),this.levelController.startLevel=t}},{key:"capLevelToPlayerSize",get:function(){return this.config.capLevelToPlayerSize},set:function(t){var e=!!t;e!==this.config.capLevelToPlayerSize&&(e?this.capLevelController.startCapping():(this.capLevelController.stopCapping(),this.autoLevelCapping=-1,this.streamController.nextLevelSwitch()),this.config.capLevelToPlayerSize=e)}},{key:"autoLevelCapping",get:function(){return this._autoLevelCapping},set:function(t){this._autoLevelCapping!==t&&(w.log("set autoLevelCapping:"+t),this._autoLevelCapping=t,this.levelController.checkMaxAutoUpdated())}},{key:"bandwidthEstimate",get:function(){var t=this.abrController.bwEstimator;return t?t.getEstimate():NaN},set:function(t){this.abrController.resetEstimator(t)}},{key:"ttfbEstimate",get:function(){var t=this.abrController.bwEstimator;return t?t.getEstimateTTFB():NaN}},{key:"maxHdcpLevel",get:function(){return this._maxHdcpLevel},set:function(t){(function(t){return Xe.indexOf(t)>-1})(t)&&this._maxHdcpLevel!==t&&(this._maxHdcpLevel=t,this.levelController.checkMaxAutoUpdated())}},{key:"autoLevelEnabled",get:function(){return-1===this.levelController.manualLevel}},{key:"manualLevel",get:function(){return this.levelController.manualLevel}},{key:"minAutoLevel",get:function(){var t=this.levels,e=this.config.minAutoBitrate;if(!t)return 0;for(var r=t.length,i=0;i<r;i++)if(t[i].maxBitrate>=e)return i;return 0}},{key:"maxAutoLevel",get:function(){var t,e=this.levels,r=this.autoLevelCapping,i=this.maxHdcpLevel;if(t=-1===r&&null!=e&&e.length?e.length-1:r,i)for(var n=t;n--;){var a=e[n].attrs["HDCP-LEVEL"];if(a&&a<=i)return n}return t}},{key:"firstAutoLevel",get:function(){return this.abrController.firstAutoLevel}},{key:"nextAutoLevel",get:function(){return this.abrController.nextAutoLevel},set:function(t){this.abrController.nextAutoLevel=t}},{key:"playingDate",get:function(){return this.streamController.currentProgramDateTime}},{key:"mainForwardBufferInfo",get:function(){return this.streamController.getMainFwdBufferInfo()}},{key:"allAudioTracks",get:function(){var t=this.audioTrackController;return t?t.allAudioTracks:[]}},{key:"audioTracks",get:function(){var t=this.audioTrackController;return t?t.audioTracks:[]}},{key:"audioTrack",get:function(){var t=this.audioTrackController;return t?t.audioTrack:-1},set:function(t){var e=this.audioTrackController;e&&(e.audioTrack=t)}},{key:"allSubtitleTracks",get:function(){var t=this.subtitleTrackController;return t?t.allSubtitleTracks:[]}},{key:"subtitleTracks",get:function(){var t=this.subtitleTrackController;return t?t.subtitleTracks:[]}},{key:"subtitleTrack",get:function(){var t=this.subtitleTrackController;return t?t.subtitleTrack:-1},set:function(t){var e=this.subtitleTrackController;e&&(e.subtitleTrack=t)}},{key:"media",get:function(){return this._media}},{key:"subtitleDisplay",get:function(){var t=this.subtitleTrackController;return!!t&&t.subtitleDisplay},set:function(t){var e=this.subtitleTrackController;e&&(e.subtitleDisplay=t)}},{key:"lowLatencyMode",get:function(){return this.config.lowLatencyMode},set:function(t){this.config.lowLatencyMode=t}},{key:"liveSyncPosition",get:function(){return this.latencyController.liveSyncPosition}},{key:"latency",get:function(){return this.latencyController.latency}},{key:"maxLatency",get:function(){return this.latencyController.maxLatency}},{key:"targetLatency",get:function(){return this.latencyController.targetLatency}},{key:"drift",get:function(){return this.latencyController.drift}},{key:"forceStartLoad",get:function(){return this.streamController.forceStartLoad}}],[{key:"version",get:function(){return"1.5.1"}},{key:"Events",get:function(){return S}},{key:"ErrorTypes",get:function(){return L}},{key:"ErrorDetails",get:function(){return A}},{key:"DefaultConfig",get:function(){return t.defaultConfig?t.defaultConfig:zs},set:function(e){t.defaultConfig=e}}]),t}();return ao.defaultConfig=void 0,ao},"object"==typeof exports&&"undefined"!=typeof module?module.exports=i():"function"==typeof define&&define.amd?define(i):(r="undefined"!=typeof globalThis?globalThis:r||self).Hls=i()}(!1); +// @license-end diff --git a/src/formatters.nim b/src/formatters.nim index 3630917..8267f23 100644 --- a/src/formatters.nim +++ b/src/formatters.nim @@ -82,6 +82,8 @@ proc proxifyVideo*(manifest: string; proxy: bool): string = for line in manifest.splitLines: let url = if line.startsWith("#EXT-X-MAP:URI"): line[16 .. ^2] + elif line.startsWith("#EXT-X-MEDIA") and "URI=" in line: + line[line.find("URI=") + 5 .. -1 + line.find("\"", start= 5 + line.find("URI="))] else: line if url.startsWith('/'): let path = "https://video.twimg.com" & url diff --git a/src/utils.nim b/src/utils.nim index ede9aed..c96a6dd 100644 --- a/src/utils.nim +++ b/src/utils.nim @@ -31,9 +31,7 @@ proc getHmac*(data: string): string = proc getVidUrl*(link: string): string = if link.len == 0: return - let - link = link.replace("cmaf", "fmp4") - sig = getHmac(link) + let sig = getHmac(link) if base64Media: &"/video/enc/{sig}/{encode(link, safe=true)}" else: diff --git a/src/views/general.nim b/src/views/general.nim index 87d30f2..5ba40a3 100644 --- a/src/views/general.nim +++ b/src/views/general.nim @@ -73,7 +73,7 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc=""; link(rel="alternate", type="application/rss+xml", href=rss, title="RSS feed") if prefs.hlsPlayback: - script(src="/js/hls.light.min.js", `defer`="") + script(src="/js/hls.min.js", `defer`="") script(src="/js/hlsPlayback.js", `defer`="") if prefs.infiniteScroll: From c6edec04901d0a37799499ed4c6921db640fb5a4 Mon Sep 17 00:00:00 2001 From: somini <somini@users.noreply.github.com> Date: Mon, 26 Feb 2024 03:08:25 +0000 Subject: [PATCH 190/409] Update auth.nim (#1164) Avoid expiring the tokens for now. See: - https://github.com/zedeus/nitter/issues/983#issuecomment-1923046398 - https://github.com/zedeus/nitter/issues/1155#issuecomment-1917167072 Thanks @cmj --- src/auth.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auth.nim b/src/auth.nim index b288c50..de1b1d8 100644 --- a/src/auth.nim +++ b/src/auth.nim @@ -202,7 +202,7 @@ proc initAccountPool*(cfg: Config; path: string) = quit 1 let accountsPrePurge = accountPool.len - accountPool.keepItIf(not it.hasExpired) + #accountPool.keepItIf(not it.hasExpired) log "Successfully added ", accountPool.len, " valid accounts." if accountsPrePurge > accountPool.len: From 19569bb19f6a725789e45e58bb86fcd7806da8a7 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Wed, 5 Feb 2025 00:25:50 +0100 Subject: [PATCH 191/409] Replace old v1 photo rail API with gql --- src/api.nim | 12 +++++------ src/apiutils.nim | 19 ----------------- src/auth.nim | 1 - src/consts.nim | 20 +----------------- src/parser.nim | 45 +++++++++++++++++++++++++---------------- src/redis_cache.nim | 12 +++++------ src/routes/timeline.nim | 2 +- src/types.nim | 1 - 8 files changed, 42 insertions(+), 70 deletions(-) diff --git a/src/api.nim b/src/api.nim index d6a4564..479cb3d 100644 --- a/src/api.nim +++ b/src/api.nim @@ -136,13 +136,13 @@ proc getGraphUserSearch*(query: Query; after=""): Future[Result[User]] {.async.} result = parseGraphSearch[User](await fetch(url, Api.search), after) result.query = query -proc getPhotoRail*(name: string): Future[PhotoRail] {.async.} = - if name.len == 0: return +proc getPhotoRail*(id: string): Future[PhotoRail] {.async.} = + if id.len == 0: return let - ps = genParams({"screen_name": name, "trim_user": "true"}, - count="18", ext=false) - url = photoRail ? ps - result = parsePhotoRail(await fetch(url, Api.photoRail)) + variables = userTweetsVariables % [id, ""] + params = {"variables": variables, "features": gqlFeatures} + url = graphUserMedia ? params + result = parseGraphPhotoRail(await fetch(url, Api.userMedia)) proc resolve*(url: string; prefs: Prefs): Future[string] {.async.} = let client = newAsyncHttpClient(maxRedirects=0) diff --git a/src/apiutils.nim b/src/apiutils.nim index 1ff05eb..75e0e4c 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -10,25 +10,6 @@ const var pool: HttpPool -proc genParams*(pars: openArray[(string, string)] = @[]; cursor=""; - count="20"; ext=true): seq[(string, string)] = - result = timelineParams - for p in pars: - result &= p - if ext: - result &= ("include_ext_alt_text", "1") - result &= ("include_ext_media_stats", "1") - result &= ("include_ext_media_availability", "1") - if count.len > 0: - result &= ("count", count) - if cursor.len > 0: - # The raw cursor often has plus signs, which sometimes get turned into spaces, - # so we need to turn them back into a plus - if " " in cursor: - result &= ("cursor", cursor.replace(" ", "+")) - else: - result &= ("cursor", cursor) - proc getOauthHeader(url, oauthToken, oauthTokenSecret: string): string = let encodedUrl = url.replace(",", "%2C").replace("+", "%20") diff --git a/src/auth.nim b/src/auth.nim index de1b1d8..a15766b 100644 --- a/src/auth.nim +++ b/src/auth.nim @@ -10,7 +10,6 @@ const apiMaxReqs: Table[Api, int] = { Api.search: 50, Api.tweetDetail: 150, - Api.photoRail: 180, Api.userTweets: 500, Api.userTweetsAndReplies: 500, Api.userMedia: 500, diff --git a/src/consts.nim b/src/consts.nim index e1c35e6..3abd1bc 100644 --- a/src/consts.nim +++ b/src/consts.nim @@ -1,5 +1,5 @@ # SPDX-License-Identifier: AGPL-3.0-only -import uri, sequtils, strutils +import uri, strutils const consumerKey* = "3nVuSoBZnx6U4vzUxf5w" @@ -8,8 +8,6 @@ const api = parseUri("https://api.twitter.com") activate* = $(api / "1.1/guest/activate.json") - photoRail* = api / "1.1/statuses/media_timeline.json" - graphql = api / "graphql" graphUser* = graphql / "u7wQyGi6oExe8_TRWGMq4Q/UserResultByScreenNameQuery" graphUserById* = graphql / "oPppcargziU1uDQHAUmH-A/UserResultByIdQuery" @@ -24,22 +22,6 @@ const graphListMembers* = graphql / "P4NpVZDqUD_7MEM84L-8nw/ListMembers" graphListTweets* = graphql / "BbGLL1ZfMibdFNWlk7a0Pw/ListTimeline" - timelineParams* = { - "include_can_media_tag": "1", - "include_cards": "1", - "include_entities": "1", - "include_profile_interstitial_type": "0", - "include_quote_count": "0", - "include_reply_count": "0", - "include_user_entities": "0", - "include_ext_reply_count": "0", - "include_ext_media_color": "0", - "cards_platform": "Web-13", - "tweet_mode": "extended", - "send_error_codes": "1", - "simple_quoted_tweet": "1" - }.toSeq - gqlFeatures* = """{ "android_graphql_skip_api_media_color_palette": false, "blue_business_profile_image_shape_enabled": false, diff --git a/src/parser.nim b/src/parser.nim index ec856a6..b8812d2 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -289,23 +289,6 @@ proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet = result.text.removeSuffix(" Learn more.") result.available = false -proc parsePhotoRail*(js: JsonNode): PhotoRail = - with error, js{"error"}: - if error.getStr == "Not authorized.": - return - - for tweet in js: - let - t = parseTweet(tweet, js{"tweet_card"}) - url = if t.photos.len > 0: t.photos[0] - elif t.video.isSome: get(t.video).thumb - elif t.gif.isSome: get(t.gif).thumb - elif t.card.isSome: get(t.card).image - else: "" - - if url.len == 0: continue - result.add GalleryPhoto(url: url, tweetId: $t.id) - proc parseGraphTweet(js: JsonNode; isLegacy=false): Tweet = if js.kind == JNull: return Tweet() @@ -445,6 +428,34 @@ proc parseGraphTimeline*(js: JsonNode; root: string; after=""): Profile = tweet.id = parseBiggestInt(entryId) result.pinned = some tweet +proc parseGraphPhotoRail*(js: JsonNode): PhotoRail = + result = @[] + + let instructions = + ? js{"data", "user_result", "result", "timeline_response", "timeline", "instructions"} + + for i in instructions: + if i{"__typename"}.getStr == "TimelineAddEntries": + for e in i{"entries"}: + let entryId = e{"entryId"}.getStr + if entryId.startsWith("tweet"): + with tweetResult, e{"content", "content", "tweetResult", "result"}: + let t = parseGraphTweet(tweetResult, false) + if not t.available: + t.id = parseBiggestInt(entryId.getId()) + + let url = + if t.photos.len > 0: t.photos[0] + elif t.video.isSome: get(t.video).thumb + elif t.gif.isSome: get(t.gif).thumb + elif t.card.isSome: get(t.card).image + else: "" + + result.add GalleryPhoto(url: url, tweetId: $t.id) + + if result.len == 16: + break + proc parseGraphSearch*[T: User | Tweets](js: JsonNode; after=""): Result[T] = result = Result[T](beginning: after.len == 0) diff --git a/src/redis_cache.nim b/src/redis_cache.nim index 1d77cca..559d299 100644 --- a/src/redis_cache.nim +++ b/src/redis_cache.nim @@ -86,7 +86,7 @@ proc cache*(data: List) {.async.} = await setEx(data.listKey, listCacheTime, compress(toFlatty(data))) proc cache*(data: PhotoRail; name: string) {.async.} = - await setEx("pr:" & toLower(name), baseCacheTime * 2, compress(toFlatty(data))) + await setEx("pr2:" & toLower(name), baseCacheTime * 2, compress(toFlatty(data))) proc cache*(data: User) {.async.} = if data.username.len == 0: return @@ -158,14 +158,14 @@ proc getCachedUsername*(userId: string): Future[string] {.async.} = # if not result.isNil: # await cache(result) -proc getCachedPhotoRail*(name: string): Future[PhotoRail] {.async.} = - if name.len == 0: return - let rail = await get("pr:" & toLower(name)) +proc getCachedPhotoRail*(id: string): Future[PhotoRail] {.async.} = + if id.len == 0: return + let rail = await get("pr2:" & toLower(id)) if rail != redisNil: rail.deserialize(PhotoRail) else: - result = await getPhotoRail(name) - await cache(result, name) + result = await getPhotoRail(id) + await cache(result, id) proc getCachedList*(username=""; slug=""; id=""): Future[List] {.async.} = let list = if id.len == 0: redisNil diff --git a/src/routes/timeline.nim b/src/routes/timeline.nim index 3568ab7..8cd1fd7 100644 --- a/src/routes/timeline.nim +++ b/src/routes/timeline.nim @@ -47,7 +47,7 @@ proc fetchProfile*(after: string; query: Query; skipRail=false; let rail = skipIf(skipRail or query.kind == media, @[]): - getCachedPhotoRail(name) + getCachedPhotoRail(userId) user = getCachedUser(name) diff --git a/src/types.nim b/src/types.nim index ddbebdf..1b9189b 100644 --- a/src/types.nim +++ b/src/types.nim @@ -15,7 +15,6 @@ type Api* {.pure.} = enum tweetDetail tweetResult - photoRail search list listBySlug From 28d3ed7d9fad2302bf357f099c8b8951631dee8c Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Wed, 5 Feb 2025 00:32:55 +0100 Subject: [PATCH 192/409] Raise NoAccountsError when all accounts limited --- src/auth.nim | 5 ++++- src/nitter.nim | 5 +++++ src/types.nim | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/auth.nim b/src/auth.nim index a15766b..20e136f 100644 --- a/src/auth.nim +++ b/src/auth.nim @@ -125,6 +125,9 @@ proc getAccountPoolDebug*(): JsonNode = proc rateLimitError*(): ref RateLimitError = newException(RateLimitError, "rate limited") +proc noAccountsError*(): ref NoAccountsError = + newException(NoAccountsError, "no accounts available") + proc isLimited(account: GuestAccount; api: Api): bool = if account.isNil: return true @@ -165,7 +168,7 @@ proc getGuestAccount*(api: Api): Future[GuestAccount] {.async.} = inc result.pending else: log "no accounts available for API: ", api - raise rateLimitError() + raise noAccountsError() proc setLimited*(account: GuestAccount; api: Api) = account.apis[api].limited = true diff --git a/src/nitter.nim b/src/nitter.nim index dfc1dfd..958dc0b 100644 --- a/src/nitter.nim +++ b/src/nitter.nim @@ -97,6 +97,11 @@ routes: resp Http429, showError( &"Instance has been rate limited.<br>Use {link} or try again later.", cfg) + error NoAccountsError: + const link = a("another instance", href = instancesUrl) + resp Http429, showError( + &"Instance has no available accounts.<br>Use {link} or try again later.", cfg) + extend rss, "" extend status, "" extend search, "" diff --git a/src/types.nim b/src/types.nim index 1b9189b..d1981d7 100644 --- a/src/types.nim +++ b/src/types.nim @@ -6,6 +6,7 @@ genPrefsType() type RateLimitError* = object of CatchableError + NoAccountsError* = object of CatchableError InternalError* = object of CatchableError BadClientError* = object of CatchableError From 1aa9b0dba67145fa052422de93e249df3d92b468 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Wed, 5 Feb 2025 00:34:50 +0100 Subject: [PATCH 193/409] Move limited flag to be account-level --- src/auth.nim | 49 ++++++++++++++++++++----------------------------- src/types.nim | 4 ++-- 2 files changed, 22 insertions(+), 31 deletions(-) diff --git a/src/auth.nim b/src/auth.nim index 20e136f..66656d3 100644 --- a/src/auth.nim +++ b/src/auth.nim @@ -32,13 +32,6 @@ template log(str: varargs[string, `$`]) = proc snowflakeToEpoch(flake: int64): int64 = int64(((flake shr 22) + 1288834974657) div 1000) -proc hasExpired(account: GuestAccount): bool = - let - created = snowflakeToEpoch(account.id) - now = epochTime().int64 - daysOld = int(now - created) div dayInSeconds - return daysOld > 30 - proc getAccountPoolHealth*(): JsonNode = let now = epochTime().int @@ -58,14 +51,14 @@ proc getAccountPoolHealth*(): JsonNode = oldest = created average += created + if account.limited: + limited.incl account.id + for api in account.apis.keys: let apiStatus = account.apis[api] reqs = apiMaxReqs[api] - apiStatus.remaining - if apiStatus.limited: - limited.incl account.id - # no requests made with this account and endpoint since the limit reset if apiStatus.reset < now: continue @@ -103,6 +96,9 @@ proc getAccountPoolDebug*(): JsonNode = "pending": account.pending, } + if account.limited: + accountJson["limited"] = %true + for api in account.apis.keys: let apiStatus = account.apis[api] @@ -110,13 +106,11 @@ proc getAccountPoolDebug*(): JsonNode = if apiStatus.reset > now.int: obj["remaining"] = %apiStatus.remaining + obj["reset"] = %apiStatus.reset - if "remaining" notin obj and not apiStatus.limited: + if "remaining" notin obj: continue - if apiStatus.limited: - obj["limited"] = %true - accountJson{"apis", $api} = obj list[$account.id] = accountJson @@ -132,14 +126,16 @@ proc isLimited(account: GuestAccount; api: Api): bool = if account.isNil: return true + if account.limited and api != Api.userTweets: + if (epochTime().int - account.limitedAt) > dayInSeconds: + account.limited = false + log "resetting limit: ", account.id + else: + return false + if api in account.apis: let limit = account.apis[api] - - if limit.limited and (epochTime().int - limit.limitedAt) > dayInSeconds: - account.apis[api].limited = false - log "resetting limit, api: ", api, ", id: ", account.id - - return limit.limited or (limit.remaining <= 10 and limit.reset > epochTime().int) + return limit.remaining <= 10 and limit.reset > epochTime().int else: return false @@ -148,7 +144,7 @@ proc isReady(account: GuestAccount; api: Api): bool = proc invalidate*(account: var GuestAccount) = if account.isNil: return - log "invalidating expired account: ", account.id + log "invalidating: ", account.id # TODO: This isn't sufficient, but it works for now let idx = accountPool.find(account) @@ -171,9 +167,9 @@ proc getGuestAccount*(api: Api): Future[GuestAccount] {.async.} = raise noAccountsError() proc setLimited*(account: GuestAccount; api: Api) = - account.apis[api].limited = true - account.apis[api].limitedAt = epochTime().int - log "rate limited, api: ", api, ", reqs left: ", account.apis[api].remaining, ", id: ", account.id + account.limited = true + account.limitedAt = epochTime().int + log "rate limited by api: ", api, ", reqs left: ", account.apis[api].remaining, ", id: ", account.id proc setRateLimit*(account: GuestAccount; api: Api; remaining, reset: int) = # avoid undefined behavior in race conditions @@ -203,9 +199,4 @@ proc initAccountPool*(cfg: Config; path: string) = echo "[accounts] ERROR: ", path, " not found. This file is required to authenticate API requests." quit 1 - let accountsPrePurge = accountPool.len - #accountPool.keepItIf(not it.hasExpired) - log "Successfully added ", accountPool.len, " valid accounts." - if accountsPrePurge > accountPool.len: - log "Purged ", accountsPrePurge - accountPool.len, " expired accounts." diff --git a/src/types.nim b/src/types.nim index d1981d7..f8cdda7 100644 --- a/src/types.nim +++ b/src/types.nim @@ -30,14 +30,14 @@ type RateLimit* = object remaining*: int reset*: int - limited*: bool - limitedAt*: int GuestAccount* = ref object id*: int64 oauthToken*: string oauthSecret*: string pending*: int + limited*: bool + limitedAt*: int apis*: Table[Api, RateLimit] Error* = enum From 2e13d7b57c4506b8545845f3d3ec847a8c6ca82b Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Wed, 5 Feb 2025 00:35:39 +0100 Subject: [PATCH 194/409] Capture "account locked" API error --- src/apiutils.nim | 9 +++++---- src/types.nim | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/apiutils.nim b/src/apiutils.nim index 75e0e4c..03de5f1 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -76,8 +76,8 @@ template fetchImpl(result, fetchBody) {.dirty.} = if result.startsWith("{\"errors"): let errors = result.fromJson(Errors) - if errors in {expiredToken, badToken}: - echo "fetch error: ", errors + echo "Fetch error, API: ", api, ", errors: ", errors + if errors in {expiredToken, badToken, locked}: invalidate(account) raise rateLimitError() elif errors in {rateLimited}: @@ -93,6 +93,7 @@ template fetchImpl(result, fetchBody) {.dirty.} = fetchBody if resp.status == $Http400: + echo "ERROR 400, ", api, ": ", result raise newException(InternalError, $url) except InternalError as e: raise e @@ -125,8 +126,8 @@ proc fetch*(url: Uri; api: Api): Future[JsonNode] {.async.} = result = newJNull() let error = result.getError - if error in {expiredToken, badToken}: - echo "fetchBody error: ", error + echo "Fetch error, API: ", api, ", error: ", error + if error in {expiredToken, badToken, locked}: invalidate(account) raise rateLimitError() diff --git a/src/types.nim b/src/types.nim index f8cdda7..685fbad 100644 --- a/src/types.nim +++ b/src/types.nim @@ -57,6 +57,7 @@ type tweetNotAuthorized = 179 forbidden = 200 badToken = 239 + locked = 326 noCsrf = 353 tweetUnavailable = 421 tweetCensored = 422 From e38276a63863d8c3ceaf27d593f6542769bfad36 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Wed, 5 Feb 2025 00:40:19 +0100 Subject: [PATCH 195/409] Update authority header --- src/apiutils.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apiutils.nim b/src/apiutils.nim index 03de5f1..d7842b1 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -35,7 +35,7 @@ proc genHeaders*(url, oauthToken, oauthTokenSecret: string): HttpHeaders = "authorization": header, "content-type": "application/json", "x-twitter-active-user": "yes", - "authority": "api.twitter.com", + "authority": "api.x.com", "accept-encoding": "gzip", "accept-language": "en-US,en;q=0.9", "accept": "*/*", From 5b6dae5228cb58b81df8c54a1c51cfc634e87fe1 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Wed, 5 Feb 2025 01:16:52 +0100 Subject: [PATCH 196/409] Add regex for x.com links --- src/formatters.nim | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/formatters.nim b/src/formatters.nim index 8267f23..7428814 100644 --- a/src/formatters.nim +++ b/src/formatters.nim @@ -11,6 +11,8 @@ const let twRegex = re"(?<=(?<!\S)https:\/\/|(?<=\s))(www\.|mobile\.)?twitter\.com" twLinkRegex = re"""<a href="https:\/\/twitter.com([^"]+)">twitter\.com(\S+)</a>""" + xRegex = re"(?<=(?<!\S)https:\/\/|(?<=\s))(www\.|mobile\.)?x\.com" + xLinkRegex = re"""<a href="https:\/\/x.com([^"]+)">x\.com(\S+)</a>""" ytRegex = re(r"([A-z.]+\.)?youtu(be\.com|\.be)", {reStudy, reIgnoreCase}) @@ -56,12 +58,18 @@ proc replaceUrls*(body: string; prefs: Prefs; absolute=""): string = if prefs.replaceYouTube.len > 0 and "youtu" in result: result = result.replace(ytRegex, prefs.replaceYouTube) - if prefs.replaceTwitter.len > 0 and ("twitter.com" in body or tco in body): - result = result.replace(tco, https & prefs.replaceTwitter & "/t.co") - result = result.replace(cards, prefs.replaceTwitter & "/cards") - result = result.replace(twRegex, prefs.replaceTwitter) - result = result.replacef(twLinkRegex, a( - prefs.replaceTwitter & "$2", href = https & prefs.replaceTwitter & "$1")) + if prefs.replaceTwitter.len > 0: + if tco in result: + result = result.replace(tco, https & prefs.replaceTwitter & "/t.co") + if "x.com" in result: + result = result.replace(xRegex, prefs.replaceTwitter) + result = result.replacef(xLinkRegex, a( + prefs.replaceTwitter & "$2", href = https & prefs.replaceTwitter & "$1")) + if "twitter.com" in result: + result = result.replace(cards, prefs.replaceTwitter & "/cards") + result = result.replace(twRegex, prefs.replaceTwitter) + result = result.replacef(twLinkRegex, a( + prefs.replaceTwitter & "$2", href = https & prefs.replaceTwitter & "$1")) if prefs.replaceReddit.len > 0 and ("reddit.com" in result or "redd.it" in result): result = result.replace(rdShortRegex, prefs.replaceReddit & "/comments/") From 81764ea0f802677d0a4cec4025b721d601e2751f Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Wed, 5 Feb 2025 01:19:06 +0100 Subject: [PATCH 197/409] Update endpoint versions, switch tweet endpoint --- src/auth.nim | 2 +- src/consts.nim | 46 ++++++++++++++++++++++++++++++---------------- src/parser.nim | 39 ++++++++++++++++++++++----------------- 3 files changed, 53 insertions(+), 34 deletions(-) diff --git a/src/auth.nim b/src/auth.nim index 66656d3..fe265c9 100644 --- a/src/auth.nim +++ b/src/auth.nim @@ -9,7 +9,7 @@ const dayInSeconds = 24 * 60 * 60 apiMaxReqs: Table[Api, int] = { Api.search: 50, - Api.tweetDetail: 150, + Api.tweetDetail: 500, Api.userTweets: 500, Api.userTweetsAndReplies: 500, Api.userMedia: 500, diff --git a/src/consts.nim b/src/consts.nim index 3abd1bc..7c67706 100644 --- a/src/consts.nim +++ b/src/consts.nim @@ -5,22 +5,20 @@ const consumerKey* = "3nVuSoBZnx6U4vzUxf5w" consumerSecret* = "Bcs59EFbbsdF6Sl9Ng71smgStWEGwXXKSjYvPVt7qys" - api = parseUri("https://api.twitter.com") - activate* = $(api / "1.1/guest/activate.json") + gql = parseUri("https://api.x.com") / "graphql" - graphql = api / "graphql" - graphUser* = graphql / "u7wQyGi6oExe8_TRWGMq4Q/UserResultByScreenNameQuery" - graphUserById* = graphql / "oPppcargziU1uDQHAUmH-A/UserResultByIdQuery" - graphUserTweets* = graphql / "3JNH4e9dq1BifLxAa3UMWg/UserWithProfileTweetsQueryV2" - graphUserTweetsAndReplies* = graphql / "8IS8MaO-2EN6GZZZb8jF0g/UserWithProfileTweetsAndRepliesQueryV2" - graphUserMedia* = graphql / "PDfFf8hGeJvUCiTyWtw4wQ/MediaTimelineV2" - graphTweet* = graphql / "q94uRCEn65LZThakYcPT6g/TweetDetail" - graphTweetResult* = graphql / "sITyJdhRPpvpEjg4waUmTA/TweetResultByIdQuery" - graphSearchTimeline* = graphql / "gkjsKepM6gl_HmFWoWKfgg/SearchTimeline" - graphListById* = graphql / "iTpgCtbdxrsJfyx0cFjHqg/ListByRestId" - graphListBySlug* = graphql / "-kmqNvm5Y-cVrfvBy6docg/ListBySlug" - graphListMembers* = graphql / "P4NpVZDqUD_7MEM84L-8nw/ListMembers" - graphListTweets* = graphql / "BbGLL1ZfMibdFNWlk7a0Pw/ListTimeline" + graphUser* = gql / "u7wQyGi6oExe8_TRWGMq4Q/UserResultByScreenNameQuery" + graphUserById* = gql / "oPppcargziU1uDQHAUmH-A/UserResultByIdQuery" + graphUserTweets* = gql / "JLApJKFY0MxGTzCoK6ps8Q/UserWithProfileTweetsQueryV2" + graphUserTweetsAndReplies* = gql / "Y86LQY7KMvxn5tu3hFTyPg/UserWithProfileTweetsAndRepliesQueryV2" + graphUserMedia* = gql / "PDfFf8hGeJvUCiTyWtw4wQ/MediaTimelineV2" + graphTweet* = gql / "Vorskcd2tZ-tc4Gx3zbk4Q/ConversationTimelineV2" + graphTweetResult* = gql / "sITyJdhRPpvpEjg4waUmTA/TweetResultByIdQuery" + graphSearchTimeline* = gql / "KI9jCXUx3Ymt-hDKLOZb9Q/SearchTimeline" + graphListById* = gql / "oygmAig8kjn0pKsx_bUadQ/ListByRestId" + graphListBySlug* = gql / "88GTz-IPPWLn1EiU8XoNVg/ListBySlug" + graphListMembers* = gql / "kSmxeqEeelqdHSR7jMnb_w/ListMembers" + graphListTweets* = gql / "BbGLL1ZfMibdFNWlk7a0Pw/ListTimeline" gqlFeatures* = """{ "android_graphql_skip_api_media_color_palette": false, @@ -62,7 +60,23 @@ const "unified_cards_ad_metadata_container_dynamic_card_content_query_enabled": false, "verified_phone_label_enabled": false, "vibe_api_enabled": false, - "view_counts_everywhere_api_enabled": false + "view_counts_everywhere_api_enabled": false, + "premium_content_api_read_enabled": false, + "communities_web_enable_tweet_community_results_fetch": false, + "responsive_web_jetfuel_frame": false, + "responsive_web_grok_analyze_button_fetch_trends_enabled": false, + "responsive_web_grok_image_annotation_enabled": false, + "rweb_tipjar_consumption_enabled": false, + "profile_label_improvements_pcf_label_in_post_enabled": false, + "creator_subscriptions_quote_tweet_preview_enabled": false, + "c9s_tweet_anatomy_moderator_badge_enabled": false, + "responsive_web_grok_analyze_post_followups_enabled": false, + "rweb_video_timestamps_enabled": false, + "responsive_web_grok_share_attachment_enabled": false, + "articles_preview_enabled": false, + "immersive_video_status_linkable_timestamps": false, + "articles_api_enabled": false, + "responsive_web_grok_analysis_button_from_backend": false }""".replace(" ", "").replace("\n", "") tweetVariables* = """{ diff --git a/src/parser.nim b/src/parser.nim index b8812d2..4bdb1e2 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -352,18 +352,23 @@ proc parseGraphTweetResult*(js: JsonNode): Tweet = with tweet, js{"data", "tweet_result", "result"}: result = parseGraphTweet(tweet, false) -proc parseGraphConversation*(js: JsonNode; tweetId: string): Conversation = +proc parseGraphConversation*(js: JsonNode; tweetId: string; v2=true): Conversation = result = Conversation(replies: Result[Chain](beginning: true)) - let instructions = ? js{"data", "threaded_conversation_with_injections_v2", "instructions"} + let + rootKey = if v2: "timeline_response" else: "threaded_conversation_with_injections_v2" + contentKey = if v2: "content" else: "itemContent" + resultKey = if v2: "tweetResult" else: "tweet_results" + + let instructions = ? js{"data", rootKey, "instructions"} if instructions.len == 0: return for e in instructions[0]{"entries"}: let entryId = e{"entryId"}.getStr if entryId.startsWith("tweet"): - with tweetResult, e{"content", "itemContent", "tweet_results", "result"}: - let tweet = parseGraphTweet(tweetResult, true) + with tweetResult, e{"content", contentKey, resultKey, "result"}: + let tweet = parseGraphTweet(tweetResult, not v2) if not tweet.available: tweet.id = parseBiggestInt(entryId.getId()) @@ -372,26 +377,26 @@ proc parseGraphConversation*(js: JsonNode; tweetId: string): Conversation = result.tweet = tweet else: result.before.content.add tweet - elif entryId.startsWith("tombstone"): - let id = entryId.getId() - let tweet = Tweet( - id: parseBiggestInt(id), - available: false, - text: e{"content", "itemContent", "tombstoneInfo", "richText"}.getTombstone - ) - - if id == tweetId: - result.tweet = tweet - else: - result.before.content.add tweet elif entryId.startsWith("conversationthread"): let (thread, self) = parseGraphThread(e) if self: result.after = thread else: result.replies.content.add thread + elif entryId.startsWith("tombstone"): + let id = entryId.getId() + let tweet = Tweet( + id: parseBiggestInt(id), + available: false, + text: e{"content", contentKey, "tombstoneInfo", "richText"}.getTombstone + ) + + if id == tweetId: + result.tweet = tweet + else: + result.before.content.add tweet elif entryId.startsWith("cursor-bottom"): - result.replies.bottom = e{"content", "itemContent", "value"}.getStr + result.replies.bottom = e{"content", contentKey, "value"}.getStr proc parseGraphTimeline*(js: JsonNode; root: string; after=""): Profile = result = Profile(tweets: Timeline(beginning: after.len == 0)) From b43bfc5d4255dacf8d19575959a9407c1444777b Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Wed, 5 Feb 2025 01:19:27 +0100 Subject: [PATCH 198/409] Return 403 on hmac error --- src/routes/media.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/media.nim b/src/routes/media.nim index eacd1f8..ac76ab6 100644 --- a/src/routes/media.nim +++ b/src/routes/media.nim @@ -122,7 +122,7 @@ proc createMediaRouter*(cfg: Config) = cond "http" in url if getHmac(url) != request.matches[1]: - resp showError("Failed to verify signature", cfg) + resp Http403, showError("Failed to verify signature", cfg) if ".mp4" in url or ".ts" in url or ".m4s" in url: let code = await proxyMedia(request, url) From 77288999485d14070f119813a26a9bd21728356b Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Wed, 5 Feb 2025 03:18:49 +0100 Subject: [PATCH 199/409] Add lazy loading for images --- src/views/renderutils.nim | 2 +- src/views/tweet.nim | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/views/renderutils.nim b/src/views/renderutils.nim index f298fad..41ef8df 100644 --- a/src/views/renderutils.nim +++ b/src/views/renderutils.nim @@ -91,7 +91,7 @@ proc genDate*(pref, state: string): VNode = proc genImg*(url: string; class=""): VNode = buildHtml(): - img(src=getPicUrl(url), class=class, alt="") + img(src=getPicUrl(url), class=class, alt="", loading="lazy") proc getTabClass*(query: Query; tab: QueryKind): string = if query.kind == tab: "tab-item active" diff --git a/src/views/tweet.nim b/src/views/tweet.nim index 2fe4ac9..34dcd4c 100644 --- a/src/views/tweet.nim +++ b/src/views/tweet.nim @@ -10,9 +10,7 @@ import general const doctype = "<!DOCTYPE html>\n" proc renderMiniAvatar(user: User; prefs: Prefs): VNode = - let url = getPicUrl(user.getUserPic("_mini")) - buildHtml(): - img(class=(prefs.getAvatarClass & " mini"), src=url) + genImg(user.getUserPic("_mini"), class=(prefs.getAvatarClass & " mini")) proc renderHeader(tweet: Tweet; retweet: string; pinned: bool; prefs: Prefs): VNode = buildHtml(tdiv): @@ -92,10 +90,10 @@ proc renderVideo*(video: Video; prefs: Prefs; path: string): VNode = tdiv(class="attachment video-container"): let thumb = getSmallPic(video.thumb) if not video.available: - img(src=thumb) + img(src=thumb, loading="lazy") renderVideoUnavailable(video) elif not prefs.isPlaybackEnabled(playbackType): - img(src=thumb) + img(src=thumb, loading="lazy") renderVideoDisabled(playbackType, path) else: let @@ -144,7 +142,7 @@ proc renderPoll(poll: Poll): VNode = proc renderCardImage(card: Card): VNode = buildHtml(tdiv(class="card-image-container")): tdiv(class="card-image"): - img(src=getPicUrl(card.image), alt="") + genImg(card.image) if card.kind == player: tdiv(class="card-overlay"): tdiv(class="overlay-circle"): From c0f2eea27659815b5bdc692f409bfe675a8892a4 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Wed, 5 Feb 2025 03:19:10 +0100 Subject: [PATCH 200/409] Fix missing video thumbnail being too small --- src/sass/tweet/video.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sass/tweet/video.scss b/src/sass/tweet/video.scss index 1e9096e..98a1c29 100644 --- a/src/sass/tweet/video.scss +++ b/src/sass/tweet/video.scss @@ -16,6 +16,8 @@ video { } .video-container { + min-height: 80px; + min-width: 200px; max-height: 530px; margin: 0; display: flex; From 5edaea23593ebfc28dcd7632445f0144c6242ef6 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Wed, 5 Feb 2025 03:20:05 +0100 Subject: [PATCH 201/409] Silence 404 proxy errors --- src/routes/media.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/routes/media.nim b/src/routes/media.nim index ac76ab6..de51061 100644 --- a/src/routes/media.nim +++ b/src/routes/media.nim @@ -37,7 +37,8 @@ proc proxyMedia*(req: jester.Request; url: string): Future[HttpCode] {.async.} = try: let res = await client.get(url) if res.status != "200 OK": - echo "[media] Proxying failed, status: $1, url: $2" % [res.status, url] + if res.status != "404 Not Found": + echo "[media] Proxying failed, status: $1, url: $2" % [res.status, url] return Http404 let hashed = $hash(url) From 5265de101d63b8d5dade547a8d5a994457643493 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Wed, 5 Feb 2025 03:49:17 +0100 Subject: [PATCH 202/409] Skip null fetch errors --- src/apiutils.nim | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/apiutils.nim b/src/apiutils.nim index d7842b1..0a6e0d2 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -126,10 +126,11 @@ proc fetch*(url: Uri; api: Api): Future[JsonNode] {.async.} = result = newJNull() let error = result.getError - echo "Fetch error, API: ", api, ", error: ", error - if error in {expiredToken, badToken, locked}: - invalidate(account) - raise rateLimitError() + if error != null: + echo "Fetch error, API: ", api, ", error: ", error + if error in {expiredToken, badToken, locked}: + invalidate(account) + raise rateLimitError() proc fetchRaw*(url: Uri; api: Api): Future[string] {.async.} = retry: From afad55749be0c6e2c51d495e7a22c09bdfdc6410 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Wed, 5 Feb 2025 03:49:34 +0100 Subject: [PATCH 203/409] Increase max concurrent reqs per account --- src/auth.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auth.nim b/src/auth.nim index fe265c9..85fe4a7 100644 --- a/src/auth.nim +++ b/src/auth.nim @@ -5,7 +5,7 @@ import experimental/parser/guestaccount # max requests at a time per account to avoid race conditions const - maxConcurrentReqs = 2 + maxConcurrentReqs = 3 dayInSeconds = 24 * 60 * 60 apiMaxReqs: Table[Api, int] = { Api.search: 50, From 6fcd849eff51ad1ee6e6078e3236896ab97803b6 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Wed, 5 Feb 2025 04:09:36 +0100 Subject: [PATCH 204/409] Rename accounts/guest accounts to sessions The new file loaded by default is now ./sessions.jsonl JSONL is also required, .json support dropped. --- .gitignore | 1 + nitter.example.conf | 2 +- src/apiutils.nim | 28 ++-- src/auth.nim | 147 +++++++++--------- src/experimental/parser/guestaccount.nim | 21 --- src/experimental/parser/session.nim | 15 ++ .../types/{guestaccount.nim => session.nim} | 2 +- src/nitter.nim | 8 +- src/routes/debug.nim | 6 +- src/types.nim | 4 +- 10 files changed, 114 insertions(+), 120 deletions(-) delete mode 100644 src/experimental/parser/guestaccount.nim create mode 100644 src/experimental/parser/session.nim rename src/experimental/types/{guestaccount.nim => session.nim} (71%) diff --git a/.gitignore b/.gitignore index ea520dc..dbd2f6b 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ nitter /public/md/*.html nitter.conf guest_accounts.json* +sessions.json* dump.rdb diff --git a/nitter.example.conf b/nitter.example.conf index f0b4214..360e07b 100644 --- a/nitter.example.conf +++ b/nitter.example.conf @@ -23,7 +23,7 @@ redisMaxConnections = 30 hmacKey = "secretkey" # random key for cryptographic signing of video urls base64Media = false # use base64 encoding for proxied media urls enableRSS = true # set this to false to disable RSS feeds -enableDebug = false # enable request logs and debug endpoints (/.accounts) +enableDebug = false # enable request logs and debug endpoints (/.sessions) proxy = "" # http/https url, SOCKS proxies are not supported proxyAuth = "" tokenCount = 10 diff --git a/src/apiutils.nim b/src/apiutils.nim index 0a6e0d2..3a0d12c 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -46,14 +46,14 @@ template fetchImpl(result, fetchBody) {.dirty.} = once: pool = HttpPool() - var account = await getGuestAccount(api) - if account.oauthToken.len == 0: - echo "[accounts] Empty oauth token, account: ", account.id + var session = await getSession(api) + if session.oauthToken.len == 0: + echo "[sessions] Empty oauth token, session: ", session.id raise rateLimitError() try: var resp: AsyncResponse - pool.use(genHeaders($url, account.oauthToken, account.oauthSecret)): + pool.use(genHeaders($url, session.oauthToken, session.oauthSecret)): template getContent = resp = await c.get($url) result = await resp.body @@ -68,7 +68,7 @@ template fetchImpl(result, fetchBody) {.dirty.} = let remaining = parseInt(resp.headers[rlRemaining]) reset = parseInt(resp.headers[rlReset]) - account.setRateLimit(api, remaining, reset) + session.setRateLimit(api, remaining, reset) if result.len > 0: if resp.headers.getOrDefault("content-encoding") == "gzip": @@ -78,15 +78,15 @@ template fetchImpl(result, fetchBody) {.dirty.} = let errors = result.fromJson(Errors) echo "Fetch error, API: ", api, ", errors: ", errors if errors in {expiredToken, badToken, locked}: - invalidate(account) + invalidate(session) raise rateLimitError() elif errors in {rateLimited}: # rate limit hit, resets after 24 hours - setLimited(account, api) + setLimited(session, api) raise rateLimitError() elif result.startsWith("429 Too Many Requests"): - echo "[accounts] 429 error, API: ", api, ", account: ", account.id - account.apis[api].remaining = 0 + echo "[sessions] 429 error, API: ", api, ", session: ", session.id + session.apis[api].remaining = 0 # rate limit hit, resets after the 15 minute window raise rateLimitError() @@ -102,17 +102,17 @@ template fetchImpl(result, fetchBody) {.dirty.} = except OSError as e: raise e except Exception as e: - let id = if account.isNil: "null" else: $account.id - echo "error: ", e.name, ", msg: ", e.msg, ", accountId: ", id, ", url: ", url + let id = if session.isNil: "null" else: $session.id + echo "error: ", e.name, ", msg: ", e.msg, ", sessionId: ", id, ", url: ", url raise rateLimitError() finally: - release(account) + release(session) template retry(bod) = try: bod except RateLimitError: - echo "[accounts] Rate limited, retrying ", api, " request..." + echo "[sessions] Rate limited, retrying ", api, " request..." bod proc fetch*(url: Uri; api: Api): Future[JsonNode] {.async.} = @@ -129,7 +129,7 @@ proc fetch*(url: Uri; api: Api): Future[JsonNode] {.async.} = if error != null: echo "Fetch error, API: ", api, ", error: ", error if error in {expiredToken, badToken, locked}: - invalidate(account) + invalidate(session) raise rateLimitError() proc fetchRaw*(url: Uri; api: Api): Future[string] {.async.} = diff --git a/src/auth.nim b/src/auth.nim index 85fe4a7..c57008b 100644 --- a/src/auth.nim +++ b/src/auth.nim @@ -1,9 +1,9 @@ #SPDX-License-Identifier: AGPL-3.0-only import std/[asyncdispatch, times, json, random, sequtils, strutils, tables, packedsets, os] import types -import experimental/parser/guestaccount +import experimental/parser/session -# max requests at a time per account to avoid race conditions +# max requests at a time per session to avoid race conditions const maxConcurrentReqs = 3 dayInSeconds = 24 * 60 * 60 @@ -23,16 +23,16 @@ const }.toTable var - accountPool: seq[GuestAccount] + sessionPool: seq[Session] enableLogging = false template log(str: varargs[string, `$`]) = - if enableLogging: echo "[accounts] ", str.join("") + if enableLogging: echo "[sessions] ", str.join("") proc snowflakeToEpoch(flake: int64): int64 = int64(((flake shr 22) + 1288834974657) div 1000) -proc getAccountPoolHealth*(): JsonNode = +proc getSessionPoolHealth*(): JsonNode = let now = epochTime().int var @@ -43,38 +43,38 @@ proc getAccountPoolHealth*(): JsonNode = newest = 0'i64 average = 0'i64 - for account in accountPool: - let created = snowflakeToEpoch(account.id) + for session in sessionPool: + let created = snowflakeToEpoch(session.id) if created > newest: newest = created if created < oldest: oldest = created average += created - if account.limited: - limited.incl account.id + if session.limited: + limited.incl session.id - for api in account.apis.keys: + for api in session.apis.keys: let - apiStatus = account.apis[api] + apiStatus = session.apis[api] reqs = apiMaxReqs[api] - apiStatus.remaining - # no requests made with this account and endpoint since the limit reset + # no requests made with this session and endpoint since the limit reset if apiStatus.reset < now: continue reqsPerApi.mgetOrPut($api, 0).inc reqs totalReqs.inc reqs - if accountPool.len > 0: - average = average div accountPool.len + if sessionPool.len > 0: + average = average div sessionPool.len else: oldest = 0 average = 0 return %*{ - "accounts": %*{ - "total": accountPool.len, + "sessions": %*{ + "total": sessionPool.len, "limited": limited.card, "oldest": $fromUnix(oldest), "newest": $fromUnix(newest), @@ -86,22 +86,22 @@ proc getAccountPoolHealth*(): JsonNode = } } -proc getAccountPoolDebug*(): JsonNode = +proc getSessionPoolDebug*(): JsonNode = let now = epochTime().int var list = newJObject() - for account in accountPool: - let accountJson = %*{ + for session in sessionPool: + let sessionJson = %*{ "apis": newJObject(), - "pending": account.pending, + "pending": session.pending, } - if account.limited: - accountJson["limited"] = %true + if session.limited: + sessionJson["limited"] = %true - for api in account.apis.keys: + for api in session.apis.keys: let - apiStatus = account.apis[api] + apiStatus = session.apis[api] obj = %*{} if apiStatus.reset > now.int: @@ -111,92 +111,91 @@ proc getAccountPoolDebug*(): JsonNode = if "remaining" notin obj: continue - accountJson{"apis", $api} = obj - list[$account.id] = accountJson + sessionJson{"apis", $api} = obj + list[$session.id] = sessionJson return %list proc rateLimitError*(): ref RateLimitError = newException(RateLimitError, "rate limited") -proc noAccountsError*(): ref NoAccountsError = - newException(NoAccountsError, "no accounts available") +proc noSessionsError*(): ref NoSessionsError = + newException(NoSessionsError, "no sessions available") -proc isLimited(account: GuestAccount; api: Api): bool = - if account.isNil: +proc isLimited(session: Session; api: Api): bool = + if session.isNil: return true - if account.limited and api != Api.userTweets: - if (epochTime().int - account.limitedAt) > dayInSeconds: - account.limited = false - log "resetting limit: ", account.id + if session.limited and api != Api.userTweets: + if (epochTime().int - session.limitedAt) > dayInSeconds: + session.limited = false + log "resetting limit: ", session.id else: return false - if api in account.apis: - let limit = account.apis[api] + if api in session.apis: + let limit = session.apis[api] return limit.remaining <= 10 and limit.reset > epochTime().int else: return false -proc isReady(account: GuestAccount; api: Api): bool = - not (account.isNil or account.pending > maxConcurrentReqs or account.isLimited(api)) +proc isReady(session: Session; api: Api): bool = + not (session.isNil or session.pending > maxConcurrentReqs or session.isLimited(api)) -proc invalidate*(account: var GuestAccount) = - if account.isNil: return - log "invalidating: ", account.id +proc invalidate*(session: var Session) = + if session.isNil: return + log "invalidating: ", session.id # TODO: This isn't sufficient, but it works for now - let idx = accountPool.find(account) - if idx > -1: accountPool.delete(idx) - account = nil + let idx = sessionPool.find(session) + if idx > -1: sessionPool.delete(idx) + session = nil -proc release*(account: GuestAccount) = - if account.isNil: return - dec account.pending +proc release*(session: Session) = + if session.isNil: return + dec session.pending -proc getGuestAccount*(api: Api): Future[GuestAccount] {.async.} = - for i in 0 ..< accountPool.len: +proc getSession*(api: Api): Future[Session] {.async.} = + for i in 0 ..< sessionPool.len: if result.isReady(api): break - result = accountPool.sample() + result = sessionPool.sample() if not result.isNil and result.isReady(api): inc result.pending else: - log "no accounts available for API: ", api - raise noAccountsError() + log "no sessions available for API: ", api + raise noSessionsError() -proc setLimited*(account: GuestAccount; api: Api) = - account.limited = true - account.limitedAt = epochTime().int - log "rate limited by api: ", api, ", reqs left: ", account.apis[api].remaining, ", id: ", account.id +proc setLimited*(session: Session; api: Api) = + session.limited = true + session.limitedAt = epochTime().int + log "rate limited by api: ", api, ", reqs left: ", session.apis[api].remaining, ", id: ", session.id -proc setRateLimit*(account: GuestAccount; api: Api; remaining, reset: int) = +proc setRateLimit*(session: Session; api: Api; remaining, reset: int) = # avoid undefined behavior in race conditions - if api in account.apis: - let limit = account.apis[api] + if api in session.apis: + let limit = session.apis[api] if limit.reset >= reset and limit.remaining < remaining: return if limit.reset == reset and limit.remaining >= remaining: - account.apis[api].remaining = remaining + session.apis[api].remaining = remaining return - account.apis[api] = RateLimit(remaining: remaining, reset: reset) + session.apis[api] = RateLimit(remaining: remaining, reset: reset) -proc initAccountPool*(cfg: Config; path: string) = +proc initSessionPool*(cfg: Config; path: string) = enableLogging = cfg.enableDebug - let jsonlPath = if path.endsWith(".json"): (path & 'l') else: path - - if fileExists(jsonlPath): - log "Parsing JSONL guest accounts file: ", jsonlPath - for line in jsonlPath.lines: - accountPool.add parseGuestAccount(line) - elif fileExists(path): - log "Parsing JSON guest accounts file: ", path - accountPool = parseGuestAccounts(path) - else: - echo "[accounts] ERROR: ", path, " not found. This file is required to authenticate API requests." + if path.endsWith(".json"): + echo "[sessions] ERROR: .json is not supported, the file must be a valid JSONL file ending in .jsonl" quit 1 - log "Successfully added ", accountPool.len, " valid accounts." + if not fileExists(path): + echo "[sessions] ERROR: ", path, " not found. This file is required to authenticate API requests." + quit 1 + + log "Parsing JSONL account sessions file: ", path + for line in path.lines: + sessionPool.add parseSession(line) + + log "Successfully added ", sessionPool.len, " valid account sessions." diff --git a/src/experimental/parser/guestaccount.nim b/src/experimental/parser/guestaccount.nim deleted file mode 100644 index f7e6d34..0000000 --- a/src/experimental/parser/guestaccount.nim +++ /dev/null @@ -1,21 +0,0 @@ -import std/strutils -import jsony -import ../types/guestaccount -from ../../types import GuestAccount - -proc toGuestAccount(account: RawAccount): GuestAccount = - let id = account.oauthToken[0 ..< account.oauthToken.find('-')] - result = GuestAccount( - id: parseBiggestInt(id), - oauthToken: account.oauthToken, - oauthSecret: account.oauthTokenSecret - ) - -proc parseGuestAccount*(raw: string): GuestAccount = - let rawAccount = raw.fromJson(RawAccount) - result = rawAccount.toGuestAccount - -proc parseGuestAccounts*(path: string): seq[GuestAccount] = - let rawAccounts = readFile(path).fromJson(seq[RawAccount]) - for account in rawAccounts: - result.add account.toGuestAccount diff --git a/src/experimental/parser/session.nim b/src/experimental/parser/session.nim new file mode 100644 index 0000000..ee9c93e --- /dev/null +++ b/src/experimental/parser/session.nim @@ -0,0 +1,15 @@ +import std/strutils +import jsony +import ../types/session +from ../../types import Session + +proc parseSession*(raw: string): Session = + let + session = raw.fromJson(RawSession) + id = session.oauthToken[0 ..< session.oauthToken.find('-')] + + result = Session( + id: parseBiggestInt(id), + oauthToken: session.oauthToken, + oauthSecret: session.oauthTokenSecret + ) diff --git a/src/experimental/types/guestaccount.nim b/src/experimental/types/session.nim similarity index 71% rename from src/experimental/types/guestaccount.nim rename to src/experimental/types/session.nim index 244edb3..4165204 100644 --- a/src/experimental/types/guestaccount.nim +++ b/src/experimental/types/session.nim @@ -1,4 +1,4 @@ type - RawAccount* = object + RawSession* = object oauthToken*: string oauthTokenSecret*: string diff --git a/src/nitter.nim b/src/nitter.nim index 958dc0b..f81dc1c 100644 --- a/src/nitter.nim +++ b/src/nitter.nim @@ -19,9 +19,9 @@ let configPath = getEnv("NITTER_CONF_FILE", "./nitter.conf") (cfg, fullCfg) = getConfig(configPath) - accountsPath = getEnv("NITTER_ACCOUNTS_FILE", "./guest_accounts.json") + sessionsPath = getEnv("NITTER_SESSIONS_FILE", "./sessions.jsonl") -initAccountPool(cfg, accountsPath) +initSessionPool(cfg, sessionsPath) if not cfg.enableDebug: # Silence Jester's query warning @@ -97,10 +97,10 @@ routes: resp Http429, showError( &"Instance has been rate limited.<br>Use {link} or try again later.", cfg) - error NoAccountsError: + error NoSessionsError: const link = a("another instance", href = instancesUrl) resp Http429, showError( - &"Instance has no available accounts.<br>Use {link} or try again later.", cfg) + &"Instance has no auth tokens, or is fully rate limited.<br>Use {link} or try again later.", cfg) extend rss, "" extend status, "" diff --git a/src/routes/debug.nim b/src/routes/debug.nim index 895a285..97c5bef 100644 --- a/src/routes/debug.nim +++ b/src/routes/debug.nim @@ -6,8 +6,8 @@ import ".."/[auth, types] proc createDebugRouter*(cfg: Config) = router debug: get "/.health": - respJson getAccountPoolHealth() + respJson getSessionPoolHealth() - get "/.accounts": + get "/.sessions": cond cfg.enableDebug - respJson getAccountPoolDebug() + respJson getSessionPoolDebug() diff --git a/src/types.nim b/src/types.nim index 685fbad..6c1f1b6 100644 --- a/src/types.nim +++ b/src/types.nim @@ -6,7 +6,7 @@ genPrefsType() type RateLimitError* = object of CatchableError - NoAccountsError* = object of CatchableError + NoSessionsError* = object of CatchableError InternalError* = object of CatchableError BadClientError* = object of CatchableError @@ -31,7 +31,7 @@ type remaining*: int reset*: int - GuestAccount* = ref object + Session* = ref object id*: int64 oauthToken*: string oauthSecret*: string From 4d5091947c81e181f34f173e4fe0c423e163f9ed Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Wed, 5 Feb 2025 04:24:35 +0100 Subject: [PATCH 205/409] Update Dockerfiles --- .github/workflows/run-tests.yml | 4 ++-- Dockerfile | 4 ++-- Dockerfile.arm64 | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index ee28e33..a273771 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -14,9 +14,9 @@ jobs: strategy: matrix: nim: - - "1.6.10" - "1.6.x" - "2.0.x" + - "2.2.x" - "devel" steps: - uses: actions/checkout@v3 @@ -49,7 +49,7 @@ jobs: sed -i 's/enableDebug = false/enableDebug = true/g' nitter.conf nimble md nimble scss - echo '${{ secrets.GUEST_ACCOUNTS }}' > ./guest_accounts.jsonl + echo '${{ secrets.SESSIONS }}' > ./sessions.jsonl - name: Run tests run: | ./nitter & diff --git a/Dockerfile b/Dockerfile index 138dc64..ab442ba 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM nimlang/nim:2.0.0-alpine-regular as nim +FROM nimlang/nim:2.2.0-alpine-regular as nim LABEL maintainer="setenforce@protonmail.com" RUN apk --no-cache add libsass-dev pcre @@ -9,7 +9,7 @@ COPY nitter.nimble . RUN nimble install -y --depsOnly COPY . . -RUN nimble build -d:danger -d:lto -d:strip \ +RUN nimble build -d:danger -d:lto -d:strip --mm:refc \ && nimble scss \ && nimble md diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 index 70024b2..bf6011f 100644 --- a/Dockerfile.arm64 +++ b/Dockerfile.arm64 @@ -1,7 +1,7 @@ -FROM alpine:3.18 as nim +FROM alpine:3.21.2 as nim LABEL maintainer="setenforce@protonmail.com" -RUN apk --no-cache add libsass-dev pcre gcc git libc-dev "nim=1.6.14-r0" "nimble=0.13.1-r2" +RUN apk --no-cache add libsass-dev pcre gcc git libc-dev nim nimble WORKDIR /src/nitter @@ -9,13 +9,13 @@ COPY nitter.nimble . RUN nimble install -y --depsOnly COPY . . -RUN nimble build -d:danger -d:lto -d:strip \ +RUN nimble build -d:danger -d:lto -d:strip --mm:refc \ && nimble scss \ && nimble md -FROM alpine:3.18 +FROM alpine:3.21.2 WORKDIR /src/ -RUN apk --no-cache add pcre ca-certificates openssl1.1-compat +RUN apk --no-cache add pcre ca-certificates openssl COPY --from=nim /src/nitter/nitter ./ COPY --from=nim /src/nitter/nitter.example.conf ./nitter.conf COPY --from=nim /src/nitter/public ./public From a3d341e7a68f721fa9f43a5849daebd1959b174c Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Wed, 5 Feb 2025 18:10:15 +0100 Subject: [PATCH 206/409] Update README, added an important note --- README.md | 57 +++++++++++++++++++++++++++------------------- public/md/about.md | 44 ++++++++++++++++++----------------- 2 files changed, 57 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 4f8235d..05c2be4 100644 --- a/README.md +++ b/README.md @@ -4,27 +4,35 @@ [![Test Matrix](https://github.com/zedeus/nitter/workflows/Docker/badge.svg)](https://github.com/zedeus/nitter/actions/workflows/build-docker.yml) [![License](https://img.shields.io/github/license/zedeus/nitter?style=flat)](#license) +> [!NOTE] +> Running a Nitter instance now requires real accounts, since Twitter removed the previous methods. \ +> This does not affect users. \ +> For instructions on how to obtain session tokens, see [Creating session tokens](https://github.com/zedeus/nitter/wiki/Creating-session-tokens). + A free and open source alternative Twitter front-end focused on privacy and performance. \ -Inspired by the [Invidious](https://github.com/iv-org/invidious) -project. +Inspired by the [Invidious](https://github.com/iv-org/invidious) project. - No JavaScript or ads - All requests go through the backend, client never talks to Twitter - Prevents Twitter from tracking your IP or JavaScript fingerprint -- Uses Twitter's unofficial API (no rate limits or developer account required) +- Uses Twitter's unofficial API (no developer account required) - Lightweight (for [@nim_lang](https://nitter.net/nim_lang), 60KB vs 784KB from twitter.com) - RSS feeds - Themes - Mobile support (responsive design) - AGPLv3 licensed, no proprietary instances permitted -Liberapay: https://liberapay.com/zedeus \ -Patreon: https://patreon.com/nitter \ -BTC: bc1qp7q4qz0fgfvftm5hwz3vy284nue6jedt44kxya \ -ETH: 0x66d84bc3fd031b62857ad18c62f1ba072b011925 \ -LTC: ltc1qhsz5nxw6jw9rdtw9qssjeq2h8hqk2f85rdgpkr \ -XMR: 42hKayRoEAw4D6G6t8mQHPJHQcXqofjFuVfavqKeNMNUZfeJLJAcNU19i1bGdDvcdN6romiSscWGWJCczFLe9RFhM3d1zpL +<details> +<summary>Donations</summary> +Liberapay: https://liberapay.com/zedeus<br> +Patreon: https://patreon.com/nitter<br> +BTC: bc1qpqpzjkcpgluhzf7x9yqe7jfe8gpfm5v08mdr55<br> +ETH: 0x24a0DB59A923B588c7A5EBd0dBDFDD1bCe9c4460<br> +XMR: 42hKayRoEAw4D6G6t8mQHPJHQcXqofjFuVfavqKeNMNUZfeJLJAcNU19i1bGdDvcdN6romiSscWGWJCczFLe9RFhM3d1zpL<br> +SOL: ANsyGNXFo6osuFwr1YnUqif2RdoYRhc27WdyQNmmETSW<br> +ZEC: u1vndfqtzyy6qkzhkapxelel7ams38wmfeccu3fdpy2wkuc4erxyjm8ncjhnyg747x6t0kf0faqhh2hxyplgaum08d2wnj4n7cyu9s6zhxkqw2aef4hgd4s6vh5hpqvfken98rg80kgtgn64ff70djy7s8f839z00hwhuzlcggvefhdlyszkvwy3c7yw623vw3rvar6q6evd3xcvveypt +</details> ## Roadmap @@ -42,12 +50,13 @@ maintained by the community. ## Why? -It's impossible to use Twitter without JavaScript enabled. For privacy-minded -folks, preventing JavaScript analytics and IP-based tracking is important, but -apart from using a VPN and uBlock/uMatrix, it's impossible. Despite being behind -a VPN and using heavy-duty adblockers, you can get accurately tracked with your -[browser's fingerprint](https://restoreprivacy.com/browser-fingerprinting/), -[no JavaScript required](https://noscriptfingerprint.com/). This all became +It's impossible to use Twitter without JavaScript enabled, and as of 2024 you +need to sign up. For privacy-minded folks, preventing JavaScript analytics and +IP-based tracking is important, but apart from using a VPN and uBlock/uMatrix, +it's impossible. Despite being behind a VPN and using heavy-duty adblockers, +you can get accurately tracked with your [browser's +fingerprint](https://restoreprivacy.com/browser-fingerprinting/), [no +JavaScript required](https://noscriptfingerprint.com/). This all became particularly important after Twitter [removed the ability](https://www.eff.org/deeplinks/2020/04/twitter-removes-privacy-option-and-shows-why-we-need-strong-privacy-laws) for users to control whether their data gets sent to advertisers. @@ -71,19 +80,21 @@ Twitter account. - libpcre - libsass -- redis +- redis/valkey To compile Nitter you need a Nim installation, see -[nim-lang.org](https://nim-lang.org/install.html) for details. It is possible to -install it system-wide or in the user directory you create below. +[nim-lang.org](https://nim-lang.org/install.html) for details. It is possible +to install it system-wide or in the user directory you create below. To compile the scss files, you need to install `libsass`. On Ubuntu and Debian, you can use `libsass-dev`. -Redis is required for caching and in the future for account info. It should be -available on most distros as `redis` or `redis-server` (Ubuntu/Debian). -Running it with the default config is fine, Nitter's default config is set to -use the default Redis port and localhost. +Redis is required for caching and in the future for account info. As of 2024 +Redis is no longer open source, so using the fork Valkey is recommended. It +should be available on most distros as `redis` or `redis-server` +(Ubuntu/Debian), or `valkey`/`valkey-server`. Running it with the default +config is fine, Nitter's default config is set to use the default port and +localhost. Here's how to create a `nitter` user, clone the repo, and build the project along with the scss and md files. @@ -93,7 +104,7 @@ along with the scss and md files. # su nitter $ git clone https://github.com/zedeus/nitter $ cd nitter -$ nimble build -d:release +$ nimble build -d:danger --mm:refc $ nimble scss $ nimble md $ cp nitter.example.conf nitter.conf diff --git a/public/md/about.md b/public/md/about.md index c0adda9..3825e8f 100644 --- a/public/md/about.md +++ b/public/md/about.md @@ -4,15 +4,15 @@ Nitter is a free and open source alternative Twitter front-end focused on privacy and performance. The source is available on GitHub at <https://github.com/zedeus/nitter> -* No JavaScript or ads -* All requests go through the backend, client never talks to Twitter -* Prevents Twitter from tracking your IP or JavaScript fingerprint -* Uses Twitter's unofficial API (no rate limits or developer account required) -* Lightweight (for [@nim_lang](/nim_lang), 60KB vs 784KB from twitter.com) -* RSS feeds -* Themes -* Mobile support (responsive design) -* AGPLv3 licensed, no proprietary instances permitted +- No JavaScript or ads +- All requests go through the backend, client never talks to Twitter +- Prevents Twitter from tracking your IP or JavaScript fingerprint +- Uses Twitter's unofficial API (no developer account required) +- Lightweight (for [@nim_lang](/nim_lang), 60KB vs 784KB from twitter.com) +- RSS feeds +- Themes +- Mobile support (responsive design) +- AGPLv3 licensed, no proprietary instances permitted Nitter's GitHub wiki contains [instances](https://github.com/zedeus/nitter/wiki/Instances) and @@ -21,12 +21,13 @@ maintained by the community. ## Why use Nitter? -It's impossible to use Twitter without JavaScript enabled. For privacy-minded -folks, preventing JavaScript analytics and IP-based tracking is important, but -apart from using a VPN and uBlock/uMatrix, it's impossible. Despite being behind -a VPN and using heavy-duty adblockers, you can get accurately tracked with your -[browser's fingerprint](https://restoreprivacy.com/browser-fingerprinting/), -[no JavaScript required](https://noscriptfingerprint.com/). This all became +It's impossible to use Twitter without JavaScript enabled, and as of 2024 you +need to sign up. For privacy-minded folks, preventing JavaScript analytics and +IP-based tracking is important, but apart from using a VPN and uBlock/uMatrix, +it's impossible. Despite being behind a VPN and using heavy-duty adblockers, +you can get accurately tracked with your [browser's +fingerprint](https://restoreprivacy.com/browser-fingerprinting/), [no +JavaScript required](https://noscriptfingerprint.com/). This all became particularly important after Twitter [removed the ability](https://www.eff.org/deeplinks/2020/04/twitter-removes-privacy-option-and-shows-why-we-need-strong-privacy-laws) for users to control whether their data gets sent to advertisers. @@ -42,12 +43,13 @@ Twitter account. ## Donating -Liberapay: <https://liberapay.com/zedeus> \ -Patreon: <https://patreon.com/nitter> \ -BTC: bc1qp7q4qz0fgfvftm5hwz3vy284nue6jedt44kxya \ -ETH: 0x66d84bc3fd031b62857ad18c62f1ba072b011925 \ -LTC: ltc1qhsz5nxw6jw9rdtw9qssjeq2h8hqk2f85rdgpkr \ -XMR: 42hKayRoEAw4D6G6t8mQHPJHQcXqofjFuVfavqKeNMNUZfeJLJAcNU19i1bGdDvcdN6romiSscWGWJCczFLe9RFhM3d1zpL +Liberapay: https://liberapay.com/zedeus \ +Patreon: https://patreon.com/nitter \ +BTC: bc1qpqpzjkcpgluhzf7x9yqe7jfe8gpfm5v08mdr55 \ +ETH: 0x24a0DB59A923B588c7A5EBd0dBDFDD1bCe9c4460 \ +XMR: 42hKayRoEAw4D6G6t8mQHPJHQcXqofjFuVfavqKeNMNUZfeJLJAcNU19i1bGdDvcdN6romiSscWGWJCczFLe9RFhM3d1zpL \ +SOL: ANsyGNXFo6osuFwr1YnUqif2RdoYRhc27WdyQNmmETSW \ +ZEC: u1vndfqtzyy6qkzhkapxelel7ams38wmfeccu3fdpy2wkuc4erxyjm8ncjhnyg747x6t0kf0faqhh2hxyplgaum08d2wnj4n7cyu9s6zhxkqw2aef4hgd4s6vh5hpqvfken98rg80kgtgn64ff70djy7s8f839z00hwhuzlcggvefhdlyszkvwy3c7yw623vw3rvar6q6evd3xcvveypt ## Contact From 10b1d9c80f7c8c70f86f12c6ea4f0889651521bd Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Wed, 5 Feb 2025 19:03:47 +0100 Subject: [PATCH 207/409] Add Python script to create account sessions --- tools/get_session.py | 161 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 tools/get_session.py diff --git a/tools/get_session.py b/tools/get_session.py new file mode 100644 index 0000000..9ff704d --- /dev/null +++ b/tools/get_session.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python3 +import requests +import json +import sys +import pyotp + +# NOTE: pyotp and requests are dependencies +# > pip install pyotp requests + +TW_CONSUMER_KEY = '3nVuSoBZnx6U4vzUxf5w' +TW_CONSUMER_SECRET = 'Bcs59EFbbsdF6Sl9Ng71smgStWEGwXXKSjYvPVt7qys' + +def auth(username, password, otp_secret): + bearer_token_req = requests.post("https://api.twitter.com/oauth2/token", + auth=(TW_CONSUMER_KEY, TW_CONSUMER_SECRET), + headers={"Content-Type": "application/x-www-form-urlencoded"}, + data='grant_type=client_credentials' + ).json() + bearer_token = ' '.join(str(x) for x in bearer_token_req.values()) + + guest_token = requests.post( + "https://api.twitter.com/1.1/guest/activate.json", + headers={'Authorization': bearer_token} + ).json().get('guest_token') + + if not guest_token: + print("Failed to obtain guest token.") + sys.exit(1) + + twitter_header = { + 'Authorization': bearer_token, + "Content-Type": "application/json", + "User-Agent": "TwitterAndroid/10.21.0-release.0 (310210000-r-0) ONEPLUS+A3010/9 (OnePlus;ONEPLUS+A3010;OnePlus;OnePlus3;0;;1;2016)", + "X-Twitter-API-Version": '5', + "X-Twitter-Client": "TwitterAndroid", + "X-Twitter-Client-Version": "10.21.0-release.0", + "OS-Version": "28", + "System-User-Agent": "Dalvik/2.1.0 (Linux; U; Android 9; ONEPLUS A3010 Build/PKQ1.181203.001)", + "X-Twitter-Active-User": "yes", + "X-Guest-Token": guest_token, + "X-Twitter-Client-DeviceID": "" + } + + session = requests.Session() + session.headers = twitter_header + + task1 = session.post( + 'https://api.twitter.com/1.1/onboarding/task.json', + params={ + 'flow_name': 'login', + 'api_version': '1', + 'known_device_token': '', + 'sim_country_code': 'us' + }, + json={ + "flow_token": None, + "input_flow_data": { + "country_code": None, + "flow_context": { + "referrer_context": { + "referral_details": "utm_source=google-play&utm_medium=organic", + "referrer_url": "" + }, + "start_location": { + "location": "deeplink" + } + }, + "requested_variant": None, + "target_user_id": 0 + } + } + ) + + session.headers['att'] = task1.headers.get('att') + + task2 = session.post( + 'https://api.twitter.com/1.1/onboarding/task.json', + json={ + "flow_token": task1.json().get('flow_token'), + "subtask_inputs": [{ + "enter_text": { + "suggestion_id": None, + "text": username, + "link": "next_link" + }, + "subtask_id": "LoginEnterUserIdentifier" + }] + } + ) + + task3 = session.post( + 'https://api.twitter.com/1.1/onboarding/task.json', + json={ + "flow_token": task2.json().get('flow_token'), + "subtask_inputs": [{ + "enter_password": { + "password": password, + "link": "next_link" + }, + "subtask_id": "LoginEnterPassword" + }], + } + ) + + for t3_subtask in task3.json().get('subtasks', []): + if "open_account" in t3_subtask: + return t3_subtask["open_account"] + elif "enter_text" in t3_subtask: + response_text = t3_subtask["enter_text"]["hint_text"] + totp = pyotp.TOTP(otp_secret) + generated_code = totp.now() + task4resp = session.post( + "https://api.twitter.com/1.1/onboarding/task.json", + json={ + "flow_token": task3.json().get("flow_token"), + "subtask_inputs": [ + { + "enter_text": { + "suggestion_id": None, + "text": generated_code, + "link": "next_link", + }, + "subtask_id": "LoginTwoFactorAuthChallenge", + } + ], + } + ) + task4 = task4resp.json() + for t4_subtask in task4.get("subtasks", []): + if "open_account" in t4_subtask: + return t4_subtask["open_account"] + + return None + +if __name__ == "__main__": + if len(sys.argv) != 5: + print("Usage: python3 get_session.py <username> <password> <2fa secret> <path>") + sys.exit(1) + + username = sys.argv[1] + password = sys.argv[2] + otp_secret = sys.argv[3] + path = sys.argv[4] + + result = auth(username, password, otp_secret) + if result is None: + print("Authentication failed.") + sys.exit(1) + + session_entry = { + "oauth_token": result.get("oauth_token"), + "oauth_token_secret": result.get("oauth_token_secret") + } + + try: + with open(path, "a") as f: + f.write(json.dumps(session_entry) + "\n") + print("Authentication successful. Session appended to", path) + except Exception as e: + print(f"Failed to write session information: {e}") + sys.exit(1) From b9af77a9bd460297b9063490d78006af05345e28 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Wed, 5 Feb 2025 19:28:10 +0100 Subject: [PATCH 208/409] Change main page search to "Tweets" search --- src/views/search.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/search.nim b/src/views/search.nim index 401e6da..9f7fc95 100644 --- a/src/views/search.nim +++ b/src/views/search.nim @@ -24,9 +24,9 @@ proc renderSearch*(): VNode = buildHtml(tdiv(class="panel-container")): tdiv(class="search-bar"): form(`method`="get", action="/search", autocomplete="off"): - hiddenField("f", "users") + hiddenField("f", "tweets") input(`type`="text", name="q", autofocus="", - placeholder="Enter username...", dir="auto") + placeholder="Search...", dir="auto") button(`type`="submit"): icon "search" proc renderProfileTabs*(query: Query; username: string): VNode = From 066407474921f6b69efdd0bec17d686830f868c4 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Wed, 5 Feb 2025 19:37:22 +0100 Subject: [PATCH 209/409] Remove old tokenCount from nitter.example.conf --- nitter.example.conf | 6 ------ 1 file changed, 6 deletions(-) diff --git a/nitter.example.conf b/nitter.example.conf index 360e07b..bddb9a4 100644 --- a/nitter.example.conf +++ b/nitter.example.conf @@ -26,12 +26,6 @@ enableRSS = true # set this to false to disable RSS feeds enableDebug = false # enable request logs and debug endpoints (/.sessions) proxy = "" # http/https url, SOCKS proxies are not supported proxyAuth = "" -tokenCount = 10 -# minimum amount of usable tokens. tokens are used to authorize API requests, -# but they expire after ~1 hour, and have a limit of 500 requests per endpoint. -# the limits reset every 15 minutes, and the pool is filled up so there's -# always at least `tokenCount` usable tokens. only increase this if you receive -# major bursts all the time and don't have a rate limiting setup via e.g. nginx # Change default preferences here, see src/prefs_impl.nim for a complete list [Preferences] From bc38315d1242bbaf5ceaa6836140975dd302f9e9 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Wed, 5 Feb 2025 20:19:18 +0100 Subject: [PATCH 210/409] Fix tests --- .github/workflows/run-tests.yml | 2 +- tests/test_card.py | 4 ++-- tests/test_profile.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index a273771..da92525 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -53,4 +53,4 @@ jobs: - name: Run tests run: | ./nitter & - pytest -n8 tests + pytest -n3 tests diff --git a/tests/test_card.py b/tests/test_card.py index 8da91a2..05b55f6 100644 --- a/tests/test_card.py +++ b/tests/test_card.py @@ -26,8 +26,8 @@ no_thumb = [ 'lnkd.in'], ['Thom_Wolf/status/1122466524860702729', - 'facebookresearch/fairseq', - 'Facebook AI Research Sequence-to-Sequence Toolkit written in Python. - GitHub - facebookresearch/fairseq: Facebook AI Research Sequence-to-Sequence Toolkit written in Python.', + 'GitHub - NVIDIA/Megatron-LM: Ongoing research training transformer models at scale', + 'Ongoing research training transformer models at scale - NVIDIA/Megatron-LM', 'github.com'], ['brent_p/status/1088857328680488961', diff --git a/tests/test_profile.py b/tests/test_profile.py index 38c5189..ea05add 100644 --- a/tests/test_profile.py +++ b/tests/test_profile.py @@ -4,7 +4,7 @@ from parameterized import parameterized profiles = [ ['mobile_test', 'Test account', 'Test Account. test test Testing username with @mobile_test_2 and a #hashtag', - 'San Francisco, CA', 'example.com/foobar', 'Joined October 2009', '98'], + 'San Francisco, CA', 'example.com/foobar', 'Joined October 2009', '97'], ['mobile_test_2', 'mobile test 2', '', '', '', 'Joined January 2011', '13'] ] From 54ba1e30b5b0be36c36c8de1626c4bed6d7f82ff Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Wed, 5 Feb 2025 20:27:23 +0100 Subject: [PATCH 211/409] Fix empty image URLs in photo rail --- src/parser.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/parser.nim b/src/parser.nim index 4bdb1e2..6e0be73 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -456,7 +456,8 @@ proc parseGraphPhotoRail*(js: JsonNode): PhotoRail = elif t.card.isSome: get(t.card).image else: "" - result.add GalleryPhoto(url: url, tweetId: $t.id) + if url.len > 0: + result.add GalleryPhoto(url: url, tweetId: $t.id) if result.len == 16: break From fb7c1d87108f8e909448ed1707ebbc59e5e62325 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Wed, 5 Feb 2025 21:48:29 +0100 Subject: [PATCH 212/409] Improve test workflow --- .github/workflows/run-tests.yml | 98 +++++++++++++++++++++++++-------- 1 file changed, 75 insertions(+), 23 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index da92525..379f901 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -8,49 +8,101 @@ on: - master workflow_call: +# Ensure that multiple runs on the same branch do not overlap. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +defaults: + run: + shell: bash + jobs: - test: + build-test: + name: Build and test runs-on: buildjet-2vcpu-ubuntu-2204 strategy: matrix: - nim: - - "1.6.x" - - "2.0.x" - - "2.2.x" - - "devel" + nim: ["1.6.x", "2.0.x", "2.2.x", "devel"] steps: - - uses: actions/checkout@v3 + - name: Checkout Code + uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Cache nimble + + - name: Cache Nimble Dependencies id: cache-nimble - uses: buildjet/cache@v3 + uses: buildjet/cache@v4 with: path: ~/.nimble - key: ${{ matrix.nim }}-nimble-${{ hashFiles('*.nimble') }} + key: ${{ matrix.nim }}-nimble-v2-${{ hashFiles('*.nimble') }} restore-keys: | - ${{ matrix.nim }}-nimble- - - uses: actions/setup-python@v4 - with: - python-version: "3.10" - cache: "pip" - - uses: jiro4989/setup-nim-action@v1 + ${{ matrix.nim }}-nimble-v2- + + - name: Setup Nim + uses: jiro4989/setup-nim-action@v2 with: nim-version: ${{ matrix.nim }} + use-nightlies: true repo-token: ${{ secrets.GITHUB_TOKEN }} - - run: nimble build -d:release -Y - - run: pip install seleniumbase - - run: seleniumbase install chromedriver - - uses: supercharge/redis-github-action@1.5.0 - - name: Prepare Nitter + + - name: Build Project + run: nimble build -d:release -Y + + integration-test: + needs: [build-test] + name: Integration test + runs-on: buildjet-2vcpu-ubuntu-2204 + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Cache Nimble Dependencies + id: cache-nimble + uses: buildjet/cache@v4 + with: + path: ~/.nimble + key: devel-nimble-v2-${{ hashFiles('*.nimble') }} + restore-keys: | + devel-nimble-v2- + + - name: Setup Python (3.10) with pip cache + uses: buildjet/setup-python@v4 + with: + python-version: "3.10" + cache: pip + + - name: Setup Nim + uses: jiro4989/setup-nim-action@v2 + with: + nim-version: devel + use-nightlies: true + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Build Project + run: nimble build -d:release -Y + + - name: Install SeleniumBase and Chromedriver run: | - sudo apt install libsass-dev -y + pip install seleniumbase + seleniumbase install chromedriver + + - name: Start Redis Service + uses: supercharge/redis-github-action@1.5.0 + + - name: Prepare Nitter Environment + run: | + sudo apt-get update && sudo apt-get install -y libsass-dev cp nitter.example.conf nitter.conf sed -i 's/enableDebug = false/enableDebug = true/g' nitter.conf nimble md nimble scss + echo '${{ secrets.SESSIONS }}' | head -n1 echo '${{ secrets.SESSIONS }}' > ./sessions.jsonl - - name: Run tests + + - name: Run Tests run: | ./nitter & pytest -n3 tests From 770257636980bf526e1e6a1da54fc3dca8bb6550 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Wed, 12 Feb 2025 14:42:05 +0100 Subject: [PATCH 213/409] Fix GitHub workflow secrets permissions --- .github/workflows/build-docker.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index 765e7a0..c9f0392 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -10,6 +10,7 @@ on: jobs: tests: uses: ./.github/workflows/run-tests.yml + secrets: inherit build-docker-amd64: needs: [tests] runs-on: buildjet-2vcpu-ubuntu-2204 From 5be37737eb43902fc1dc45c4862637c1d3fd3178 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Wed, 12 Feb 2025 18:36:29 +0100 Subject: [PATCH 214/409] Fix rate limit handling --- src/auth.nim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/auth.nim b/src/auth.nim index c57008b..bf3c957 100644 --- a/src/auth.nim +++ b/src/auth.nim @@ -5,7 +5,7 @@ import experimental/parser/session # max requests at a time per session to avoid race conditions const - maxConcurrentReqs = 3 + maxConcurrentReqs = 2 dayInSeconds = 24 * 60 * 60 apiMaxReqs: Table[Api, int] = { Api.search: 50, @@ -130,8 +130,9 @@ proc isLimited(session: Session; api: Api): bool = if (epochTime().int - session.limitedAt) > dayInSeconds: session.limited = false log "resetting limit: ", session.id - else: return false + else: + return true if api in session.apis: let limit = session.apis[api] From 6da152db07b5adc424e34488b6785550bdc5b863 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Wed, 12 Feb 2025 20:43:45 +0100 Subject: [PATCH 215/409] Reduce integration test concurrency --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 379f901..6d02dd2 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -105,4 +105,4 @@ jobs: - name: Run Tests run: | ./nitter & - pytest -n3 tests + pytest -n2 tests From 9ccfd8ee99da7825b288c5f34960017800bedb7a Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Wed, 12 Feb 2025 21:22:09 +0100 Subject: [PATCH 216/409] Reduce integration test concurrency even more --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 6d02dd2..46e4ace 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -105,4 +105,4 @@ jobs: - name: Run Tests run: | ./nitter & - pytest -n2 tests + pytest -n1 tests From 92cd6abcf6d9935bc0d7f013acbfbfd8ddd896ba Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Sun, 16 Feb 2025 02:06:19 +0100 Subject: [PATCH 217/409] Stop logging unimportant errors --- src/apiutils.nim | 20 +++++++++++--------- src/types.nim | 3 +++ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/apiutils.nim b/src/apiutils.nim index 3a0d12c..65dcc29 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -7,6 +7,7 @@ import experimental/types/common const rlRemaining = "x-rate-limit-remaining" rlReset = "x-rate-limit-reset" + errorsToSkip = {doesntExist, tweetNotFound, timeout, unauthorized, badRequest} var pool: HttpPool @@ -76,14 +77,15 @@ template fetchImpl(result, fetchBody) {.dirty.} = if result.startsWith("{\"errors"): let errors = result.fromJson(Errors) - echo "Fetch error, API: ", api, ", errors: ", errors - if errors in {expiredToken, badToken, locked}: - invalidate(session) - raise rateLimitError() - elif errors in {rateLimited}: - # rate limit hit, resets after 24 hours - setLimited(session, api) - raise rateLimitError() + if errors notin errorsToSkip: + echo "Fetch error, API: ", api, ", errors: ", errors + if errors in {expiredToken, badToken, locked}: + invalidate(session) + raise rateLimitError() + elif errors in {rateLimited}: + # rate limit hit, resets after 24 hours + setLimited(session, api) + raise rateLimitError() elif result.startsWith("429 Too Many Requests"): echo "[sessions] 429 error, API: ", api, ", session: ", session.id session.apis[api].remaining = 0 @@ -126,7 +128,7 @@ proc fetch*(url: Uri; api: Api): Future[JsonNode] {.async.} = result = newJNull() let error = result.getError - if error != null: + if error != null and error notin errorsToSkip: echo "Fetch error, API: ", api, ", error: ", error if error in {expiredToken, badToken, locked}: invalidate(session) diff --git a/src/types.nim b/src/types.nim index 6c1f1b6..4e565ee 100644 --- a/src/types.nim +++ b/src/types.nim @@ -45,8 +45,10 @@ type noUserMatches = 17 protectedUser = 22 missingParams = 25 + timeout = 29 couldntAuth = 32 doesntExist = 34 + unauthorized = 37 invalidParam = 47 userNotFound = 50 suspended = 63 @@ -56,6 +58,7 @@ type tweetNotFound = 144 tweetNotAuthorized = 179 forbidden = 200 + badRequest = 214 badToken = 239 locked = 326 noCsrf = 353 From cc28d21a622be1787f120ce536929f736454a4e7 Mon Sep 17 00:00:00 2001 From: SoonKhen OwYong <dude@owyong.sk> Date: Sat, 22 Feb 2025 08:36:04 -0800 Subject: [PATCH 218/409] Add mounting of sessions.jsonl in docker-compose.yml (#1221) --- docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yml b/docker-compose.yml index ec8ade5..3d75751 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,6 +9,7 @@ services: - "127.0.0.1:8080:8080" # Replace with "8080:8080" if you don't use a reverse proxy volumes: - ./nitter.conf:/src/nitter.conf:Z,ro + - ./sessions.jsonl:/src/sessions.jsonl:Z,ro # Run get_sessions.py to get the credentials depends_on: - nitter-redis restart: unless-stopped From cb334a7d6871f6d108051baea3dd73df2c6fd5fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milien=20=28perso=29?= <4016501+unixfox@users.noreply.github.com> Date: Sun, 23 Feb 2025 23:07:45 +0100 Subject: [PATCH 219/409] chore: Revert back to nim 2.0 for alpine ARM64 (#1222) --- Dockerfile.arm64 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 index bf6011f..46352c7 100644 --- a/Dockerfile.arm64 +++ b/Dockerfile.arm64 @@ -1,4 +1,4 @@ -FROM alpine:3.21.2 as nim +FROM alpine:3.20.6 as nim LABEL maintainer="setenforce@protonmail.com" RUN apk --no-cache add libsass-dev pcre gcc git libc-dev nim nimble @@ -13,7 +13,7 @@ RUN nimble build -d:danger -d:lto -d:strip --mm:refc \ && nimble scss \ && nimble md -FROM alpine:3.21.2 +FROM alpine:3.20.6 WORKDIR /src/ RUN apk --no-cache add pcre ca-certificates openssl COPY --from=nim /src/nitter/nitter ./ From 4f9ba9c7d69c33d1273ad7a6cd6415c3d0b5c903 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Tue, 25 Feb 2025 04:28:45 +0000 Subject: [PATCH 220/409] Always print sessions logs --- src/auth.nim | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/auth.nim b/src/auth.nim index bf3c957..0ff40f8 100644 --- a/src/auth.nim +++ b/src/auth.nim @@ -27,7 +27,7 @@ var enableLogging = false template log(str: varargs[string, `$`]) = - if enableLogging: echo "[sessions] ", str.join("") + echo "[sessions] ", str.join("") proc snowflakeToEpoch(flake: int64): int64 = int64(((flake shr 22) + 1288834974657) div 1000) @@ -188,15 +188,15 @@ proc initSessionPool*(cfg: Config; path: string) = enableLogging = cfg.enableDebug if path.endsWith(".json"): - echo "[sessions] ERROR: .json is not supported, the file must be a valid JSONL file ending in .jsonl" + log "ERROR: .json is not supported, the file must be a valid JSONL file ending in .jsonl" quit 1 if not fileExists(path): - echo "[sessions] ERROR: ", path, " not found. This file is required to authenticate API requests." + log "ERROR: ", path, " not found. This file is required to authenticate API requests." quit 1 - log "Parsing JSONL account sessions file: ", path + log "parsing JSONL account sessions file: ", path for line in path.lines: sessionPool.add parseSession(line) - log "Successfully added ", sessionPool.len, " valid account sessions." + log "successfully added ", sessionPool.len, " valid account sessions" From 661be438ec0363dd3b153854765081707ab2d369 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Tue, 25 Feb 2025 05:46:18 +0000 Subject: [PATCH 221/409] Support both web and Android sessions --- src/apiutils.nim | 20 +++++++++++++------- src/experimental/parser/session.nim | 27 ++++++++++++++++++--------- src/experimental/types/session.nim | 4 ++++ src/types.nim | 14 ++++++++++++-- 4 files changed, 47 insertions(+), 18 deletions(-) diff --git a/src/apiutils.nim b/src/apiutils.nim index 65dcc29..9f28f7f 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -1,5 +1,5 @@ # SPDX-License-Identifier: AGPL-3.0-only -import httpclient, asyncdispatch, options, strutils, uri, times, math, tables +import httpclient, asyncdispatch, options, strformat, strutils, uri, times, math, tables import jsony, packedjson, zippy, oauth1 import types, auth, consts, parserutils, http_pool import experimental/types/common @@ -28,21 +28,27 @@ proc getOauthHeader(url, oauthToken, oauthTokenSecret: string): string = return getOauth1RequestHeader(params)["authorization"] -proc genHeaders*(url, oauthToken, oauthTokenSecret: string): HttpHeaders = - let header = getOauthHeader(url, oauthToken, oauthTokenSecret) - +proc genHeaders*(url: string; session: Session): HttpHeaders = result = newHttpHeaders({ "connection": "keep-alive", - "authorization": header, "content-type": "application/json", "x-twitter-active-user": "yes", "authority": "api.x.com", "accept-encoding": "gzip", "accept-language": "en-US,en;q=0.9", "accept": "*/*", - "DNT": "1" + "DNT": "1", }) + case session.kind + of oauth: + result["authorization"] = getOauthHeader(url, session.oauthToken, session.oauthSecret) + of cookie: + result["authorization"] = "Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA" + result["x-twitter-auth-type"] = "OAuth2Session" + result["x-csrf-token"] = session.ct0 + result["cookie"] = &"ct0={session.ct0}; auth_token={session.authToken}" + template fetchImpl(result, fetchBody) {.dirty.} = once: pool = HttpPool() @@ -54,7 +60,7 @@ template fetchImpl(result, fetchBody) {.dirty.} = try: var resp: AsyncResponse - pool.use(genHeaders($url, session.oauthToken, session.oauthSecret)): + pool.use(genHeaders($url, session)): template getContent = resp = await c.get($url) result = await resp.body diff --git a/src/experimental/parser/session.nim b/src/experimental/parser/session.nim index ee9c93e..0eea787 100644 --- a/src/experimental/parser/session.nim +++ b/src/experimental/parser/session.nim @@ -1,15 +1,24 @@ import std/strutils import jsony import ../types/session -from ../../types import Session +from ../../types import Session, SessionKind proc parseSession*(raw: string): Session = - let - session = raw.fromJson(RawSession) - id = session.oauthToken[0 ..< session.oauthToken.find('-')] + let session = raw.fromJson(RawSession) - result = Session( - id: parseBiggestInt(id), - oauthToken: session.oauthToken, - oauthSecret: session.oauthTokenSecret - ) + case session.kind + of "oauth": + let id = session.oauthToken[0 ..< session.oauthToken.find('-')] + result = Session( + kind: oauth, + id: parseBiggestInt(id), + oauthToken: session.oauthToken, + oauthSecret: session.oauthTokenSecret + ) + of "cookie": + result = Session( + kind: cookie, + id: 999, + ct0: session.ct0, + authToken: session.authToken + ) diff --git a/src/experimental/types/session.nim b/src/experimental/types/session.nim index 4165204..c1588cf 100644 --- a/src/experimental/types/session.nim +++ b/src/experimental/types/session.nim @@ -1,4 +1,8 @@ type RawSession* = object + kind*: string oauthToken*: string oauthTokenSecret*: string + ct0*: string + authToken*: string + diff --git a/src/types.nim b/src/types.nim index 4e565ee..a99c23a 100644 --- a/src/types.nim +++ b/src/types.nim @@ -31,10 +31,20 @@ type remaining*: int reset*: int + SessionKind* = enum + oauth + cookie + Session* = ref object + case kind*: SessionKind + of oauth: + oauthToken*: string + oauthSecret*: string + of cookie: + ct0*: string + authToken*: string + id*: int64 - oauthToken*: string - oauthSecret*: string pending*: int limited*: bool limitedAt*: int From 41fa47bfbf3917e9b3ac4f7b49c89a75a7a2bd44 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Tue, 25 Feb 2025 23:36:02 +0000 Subject: [PATCH 222/409] Revert "Support both web and Android sessions" This reverts commit 661be438ec0363dd3b153854765081707ab2d369. --- src/apiutils.nim | 20 +++++++------------- src/experimental/parser/session.nim | 27 +++++++++------------------ src/experimental/types/session.nim | 4 ---- src/types.nim | 14 ++------------ 4 files changed, 18 insertions(+), 47 deletions(-) diff --git a/src/apiutils.nim b/src/apiutils.nim index 9f28f7f..65dcc29 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -1,5 +1,5 @@ # SPDX-License-Identifier: AGPL-3.0-only -import httpclient, asyncdispatch, options, strformat, strutils, uri, times, math, tables +import httpclient, asyncdispatch, options, strutils, uri, times, math, tables import jsony, packedjson, zippy, oauth1 import types, auth, consts, parserutils, http_pool import experimental/types/common @@ -28,27 +28,21 @@ proc getOauthHeader(url, oauthToken, oauthTokenSecret: string): string = return getOauth1RequestHeader(params)["authorization"] -proc genHeaders*(url: string; session: Session): HttpHeaders = +proc genHeaders*(url, oauthToken, oauthTokenSecret: string): HttpHeaders = + let header = getOauthHeader(url, oauthToken, oauthTokenSecret) + result = newHttpHeaders({ "connection": "keep-alive", + "authorization": header, "content-type": "application/json", "x-twitter-active-user": "yes", "authority": "api.x.com", "accept-encoding": "gzip", "accept-language": "en-US,en;q=0.9", "accept": "*/*", - "DNT": "1", + "DNT": "1" }) - case session.kind - of oauth: - result["authorization"] = getOauthHeader(url, session.oauthToken, session.oauthSecret) - of cookie: - result["authorization"] = "Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA" - result["x-twitter-auth-type"] = "OAuth2Session" - result["x-csrf-token"] = session.ct0 - result["cookie"] = &"ct0={session.ct0}; auth_token={session.authToken}" - template fetchImpl(result, fetchBody) {.dirty.} = once: pool = HttpPool() @@ -60,7 +54,7 @@ template fetchImpl(result, fetchBody) {.dirty.} = try: var resp: AsyncResponse - pool.use(genHeaders($url, session)): + pool.use(genHeaders($url, session.oauthToken, session.oauthSecret)): template getContent = resp = await c.get($url) result = await resp.body diff --git a/src/experimental/parser/session.nim b/src/experimental/parser/session.nim index 0eea787..ee9c93e 100644 --- a/src/experimental/parser/session.nim +++ b/src/experimental/parser/session.nim @@ -1,24 +1,15 @@ import std/strutils import jsony import ../types/session -from ../../types import Session, SessionKind +from ../../types import Session proc parseSession*(raw: string): Session = - let session = raw.fromJson(RawSession) + let + session = raw.fromJson(RawSession) + id = session.oauthToken[0 ..< session.oauthToken.find('-')] - case session.kind - of "oauth": - let id = session.oauthToken[0 ..< session.oauthToken.find('-')] - result = Session( - kind: oauth, - id: parseBiggestInt(id), - oauthToken: session.oauthToken, - oauthSecret: session.oauthTokenSecret - ) - of "cookie": - result = Session( - kind: cookie, - id: 999, - ct0: session.ct0, - authToken: session.authToken - ) + result = Session( + id: parseBiggestInt(id), + oauthToken: session.oauthToken, + oauthSecret: session.oauthTokenSecret + ) diff --git a/src/experimental/types/session.nim b/src/experimental/types/session.nim index c1588cf..4165204 100644 --- a/src/experimental/types/session.nim +++ b/src/experimental/types/session.nim @@ -1,8 +1,4 @@ type RawSession* = object - kind*: string oauthToken*: string oauthTokenSecret*: string - ct0*: string - authToken*: string - diff --git a/src/types.nim b/src/types.nim index a99c23a..4e565ee 100644 --- a/src/types.nim +++ b/src/types.nim @@ -31,20 +31,10 @@ type remaining*: int reset*: int - SessionKind* = enum - oauth - cookie - Session* = ref object - case kind*: SessionKind - of oauth: - oauthToken*: string - oauthSecret*: string - of cookie: - ct0*: string - authToken*: string - id*: int64 + oauthToken*: string + oauthSecret*: string pending*: int limited*: bool limitedAt*: int From 83b0f8b55ae7bfb8a19a0bf14de52f30d06b8db6 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Sat, 5 Apr 2025 15:57:14 +0100 Subject: [PATCH 223/409] Retry limited accounts after an hour --- src/auth.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/auth.nim b/src/auth.nim index 0ff40f8..81f248a 100644 --- a/src/auth.nim +++ b/src/auth.nim @@ -6,7 +6,7 @@ import experimental/parser/session # max requests at a time per session to avoid race conditions const maxConcurrentReqs = 2 - dayInSeconds = 24 * 60 * 60 + hourInSeconds = 60 * 60 apiMaxReqs: Table[Api, int] = { Api.search: 50, Api.tweetDetail: 500, @@ -127,7 +127,7 @@ proc isLimited(session: Session; api: Api): bool = return true if session.limited and api != Api.userTweets: - if (epochTime().int - session.limitedAt) > dayInSeconds: + if (epochTime().int - session.limitedAt) > hourInSeconds: session.limited = false log "resetting limit: ", session.id return false From 94c83f38114abaef10c36903fbcd59d78db7a578 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Tue, 15 Apr 2025 02:06:00 +0100 Subject: [PATCH 224/409] Hide ads/promoted tweets Fixes #1234 --- src/parser.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/parser.nim b/src/parser.nim index 6e0be73..01f0341 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -336,7 +336,7 @@ proc parseGraphThread(js: JsonNode): tuple[thread: Chain; self: bool] = let cursor = t{"item", "content", "value"} result.thread.cursor = cursor.getStr result.thread.hasMore = true - elif "tweet" in entryId: + elif "tweet" in entryId and "promoted" notin entryId: let isLegacy = t{"item"}.hasKey("itemContent") (contentKey, resultKey) = if isLegacy: ("itemContent", "tweet_results") @@ -381,7 +381,7 @@ proc parseGraphConversation*(js: JsonNode; tweetId: string; v2=true): Conversati let (thread, self) = parseGraphThread(e) if self: result.after = thread - else: + elif thread.content.len > 0: result.replies.content.add thread elif entryId.startsWith("tombstone"): let id = entryId.getId() From e40c61a6ae76431c570951cc4925f38523b00a82 Mon Sep 17 00:00:00 2001 From: Gabriel Simmer <github@gmem.ca> Date: Thu, 1 May 2025 12:39:05 +0100 Subject: [PATCH 225/409] Find TimelineAddEntries in tweets response (#1251) See https://github.com/zedeus/nitter/issues/1250. Sometimes the API gives us more results and the tweets are no longer at index 0. --- src/parser.nim | 62 ++++++++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/src/parser.nim b/src/parser.nim index 01f0341..5fdfebd 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -364,39 +364,41 @@ proc parseGraphConversation*(js: JsonNode; tweetId: string; v2=true): Conversati if instructions.len == 0: return - for e in instructions[0]{"entries"}: - let entryId = e{"entryId"}.getStr - if entryId.startsWith("tweet"): - with tweetResult, e{"content", contentKey, resultKey, "result"}: - let tweet = parseGraphTweet(tweetResult, not v2) + for i in instructions: + if i{"__typename"}.getStr == "TimelineAddEntries": + for e in i{"entries"}: + let entryId = e{"entryId"}.getStr + if entryId.startsWith("tweet"): + with tweetResult, e{"content", contentKey, resultKey, "result"}: + let tweet = parseGraphTweet(tweetResult, not v2) - if not tweet.available: - tweet.id = parseBiggestInt(entryId.getId()) + if not tweet.available: + tweet.id = parseBiggestInt(entryId.getId()) - if $tweet.id == tweetId: - result.tweet = tweet - else: - result.before.content.add tweet - elif entryId.startsWith("conversationthread"): - let (thread, self) = parseGraphThread(e) - if self: - result.after = thread - elif thread.content.len > 0: - result.replies.content.add thread - elif entryId.startsWith("tombstone"): - let id = entryId.getId() - let tweet = Tweet( - id: parseBiggestInt(id), - available: false, - text: e{"content", contentKey, "tombstoneInfo", "richText"}.getTombstone - ) + if $tweet.id == tweetId: + result.tweet = tweet + else: + result.before.content.add tweet + elif entryId.startsWith("conversationthread"): + let (thread, self) = parseGraphThread(e) + if self: + result.after = thread + elif thread.content.len > 0: + result.replies.content.add thread + elif entryId.startsWith("tombstone"): + let id = entryId.getId() + let tweet = Tweet( + id: parseBiggestInt(id), + available: false, + text: e{"content", contentKey, "tombstoneInfo", "richText"}.getTombstone + ) - if id == tweetId: - result.tweet = tweet - else: - result.before.content.add tweet - elif entryId.startsWith("cursor-bottom"): - result.replies.bottom = e{"content", contentKey, "value"}.getStr + if id == tweetId: + result.tweet = tweet + else: + result.before.content.add tweet + elif entryId.startsWith("cursor-bottom"): + result.replies.bottom = e{"content", contentKey, "value"}.getStr proc parseGraphTimeline*(js: JsonNode; root: string; after=""): Profile = result = Profile(tweets: Timeline(beginning: after.len == 0)) From 662ae90e2246c8a01c811f68750b7e5033e0fa69 Mon Sep 17 00:00:00 2001 From: 0xbarchitect <tbng84@gmail.com> Date: Sun, 12 Oct 2025 14:07:37 +0700 Subject: [PATCH 226/409] Bypass Cloudflare 403 error using cloudscraper (#1291) * Bypass Cloudflare 403 error using cloudscraper * add docs --- tools/get_session.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/tools/get_session.py b/tools/get_session.py index 9ff704d..9f91483 100644 --- a/tools/get_session.py +++ b/tools/get_session.py @@ -3,9 +3,10 @@ import requests import json import sys import pyotp +import cloudscraper -# NOTE: pyotp and requests are dependencies -# > pip install pyotp requests +# NOTE: pyotp, requests and cloudscraper are dependencies +# > pip install pyotp requests cloudscraper TW_CONSUMER_KEY = '3nVuSoBZnx6U4vzUxf5w' TW_CONSUMER_SECRET = 'Bcs59EFbbsdF6Sl9Ng71smgStWEGwXXKSjYvPVt7qys' @@ -41,10 +42,10 @@ def auth(username, password, otp_secret): "X-Twitter-Client-DeviceID": "" } - session = requests.Session() - session.headers = twitter_header + scraper = cloudscraper.create_scraper() + scraper.headers = twitter_header - task1 = session.post( + task1 = scraper.post( 'https://api.twitter.com/1.1/onboarding/task.json', params={ 'flow_name': 'login', @@ -71,9 +72,9 @@ def auth(username, password, otp_secret): } ) - session.headers['att'] = task1.headers.get('att') + scraper.headers['att'] = task1.headers.get('att') - task2 = session.post( + task2 = scraper.post( 'https://api.twitter.com/1.1/onboarding/task.json', json={ "flow_token": task1.json().get('flow_token'), @@ -88,7 +89,7 @@ def auth(username, password, otp_secret): } ) - task3 = session.post( + task3 = scraper.post( 'https://api.twitter.com/1.1/onboarding/task.json', json={ "flow_token": task2.json().get('flow_token'), @@ -109,7 +110,7 @@ def auth(username, password, otp_secret): response_text = t3_subtask["enter_text"]["hint_text"] totp = pyotp.TOTP(otp_secret) generated_code = totp.now() - task4resp = session.post( + task4resp = scraper.post( "https://api.twitter.com/1.1/onboarding/task.json", json={ "flow_token": task3.json().get("flow_token"), From 32b04a772bba866f51d11eed6708d0e41098d926 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Sat, 15 Nov 2025 06:50:48 +0100 Subject: [PATCH 227/409] Include pinned tweets in RSS Fixes #1262 --- src/views/rss.nimf | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/views/rss.nimf b/src/views/rss.nimf index 036a7b9..819f99c 100644 --- a/src/views/rss.nimf +++ b/src/views/rss.nimf @@ -28,6 +28,24 @@ Twitter feed for: ${desc}. Generated by ${cfg.hostname} #end proc # +#proc getTweetsWithPinned(profile: Profile): seq[Tweets] = +#result = profile.tweets.content +#if profile.pinned.isSome and result.len > 0: +# let pinnedTweet = profile.pinned.get +# var inserted = false +# for threadIdx in 0 ..< result.len: +# if not inserted: +# for tweetIdx in 0 ..< result[threadIdx].len: +# if result[threadIdx][tweetIdx].id < pinnedTweet.id: +# result[threadIdx].insert(pinnedTweet, tweetIdx) +# inserted = true +# end if +# end for +# end if +# end for +#end if +#end proc +# #proc renderRssTweet(tweet: Tweet; cfg: Config): string = #let tweet = tweet.retweet.get(tweet) #let urlPrefix = getUrlPrefix(cfg) @@ -106,8 +124,9 @@ Twitter feed for: ${desc}. Generated by ${cfg.hostname} <width>128</width> <height>128</height> </image> -#if profile.tweets.content.len > 0: -${renderRssTweets(profile.tweets.content, cfg, userId=profile.user.id)} +#let tweetsList = getTweetsWithPinned(profile) +#if tweetsList.len > 0: +${renderRssTweets(tweetsList, cfg, userId=profile.user.id)} #end if </channel> </rss> From 9e956150217dd6958fa8cf0f5e916bb080518642 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Sat, 15 Nov 2025 07:01:03 +0100 Subject: [PATCH 228/409] Remove unused skipPinned parameter --- src/routes/rss.nim | 2 +- src/routes/timeline.nim | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/routes/rss.nim b/src/routes/rss.nim index 447f4ad..b0e781d 100644 --- a/src/routes/rss.nim +++ b/src/routes/rss.nim @@ -23,7 +23,7 @@ proc timelineRss*(req: Request; cfg: Config; query: Query): Future[Rss] {.async. names = getNames(name) if names.len == 1: - profile = await fetchProfile(after, query, skipRail=true, skipPinned=true) + profile = await fetchProfile(after, query, skipRail=true) else: var q = query q.fromUser = names diff --git a/src/routes/timeline.nim b/src/routes/timeline.nim index 8cd1fd7..7a10e91 100644 --- a/src/routes/timeline.nim +++ b/src/routes/timeline.nim @@ -27,8 +27,7 @@ template skipIf[T](cond: bool; default; body: Future[T]): Future[T] = else: body -proc fetchProfile*(after: string; query: Query; skipRail=false; - skipPinned=false): Future[Profile] {.async.} = +proc fetchProfile*(after: string; query: Query; skipRail=false): Future[Profile] {.async.} = let name = query.fromUser[0] userId = await getUserId(name) @@ -71,7 +70,7 @@ proc showTimeline*(request: Request; query: Query; cfg: Config; prefs: Prefs; html = renderTweetSearch(timeline, prefs, getPath()) return renderMain(html, request, cfg, prefs, "Multi", rss=rss) - var profile = await fetchProfile(after, query, skipPinned=prefs.hidePins) + var profile = await fetchProfile(after, query) template u: untyped = profile.user if u.suspended: From f89d2329d28c802e88747f7beb28db3b7e90cc5d Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Sat, 15 Nov 2025 22:59:35 +0100 Subject: [PATCH 229/409] Add cookie-based authentication support Fixes #1303 --- src/apiutils.nim | 28 ++++++++++++++++++++------- src/experimental/parser/session.nim | 30 ++++++++++++++++++++--------- src/experimental/types/session.nim | 3 +++ src/types.nim | 13 +++++++++++-- 4 files changed, 56 insertions(+), 18 deletions(-) diff --git a/src/apiutils.nim b/src/apiutils.nim index 65dcc29..875fcb0 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -28,12 +28,12 @@ proc getOauthHeader(url, oauthToken, oauthTokenSecret: string): string = return getOauth1RequestHeader(params)["authorization"] -proc genHeaders*(url, oauthToken, oauthTokenSecret: string): HttpHeaders = - let header = getOauthHeader(url, oauthToken, oauthTokenSecret) +proc getCookieHeader(authToken, ct0: string): string = + "auth_token=" & authToken & "; ct0=" & ct0 +proc genHeaders*(session: Session, url: string): HttpHeaders = result = newHttpHeaders({ "connection": "keep-alive", - "authorization": header, "content-type": "application/json", "x-twitter-active-user": "yes", "authority": "api.x.com", @@ -43,18 +43,32 @@ proc genHeaders*(url, oauthToken, oauthTokenSecret: string): HttpHeaders = "DNT": "1" }) + case session.kind + of SessionKind.oauth: + result["authorization"] = getOauthHeader(url, session.oauthToken, session.oauthSecret) + of SessionKind.cookie: + result["cookie"] = getCookieHeader(session.authToken, session.ct0) + result["x-csrf-token"] = session.ct0 + result["x-twitter-auth-type"] = "OAuth2Session" + template fetchImpl(result, fetchBody) {.dirty.} = once: pool = HttpPool() var session = await getSession(api) - if session.oauthToken.len == 0: - echo "[sessions] Empty oauth token, session: ", session.id - raise rateLimitError() + case session.kind + of SessionKind.oauth: + if session.oauthToken.len == 0: + echo "[sessions] Empty oauth token, session: ", session.id + raise rateLimitError() + of SessionKind.cookie: + if session.authToken.len == 0 or session.ct0.len == 0: + echo "[sessions] Empty cookie credentials, session: ", session.id + raise rateLimitError() try: var resp: AsyncResponse - pool.use(genHeaders($url, session.oauthToken, session.oauthSecret)): + pool.use(genHeaders(session, $url)): template getContent = resp = await c.get($url) result = await resp.body diff --git a/src/experimental/parser/session.nim b/src/experimental/parser/session.nim index ee9c93e..db72e85 100644 --- a/src/experimental/parser/session.nim +++ b/src/experimental/parser/session.nim @@ -1,15 +1,27 @@ import std/strutils import jsony import ../types/session -from ../../types import Session +from ../../types import Session, SessionKind proc parseSession*(raw: string): Session = - let - session = raw.fromJson(RawSession) - id = session.oauthToken[0 ..< session.oauthToken.find('-')] + let session = raw.fromJson(RawSession) + let kind = if session.kind == "": "oauth" else: session.kind - result = Session( - id: parseBiggestInt(id), - oauthToken: session.oauthToken, - oauthSecret: session.oauthTokenSecret - ) + case kind + of "oauth": + let id = session.oauthToken[0 ..< session.oauthToken.find('-')] + result = Session( + kind: SessionKind.oauth, + id: parseBiggestInt(id), + oauthToken: session.oauthToken, + oauthSecret: session.oauthTokenSecret + ) + of "cookie": + result = Session( + kind: SessionKind.cookie, + id: 999, + authToken: session.authToken, + ct0: session.ct0 + ) + else: + raise newException(ValueError, "Unknown session kind: " & kind) diff --git a/src/experimental/types/session.nim b/src/experimental/types/session.nim index 4165204..c0cb58f 100644 --- a/src/experimental/types/session.nim +++ b/src/experimental/types/session.nim @@ -1,4 +1,7 @@ type RawSession* = object + kind*: string oauthToken*: string oauthTokenSecret*: string + authToken*: string + ct0*: string diff --git a/src/types.nim b/src/types.nim index 4e565ee..5755f65 100644 --- a/src/types.nim +++ b/src/types.nim @@ -31,14 +31,23 @@ type remaining*: int reset*: int + SessionKind* = enum + oauth + cookie + Session* = ref object id*: int64 - oauthToken*: string - oauthSecret*: string pending*: int limited*: bool limitedAt*: int apis*: Table[Api, RateLimit] + case kind*: SessionKind + of oauth: + oauthToken*: string + oauthSecret*: string + of cookie: + authToken*: string + ct0*: string Error* = enum null = 0 From 3768762fca4a89ab7e2aaa1bcf934ec438ec9041 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Sun, 16 Nov 2025 05:02:57 +0100 Subject: [PATCH 230/409] Add script for generating cookies --- tools/get_web_session.py | 143 +++++++++++++++++++++++++++++++++++++++ tools/requirements.txt | 2 + 2 files changed, 145 insertions(+) create mode 100644 tools/get_web_session.py create mode 100644 tools/requirements.txt diff --git a/tools/get_web_session.py b/tools/get_web_session.py new file mode 100644 index 0000000..36a40b0 --- /dev/null +++ b/tools/get_web_session.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python3 +""" +Authenticates with X.com/Twitter and extracts session cookies for use with Nitter. +Handles 2FA, extracts user info, and outputs clean JSON for sessions.jsonl. + +Requirements: + pip install -r tools/requirements.txt + +Usage: + python3 tools/get_web_session.py <username> <password> [totp_seed] [--append sessions.jsonl] [--headless] + +Examples: + # Output to terminal + python3 tools/get_web_session.py myusername mypassword TOTP_BASE32_SECRET + + # Append to sessions.jsonl + python3 tools/get_web_session.py myusername mypassword TOTP_SECRET --append sessions.jsonl + + # Headless mode (may increase detection risk) + python3 tools/get_web_session.py myusername mypassword TOTP_SECRET --headless + +Output: + {"kind": "cookie", "username": "...", "id": "...", "auth_token": "...", "ct0": "..."} +""" + +import sys +import json +import asyncio +import pyotp +import nodriver as uc + + +async def login_and_get_cookies(username, password, totp_seed=None, headless=False): + """Authenticate with X.com and extract session cookies""" + # Note: headless mode may increase detection risk from bot-detection systems + browser = await uc.start(headless=headless) + tab = await browser.get('https://x.com/i/flow/login') + + try: + # Enter username + print('[*] Entering username...', file=sys.stderr) + username_input = await tab.find('input[autocomplete="username"]', timeout=10) + await username_input.send_keys(username + '\n') + await asyncio.sleep(1) + + # Enter password + print('[*] Entering password...', file=sys.stderr) + password_input = await tab.find('input[autocomplete="current-password"]', timeout=15) + await password_input.send_keys(password + '\n') + await asyncio.sleep(2) + + # Handle 2FA if needed + page_content = await tab.get_content() + if 'verification code' in page_content or 'Enter code' in page_content: + if not totp_seed: + raise Exception('2FA required but no TOTP seed provided') + + print('[*] 2FA detected, entering code...', file=sys.stderr) + totp_code = pyotp.TOTP(totp_seed).now() + code_input = await tab.select('input[type="text"]') + await code_input.send_keys(totp_code + '\n') + await asyncio.sleep(3) + + # Get cookies + print('[*] Retrieving cookies...', file=sys.stderr) + for _ in range(20): # 20 second timeout + cookies = await browser.cookies.get_all() + cookies_dict = {cookie.name: cookie.value for cookie in cookies} + + if 'auth_token' in cookies_dict and 'ct0' in cookies_dict: + print('[*] Found both cookies', file=sys.stderr) + + # Extract ID from twid cookie (may be URL-encoded) + user_id = None + if 'twid' in cookies_dict: + twid = cookies_dict['twid'] + # Try to extract the ID from twid (format: u%3D<id> or u=<id>) + if 'u%3D' in twid: + user_id = twid.split('u%3D')[1].split('&')[0] + elif 'u=' in twid: + user_id = twid.split('u=')[1].split('&')[0] + + cookies_dict['username'] = username + if user_id: + cookies_dict['id'] = user_id + + return cookies_dict + + await asyncio.sleep(1) + + raise Exception('Timeout waiting for cookies') + + finally: + browser.stop() + + +async def main(): + if len(sys.argv) < 3: + print('Usage: python3 twitter-auth.py username password [totp_seed] [--append sessions.jsonl] [--headless]') + sys.exit(1) + + username = sys.argv[1] + password = sys.argv[2] + totp_seed = None + append_file = None + headless = False + + # Parse optional arguments + for i, arg in enumerate(sys.argv[3:], 3): + if arg == '--append' and i + 1 < len(sys.argv): + append_file = sys.argv[i + 1] + elif arg == '--headless': + headless = True + elif not arg.startswith('--'): + totp_seed = arg + + try: + cookies = await login_and_get_cookies(username, password, totp_seed, headless) + session = { + 'kind': 'cookie', + 'username': cookies['username'], + 'id': cookies.get('id'), + 'auth_token': cookies['auth_token'], + 'ct0': cookies['ct0'] + } + output = json.dumps(session) + + if append_file: + with open(append_file, 'a') as f: + f.write(output + '\n') + print(f'✓ Session appended to {append_file}', file=sys.stderr) + else: + print(output) + + os._exit(0) + + except Exception as error: + print(f'[!] Error: {error}', file=sys.stderr) + sys.exit(1) + + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/tools/requirements.txt b/tools/requirements.txt new file mode 100644 index 0000000..4827475 --- /dev/null +++ b/tools/requirements.txt @@ -0,0 +1,2 @@ +nodriver>=0.48.0 +pyotp From 6fe850b2c62dc664ee49645e8aad48bc6db9328e Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Sun, 16 Nov 2025 05:03:01 +0100 Subject: [PATCH 231/409] Add optional cookie session fields --- src/experimental/parser/session.nim | 3 ++- src/experimental/types/session.nim | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/experimental/parser/session.nim b/src/experimental/parser/session.nim index db72e85..bb31d83 100644 --- a/src/experimental/parser/session.nim +++ b/src/experimental/parser/session.nim @@ -17,9 +17,10 @@ proc parseSession*(raw: string): Session = oauthSecret: session.oauthTokenSecret ) of "cookie": + let id = if session.id.len > 0: parseBiggestInt(session.id) else: 0 result = Session( kind: SessionKind.cookie, - id: 999, + id: id, authToken: session.authToken, ct0: session.ct0 ) diff --git a/src/experimental/types/session.nim b/src/experimental/types/session.nim index c0cb58f..dd6be22 100644 --- a/src/experimental/types/session.nim +++ b/src/experimental/types/session.nim @@ -1,6 +1,8 @@ type RawSession* = object kind*: string + username*: string + id*: string oauthToken*: string oauthTokenSecret*: string authToken*: string From 4fc7b873c44c0dcd358bb969965345712610b8c5 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Sun, 16 Nov 2025 05:22:45 +0100 Subject: [PATCH 232/409] Use dynamic rate limits from API responses --- src/apiutils.nim | 4 +++- src/auth.nim | 13 +++++++------ src/types.nim | 1 + 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/apiutils.nim b/src/apiutils.nim index 875fcb0..f6fa9f1 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -7,6 +7,7 @@ import experimental/types/common const rlRemaining = "x-rate-limit-remaining" rlReset = "x-rate-limit-reset" + rlLimit = "x-rate-limit-limit" errorsToSkip = {doesntExist, tweetNotFound, timeout, unauthorized, badRequest} var pool: HttpPool @@ -83,7 +84,8 @@ template fetchImpl(result, fetchBody) {.dirty.} = let remaining = parseInt(resp.headers[rlRemaining]) reset = parseInt(resp.headers[rlReset]) - session.setRateLimit(api, remaining, reset) + limit = parseInt(resp.headers[rlLimit]) + session.setRateLimit(api, remaining, reset, limit) if result.len > 0: if resp.headers.getOrDefault("content-encoding") == "gzip": diff --git a/src/auth.nim b/src/auth.nim index 81f248a..9f9fe8a 100644 --- a/src/auth.nim +++ b/src/auth.nim @@ -57,7 +57,8 @@ proc getSessionPoolHealth*(): JsonNode = for api in session.apis.keys: let apiStatus = session.apis[api] - reqs = apiMaxReqs[api] - apiStatus.remaining + limit = if apiStatus.limit > 0: apiStatus.limit else: apiMaxReqs.getOrDefault(api, 0) + reqs = limit - apiStatus.remaining # no requests made with this session and endpoint since the limit reset if apiStatus.reset < now: @@ -172,17 +173,17 @@ proc setLimited*(session: Session; api: Api) = session.limitedAt = epochTime().int log "rate limited by api: ", api, ", reqs left: ", session.apis[api].remaining, ", id: ", session.id -proc setRateLimit*(session: Session; api: Api; remaining, reset: int) = +proc setRateLimit*(session: Session; api: Api; remaining, reset, limit: int) = # avoid undefined behavior in race conditions if api in session.apis: - let limit = session.apis[api] - if limit.reset >= reset and limit.remaining < remaining: + let rateLimit = session.apis[api] + if rateLimit.reset >= reset and rateLimit.remaining < remaining: return - if limit.reset == reset and limit.remaining >= remaining: + if rateLimit.reset == reset and rateLimit.remaining >= remaining: session.apis[api].remaining = remaining return - session.apis[api] = RateLimit(remaining: remaining, reset: reset) + session.apis[api] = RateLimit(limit: limit, remaining: remaining, reset: reset) proc initSessionPool*(cfg: Config; path: string) = enableLogging = cfg.enableDebug diff --git a/src/types.nim b/src/types.nim index 5755f65..a138dae 100644 --- a/src/types.nim +++ b/src/types.nim @@ -28,6 +28,7 @@ type userMedia RateLimit* = object + limit*: int remaining*: int reset*: int From 5aa0b65fea43dcdabe9d191bb67df3e187796bf4 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Sun, 16 Nov 2025 05:24:23 +0100 Subject: [PATCH 233/409] Add bearer token for cookie-based auth --- src/apiutils.nim | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/apiutils.nim b/src/apiutils.nim index f6fa9f1..0745ee6 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -37,20 +37,23 @@ proc genHeaders*(session: Session, url: string): HttpHeaders = "connection": "keep-alive", "content-type": "application/json", "x-twitter-active-user": "yes", + "x-twitter-client-language": "en", "authority": "api.x.com", "accept-encoding": "gzip", "accept-language": "en-US,en;q=0.9", "accept": "*/*", - "DNT": "1" + "DNT": "1", + "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36" }) case session.kind of SessionKind.oauth: result["authorization"] = getOauthHeader(url, session.oauthToken, session.oauthSecret) of SessionKind.cookie: - result["cookie"] = getCookieHeader(session.authToken, session.ct0) - result["x-csrf-token"] = session.ct0 + result["authorization"] = "Bearer AAAAAAAAAAAAAAAAAAAAAFQODgEAAAAAVHTp76lzh3rFzcHbmHVvQxYYpTw%3DckAlMINMjmCwxUcaXbAN4XqJVdgMJaHqNOFgPMK0zN1qLqLQCF" result["x-twitter-auth-type"] = "OAuth2Session" + result["x-csrf-token"] = session.ct0 + result["cookie"] = getCookieHeader(session.authToken, session.ct0) template fetchImpl(result, fetchBody) {.dirty.} = once: From bf36fc471b7335d60168f1f9a08be9288f928ad9 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Sun, 16 Nov 2025 06:04:46 +0100 Subject: [PATCH 234/409] Update tests --- tests/test_card.py | 10 +++++----- tests/test_quote.py | 6 ------ tests/test_tweet.py | 8 -------- 3 files changed, 5 insertions(+), 19 deletions(-) diff --git a/tests/test_card.py b/tests/test_card.py index 05b55f6..504c079 100644 --- a/tests/test_card.py +++ b/tests/test_card.py @@ -26,14 +26,14 @@ no_thumb = [ 'lnkd.in'], ['Thom_Wolf/status/1122466524860702729', - 'GitHub - NVIDIA/Megatron-LM: Ongoing research training transformer models at scale', - 'Ongoing research training transformer models at scale - NVIDIA/Megatron-LM', + 'GitHub - facebookresearch/fairseq: Facebook AI Research Sequence-to-Sequence Toolkit written in', + '', 'github.com'], ['brent_p/status/1088857328680488961', - 'Hts Nim Sugar', - 'hts-nim is a library that allows one to use htslib via the nim programming language. Nim is a garbage-collected language that compiles to C and often has similar performance. I have become very...', - 'brentp.github.io'], + 'GitHub - brentp/hts-nim: nim wrapper for htslib for parsing genomics data files', + '', + 'github.com'], ['voidtarget/status/1133028231672582145', 'sinkingsugar/nimqt-example', diff --git a/tests/test_quote.py b/tests/test_quote.py index 4921c21..53ad79f 100644 --- a/tests/test_quote.py +++ b/tests/test_quote.py @@ -2,12 +2,6 @@ from base import BaseTestCase, Quote, Conversation from parameterized import parameterized text = [ - ['elonmusk/status/1138136540096319488', - 'TREV PAGE', '@Model3Owners', - """As of March 58.4% of new car sales in Norway are electric. - -What are we doing wrong? reuters.com/article/us-norwa…"""], - ['nim_lang/status/1491461266849808397#m', 'Nim', '@nim_lang', """What's better than Nim 1.6.0? diff --git a/tests/test_tweet.py b/tests/test_tweet.py index 839e6c5..bf9e267 100644 --- a/tests/test_tweet.py +++ b/tests/test_tweet.py @@ -28,14 +28,6 @@ invalid = [ ] multiline = [ - [400897186990284800, 'mobile_test_3', - """ -♔ - KEEP - CALM - AND -CLICHÉ - ON"""], [1718660434457239868, 'WebDesignMuseum', """ Happy 32nd Birthday HTML tags! From 55d4469401479f7feed166b745821f7abca8fb9d Mon Sep 17 00:00:00 2001 From: 0xCathiefish <72328723+0xcathiefish@users.noreply.github.com> Date: Mon, 17 Nov 2025 04:18:38 +0800 Subject: [PATCH 235/409] fix: correct argument parsing for --append flag in get_web_session.py (#1305) * fix: correct argument parsing for --append flag * fix: strip quotes from extracted user_id in twid cookie --- tools/get_web_session.py | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/tools/get_web_session.py b/tools/get_web_session.py index 36a40b0..502c7f4 100644 --- a/tools/get_web_session.py +++ b/tools/get_web_session.py @@ -28,6 +28,7 @@ import json import asyncio import pyotp import nodriver as uc +import os async def login_and_get_cookies(username, password, totp_seed=None, headless=False): @@ -76,9 +77,9 @@ async def login_and_get_cookies(username, password, totp_seed=None, headless=Fal twid = cookies_dict['twid'] # Try to extract the ID from twid (format: u%3D<id> or u=<id>) if 'u%3D' in twid: - user_id = twid.split('u%3D')[1].split('&')[0] + user_id = twid.split('u%3D')[1].split('&')[0].strip('"') elif 'u=' in twid: - user_id = twid.split('u=')[1].split('&')[0] + user_id = twid.split('u=')[1].split('&')[0].strip('"') cookies_dict['username'] = username if user_id: @@ -106,13 +107,27 @@ async def main(): headless = False # Parse optional arguments - for i, arg in enumerate(sys.argv[3:], 3): - if arg == '--append' and i + 1 < len(sys.argv): - append_file = sys.argv[i + 1] + i = 3 + while i < len(sys.argv): + arg = sys.argv[i] + if arg == '--append': + if i + 1 < len(sys.argv): + append_file = sys.argv[i + 1] + i += 2 # Skip '--append' and filename + else: + print('[!] Error: --append requires a filename', file=sys.stderr) + sys.exit(1) elif arg == '--headless': headless = True + i += 1 elif not arg.startswith('--'): - totp_seed = arg + if totp_seed is None: + totp_seed = arg + i += 1 + else: + # Unkown args + print(f'[!] Warning: Unknown argument: {arg}', file=sys.stderr) + i += 1 try: cookies = await login_and_get_cookies(username, password, totp_seed, headless) From 68fc7b71c86d34f43cbfb9147c22babe8604b305 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Sun, 16 Nov 2025 23:21:15 +0100 Subject: [PATCH 236/409] Fix media support for cookie sessions Fixes #1304 --- src/api.nim | 38 +++++++++++------ src/apiutils.nim | 44 +++++++++++++------ src/consts.nim | 58 ++++++++++++++++--------- src/parser.nim | 109 +++++++++++++++++++++++++++++++++-------------- src/types.nim | 6 ++- 5 files changed, 174 insertions(+), 81 deletions(-) diff --git a/src/api.nim b/src/api.nim index 479cb3d..c0efa58 100644 --- a/src/api.nim +++ b/src/api.nim @@ -4,6 +4,15 @@ import packedjson import types, query, formatters, consts, apiutils, parser import experimental/parser as newParser +proc mediaUrl(id: string; cursor: string): SessionAwareUrl = + let + cookieVariables = userMediaVariables % [id, cursor] + oauthVariables = userTweetsVariables % [id, cursor] + result = SessionAwareUrl( + cookieUrl: graphUserMedia ? {"variables": cookieVariables, "features": gqlFeatures}, + oauthUrl: graphUserMediaV2 ? {"variables": oauthVariables, "features": gqlFeatures} + ) + proc getGraphUser*(username: string): Future[User] {.async.} = if username.len == 0: return let @@ -26,12 +35,14 @@ proc getGraphUserTweets*(id: string; kind: TimelineKind; after=""): Future[Profi cursor = if after.len > 0: "\"cursor\":\"$1\"," % after else: "" variables = userTweetsVariables % [id, cursor] params = {"variables": variables, "features": gqlFeatures} - (url, apiId) = case kind - of TimelineKind.tweets: (graphUserTweets, Api.userTweets) - of TimelineKind.replies: (graphUserTweetsAndReplies, Api.userTweetsAndReplies) - of TimelineKind.media: (graphUserMedia, Api.userMedia) - js = await fetch(url ? params, apiId) - result = parseGraphTimeline(js, "user", after) + js = case kind + of TimelineKind.tweets: + await fetch(graphUserTweets ? params, Api.userTweets) + of TimelineKind.replies: + await fetch(graphUserTweetsAndReplies ? params, Api.userTweetsAndReplies) + of TimelineKind.media: + await fetch(mediaUrl(id, cursor), Api.userMedia) + result = parseGraphTimeline(js, after) proc getGraphListTweets*(id: string; after=""): Future[Timeline] {.async.} = if id.len == 0: return @@ -40,19 +51,21 @@ proc getGraphListTweets*(id: string; after=""): Future[Timeline] {.async.} = variables = listTweetsVariables % [id, cursor] params = {"variables": variables, "features": gqlFeatures} js = await fetch(graphListTweets ? params, Api.listTweets) - result = parseGraphTimeline(js, "list", after).tweets + result = parseGraphTimeline(js, after).tweets proc getGraphListBySlug*(name, list: string): Future[List] {.async.} = let variables = %*{"screenName": name, "listSlug": list} params = {"variables": $variables, "features": gqlFeatures} - result = parseGraphList(await fetch(graphListBySlug ? params, Api.listBySlug)) + url = graphListBySlug ? params + result = parseGraphList(await fetch(url, Api.listBySlug)) proc getGraphList*(id: string): Future[List] {.async.} = let variables = """{"listId": "$1"}""" % id params = {"variables": variables, "features": gqlFeatures} - result = parseGraphList(await fetch(graphListById ? params, Api.list)) + url = graphListById ? params + result = parseGraphList(await fetch(url, Api.list)) proc getGraphListMembers*(list: List; after=""): Future[Result[User]] {.async.} = if list.id.len == 0: return @@ -138,11 +151,8 @@ proc getGraphUserSearch*(query: Query; after=""): Future[Result[User]] {.async.} proc getPhotoRail*(id: string): Future[PhotoRail] {.async.} = if id.len == 0: return - let - variables = userTweetsVariables % [id, ""] - params = {"variables": variables, "features": gqlFeatures} - url = graphUserMedia ? params - result = parseGraphPhotoRail(await fetch(url, Api.userMedia)) + let js = await fetch(mediaUrl(id, ""), Api.userMedia) + result = parseGraphPhotoRail(js) proc resolve*(url: string; prefs: Prefs): Future[string] {.async.} = let client = newAsyncHttpClient(maxRedirects=0) diff --git a/src/apiutils.nim b/src/apiutils.nim index 0745ee6..ae459fa 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -55,21 +55,22 @@ proc genHeaders*(session: Session, url: string): HttpHeaders = result["x-csrf-token"] = session.ct0 result["cookie"] = getCookieHeader(session.authToken, session.ct0) +proc getAndValidateSession*(api: Api): Future[Session] {.async.} = + result = await getSession(api) + case result.kind + of SessionKind.oauth: + if result.oauthToken.len == 0: + echo "[sessions] Empty oauth token, session: ", result.id + raise rateLimitError() + of SessionKind.cookie: + if result.authToken.len == 0 or result.ct0.len == 0: + echo "[sessions] Empty cookie credentials, session: ", result.id + raise rateLimitError() + template fetchImpl(result, fetchBody) {.dirty.} = once: pool = HttpPool() - var session = await getSession(api) - case session.kind - of SessionKind.oauth: - if session.oauthToken.len == 0: - echo "[sessions] Empty oauth token, session: ", session.id - raise rateLimitError() - of SessionKind.cookie: - if session.authToken.len == 0 or session.ct0.len == 0: - echo "[sessions] Empty cookie credentials, session: ", session.id - raise rateLimitError() - try: var resp: AsyncResponse pool.use(genHeaders(session, $url)): @@ -136,9 +137,17 @@ template retry(bod) = echo "[sessions] Rate limited, retrying ", api, " request..." bod -proc fetch*(url: Uri; api: Api): Future[JsonNode] {.async.} = +proc fetch*(url: Uri | SessionAwareUrl; api: Api): Future[JsonNode] {.async.} = retry: - var body: string + var + body: string + session = await getAndValidateSession(api) + + when url is SessionAwareUrl: + let url = case session.kind + of SessionKind.oauth: url.oauthUrl + of SessionKind.cookie: url.cookieUrl + fetchImpl body: if body.startsWith('{') or body.startsWith('['): result = parseJson(body) @@ -153,8 +162,15 @@ proc fetch*(url: Uri; api: Api): Future[JsonNode] {.async.} = invalidate(session) raise rateLimitError() -proc fetchRaw*(url: Uri; api: Api): Future[string] {.async.} = +proc fetchRaw*(url: Uri | SessionAwareUrl; api: Api): Future[string] {.async.} = retry: + var session = await getAndValidateSession(api) + + when url is SessionAwareUrl: + let url = case session.kind + of SessionKind.oauth: url.oauthUrl + of SessionKind.cookie: url.cookieUrl + fetchImpl result: if not (result.startsWith('{') or result.startsWith('[')): echo resp.status, ": ", result, " --- url: ", url diff --git a/src/consts.nim b/src/consts.nim index 7c67706..c8ae8d2 100644 --- a/src/consts.nim +++ b/src/consts.nim @@ -11,7 +11,8 @@ const graphUserById* = gql / "oPppcargziU1uDQHAUmH-A/UserResultByIdQuery" graphUserTweets* = gql / "JLApJKFY0MxGTzCoK6ps8Q/UserWithProfileTweetsQueryV2" graphUserTweetsAndReplies* = gql / "Y86LQY7KMvxn5tu3hFTyPg/UserWithProfileTweetsAndRepliesQueryV2" - graphUserMedia* = gql / "PDfFf8hGeJvUCiTyWtw4wQ/MediaTimelineV2" + graphUserMedia* = gql / "36oKqyQ7E_9CmtONGjJRsA/UserMedia" + graphUserMediaV2* = gql / "PDfFf8hGeJvUCiTyWtw4wQ/MediaTimelineV2" graphTweet* = gql / "Vorskcd2tZ-tc4Gx3zbk4Q/ConversationTimelineV2" graphTweetResult* = gql / "sITyJdhRPpvpEjg4waUmTA/TweetResultByIdQuery" graphSearchTimeline* = gql / "KI9jCXUx3Ymt-hDKLOZb9Q/SearchTimeline" @@ -25,28 +26,28 @@ const "blue_business_profile_image_shape_enabled": false, "creator_subscriptions_subscription_count_enabled": false, "creator_subscriptions_tweet_preview_api_enabled": true, - "freedom_of_speech_not_reach_fetch_enabled": false, - "graphql_is_translatable_rweb_tweet_is_translatable_enabled": false, + "freedom_of_speech_not_reach_fetch_enabled": true, + "graphql_is_translatable_rweb_tweet_is_translatable_enabled": true, "hidden_profile_likes_enabled": false, "highlights_tweets_tab_ui_enabled": false, "interactive_text_enabled": false, "longform_notetweets_consumption_enabled": true, - "longform_notetweets_inline_media_enabled": false, + "longform_notetweets_inline_media_enabled": true, "longform_notetweets_richtext_consumption_enabled": true, - "longform_notetweets_rich_text_read_enabled": false, - "responsive_web_edit_tweet_api_enabled": false, + "longform_notetweets_rich_text_read_enabled": true, + "responsive_web_edit_tweet_api_enabled": true, "responsive_web_enhance_cards_enabled": false, "responsive_web_graphql_exclude_directive_enabled": true, "responsive_web_graphql_skip_user_profile_image_extensions_enabled": false, - "responsive_web_graphql_timeline_navigation_enabled": false, + "responsive_web_graphql_timeline_navigation_enabled": true, "responsive_web_media_download_video_enabled": false, "responsive_web_text_conversations_enabled": false, - "responsive_web_twitter_article_tweet_consumption_enabled": false, + "responsive_web_twitter_article_tweet_consumption_enabled": true, "responsive_web_twitter_blue_verified_badge_is_enabled": true, "rweb_lists_timeline_redesign_enabled": true, "spaces_2022_h2_clipping": true, "spaces_2022_h2_spaces_communities": true, - "standardized_nudges_misinfo": false, + "standardized_nudges_misinfo": true, "subscriptions_verification_info_enabled": true, "subscriptions_verification_info_reason_enabled": true, "subscriptions_verification_info_verified_since_enabled": true, @@ -55,28 +56,34 @@ const "super_follow_tweet_api_enabled": false, "super_follow_user_api_enabled": false, "tweet_awards_web_tipping_enabled": false, - "tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled": false, + "tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled": true, "tweetypie_unmention_optimization_enabled": false, "unified_cards_ad_metadata_container_dynamic_card_content_query_enabled": false, "verified_phone_label_enabled": false, "vibe_api_enabled": false, - "view_counts_everywhere_api_enabled": false, + "view_counts_everywhere_api_enabled": true, "premium_content_api_read_enabled": false, - "communities_web_enable_tweet_community_results_fetch": false, - "responsive_web_jetfuel_frame": false, + "communities_web_enable_tweet_community_results_fetch": true, + "responsive_web_jetfuel_frame": true, "responsive_web_grok_analyze_button_fetch_trends_enabled": false, - "responsive_web_grok_image_annotation_enabled": false, - "rweb_tipjar_consumption_enabled": false, - "profile_label_improvements_pcf_label_in_post_enabled": false, + "responsive_web_grok_image_annotation_enabled": true, + "responsive_web_grok_imagine_annotation_enabled": true, + "rweb_tipjar_consumption_enabled": true, + "profile_label_improvements_pcf_label_in_post_enabled": true, "creator_subscriptions_quote_tweet_preview_enabled": false, - "c9s_tweet_anatomy_moderator_badge_enabled": false, - "responsive_web_grok_analyze_post_followups_enabled": false, + "c9s_tweet_anatomy_moderator_badge_enabled": true, + "responsive_web_grok_analyze_post_followups_enabled": true, "rweb_video_timestamps_enabled": false, - "responsive_web_grok_share_attachment_enabled": false, - "articles_preview_enabled": false, + "responsive_web_grok_share_attachment_enabled": true, + "articles_preview_enabled": true, "immersive_video_status_linkable_timestamps": false, "articles_api_enabled": false, - "responsive_web_grok_analysis_button_from_backend": false + "responsive_web_grok_analysis_button_from_backend": true, + "rweb_video_screen_enabled": false, + "payments_enabled": false, + "responsive_web_profile_redirect_enabled": false, + "responsive_web_grok_show_grok_translated_post": false, + "responsive_web_grok_community_note_auto_translation_is_enabled": false }""".replace(" ", "").replace("\n", "") tweetVariables* = """{ @@ -110,3 +117,12 @@ const "rest_id": "$1", $2 "count": 20 }""" + + userMediaVariables* = """{ + "userId": "$1", $2 + "count": 20, + "includePromotedContent": false, + "withClientEventToken": false, + "withBirdwatchNotes": false, + "withVoice": true +}""".replace(" ", "").replace("\n", "") diff --git a/src/parser.nim b/src/parser.nim index 5fdfebd..af81469 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -6,6 +6,22 @@ import experimental/parser/unifiedcard proc parseGraphTweet(js: JsonNode; isLegacy=false): Tweet +proc extractTweetsFromEntry(e: JsonNode; entryId: string): seq[Tweet] = + if e{"content", "items"}.notNull: + for item in e{"content", "items"}: + with tweetResult, item{"item", "itemContent", "tweet_results", "result"}: + var tweet = parseGraphTweet(tweetResult, false) + if not tweet.available: + tweet.id = parseBiggestInt(item{"entryId"}.getStr.getId()) + result.add tweet + return + + with tweetResult, e{"content", "content", "tweetResult", "result"}: + var tweet = parseGraphTweet(tweetResult, false) + if not tweet.available: + tweet.id = parseBiggestInt(entryId.getId()) + result.add tweet + proc parseUser(js: JsonNode; id=""): User = if js.isNull: return result = User( @@ -32,10 +48,26 @@ proc parseGraphUser(js: JsonNode): User = var user = js{"user_result", "result"} if user.isNull: user = ? js{"user_results", "result"} + + if user.isNull: + if js{"core"}.notNull and js{"legacy"}.notNull: + user = js + else: + return + result = parseUser(user{"legacy"}, user{"rest_id"}.getStr) - if result.verifiedType == VerifiedType.none and user{"is_blue_verified"}.getBool(false): - result.verifiedType = blue + # fallback to support UserMedia/recent GraphQL updates + if result.username.len == 0 and user{"core", "screen_name"}.notNull: + result.username = user{"core", "screen_name"}.getStr + result.fullname = user{"core", "name"}.getStr + result.userPic = user{"avatar", "image_url"}.getImageStr.replace("_normal", "") + + if user{"is_blue_verified"}.getBool(false): + result.verifiedType = blue + elif user{"verification", "verified_type"}.notNull: + let verifiedType = user{"verification", "verified_type"}.getStr("None") + result.verifiedType = parseEnum[VerifiedType](verifiedType) proc parseGraphList*(js: JsonNode): List = if js.isNull: return @@ -400,31 +432,43 @@ proc parseGraphConversation*(js: JsonNode; tweetId: string; v2=true): Conversati elif entryId.startsWith("cursor-bottom"): result.replies.bottom = e{"content", contentKey, "value"}.getStr -proc parseGraphTimeline*(js: JsonNode; root: string; after=""): Profile = +proc parseGraphTimeline*(js: JsonNode; after=""): Profile = result = Profile(tweets: Timeline(beginning: after.len == 0)) let instructions = - if root == "list": ? js{"data", "list", "timeline_response", "timeline", "instructions"} - else: ? js{"data", "user_result", "result", "timeline_response", "timeline", "instructions"} + if js{"data", "list"}.notNull: + ? js{"data", "list", "timeline_response", "timeline", "instructions"} + elif js{"data", "user"}.notNull: + ? js{"data", "user", "result", "timeline", "timeline", "instructions"} + else: + ? js{"data", "user_result", "result", "timeline_response", "timeline", "instructions"} if instructions.len == 0: return for i in instructions: - if i{"__typename"}.getStr == "TimelineAddEntries": + # TimelineAddToModule instruction is used by UserMedia + if i{"moduleItems"}.notNull: + for item in i{"moduleItems"}: + with tweetResult, item{"item", "itemContent", "tweet_results", "result"}: + let tweet = parseGraphTweet(tweetResult, false) + if not tweet.available: + tweet.id = parseBiggestInt(item{"entryId"}.getStr.getId()) + result.tweets.content.add tweet + continue + + if i{"entries"}.notNull: for e in i{"entries"}: let entryId = e{"entryId"}.getStr - if entryId.startsWith("tweet"): - with tweetResult, e{"content", "content", "tweetResult", "result"}: - let tweet = parseGraphTweet(tweetResult, false) - if not tweet.available: - tweet.id = parseBiggestInt(entryId.getId()) + if entryId.startsWith("tweet") or entryId.startsWith("profile-grid"): + for tweet in extractTweetsFromEntry(e, entryId): result.tweets.content.add tweet elif "-conversation-" in entryId or entryId.startsWith("homeConversation"): let (thread, self) = parseGraphThread(e) result.tweets.content.add thread.content elif entryId.startsWith("cursor-bottom"): result.tweets.bottom = e{"content", "value"}.getStr + if after.len == 0 and i{"__typename"}.getStr == "TimelinePinEntry": with tweetResult, i{"entry", "content", "content", "tweetResult", "result"}: let tweet = parseGraphTweet(tweetResult, false) @@ -438,31 +482,34 @@ proc parseGraphTimeline*(js: JsonNode; root: string; after=""): Profile = proc parseGraphPhotoRail*(js: JsonNode): PhotoRail = result = @[] - let instructions = - ? js{"data", "user_result", "result", "timeline_response", "timeline", "instructions"} + var instructions = ? js{"data", "user", "result", "timeline", "timeline", "instructions"} + if instructions.len == 0: + instructions = ? js{"data", "user_result", "result", "timeline_response", "timeline", "instructions"} for i in instructions: - if i{"__typename"}.getStr == "TimelineAddEntries": - for e in i{"entries"}: - let entryId = e{"entryId"}.getStr - if entryId.startsWith("tweet"): - with tweetResult, e{"content", "content", "tweetResult", "result"}: - let t = parseGraphTweet(tweetResult, false) - if not t.available: - t.id = parseBiggestInt(entryId.getId()) + let instrType = i{"type"}.getStr + if instrType.len == 0: + if i{"__typename"}.getStr != "TimelineAddEntries": + continue + elif instrType != "TimelineAddEntries": + continue - let url = - if t.photos.len > 0: t.photos[0] - elif t.video.isSome: get(t.video).thumb - elif t.gif.isSome: get(t.gif).thumb - elif t.card.isSome: get(t.card).image - else: "" + for e in i{"entries"}: + let entryId = e{"entryId"}.getStr + if entryId.startsWith("tweet") or entryId.startsWith("profile-grid"): + for t in extractTweetsFromEntry(e, entryId): + let url = + if t.photos.len > 0: t.photos[0] + elif t.video.isSome: get(t.video).thumb + elif t.gif.isSome: get(t.gif).thumb + elif t.card.isSome: get(t.card).image + else: "" - if url.len > 0: - result.add GalleryPhoto(url: url, tweetId: $t.id) + if url.len > 0: + result.add GalleryPhoto(url: url, tweetId: $t.id) - if result.len == 16: - break + if result.len == 16: + return proc parseGraphSearch*[T: User | Tweets](js: JsonNode; after=""): Result[T] = result = Result[T](beginning: after.len == 0) diff --git a/src/types.nim b/src/types.nim index a138dae..092d85f 100644 --- a/src/types.nim +++ b/src/types.nim @@ -1,5 +1,5 @@ # SPDX-License-Identifier: AGPL-3.0-only -import times, sequtils, options, tables +import times, sequtils, options, tables, uri import prefs_impl genPrefsType() @@ -50,6 +50,10 @@ type authToken*: string ct0*: string + SessionAwareUrl* = object + oauthUrl*: Uri + cookieUrl*: Uri + Error* = enum null = 0 noUserMatches = 17 From 3f3196d10357069153dee43718bdd80b16f5b2fc Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Mon, 17 Nov 2025 00:14:30 +0100 Subject: [PATCH 237/409] Fix broken UserMedia photo rail parsing Fixes #1307 --- src/parser.nim | 76 ++++++++++++++++++++++++++------------------- src/parserutils.nim | 10 ++++++ 2 files changed, 54 insertions(+), 32 deletions(-) diff --git a/src/parser.nim b/src/parser.nim index af81469..aa0f8b2 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -6,22 +6,6 @@ import experimental/parser/unifiedcard proc parseGraphTweet(js: JsonNode; isLegacy=false): Tweet -proc extractTweetsFromEntry(e: JsonNode; entryId: string): seq[Tweet] = - if e{"content", "items"}.notNull: - for item in e{"content", "items"}: - with tweetResult, item{"item", "itemContent", "tweet_results", "result"}: - var tweet = parseGraphTweet(tweetResult, false) - if not tweet.available: - tweet.id = parseBiggestInt(item{"entryId"}.getStr.getId()) - result.add tweet - return - - with tweetResult, e{"content", "content", "tweetResult", "result"}: - var tweet = parseGraphTweet(tweetResult, false) - if not tweet.available: - tweet.id = parseBiggestInt(entryId.getId()) - result.add tweet - proc parseUser(js: JsonNode; id=""): User = if js.isNull: return result = User( @@ -432,6 +416,22 @@ proc parseGraphConversation*(js: JsonNode; tweetId: string; v2=true): Conversati elif entryId.startsWith("cursor-bottom"): result.replies.bottom = e{"content", contentKey, "value"}.getStr +proc extractTweetsFromEntry*(e: JsonNode; entryId: string): seq[Tweet] = + if e{"content", "items"}.notNull: + for item in e{"content", "items"}: + with tweetResult, item{"item", "itemContent", "tweet_results", "result"}: + var tweet = parseGraphTweet(tweetResult, false) + if not tweet.available: + tweet.id = parseBiggestInt(item{"entryId"}.getStr.getId()) + result.add tweet + return + + with tweetResult, e{"content", "content", "tweetResult", "result"}: + var tweet = parseGraphTweet(tweetResult, false) + if not tweet.available: + tweet.id = parseBiggestInt(entryId.getId()) + result.add tweet + proc parseGraphTimeline*(js: JsonNode; after=""): Profile = result = Profile(tweets: Timeline(beginning: after.len == 0)) @@ -482,31 +482,43 @@ proc parseGraphTimeline*(js: JsonNode; after=""): Profile = proc parseGraphPhotoRail*(js: JsonNode): PhotoRail = result = @[] - var instructions = ? js{"data", "user", "result", "timeline", "timeline", "instructions"} + let instructions = + if js{"data", "user"}.notNull: + ? js{"data", "user", "result", "timeline", "timeline", "instructions"} + else: + ? js{"data", "user_result", "result", "timeline_response", "timeline", "instructions"} + if instructions.len == 0: - instructions = ? js{"data", "user_result", "result", "timeline_response", "timeline", "instructions"} + return for i in instructions: - let instrType = i{"type"}.getStr - if instrType.len == 0: - if i{"__typename"}.getStr != "TimelineAddEntries": - continue - elif instrType != "TimelineAddEntries": + # TimelineAddToModule instruction is used by MediaTimelineV2 + if i{"moduleItems"}.notNull: + for item in i{"moduleItems"}: + with tweetResult, item{"item", "itemContent", "tweet_results", "result"}: + let t = parseGraphTweet(tweetResult, false) + if not t.available: + t.id = parseBiggestInt(item{"entryId"}.getStr.getId()) + + let photo = extractGalleryPhoto(t) + if photo.url.len > 0: + result.add photo + + if result.len == 16: + return + continue + + let instrType = i{"type"}.getStr(i{"__typename"}.getStr) + if instrType != "TimelineAddEntries": continue for e in i{"entries"}: let entryId = e{"entryId"}.getStr if entryId.startsWith("tweet") or entryId.startsWith("profile-grid"): for t in extractTweetsFromEntry(e, entryId): - let url = - if t.photos.len > 0: t.photos[0] - elif t.video.isSome: get(t.video).thumb - elif t.gif.isSome: get(t.gif).thumb - elif t.card.isSome: get(t.card).image - else: "" - - if url.len > 0: - result.add GalleryPhoto(url: url, tweetId: $t.id) + let photo = extractGalleryPhoto(t) + if photo.url.len > 0: + result.add photo if result.len == 16: return diff --git a/src/parserutils.nim b/src/parserutils.nim index 00ea6f4..7e246dd 100644 --- a/src/parserutils.nim +++ b/src/parserutils.nim @@ -319,3 +319,13 @@ proc expandNoteTweetEntities*(tweet: Tweet; js: JsonNode) = tweet.expandTextEntities(entities, text, textSlice) tweet.text = tweet.text.multiReplace((unicodeOpen, xmlOpen), (unicodeClose, xmlClose)) + +proc extractGalleryPhoto*(t: Tweet): GalleryPhoto = + let url = + if t.photos.len > 0: t.photos[0] + elif t.video.isSome: get(t.video).thumb + elif t.gif.isSome: get(t.gif).thumb + elif t.card.isSome: get(t.card).image + else: "" + + result = GalleryPhoto(url: url, tweetId: $t.id) From 778eb35ee33cbdc80733e0d9db65b15059e8a3fa Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Mon, 17 Nov 2025 03:55:23 +0100 Subject: [PATCH 238/409] Add curl-based cookie session script --- ...b_session.py => create_session_browser.py} | 11 +- tools/create_session_curl.py | 328 ++++++++++++++++++ tools/requirements.txt | 1 + 3 files changed, 333 insertions(+), 7 deletions(-) rename tools/{get_web_session.py => create_session_browser.py} (90%) create mode 100644 tools/create_session_curl.py diff --git a/tools/get_web_session.py b/tools/create_session_browser.py similarity index 90% rename from tools/get_web_session.py rename to tools/create_session_browser.py index 502c7f4..40e3dcd 100644 --- a/tools/get_web_session.py +++ b/tools/create_session_browser.py @@ -1,23 +1,20 @@ #!/usr/bin/env python3 """ -Authenticates with X.com/Twitter and extracts session cookies for use with Nitter. -Handles 2FA, extracts user info, and outputs clean JSON for sessions.jsonl. - Requirements: pip install -r tools/requirements.txt Usage: - python3 tools/get_web_session.py <username> <password> [totp_seed] [--append sessions.jsonl] [--headless] + python3 tools/create_session_browser.py <username> <password> [totp_seed] [--append sessions.jsonl] [--headless] Examples: # Output to terminal - python3 tools/get_web_session.py myusername mypassword TOTP_BASE32_SECRET + python3 tools/create_session_browser.py myusername mypassword TOTP_SECRET # Append to sessions.jsonl - python3 tools/get_web_session.py myusername mypassword TOTP_SECRET --append sessions.jsonl + python3 tools/create_session_browser.py myusername mypassword TOTP_SECRET --append sessions.jsonl # Headless mode (may increase detection risk) - python3 tools/get_web_session.py myusername mypassword TOTP_SECRET --headless + python3 tools/create_session_browser.py myusername mypassword TOTP_SECRET --headless Output: {"kind": "cookie", "username": "...", "id": "...", "auth_token": "...", "ct0": "..."} diff --git a/tools/create_session_curl.py b/tools/create_session_curl.py new file mode 100644 index 0000000..f569422 --- /dev/null +++ b/tools/create_session_curl.py @@ -0,0 +1,328 @@ +#!/usr/bin/env python3 +""" +Requirements: + pip install curl_cffi pyotp + +Usage: + python3 tools/create_session_curl.py <username> <password> [totp_seed] [--append sessions.jsonl] + +Examples: + # Output to terminal + python3 tools/create_session_curl.py myusername mypassword TOTP_SECRET + + # Append to sessions.jsonl + python3 tools/create_session_curl.py myusername mypassword TOTP_SECRET --append sessions.jsonl + +Output: + {"kind": "cookie", "username": "...", "id": "...", "auth_token": "...", "ct0": "..."} +""" + +import sys +import json +import pyotp +from curl_cffi import requests + +BEARER_TOKEN = "AAAAAAAAAAAAAAAAAAAAAFQODgEAAAAAVHTp76lzh3rFzcHbmHVvQxYYpTw%3DckAlMINMjmCwxUcaXbAN4XqJVdgMJaHqNOFgPMK0zN1qLqLQCF" +BASE_URL = "https://api.x.com/1.1/onboarding/task.json" +GUEST_ACTIVATE_URL = "https://api.x.com/1.1/guest/activate.json" + +# Subtask versions required by API +SUBTASK_VERSIONS = { + "action_list": 2, "alert_dialog": 1, "app_download_cta": 1, + "check_logged_in_account": 2, "choice_selection": 3, + "contacts_live_sync_permission_prompt": 0, "cta": 7, "email_verification": 2, + "end_flow": 1, "enter_date": 1, "enter_email": 2, "enter_password": 5, + "enter_phone": 2, "enter_recaptcha": 1, "enter_text": 5, "generic_urt": 3, + "in_app_notification": 1, "interest_picker": 3, "js_instrumentation": 1, + "menu_dialog": 1, "notifications_permission_prompt": 2, "open_account": 2, + "open_home_timeline": 1, "open_link": 1, "phone_verification": 4, + "privacy_options": 1, "security_key": 3, "select_avatar": 4, + "select_banner": 2, "settings_list": 7, "show_code": 1, "sign_up": 2, + "sign_up_review": 4, "tweet_selection_urt": 1, "update_users": 1, + "upload_media": 1, "user_recommendations_list": 4, + "user_recommendations_urt": 1, "wait_spinner": 3, "web_modal": 1 +} + + +def get_base_headers(guest_token=None): + """Build base headers for API requests.""" + headers = { + "Authorization": f"Bearer {BEARER_TOKEN}", + "Content-Type": "application/json", + "Accept": "*/*", + "Accept-Language": "en-US", + "X-Twitter-Client-Language": "en-US", + "Origin": "https://x.com", + "Referer": "https://x.com/", + } + if guest_token: + headers["X-Guest-Token"] = guest_token + return headers + + +def get_cookies_dict(session): + """Extract cookies from session.""" + return session.cookies.get_dict() if hasattr(session.cookies, 'get_dict') else dict(session.cookies) + + +def make_request(session, headers, flow_token, subtask_data, print_msg): + """Generic request handler for flow steps.""" + print(f"[*] {print_msg}...", file=sys.stderr) + + payload = { + "flow_token": flow_token, + "subtask_inputs": [subtask_data] if isinstance(subtask_data, dict) else subtask_data + } + + response = session.post(BASE_URL, json=payload, headers=headers) + response.raise_for_status() + + data = response.json() + new_flow_token = data.get('flow_token') + if not new_flow_token: + raise Exception(f"Failed to get flow token: {print_msg}") + + return new_flow_token, data + + +def get_guest_token(session): + """Get guest token for unauthenticated requests.""" + print("[*] Getting guest token...", file=sys.stderr) + response = session.post(GUEST_ACTIVATE_URL, headers={"Authorization": f"Bearer {BEARER_TOKEN}"}) + response.raise_for_status() + + guest_token = response.json().get('guest_token') + if not guest_token: + raise Exception("Failed to obtain guest token") + + print(f"[*] Got guest token: {guest_token}", file=sys.stderr) + return guest_token + + +def init_flow(session, guest_token): + """Initialize the login flow.""" + print("[*] Initializing login flow...", file=sys.stderr) + + headers = get_base_headers(guest_token) + payload = { + "input_flow_data": { + "flow_context": { + "debug_overrides": {}, + "start_location": {"location": "manual_link"} + }, + "subtask_versions": SUBTASK_VERSIONS + } + } + + response = session.post(f"{BASE_URL}?flow_name=login", json=payload, headers=headers) + response.raise_for_status() + + flow_token = response.json().get('flow_token') + if not flow_token: + raise Exception("Failed to get initial flow token") + + print("[*] Got initial flow token", file=sys.stderr) + return flow_token, headers + + +def submit_username(session, flow_token, headers, guest_token, username): + """Submit username.""" + headers = headers.copy() + headers["X-Guest-Token"] = guest_token + + subtask = { + "subtask_id": "LoginEnterUserIdentifierSSO", + "settings_list": { + "setting_responses": [{ + "key": "user_identifier", + "response_data": {"text_data": {"result": username}} + }], + "link": "next_link" + } + } + + flow_token, data = make_request(session, headers, flow_token, subtask, "Submitting username") + + # Check for denial (suspicious activity) + if data.get('subtasks') and 'cta' in data['subtasks'][0]: + error_msg = data['subtasks'][0]['cta'].get('primary_text', {}).get('text') + if error_msg: + raise Exception(f"Login denied: {error_msg}") + + return flow_token + + +def submit_password(session, flow_token, headers, guest_token, password): + """Submit password and detect if 2FA is needed.""" + headers = headers.copy() + headers["X-Guest-Token"] = guest_token + + subtask = { + "subtask_id": "LoginEnterPassword", + "enter_password": {"password": password, "link": "next_link"} + } + + flow_token, data = make_request(session, headers, flow_token, subtask, "Submitting password") + + needs_2fa = any(s.get('subtask_id') == 'LoginTwoFactorAuthChallenge' for s in data.get('subtasks', [])) + if needs_2fa: + print("[*] 2FA required", file=sys.stderr) + + return flow_token, needs_2fa + + +def submit_2fa(session, flow_token, headers, guest_token, totp_seed): + """Submit 2FA code.""" + if not totp_seed: + raise Exception("2FA required but no TOTP seed provided") + + code = pyotp.TOTP(totp_seed).now() + print("[*] Generating 2FA code...", file=sys.stderr) + + headers = headers.copy() + headers["X-Guest-Token"] = guest_token + + subtask = { + "subtask_id": "LoginTwoFactorAuthChallenge", + "enter_text": {"text": code, "link": "next_link"} + } + + flow_token, _ = make_request(session, headers, flow_token, subtask, "Submitting 2FA code") + return flow_token + + +def submit_js_instrumentation(session, flow_token, headers, guest_token): + """Submit JS instrumentation response.""" + headers = headers.copy() + headers["X-Guest-Token"] = guest_token + + subtask = { + "subtask_id": "LoginJsInstrumentationSubtask", + "js_instrumentation": { + "response": '{"rf":{"a4fc506d24bb4843c48a1966940c2796bf4fb7617a2d515ad3297b7df6b459b6":121,"bff66e16f1d7ea28c04653dc32479cf416a9c8b67c80cb8ad533b2a44fee82a3":-1,"ac4008077a7e6ca03210159dbe2134dea72a616f03832178314bb9931645e4f7":-22,"c3a8a81a9b2706c6fec42c771da65a9597c537b8e4d9b39e8e58de9fe31ff239":-12},"s":"ZHYaDA9iXRxOl2J3AZ9cc23iJx-Fg5E82KIBA_fgeZFugZGYzRtf8Bl3EUeeYgsK30gLFD2jTQx9fAMsnYCw0j8ahEy4Pb5siM5zD6n7YgOeWmFFaXoTwaGY4H0o-jQnZi5yWZRAnFi4lVuCVouNz_xd2BO2sobCO7QuyOsOxQn2CWx7bjD8vPAzT5BS1mICqUWyjZDjLnRZJU6cSQG5YFIHEPBa8Kj-v1JFgkdAfAMIdVvP7C80HWoOqYivQR7IBuOAI4xCeLQEdxlGeT-JYStlP9dcU5St7jI6ExyMeQnRicOcxXLXsan8i5Joautk2M8dAJFByzBaG4wtrPhQ3QAAAZEi-_t7"}', + "link": "next_link" + } + } + + flow_token, _ = make_request(session, headers, flow_token, subtask, "Submitting JS instrumentation") + return flow_token + + +def complete_flow(session, flow_token, headers): + """Complete the login flow.""" + cookies = get_cookies_dict(session) + + headers = headers.copy() + headers["X-Twitter-Auth-Type"] = "OAuth2Session" + if cookies.get('ct0'): + headers["X-Csrf-Token"] = cookies['ct0'] + + subtask = { + "subtask_id": "AccountDuplicationCheck", + "check_logged_in_account": {"link": "AccountDuplicationCheck_false"} + } + + make_request(session, headers, flow_token, subtask, "Completing login flow") + + +def extract_user_id(cookies_dict): + """Extract user ID from twid cookie.""" + twid = cookies_dict.get('twid', '').strip('"') + + for prefix in ['u=', 'u%3D']: + if prefix in twid: + return twid.split(prefix)[1].split('&')[0].strip('"') + + return None + + +def login_and_get_cookies(username, password, totp_seed=None): + """Authenticate with X.com and extract session cookies.""" + session = requests.Session(impersonate="chrome") + + try: + guest_token = get_guest_token(session) + flow_token, headers = init_flow(session, guest_token) + flow_token = submit_js_instrumentation(session, flow_token, headers, guest_token) + flow_token = submit_username(session, flow_token, headers, guest_token, username) + flow_token, needs_2fa = submit_password(session, flow_token, headers, guest_token, password) + + if needs_2fa: + flow_token = submit_2fa(session, flow_token, headers, guest_token, totp_seed) + + complete_flow(session, flow_token, headers) + + cookies_dict = get_cookies_dict(session) + cookies_dict['username'] = username + + user_id = extract_user_id(cookies_dict) + if user_id: + cookies_dict['id'] = user_id + + print("[*] Successfully authenticated", file=sys.stderr) + return cookies_dict + + finally: + session.close() + + +def main(): + if len(sys.argv) < 3: + print('Usage: python3 create_session_curl.py username password [totp_seed] [--append sessions.jsonl]', file=sys.stderr) + sys.exit(1) + + username = sys.argv[1] + password = sys.argv[2] + totp_seed = None + append_file = None + + # Parse optional arguments + i = 3 + while i < len(sys.argv): + arg = sys.argv[i] + if arg == '--append': + if i + 1 < len(sys.argv): + append_file = sys.argv[i + 1] + i += 2 + else: + print('[!] Error: --append requires a filename', file=sys.stderr) + sys.exit(1) + elif not arg.startswith('--'): + if totp_seed is None: + totp_seed = arg + i += 1 + else: + print(f'[!] Warning: Unknown argument: {arg}', file=sys.stderr) + i += 1 + + try: + cookies = login_and_get_cookies(username, password, totp_seed) + + session = { + 'kind': 'cookie', + 'username': cookies['username'], + 'id': cookies.get('id'), + 'auth_token': cookies['auth_token'], + 'ct0': cookies['ct0'] + } + + output = json.dumps(session) + + if append_file: + with open(append_file, 'a') as f: + f.write(output + '\n') + print(f'✓ Session appended to {append_file}', file=sys.stderr) + else: + print(output) + + sys.exit(0) + + except Exception as error: + print(f'[!] Error: {error}', file=sys.stderr) + import traceback + traceback.print_exc(file=sys.stderr) + sys.exit(1) + + +if __name__ == '__main__': + main() diff --git a/tools/requirements.txt b/tools/requirements.txt index 4827475..2fdac24 100644 --- a/tools/requirements.txt +++ b/tools/requirements.txt @@ -1,2 +1,3 @@ nodriver>=0.48.0 pyotp +curl_cffi From a666c4867c9a0b4e187bae6c5d826b4aec34fd29 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Mon, 17 Nov 2025 05:42:35 +0100 Subject: [PATCH 239/409] Include username in session logs if available Fixes #1310 --- src/apiutils.nim | 10 +++++----- src/auth.nim | 20 +++++++++++++++++--- src/experimental/parser/session.nim | 2 ++ src/experimental/types/session.nim | 2 +- src/types.nim | 1 + 5 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/apiutils.nim b/src/apiutils.nim index ae459fa..defffd1 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -60,11 +60,11 @@ proc getAndValidateSession*(api: Api): Future[Session] {.async.} = case result.kind of SessionKind.oauth: if result.oauthToken.len == 0: - echo "[sessions] Empty oauth token, session: ", result.id + echo "[sessions] Empty oauth token, session: ", result.pretty raise rateLimitError() of SessionKind.cookie: if result.authToken.len == 0 or result.ct0.len == 0: - echo "[sessions] Empty cookie credentials, session: ", result.id + echo "[sessions] Empty cookie credentials, session: ", result.pretty raise rateLimitError() template fetchImpl(result, fetchBody) {.dirty.} = @@ -107,7 +107,7 @@ template fetchImpl(result, fetchBody) {.dirty.} = setLimited(session, api) raise rateLimitError() elif result.startsWith("429 Too Many Requests"): - echo "[sessions] 429 error, API: ", api, ", session: ", session.id + echo "[sessions] 429 error, API: ", api, ", session: ", session.pretty session.apis[api].remaining = 0 # rate limit hit, resets after the 15 minute window raise rateLimitError() @@ -124,8 +124,8 @@ template fetchImpl(result, fetchBody) {.dirty.} = except OSError as e: raise e except Exception as e: - let id = if session.isNil: "null" else: $session.id - echo "error: ", e.name, ", msg: ", e.msg, ", sessionId: ", id, ", url: ", url + let s = session.pretty + echo "error: ", e.name, ", msg: ", e.msg, ", session: ", s, ", url: ", url raise rateLimitError() finally: release(session) diff --git a/src/auth.nim b/src/auth.nim index 9f9fe8a..6c52918 100644 --- a/src/auth.nim +++ b/src/auth.nim @@ -29,6 +29,20 @@ var template log(str: varargs[string, `$`]) = echo "[sessions] ", str.join("") +proc pretty*(session: Session): string = + if session.isNil: + return "<null>" + + if session.id > 0 and session.username.len > 0: + result = $session.id & " (" & session.username & ")" + elif session.username.len > 0: + result = session.username + elif session.id > 0: + result = $session.id + else: + result = "<unknown>" + result = $session.kind & " " & result + proc snowflakeToEpoch(flake: int64): int64 = int64(((flake shr 22) + 1288834974657) div 1000) @@ -130,7 +144,7 @@ proc isLimited(session: Session; api: Api): bool = if session.limited and api != Api.userTweets: if (epochTime().int - session.limitedAt) > hourInSeconds: session.limited = false - log "resetting limit: ", session.id + log "resetting limit: ", session.pretty return false else: return true @@ -146,7 +160,7 @@ proc isReady(session: Session; api: Api): bool = proc invalidate*(session: var Session) = if session.isNil: return - log "invalidating: ", session.id + log "invalidating: ", session.pretty # TODO: This isn't sufficient, but it works for now let idx = sessionPool.find(session) @@ -171,7 +185,7 @@ proc getSession*(api: Api): Future[Session] {.async.} = proc setLimited*(session: Session; api: Api) = session.limited = true session.limitedAt = epochTime().int - log "rate limited by api: ", api, ", reqs left: ", session.apis[api].remaining, ", id: ", session.id + log "rate limited by api: ", api, ", reqs left: ", session.apis[api].remaining, ", ", session.pretty proc setRateLimit*(session: Session; api: Api; remaining, reset, limit: int) = # avoid undefined behavior in race conditions diff --git a/src/experimental/parser/session.nim b/src/experimental/parser/session.nim index bb31d83..2e5a171 100644 --- a/src/experimental/parser/session.nim +++ b/src/experimental/parser/session.nim @@ -13,6 +13,7 @@ proc parseSession*(raw: string): Session = result = Session( kind: SessionKind.oauth, id: parseBiggestInt(id), + username: session.username, oauthToken: session.oauthToken, oauthSecret: session.oauthTokenSecret ) @@ -21,6 +22,7 @@ proc parseSession*(raw: string): Session = result = Session( kind: SessionKind.cookie, id: id, + username: session.username, authToken: session.authToken, ct0: session.ct0 ) diff --git a/src/experimental/types/session.nim b/src/experimental/types/session.nim index dd6be22..dfec428 100644 --- a/src/experimental/types/session.nim +++ b/src/experimental/types/session.nim @@ -1,8 +1,8 @@ type RawSession* = object kind*: string - username*: string id*: string + username*: string oauthToken*: string oauthTokenSecret*: string authToken*: string diff --git a/src/types.nim b/src/types.nim index 092d85f..55d990d 100644 --- a/src/types.nim +++ b/src/types.nim @@ -38,6 +38,7 @@ type Session* = ref object id*: int64 + username*: string pending*: int limited*: bool limitedAt*: int From 0bb0b7e78cc617544231c286a9bb641166010897 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Mon, 17 Nov 2025 06:32:13 +0100 Subject: [PATCH 240/409] Support grok_share card Fixes #1306 --- src/experimental/parser/unifiedcard.nim | 15 +++++++++++++++ src/experimental/types/unifiedcard.nim | 7 +++++++ src/formatters.nim | 7 +++++-- src/sass/tweet/card.scss | 1 + 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/experimental/parser/unifiedcard.nim b/src/experimental/parser/unifiedcard.nim index a112974..de4df18 100644 --- a/src/experimental/parser/unifiedcard.nim +++ b/src/experimental/parser/unifiedcard.nim @@ -1,6 +1,7 @@ import std/[options, tables, strutils, strformat, sugar] import jsony import user, ../types/unifiedcard +import ../../formatters from ../../types import Card, CardKind, Video from ../../utils import twimg, https @@ -77,6 +78,18 @@ proc parseMedia(component: Component; card: UnifiedCard; result: var Card) = of model3d: result.title = "Unsupported 3D model ad" +proc parseGrokShare(data: ComponentData; card: UnifiedCard; result: var Card) = + result.kind = summaryLarge + + data.destination.parseDestination(card, result) + result.dest = "Answer by Grok" + + for msg in data.conversationPreview: + if msg.sender == "USER": + result.title = msg.message.shorten(70) + elif msg.sender == "AGENT": + result.text = msg.message.shorten(500) + proc parseUnifiedCard*(json: string): Card = let card = json.fromJson(UnifiedCard) @@ -92,6 +105,8 @@ proc parseUnifiedCard*(json: string): Card = component.parseMedia(card, result) of buttonGroup: discard + of grokShare: + component.data.parseGrokShare(card, result) of ComponentType.jobDetails: component.data.parseJobDetails(card, result) of ComponentType.hidden: diff --git a/src/experimental/types/unifiedcard.nim b/src/experimental/types/unifiedcard.nim index e540a64..cef6f44 100644 --- a/src/experimental/types/unifiedcard.nim +++ b/src/experimental/types/unifiedcard.nim @@ -22,6 +22,7 @@ type communityDetails mediaWithDetailsHorizontal hidden + grokShare unknown Component* = object @@ -42,6 +43,7 @@ type topicDetail*: tuple[title: Text] profileUser*: User shortDescriptionText*: string + conversationPreview*: seq[GrokConversation] MediaItem* = object id*: string @@ -76,6 +78,10 @@ type title*: Text category*: Text + GrokConversation* = object + message*: string + sender*: string + TypeField = Component | Destination | MediaEntity | AppStoreData converter fromText*(text: Text): string = string(text) @@ -96,6 +102,7 @@ proc enumHook*(s: string; v: var ComponentType) = of "community_details": communityDetails of "media_with_details_horizontal": mediaWithDetailsHorizontal of "commerce_drop_details": hidden + of "grok_share": grokShare else: echo "ERROR: Unknown enum value (ComponentType): ", s; unknown proc enumHook*(s: string; v: var AppType) = diff --git a/src/formatters.nim b/src/formatters.nim index 7428814..cafaa4f 100644 --- a/src/formatters.nim +++ b/src/formatters.nim @@ -33,10 +33,13 @@ proc getUrlPrefix*(cfg: Config): string = if cfg.useHttps: https & cfg.hostname else: "http://" & cfg.hostname -proc shortLink*(text: string; length=28): string = - result = text.replace(wwwRegex, "") +proc shorten*(text: string; length=28): string = + result = text if result.len > length: result = result[0 ..< length] & "…" + +proc shortLink*(text: string; length=28): string = + result = text.replace(wwwRegex, "").shorten(length) proc stripHtml*(text: string; shorten=false): string = var html = parseHtml(text) diff --git a/src/sass/tweet/card.scss b/src/sass/tweet/card.scss index 680310c..5575191 100644 --- a/src/sass/tweet/card.scss +++ b/src/sass/tweet/card.scss @@ -42,6 +42,7 @@ .card-description { margin: 0.3em 0; + white-space: pre-wrap; } .card-destination { From bb6eb81a20b482b6ba1dd6da3332cd292427778e Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Mon, 17 Nov 2025 10:59:50 +0100 Subject: [PATCH 241/409] Add support for tweet views --- public/css/fontello.css | 44 ++++++++++++++++++------------------ public/fonts/LICENSE.txt | 18 +++++++-------- public/fonts/fontello.eot | Bin 9264 -> 9368 bytes public/fonts/fontello.svg | 32 ++++++++++++++------------ public/fonts/fontello.ttf | Bin 9096 -> 9200 bytes public/fonts/fontello.woff | Bin 5752 -> 5812 bytes public/fonts/fontello.woff2 | Bin 4772 -> 4832 bytes src/parser.nim | 6 ++++- src/types.nim | 1 + src/views/general.nim | 4 ++-- src/views/tweet.nim | 2 ++ 11 files changed, 58 insertions(+), 49 deletions(-) diff --git a/public/css/fontello.css b/public/css/fontello.css index d022bb5..2453575 100644 --- a/public/css/fontello.css +++ b/public/css/fontello.css @@ -1,16 +1,15 @@ @font-face { font-family: 'fontello'; - src: url('/fonts/fontello.eot?21002321'); - src: url('/fonts/fontello.eot?21002321#iefix') format('embedded-opentype'), - url('/fonts/fontello.woff2?21002321') format('woff2'), - url('/fonts/fontello.woff?21002321') format('woff'), - url('/fonts/fontello.ttf?21002321') format('truetype'), - url('/fonts/fontello.svg?21002321#fontello') format('svg'); + src: url('/fonts/fontello.eot?61663884'); + src: url('/fonts/fontello.eot?61663884#iefix') format('embedded-opentype'), + url('/fonts/fontello.woff2?61663884') format('woff2'), + url('/fonts/fontello.woff?61663884') format('woff'), + url('/fonts/fontello.ttf?61663884') format('truetype'), + url('/fonts/fontello.svg?61663884#fontello') format('svg'); font-weight: normal; font-style: normal; } - - [class^="icon-"]:before, [class*=" icon-"]:before { +[class^="icon-"]:before, [class*=" icon-"]:before { font-family: "fontello"; font-style: normal; font-weight: normal; @@ -32,22 +31,23 @@ -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } - -.icon-heart:before { content: '\2665'; } /* '♥' */ -.icon-quote:before { content: '\275e'; } /* '❞' */ -.icon-comment:before { content: '\e802'; } /* '' */ -.icon-ok:before { content: '\e803'; } /* '' */ -.icon-play:before { content: '\e804'; } /* '' */ -.icon-link:before { content: '\e805'; } /* '' */ -.icon-calendar:before { content: '\e806'; } /* '' */ -.icon-location:before { content: '\e807'; } /* '' */ + +.icon-views:before { content: '\e800'; } /* '' */ +.icon-heart:before { content: '\e801'; } /* '' */ +.icon-quote:before { content: '\e802'; } /* '' */ +.icon-comment:before { content: '\e803'; } /* '' */ +.icon-ok:before { content: '\e804'; } /* '' */ +.icon-play:before { content: '\e805'; } /* '' */ +.icon-link:before { content: '\e806'; } /* '' */ +.icon-calendar:before { content: '\e807'; } /* '' */ +.icon-location:before { content: '\e808'; } /* '' */ .icon-picture:before { content: '\e809'; } /* '' */ .icon-lock:before { content: '\e80a'; } /* '' */ .icon-down:before { content: '\e80b'; } /* '' */ -.icon-retweet:before { content: '\e80d'; } /* '' */ -.icon-search:before { content: '\e80e'; } /* '' */ -.icon-pin:before { content: '\e80f'; } /* '' */ -.icon-cog:before { content: '\e812'; } /* '' */ -.icon-rss-feed:before { content: '\e813'; } /* '' */ +.icon-retweet:before { content: '\e80c'; } /* '' */ +.icon-search:before { content: '\e80d'; } /* '' */ +.icon-pin:before { content: '\e80e'; } /* '' */ +.icon-cog:before { content: '\e80f'; } /* '' */ +.icon-rss:before { content: '\e810'; } /* '' */ .icon-info:before { content: '\f128'; } /* '' */ .icon-bird:before { content: '\f309'; } /* '' */ diff --git a/public/fonts/LICENSE.txt b/public/fonts/LICENSE.txt index c8d90ff..41f18a8 100644 --- a/public/fonts/LICENSE.txt +++ b/public/fonts/LICENSE.txt @@ -1,6 +1,15 @@ Font license info +## Modern Pictograms + + Copyright (c) 2012 by John Caserta. All rights reserved. + + Author: John Caserta + License: SIL (http://scripts.sil.org/OFL) + Homepage: http://thedesignoffice.org/project/modern-pictograms/ + + ## Entypo Copyright (C) 2012 by Daniel Bruce @@ -37,12 +46,3 @@ Font license info Homepage: http://aristeides.com/ -## Modern Pictograms - - Copyright (c) 2012 by John Caserta. All rights reserved. - - Author: John Caserta - License: SIL (http://scripts.sil.org/OFL) - Homepage: http://thedesignoffice.org/project/modern-pictograms/ - - diff --git a/public/fonts/fontello.eot b/public/fonts/fontello.eot index aaddd6bf3959a71b00b92cd01dd4067db5d94b53..2b2982a5711bffac0cb1aad4690f661e34e308c4 100644 GIT binary patch delta 901 zcmYL`Pe>F|9LIle-kaGO-DaKLSzJZM*3HB}5teR{$kHLKgNjIG3c2d~Cw6skm(4=l zhy<xi@(xLm*v&(Pw6Yg5I&=!`P*Na79r`oap~CGTv~IsyCz>~(dEf8v{T}Z(zqi)0 zbU|DS0Q_7OgG}AbZY+itQtg4>EdbID0E5G^u<_az?MJ@`Aj}RY6V({U$9o1p>Zmam z8#=Ln6MY9-=U6yyV7vtVNu1@e=#A0-kF`GlSR;UQc03XuX?h#@fGMlE-a3u}#~u0@ z{U`K+@mQj7=3FkH2hbP>x}#IW;fl4g5%j(2>to>=g9O!w=x?E~o(#t#TaPL#(XU{E zTgFs8@$7s1X8_$QWYd_A7;~Ak8GxEzzC!vaqka4jX-k=~r_i4KHMl~bzynMr$p1$` zDIyfX$^nZZ3wF+Jj9S@^I2RS-3@)LUao|3)<X7FEV*P>%{}irTC&*BT!&3ac;1@lp zBu?l7FQFpOQ>ijrNCdzY6QzZ9lv#lLSyxI%hk$~!ja7tc1I4Iqz=_%hj-a*y6}1hx zP}^A7X4-%TSo|U225i<LDJ%_plHV0pg*T`icwkGYStcS8=OscYnRgHhbe;*=ili(v z?3`RI7Z)ka!5or^d#I*Jr41gfMiUW&tgn-&)+?~wSE2mwfkEz*%Q>H57WxcyKzNRY zx<f8!dY>f~u?R}xI8Ug}ZcmL{mZ}<py5z4t-Fm9Iitvo;XUEFw%lVFal$O=Ucup-% zdtKj1#YT2hz3OWF4K7;f?3a+SY<1Y0xlao^bSo~b@*b_LsTBrc8Fq<4s>!k-3Fn0u z)JHq%HuJGLmK8h29jX2BdO%OV(iB2k__jMkTGIDC7YWh%JH1vRUJ#QLk?V1u(+6C1 VIv!^elcQ5?XkvN<(oyeA^)Fc#!jb?0 delta 802 zcmXw0O-vI}5T3Vhx3p5aZI=RKW02ss3MHhG)+h&#RY)*WNic|6+HDs@VWka0f;L7% zh$nI5PmB`O3nybJ;o!lO2M@(?FkZYUr;TZ12pTbw)Oox5Hs4IXZ)V?ov-|km>x9tN z2C%d&#QB5$NOHM%sW`uwoChEm0AMJUQ}n0zKQ$s>1K=K}W(qB+<Eh`q;K}IO+*s%D zIpklEy0c1NN8OA3J!WTi;zs8AD!mTC_X4=xnyRFmE73T%^kY4yp-{6xSCKCuZ_{#x z;o06x)hY@&5c(&kQcB}|^F8E0kcV^1tWIK{Cjh=6@|H;@r*1uKY(n0H2DkL7e4(>4 z_yG;LvHnJ%R`pw(XEy;_E5<4rri7-9KcuH9t9vebv3d<I(?NKItps5o0TV@ZAb2di z`d3>+m34JsK4j@U;FkhF?n{A_)zGL}W7e9YDeZ>$Tx<zT<4l)XXZrtZ_y)icD_Viz z1?&kz3$%d@va!yEaFXNYSB}Wo!WIEAt;nX~tu~mO&?tk!^8-Z}HX(pz^vA$_nzc57 z<vJVePSaxpm?qi)rr`tzV47ru{n6d94e$Z%;S2`m#{N2Vq?8bfF2M-_IBt|ZQLkU} zI~_;c<MobK`BW_0V0gt=t}kxH#AAnJb3+f>BQxFIinchZrB^y4x6>n6Zr*{CE}A5m zjO-s=6?=YyNUgO$mw)x)AYj=j_~KNerm^lDIB^m**nl#8BO2M}yxd)Ghc?j_y2B^< zw?c#P!r`#&Nu^G|i;ynkw6saOO5dgPgh<AjfK0#`4~$68>3lwtQPng*KAD-~$Hu4A JrH_Gkp1<)Mv_b#? diff --git a/public/fonts/fontello.svg b/public/fonts/fontello.svg index 1f30ccc..2a64343 100644 --- a/public/fonts/fontello.svg +++ b/public/fonts/fontello.svg @@ -1,26 +1,28 @@ <?xml version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg xmlns="http://www.w3.org/2000/svg"> -<metadata>Copyright (C) 2020 by original authors @ fontello.com</metadata> +<metadata>Copyright (C) 2025 by original authors @ fontello.com</metadata> <defs> <font id="fontello" horiz-adv-x="1000" > <font-face font-family="fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" /> <missing-glyph horiz-adv-x="1000" /> -<glyph glyph-name="heart" unicode="♥" d="M790 644q70-64 70-156t-70-158l-360-330-360 330q-70 66-70 158t70 156q62 58 151 58t153-58l56-52 58 52q62 58 150 58t152-58z" horiz-adv-x="860" /> +<glyph glyph-name="views" unicode="" d="M180 516l0-538-180 0 0 538 180 0z m250-138l0-400-180 0 0 400 180 0z m250 344l0-744-180 0 0 744 180 0z" horiz-adv-x="680" /> -<glyph glyph-name="quote" unicode="❞" d="M18 685l335 0 0-334q0-140-98-238t-237-97l0 111q92 0 158 65t65 159l-223 0 0 334z m558 0l335 0 0-334q0-140-98-238t-237-97l0 111q92 0 158 65t65 159l-223 0 0 334z" horiz-adv-x="928" /> +<glyph glyph-name="heart" unicode="" d="M790 644q70-64 70-156t-70-158l-360-330-360 330q-70 66-70 158t70 156q62 58 151 58t153-58l56-52 58 52q62 58 150 58t152-58z" horiz-adv-x="860" /> -<glyph glyph-name="comment" unicode="" d="M1000 350q0-97-67-179t-182-130-251-48q-39 0-81 4-110-97-257-135-27-8-63-12-10-1-17 5t-10 16v1q-2 2 0 6t1 6 2 5l4 5t4 5 4 5q4 5 17 19t20 22 17 22 18 28 15 33 15 42q-88 50-138 123t-51 157q0 73 40 139t106 114 160 76 194 28q136 0 251-48t182-130 67-179z" horiz-adv-x="1000" /> +<glyph glyph-name="quote" unicode="" d="M18 685l335 0 0-334q0-140-98-238t-237-97l0 111q92 0 158 65t65 159l-223 0 0 334z m558 0l335 0 0-334q0-140-98-238t-237-97l0 111q92 0 158 65t65 159l-223 0 0 334z" horiz-adv-x="928" /> -<glyph glyph-name="ok" unicode="" d="M0 260l162 162 166-164 508 510 164-164-510-510-162-162-162 164z" horiz-adv-x="1000" /> +<glyph glyph-name="comment" unicode="" d="M1000 350q0-97-67-179t-182-130-251-48q-39 0-81 4-110-97-257-135-27-8-63-12-10-1-17 5t-10 16v1q-2 2 0 6t1 6 2 5l4 5t4 5 4 5q4 5 17 19t20 22 17 22 18 28 15 33 15 42q-88 50-138 123t-51 157q0 73 40 139t106 114 160 76 194 28q136 0 251-48t182-130 67-179z" horiz-adv-x="1000" /> -<glyph glyph-name="play" unicode="" d="M772 333l-741-412q-13-7-22-2t-9 20v822q0 14 9 20t22-2l741-412q13-7 13-17t-13-17z" horiz-adv-x="785.7" /> +<glyph glyph-name="ok" unicode="" d="M0 260l162 162 166-164 508 510 164-164-510-510-162-162-162 164z" horiz-adv-x="1000" /> -<glyph glyph-name="link" unicode="" d="M294 116q14 14 34 14t36-14q32-34 0-70l-42-40q-56-56-132-56-78 0-134 56t-56 132q0 78 56 134l148 148q70 68 144 77t128-43q16-16 16-36t-16-36q-36-32-70 0-50 48-132-34l-148-146q-26-26-26-64t26-62q26-26 63-26t63 26z m450 574q56-56 56-132 0-78-56-134l-158-158q-74-72-150-72-62 0-112 50-14 14-14 34t14 36q14 14 35 14t35-14q50-48 122 24l158 156q28 28 28 64 0 38-28 62-24 26-56 31t-60-21l-50-50q-16-14-36-14t-34 14q-34 34 0 70l50 50q54 54 127 51t129-61z" horiz-adv-x="800" /> +<glyph glyph-name="play" unicode="" d="M772 333l-741-412q-13-7-22-2t-9 20v822q0 14 9 20t22-2l741-412q13-7 13-17t-13-17z" horiz-adv-x="785.7" /> -<glyph glyph-name="calendar" unicode="" d="M800 700q42 0 71-29t29-71l0-600q0-40-29-70t-71-30l-700 0q-40 0-70 30t-30 70l0 600q0 42 30 71t70 29l46 0 0-100 160 0 0 100 290 0 0-100 160 0 0 100 44 0z m0-700l0 400-700 0 0-400 700 0z m-540 800l0-170-70 0 0 170 70 0z m450 0l0-170-70 0 0 170 70 0z" horiz-adv-x="900" /> +<glyph glyph-name="link" unicode="" d="M294 116q14 14 34 14t36-14q32-34 0-70l-42-40q-56-56-132-56-78 0-134 56t-56 132q0 78 56 134l148 148q70 68 144 77t128-43q16-16 16-36t-16-36q-36-32-70 0-50 48-132-34l-148-146q-26-26-26-64t26-62q26-26 63-26t63 26z m450 574q56-56 56-132 0-78-56-134l-158-158q-74-72-150-72-62 0-112 50-14 14-14 34t14 36q14 14 35 14t35-14q50-48 122 24l158 156q28 28 28 64 0 38-28 62-24 26-56 31t-60-21l-50-50q-16-14-36-14t-34 14q-34 34 0 70l50 50q54 54 127 51t129-61z" horiz-adv-x="800" /> -<glyph glyph-name="location" unicode="" d="M250 750q104 0 177-73t73-177q0-106-62-243t-126-223l-62-84q-10 12-27 35t-60 89-76 130-60 147-27 149q0 104 73 177t177 73z m0-388q56 0 96 40t40 96-40 95-96 39-95-39-39-95 39-96 95-40z" horiz-adv-x="500" /> +<glyph glyph-name="calendar" unicode="" d="M800 700q42 0 71-29t29-71l0-600q0-40-29-70t-71-30l-700 0q-40 0-70 30t-30 70l0 600q0 42 30 71t70 29l46 0 0-100 160 0 0 100 290 0 0-100 160 0 0 100 44 0z m0-700l0 400-700 0 0-400 700 0z m-540 800l0-170-70 0 0 170 70 0z m450 0l0-170-70 0 0 170 70 0z" horiz-adv-x="900" /> + +<glyph glyph-name="location" unicode="" d="M250 750q104 0 177-73t73-177q0-106-62-243t-126-223l-62-84q-10 12-27 35t-60 89-76 130-60 147-27 149q0 104 73 177t177 73z m0-388q56 0 96 40t40 96-40 95-96 39-95-39-39-95 39-96 95-40z" horiz-adv-x="500" /> <glyph glyph-name="picture" unicode="" d="M357 529q0-45-31-76t-76-32-76 32-31 76 31 76 76 31 76-31 31-76z m572-215v-250h-786v107l178 179 90-89 285 285z m53 393h-893q-7 0-12-5t-6-13v-678q0-7 6-13t12-5h893q7 0 13 5t5 13v678q0 8-5 13t-13 5z m89-18v-678q0-37-26-63t-63-27h-893q-36 0-63 27t-26 63v678q0 37 26 63t63 27h893q37 0 63-27t26-63z" horiz-adv-x="1071.4" /> @@ -28,19 +30,19 @@ <glyph glyph-name="down" unicode="" d="M939 399l-414-413q-10-11-25-11t-25 11l-414 413q-11 11-11 26t11 25l93 92q10 11 25 11t25-11l296-296 296 296q11 11 25 11t26-11l92-92q11-11 11-25t-11-26z" horiz-adv-x="1000" /> -<glyph glyph-name="retweet" unicode="" d="M714 11q0-7-5-13t-13-5h-535q-5 0-8 1t-5 4-3 4-2 7 0 6v335h-107q-15 0-25 11t-11 25q0 13 8 23l179 214q11 12 27 12t28-12l178-214q9-10 9-23 0-15-11-25t-25-11h-107v-214h321q9 0 14-6l89-108q4-5 4-11z m357 232q0-13-8-23l-178-214q-12-13-28-13t-27 13l-179 214q-8 10-8 23 0 14 11 25t25 11h107v214h-322q-9 0-14 7l-89 107q-4 5-4 11 0 7 5 12t13 6h536q4 0 7-1t5-4 3-5 2-6 1-7v-334h107q14 0 25-11t10-25z" horiz-adv-x="1071.4" /> +<glyph glyph-name="retweet" unicode="" d="M714 11q0-7-5-13t-13-5h-535q-5 0-8 1t-5 4-3 4-2 7 0 6v335h-107q-15 0-25 11t-11 25q0 13 8 23l179 214q11 12 27 12t28-12l178-214q9-10 9-23 0-15-11-25t-25-11h-107v-214h321q9 0 14-6l89-108q4-5 4-11z m357 232q0-13-8-23l-178-214q-12-13-28-13t-27 13l-179 214q-8 10-8 23 0 14 11 25t25 11h107v214h-322q-9 0-14 7l-89 107q-4 5-4 11 0 7 5 12t13 6h536q4 0 7-1t5-4 3-5 2-6 1-7v-334h107q14 0 25-11t10-25z" horiz-adv-x="1071.4" /> -<glyph glyph-name="search" unicode="" d="M772 78q30-34 6-62l-46-46q-36-32-68 0l-190 190q-74-42-156-42-128 0-223 95t-95 223 90 219 218 91 224-95 96-223q0-88-46-162z m-678 358q0-88 68-156t156-68 151 63 63 153q0 88-68 155t-156 67-151-63-63-151z" horiz-adv-x="789" /> +<glyph glyph-name="search" unicode="" d="M772 78q30-34 6-62l-46-46q-36-32-68 0l-190 190q-74-42-156-42-128 0-223 95t-95 223 90 219 218 91 224-95 96-223q0-88-46-162z m-678 358q0-88 68-156t156-68 151 63 63 153q0 88-68 155t-156 67-151-63-63-151z" horiz-adv-x="789" /> -<glyph glyph-name="pin" unicode="" d="M268 368v250q0 8-5 13t-13 5-13-5-5-13v-250q0-8 5-13t13-5 13 5 5 13z m375-197q0-14-11-25t-25-10h-239l-29-270q-1-7-6-11t-11-5h-1q-15 0-17 15l-43 271h-225q-15 0-25 10t-11 25q0 69 44 124t99 55v286q-29 0-50 21t-22 50 22 50 50 22h357q29 0 50-22t21-50-21-50-50-21v-286q55 0 99-55t44-124z" horiz-adv-x="642.9" /> +<glyph glyph-name="pin" unicode="" d="M268 368v250q0 8-5 13t-13 5-13-5-5-13v-250q0-8 5-13t13-5 13 5 5 13z m375-197q0-14-11-25t-25-10h-239l-29-270q-1-7-6-11t-11-5h-1q-15 0-17 15l-43 271h-225q-15 0-25 10t-11 25q0 69 44 124t99 55v286q-29 0-50 21t-22 50 22 50 50 22h357q29 0 50-22t21-50-21-50-50-21v-286q55 0 99-55t44-124z" horiz-adv-x="642.9" /> -<glyph glyph-name="cog" unicode="" d="M911 295l-133-56q-8-22-12-31l55-133-79-79-135 53q-9-4-31-12l-55-134-112 0-56 133q-11 4-33 13l-132-55-78 79 53 134q-1 3-4 9t-6 12-4 11l-131 55 0 112 131 56 14 33-54 132 78 79 133-54q22 9 33 13l55 132 112 0 56-132q14-5 31-13l133 55 80-79-54-135q6-12 12-30l133-56 0-112z m-447-111q69 0 118 48t49 118-49 119-118 50-119-50-49-119 49-118 119-48z" horiz-adv-x="928" /> +<glyph glyph-name="cog" unicode="" d="M911 295l-133-56q-8-22-12-31l55-133-79-79-135 53q-9-4-31-12l-55-134-112 0-56 133q-11 4-33 13l-132-55-78 79 53 134q-1 3-4 9t-6 12-4 11l-131 55 0 112 131 56 14 33-54 132 78 79 133-54q22 9 33 13l55 132 112 0 56-132q14-5 31-13l133 55 80-79-54-135q6-12 12-30l133-56 0-112z m-447-111q69 0 118 48t49 118-49 119-118 50-119-50-49-119 49-118 119-48z" horiz-adv-x="928" /> -<glyph glyph-name="rss-feed" unicode="" d="M184 93c0-51-43-91-93-91s-91 40-91 91c0 50 41 91 91 91s93-41 93-91z m261-85l-125 0c0 174-140 323-315 323l0 118c231 0 440-163 440-441z m259 0l-136 0c0 300-262 561-563 561l0 129c370 0 699-281 699-690z" horiz-adv-x="704" /> +<glyph glyph-name="rss" unicode="" d="M184 93c0-51-43-91-93-91s-91 40-91 91c0 50 41 91 91 91s93-41 93-91z m261-85l-125 0c0 174-140 323-315 323l0 118c231 0 440-163 440-441z m259 0l-136 0c0 300-262 561-563 561l0 129c370 0 699-281 699-690z" horiz-adv-x="704" /> <glyph glyph-name="info" unicode="" d="M393 149v-134q0-9-7-15t-15-7h-134q-9 0-16 7t-7 15v134q0 9 7 16t16 6h134q9 0 15-6t7-16z m176 335q0-30-8-56t-20-43-31-33-32-25-34-19q-23-13-38-37t-15-37q0-10-7-18t-16-9h-134q-8 0-14 11t-6 20v26q0 46 37 87t79 60q33 16 47 32t14 42q0 24-26 41t-60 18q-36 0-60-16-20-14-60-64-7-9-17-9-7 0-14 4l-91 70q-8 6-9 14t3 16q89 148 259 148 45 0 90-17t81-46 59-72 23-88z" horiz-adv-x="571.4" /> <glyph glyph-name="bird" unicode="" d="M920 636q-36-54-94-98l0-24q0-130-60-250t-186-203-290-83q-160 0-290 84 14-2 46-2 132 0 234 80-62 2-110 38t-66 94q10-4 34-4 26 0 50 6-66 14-108 66t-42 120l0 2q36-20 84-24-84 58-84 158 0 48 26 94 154-188 390-196-6 18-6 42 0 78 55 133t135 55q82 0 136-58 60 12 120 44-20-66-82-104 56 8 108 30z" horiz-adv-x="920" /> </font> </defs> -</svg> \ No newline at end of file +</svg> diff --git a/public/fonts/fontello.ttf b/public/fonts/fontello.ttf index 29f1ec6d6ee124b4f25d2cad015933efaf7f982b..ef775f87308c85319ecd0d8c36791622adf5c7eb 100644 GIT binary patch delta 903 zcmYL{TSyd97{|XeXJ&WC%j{LWpt7vCiSAlhvv!e9ddP}UiB&R{9Nk^l*vsJBX5yur zB|aHL1VKx^Nkm91zJ$<&kJ&@@6huK6279Q`pdPmE8y#uR{O0`s-#KT_cfOgqwjXT^ zu9-k>a2<ej0>HpfTvlFLV!h~>0GP?4^q33dc$FN&hZs?Y<AaTVCeWWmTQn@E6pUNZ z@5i1Wj$MiLe(-z;V7&lElhLpo^1Z2jk5d+L+#kh&`6hjY{v-O@Xnd?^{CF;(2e7di z=!_+Y<jSS;5c(kc`nWu<kY@2d`m5+&2{|5Ke^6P4{vjr~t|U`q&%Omd132|WR+Z7P zGQDzi1)wH)=XVcfX^$EtEi)Qy3iNFLBAlmB;2zE-$p4E$$srWM!~t_5bH<$O7}aNc zu`k$;v(Sy6$A;@P>(9DPK|g^8y9!739eAk2rYm(H<0g)5CsRzSwzfwB9QukK^!IDq zs1vkfhDV2hfW3jfvt|H=s12Y9wE+~PHUJT|0a#EQ=$}e602^S&JAfUqLOUcdH7rDa zgIQ!=qcY=$Qfm`WIKs`E2%%)wOeoM<mcjF<d7i~`@r8V0fxwzsvx&ns)Fzlp8yvP8 z8;1~Nd6_)bZ-&i%Wh&<XDrAA2%at)&ww1jta0O5bd)1f4O?F3(oi|lCG&@ah&q4oz zrfQ;!qC2psyuN~PQ(bMRx-W$yM~)FX*XYfJqRIK39P%IT_ejzisk}bkeYe4vmLxfP zD-jK4yuNFpzW%8j*_f!2%I90F;sr~~FR)O(^zfxs$?W#3Vr@1%^*FPrKDKuF{4f9+ z*dPpXkql#EPB1TM8EvO;*)n#T-QwE0ccuWY*8dG>)@>6AkyNjJg-F>?_S1wo)kddB iASJ-1N5Yp=YL~OmLPt|6b|eu=vV$X|A;_*d*TjFl`^A+2 delta 787 zcmXw0O-vI}5T3VhTl&ZLXUmUBOhs&~w1hO$9^`;0jF3PCNiY?&wA(I*!b%B2f;L7% zj0c0fsEJX6o;Z1_;oyP9!~=(7IC=46IBiT15;WF>sq=RAZN8a&-^{-GX7~HR!-4fs zX=QC}1%QkIz<8#h=r1=uw;<mJ5T0cg7ec7x2_9gOa(ccn)%|-J`B$W#yi(Lr_aWcI z?8wjD$-Um9w*ka{0FOsgm26uzk;0ZCtS2=T8dm8R@>S%WT47;gssCENjsgyZp_#di z(z4RFj{FDmXhB)hNm6<N;BQABnpFzw{;QT&<b7yxU!N;3bXP||p#cxp-|O?LesAaE z4nRlsNo|4>nqgz4uM}2~o%Cw`7F?&J@Ekh{!a6og6cIy+Sa|!dwv8(9ieYw{x(N88 zAb{&q5W;F?(r7T;hS!jfqQ_3YgQ*FI(`YmT|22FA;G`MNK=1<ov_l9wAq-*W5+XRs z&MpZxHX^vs@I?R&GxBkGtp#o;GReWxbhzZiCIs+|206G<!)*b0ZnOY+ry*GYu6Zp0 z*Kh&{a80(r|L9TF0{8)raRvu>V|^Z)E^mn?r_BLPaF%sS312`CIP9moQbBu1_(C$# z%rvP(7)-Ip(&-b)<?*Lo@x|U=MO&NIvYWB^{cQTi-3L(KlMK>67O(xe>FxUoUTUuW zrSfYK2LaEl;!9DP8qDP%K6f58cn1~uMl^CD_=HEoA#J6b^iaGk?%JAdZ|ru{o?Kq@ xI|=Dw>wz88Qy!475F)dUV3+{=5KPOC`C>7iQ`M|EJ)4^or>5t#<yg~4=`Wr=u$=$^ diff --git a/public/fonts/fontello.woff b/public/fonts/fontello.woff index 8428cf8cf6b3362734cae51568f5c293ef1e9eb8..63c3c233509e89b741c92971efe70a441b972b8b 100644 GIT binary patch delta 3748 zcmXw*byyQ#7stm&jP6omgwmry8gYzCC@DWcVsuSVKqhQVN8>;V8A^$OfGCZ$gruaP zNC}Ps5<j|;c<1}P?|q)nbI<wS6Zigg&yj4699QUVI2=F)pe&vm0PEjfY%7^d9S5UW zF1q5@gKj8fAOJu$LGeET06@$TtG|QW!(a%-j-zOd6j&$#JOjN@6fF$^pk1d(Os>%5 zFfU{fC6_*mat!_-Y+nA4JSiH1LJt7IgmIX=y6NqKbO!)9ttm0e6x8I#aN<9`DJn($ z+XO;EoGuEK=Zy+RQ?z1=uS>z);OyeUKfvv8+%m<N{)<2bzdH(vrZnc_qWF-%P^q)L zx`#x0P&5_FV}dCc9UROS5DW|mqS)Mj>pi2;QnjYL5ct3&5C8)3TuU{jG6f-%+*32D z#Bfdum?d|XliV)0uI3n|rg;XtAv-6)x11>qowc5Fodd7HWGIZ6Oh|Y@&y&*wYS<~B z)U5Bx-D!zl(Y|WtgWO^8e_}sl2>Sjsfl&0$rsRi!aSJK$*vSo~*j&`$z~gaR#qnZX zB5^=%YI>PbPRm9sJE-7y=SxPA?L9)+2gIT<_ECiP<?2$I6^Q9d(@ihrs6)mcKO8GT zU}A$q!D2E|H;0k#2@1!dNcU$7I|x4hD}<TVi1)o!8$Aga<xm-dbAF}r!|FmHGab&s zmb>-$?BVe6L3QqhwpJ2(V)!7)SK{8Dt6ummM9WBwT9i1m<Eyk9Gvo2AT#exCq#F*= zs`MVFt6Z8T@8D84HfDx595vt4zwpLm=RpY0cSkkt;_#BUN@f^78**To)r2mxRqt>% z^b|96oISUBw*x#Grte(1$pLqtzlvLI{O}k&pFrK_Vn_?0NYIxODdIL{16#}_IE2tw zK}<x?V^*M2mf%$%$4<^I44L`pie7QWu}EUe$Ujj;mj}wmo1Bm%<XCXz5#sg6E;#L9 z&|Pgo-#p72037k~iOsuf54C<p^G8M|_9ym7MxL$h`+m4kb1Z9B9YJ%((Bfv_{qQZc zWOg>n+-ycTi%HX`mW4S1_7eV>@ptP}EpyFE4kTQQI-}AlCa>5Qz(g`eZ&;T3Yc;4~ z)R@|qwe)hFVh(+_arr+kt;v4^4eQ{j;pbW%t%g79cqXtXTCU0HmO^fDTLo{gE(R#$ z9jNK{R_ZOC!KH%1EOGLWFSr+SJySG8(B-10*Wd<Az4c1YwvF-#v<OjeKXsh&PEp!? ziM?4wV}XcG4N~}s3Ek+3Ee&)(=q4!mNdeK;uM^rZZ#*Tp(Fy_Of)jPxR@n_-_tv0I z&qj=4jbcg_jA2)R?7fby^v?qPhwjpmPME!aEz>^Y=#hI{4Vjn!iA$E6YSORz64n<F zku;s~-=_sSov6!W3Kb<vb(g$_XpHha&+E?HQua7GB(MeL=UmA!pXfMz9KAjh*ijTH zDq2A^N*x&}3yytL{zGbWxi{lOWd&61+^#2yPxrZLX;i}#aC&4~b)bm*1os}uC<9j7 zIjDduyvtr6k|Z&P1DLFBNu(aHzUW#lY&S`o%T-+8HmMvnXVQIlr<{fPaN1tGd@{Ve zoBTCw<{;Gjt&f)9+*cLlF$>3L%a$4~^POE{@U!x+m~}oKrxweW#vkLF9QKE^w(~c> zW?ZUnXjn&R2lU2#u$@pcNdfy@Sr<3+T#cn!MRpd}$5kx^;rs>)@f~-+(6h&j_nB&v zX!!MY<J9?+II;*=Q<p8c5K7smnI-7(W}UA*sKk%m_Jlx5zLB$6CZgb6XC(%g%gFcN zg1`88j4_gO9xx2Q&KZp)sU(egq!0Y?Js$iih{Xza`MEf)&CFm?&!r>xpwQMY#1;2X zYO%S4KesI1aLHE!NWbaRx8zTrd-GX@wlJ!?<hJ=#e*xSVvqFmn^DwVOw6Y>3m!BSq z!9ZrPIs8ZVH@cDxiz%En*(n~P51uXk82a|dCdjqTF0(u-Q7(xv66?3ACUi?+i;GCS z$1+f^I<)n@OHtGxg=&HR>wfReYlE89?1h&nXooo9XWRvFaP5AcYv*SKGm;O>2Ifvz zC@Hq$&}uKu0NO$Dk_>e5kT|19+)1nFuI<Kku+#Oha0Nc|g7m9`l>`m|$(lbt+3aP? z`&fc8!*MzBJ=pV~ZYEoW&-I>d)n~CM+pn?Dgv*5SY6xvz`O;s>6LyoJ)pN^(?PCI{ zP*y7wCuh-8n#*F!1g`DQH<H*iy*@o`<FU)8ZoO<~$m}olCB3b0B=Z#hkJSyKigMQh z_7u@uEwQ%guqYZ)sj6PS#rFHF0;dGcuih%$W*#??I&4h!1t0NF^DAx2DWhW^1+<1G z@de>u&dgVC>Wee+Gy??<$nuFR-E_GfJ!Jbp-24%G;&yC+l`^y@1R!uM_D{{W6B$9) zVYnmc+frg}n|KgX$h!a0i*G&d@Sb1S++=1j9er|oBMIdz6m|&PGDrq|W@@@7|74f1 zZxsk#E*5L`7O>*<C1*@y`8G$W1UKrvrkHszJBK!riZ-G&*B`2%6Z$uNLNQH3dfbh; z75;&K-adUSkseR|ndvyP*u>;Wej++5h{0SG^x}3PT&+@~_~6>4p1Hc^r1uwaD78&V z%X7m-qr0JRRu7has=-Y;^;i2=0u!E9-SrsAZq^RV%bF6}zT_QC9knks{kqlY+J%U5 znh2W&R>XXxJ~bzVn3o+@<T#%O7?xhc<rMr$!pkHwf5NvJMK7`5@{PGH^F%2vfKS!_ zd$`cBj;atod{n)QwRu&i{4i(AcP90Af>T=PRm*7A@x*oAx_t-FQ977};;7wR9@#Cv z`T?IJvNxIL-q<%=rT+CpI<fXFNEd@0^qp1%+CD-e{*|bgg+=D6yMd_vfVg@LuA8Vf z3)p#B$=(w9M?gr-vpK``-h%q$=7vfYjbOi>j)wj3LDLbK$Q+}j#+%7&I#-3-g_rDG zjol636#KZhU$LAGE;<riwxCK}_O|t^Fk*eFLki?%mJH&ZdaNZTs^s(b34vjF<m=tO zm9OltJvX*rCduv<M{u(q<>Z}JGvlo3F*RGZ)Gk${=|x?4&>!V;14>Oh=EV$cxFe1e zJ@v_)cI~<(#^Zx5JnU0??2xa7ite>q!T#Y1!*TdSw#<La^s)yF$R8Br+gVDlxE}Fa zNbdSL-Gu2{#>96Eva<%$kVR_jwxUmmTxX#ZeFr?rp<(X_;<8dP&xh<ZG;p@<uAlRp z86Ug=e>}~JV=sZZ%Sy1bOX8!j7xM|C)^OailJrxXx=L+J+8Ah-Ub%*tOw46oRjkKA za>0x4vG63paAeYuewlCm9e6*!s!_CFI=|F(2O(<zHr+^&SkWviePnR^^~w3b)1KvR z#-OM*CfZZAGu4e$QE?;beO$@}EGgy6%+Y;Oxh*(zvjthyRHX8e2-RfyZVsm@-BC^n z$NbqO=`xR^W7UH@jfC}UuD_`N+z;~II(B$Ls!B!m_2_H=86X~*QT`@0inoHXYp676 zcetfOL+iJ8$b>X8wqNczz^wW1vZf<+c&I$*Rc_PN<bGts%4e@(b=>x`I+o$kR(I6o z)_H<T*HV8S)-5%JF_a6DACRx)x}7Ul&&v(l&<w*oZ;D83(w(v&MqQp)Textz_1&?- zTm<PMF6Go4(;%KP&I_p1N1V7I+^T4E9X{-V-~l^=j#CSGOOi(3mYA7ch|ep(?~9Lq z3fH=l5I~6pFUi~l9C5%@<(o)p)v0PUgH>H4%everLd?i1vvfxetk0CzI-t&WKa!Nd z-4l6UfWkXhJ9fNYu!X*?jYly@v_Z%8lC>wTgeQ{wKU(;Q$iYYC722btKHH-<+5#IL zEBLg>Wmu1c>SN}w|ExhbL;6VTI`6)^*|Hf$I=2Ggozg^{^q>qwgH^J7tmDwPPZvks zwZCf@cDB!7KK3=DV{va}?JUEHXE$!_?MI)K?h(fLvFGGWx@Eqf4xHks2z6%q+J=pJ z$17}t8aBJ)bh3+jkIf+y0Cbj{i@S>pVE(5w!)({?G{)nXR|FLo61{4!zzV;(xX?@O zdaS|MyXb;*0mcIXL`N3-|0n&OY}?x2P=YRoWoeo8*|{J<K)M92ETw2fds|x#{>c*! z+BjaoHlrjDwJQze9@53-wBx@idZtN+!icB9ebwuG3W5MvKS($&2n0~tVW!O5|6OVT z7eEeRl?q4&p~?Z$0tMl~HV_}^7U&x_A9V!v9?dP9DcZjSJrxD6)LBj;+?;QR0#__< zdrmDH<zF$q=qu@F!#E4Z{~F0$hsV_Ak5Aq474zZ3B({7O@|3UinjF5=V?Lg;q~(0; z)+p!~?Ht>Lo?wy=W0vw%5S7=m9duOB0h;VAOSJf$xyW$Vv?wjaZeK>?5q=Lb<|XhP z)yM|QmC5o3N^QvTuH?$%h`l=f9y9T_{oG-554bs#?aD~kG0fk5OUVjKRl44Yy2XbJ z!7|)7Sz+;e<5w*;Q?VMhCG;sUjZgMMXo0A;&av1nh1<aFF6kGjq7d_Z2y=}WezGj= zBfX>B(u1<^mMo~4yFRsG3>nt_zHxAAZdc)P2ZMumr@sv~C|(nXimZOhV-&065#zq3 zD+6a>rB94i4TT*Wch?UqdZmrh4j_72FcCzF2vddzJtQ}_Doo%uqDgsv#-zrsE+j=& zwD8ee;`HR^Pi24fD-*wZzfhCxIb&C}^^GdwrdpcB+nWoHiZzZZ$LG5^J+gn)ZFClD zV44FD;esP0uHzK7h&m7wls_5uSfd3y6g(Z;08N+dye)R_XQ6M92ct>6bXjG>HCBh^ z+I*BUwEj((Sz3URiFCM<BC-6XvQIx5A;PO&+?V-m2g+2dnq%PQx<2{p-)qZ#liEg> uetgzFCL_r^25F@=L;ms-yakIz1Su(Aq4yimhXxp`d{CKWLFylF?0*2}8o!zV delta 3710 zcmXYz2Q=JG7shvyjb0KpN;XTZkawf5)rl6-6D84EHEOV|5`R6261)UiohZ?X5MA`> zy+rRdgsjfD`Mz(?`JK7<nS0O7oH=vOQ}xfuLwZk5B_$9TL>P=%5Y6A2Gc1-~6p$xG zdcL7LzGz2t7Z3<MNyz&^AfiNE+&B~3!yQV9^9dRS0crvuYZn_wf>s3rL3Rler3K>D z+s53D&`Xv>SSI~Hv^EZ2)&#AHfGY?@Ntu~J{My#a+!6%34JFhF5<uSB0R%;D2`WL< zAqamLxNZg$XV^NrV+oo&A(tSaCaZgW?cj|5TUSQNdH%xWd*{8QIhL^T?Z1bH{soMr zPH{4Kv?6FHgv${6iTe6dnV4Oi-3T#*1ED6Gz&FIs^L`gsD;E$kh~Xk$4Xj3NsbL-e z5zGmUK9g}1ym>ib5oSScokUG<P0|epEgw^KgRr>OI04sIep<!!NMcenn}VubESS7s z;e+9dYyoPbI+8~A%3G0Gx$9M$h&Ng_<q{(8pIj5hI2;u`QR<L#5jnPE=vRgC?Gf($ zH8RFHK4n&S?$sM}Q>~=jp&=p9ry%Fon;k%Gzg_#gaW=c*tYv@T{g>;QqRlD(jjG}~ zc;7t!4EU0krl;R7!GV32XP@C@!Js+Ln^vMLiTRwr{X+4wllLjn53qD8(KTxk#eOZ# zg-11+izrDAA9&9rAa2!<`d<MwH4i{tCHD83usF1tEadOAf(dc4zP%x9XC;mZ4*^(h zSb+5720(hoCjA=6-uZf`@{PxCTNDC6<yO%~;b>d=J||N(KCW7&dpbA^@tB|X)#ozq zmn`uSkKwvwJh_FuI>H62<|$g?+>>x=|6r~5JSuL14&em5rSJK7KCENNXW;#w$5*wT zo&)C>Y4g9l$Y>=eAI`$0Z`qzm5&`$m!SHmNFWL_bNI1FbntAov>u&FNL@q9_t*)&u zE(Y`|6-&E(EJ4n(y=nw=$~z;s{<zjgmu44xn&gzyB?PN@W<x>dmg*3tsT2}=|9&%h zbCWg%8|j7|`=ch<{fL%-IpSOyX1a41E1f_c^H(i9y!EQ8*V_%_KYdRfvA|wPHa~uz z!;jGzsA`kWXu8iBE;>CO0E+Yu*y)6qg+c~wdW92Uillb$k#2F3GJN{wZ1%J`Ip&NY ztw19$fBZC=+1;Wh;GRC}G8-RRD8Q?=Mpu7NY~{n75eoitZ_mZn^KBRRs}3B$^8+HR zPDVttZ<aOB0k+J{neAs2Ag=$hZs_+m;@qDOMz<N=)r{C0BUONUUUEy;mrHrF2XBjJ zgg3CR9Edv!q}~W;-L;CqBi<K9n0UZQ$!}NBGz9p6xay-YeTC>Mj1fywZZXMcxK?hm zQ8^<EkM|p%#|A#hs!~#rYXd<c!6md{I=abQb^&rI#$frO`E?z@V^zr7=V8&0Fkrl# zGn9sFgct1q4cvsTKE#o-craWxb3lf8-Gm}d#q^1sQkPqVn}>g2SypJ(RT$L?`*t6k z3w@IEdZ5P<-lF_vdMtJbZ1|2z*k^tWD>)rDb5(DhQeAnVt}Y~WI{iCgI#(*7BE2cc z#+4KKs&gZJI$;_ZdsCgLX)ASZ5*4F>e$~6SR?T@(Lso;=pW;=GBejF4a;dM~Q6||k zZ_BL-t5|Zg@68QtHR@5JV~(}tPCW{wgx+mc3PqFLT<+%broR!V1|PBJon!f>=B_Z4 zy<3jtWFv>TH?75ywyuAOgiR_j3yGu(8txy-)qIU><;w^1j?7R|m+m3=DgJny`)D>c z$(VkFtbHljm6`}G5LElMbTn`xoQfD_+Mk>wmT^E-uKXj(fhN1oIt~xz+p=?41)bl} z_=tgou_m{K4tfda+`N#6W@bEEilL<vrh7v%trT{a!KKrqG3e2g7~UoI;wNf^S*x$$ zo(;MjDNqhbB9(qQUzB?RTN!y(-(1~nUuS!T4j?pX7l*QyODXD-Kl0?$rdo=6eG-_W z%WB>?%<z&z=tN?f3+_c`(GX>}l0z7npX_iASdd$WlV<U-!2+Qi%P&zS=c<Z)vy2Cx zTd6H(va!<!?rp+PLBY=%xRj7qPb)YC3m>dBnN$NXW^)!!qju6|9C(15jiWgpFH_&- z#Vigz*sm`NRJO3OqRGG6Gxc^|i2C2k9fxYQKYL(tVG*v;k2D;(Z$iZEGP()7-+yXl ziZY0ly+Ij5;PTGDA;~QwEAn_NBe&q3u#t_`<ok5y;ZGtS3M!p0p)k~Mlu~VlQzAZ9 zWR?LOq!6QL%G!?(T^1NrHaE$i1!avY3^ycz^YPr!TM!D9kov(3Rk!NXxxD>@97deD z01JL(Xl68$g5AqM8vwmS&f9S~Hp;iWl5KdDuvId0wRg1G*iV^#ik*+vyGU)2^Ngjn zvuB=5T+8%~x$%gej6bsM-Qg`%Ol%QiArOcb|9!ajS|k%0Q#Y$z79zPa`+A^=6@hHp z^7tMzONJTWb-H57o2}eC(UA7zJsK2Z{_yC1W=95DAW1gmX;8i@-wA_UVz3*z7K}JS z(^aXu44#iiq4-DnpH12Jkgf+6j=t>8>fkJ?)5iGxsd^dM(pjS7Dvn!fc&N|fCJJcA zNayExcuU?CzXvvNm_Tm)qU{I5ZdsAGJuoHxS7I;6X2i-iZ{fiQ6lZf~&nNh1WA=iY z+q(hizINuHx6e}+JJd2LS6MrvJpQD0x@kteT}V5^97vUV4{SH&RkI$iJkMfUO%2yE z?^8$T^-G;m32nYrfC~(dP3A76!z%$-dI9sU*!xZ+!}@{)yEG)6EjVa<#7jhBT`ktY z4<4QZv(eTzys&%>Gw9*^s>H2d?FU{dekSC-h!>OW?8QDv{>keA_CniRd9REgg}8m( zUiY{-xg!<BhKav5;X^4IDAV)BFSRQ|L3^YtPBo`~qko@qLjWALf%Vic(+6mh8x~zC zsPbKy#Qh~WVS;v_0wT!=28WEMHpl2P^mY!CqaPmS`!Q&qRA*jR66=upR-NdRn0=ep z&dBw^R%dH%<ZFEYO{}QP;CKGTTK4ev%NFrPic@@As6wWRe~AH;@VrDrWny~SyP}{S z7;<U^%llEKa|V4Y$XL&?Z66TI(pnp-j|=n7p(O6(|8d$uG$C)xxA-n(HaF0?Dn8W_ zR(h0_OeY3Eg{D@zx|T$AhuT=tx{i@1Ud)Bj6)0J%!RhFD0)jEuODQm&PE&W5vDK8B z7wn%w@=X2d6S#7b?@{q7jmX6(!v=>14V^JfRkG_)eIX<JWd^yIroe5Zu>%aZ7Z=?> z&p$6mtG+O1VDQa^Yx#KYtu7z6h1_VxK$tEVFB!Jfk}OreXfqmiX;(b9Yz?n!qY&AD z92L(W)V3B*vf|%wui<ZRll6k?;B03yGznoS#%J7O!L~?T)KQ$-(&?<H%-27nOFJNa z*natZ-T<Y2m&7A;6a!$)4{ASDuQTD`(JvCnr+q!Q5-g4n6M8O|&NkaaJ{!-@8zxD) z;xDL?*Pk>O=6aORx(ZC=5(g#U%!mrimbr)q(4RpHVcb_@uv^?=KOK+ow4Ue8o<8*r zu`>c~|8!k{#o5)kW?HoNJb6~1MCB&NUknF-m3eYv_5@u4Ne4a_Q-exoSL$l3y4!QM z79i2B$0Am<xppNOf_DH$BSJXI*m%-nAM=_Zg@&|D^sG+#$Qi7iFn8zly>C!u{<*6Y z)DT{gOqoB&QMz^WI|H9O<WgmZq{+H2*u1;cNO1eP#M$}1I<;#Ba!Y5ltv+H#uDtgP zi(FGP2~!-^AyD<@yme8dVrfZ0|1?hKtsM`?sQSB7CN8o1N*NLFUEVC){quM;g`%gm z)8DegBy?3gCRf!iC1{+DeB@M1(wLJd@;%%)%s#D~?er)QZ*FS<fNh4-0LplXNxISN zy~f}hjNCBe@!)$c(~SqSx<o_eBN$EBu4h-o^mbGlAmG2&%+kyZv_v~QmV9u5G-WwS zHU%j%JEfoQQDt9WU#E9lG_z5zxbpJ{C=fmx)#d-}9HEE$EWN$CIg3zpJ@61hDN9EW zr358%L-+`G&#$GqnT@!O!vEw3Jq1xY5$#ugetti5Gqa)V>$}{YSu{C{EX+wMvQY0N zYEZezd#RuRFz6CZ89{K1gux541yzB1L0e#3@E{Q_Q3TNvF*9)*@e;`slExd1H?klQ zf|VqA@Ay0P96;aG@3QntO+#lBff0d*zn&Vj2}6F;lZQk&xpNK|=h&s><Rl9wGoIaP z45C?9J$dM-o(E&R&F!|AEaAz`)XdVDQRcUY8ZUC`vh9;V$xa*~E*qBboOT~@+`(e% zHLurdqV2h&Sy7!5eEHLK*<{jee7M5fof0U$57C~R&H&p)mvN_0jn+W)@g`>{hEiv< zCV=nH)I?=&ciiaH%K5lv8ebNK;&Us-BvsVc0%Y!yWf2AIdMzF%HM_5h>pdKFXzh~> zQ%~M}>?8$bb1cJ<nNiB0w^m_fMeIK&Hwq+cD3e*F!a28d$NR;1@xKlS-*!-{GFv+6 zl2gdV1J&(f{-#td?^#-VF?yjy_gqtG-S(b*PKbLQgYM1Gh)Rx)mr}UU_<BL{VDSp4 z+dIBBkCin@jny@A`SI;aZ=ACrU+{ZRwBMeIMau}0!4KolhACTmfp29*f-hebtJ}i7 zOSd9&cQg8q)347VqtAmCu{g)xnOlKSdL?r|Mc|%<P3e$4okKj`R47WrIbCQLZ(Q>3 zKKa2&*tG#Y&pqwT(7+CADm-c->~??6LIkV9PsOo#m0IsdIF*+i$`Q2-avCkZmiw;< z$4Mgkx!5ODcEk$1XX?4)44HG5MG6(X?)Utu?{hHKkWNfA74JLD=5!tIj9wjq!`qmw O#fW*Jpm<TnH~#_s{>Ul- diff --git a/public/fonts/fontello.woff2 b/public/fonts/fontello.woff2 index 551f49d02d766d86f92cf78bc80f94379da58c6e..b7541f0278434295a1178b8d5a6adff4deea570c 100644 GIT binary patch literal 4832 zcmV<65+Ch%Pew8T0RR91021H;4*&oF03+}K01}A+0RR9100000000000000000000 z0000SR0d!GgnS4N37iZO2nv{dl^F{v00A}vBm+<cAO(d@2Z22dfgBs!AhWV=kXAoJ z_Fo@xV+eLedBvzIT6C3Xd4>~VPdJ_!2WSTttvBuOt5t?NL)YmCJG~_qJ(8)J$XD0k zAj9=sLs%nncEeRTi$yi=m=Ti9^8CZSdT!<vjo^<bSn$ck(li*`toSs;|8LE{`-+;9 zbTtRLhoL?&8?kIN$M3RmgpQP0qgIcv?oFp-O-q(@BM+dC_4HBqO75=WErA83gw6@v zv;?RnH4&sD#vnk~I&}O!tIc{nCQ0kb9ILFe>Qw7*T1%LT*w=n#cfU6?OMFBsM2Y~^ z*2b|s{^x=!lE|2oF)EIH;t!rrTe=Pu6_5g7`Kj#D2FQRqO8D%M6qI}$hN9lCdRjZw zR~cI8L!l2uf;to*P-tuMu5E*V;+X#}5^!3g0hCoYK%W2KTwnIxN@{)$plUxz!k}d5 z(v?n5y-Geq`V*!Hc!5YLRR4@5$^xQV+ivmrwd>Zn6>_a#VdM^D7l4UtI6Z30w4#I< zC65(qHZt+QrGSwoIS|$iuivRkm-*}aswx6Npm}I6U%kkcUF{_Z1iMN_ecAT?)K<OF zy4x{My!3wi{0j?A&?4J{|GeGvK~w!zUORU2U-NM1L0?2*)uOsoTf70*m<KxFpF~t$ z#JPFA@NNZbCpoGXhw;f-`9DgXfZjEh@rE$mBK|)fo5STPC@S%32_%$Ake=XK6LZcO zTMJ5Ei7cfhOKHneI<l0mETtz)>B~|EvXr4LWh6@(%UWszrA%chGg-=9ma>qgEax1? z%4+t>(em9G|L4Afg6lAdBG>+6Y<~@P`5>^i%=kV%m-fyb%=156Y_BG|X{-hJfZFd6 zoJ!t7pq-yP7}TnDa7eSoXZBSmdqk9!d|@*^Uk2T$V7&8FC>V^r+#t?=cC4$5$<ui| zVqxrcrP-;IM+8RoT@<2T@(Rwvv)HA9EsR8pClUtYy{9w?{Gdh};jb|wKIq1w$iZZQ zs)B`k+;|usrcHZaxqh}~qwCljjBVXVDJaPeV;oF)cVfvdMWVl^$a86Dc_Nq7&bG<c z9f%FuXh|_Xdl8sZ-~?O*a9Cx#HU^7kY4b4u)$)pZ-oH{_#@OTtr5=E%_!W{;eRN33 zKoFiA!Qwbzna4J7tc_51C{=re(nzsdUIMhJvB|oAY5`d$$fmvc;FPxg`NLEyOT7a` zTn}k%@<t3AZ4}I9AlB$4$j0Ry7H~Mzq07W%e<;Vi3b2C=YdweBP{wLH90<^3N}T)^ z@=_8PF*yd;r+(pDE;?sSP7VCDwHoqnY(;T_!7EczK!hh5w7nvYqU*41j7i$d7-hA< z{Wk8We@}{WYw7<0Zni`vEzIlRDvk)>j)OI7<Fs}7D6FgE^_#?@AxlK?c{O%^7%?Bw z6S#m*909bikWBe2<W<`%1WeLC*X~`CW`?+SClt}?$CDJ_^yiMEzV&eJcD>eE=|n5W zD46116V?}piYG`B5az!4CFqMmb1HBjhoZVtg!^_Ti%fLW>X&d78yc~E_FGdUK1XPn zKe-7*-4gS?WPz-$4-xGAQV7j2EkX68uI-&bXkG@C%u_Uwy`(?og((CQ$w}{}r}*_j zrNjd{M>ESOq9aAYZ0eK=qOxFA5uB<*P)$gx3q=iKQB&B|5>8hR)%`_L`P!YJQbW&c z$`c)P0TrqHzU6$y(ET`NZB#$ZlZ<^(`Bm5GMzWy;MHh-56n!WLPz<3MK{1A60>u>C zy8v0VR$5Fa%2|54jC$rQQf9$jYnE_hk~}?4$1+4lvLuU|XLX}~%c<5#PAi34v6T8M zXS)>3%_76>Ae@d8#;dBi<@DY)$OX`y>!{G(RQ#SAN-#oF5p(~*$27}2Th>A%=PB8M zEXoLa3n2dkIW|!rSafWKOCV03zQ{HTlhmb_5wJC7=t?^qSscj{Hq93I4Vdq?0?qE7 zcI*JmeP^Opc=E$!mx7$c<IJ;m!9*6@EwGK<2B}#Mpv*^X6P|BRjx^_COX%*lsUvZB zHTtdWs7q;kk*BG{gyO1d)0VEVN4kJuY81)bsAB}PK{C~DGJ0!PdHDiShk4d0E{8C0 zK8J@*QnHFzPdW8zBRwM-wjp8$gfp+fR`dpZ2BKq64{M1CGPdaA>~Y0@Jub}*p<W<o zJ>$R^q@ZPQx6xIoWl^mO>sBCDiojast2<peZYdb>W*t3PQr7If>aeRdAGV$~(az5s zp?OCPRB8=+IXhz*n~fM`G1g@%#4|TrpV)W9jPL$rvRfSad7lD1X<<`OgWc4&wRW#Z z*lvYOp;)u@n$FZ?&Nbtx^|4IByx7`0jXv*}T4!0O&!V_J?%{22&;N>%18aHoHtgw( zI%}+}G};J}&UIlUX@t+OJl$xutR=aT__z?yuJUyq)^4-sOl>rG<l@jJ6of@Bn<Tjm zwi{i!8s;jMT83?}XsYfithtseQ|cPfJK-!tNRYy_5F!E_rQ@84X+-f^2nj(t+1(Ow ziX!EZ(z6jV4wPLa5ONMFKO3RoV2k{4MwD<yl#wPXIB2UyoEJ4+r1~s`h9GJ}!fDa+ zo7A3-&~b>mNF($dQhzqWz+rOOE!ttcXf#oaU$|og>_p*y0YNi8jG-%L5@#-<MYDD? z#M0e~mAf-u4-updfFIi+kaiN<H)|Iw9Nb+vy1Q_4ci|i$x=83M;Tx>jjk~)W4|g}7 z?ryvSL~jXwB)#9ccjr;<y9dhkZZg=xSH&6|-@6XBI^gbIvZKC8uR;MHuP`P)fM;1! zzAobfpzQzv`Z@q};EbEYbbx?7xJZB(W`jFx6vGHdYZxBs88%#P=MU~Xj-^cvOx(oX z-Q2*i=WhTPwP|2LIRla}Xb^wbGu|UnzGNAN2^R=1mnVK!xe@K-MFB`?1<=Z_k+b)? z2;uBS{vcDSwg%N2wT+F&CO~tb&%uSF1ei9I4@{6GU5H4LRRVdvav0S6WW=gLg^D$V z)uF)HS)9UnLammoLM_im|HtQ#qH{!mYz{5tFGBbdHFkio`d27CfcfwL&9_jATTdqN z7DzcU2RJ$l8PDcHolh2E7)7usH-17lX72$I=%42qL82G<eDx#+%L~M`MT8ck@I{oA zp|gt=rX}YZ0MiH&sn`@(S~5_TjTxb_*1^;W1D$1>;`1&#*aF9ln4@F&Z{EFd=f*w6 zi0;xS&W9;#F>nV0!Sv;n4?Hws2goH#+<KWBvk__tt7@r!Lx>8kLzULXxZ2uUt%m9D zY=7^KKX+u*5(3JDM2uSUKapVc01Q(BeH|Jb6GCNWrBVSj_-LQ}kZ<f+f59SzQ!Uy} zBrwabLP;5ZbSa3baU}qtDne6<j#@(^rP30@t(QXherrq%(`S==r+kcv33S|>Mv!uT zw<W?CC_VX*K<&*J2s1TgEdYfsQw=g=4UCM}Fv3^aAr-8%sYQ|$qO<rswg4074pl<i zsR9ZDj}S8E7al3pEm>|811Q%iY7qQHWA*Yfs$z8a!Ly^Dg9l0IJwh9E<^c8|8y9Vf zN=U|fwT(@TlJe<Xz<J)VKm`-R->7gE;X_d%_$P}3u@DmJ{r7ng+~0E+bALZUw?Aor zsB7!Vt%rmUPCj6TRkfxioJ~uTW=XTEC;v(b7mt5jsknI5-0WaM{>Gnw#(}z$77Qr8 z9+Wrv`Nv~rm2Cre%R7A(H5J>vMk2Oe_5;m07VeYGe_7I)&s7hFVd8B7FRP%XrF@Jd z>3rrr{UDBvJ$r@*K7Sn;*tKiVfNp3~-krYR>`d6k4!`5Fu+;ThdDZvp%h#I5-nzQp zMuj3RMPbJKK0Z!pxp29PZ9TbR**%Y|8+#l3`?E4~<IS%6PWWGS$;jyM8_Pb^^zoe8 zSKq5<7G}sco!D^NxPOno$hXgb+;_}Z<bTdLSh8E<o-Y_Mwsm|fL~rj;->(neV8zF$ z%C>Fua<c>^j@Rdev?Og{4JB_cAZ>{(D5x*UtIt#CD+raC23;)z<B|m6br;bu#^`(s zWyg*ei5wlBcCXXx(HqtqE9`XclKMS`X2V_)iR(NOdhdJmVj*S!dwKPNK5q0qE^Y&$ zlh^x%7tu>%+-3ujsA`ZKlluxR$~_w6Y;l}1(+J0~qmOOr-_h&bD~9(w?<*-D*(VI` zcRXKQjDFH~e?od+*4yZC%{>5z6v#|iP}6bpfa};XvdfV-E2EMO(nK<*<fc*6F>>7X z;Bm6a0Wp?Q$p-17ADKoy^|lPIxhH5t&zBh^$)Sn)bMmVR-zO%r{Fb2fjR7Z40aFvG zC@AaxK2b-<(RJ&NceH`c1L3+)`aPyS`k&L9abslmxucQpr-h(N)8F`}NrZ8lu!iI8 z)MIyXcePVrWZ$$iXY)D6M!xiE<*bfmx8~=!Gh2l0wt7zMfT)F?m(OgYc@=vxn%VWu zoLN>2n9|A;4X|78%A;jbua#Yp&uTB`wsIz~rB#-1tgP$;#noU(xj6MqiMJ#-HJ9&Y z*WJTdzD~ebru%0G3f8O^Gpm_V74cG9TW4*MG(g<nFAk6f)?xS;aYeDXgta!ZnbWCk zH@IBiZg5pU6Phu0G}ZZaL;1F><=2g%|4=#VpE9ry&f;!2nF<dx_1qBG@B^gxYH8YP z7mi4=Kd8tpBPs}yMK!Nw_1QKzKL0#4xEtfmxwcjp%#~-@DZ*DL8S$j4w20TdC`o94 z&)8!<u5Erz&EBH1DT^esG0jx}h6n)wd*1pMR_@#B)})(VO0^p+0a|9~Kd+D4X8)S8 zO@Sfn5sg`&43>46fG4)?4&;h4SIB$H#Q^(|bw~B(#xJ;Vln8hdSD5P0%~TK!u}SG3 ziPY7Wh<D|b;@L2Wg8VsYTaD(XQX`NjiiD#em;s*jyFI4*@}C3eW<E2qk7l+JL(E7I z8JG;3C17ytfw@J=qIB`S-Hr9hxZkZ+lfcSM?ulTfFCd@GBR4zuDwZ!WXZc)Lk|8FG z-6AD2U2aL@Z7i}+x7+o4xl9<JYWKRGdMoCdjdJaKs#ek@j>15foQOVVTBW&}SHz=& zC@!@L60ULrbQgloYM=1>SU=MlDy)RcKh4cX%*d()E9Fw&h2b_6@)Hmo@>wd;7_AEe zB6{!;{4(6bZ3GXe1HF(BdzDPLf>soTwi?LIDgcl09%3#9Gezq{q%Ld4l!NKep+TGk z;g1E58mUi<s&8UcE5=#K(`q^F67dd+f0Psx<Lr>4r|8o*35iNwH~{C%!(B;moIPpM z&Y;{$lX2v#vD{1oVipPM1PBwC(rkcmJ_u){EE)+l_)cwGABNxwm~4raivkmRjua(c z?G0161uZXkdVVfi4dU)<lJNNOCkFjqy;fD6tA>><)f_olwNj43BV;Gw$gQ{tQ6vd3 znok!OrYreMyH$}ET)!rHmk6^g2x)tkC77eYIx@S&V}djZ;;0u|ZvC$)WnKEgzkc5g z;U@D3KxC!U2rv&x3F5N2-FLcXMxh>!X3HP`fpJE0-W>Lo8qUqh$!O4E(!)R+X~8{D z%H^*M`DH=4R*dAq1h6G>Jqo(R6f{vkysPhLH+|;s{crpB7l2>J`KD)%!+||t;<tdC zWrfK8jks3$h8y~KQ|&gO#iCYUJm1K8Bmbg~aawHP|HL7Arf;BjgXW_vEzi_;dhIh` z+`<9)%LwB*f~SD{r2=qw13w|qwqUfB$YI-3Az>%mnLNM_<%y*MMO@$r<?W>ftU>pc zi)i8%4tgHIJ$yt<i5mT-LWN^&P|mYMxxX}^!z&!2EG=FNK@k6GFue2Ue=^Y4Ysq&Q z4{uNh{`GeC4Q)D)g?{NdM8Q|ig!Ql&_?vZ|sZUSe=<NfgUm|(@HITf<Du>kYQw0NG zog4A`z{sA>k@Bcyia=4*UQ5x~F!O9=4tEyW5nftlVHt{8p@2eUAMpmU#MwGw5wkH* zzmI1#Nqj-AvJgEb%cI=%BtJ&YK2k~3FB<6^M!-3;B1J5rT7cc^ix`iQ$0G?~<N<oB zqEofK$RV?~WBx4aYlH*SvNnjfOtuCLcqd$Zm2qe^Is+pUbIh~AB1<f@!YXSqi+L<! z87rj6Kt%d1L}WgPN;+>s5wmsT_P-@^B~&rK09A}9K_#94z!IJjmdFuc=tWBqi$11u z7#RHWA{NfuGVb|4k#E5gz9TP-Ah`64AC~}$XA#hK!V_;H(n$zRds`Qt{sZ7uU-VA~ z;PhKRo$D`PKKCa{BF1fBTV)+syvP(^b;{p9RTOWUUft$ue13?^^p}5z_!YJ<{g5>Z GNo@dwG#q~b literal 4772 zcmV;V5?k$ePew8T0RR9101~7C4*&oF03(P101`?70RR9100000000000000000000 z0000SR0dW6g%$`737iZO2nv`Cls^k700A}vBm+zYAO(d@2Z1^afgBr&AXNuyj{~vB z)nxyFNYKUzMcaX2n?ji|pw{Y7@lv-)^UAq(HT$QmmPZpoQIJ4cyvmT)ilW<P;x4D} zvu{J|;zQwwC}yU697^QNfH8H`SW((go5qzhBP5xH=l}cj`=4uVoO1%-q{`<)PJu`% z1>nga?vbM}H`;Ed`_!&P*R`dX+&yp$DD^INt5~3xd_{dmhH>QBxt;p>w@9y@wm%nL zhzFP_CUDadq!MakKt+r}fQmxL-!q}JUXMxAdNK#^#Jv8du#zwnf#=g^hM)ve;443s zJ=y>rpiBw-9Fl_C^PcOJE^2kv(>mabS<6gvNKsX?7VI=Zs~Lw7XD<}D0Wx$(I6dd@ z|2J2keYcW2Qw^U$Vm_fTOOl;SS2{VVN~Zq+rDth4|8ug04a+DLsySaWbuR#^we1#- zS*2FUwQ_~=JB(e_L_Wr5yG1pi+fd2DDt!6qPl5mmhAhza8@KN#bG3H&I?Gs!l2kdl zwOqYauMrkTuvkD?@`$sVv+_k8=wtm2@WJ4Zm;W=Ig5nI#ceVS%hI9O(I{zs$dtdfB zYWDFBdJYJOFob>V^vW-h>c^2l3bPD}8J4G?q#(zj9p^hwHUddm#gCg$(aTU>Q$^AW zRhxrCA-DVru{IsvVbVKZ{vRG85ebr{NRuH;PF?}xfozI=gVF_M7aIb}cfLS*LtuDA zV0lB}cthZMLlE$WAj~%q$Tw~v-?)Q(LlLC%4OO!kYM_CTfCfSUK`#GsBgp4JMS?;_ zzGdAs^a&gYgDCQ9N#^Jk{^UzwY>}?3G?RSb61Dm;q&{j+b>CPEM_`@yTZBA$hk!WG z9168+9R|`Y%+#eLzDLm|C0}%^=SzF1qyAwoq;!8fy}H33&Rgy1@Z@Q3->`^eo0&dZ z-H<b(zAf@({74GB!$EAz+z}Yrgb6fxAa*^KUF=6S(g=STV~a0s7Z3YjI>4u3F%~CZ zLt)b7!@};vx2ozcrbgSRuA&qesvEknJ7eeAkRI|yf7nfClHaRON~rYFF<K`^Y=R~R zQjBG%L5-A8VQ+wK1!bSU!E9A})lR+4(Uf@5rK3iSO&yW2xy)1i1}VGx=x~sMV6AS1 zz{i0=J)wGIZA7xe%i1Osww9pb1tWP4P1I#?08YyxTxk;@-6h9wzvfVex(!?Gg)}zx zBnFK(3e}T9v{8v0jiWgN5P7#nbxh&Miqxw>YgMC-8PY^DLFtGfj2<H6)1{?`054Z_ z#CNBD`a}t|(Fpm#cdXG+veJmc6oqq3P*{hxB!k>qOC#wnmTO}d@gh}e4PY!A_Ba1{ z(yMmT)&Xi}DI>XQe32fa&NL3zsEr-#$f9UR;#|#;BL|rhA?q(!F6>*?M{+aRi`<YS z5A8^#0a;pV=GL0LC&{OUe3G=00$zOx#kTtKF6p+;9mUV+UU>Ds)M>0Vip!T!DELRe zu-pl$J^2v%*{bGCwv62EkQo(>8C_(T#+uLXNsvRi(rSx02@Q#~FgevYlJ$|WQ6W-A zQ}yWYj0G-eA8}at73Rv<)`TMkO&<aU^)eu%p0vVgFX`WsxuY~r7sz`rJe3+o>Qd?p zydM_tL`NBgrr@Y-OlS#HTFMM9W0saPM=O}8l`NoDETq*ey1o~x`orS$s}Dh?LhkmI zmpl3}ZDsTEqWx_h=F=XFafECTaTKZllq6J$hGxWQfuI$FHVE1w=zyRTf-VTUA?SgO z3_v!Fl?IcEC0TlU4|@6>mdt>?#zSB*jC-ea3`1ljTXI<I6+NiePt-<m8YvcsOQt7T z+mRS9i{t4*xE}=!M`cq#d1MUogz4+XaReSCh4<=6V8cTVQ~pQuPW>%iDPue><{p`V zY{-c90a(ZXLXJsPS2i3|-i8pLmY!xBMQO&Qk`c7-De;T^-i8iaYYF@2h}(qvc8YoW zV#hH9D1TQ(r?t`Xa`KX*illcB@_>qL<SB3ovprJL8po^m`H6+ohe4<wg@u)qVivYI zxe&cqA>oo+eNR${3GchAPDi@p8R-z#Q=>Sq8|B+|tHF7ySM?v&DwU@wbXcz#CE-KV zuoJ>_Cdq0ArBR(U5jm@;svgSNA_B~mS=^)O{lXcKjyW}~AtJb9KsQ$Y+vdATko+N( z0m>~{T0lFT6qRg$HrO93*-&e|(hY*sLxi`-BVJs$6g61W(%mhk)!a*q4M*{A)i$ph z=UL9v@75|*Dg`z&q~c+0CNaoHtjZ8ibu$-VbIpkvKla-7>h}GX*MXT3*zW7WY{;>e z_VqmTl(z)lD~76RLpNpH3=5@iuSu!rTN<aY<zrIm3hneplth<#c(Bd+Uoo;^DULjb zy>>%ajJ1_s8(E}H*E16oqBcKl-D@=`1*za4Tf(MYzN=;L_AkW5M0r`Z#@kSkTPKrb zXgio*X~*#>CzVQuxwTeQ*G?F7Cp*r>F0?RY*v&YCtSrE0X@`sr=1L$h3-DRV01F~= ziqHT>IY4Zr6F-bu%9v%0S<YA&;UTdSiPex;9Z5IH*knyPq*fNtW(6IV5RERP(L*%) zh{gcX7$O=YL}QG&Yl1>dd%<+36D7<Uw4O`cNyGSyUjRxkGY&b&ii6hu#3^E9a%@c= z?$R){2cS^7SUPafv7ZDjoJ@hUDR40bt}f7xgYF#a0ox>=ro_vXc$*R*7wF4DKaP=q zAbyiqweKIl*xHWH6x13Uqwf;7F>B-biTW^ZOaytEEX{0@=x~%a=tp3#kWzgYejW+x zA+r=47=S{597S@;WM`0C89SV{*(eHfIh0K_wPXaPT6<8G&gF*w8<7(TPduAM1v5B@ z!_~j4#g>AY_#kfPLkr8koP9_Wgt1J#9Vtq+8>rT(-Q6|r(VG1u-u@)XN6n#i7&t*t z1+WBO#FxvIyFg2Hw`->g5ovJOgE5%~j6}HtvzenpT+XEb$K&lmjCi!vh}7~-5H?GV z?#EsK6$tjDQTu+2s8wQa6FJ-&LXH~&`-%aw!_I?rbcVqI?qVx9Y@*sx<Nzf0t=obV z$ORrxJwcKrh??tgGfHA6gpeWz6N#D$V<+GM0W1+YWTguYQMo(f8aFzshG~c)#Q{6- zPe*6ij<}JE-oJVG!kruUU|T8#qJD=+(iF1+h8W<;WH@8zxF=9GO3ZDx8g&<FaMunA zZ3<Q)^AV++9j113Q>z(N2-~O6I7_$gae)A3G$cY?cumCLwx7WOZQ$F`xZC3@S67t^ z&?LTBelQ~QOcdV)VPvm)6#=}$YextXes&3nsxc)1qQW(8$R0OHs8qT@m|H!B?Q=uT z3@SFUZ8F>zA4kT*C^#YKX<c9*Um3!K`06koUl6I-jSmX^&lukAE?_`E)NJLcJmFT_ z4U;B<5FiHZJUW9L+aQ$WlMsl*#89}|q*&5Kt}FtOFyDn51W}DE16MU5TMnGr6SCs~ z3i{ibGfwYE|6^gIoU4Qc>{h!Q#5O`cRSDQm(|i>J7yL#hsqk=;1i`aR5=7$(Sl@SY z+%h=7SC}e)Pi*fi$~jY%Cn+23#g>1koQuakE>v7R;^cUsrh4ViKWAWmV=oFcU5~Ar z{qnQX=C=OrcUy+S6?0qHhwe^ZdpQzxWIG2<r2Na2WPEOaC`c5o1Gr^1y}d1?Z1GT; zUHVacXj`}L-2VCN_U)TDZ{2Q@kXLnQ<Tp=ymYw(SxFuXHLKj|*`1<m-rb|D6zP?ML zC@N4ma=$lo_;Walb-K#La^2&OE`8U=jb$a3*^XBu#-pzKmy~QA87)8E{qd~h*NCf* z&W`XpdS()3EJ_z8j2MX;ix`a%MxBj_6K@d*O^1vgT|2fGq6X|M-e(1xZ2kDOyKdci zxnmBT#U1g5d-GN>cjmtjA?Z!6sgc!G$*R=BY7)c_u~+NBtUNwwxQ==;gZ_y~j~=TN z`uO;6S!SuT++{ghyV2PrTIp&XcZDYBE(?LFsUI<v2{->A8ae`o2U55C2W|&_L*M7T zNS~DvXo!K+IzUcF<twnR@<@iC^D)|V7bAm}ess;o4SGMl*s{-WZ)3xu!b7~z=UhVr z`bB&G30aQF-02*rV8Eb8YR`naj}iL=MvoH1K3s#8%(E^MN>ju)ZM%;WV*v+_5#8Rf zos`VCE*AcnVylyR&^S&ZpgXl%Y6s^h<W?KyS989PkC#QxLTGcq@sq&hI3kQKyT4aB zICx~)vSWk&VAB}J{7+Uo2c6Y_`I0d^*zo*@t%ar*wD*g$yJ??nM^qHdW&8Q+JTGo( z`HrNHOhs(L+|1OM^@F`QmDN{WJ&@8XVD-z`ecMAXtExJspJGWUt>=>Uuno-Ks_H(b za67A4#_lzVkE^DdIncoAW0yE_t*EVKWn0@YDCz(QTSSGY8^gqvg_XRD4qEpzK2KR3 zRT{%zvRIVTk&@P$Eur)e&5M;pi#Bc)MN4Alqu3WwYlEnfxiqzhJ)|74zAR&zt5$@Z zWb{a(-|Nnnb!Bs|+d|i0t!Q=9+AFS%v)+C(DbXQhMONnzkfo@;Xt6(AsMr@<7g&-O z3ro{_mNG{?db*x}-nnB7$~6Y`b<`MJN<0-wi}P%`l0r)IYi^o2VWV#Ju_ec&dhXn9 zb)%Ecc|=#y-Zakt0GKc6FW}{UlXh?ZW+lwZ6+r)>f2ALFTK`<tQdZd_0{2Tx8Na0W z(PIU}n}HZLZ+cM?J)#z<)SM(_PazLjd`Y_GSM@%YE~gRMo))$s$eY0Zww9dnmEMmt zGQu-~ezM0j-uX9i*1ZWv4f+ZegEg0+uE}b$ho76h&bBEs-!rionfWKIroMtGj0j)J zCNm~VNNA$4?!@2&<LhY&M1n(W9LOnzoqSgu2Z7_z#Fyk*8pL9L6gvLT@41#~0KvK% zc`8+MLb><R??tv)5tX-$pvn;=pfwcTlgkRF(JDEc%1Flf{ioTxvc!nV3X(~uBCD)e z#;V<pVp01FJ`f|`)DckO64p6;2y5LboOQeMxv8&m*Vbbk0}#nC?R|^_)?+W>!@wD% z-qcYMJ){(WN$^t~mxhT+$8_XZc9+w#?^7ubmnwwa)WBj3P3&JXpCs^s_*WwFHt_a& zC`uq2)Um)$gVda0*|WA<ZZ4hFG8?%0)#CKsd0LGZRJS7-m93Nm;$SFyiWR6izpPG7 z2Omd}?HDz05rkfmfJ>26QI#WucO!brii+;)?}Hs1*>0L)d`)ZG+w#Eo1S8!Yu5IZo zp_*(a?Va_g*^U@_6FVf^jSBejdVA)n+xjhuUEWuEA7f5b5aksDO8SyvF_|xlbcsZl z<WBjri46k8aJ#=`PqyvX+U~>E4y;jQZ-dCBpW`srtxK>&?9}M4<<(A4FGOF-ef%TU zsI$~ZRU!Bly^poson^>4>z0;5=ZuX+#B%n9!oA2{gbI14z-+3t0&rGsz>Q;jf2{BS z*|!R9?sF+?8UX+LzkfCGhvhEWA+wL|Aqu{o__GK*n4wQ!1tFu0G&;?3k8kq^@`&k; z9{Frd51p}t)YPM|`4{?!jLy<G9m<P~)?-hs#5VIGa4VCF|JnTC@M+wgSVG9Sr3B1d zhLH8Je7ogHvfuKAoU#)1s-HVa(U&oZjeyJiAY}=;vX+vO)yl+pzp{|eTaI!&l!yGR zmEfS<c}<dz{`oHol;7R)&kgtDl-&gJy80G0=@cPfEcU?tk{GY#4cr7`*AGw2e1*e* z`p*6O$lbdal6kdnBzm6%=q9eI2<sEGjvL(M5x2QSor~Nh)@2>tVk3q+QS1!5E_TDm znJ;mj8dr(cxW`>CBGnzFsz_bn>Uu<-$Li`e`2lCS!8MfHi=sOB(5u$CO{hLQc#!k- zgG;E%$A%3U@_|jkbzeaZvH2X^(H-tHHG*s&TwpM`&CsjGHWmhBH___yN1crY0}4@h zO*CV0s7tgSrFl6Ld~?NAj978vC6G`eNk~#sl9r5QB`0|)NKs0-#07zKNN5k72!{2> z6&q8pt@}VJjI}R?asKUrqky(a7_@!q-VbgAcORVhp?3xFP4Y0;&Dk6E@b;ZM{Z0ki zCNt31(c`SZHi{+xutO34ir?}W@9w_}K~<nLxOn2Vhj8S4;k1VsqJC-Pm%U4>FNbHT y#k#IthLlTDKbq26*zJG#d_=lx`p%v%mCb_}GUZbKKk51`9pCiAwN8nB%=XPqG8yUs diff --git a/src/parser.nim b/src/parser.nim index aa0f8b2..f132dea 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -231,7 +231,8 @@ proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet = replies: js{"reply_count"}.getInt, retweets: js{"retweet_count"}.getInt, likes: js{"favorite_count"}.getInt, - quotes: js{"quote_count"}.getInt + quotes: js{"quote_count"}.getInt, + views: js{"views_count"}.getInt ) ) @@ -339,6 +340,9 @@ proc parseGraphTweet(js: JsonNode; isLegacy=false): Tweet = result.id = js{"rest_id"}.getId result.user = parseGraphUser(js{"core"}) + with count, js{"views", "count"}: + result.stats.views = count.getStr("0").parseInt + with noteTweet, js{"note_tweet", "note_tweet_results", "result"}: result.expandNoteTweetEntities(noteTweet) diff --git a/src/types.nim b/src/types.nim index 55d990d..5a08bb7 100644 --- a/src/types.nim +++ b/src/types.nim @@ -203,6 +203,7 @@ type retweets*: int likes*: int quotes*: int + views*: int Tweet* = ref object id*: int64 diff --git a/src/views/general.nim b/src/views/general.nim index 5ba40a3..0571aa7 100644 --- a/src/views/general.nim +++ b/src/views/general.nim @@ -53,7 +53,7 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc=""; buildHtml(head): link(rel="stylesheet", type="text/css", href="/css/style.css?v=19") - link(rel="stylesheet", type="text/css", href="/css/fontello.css?v=2") + link(rel="stylesheet", type="text/css", href="/css/fontello.css?v=3") if theme.len > 0: link(rel="stylesheet", type="text/css", href=(&"/css/themes/{theme}.css")) @@ -119,7 +119,7 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc=""; # this is last so images are also preloaded # if this is done earlier, Chrome only preloads one image for some reason link(rel="preload", type="font/woff2", `as`="font", - href="/fonts/fontello.woff2?21002321", crossorigin="anonymous") + href="/fonts/fontello.woff2?61663884", crossorigin="anonymous") proc renderMain*(body: VNode; req: Request; cfg: Config; prefs=defaultPrefs; titleText=""; desc=""; ogTitle=""; rss=""; video=""; diff --git a/src/views/tweet.nim b/src/views/tweet.nim index 34dcd4c..6d76755 100644 --- a/src/views/tweet.nim +++ b/src/views/tweet.nim @@ -184,6 +184,8 @@ proc renderStats(stats: TweetStats; views: string): VNode = span(class="tweet-stat"): icon "retweet", formatStat(stats.retweets) span(class="tweet-stat"): icon "quote", formatStat(stats.quotes) span(class="tweet-stat"): icon "heart", formatStat(stats.likes) + if stats.views > 0: + span(class="tweet-stat"): icon "views", formatStat(stats.views) if views.len > 0: span(class="tweet-stat"): icon "play", insertSep(views, ',') From 886f2d2a4540e238c3ff71b7c2341aba159c3f8c Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Mon, 17 Nov 2025 11:00:38 +0100 Subject: [PATCH 242/409] Bump API versions, use more SessionAwareUrls --- src/api.nim | 41 ++++++++++++++---- src/consts.nim | 64 ++++++++++++++++++---------- src/experimental/parser/graphql.nim | 32 +++++++++++--- src/experimental/types/graphuser.nim | 30 +++++++++++-- src/parser.nim | 36 +++++++++------- 5 files changed, 145 insertions(+), 58 deletions(-) diff --git a/src/api.nim b/src/api.nim index c0efa58..aeb0f17 100644 --- a/src/api.nim +++ b/src/api.nim @@ -7,12 +7,39 @@ import experimental/parser as newParser proc mediaUrl(id: string; cursor: string): SessionAwareUrl = let cookieVariables = userMediaVariables % [id, cursor] - oauthVariables = userTweetsVariables % [id, cursor] + oauthVariables = restIdVariables % [id, cursor] result = SessionAwareUrl( cookieUrl: graphUserMedia ? {"variables": cookieVariables, "features": gqlFeatures}, oauthUrl: graphUserMediaV2 ? {"variables": oauthVariables, "features": gqlFeatures} ) +proc userTweetsUrl(id: string; cursor: string): SessionAwareUrl = + let + cookieVariables = userTweetsVariables % [id, cursor] + oauthVariables = restIdVariables % [id, cursor] + result = SessionAwareUrl( + cookieUrl: graphUserTweets ? {"variables": cookieVariables, "features": gqlFeatures, "fieldToggles": fieldToggles}, + oauthUrl: graphUserTweetsV2 ? {"variables": oauthVariables, "features": gqlFeatures} + ) + +proc userTweetsAndRepliesUrl(id: string; cursor: string): SessionAwareUrl = + let + cookieVariables = userTweetsAndRepliesVariables % [id, cursor] + oauthVariables = restIdVariables % [id, cursor] + result = SessionAwareUrl( + cookieUrl: graphUserTweetsAndReplies ? {"variables": cookieVariables, "features": gqlFeatures, "fieldToggles": fieldToggles}, + oauthUrl: graphUserTweetsAndRepliesV2 ? {"variables": oauthVariables, "features": gqlFeatures} + ) + +proc tweetDetailUrl(id: string; cursor: string): SessionAwareUrl = + let + cookieVariables = tweetDetailVariables % [id, cursor] + oauthVariables = tweetVariables % [id, cursor] + result = SessionAwareUrl( + cookieUrl: graphTweetDetail ? {"variables": cookieVariables, "features": gqlFeatures, "fieldToggles": tweetDetailFieldToggles}, + oauthUrl: graphTweet ? {"variables": oauthVariables, "features": gqlFeatures} + ) + proc getGraphUser*(username: string): Future[User] {.async.} = if username.len == 0: return let @@ -33,13 +60,11 @@ proc getGraphUserTweets*(id: string; kind: TimelineKind; after=""): Future[Profi if id.len == 0: return let cursor = if after.len > 0: "\"cursor\":\"$1\"," % after else: "" - variables = userTweetsVariables % [id, cursor] - params = {"variables": variables, "features": gqlFeatures} js = case kind of TimelineKind.tweets: - await fetch(graphUserTweets ? params, Api.userTweets) + await fetch(userTweetsUrl(id, cursor), Api.userTweets) of TimelineKind.replies: - await fetch(graphUserTweetsAndReplies ? params, Api.userTweetsAndReplies) + await fetch(userTweetsAndRepliesUrl(id, cursor), Api.userTweetsAndReplies) of TimelineKind.media: await fetch(mediaUrl(id, cursor), Api.userMedia) result = parseGraphTimeline(js, after) @@ -48,7 +73,7 @@ proc getGraphListTweets*(id: string; after=""): Future[Timeline] {.async.} = if id.len == 0: return let cursor = if after.len > 0: "\"cursor\":\"$1\"," % after else: "" - variables = listTweetsVariables % [id, cursor] + variables = restIdVariables % [id, cursor] params = {"variables": variables, "features": gqlFeatures} js = await fetch(graphListTweets ? params, Api.listTweets) result = parseGraphTimeline(js, after).tweets @@ -94,9 +119,7 @@ proc getGraphTweet(id: string; after=""): Future[Conversation] {.async.} = if id.len == 0: return let cursor = if after.len > 0: "\"cursor\":\"$1\"," % after else: "" - variables = tweetVariables % [id, cursor] - params = {"variables": variables, "features": gqlFeatures} - js = await fetch(graphTweet ? params, Api.tweetDetail) + js = await fetch(tweetDetailUrl(id, cursor), Api.tweetDetail) result = parseGraphConversation(js, id) proc getReplies*(id, after: string): Future[Result[Chain]] {.async.} = diff --git a/src/consts.nim b/src/consts.nim index c8ae8d2..2623484 100644 --- a/src/consts.nim +++ b/src/consts.nim @@ -9,16 +9,19 @@ const graphUser* = gql / "u7wQyGi6oExe8_TRWGMq4Q/UserResultByScreenNameQuery" graphUserById* = gql / "oPppcargziU1uDQHAUmH-A/UserResultByIdQuery" - graphUserTweets* = gql / "JLApJKFY0MxGTzCoK6ps8Q/UserWithProfileTweetsQueryV2" - graphUserTweetsAndReplies* = gql / "Y86LQY7KMvxn5tu3hFTyPg/UserWithProfileTweetsAndRepliesQueryV2" + graphUserTweetsV2* = gql / "JLApJKFY0MxGTzCoK6ps8Q/UserWithProfileTweetsQueryV2" + graphUserTweetsAndRepliesV2* = gql / "Y86LQY7KMvxn5tu3hFTyPg/UserWithProfileTweetsAndRepliesQueryV2" + graphUserTweets* = gql / "oRJs8SLCRNRbQzuZG93_oA/UserTweets" + graphUserTweetsAndReplies* = gql / "kkaJ0Mf34PZVarrxzLihjg/UserTweetsAndReplies" graphUserMedia* = gql / "36oKqyQ7E_9CmtONGjJRsA/UserMedia" graphUserMediaV2* = gql / "PDfFf8hGeJvUCiTyWtw4wQ/MediaTimelineV2" graphTweet* = gql / "Vorskcd2tZ-tc4Gx3zbk4Q/ConversationTimelineV2" + graphTweetDetail* = gql / "YVyS4SfwYW7Uw5qwy0mQCA/TweetDetail" graphTweetResult* = gql / "sITyJdhRPpvpEjg4waUmTA/TweetResultByIdQuery" - graphSearchTimeline* = gql / "KI9jCXUx3Ymt-hDKLOZb9Q/SearchTimeline" - graphListById* = gql / "oygmAig8kjn0pKsx_bUadQ/ListByRestId" - graphListBySlug* = gql / "88GTz-IPPWLn1EiU8XoNVg/ListBySlug" - graphListMembers* = gql / "kSmxeqEeelqdHSR7jMnb_w/ListMembers" + graphSearchTimeline* = gql / "7r8ibjHuK3MWUyzkzHNMYQ/SearchTimeline" + graphListById* = gql / "cIUpT1UjuGgl_oWiY7Snhg/ListByRestId" + graphListBySlug* = gql / "K6wihoTiTrzNzSF8y1aeKQ/ListBySlug" + graphListMembers* = gql / "fuVHh5-gFn8zDBBxb8wOMA/ListMembers" graphListTweets* = gql / "BbGLL1ZfMibdFNWlk7a0Pw/ListTimeline" gqlFeatures* = """{ @@ -96,24 +99,20 @@ const "withV2Timeline": true }""".replace(" ", "").replace("\n", "") -# oldUserTweetsVariables* = """{ -# "userId": "$1", $2 -# "count": 20, -# "includePromotedContent": false, -# "withDownvotePerspective": false, -# "withReactionsMetadata": false, -# "withReactionsPerspective": false, -# "withVoice": false, -# "withV2Timeline": true -# } -# """ + tweetDetailVariables* = """{ + "focalTweetId": "$1", + $2 + "referrer": "profile", + "with_rux_injections": false, + "rankingMode": "Relevance", + "includePromotedContent": true, + "withCommunity": true, + "withQuickPromoteEligibilityTweetFields": true, + "withBirdwatchNotes": true, + "withVoice": true +}""".replace(" ", "").replace("\n", "") - userTweetsVariables* = """{ - "rest_id": "$1", $2 - "count": 20 -}""" - - listTweetsVariables* = """{ + restIdVariables* = """{ "rest_id": "$1", $2 "count": 20 }""" @@ -126,3 +125,22 @@ const "withBirdwatchNotes": false, "withVoice": true }""".replace(" ", "").replace("\n", "") + + userTweetsVariables* = """{ + "userId": "$1", $2 + "count": 20, + "includePromotedContent": false, + "withQuickPromoteEligibilityTweetFields": true, + "withVoice": true +}""".replace(" ", "").replace("\n", "") + + userTweetsAndRepliesVariables* = """{ + "userId": "$1", $2 + "count": 20, + "includePromotedContent": false, + "withCommunity": true, + "withVoice": true +}""".replace(" ", "").replace("\n", "") + + fieldToggles* = """{"withArticlePlainText":false}""" + tweetDetailFieldToggles* = """{"withArticleRichContentState":true,"withArticlePlainText":false,"withGrokAnalyze":false,"withDisallowedReplyControls":false}""" diff --git a/src/experimental/parser/graphql.nim b/src/experimental/parser/graphql.nim index 69837ab..045a5d6 100644 --- a/src/experimental/parser/graphql.nim +++ b/src/experimental/parser/graphql.nim @@ -1,21 +1,39 @@ -import options +import options, strutils import jsony import user, ../types/[graphuser, graphlistmembers] from ../../types import User, VerifiedType, Result, Query, QueryKind +proc parseUserResult*(userResult: UserResult): User = + result = userResult.legacy + + if result.verifiedType == none and userResult.isBlueVerified: + result.verifiedType = blue + + if result.username.len == 0 and userResult.core.screenName.len > 0: + result.id = userResult.restId + result.username = userResult.core.screenName + result.fullname = userResult.core.name + result.userPic = userResult.avatar.imageUrl.replace("_normal", "") + + if userResult.verification.isSome: + let v = userResult.verification.get + if v.verifiedType != VerifiedType.none: + result.verifiedType = v.verifiedType + + if userResult.profileBio.isSome: + result.bio = userResult.profileBio.get.description + proc parseGraphUser*(json: string): User = if json.len == 0 or json[0] != '{': return let raw = json.fromJson(GraphUser) + let userResult = raw.data.userResult.result - if raw.data.userResult.result.unavailableReason.get("") == "Suspended": + if userResult.unavailableReason.get("") == "Suspended": return User(suspended: true) - result = raw.data.userResult.result.legacy - result.id = raw.data.userResult.result.restId - if result.verifiedType == VerifiedType.none and raw.data.userResult.result.isBlueVerified: - result.verifiedType = blue + result = parseUserResult(userResult) proc parseGraphListMembers*(json, cursor: string): Result[User] = result = Result[User]( @@ -31,7 +49,7 @@ proc parseGraphListMembers*(json, cursor: string): Result[User] = of TimelineTimelineItem: let userResult = entry.content.itemContent.userResults.result if userResult.restId.len > 0: - result.content.add userResult.legacy + result.content.add parseUserResult(userResult) of TimelineTimelineCursor: if entry.content.cursorType == "Bottom": result.bottom = entry.content.value diff --git a/src/experimental/types/graphuser.nim b/src/experimental/types/graphuser.nim index 08100f9..d732b4e 100644 --- a/src/experimental/types/graphuser.nim +++ b/src/experimental/types/graphuser.nim @@ -1,5 +1,5 @@ -import options -from ../../types import User +import options, strutils +from ../../types import User, VerifiedType type GraphUser* = object @@ -8,8 +8,32 @@ type UserData* = object result*: UserResult - UserResult = object + UserCore* = object + name*: string + screenName*: string + createdAt*: string + + UserBio* = object + description*: string + + UserAvatar* = object + imageUrl*: string + + Verification* = object + verifiedType*: VerifiedType + + UserResult* = object legacy*: User restId*: string isBlueVerified*: bool unavailableReason*: Option[string] + core*: UserCore + avatar*: UserAvatar + profileBio*: Option[UserBio] + verification*: Option[Verification] + +proc enumHook*(s: string; v: var VerifiedType) = + v = try: + parseEnum[VerifiedType](s) + except: + VerifiedType.none diff --git a/src/parser.nim b/src/parser.nim index f132dea..c4ccab0 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -42,16 +42,16 @@ proc parseGraphUser(js: JsonNode): User = result = parseUser(user{"legacy"}, user{"rest_id"}.getStr) # fallback to support UserMedia/recent GraphQL updates - if result.username.len == 0 and user{"core", "screen_name"}.notNull: + if result.username.len == 0: result.username = user{"core", "screen_name"}.getStr result.fullname = user{"core", "name"}.getStr result.userPic = user{"avatar", "image_url"}.getImageStr.replace("_normal", "") if user{"is_blue_verified"}.getBool(false): result.verifiedType = blue - elif user{"verification", "verified_type"}.notNull: - let verifiedType = user{"verification", "verified_type"}.getStr("None") - result.verifiedType = parseEnum[VerifiedType](verifiedType) + + with verifiedType, user{"verification", "verified_type"}: + result.verifiedType = parseEnum[VerifiedType](verifiedType.getStr) proc parseGraphList*(js: JsonNode): List = if js.isNull: return @@ -372,10 +372,10 @@ proc parseGraphTweetResult*(js: JsonNode): Tweet = with tweet, js{"data", "tweet_result", "result"}: result = parseGraphTweet(tweet, false) -proc parseGraphConversation*(js: JsonNode; tweetId: string; v2=true): Conversation = +proc parseGraphConversation*(js: JsonNode; tweetId: string): Conversation = result = Conversation(replies: Result[Chain](beginning: true)) - let + v2 = js{"data", "timeline_response"}.notNull rootKey = if v2: "timeline_response" else: "threaded_conversation_with_injections_v2" contentKey = if v2: "content" else: "itemContent" resultKey = if v2: "tweetResult" else: "tweet_results" @@ -385,7 +385,8 @@ proc parseGraphConversation*(js: JsonNode; tweetId: string; v2=true): Conversati return for i in instructions: - if i{"__typename"}.getStr == "TimelineAddEntries": + let instrType = i{"__typename"}.getStr(i{"type"}.getStr) + if instrType == "TimelineAddEntries": for e in i{"entries"}: let entryId = e{"entryId"}.getStr if entryId.startsWith("tweet"): @@ -421,20 +422,23 @@ proc parseGraphConversation*(js: JsonNode; tweetId: string; v2=true): Conversati result.replies.bottom = e{"content", contentKey, "value"}.getStr proc extractTweetsFromEntry*(e: JsonNode; entryId: string): seq[Tweet] = - if e{"content", "items"}.notNull: - for item in e{"content", "items"}: - with tweetResult, item{"item", "itemContent", "tweet_results", "result"}: - var tweet = parseGraphTweet(tweetResult, false) - if not tweet.available: - tweet.id = parseBiggestInt(item{"entryId"}.getStr.getId()) - result.add tweet - return + var tweetResult = e{"content", "itemContent", "tweet_results", "result"} + if tweetResult.isNull: + tweetResult = e{"content", "content", "tweetResult", "result"} - with tweetResult, e{"content", "content", "tweetResult", "result"}: + if tweetResult.notNull: var tweet = parseGraphTweet(tweetResult, false) if not tweet.available: tweet.id = parseBiggestInt(entryId.getId()) result.add tweet + return + + for item in e{"content", "items"}: + with tweetResult, item{"item", "itemContent", "tweet_results", "result"}: + var tweet = parseGraphTweet(tweetResult, false) + if not tweet.available: + tweet.id = parseBiggestInt(item{"entryId"}.getStr.getId()) + result.add tweet proc parseGraphTimeline*(js: JsonNode; after=""): Profile = result = Profile(tweets: Timeline(beginning: after.len == 0)) From 6b655cddd803fb1efc424b70d8f7be760b6c7c60 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Mon, 17 Nov 2025 11:01:20 +0100 Subject: [PATCH 243/409] Cleanup --- src/auth.nim | 17 +---------------- src/experimental/parser/user.nim | 18 ------------------ src/experimental/types/timeline.nim | 23 ----------------------- 3 files changed, 1 insertion(+), 57 deletions(-) delete mode 100644 src/experimental/types/timeline.nim diff --git a/src/auth.nim b/src/auth.nim index 6c52918..734b43e 100644 --- a/src/auth.nim +++ b/src/auth.nim @@ -7,20 +7,6 @@ import experimental/parser/session const maxConcurrentReqs = 2 hourInSeconds = 60 * 60 - apiMaxReqs: Table[Api, int] = { - Api.search: 50, - Api.tweetDetail: 500, - Api.userTweets: 500, - Api.userTweetsAndReplies: 500, - Api.userMedia: 500, - Api.userRestId: 500, - Api.userScreenName: 500, - Api.tweetResult: 500, - Api.list: 500, - Api.listTweets: 500, - Api.listMembers: 500, - Api.listBySlug: 500 - }.toTable var sessionPool: seq[Session] @@ -71,8 +57,7 @@ proc getSessionPoolHealth*(): JsonNode = for api in session.apis.keys: let apiStatus = session.apis[api] - limit = if apiStatus.limit > 0: apiStatus.limit else: apiMaxReqs.getOrDefault(api, 0) - reqs = limit - apiStatus.remaining + reqs = apiStatus.limit - apiStatus.remaining # no requests made with this session and endpoint since the limit reset if apiStatus.reset < now: diff --git a/src/experimental/parser/user.nim b/src/experimental/parser/user.nim index 07e0477..498757a 100644 --- a/src/experimental/parser/user.nim +++ b/src/experimental/parser/user.nim @@ -72,21 +72,3 @@ proc parseHook*(s: string; i: var int; v: var User) = var u: RawUser parseHook(s, i, u) v = toUser u - -proc parseUser*(json: string; username=""): User = - handleErrors: - case error.code - of suspended: return User(username: username, suspended: true) - of userNotFound: return - else: echo "[error - parseUser]: ", error - - result = json.fromJson(User) - -proc parseUsers*(json: string; after=""): Result[User] = - result = Result[User](beginning: after.len == 0) - - # starting with '{' means it's an error - if json[0] == '[': - let raw = json.fromJson(seq[RawUser]) - for user in raw: - result.content.add user.toUser diff --git a/src/experimental/types/timeline.nim b/src/experimental/types/timeline.nim deleted file mode 100644 index 5ce6d9f..0000000 --- a/src/experimental/types/timeline.nim +++ /dev/null @@ -1,23 +0,0 @@ -import std/tables -from ../../types import User - -type - Search* = object - globalObjects*: GlobalObjects - timeline*: Timeline - - GlobalObjects = object - users*: Table[string, User] - - Timeline = object - instructions*: seq[Instructions] - - Instructions = object - addEntries*: tuple[entries: seq[Entry]] - - Entry = object - entryId*: string - content*: tuple[operation: Operation] - - Operation = object - cursor*: tuple[value, cursorType: string] From e8de18317ec072e47a56b42dff44f7136b20117e Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Mon, 17 Nov 2025 11:21:21 +0100 Subject: [PATCH 244/409] Fix broken pinned tweet parsing --- src/parser.nim | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/parser.nim b/src/parser.nim index c4ccab0..614ab57 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -385,7 +385,7 @@ proc parseGraphConversation*(js: JsonNode; tweetId: string): Conversation = return for i in instructions: - let instrType = i{"__typename"}.getStr(i{"type"}.getStr) + let instrType = i{"type"}.getStr(i{"__typename"}.getStr) if instrType == "TimelineAddEntries": for e in i{"entries"}: let entryId = e{"entryId"}.getStr @@ -421,7 +421,7 @@ proc parseGraphConversation*(js: JsonNode; tweetId: string): Conversation = elif entryId.startsWith("cursor-bottom"): result.replies.bottom = e{"content", contentKey, "value"}.getStr -proc extractTweetsFromEntry*(e: JsonNode; entryId: string): seq[Tweet] = +proc extractTweetsFromEntry*(e: JsonNode): seq[Tweet] = var tweetResult = e{"content", "itemContent", "tweet_results", "result"} if tweetResult.isNull: tweetResult = e{"content", "content", "tweetResult", "result"} @@ -429,7 +429,7 @@ proc extractTweetsFromEntry*(e: JsonNode; entryId: string): seq[Tweet] = if tweetResult.notNull: var tweet = parseGraphTweet(tweetResult, false) if not tweet.available: - tweet.id = parseBiggestInt(entryId.getId()) + tweet.id = parseBiggestInt(e.getEntryId()) result.add tweet return @@ -469,7 +469,7 @@ proc parseGraphTimeline*(js: JsonNode; after=""): Profile = for e in i{"entries"}: let entryId = e{"entryId"}.getStr if entryId.startsWith("tweet") or entryId.startsWith("profile-grid"): - for tweet in extractTweetsFromEntry(e, entryId): + for tweet in extractTweetsFromEntry(e): result.tweets.content.add tweet elif "-conversation-" in entryId or entryId.startsWith("homeConversation"): let (thread, self) = parseGraphThread(e) @@ -477,15 +477,14 @@ proc parseGraphTimeline*(js: JsonNode; after=""): Profile = elif entryId.startsWith("cursor-bottom"): result.tweets.bottom = e{"content", "value"}.getStr - if after.len == 0 and i{"__typename"}.getStr == "TimelinePinEntry": - with tweetResult, i{"entry", "content", "content", "tweetResult", "result"}: - let tweet = parseGraphTweet(tweetResult, false) - tweet.pinned = true - if not tweet.available and tweet.tombstone.len == 0: - let entryId = i{"entry", "entryId"}.getEntryId - if entryId.len > 0: - tweet.id = parseBiggestInt(entryId) - result.pinned = some tweet + if after.len == 0: + let instrType = i{"type"}.getStr(i{"__typename"}.getStr) + if instrType == "TimelinePinEntry": + let tweets = extractTweetsFromEntry(i{"entry"}) + if tweets.len > 0: + var tweet = tweets[0] + tweet.pinned = true + result.pinned = some tweet proc parseGraphPhotoRail*(js: JsonNode): PhotoRail = result = @[] @@ -523,7 +522,7 @@ proc parseGraphPhotoRail*(js: JsonNode): PhotoRail = for e in i{"entries"}: let entryId = e{"entryId"}.getStr if entryId.startsWith("tweet") or entryId.startsWith("profile-grid"): - for t in extractTweetsFromEntry(e, entryId): + for t in extractTweetsFromEntry(e): let photo = extractGalleryPhoto(t) if photo.url.len > 0: result.add photo From 824a7e346a730a3eeb87a945e8268b3e40867f0d Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Mon, 17 Nov 2025 12:08:44 +0100 Subject: [PATCH 245/409] Fix rss icon tag --- src/views/general.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/general.nim b/src/views/general.nim index 0571aa7..0091c74 100644 --- a/src/views/general.nim +++ b/src/views/general.nim @@ -30,7 +30,7 @@ proc renderNavbar(cfg: Config; req: Request; rss, canonical: string): VNode = tdiv(class="nav-item right"): icon "search", title="Search", href="/search" if cfg.enableRss and rss.len > 0: - icon "rss-feed", title="RSS Feed", href=rss + icon "rss", title="RSS Feed", href=rss icon "bird", title="Open in Twitter", href=canonical a(href="https://liberapay.com/zedeus"): verbatim lp icon "info", title="About", href="/about" From 78d788b27f977ec8f7d6c91d9742c1b8343d08bf Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Wed, 19 Nov 2025 07:33:32 +0100 Subject: [PATCH 246/409] Fix verified parsing for oauth endpoints --- src/parser.nim | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/parser.nim b/src/parser.nim index 614ab57..8712a5f 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -21,11 +21,16 @@ proc parseUser(js: JsonNode; id=""): User = tweets: js{"statuses_count"}.getInt, likes: js{"favourites_count"}.getInt, media: js{"media_count"}.getInt, - verifiedType: parseEnum[VerifiedType](js{"verified_type"}.getStr("None")), protected: js{"protected"}.getBool, joinDate: js{"created_at"}.getTime ) + if js{"is_blue_verified"}.getBool(false): + result.verifiedType = blue + + with verifiedType, js{"verified_type"}: + result.verifiedType = parseEnum[VerifiedType](verifiedType.getStr) + result.expandUserEntities(js) proc parseGraphUser(js: JsonNode): User = From b0d9c1d51a4039cecbb58ab0180883baf5325d59 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Sat, 22 Nov 2025 21:21:10 +0100 Subject: [PATCH 247/409] Update endpoints, fix parser, remove quotes stat --- src/api.nim | 58 +++++---- src/consts.nim | 35 +++-- src/parser.nim | 308 ++++++++++++++++++++++++++------------------ src/parserutils.nim | 53 ++++---- src/types.nim | 2 - src/views/tweet.nim | 13 +- 6 files changed, 269 insertions(+), 200 deletions(-) diff --git a/src/api.nim b/src/api.nim index aeb0f17..ef3a0f9 100644 --- a/src/api.nim +++ b/src/api.nim @@ -1,16 +1,23 @@ # SPDX-License-Identifier: AGPL-3.0-only -import asyncdispatch, httpclient, uri, strutils, sequtils, sugar +import asyncdispatch, httpclient, uri, strutils, sequtils, sugar, tables import packedjson import types, query, formatters, consts, apiutils, parser import experimental/parser as newParser +# Helper to generate params object for GraphQL requests +proc genParams(variables: string; fieldToggles = ""): seq[(string, string)] = + result.add ("variables", variables) + result.add ("features", gqlFeatures) + if fieldToggles.len > 0: + result.add ("fieldToggles", fieldToggles) + proc mediaUrl(id: string; cursor: string): SessionAwareUrl = let cookieVariables = userMediaVariables % [id, cursor] oauthVariables = restIdVariables % [id, cursor] result = SessionAwareUrl( - cookieUrl: graphUserMedia ? {"variables": cookieVariables, "features": gqlFeatures}, - oauthUrl: graphUserMediaV2 ? {"variables": oauthVariables, "features": gqlFeatures} + cookieUrl: graphUserMedia ? genParams(cookieVariables), + oauthUrl: graphUserMediaV2 ? genParams(oauthVariables) ) proc userTweetsUrl(id: string; cursor: string): SessionAwareUrl = @@ -18,17 +25,19 @@ proc userTweetsUrl(id: string; cursor: string): SessionAwareUrl = cookieVariables = userTweetsVariables % [id, cursor] oauthVariables = restIdVariables % [id, cursor] result = SessionAwareUrl( - cookieUrl: graphUserTweets ? {"variables": cookieVariables, "features": gqlFeatures, "fieldToggles": fieldToggles}, - oauthUrl: graphUserTweetsV2 ? {"variables": oauthVariables, "features": gqlFeatures} + # cookieUrl: graphUserTweets ? genParams(cookieVariables, fieldToggles), + oauthUrl: graphUserTweetsV2 ? genParams(oauthVariables) ) + # might change this in the future pending testing + result.cookieUrl = result.oauthUrl proc userTweetsAndRepliesUrl(id: string; cursor: string): SessionAwareUrl = let cookieVariables = userTweetsAndRepliesVariables % [id, cursor] oauthVariables = restIdVariables % [id, cursor] result = SessionAwareUrl( - cookieUrl: graphUserTweetsAndReplies ? {"variables": cookieVariables, "features": gqlFeatures, "fieldToggles": fieldToggles}, - oauthUrl: graphUserTweetsAndRepliesV2 ? {"variables": oauthVariables, "features": gqlFeatures} + cookieUrl: graphUserTweetsAndReplies ? genParams(cookieVariables, fieldToggles), + oauthUrl: graphUserTweetsAndRepliesV2 ? genParams(oauthVariables) ) proc tweetDetailUrl(id: string; cursor: string): SessionAwareUrl = @@ -36,24 +45,22 @@ proc tweetDetailUrl(id: string; cursor: string): SessionAwareUrl = cookieVariables = tweetDetailVariables % [id, cursor] oauthVariables = tweetVariables % [id, cursor] result = SessionAwareUrl( - cookieUrl: graphTweetDetail ? {"variables": cookieVariables, "features": gqlFeatures, "fieldToggles": tweetDetailFieldToggles}, - oauthUrl: graphTweet ? {"variables": oauthVariables, "features": gqlFeatures} + cookieUrl: graphTweetDetail ? genParams(cookieVariables, tweetDetailFieldToggles), + oauthUrl: graphTweet ? genParams(oauthVariables) ) proc getGraphUser*(username: string): Future[User] {.async.} = if username.len == 0: return let - variables = """{"screen_name": "$1"}""" % username - params = {"variables": variables, "features": gqlFeatures} - js = await fetchRaw(graphUser ? params, Api.userScreenName) + url = graphUser ? genParams("""{"screen_name": "$1"}""" % username) + js = await fetchRaw(url, Api.userScreenName) result = parseGraphUser(js) proc getGraphUserById*(id: string): Future[User] {.async.} = if id.len == 0 or id.any(c => not c.isDigit): return let - variables = """{"rest_id": "$1"}""" % id - params = {"variables": variables, "features": gqlFeatures} - js = await fetchRaw(graphUserById ? params, Api.userRestId) + url = graphUserById ? genParams("""{"rest_id": "$1"}""" % id) + js = await fetchRaw(url, Api.userRestId) result = parseGraphUser(js) proc getGraphUserTweets*(id: string; kind: TimelineKind; after=""): Future[Profile] {.async.} = @@ -73,23 +80,18 @@ proc getGraphListTweets*(id: string; after=""): Future[Timeline] {.async.} = if id.len == 0: return let cursor = if after.len > 0: "\"cursor\":\"$1\"," % after else: "" - variables = restIdVariables % [id, cursor] - params = {"variables": variables, "features": gqlFeatures} - js = await fetch(graphListTweets ? params, Api.listTweets) - result = parseGraphTimeline(js, after).tweets + url = graphListTweets ? genParams(restIdVariables % [id, cursor]) + result = parseGraphTimeline(await fetch(url, Api.listTweets), after).tweets proc getGraphListBySlug*(name, list: string): Future[List] {.async.} = let variables = %*{"screenName": name, "listSlug": list} - params = {"variables": $variables, "features": gqlFeatures} - url = graphListBySlug ? params + url = graphListBySlug ? genParams($variables) result = parseGraphList(await fetch(url, Api.listBySlug)) proc getGraphList*(id: string): Future[List] {.async.} = let - variables = """{"listId": "$1"}""" % id - params = {"variables": variables, "features": gqlFeatures} - url = graphListById ? params + url = graphListById ? genParams("""{"listId": "$1"}""" % id) result = parseGraphList(await fetch(url, Api.list)) proc getGraphListMembers*(list: List; after=""): Future[Result[User]] {.async.} = @@ -104,7 +106,7 @@ proc getGraphListMembers*(list: List; after=""): Future[Result[User]] {.async.} } if after.len > 0: variables["cursor"] = % after - let url = graphListMembers ? {"variables": $variables, "features": gqlFeatures} + let url = graphListMembers ? genParams($variables) result = parseGraphListMembers(await fetchRaw(url, Api.listMembers), after) proc getGraphTweetResult*(id: string): Future[Tweet] {.async.} = @@ -139,6 +141,7 @@ proc getGraphTweetSearch*(query: Query; after=""): Future[Timeline] {.async.} = var variables = %*{ "rawQuery": q, + "query_source": "typedQuery", "count": 20, "product": "Latest", "withDownvotePerspective": false, @@ -147,7 +150,7 @@ proc getGraphTweetSearch*(query: Query; after=""): Future[Timeline] {.async.} = } if after.len > 0: variables["cursor"] = % after - let url = graphSearchTimeline ? {"variables": $variables, "features": gqlFeatures} + let url = graphSearchTimeline ? genParams($variables) result = parseGraphSearch[Tweets](await fetch(url, Api.search), after) result.query = query @@ -158,6 +161,7 @@ proc getGraphUserSearch*(query: Query; after=""): Future[Result[User]] {.async.} var variables = %*{ "rawQuery": query.text, + "query_source": "typedQuery", "count": 20, "product": "People", "withDownvotePerspective": false, @@ -168,7 +172,7 @@ proc getGraphUserSearch*(query: Query; after=""): Future[Result[User]] {.async.} variables["cursor"] = % after result.beginning = false - let url = graphSearchTimeline ? {"variables": $variables, "features": gqlFeatures} + let url = graphSearchTimeline ? genParams($variables) result = parseGraphSearch[User](await fetch(url, Api.search), after) result.query = query diff --git a/src/consts.nim b/src/consts.nim index 2623484..792a519 100644 --- a/src/consts.nim +++ b/src/consts.nim @@ -7,26 +7,29 @@ const gql = parseUri("https://api.x.com") / "graphql" - graphUser* = gql / "u7wQyGi6oExe8_TRWGMq4Q/UserResultByScreenNameQuery" - graphUserById* = gql / "oPppcargziU1uDQHAUmH-A/UserResultByIdQuery" - graphUserTweetsV2* = gql / "JLApJKFY0MxGTzCoK6ps8Q/UserWithProfileTweetsQueryV2" - graphUserTweetsAndRepliesV2* = gql / "Y86LQY7KMvxn5tu3hFTyPg/UserWithProfileTweetsAndRepliesQueryV2" + graphUser* = gql / "WEoGnYB0EG1yGwamDCF6zg/UserResultByScreenNameQuery" + graphUserById* = gql / "VN33vKXrPT7p35DgNR27aw/UserResultByIdQuery" + graphUserTweetsV2* = gql / "6QdSuZ5feXxOadEdXa4XZg/UserWithProfileTweetsQueryV2" + graphUserTweetsAndRepliesV2* = gql / "BDX77Xzqypdt11-mDfgdpQ/UserWithProfileTweetsAndRepliesQueryV2" graphUserTweets* = gql / "oRJs8SLCRNRbQzuZG93_oA/UserTweets" graphUserTweetsAndReplies* = gql / "kkaJ0Mf34PZVarrxzLihjg/UserTweetsAndReplies" graphUserMedia* = gql / "36oKqyQ7E_9CmtONGjJRsA/UserMedia" - graphUserMediaV2* = gql / "PDfFf8hGeJvUCiTyWtw4wQ/MediaTimelineV2" - graphTweet* = gql / "Vorskcd2tZ-tc4Gx3zbk4Q/ConversationTimelineV2" + graphUserMediaV2* = gql / "bp0e_WdXqgNBIwlLukzyYA/MediaTimelineV2" + graphTweet* = gql / "Y4Erk_-0hObvLpz0Iw3bzA/ConversationTimeline" graphTweetDetail* = gql / "YVyS4SfwYW7Uw5qwy0mQCA/TweetDetail" - graphTweetResult* = gql / "sITyJdhRPpvpEjg4waUmTA/TweetResultByIdQuery" - graphSearchTimeline* = gql / "7r8ibjHuK3MWUyzkzHNMYQ/SearchTimeline" + graphTweetResult* = gql / "nzme9KiYhfIOrrLrPP_XeQ/TweetResultByIdQuery" + graphSearchTimeline* = gql / "bshMIjqDk8LTXTq4w91WKw/SearchTimeline" graphListById* = gql / "cIUpT1UjuGgl_oWiY7Snhg/ListByRestId" graphListBySlug* = gql / "K6wihoTiTrzNzSF8y1aeKQ/ListBySlug" graphListMembers* = gql / "fuVHh5-gFn8zDBBxb8wOMA/ListMembers" - graphListTweets* = gql / "BbGLL1ZfMibdFNWlk7a0Pw/ListTimeline" + graphListTweets* = gql / "VQf8_XQynI3WzH6xopOMMQ/ListTimeline" gqlFeatures* = """{ + "android_ad_formats_media_component_render_overlay_enabled": false, "android_graphql_skip_api_media_color_palette": false, + "android_professional_link_spotlight_display_enabled": false, "blue_business_profile_image_shape_enabled": false, + "commerce_android_shop_module_enabled": false, "creator_subscriptions_subscription_count_enabled": false, "creator_subscriptions_tweet_preview_api_enabled": true, "freedom_of_speech_not_reach_fetch_enabled": true, @@ -36,8 +39,9 @@ const "interactive_text_enabled": false, "longform_notetweets_consumption_enabled": true, "longform_notetweets_inline_media_enabled": true, - "longform_notetweets_richtext_consumption_enabled": true, "longform_notetweets_rich_text_read_enabled": true, + "longform_notetweets_richtext_consumption_enabled": true, + "mobile_app_spotlight_module_enabled": false, "responsive_web_edit_tweet_api_enabled": true, "responsive_web_enhance_cards_enabled": false, "responsive_web_graphql_exclude_directive_enabled": true, @@ -46,6 +50,7 @@ const "responsive_web_media_download_video_enabled": false, "responsive_web_text_conversations_enabled": false, "responsive_web_twitter_article_tweet_consumption_enabled": true, + "unified_cards_destination_url_params_enabled": false, "responsive_web_twitter_blue_verified_badge_is_enabled": true, "rweb_lists_timeline_redesign_enabled": true, "spaces_2022_h2_clipping": true, @@ -86,11 +91,17 @@ const "payments_enabled": false, "responsive_web_profile_redirect_enabled": false, "responsive_web_grok_show_grok_translated_post": false, - "responsive_web_grok_community_note_auto_translation_is_enabled": false + "responsive_web_grok_community_note_auto_translation_is_enabled": false, + "profile_label_improvements_pcf_label_in_profile_enabled": false, + "grok_android_analyze_trend_fetch_enabled": false, + "grok_translations_community_note_auto_translation_is_enabled": false, + "grok_translations_post_auto_translation_is_enabled": false, + "grok_translations_community_note_translation_is_enabled": false, + "grok_translations_timeline_user_bio_auto_translation_is_enabled": false }""".replace(" ", "").replace("\n", "") tweetVariables* = """{ - "focalTweetId": "$1", + "postId": "$1", $2 "includeHasBirdwatchNotes": false, "includePromotedContent": false, diff --git a/src/parser.nim b/src/parser.nim index 8712a5f..700e896 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -1,10 +1,10 @@ # SPDX-License-Identifier: AGPL-3.0-only -import strutils, options, times, math +import strutils, options, times, math, tables import packedjson, packedjson/deserialiser import types, parserutils, utils import experimental/parser/unifiedcard -proc parseGraphTweet(js: JsonNode; isLegacy=false): Tweet +proc parseGraphTweet(js: JsonNode): Tweet proc parseUser(js: JsonNode; id=""): User = if js.isNull: return @@ -46,6 +46,9 @@ proc parseGraphUser(js: JsonNode): User = result = parseUser(user{"legacy"}, user{"rest_id"}.getStr) + if result.verifiedType == none and user{"is_blue_verified"}.getBool(false): + result.verifiedType = blue + # fallback to support UserMedia/recent GraphQL updates if result.username.len == 0: result.username = user{"core", "screen_name"}.getStr @@ -95,16 +98,24 @@ proc parsePoll(js: JsonNode): Poll = result.leader = result.values.find(max(result.values)) result.votes = result.values.sum -proc parseGif(js: JsonNode): Gif = - result = Gif( - url: js{"video_info", "variants"}[0]{"url"}.getImageStr, - thumb: js{"media_url_https"}.getImageStr - ) +proc parseVideoVariants(variants: JsonNode): seq[VideoVariant] = + result = @[] + for v in variants: + let + url = v{"url"}.getStr + contentType = parseEnum[VideoType](v{"content_type"}.getStr("video/mp4")) + bitrate = v{"bit_rate"}.getInt(v{"bitrate"}.getInt(0)) + + result.add VideoVariant( + contentType: contentType, + bitrate: bitrate, + url: url, + resolution: if contentType == mp4: getMp4Resolution(url) else: 0 + ) proc parseVideo(js: JsonNode): Video = result = Video( thumb: js{"media_url_https"}.getImageStr, - views: getVideoViewCount(js), available: true, title: js{"ext_alt_text"}.getStr, durationMs: js{"video_info", "duration_millis"}.getInt @@ -121,17 +132,62 @@ proc parseVideo(js: JsonNode): Video = with description, js{"additional_media_info", "description"}: result.description = description.getStr - for v in js{"video_info", "variants"}: - let - contentType = parseEnum[VideoType](v{"content_type"}.getStr("summary")) - url = v{"url"}.getStr + result.variants = parseVideoVariants(js{"video_info", "variants"}) - result.variants.add VideoVariant( - contentType: contentType, - bitrate: v{"bitrate"}.getInt, - url: url, - resolution: if contentType == mp4: getMp4Resolution(url) else: 0 - ) +proc parseLegacyMediaEntities(js: JsonNode; result: var Tweet) = + with jsMedia, js{"extended_entities", "media"}: + for m in jsMedia: + case m.getTypeName: + of "photo": + result.photos.add m{"media_url_https"}.getImageStr + of "video": + result.video = some(parseVideo(m)) + with user, m{"additional_media_info", "source_user"}: + if user{"id"}.getInt > 0: + result.attribution = some(parseUser(user)) + else: + result.attribution = some(parseGraphUser(user)) + of "animated_gif": + result.gif = some Gif( + url: m{"video_info", "variants"}[0]{"url"}.getImageStr, + thumb: m{"media_url_https"}.getImageStr + ) + else: discard + + with url, m{"url"}: + if result.text.endsWith(url.getStr): + result.text.removeSuffix(url.getStr) + result.text = result.text.strip() + +proc parseMediaEntities(js: JsonNode; result: var Tweet) = + with mediaEntities, js{"media_entities"}: + for mediaEntity in mediaEntities: + with mediaInfo, mediaEntity{"media_results", "result", "media_info"}: + case mediaInfo.getTypeName + of "ApiImage": + result.photos.add mediaInfo{"original_img_url"}.getImageStr + of "ApiVideo": + let status = mediaEntity{"media_results", "result", "media_availability_v2", "status"} + result.video = some Video( + available: status.getStr == "Available", + thumb: mediaInfo{"preview_image", "original_img_url"}.getImageStr, + durationMs: mediaInfo{"duration_millis"}.getInt, + variants: parseVideoVariants(mediaInfo{"variants"}) + ) + of "ApiGif": + result.gif = some Gif( + url: mediaInfo{"variants"}[0]{"url"}.getImageStr, + thumb: mediaInfo{"preview_image", "original_img_url"}.getImageStr + ) + else: discard + + # Remove media URLs from text + with mediaList, js{"legacy", "entities", "media"}: + for url in mediaList: + let expandedUrl = url{"expanded_url"}.getStr + if result.text.endsWith(expandedUrl): + result.text.removeSuffix(expandedUrl) + result.text = result.text.strip() proc parsePromoVideo(js: JsonNode): Video = result = Video( @@ -223,12 +279,17 @@ proc parseCard(js: JsonNode; urls: JsonNode): Card = proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet = if js.isNull: return + + let time = + if js{"created_at"}.notNull: js{"created_at"}.getTime + else: js{"created_at_ms"}.getTimeFromMs + result = Tweet( id: js{"id_str"}.getId, threadId: js{"conversation_id_str"}.getId, replyId: js{"in_reply_to_status_id_str"}.getId, text: js{"full_text"}.getStr, - time: js{"created_at"}.getTime, + time: time, hasThread: js{"self_thread"}.notNull, available: true, user: User(id: js{"user_id_str"}.getStr), @@ -236,7 +297,6 @@ proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet = replies: js{"reply_count"}.getInt, retweets: js{"retweet_count"}.getInt, likes: js{"favorite_count"}.getInt, - quotes: js{"quote_count"}.getInt, views: js{"views_count"}.getInt ) ) @@ -262,6 +322,12 @@ proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet = result.retweet = some parseGraphTweet(rt) return + with reposts, js{"repostedStatusResults"}: + with rt, reposts{"result"}: + if "legacy" in rt: + result.retweet = some parseGraphTweet(rt) + return + if jsCard.kind != JNull: let name = jsCard{"name"}.getStr if "poll" in name: @@ -275,27 +341,7 @@ proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet = result.card = some parseCard(jsCard, js{"entities", "urls"}) result.expandTweetEntities(js) - - with jsMedia, js{"extended_entities", "media"}: - for m in jsMedia: - case m{"type"}.getStr - of "photo": - result.photos.add m{"media_url_https"}.getImageStr - of "video": - result.video = some(parseVideo(m)) - with user, m{"additional_media_info", "source_user"}: - if user{"id"}.getInt > 0: - result.attribution = some(parseUser(user)) - else: - result.attribution = some(parseGraphUser(user)) - of "animated_gif": - result.gif = some(parseGif(m)) - else: discard - - with url, m{"url"}: - if result.text.endsWith(url.getStr): - result.text.removeSuffix(url.getStr) - result.text = result.text.strip() + parseLegacyMediaEntities(js, result) with jsWithheld, js{"withheld_in_countries"}: let withheldInCountries: seq[string] = @@ -311,95 +357,108 @@ proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet = result.text.removeSuffix(" Learn more.") result.available = false -proc parseGraphTweet(js: JsonNode; isLegacy=false): Tweet = +proc parseGraphTweet(js: JsonNode): Tweet = if js.kind == JNull: return Tweet() - case js{"__typename"}.getStr + case js.getTypeName: of "TweetUnavailable": return Tweet() of "TweetTombstone": - with text, js{"tombstone", "richText"}: - return Tweet(text: text.getTombstone) - with text, js{"tombstone", "text"}: + with text, select(js{"tombstone", "richText"}, js{"tombstone", "text"}): return Tweet(text: text.getTombstone) return Tweet() of "TweetPreviewDisplay": return Tweet(text: "You're unable to view this Tweet because it's only available to the Subscribers of the account owner.") of "TweetWithVisibilityResults": - return parseGraphTweet(js{"tweet"}, isLegacy) + return parseGraphTweet(js{"tweet"}) else: discard if not js.hasKey("legacy"): return Tweet() - var jsCard = copy(js{if isLegacy: "card" else: "tweet_card", "legacy"}) + var jsCard = select(js{"card"}, js{"tweet_card"}, js{"legacy", "tweet_card"}) if jsCard.kind != JNull: - var values = newJObject() - for val in jsCard["binding_values"]: - values[val["key"].getStr] = val["value"] - jsCard["binding_values"] = values + let legacyCard = jsCard{"legacy"} + if legacyCard.kind != JNull: + let bindingArray = legacyCard{"binding_values"} + if bindingArray.kind == JArray: + var bindingObj: seq[(string, JsonNode)] + for item in bindingArray: + bindingObj.add((item{"key"}.getStr, item{"value"})) + # Create a new card object with flattened structure + jsCard = %*{ + "name": legacyCard{"name"}, + "url": legacyCard{"url"}, + "binding_values": %bindingObj + } result = parseTweet(js{"legacy"}, jsCard) result.id = js{"rest_id"}.getId result.user = parseGraphUser(js{"core"}) + if result.replyId == 0: + result.replyId = js{"reply_to_results", "rest_id"}.getId + with count, js{"views", "count"}: result.stats.views = count.getStr("0").parseInt with noteTweet, js{"note_tweet", "note_tweet_results", "result"}: result.expandNoteTweetEntities(noteTweet) + parseMediaEntities(js, result) + if result.quote.isSome: - result.quote = some(parseGraphTweet(js{"quoted_status_result", "result"}, isLegacy)) + result.quote = some(parseGraphTweet(js{"quoted_status_result", "result"})) + + with quoted, js{"quotedPostResults", "result"}: + result.quote = some(parseGraphTweet(quoted)) proc parseGraphThread(js: JsonNode): tuple[thread: Chain; self: bool] = - for t in js{"content", "items"}: - let entryId = t{"entryId"}.getStr + for t in ? js{"content", "items"}: + let entryId = t.getEntryId if "cursor-showmore" in entryId: let cursor = t{"item", "content", "value"} result.thread.cursor = cursor.getStr result.thread.hasMore = true elif "tweet" in entryId and "promoted" notin entryId: - let - isLegacy = t{"item"}.hasKey("itemContent") - (contentKey, resultKey) = if isLegacy: ("itemContent", "tweet_results") - else: ("content", "tweetResult") + with tweet, t.getTweetResult("item"): + result.thread.content.add parseGraphTweet(tweet) - with content, t{"item", contentKey}: - result.thread.content.add parseGraphTweet(content{resultKey, "result"}, isLegacy) - - if content{"tweetDisplayType"}.getStr == "SelfThread": + let tweetDisplayType = select( + t{"item", "content", "tweet_display_type"}, + t{"item", "itemContent", "tweetDisplayType"} + ) + if tweetDisplayType.getStr == "SelfThread": result.self = true proc parseGraphTweetResult*(js: JsonNode): Tweet = with tweet, js{"data", "tweet_result", "result"}: - result = parseGraphTweet(tweet, false) + result = parseGraphTweet(tweet) proc parseGraphConversation*(js: JsonNode; tweetId: string): Conversation = result = Conversation(replies: Result[Chain](beginning: true)) - let - v2 = js{"data", "timeline_response"}.notNull - rootKey = if v2: "timeline_response" else: "threaded_conversation_with_injections_v2" - contentKey = if v2: "content" else: "itemContent" - resultKey = if v2: "tweetResult" else: "tweet_results" - let instructions = ? js{"data", rootKey, "instructions"} + let instructions = ? select( + js{"data", "timelineResponse", "instructions"}, + js{"data", "timeline_response", "instructions"}, + js{"data", "threaded_conversation_with_injections_v2", "instructions"} + ) if instructions.len == 0: return for i in instructions: - let instrType = i{"type"}.getStr(i{"__typename"}.getStr) - if instrType == "TimelineAddEntries": + if i.getTypeName == "TimelineAddEntries": for e in i{"entries"}: - let entryId = e{"entryId"}.getStr + let entryId = e.getEntryId if entryId.startsWith("tweet"): - with tweetResult, e{"content", contentKey, resultKey, "result"}: - let tweet = parseGraphTweet(tweetResult, not v2) + let tweetResult = getTweetResult(e) + if tweetResult.notNull: + let tweet = parseGraphTweet(tweetResult) if not tweet.available: - tweet.id = parseBiggestInt(entryId.getId()) + tweet.id = entryId.getId if $tweet.id == tweetId: result.tweet = tweet @@ -412,67 +471,64 @@ proc parseGraphConversation*(js: JsonNode; tweetId: string): Conversation = elif thread.content.len > 0: result.replies.content.add thread elif entryId.startsWith("tombstone"): - let id = entryId.getId() - let tweet = Tweet( - id: parseBiggestInt(id), - available: false, - text: e{"content", contentKey, "tombstoneInfo", "richText"}.getTombstone - ) + let + content = select(e{"content", "content"}, e{"content", "itemContent"}) + tweet = Tweet( + id: entryId.getId, + available: false, + text: content{"tombstoneInfo", "richText"}.getTombstone + ) - if id == tweetId: + if $tweet.id == tweetId: result.tweet = tweet else: result.before.content.add tweet elif entryId.startsWith("cursor-bottom"): - result.replies.bottom = e{"content", contentKey, "value"}.getStr + var cursorValue = select( + e{"content", "content", "value"}, + e{"content", "itemContent", "value"} + ) + result.replies.bottom = cursorValue.getStr proc extractTweetsFromEntry*(e: JsonNode): seq[Tweet] = - var tweetResult = e{"content", "itemContent", "tweet_results", "result"} - if tweetResult.isNull: - tweetResult = e{"content", "content", "tweetResult", "result"} - - if tweetResult.notNull: - var tweet = parseGraphTweet(tweetResult, false) + with tweetResult, getTweetResult(e): + var tweet = parseGraphTweet(tweetResult) if not tweet.available: - tweet.id = parseBiggestInt(e.getEntryId()) + tweet.id = e.getEntryId.getId result.add tweet return for item in e{"content", "items"}: - with tweetResult, item{"item", "itemContent", "tweet_results", "result"}: - var tweet = parseGraphTweet(tweetResult, false) + with tweetResult, item.getTweetResult("item"): + var tweet = parseGraphTweet(tweetResult) if not tweet.available: - tweet.id = parseBiggestInt(item{"entryId"}.getStr.getId()) + tweet.id = item.getEntryId.getId result.add tweet proc parseGraphTimeline*(js: JsonNode; after=""): Profile = result = Profile(tweets: Timeline(beginning: after.len == 0)) - let instructions = - if js{"data", "list"}.notNull: - ? js{"data", "list", "timeline_response", "timeline", "instructions"} - elif js{"data", "user"}.notNull: - ? js{"data", "user", "result", "timeline", "timeline", "instructions"} - else: - ? js{"data", "user_result", "result", "timeline_response", "timeline", "instructions"} - + let instructions = ? select( + js{"data", "list", "timeline_response", "timeline", "instructions"}, + js{"data", "user", "result", "timeline", "timeline", "instructions"}, + js{"data", "user_result", "result", "timeline_response", "timeline", "instructions"} + ) if instructions.len == 0: return for i in instructions: - # TimelineAddToModule instruction is used by UserMedia if i{"moduleItems"}.notNull: for item in i{"moduleItems"}: - with tweetResult, item{"item", "itemContent", "tweet_results", "result"}: - let tweet = parseGraphTweet(tweetResult, false) + with tweetResult, item.getTweetResult("item"): + let tweet = parseGraphTweet(tweetResult) if not tweet.available: - tweet.id = parseBiggestInt(item{"entryId"}.getStr.getId()) + tweet.id = item.getEntryId.getId result.tweets.content.add tweet continue if i{"entries"}.notNull: for e in i{"entries"}: - let entryId = e{"entryId"}.getStr + let entryId = e.getEntryId if entryId.startsWith("tweet") or entryId.startsWith("profile-grid"): for tweet in extractTweetsFromEntry(e): result.tweets.content.add tweet @@ -483,8 +539,7 @@ proc parseGraphTimeline*(js: JsonNode; after=""): Profile = result.tweets.bottom = e{"content", "value"}.getStr if after.len == 0: - let instrType = i{"type"}.getStr(i{"__typename"}.getStr) - if instrType == "TimelinePinEntry": + if i.getTypeName == "TimelinePinEntry": let tweets = extractTweetsFromEntry(i{"entry"}) if tweets.len > 0: var tweet = tweets[0] @@ -494,23 +549,20 @@ proc parseGraphTimeline*(js: JsonNode; after=""): Profile = proc parseGraphPhotoRail*(js: JsonNode): PhotoRail = result = @[] - let instructions = - if js{"data", "user"}.notNull: - ? js{"data", "user", "result", "timeline", "timeline", "instructions"} - else: - ? js{"data", "user_result", "result", "timeline_response", "timeline", "instructions"} - + let instructions = select( + js{"data", "user", "result", "timeline", "timeline", "instructions"}, + js{"data", "user_result", "result", "timeline_response", "timeline", "instructions"} + ) if instructions.len == 0: return for i in instructions: - # TimelineAddToModule instruction is used by MediaTimelineV2 if i{"moduleItems"}.notNull: for item in i{"moduleItems"}: - with tweetResult, item{"item", "itemContent", "tweet_results", "result"}: - let t = parseGraphTweet(tweetResult, false) + with tweetResult, item.getTweetResult("item"): + let t = parseGraphTweet(tweetResult) if not t.available: - t.id = parseBiggestInt(item{"entryId"}.getStr.getId()) + t.id = item.getEntryId.getId let photo = extractGalleryPhoto(t) if photo.url.len > 0: @@ -520,12 +572,11 @@ proc parseGraphPhotoRail*(js: JsonNode): PhotoRail = return continue - let instrType = i{"type"}.getStr(i{"__typename"}.getStr) - if instrType != "TimelineAddEntries": + if i.getTypeName != "TimelineAddEntries": continue for e in i{"entries"}: - let entryId = e{"entryId"}.getStr + let entryId = e.getEntryId if entryId.startsWith("tweet") or entryId.startsWith("profile-grid"): for t in extractTweetsFromEntry(e): let photo = extractGalleryPhoto(t) @@ -538,21 +589,24 @@ proc parseGraphPhotoRail*(js: JsonNode): PhotoRail = proc parseGraphSearch*[T: User | Tweets](js: JsonNode; after=""): Result[T] = result = Result[T](beginning: after.len == 0) - let instructions = js{"data", "search_by_raw_query", "search_timeline", "timeline", "instructions"} + let instructions = select( + js{"data", "search", "timeline_response", "timeline", "instructions"}, + js{"data", "search_by_raw_query", "search_timeline", "timeline", "instructions"} + ) if instructions.len == 0: return for instruction in instructions: - let typ = instruction{"type"}.getStr + let typ = getTypeName(instruction) if typ == "TimelineAddEntries": for e in instruction{"entries"}: - let entryId = e{"entryId"}.getStr + let entryId = e.getEntryId when T is Tweets: if entryId.startsWith("tweet"): - with tweetRes, e{"content", "itemContent", "tweet_results", "result"}: + with tweetRes, getTweetResult(e): let tweet = parseGraphTweet(tweetRes) if not tweet.available: - tweet.id = parseBiggestInt(entryId.getId()) + tweet.id = entryId.getId result.content.add tweet elif T is User: if entryId.startsWith("user"): diff --git a/src/parserutils.nim b/src/parserutils.nim index 7e246dd..f40082c 100644 --- a/src/parserutils.nim +++ b/src/parserutils.nim @@ -36,6 +36,12 @@ template `?`*(js: JsonNode): untyped = if j.isNull: return j +template select*(a, b: JsonNode): untyped = + if a.notNull: a else: b + +template select*(a, b, c: JsonNode): untyped = + if a.notNull: a elif b.notNull: b else: c + template with*(ident, value, body): untyped = if true: let ident {.inject.} = value @@ -54,6 +60,20 @@ template getError*(js: JsonNode): Error = if js.kind != JArray or js.len == 0: null else: Error(js[0]{"code"}.getInt) +proc getTweetResult*(js: JsonNode; root="content"): JsonNode = + select( + js{root, "content", "tweet_results", "result"}, + js{root, "itemContent", "tweet_results", "result"}, + js{root, "content", "tweetResult", "result"} + ) + +template getTypeName*(js: JsonNode): string = + js{"__typename"}.getStr(js{"type"}.getStr) + +template getEntryId*(e: JsonNode): string = + e{"entryId"}.getStr(e{"entry_id"}.getStr) + + template parseTime(time: string; f: static string; flen: int): DateTime = if time.len != flen: return parse(time, f, utc()) @@ -64,29 +84,24 @@ proc getDateTime*(js: JsonNode): DateTime = proc getTime*(js: JsonNode): DateTime = parseTime(js.getStr, "ddd MMM dd hh:mm:ss \'+0000\' yyyy", 30) -proc getId*(id: string): string {.inline.} = +proc getTimeFromMs*(js: JsonNode): DateTime = + let ms = js.getInt(0) + if ms == 0: return + let seconds = ms div 1000 + return fromUnix(seconds).utc() + +proc getId*(id: string): int64 {.inline.} = let start = id.rfind("-") - if start < 0: return id - id[start + 1 ..< id.len] + if start < 0: + return parseBiggestInt(id) + return parseBiggestInt(id[start + 1 ..< id.len]) proc getId*(js: JsonNode): int64 {.inline.} = case js.kind - of JString: return parseBiggestInt(js.getStr("0")) + of JString: return js.getStr("0").getId of JInt: return js.getBiggestInt() else: return 0 -proc getEntryId*(js: JsonNode): string {.inline.} = - let entry = js{"entryId"}.getStr - if entry.len == 0: return - - if "tweet" in entry or "sq-I-t" in entry: - return entry.getId - elif "tombstone" in entry: - return js{"content", "item", "content", "tombstone", "tweet", "id"}.getStr - else: - echo "unknown entry: ", entry - return - template getStrVal*(js: JsonNode; default=""): string = js{"string_value"}.getStr(default) @@ -157,12 +172,6 @@ proc getMp4Resolution*(url: string): int = # cannot determine resolution (e.g. m3u8/non-mp4 video) return 0 -proc getVideoViewCount*(js: JsonNode): string = - with stats, js{"ext_media_stats"}: - return stats{"view_count"}.getStr($stats{"viewCount"}.getInt) - - return $js{"mediaStats", "viewCount"}.getInt(0) - proc extractSlice(js: JsonNode): Slice[int] = result = js["indices"][0].getInt ..< js["indices"][1].getInt diff --git a/src/types.nim b/src/types.nim index 5a08bb7..f16fe6f 100644 --- a/src/types.nim +++ b/src/types.nim @@ -121,7 +121,6 @@ type durationMs*: int url*: string thumb*: string - views*: string available*: bool reason*: string title*: string @@ -202,7 +201,6 @@ type replies*: int retweets*: int likes*: int - quotes*: int views*: int Tweet* = ref object diff --git a/src/views/tweet.nim b/src/views/tweet.nim index 6d76755..8ff8cb1 100644 --- a/src/views/tweet.nim +++ b/src/views/tweet.nim @@ -178,16 +178,12 @@ func formatStat(stat: int): string = if stat > 0: insertSep($stat, ',') else: "" -proc renderStats(stats: TweetStats; views: string): VNode = +proc renderStats(stats: TweetStats): VNode = buildHtml(tdiv(class="tweet-stats")): span(class="tweet-stat"): icon "comment", formatStat(stats.replies) span(class="tweet-stat"): icon "retweet", formatStat(stats.retweets) - span(class="tweet-stat"): icon "quote", formatStat(stats.quotes) span(class="tweet-stat"): icon "heart", formatStat(stats.likes) - if stats.views > 0: - span(class="tweet-stat"): icon "views", formatStat(stats.views) - if views.len > 0: - span(class="tweet-stat"): icon "play", insertSep(views, ',') + span(class="tweet-stat"): icon "views", formatStat(stats.views) proc renderReply(tweet: Tweet): VNode = buildHtml(tdiv(class="replying-to")): @@ -303,7 +299,6 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class=""; index=0; a(class="tweet-link", href=getLink(tweet)) tdiv(class="tweet-body"): - var views = "" renderHeader(tweet, retweet, pinned, prefs) if not afterTweet and index == 0 and tweet.reply.len > 0 and @@ -327,10 +322,8 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class=""; index=0; renderAlbum(tweet) elif tweet.video.isSome: renderVideo(tweet.video.get(), prefs, path) - views = tweet.video.get().views elif tweet.gif.isSome: renderGif(tweet.gif.get(), prefs) - views = "GIF" if tweet.poll.isSome: renderPoll(tweet.poll.get()) @@ -345,7 +338,7 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class=""; index=0; renderMediaTags(tweet.mediaTags) if not prefs.hideTweetStats: - renderStats(tweet.stats, views) + renderStats(tweet.stats) if showThread: a(class="show-thread", href=("/i/status/" & $tweet.threadId)): From f8a17fdaa5d88ae425be3cf24a56fcc364d35a11 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Sun, 23 Nov 2025 17:28:11 +0100 Subject: [PATCH 248/409] Remove Nim 1.6.x support Fixes #1311 --- .github/workflows/run-tests.yml | 2 +- nitter.nimble | 2 +- src/parserutils.nim | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 46e4ace..f4639a4 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -23,7 +23,7 @@ jobs: runs-on: buildjet-2vcpu-ubuntu-2204 strategy: matrix: - nim: ["1.6.x", "2.0.x", "2.2.x", "devel"] + nim: ["2.0.x", "2.2.x", "devel"] steps: - name: Checkout Code uses: actions/checkout@v4 diff --git a/nitter.nimble b/nitter.nimble index 37f9229..7ff8196 100644 --- a/nitter.nimble +++ b/nitter.nimble @@ -10,7 +10,7 @@ bin = @["nitter"] # Dependencies -requires "nim >= 1.6.10" +requires "nim >= 2.0.0" requires "jester#baca3f" requires "karax#5cf360c" requires "sass#7dfdd03" diff --git a/src/parserutils.nim b/src/parserutils.nim index f40082c..72c50e1 100644 --- a/src/parserutils.nim +++ b/src/parserutils.nim @@ -50,8 +50,7 @@ template with*(ident, value, body): untyped = template with*(ident; value: JsonNode; body): untyped = if true: let ident {.inject.} = value - # value.notNull causes a compilation error for versions < 1.6.14 - if notNull(value): body + if value.notNull: body template getCursor*(js: JsonNode): string = js{"content", "operation", "cursor", "value"}.getStr From 5b4a3fe691ce7a1a8e7cd491b0b37d3c95a621ce Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Sun, 23 Nov 2025 19:26:48 +0100 Subject: [PATCH 249/409] Redirect /i/status/id/history to /i/status/id Fixes #1231 --- src/routes/status.nim | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/routes/status.nim b/src/routes/status.nim index 7e89220..0168dac 100644 --- a/src/routes/status.nim +++ b/src/routes/status.nim @@ -31,8 +31,6 @@ proc createStatusRouter*(cfg: Config) = resp $renderReplies(replies, prefs, getPath()) let conv = await getTweet(id, getCursor()) - if conv == nil: - echo "nil conv" if conv == nil or conv.tweet == nil or conv.tweet.id == 0: var error = "Tweet not found" @@ -68,7 +66,7 @@ proc createStatusRouter*(cfg: Config) = get "/@name/@s/@id/@m/?@i?": cond @"s" in ["status", "statuses"] - cond @"m" in ["video", "photo"] + cond @"m" in ["video", "photo", "history"] redirect("/$1/status/$2" % [@"name", @"id"]) get "/@name/statuses/@id/?": @@ -76,6 +74,6 @@ proc createStatusRouter*(cfg: Config) = get "/i/web/status/@id": redirect("/i/status/" & @"id") - + get "/@name/thread/@id/?": redirect("/$1/status/$2" % [@"name", @"id"]) From 53edbbc4e9d5cc8177596472894d49ea753a7049 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Sun, 23 Nov 2025 19:58:24 +0100 Subject: [PATCH 250/409] Fix broken tweet pagination ("Load more" button) Fixes #1277 --- src/parser.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/src/parser.nim b/src/parser.nim index 700e896..5bf2b0b 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -485,6 +485,7 @@ proc parseGraphConversation*(js: JsonNode; tweetId: string): Conversation = result.before.content.add tweet elif entryId.startsWith("cursor-bottom"): var cursorValue = select( + e{"content", "value"}, e{"content", "content", "value"}, e{"content", "itemContent", "value"} ) From 25df68209472133bf0b9f9abecc725587d9facba Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Mon, 24 Nov 2025 23:04:25 +0100 Subject: [PATCH 251/409] Expose username as HTML attribute Fixes #551 --- src/views/timeline.nim | 2 +- src/views/tweet.nim | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/views/timeline.nim b/src/views/timeline.nim index abeb6d3..a205c04 100644 --- a/src/views/timeline.nim +++ b/src/views/timeline.nim @@ -56,7 +56,7 @@ proc renderThread(thread: Tweets; prefs: Prefs; path: string): VNode = index=i, last=(i == thread.high), showThread=show) proc renderUser(user: User; prefs: Prefs): VNode = - buildHtml(tdiv(class="timeline-item")): + buildHtml(tdiv(class="timeline-item", data-username=user.username)): a(class="tweet-link", href=("/" & user.username)) tdiv(class="tweet-body profile-result"): tdiv(class="tweet-header"): diff --git a/src/views/tweet.nim b/src/views/tweet.nim index 8ff8cb1..552ab89 100644 --- a/src/views/tweet.nim +++ b/src/views/tweet.nim @@ -272,7 +272,7 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class=""; index=0; divClass = "thread-last " & class if not tweet.available: - return buildHtml(tdiv(class=divClass & "unavailable timeline-item")): + return buildHtml(tdiv(class=divClass & "unavailable timeline-item", data-username=tweet.user.username)): tdiv(class="unavailable-box"): if tweet.tombstone.len > 0: text tweet.tombstone @@ -294,7 +294,7 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class=""; index=0; tweet = tweet.retweet.get retweet = fullTweet.user.fullname - buildHtml(tdiv(class=("timeline-item " & divClass))): + buildHtml(tdiv(class=("timeline-item " & divClass), data-username=tweet.user.username)): if not mainTweet: a(class="tweet-link", href=getLink(tweet)) From 1657eeb769d79ffe414ec31bd4ce4686e3a3400c Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Mon, 24 Nov 2025 23:04:25 +0100 Subject: [PATCH 252/409] Fix canonical link causing redirects to Twitter Fixes #526 --- src/formatters.nim | 2 +- src/views/general.nim | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/formatters.nim b/src/formatters.nim index cafaa4f..7bbbe8b 100644 --- a/src/formatters.nim +++ b/src/formatters.nim @@ -6,7 +6,7 @@ import types, utils, query const cards = "cards.twitter.com/cards" tco = "https://t.co" - twitter = parseUri("https://twitter.com") + twitter = parseUri("https://x.com") let twRegex = re"(?<=(?<!\S)https:\/\/|(?<=\s))(www\.|mobile\.)?twitter\.com" diff --git a/src/views/general.nim b/src/views/general.nim index 0091c74..faf4c1d 100644 --- a/src/views/general.nim +++ b/src/views/general.nim @@ -31,14 +31,14 @@ proc renderNavbar(cfg: Config; req: Request; rss, canonical: string): VNode = icon "search", title="Search", href="/search" if cfg.enableRss and rss.len > 0: icon "rss", title="RSS Feed", href=rss - icon "bird", title="Open in Twitter", href=canonical + icon "bird", title="Open in X", href=canonical a(href="https://liberapay.com/zedeus"): verbatim lp icon "info", title="About", href="/about" icon "cog", title="Preferences", href=("/settings?referer=" & encodeUrl(path)) proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc=""; video=""; images: seq[string] = @[]; banner=""; ogTitle=""; - rss=""; canonical=""): VNode = + rss=""; alternate=""): VNode = var theme = prefs.theme.toTheme if "theme" in req.params: theme = req.params["theme"].toTheme @@ -66,8 +66,8 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc=""; link(rel="search", type="application/opensearchdescription+xml", title=cfg.title, href=opensearchUrl) - if canonical.len > 0: - link(rel="canonical", href=canonical) + if alternate.len > 0: + link(rel="alternate", href=alternate, title="View on X") if cfg.enableRss and rss.len > 0: link(rel="alternate", type="application/rss+xml", href=rss, title="RSS feed") @@ -125,14 +125,14 @@ proc renderMain*(body: VNode; req: Request; cfg: Config; prefs=defaultPrefs; titleText=""; desc=""; ogTitle=""; rss=""; video=""; images: seq[string] = @[]; banner=""): string = - let canonical = getTwitterLink(req.path, req.params) + let twitterLink = getTwitterLink(req.path, req.params) let node = buildHtml(html(lang="en")): renderHead(prefs, cfg, req, titleText, desc, video, images, banner, ogTitle, - rss, canonical) + rss, twitterLink) body: - renderNavbar(cfg, req, rss, canonical) + renderNavbar(cfg, req, rss, twitterLink) tdiv(class="container"): body From d47eb8f0eb6ba34055ba80e1537e7b415107a7af Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Mon, 24 Nov 2025 23:04:25 +0100 Subject: [PATCH 253/409] Fix double slashes in url replacements Fixes #520 --- src/formatters.nim | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/formatters.nim b/src/formatters.nim index 7bbbe8b..e491928 100644 --- a/src/formatters.nim +++ b/src/formatters.nim @@ -59,25 +59,28 @@ proc replaceUrls*(body: string; prefs: Prefs; absolute=""): string = result = body if prefs.replaceYouTube.len > 0 and "youtu" in result: - result = result.replace(ytRegex, prefs.replaceYouTube) + let youtubeHost = strip(prefs.replaceYouTube, chars={'/'}) + result = result.replace(ytRegex, youtubeHost) if prefs.replaceTwitter.len > 0: + let twitterHost = strip(prefs.replaceTwitter, chars={'/'}) if tco in result: - result = result.replace(tco, https & prefs.replaceTwitter & "/t.co") + result = result.replace(tco, https & twitterHost & "/t.co") if "x.com" in result: - result = result.replace(xRegex, prefs.replaceTwitter) + result = result.replace(xRegex, twitterHost) result = result.replacef(xLinkRegex, a( - prefs.replaceTwitter & "$2", href = https & prefs.replaceTwitter & "$1")) + twitterHost & "$2", href = https & twitterHost & "$1")) if "twitter.com" in result: - result = result.replace(cards, prefs.replaceTwitter & "/cards") - result = result.replace(twRegex, prefs.replaceTwitter) + result = result.replace(cards, twitterHost & "/cards") + result = result.replace(twRegex, twitterHost) result = result.replacef(twLinkRegex, a( - prefs.replaceTwitter & "$2", href = https & prefs.replaceTwitter & "$1")) + twitterHost & "$2", href = https & twitterHost & "$1")) if prefs.replaceReddit.len > 0 and ("reddit.com" in result or "redd.it" in result): - result = result.replace(rdShortRegex, prefs.replaceReddit & "/comments/") - result = result.replace(rdRegex, prefs.replaceReddit) - if prefs.replaceReddit in result and "/gallery/" in result: + let redditHost = strip(prefs.replaceReddit, chars={'/'}) + result = result.replace(rdShortRegex, redditHost & "/comments/") + result = result.replace(rdRegex, redditHost) + if redditHost in result and "/gallery/" in result: result = result.replace("/gallery/", "/comments/") if absolute.len > 0 and "href" in result: From 4748311f8dd9de0e3cab7cd00027c333b024dd9b Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Mon, 24 Nov 2025 23:04:25 +0100 Subject: [PATCH 254/409] Fix intent/follow URL redirect Fixes #629 --- src/routes/timeline.nim | 6 ++++++ src/routes/unsupported.nim | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/routes/timeline.nim b/src/routes/timeline.nim index 7a10e91..49c7ce2 100644 --- a/src/routes/timeline.nim +++ b/src/routes/timeline.nim @@ -105,6 +105,12 @@ proc createTimelineRouter*(cfg: Config) = get "/intent/user": respUserId() + get "/intent/follow/?": + let username = request.params.getOrDefault("screen_name") + if username.len == 0: + resp Http400, showError("Missing screen_name parameter", cfg) + redirect("/" & username) + get "/@name/?@tab?/?": cond '.' notin @"name" cond @"name" notin ["pic", "gif", "video", "search", "settings", "login", "intent", "i"] diff --git a/src/routes/unsupported.nim b/src/routes/unsupported.nim index 0c085d4..362b36b 100644 --- a/src/routes/unsupported.nim +++ b/src/routes/unsupported.nim @@ -17,7 +17,7 @@ proc createUnsupportedRouter*(cfg: Config) = get "/@name/lists/?": feature() get "/intent/?@i?": - cond @"i" notin ["user"] + cond @"i" notin ["user", "follow"] feature() get "/i/@i?/?@j?": From f038b53fa2790866742dc1d6ca511950ed8bf276 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Mon, 24 Nov 2025 23:04:25 +0100 Subject: [PATCH 255/409] Fix body font size to match x.com Fixes #711 --- src/sass/index.scss | 2 +- src/views/general.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sass/index.scss b/src/sass/index.scss index 6cab48e..36a8a93 100644 --- a/src/sass/index.scss +++ b/src/sass/index.scss @@ -51,7 +51,7 @@ body { background-color: var(--bg_color); color: var(--fg_color); font-family: $font_0, $font_1, $font_2, $font_3; - font-size: 14px; + font-size: 15px; line-height: 1.3; margin: 0; } diff --git a/src/views/general.nim b/src/views/general.nim index faf4c1d..d431e98 100644 --- a/src/views/general.nim +++ b/src/views/general.nim @@ -52,7 +52,7 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc=""; let opensearchUrl = getUrlPrefix(cfg) & "/opensearch" buildHtml(head): - link(rel="stylesheet", type="text/css", href="/css/style.css?v=19") + link(rel="stylesheet", type="text/css", href="/css/style.css?v=20") link(rel="stylesheet", type="text/css", href="/css/fontello.css?v=3") if theme.len > 0: From 4979d07f2ed9ee15238fd29d52acca42710a5333 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Mon, 24 Nov 2025 23:04:25 +0100 Subject: [PATCH 256/409] Add spaces filter, remove broken filters --- src/query.nim | 5 ++--- src/views/search.nim | 4 +--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/query.nim b/src/query.nim index 06e1da2..b6ff5df 100644 --- a/src/query.nim +++ b/src/query.nim @@ -6,10 +6,9 @@ import types const validFilters* = @[ "media", "images", "twimg", "videos", - "native_video", "consumer_video", "pro_video", + "native_video", "consumer_video", "spaces", "links", "news", "quote", "mentions", - "replies", "retweets", "nativeretweets", - "verified", "safe" + "replies", "retweets", "nativeretweets" ] emptyQuery* = "include:nativeretweets" diff --git a/src/views/search.nim b/src/views/search.nim index 9f7fc95..35af526 100644 --- a/src/views/search.nim +++ b/src/views/search.nim @@ -10,14 +10,12 @@ const toggles = { "media": "Media", "videos": "Videos", "news": "News", - "verified": "Verified", "native_video": "Native videos", "replies": "Replies", "links": "Links", "images": "Images", - "safe": "Safe", "quote": "Quotes", - "pro_video": "Pro videos" + "spaces": "Spaces" }.toOrderedTable proc renderSearch*(): VNode = From 12bbddf204e805885e0760b93120f90560b8354b Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Mon, 24 Nov 2025 23:04:25 +0100 Subject: [PATCH 257/409] Update search panel grid layout and animation --- src/sass/include/_mixins.css | 13 +------------ src/sass/search.scss | 15 +++++++-------- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/src/sass/include/_mixins.css b/src/sass/include/_mixins.css index 94e11ee..5fde51a 100644 --- a/src/sass/include/_mixins.css +++ b/src/sass/include/_mixins.css @@ -66,18 +66,7 @@ } #search-panel-toggle:checked ~ .search-panel { - @if $rows == 6 { - max-height: 200px !important; - } - @if $rows == 5 { - max-height: 300px !important; - } - @if $rows == 4 { - max-height: 300px !important; - } - @if $rows == 3 { - max-height: 365px !important; - } + max-height: 380px !important; } } } diff --git a/src/sass/search.scss b/src/sass/search.scss index f70f7ea..db0bc66 100644 --- a/src/sass/search.scss +++ b/src/sass/search.scss @@ -42,7 +42,7 @@ @include input-colors; } - @include create-toggle(search-panel, 200px); + @include create-toggle(search-panel, 380px); } .search-panel { @@ -104,19 +104,18 @@ .search-toggles { flex-grow: 1; display: grid; - grid-template-columns: repeat(6, auto); + grid-template-columns: repeat(5, auto); grid-column-gap: 10px; } .profile-tabs { @include search-resize(820px, 5); - @include search-resize(725px, 4); - @include search-resize(600px, 6); - @include search-resize(560px, 5); - @include search-resize(480px, 4); + @include search-resize(715px, 4); + @include search-resize(700px, 5); + @include search-resize(485px, 4); @include search-resize(410px, 3); } -@include search-resize(560px, 5); -@include search-resize(480px, 4); +@include search-resize(700px, 5); +@include search-resize(485px, 4); @include search-resize(410px, 3); From 78101df2cc22e30158bd77d18cc8267ca42834f2 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Mon, 24 Nov 2025 23:04:25 +0100 Subject: [PATCH 258/409] Style number input field --- src/query.nim | 15 ++++++++++----- src/sass/inputs.scss | 22 ++++++++++++++++++++-- src/sass/search.scss | 3 ++- src/types.nim | 2 +- src/views/general.nim | 2 +- src/views/renderutils.nim | 7 +++++++ src/views/search.nim | 6 +++--- 7 files changed, 44 insertions(+), 13 deletions(-) diff --git a/src/query.nim b/src/query.nim index b6ff5df..c77bf5f 100644 --- a/src/query.nim +++ b/src/query.nim @@ -17,6 +17,11 @@ template `@`(param: string): untyped = if param in pms: pms[param] else: "" +proc validateNumber(value: string): string = + if value.anyIt(not it.isDigit): + return "" + return value + proc initQuery*(pms: Table[string, string]; name=""): Query = result = Query( kind: parseEnum[QueryKind](@"f", tweets), @@ -25,7 +30,7 @@ proc initQuery*(pms: Table[string, string]; name=""): Query = excludes: validFilters.filterIt("e-" & it in pms), since: @"since", until: @"until", - near: @"near" + minLikes: validateNumber(@"min_faves") ) if name.len > 0: @@ -77,8 +82,8 @@ proc genQueryParam*(query: Query): string = result &= " since:" & query.since if query.until.len > 0: result &= " until:" & query.until - if query.near.len > 0: - result &= &" near:\"{query.near}\" within:15mi" + if query.minLikes.len > 0: + result &= " min_faves:" & query.minLikes if query.text.len > 0: if result.len > 0: result &= " " & query.text @@ -102,8 +107,8 @@ proc genQueryUrl*(query: Query): string = params.add "since=" & query.since if query.until.len > 0: params.add "until=" & query.until - if query.near.len > 0: - params.add "near=" & query.near + if query.minLikes.len > 0: + params.add "min_faves=" & query.minLikes if params.len > 0: result &= params.join("&") diff --git a/src/sass/inputs.scss b/src/sass/inputs.scss index 17c2a22..d6cbb1d 100644 --- a/src/sass/inputs.scss +++ b/src/sass/inputs.scss @@ -14,6 +14,7 @@ button { input[type="text"], input[type="date"], +input[type="number"], select { @include input-colors; background-color: var(--bg_elements); @@ -24,7 +25,12 @@ select { font-size: 14px; } -input[type="text"] { +input[type="number"] { + -moz-appearance: textfield; +} + +input[type="text"], +input[type="number"] { height: 16px; } @@ -38,6 +44,17 @@ input[type="date"]::-webkit-inner-spin-button { display: none; } +input[type="number"] { + -moz-appearance: textfield; +} + +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + display: none; + -webkit-appearance: none; + margin: 0; +} + input[type="date"]::-webkit-clear-button { margin-left: 17px; filter: grayscale(100%); @@ -164,7 +181,8 @@ input::-webkit-datetime-edit-year-field:focus { appearance: none; } - input[type="text"] { + input[type="text"], + input[type="number"] { position: absolute; right: 0; max-width: 140px; diff --git a/src/sass/search.scss b/src/sass/search.scss index db0bc66..234d677 100644 --- a/src/sass/search.scss +++ b/src/sass/search.scss @@ -24,7 +24,8 @@ height: 23px; } - input[type="text"] { + input[type="text"], + input[type="number"] { height: calc(100% - 4px); width: calc(100% - 8px); } diff --git a/src/types.nim b/src/types.nim index f16fe6f..20a49c9 100644 --- a/src/types.nim +++ b/src/types.nim @@ -140,7 +140,7 @@ type fromUser*: seq[string] since*: string until*: string - near*: string + minLikes*: string sep*: string Gif* = object diff --git a/src/views/general.nim b/src/views/general.nim index d431e98..23681b5 100644 --- a/src/views/general.nim +++ b/src/views/general.nim @@ -52,7 +52,7 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc=""; let opensearchUrl = getUrlPrefix(cfg) & "/opensearch" buildHtml(head): - link(rel="stylesheet", type="text/css", href="/css/style.css?v=20") + link(rel="stylesheet", type="text/css", href="/css/style.css?v=21") link(rel="stylesheet", type="text/css", href="/css/fontello.css?v=3") if theme.len > 0: diff --git a/src/views/renderutils.nim b/src/views/renderutils.nim index 41ef8df..fcdf06f 100644 --- a/src/views/renderutils.nim +++ b/src/views/renderutils.nim @@ -89,6 +89,13 @@ proc genDate*(pref, state: string): VNode = input(name=pref, `type`="date", value=state) icon "calendar" +proc genNumberInput*(pref, label, state, placeholder: string; class=""; autofocus=true; min="0"): VNode = + let p = placeholder + buildHtml(tdiv(class=("pref-group pref-input " & class))): + if label.len > 0: + label(`for`=pref): text label + input(name=pref, `type`="number", placeholder=p, value=state, autofocus=(autofocus and state.len == 0), min=min, step="1") + proc genImg*(url: string; class=""): VNode = buildHtml(): img(src=getPicUrl(url), class=class, alt="", loading="lazy") diff --git a/src/views/search.nim b/src/views/search.nim index 35af526..a43008f 100644 --- a/src/views/search.nim +++ b/src/views/search.nim @@ -51,7 +51,7 @@ proc renderSearchTabs*(query: Query): VNode = proc isPanelOpen(q: Query): bool = q.fromUser.len == 0 and (q.filters.len > 0 or q.excludes.len > 0 or - @[q.near, q.until, q.since].anyIt(it.len > 0)) + @[q.minLikes, q.until, q.since].anyIt(it.len > 0)) proc renderSearchPanel*(query: Query): VNode = let user = query.fromUser.join(",") @@ -83,8 +83,8 @@ proc renderSearchPanel*(query: Query): VNode = span(class="search-title"): text "-" genDate("until", query.until) tdiv: - span(class="search-title"): text "Near" - genInput("near", "", query.near, "Location...", autofocus=false) + span(class="search-title"): text "Minimum likes" + genNumberInput("min_faves", "", query.minLikes, "Number...", autofocus=false) proc renderTweetSearch*(results: Timeline; prefs: Prefs; path: string; pinned=none(Tweet)): VNode = From 2b922c049af946253df17a40f78d7e6027fdea0d Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Tue, 25 Nov 2025 01:02:45 +0100 Subject: [PATCH 259/409] Embed quote tweet in RSS (#1316) Fixes #132 Closes #820 --- src/views/rss.nimf | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/views/rss.nimf b/src/views/rss.nimf index 819f99c..a6f069c 100644 --- a/src/views/rss.nimf +++ b/src/views/rss.nimf @@ -25,7 +25,7 @@ #end proc # #proc getDescription(desc: string; cfg: Config): string = -Twitter feed for: ${desc}. Generated by ${cfg.hostname} +Twitter feed for: ${desc}. Generated by ${getUrlPrefix(cfg)} #end proc # #proc getTweetsWithPinned(profile: Profile): seq[Tweets] = @@ -51,10 +51,6 @@ Twitter feed for: ${desc}. Generated by ${cfg.hostname} #let urlPrefix = getUrlPrefix(cfg) #let text = replaceUrls(tweet.text, defaultPrefs, absolute=urlPrefix) <p>${text.replace("\n", "<br>\n")}</p> -#if tweet.quote.isSome and get(tweet.quote).available: -# let quoteLink = getLink(get(tweet.quote)) -<p><a href="${urlPrefix}${quoteLink}">${cfg.hostname}${quoteLink}</a></p> -#end if #if tweet.photos.len > 0: # for photo in tweet.photos: <img src="${urlPrefix}${getPicUrl(photo)}" style="max-width:250px;" /> @@ -72,6 +68,20 @@ Twitter feed for: ${desc}. Generated by ${cfg.hostname} <img src="${urlPrefix}${getPicUrl(card.image)}" style="max-width:250px;" /> # end if #end if +#if tweet.quote.isSome and get(tweet.quote).available: +# let quoteTweet = get(tweet.quote) +# let quoteLink = urlPrefix & getLink(quoteTweet) +<hr/> +<blockquote> +<b>${quoteTweet.user.fullname} (@${quoteTweet.user.username})</b> +<p> +${renderRssTweet(quoteTweet, cfg)} +</p> +<footer> +— <cite><a href="${quoteLink}">${quoteLink}</a> +</footer> +</blockquote> +#end if #end proc # #proc renderRssTweets(tweets: seq[Tweets]; cfg: Config; userId=""): string = From 404b06b5f35ae66f7f25bcc62edd5611ff00d77a Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Tue, 25 Nov 2025 01:03:45 +0100 Subject: [PATCH 260/409] Include "Video" and link for video tweets in RSS (#1315) Fixes #836 --- src/views/rss.nimf | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/views/rss.nimf b/src/views/rss.nimf index a6f069c..23744e5 100644 --- a/src/views/rss.nimf +++ b/src/views/rss.nimf @@ -56,7 +56,10 @@ Twitter feed for: ${desc}. Generated by ${getUrlPrefix(cfg)} <img src="${urlPrefix}${getPicUrl(photo)}" style="max-width:250px;" /> # end for #elif tweet.video.isSome: -<img src="${urlPrefix}${getPicUrl(get(tweet.video).thumb)}" style="max-width:250px;" /> +<a href="${urlPrefix}${tweet.getLink}"> +<br>Video<br> + <img src="${urlPrefix}${getPicUrl(get(tweet.video).thumb)}" style="max-width:250px;" /> +</a> #elif tweet.gif.isSome: # let thumb = &"{urlPrefix}{getPicUrl(get(tweet.gif).thumb)}" # let url = &"{urlPrefix}{getPicUrl(get(tweet.gif).url)}" From b83227aaf5acbd8e8803c85dfd2b8f311a021604 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Tue, 25 Nov 2025 17:23:04 +0100 Subject: [PATCH 261/409] Implement temp fix for cookie sessions Fixes #1319 --- src/api.nim | 47 ++++++++++++++++------------ src/apiutils.nim | 2 +- src/consts.nim | 24 ++++++++------ src/experimental/parser/graphql.nim | 24 +++++++++++--- src/experimental/parser/user.nim | 4 ++- src/experimental/types/graphuser.nim | 13 ++++++-- src/parser.nim | 2 +- 7 files changed, 77 insertions(+), 39 deletions(-) diff --git a/src/api.nim b/src/api.nim index ef3a0f9..fb9c516 100644 --- a/src/api.nim +++ b/src/api.nim @@ -13,47 +13,54 @@ proc genParams(variables: string; fieldToggles = ""): seq[(string, string)] = proc mediaUrl(id: string; cursor: string): SessionAwareUrl = let - cookieVariables = userMediaVariables % [id, cursor] - oauthVariables = restIdVariables % [id, cursor] + cookieVars = userMediaVars % [id, cursor] + oauthVars = restIdVars % [id, cursor] result = SessionAwareUrl( - cookieUrl: graphUserMedia ? genParams(cookieVariables), - oauthUrl: graphUserMediaV2 ? genParams(oauthVariables) + cookieUrl: graphUserMedia ? genParams(cookieVars), + oauthUrl: graphUserMediaV2 ? genParams(oauthVars) ) proc userTweetsUrl(id: string; cursor: string): SessionAwareUrl = let - cookieVariables = userTweetsVariables % [id, cursor] - oauthVariables = restIdVariables % [id, cursor] + cookieVars = userTweetsVars % [id, cursor] + oauthVars = restIdVars % [id, cursor] result = SessionAwareUrl( - # cookieUrl: graphUserTweets ? genParams(cookieVariables, fieldToggles), - oauthUrl: graphUserTweetsV2 ? genParams(oauthVariables) + # cookieUrl: graphUserTweets ? genParams(cookieVars, userTweetsFieldToggles), + oauthUrl: graphUserTweetsV2 ? genParams(oauthVars) ) # might change this in the future pending testing result.cookieUrl = result.oauthUrl proc userTweetsAndRepliesUrl(id: string; cursor: string): SessionAwareUrl = let - cookieVariables = userTweetsAndRepliesVariables % [id, cursor] - oauthVariables = restIdVariables % [id, cursor] + cookieVars = userTweetsAndRepliesVars % [id, cursor] + oauthVars = restIdVars % [id, cursor] result = SessionAwareUrl( - cookieUrl: graphUserTweetsAndReplies ? genParams(cookieVariables, fieldToggles), - oauthUrl: graphUserTweetsAndRepliesV2 ? genParams(oauthVariables) + cookieUrl: graphUserTweetsAndReplies ? genParams(cookieVars, userTweetsFieldToggles), + oauthUrl: graphUserTweetsAndRepliesV2 ? genParams(oauthVars) ) proc tweetDetailUrl(id: string; cursor: string): SessionAwareUrl = let - cookieVariables = tweetDetailVariables % [id, cursor] - oauthVariables = tweetVariables % [id, cursor] + cookieVars = tweetDetailVars % [id, cursor] + oauthVars = tweetVars % [id, cursor] result = SessionAwareUrl( - cookieUrl: graphTweetDetail ? genParams(cookieVariables, tweetDetailFieldToggles), - oauthUrl: graphTweet ? genParams(oauthVariables) + cookieUrl: graphTweetDetail ? genParams(cookieVars, tweetDetailFieldToggles), + oauthUrl: graphTweet ? genParams(oauthVars) + ) + +proc userUrl(username: string): SessionAwareUrl = + let + cookieVars = """{"screen_name":"$1","withGrokTranslatedBio":false}""" % username + oauthVars = """{"screen_name": "$1"}""" % username + result = SessionAwareUrl( + cookieUrl: graphUser ? genParams(cookieVars, tweetDetailFieldToggles), + oauthUrl: graphUserV2 ? genParams(oauthVars) ) proc getGraphUser*(username: string): Future[User] {.async.} = if username.len == 0: return - let - url = graphUser ? genParams("""{"screen_name": "$1"}""" % username) - js = await fetchRaw(url, Api.userScreenName) + let js = await fetchRaw(userUrl(username), Api.userScreenName) result = parseGraphUser(js) proc getGraphUserById*(id: string): Future[User] {.async.} = @@ -80,7 +87,7 @@ proc getGraphListTweets*(id: string; after=""): Future[Timeline] {.async.} = if id.len == 0: return let cursor = if after.len > 0: "\"cursor\":\"$1\"," % after else: "" - url = graphListTweets ? genParams(restIdVariables % [id, cursor]) + url = graphListTweets ? genParams(restIdVars % [id, cursor]) result = parseGraphTimeline(await fetch(url, Api.listTweets), after).tweets proc getGraphListBySlug*(name, list: string): Future[List] {.async.} = diff --git a/src/apiutils.nim b/src/apiutils.nim index defffd1..94d4e8a 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -50,7 +50,7 @@ proc genHeaders*(session: Session, url: string): HttpHeaders = of SessionKind.oauth: result["authorization"] = getOauthHeader(url, session.oauthToken, session.oauthSecret) of SessionKind.cookie: - result["authorization"] = "Bearer AAAAAAAAAAAAAAAAAAAAAFQODgEAAAAAVHTp76lzh3rFzcHbmHVvQxYYpTw%3DckAlMINMjmCwxUcaXbAN4XqJVdgMJaHqNOFgPMK0zN1qLqLQCF" + result["authorization"] = "Bearer AAAAAAAAAAAAAAAAAAAAAFXzAwAAAAAAMHCxpeSDG1gLNLghVe8d74hl6k4%3DRUMF4xAQLsbeBhTSRrCiQpJtxoGWeyHrDb5te2jpGskWDFW82F" result["x-twitter-auth-type"] = "OAuth2Session" result["x-csrf-token"] = session.ct0 result["cookie"] = getCookieHeader(session.authToken, session.ct0) diff --git a/src/consts.nim b/src/consts.nim index 792a519..e55ba0e 100644 --- a/src/consts.nim +++ b/src/consts.nim @@ -7,7 +7,8 @@ const gql = parseUri("https://api.x.com") / "graphql" - graphUser* = gql / "WEoGnYB0EG1yGwamDCF6zg/UserResultByScreenNameQuery" + graphUser* = gql / "-oaLodhGbbnzJBACb1kk2Q/UserByScreenName" + graphUserV2* = gql / "WEoGnYB0EG1yGwamDCF6zg/UserResultByScreenNameQuery" graphUserById* = gql / "VN33vKXrPT7p35DgNR27aw/UserResultByIdQuery" graphUserTweetsV2* = gql / "6QdSuZ5feXxOadEdXa4XZg/UserWithProfileTweetsQueryV2" graphUserTweetsAndRepliesV2* = gql / "BDX77Xzqypdt11-mDfgdpQ/UserWithProfileTweetsAndRepliesQueryV2" @@ -97,10 +98,14 @@ const "grok_translations_community_note_auto_translation_is_enabled": false, "grok_translations_post_auto_translation_is_enabled": false, "grok_translations_community_note_translation_is_enabled": false, - "grok_translations_timeline_user_bio_auto_translation_is_enabled": false + "grok_translations_timeline_user_bio_auto_translation_is_enabled": false, + "subscriptions_feature_can_gift_premium": false, + "responsive_web_twitter_article_notes_tab_enabled": false, + "subscriptions_verification_info_is_identity_verified_enabled": false, + "hidden_profile_subscriptions_enabled": false }""".replace(" ", "").replace("\n", "") - tweetVariables* = """{ + tweetVars* = """{ "postId": "$1", $2 "includeHasBirdwatchNotes": false, @@ -110,7 +115,7 @@ const "withV2Timeline": true }""".replace(" ", "").replace("\n", "") - tweetDetailVariables* = """{ + tweetDetailVars* = """{ "focalTweetId": "$1", $2 "referrer": "profile", @@ -123,12 +128,12 @@ const "withVoice": true }""".replace(" ", "").replace("\n", "") - restIdVariables* = """{ + restIdVars* = """{ "rest_id": "$1", $2 "count": 20 }""" - userMediaVariables* = """{ + userMediaVars* = """{ "userId": "$1", $2 "count": 20, "includePromotedContent": false, @@ -137,7 +142,7 @@ const "withVoice": true }""".replace(" ", "").replace("\n", "") - userTweetsVariables* = """{ + userTweetsVars* = """{ "userId": "$1", $2 "count": 20, "includePromotedContent": false, @@ -145,7 +150,7 @@ const "withVoice": true }""".replace(" ", "").replace("\n", "") - userTweetsAndRepliesVariables* = """{ + userTweetsAndRepliesVars* = """{ "userId": "$1", $2 "count": 20, "includePromotedContent": false, @@ -153,5 +158,6 @@ const "withVoice": true }""".replace(" ", "").replace("\n", "") - fieldToggles* = """{"withArticlePlainText":false}""" + userFieldToggles = """{"withPayments":false,"withAuxiliaryUserLabels":true}""" + userTweetsFieldToggles* = """{"withArticlePlainText":false}""" tweetDetailFieldToggles* = """{"withArticleRichContentState":true,"withArticlePlainText":false,"withGrokAnalyze":false,"withDisallowedReplyControls":false}""" diff --git a/src/experimental/parser/graphql.nim b/src/experimental/parser/graphql.nim index 045a5d6..65ebc3d 100644 --- a/src/experimental/parser/graphql.nim +++ b/src/experimental/parser/graphql.nim @@ -1,6 +1,6 @@ import options, strutils import jsony -import user, ../types/[graphuser, graphlistmembers] +import user, utils, ../types/[graphuser, graphlistmembers] from ../../types import User, VerifiedType, Result, Query, QueryKind proc parseUserResult*(userResult: UserResult): User = @@ -15,22 +15,36 @@ proc parseUserResult*(userResult: UserResult): User = result.fullname = userResult.core.name result.userPic = userResult.avatar.imageUrl.replace("_normal", "") + if userResult.privacy.isSome: + result.protected = userResult.privacy.get.protected + + if userResult.location.isSome: + result.location = userResult.location.get.location + + if userResult.core.createdAt.len > 0: + result.joinDate = parseTwitterDate(userResult.core.createdAt) + if userResult.verification.isSome: let v = userResult.verification.get if v.verifiedType != VerifiedType.none: result.verifiedType = v.verifiedType - if userResult.profileBio.isSome: + if userResult.profileBio.isSome and result.bio.len == 0: result.bio = userResult.profileBio.get.description proc parseGraphUser*(json: string): User = if json.len == 0 or json[0] != '{': return - let raw = json.fromJson(GraphUser) - let userResult = raw.data.userResult.result + let + raw = json.fromJson(GraphUser) + userResult = + if raw.data.userResult.isSome: raw.data.userResult.get.result + elif raw.data.user.isSome: raw.data.user.get.result + else: UserResult() - if userResult.unavailableReason.get("") == "Suspended": + if userResult.unavailableReason.get("") == "Suspended" or + userResult.reason.get("") == "Suspended": return User(suspended: true) result = parseUserResult(userResult) diff --git a/src/experimental/parser/user.nim b/src/experimental/parser/user.nim index 498757a..8517bdc 100644 --- a/src/experimental/parser/user.nim +++ b/src/experimental/parser/user.nim @@ -58,11 +58,13 @@ proc toUser*(raw: RawUser): User = media: raw.mediaCount, verifiedType: raw.verifiedType, protected: raw.protected, - joinDate: parseTwitterDate(raw.createdAt), banner: getBanner(raw), userPic: getImageUrl(raw.profileImageUrlHttps).replace("_normal", "") ) + if raw.createdAt.len > 0: + result.joinDate = parseTwitterDate(raw.createdAt) + if raw.pinnedTweetIdsStr.len > 0: result.pinnedTweet = parseBiggestInt(raw.pinnedTweetIdsStr[0]) diff --git a/src/experimental/types/graphuser.nim b/src/experimental/types/graphuser.nim index d732b4e..62c6612 100644 --- a/src/experimental/types/graphuser.nim +++ b/src/experimental/types/graphuser.nim @@ -3,7 +3,7 @@ from ../../types import User, VerifiedType type GraphUser* = object - data*: tuple[userResult: UserData] + data*: tuple[userResult: Option[UserData], user: Option[UserData]] UserData* = object result*: UserResult @@ -22,15 +22,24 @@ type Verification* = object verifiedType*: VerifiedType + Location* = object + location*: string + + Privacy* = object + protected*: bool + UserResult* = object legacy*: User restId*: string isBlueVerified*: bool - unavailableReason*: Option[string] core*: UserCore avatar*: UserAvatar + unavailableReason*: Option[string] + reason*: Option[string] + privacy*: Option[Privacy] profileBio*: Option[UserBio] verification*: Option[Verification] + location*: Option[Location] proc enumHook*(s: string; v: var VerifiedType) = v = try: diff --git a/src/parser.nim b/src/parser.nim index 5bf2b0b..7b1e1c0 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -21,7 +21,7 @@ proc parseUser(js: JsonNode; id=""): User = tweets: js{"statuses_count"}.getInt, likes: js{"favourites_count"}.getInt, media: js{"media_count"}.getInt, - protected: js{"protected"}.getBool, + protected: js{"protected"}.getBool(js{"privacy", "protected"}.getBool), joinDate: js{"created_at"}.getTime ) From 8516ebe2b7ac772f4f33ee141f2e03fdf4d21467 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Sat, 29 Nov 2025 00:36:44 +0100 Subject: [PATCH 262/409] Fix 'key not found in object: expanded_url' error Fixes #1318 --- src/parser.nim | 4 ++-- src/parserutils.nim | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/parser.nim b/src/parser.nim index 7b1e1c0..9b4dc2f 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -184,7 +184,7 @@ proc parseMediaEntities(js: JsonNode; result: var Tweet) = # Remove media URLs from text with mediaList, js{"legacy", "entities", "media"}: for url in mediaList: - let expandedUrl = url{"expanded_url"}.getStr + let expandedUrl = url.getExpandedUrl if result.text.endsWith(expandedUrl): result.text.removeSuffix(expandedUrl) result.text = result.text.strip() @@ -267,7 +267,7 @@ proc parseCard(js: JsonNode; urls: JsonNode): Card = for u in ? urls: if u{"url"}.getStr == result.url: - result.url = u{"expanded_url"}.getStr + result.url = u.getExpandedUrl(result.url) break if kind in {videoDirectMessage, imageDirectMessage}: diff --git a/src/parserutils.nim b/src/parserutils.nim index 72c50e1..b6ccd52 100644 --- a/src/parserutils.nim +++ b/src/parserutils.nim @@ -112,6 +112,9 @@ proc getImageStr*(js: JsonNode): string = template getImageVal*(js: JsonNode): string = js{"image_value", "url"}.getImageStr +template getExpandedUrl*(js: JsonNode; fallback=""): string = + js{"expanded_url"}.getStr(js{"url"}.getStr(fallback)) + proc getCardUrl*(js: JsonNode; kind: CardKind): string = result = js{"website_url"}.getStrVal if kind == promoVideoConvo: @@ -177,7 +180,7 @@ proc extractSlice(js: JsonNode): Slice[int] = proc extractUrls(result: var seq[ReplaceSlice]; js: JsonNode; textLen: int; hideTwitter = false) = let - url = js["expanded_url"].getStr + url = js.getExpandedUrl slice = js.extractSlice if hideTwitter and slice.b.succ >= textLen and url.isTwitterUrl: @@ -238,7 +241,7 @@ proc expandUserEntities*(user: var User; js: JsonNode) = ent = ? js{"entities"} with urls, ent{"url", "urls"}: - user.website = urls[0]{"expanded_url"}.getStr + user.website = urls[0].getExpandedUrl var replacements = newSeq[ReplaceSlice]() @@ -268,7 +271,7 @@ proc expandTextEntities(tweet: Tweet; entities: JsonNode; text: string; textSlic replacements.extractUrls(u, textSlice.b, hideTwitter = hasRedundantLink) if hasCard and u{"url"}.getStr == get(tweet.card).url: - get(tweet.card).url = u{"expanded_url"}.getStr + get(tweet.card).url = u.getExpandedUrl with media, entities{"media"}: for m in media: From dae68b4f13c773ca0394e30dc87d7b001a74431f Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Sat, 29 Nov 2025 01:05:57 +0100 Subject: [PATCH 263/409] Ignore null errors, they're internal API errors --- src/apiutils.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apiutils.nim b/src/apiutils.nim index 94d4e8a..f48cda2 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -8,7 +8,7 @@ const rlRemaining = "x-rate-limit-remaining" rlReset = "x-rate-limit-reset" rlLimit = "x-rate-limit-limit" - errorsToSkip = {doesntExist, tweetNotFound, timeout, unauthorized, badRequest} + errorsToSkip = {null, doesntExist, tweetNotFound, timeout, unauthorized, badRequest} var pool: HttpPool From 31d210ca47f09a756d761699bd41a35cabb4c3ab Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Sat, 29 Nov 2025 01:13:08 +0100 Subject: [PATCH 264/409] Add experimental x-client-transaction-id support (#1324) * Add experimental x-client-transaction-id support * Remove broken test --- nitter.example.conf | 1 + src/api.nim | 131 ++++++++++++++++---------------- src/apiutils.nim | 74 ++++++++++-------- src/auth.nim | 32 +++++--- src/config.nim | 3 +- src/consts.nim | 40 +++++----- src/experimental/parser/tid.nim | 8 ++ src/experimental/types/tid.nim | 4 + src/nitter.nim | 3 +- src/tid.nim | 62 +++++++++++++++ src/types.nim | 29 +++---- tests/test_card.py | 7 +- tools/create_session_browser.py | 2 +- 13 files changed, 239 insertions(+), 157 deletions(-) create mode 100644 src/experimental/parser/tid.nim create mode 100644 src/experimental/types/tid.nim create mode 100644 src/tid.nim diff --git a/nitter.example.conf b/nitter.example.conf index bddb9a4..dfdaf50 100644 --- a/nitter.example.conf +++ b/nitter.example.conf @@ -26,6 +26,7 @@ enableRSS = true # set this to false to disable RSS feeds enableDebug = false # enable request logs and debug endpoints (/.sessions) proxy = "" # http/https url, SOCKS proxies are not supported proxyAuth = "" +disableTid = false # enable this if cookie-based auth is failing # Change default preferences here, see src/prefs_impl.nim for a complete list [Preferences] diff --git a/src/api.nim b/src/api.nim index fb9c516..e97b4e0 100644 --- a/src/api.nim +++ b/src/api.nim @@ -1,5 +1,5 @@ # SPDX-License-Identifier: AGPL-3.0-only -import asyncdispatch, httpclient, uri, strutils, sequtils, sugar, tables +import asyncdispatch, httpclient, strutils, sequtils, sugar import packedjson import types, query, formatters, consts, apiutils, parser import experimental/parser as newParser @@ -11,95 +11,91 @@ proc genParams(variables: string; fieldToggles = ""): seq[(string, string)] = if fieldToggles.len > 0: result.add ("fieldToggles", fieldToggles) -proc mediaUrl(id: string; cursor: string): SessionAwareUrl = - let - cookieVars = userMediaVars % [id, cursor] - oauthVars = restIdVars % [id, cursor] - result = SessionAwareUrl( - cookieUrl: graphUserMedia ? genParams(cookieVars), - oauthUrl: graphUserMediaV2 ? genParams(oauthVars) +proc apiUrl(endpoint, variables: string; fieldToggles = ""): ApiUrl = + return ApiUrl(endpoint: endpoint, params: genParams(variables, fieldToggles)) + +proc apiReq(endpoint, variables: string; fieldToggles = ""): ApiReq = + let url = apiUrl(endpoint, variables, fieldToggles) + return ApiReq(cookie: url, oauth: url) + +proc mediaUrl(id: string; cursor: string): ApiReq = + result = ApiReq( + cookie: apiUrl(graphUserMedia, userMediaVars % [id, cursor]), + oauth: apiUrl(graphUserMediaV2, restIdVars % [id, cursor]) ) -proc userTweetsUrl(id: string; cursor: string): SessionAwareUrl = - let - cookieVars = userTweetsVars % [id, cursor] - oauthVars = restIdVars % [id, cursor] - result = SessionAwareUrl( - # cookieUrl: graphUserTweets ? genParams(cookieVars, userTweetsFieldToggles), - oauthUrl: graphUserTweetsV2 ? genParams(oauthVars) +proc userTweetsUrl(id: string; cursor: string): ApiReq = + result = ApiReq( + # cookie: apiUrl(graphUserTweets, userTweetsVars % [id, cursor], userTweetsFieldToggles), + oauth: apiUrl(graphUserTweetsV2, restIdVars % [id, cursor]) ) # might change this in the future pending testing - result.cookieUrl = result.oauthUrl + result.cookie = result.oauth -proc userTweetsAndRepliesUrl(id: string; cursor: string): SessionAwareUrl = - let - cookieVars = userTweetsAndRepliesVars % [id, cursor] - oauthVars = restIdVars % [id, cursor] - result = SessionAwareUrl( - cookieUrl: graphUserTweetsAndReplies ? genParams(cookieVars, userTweetsFieldToggles), - oauthUrl: graphUserTweetsAndRepliesV2 ? genParams(oauthVars) +proc userTweetsAndRepliesUrl(id: string; cursor: string): ApiReq = + let cookieVars = userTweetsAndRepliesVars % [id, cursor] + result = ApiReq( + cookie: apiUrl(graphUserTweetsAndReplies, cookieVars, userTweetsFieldToggles), + oauth: apiUrl(graphUserTweetsAndRepliesV2, restIdVars % [id, cursor]) ) -proc tweetDetailUrl(id: string; cursor: string): SessionAwareUrl = - let - cookieVars = tweetDetailVars % [id, cursor] - oauthVars = tweetVars % [id, cursor] - result = SessionAwareUrl( - cookieUrl: graphTweetDetail ? genParams(cookieVars, tweetDetailFieldToggles), - oauthUrl: graphTweet ? genParams(oauthVars) +proc tweetDetailUrl(id: string; cursor: string): ApiReq = + let cookieVars = tweetDetailVars % [id, cursor] + result = ApiReq( + cookie: apiUrl(graphTweetDetail, cookieVars, tweetDetailFieldToggles), + oauth: apiUrl(graphTweet, tweetVars % [id, cursor]) ) -proc userUrl(username: string): SessionAwareUrl = - let - cookieVars = """{"screen_name":"$1","withGrokTranslatedBio":false}""" % username - oauthVars = """{"screen_name": "$1"}""" % username - result = SessionAwareUrl( - cookieUrl: graphUser ? genParams(cookieVars, tweetDetailFieldToggles), - oauthUrl: graphUserV2 ? genParams(oauthVars) +proc userUrl(username: string): ApiReq = + let cookieVars = """{"screen_name":"$1","withGrokTranslatedBio":false}""" % username + result = ApiReq( + cookie: apiUrl(graphUser, cookieVars, tweetDetailFieldToggles), + oauth: apiUrl(graphUserV2, """{"screen_name": "$1"}""" % username) ) proc getGraphUser*(username: string): Future[User] {.async.} = if username.len == 0: return - let js = await fetchRaw(userUrl(username), Api.userScreenName) + let js = await fetchRaw(userUrl(username)) result = parseGraphUser(js) proc getGraphUserById*(id: string): Future[User] {.async.} = if id.len == 0 or id.any(c => not c.isDigit): return let - url = graphUserById ? genParams("""{"rest_id": "$1"}""" % id) - js = await fetchRaw(url, Api.userRestId) + url = apiReq(graphUserById, """{"rest_id": "$1"}""" % id) + js = await fetchRaw(url) result = parseGraphUser(js) proc getGraphUserTweets*(id: string; kind: TimelineKind; after=""): Future[Profile] {.async.} = if id.len == 0: return let cursor = if after.len > 0: "\"cursor\":\"$1\"," % after else: "" - js = case kind - of TimelineKind.tweets: - await fetch(userTweetsUrl(id, cursor), Api.userTweets) - of TimelineKind.replies: - await fetch(userTweetsAndRepliesUrl(id, cursor), Api.userTweetsAndReplies) - of TimelineKind.media: - await fetch(mediaUrl(id, cursor), Api.userMedia) + url = case kind + of TimelineKind.tweets: userTweetsUrl(id, cursor) + of TimelineKind.replies: userTweetsAndRepliesUrl(id, cursor) + of TimelineKind.media: mediaUrl(id, cursor) + js = await fetch(url) result = parseGraphTimeline(js, after) proc getGraphListTweets*(id: string; after=""): Future[Timeline] {.async.} = if id.len == 0: return let cursor = if after.len > 0: "\"cursor\":\"$1\"," % after else: "" - url = graphListTweets ? genParams(restIdVars % [id, cursor]) - result = parseGraphTimeline(await fetch(url, Api.listTweets), after).tweets + url = apiReq(graphListTweets, restIdVars % [id, cursor]) + js = await fetch(url) + result = parseGraphTimeline(js, after).tweets proc getGraphListBySlug*(name, list: string): Future[List] {.async.} = let variables = %*{"screenName": name, "listSlug": list} - url = graphListBySlug ? genParams($variables) - result = parseGraphList(await fetch(url, Api.listBySlug)) + url = apiReq(graphListBySlug, $variables) + js = await fetch(url) + result = parseGraphList(js) proc getGraphList*(id: string): Future[List] {.async.} = - let - url = graphListById ? genParams("""{"listId": "$1"}""" % id) - result = parseGraphList(await fetch(url, Api.list)) + let + url = apiReq(graphListById, """{"listId": "$1"}""" % id) + js = await fetch(url) + result = parseGraphList(js) proc getGraphListMembers*(list: List; after=""): Future[Result[User]] {.async.} = if list.id.len == 0: return @@ -113,22 +109,23 @@ proc getGraphListMembers*(list: List; after=""): Future[Result[User]] {.async.} } if after.len > 0: variables["cursor"] = % after - let url = graphListMembers ? genParams($variables) - result = parseGraphListMembers(await fetchRaw(url, Api.listMembers), after) + let + url = apiReq(graphListMembers, $variables) + js = await fetchRaw(url) + result = parseGraphListMembers(js, after) proc getGraphTweetResult*(id: string): Future[Tweet] {.async.} = if id.len == 0: return let - variables = """{"rest_id": "$1"}""" % id - params = {"variables": variables, "features": gqlFeatures} - js = await fetch(graphTweetResult ? params, Api.tweetResult) + url = apiReq(graphTweetResult, """{"rest_id": "$1"}""" % id) + js = await fetch(url) result = parseGraphTweetResult(js) proc getGraphTweet(id: string; after=""): Future[Conversation] {.async.} = if id.len == 0: return let cursor = if after.len > 0: "\"cursor\":\"$1\"," % after else: "" - js = await fetch(tweetDetailUrl(id, cursor), Api.tweetDetail) + js = await fetch(tweetDetailUrl(id, cursor)) result = parseGraphConversation(js, id) proc getReplies*(id, after: string): Future[Result[Chain]] {.async.} = @@ -157,8 +154,10 @@ proc getGraphTweetSearch*(query: Query; after=""): Future[Timeline] {.async.} = } if after.len > 0: variables["cursor"] = % after - let url = graphSearchTimeline ? genParams($variables) - result = parseGraphSearch[Tweets](await fetch(url, Api.search), after) + let + url = apiReq(graphSearchTimeline, $variables) + js = await fetch(url) + result = parseGraphSearch[Tweets](js, after) result.query = query proc getGraphUserSearch*(query: Query; after=""): Future[Result[User]] {.async.} = @@ -179,13 +178,15 @@ proc getGraphUserSearch*(query: Query; after=""): Future[Result[User]] {.async.} variables["cursor"] = % after result.beginning = false - let url = graphSearchTimeline ? genParams($variables) - result = parseGraphSearch[User](await fetch(url, Api.search), after) + let + url = apiReq(graphSearchTimeline, $variables) + js = await fetch(url) + result = parseGraphSearch[User](js, after) result.query = query proc getPhotoRail*(id: string): Future[PhotoRail] {.async.} = if id.len == 0: return - let js = await fetch(mediaUrl(id, ""), Api.userMedia) + let js = await fetch(mediaUrl(id, "")) result = parseGraphPhotoRail(js) proc resolve*(url: string; prefs: Prefs): Future[string] {.async.} = diff --git a/src/apiutils.nim b/src/apiutils.nim index f48cda2..b288141 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only import httpclient, asyncdispatch, options, strutils, uri, times, math, tables import jsony, packedjson, zippy, oauth1 -import types, auth, consts, parserutils, http_pool +import types, auth, consts, parserutils, http_pool, tid import experimental/types/common const @@ -10,7 +10,21 @@ const rlLimit = "x-rate-limit-limit" errorsToSkip = {null, doesntExist, tweetNotFound, timeout, unauthorized, badRequest} -var pool: HttpPool +var + pool: HttpPool + disableTid: bool + +proc setDisableTid*(disable: bool) = + disableTid = disable + +proc toUrl(req: ApiReq; sessionKind: SessionKind): Uri = + case sessionKind + of oauth: + let o = req.oauth + parseUri("https://api.x.com/graphql") / o.endpoint ? o.params + of cookie: + let c = req.cookie + parseUri("https://x.com/i/api/graphql") / c.endpoint ? c.params proc getOauthHeader(url, oauthToken, oauthTokenSecret: string): string = let @@ -32,15 +46,15 @@ proc getOauthHeader(url, oauthToken, oauthTokenSecret: string): string = proc getCookieHeader(authToken, ct0: string): string = "auth_token=" & authToken & "; ct0=" & ct0 -proc genHeaders*(session: Session, url: string): HttpHeaders = +proc genHeaders*(session: Session, url: Uri): Future[HttpHeaders] {.async.} = result = newHttpHeaders({ "connection": "keep-alive", "content-type": "application/json", "x-twitter-active-user": "yes", "x-twitter-client-language": "en", - "authority": "api.x.com", + "origin": "https://x.com", "accept-encoding": "gzip", - "accept-language": "en-US,en;q=0.9", + "accept-language": "en-US,en;q=0.5", "accept": "*/*", "DNT": "1", "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36" @@ -48,15 +62,20 @@ proc genHeaders*(session: Session, url: string): HttpHeaders = case session.kind of SessionKind.oauth: - result["authorization"] = getOauthHeader(url, session.oauthToken, session.oauthSecret) + result["authority"] = "api.x.com" + result["authorization"] = getOauthHeader($url, session.oauthToken, session.oauthSecret) of SessionKind.cookie: - result["authorization"] = "Bearer AAAAAAAAAAAAAAAAAAAAAFXzAwAAAAAAMHCxpeSDG1gLNLghVe8d74hl6k4%3DRUMF4xAQLsbeBhTSRrCiQpJtxoGWeyHrDb5te2jpGskWDFW82F" result["x-twitter-auth-type"] = "OAuth2Session" result["x-csrf-token"] = session.ct0 result["cookie"] = getCookieHeader(session.authToken, session.ct0) + if disableTid: + result["authorization"] = bearerToken2 + else: + result["authorization"] = bearerToken + result["x-client-transaction-id"] = await genTid(url.path) -proc getAndValidateSession*(api: Api): Future[Session] {.async.} = - result = await getSession(api) +proc getAndValidateSession*(req: ApiReq): Future[Session] {.async.} = + result = await getSession(req) case result.kind of SessionKind.oauth: if result.oauthToken.len == 0: @@ -73,7 +92,7 @@ template fetchImpl(result, fetchBody) {.dirty.} = try: var resp: AsyncResponse - pool.use(genHeaders(session, $url)): + pool.use(await genHeaders(session, url)): template getContent = resp = await c.get($url) result = await resp.body @@ -89,7 +108,7 @@ template fetchImpl(result, fetchBody) {.dirty.} = remaining = parseInt(resp.headers[rlRemaining]) reset = parseInt(resp.headers[rlReset]) limit = parseInt(resp.headers[rlLimit]) - session.setRateLimit(api, remaining, reset, limit) + session.setRateLimit(req, remaining, reset, limit) if result.len > 0: if resp.headers.getOrDefault("content-encoding") == "gzip": @@ -98,24 +117,22 @@ template fetchImpl(result, fetchBody) {.dirty.} = if result.startsWith("{\"errors"): let errors = result.fromJson(Errors) if errors notin errorsToSkip: - echo "Fetch error, API: ", api, ", errors: ", errors + echo "Fetch error, API: ", url.path, ", errors: ", errors if errors in {expiredToken, badToken, locked}: invalidate(session) raise rateLimitError() elif errors in {rateLimited}: # rate limit hit, resets after 24 hours - setLimited(session, api) + setLimited(session, req) raise rateLimitError() elif result.startsWith("429 Too Many Requests"): - echo "[sessions] 429 error, API: ", api, ", session: ", session.pretty - session.apis[api].remaining = 0 - # rate limit hit, resets after the 15 minute window + echo "[sessions] 429 error, API: ", url.path, ", session: ", session.pretty raise rateLimitError() fetchBody if resp.status == $Http400: - echo "ERROR 400, ", api, ": ", result + echo "ERROR 400, ", url.path, ": ", result raise newException(InternalError, $url) except InternalError as e: raise e @@ -134,19 +151,16 @@ template retry(bod) = try: bod except RateLimitError: - echo "[sessions] Rate limited, retrying ", api, " request..." + echo "[sessions] Rate limited, retrying ", req.cookie.endpoint, " request..." bod -proc fetch*(url: Uri | SessionAwareUrl; api: Api): Future[JsonNode] {.async.} = +proc fetch*(req: ApiReq): Future[JsonNode] {.async.} = retry: var body: string - session = await getAndValidateSession(api) + session = await getAndValidateSession(req) - when url is SessionAwareUrl: - let url = case session.kind - of SessionKind.oauth: url.oauthUrl - of SessionKind.cookie: url.cookieUrl + let url = req.toUrl(session.kind) fetchImpl body: if body.startsWith('{') or body.startsWith('['): @@ -157,19 +171,15 @@ proc fetch*(url: Uri | SessionAwareUrl; api: Api): Future[JsonNode] {.async.} = let error = result.getError if error != null and error notin errorsToSkip: - echo "Fetch error, API: ", api, ", error: ", error + echo "Fetch error, API: ", url.path, ", error: ", error if error in {expiredToken, badToken, locked}: invalidate(session) raise rateLimitError() -proc fetchRaw*(url: Uri | SessionAwareUrl; api: Api): Future[string] {.async.} = +proc fetchRaw*(req: ApiReq): Future[string] {.async.} = retry: - var session = await getAndValidateSession(api) - - when url is SessionAwareUrl: - let url = case session.kind - of SessionKind.oauth: url.oauthUrl - of SessionKind.cookie: url.cookieUrl + var session = await getAndValidateSession(req) + let url = req.toUrl(session.kind) fetchImpl result: if not (result.startsWith('{') or result.startsWith('[')): diff --git a/src/auth.nim b/src/auth.nim index 734b43e..5d7ef0e 100644 --- a/src/auth.nim +++ b/src/auth.nim @@ -1,6 +1,6 @@ #SPDX-License-Identifier: AGPL-3.0-only -import std/[asyncdispatch, times, json, random, sequtils, strutils, tables, packedsets, os] -import types +import std/[asyncdispatch, times, json, random, strutils, tables, packedsets, os] +import types, consts import experimental/parser/session # max requests at a time per session to avoid race conditions @@ -15,6 +15,11 @@ var template log(str: varargs[string, `$`]) = echo "[sessions] ", str.join("") +proc endpoint(req: ApiReq; session: Session): string = + case session.kind + of oauth: req.oauth.endpoint + of cookie: req.cookie.endpoint + proc pretty*(session: Session): string = if session.isNil: return "<null>" @@ -122,11 +127,12 @@ proc rateLimitError*(): ref RateLimitError = proc noSessionsError*(): ref NoSessionsError = newException(NoSessionsError, "no sessions available") -proc isLimited(session: Session; api: Api): bool = +proc isLimited(session: Session; req: ApiReq): bool = if session.isNil: return true - if session.limited and api != Api.userTweets: + let api = req.endpoint(session) + if session.limited and api != graphUserTweetsV2: if (epochTime().int - session.limitedAt) > hourInSeconds: session.limited = false log "resetting limit: ", session.pretty @@ -140,8 +146,8 @@ proc isLimited(session: Session; api: Api): bool = else: return false -proc isReady(session: Session; api: Api): bool = - not (session.isNil or session.pending > maxConcurrentReqs or session.isLimited(api)) +proc isReady(session: Session; req: ApiReq): bool = + not (session.isNil or session.pending > maxConcurrentReqs or session.isLimited(req)) proc invalidate*(session: var Session) = if session.isNil: return @@ -156,24 +162,26 @@ proc release*(session: Session) = if session.isNil: return dec session.pending -proc getSession*(api: Api): Future[Session] {.async.} = +proc getSession*(req: ApiReq): Future[Session] {.async.} = for i in 0 ..< sessionPool.len: - if result.isReady(api): break + if result.isReady(req): break result = sessionPool.sample() - if not result.isNil and result.isReady(api): + if not result.isNil and result.isReady(req): inc result.pending else: - log "no sessions available for API: ", api + log "no sessions available for API: ", req.cookie.endpoint raise noSessionsError() -proc setLimited*(session: Session; api: Api) = +proc setLimited*(session: Session; req: ApiReq) = + let api = req.endpoint(session) session.limited = true session.limitedAt = epochTime().int log "rate limited by api: ", api, ", reqs left: ", session.apis[api].remaining, ", ", session.pretty -proc setRateLimit*(session: Session; api: Api; remaining, reset, limit: int) = +proc setRateLimit*(session: Session; req: ApiReq; remaining, reset, limit: int) = # avoid undefined behavior in race conditions + let api = req.endpoint(session) if api in session.apis: let rateLimit = session.apis[api] if rateLimit.reset >= reset and rateLimit.remaining < remaining: diff --git a/src/config.nim b/src/config.nim index 1b05ffe..571508b 100644 --- a/src/config.nim +++ b/src/config.nim @@ -40,7 +40,8 @@ proc getConfig*(path: string): (Config, parseCfg.Config) = enableRss: cfg.get("Config", "enableRSS", true), enableDebug: cfg.get("Config", "enableDebug", false), proxy: cfg.get("Config", "proxy", ""), - proxyAuth: cfg.get("Config", "proxyAuth", "") + proxyAuth: cfg.get("Config", "proxyAuth", ""), + disableTid: cfg.get("Config", "disableTid", false) ) return (conf, cfg) diff --git a/src/consts.nim b/src/consts.nim index e55ba0e..6456efc 100644 --- a/src/consts.nim +++ b/src/consts.nim @@ -1,29 +1,29 @@ # SPDX-License-Identifier: AGPL-3.0-only -import uri, strutils +import strutils const consumerKey* = "3nVuSoBZnx6U4vzUxf5w" consumerSecret* = "Bcs59EFbbsdF6Sl9Ng71smgStWEGwXXKSjYvPVt7qys" + bearerToken* = "Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA" + bearerToken2* = "Bearer AAAAAAAAAAAAAAAAAAAAAFXzAwAAAAAAMHCxpeSDG1gLNLghVe8d74hl6k4%3DRUMF4xAQLsbeBhTSRrCiQpJtxoGWeyHrDb5te2jpGskWDFW82F" - gql = parseUri("https://api.x.com") / "graphql" - - graphUser* = gql / "-oaLodhGbbnzJBACb1kk2Q/UserByScreenName" - graphUserV2* = gql / "WEoGnYB0EG1yGwamDCF6zg/UserResultByScreenNameQuery" - graphUserById* = gql / "VN33vKXrPT7p35DgNR27aw/UserResultByIdQuery" - graphUserTweetsV2* = gql / "6QdSuZ5feXxOadEdXa4XZg/UserWithProfileTweetsQueryV2" - graphUserTweetsAndRepliesV2* = gql / "BDX77Xzqypdt11-mDfgdpQ/UserWithProfileTweetsAndRepliesQueryV2" - graphUserTweets* = gql / "oRJs8SLCRNRbQzuZG93_oA/UserTweets" - graphUserTweetsAndReplies* = gql / "kkaJ0Mf34PZVarrxzLihjg/UserTweetsAndReplies" - graphUserMedia* = gql / "36oKqyQ7E_9CmtONGjJRsA/UserMedia" - graphUserMediaV2* = gql / "bp0e_WdXqgNBIwlLukzyYA/MediaTimelineV2" - graphTweet* = gql / "Y4Erk_-0hObvLpz0Iw3bzA/ConversationTimeline" - graphTweetDetail* = gql / "YVyS4SfwYW7Uw5qwy0mQCA/TweetDetail" - graphTweetResult* = gql / "nzme9KiYhfIOrrLrPP_XeQ/TweetResultByIdQuery" - graphSearchTimeline* = gql / "bshMIjqDk8LTXTq4w91WKw/SearchTimeline" - graphListById* = gql / "cIUpT1UjuGgl_oWiY7Snhg/ListByRestId" - graphListBySlug* = gql / "K6wihoTiTrzNzSF8y1aeKQ/ListBySlug" - graphListMembers* = gql / "fuVHh5-gFn8zDBBxb8wOMA/ListMembers" - graphListTweets* = gql / "VQf8_XQynI3WzH6xopOMMQ/ListTimeline" + graphUser* = "-oaLodhGbbnzJBACb1kk2Q/UserByScreenName" + graphUserV2* = "WEoGnYB0EG1yGwamDCF6zg/UserResultByScreenNameQuery" + graphUserById* = "VN33vKXrPT7p35DgNR27aw/UserResultByIdQuery" + graphUserTweetsV2* = "6QdSuZ5feXxOadEdXa4XZg/UserWithProfileTweetsQueryV2" + graphUserTweetsAndRepliesV2* = "BDX77Xzqypdt11-mDfgdpQ/UserWithProfileTweetsAndRepliesQueryV2" + graphUserTweets* = "oRJs8SLCRNRbQzuZG93_oA/UserTweets" + graphUserTweetsAndReplies* = "kkaJ0Mf34PZVarrxzLihjg/UserTweetsAndReplies" + graphUserMedia* = "36oKqyQ7E_9CmtONGjJRsA/UserMedia" + graphUserMediaV2* = "bp0e_WdXqgNBIwlLukzyYA/MediaTimelineV2" + graphTweet* = "Y4Erk_-0hObvLpz0Iw3bzA/ConversationTimeline" + graphTweetDetail* = "YVyS4SfwYW7Uw5qwy0mQCA/TweetDetail" + graphTweetResult* = "nzme9KiYhfIOrrLrPP_XeQ/TweetResultByIdQuery" + graphSearchTimeline* = "bshMIjqDk8LTXTq4w91WKw/SearchTimeline" + graphListById* = "cIUpT1UjuGgl_oWiY7Snhg/ListByRestId" + graphListBySlug* = "K6wihoTiTrzNzSF8y1aeKQ/ListBySlug" + graphListMembers* = "fuVHh5-gFn8zDBBxb8wOMA/ListMembers" + graphListTweets* = "VQf8_XQynI3WzH6xopOMMQ/ListTimeline" gqlFeatures* = """{ "android_ad_formats_media_component_render_overlay_enabled": false, diff --git a/src/experimental/parser/tid.nim b/src/experimental/parser/tid.nim new file mode 100644 index 0000000..28fccea --- /dev/null +++ b/src/experimental/parser/tid.nim @@ -0,0 +1,8 @@ +import jsony +import ../types/tid +export TidPair + +proc parseTidPairs*(raw: string): seq[TidPair] = + result = raw.fromJson(seq[TidPair]) + if result.len == 0: + raise newException(ValueError, "Parsing pairs failed: " & raw) diff --git a/src/experimental/types/tid.nim b/src/experimental/types/tid.nim new file mode 100644 index 0000000..ad036d9 --- /dev/null +++ b/src/experimental/types/tid.nim @@ -0,0 +1,4 @@ +type + TidPair* = object + animationKey*: string + verification*: string diff --git a/src/nitter.nim b/src/nitter.nim index f81dc1c..e6d66ab 100644 --- a/src/nitter.nim +++ b/src/nitter.nim @@ -6,7 +6,7 @@ from os import getEnv import jester -import types, config, prefs, formatters, redis_cache, http_pool, auth +import types, config, prefs, formatters, redis_cache, http_pool, auth, apiutils import views/[general, about] import routes/[ preferences, timeline, status, media, search, rss, list, debug, @@ -37,6 +37,7 @@ setHmacKey(cfg.hmacKey) setProxyEncoding(cfg.base64Media) setMaxHttpConns(cfg.httpMaxConns) setHttpProxy(cfg.proxy, cfg.proxyAuth) +setDisableTid(cfg.disableTid) initAboutPage(cfg.staticDir) waitFor initRedisPool(cfg) diff --git a/src/tid.nim b/src/tid.nim new file mode 100644 index 0000000..7b453fb --- /dev/null +++ b/src/tid.nim @@ -0,0 +1,62 @@ +import std/[asyncdispatch, base64, httpclient, random, strutils, sequtils, times] +import nimcrypto +import experimental/parser/tid + +randomize() + +const defaultKeyword = "obfiowerehiring"; +const pairsUrl = + "https://raw.githubusercontent.com/fa0311/x-client-transaction-id-pair-dict/refs/heads/main/pair.json"; + +var + cachedPairs: seq[TidPair] = @[] + lastCached = 0 + # refresh every hour + ttlSec = 60 * 60 + +proc getPair(): Future[TidPair] {.async.} = + if cachedPairs.len == 0 or int(epochTime()) - lastCached > ttlSec: + lastCached = int(epochTime()) + + let client = newAsyncHttpClient() + defer: client.close() + + let resp = await client.get(pairsUrl) + if resp.status == $Http200: + cachedPairs = parseTidPairs(await resp.body) + + return sample(cachedPairs) + +proc encodeSha256(text: string): array[32, byte] = + let + data = cast[ptr byte](addr text[0]) + dataLen = uint(len(text)) + digest = sha256.digest(data, dataLen) + return digest.data + +proc encodeBase64[T](data: T): string = + return encode(data).replace("=", "") + +proc decodeBase64(data: string): seq[byte] = + return cast[seq[byte]](decode(data)) + +proc genTid*(path: string): Future[string] {.async.} = + let + pair = await getPair() + + timeNow = int(epochTime() - 1682924400) + timeNowBytes = @[ + byte(timeNow and 0xff), + byte((timeNow shr 8) and 0xff), + byte((timeNow shr 16) and 0xff), + byte((timeNow shr 24) and 0xff) + ] + + data = "GET!" & path & "!" & $timeNow & defaultKeyword & pair.animationKey + hashBytes = encodeSha256(data) + keyBytes = decodeBase64(pair.verification) + bytesArr = keyBytes & timeNowBytes & hashBytes[0 ..< 16] & @[3'u8] + randomNum = byte(rand(256)) + tid = @[randomNum] & bytesArr.mapIt(it xor randomNum) + + return encodeBase64(tid) diff --git a/src/types.nim b/src/types.nim index 20a49c9..815e223 100644 --- a/src/types.nim +++ b/src/types.nim @@ -1,5 +1,5 @@ # SPDX-License-Identifier: AGPL-3.0-only -import times, sequtils, options, tables, uri +import times, sequtils, options, tables import prefs_impl genPrefsType() @@ -13,19 +13,13 @@ type TimelineKind* {.pure.} = enum tweets, replies, media - Api* {.pure.} = enum - tweetDetail - tweetResult - search - list - listBySlug - listMembers - listTweets - userRestId - userScreenName - userTweets - userTweetsAndReplies - userMedia + ApiUrl* = object + endpoint*: string + params*: seq[(string, string)] + + ApiReq* = object + oauth*: ApiUrl + cookie*: ApiUrl RateLimit* = object limit*: int @@ -42,7 +36,7 @@ type pending*: int limited*: bool limitedAt*: int - apis*: Table[Api, RateLimit] + apis*: Table[string, RateLimit] case kind*: SessionKind of oauth: oauthToken*: string @@ -51,10 +45,6 @@ type authToken*: string ct0*: string - SessionAwareUrl* = object - oauthUrl*: Uri - cookieUrl*: Uri - Error* = enum null = 0 noUserMatches = 17 @@ -285,6 +275,7 @@ type enableDebug*: bool proxy*: string proxyAuth*: string + disableTid*: bool rssCacheTime*: int listCacheTime*: int diff --git a/tests/test_card.py b/tests/test_card.py index 504c079..129e65a 100644 --- a/tests/test_card.py +++ b/tests/test_card.py @@ -11,12 +11,7 @@ card = [ ['voidtarget/status/1094632512926605312', 'Basic OBS Studio plugin, written in nim, supporting C++ (C fine too)', 'Basic OBS Studio plugin, written in nim, supporting C++ (C fine too) - obsplugin.nim', - 'gist.github.com', True], - - ['nim_lang/status/1082989146040340480', - 'Nim in 2018: A short recap', - 'There were several big news in the Nim world in 2018 – two new major releases, partnership with Status, and much more. But let us go chronologically.', - 'nim-lang.org', True] + 'gist.github.com', True] ] no_thumb = [ diff --git a/tools/create_session_browser.py b/tools/create_session_browser.py index 40e3dcd..3a05cb1 100644 --- a/tools/create_session_browser.py +++ b/tools/create_session_browser.py @@ -94,7 +94,7 @@ async def login_and_get_cookies(username, password, totp_seed=None, headless=Fal async def main(): if len(sys.argv) < 3: - print('Usage: python3 twitter-auth.py username password [totp_seed] [--append sessions.jsonl] [--headless]') + print('Usage: python3 create_session_browser.py username password [totp_seed] [--append file.jsonl] [--headless]') sys.exit(1) username = sys.argv[1] From 7a08a9e1321a511a35bee7ca736db035f9fce52a Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Sat, 29 Nov 2025 03:36:21 +0100 Subject: [PATCH 265/409] Format css --- src/sass/general.scss | 56 +++--- src/sass/include/_variables.scss | 49 +++-- src/sass/index.scss | 242 ++++++++++++------------ src/sass/inputs.scss | 258 ++++++++++++------------- src/sass/navbar.scss | 121 ++++++------ src/sass/search.scss | 170 ++++++++--------- src/sass/timeline.scss | 208 ++++++++++----------- src/sass/tweet/_base.scss | 310 ++++++++++++++++--------------- src/sass/tweet/embed.scss | 26 +-- src/sass/tweet/media.scss | 146 +++++++-------- src/sass/tweet/poll.scss | 48 ++--- src/sass/tweet/quote.scss | 149 +++++++-------- src/sass/tweet/thread.scss | 195 +++++++++---------- src/sass/tweet/video.scss | 97 +++++----- 14 files changed, 1041 insertions(+), 1034 deletions(-) diff --git a/src/sass/general.scss b/src/sass/general.scss index 9feb3d3..ce97564 100644 --- a/src/sass/general.scss +++ b/src/sass/general.scss @@ -1,39 +1,39 @@ -@import '_variables'; -@import '_mixins'; +@import "_variables"; +@import "_mixins"; .panel-container { - margin: auto; - font-size: 130%; + margin: auto; + font-size: 130%; } .error-panel { - @include center-panel(var(--error_red)); - text-align: center; + @include center-panel(var(--error_red)); + text-align: center; } .search-bar > form { - @include center-panel(var(--darkest_grey)); + @include center-panel(var(--darkest_grey)); - button { - background: var(--bg_elements); - color: var(--fg_color); - border: 0; - border-radius: 3px; - cursor: pointer; - font-weight: bold; - width: 30px; - height: 30px; - } + button { + background: var(--bg_elements); + color: var(--fg_color); + border: 0; + border-radius: 3px; + cursor: pointer; + font-weight: bold; + width: 30px; + height: 30px; + } - input { - font-size: 16px; - width: 100%; - background: var(--bg_elements); - color: var(--fg_color); - border: 0; - border-radius: 4px; - padding: 4px; - margin-right: 8px; - height: unset; - } + input { + font-size: 16px; + width: 100%; + background: var(--bg_elements); + color: var(--fg_color); + border: 0; + border-radius: 4px; + padding: 4px; + margin-right: 8px; + height: unset; + } } diff --git a/src/sass/include/_variables.scss b/src/sass/include/_variables.scss index 0c95ff6..127cccb 100644 --- a/src/sass/include/_variables.scss +++ b/src/sass/include/_variables.scss @@ -1,46 +1,43 @@ // colors -$bg_color: #0F0F0F; -$fg_color: #F8F8F2; -$fg_faded: #F8F8F2CF; -$fg_dark: #FF6C60; -$fg_nav: #FF6C60; +$bg_color: #0f0f0f; +$fg_color: #f8f8f2; +$fg_faded: #f8f8f2cf; +$fg_dark: #ff6c60; +$fg_nav: #ff6c60; $bg_panel: #161616; $bg_elements: #121212; -$bg_overlays: #1F1F1F; -$bg_hover: #1A1A1A; +$bg_overlays: #1f1f1f; +$bg_hover: #1a1a1a; $grey: #888889; $dark_grey: #404040; $darker_grey: #282828; $darkest_grey: #222222; -$border_grey: #3E3E35; +$border_grey: #3e3e35; -$accent: #FF6C60; -$accent_light: #FFACA0; -$accent_dark: #8A3731; -$accent_border: #FF6C6091; +$accent: #ff6c60; +$accent_light: #ffaca0; +$accent_dark: #8a3731; +$accent_border: #ff6c6091; -$play_button: #D8574D; -$play_button_hover: #FF6C60; +$play_button: #d8574d; +$play_button_hover: #ff6c60; -$more_replies_dots: #AD433B; -$error_red: #420A05; +$more_replies_dots: #ad433b; +$error_red: #420a05; -$verified_blue: #1DA1F2; -$verified_business: #FAC82B; -$verified_government: #C1B6A4; +$verified_blue: #1da1f2; +$verified_business: #fac82b; +$verified_government: #c1b6a4; $icon_text: $fg_color; $tab: $fg_color; $tab_selected: $accent; -$shadow: rgba(0,0,0,.6); -$shadow_dark: rgba(0,0,0,.2); +$shadow: rgba(0, 0, 0, 0.6); +$shadow_dark: rgba(0, 0, 0, 0.2); //fonts -$font_0: Helvetica Neue; -$font_1: Helvetica; -$font_2: Arial; -$font_3: sans-serif; -$font_4: fontello; +$font_0: sans-serif; +$font_1: fontello; diff --git a/src/sass/index.scss b/src/sass/index.scss index 36a8a93..a19165a 100644 --- a/src/sass/index.scss +++ b/src/sass/index.scss @@ -1,180 +1,182 @@ -@import '_variables'; +@import "_variables"; -@import 'tweet/_base'; -@import 'profile/_base'; -@import 'general'; -@import 'navbar'; -@import 'inputs'; -@import 'timeline'; -@import 'search'; +@import "tweet/_base"; +@import "profile/_base"; +@import "general"; +@import "navbar"; +@import "inputs"; +@import "timeline"; +@import "search"; body { - // colors - --bg_color: #{$bg_color}; - --fg_color: #{$fg_color}; - --fg_faded: #{$fg_faded}; - --fg_dark: #{$fg_dark}; - --fg_nav: #{$fg_nav}; + // colors + --bg_color: #{$bg_color}; + --fg_color: #{$fg_color}; + --fg_faded: #{$fg_faded}; + --fg_dark: #{$fg_dark}; + --fg_nav: #{$fg_nav}; - --bg_panel: #{$bg_panel}; - --bg_elements: #{$bg_elements}; - --bg_overlays: #{$bg_overlays}; - --bg_hover: #{$bg_hover}; + --bg_panel: #{$bg_panel}; + --bg_elements: #{$bg_elements}; + --bg_overlays: #{$bg_overlays}; + --bg_hover: #{$bg_hover}; - --grey: #{$grey}; - --dark_grey: #{$dark_grey}; - --darker_grey: #{$darker_grey}; - --darkest_grey: #{$darkest_grey}; - --border_grey: #{$border_grey}; + --grey: #{$grey}; + --dark_grey: #{$dark_grey}; + --darker_grey: #{$darker_grey}; + --darkest_grey: #{$darkest_grey}; + --border_grey: #{$border_grey}; - --accent: #{$accent}; - --accent_light: #{$accent_light}; - --accent_dark: #{$accent_dark}; - --accent_border: #{$accent_border}; + --accent: #{$accent}; + --accent_light: #{$accent_light}; + --accent_dark: #{$accent_dark}; + --accent_border: #{$accent_border}; - --play_button: #{$play_button}; - --play_button_hover: #{$play_button_hover}; + --play_button: #{$play_button}; + --play_button_hover: #{$play_button_hover}; - --more_replies_dots: #{$more_replies_dots}; - --error_red: #{$error_red}; + --more_replies_dots: #{$more_replies_dots}; + --error_red: #{$error_red}; - --verified_blue: #{$verified_blue}; - --verified_business: #{$verified_business}; - --verified_government: #{$verified_government}; - --icon_text: #{$icon_text}; + --verified_blue: #{$verified_blue}; + --verified_business: #{$verified_business}; + --verified_government: #{$verified_government}; + --icon_text: #{$icon_text}; - --tab: #{$fg_color}; - --tab_selected: #{$accent}; + --tab: #{$fg_color}; + --tab_selected: #{$accent}; - --profile_stat: #{$fg_color}; + --profile_stat: #{$fg_color}; - background-color: var(--bg_color); - color: var(--fg_color); - font-family: $font_0, $font_1, $font_2, $font_3; - font-size: 15px; - line-height: 1.3; - margin: 0; + background-color: var(--bg_color); + color: var(--fg_color); + font-family: $font_0, $font_1, $font_2, $font_3; + font-size: 15px; + line-height: 1.3; + margin: 0; } * { - outline: unset; - margin: 0; - text-decoration: none; + outline: unset; + margin: 0; + text-decoration: none; } h1 { - display: inline; + display: inline; } -h2, h3 { - font-weight: normal; +h2, +h3 { + font-weight: normal; } p { - margin: 14px 0; + margin: 14px 0; } a { - color: var(--accent); + color: var(--accent); - &:hover { - text-decoration: underline; - } + &:hover { + text-decoration: underline; + } } fieldset { - border: 0; - padding: 0; - margin-top: -0.6em; + border: 0; + padding: 0; + margin-top: -0.6em; } legend { - width: 100%; - padding: .6em 0 .3em 0; - border: 0; - font-size: 16px; - font-weight: 600; - border-bottom: 1px solid var(--border_grey); - margin-bottom: 8px; + width: 100%; + padding: 0.6em 0 0.3em 0; + border: 0; + font-size: 16px; + font-weight: 600; + border-bottom: 1px solid var(--border_grey); + margin-bottom: 8px; } .preferences .note { - border-top: 1px solid var(--border_grey); - border-bottom: 1px solid var(--border_grey); - padding: 6px 0 8px 0; - margin-bottom: 8px; - margin-top: 16px; + border-top: 1px solid var(--border_grey); + border-bottom: 1px solid var(--border_grey); + padding: 6px 0 8px 0; + margin-bottom: 8px; + margin-top: 16px; } ul { - padding-left: 1.3em; + padding-left: 1.3em; } .container { - display: flex; - flex-wrap: wrap; - box-sizing: border-box; - padding-top: 50px; - margin: auto; - min-height: 100vh; + display: flex; + flex-wrap: wrap; + box-sizing: border-box; + padding-top: 50px; + margin: auto; + min-height: 100vh; } .icon-container { - display: inline; + display: inline; } .overlay-panel { - max-width: 600px; - width: 100%; - margin: 0 auto; - margin-top: 10px; - background-color: var(--bg_overlays); - padding: 10px 15px; - align-self: start; + max-width: 600px; + width: 100%; + margin: 0 auto; + margin-top: 10px; + background-color: var(--bg_overlays); + padding: 10px 15px; + align-self: start; - ul { - margin-bottom: 14px; - } + ul { + margin-bottom: 14px; + } - p { - word-break: break-word; - } + p { + word-break: break-word; + } } .verified-icon { - color: var(--icon_text); - border-radius: 50%; - flex-shrink: 0; - margin: 2px 0 3px 3px; - padding-top: 3px; - height: 11px; - width: 14px; - font-size: 8px; - display: inline-block; - text-align: center; - vertical-align: middle; + color: var(--icon_text); + border-radius: 50%; + flex-shrink: 0; + margin: 2px 0 3px 3px; + padding-top: 3px; + height: 11px; + width: 14px; + font-size: 8px; + display: inline-block; + text-align: center; + vertical-align: middle; - &.blue { - background-color: var(--verified_blue); - } + &.blue { + background-color: var(--verified_blue); + } - &.business { - color: var(--bg_panel); - background-color: var(--verified_business); - } + &.business { + color: var(--bg_panel); + background-color: var(--verified_business); + } - &.government { - color: var(--bg_panel); - background-color: var(--verified_government); - } + &.government { + color: var(--bg_panel); + background-color: var(--verified_government); + } } -@media(max-width: 600px) { - .preferences-container { - max-width: 95vw; - } +@media (max-width: 600px) { + .preferences-container { + max-width: 95vw; + } - .nav-item, .nav-item .icon-container { - font-size: 16px; - } + .nav-item, + .nav-item .icon-container { + font-size: 16px; + } } diff --git a/src/sass/inputs.scss b/src/sass/inputs.scss index d6cbb1d..aafa5b8 100644 --- a/src/sass/inputs.scss +++ b/src/sass/inputs.scss @@ -1,203 +1,203 @@ -@import '_variables'; -@import '_mixins'; +@import "_variables"; +@import "_mixins"; button { - @include input-colors; - background-color: var(--bg_elements); - color: var(--fg_color); - border: 1px solid var(--accent_border); - padding: 3px 6px; - font-size: 14px; - cursor: pointer; - float: right; + @include input-colors; + background-color: var(--bg_elements); + color: var(--fg_color); + border: 1px solid var(--accent_border); + padding: 3px 6px; + font-size: 14px; + cursor: pointer; + float: right; } input[type="text"], input[type="date"], input[type="number"], select { - @include input-colors; - background-color: var(--bg_elements); - padding: 1px 4px; - color: var(--fg_color); - border: 1px solid var(--accent_border); - border-radius: 0; - font-size: 14px; + @include input-colors; + background-color: var(--bg_elements); + padding: 1px 4px; + color: var(--fg_color); + border: 1px solid var(--accent_border); + border-radius: 0; + font-size: 14px; } input[type="number"] { - -moz-appearance: textfield; + -moz-appearance: textfield; } input[type="text"], input[type="number"] { - height: 16px; + height: 16px; } select { - height: 20px; - padding: 0 2px; - line-height: 1; + height: 20px; + padding: 0 2px; + line-height: 1; } input[type="date"]::-webkit-inner-spin-button { - display: none; + display: none; } input[type="number"] { - -moz-appearance: textfield; + -moz-appearance: textfield; } input[type="number"]::-webkit-inner-spin-button, input[type="number"]::-webkit-outer-spin-button { - display: none; - -webkit-appearance: none; - margin: 0; + display: none; + -webkit-appearance: none; + margin: 0; } input[type="date"]::-webkit-clear-button { - margin-left: 17px; - filter: grayscale(100%); - filter: hue-rotate(120deg); + margin-left: 17px; + filter: grayscale(100%); + filter: hue-rotate(120deg); } input::-webkit-calendar-picker-indicator { - opacity: 0; + opacity: 0; } input::-webkit-datetime-edit-day-field:focus, input::-webkit-datetime-edit-month-field:focus, input::-webkit-datetime-edit-year-field:focus { - background-color: var(--accent); - color: var(--fg_color); - outline: none; + background-color: var(--accent); + color: var(--fg_color); + outline: none; } .date-range { - .date-input { - display: inline-block; - position: relative; - } + .date-input { + display: inline-block; + position: relative; + } - .icon-container { - pointer-events: none; - position: absolute; - top: 2px; - right: 5px; - } + .icon-container { + pointer-events: none; + position: absolute; + top: 2px; + right: 5px; + } - .search-title { - margin: 0 2px; - } + .search-title { + margin: 0 2px; + } } .icon-button button { - color: var(--accent); - text-decoration: none; - background: none; - border: none; - float: none; - padding: unset; - padding-left: 4px; + color: var(--accent); + text-decoration: none; + background: none; + border: none; + float: none; + padding: unset; + padding-left: 4px; - &:hover { - color: var(--accent_light); - } + &:hover { + color: var(--accent_light); + } } .checkbox { - position: absolute; - top: 1px; - right: 0; - height: 17px; - width: 17px; - background-color: var(--bg_elements); - border: 1px solid var(--accent_border); + position: absolute; + top: 1px; + right: 0; + height: 17px; + width: 17px; + background-color: var(--bg_elements); + border: 1px solid var(--accent_border); - &:after { - content: ""; - position: absolute; - display: none; - } + &:after { + content: ""; + position: absolute; + display: none; + } } .checkbox-container { - display: block; - position: relative; - margin-bottom: 5px; + display: block; + position: relative; + margin-bottom: 5px; + cursor: pointer; + user-select: none; + padding-right: 22px; + + input { + position: absolute; + opacity: 0; cursor: pointer; - user-select: none; - padding-right: 22px; + height: 0; + width: 0; - input { - position: absolute; - opacity: 0; - cursor: pointer; - height: 0; - width: 0; - - &:checked ~ .checkbox:after { - display: block; - } + &:checked ~ .checkbox:after { + display: block; } + } - &:hover input ~ .checkbox { - border-color: var(--accent); - } + &:hover input ~ .checkbox { + border-color: var(--accent); + } - &:active input ~ .checkbox { - border-color: var(--accent_light); - } + &:active input ~ .checkbox { + border-color: var(--accent_light); + } - .checkbox:after { - left: 2px; - bottom: 0; - font-size: 13px; - font-family: $font_4; - content: '\e803'; - } + .checkbox:after { + left: 2px; + bottom: 0; + font-size: 13px; + font-family: $font_1; + content: "\e811"; + } } .pref-group { - display: inline; + display: inline; } .preferences { - button { - margin: 6px 0 3px 0; - } + button { + margin: 6px 0 3px 0; + } - label { - padding-right: 150px; - } + label { + padding-right: 150px; + } - select { - position: absolute; - top: 0; - right: 0; - display: block; - -moz-appearance: none; - -webkit-appearance: none; - appearance: none; - } + select { + position: absolute; + top: 0; + right: 0; + display: block; + -moz-appearance: none; + -webkit-appearance: none; + appearance: none; + } - input[type="text"], - input[type="number"] { - position: absolute; - right: 0; - max-width: 140px; - } + input[type="text"], + input[type="number"] { + position: absolute; + right: 0; + max-width: 140px; + } - .pref-group { - display: block; - } + .pref-group { + display: block; + } - .pref-input { - position: relative; - margin-bottom: 6px; - } + .pref-input { + position: relative; + margin-bottom: 6px; + } - .pref-reset { - float: left; - } + .pref-reset { + float: left; + } } diff --git a/src/sass/navbar.scss b/src/sass/navbar.scss index 47a8765..4e150e0 100644 --- a/src/sass/navbar.scss +++ b/src/sass/navbar.scss @@ -1,89 +1,90 @@ -@import '_variables'; +@import "_variables"; nav { - display: flex; - align-items: center; - position: fixed; - background-color: var(--bg_overlays); - box-shadow: 0 0 4px $shadow; - padding: 0; - width: 100%; - height: 50px; - z-index: 1000; - font-size: 16px; + display: flex; + align-items: center; + position: fixed; + background-color: var(--bg_overlays); + box-shadow: 0 0 4px $shadow; + padding: 0; + width: 100%; + height: 50px; + z-index: 1000; + font-size: 16px; - a, .icon-button button { - color: var(--fg_nav); - } + a, + .icon-button button { + color: var(--fg_nav); + } } .inner-nav { - margin: auto; - box-sizing: border-box; - padding: 0 10px; - display: flex; - align-items: center; - flex-basis: 920px; - height: 50px; + margin: auto; + box-sizing: border-box; + padding: 0 10px; + display: flex; + align-items: center; + flex-basis: 920px; + height: 50px; } .site-name { - font-size: 15px; - font-weight: 600; - line-height: 1; + font-size: 15px; + font-weight: 600; + line-height: 1; - &:hover { - color: var(--accent_light); - text-decoration: unset; - } + &:hover { + color: var(--accent_light); + text-decoration: unset; + } } .site-logo { - display: block; - width: 35px; - height: 35px; + display: block; + width: 35px; + height: 35px; } .nav-item { - display: flex; - flex: 1; - line-height: 50px; - height: 50px; - overflow: hidden; - flex-wrap: wrap; - align-items: center; + display: flex; + flex: 1; + line-height: 50px; + height: 50px; + overflow: hidden; + flex-wrap: wrap; + align-items: center; - &.right { - text-align: right; - justify-content: flex-end; - } - - &.right a { - padding-left: 4px; - - &:hover { - color: var(--accent_light); - text-decoration: unset; - } + &.right { + text-align: right; + justify-content: flex-end; + } + + &.right a { + padding-left: 4px; + + &:hover { + color: var(--accent_light); + text-decoration: unset; } + } } .lp { - height: 14px; - display: inline-block; - position: relative; - top: 2px; - fill: var(--fg_nav); + height: 14px; + display: inline-block; + position: relative; + top: 2px; + fill: var(--fg_nav); - &:hover { - fill: var(--accent_light); - } + &:hover { + fill: var(--accent_light); + } } .icon-info:before { - margin: 0 -3px; + margin: 0 -3px; } .icon-cog { - font-size: 15px; + font-size: 15px; } diff --git a/src/sass/search.scss b/src/sass/search.scss index 234d677..444f9bb 100644 --- a/src/sass/search.scss +++ b/src/sass/search.scss @@ -1,120 +1,120 @@ -@import '_variables'; -@import '_mixins'; +@import "_variables"; +@import "_mixins"; .search-title { - font-weight: bold; - display: inline-block; - margin-top: 4px; + font-weight: bold; + display: inline-block; + margin-top: 4px; } .search-field { + display: flex; + flex-wrap: wrap; + + button { + margin: 0 2px 0 0; + height: 23px; display: flex; - flex-wrap: wrap; + align-items: center; + } - button { - margin: 0 2px 0 0; - height: 23px; - display: flex; - align-items: center; - } + .pref-input { + margin: 0 4px 0 0; + flex-grow: 1; + height: 23px; + } - .pref-input { - margin: 0 4px 0 0; - flex-grow: 1; - height: 23px; - } + input[type="text"], + input[type="number"] { + height: calc(100% - 4px); + width: calc(100% - 8px); + } - input[type="text"], - input[type="number"] { - height: calc(100% - 4px); - width: calc(100% - 8px); - } + > label { + display: inline; + background-color: var(--bg_elements); + color: var(--fg_color); + border: 1px solid var(--accent_border); + padding: 1px 6px 2px 6px; + font-size: 14px; + cursor: pointer; + margin-bottom: 2px; - > label { - display: inline; - background-color: var(--bg_elements); - color: var(--fg_color); - border: 1px solid var(--accent_border); - padding: 1px 6px 2px 6px; - font-size: 14px; - cursor: pointer; - margin-bottom: 2px; + @include input-colors; + } - @include input-colors; - } - - @include create-toggle(search-panel, 380px); + @include create-toggle(search-panel, 380px); } .search-panel { - width: 100%; - max-height: 0; - overflow: hidden; - transition: max-height 0.4s; + width: 100%; + max-height: 0; + overflow: hidden; + transition: max-height 0.4s; - flex-grow: 1; - font-weight: initial; - text-align: left; + flex-grow: 1; + font-weight: initial; + text-align: left; - > div { - line-height: 1.7em; - } + > div { + line-height: 1.7em; + } - .checkbox-container { - display: inline; - padding-right: unset; - margin-bottom: unset; - margin-left: 23px; - } + .checkbox-container { + display: inline; + padding-right: unset; + margin-bottom: unset; + margin-left: 23px; + } - .checkbox { - right: unset; - left: -22px; - } + .checkbox { + right: unset; + left: -22px; + } - .checkbox-container .checkbox:after { - top: -4px; - } + .checkbox-container .checkbox:after { + top: -4px; + } } .search-row { - display: flex; - flex-wrap: wrap; - line-height: unset; + display: flex; + flex-wrap: wrap; + line-height: unset; - > div { - flex-grow: 1; - flex-shrink: 1; - } + > div { + flex-grow: 1; + flex-shrink: 1; + } + + input { + height: 21px; + } + + .pref-input { + display: block; + padding-bottom: 5px; input { - height: 21px; - } - - .pref-input { - display: block; - padding-bottom: 5px; - - input { - height: 21px; - margin-top: 1px; - } + height: 21px; + margin-top: 1px; } + } } .search-toggles { - flex-grow: 1; - display: grid; - grid-template-columns: repeat(5, auto); - grid-column-gap: 10px; + flex-grow: 1; + display: grid; + grid-template-columns: repeat(5, auto); + grid-column-gap: 10px; } .profile-tabs { - @include search-resize(820px, 5); - @include search-resize(715px, 4); - @include search-resize(700px, 5); - @include search-resize(485px, 4); - @include search-resize(410px, 3); + @include search-resize(820px, 5); + @include search-resize(715px, 4); + @include search-resize(700px, 5); + @include search-resize(485px, 4); + @include search-resize(410px, 3); } @include search-resize(700px, 5); diff --git a/src/sass/timeline.scss b/src/sass/timeline.scss index c8ce309..40882b2 100644 --- a/src/sass/timeline.scss +++ b/src/sass/timeline.scss @@ -1,162 +1,162 @@ -@import '_variables'; +@import "_variables"; .timeline-container { - @include panel(100%, 600px); + @include panel(100%, 600px); } .timeline { - background-color: var(--bg_panel); + background-color: var(--bg_panel); - > div:not(:first-child) { - border-top: 1px solid var(--border_grey); - } + > div:not(:first-child) { + border-top: 1px solid var(--border_grey); + } } .timeline-header { - width: 100%; - background-color: var(--bg_panel); - text-align: center; - padding: 8px; - display: block; - font-weight: bold; - margin-bottom: 5px; - box-sizing: border-box; + width: 100%; + background-color: var(--bg_panel); + text-align: center; + padding: 8px; + display: block; + font-weight: bold; + margin-bottom: 5px; + box-sizing: border-box; - button { - float: unset; - } + button { + float: unset; + } } .timeline-banner img { - width: 100%; + width: 100%; } .timeline-description { - font-weight: normal; + font-weight: normal; } .tab { - align-items: center; - display: flex; - flex-wrap: wrap; - list-style: none; - margin: 0 0 5px 0; - background-color: var(--bg_panel); - padding: 0; + align-items: center; + display: flex; + flex-wrap: wrap; + list-style: none; + margin: 0 0 5px 0; + background-color: var(--bg_panel); + padding: 0; } .tab-item { - flex: 1 1 0; - text-align: center; - margin-top: 0; + flex: 1 1 0; + text-align: center; + margin-top: 0; - a { - border-bottom: .1rem solid transparent; - color: var(--tab); - display: block; - padding: 8px 0; - text-decoration: none; - font-weight: bold; + a { + border-bottom: 0.1rem solid transparent; + color: var(--tab); + display: block; + padding: 8px 0; + text-decoration: none; + font-weight: bold; - &:hover { - text-decoration: none; - } - - &.active { - border-bottom-color: var(--tab_selected); - color: var(--tab_selected); - } + &:hover { + text-decoration: none; } - &.active a { - border-bottom-color: var(--tab_selected); - color: var(--tab_selected); + &.active { + border-bottom-color: var(--tab_selected); + color: var(--tab_selected); } + } - &.wide { - flex-grow: 1.2; - flex-basis: 50px; - } + &.active a { + border-bottom-color: var(--tab_selected); + color: var(--tab_selected); + } + + &.wide { + flex-grow: 1.2; + flex-basis: 50px; + } } .timeline-footer { - background-color: var(--bg_panel); - padding: 6px 0; + background-color: var(--bg_panel); + padding: 6px 0; } .timeline-protected { - text-align: center; + text-align: center; - p { - margin: 8px 0; - } + p { + margin: 8px 0; + } - h2 { - color: var(--accent); - font-size: 20px; - font-weight: 600; - } -} - -.timeline-none { + h2 { color: var(--accent); font-size: 20px; font-weight: 600; - text-align: center; + } +} + +.timeline-none { + color: var(--accent); + font-size: 20px; + font-weight: 600; + text-align: center; } .timeline-end { - background-color: var(--bg_panel); - color: var(--accent); - font-size: 16px; - font-weight: 600; - text-align: center; + background-color: var(--bg_panel); + color: var(--accent); + font-size: 16px; + font-weight: 600; + text-align: center; } .show-more { - background-color: var(--bg_panel); - text-align: center; - padding: .75em 0; - display: block !important; + background-color: var(--bg_panel); + text-align: center; + padding: 0.75em 0; + display: block !important; - a { - background-color: var(--darkest_grey); - display: inline-block; - height: 2em; - padding: 0 2em; - line-height: 2em; + a { + background-color: var(--darkest_grey); + display: inline-block; + height: 2em; + padding: 0 2em; + line-height: 2em; - &:hover { - background-color: var(--darker_grey); - } + &:hover { + background-color: var(--darker_grey); } + } } .top-ref { - background-color: var(--bg_color); - border-top: none !important; + background-color: var(--bg_color); + border-top: none !important; - .icon-down { - font-size: 20px; - display: flex; - justify-content: center; - text-decoration: none; + .icon-down { + font-size: 20px; + display: flex; + justify-content: center; + text-decoration: none; - &:hover { - color: var(--accent_light); - } - - &::before { - transform: rotate(180deg) translateY(-1px); - } + &:hover { + color: var(--accent_light); } + + &::before { + transform: rotate(180deg) translateY(-1px); + } + } } .timeline-item { - overflow-wrap: break-word; - border-left-width: 0; - min-width: 0; - padding: .75em; - display: flex; - position: relative; + overflow-wrap: break-word; + border-left-width: 0; + min-width: 0; + padding: 0.75em; + display: flex; + position: relative; } diff --git a/src/sass/tweet/_base.scss b/src/sass/tweet/_base.scss index 69f51c0..7f2d931 100644 --- a/src/sass/tweet/_base.scss +++ b/src/sass/tweet/_base.scss @@ -1,240 +1,244 @@ -@import '_variables'; -@import '_mixins'; -@import 'thread'; -@import 'media'; -@import 'video'; -@import 'embed'; -@import 'card'; -@import 'poll'; -@import 'quote'; +@import "_variables"; +@import "_mixins"; +@import "thread"; +@import "media"; +@import "video"; +@import "embed"; +@import "card"; +@import "poll"; +@import "quote"; .tweet-body { - flex: 1; - min-width: 0; - margin-left: 58px; - pointer-events: none; - z-index: 1; + flex: 1; + min-width: 0; + margin-left: 58px; + pointer-events: none; + z-index: 1; } .tweet-content { - font-family: $font_3; - line-height: 1.3em; - pointer-events: all; - display: inline; + line-height: 1.3em; + pointer-events: all; + display: inline; } .tweet-bidi { - display: block !important; + display: block !important; } .tweet-header { - padding: 0; - vertical-align: bottom; - flex-basis: 100%; - margin-bottom: .2em; + padding: 0; + vertical-align: bottom; + flex-basis: 100%; + margin-bottom: 0.2em; - a { - display: inline-block; - word-break: break-all; - max-width: 100%; - pointer-events: all; - } + a { + display: inline-block; + word-break: break-all; + max-width: 100%; + pointer-events: all; + } } .tweet-name-row { - padding: 0; - display: flex; - justify-content: space-between; + padding: 0; + display: flex; + justify-content: space-between; } .fullname-and-username { - display: flex; - min-width: 0; + display: flex; + min-width: 0; } .fullname { - @include ellipsis; - flex-shrink: 2; - max-width: 80%; - font-size: 14px; - font-weight: 700; - color: var(--fg_color); + @include ellipsis; + flex-shrink: 2; + max-width: 80%; + font-size: 14px; + font-weight: 700; + color: var(--fg_color); } .username { - @include ellipsis; - min-width: 1.6em; - margin-left: .4em; - word-wrap: normal; + @include ellipsis; + min-width: 1.6em; + margin-left: 0.4em; + word-wrap: normal; } .tweet-date { - display: flex; - flex-shrink: 0; - margin-left: 4px; + display: flex; + flex-shrink: 0; + margin-left: 4px; } -.tweet-date a, .username, .show-more a { - color: var(--fg_dark); +.tweet-date a, +.username, +.show-more a { + color: var(--fg_dark); } .tweet-published { - margin: 0; - margin-top: 5px; - color: var(--grey); - pointer-events: all; + margin: 0; + margin-top: 5px; + color: var(--grey); + pointer-events: all; } .tweet-avatar { - display: contents !important; + display: contents !important; - img { - float: left; - margin-top: 3px; - margin-left: -58px; - width: 48px; - height: 48px; - } + img { + float: left; + margin-top: 3px; + margin-left: -58px; + width: 48px; + height: 48px; + } } .avatar { - &.round { - border-radius: 50%; - -webkit-user-select: none; - } - - &.mini { - position: unset; - margin-right: 5px; - margin-top: -1px; - width: 20px; - height: 20px; - } + &.round { + border-radius: 50%; + -webkit-user-select: none; + } + + &.mini { + position: unset; + margin-right: 5px; + margin-top: -1px; + width: 20px; + height: 20px; + } } .tweet-embed { + display: flex; + flex-direction: column; + justify-content: center; + height: 100%; + background-color: var(--bg_panel); + + .tweet-content { + font-size: 18px; + } + + .tweet-body { display: flex; flex-direction: column; - justify-content: center; - height: 100%; - background-color: var(--bg_panel); + max-height: calc(100vh - 0.75em * 2); + } - .tweet-content { - font-size: 18px; - } - - .tweet-body { - display: flex; - flex-direction: column; - max-height: calc(100vh - 0.75em * 2); - } + .card-image img { + height: auto; + } - .card-image img { - height: auto; - } - - .avatar { - position: absolute; - } + .avatar { + position: absolute; + } } .attribution { - display: flex; - pointer-events: all; - margin: 5px 0; + display: flex; + pointer-events: all; + margin: 5px 0; - strong { - color: var(--fg_color); - } + strong { + color: var(--fg_color); + } } .media-tag-block { - padding-top: 5px; - pointer-events: all; + padding-top: 5px; + pointer-events: all; + color: var(--fg_faded); + + .icon-container { + padding-right: 2px; + } + + .media-tag, + .icon-container { color: var(--fg_faded); - - .icon-container { - padding-right: 2px; - } - - .media-tag, .icon-container { - color: var(--fg_faded); - } + } } .timeline-container .media-tag-block { - font-size: 13px; + font-size: 13px; } .tweet-geo { - color: var(--fg_faded); + color: var(--fg_faded); } .replying-to { - color: var(--fg_faded); - margin: -2px 0 4px; + color: var(--fg_faded); + margin: -2px 0 4px; - a { - pointer-events: all; - } + a { + pointer-events: all; + } } -.retweet-header, .pinned, .tweet-stats { - align-content: center; - color: var(--grey); - display: flex; - flex-shrink: 0; - flex-wrap: wrap; - font-size: 14px; - font-weight: 600; - line-height: 22px; +.retweet-header, +.pinned, +.tweet-stats { + align-content: center; + color: var(--grey); + display: flex; + flex-shrink: 0; + flex-wrap: wrap; + font-size: 14px; + font-weight: 600; + line-height: 22px; - span { - @include ellipsis; - } + span { + @include ellipsis; + } } .retweet-header { - margin-top: -5px !important; + margin-top: -5px !important; } .tweet-stats { - margin-bottom: -3px; - -webkit-user-select: none; + margin-bottom: -3px; + -webkit-user-select: none; } .tweet-stat { - padding-top: 5px; - min-width: 1em; - margin-right: 0.8em; + padding-top: 5px; + min-width: 1em; + margin-right: 0.8em; } .show-thread { - display: block; - pointer-events: all; - padding-top: 2px; + display: block; + pointer-events: all; + padding-top: 2px; } .unavailable-box { - width: 100%; - height: 100%; - padding: 12px; - border: solid 1px var(--dark_grey); - box-sizing: border-box; - border-radius: 10px; - background-color: var(--bg_color); - z-index: 2; + width: 100%; + height: 100%; + padding: 12px; + border: solid 1px var(--dark_grey); + box-sizing: border-box; + border-radius: 10px; + background-color: var(--bg_color); + z-index: 2; } .tweet-link { - height: 100%; - width: 100%; - left: 0; - top: 0; - position: absolute; - -webkit-user-select: none; + height: 100%; + width: 100%; + left: 0; + top: 0; + position: absolute; + -webkit-user-select: none; - &:hover { - background-color: var(--bg_hover); - } + &:hover { + background-color: var(--bg_hover); + } } diff --git a/src/sass/tweet/embed.scss b/src/sass/tweet/embed.scss index 227fc5e..fbdbd41 100644 --- a/src/sass/tweet/embed.scss +++ b/src/sass/tweet/embed.scss @@ -1,17 +1,17 @@ -@import '_variables'; -@import '_mixins'; +@import "_variables"; +@import "_mixins"; .embed-video { - .gallery-video { - width: 100%; - height: 100%; - position: absolute; - background-color: black; - top: 0%; - left: 0%; - } + .gallery-video { + width: 100%; + height: 100%; + position: absolute; + background-color: black; + top: 0%; + left: 0%; + } - .video-container { - max-height: unset; - } + .video-container { + max-height: unset; + } } diff --git a/src/sass/tweet/media.scss b/src/sass/tweet/media.scss index 91c9dab..66a300f 100644 --- a/src/sass/tweet/media.scss +++ b/src/sass/tweet/media.scss @@ -1,76 +1,76 @@ -@import '_variables'; +@import "_variables"; .gallery-row { - display: flex; - flex-direction: row; - flex-wrap: nowrap; - align-items: center; - overflow: hidden; - flex-grow: 1; - max-height: 379.5px; - max-width: 533px; - pointer-events: all; + display: flex; + flex-direction: row; + flex-wrap: nowrap; + align-items: center; + overflow: hidden; + flex-grow: 1; + max-height: 379.5px; + max-width: 533px; + pointer-events: all; - .still-image { - width: 100%; - display: flex; - } + .still-image { + width: 100%; + display: flex; + } } .attachments { - margin-top: .35em; - display: flex; - flex-direction: row; - width: 100%; - max-height: 600px; - border-radius: 7px; - overflow: hidden; - flex-flow: column; - background-color: var(--bg_color); - align-items: center; - pointer-events: all; + margin-top: 0.35em; + display: flex; + flex-direction: row; + width: 100%; + max-height: 600px; + border-radius: 7px; + overflow: hidden; + flex-flow: column; + background-color: var(--bg_color); + align-items: center; + pointer-events: all; - .image-attachment { - width: 100%; - } + .image-attachment { + width: 100%; + } } .attachment { - position: relative; - line-height: 0; - overflow: hidden; - margin: 0 .25em 0 0; - flex-grow: 1; - box-sizing: border-box; - min-width: 2em; + position: relative; + line-height: 0; + overflow: hidden; + margin: 0 0.25em 0 0; + flex-grow: 1; + box-sizing: border-box; + min-width: 2em; - &:last-child { - margin: 0; - max-height: 530px; - } + &:last-child { + margin: 0; + max-height: 530px; + } } .gallery-gif video { - max-height: 530px; - background-color: #101010; + max-height: 530px; + background-color: #101010; } .still-image { - max-height: 379.5px; - max-width: 533px; - justify-content: center; + max-height: 379.5px; + max-width: 533px; + justify-content: center; - img { - object-fit: cover; - max-width: 100%; - max-height: 379.5px; - flex-basis: 300px; - flex-grow: 1; - } + img { + object-fit: cover; + max-width: 100%; + max-height: 379.5px; + flex-basis: 300px; + flex-grow: 1; + } } .image { - display: inline-block; + display: inline-block; } // .single-image { @@ -86,34 +86,34 @@ // } .overlay-circle { - border-radius: 50%; - background-color: var(--dark_grey); - width: 40px; - height: 40px; - align-items: center; - display: flex; - border-width: 5px; - border-color: var(--play_button); - border-style: solid; + border-radius: 50%; + background-color: var(--dark_grey); + width: 40px; + height: 40px; + align-items: center; + display: flex; + border-width: 5px; + border-color: var(--play_button); + border-style: solid; } .overlay-triangle { - width: 0; - height: 0; - border-style: solid; - border-width: 12px 0 12px 17px; - border-color: transparent transparent transparent var(--play_button); - margin-left: 14px; + width: 0; + height: 0; + border-style: solid; + border-width: 12px 0 12px 17px; + border-color: transparent transparent transparent var(--play_button); + margin-left: 14px; } .media-gif { - display: table; - background-color: unset; - width: unset; + display: table; + background-color: unset; + width: unset; } .media-body { - flex: 1; - padding: 0; - white-space: pre-wrap; + flex: 1; + padding: 0; + white-space: pre-wrap; } diff --git a/src/sass/tweet/poll.scss b/src/sass/tweet/poll.scss index 57590c8..6d54e00 100644 --- a/src/sass/tweet/poll.scss +++ b/src/sass/tweet/poll.scss @@ -1,42 +1,42 @@ -@import '_variables'; +@import "_variables"; .poll-meter { - overflow: hidden; - position: relative; - margin: 6px 0; - height: 26px; - background: var(--bg_color); - border-radius: 5px; - display: flex; - align-items: center; + overflow: hidden; + position: relative; + margin: 6px 0; + height: 26px; + background: var(--bg_color); + border-radius: 5px; + display: flex; + align-items: center; } .poll-choice-bar { - height: 100%; - position: absolute; - background: var(--dark_grey); + height: 100%; + position: absolute; + background: var(--dark_grey); } .poll-choice-value { - position: relative; - font-weight: bold; - margin-left: 5px; - margin-right: 6px; - min-width: 30px; - text-align: right; - pointer-events: all; + position: relative; + font-weight: bold; + margin-left: 5px; + margin-right: 6px; + min-width: 30px; + text-align: right; + pointer-events: all; } .poll-choice-option { - position: relative; - pointer-events: all; + position: relative; + pointer-events: all; } .poll-info { - color: var(--grey); - pointer-events: all; + color: var(--grey); + pointer-events: all; } .leader .poll-choice-bar { - background: var(--accent_dark); + background: var(--accent_dark); } diff --git a/src/sass/tweet/quote.scss b/src/sass/tweet/quote.scss index b4bc60e..1db4f7e 100644 --- a/src/sass/tweet/quote.scss +++ b/src/sass/tweet/quote.scss @@ -1,94 +1,95 @@ -@import '_variables'; +@import "_variables"; .quote { - margin-top: 10px; - border: solid 1px var(--dark_grey); - border-radius: 10px; - background-color: var(--bg_elements); + margin-top: 10px; + border: solid 1px var(--dark_grey); + border-radius: 10px; + background-color: var(--bg_elements); + overflow: hidden; + pointer-events: all; + position: relative; + width: 100%; + + &:hover { + border-color: var(--grey); + } + + &.unavailable:hover { + border-color: var(--dark_grey); + } + + .tweet-name-row { + padding: 6px 8px; + margin-top: 1px; + } + + .quote-text { overflow: hidden; - pointer-events: all; - position: relative; - width: 100%; + white-space: pre-wrap; + word-wrap: break-word; + padding: 0px 8px 8px 8px; + } - &:hover { - border-color: var(--grey); - } + .show-thread { + padding: 0px 8px 6px 8px; + margin-top: -6px; + } - &.unavailable:hover { - border-color: var(--dark_grey); - } - - .tweet-name-row { - padding: 6px 8px; - margin-top: 1px; - } - - .quote-text { - overflow: hidden; - white-space: pre-wrap; - word-wrap: break-word; - padding: 0px 8px 8px 8px; - } - - .show-thread { - padding: 0px 8px 6px 8px; - margin-top: -6px; - } - - .replying-to { - padding: 0px 8px; - margin: unset; - } + .replying-to { + padding: 0px 8px; + margin: unset; + } } .unavailable-quote { - padding: 12px; + padding: 12px; } .quote-link { - width: 100%; - height: 100%; - left: 0; - top: 0; - position: absolute; + width: 100%; + height: 100%; + left: 0; + top: 0; + position: absolute; } .quote-media-container { - max-height: 300px; + max-height: 300px; + display: flex; + + .card { + margin: unset; + } + + .attachments { + border-radius: 0; + } + + .media-gif { + width: 100%; display: flex; + justify-content: center; + } - .card { - margin: unset; + .gallery-gif .attachment { + display: flex; + justify-content: center; + background-color: var(--bg_color); + + video { + height: unset; + width: unset; + max-height: 100%; + max-width: 100%; } + } - .attachments { - border-radius: 0; - } + .gallery-video, + .gallery-gif { + max-height: 300px; + } - .media-gif { - width: 100%; - display: flex; - justify-content: center; - } - - .gallery-gif .attachment { - display: flex; - justify-content: center; - background-color: var(--bg_color); - - video { - height: unset; - width: unset; - max-height: 100%; - max-width: 100%; - } - } - - .gallery-video, .gallery-gif { - max-height: 300px; - } - - .still-image img { - max-height: 250px - } + .still-image img { + max-height: 250px; + } } diff --git a/src/sass/tweet/thread.scss b/src/sass/tweet/thread.scss index 19fb3e0..9d2fb64 100644 --- a/src/sass/tweet/thread.scss +++ b/src/sass/tweet/thread.scss @@ -1,138 +1,139 @@ -@import '_variables'; -@import '_mixins'; +@import "_variables"; +@import "_mixins"; .conversation { - @include panel(100%, 600px); + @include panel(100%, 600px); - .show-more { - margin-bottom: 10px; - } + .show-more { + margin-bottom: 10px; + } } .main-thread { - margin-bottom: 20px; - background-color: var(--bg_panel); + margin-bottom: 20px; + background-color: var(--bg_panel); } -.main-tweet, .replies { - padding-top: 50px; - margin-top: -50px; +.main-tweet, +.replies { + padding-top: 50px; + margin-top: -50px; } .main-tweet .tweet-content { - font-size: 18px; + font-size: 18px; } -@media(max-width: 600px) { - .main-tweet .tweet-content { - font-size: 16px; - } +@media (max-width: 600px) { + .main-tweet .tweet-content { + font-size: 16px; + } } .reply { - background-color: var(--bg_panel); - margin-bottom: 10px; + background-color: var(--bg_panel); + margin-bottom: 10px; } .thread-line { - .timeline-item::before, - &.timeline-item::before { - background: var(--accent_dark); - content: ''; - position: relative; - min-width: 3px; - width: 3px; - left: 26px; - border-radius: 2px; - margin-left: -3px; - margin-bottom: 37px; - top: 56px; - z-index: 1; - pointer-events: none; - } + .timeline-item::before, + &.timeline-item::before { + background: var(--accent_dark); + content: ""; + position: relative; + min-width: 3px; + width: 3px; + left: 26px; + border-radius: 2px; + margin-left: -3px; + margin-bottom: 37px; + top: 56px; + z-index: 1; + pointer-events: none; + } - .with-header:not(:first-child)::after { - background: var(--accent_dark); - content: ''; - position: relative; - float: left; - min-width: 3px; - width: 3px; - right: calc(100% - 26px); - border-radius: 2px; - margin-left: -3px; - margin-bottom: 37px; - bottom: 10px; - height: 30px; - z-index: 1; - pointer-events: none; - } + .with-header:not(:first-child)::after { + background: var(--accent_dark); + content: ""; + position: relative; + float: left; + min-width: 3px; + width: 3px; + right: calc(100% - 26px); + border-radius: 2px; + margin-left: -3px; + margin-bottom: 37px; + bottom: 10px; + height: 30px; + z-index: 1; + pointer-events: none; + } - .unavailable::before { - top: 48px; - margin-bottom: 28px; - } + .unavailable::before { + top: 48px; + margin-bottom: 28px; + } - .more-replies::before { - content: '...'; - background: unset; - color: var(--more_replies_dots); - font-weight: bold; - font-size: 20px; - line-height: 0.25em; - left: 1.2em; - width: 5px; - top: 2px; - margin-bottom: 0; - margin-left: -2.5px; - } + .more-replies::before { + content: "..."; + background: unset; + color: var(--more_replies_dots); + font-weight: bold; + font-size: 20px; + line-height: 0.25em; + left: 1.2em; + width: 5px; + top: 2px; + margin-bottom: 0; + margin-left: -2.5px; + } - .earlier-replies { - padding-bottom: 0; - margin-bottom: -5px; - } + .earlier-replies { + padding-bottom: 0; + margin-bottom: -5px; + } } .timeline-item.thread-last::before { - background: unset; - min-width: unset; - width: 0; - margin: 0; + background: unset; + min-width: unset; + width: 0; + margin: 0; } .more-replies { - padding-top: 0.3em !important; + padding-top: 0.3em !important; } .more-replies-text { - @include ellipsis; - display: block; - margin-left: 58px; - padding: 7px 0; + @include ellipsis; + display: block; + margin-left: 58px; + padding: 7px 0; } .timeline-item.thread.more-replies-thread { - padding: 0 0.75em; + padding: 0 0.75em; + + &::before { + top: 40px; + margin-bottom: 31px; + } + + .more-replies { + display: flex; + padding-top: unset !important; + margin-top: 8px; &::before { - top: 40px; - margin-bottom: 31px; + display: inline-block; + position: relative; + top: -1px; + line-height: 0.4em; } - .more-replies { - display: flex; - padding-top: unset !important; - margin-top: 8px; - - &::before { - display: inline-block; - position: relative; - top: -1px; - line-height: 0.4em; - } - - .more-replies-text { - display: inline; - } + .more-replies-text { + display: inline; } + } } diff --git a/src/sass/tweet/video.scss b/src/sass/tweet/video.scss index 98a1c29..1e00d39 100644 --- a/src/sass/tweet/video.scss +++ b/src/sass/tweet/video.scss @@ -1,68 +1,69 @@ -@import '_variables'; -@import '_mixins'; +@import "_variables"; +@import "_mixins"; video { - max-height: 100%; - width: 100%; + max-height: 100%; + width: 100%; } .gallery-video { - display: flex; - overflow: hidden; + display: flex; + overflow: hidden; } .gallery-video.card-container { - flex-direction: column; + flex-direction: column; } .video-container { - min-height: 80px; - min-width: 200px; - max-height: 530px; - margin: 0; - display: flex; - align-items: center; - justify-content: center; + min-height: 80px; + min-width: 200px; + max-height: 530px; + margin: 0; + display: flex; + align-items: center; + justify-content: center; - img { - max-height: 100%; - max-width: 100%; - } + img { + max-height: 100%; + max-width: 100%; + } } .video-overlay { - @include play-button; - background-color: $shadow; + @include play-button; + background-color: $shadow; - p { - position: relative; - z-index: 0; - text-align: center; - top: calc(50% - 20px); - font-size: 20px; - line-height: 1.3; - margin: 0 20px; - } + p { + position: relative; + z-index: 0; + text-align: center; + top: calc(50% - 20px); + font-size: 20px; + line-height: 1.3; + margin: 0 20px; + } - div { - position: relative; - z-index: 0; - top: calc(50% - 20px); - margin: 0 auto; - width: 40px; - height: 40px; - } + .overlay-circle { + position: relative; + z-index: 0; + top: calc(50% - 20px); + margin: 0 auto; + width: 40px; + height: 40px; + } - form { - width: 100%; - height: 100%; - align-items: center; - justify-content: center; - display: flex; - } - button { - padding: 5px 8px; - font-size: 16px; - } + form { + width: 100%; + height: 100%; + align-items: center; + justify-content: center; + display: flex; + } + + button { + padding: 5px 8px; + font-size: 16px; + } } From 96ec75fc7f358471dd0390b4f9a0d9f547e3dacd Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Sat, 29 Nov 2025 03:38:40 +0100 Subject: [PATCH 266/409] Add video duration to overlay Fixes #498 --- src/formatters.nim | 13 +++++++++++++ src/sass/tweet/video.scss | 10 ++++++++++ src/views/tweet.nim | 1 + 3 files changed, 24 insertions(+) diff --git a/src/formatters.nim b/src/formatters.nim index e491928..3ad1da6 100644 --- a/src/formatters.nim +++ b/src/formatters.nim @@ -154,6 +154,19 @@ proc getShortTime*(tweet: Tweet): string = else: result = "now" +proc getDuration*(video: Video): string = + let + ms = video.durationMs + sec = int(ms / 1000) + min = int(sec / 60) + hour = int(min / 60) + if hour > 1: + return &"{hour}:{min mod 60}:{sec mod 60:02}" + elif min > 1: + return &"{min mod 60}:{sec mod 60:02}" + else: + return &"0:{sec mod 60:02}" + proc getLink*(tweet: Tweet; focus=true): string = if tweet.id == 0: return var username = tweet.user.username diff --git a/src/sass/tweet/video.scss b/src/sass/tweet/video.scss index 1e00d39..790b3da 100644 --- a/src/sass/tweet/video.scss +++ b/src/sass/tweet/video.scss @@ -53,6 +53,16 @@ video { height: 40px; } + .overlay-duration { + position: absolute; + bottom: 8px; + left: 8px; + background-color: #0000007a; + line-height: 1em; + padding: 4px 6px 4px 6px; + border-radius: 5px; + font-weight: bold; + } form { width: 100%; diff --git a/src/views/tweet.nim b/src/views/tweet.nim index 552ab89..58d03a9 100644 --- a/src/views/tweet.nim +++ b/src/views/tweet.nim @@ -109,6 +109,7 @@ proc renderVideo*(video: Video; prefs: Prefs; path: string): VNode = video(poster=thumb, data-url=source, data-autoload="false", muted=prefs.muteVideos) verbatim "<div class=\"video-overlay\" onclick=\"playVideo(this)\">" tdiv(class="overlay-circle"): span(class="overlay-triangle") + tdiv(class="overlay-duration"): text getDuration(video) verbatim "</div>" if container.len > 0: tdiv(class="card-content"): From 436a873e4b495e40c7394b3eedaf240ac79dcfe5 Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Sat, 29 Nov 2025 03:39:29 +0100 Subject: [PATCH 267/409] Improve verified checkmark icon, css improvements --- public/css/fontello.css | 145 ++++++++++++++++++++++++++++-------- public/fonts/fontello.eot | Bin 9368 -> 9544 bytes public/fonts/fontello.svg | 6 +- public/fonts/fontello.ttf | Bin 9200 -> 9376 bytes public/fonts/fontello.woff | Bin 5812 -> 5924 bytes public/fonts/fontello.woff2 | Bin 4832 -> 4896 bytes src/sass/general.scss | 1 + src/sass/index.scss | 52 +++++++++---- src/sass/navbar.scss | 13 ++-- src/sass/search.scss | 10 +-- src/sass/tweet/video.scss | 6 +- src/views/general.nim | 4 +- src/views/renderutils.nim | 4 +- 13 files changed, 172 insertions(+), 69 deletions(-) diff --git a/public/css/fontello.css b/public/css/fontello.css index 2453575..52362d8 100644 --- a/public/css/fontello.css +++ b/public/css/fontello.css @@ -1,53 +1,138 @@ @font-face { - font-family: 'fontello'; - src: url('/fonts/fontello.eot?61663884'); - src: url('/fonts/fontello.eot?61663884#iefix') format('embedded-opentype'), - url('/fonts/fontello.woff2?61663884') format('woff2'), - url('/fonts/fontello.woff?61663884') format('woff'), - url('/fonts/fontello.ttf?61663884') format('truetype'), - url('/fonts/fontello.svg?61663884#fontello') format('svg'); + font-family: "fontello"; + src: url("/fonts/fontello.eot?77185648"); + src: + url("/fonts/fontello.eot?77185648#iefix") format("embedded-opentype"), + url("/fonts/fontello.woff2?77185648") format("woff2"), + url("/fonts/fontello.woff?77185648") format("woff"), + url("/fonts/fontello.ttf?77185648") format("truetype"), + url("/fonts/fontello.svg?77185648#fontello") format("svg"); font-weight: normal; font-style: normal; } -[class^="icon-"]:before, [class*=" icon-"]:before { + +[class^="icon-"]:before, +[class*=" icon-"]:before { font-family: "fontello"; font-style: normal; font-weight: normal; speak: never; - + display: inline-block; text-decoration: inherit; width: 1em; + margin-right: 0.2em; text-align: center; /* For safety - reset parent styles, that can break glyph codes*/ font-variant: normal; text-transform: none; - + /* fix buttons height, for twitter bootstrap */ line-height: 1em; - + /* Font smoothing. That was taken from TWBS */ -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } -.icon-views:before { content: '\e800'; } /* '' */ -.icon-heart:before { content: '\e801'; } /* '' */ -.icon-quote:before { content: '\e802'; } /* '' */ -.icon-comment:before { content: '\e803'; } /* '' */ -.icon-ok:before { content: '\e804'; } /* '' */ -.icon-play:before { content: '\e805'; } /* '' */ -.icon-link:before { content: '\e806'; } /* '' */ -.icon-calendar:before { content: '\e807'; } /* '' */ -.icon-location:before { content: '\e808'; } /* '' */ -.icon-picture:before { content: '\e809'; } /* '' */ -.icon-lock:before { content: '\e80a'; } /* '' */ -.icon-down:before { content: '\e80b'; } /* '' */ -.icon-retweet:before { content: '\e80c'; } /* '' */ -.icon-search:before { content: '\e80d'; } /* '' */ -.icon-pin:before { content: '\e80e'; } /* '' */ -.icon-cog:before { content: '\e80f'; } /* '' */ -.icon-rss:before { content: '\e810'; } /* '' */ -.icon-info:before { content: '\f128'; } /* '' */ -.icon-bird:before { content: '\f309'; } /* '' */ +.icon-views:before { + content: "\e800"; +} + +/* '' */ +.icon-heart:before { + content: "\e801"; +} + +/* '' */ +.icon-quote:before { + content: "\e802"; +} + +/* '' */ +.icon-comment:before { + content: "\e803"; +} + +/* '' */ +.icon-play:before { + content: "\e805"; +} + +/* '' */ +.icon-link:before { + content: "\e806"; +} + +/* '' */ +.icon-calendar:before { + content: "\e807"; +} + +/* '' */ +.icon-location:before { + content: "\e808"; +} + +/* '' */ +.icon-picture:before { + content: "\e809"; +} + +/* '' */ +.icon-lock:before { + content: "\e80a"; +} + +/* '' */ +.icon-down:before { + content: "\e80b"; +} + +/* '' */ +.icon-retweet:before { + content: "\e80c"; +} + +/* '' */ +.icon-search:before { + content: "\e80d"; +} + +/* '' */ +.icon-pin:before { + content: "\e80e"; +} + +/* '' */ +.icon-cog:before { + content: "\e80f"; +} + +/* '' */ +.icon-rss:before { + content: "\e810"; +} + +/* '' */ +.icon-ok:before { + content: "\e811"; +} + +/* '' */ +.icon-circle:before { + content: "\f111"; +} + +/* '' */ +.icon-info:before { + content: "\f128"; +} + +/* '' */ +.icon-bird:before { + content: "\f309"; +} + +/* '' */ diff --git a/public/fonts/fontello.eot b/public/fonts/fontello.eot index 2b2982a5711bffac0cb1aad4690f661e34e308c4..8671134d7bcae39b2c2b05872362415a5a3bfbe7 100644 GIT binary patch delta 957 zcmYL|Ur19?9LIm>oO`b`b@y)WE(`x<TB~e?$%u>^J%p1z^bm*;*_@jjxj)V8G9OeT zd+H&X4`WbBB?!G#10O<q5>vuA5dwpH2rfQ^!-9h5?RW0gI^%P`_xn4)o!{@AyX6C` zJyLfYz+CH$<P!p$Yr>2_KY4uRRS|%^2B3v-R%-@xs5**0njT6Fcz=%rsK3xw4~0h3 z7+27LLA4DH-;Z{WKkfz)f&euTi-ZQx?)20EIM9PHhJlI)>^b^}=v!k6ZR}dF@dy0^ z`WwTka3IuZKNiIR2BAF>8cUN4_5y&@gT6T#N<`M5);FNXB6K~S8qq%VZS`UUb-2Eg z&P37^i(QMjw~(77AA1WxFb*&a%zSD`J%q<AfB-R!SAz{G2s+apB}d7#TWJ^oI_Tnm zXMUeg1hbuRntyU{=_%2LA?d|eN}T3KW(Lp0;mYl>^OGWOg#cH{0=QOK09Ts@wgGyj z1#rbCIlNiD$^y7nTVN}o<Gph5qCvM?09V<<5BvdN4u`)a-C+T!rG4;3Q1VznEx;}o z=K!9rxFLKZ#m~~LZPL6&Hc#`QLWBRPOn{IeP(r8iTF5l!XI+O~OgeIgxGIR$a*~|r zyh3~(O~jnsY+l)GR@#-lZCMVYz9dPUg9>%KaY2@e+qB9&NhwND24a+rH>{sk;XxgK zd*{C@YAu!Ou!x<sc<E)*(MVWdW<_63O5;mE-c68OqsyAMyv?U#TGmOUu(lcKmoNPU znd#Uje7nwk2WpgaSw~Y_+1M4%nLiJ{a4=M!PZt2#O=K?j-r>8-L#)pz(=OEfex?s@ z!!y_>Hu8vkr(U|ifce;x076vQ5`AJ)d?M9LQ)U6R=yg^xqUJ8Do(AFuF&mHE8xiq; tk<rAvqbV&?5l$r%k))QJQUflUx+8_-necE#h$o{dVIZCvEN-aF_P;fX)KUNd delta 738 zcmXw0OK1~O6g_X|Wty5e`81!ZRRaw++A5`mRIHTZLXC^kMRC)GO`BgaX@)jPmC}lI z(QTPc6bv;4aakPbrUpb@6r^qxp@Jx)IOsx4;zF>-JL&5i&fIg(eV=>Z+qzbaDaA1G zt8-O}a<y9DTpcbhy>?~C0k#fE3sbiWAvA~oJOwqSrE`<#{@ellS4le4@i~orH|buA z_H_1kYW(A=p8)RxoC}#`JaP6-_ybLOLiuQhjFx5j5$R8)!<k%R{?cBf0eCp+(QJMy z7VlU;k|1M@j5E3Tyv7F92c++k4$Z`K$?b<7$4ReIf!kVsuJH87&=<gG6;jn^liK3O z;0Dk+rmwMAS60Bt2kt>`vL1?usJ#_%Tqoa7p9Glh-AJv~D#XMAYp!FIk07GIkWSME zim@w6%nfHVS8x7-HeKTVOVogAHDRMD0EM^!T8InKo-hT_N?gFI%oKowxPWz@DS*So z1)vfa=!1)dfb}A#06d_U_5m+Yqr)tq&8YJo*8NO2Wl*JxVUt;3w`U;8f~>3jnEkEn zO_UigSJ*#d6<T3zDfjJ$-~El9*sNF88?KAL;gU`38omx|rGwaL=tsRLgN;Uw_loPc zQv1WFk9z%j+}nRa8U*B`u%nnj3E!E-LaZby(q-wn+%AvEZ+Sak<aOJK?Y%NYbFJ6( o8SlIfnfdh3{)p}j9FwK|O}#4+bMWbzRGv>x&n93j2VScG0pvrdS^xk5 diff --git a/public/fonts/fontello.svg b/public/fonts/fontello.svg index 2a64343..31bd38c 100644 --- a/public/fonts/fontello.svg +++ b/public/fonts/fontello.svg @@ -14,8 +14,6 @@ <glyph glyph-name="comment" unicode="" d="M1000 350q0-97-67-179t-182-130-251-48q-39 0-81 4-110-97-257-135-27-8-63-12-10-1-17 5t-10 16v1q-2 2 0 6t1 6 2 5l4 5t4 5 4 5q4 5 17 19t20 22 17 22 18 28 15 33 15 42q-88 50-138 123t-51 157q0 73 40 139t106 114 160 76 194 28q136 0 251-48t182-130 67-179z" horiz-adv-x="1000" /> -<glyph glyph-name="ok" unicode="" d="M0 260l162 162 166-164 508 510 164-164-510-510-162-162-162 164z" horiz-adv-x="1000" /> - <glyph glyph-name="play" unicode="" d="M772 333l-741-412q-13-7-22-2t-9 20v822q0 14 9 20t22-2l741-412q13-7 13-17t-13-17z" horiz-adv-x="785.7" /> <glyph glyph-name="link" unicode="" d="M294 116q14 14 34 14t36-14q32-34 0-70l-42-40q-56-56-132-56-78 0-134 56t-56 132q0 78 56 134l148 148q70 68 144 77t128-43q16-16 16-36t-16-36q-36-32-70 0-50 48-132-34l-148-146q-26-26-26-64t26-62q26-26 63-26t63 26z m450 574q56-56 56-132 0-78-56-134l-158-158q-74-72-150-72-62 0-112 50-14 14-14 34t14 36q14 14 35 14t35-14q50-48 122 24l158 156q28 28 28 64 0 38-28 62-24 26-56 31t-60-21l-50-50q-16-14-36-14t-34 14q-34 34 0 70l50 50q54 54 127 51t129-61z" horiz-adv-x="800" /> @@ -40,6 +38,10 @@ <glyph glyph-name="rss" unicode="" d="M184 93c0-51-43-91-93-91s-91 40-91 91c0 50 41 91 91 91s93-41 93-91z m261-85l-125 0c0 174-140 323-315 323l0 118c231 0 440-163 440-441z m259 0l-136 0c0 300-262 561-563 561l0 129c370 0 699-281 699-690z" horiz-adv-x="704" /> +<glyph glyph-name="ok" unicode="" d="M933 534q0-22-16-38l-404-404-76-76q-16-15-38-15t-38 15l-76 76-202 202q-15 16-15 38t15 38l76 76q16 16 38 16t38-16l164-165 366 367q16 16 38 16t38-16l76-76q16-15 16-38z" horiz-adv-x="1000" /> + +<glyph glyph-name="circle" unicode="" d="M857 350q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z" horiz-adv-x="857.1" /> + <glyph glyph-name="info" unicode="" d="M393 149v-134q0-9-7-15t-15-7h-134q-9 0-16 7t-7 15v134q0 9 7 16t16 6h134q9 0 15-6t7-16z m176 335q0-30-8-56t-20-43-31-33-32-25-34-19q-23-13-38-37t-15-37q0-10-7-18t-16-9h-134q-8 0-14 11t-6 20v26q0 46 37 87t79 60q33 16 47 32t14 42q0 24-26 41t-60 18q-36 0-60-16-20-14-60-64-7-9-17-9-7 0-14 4l-91 70q-8 6-9 14t3 16q89 148 259 148 45 0 90-17t81-46 59-72 23-88z" horiz-adv-x="571.4" /> <glyph glyph-name="bird" unicode="" d="M920 636q-36-54-94-98l0-24q0-130-60-250t-186-203-290-83q-160 0-290 84 14-2 46-2 132 0 234 80-62 2-110 38t-66 94q10-4 34-4 26 0 50 6-66 14-108 66t-42 120l0 2q36-20 84-24-84 58-84 158 0 48 26 94 154-188 390-196-6 18-6 42 0 78 55 133t135 55q82 0 136-58 60 12 120 44-20-66-82-104 56 8 108 30z" horiz-adv-x="920" /> diff --git a/public/fonts/fontello.ttf b/public/fonts/fontello.ttf index ef775f87308c85319ecd0d8c36791622adf5c7eb..0c04c6c934c632486fc0fc56ce07b794a1fcba07 100644 GIT binary patch delta 918 zcmYL{Pe>F|9LIm}z1eYfb^hqgX63)^-&UK5*$^0I9m<V8cnCy@Y+ZL<$^FB+iFwe7 z^whx`53*25BnVxK1rOp;%m_P1frl=^;2~TrFle2AZ$@i|&->2z_kJ_K-+OPiZ_bBi zS|^XMy(s{Yw*WLB9nCd?bLkd}6weGM`&)jG1L(VGD+ePZ8H_9FzoB{uhabkf#-DZp z2w{LKNW>xo=eKWF0jTJ~NMNAsF?)sn3HlR>WNz$6kNpSzBKq6I>1Zg@ARUWi0D}-r zM#eJal9UIi4xn#JMUt_N7j^aMu?XGBq(^e^dpCQqfm)nj%VcAjiKUJu{I>9H_hU~U z2!;V>fjLh>)Cyc(9t22Wyb`=XL9m#m7S&>4r`)yyEU-NPo%22)2xeEqS$>-p^o&Qt zkZ4|`yJk1_$(3%(ezs<Iu?v$P{1yUSMHk>&<^o*3F4zZH<u1S#yX5e0EuRZ;#X219 z1uWcG4sJA9k_&K^T{?k3;NfuiAz7*mpcePRB|$OZ0yO|TSeyg6yh2d;N(vXnS?{E? zjckF=fe!VZM`Qwogn<$|h5JILFn?4#q%raEIii&jvH3JP)qa&2ZH>ek{A@wlYf^&B z-j*zfQD2i3j$wuR{Wu}Z#P3*Tfut2BECaDi_B+<ce7I1xQ)>TLMX#Y^Ef$eTa|^vf z+8PM!&8}JNDRF$|=kf%(`(QPfTixPAVO_FFgYe;Vs87E93uI<toAAAx83$^Y%uh<= z$&$Szv^xI>pE$U5o=+D6*d1iv^sB~o9%8+AiFTmQ?PnTr4_?9+@sersgSOB`2Fze9 z0tj(o(_?s2o*A)DeB>0+3)8BP5#4;Q1t{^G%i2-%ix$#o`o0()%0`D{!cZ!n7W#*> K1BIZzD*XkEM#mNa delta 811 zcmXw%Pe>GT6vw~6`OU0r+ODg+XrZx?)Rtz8frf@ehfE0Rk95c&S6y|>aVOo(Lfi-s z9*enDkeDptQCjd)LWgc*9qLjPbtoF@P@zFZwtZ*%Im2h(`@Z*^H}C!CH`np4{boG- zEI8Z^h($n~>>JNS;a!n~G=TwY(Clsabq$Cdqbwavj9HAAQO9ZhgTq$`y5H^k3h4Df z>Euu{(ZBCS?OQHcV7_UHfud=7pZYy@?U0$7II>YF03n_F#BjQ=BT>D$t)GD|2KJhX z2}?8w?owZ;j*cYE<m$cZ8tOSVxN4=xG7rDBeE`C4Bg@uk(wbUoT>^G?-TJxGJ_Erp za0Alw>S?!dx*6y=!?>Tf1~_HkV{6539}YG>?>)hM3^9AVwuc3L_DSux-J=x;OTFy6 z_XH~Jv+_We6FBY^2CV`<`U(`$SFkD^1&Zk_+}<1oO6V)V?>P!=rLRDMzM>I93WfVi z90fw)Q2#+Wu*Q$Mpdx=<e<kW3OGkp8`tpVpjdh{sN>M3x&p!}<avMGKLZ8oxzw~l^ zBOmqM@#oFpCy{-$z8p9oJoE!W>A2hQ@vt?!X$yryN{`Fa)ZX^Gad-RWslo*wjs488 z>2dXIyrlOCyOp#Bt~*EO2(<?%kWn(tGTd}lu%Cx&TC`TiZmR-K=)o*L3r$4Dtme~> zYENX9?36F{Dt$^{H#&{izBaCP4@5ZMP+lTM*lvl$cEA`<C9jMbJe#8#<I?4HCRx;% fHqGQnMoV9`vyqMxJvB0r)_YT<{m8$GJPrH<_3pM8 diff --git a/public/fonts/fontello.woff b/public/fonts/fontello.woff index 63c3c233509e89b741c92971efe70a441b972b8b..e4582ad0ee7148607d5a5e183e595a785167af4e 100644 GIT binary patch delta 3442 zcmXw+XH=70u!g@R^w15xLqMcS6R8^MMan@CkX`}^1W+SY5_%KF&`UT}0R;pEsnVhb zq*sw<s7e<Ulxl9yUH9&_p0#J*nb~`O&NRuE%fW)p_4NS|Ksj7+faSj%UW|j8U^@T+ zoG(VKZgdYq3sdY&N(4%QnF8P$=!K$a#Q;FFOOY7e<W9o8TptAj0R3}HnC|~z_3{h# zq-ePSz#s$wOn_lnilH~s)dK)7wNi2vDX7Tq8#8%RM2faU@r5bCX```_Qg0MG1OQ-+ z6hW7QmA(!6+%LeLk_fY)_%i<?$SmTCat)z;40}THh5rMj%AD`-ib7H}JOF^ultzb# z3;6{C10GRqPF6}z76oFT-GWWvLnNh^vx?$}QNUxGHJU^4?k*k~jvx#cXU`msVALD! zH(}My)MH&GI6QZLUEdu`?R`~mF}4ujc7GBr(TM<gj0}Gg(K3=0bTakTDzn>G3m&!q zz0)t-=qmW_{!8bp@-@dulHu2gZ|#CejiRixr{Y=WFC+GPHPoLS;<?}bA#9|Ilf56m zFjw*Ei%M=@=KH9!R*LFZGm-|Y`5_~l7@qGz${$BI(XYJ|0GFeRNp@VqS~P}dz~&W% zI@O-iuXv3SP3k=@69M@y5SpN-(U<_Hr^QD2UHZ<0E&9`WwP^QO;mwg}tE#`%ZqRBd z;M|KGFGKxlcs%Ub6?xe?h3jGcUnZ)2W|ct$#_0{O*{$`(W7UZg$x;<&%4lorhN`(S z<=r&XR}xL#bF~xd@ltoKFUoX2%>Gh8bSLjT3I5U7*%khk^y?fms>3b97rZ%mh$3JU zZ!eTvNlOYxx57d4w&_u9&2S6jetJDNDs5QH%3a53nf~-dvTM8v!*{U$0P6wxol`gy z=A*e(O}Ba3=;!atp}DqcV;WR+f_}-b*@O;eo(M*zJ4R+)jyAPQUh82TkgI=+tQ=zq zasSqXxX`{J^dLw+wU)abW?a4jhcj5`gx<f`eWraHY(LJ76E#N1Z4!;O%Nw&ffZCOy zJMrPg0GgeTAwhSC;NQ(Azdylo<zFZ~wK($%2mUt0jUT73YoF69IhP&t$u)nBBaYha z_dh7nx*SgmVmM7nxyay+ABm=JZ!0{70Uamdj(vKW<>*VOyUC)OtW&~BBpl0w4XW_a z_^lGyd`oDNCbS=R<{d1;yfgCi%u8vutw_1$C2tuRDcH-G8PmQ~kQ+7ru|Ua-<Qxx4 zczqk1;HL+qF<7zXnor#p=Y)DdrNe_{&1@>_ly};RjGPFp@pTV|I6M^I;Q=d@j~<FW z@~k8MctPGL%xo#W*>ZpO4f{_$m)}y`!R72?AsqLnc;{P#M7XhuE=+fU)y;SV%*N^( zHJnb<8tZPN+SI9KcHbntV49zaNAXl?7e=UHfe*^9IEkd}WhttdQ4MSJ?DhyoyK$W; zekjZR1aU50Udnax-3=4P)jHto@ItCTHo^+5xiy4jne~@rjMpcYuolj@o+958Mob!P z%Q>YtcJq-O_^e9hVISYRA?F%AY47URxwxad^mvJOrr=A58izT%vANZIGb3E?F}NyT z+5L5DgB91<@y-KLdk=bciQ)3B@_sa`8=;lb3gh~R^ND|Io0UbbiJFwC0zA*ipi>yq zAKrjl7Vhme6piA<>P5mWlQOeX-V*Pv%HqBmK5F{JNDaa0%s8;q>)7TD@xz8#^_`^d zUt5@jl<Zlu*G1x#!6mTYcf_;%?^e$q?e2DV?w$;~IPTe!FrkrBkV6Tx?xD&}-DdTe zzSE$Pt{bE5Gy5SMCdav7#d!Cssy`>;zlsO)$|xbaaYlYvI;~jfrvD->oO?g+NPZk~ zX}lTH%V;UI7H2t555t$DO1|$2*{j)Y->E&oc&*za(P&Ia?pOQJ4otw4w9DPCfp5uQ zj4$9tg+k&4B9T~4xcg0lL9r{#Pdsg!crO~pje#oH^~Eu;LS3t%I^Hpes`|*{(mlf+ z$uu_x4{SDFspO7$w6NgXKdNpUC-_1ihV5E?U*RR2@(P+x`IHr)#N#D@JbEJCL(Drn zqu;B{JAuuWhfPk5pz1|fjW|2qFYjd8tKEy?OuVToR=tG#a%C2>wdZ!)o2NpXd_PFa zv0UFvXjkY5*Q=wmKWU`g*zBU}fzumZ79}<u)7b8=tBzEasZQ%mu1uz_gI&+UnKR%I zd=@p|z(t4Hjc|HINfLkM$k)-Amy&eTaUSa2iszti6Q55!dd##=%jGyJk!zC12CCDJ zTQLuV`3WNR%X;2iTi9yzY0??h4gN{+ssT}3U7+2pHg{}-IdEyzW%5+x2yzrG`Q=*G zZ!A2c(0Xn@Z*FFCj_4g;<8|l#-%n1J_J+@ur*8-7ydP^%0CO^)@rdrMyrUX%hgU;e z5oOQL-(YN0tB>CM|H1f`2+S=ijArtUOm}Yvy*FQc@OWVNHJPJw@y^~CPfd&{dORa> zFvkdsDxi+0(qTM_amwM`m(I10on){Og<!MN9_p(+F}LV^P~Xw*DKUvF=kZ~wvh<Zp z%}>QFCQHqs%4;<J!oBn1mD)yMO&(=6H2>V+h_Zax*ymo%vBIj@4wbkNYyah3s>`Fk z(t4F{fGL>JxTW}Zb-&644_kVGoX!X&Sp-&e5xvF^*h&r(R`WNBhQ*BQ-1<YFN7&}R zNA{`I%gu+V1FgES`R&eHm4cS_)l#nY{3I*aBkM;>0jTqAyF)nz!%Xy4GxxFWN)5ku zk^EY}jPvA}&BMexk^mj8Vl-^{MO&5s`pW%nGw?M%v$9f^<4>_6a8kA{AHThAI8(p< zN!Z%X(PRj-eZv#chHOX3&(k@=2kg?@q{d2{9P@y=1KBVdwJnLz*MI0V)v<}b7m7HJ zq|yqUU5;TWd*PbwSPWRMRIE@S%3sX;ae_8e03xYi47|Y2rj_Bw+h4WM$TSQq^oIyc z^#~U6^lPWRstzFpo9>cii;b7c+Pcdi5(Bb!`2q2L_HSmhe^L#RY^<!8u`MKTiK(0E z97C77iUa!iKQvCupsm!4|9)Ng=7b@RG;qF&gZ2NOE8A?gs!@|t_~Ldw&a{v%PO<1) zmhPGul1}-q>XR~)Rv~RZP|=|OuotvVyY#KmS{|R6l8hHzs9Z5fe<V}k%f1XM=C)eO zm3JC`@q?~B?~Qa>)@~6rF<5^iwgIu5=)Col<aq~vIgrk!5&SrQS$r}zBfmj;%W%T- zVq74B<)(&CrN|uxM-wseTXs(D4(m9iL@1oiMuN+LKc~J`QtDVct^4QR;iH62T%%I# zHlzQs?5V_OE!T9B^?U#Lg*;2AmRVE9d4`tK{9E;aT>|$-^LgZsN(gp@zWn?hjTCJt zCMX0XT9F(?G|Q3~NHFDhx#7=fdIM9YY4vczbXGF4Qt)ix^+BcAGpiPmM!}C(<wrY7 zUNbDis%bkdCND}X@BG$Sy}G7uo@t!$Om<YDplxdWBqs0WMdgTQ4cV!btKQQ0rCtP6 z?X4@iYd2>KgncF3CR@5h<mYs@(e)BZ8G9URdm;#b$h|1|;alYiXv9nz>U<s5w( z9no*#;vTVT9mMKBualDhd)7cZO1~}1?)|{zjit9UYZ=|$zY^3o&#vFZ{8Hu*IsJUc ze6f8nAT{@tC4$OVQ)pA94{OS+k3DJ+K$m*DbHq+z*;^uGwM^>;v7EZh*Gu2!ypMSP zUTeZ(&`<8e#3s28U(=@8>#*RTQEn?Iu%hTW>kO8ex2OW&O8vz9#I<>Wc0=K*P1cpC zPhIlPaD+5v<m`+4uaP<5t+m_?be2__VWTc8Jks}2IG2u^fuGl5Qn#J97h06aV7XW# z*5YH(ho^T5bzvRvUSXLlr1OQK8%fdB?RNv|A?dI2Ew5G!LRaNF5M=E)9tO2o6NyD_ z1+D9OAV-&Is2=!ax{CKftI->Ct&5ut+$q$IgWt}mjhIoNKY0e1q%?T_Jj>}}oPXig z_MmTVHB)@nm22)5DLiofr_w#Q14B}iv#jpvpQgvVNlxd8{4;Fa<UHR_^;m|P!CD?m z-Cju<+ROTRll%OAi>eXydVtj5{(Xn(jE$BoGWeyFDirwN!PmtFaLdkEyuNyZ>QdZS z>IRbKHJMygV#dkjzoQ-sF3dC!)wpvJrGUwIXbJHDHO!|)AA5RA$>bm8Q5hOW1RG44 z0SjcE(nwPt#q{>{G(|^6snH~I0SeDYW#~RIAQlKcJ;!lTQF>%Dw*DB(%4hf#e58D9 zN=YDcE9sn^^NH7Rl+p{}zrd10nanAR0^kDt3v7YtK=GhGupGDw0zgzC@2CJOPpSoK z6>5L#=QR8@kpPI2%R$)~s2T-fc_6IN&V_e}v_2JNXmj^b#eCP`;|5(34DW&SQ!qP& zxRm#lZAmXLpI^g!L0bL?1Rk!Y*5se4P_y+6)1h@=hP-QWt}aE>8$$+a+h_SH?xZ~1 z4ur(~(vkNSbKEq0*mT_Nc7Leni1j_Q!_2aybb<~sGkMI$Lrm@NXHrc2>=%0a1L_@G MX=MP&xXu~zKlz+IHvj+t delta 3326 zcmXw*dpy(c`^VolhdG~_%4s8~%pvD<Vpb@J<l`{R`4~kJo7J4x(2yzTBBXN6c^Tyt zAv%mo4b|`=<ed1f&*S&I9*^hazOL7O9q#|`s{+xiE*5(pi3HdHmPs@Nf`3o=Dp|}H z{{#R)*ipf755H?Max6ccb;HX-fCUg36%@hJ@&JHynI-Z0B6j10Fws!}z@5yxhWsBw zLE+Z}SsIDO008h|J-!_O861G|2LRCvtelf9^wcL6J_oZzmiBjs918`mWIVV$I3gyF zC04TpGZqe(e-01BBmMp+GFh?eUyjvF`A1;lScApHSh3t+*z^VNUBW~JurwV2V2fcr z^!4lFW71KP(JWv5Z@+97)W#*V`KT)atXc^JRvgcwD6z%3C9cTN$3GX%mWa<eB|^~T zvz8?&6Ny*2B}xXs?d#Q3Mr{M7>(rD5ljHUwn021;o9>^i!S8MnNmY+rYCar0OJ$Vr zdijC1x~keeBm(vuMee*;${f*~{>bE2H+C^Dj;{Fe@-8pf^%5!m$v0Wt^+c1SO|@zZ za&`-yXM!*j9t9gxNSqRhPe>h!fWXy~&x~RGiJH5y82@a|bre)ek@RUX@$q2e$^a32 zGFFY`{h;CGH2`SJ1#xrXBf~vh#k+sZY>$m?HI*Kk7-!AV$F`zFl`d`gnkSq?QO8sD zk`?&T_wt(U&%Qq{)&V)iIPH<5%N=01C}vdi2&v-YVsCvKZS;`)PA~yC3r2}P+G*}l zK-N6ew#PzU=AxcD)6Fp5=G!x|``FRl;x9ivdm&Tt7T%RVMUejZ+2h|jp4@=U64_}! z)|^N>(LzPOO59oq;`oW^ah1DK&K7o%x`0q|f-HuhUy80`=lFLN&8zEo<uj?{|0Gj< zuADskQ&esTGZ~Y#gDPA31<Cst>}et$TJF>gAW_$Dx;!$tX8bipDk&**ICD5D>Ce(; z=#xV|^wVzL@i<X@D(B3j522M#{N6T&Kf6%ga8>Tyr}19E2JyGORKEoqQ*1_gglLfb zht0HlZo##}O1@y?wA0gY<8~da9v_WqY+mA(x*f8HfAHbRlE!oJm?@qed0^bzZT+ED zf{xoW_9e$rE5#wSddWdEEJ!Ul%E<4b%0n*8BNeZU6z1?BUfoe2nC2KoFsnMtAni`( z%MGHmmAb?@d5ZaF?t9WBEmeo_!d>!)^Aucev}Ox7cBMD1Hp+jipQIV40*bp(Mx<fy z2P&_{X-3vb(_2lPi`(C9EFn6x$In`Iz_sgn;}t=|gXnJV?8xv@PcFtDfACi(=XH?* z^@mMzvv`fqWR>Yoi^f;+Lm6_)cJ%N~PLS80z6Q2ZOR3iEd$0_LO?lu!>j5oiLsUcw zS5bE$Mz#t`NhhRpTkt{9Fc1t@&oRNC6m<fUcE9d}%1`EC!IOr11pL5lAPZ`C+padb z{U#(oDX%F?-k&bM0k$cys(txd6JO~m+;%k!uQUnRF0Cr%?k?WxTdeG{&H7TRwY+BA zFyX*w_UK}r0RQ$!cayrQgu4E@H}RjgVuK%s7>9j%qjPf75#8lPZ8mmT|3!((uIo!( zhMIa&ov0lj-W!RyZ_l{So_<qsq^Z4q8D$bVnEJ$(u5FtG2~k{Dun$~J<5<MJtZYkf z#Lq{EjZ_kPJzsGPXDAHW88JAdEX>mNrLshdNXK)Tj$$b7V!OhcxP&g#Hxdz<E&cAK zC}rsQpL@12NU66rkIzxe<F_%d!h0uq86{VE#>z@2k{CK!lL7f7A3}Fuf0oAKr2E2r zyp}$F!bRLxP1-;px?fQi{GaQkmA?MGig)rOD@HPYaObaT?A;EAI>u6Yb$v={Ax*D< z%W&s7c#H)9LSnZdN|||U2W|znxB5b85x#Gx%=0Zrw7EDZ0Cpw&`-joDzg?nzX>Nse zS()ltr17+{pL#Oqj;)GOD3=6A>U2j}-}Pz1!XqN6h=2Vr-7m9j&Mlt5yBFt?jt9Ls z1ifCmT<-hw1&SX7#R);g^EGR#okfg$Y70Paas*`_t_-<!o9p6PiwC|vXIriIm*W#O zp$--K$E6!cA^_upR0i4pZqDO0(pjF}I?7{6;6MF*u9`2}0%=Wu()QNMaM`j?WeIvH z6Elt4uVlL06nOE#X=`nl1g<=RH!f6nq}G-S*zrN0^*^vt`e}FS<CsgpFCqO4OnYno zaG6*6w4w3BedIsRr)BEvd`E<HVCSf5uK8BU959u}LFl)h%ZtbMNk(sib;RuhPGd}k z_}VI32wt-bw39g#lK~Z+)-@Rw39tS*ES#}W;FIVANn6foWG?h`mG%zI;oYNVcjD;h z(;}TuBATxP$9CcWG_QHhq2^3^E=q?|Yc9BEZe6Vu+-wPgE~jr_3hVnaRT#s?P0sIN zM1;!3Z(FTel7Sa|ody~=e?f;9K?r6wygT@qvuNmC!ABhQ=Qx}6N?Xu0zvNNx*iJ^( zO0v=NHT?t9@JdK5wo}F&FW#{rH8LzYWPzh_6WCwy?Iu;*+U{u3Q<9^39AMx(=cAB% z4NBEp22<t^`c6~9uOPAPE;ZEK)|ob*vG*6Zm?2G&&XTs{Lkm&F>_*Rkk>W0s`0}D@ znYAOqY3#|HG9SyjZ43_O&*sSsDdFTDR@!n)u2Ra2ldI6PdB7N6b*ZG{cNRe{lm9t^ zW|Q(=@LXu>QMH@ed67_E_jd_0W2U+?goFwGKEbX<)4J`F>CjKP=ZRi<vB#ZKbl+z# zo3(Cw1Ws^SDQQi(eJP*w%V@d+)xr#tIWA4Ub=4kT-sXb$6v_4RxFH@Hx5v3BDrMY* z1vxqv?)zK9+(#7f`l<aCy%}KrT7xh(>i01jcwkq7-KBZ`8(r-UI)*V}>%Hxp@1j2@ zVv|a2vO3O?mrRe#^vHg9?>_5qeZM-yzemw&CZ=jfn(4@v$qaT4s<#omYs!cc<yVfD zoW5ZUhiQjAyh-928-L?DwD3l_EO2G*Zq|v7>O^tDos#lD_$K}f+}P$-S9YJqiTtX* zi*YULrIEFEy|Zv@7xBd1%s`7dQMVp52Jh}x5y9$te%ffLl8%|dGwI<my7hbHHKD?P zpPCoHu9$nGmC++mtLVEU<*58C#OsWenNw;;zqGJm49A>&v)gLQ{;2N^f<Ck*L5_`o zJd$3Ni@iPShBq{H?eTr_po{m)9Z1W5NxE>2mH!DPVPR!LGVXAe2)ls9GqqK3xwJNz zIB}*Tip=W_;cBTzC3SHDBjk!Z{gVk<(g~QXQH!UcZ5NTlgvJh7o9csFyLA-I{b9b1 zG^M`lgzApv`Lex(ky``IHQwmtB|gr5y+67uxiAGAyy|8S-6|_b@zc&_nEEP`zsqqB zW+&fpU7l^K?pKMZ9oJ4tjmYeuDaKI=Ep+3Riyfq81K+Rgzb{9JuI_r=VKnAO3=LSA z{2rlf*|YwpxCqI5-oDY==wD;hdPCzMCRgdIl(b>>-AMZ`Po@zXF*aIPa<8;=dTKMN zec?qAeoTLDS0Bf-?P@k*d+va!)AxP273Y_Gl{Z!l^&s+rw(nXgyiHQvYQ-oXd%H6+ zuhVSWeJtYWtls>g$GLatb_aP(fP#wGU~0QU!Fx%d)dIEWgYs+SEcJM@0Y*lyOQWaf z2~G^d@>RIK+trYJVeh_O|1A5=mw^H)5rdTRr9{ezoz7eN+Qxm|6dvc+4uNI$=cLr} zeSX#6600FQ&I^&PuA4~=qWD14K}7_?y9wP}Ht&kK`z#}ZKaqx*G$)%(Im^<?!!3^C zSJjc@8k#*R$uHa^R%lTj=z6Hhdo{t`=(g0^Q=gmTy5xo!%chUs__+$%BzboO$d`Ck zit1}Mo_1%le;Ru9?eoK({%1d)&9AQ=+`SQM!zJL~A^7qsR-w3KWn(jCuXckpDTO<j zE97EAKYNHOL?r6-b3bcenMErKNgKNSQs6rA&HRRg93A*?C-w0GW<PuL%=G>Ghz<A} zl*lHAiCc8g6hs~#9-8O&-7tg>;=lR$fHFV;1uek+|3w0OrF}HoeOA)p*a=QP3t=%i z5Rk9Ld4kn6v4=)$CfvMf$eAt)tnn&Ku={ezUBdYI?DzgxVs4jZEsMGZ+SEO@p(zdc zhRG#xg290HIzMZ*{?GIPAD{$SWCO9uv6X;iIYCGe4GaaJ1HWa5vL~`{aGc|q=KQ<A zv#}6sB8U~U;VGta41^Rp;)Wp=HyZOXwjGhL{PH~fAk5LivfPRz^T<&hx^J4PfWd6? zNkrTIKKr~#8(Y-`Z7oXO-IF21b0~R9lj@<u>~#d+Gu;x)Am8Pwd;c0Rho($yoWh_N r25iU4>y~-7&7<KON|F`dsz@p-k}{815Z5fRY!99)vsL6)N#Onm1w{Mr diff --git a/public/fonts/fontello.woff2 b/public/fonts/fontello.woff2 index b7541f0278434295a1178b8d5a6adff4deea570c..d2c246d1aa701c85f3438d4d3baa7e3a245aff90 100644 GIT binary patch literal 4896 zcmV+*6W{E2Pew8T0RR91023ep4*&oF03@IQ020Um0RR9100000000000000000000 z0000SR0d!Gg&GJB37iZO2nw12mJ$mr00A}vBm-0gAO(d@2Z2TmfgBrtAyo%z*f;<- zb8SadX)@XW9}=`NLb2b0whC8LgHzUc5`?&*CiWn^qifwJ<%Z;LAzs8v#EOFKPNn~= z52hO;R+Mf|woCCXPhLa2<8JtwTesd7dg#JeOFhceLtf)^<{&h8${&xAWEP(9*M8^8 z7tE4Z)1+!iBQ!h_iELv?q$qYP($_$pc9JCn$`~M1pm7;g0Gb|fn)}<r`wvgs{C9^{ zXC-c8HDDEHqD>hEW`dRM2Vzuh=rRlYewyy?Yx?e;c}zo?YeP4OG@@zHsWJ==q(LzJ zL*CxMJfyZsB7wxQc1iGj+R}3XNP#c^RKC#$$bdRZ*k&mx`PNs|F6!;7rwv<bHt!Qs zT4`B-@DvpxjlLo$`;Jo&O$kJx=ii)l@rcr4!Bj}$`Pp8N#NmtH)x_Q|23*BtjKS*$ zuiHu8*N9Hpz9mS;)}e??_4iA?@4%hjxujGJP&6EWi(<wD7_$HckOPAGzq!)>|2H`T zx3<W_pk(J}C!4&?B#TFwJ<gR9RVD`{6rm*A5`aBdG^R>b$PQzdG=m4n;WTt&iq+A< zO#X6{cTj*pW;uZ?JsUfbS|n{Qqe_7Gh+E9=)neMr7$T)B3<l88D(y@23Uv}d@55Vw z)6wVE3)#Vdh8wiM-g;L}vj4e1QFF&nN6a?MUqBJ;G{S=+&9E1BTiu<X97w4|Vdl>F zr@`n@m>7xpSq7s&!{s9nf(skaSJ(s$*L%kG0rFoSItC^dHV!Tx0z#G?c|duQ4}ysm zw`f2>r9uJm(T?~SM|`YA5F|n%5eA6}NJK#*1`=_QNPt8VBvK%e28j$v5O98cBK4#1 z6Qw_HHbzkw-p~CBQt%iCQRLk}OozNfz5g6o++g&YCel2<Kt2E0!zif7x^JunuYlTb zA%Mv{2uSn9L7`TyLxI#kmBs5F-y@`?<O^Nw`O+WQhjRN+uno#yu3<NaEp5^L<jI=5 z7Rp}hE=}#-6)4rWp@hBU72Je_*d{__s}0PfBw_}_-b11z@7G8z{LjGR=XNB{{$M;n z?FWnEaq~-f=rzrYa&xm4QYTrROj&)6Qh<|dMp$29cQh+!so?(%GLhyj7cfQI$rLWl z2`$5NWs33H1)z@2F}Mrhyd-RI8cbb(_dNd-VT?WQKOU+W>l`5|>dZrYi6m4X9UL+c znCDv1xE|2VqnkI@Mo4z3vStL5TeQjxg7a#dwDUnF$kKuL?8W_4n)LtYR4TJWeOTNG zY2D<_7&O`_$ooK~QHh?8(>XL?u-l@gW6&SSFs}k^FGH&dq=sa)p2L7JI-AS!KNczp zoI>V^E=hgwTqas$8XWi;YcyajYDBgj!R1L(K$s^PG)0kG(n)M+V>;;uQd*TLerxx` zUmM47g7ODqiFt52H`o5tnZy8LJy@eQ&RB<sLR*J?e&aX<q)!BoZ+(Z7Yd#_t@D7FJ z@S!z{q?Lax)J;+FnWTBIkzInO4e)L|6!z1P4{;hj+(Fp95#DW862{6v*ms12`TZR; z@UuaR$4`+Tm%1-r6~#=DWfc@RvdGR4bKks4oQ~*O{bI~)g993}TD?8uaU>WDoh(LE zOQXMw7KE_qLkNz36Ct%17Nb*W5w-(?c^TlE$1#w-q`&pmDa_$2y!XOGbZwAQqS1;U z-Hqw!NKmj;MW_*hI#Fnl0!?DjA`Wd*p+g#UNrxU8^%Ye0Cq>Qgwu4Fq`P@;MSa}kW znJB*_Ph}32zeARyLs$~A8B%^%I9Nn9%uq2$#R3&eRP3YT02PO*I6}n<R2-u=O+yxq z6=uANdX}EHR!^TSCYiR^_$+SBBo9x|F$a;6Y{-=6S>3BIJysgQVLn_=6?gd1+D^q> zuW*S6;c*nuUXabw<C_*CFPN@dLI<p&pWN0Vi46mm!t!6VXFFKd*|I3Hn1^H;vM3|g zD*^c*<XAy<bJ4L%=0G@pdVw_*8kYM~$q3kv(*CA-8`<J!3H#=NdmZN6RUz@U8OJ)n z^3R$`Fkdy2^u-QmSnM7&(<ZXmtb);Olt@*YCaC5qtA+Q6lbPjh_{sEmYP=hdH={qu zcAAu?pguqyCRA>#G9Bo`C!`ZFrdr|5wVI7F8#q&WvGi40<>ker4)d&0Ob(%r$pqG! zBrg>RJryY)Fx)dd!fIGd0l84Teh^(KnbzpoRKp-50*{rtID4GARf35#hmgl8vd-An zI0-A6?S^>>l`N_?Mnwus5r#~t-mUWHvL&p+C0e?^p`@96bf?7?Z`7IDYMLhnL%*(s zN~J(%)7Kc*&1MX;7^^ZXQTJxl-N`r2`kMEGEj9Hn?}POe+l&WbBQ>e!_O+hPDwzkB z6|=A5umsDtX-B0`$=K%k=EiC2^A@ReHfrw?iaFT#aJifFe=)LPE)GA0ZM&$m#@b4m zjV!`(J@zVvh?9=#GOGnH$RhmXslBw#CtK{?=GC*T<=)twwV$IvnglDvxd3YcF3*PL zLzPN~O;Hq;yn-<&P;M8Gi&j)K%utZc0p=)~$72l@IDldfutYVM@l!Nbh>8vb)m&gL zkgT^zQG=i+K`lbIlfx?NphaD@sE3yH@K{SeA48A>3@IyO2`eeqYS7OG4gy(2zyXc| z!8jK<2{e6*B8T>@*+K$UD09^0LgJc-dUJvTK8vMFV5I`<3K<dNMj5zOhP|7j4toSu zJm^R`s=%p2W>k1qCSH_@sWS1Z1GuTcT}3=#=EA44@TDw#D+@n5fUgStRGR*1m5bUL zN~3PHtd*}E)EXP3@1i#-yX%KX>XW$66cE141MQp`S!0}Lgbv*llnL#NEuzCj5ugA8 zEg%DHu&Ax*^~eD9gd)J0IBN~hb>vog9-d)n;?il)jHjosoVJUZ(`N=HGt0>gmY!;W zjc4j${iT?uO*4G{qT&9yJaFKU_^wmQI;JR)mco0U*~acsL9s#Mx9ft)1yLM%UhFN6 zzdV^b(-_L_+y#)-y6txG9yIe0#XBK87t7+eW!tL4@I$_}2SQfp*;2;%Bdf`!*J^A( zR!|&__!n6$`TiHa<n!pF{ef_Yb{-nnZL;S0_bN<@G0@1z5pocda@Z5~sk$}?GJTv= zQnLw+qag#k7U1@XwA={A^fgvhhFe{`<7D#5rE?h(6dG9QWo7I(@`yCB>bi^=o7u?6 zH)dt6p8x;(cjC{Fe<WEC0iW^R7`5yvfe1VqUWncePIlV-K)4qp?)kASUbv*Mi-hbJ z3*3~5TsMkPJrtiG-)L*yj|J!?C(b!mz@LD2enAK%z^jP8v<!>IFmyC4Kec^Zw5?|e zRZy7w_JMZ3<FibQa4nYrk3`@g4P|_WeRSdBfW1g4+@nx@cDY6MV(9ho!lWEz0}JY; zZo>}?1k+$1hBR7$sh_HLmjZugiIWe5iI6!zxj39mPDg!TMrrM{su<(rNcMg^1G%9F z0!cK{4I|--)P*hDSV44!aBDEdlS*SPN$GPAOW7?%0PwhoWO$6d{Mz$z-rKJ*d_0rg z+Wm!>HwaO^D}Zt+i9B;*v}R<tV_9(;_V3UiHETk-&=>%E*BiiBYVsIxE6FJ2nBQgp zV=E}tRn@ts&ehJjsOPKCu@+<&!FiM5n&Ok<<C*-MoNp}uNZ2<PWs%vSz}7>DngKnI zSp(wlnFM&x-l-;mR5$V@u~CnmmDG)yu<E>J03mp}9eb_MU{maWkzYhvP`W^vPY{!t z$iq_`pRuPWRyTJIc+5SjB8`($JBHtZybvbuj{ctypZ+WAq0@Vc*ldD38wulgT<1is zkPvnk$KS%2^NLo~Q`Fbz<K^#!sOP?Ict^|2tB=d`-6;978~LMu2a!V}x&$38;Vnas zAyJ=exTVk2CmQb7H>D0zci0me@jACU#Zc&J&uI~Gok>65#9dt%%Mj-X$1tu&1k<$& zJLa}MK?>6*D2Ndhzz9&s3o@t5nB0km99<*9HAkav*bDv)GM1L3iR$VagB8LY;c;PJ zaD?5b6yXFT;}|QK3Y`el=pACP5eero7%rrzjT+L@9)UD4-<;puh1zSMH;%BSK_T}3 zAE9XfMSD%TWq|`l;`TVZrH;Pd9?c%nF|9cj7n_CN+&-<oCpH#+(<&##gt-hQ!4TP< zz!a1rjTI|dzA}U6EnR6+4?CZ6)m4;0%&?|DkStlcvW1>qzEYwVA(e5}P1KW^ZY{}S zC<}zh>Oe}UzznGfH*=T33uSkl`P<t*2BFpT`oPK>*tcCxH1T;hMQmtTtf*LSr~@M! zi_?FKaAY_l&!})qD&qV*bxEQBf+1;5L#YSSR#JqhEWD-$N9D#KO@nL0-LHw?JVOVY z4|*(ofenro7+7!3jKbA1@T?Ic6BiI@T?b=gF#@$XMlJq4mI=Bt3rifqF&TK~WnmUB zC=gp88^Xfxy-Oe^wvtFq5QPRcBvL#!;&iG09{%ANmH#;cnH7=YyF3lOjUx*SDAqJ< z+a#wHxUMnVB*lo**GDl*F{alv|Mrq%DRJ0xn_9eGsG=hO-o3xZe-#wo8Nc)M<HH>p z&C1rS-uqt-Y*#`-&Y>fD&yVG09TI#|a)_7rd3RpLA*69vz3455&AuZ7)oFN(9-5zW z61#jd?@5Z?!(CBiO;pv?C!||?pQVM2PL*R$s+ce(hESk|C$gtaqP4wjO(HUEYs;}* z<=Ucmzs5!fYr-y|S+t-FiC!vf%K}%)uoMs2>Qk64)x3|xdnFvA9GI0g9L?J&=X#}x zAcLF-0Mh}_0pLDEt-}V<98g&(4L|7p@E(9G9)tgXRzGA5A9Ar2fi;6MCTSdt1{+-h zzUrMj(9JHvndTMDfIZ-{k(-s2o^#=p5cnE3Xl_-~kP!yoAa7EnUV|e}dGYWz@I&7= z3Ox@VW~sD3B6;Buryw{2_*&)#&CTjhi61Iyi;Nr*9wT5f=sO$+w*k>g<R$VmY;G(s zO^m*}t!C_N=?KO%%{_<KHFVz>XBVu^u(Q_noQ5pR-eHmT9Gv0=P5SFJZMW-nf+I_d zJZtxsV7F6my`yF$iK8&^h1(LFvMJP!N}8zTDI>*qBP6^k1T=-9Tj3F}XZJQm4GUa| z;=d-e6H(eC!G-y`=EB|*b@>GZhx|LTXiV!tK*Ru#@MpNk1mSV*i|J}mG-{&f_rfsH zbGwpO06gIXva2vtS`U$WR*G4l&5XA9qu39A6dAc+qUcDyoU)vXYJn0~rE^XQI|Sun zDvSBOF}>2?bP7dP4+q$}Moc;3xZRUU)~{ztJPOS`6c;w|i7W}}0)&YhDHi}755jF& z3x_Os-euWp4?^$)CVNM9DX=gYAxeB5bx;$=_H@@%li`9N<qL6~#Dlx`%c9+Cs>YK+ zqm~E-oosPoKEo5T3)rxP1%j7A0-m%Q&rFZ!tNFCo;HFz*&6D;C-xau&gJBVa6qqE_ z3!KAGVm}Itz+`h(y?sE`2;~K$nAA!#4tIu<e;|C2^Pk?$iyXjB<{u%N_=bRaBm|&m znlxXxq#n_R=zHP8U!`#zRoy|U=3XV;CWieE4Ug?46wJJlO3-$kP#czok>*83^F{5@ z&j%rJDDU0TPW?a88Y9vUU-U5h5AYdmaHC7||G0B*jtT<+KNL`F&kt4sn|T9&i)&Ov zOcWk{56qy&I~0t-BseKi{{byNOv)?-A*NIx(U$3F`fh#vKe_;}wFG*^@E~vrP0Cvv zu(nH}-4N+68<4NhdMR|4dTBJaggH|WgoWxwFAo+E!VA@Ry#lGETt0|UH-p#)FmDX> zB8a4WQCxK>6sVdA235yioTLDuLY30X<f*)EEC!wUw+O2Dlasz(x$)e&?)WcDUu?&- zfAFWp4WNG<j!`j2x+7eD_Vn!XA}?+5Z*0%xY;I1wvqh@0#^)XCPDG0&b>Fz7V<?d; zO^Q_c(Db=8fM&_u3Tzf+T!%)dVEAI$NHU?36v&qf6?s^=Ad%dO>=Qx5BC{U2DGItQ zm<UBxDGJb5NpdlZUlfs<M{lLU4jj#bDRT<+va+cx5+p`tyDCp16PAc^E15wnm!Ykp zd6sI>7;urk6O=Q{R3ReY?3x`jW}=nCw8tTkmm3pIqcfN+c5IyCEay1S1uk+4LLga4 z4w8oyAVnbP%wT!kDJ)MWjEFDegtQqGq;;2w`ZFm+{aG9$KF0`YvusG~s$lw>%FUgr z$@aQFnC9}#%Z5UqF}Zmgnx5hP-V4&YR!Ez@<&ppze*6pVb%^@Yw@4mV&iy#3@+BZ; zH0sQ@y8(hq7u*d4m2X*N8}UIlzAVCL8r44P6nv<>Vx8mC3{Uz2v~S8kg|Fr9<2zVL S*`!;CpE=t5KRzi04o(09?GiZv literal 4832 zcmV<65+Ch%Pew8T0RR91021H;4*&oF03+}K01}A+0RR9100000000000000000000 z0000SR0d!GgnS4N37iZO2nv{dl^F{v00A}vBm+<cAO(d@2Z22dfgBs!AhWV=kXAoJ z_Fo@xV+eLedBvzIT6C3Xd4>~VPdJ_!2WSTttvBuOt5t?NL)YmCJG~_qJ(8)J$XD0k zAj9=sLs%nncEeRTi$yi=m=Ti9^8CZSdT!<vjo^<bSn$ck(li*`toSs;|8LE{`-+;9 zbTtRLhoL?&8?kIN$M3RmgpQP0qgIcv?oFp-O-q(@BM+dC_4HBqO75=WErA83gw6@v zv;?RnH4&sD#vnk~I&}O!tIc{nCQ0kb9ILFe>Qw7*T1%LT*w=n#cfU6?OMFBsM2Y~^ z*2b|s{^x=!lE|2oF)EIH;t!rrTe=Pu6_5g7`Kj#D2FQRqO8D%M6qI}$hN9lCdRjZw zR~cI8L!l2uf;to*P-tuMu5E*V;+X#}5^!3g0hCoYK%W2KTwnIxN@{)$plUxz!k}d5 z(v?n5y-Geq`V*!Hc!5YLRR4@5$^xQV+ivmrwd>Zn6>_a#VdM^D7l4UtI6Z30w4#I< zC65(qHZt+QrGSwoIS|$iuivRkm-*}aswx6Npm}I6U%kkcUF{_Z1iMN_ecAT?)K<OF zy4x{My!3wi{0j?A&?4J{|GeGvK~w!zUORU2U-NM1L0?2*)uOsoTf70*m<KxFpF~t$ z#JPFA@NNZbCpoGXhw;f-`9DgXfZjEh@rE$mBK|)fo5STPC@S%32_%$Ake=XK6LZcO zTMJ5Ei7cfhOKHneI<l0mETtz)>B~|EvXr4LWh6@(%UWszrA%chGg-=9ma>qgEax1? z%4+t>(em9G|L4Afg6lAdBG>+6Y<~@P`5>^i%=kV%m-fyb%=156Y_BG|X{-hJfZFd6 zoJ!t7pq-yP7}TnDa7eSoXZBSmdqk9!d|@*^Uk2T$V7&8FC>V^r+#t?=cC4$5$<ui| zVqxrcrP-;IM+8RoT@<2T@(Rwvv)HA9EsR8pClUtYy{9w?{Gdh};jb|wKIq1w$iZZQ zs)B`k+;|usrcHZaxqh}~qwCljjBVXVDJaPeV;oF)cVfvdMWVl^$a86Dc_Nq7&bG<c z9f%FuXh|_Xdl8sZ-~?O*a9Cx#HU^7kY4b4u)$)pZ-oH{_#@OTtr5=E%_!W{;eRN33 zKoFiA!Qwbzna4J7tc_51C{=re(nzsdUIMhJvB|oAY5`d$$fmvc;FPxg`NLEyOT7a` zTn}k%@<t3AZ4}I9AlB$4$j0Ry7H~Mzq07W%e<;Vi3b2C=YdweBP{wLH90<^3N}T)^ z@=_8PF*yd;r+(pDE;?sSP7VCDwHoqnY(;T_!7EczK!hh5w7nvYqU*41j7i$d7-hA< z{Wk8We@}{WYw7<0Zni`vEzIlRDvk)>j)OI7<Fs}7D6FgE^_#?@AxlK?c{O%^7%?Bw z6S#m*909bikWBe2<W<`%1WeLC*X~`CW`?+SClt}?$CDJ_^yiMEzV&eJcD>eE=|n5W zD46116V?}piYG`B5az!4CFqMmb1HBjhoZVtg!^_Ti%fLW>X&d78yc~E_FGdUK1XPn zKe-7*-4gS?WPz-$4-xGAQV7j2EkX68uI-&bXkG@C%u_Uwy`(?og((CQ$w}{}r}*_j zrNjd{M>ESOq9aAYZ0eK=qOxFA5uB<*P)$gx3q=iKQB&B|5>8hR)%`_L`P!YJQbW&c z$`c)P0TrqHzU6$y(ET`NZB#$ZlZ<^(`Bm5GMzWy;MHh-56n!WLPz<3MK{1A60>u>C zy8v0VR$5Fa%2|54jC$rQQf9$jYnE_hk~}?4$1+4lvLuU|XLX}~%c<5#PAi34v6T8M zXS)>3%_76>Ae@d8#;dBi<@DY)$OX`y>!{G(RQ#SAN-#oF5p(~*$27}2Th>A%=PB8M zEXoLa3n2dkIW|!rSafWKOCV03zQ{HTlhmb_5wJC7=t?^qSscj{Hq93I4Vdq?0?qE7 zcI*JmeP^Opc=E$!mx7$c<IJ;m!9*6@EwGK<2B}#Mpv*^X6P|BRjx^_COX%*lsUvZB zHTtdWs7q;kk*BG{gyO1d)0VEVN4kJuY81)bsAB}PK{C~DGJ0!PdHDiShk4d0E{8C0 zK8J@*QnHFzPdW8zBRwM-wjp8$gfp+fR`dpZ2BKq64{M1CGPdaA>~Y0@Jub}*p<W<o zJ>$R^q@ZPQx6xIoWl^mO>sBCDiojast2<peZYdb>W*t3PQr7If>aeRdAGV$~(az5s zp?OCPRB8=+IXhz*n~fM`G1g@%#4|TrpV)W9jPL$rvRfSad7lD1X<<`OgWc4&wRW#Z z*lvYOp;)u@n$FZ?&Nbtx^|4IByx7`0jXv*}T4!0O&!V_J?%{22&;N>%18aHoHtgw( zI%}+}G};J}&UIlUX@t+OJl$xutR=aT__z?yuJUyq)^4-sOl>rG<l@jJ6of@Bn<Tjm zwi{i!8s;jMT83?}XsYfithtseQ|cPfJK-!tNRYy_5F!E_rQ@84X+-f^2nj(t+1(Ow ziX!EZ(z6jV4wPLa5ONMFKO3RoV2k{4MwD<yl#wPXIB2UyoEJ4+r1~s`h9GJ}!fDa+ zo7A3-&~b>mNF($dQhzqWz+rOOE!ttcXf#oaU$|og>_p*y0YNi8jG-%L5@#-<MYDD? z#M0e~mAf-u4-updfFIi+kaiN<H)|Iw9Nb+vy1Q_4ci|i$x=83M;Tx>jjk~)W4|g}7 z?ryvSL~jXwB)#9ccjr;<y9dhkZZg=xSH&6|-@6XBI^gbIvZKC8uR;MHuP`P)fM;1! zzAobfpzQzv`Z@q};EbEYbbx?7xJZB(W`jFx6vGHdYZxBs88%#P=MU~Xj-^cvOx(oX z-Q2*i=WhTPwP|2LIRla}Xb^wbGu|UnzGNAN2^R=1mnVK!xe@K-MFB`?1<=Z_k+b)? z2;uBS{vcDSwg%N2wT+F&CO~tb&%uSF1ei9I4@{6GU5H4LRRVdvav0S6WW=gLg^D$V z)uF)HS)9UnLammoLM_im|HtQ#qH{!mYz{5tFGBbdHFkio`d27CfcfwL&9_jATTdqN z7DzcU2RJ$l8PDcHolh2E7)7usH-17lX72$I=%42qL82G<eDx#+%L~M`MT8ck@I{oA zp|gt=rX}YZ0MiH&sn`@(S~5_TjTxb_*1^;W1D$1>;`1&#*aF9ln4@F&Z{EFd=f*w6 zi0;xS&W9;#F>nV0!Sv;n4?Hws2goH#+<KWBvk__tt7@r!Lx>8kLzULXxZ2uUt%m9D zY=7^KKX+u*5(3JDM2uSUKapVc01Q(BeH|Jb6GCNWrBVSj_-LQ}kZ<f+f59SzQ!Uy} zBrwabLP;5ZbSa3baU}qtDne6<j#@(^rP30@t(QXherrq%(`S==r+kcv33S|>Mv!uT zw<W?CC_VX*K<&*J2s1TgEdYfsQw=g=4UCM}Fv3^aAr-8%sYQ|$qO<rswg4074pl<i zsR9ZDj}S8E7al3pEm>|811Q%iY7qQHWA*Yfs$z8a!Ly^Dg9l0IJwh9E<^c8|8y9Vf zN=U|fwT(@TlJe<Xz<J)VKm`-R->7gE;X_d%_$P}3u@DmJ{r7ng+~0E+bALZUw?Aor zsB7!Vt%rmUPCj6TRkfxioJ~uTW=XTEC;v(b7mt5jsknI5-0WaM{>Gnw#(}z$77Qr8 z9+Wrv`Nv~rm2Cre%R7A(H5J>vMk2Oe_5;m07VeYGe_7I)&s7hFVd8B7FRP%XrF@Jd z>3rrr{UDBvJ$r@*K7Sn;*tKiVfNp3~-krYR>`d6k4!`5Fu+;ThdDZvp%h#I5-nzQp zMuj3RMPbJKK0Z!pxp29PZ9TbR**%Y|8+#l3`?E4~<IS%6PWWGS$;jyM8_Pb^^zoe8 zSKq5<7G}sco!D^NxPOno$hXgb+;_}Z<bTdLSh8E<o-Y_Mwsm|fL~rj;->(neV8zF$ z%C>Fua<c>^j@Rdev?Og{4JB_cAZ>{(D5x*UtIt#CD+raC23;)z<B|m6br;bu#^`(s zWyg*ei5wlBcCXXx(HqtqE9`XclKMS`X2V_)iR(NOdhdJmVj*S!dwKPNK5q0qE^Y&$ zlh^x%7tu>%+-3ujsA`ZKlluxR$~_w6Y;l}1(+J0~qmOOr-_h&bD~9(w?<*-D*(VI` zcRXKQjDFH~e?od+*4yZC%{>5z6v#|iP}6bpfa};XvdfV-E2EMO(nK<*<fc*6F>>7X z;Bm6a0Wp?Q$p-17ADKoy^|lPIxhH5t&zBh^$)Sn)bMmVR-zO%r{Fb2fjR7Z40aFvG zC@AaxK2b-<(RJ&NceH`c1L3+)`aPyS`k&L9abslmxucQpr-h(N)8F`}NrZ8lu!iI8 z)MIyXcePVrWZ$$iXY)D6M!xiE<*bfmx8~=!Gh2l0wt7zMfT)F?m(OgYc@=vxn%VWu zoLN>2n9|A;4X|78%A;jbua#Yp&uTB`wsIz~rB#-1tgP$;#noU(xj6MqiMJ#-HJ9&Y z*WJTdzD~ebru%0G3f8O^Gpm_V74cG9TW4*MG(g<nFAk6f)?xS;aYeDXgta!ZnbWCk zH@IBiZg5pU6Phu0G}ZZaL;1F><=2g%|4=#VpE9ry&f;!2nF<dx_1qBG@B^gxYH8YP z7mi4=Kd8tpBPs}yMK!Nw_1QKzKL0#4xEtfmxwcjp%#~-@DZ*DL8S$j4w20TdC`o94 z&)8!<u5Erz&EBH1DT^esG0jx}h6n)wd*1pMR_@#B)})(VO0^p+0a|9~Kd+D4X8)S8 zO@Sfn5sg`&43>46fG4)?4&;h4SIB$H#Q^(|bw~B(#xJ;Vln8hdSD5P0%~TK!u}SG3 ziPY7Wh<D|b;@L2Wg8VsYTaD(XQX`NjiiD#em;s*jyFI4*@}C3eW<E2qk7l+JL(E7I z8JG;3C17ytfw@J=qIB`S-Hr9hxZkZ+lfcSM?ulTfFCd@GBR4zuDwZ!WXZc)Lk|8FG z-6AD2U2aL@Z7i}+x7+o4xl9<JYWKRGdMoCdjdJaKs#ek@j>15foQOVVTBW&}SHz=& zC@!@L60ULrbQgloYM=1>SU=MlDy)RcKh4cX%*d()E9Fw&h2b_6@)Hmo@>wd;7_AEe zB6{!;{4(6bZ3GXe1HF(BdzDPLf>soTwi?LIDgcl09%3#9Gezq{q%Ld4l!NKep+TGk z;g1E58mUi<s&8UcE5=#K(`q^F67dd+f0Psx<Lr>4r|8o*35iNwH~{C%!(B;moIPpM z&Y;{$lX2v#vD{1oVipPM1PBwC(rkcmJ_u){EE)+l_)cwGABNxwm~4raivkmRjua(c z?G0161uZXkdVVfi4dU)<lJNNOCkFjqy;fD6tA>><)f_olwNj43BV;Gw$gQ{tQ6vd3 znok!OrYreMyH$}ET)!rHmk6^g2x)tkC77eYIx@S&V}djZ;;0u|ZvC$)WnKEgzkc5g z;U@D3KxC!U2rv&x3F5N2-FLcXMxh>!X3HP`fpJE0-W>Lo8qUqh$!O4E(!)R+X~8{D z%H^*M`DH=4R*dAq1h6G>Jqo(R6f{vkysPhLH+|;s{crpB7l2>J`KD)%!+||t;<tdC zWrfK8jks3$h8y~KQ|&gO#iCYUJm1K8Bmbg~aawHP|HL7Arf;BjgXW_vEzi_;dhIh` z+`<9)%LwB*f~SD{r2=qw13w|qwqUfB$YI-3Az>%mnLNM_<%y*MMO@$r<?W>ftU>pc zi)i8%4tgHIJ$yt<i5mT-LWN^&P|mYMxxX}^!z&!2EG=FNK@k6GFue2Ue=^Y4Ysq&Q z4{uNh{`GeC4Q)D)g?{NdM8Q|ig!Ql&_?vZ|sZUSe=<NfgUm|(@HITf<Du>kYQw0NG zog4A`z{sA>k@Bcyia=4*UQ5x~F!O9=4tEyW5nftlVHt{8p@2eUAMpmU#MwGw5wkH* zzmI1#Nqj-AvJgEb%cI=%BtJ&YK2k~3FB<6^M!-3;B1J5rT7cc^ix`iQ$0G?~<N<oB zqEofK$RV?~WBx4aYlH*SvNnjfOtuCLcqd$Zm2qe^Is+pUbIh~AB1<f@!YXSqi+L<! z87rj6Kt%d1L}WgPN;+>s5wmsT_P-@^B~&rK09A}9K_#94z!IJjmdFuc=tWBqi$11u z7#RHWA{NfuGVb|4k#E5gz9TP-Ah`64AC~}$XA#hK!V_;H(n$zRds`Qt{sZ7uU-VA~ z;PhKRo$D`PKKCa{BF1fBTV)+syvP(^b;{p9RTOWUUft$ue13?^^p}5z_!YJ<{g5>Z GNo@dwG#q~b diff --git a/src/sass/general.scss b/src/sass/general.scss index ce97564..e6247d6 100644 --- a/src/sass/general.scss +++ b/src/sass/general.scss @@ -23,6 +23,7 @@ font-weight: bold; width: 30px; height: 30px; + padding: 0px 5px 1px 8px; } input { diff --git a/src/sass/index.scss b/src/sass/index.scss index a19165a..3f4b123 100644 --- a/src/sass/index.scss +++ b/src/sass/index.scss @@ -50,7 +50,7 @@ body { background-color: var(--bg_color); color: var(--fg_color); - font-family: $font_0, $font_1, $font_2, $font_3; + font-family: $font_0, $font_1; font-size: 15px; line-height: 1.3; margin: 0; @@ -143,30 +143,50 @@ ul { } .verified-icon { - color: var(--icon_text); - border-radius: 50%; - flex-shrink: 0; - margin: 2px 0 3px 3px; - padding-top: 3px; - height: 11px; - width: 14px; - font-size: 8px; display: inline-block; - text-align: center; - vertical-align: middle; + width: 14px; + height: 14px; + margin-left: 2px; + + .verified-icon-circle { + position: absolute; + font-size: 15px; + } + + .verified-icon-check { + position: absolute; + font-size: 9px; + margin: 5px 3px; + } &.blue { - background-color: var(--verified_blue); + .verified-icon-circle { + color: var(--verified_blue); + } + + .verified-icon-check { + color: var(--icon_text); + } } &.business { - color: var(--bg_panel); - background-color: var(--verified_business); + .verified-icon-circle { + color: var(--verified_business); + } + + .verified-icon-check { + color: var(--bg_panel); + } } &.government { - color: var(--bg_panel); - background-color: var(--verified_government); + .verified-icon-circle { + color: var(--verified_government); + } + + .verified-icon-check { + color: var(--bg_panel); + } } } diff --git a/src/sass/navbar.scss b/src/sass/navbar.scss index 4e150e0..86bfbe7 100644 --- a/src/sass/navbar.scss +++ b/src/sass/navbar.scss @@ -59,13 +59,9 @@ nav { justify-content: flex-end; } - &.right a { - padding-left: 4px; - - &:hover { - color: var(--accent_light); - text-decoration: unset; - } + &.right a:hover { + color: var(--accent_light); + text-decoration: unset; } } @@ -81,10 +77,11 @@ nav { } } -.icon-info:before { +.icon-info { margin: 0 -3px; } .icon-cog { font-size: 15px; + padding-left: 0 !important; } diff --git a/src/sass/search.scss b/src/sass/search.scss index 444f9bb..c2adaf7 100644 --- a/src/sass/search.scss +++ b/src/sass/search.scss @@ -13,6 +13,7 @@ button { margin: 0 2px 0 0; + padding: 0px 1px 1px 4px; height: 23px; display: flex; align-items: center; @@ -35,7 +36,7 @@ background-color: var(--bg_elements); color: var(--fg_color); border: 1px solid var(--accent_border); - padding: 1px 6px 2px 6px; + padding: 1px 1px 2px 4px; font-size: 14px; cursor: pointer; margin-bottom: 2px; @@ -56,20 +57,17 @@ font-weight: initial; text-align: left; - > div { - line-height: 1.7em; - } - .checkbox-container { display: inline; padding-right: unset; - margin-bottom: unset; + margin-bottom: 5px; margin-left: 23px; } .checkbox { right: unset; left: -22px; + line-height: 1.6em; } .checkbox-container .checkbox:after { diff --git a/src/sass/tweet/video.scss b/src/sass/tweet/video.scss index 790b3da..ba77b14 100644 --- a/src/sass/tweet/video.scss +++ b/src/sass/tweet/video.scss @@ -2,7 +2,7 @@ @import "_mixins"; video { - max-height: 100%; + height: 100%; width: 100%; } @@ -13,6 +13,7 @@ video { .gallery-video.card-container { flex-direction: column; + width: 100%; } .video-container { @@ -20,9 +21,6 @@ video { min-width: 200px; max-height: 530px; margin: 0; - display: flex; - align-items: center; - justify-content: center; img { max-height: 100%; diff --git a/src/views/general.nim b/src/views/general.nim index 23681b5..2525841 100644 --- a/src/views/general.nim +++ b/src/views/general.nim @@ -52,8 +52,8 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc=""; let opensearchUrl = getUrlPrefix(cfg) & "/opensearch" buildHtml(head): - link(rel="stylesheet", type="text/css", href="/css/style.css?v=21") - link(rel="stylesheet", type="text/css", href="/css/fontello.css?v=3") + link(rel="stylesheet", type="text/css", href="/css/style.css?v=22") + link(rel="stylesheet", type="text/css", href="/css/fontello.css?v=4") if theme.len > 0: link(rel="stylesheet", type="text/css", href=(&"/css/themes/{theme}.css")) diff --git a/src/views/renderutils.nim b/src/views/renderutils.nim index fcdf06f..377a443 100644 --- a/src/views/renderutils.nim +++ b/src/views/renderutils.nim @@ -26,7 +26,9 @@ proc icon*(icon: string; text=""; title=""; class=""; href=""): VNode = template verifiedIcon*(user: User): untyped {.dirty.} = if user.verifiedType != VerifiedType.none: let lower = ($user.verifiedType).toLowerAscii() - icon "ok", class=(&"verified-icon {lower}"), title=(&"Verified {lower} account") + buildHtml(tdiv(class=(&"verified-icon {lower}"))): + icon "circle", class="verified-icon-circle", title=(&"Verified {lower} account") + icon "ok", class="verified-icon-check", title=(&"Verified {lower} account") else: text "" From 71e65c84d767ebb1d788e138d853cf0567fc970c Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Sat, 29 Nov 2025 04:34:04 +0100 Subject: [PATCH 268/409] Round video duration properly --- src/formatters.nim | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/formatters.nim b/src/formatters.nim index 3ad1da6..fc0e197 100644 --- a/src/formatters.nim +++ b/src/formatters.nim @@ -1,5 +1,5 @@ # SPDX-License-Identifier: AGPL-3.0-only -import strutils, strformat, times, uri, tables, xmltree, htmlparser, htmlgen +import strutils, strformat, times, uri, tables, xmltree, htmlparser, htmlgen, math import std/[enumerate, re] import types, utils, query @@ -157,15 +157,13 @@ proc getShortTime*(tweet: Tweet): string = proc getDuration*(video: Video): string = let ms = video.durationMs - sec = int(ms / 1000) - min = int(sec / 60) - hour = int(min / 60) - if hour > 1: + sec = int(round(ms / 1000)) + min = floorDiv(sec, 60) + hour = floorDiv(min, 60) + if hour > 0: return &"{hour}:{min mod 60}:{sec mod 60:02}" - elif min > 1: - return &"{min mod 60}:{sec mod 60:02}" else: - return &"0:{sec mod 60:02}" + return &"{min mod 60}:{sec mod 60:02}" proc getLink*(tweet: Tweet; focus=true): string = if tweet.id == 0: return From 064ec8808022abb071f93f0fc976a8aa123699dc Mon Sep 17 00:00:00 2001 From: Zed <zedeus@pm.me> Date: Sun, 30 Nov 2025 02:56:19 +0100 Subject: [PATCH 269/409] Transition to ID-only RSS GUIDs on Dec 14, 2025 Fixes #447 --- src/views/rss.nimf | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/views/rss.nimf b/src/views/rss.nimf index 23744e5..717ad99 100644 --- a/src/views/rss.nimf +++ b/src/views/rss.nimf @@ -2,6 +2,9 @@ ## SPDX-License-Identifier: AGPL-3.0-only #import strutils, xmltree, strformat, options, unicode #import ../types, ../utils, ../formatters, ../prefs +## Snowflake ID cutoff for RSS GUID format transition +## Corresponds to approximately December 14, 2025 UTC +#const guidCutoff = 2000000000000000000'i64 # #proc getTitle(tweet: Tweet; retweet: string): string = #if tweet.pinned: result = "Pinned: " @@ -101,12 +104,17 @@ ${renderRssTweet(quoteTweet, cfg)} # if link in links: continue # end if # links.add link +# let useGlobalGuid = tweet.id >= guidCutoff <item> <title>${getTitle(tweet, retweet)} @${tweet.user.username} ${getRfc822Time(tweet)} +#if useGlobalGuid: + ${tweet.id} +#else: ${urlPrefix & link} +#end if ${urlPrefix & link} # end for From 4b9aec6fdebbadfaf91786e934e43679242f5c13 Mon Sep 17 00:00:00 2001 From: Zed Date: Sun, 30 Nov 2025 02:57:34 +0100 Subject: [PATCH 270/409] Use graphTweet for cookie sessions for now --- src/api.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/api.nim b/src/api.nim index e97b4e0..acd25f1 100644 --- a/src/api.nim +++ b/src/api.nim @@ -42,7 +42,8 @@ proc userTweetsAndRepliesUrl(id: string; cursor: string): ApiReq = proc tweetDetailUrl(id: string; cursor: string): ApiReq = let cookieVars = tweetDetailVars % [id, cursor] result = ApiReq( - cookie: apiUrl(graphTweetDetail, cookieVars, tweetDetailFieldToggles), + # cookie: apiUrl(graphTweetDetail, cookieVars, tweetDetailFieldToggles), + cookie: apiUrl(graphTweet, tweetVars % [id, cursor]), oauth: apiUrl(graphTweet, tweetVars % [id, cursor]) ) From a62ec9cbb40066c2e05b5e81cb8aa61f921644d1 Mon Sep 17 00:00:00 2001 From: Zed Date: Sun, 30 Nov 2025 03:58:43 +0100 Subject: [PATCH 271/409] Normalize headers --- src/apiutils.nim | 14 ++++++-------- src/routes/media.nim | 8 ++++---- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/apiutils.nim b/src/apiutils.nim index b288141..b392ef7 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -48,21 +48,19 @@ proc getCookieHeader(authToken, ct0: string): string = proc genHeaders*(session: Session, url: Uri): Future[HttpHeaders] {.async.} = result = newHttpHeaders({ + "accept": "*/*", + "accept-encoding": "gzip", + "accept-language": "en-US,en;q=0.9", "connection": "keep-alive", "content-type": "application/json", - "x-twitter-active-user": "yes", - "x-twitter-client-language": "en", "origin": "https://x.com", - "accept-encoding": "gzip", - "accept-language": "en-US,en;q=0.5", - "accept": "*/*", - "DNT": "1", - "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36" + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36", + "x-twitter-active-user": "yes", + "x-twitter-client-language": "en" }) case session.kind of SessionKind.oauth: - result["authority"] = "api.x.com" result["authorization"] = getOauthHeader($url, session.oauthToken, session.oauthSecret) of SessionKind.cookie: result["x-twitter-auth-type"] = "OAuth2Session" diff --git a/src/routes/media.nim b/src/routes/media.nim index de51061..186b8d8 100644 --- a/src/routes/media.nim +++ b/src/routes/media.nim @@ -52,10 +52,10 @@ proc proxyMedia*(req: jester.Request; url: string): Future[HttpCode] {.async.} = "" let headers = newHttpHeaders({ - "Content-Type": res.headers["content-type", 0], - "Content-Length": contentLength, - "Cache-Control": maxAge, - "ETag": hashed + "content-type": res.headers["content-type", 0], + "content-length": contentLength, + "cache-control": maxAge, + "etag": hashed }) respond(request, headers) From 7734d976f7a6aee4d1abf16a14f0174500f04e7c Mon Sep 17 00:00:00 2001 From: Zed Date: Sun, 30 Nov 2025 04:12:38 +0100 Subject: [PATCH 272/409] Add username validation Fixes #1317 --- src/routes/timeline.nim | 1 + tests/test_profile.py | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/routes/timeline.nim b/src/routes/timeline.nim index 49c7ce2..0894d83 100644 --- a/src/routes/timeline.nim +++ b/src/routes/timeline.nim @@ -114,6 +114,7 @@ proc createTimelineRouter*(cfg: Config) = get "/@name/?@tab?/?": cond '.' notin @"name" cond @"name" notin ["pic", "gif", "video", "search", "settings", "login", "intent", "i"] + cond @"name".allCharsInSet({'a'..'z', 'A'..'Z', '0'..'9', '_'}) cond @"tab" in ["with_replies", "media", "search", ""] let prefs = cookiePrefs() diff --git a/tests/test_profile.py b/tests/test_profile.py index ea05add..cbf0256 100644 --- a/tests/test_profile.py +++ b/tests/test_profile.py @@ -15,7 +15,19 @@ protected = [ ['Poop', 'Randy', 'Social media fanatic.'] ] -invalid = [['thisprofiledoesntexist'], ['%']] +invalid = [['thisprofiledoesntexist']] + +malformed = [ + ['${userId}'], + ['$%7BuserId%7D'], # URL encoded version + ['%'], # Percent sign is invalid + ['user@name'], + ['user.name'], + ['user-name'], + ['user$name'], + ['user{name}'], + ['user name'], # space +] banner_image = [ ['mobile_test', 'profile_banners%2F82135242%2F1384108037%2F1500x500'] @@ -65,6 +77,13 @@ class ProfileTest(BaseTestCase): self.open_nitter(username) self.assert_text(f'User "{username}" not found') + @parameterized.expand(malformed) + def test_malformed_username(self, username): + """Test that malformed usernames (with invalid characters) return 404""" + self.open_nitter(username) + # Malformed usernames should return 404 page not found, not try to fetch from Twitter + self.assert_text('Page not found') + def test_suspended(self): self.open_nitter('suspendme') self.assert_text('User "suspendme" has been suspended') From 693a1894625e0ba035554d23b3e9b94e99dab8a4 Mon Sep 17 00:00:00 2001 From: Zed Date: Sun, 30 Nov 2025 05:43:17 +0100 Subject: [PATCH 273/409] Add heuristics to detect when to show "Load more" Fixes #1328 --- src/views/status.nim | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/views/status.nim b/src/views/status.nim index 71c2c67..96af807 100644 --- a/src/views/status.nim +++ b/src/views/status.nim @@ -28,14 +28,19 @@ proc renderReplyThread(thread: Chain; prefs: Prefs; path: string): VNode = if thread.hasMore: renderMoreReplies(thread) -proc renderReplies*(replies: Result[Chain]; prefs: Prefs; path: string): VNode = +proc renderReplies*(replies: Result[Chain]; prefs: Prefs; path: string; tweet: Tweet = nil): VNode = buildHtml(tdiv(class="replies", id="r")): + var hasReplies = false + var replyCount = 0 for thread in replies.content: if thread.content.len == 0: continue + hasReplies = true + replyCount += thread.content.len renderReplyThread(thread, prefs, path) - if replies.bottom.len > 0: - renderMore(Query(), replies.bottom, focus="#r") + if hasReplies and replies.bottom.len > 0: + if tweet == nil or not replies.beginning or replyCount < tweet.stats.replies: + renderMore(Query(), replies.bottom, focus="#r") proc renderConversation*(conv: Conversation; prefs: Prefs; path: string): VNode = let hasAfter = conv.after.content.len > 0 @@ -70,6 +75,6 @@ proc renderConversation*(conv: Conversation; prefs: Prefs; path: string): VNode if not conv.replies.beginning: renderNewer(Query(), getLink(conv.tweet), focus="#r") if conv.replies.content.len > 0 or conv.replies.bottom.len > 0: - renderReplies(conv.replies, prefs, path) + renderReplies(conv.replies, prefs, path, conv.tweet) renderToTop(focus="#m") From e7413858286cc4c188aef14433dbd9e76256180c Mon Sep 17 00:00:00 2001 From: Zed Date: Sun, 30 Nov 2025 18:06:22 +0100 Subject: [PATCH 274/409] Allow , in username to support multiple users Fixes #1329 --- src/routes/timeline.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/timeline.nim b/src/routes/timeline.nim index 0894d83..4d8561b 100644 --- a/src/routes/timeline.nim +++ b/src/routes/timeline.nim @@ -114,7 +114,7 @@ proc createTimelineRouter*(cfg: Config) = get "/@name/?@tab?/?": cond '.' notin @"name" cond @"name" notin ["pic", "gif", "video", "search", "settings", "login", "intent", "i"] - cond @"name".allCharsInSet({'a'..'z', 'A'..'Z', '0'..'9', '_'}) + cond @"name".allCharsInSet({'a'..'z', 'A'..'Z', '0'..'9', '_', ","}) cond @"tab" in ["with_replies", "media", "search", ""] let prefs = cookiePrefs() From 17fc2628f91f70b9bfda1915c76e94708a5197bf Mon Sep 17 00:00:00 2001 From: Zed Date: Sun, 30 Nov 2025 18:07:13 +0100 Subject: [PATCH 275/409] Minor fix --- src/routes/timeline.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/timeline.nim b/src/routes/timeline.nim index 4d8561b..2ac87bb 100644 --- a/src/routes/timeline.nim +++ b/src/routes/timeline.nim @@ -114,7 +114,7 @@ proc createTimelineRouter*(cfg: Config) = get "/@name/?@tab?/?": cond '.' notin @"name" cond @"name" notin ["pic", "gif", "video", "search", "settings", "login", "intent", "i"] - cond @"name".allCharsInSet({'a'..'z', 'A'..'Z', '0'..'9', '_', ","}) + cond @"name".allCharsInSet({'a'..'z', 'A'..'Z', '0'..'9', '_', ','}) cond @"tab" in ["with_replies", "media", "search", ""] let prefs = cookiePrefs() From 663f5a52e13783075287e779b1b947b862259e7e Mon Sep 17 00:00:00 2001 From: Zed Date: Sat, 6 Dec 2025 05:00:34 +0100 Subject: [PATCH 276/409] Improve headers --- src/apiutils.nim | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/apiutils.nim b/src/apiutils.nim index b392ef7..7a49e3e 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -56,7 +56,8 @@ proc genHeaders*(session: Session, url: Uri): Future[HttpHeaders] {.async.} = "origin": "https://x.com", "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36", "x-twitter-active-user": "yes", - "x-twitter-client-language": "en" + "x-twitter-client-language": "en", + "priority": "u=1, i" }) case session.kind @@ -66,6 +67,12 @@ proc genHeaders*(session: Session, url: Uri): Future[HttpHeaders] {.async.} = result["x-twitter-auth-type"] = "OAuth2Session" result["x-csrf-token"] = session.ct0 result["cookie"] = getCookieHeader(session.authToken, session.ct0) + result["sec-ch-ua"] = """"Google Chrome";v="142", "Chromium";v="142", "Not A(Brand";v="24"""" + result["sec-ch-ua-mobile"] = "?0" + result["sec-ch-ua-platform"] = "Windows" + result["sec-fetch-dest"] = "empty" + result["sec-fetch-mode"] = "cors" + result["sec-fetch-site"] = "same-site" if disableTid: result["authorization"] = bearerToken2 else: From 51b54852dcf617c6131376a469c537ab6b8326e3 Mon Sep 17 00:00:00 2001 From: Zed Date: Sat, 6 Dec 2025 05:15:01 +0100 Subject: [PATCH 277/409] Add preliminary support for nitter-proxy --- nitter.example.conf | 1 + src/apiutils.nim | 13 ++++++++++++- src/config.nim | 1 + src/nitter.nim | 1 + src/types.nim | 1 + 5 files changed, 16 insertions(+), 1 deletion(-) diff --git a/nitter.example.conf b/nitter.example.conf index dfdaf50..7e4c846 100644 --- a/nitter.example.conf +++ b/nitter.example.conf @@ -26,6 +26,7 @@ enableRSS = true # set this to false to disable RSS feeds enableDebug = false # enable request logs and debug endpoints (/.sessions) proxy = "" # http/https url, SOCKS proxies are not supported proxyAuth = "" +apiProxy = "" # nitter-proxy host, e.g. localhost:7000 disableTid = false # enable this if cookie-based auth is failing # Change default preferences here, see src/prefs_impl.nim for a complete list diff --git a/src/apiutils.nim b/src/apiutils.nim index 7a49e3e..ddb5027 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -13,10 +13,17 @@ const var pool: HttpPool disableTid: bool + apiProxy: string proc setDisableTid*(disable: bool) = disableTid = disable +proc setApiProxy*(url: string) = + if url.len > 0: + apiProxy = url.strip(chars={'/'}) & "/" + if "http" notin apiProxy: + apiProxy = "http://" & apiProxy + proc toUrl(req: ApiReq; sessionKind: SessionKind): Uri = case sessionKind of oauth: @@ -99,7 +106,11 @@ template fetchImpl(result, fetchBody) {.dirty.} = var resp: AsyncResponse pool.use(await genHeaders(session, url)): template getContent = - resp = await c.get($url) + # TODO: this is a temporary simple implementation + if apiProxy.len > 0: + resp = await c.get(($url).replace("https://", apiProxy)) + else: + resp = await c.get($url) result = await resp.body getContent() diff --git a/src/config.nim b/src/config.nim index 571508b..2b38d86 100644 --- a/src/config.nim +++ b/src/config.nim @@ -41,6 +41,7 @@ proc getConfig*(path: string): (Config, parseCfg.Config) = enableDebug: cfg.get("Config", "enableDebug", false), proxy: cfg.get("Config", "proxy", ""), proxyAuth: cfg.get("Config", "proxyAuth", ""), + apiProxy: cfg.get("Config", "apiProxy", ""), disableTid: cfg.get("Config", "disableTid", false) ) diff --git a/src/nitter.nim b/src/nitter.nim index e6d66ab..91a3a9f 100644 --- a/src/nitter.nim +++ b/src/nitter.nim @@ -37,6 +37,7 @@ setHmacKey(cfg.hmacKey) setProxyEncoding(cfg.base64Media) setMaxHttpConns(cfg.httpMaxConns) setHttpProxy(cfg.proxy, cfg.proxyAuth) +setApiProxy(cfg.apiProxy) setDisableTid(cfg.disableTid) initAboutPage(cfg.staticDir) diff --git a/src/types.nim b/src/types.nim index 815e223..c994148 100644 --- a/src/types.nim +++ b/src/types.nim @@ -275,6 +275,7 @@ type enableDebug*: bool proxy*: string proxyAuth*: string + apiProxy*: string disableTid*: bool rssCacheTime*: int From baeaf685d32098cd90ae1424cf8a19f3de9b0e2c Mon Sep 17 00:00:00 2001 From: jackyzy823 Date: Mon, 8 Dec 2025 17:05:08 +0800 Subject: [PATCH 278/409] Make maxConcurrentReqs configurable (#1341) --- nitter.example.conf | 1 + src/auth.nim | 11 +++++++---- src/config.nim | 3 ++- src/nitter.nim | 1 + src/types.nim | 1 + 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/nitter.example.conf b/nitter.example.conf index 7e4c846..4a6a026 100644 --- a/nitter.example.conf +++ b/nitter.example.conf @@ -28,6 +28,7 @@ proxy = "" # http/https url, SOCKS proxies are not supported proxyAuth = "" apiProxy = "" # nitter-proxy host, e.g. localhost:7000 disableTid = false # enable this if cookie-based auth is failing +maxConcurrentReqs = 2 # max requests at a time per session to avoid race conditions # Change default preferences here, see src/prefs_impl.nim for a complete list [Preferences] diff --git a/src/auth.nim b/src/auth.nim index 5d7ef0e..d801489 100644 --- a/src/auth.nim +++ b/src/auth.nim @@ -3,14 +3,17 @@ import std/[asyncdispatch, times, json, random, strutils, tables, packedsets, os import types, consts import experimental/parser/session -# max requests at a time per session to avoid race conditions -const - maxConcurrentReqs = 2 - hourInSeconds = 60 * 60 +const hourInSeconds = 60 * 60 var sessionPool: seq[Session] enableLogging = false + # max requests at a time per session to avoid race conditions + maxConcurrentReqs = 2 + +proc setMaxConcurrentReqs*(reqs: int) = + if reqs > 0: + maxConcurrentReqs = reqs template log(str: varargs[string, `$`]) = echo "[sessions] ", str.join("") diff --git a/src/config.nim b/src/config.nim index 2b38d86..8cb334a 100644 --- a/src/config.nim +++ b/src/config.nim @@ -42,7 +42,8 @@ proc getConfig*(path: string): (Config, parseCfg.Config) = proxy: cfg.get("Config", "proxy", ""), proxyAuth: cfg.get("Config", "proxyAuth", ""), apiProxy: cfg.get("Config", "apiProxy", ""), - disableTid: cfg.get("Config", "disableTid", false) + disableTid: cfg.get("Config", "disableTid", false), + maxConcurrentReqs: cfg.get("Config", "maxConcurrentReqs", 2) ) return (conf, cfg) diff --git a/src/nitter.nim b/src/nitter.nim index 91a3a9f..e2d6bec 100644 --- a/src/nitter.nim +++ b/src/nitter.nim @@ -39,6 +39,7 @@ setMaxHttpConns(cfg.httpMaxConns) setHttpProxy(cfg.proxy, cfg.proxyAuth) setApiProxy(cfg.apiProxy) setDisableTid(cfg.disableTid) +setMaxConcurrentReqs(cfg.maxConcurrentReqs) initAboutPage(cfg.staticDir) waitFor initRedisPool(cfg) diff --git a/src/types.nim b/src/types.nim index c994148..90487ab 100644 --- a/src/types.nim +++ b/src/types.nim @@ -277,6 +277,7 @@ type proxyAuth*: string apiProxy*: string disableTid*: bool + maxConcurrentReqs*: int rssCacheTime*: int listCacheTime*: int From a92e79ebc3581702dc427434a782a5fc1d28cc91 Mon Sep 17 00:00:00 2001 From: yav <150280490+796176@users.noreply.github.com> Date: Wed, 24 Dec 2025 10:22:20 +0300 Subject: [PATCH 279/409] Fix the checkmark position (#1347) Co-authored-by: yav <796176@protonmail.com> --- src/views/profile.nim | 1 + src/views/renderutils.nim | 1 - src/views/timeline.nim | 1 + src/views/tweet.nim | 2 ++ 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/views/profile.nim b/src/views/profile.nim index 2b2e410..ee3f71d 100644 --- a/src/views/profile.nim +++ b/src/views/profile.nim @@ -26,6 +26,7 @@ proc renderUserCard*(user: User; prefs: Prefs): VNode = tdiv(class="profile-card-tabs-name"): linkUser(user, class="profile-card-fullname") + verifiedIcon(user) linkUser(user, class="profile-card-username") tdiv(class="profile-card-extra"): diff --git a/src/views/renderutils.nim b/src/views/renderutils.nim index 377a443..a5fe3b2 100644 --- a/src/views/renderutils.nim +++ b/src/views/renderutils.nim @@ -42,7 +42,6 @@ proc linkUser*(user: User, class=""): VNode = buildHtml(a(href=href, class=class, title=nameText)): text nameText if isName: - verifiedIcon(user) if user.protected: text " " icon "lock", title="Protected account" diff --git a/src/views/timeline.nim b/src/views/timeline.nim index a205c04..fee45bc 100644 --- a/src/views/timeline.nim +++ b/src/views/timeline.nim @@ -66,6 +66,7 @@ proc renderUser(user: User; prefs: Prefs): VNode = tdiv(class="tweet-name-row"): tdiv(class="fullname-and-username"): linkUser(user, class="fullname") + verifiedIcon(user) linkUser(user, class="username") tdiv(class="tweet-content media-body", dir="auto"): diff --git a/src/views/tweet.nim b/src/views/tweet.nim index 58d03a9..d680509 100644 --- a/src/views/tweet.nim +++ b/src/views/tweet.nim @@ -31,6 +31,7 @@ proc renderHeader(tweet: Tweet; retweet: string; pinned: bool; prefs: Prefs): VN tdiv(class="tweet-name-row"): tdiv(class="fullname-and-username"): linkUser(tweet.user, class="fullname") + verifiedIcon(tweet.user) linkUser(tweet.user, class="username") span(class="tweet-date"): @@ -235,6 +236,7 @@ proc renderQuote(quote: Tweet; prefs: Prefs; path: string): VNode = tdiv(class="fullname-and-username"): renderMiniAvatar(quote.user, prefs) linkUser(quote.user, class="fullname") + verifiedIcon(quote.user) linkUser(quote.user, class="username") span(class="tweet-date"): From a45227b8835719dfb443600052d69374db8b515c Mon Sep 17 00:00:00 2001 From: cmj <129799+cmj@users.noreply.github.com> Date: Thu, 29 Jan 2026 08:27:41 -0800 Subject: [PATCH 280/409] Add user-agent to guest_token request (#1359) --- tools/get_session.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/get_session.py b/tools/get_session.py index 9f91483..da03322 100644 --- a/tools/get_session.py +++ b/tools/get_session.py @@ -21,7 +21,10 @@ def auth(username, password, otp_secret): guest_token = requests.post( "https://api.twitter.com/1.1/guest/activate.json", - headers={'Authorization': bearer_token} + headers={ + 'Authorization': bearer_token, + "User-Agent": "TwitterAndroid/10.21.0-release.0 (310210000-r-0) ONEPLUS+A3010/9" + } ).json().get('guest_token') if not guest_token: From 33dd9b66683e6838d6da16f5563c20de5d32ef5a Mon Sep 17 00:00:00 2001 From: Zed Date: Fri, 6 Feb 2026 20:32:44 +0100 Subject: [PATCH 281/409] Fix /pic/ exploit --- src/routes/media.nim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/routes/media.nim b/src/routes/media.nim index 186b8d8..011d0f3 100644 --- a/src/routes/media.nim +++ b/src/routes/media.nim @@ -93,6 +93,8 @@ proc createMediaRouter*(cfg: Config) = get re"^\/pic\/orig\/(enc)?\/?(.+)": var url = decoded(request, 1) + cond "amplify_video" notin url + if "twimg.com" notin url: url.insert(twimg) if not url.startsWith(https): @@ -107,6 +109,8 @@ proc createMediaRouter*(cfg: Config) = get re"^\/pic\/(enc)?\/?(.+)": var url = decoded(request, 1) + cond "amplify_video" notin url + if "twimg.com" notin url: url.insert(twimg) if not url.startsWith(https): From 0a6e79e6263b0c3aba2c0fec3b6af906d71a1622 Mon Sep 17 00:00:00 2001 From: Zed Date: Mon, 9 Feb 2026 02:55:07 +0100 Subject: [PATCH 282/409] Add bulk script create_sessions_browser.py --- tools/create_session_browser.py | 135 ++++++++++++------- tools/create_sessions_browser.py | 219 +++++++++++++++++++++++++++++++ 2 files changed, 307 insertions(+), 47 deletions(-) create mode 100644 tools/create_sessions_browser.py diff --git a/tools/create_session_browser.py b/tools/create_session_browser.py index 3a05cb1..eb3936b 100644 --- a/tools/create_session_browser.py +++ b/tools/create_session_browser.py @@ -20,73 +20,112 @@ Output: {"kind": "cookie", "username": "...", "id": "...", "auth_token": "...", "ct0": "..."} """ -import sys -import json import asyncio -import pyotp -import nodriver as uc +import json import os +import sys + +import nodriver as uc +import pyotp async def login_and_get_cookies(username, password, totp_seed=None, headless=False): """Authenticate with X.com and extract session cookies""" # Note: headless mode may increase detection risk from bot-detection systems browser = await uc.start(headless=headless) - tab = await browser.get('https://x.com/i/flow/login') + tab = await browser.get("https://x.com/i/flow/login") try: # Enter username - print('[*] Entering username...', file=sys.stderr) - username_input = await tab.find('input[autocomplete="username"]', timeout=10) - await username_input.send_keys(username + '\n') - await asyncio.sleep(1) + print(f"[*] Entering username {username}...", file=sys.stderr) + + retry = 0 + while retry < 5: + username_input = await tab.find( + 'input[autocomplete="username"]', timeout=10 + ) + + pos = await username_input.get_position() + await tab.mouse_move(pos.x, pos.y, steps=50, flash=True) + await asyncio.sleep(0.1) + + await username_input.click() + await asyncio.sleep(0.5) + await username_input.send_keys(username) + await asyncio.sleep(0.2) + await username_input.send_keys("\n") + await asyncio.sleep(2) + + page_content = await tab.get_content() + if "Could not log you in" in page_content: + retry += 1 + wait = retry * 10 + print(f"Retrying in {wait} seconds...") + await asyncio.sleep(wait) + else: + break # Enter password - print('[*] Entering password...', file=sys.stderr) - password_input = await tab.find('input[autocomplete="current-password"]', timeout=15) - await password_input.send_keys(password + '\n') - await asyncio.sleep(2) + print("[*] Entering password...", file=sys.stderr) + pretry = 0 + while pretry < 5: + password_input = await tab.find( + 'input[autocomplete="current-password"]', timeout=15 + ) + await password_input.click() + await asyncio.sleep(0.5) + await password_input.send_keys(password) + await asyncio.sleep(0.2) + await password_input.send_keys("\n") + await asyncio.sleep(2) + + page_content = await tab.get_content() + if "Could not log you in" in page_content: + pretry += 1 + wait = pretry * 10 + print(f"Retrying in {wait} seconds...") + await asyncio.sleep(wait) + else: + break # Handle 2FA if needed page_content = await tab.get_content() - if 'verification code' in page_content or 'Enter code' in page_content: + if "verification code" in page_content or "Enter code" in page_content: if not totp_seed: - raise Exception('2FA required but no TOTP seed provided') + raise Exception("2FA required but no TOTP seed provided") - print('[*] 2FA detected, entering code...', file=sys.stderr) + print("[*] 2FA detected, entering code...", file=sys.stderr) totp_code = pyotp.TOTP(totp_seed).now() code_input = await tab.select('input[type="text"]') - await code_input.send_keys(totp_code + '\n') + await code_input.send_keys(totp_code + "\n") await asyncio.sleep(3) # Get cookies - print('[*] Retrieving cookies...', file=sys.stderr) + print("[*] Retrieving cookies...", file=sys.stderr) for _ in range(20): # 20 second timeout cookies = await browser.cookies.get_all() cookies_dict = {cookie.name: cookie.value for cookie in cookies} - if 'auth_token' in cookies_dict and 'ct0' in cookies_dict: - print('[*] Found both cookies', file=sys.stderr) - + if "auth_token" in cookies_dict and "ct0" in cookies_dict: # Extract ID from twid cookie (may be URL-encoded) user_id = None - if 'twid' in cookies_dict: - twid = cookies_dict['twid'] + if "twid" in cookies_dict: + twid = cookies_dict["twid"] # Try to extract the ID from twid (format: u%3D or u=) - if 'u%3D' in twid: - user_id = twid.split('u%3D')[1].split('&')[0].strip('"') - elif 'u=' in twid: - user_id = twid.split('u=')[1].split('&')[0].strip('"') + if "u%3D" in twid: + user_id = twid.split("u%3D")[1].split("&")[0].strip('"') + elif "u=" in twid: + user_id = twid.split("u=")[1].split("&")[0].strip('"') - cookies_dict['username'] = username + cookies_dict["username"] = username if user_id: - cookies_dict['id'] = user_id + cookies_dict["id"] = user_id return cookies_dict await asyncio.sleep(1) - raise Exception('Timeout waiting for cookies') + raise Exception("Timeout waiting for cookies") finally: browser.stop() @@ -94,7 +133,9 @@ async def login_and_get_cookies(username, password, totp_seed=None, headless=Fal async def main(): if len(sys.argv) < 3: - print('Usage: python3 create_session_browser.py username password [totp_seed] [--append file.jsonl] [--headless]') + print( + "Usage: python3 create_session_browser.py username password [totp_seed] [--append file.jsonl] [--headless]" + ) sys.exit(1) username = sys.argv[1] @@ -107,49 +148,49 @@ async def main(): i = 3 while i < len(sys.argv): arg = sys.argv[i] - if arg == '--append': + if arg == "--append": if i + 1 < len(sys.argv): append_file = sys.argv[i + 1] i += 2 # Skip '--append' and filename else: - print('[!] Error: --append requires a filename', file=sys.stderr) + print("[!] Error: --append requires a filename", file=sys.stderr) sys.exit(1) - elif arg == '--headless': + elif arg == "--headless": headless = True i += 1 - elif not arg.startswith('--'): - if totp_seed is None: + elif not arg.startswith("--"): + if totp_seed is None: totp_seed = arg i += 1 else: # Unkown args - print(f'[!] Warning: Unknown argument: {arg}', file=sys.stderr) + print(f"[!] Warning: Unknown argument: {arg}", file=sys.stderr) i += 1 try: cookies = await login_and_get_cookies(username, password, totp_seed, headless) session = { - 'kind': 'cookie', - 'username': cookies['username'], - 'id': cookies.get('id'), - 'auth_token': cookies['auth_token'], - 'ct0': cookies['ct0'] + "kind": "cookie", + "username": cookies["username"], + "id": cookies.get("id"), + "auth_token": cookies["auth_token"], + "ct0": cookies["ct0"], } output = json.dumps(session) if append_file: - with open(append_file, 'a') as f: - f.write(output + '\n') - print(f'✓ Session appended to {append_file}', file=sys.stderr) + with open(append_file, "a") as f: + f.write(output + "\n") + print(f"✓ Session appended to {append_file}", file=sys.stderr) else: print(output) os._exit(0) except Exception as error: - print(f'[!] Error: {error}', file=sys.stderr) + print(f"[!] Error: {error}", file=sys.stderr) sys.exit(1) -if __name__ == '__main__': +if __name__ == "__main__": asyncio.run(main()) diff --git a/tools/create_sessions_browser.py b/tools/create_sessions_browser.py new file mode 100644 index 0000000..003eec3 --- /dev/null +++ b/tools/create_sessions_browser.py @@ -0,0 +1,219 @@ +#!/usr/bin/env python3 +""" +Requirements: + pip install -r tools/requirements.txt + +Usage: + python3 tools/create_sessions_browser.py [--append sessions.jsonl] [--headless] [--delay] + +Examples: + # Output to terminal + python3 tools/create_sessions_browser.py + + # Append to sessions.jsonl + python3 tools/create_sessions_browser.py --append sessions.jsonl + + # Add 5 second delay between sessions (default: 1) + python3 tools/create_sessions_browser.py --delay 5 + + # Headless mode (may increase detection risk) + python3 tools/create_sessions_browser.py --headless + +Input (accounts_file): + [{"username": "user", "password": "pass", "totp": "totp_code"}, {...}, ...] + +Output: + {"kind": "cookie", "username": "...", "id": "...", "auth_token": "...", "ct0": "..."} + {"kind": "cookie", "username": "...", "id": "...", "auth_token": "...", "ct0": "..."} + ... +""" + +import asyncio +import json +import sys +from time import sleep + +import nodriver as uc +import pyotp + + +async def login_and_get_cookies(account, headless=False): + """Authenticate with X.com and extract session cookies""" + # Note: headless mode may increase detection risk from bot-detection systems + browser = await uc.start(headless=headless) + tab = await browser.get("https://x.com/i/flow/login") + + username = account["username"] + password = account["password"] + totp_seed = account["totp"] + + try: + # Enter username + print(f"[*] Entering username {username}...", file=sys.stderr) + + retry = 0 + while retry < 5: + username_input = await tab.find( + 'input[autocomplete="username"]', timeout=10 + ) + + pos = await username_input.get_position() + await tab.mouse_move(pos.x, pos.y, steps=50, flash=True) + await asyncio.sleep(0.1) + + await username_input.click() + await asyncio.sleep(0.5) + await username_input.send_keys(username) + await asyncio.sleep(0.2) + await username_input.send_keys("\n") + await asyncio.sleep(2) + + page_content = await tab.get_content() + if "Could not log you in" in page_content: + retry += 1 + wait = retry * 10 + print(f"Retrying in {wait} seconds...") + await asyncio.sleep(wait) + else: + break + + # Enter password + print("[*] Entering password...", file=sys.stderr) + pretry = 0 + while pretry < 5: + password_input = await tab.find( + 'input[autocomplete="current-password"]', timeout=15 + ) + await password_input.click() + await asyncio.sleep(0.5) + await password_input.send_keys(password) + await asyncio.sleep(0.2) + await password_input.send_keys("\n") + await asyncio.sleep(2) + + page_content = await tab.get_content() + if "Could not log you in" in page_content: + pretry += 1 + wait = pretry * 10 + print(f"Retrying in {wait} seconds...") + await asyncio.sleep(wait) + else: + break + + # Handle 2FA if needed + page_content = await tab.get_content() + if "verification code" in page_content or "Enter code" in page_content: + if not totp_seed: + raise Exception("2FA required but no TOTP seed provided") + + print("[*] 2FA detected, entering code...", file=sys.stderr) + totp_code = pyotp.TOTP(totp_seed).now() + code_input = await tab.select('input[type="text"]') + await code_input.send_keys(totp_code + "\n") + await asyncio.sleep(3) + + # Get cookies + print("[*] Retrieving cookies...", file=sys.stderr) + for _ in range(20): # 20 second timeout + cookies = await browser.cookies.get_all() + cookies_dict = {cookie.name: cookie.value for cookie in cookies} + + if "auth_token" in cookies_dict and "ct0" in cookies_dict: + # Extract ID from twid cookie (may be URL-encoded) + user_id = None + if "twid" in cookies_dict: + twid = cookies_dict["twid"] + # Try to extract the ID from twid (format: u%3D or u=) + if "u%3D" in twid: + user_id = twid.split("u%3D")[1].split("&")[0].strip('"') + elif "u=" in twid: + user_id = twid.split("u=")[1].split("&")[0].strip('"') + + cookies_dict["username"] = username + if user_id: + cookies_dict["id"] = user_id + + return cookies_dict + + await asyncio.sleep(1) + + raise Exception("Timeout waiting for cookies") + + finally: + browser.stop() + + +async def main(): + if len(sys.argv) < 2: + print( + "Usage: python3 create_sessions_browser.py [--append sessions.jsonl] [--headless]" + ) + sys.exit(1) + + input = sys.argv[1] + append_file = None + headless = False + delay = 1 + + # Parse optional arguments + i = 2 + while i < len(sys.argv): + arg = sys.argv[i] + if arg == "--append": + if i + 1 < len(sys.argv): + append_file = sys.argv[i + 1] + i += 2 # Skip '--append' and filename + else: + print("[!] Error: --append requires a filename", file=sys.stderr) + sys.exit(1) + elif arg == "--headless": + headless = True + i += 1 + elif arg == "--delay": + delay = int(sys.argv[i + 1]) + i += 2 + else: + # Unkown args + print(f"[!] Warning: Unknown argument: {arg}", file=sys.stderr) + i += 1 + + accounts = [] + with open(input) as f: + accounts = json.load(f) + + if len(accounts) == 0: + print("no accounts in file") + sys.exit(0) + + sessions = 0 + for acc in accounts: + sessions += 1 + try: + cookies = await login_and_get_cookies(acc, headless) + session = { + "kind": "cookie", + "username": cookies["username"], + "id": cookies.get("id"), + "auth_token": cookies["auth_token"], + "ct0": cookies["ct0"], + } + + if append_file: + with open(append_file, "a") as f: + f.write(json.dumps(session) + "\n") + else: + print(json.dumps(session)) + + print(f"Progress: {sessions} / {len(accounts)}") + if sessions < len(accounts): + print("Waiting", delay, "seconds") + sleep(delay) + except Exception as error: + print( + f"[!] Error getting session for {acc["username"]}, skipping: {error}", + file=sys.stderr, + ) + + +if __name__ == "__main__": + asyncio.run(main()) From 5d28bd18c631417129a6c6a1ed45e20ef4528269 Mon Sep 17 00:00:00 2001 From: Zed Date: Mon, 9 Feb 2026 17:32:03 +0100 Subject: [PATCH 283/409] Add preference for configuring sticky navbar Fixes #1354 --- src/nitter.nim | 4 ++-- src/prefs_impl.nim | 3 +++ src/routes/router_utils.nim | 7 +------ src/routes/unsupported.nim | 2 +- src/sass/index.scss | 5 ++++- src/sass/navbar.scss | 5 ++++- src/sass/profile/_base.scss | 6 +++++- src/sass/tweet/thread.scss | 6 ++++-- src/views/general.nim | 3 ++- 9 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/nitter.nim b/src/nitter.nim index e2d6bec..9e20ecb 100644 --- a/src/nitter.nim +++ b/src/nitter.nim @@ -66,10 +66,10 @@ settings: routes: get "/": - resp renderMain(renderSearch(), request, cfg, themePrefs()) + resp renderMain(renderSearch(), request, cfg, cookiePrefs()) get "/about": - resp renderMain(renderAbout(), request, cfg, themePrefs()) + resp renderMain(renderAbout(), request, cfg, cookiePrefs()) get "/explore": redirect("/about") diff --git a/src/prefs_impl.nim b/src/prefs_impl.nim index 8e2ac8f..e55c2b8 100644 --- a/src/prefs_impl.nim +++ b/src/prefs_impl.nim @@ -60,6 +60,9 @@ genPrefs: stickyProfile(checkbox, true): "Make profile sidebar stick to top" + stickyNav(checkbox, true): + "Keep navbar fixed to top" + bidiSupport(checkbox, false): "Support bidirectional text (makes clicking on tweets harder)" diff --git a/src/routes/router_utils.nim b/src/routes/router_utils.nim index a071a0d..34fd163 100644 --- a/src/routes/router_utils.nim +++ b/src/routes/router_utils.nim @@ -17,13 +17,8 @@ template cookiePrefs*(): untyped {.dirty.} = template cookiePref*(pref): untyped {.dirty.} = getPref(cookies(request), pref) -template themePrefs*(): Prefs = - var res = defaultPrefs - res.theme = cookiePref(theme) - res - template showError*(error: string; cfg: Config): string = - renderMain(renderError(error), request, cfg, themePrefs(), "Error") + renderMain(renderError(error), request, cfg, cookiePrefs(), "Error") template getPath*(): untyped {.dirty.} = $(parseUri(request.path) ? filterParams(request.params)) diff --git a/src/routes/unsupported.nim b/src/routes/unsupported.nim index 362b36b..e06a183 100644 --- a/src/routes/unsupported.nim +++ b/src/routes/unsupported.nim @@ -10,7 +10,7 @@ export feature proc createUnsupportedRouter*(cfg: Config) = router unsupported: template feature {.dirty.} = - resp renderMain(renderFeature(), request, cfg, themePrefs()) + resp renderMain(renderFeature(), request, cfg, cookiePrefs()) get "/about/feature": feature() get "/login/?@i?": feature() diff --git a/src/sass/index.scss b/src/sass/index.scss index 3f4b123..c12b814 100644 --- a/src/sass/index.scss +++ b/src/sass/index.scss @@ -115,11 +115,14 @@ ul { display: flex; flex-wrap: wrap; box-sizing: border-box; - padding-top: 50px; margin: auto; min-height: 100vh; } +body.fixed-nav .container { + padding-top: 50px; +} + .icon-container { display: inline; } diff --git a/src/sass/navbar.scss b/src/sass/navbar.scss index 86bfbe7..c999022 100644 --- a/src/sass/navbar.scss +++ b/src/sass/navbar.scss @@ -3,7 +3,6 @@ nav { display: flex; align-items: center; - position: fixed; background-color: var(--bg_overlays); box-shadow: 0 0 4px $shadow; padding: 0; @@ -16,6 +15,10 @@ nav { .icon-button button { color: var(--fg_nav); } + + body.fixed-nav & { + position: fixed; + } } .inner-nav { diff --git a/src/sass/profile/_base.scss b/src/sass/profile/_base.scss index b7f33e6..3abc736 100644 --- a/src/sass/profile/_base.scss +++ b/src/sass/profile/_base.scss @@ -39,7 +39,11 @@ text-align: left; vertical-align: top; max-width: 32%; - top: 50px; + top: 0; + + body.fixed-nav & { + top: 50px; + } } .profile-result { diff --git a/src/sass/tweet/thread.scss b/src/sass/tweet/thread.scss index 9d2fb64..d9bc457 100644 --- a/src/sass/tweet/thread.scss +++ b/src/sass/tweet/thread.scss @@ -16,8 +16,10 @@ .main-tweet, .replies { - padding-top: 50px; - margin-top: -50px; + body.fixed-nav & { + padding-top: 50px; + margin-top: -50px; + } } .main-tweet .tweet-content { diff --git a/src/views/general.nim b/src/views/general.nim index 2525841..3bae9d3 100644 --- a/src/views/general.nim +++ b/src/views/general.nim @@ -131,7 +131,8 @@ proc renderMain*(body: VNode; req: Request; cfg: Config; prefs=defaultPrefs; renderHead(prefs, cfg, req, titleText, desc, video, images, banner, ogTitle, rss, twitterLink) - body: + let bodyClass = if prefs.stickyNav: "fixed-nav" else: "" + body(class=bodyClass): renderNavbar(cfg, req, rss, twitterLink) tdiv(class="container"): From db36f75519b2295c184d107f29210141bdd01082 Mon Sep 17 00:00:00 2001 From: Zed Date: Mon, 9 Feb 2026 20:23:31 +0100 Subject: [PATCH 284/409] Support restoring preferences via new prefs param Fixes #1352 Fixes #553 Fixes #249 --- src/nitter.nim | 5 +++++ src/prefs.nim | 9 +++++++-- src/prefs_impl.nim | 30 ++++++++++++++++++++++++++++++ src/routes/preferences.nim | 4 +++- src/routes/router_utils.nim | 32 +++++++++++++++++++++++++++++--- src/sass/index.scss | 18 ++++++++++++------ src/sass/inputs.scss | 12 ++++++++++++ src/utils.nim | 2 +- src/views/preferences.nim | 9 ++++++++- 9 files changed, 107 insertions(+), 14 deletions(-) diff --git a/src/nitter.nim b/src/nitter.nim index 9e20ecb..442a8c0 100644 --- a/src/nitter.nim +++ b/src/nitter.nim @@ -65,6 +65,11 @@ settings: reusePort = true routes: + before: + # skip all file URLs + cond "." notin request.path + applyUrlPrefs() + get "/": resp renderMain(renderSearch(), request, cfg, cookiePrefs()) diff --git a/src/prefs.nim b/src/prefs.nim index fa40a6d..573ccae 100644 --- a/src/prefs.nim +++ b/src/prefs.nim @@ -1,10 +1,10 @@ # SPDX-License-Identifier: AGPL-3.0-only -import tables +import tables, strutils, base64 import types, prefs_impl from config import get from parsecfg import nil -export genUpdatePrefs, genResetPrefs +export genUpdatePrefs, genResetPrefs, genApplyPrefs var defaultPrefs*: Prefs @@ -20,3 +20,8 @@ template getPref*(cookies: Table[string, string], pref): untyped = var res = defaultPrefs.`pref` genCookiePref(cookies, pref, res) res + +proc encodePrefs*(prefs: Prefs): string = + var encPairs: seq[string] + genEncodePrefs(prefs) + encode(encPairs.join("&"), safe=true) diff --git a/src/prefs_impl.nim b/src/prefs_impl.nim index e55c2b8..2faf8ef 100644 --- a/src/prefs_impl.nim +++ b/src/prefs_impl.nim @@ -205,6 +205,36 @@ macro genResetPrefs*(): untyped = result.add quote do: savePref(`name`, "", `req`, expire=true) +macro genEncodePrefs*(prefs): untyped = + result = nnkStmtList.newTree() + for pref in allPrefs(): + let + name = newLit(pref.name) + ident = ident(pref.name) + kind = newLit(pref.kind) + defaultIdent = nnkDotExpr.newTree(ident("defaultPrefs"), ident(pref.name)) + + result.add quote do: + when `kind` == checkbox: + if `prefs`.`ident` != `defaultIdent`: + if `prefs`.`ident`: + encPairs.add `name` & "=on" + else: + encPairs.add `name` & "=" + else: + if `prefs`.`ident` != `defaultIdent`: + encPairs.add `name` & "=" & `prefs`.`ident` + +macro genApplyPrefs*(params, req): untyped = + result = nnkStmtList.newTree() + for pref in allPrefs(): + let name = newLit(pref.name) + result.add quote do: + if `name` in `params`: + savePref(`name`, `params`[`name`], `req`) + else: + savePref(`name`, "", `req`, expire=true) + macro genPrefsType*(): untyped = let name = nnkPostfix.newTree(ident("*"), ident("Prefs")) result = quote do: diff --git a/src/routes/preferences.nim b/src/routes/preferences.nim index b8af03d..345ff34 100644 --- a/src/routes/preferences.nim +++ b/src/routes/preferences.nim @@ -20,7 +20,9 @@ proc createPrefRouter*(cfg: Config) = get "/settings": let prefs = cookiePrefs() - html = renderPreferences(prefs, refPath(), findThemes(cfg.staticDir)) + prefsCode = encodePrefs(prefs) + prefsUrl = getUrlPrefix(cfg) & "/?prefs=" & prefsCode + html = renderPreferences(prefs, refPath(), findThemes(cfg.staticDir), prefsUrl) resp renderMain(html, request, cfg, prefs, "Preferences") get "/settings/@i?": diff --git a/src/routes/router_utils.nim b/src/routes/router_utils.nim index 34fd163..2ef248a 100644 --- a/src/routes/router_utils.nim +++ b/src/routes/router_utils.nim @@ -1,15 +1,15 @@ # SPDX-License-Identifier: AGPL-3.0-only -import strutils, sequtils, uri, tables, json +import strutils, sequtils, uri, tables, json, base64 from jester import Request, cookies import ../views/general import ".."/[utils, prefs, types] -export utils, prefs, types, uri +export utils, prefs, types, uri, base64 template savePref*(pref, value: string; req: Request; expire=false) = if not expire or pref in cookies(req): setCookie(pref, value, daysForward(when expire: -10 else: 360), - httpOnly=true, secure=cfg.useHttps, sameSite=None) + httpOnly=true, secure=cfg.useHttps, sameSite=None, path="/") template cookiePrefs*(): untyped {.dirty.} = getPrefs(cookies(request)) @@ -38,5 +38,31 @@ template getCursor*(req: Request): string = proc getNames*(name: string): seq[string] = name.strip(chars={'/'}).split(",").filterIt(it.len > 0) +template applyUrlPrefs*() {.dirty.} = + if @"prefs".len > 0: + try: + let decoded = decode(@"prefs") + var params = initTable[string, string]() + for pair in decoded.split('&'): + let kv = pair.split('=', maxsplit=1) + if kv.len == 2: + params[kv[0]] = kv[1] + elif kv.len == 1 and kv[0].len > 0: + params[kv[0]] = "" + genApplyPrefs(params, request) + except: discard + + # Rebuild URL without prefs param + var params: seq[(string, string)] + for k, v in request.params: + if k != "prefs": + params.add (k, v) + + if params.len > 0: + let cleanUrl = request.getNativeReq.url ? params + redirect($cleanUrl) + else: + redirect(request.path) + template respJson*(node: JsonNode) = resp $node, "application/json" diff --git a/src/sass/index.scss b/src/sass/index.scss index c12b814..4ca4f3d 100644 --- a/src/sass/index.scss +++ b/src/sass/index.scss @@ -99,12 +99,18 @@ legend { margin-bottom: 8px; } -.preferences .note { - border-top: 1px solid var(--border_grey); - border-bottom: 1px solid var(--border_grey); - padding: 6px 0 8px 0; - margin-bottom: 8px; - margin-top: 16px; +.preferences { + .note { + border-top: 1px solid var(--border_grey); + border-bottom: 1px solid var(--border_grey); + padding: 6px 0 8px 0; + margin-bottom: 8px; + margin-top: 16px; + } + + .bookmark-note { + margin: 0; + } } ul { diff --git a/src/sass/inputs.scss b/src/sass/inputs.scss index aafa5b8..7ea2b0a 100644 --- a/src/sass/inputs.scss +++ b/src/sass/inputs.scss @@ -200,4 +200,16 @@ input::-webkit-datetime-edit-year-field:focus { .pref-reset { float: left; } + + .prefs-code { + background-color: var(--bg_elements); + border: 1px solid var(--accent_border); + color: var(--fg_color); + font-size: 12px; + padding: 6px 8px; + margin: 4px 0; + word-break: break-all; + white-space: pre-wrap; + user-select: all; + } } diff --git a/src/utils.nim b/src/utils.nim index c96a6dd..667299c 100644 --- a/src/utils.nim +++ b/src/utils.nim @@ -9,7 +9,7 @@ var const https* = "https://" twimg* = "pbs.twimg.com/" - nitterParams = ["name", "tab", "id", "list", "referer", "scroll"] + nitterParams* = ["name", "tab", "id", "list", "referer", "scroll", "prefs"] twitterDomains = @[ "twitter.com", "pic.twitter.com", diff --git a/src/views/preferences.nim b/src/views/preferences.nim index 1787704..40e9e11 100644 --- a/src/views/preferences.nim +++ b/src/views/preferences.nim @@ -32,7 +32,8 @@ macro renderPrefs*(): untyped = result[2].add stmt -proc renderPreferences*(prefs: Prefs; path: string; themes: seq[string]): VNode = +proc renderPreferences*(prefs: Prefs; path: string; themes: seq[string]; + prefsUrl: string): VNode = buildHtml(tdiv(class="overlay-panel")): fieldset(class="preferences"): form(`method`="post", action="/saveprefs", autocomplete="off"): @@ -40,6 +41,12 @@ proc renderPreferences*(prefs: Prefs; path: string; themes: seq[string]): VNode renderPrefs() + legend: text "Bookmark" + p(class="bookmark-note"): + text "Save this URL to restore your preferences (?prefs works on all pages)" + pre(class="prefs-code"): + text prefsUrl + h4(class="note"): text "Preferences are stored client-side using cookies without any personal information." From b85e8c5d7d7af91a2d86a497265142b6f6a4f490 Mon Sep 17 00:00:00 2001 From: Zed Date: Mon, 9 Feb 2026 21:54:57 +0100 Subject: [PATCH 285/409] Support preference overrides using URL params Fixes #186 --- src/nitter.nim | 6 +++--- src/prefs.nim | 15 +++++---------- src/prefs_impl.nim | 34 +++++++--------------------------- src/routes/embed.nim | 2 +- src/routes/list.nim | 4 ++-- src/routes/media.nim | 2 +- src/routes/preferences.nim | 2 +- src/routes/resolver.nim | 4 ++-- src/routes/router_utils.nim | 32 +++++++++++++------------------- src/routes/rss.nim | 19 ++++++++++++------- src/routes/search.nim | 2 +- src/routes/status.nim | 2 +- src/routes/timeline.nim | 2 +- src/routes/unsupported.nim | 2 +- src/sass/index.scss | 1 + src/sass/inputs.scss | 2 +- src/views/general.nim | 4 +--- src/views/preferences.nim | 2 ++ src/views/renderutils.nim | 6 +++--- src/views/rss.nimf | 22 +++++++++++----------- 20 files changed, 70 insertions(+), 95 deletions(-) diff --git a/src/nitter.nim b/src/nitter.nim index 442a8c0..ec2decf 100644 --- a/src/nitter.nim +++ b/src/nitter.nim @@ -71,10 +71,10 @@ routes: applyUrlPrefs() get "/": - resp renderMain(renderSearch(), request, cfg, cookiePrefs()) + resp renderMain(renderSearch(), request, cfg, requestPrefs()) get "/about": - resp renderMain(renderAbout(), request, cfg, cookiePrefs()) + resp renderMain(renderAbout(), request, cfg, requestPrefs()) get "/explore": redirect("/about") @@ -85,7 +85,7 @@ routes: get "/i/redirect": let url = decodeUrl(@"url") if url.len == 0: resp Http404 - redirect(replaceUrls(url, cookiePrefs())) + redirect(replaceUrls(url, requestPrefs())) error Http404: resp Http404, showError("Page not found", cfg) diff --git a/src/prefs.nim b/src/prefs.nim index 573ccae..1a75f75 100644 --- a/src/prefs.nim +++ b/src/prefs.nim @@ -1,5 +1,5 @@ # SPDX-License-Identifier: AGPL-3.0-only -import tables, strutils, base64 +import tables, strutils import types, prefs_impl from config import get from parsecfg import nil @@ -11,17 +11,12 @@ var defaultPrefs*: Prefs proc updateDefaultPrefs*(cfg: parsecfg.Config) = genDefaultPrefs() -proc getPrefs*(cookies: Table[string, string]): Prefs = +proc getPrefs*(cookies, params: Table[string, string]): Prefs = result = defaultPrefs - genCookiePrefs(cookies) - -template getPref*(cookies: Table[string, string], pref): untyped = - bind genCookiePref - var res = defaultPrefs.`pref` - genCookiePref(cookies, pref, res) - res + genParsePrefs(cookies) + genParsePrefs(params) proc encodePrefs*(prefs: Prefs): string = var encPairs: seq[string] genEncodePrefs(prefs) - encode(encPairs.join("&"), safe=true) + encPairs.join(",") diff --git a/src/prefs_impl.nim b/src/prefs_impl.nim index 2faf8ef..149eadf 100644 --- a/src/prefs_impl.nim +++ b/src/prefs_impl.nim @@ -130,7 +130,7 @@ macro genDefaultPrefs*(): untyped = result.add quote do: defaultPrefs.`ident` = cfg.get("Preferences", `name`, `default`) -macro genCookiePrefs*(cookies): untyped = +macro genParsePrefs*(prefs): untyped = result = nnkStmtList.newTree() for pref in allPrefs(): let @@ -140,37 +140,17 @@ macro genCookiePrefs*(cookies): untyped = options = pref.options result.add quote do: - if `name` in `cookies`: + if `name` in `prefs`: when `kind` == input or `name` == "theme": - result.`ident` = `cookies`[`name`] + result.`ident` = `prefs`[`name`] elif `kind` == checkbox: - result.`ident` = `cookies`[`name`] == "on" + result.`ident` = `prefs`[`name`] == "on" or + `prefs`[`name`] == "true" or + `prefs`[`name`] == "1" else: - let value = `cookies`[`name`] + let value = `prefs`[`name`] if value in `options`: result.`ident` = value -macro genCookiePref*(cookies, prefName, res): untyped = - result = nnkStmtList.newTree() - for pref in allPrefs(): - let ident = ident(pref.name) - if ident != prefName: - continue - - let - name = pref.name - kind = newLit(pref.kind) - options = pref.options - - result.add quote do: - if `name` in `cookies`: - when `kind` == input or `name` == "theme": - `res` = `cookies`[`name`] - elif `kind` == checkbox: - `res` = `cookies`[`name`] == "on" - else: - let value = `cookies`[`name`] - if value in `options`: `res` = value - macro genUpdatePrefs*(): untyped = result = nnkStmtList.newTree() let req = ident("request") diff --git a/src/routes/embed.nim b/src/routes/embed.nim index 994364b..0527d3d 100644 --- a/src/routes/embed.nim +++ b/src/routes/embed.nim @@ -19,7 +19,7 @@ proc createEmbedRouter*(cfg: Config) = get "/@user/status/@id/embed": let tweet = await getGraphTweetResult(@"id") - prefs = cookiePrefs() + prefs = requestPrefs() path = getPath() if tweet == nil: diff --git a/src/routes/list.nim b/src/routes/list.nim index ac3e97e..7dadc22 100644 --- a/src/routes/list.nim +++ b/src/routes/list.nim @@ -36,7 +36,7 @@ proc createListRouter*(cfg: Config) = get "/i/lists/@id/?": cond '.' notin @"id" let - prefs = cookiePrefs() + prefs = requestPrefs() list = await getCachedList(id=(@"id")) timeline = await getGraphListTweets(list.id, getCursor()) vnode = renderTimelineTweets(timeline, prefs, request.path) @@ -45,7 +45,7 @@ proc createListRouter*(cfg: Config) = get "/i/lists/@id/members": cond '.' notin @"id" let - prefs = cookiePrefs() + prefs = requestPrefs() list = await getCachedList(id=(@"id")) members = await getGraphListMembers(list, getCursor()) respList(list, members, list.title, renderTimelineUsers(members, prefs, request.path)) diff --git a/src/routes/media.nim b/src/routes/media.nim index 011d0f3..b3e5374 100644 --- a/src/routes/media.nim +++ b/src/routes/media.nim @@ -143,6 +143,6 @@ proc createMediaRouter*(cfg: Config) = if ".m3u8" in url: let vid = await safeFetch(url) - content = proxifyVideo(vid, cookiePref(proxyVideos)) + content = proxifyVideo(vid, requestPrefs().proxyVideos) resp content, m3u8Mime diff --git a/src/routes/preferences.nim b/src/routes/preferences.nim index 345ff34..5886c0e 100644 --- a/src/routes/preferences.nim +++ b/src/routes/preferences.nim @@ -19,7 +19,7 @@ proc createPrefRouter*(cfg: Config) = router preferences: get "/settings": let - prefs = cookiePrefs() + prefs = requestPrefs() prefsCode = encodePrefs(prefs) prefsUrl = getUrlPrefix(cfg) & "/?prefs=" & prefsCode html = renderPreferences(prefs, refPath(), findThemes(cfg.staticDir), prefsUrl) diff --git a/src/routes/resolver.nim b/src/routes/resolver.nim index 1baf873..5f074a5 100644 --- a/src/routes/resolver.nim +++ b/src/routes/resolver.nim @@ -18,8 +18,8 @@ proc createResolverRouter*(cfg: Config) = router resolver: get "/cards/@card/@id": let url = "https://cards.twitter.com/cards/$1/$2" % [@"card", @"id"] - respResolved(await resolve(url, cookiePrefs()), "card") + respResolved(await resolve(url, requestPrefs()), "card") get "/t.co/@url": let url = "https://t.co/" & @"url" - respResolved(await resolve(url, cookiePrefs()), "t.co") + respResolved(await resolve(url, requestPrefs()), "t.co") diff --git a/src/routes/router_utils.nim b/src/routes/router_utils.nim index 2ef248a..379280c 100644 --- a/src/routes/router_utils.nim +++ b/src/routes/router_utils.nim @@ -1,24 +1,21 @@ # SPDX-License-Identifier: AGPL-3.0-only -import strutils, sequtils, uri, tables, json, base64 +import strutils, sequtils, uri, tables, json from jester import Request, cookies import ../views/general import ".."/[utils, prefs, types] -export utils, prefs, types, uri, base64 +export utils, prefs, types, uri template savePref*(pref, value: string; req: Request; expire=false) = if not expire or pref in cookies(req): setCookie(pref, value, daysForward(when expire: -10 else: 360), httpOnly=true, secure=cfg.useHttps, sameSite=None, path="/") -template cookiePrefs*(): untyped {.dirty.} = - getPrefs(cookies(request)) - -template cookiePref*(pref): untyped {.dirty.} = - getPref(cookies(request), pref) +template requestPrefs*(): untyped {.dirty.} = + getPrefs(cookies(request), params(request)) template showError*(error: string; cfg: Config): string = - renderMain(renderError(error), request, cfg, cookiePrefs(), "Error") + renderMain(renderError(error), request, cfg, requestPrefs(), "Error") template getPath*(): untyped {.dirty.} = $(parseUri(request.path) ? filterParams(request.params)) @@ -40,17 +37,14 @@ proc getNames*(name: string): seq[string] = template applyUrlPrefs*() {.dirty.} = if @"prefs".len > 0: - try: - let decoded = decode(@"prefs") - var params = initTable[string, string]() - for pair in decoded.split('&'): - let kv = pair.split('=', maxsplit=1) - if kv.len == 2: - params[kv[0]] = kv[1] - elif kv.len == 1 and kv[0].len > 0: - params[kv[0]] = "" - genApplyPrefs(params, request) - except: discard + var prefParams = initTable[string, string]() + for pair in @"prefs".split(','): + let kv = pair.split('=', maxsplit=1) + if kv.len == 2: + prefParams[kv[0]] = kv[1] + elif kv.len == 1 and kv[0].len > 0: + prefParams[kv[0]] = "" + genApplyPrefs(prefParams, request) # Rebuild URL without prefs param var params: seq[(string, string)] diff --git a/src/routes/rss.nim b/src/routes/rss.nim index b0e781d..6902001 100644 --- a/src/routes/rss.nim +++ b/src/routes/rss.nim @@ -15,7 +15,7 @@ proc redisKey*(page, name, cursor: string): string = if cursor.len > 0: result &= ":" & cursor -proc timelineRss*(req: Request; cfg: Config; query: Query): Future[Rss] {.async.} = +proc timelineRss*(req: Request; cfg: Config; query: Query; prefs: Prefs): Future[Rss] {.async.} = var profile: Profile let name = req.params.getOrDefault("name") @@ -39,7 +39,7 @@ proc timelineRss*(req: Request; cfg: Config; query: Query): Future[Rss] {.async. return Rss(feed: profile.user.username, cursor: "suspended") if profile.user.fullname.len > 0: - let rss = renderTimelineRss(profile, cfg, multi=(names.len > 1)) + let rss = renderTimelineRss(profile, cfg, prefs, multi=(names.len > 1)) return Rss(feed: rss, cursor: profile.tweets.bottom) template respRss*(rss, page) = @@ -64,7 +64,9 @@ proc createRssRouter*(cfg: Config) = if @"q".len > 200: resp Http400, showError("Search input too long.", cfg) - let query = initQuery(params(request)) + let + prefs = requestPrefs() + query = initQuery(params(request)) if query.kind != tweets: resp Http400, showError("Only Tweet searches are allowed for RSS feeds.", cfg) @@ -78,7 +80,7 @@ proc createRssRouter*(cfg: Config) = let tweets = await getGraphTweetSearch(query, cursor) rss.cursor = tweets.bottom - rss.feed = renderSearchRss(tweets.content, query.text, genQueryUrl(query), cfg) + rss.feed = renderSearchRss(tweets.content, query.text, genQueryUrl(query), cfg, prefs) await cacheRss(key, rss) respRss(rss, "Search") @@ -87,6 +89,7 @@ proc createRssRouter*(cfg: Config) = cond cfg.enableRss cond '.' notin @"name" let + prefs = requestPrefs() name = @"name" key = redisKey("twitter", name, getCursor()) @@ -94,7 +97,7 @@ proc createRssRouter*(cfg: Config) = if rss.cursor.len > 0: respRss(rss, "User") - rss = await timelineRss(request, cfg, Query(fromUser: @[name])) + rss = await timelineRss(request, cfg, Query(fromUser: @[name]), prefs) await cacheRss(key, rss) respRss(rss, "User") @@ -104,6 +107,7 @@ proc createRssRouter*(cfg: Config) = cond '.' notin @"name" cond @"tab" in ["with_replies", "media", "search"] let + prefs = requestPrefs() name = @"name" tab = @"tab" query = @@ -122,7 +126,7 @@ proc createRssRouter*(cfg: Config) = if rss.cursor.len > 0: respRss(rss, "User") - rss = await timelineRss(request, cfg, query) + rss = await timelineRss(request, cfg, query, prefs) await cacheRss(key, rss) respRss(rss, "User") @@ -147,6 +151,7 @@ proc createRssRouter*(cfg: Config) = get "/i/lists/@id/rss": cond cfg.enableRss let + prefs = requestPrefs() id = @"id" cursor = getCursor() key = redisKey("lists", id, cursor) @@ -159,7 +164,7 @@ proc createRssRouter*(cfg: Config) = list = await getCachedList(id=id) timeline = await getGraphListTweets(list.id, cursor) rss.cursor = timeline.bottom - rss.feed = renderListRss(timeline.content, list, cfg) + rss.feed = renderListRss(timeline.content, list, cfg, prefs) await cacheRss(key, rss) respRss(rss, "List") diff --git a/src/routes/search.nim b/src/routes/search.nim index e9f991d..4220427 100644 --- a/src/routes/search.nim +++ b/src/routes/search.nim @@ -19,7 +19,7 @@ proc createSearchRouter*(cfg: Config) = resp Http400, showError("Search input too long.", cfg) let - prefs = cookiePrefs() + prefs = requestPrefs() query = initQuery(params(request)) title = "Search" & (if q.len > 0: " (" & q & ")" else: "") diff --git a/src/routes/status.nim b/src/routes/status.nim index 0168dac..838b327 100644 --- a/src/routes/status.nim +++ b/src/routes/status.nim @@ -21,7 +21,7 @@ proc createStatusRouter*(cfg: Config) = if id.len > 19 or id.any(c => not c.isDigit): resp Http404, showError("Invalid tweet ID", cfg) - let prefs = cookiePrefs() + let prefs = requestPrefs() # used for the infinite scroll feature if @"scroll".len > 0: diff --git a/src/routes/timeline.nim b/src/routes/timeline.nim index 2ac87bb..d6d8c21 100644 --- a/src/routes/timeline.nim +++ b/src/routes/timeline.nim @@ -117,7 +117,7 @@ proc createTimelineRouter*(cfg: Config) = cond @"name".allCharsInSet({'a'..'z', 'A'..'Z', '0'..'9', '_', ','}) cond @"tab" in ["with_replies", "media", "search", ""] let - prefs = cookiePrefs() + prefs = requestPrefs() after = getCursor() names = getNames(@"name") diff --git a/src/routes/unsupported.nim b/src/routes/unsupported.nim index e06a183..345dee7 100644 --- a/src/routes/unsupported.nim +++ b/src/routes/unsupported.nim @@ -10,7 +10,7 @@ export feature proc createUnsupportedRouter*(cfg: Config) = router unsupported: template feature {.dirty.} = - resp renderMain(renderFeature(), request, cfg, cookiePrefs()) + resp renderMain(renderFeature(), request, cfg, requestPrefs()) get "/about/feature": feature() get "/login/?@i?": feature() diff --git a/src/sass/index.scss b/src/sass/index.scss index 4ca4f3d..a85002e 100644 --- a/src/sass/index.scss +++ b/src/sass/index.scss @@ -110,6 +110,7 @@ legend { .bookmark-note { margin: 0; + margin-bottom: 10px; } } diff --git a/src/sass/inputs.scss b/src/sass/inputs.scss index 7ea2b0a..c69711a 100644 --- a/src/sass/inputs.scss +++ b/src/sass/inputs.scss @@ -205,7 +205,7 @@ input::-webkit-datetime-edit-year-field:focus { background-color: var(--bg_elements); border: 1px solid var(--accent_border); color: var(--fg_color); - font-size: 12px; + font-size: 13px; padding: 6px 8px; margin: 4px 0; word-break: break-all; diff --git a/src/views/general.nim b/src/views/general.nim index 3bae9d3..74ddcb4 100644 --- a/src/views/general.nim +++ b/src/views/general.nim @@ -39,9 +39,7 @@ proc renderNavbar(cfg: Config; req: Request; rss, canonical: string): VNode = proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc=""; video=""; images: seq[string] = @[]; banner=""; ogTitle=""; rss=""; alternate=""): VNode = - var theme = prefs.theme.toTheme - if "theme" in req.params: - theme = req.params["theme"].toTheme + let theme = prefs.theme.toTheme let ogType = if video.len > 0: "video" diff --git a/src/views/preferences.nim b/src/views/preferences.nim index 40e9e11..b051a01 100644 --- a/src/views/preferences.nim +++ b/src/views/preferences.nim @@ -46,6 +46,8 @@ proc renderPreferences*(prefs: Prefs; path: string; themes: seq[string]; text "Save this URL to restore your preferences (?prefs works on all pages)" pre(class="prefs-code"): text prefsUrl + p(class="bookmark-note"): + verbatim "You can override preferences with query parameters (e.g. ?hlsPlayback=on). These overrides aren't saved to cookies, and links won't retain the parameters. Intended for configuring RSS feeds and other cookieless environments. Hover over a preference to see its name." h4(class="note"): text "Preferences are stored client-side using cookies without any personal information." diff --git a/src/views/renderutils.nim b/src/views/renderutils.nim index a5fe3b2..6753c5a 100644 --- a/src/views/renderutils.nim +++ b/src/views/renderutils.nim @@ -65,20 +65,20 @@ proc buttonReferer*(action, text, path: string; class=""; `method`="post"): VNod text text proc genCheckbox*(pref, label: string; state: bool): VNode = - buildHtml(label(class="pref-group checkbox-container")): + buildHtml(label(class="pref-group checkbox-container", title=pref)): text label input(name=pref, `type`="checkbox", checked=state) span(class="checkbox") proc genInput*(pref, label, state, placeholder: string; class=""; autofocus=true): VNode = let p = placeholder - buildHtml(tdiv(class=("pref-group pref-input " & class))): + buildHtml(tdiv(class=("pref-group pref-input " & class), title=pref)): if label.len > 0: label(`for`=pref): text label input(name=pref, `type`="text", placeholder=p, value=state, autofocus=(autofocus and state.len == 0)) proc genSelect*(pref, label, state: string; options: seq[string]): VNode = - buildHtml(tdiv(class="pref-group pref-input")): + buildHtml(tdiv(class="pref-group pref-input", title=pref)): label(`for`=pref): text label select(name=pref): for opt in options: diff --git a/src/views/rss.nimf b/src/views/rss.nimf index 717ad99..46d7eaf 100644 --- a/src/views/rss.nimf +++ b/src/views/rss.nimf @@ -49,10 +49,10 @@ Twitter feed for: ${desc}. Generated by ${getUrlPrefix(cfg)} #end if #end proc # -#proc renderRssTweet(tweet: Tweet; cfg: Config): string = +#proc renderRssTweet(tweet: Tweet; cfg: Config; prefs: Prefs): string = #let tweet = tweet.retweet.get(tweet) #let urlPrefix = getUrlPrefix(cfg) -#let text = replaceUrls(tweet.text, defaultPrefs, absolute=urlPrefix) +#let text = replaceUrls(tweet.text, prefs, absolute=urlPrefix)

${text.replace("\n", "
\n")}

#if tweet.photos.len > 0: # for photo in tweet.photos: @@ -81,7 +81,7 @@ Twitter feed for: ${desc}. Generated by ${getUrlPrefix(cfg)}
${quoteTweet.user.fullname} (@${quoteTweet.user.username})

-${renderRssTweet(quoteTweet, cfg)} +${renderRssTweet(quoteTweet, cfg, prefs)}