openvibers/openvibe.surf Commits
OpenVibe.Surf: Ride maps, surf the web, surf what's on
Changes
nnew file mode 100644@@@ -0,0 +1,38 @@++# OpenVibe.Surf — environment (production: /etc/openvibe/surf.env). Names only; never commit values.++++NODE_ENV=production++PORT=5110++HOST=127.0.0.1++BASE_URL=https://openvibe.surf++TRUST_PROXY=2++++# OpenVibe.Network: sign-in (OAuth client `surf`, PKCE S256) and its JWKS.++OV_NETWORK_INTERNAL_URL=http://127.0.0.1:4000++OV_OAUTH_CLIENT_ID=surf++OV_OAUTH_CLIENT_SECRET=++# The browser is sent to the public origin; the token exchange and the JWKS use the internal one above.++# OV_NETWORK_URL=https://openvibe.network++# OV_NETWORK_ISSUER=https://openvibe.network++# OV_OAUTH_REDIRECT_URI=https://openvibe.surf/auth/callback++# OV_SESSION_AUDIENCE=openvibe.network++++# The three rooms this front joins. Internal origins are what this process calls; public origins are what a person's++# browser and the page links use.++OV_LIVE_INTERNAL_URL=http://127.0.0.1:3000++OV_GAMES_INTERNAL_URL=http://127.0.0.1:8000++OV_ACTOR_INTERNAL_URL=http://127.0.0.1:4950++SURF_GAMES_PUBLIC_URL=https://openvibe.games++# SURF_LIVE_PUBLIC_URL=https://openvibe.live++# SURF_ACTOR_PUBLIC_URL=https://openvibe.actor++++# Optional: the Watch merge (30 s cache, 2 s per Live source), the Ride cache (60 s) and Games timeout, the Actor++# timeout, the per-IP limit.++# SURF_WATCH_CACHE_MS=30000++# SURF_WATCH_SOURCE_TIMEOUT_MS=2000++# SURF_RIDE_CACHE_MS=60000++# SURF_GAMES_TIMEOUT_MS=4000++# SURF_ACTOR_TIMEOUT_MS=15000++# SURF_RATE_LIMIT_PER_MIN=120++++# optional: defaults to true in production++# COOKIE_SECURE=truennew file mode 100644@@@ -0,0 +1,16 @@++name: ci++on:++ push:++ branches: [main]++ pull_request:++jobs:++ # install, node --check and npm test: the shared test job (Track Q).++ test:++ uses: OpenVibers/OpenVibe.Shared/.github/workflows/test.yml@423fe519b5fe9594cc81b670e5ba5cb9f21f6176++ with:++ syntax-dirs: server test++ # No database, no Valkey, no Events: every test runs against in-process Network/Live/Games/Actor stand-ins.++ test: OV_ALLOW_SUITE=1 npm test++ # Secret scan (gitleaks) and dependency audit, shared by every OpenVibers repo (Track Q).++ security:++ uses: OpenVibers/OpenVibe.Shared/.github/workflows/security.yml@423fe519b5fe9594cc81b670e5ba5cb9f21f6176nnew file mode 100644@@@ -0,0 +1,12 @@++node_modules/++data/++dist/++.env++.env.*++!.env.example++*.log++*.pem++*.key++.npm-cache/++.ref/++.npm-cachennew file mode 100644@@@ -0,0 +1,41 @@++# Changelog++++What changed in OpenVibe.Surf, newest first. Each site also publishes its patch notes at /updates.++++## 0.1.0 — 2026-10-28++++The service skeleton: three surfs, one room.++++- **Sign-in with OpenVibe.Network** (`server/auth/sso.js`, OAuth client `surf`): authorization code with PKCE++ S256, a state check, a server-side code exchange, httpOnly `surf_at` and `surf_rt` cookies (SameSite=Lax,++ Secure in production), the `surf_oauth` flow cookie, and `sanitizeNext` (this site's own origin only, no++ control characters or backslashes). `/auth/login`, `/auth/callback`, `/auth/logout`, `/auth/me`.++- **Watch** (`server/watch/feed.js`, `GET /api/v1/watch/feed`): OpenVibe.Live's live streams++ (`GET /api/streams`), clips (`GET /api/content/feed?type=clips&sort=new&window=week`) and AI moments++ (`GET /api/content/moments?type=clips&sort=top&window=week`) merged into one item shape. Live streams first by++ viewers, then moments and clips interleaved by freshness, never two cards from the same channel in a row. The++ merged feed is cached 30 s; each Live source gets 2 s and a failing one is left out and named in `sources`. The++ topic filter, an opaque per-source cursor and `public, max-age=30` are included.++- **Ride** (`server/ride/games.js`, `GET /api/v1/ride/maps`, `/api/v1/ride/maps/:id`): a proxy to OpenVibe.Games'++ `/api/surf/maps` routes, cached 60 s, each map carrying its++ `https://openvibe.games/play/surf?map=<id>&embed=1` embed URL. While Games' new routes answer 404 the list is++ empty and `games` is `'not_ready'`; a Games failure is a 502 and is never cached.++- **Web** (`server/web/tasks.js`, `POST /api/v1/web/tasks`, `GET /web/go`): Surf with Vibe. A signed-in,++ same-origin request creates one OpenVibe.Actor task with the person's own Network token from `surf_at` as a++ Bearer, an `Idempotency-Key`, and a short browsing brief (search_web, read_page, a desk when a page needs one,++ keep sources, finish with a cited report). Answers `{ task_id, page }`; Actor's 4xx refusal keeps its status++ and words. `/web/go` is the same task as a no-JavaScript form that 303s to the Actor task page.++- **Pages** (`server/http/pages.js`, `server/views/index.js`): `/`, `/ride`, `/ride/:map`, `/watch`, `/web`,++ `/developers` and a 404, all server-rendered through the OpenVibe Frame and complete without JavaScript, with++ stable `sf-` ids and classes. Public pages are `public, max-age=60` with `Vary: Cookie`; a page rendered for a++ signed-in person is `private, no-store`. `public/css/surf.css` and `public/js/surf.js` are one-comment++ placeholders for the main session.++- **Plumbing** (`server/app.js`): helmet with the network's CSP (the Games embed, a possible Live player, the++ media edge for HLS), content-hashed `/css` and `/js`, `/api/health`, `/api/ready` (degraded, never 503, when an++ optional upstream is down), `/release.json`, `/release-metrics`, `/shared/*`, `/robots.txt`, `/sitemap.xml` and++ `/llms.txt`, a 120/min per-IP limit on `/api` and `/auth`, and a graceful stop.++- **Deploy:** `deploy/systemd/openvibe-surf.service` (port 5110, `/etc/openvibe/surf.env`) and++ `deploy/nginx/openvibe.surf.conf` (TLS for openvibe.surf, www → apex, the Watch/Ride read zone, the forms zone++ for `/web/go` and `/api/v1/web/`, `/auth/me` on the API zone, `/internal` and `/metrics` 404).++- **Tests:** `test/feed.test.js`, `test/ride.test.js`, `test/web.test.js`, `test/sso.test.js`,++ `test/pages.test.js` and `test/discovery.test.js`, each against in-process Network/Live/Games/Actor stand-ins.nnew file mode 100644@@@ -0,0 +1,661 @@++ GNU AFFERO GENERAL PUBLIC LICENSE++ Version 3, 19 November 2007++++ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>++ Everyone is permitted to copy and distribute verbatim copies++ of this license document, but changing it is not allowed.++++ Preamble++++ The GNU Affero General Public License is a free, copyleft license for++software and other kinds of works, specifically designed to ensure++cooperation with the community in the case of network server software.++++ The licenses for most software and other practical works are designed++to take away your freedom to share and change the works. By contrast,++our General Public Licenses are intended to guarantee your freedom to++share and change all versions of a program--to make sure it remains free++software for all its users.++++ When we speak of free software, we are referring to freedom, not++price. Our General Public Licenses are designed to make sure that you++have the freedom to distribute copies of free software (and charge for++them if you wish), that you receive source code or can get it if you++want it, that you can change the software or use pieces of it in new++free programs, and that you know you can do these things.++++ Developers that use our General Public Licenses protect your rights++with two steps: (1) assert copyright on the software, and (2) offer++you this License which gives you legal permission to copy, distribute++and/or modify the software.++++ A secondary benefit of defending all users' freedom is that++improvements made in alternate versions of the program, if they++receive widespread use, become available for other developers to++incorporate. Many developers of free software are heartened and++encouraged by the resulting cooperation. However, in the case of++software used on network servers, this result may fail to come about.++The GNU General Public License permits making a modified version and++letting the public access it on a server without ever releasing its++source code to the public.++++ The GNU Affero General Public License is designed specifically to++ensure that, in such cases, the modified source code becomes available++to the community. It requires the operator of a network server to++provide the source code of the modified version running there to the++users of that server. Therefore, public use of a modified version, on++a publicly accessible server, gives the public access to the source++code of the modified version.++++ An older license, called the Affero General Public License and++published by Affero, was designed to accomplish similar goals. This is++a different license, not a version of the Affero GPL, but Affero has++released a new version of the Affero GPL which permits relicensing under++this license.++++ The precise terms and conditions for copying, distribution and++modification follow.++++ TERMS AND CONDITIONS++++ 0. Definitions.++++ "This License" refers to version 3 of the GNU Affero General Public License.++++ "Copyright" also means copyright-like laws that apply to other kinds of++works, such as semiconductor masks.++++ "The Program" refers to any copyrightable work licensed under this++License. Each licensee is addressed as "you". "Licensees" and++"recipients" may be individuals or organizations.++++ To "modify" a work means to copy from or adapt all or part of the work++in a fashion requiring copyright permission, other than the making of an++exact copy. The resulting work is called a "modified version" of the++earlier work or a work "based on" the earlier work.++++ A "covered work" means either the unmodified Program or a work based++on the Program.++++ To "propagate" a work means to do anything with it that, without++permission, would make you directly or secondarily liable for++infringement under applicable copyright law, except executing it on a++computer or modifying a private copy. Propagation includes copying,++distribution (with or without modification), making available to the++public, and in some countries other activities as well.++++ To "convey" a work means any kind of propagation that enables other++parties to make or receive copies. Mere interaction with a user through++a computer network, with no transfer of a copy, is not conveying.++++ An interactive user interface displays "Appropriate Legal Notices"++to the extent that it includes a convenient and prominently visible++feature that (1) displays an appropriate copyright notice, and (2)++tells the user that there is no warranty for the work (except to the++extent that warranties are provided), that licensees may convey the++work under this License, and how to view a copy of this License. If++the interface presents a list of user commands or options, such as a++menu, a prominent item in the list meets this criterion.++++ 1. Source Code.++++ The "source code" for a work means the preferred form of the work++for making modifications to it. "Object code" means any non-source++form of a work.++++ A "Standard Interface" means an interface that either is an official++standard defined by a recognized standards body, or, in the case of++interfaces specified for a particular programming language, one that++is widely used among developers working in that language.++++ The "System Libraries" of an executable work include anything, other++than the work as a whole, that (a) is included in the normal form of++packaging a Major Component, but which is not part of that Major++Component, and (b) serves only to enable use of the work with that++Major Component, or to implement a Standard Interface for which an++implementation is available to the public in source code form. A++"Major Component", in this context, means a major essential component++(kernel, window system, and so on) of the specific operating system++(if any) on which the executable work runs, or a compiler used to++produce the work, or an object code interpreter used to run it.++++ The "Corresponding Source" for a work in object code form means all++the source code needed to generate, install, and (for an executable++work) run the object code and to modify the work, including scripts to++control those activities. However, it does not include the work's++System Libraries, or general-purpose tools or generally available free++programs which are used unmodified in performing those activities but++which are not part of the work. For example, Corresponding Source++includes interface definition files associated with source files for++the work, and the source code for shared libraries and dynamically++linked subprograms that the work is specifically designed to require,++such as by intimate data communication or control flow between those++subprograms and other parts of the work.++++ The Corresponding Source need not include anything that users++can regenerate automatically from other parts of the Corresponding++Source.++++ The Corresponding Source for a work in source code form is that++same work.++++ 2. Basic Permissions.++++ All rights granted under this License are granted for the term of++copyright on the Program, and are irrevocable provided the stated++conditions are met. This License explicitly affirms your unlimited++permission to run the unmodified Program. The output from running a++covered work is covered by this License only if the output, given its++content, constitutes a covered work. This License acknowledges your++rights of fair use or other equivalent, as provided by copyright law.++++ You may make, run and propagate covered works that you do not++convey, without conditions so long as your license otherwise remains++in force. You may convey covered works to others for the sole purpose++of having them make modifications exclusively for you, or provide you++with facilities for running those works, provided that you comply with++the terms of this License in conveying all material for which you do++not control copyright. Those thus making or running the covered works++for you must do so exclusively on your behalf, under your direction++and control, on terms that prohibit them from making any copies of++your copyrighted material outside their relationship with you.++++ Conveying under any other circumstances is permitted solely under++the conditions stated below. Sublicensing is not allowed; section 10++makes it unnecessary.++++ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.++++ No covered work shall be deemed part of an effective technological++measure under any applicable law fulfilling obligations under article++11 of the WIPO copyright treaty adopted on 20 December 1996, or++similar laws prohibiting or restricting circumvention of such++measures.++++ When you convey a covered work, you waive any legal power to forbid++circumvention of technological measures to the extent such circumvention++is effected by exercising rights under this License with respect to++the covered work, and you disclaim any intention to limit operation or++modification of the work as a means of enforcing, against the work's++users, your or third parties' legal rights to forbid circumvention of++technological measures.++++ 4. Conveying Verbatim Copies.++++ You may convey verbatim copies of the Program's source code as you++receive it, in any medium, provided that you conspicuously and++appropriately publish on each copy an appropriate copyright notice;++keep intact all notices stating that this License and any++non-permissive terms added in accord with section 7 apply to the code;++keep intact all notices of the absence of any warranty; and give all++recipients a copy of this License along with the Program.++++ You may charge any price or no price for each copy that you convey,++and you may offer support or warranty protection for a fee.++++ 5. Conveying Modified Source Versions.++++ You may convey a work based on the Program, or the modifications to++produce it from the Program, in the form of source code under the++terms of section 4, provided that you also meet all of these conditions:++++ a) The work must carry prominent notices stating that you modified++ it, and giving a relevant date.++++ b) The work must carry prominent notices stating that it is++ released under this License and any conditions added under section++ 7. This requirement modifies the requirement in section 4 to++ "keep intact all notices".++++ c) You must license the entire work, as a whole, under this++ License to anyone who comes into possession of a copy. This++ License will therefore apply, along with any applicable section 7++ additional terms, to the whole of the work, and all its parts,++ regardless of how they are packaged. This License gives no++ permission to license the work in any other way, but it does not++ invalidate such permission if you have separately received it.++++ d) If the work has interactive user interfaces, each must display++ Appropriate Legal Notices; however, if the Program has interactive++ interfaces that do not display Appropriate Legal Notices, your++ work need not make them do so.++++ A compilation of a covered work with other separate and independent++works, which are not by their nature extensions of the covered work,++and which are not combined with it such as to form a larger program,++in or on a volume of a storage or distribution medium, is called an++"aggregate" if the compilation and its resulting copyright are not++used to limit the access or legal rights of the compilation's users++beyond what the individual works permit. Inclusion of a covered work++in an aggregate does not cause this License to apply to the other++parts of the aggregate.++++ 6. Conveying Non-Source Forms.++++ You may convey a covered work in object code form under the terms++of sections 4 and 5, provided that you also convey the++machine-readable Corresponding Source under the terms of this License,++in one of these ways:++++ a) Convey the object code in, or embodied in, a physical product++ (including a physical distribution medium), accompanied by the++ Corresponding Source fixed on a durable physical medium++ customarily used for software interchange.++++ b) Convey the object code in, or embodied in, a physical product++ (including a physical distribution medium), accompanied by a++ written offer, valid for at least three years and valid for as++ long as you offer spare parts or customer support for that product++ model, to give anyone who possesses the object code either (1) a++ copy of the Corresponding Source for all the software in the++ product that is covered by this License, on a durable physical++ medium customarily used for software interchange, for a price no++ more than your reasonable cost of physically performing this++ conveying of source, or (2) access to copy the++ Corresponding Source from a network server at no charge.++++ c) Convey individual copies of the object code with a copy of the++ written offer to provide the Corresponding Source. This++ alternative is allowed only occasionally and noncommercially, and++ only if you received the object code with such an offer, in accord++ with subsection 6b.++++ d) Convey the object code by offering access from a designated++ place (gratis or for a charge), and offer equivalent access to the++ Corresponding Source in the same way through the same place at no++ further charge. You need not require recipients to copy the++ Corresponding Source along with the object code. If the place to++ copy the object code is a network server, the Corresponding Source++ may be on a different server (operated by you or a third party)++ that supports equivalent copying facilities, provided you maintain++ clear directions next to the object code saying where to find the++ Corresponding Source. Regardless of what server hosts the++ Corresponding Source, you remain obligated to ensure that it is++ available for as long as needed to satisfy these requirements.++++ e) Convey the object code using peer-to-peer transmission, provided++ you inform other peers where the object code and Corresponding++ Source of the work are being offered to the general public at no++ charge under subsection 6d.++++ A separable portion of the object code, whose source code is excluded++from the Corresponding Source as a System Library, need not be++included in conveying the object code work.++++ A "User Product" is either (1) a "consumer product", which means any++tangible personal property which is normally used for personal, family,++or household purposes, or (2) anything designed or sold for incorporation++into a dwelling. In determining whether a product is a consumer product,++doubtful cases shall be resolved in favor of coverage. For a particular++product received by a particular user, "normally used" refers to a++typical or common use of that class of product, regardless of the status++of the particular user or of the way in which the particular user++actually uses, or expects or is expected to use, the product. A product++is a consumer product regardless of whether the product has substantial++commercial, industrial or non-consumer uses, unless such uses represent++the only significant mode of use of the product.++++ "Installation Information" for a User Product means any methods,++procedures, authorization keys, or other information required to install++and execute modified versions of a covered work in that User Product from++a modified version of its Corresponding Source. The information must++suffice to ensure that the continued functioning of the modified object++code is in no case prevented or interfered with solely because++modification has been made.++++ If you convey an object code work under this section in, or with, or++specifically for use in, a User Product, and the conveying occurs as++part of a transaction in which the right of possession and use of the++User Product is transferred to the recipient in perpetuity or for a++fixed term (regardless of how the transaction is characterized), the++Corresponding Source conveyed under this section must be accompanied++by the Installation Information. But this requirement does not apply++if neither you nor any third party retains the ability to install++modified object code on the User Product (for example, the work has++been installed in ROM).++++ The requirement to provide Installation Information does not include a++requirement to continue to provide support service, warranty, or updates++for a work that has been modified or installed by the recipient, or for++the User Product in which it has been modified or installed. Access to a++network may be denied when the modification itself materially and++adversely affects the operation of the network or violates the rules and++protocols for communication across the network.++++ Corresponding Source conveyed, and Installation Information provided,++in accord with this section must be in a format that is publicly++documented (and with an implementation available to the public in++source code form), and must require no special password or key for++unpacking, reading or copying.++++ 7. Additional Terms.++++ "Additional permissions" are terms that supplement the terms of this++License by making exceptions from one or more of its conditions.++Additional permissions that are applicable to the entire Program shall++be treated as though they were included in this License, to the extent++that they are valid under applicable law. If additional permissions++apply only to part of the Program, that part may be used separately++under those permissions, but the entire Program remains governed by++this License without regard to the additional permissions.++++ When you convey a copy of a covered work, you may at your option++remove any additional permissions from that copy, or from any part of++it. (Additional permissions may be written to require their own++removal in certain cases when you modify the work.) You may place++additional permissions on material, added by you to a covered work,++for which you have or can give appropriate copyright permission.++++ Notwithstanding any other provision of this License, for material you++add to a covered work, you may (if authorized by the copyright holders of++that material) supplement the terms of this License with terms:++++ a) Disclaiming warranty or limiting liability differently from the++ terms of sections 15 and 16 of this License; or++++ b) Requiring preservation of specified reasonable legal notices or++ author attributions in that material or in the Appropriate Legal++ Notices displayed by works containing it; or++++ c) Prohibiting misrepresentation of the origin of that material, or++ requiring that modified versions of such material be marked in++ reasonable ways as different from the original version; or++++ d) Limiting the use for publicity purposes of names of licensors or++ authors of the material; or++++ e) Declining to grant rights under trademark law for use of some++ trade names, trademarks, or service marks; or++++ f) Requiring indemnification of licensors and authors of that++ material by anyone who conveys the material (or modified versions of++ it) with contractual assumptions of liability to the recipient, for++ any liability that these contractual assumptions directly impose on++ those licensors and authors.++++ All other non-permissive additional terms are considered "further++restrictions" within the meaning of section 10. If the Program as you++received it, or any part of it, contains a notice stating that it is++governed by this License along with a term that is a further++restriction, you may remove that term. If a license document contains++a further restriction but permits relicensing or conveying under this++License, you may add to a covered work material governed by the terms++of that license document, provided that the further restriction does++not survive such relicensing or conveying.++++ If you add terms to a covered work in accord with this section, you++must place, in the relevant source files, a statement of the++additional terms that apply to those files, or a notice indicating++where to find the applicable terms.++++ Additional terms, permissive or non-permissive, may be stated in the++form of a separately written license, or stated as exceptions;++the above requirements apply either way.++++ 8. Termination.++++ You may not propagate or modify a covered work except as expressly++provided under this License. Any attempt otherwise to propagate or++modify it is void, and will automatically terminate your rights under++this License (including any patent licenses granted under the third++paragraph of section 11).++++ However, if you cease all violation of this License, then your++license from a particular copyright holder is reinstated (a)++provisionally, unless and until the copyright holder explicitly and++finally terminates your license, and (b) permanently, if the copyright++holder fails to notify you of the violation by some reasonable means++prior to 60 days after the cessation.++++ Moreover, your license from a particular copyright holder is++reinstated permanently if the copyright holder notifies you of the++violation by some reasonable means, this is the first time you have++received notice of violation of this License (for any work) from that++copyright holder, and you cure the violation prior to 30 days after++your receipt of the notice.++++ Termination of your rights under this section does not terminate the++licenses of parties who have received copies or rights from you under++this License. If your rights have been terminated and not permanently++reinstated, you do not qualify to receive new licenses for the same++material under section 10.++++ 9. Acceptance Not Required for Having Copies.++++ You are not required to accept this License in order to receive or++run a copy of the Program. Ancillary propagation of a covered work++occurring solely as a consequence of using peer-to-peer transmission++to receive a copy likewise does not require acceptance. However,++nothing other than this License grants you permission to propagate or++modify any covered work. These actions infringe copyright if you do++not accept this License. Therefore, by modifying or propagating a++covered work, you indicate your acceptance of this License to do so.++++ 10. Automatic Licensing of Downstream Recipients.++++ Each time you convey a covered work, the recipient automatically++receives a license from the original licensors, to run, modify and++propagate that work, subject to this License. You are not responsible++for enforcing compliance by third parties with this License.++++ An "entity transaction" is a transaction transferring control of an++organization, or substantially all assets of one, or subdividing an++organization, or merging organizations. If propagation of a covered++work results from an entity transaction, each party to that++transaction who receives a copy of the work also receives whatever++licenses to the work the party's predecessor in interest had or could++give under the previous paragraph, plus a right to possession of the++Corresponding Source of the work from the predecessor in interest, if++the predecessor has it or can get it with reasonable efforts.++++ You may not impose any further restrictions on the exercise of the++rights granted or affirmed under this License. For example, you may++not impose a license fee, royalty, or other charge for exercise of++rights granted under this License, and you may not initiate litigation++(including a cross-claim or counterclaim in a lawsuit) alleging that++any patent claim is infringed by making, using, selling, offering for++sale, or importing the Program or any portion of it.++++ 11. Patents.++++ A "contributor" is a copyright holder who authorizes use under this++License of the Program or a work on which the Program is based. The++work thus licensed is called the contributor's "contributor version".++++ A contributor's "essential patent claims" are all patent claims++owned or controlled by the contributor, whether already acquired or++hereafter acquired, that would be infringed by some manner, permitted++by this License, of making, using, or selling its contributor version,++but do not include claims that would be infringed only as a++consequence of further modification of the contributor version. For++purposes of this definition, "control" includes the right to grant++patent sublicenses in a manner consistent with the requirements of++this License.++++ Each contributor grants you a non-exclusive, worldwide, royalty-free++patent license under the contributor's essential patent claims, to++make, use, sell, offer for sale, import and otherwise run, modify and++propagate the contents of its contributor version.++++ In the following three paragraphs, a "patent license" is any express++agreement or commitment, however denominated, not to enforce a patent++(such as an express permission to practice a patent or covenant not to++sue for patent infringement). To "grant" such a patent license to a++party means to make such an agreement or commitment not to enforce a++patent against the party.++++ If you convey a covered work, knowingly relying on a patent license,++and the Corresponding Source of the work is not available for anyone++to copy, free of charge and under the terms of this License, through a++publicly available network server or other readily accessible means,++then you must either (1) cause the Corresponding Source to be so++available, or (2) arrange to deprive yourself of the benefit of the++patent license for this particular work, or (3) arrange, in a manner++consistent with the requirements of this License, to extend the patent++license to downstream recipients. "Knowingly relying" means you have++actual knowledge that, but for the patent license, your conveying the++covered work in a country, or your recipient's use of the covered work++in a country, would infringe one or more identifiable patents in that++country that you have reason to believe are valid.++++ If, pursuant to or in connection with a single transaction or++arrangement, you convey, or propagate by procuring conveyance of, a++covered work, and grant a patent license to some of the parties++receiving the covered work authorizing them to use, propagate, modify++or convey a specific copy of the covered work, then the patent license++you grant is automatically extended to all recipients of the covered++work and works based on it.++++ A patent license is "discriminatory" if it does not include within++the scope of its coverage, prohibits the exercise of, or is++conditioned on the non-exercise of one or more of the rights that are++specifically granted under this License. You may not convey a covered++work if you are a party to an arrangement with a third party that is++in the business of distributing software, under which you make payment++to the third party based on the extent of your activity of conveying++the work, and under which the third party grants, to any of the++parties who would receive the covered work from you, a discriminatory++patent license (a) in connection with copies of the covered work++conveyed by you (or copies made from those copies), or (b) primarily++for and in connection with specific products or compilations that++contain the covered work, unless you entered into that arrangement,++or that patent license was granted, prior to 28 March 2007.++++ Nothing in this License shall be construed as excluding or limiting++any implied license or other defenses to infringement that may++otherwise be available to you under applicable patent law.++++ 12. No Surrender of Others' Freedom.++++ If conditions are imposed on you (whether by court order, agreement or++otherwise) that contradict the conditions of this License, they do not++excuse you from the conditions of this License. If you cannot convey a++covered work so as to satisfy simultaneously your obligations under this++License and any other pertinent obligations, then as a consequence you may++not convey it at all. For example, if you agree to terms that obligate you++to collect a royalty for further conveying from those to whom you convey++the Program, the only way you could satisfy both those terms and this++License would be to refrain entirely from conveying the Program.++++ 13. Remote Network Interaction; Use with the GNU General Public License.++++ Notwithstanding any other provision of this License, if you modify the++Program, your modified version must prominently offer all users++interacting with it remotely through a computer network (if your version++supports such interaction) an opportunity to receive the Corresponding++Source of your version by providing access to the Corresponding Source++from a network server at no charge, through some standard or customary++means of facilitating copying of software. This Corresponding Source++shall include the Corresponding Source for any work covered by version 3++of the GNU General Public License that is incorporated pursuant to the++following paragraph.++++ Notwithstanding any other provision of this License, you have++permission to link or combine any covered work with a work licensed++under version 3 of the GNU General Public License into a single++combined work, and to convey the resulting work. The terms of this++License will continue to apply to the part which is the covered work,++but the work with which it is combined will remain governed by version++3 of the GNU General Public License.++++ 14. Revised Versions of this License.++++ The Free Software Foundation may publish revised and/or new versions of++the GNU Affero General Public License from time to time. Such new versions++will be similar in spirit to the present version, but may differ in detail to++address new problems or concerns.++++ Each version is given a distinguishing version number. If the++Program specifies that a certain numbered version of the GNU Affero General++Public License "or any later version" applies to it, you have the++option of following the terms and conditions either of that numbered++version or of any later version published by the Free Software++Foundation. If the Program does not specify a version number of the++GNU Affero General Public License, you may choose any version ever published++by the Free Software Foundation.++++ If the Program specifies that a proxy can decide which future++versions of the GNU Affero General Public License can be used, that proxy's++public statement of acceptance of a version permanently authorizes you++to choose that version for the Program.++++ Later license versions may give you additional or different++permissions. However, no additional obligations are imposed on any++author or copyright holder as a result of your choosing to follow a++later version.++++ 15. Disclaimer of Warranty.++++ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY++APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT++HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY++OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,++THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR++PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM++IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF++ALL NECESSARY SERVICING, REPAIR OR CORRECTION.++++ 16. Limitation of Liability.++++ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING++WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS++THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY++GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE++USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF++DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD++PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),++EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF++SUCH DAMAGES.++++ 17. Interpretation of Sections 15 and 16.++++ If the disclaimer of warranty and limitation of liability provided++above cannot be given local legal effect according to their terms,++reviewing courts shall apply local law that most closely approximates++an absolute waiver of all civil liability in connection with the++Program, unless a warranty or assumption of liability accompanies a++copy of the Program in return for a fee.++++ END OF TERMS AND CONDITIONS++++ How to Apply These Terms to Your New Programs++++ If you develop a new program, and you want it to be of the greatest++possible use to the public, the best way to achieve this is to make it++free software which everyone can redistribute and change under these terms.++++ To do so, attach the following notices to the program. It is safest++to attach them to the start of each source file to most effectively++state the exclusion of warranty; and each file should have at least++the "copyright" line and a pointer to where the full notice is found.++++ <one line to give the program's name and a brief idea of what it does.>++ Copyright (C) <year> <name of author>++++ This program is free software: you can redistribute it and/or modify++ it under the terms of the GNU Affero General Public License as published++ by the Free Software Foundation, either version 3 of the License, or++ (at your option) any later version.++++ This program is distributed in the hope that it will be useful,++ but WITHOUT ANY WARRANTY; without even the implied warranty of++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the++ GNU Affero General Public License for more details.++++ You should have received a copy of the GNU Affero General Public License++ along with this program. If not, see <https://www.gnu.org/licenses/>.++++Also add information on how to contact you by electronic and paper mail.++++ If your software can interact with users remotely through a computer++network, you should also make sure that it provides a way for users to++get its source. For example, if your program is a web application, its++interface could display a "Source" link that leads users to an archive++of the code. There are many ways you could offer source, and different++solutions will be better for different programs; see section 13 for the++specific requirements.++++ You should also get your employer (if you work as a programmer) or school,++if any, to sign a "copyright disclaimer" for the program, if necessary.++For more information on this, and how to apply and follow the GNU AGPL, see++<https://www.gnu.org/licenses/>.nnew file mode 100644@@@ -0,0 +1,221 @@++# OpenVibe.Surf++++> Ride maps, surf the web, surf what's on.++++**Status:** v1, the three-surf front (v0.1.0, see [STATUS.json](STATUS.json)).++**Domain:** `openvibe.surf` · **Port:** 5110 · **Service id:** `surf` · **Env prefix:** `SURF`++**License:** AGPL-3.0-only (the same as every OpenVibe service).++++OpenVibe.Surf is the Surf room. One room, three surfs:++++- **Ride** — CS-style surf in the browser, served by OpenVibe.Games. The map catalogue is proxied from Games and++ every map plays in an embed (`https://openvibe.games/play/surf?map=<id>&embed=1`).++- **Watch** — lean-back channel surfing over OpenVibe.Live: live streams, clips people cut and the AI moments of++ the week, merged into one feed with one card shape.++- **Web** — *Surf with Vibe*: tell an agent what you are looking for and it browses the web for you on a private++ desk through OpenVibe.Actor, keeps its sources and finishes with a cited report.++++## What it is++++- **Identity:** service id `surf`, name OpenVibe.Surf, port 5110, host 127.0.0.1. It stores no accounts, no++ database and no tokens: the person's Network session lives in httpOnly cookies.++- **Sign-in:** an OAuth 2 client of OpenVibe.Network (`surf`), authorization code with PKCE S256, a state check++ and a server-side code exchange.++- **Stateless:** no database, no Valkey, no Events. The only state is a 30 s Watch feed cache, a 60 s Ride cache++ and the SDK's JWKS cache, all in memory.++++## Watch++++`GET /api/v1/watch/feed?topic=all|live|clips|moments&cursor=`++++Merges three public OpenVibe.Live reads (`OV_LIVE_INTERNAL_URL`, default `http://127.0.0.1:3000`):++++| Source | Live route | What it gives |++|---|---|---|++| live | `GET /api/streams` | `{ streams: [...] }` with `username`, `display_name`, `avatar_url`, `title`, `viewer_count`, `thumbnail_url` |++| clips | `GET /api/content/feed?type=clips&sort=new&window=week` | `items` with `href`, `thumbnail_url`, `preview_url`, `title`, `channel`, `duration_seconds` |++| moments | `GET /api/content/moments?type=clips&sort=top&window=week` | the same shape, AI-cut clips |++++Every card is one shape:++++```json++{++ "key": "live:bo",++ "kind": "live",++ "title": "Surf practice, all day",++ "channel": { "username": "bo", "display_name": "Bo", "avatar_url": null },++ "thumbnail": "https://openvibe.media/live/bo.jpg",++ "play": { "type": "live", "src": null, "page": "https://openvibe.live/@bo" },++ "duration_seconds": null,++ "viewers": 38,++ "why": "Live now · 38 watching"++}++```++++- A clip or moment has `play.type: "hls"`, `play.src` = its `preview_url` (an HLS master playlist on++ `openvibe.media`: Media 302s `/c/<id>` to `master.m3u8`) and `play.page` = its Live page. A live stream has++ `play.type: "live"`, `src: null` and `page` = `https://openvibe.live/@<username>` (Live has no embeddable++ player yet).++- **Ranking:** live streams first, by viewers; then moments and clips interleaved by freshness. Finally, no two++ cards from the same channel sit next to each other when another channel is available.++- **Cache:** a complete merged page for a topic/cursor is cached 30 s (`SURF_WATCH_CACHE_MS`); a partial page is++ not kept, so the next request retries the missing source. Each Live source gets 2 s++ (`SURF_WATCH_SOURCE_TIMEOUT_MS`); a source that fails or times out is left out, named in `sources`++ (`{ live: "ok", clips: "ok", moments: "error" }`) and `partial` is true.++- **Cursor:** opaque; it carries each Live feed's own `next` cursor. Page two does not repeat live streams.++- **Cache-Control:** `public, max-age=30`.++++## Ride++++`GET /api/v1/ride/maps` and `GET /api/v1/ride/maps/:id`++++Proxy to OpenVibe.Games (`OV_GAMES_INTERNAL_URL`, default `http://127.0.0.1:8000`), cached 60 s++(`SURF_RIDE_CACHE_MS`). The routes `/api/surf/maps` and `/api/surf/maps/:id` are being added to Games now. Until++they answer, Games gives 404: the list is `{ "maps": [], "games": "not_ready" }` and a map detail is a shell that++still carries the embed (`{ "map": { "id": … }, "top": [], "games": "not_ready" }`). A Games timeout or 5xx is a++`502 games.unavailable` problem and is never cached.++++Each map carries `embed_url`:++`https://openvibe.games/play/surf?map=<id>&embed=1` (`SURF_GAMES_PUBLIC_URL`).++++## Web — Surf with Vibe++++`POST /api/v1/web/tasks` (signed in; this site's own `Origin`) with `{ "query": "…" }` (3–500 characters).++++The person's own Network access token from the httpOnly `surf_at` cookie is sent to OpenVibe.Actor++(`OV_ACTOR_INTERNAL_URL`, default `http://127.0.0.1:4950`) as a Bearer on `POST /api/v1/tasks`. Actor accepts a++person's session token there; the task is theirs — their allowance and their task list — not OpenVibe.Surf's.++++The task text is a short standing brief plus the query unchanged:++++> Surf the web for me with your tools. Use search_web to find pages, read_page to read them, and a desk when a++> page needs one. Keep every source you use and finish with a short report that cites each source.++++The request carries an `Idempotency-Key` header (and Actor's body `idempotency_key`, the same value), so a retried++form does not start a second task. The answer is `{ "task_id": "tsk_…", "page": "https://openvibe.actor/tasks/tsk_…" }`++(201, `Location` set). Actor's 4xx refusal (401, 403, 429 allowance/budget, 422) keeps its status and its words;++an Actor that does not answer is a `502 actor.unavailable`.++++`GET /web/go?q=…` is the same task as a plain form for no-JavaScript: it creates the task and **303**s to the++Actor task page. A guest is sent to sign in; a bad query is a 400; an Actor refusal keeps its status and prints++its words. It is `private, no-store`.++++## Pages++++| Route | What it is |++|---|---|++| `/` | the front door: three tiles, Ride, Web, Watch |++| `/ride` | the maps list and the embed of the first map |++| `/ride/:map` | one map: name, tier, stages, records if Games answers, the embed |++| `/watch` | the feed as a list with each item's thumbnail and a play link; data hooks for the HLS `<video>` |++| `/web` | the Surf with Vibe form and what it does |++| `/developers` | the API, for people and agents |++| anything else | the 404 page |++++All of them are server-rendered through the OpenVibe Frame (`openvibe-shared/shell`) and readable without++JavaScript, with stable `sf-` ids and classes. A page rendered for a signed-in person is `private, no-store`; a++public page is `public, max-age=60` with `Vary: Cookie`. `public/css/surf.css` and `public/js/surf.js` are++one-comment placeholders: the main session designs them and adds the HLS player.++++## API++++| Route | Auth | Cache | What it does |++|---|---|---|---|++| `GET /api/v1/watch/feed` | none | `public, max-age=30` | the merged Live feed |++| `GET /api/v1/ride/maps` | none | `public, max-age=60` | the Games surf map catalogue |++| `GET /api/v1/ride/maps/:id` | none | `public, max-age=60` | one map and its fastest runs |++| `POST /api/v1/web/tasks` | cookie + Origin | `private, no-store` | create one Actor task as the person |++| `GET /web/go` | cookie | `private, no-store` | the no-JavaScript task form, 303 to Actor |++++Errors are RFC 9457 `application/problem+json`. A blank map id is 400; a bad Watch topic or cursor is 400. The++only outbound person token is the `Authorization: Bearer` on the request to Actor.++++Plumbing: `GET /api/health`, `GET /api/ready` (200 `ready` or `degraded`; Network, JWKS, the OAuth client, Live,++Games and Actor are optional checks), `GET /release.json`, `POST /release-metrics`, `/shared/*` (the pinned Frame),++`/css/*` and `/js/*` (content-hashed `?v=` → immutable), `GET /robots.txt`, `GET /sitemap.xml` (the public pages++and the Ride maps), `GET /llms.txt` and `GET /metrics` (loopback only).++++## Sign-in++++`server/auth/sso.js` is OpenVibe.Design's module with this site's names:++++- `GET /auth/login?next=/web[&silent=1]` mints a state and a PKCE S256 verifier/challenge, stores them (and the++ sanitized `next`) in the httpOnly `surf_oauth` flow cookie scoped to `/auth`, and redirects to++ `${OV_NETWORK_URL}/oauth/authorize` with `client_id=surf` and `redirect_uri=${BASE_URL}/auth/callback`.++- `GET /auth/callback` checks the state in constant time, exchanges the code at the Network's internal++ `/oauth/token` with the client secret and the code verifier, and sets `surf_at` (24 h) and `surf_rt` (30 d):++ httpOnly, SameSite=Lax, Secure in production, Path=/.++- `GET /auth/logout` clears them and revokes the refresh token (best effort); `GET /auth/me` answers `{ user }` or++ `{ user: null }` for the shared navbar. A FedCM assertion or an app/service token never becomes a session.++- `sanitizeNext` accepts same-site relative paths only. An absolute URL on this site's own origin counts as its++ path; another origin, `//evil`, a control character or a backslash goes to `/`.++++The Network signs the access token (RS256); `server/auth/keys.js` fetches and refreshes its JWKS from++`${OV_NETWORK_INTERNAL_URL}/api/.well-known/jwks` through `openvibe-sdk/auth`.++++## Configuration++++See [.env.example](.env.example). Required in production: `BASE_URL` and `OV_OAUTH_CLIENT_SECRET` (the `surf`++OAuth client on the Network).++++| Variable | Default | What |++|---|---|---|++| `OV_NETWORK_INTERNAL_URL` | `http://127.0.0.1:4000` | sign-in token exchange and JWKS |++| `OV_NETWORK_URL` | `https://openvibe.network` | where a browser is sent to authorize |++| `OV_NETWORK_ISSUER` | `OV_NETWORK_URL` | the token issuer |++| `OV_LIVE_INTERNAL_URL` | `http://127.0.0.1:3000` | Watch's three Live reads |++| `OV_GAMES_INTERNAL_URL` | `http://127.0.0.1:8000` | Ride's map reads |++| `OV_ACTOR_INTERNAL_URL` | `http://127.0.0.1:4950` | Web's task creation |++| `SURF_GAMES_PUBLIC_URL` | `https://openvibe.games` | the embed origin and `embed_url` |++| `SURF_LIVE_PUBLIC_URL` | `https://openvibe.live` | card page links |++| `SURF_ACTOR_PUBLIC_URL` | `https://openvibe.actor` | the task page a person is sent to |++| `SURF_WATCH_CACHE_MS` | `30000` | the merged Watch feed cache |++| `SURF_WATCH_SOURCE_TIMEOUT_MS` | `2000` | one Live source's budget |++| `SURF_RIDE_CACHE_MS` | `60000` | the Ride cache |++| `SURF_GAMES_TIMEOUT_MS` | `4000` | one Games read's budget |++| `SURF_ACTOR_TIMEOUT_MS` | `15000` | one Actor task creation's budget |++| `SURF_RATE_LIMIT_PER_MIN` | `120` | per-IP `/api` and `/auth` budget |++| `COOKIE_SECURE` | true in production | Secure cookies |++++## How to run++++```bash++npm install++npm run dev # http://localhost:5110++OV_ALLOW_SUITE=1 npm test++```++++`npm test` runs every `test/*.test.js` in its own process with in-process stand-ins++(`test/helpers/network.js`, `test/helpers/upstreams.js`): a real RS256 key pair and the OAuth token endpoint for++Network, and recorded Live, Games and Actor routes. Nothing calls the real internet. `OV_ALLOW_SUITE=1` is the++suite opt-in the shared test runner expects.++++## Deploy (for the lead)++++- **Deploy:** `sudo ovhost deploy surf` on the host (checkout at `/opt/openvibe.surf`, unit++ `openvibe-surf.service` on 127.0.0.1:5110, env `/etc/openvibe/surf.env`).++- **nginx:** [deploy/nginx/openvibe.surf.conf](deploy/nginx/openvibe.surf.conf), installed with++ `ov-vhost-install`. `/api` is reads only; `/api/v1/web/` and `/web/go` are the forms zone; `/auth` is GET only;++ `/auth/me` gets the API budget; `/internal` and `/metrics` are 404.++- **Readiness:** `/api/ready` reports `degraded` (HTTP 200) when Network, Live or Games does not answer; the++ process still serves the pages and the crawl artifacts.++++## Security (threat notes)++++Reporting a vulnerability: [SECURITY.md](SECURITY.md).++++- Session tokens are httpOnly cookies; no script can read them and nothing renders or logs them. The only place++ they go is the `Authorization` header of a request to the configured Actor origin.++- A Web task must come from this site's own origin (`Origin`), so another site cannot make a signed-in visitor++ spend their Actor allowance. `/web/go` refuses a cross-site navigation when the browser says it is one.++- `next` after sign-in is this site's own path only; control characters and backslashes are refused because++ browsers drop or rewrite them.++- Watch's card fields are allow-listed and escaped; only `http(s)` URLs become `href`/`src`. Actor's error++ detail is trimmed and no token or request body is logged.++- Secrets live only in the environment file; only variable names appear in code and docs, and no secret or token++ is logged.++++---++++Part of the [OpenVibe network](https://openvibe.network). Built in the open by++[OpenVibers](https://github.com/OpenVibers).nnew file mode 100644@@@ -0,0 +1,22 @@++# Security policy++++OpenVibe is run in the open, and we want to hear about security problems before anyone else does.++++## Reporting a vulnerability++++Email **[email protected]** with "security" in the subject. Please include what you found, where (URL,++repository, file), how to reproduce it, and what an attacker could do with it. Do not open a public GitHub issue for++a vulnerability, and do not test against other people's accounts, data or streams.++++We reply within 7 days, keep you updated while we fix it, and credit you when the fix ships if you would like.++++## Scope++++Every OpenVibe service and site (openvibe.surf, openvibe.design, openvibe.live, openvibe.network,++openvibe.tools, openvibe.media, openvibe.community, openvibe.games and the other openvibe.* domains) and every++repository under github.com/OpenVibers. Machine-readable contact details are at `/.well-known/security.txt` on++each site.++++## Supported versions++++Only the current `main` branch, which is what runs in production, receives fixes.nnew file mode 100644@@@ -0,0 +1,53 @@++{++ "repository": "OpenVibers/OpenVibe.Surf",++ "version": "0.1.0",++ "stage": "v1",++ "code": true,++ "wave": "W20",++ "plan": "OpenVibe.Surf: one room with three surfs. Ride is CS-style surf in the browser served by OpenVibe.Games; Watch is lean-back channel surfing over OpenVibe.Live's live streams, clips and AI moments; Web is Surf with Vibe, where an agent browses for you on a private desk through OpenVibe.Actor.",++ "runtime": "node 22, express 4, openvibe-shared/shell; stateless (no database, no Valkey, no Events)",++ "port": "5110",++ "domain": "openvibe.surf",++ "contracts": "openvibe-contracts v0.230.0",++ "sdk": "openvibe-sdk v0.70.0",++ "shared": "openvibe-shared v4.31.0",++ "packages": [++ "openvibe-contracts v0.230.0",++ "openvibe-sdk v0.70.0",++ "openvibe-shared v4.31.0"++ ],++ "summary": "The service skeleton for openvibe.surf: Network sign-in as OAuth client `surf` (PKCE S256, httpOnly surf_at/surf_rt cookies), the Watch feed merged from OpenVibe.Live (live streams by viewers, then clips and moments interleaved by freshness, no same-channel runs, cached 30 s, each source given 2 s, failures named in `sources`), the Ride proxy to OpenVibe.Games' /api/surf/maps (cached 60 s, games: 'not_ready' while Games' new routes answer 404, an embed URL per map), and Surf with Vibe: a signed-in, same-origin POST that creates one OpenVibe.Actor task with the person's own Network token as a Bearer, an Idempotency-Key and a cited browsing brief, plus /web/go as the no-JavaScript form. The pages are placeholders for the main session: stable sf- ids and classes, semantic HTML, readable without JavaScript.",++ "works": [++ "Sign-in with OpenVibe.Network: OAuth 2 authorization code, PKCE S256, state check, server-side exchange, httpOnly surf_at/surf_rt cookies, sanitizeNext (own origins only, no control characters or backslashes)",++ "Watch GET /api/v1/watch/feed?topic=all|live|clips|moments&cursor=: merges OpenVibe.Live's /api/streams, /api/content/feed?type=clips and /api/content/moments?type=clips into one item shape, ranks live by viewers then moments/clips by freshness with no channel twice in a row, caches 30 s, 2 s per source, a failing source left out and named in sources, public max-age=30",++ "Ride GET /api/v1/ride/maps and /api/v1/ride/maps/:id: proxies OpenVibe.Games' /api/surf/maps routes, adds each map's embed_url (https://openvibe.games/play/surf?map=<id>&embed=1), caches 60 s, Games 404 → empty list/map shell with games: 'not_ready'",++ "Web POST /api/v1/web/tasks: signed in, same-origin, query 3–500 characters, sends the person's surf_at token to OpenVibe.Actor as a Bearer with an Idempotency-Key and the prefixed browsing brief, answers { task_id, page }, and passes Actor's 4xx refusal through in words",++ "GET /web/go?q=: the same task as a plain form for no-JavaScript: 303 to the Actor task page, sign-in redirect for a guest, Actor's refusal in plain words",++ "Server-rendered pages: / (three tiles), /ride (maps list and first embed), /ride/:map (name, tier, stages, records, embed), /watch (feed list with thumbnails and play links, HLS hooks for the main session's video), /web (the form), /developers and a 404; public max-age=60 with Vary: Cookie, signed-in private, no-store",++ "Crawl artifacts (robots.txt, sitemap.xml with the public pages and Ride maps, llms.txt), /api/health, /api/ready (degraded when Network/Live/Games/Actor do not answer), /release.json, /release-metrics, /shared/*",++ "Helmet with the network's CSP (Games and Live frames, openvibe.media HLS), per-IP 120/min on /api and /auth, content-hashed /css and /js, graceful stop",++ "Deploy files: openvibe-surf.service, nginx vhost for openvibe.surf on 127.0.0.1:5110 with the forms zone, /auth/me on the API zone and /internal + /metrics 404",++ "Tests: feed merge/order/shape/failing source/30 s cache/cursor, ride proxy/cache/not_ready/failure, web 401/403/Actor Bearer + Idempotency-Key + prefixed text/refusal/redirect, SSO (Design's cases), pages/cache headers/CSP, discovery"++ ],++ "notYet": [++ "The pages' design and public/css/surf.css + public/js/surf.js: the main session writes them; today the views are semantic placeholders and the assets are one-comment files (the HLS <video> player is the main session's script)",++ "OpenVibe.Games' GET /api/surf/maps and /api/surf/maps/:id are being added in parallel; until they ship, Ride answers games: 'not_ready'",++ "A Surf service manifest and capabilities in OpenVibe.Contracts: this service has no capability of its own and no other service calls it",++ "Deployment: the deploy files are written, but the service is not deployed yet"++ ],++ "capabilitiesRegistered": false,++ "serviceManifestRegistered": false,++ "deployed": false,++ "placeholderInSites": false,++ "features": [++ "Network sign-in (PKCE)",++ "Watch merged Live feed",++ "Ride Games maps proxy",++ "Surf with Vibe via OpenVibe.Actor",++ "server-rendered pages",++ "crawl artifacts",++ "limits and readiness",++ "deploy files"++ ],++ "updated": "2026-10-28"++}nnew file mode 100644@@@ -0,0 +1,127 @@++## ═══════════════════════════════════════════════════════════════++## openvibe.surf — Nginx configuration (REFERENCE)++##++## Deployed to /etc/nginx/sites-available/openvibe.surf.conf (ov-vhost-install) — keep them in sync and reload++## nginx after changing it. The Node app (port 5110, systemd unit openvibe-surf) renders every page and serves the++## Watch/Ride/Web API; nginx terminates TLS, redirects www → apex, rate-limits the API, sign-in and the task forms,++## and compresses text.++##++## No proxy_cache: the app decides cacheability per response (public 30 s for the Watch feed, 60 s for Ride and the++## pages; private, no-store for a page or answer rendered for a signed-in person). Never log request bodies here.++## ═══════════════════════════════════════════════════════════════++++limit_req_zone $binary_remote_addr zone=surf_auth:10m rate=10r/m;++limit_req_zone $binary_remote_addr zone=surf_api:10m rate=5r/s;++# Starting an Actor task is a form, not a read: a person types, so a small budget is plenty. Both the JSON route and++# the no-JavaScript form take it, so neither is a way around the other.++limit_req_zone $binary_remote_addr zone=surf_forms:10m rate=20r/m;++++server {++ listen 80;++ listen [::]:80;++ server_name openvibe.surf www.openvibe.surf;++++ location /.well-known/acme-challenge/ { root /var/www/certbot; }++ location / { return 301 https://openvibe.surf$request_uri; }++}++++server {++ listen 443 ssl;++ listen [::]:443 ssl;++ http2 on;++ server_name www.openvibe.surf;++++ ssl_certificate /etc/letsencrypt/live/openvibe.surf/fullchain.pem;++ ssl_certificate_key /etc/letsencrypt/live/openvibe.surf/privkey.pem;++ ssl_protocols TLSv1.2 TLSv1.3;++++ return 301 https://openvibe.surf$request_uri;++}++++server {++ listen 443 ssl;++ listen [::]:443 ssl;++ http2 on;++ server_name openvibe.surf;++++ ssl_certificate /etc/letsencrypt/live/openvibe.surf/fullchain.pem;++ ssl_certificate_key /etc/letsencrypt/live/openvibe.surf/privkey.pem;++ ssl_protocols TLSv1.2 TLSv1.3;++++ access_log /var/log/nginx/openvibe.surf.access.log;++ error_log /var/log/nginx/openvibe.surf.error.log;++++ # A query is at most 500 characters; 32 KB leaves room for headers.++ client_max_body_size 32k;++++ add_header X-Content-Type-Options nosniff always;++ add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;++++ gzip on;++ gzip_proxied any;++ gzip_vary on;++ gzip_comp_level 5;++ gzip_min_length 1024;++ gzip_types text/css application/javascript text/javascript application/json application/problem+json image/svg+xml text/plain application/xml;++++ proxy_http_version 1.1;++ proxy_set_header Host $host;++ proxy_set_header X-Real-IP $remote_addr;++ proxy_set_header X-Forwarded-For $remote_addr;++ proxy_set_header CF-Connecting-IP $remote_addr;++ proxy_set_header X-Forwarded-Proto $scheme;++++ # ── The task form and the JSON route it shares: GET /web/go, POST /api/v1/web/tasks ──++ # A person's query is a write, so these have their own small budget. Nothing else under /api/v1/web is exposed.++ location = /web/go {++ limit_except GET { deny all; }++ limit_req zone=surf_forms burst=5 nodelay;++ limit_req_status 429;++ proxy_pass http://127.0.0.1:5110;++ }++++ location ^~ /api/v1/web/ {++ limit_except GET POST { deny all; }++ limit_req zone=surf_forms burst=5 nodelay;++ limit_req_status 429;++ proxy_pass http://127.0.0.1:5110;++ }++++ # ── The Watch and Ride API: reads only ──────────────────++ location /api/ {++ limit_except GET { deny all; }++ limit_req zone=surf_api burst=20 nodelay;++ limit_req_status 429;++ proxy_pass http://127.0.0.1:5110;++ }++++ # ── Sign-in: GET only ───────────────────────────────────++ # /auth/login, /auth/callback, /auth/me and /auth/logout are all GETs here. The app's POST /auth/logout confirm++ # is defence in depth and is not exposed by nginx; a same-origin navigation signs out directly.++ # The navbar's session probe runs on every page view: it gets the API's budget, not sign-in's.++ location = /auth/me {++ limit_req zone=surf_api burst=30 nodelay;++ limit_req_status 429;++ proxy_pass http://127.0.0.1:5110;++ }++++ location /auth/ {++ limit_except GET { deny all; }++ limit_req zone=surf_auth burst=10 nodelay;++ limit_req_status 429;++ proxy_pass http://127.0.0.1:5110;++ }++++ # ── Prometheus metrics: host-local scrapers only (http://127.0.0.1:5110/metrics) ──++ location ~* ^/metrics(/|$) { return 404; }++ location ~* ^/internal(/|$) { return 404; }++++ # ── Pages, discovery, static assets, release.json ──────++ location / {++ proxy_pass http://127.0.0.1:5110;++ }++++ location ~ /\.(?!well-known) {++ deny all;++ }++}nnew file mode 100644@@@ -0,0 +1,30 @@++[Unit]++Description=OpenVibe.Surf — Ride maps, surf the web, surf what's on.++# Network (sign-in, JWKS) should be up first, but OpenVibe.Surf degrades honestly without it: the pages, Watch and++# Ride serve, and sign-in waits for the signing key (see /api/ready). Live, Games and Actor are optional per request.++After=network.target++Wants=openvibe-network.service++++[Service]++Type=simple++User=ubuntu++Group=ubuntu++WorkingDirectory=/opt/openvibe.surf++EnvironmentFile=/etc/openvibe/surf.env++Environment=NODE_ENV=production++Environment=PORT=5110++Environment=PATH=/usr/local/bin:/usr/bin:/bin++ExecStart=/usr/bin/env node /opt/openvibe.surf/server/index.js++Restart=always++RestartSec=5++KillSignal=SIGTERM++# In-flight requests get the 10 s drain.++TimeoutStopSec=15++NoNewPrivileges=true++PrivateTmp=true++ProtectSystem=strict++ProtectHome=true++# Stateless: the code tree is read-only and nothing here writes to disk.++++[Install]++WantedBy=multi-user.targetnnew file mode 100644@@@ -0,0 +1,1143 @@++{++ "name": "openvibe-surf",++ "version": "0.1.0",++ "lockfileVersion": 3,++ "requires": true,++ "packages": {++ "": {++ "name": "openvibe-surf",++ "version": "0.1.0",++ "license": "AGPL-3.0-only",++ "dependencies": {++ "cookie-parser": "^1.4.7",++ "dotenv": "^16.4.7",++ "express": "^4.21.2",++ "express-rate-limit": "^7.5.0",++ "helmet": "^8.1.0",++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.230.0",++ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.70.0",++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.32.0"++ },++ "engines": {++ "node": ">=22.0.0"++ }++ },++ "node_modules/accepts": {++ "version": "1.3.8",++ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",++ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",++ "license": "MIT",++ "dependencies": {++ "mime-types": "~2.1.34",++ "negotiator": "0.6.3"++ },++ "engines": {++ "node": ">= 0.6"++ }++ },++ "node_modules/ajv": {++ "version": "8.20.0",++ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",++ "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==",++ "license": "MIT",++ "dependencies": {++ "fast-deep-equal": "^3.1.3",++ "fast-uri": "^3.0.1",++ "json-schema-traverse": "^1.0.0",++ "require-from-string": "^2.0.2"++ },++ "funding": {++ "type": "github",++ "url": "https://github.com/sponsors/epoberezkin"++ }++ },++ "node_modules/ajv-formats": {++ "version": "3.0.1",++ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz",++ "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==",++ "license": "MIT",++ "dependencies": {++ "ajv": "^8.0.0"++ },++ "peerDependencies": {++ "ajv": "^8.0.0"++ },++ "peerDependenciesMeta": {++ "ajv": {++ "optional": true++ }++ }++ },++ "node_modules/array-flatten": {++ "version": "1.1.1",++ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",++ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",++ "license": "MIT"++ },++ "node_modules/body-parser": {++ "version": "1.20.9",++ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.9.tgz",++ "integrity": "sha512-FISIQxV5u91Y9gvuFq3q/tA/qTqLJn3AhRcEvyozyhJTh5OBFFtv827AdHU7Rbyct4r+Ba1/99oFa1Y3yuFsdw==",++ "license": "MIT",++ "dependencies": {++ "bytes": "~3.1.2",++ "content-type": "~1.0.5",++ "debug": "2.6.9",++ "depd": "2.0.0",++ "destroy": "~1.2.0",++ "http-errors": "~2.0.1",++ "iconv-lite": "~0.4.24",++ "on-finished": "~2.4.1",++ "qs": "~6.16.0",++ "raw-body": "~2.5.3",++ "type-is": "~1.6.18",++ "unpipe": "~1.0.0"++ },++ "engines": {++ "node": ">= 0.8",++ "npm": "1.2.8000 || >= 1.4.16"++ }++ },++ "node_modules/buffer-equal-constant-time": {++ "version": "1.0.1",++ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",++ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",++ "license": "BSD-3-Clause"++ },++ "node_modules/bytes": {++ "version": "3.1.2",++ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",++ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",++ "license": "MIT",++ "engines": {++ "node": ">= 0.8"++ }++ },++ "node_modules/call-bind-apply-helpers": {++ "version": "1.0.2",++ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",++ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",++ "license": "MIT",++ "dependencies": {++ "es-errors": "^1.3.0",++ "function-bind": "^1.1.2"++ },++ "engines": {++ "node": ">= 0.4"++ }++ },++ "node_modules/call-bound": {++ "version": "1.0.4",++ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",++ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",++ "license": "MIT",++ "dependencies": {++ "call-bind-apply-helpers": "^1.0.2",++ "get-intrinsic": "^1.3.0"++ },++ "engines": {++ "node": ">= 0.4"++ },++ "funding": {++ "url": "https://github.com/sponsors/ljharb"++ }++ },++ "node_modules/content-disposition": {++ "version": "0.5.4",++ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",++ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",++ "license": "MIT",++ "dependencies": {++ "safe-buffer": "5.2.1"++ },++ "engines": {++ "node": ">= 0.6"++ }++ },++ "node_modules/content-type": {++ "version": "1.0.5",++ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",++ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",++ "license": "MIT",++ "engines": {++ "node": ">= 0.6"++ }++ },++ "node_modules/cookie": {++ "version": "0.7.2",++ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",++ "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",++ "license": "MIT",++ "engines": {++ "node": ">= 0.6"++ }++ },++ "node_modules/cookie-parser": {++ "version": "1.4.7",++ "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz",++ "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==",++ "license": "MIT",++ "dependencies": {++ "cookie": "0.7.2",++ "cookie-signature": "1.0.6"++ },++ "engines": {++ "node": ">= 0.8.0"++ }++ },++ "node_modules/cookie-signature": {++ "version": "1.0.6",++ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",++ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",++ "license": "MIT"++ },++ "node_modules/debug": {++ "version": "2.6.9",++ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",++ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",++ "license": "MIT",++ "dependencies": {++ "ms": "2.0.0"++ }++ },++ "node_modules/depd": {++ "version": "2.0.0",++ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",++ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",++ "license": "MIT",++ "engines": {++ "node": ">= 0.8"++ }++ },++ "node_modules/destroy": {++ "version": "1.2.0",++ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",++ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",++ "license": "MIT",++ "engines": {++ "node": ">= 0.8",++ "npm": "1.2.8000 || >= 1.4.16"++ }++ },++ "node_modules/dotenv": {++ "version": "16.6.1",++ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",++ "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",++ "license": "BSD-2-Clause",++ "engines": {++ "node": ">=12"++ },++ "funding": {++ "url": "https://dotenvx.com"++ }++ },++ "node_modules/dunder-proto": {++ "version": "1.0.1",++ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",++ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",++ "license": "MIT",++ "dependencies": {++ "call-bind-apply-helpers": "^1.0.1",++ "es-errors": "^1.3.0",++ "gopd": "^1.2.0"++ },++ "engines": {++ "node": ">= 0.4"++ }++ },++ "node_modules/ecdsa-sig-formatter": {++ "version": "1.0.11",++ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",++ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",++ "license": "Apache-2.0",++ "dependencies": {++ "safe-buffer": "^5.0.1"++ }++ },++ "node_modules/ee-first": {++ "version": "1.1.1",++ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",++ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",++ "license": "MIT"++ },++ "node_modules/encodeurl": {++ "version": "2.0.0",++ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",++ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",++ "license": "MIT",++ "engines": {++ "node": ">= 0.8"++ }++ },++ "node_modules/es-define-property": {++ "version": "1.0.1",++ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",++ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",++ "license": "MIT",++ "engines": {++ "node": ">= 0.4"++ }++ },++ "node_modules/es-errors": {++ "version": "1.3.0",++ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",++ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",++ "license": "MIT",++ "engines": {++ "node": ">= 0.4"++ }++ },++ "node_modules/es-object-atoms": {++ "version": "1.1.2",++ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",++ "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",++ "license": "MIT",++ "dependencies": {++ "es-errors": "^1.3.0"++ },++ "engines": {++ "node": ">= 0.4"++ }++ },++ "node_modules/escape-html": {++ "version": "1.0.3",++ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",++ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",++ "license": "MIT"++ },++ "node_modules/etag": {++ "version": "1.8.1",++ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",++ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",++ "license": "MIT",++ "engines": {++ "node": ">= 0.6"++ }++ },++ "node_modules/express": {++ "version": "4.22.3",++ "resolved": "https://registry.npmjs.org/express/-/express-4.22.3.tgz",++ "integrity": "sha512-Bdcs4+3qlpVlx2NRn6fgX2Ue2/gGRaPeawebgclM0ERSCqDpA+owF1fdPwjJUTAJWMTuAaxjDf+hzb0/4eKvvw==",++ "license": "MIT",++ "dependencies": {++ "accepts": "~1.3.8",++ "array-flatten": "1.1.1",++ "body-parser": "~1.20.5",++ "content-disposition": "~0.5.4",++ "content-type": "~1.0.4",++ "cookie": "~0.7.1",++ "cookie-signature": "~1.0.6",++ "debug": "2.6.9",++ "depd": "2.0.0",++ "encodeurl": "~2.0.0",++ "escape-html": "~1.0.3",++ "etag": "~1.8.1",++ "finalhandler": "~1.3.1",++ "fresh": "~0.5.2",++ "http-errors": "~2.0.0",++ "merge-descriptors": "1.0.3",++ "methods": "~1.1.2",++ "on-finished": "~2.4.1",++ "parseurl": "~1.3.3",++ "path-to-regexp": "~0.1.13",++ "proxy-addr": "~2.0.7",++ "qs": "~6.16.0",++ "range-parser": "~1.2.1",++ "safe-buffer": "5.2.1",++ "send": "~0.19.0",++ "serve-static": "~1.16.2",++ "setprototypeof": "1.2.0",++ "statuses": "~2.0.1",++ "type-is": "~1.6.18",++ "utils-merge": "1.0.1",++ "vary": "~1.1.2"++ },++ "engines": {++ "node": ">= 0.10.0"++ },++ "funding": {++ "type": "opencollective",++ "url": "https://opencollective.com/express"++ }++ },++ "node_modules/express-rate-limit": {++ "version": "7.5.1",++ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz",++ "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==",++ "license": "MIT",++ "engines": {++ "node": ">= 16"++ },++ "funding": {++ "url": "https://github.com/sponsors/express-rate-limit"++ },++ "peerDependencies": {++ "express": ">= 4.11"++ }++ },++ "node_modules/fast-deep-equal": {++ "version": "3.1.3",++ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",++ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",++ "license": "MIT"++ },++ "node_modules/fast-uri": {++ "version": "3.1.9",++ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.9.tgz",++ "integrity": "sha512-hS/0Wt3Ya043oKXs0sIxrMpKQITXhMLIrVAKKXi4gozGC9v7YRWVnXIZzjNPTO02M9BdxLCWqK1yRXgNziVyJQ==",++ "funding": [++ {++ "type": "github",++ "url": "https://github.com/sponsors/fastify"++ },++ {++ "type": "opencollective",++ "url": "https://opencollective.com/fastify"++ }++ ],++ "license": "BSD-3-Clause"++ },++ "node_modules/finalhandler": {++ "version": "1.3.2",++ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",++ "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==",++ "license": "MIT",++ "dependencies": {++ "debug": "2.6.9",++ "encodeurl": "~2.0.0",++ "escape-html": "~1.0.3",++ "on-finished": "~2.4.1",++ "parseurl": "~1.3.3",++ "statuses": "~2.0.2",++ "unpipe": "~1.0.0"++ },++ "engines": {++ "node": ">= 0.8"++ }++ },++ "node_modules/forwarded": {++ "version": "0.2.0",++ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",++ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",++ "license": "MIT",++ "engines": {++ "node": ">= 0.6"++ }++ },++ "node_modules/fresh": {++ "version": "0.5.2",++ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",++ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",++ "license": "MIT",++ "engines": {++ "node": ">= 0.6"++ }++ },++ "node_modules/function-bind": {++ "version": "1.1.2",++ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",++ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",++ "license": "MIT",++ "funding": {++ "url": "https://github.com/sponsors/ljharb"++ }++ },++ "node_modules/get-intrinsic": {++ "version": "1.3.0",++ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",++ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",++ "license": "MIT",++ "dependencies": {++ "call-bind-apply-helpers": "^1.0.2",++ "es-define-property": "^1.0.1",++ "es-errors": "^1.3.0",++ "es-object-atoms": "^1.1.1",++ "function-bind": "^1.1.2",++ "get-proto": "^1.0.1",++ "gopd": "^1.2.0",++ "has-symbols": "^1.1.0",++ "hasown": "^2.0.2",++ "math-intrinsics": "^1.1.0"++ },++ "engines": {++ "node": ">= 0.4"++ },++ "funding": {++ "url": "https://github.com/sponsors/ljharb"++ }++ },++ "node_modules/get-proto": {++ "version": "1.0.1",++ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",++ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",++ "license": "MIT",++ "dependencies": {++ "dunder-proto": "^1.0.1",++ "es-object-atoms": "^1.0.0"++ },++ "engines": {++ "node": ">= 0.4"++ }++ },++ "node_modules/gopd": {++ "version": "1.2.0",++ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",++ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",++ "license": "MIT",++ "engines": {++ "node": ">= 0.4"++ },++ "funding": {++ "url": "https://github.com/sponsors/ljharb"++ }++ },++ "node_modules/has-symbols": {++ "version": "1.1.0",++ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",++ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",++ "license": "MIT",++ "engines": {++ "node": ">= 0.4"++ },++ "funding": {++ "url": "https://github.com/sponsors/ljharb"++ }++ },++ "node_modules/hasown": {++ "version": "2.0.4",++ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",++ "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",++ "license": "MIT",++ "dependencies": {++ "function-bind": "^1.1.2"++ },++ "engines": {++ "node": ">= 0.4"++ }++ },++ "node_modules/helmet": {++ "version": "8.3.0",++ "resolved": "https://registry.npmjs.org/helmet/-/helmet-8.3.0.tgz",++ "integrity": "sha512-Qgpiaws3Sm30Av8Eah6sjMCZZwjlBu+E68rhpCWBshY1lb09HtLwj5GviX0OyQIn+ulUS0iX0AxN5n3tLZzz1w==",++ "license": "MIT",++ "engines": {++ "node": ">=18.0.0"++ },++ "funding": {++ "url": "https://github.com/sponsors/EvanHahn"++ }++ },++ "node_modules/http-errors": {++ "version": "2.0.1",++ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",++ "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",++ "license": "MIT",++ "dependencies": {++ "depd": "~2.0.0",++ "inherits": "~2.0.4",++ "setprototypeof": "~1.2.0",++ "statuses": "~2.0.2",++ "toidentifier": "~1.0.1"++ },++ "engines": {++ "node": ">= 0.8"++ },++ "funding": {++ "type": "opencollective",++ "url": "https://opencollective.com/express"++ }++ },++ "node_modules/iconv-lite": {++ "version": "0.4.24",++ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",++ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",++ "license": "MIT",++ "dependencies": {++ "safer-buffer": ">= 2.1.2 < 3"++ },++ "engines": {++ "node": ">=0.10.0"++ }++ },++ "node_modules/inherits": {++ "version": "2.0.4",++ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",++ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",++ "license": "ISC"++ },++ "node_modules/ipaddr.js": {++ "version": "1.9.1",++ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",++ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",++ "license": "MIT",++ "engines": {++ "node": ">= 0.10"++ }++ },++ "node_modules/json-schema-traverse": {++ "version": "1.0.0",++ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",++ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",++ "license": "MIT"++ },++ "node_modules/jsonwebtoken": {++ "version": "9.0.3",++ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz",++ "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==",++ "license": "MIT",++ "dependencies": {++ "jws": "^4.0.1",++ "lodash.includes": "^4.3.0",++ "lodash.isboolean": "^3.0.3",++ "lodash.isinteger": "^4.0.4",++ "lodash.isnumber": "^3.0.3",++ "lodash.isplainobject": "^4.0.6",++ "lodash.isstring": "^4.0.1",++ "lodash.once": "^4.0.0",++ "ms": "^2.1.1",++ "semver": "^7.5.4"++ },++ "engines": {++ "node": ">=12",++ "npm": ">=6"++ }++ },++ "node_modules/jsonwebtoken/node_modules/ms": {++ "version": "2.1.3",++ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",++ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",++ "license": "MIT"++ },++ "node_modules/jwa": {++ "version": "2.0.1",++ "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",++ "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",++ "license": "MIT",++ "dependencies": {++ "buffer-equal-constant-time": "^1.0.1",++ "ecdsa-sig-formatter": "1.0.11",++ "safe-buffer": "^5.0.1"++ }++ },++ "node_modules/jws": {++ "version": "4.0.1",++ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",++ "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",++ "license": "MIT",++ "dependencies": {++ "jwa": "^2.0.1",++ "safe-buffer": "^5.0.1"++ }++ },++ "node_modules/lodash.includes": {++ "version": "4.3.0",++ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",++ "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",++ "license": "MIT"++ },++ "node_modules/lodash.isboolean": {++ "version": "3.0.3",++ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",++ "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",++ "license": "MIT"++ },++ "node_modules/lodash.isinteger": {++ "version": "4.0.4",++ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",++ "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",++ "license": "MIT"++ },++ "node_modules/lodash.isnumber": {++ "version": "3.0.3",++ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",++ "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",++ "license": "MIT"++ },++ "node_modules/lodash.isplainobject": {++ "version": "4.0.6",++ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",++ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",++ "license": "MIT"++ },++ "node_modules/lodash.isstring": {++ "version": "4.0.1",++ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",++ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",++ "license": "MIT"++ },++ "node_modules/lodash.once": {++ "version": "4.1.1",++ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",++ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",++ "license": "MIT"++ },++ "node_modules/math-intrinsics": {++ "version": "1.1.0",++ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",++ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",++ "license": "MIT",++ "engines": {++ "node": ">= 0.4"++ }++ },++ "node_modules/media-typer": {++ "version": "0.3.0",++ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",++ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",++ "license": "MIT",++ "engines": {++ "node": ">= 0.6"++ }++ },++ "node_modules/merge-descriptors": {++ "version": "1.0.3",++ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",++ "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",++ "license": "MIT",++ "funding": {++ "url": "https://github.com/sponsors/sindresorhus"++ }++ },++ "node_modules/methods": {++ "version": "1.1.2",++ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",++ "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",++ "license": "MIT",++ "engines": {++ "node": ">= 0.6"++ }++ },++ "node_modules/mime": {++ "version": "1.6.0",++ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",++ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",++ "license": "MIT",++ "bin": {++ "mime": "cli.js"++ },++ "engines": {++ "node": ">=4"++ }++ },++ "node_modules/mime-db": {++ "version": "1.52.0",++ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",++ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",++ "license": "MIT",++ "engines": {++ "node": ">= 0.6"++ }++ },++ "node_modules/mime-types": {++ "version": "2.1.35",++ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",++ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",++ "license": "MIT",++ "dependencies": {++ "mime-db": "1.52.0"++ },++ "engines": {++ "node": ">= 0.6"++ }++ },++ "node_modules/ms": {++ "version": "2.0.0",++ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",++ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",++ "license": "MIT"++ },++ "node_modules/negotiator": {++ "version": "0.6.3",++ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",++ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",++ "license": "MIT",++ "engines": {++ "node": ">= 0.6"++ }++ },++ "node_modules/object-inspect": {++ "version": "1.13.4",++ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",++ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",++ "license": "MIT",++ "engines": {++ "node": ">= 0.4"++ },++ "funding": {++ "url": "https://github.com/sponsors/ljharb"++ }++ },++ "node_modules/on-finished": {++ "version": "2.4.2",++ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.2.tgz",++ "integrity": "sha512-ATWUv387i+nLD49EiDoOft5scf32Vsdz3ktrQJG9bi7jRFmaXTssvyvj1EmeKVVgvesbqUtvQQvaS2TpTukAZQ==",++ "license": "MIT",++ "dependencies": {++ "ee-first": "1.1.1"++ },++ "engines": {++ "node": ">= 0.8"++ },++ "funding": {++ "type": "opencollective",++ "url": "https://opencollective.com/express"++ }++ },++ "node_modules/openvibe-contracts": {++ "version": "0.230.0",++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.230.0",++ "integrity": "sha512-wvzSYV0GKfHV6UnEKtqDAXkrl3yimdWe/KHM0jeU0JWkrRo93zGNAZ2fRPwATBeihITVE0b973dM899U/YOzgw==",++ "license": "AGPL-3.0-only",++ "dependencies": {++ "ajv": "^8.17.1",++ "ajv-formats": "^3.0.1"++ },++ "bin": {++ "openvibe-contracts-check": "bin/check-service.js"++ },++ "engines": {++ "node": ">=20"++ }++ },++ "node_modules/openvibe-sdk": {++ "version": "0.70.0",++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.70.0",++ "integrity": "sha512-iMNxlnRm0fmyj60nHiC/lKAaG3JAqiJgu42lShnkby4yF4dQJRG3G0trrqOoULZKBalrYHb/gjauRwNQYN/tog==",++ "license": "MIT",++ "bin": {++ "openvibe-download": "bin/openvibe-download.js"++ },++ "engines": {++ "node": ">=20"++ },++ "peerDependencies": {++ "@electric-sql/pglite": "^0.5.8",++ "iovalkey": "^0.4.0",++ "pg": "^8.23.0"++ },++ "peerDependenciesMeta": {++ "@electric-sql/pglite": {++ "optional": true++ },++ "iovalkey": {++ "optional": true++ },++ "pg": {++ "optional": true++ }++ }++ },++ "node_modules/openvibe-shared": {++ "version": "4.32.0",++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.32.0",++ "integrity": "sha512-D4P2ron5WiE0Qzyvfnp6Fh7JFjTZcoxkKlB+6nq+ceZewbInPXyPDcur+XwtINKuTIdC7lRu3EZDSFWfZQv+Nw==",++ "license": "MIT",++ "dependencies": {++ "jsonwebtoken": "^9.0.0"++ },++ "bin": {++ "openvibe-build-assets": "scripts/build-assets.js"++ }++ },++ "node_modules/parseurl": {++ "version": "1.3.3",++ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",++ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",++ "license": "MIT",++ "engines": {++ "node": ">= 0.8"++ }++ },++ "node_modules/path-to-regexp": {++ "version": "0.1.13",++ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz",++ "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==",++ "license": "MIT"++ },++ "node_modules/proxy-addr": {++ "version": "2.0.8",++ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.8.tgz",++ "integrity": "sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==",++ "license": "MIT",++ "dependencies": {++ "forwarded": "0.2.0",++ "ipaddr.js": "1.9.1"++ },++ "engines": {++ "node": ">= 0.10"++ },++ "funding": {++ "type": "opencollective",++ "url": "https://opencollective.com/express"++ }++ },++ "node_modules/qs": {++ "version": "6.16.2",++ "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.2.tgz",++ "integrity": "sha512-VYdnB5S7/0IeXzBacUtwNAaM6d7Y/PgYnNC7bJCI6meqA/OgnsQbZMtVW8Dt2D1PHgbHGtYmhSoEIgvt7xZ2ZA==",++ "license": "BSD-3-Clause",++ "dependencies": {++ "es-define-property": "^1.0.1",++ "side-channel": "^1.1.1"++ },++ "engines": {++ "node": ">=0.6"++ },++ "funding": {++ "url": "https://github.com/sponsors/ljharb"++ }++ },++ "node_modules/range-parser": {++ "version": "1.2.1",++ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",++ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",++ "license": "MIT",++ "engines": {++ "node": ">= 0.6"++ }++ },++ "node_modules/raw-body": {++ "version": "2.5.3",++ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",++ "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",++ "license": "MIT",++ "dependencies": {++ "bytes": "~3.1.2",++ "http-errors": "~2.0.1",++ "iconv-lite": "~0.4.24",++ "unpipe": "~1.0.0"++ },++ "engines": {++ "node": ">= 0.8"++ }++ },++ "node_modules/require-from-string": {++ "version": "2.0.2",++ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",++ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",++ "license": "MIT",++ "engines": {++ "node": ">=0.10.0"++ }++ },++ "node_modules/safe-buffer": {++ "version": "5.2.1",++ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",++ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",++ "funding": [++ {++ "type": "github",++ "url": "https://github.com/sponsors/feross"++ },++ {++ "type": "patreon",++ "url": "https://www.patreon.com/feross"++ },++ {++ "type": "consulting",++ "url": "https://feross.org/support"++ }++ ],++ "license": "MIT"++ },++ "node_modules/safer-buffer": {++ "version": "2.1.2",++ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",++ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",++ "license": "MIT"++ },++ "node_modules/semver": {++ "version": "7.8.5",++ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",++ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",++ "license": "ISC",++ "bin": {++ "semver": "bin/semver.js"++ },++ "engines": {++ "node": ">=10"++ }++ },++ "node_modules/send": {++ "version": "0.19.2",++ "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz",++ "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==",++ "license": "MIT",++ "dependencies": {++ "debug": "2.6.9",++ "depd": "2.0.0",++ "destroy": "1.2.0",++ "encodeurl": "~2.0.0",++ "escape-html": "~1.0.3",++ "etag": "~1.8.1",++ "fresh": "~0.5.2",++ "http-errors": "~2.0.1",++ "mime": "1.6.0",++ "ms": "2.1.3",++ "on-finished": "~2.4.1",++ "range-parser": "~1.2.1",++ "statuses": "~2.0.2"++ },++ "engines": {++ "node": ">= 0.8.0"++ }++ },++ "node_modules/send/node_modules/ms": {++ "version": "2.1.3",++ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",++ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",++ "license": "MIT"++ },++ "node_modules/serve-static": {++ "version": "1.16.3",++ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz",++ "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==",++ "license": "MIT",++ "dependencies": {++ "encodeurl": "~2.0.0",++ "escape-html": "~1.0.3",++ "parseurl": "~1.3.3",++ "send": "~0.19.1"++ },++ "engines": {++ "node": ">= 0.8.0"++ }++ },++ "node_modules/setprototypeof": {++ "version": "1.2.0",++ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",++ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",++ "license": "ISC"++ },++ "node_modules/side-channel": {++ "version": "1.1.1",++ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz",++ "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",++ "license": "MIT",++ "dependencies": {++ "es-errors": "^1.3.0",++ "object-inspect": "^1.13.4",++ "side-channel-list": "^1.0.1",++ "side-channel-map": "^1.0.1",++ "side-channel-weakmap": "^1.0.2"++ },++ "engines": {++ "node": ">= 0.4"++ },++ "funding": {++ "url": "https://github.com/sponsors/ljharb"++ }++ },++ "node_modules/side-channel-list": {++ "version": "1.0.1",++ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",++ "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",++ "license": "MIT",++ "dependencies": {++ "es-errors": "^1.3.0",++ "object-inspect": "^1.13.4"++ },++ "engines": {++ "node": ">= 0.4"++ },++ "funding": {++ "url": "https://github.com/sponsors/ljharb"++ }++ },++ "node_modules/side-channel-map": {++ "version": "1.0.1",++ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",++ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",++ "license": "MIT",++ "dependencies": {++ "call-bound": "^1.0.2",++ "es-errors": "^1.3.0",++ "get-intrinsic": "^1.2.5",++ "object-inspect": "^1.13.3"++ },++ "engines": {++ "node": ">= 0.4"++ },++ "funding": {++ "url": "https://github.com/sponsors/ljharb"++ }++ },++ "node_modules/side-channel-weakmap": {++ "version": "1.0.2",++ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",++ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",++ "license": "MIT",++ "dependencies": {++ "call-bound": "^1.0.2",++ "es-errors": "^1.3.0",++ "get-intrinsic": "^1.2.5",++ "object-inspect": "^1.13.3",++ "side-channel-map": "^1.0.1"++ },++ "engines": {++ "node": ">= 0.4"++ },++ "funding": {++ "url": "https://github.com/sponsors/ljharb"++ }++ },++ "node_modules/statuses": {++ "version": "2.0.2",++ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",++ "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",++ "license": "MIT",++ "engines": {++ "node": ">= 0.8"++ }++ },++ "node_modules/toidentifier": {++ "version": "1.0.1",++ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",++ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",++ "license": "MIT",++ "engines": {++ "node": ">=0.6"++ }++ },++ "node_modules/type-is": {++ "version": "1.6.18",++ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",++ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",++ "license": "MIT",++ "dependencies": {++ "media-typer": "0.3.0",++ "mime-types": "~2.1.24"++ },++ "engines": {++ "node": ">= 0.6"++ }++ },++ "node_modules/unpipe": {++ "version": "1.0.0",++ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",++ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",++ "license": "MIT",++ "engines": {++ "node": ">= 0.8"++ }++ },++ "node_modules/utils-merge": {++ "version": "1.0.1",++ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",++ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",++ "license": "MIT",++ "engines": {++ "node": ">= 0.4.0"++ }++ },++ "node_modules/vary": {++ "version": "1.1.2",++ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",++ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",++ "license": "MIT",++ "engines": {++ "node": ">= 0.8"++ }++ }++ }++}nnew file mode 100644@@@ -0,0 +1,30 @@++{++ "name": "openvibe-surf",++ "version": "0.1.0",++ "private": true,++ "description": "OpenVibe.Surf — Ride maps, surf the web, surf what's on.",++ "main": "server/index.js",++ "scripts": {++ "start": "node server/index.js",++ "dev": "NODE_ENV=development node server/index.js",++ "test": "node test/run.js"++ },++ "keywords": [++ "openvibe"++ ],++ "author": "OpenVibers",++ "license": "AGPL-3.0-only",++ "dependencies": {++ "cookie-parser": "^1.4.7",++ "dotenv": "^16.4.7",++ "express": "^4.21.2",++ "express-rate-limit": "^7.5.0",++ "helmet": "^8.1.0",++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.230.0",++ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.70.0",++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.32.0"++ },++ "engines": {++ "node": ">=22.0.0"++ }++}nnew file mode 100644@@@ -0,0 +1,174 @@++/* OpenVibe.Surf: the room's own pieces, on the shared room kit (kit.css, room.css, showcase.css). The page wears the++ person's theme; --room-* is Surf's cyan (rooms.js). Phone first: the TV stacks (screen, then the guide), the map++ page stacks (game, then records); from 980 px they sit side by side. */++*, *::before, *::after { box-sizing: border-box; }++html, body { margin: 0; background: var(--bg-primary, #0a0f1c); color: var(--text-primary, #e6edf7); font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; }++.skip { position: absolute; left: -9999px; }++.skip:focus { left: 12px; top: 12px; z-index: 99; padding: 8px 12px; border-radius: 10px; background: var(--bg-card, #0f1729); color: inherit; }++.sf-wrap { max-width: 1240px; margin: 0 auto; padding: 0 16px 64px; }++.sf-page-pad { padding-top: clamp(20px, 4vw, 40px); }++.sf-narrow { max-width: 760px; }++.sf a:not([class]) { color: var(--room-strong); }++.sf h2 { margin: 0 0 10px; font-size: 1.05rem; font-weight: 800; letter-spacing: -.01em; }++:where(.sf) .ovk-i { fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }++.sf-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }++.sf-kicker { margin: 0 0 4px; font-size: .74rem; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--room-strong); }++.sf-kicker a { color: inherit; }++.sf-lede { margin: 8px 0 0; max-width: 700px; color: var(--text-secondary, #96a7c2); line-height: 1.55; }++.sf-fine { color: var(--text-muted, #7386a3); font-size: .84rem; line-height: 1.5; }++.sf-page-h { margin-bottom: 20px; }++/* Filled buttons wear the room's blue, as the home page's Start does. */++.sf .ovk-btn--primary { background: var(--room-fill); color: var(--room-on); }++.sf-page-h h1 { margin: 0; font-size: clamp(1.7rem, 4.2vw, 2.4rem); font-weight: 800; letter-spacing: -.03em; line-height: 1.1; }++.sf-head-row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }++.sf-more { display: inline-flex; align-items: center; gap: 2px; font-weight: 700; font-size: .9rem; text-decoration: none; color: var(--room-strong); }++.sf-more .ovk-i { width: 1em; height: 1em; }++.sf section + section { margin-top: 28px; }++.sf-note { margin: 16px 0; padding: 10px 14px; border-radius: 12px; font-size: .9rem; color: var(--text-secondary, #96a7c2); background: color-mix(in srgb, var(--warning, #f5a524) 12%, transparent); }++.sf-empty { display: grid; justify-items: center; gap: 10px; padding: 36px 20px; border-radius: 18px; border: 1px dashed var(--border, #1f2d47); text-align: center; color: var(--text-secondary, #96a7c2); }++.sf-empty > .ovk-i { width: 40px; height: 40px; color: var(--room-accent); stroke-width: 1.6; }++.sf-empty p { margin: 0; max-width: 460px; line-height: 1.5; }++.sf-empty h1 { margin: 0; font-size: 1.6rem; font-weight: 800; color: var(--text-primary, #e6edf7); }++.sf-card { padding: 16px; border-radius: 18px; border: 1px solid var(--border, #1f2d47); background: var(--bg-secondary, #101828); }++.sf-card h2 { display: flex; align-items: center; gap: 8px; }++.sf-card h2 .ovk-i { width: 18px; height: 18px; color: var(--room-strong); }++kbd { display: inline-block; min-width: 1.6em; padding: 0 5px; border-radius: 6px; border: 1px solid var(--border, #1f2d47); border-bottom-width: 2px; background: var(--bg-card, #0f1729); font: 700 .78rem/1.5 ui-monospace, SFMono-Regular, Menlo, monospace; text-align: center; color: var(--text-primary, #e6edf7); }++++/* The home's run motif: a first-person run down two ramps, speed lines, the timer. */++.sf-run { position: relative; width: min(100%, 440px); aspect-ratio: 400 / 260; margin: 0 auto; border-radius: 22px; overflow: hidden; color: var(--room-accent);++ background: radial-gradient(120% 90% at 50% 35%, color-mix(in srgb, var(--room-accent) 22%, transparent), transparent 60%), linear-gradient(180deg, color-mix(in srgb, var(--room-accent) 8%, var(--bg-secondary, #101828)), var(--bg-primary, #0a0f1c));++ border: 1px solid color-mix(in srgb, var(--room-accent) 30%, var(--border, #1f2d47)); }++.sf-run-ramps { position: absolute; inset: 0; width: 100%; height: 100%; }++.sf-run-speed { position: absolute; inset: 0; }++.sf-run-speed i { position: absolute; left: 50%; top: 42%; width: 2px; height: 30%; border-radius: 2px; background: linear-gradient(to bottom, transparent, color-mix(in srgb, var(--text-primary, #fff) 55%, transparent));++ transform-origin: 50% 0; transform: rotate(calc(var(--i) * 40deg - 160deg)) translateY(40%); opacity: 0; }++html.ov-motion .sf-run-speed i { animation: sf-speed 1.1s linear infinite; animation-delay: calc(var(--i) * -.12s); }++@keyframes sf-speed { 0% { opacity: 0; transform: rotate(calc(var(--i) * 40deg - 160deg)) translateY(10%) scaleY(.4); } 30% { opacity: .9; } 100% { opacity: 0; transform: rotate(calc(var(--i) * 40deg - 160deg)) translateY(120%) scaleY(1.3); } }++.sf-run-hud { position: absolute; left: 50%; bottom: 10%; translate: -50% 0; display: grid; justify-items: center; gap: 2px; padding: 8px 16px; border-radius: 14px; background: rgba(0, 0, 0, .55); color: #fff; }++.sf-run-hud b { font: 800 1.6rem/1 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }++.sf-run-hud small { font-size: .72rem; font-weight: 700; opacity: .8; font-variant-numeric: tabular-nums; }++/* On a phone the room's picture is a faint corner mark behind the title: the ramps alone, no timer to read through. */++@media (max-width: 759px) { .sc-room-pic .sf-run-hud, .sc-room-pic .sf-run-speed { display: none; } }++++/* Three tiles under one Start. */++.sf-tiles { list-style: none; margin: 4px 0 0; padding: 0; display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); }++.sf-tile a { display: grid; gap: 8px; height: 100%; padding: 18px; border-radius: 20px; border: 1px solid var(--border, #1f2d47); background: var(--bg-card, #0f1729); color: inherit; text-decoration: none; transition: border-color .15s, transform .15s; }++.sf-tile a:hover, .sf-tile a:focus-visible { border-color: var(--room-accent); transform: translateY(-2px); outline: none; }++.sf-tile-ic { display: grid; place-items: center; width: 44px; height: 44px; border-radius: 14px; background: var(--room-fill); color: var(--room-on); }++.sf-tile-ic .ovk-i { width: 22px; height: 22px; }++.sf-tile b { font-size: 1.25rem; font-weight: 800; letter-spacing: -.01em; }++.sf-tile span:not(.sf-tile-ic):not(.sf-tile-go) { color: var(--text-secondary, #96a7c2); line-height: 1.5; font-size: .95rem; }++.sf-tile-go { display: inline-flex; align-items: center; gap: 2px; margin-top: 4px; font-weight: 800; color: var(--room-strong); }++.sf-tile-go .ovk-i { width: 1em; height: 1em; }++@media (prefers-reduced-motion: reduce) { .sf-tile a { transition: none; } .sf-tile a:hover { transform: none; } }++++/* Scenes. */++.sf-demo-run { display: grid; gap: 10px; padding: 16px; border-radius: 18px; background: #0b0f17; color: #e9f2ff; border: 1px solid color-mix(in srgb, var(--room-accent) 35%, transparent); }++.sf-demo-hud { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; font-weight: 700; font-size: .85rem; }++.sf-demo-hud b { font: 800 1.8rem/1 ui-monospace, SFMono-Regular, Menlo, monospace; font-variant-numeric: tabular-nums; }++.sf-demo-hud .is-good { color: #4ade80; font-variant-numeric: tabular-nums; }++.sf-demo-bar { height: 6px; border-radius: 6px; background: rgba(255, 255, 255, .12); overflow: hidden; }++.sf-demo-bar i { display: block; height: 100%; background: #00bef6; border-radius: inherit; }++.sf-demo-run p { margin: 0; font-size: .8rem; opacity: .75; }++.sf-demo-steps { list-style: none; margin: 0; padding: 14px; display: grid; gap: 8px; border-radius: 18px; background: var(--bg-card, #0f1729); border: 1px solid var(--border, #1f2d47); }++.sf-demo-steps li { display: flex; align-items: center; gap: 10px; font-size: .9rem; color: var(--text-secondary, #96a7c2); }++.sf-demo-steps li::before { content: ""; flex: none; width: 10px; height: 10px; border-radius: 50%; border: 2px solid var(--text-muted, #7386a3); }++.sf-demo-steps li.is-done::before { background: var(--success, #22c55e); border-color: var(--success, #22c55e); }++.sf-demo-steps li.is-now { color: var(--text-primary, #e6edf7); font-weight: 650; }++.sf-demo-steps li.is-now::before { border-color: var(--room-accent); border-right-color: transparent; animation: sf-spin .9s linear infinite; }++@keyframes sf-spin { to { transform: rotate(360deg); } }++.sf-demo-tv { display: grid; gap: 8px; padding: 10px; border-radius: 18px; background: var(--bg-card, #0f1729); border: 1px solid var(--border, #1f2d47); }++.sf-demo-screen { position: relative; display: flex; align-items: flex-end; gap: 8px; aspect-ratio: 16 / 6; max-height: 210px; padding: 12px; border-radius: 12px; color: #fff; font-weight: 700; font-size: .85rem;++ background: radial-gradient(120% 120% at 70% 30%, color-mix(in srgb, var(--room-accent) 45%, transparent), transparent 60%), linear-gradient(160deg, #1e293b, #0b1020); }++.sf-demo-tv ul { list-style: none; margin: 0; padding: 0 4px; display: grid; gap: 4px; font-size: .82rem; color: var(--text-secondary, #96a7c2); }++.sf-pill-live { padding: 1px 7px; border-radius: 5px; background: var(--live-red, #e11d48); color: #fff; font-size: .68rem; font-weight: 800; }++++/* Ride: map cards. */++.sf-maps { list-style: none; margin: 16px 0 0; padding: 0; display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr)); }++.sf-map-link { display: grid; grid-template-columns: 84px minmax(0, 1fr); gap: 14px; align-items: center; padding: 12px; border-radius: 18px; border: 1px solid var(--border, #1f2d47); background: var(--bg-card, #0f1729); color: inherit; text-decoration: none; transition: border-color .15s; }++.sf-map-link:hover, .sf-map-link:focus-visible { border-color: var(--room-accent); outline: none; }++.sf-map-art { position: relative; aspect-ratio: 1; border-radius: 14px; overflow: hidden; background: linear-gradient(180deg, color-mix(in srgb, var(--room-accent) 18%, var(--bg-secondary, #101828)), var(--bg-primary, #0a0f1c)); }++.sf-map-art i { position: absolute; bottom: 0; width: 46%; height: 70%; background: linear-gradient(to top, color-mix(in srgb, var(--room-accent) 70%, transparent), color-mix(in srgb, var(--room-accent) 15%, transparent)); clip-path: polygon(0 100%, 100% 0, 100% 100%); }++.sf-map-art i:nth-child(1) { left: -6%; } .sf-map-art i:nth-child(2) { left: 30%; height: 50%; opacity: .7; transform: scaleX(-1); } .sf-map-art i:nth-child(3) { left: 60%; height: 85%; opacity: .55; }++.sf-map[data-tier="4"] .sf-map-art i, .sf-map[data-tier="5"] .sf-map-art i { height: 92%; }++.sf-map-body { display: grid; gap: 3px; min-width: 0; }++.sf-map-body > b { font-size: 1.05rem; font-weight: 800; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }++.sf-tier { width: fit-content; padding: 1px 8px; border-radius: 999px; font-size: .7rem; font-weight: 800; letter-spacing: .02em; background: color-mix(in srgb, var(--room-accent) 16%, transparent); color: var(--room-strong); }++.sf-map-meta { font-size: .84rem; color: var(--text-muted, #7386a3); }++.sf-wr { font-size: .84rem; color: var(--text-secondary, #96a7c2); }++.sf-wr b { font-variant-numeric: tabular-nums; color: var(--text-primary, #e6edf7); }++.sf-play { margin-top: 28px; }++.sf-game { position: relative; aspect-ratio: 16 / 9; border-radius: 18px; overflow: hidden; background: #000; border: 1px solid var(--border, #1f2d47); }++.sf-game iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }++.sf-game-help { margin-top: 8px; }++.sf-map-grid { display: grid; gap: 16px; margin-top: 20px; grid-template-columns: minmax(0, 1fr); }++@media (min-width: 980px) { .sf-map-grid { grid-template-columns: minmax(0, 1fr) 320px; align-items: start; } }++.sf-board { width: 100%; border-collapse: collapse; font-size: .9rem; }++.sf-board th { text-align: left; font-size: .72rem; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; color: var(--text-muted, #7386a3); padding: 0 6px 8px; }++.sf-board td { padding: 8px 6px; border-top: 1px solid var(--border, #1f2d47); }++.sf-board .sf-time { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-variant-numeric: tabular-nums; text-align: right; }++.sf-board th:last-child { text-align: right; }++.sf-board .is-wr td { font-weight: 800; color: var(--room-strong); }++++/* Watch: the TV and the guide. */++.sf-watch-h { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 12px; }++.sf-topics { display: flex; gap: 4px; padding: 3px; border-radius: 999px; background: var(--bg-card, #0f1729); border: 1px solid var(--border, #1f2d47); overflow-x: auto; scrollbar-width: none; max-width: 100%; }++.sf-topic { flex: none; padding: 6px 14px; border-radius: 999px; font-size: .86rem; font-weight: 700; text-decoration: none; color: var(--text-secondary, #96a7c2); }++.sf-topic[aria-current="page"] { background: var(--room-fill); color: var(--room-on); }++.sf-tv { display: grid; gap: 16px; margin-top: 18px; grid-template-columns: minmax(0, 1fr); }++@media (min-width: 980px) { .sf-tv { grid-template-columns: minmax(0, 1fr) 340px; align-items: start; } .sf-guide { position: sticky; top: calc(var(--ovnav-real-h, 56px) + 12px); max-height: calc(100vh - var(--ovnav-real-h, 56px) - 24px); overflow-y: auto; } }++.sf-tv-main { min-width: 0; }++.sf-tv-screen { position: relative; aspect-ratio: 16 / 9; border-radius: 18px; overflow: hidden; background: #000; touch-action: pan-x; }++.sf-tv-video, .sf-tv-poster { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; background: #000; }++.sf-tv-poster { object-fit: cover; filter: brightness(.6); }++.sf-tv-out { position: absolute; left: 50%; top: 50%; translate: -50% -50%; display: inline-flex; align-items: center; gap: 8px; padding: 12px 18px; border-radius: 999px; background: var(--room-fill); color: var(--room-on); font-weight: 800; text-decoration: none; white-space: nowrap; box-shadow: 0 10px 30px rgba(0, 0, 0, .4); }++.sf-tv-out .ovk-i { width: 18px; height: 18px; }++.sf-tv-screen.is-changing { animation: sf-change .28s ease; }++@keyframes sf-change { 0% { opacity: .2; transform: translateY(var(--sf-dir, 12px)); } 100% { opacity: 1; transform: none; } }++.sf-tv-info { display: grid; gap: 6px; margin-top: 12px; }++.sf-tv-info h2 { margin: 0; font-size: clamp(1.1rem, 2.6vw, 1.45rem); line-height: 1.25; }++.sf-tv-kind { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 0; font-size: .85rem; color: var(--text-muted, #7386a3); }++.sf-pill { padding: 2px 9px; border-radius: 999px; font-size: .72rem; font-weight: 800; background: color-mix(in srgb, var(--room-accent) 16%, transparent); color: var(--room-strong); }++.sf-pill--live { background: var(--live-red, #e11d48); color: #fff; }++.sf-tv-meta { margin: 0; font-size: .9rem; }++.sf-tv-meta a { color: var(--text-secondary, #96a7c2); text-decoration: none; font-weight: 650; }++.sf-tv-keys { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 4px 0 0; }++.sf-tv-keys .ovk-i { width: 18px; height: 18px; }++.sf-guide h2 { font-size: .8rem; letter-spacing: .07em; text-transform: uppercase; color: var(--text-muted, #7386a3); }++.sf-chs { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }++.sf-ch-link { display: grid; grid-template-columns: 112px minmax(0, 1fr); gap: 10px; align-items: center; padding: 6px; border-radius: 14px; color: inherit; text-decoration: none; border: 1px solid transparent; }++.sf-ch-link:hover, .sf-ch-link:focus-visible { background: var(--bg-card, #0f1729); outline: none; }++.sf-ch.is-on .sf-ch-link { background: var(--bg-card, #0f1729); border-color: var(--room-accent); }++.sf-ch-thumb { position: relative; aspect-ratio: 16 / 9; border-radius: 10px; overflow: hidden; background: var(--bg-tertiary, #1a2233); }++.sf-ch-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }++.sf-ch-len, .sf-ch-live { position: absolute; right: 4px; bottom: 4px; padding: 0 5px; border-radius: 4px; font-size: .66rem; font-weight: 800; background: rgba(0, 0, 0, .7); color: #fff; font-variant-numeric: tabular-nums; }++.sf-ch-live { left: 4px; right: auto; top: 4px; bottom: auto; background: var(--live-red, #e11d48); }++.sf-ch-body { display: grid; gap: 2px; min-width: 0; }++.sf-ch-body b { font-size: .88rem; line-height: 1.3; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }++.sf-ch-body small { font-size: .76rem; color: var(--text-muted, #7386a3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }++.sf-guide .sf-more { margin-top: 10px; }++++/* Web: Surf with Vibe. */++.sf-web-form { display: grid; gap: 10px; }++.sf-web-form textarea { width: 100%; min-height: 96px; padding: 12px 14px; border-radius: 14px; border: 1px solid var(--border, #1f2d47); background: var(--bg-input, #0d1322); color: var(--text-primary, #e6edf7); font: inherit; font-size: 1.02rem; line-height: 1.5; resize: vertical; }++.sf-web-form textarea:focus { outline: 2px solid var(--room-accent); outline-offset: 1px; border-color: transparent; }++.sf-web-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px; }++.sf-web-row .ovk-btn .ovk-i { width: 1.05em; height: 1.05em; }++.sf-status { min-height: 1.4em; margin: 8px 0 0; font-size: .9rem; color: var(--text-secondary, #96a7c2); }++.sf-status.is-bad { color: var(--danger-text, #f87171); }++.sf-examples { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }++.sf-ex { padding: 6px 12px; border-radius: 999px; border: 1px solid var(--border, #1f2d47); background: transparent; color: var(--text-secondary, #96a7c2); font: inherit; font-size: .84rem; cursor: pointer; text-align: left; }++.sf-ex:hover, .sf-ex:focus-visible { border-color: var(--room-accent); color: var(--text-primary, #e6edf7); outline: none; }++.sf-how { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; counter-reset: how; }++.sf-how li { position: relative; display: grid; gap: 2px; padding: 12px 14px 12px 52px; border-radius: 14px; background: var(--bg-card, #0f1729); border: 1px solid var(--border, #1f2d47); counter-increment: how; }++.sf-how li::before { content: counter(how); position: absolute; left: 14px; top: 12px; display: grid; place-items: center; width: 26px; height: 26px; border-radius: 50%; background: var(--room-fill); color: var(--room-on); font-weight: 800; font-size: .85rem; }++.sf-how span { color: var(--text-secondary, #96a7c2); line-height: 1.5; font-size: .92rem; }++++/* The API. */++.sf-eps { display: grid; gap: 10px; margin: 20px 0 0; }++.sf-eps > div { padding: 12px 14px; border-radius: 14px; background: var(--bg-card, #0f1729); border: 1px solid var(--border, #1f2d47); }++.sf-eps dt code { font: 600 .88rem/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--room-strong); overflow-wrap: anywhere; }++.sf-eps dd { margin: 6px 0 0; color: var(--text-secondary, #96a7c2); line-height: 1.5; }++++@media (prefers-reduced-motion: reduce) { .sf-run-speed i, .sf-demo-steps li.is-now::before, .sf-tv-screen.is-changing { animation: none !important; } }++html[data-ov-motion=reduced] .sf-run-speed i { animation: none !important; }nnew file mode 100644@@@ -0,0 +1,134 @@++/* OpenVibe.Surf: what the pages are complete without.++ * - Watch: the TV plays clips and moments in the page (native HLS, else hls.js from /vendor, loaded only then);++ * ↑ ↓, the buttons, a swipe on the screen or a tap in the guide change channel; live streams open on Live.++ * - Web: the form starts the task in the page (POST /api/v1/web/tasks) and goes to it; examples fill the box.++ * - Home: the run motif's timer and speed tick. */++(function () {++ 'use strict';++ var reduced = function () { try { return matchMedia('(prefers-reduced-motion: reduce)').matches || document.documentElement.dataset.ovMotion === 'reduced'; } catch (e) { return false; } };++++ // ── Home: the run's timer ─────────────────────────────────++ function runMotif() {++ var t = document.querySelector('[data-sf-timer]'), sp = document.querySelector('[data-sf-speed]');++ if (!t || t.dataset.ticking || reduced()) return;++ t.dataset.ticking = '1';++ var start = performance.now() - 12384;++ var tick = function () {++ if (!document.body.contains(t)) return;++ var ms = (performance.now() - start) % 59999, m = Math.floor(ms / 60000), s = Math.floor((ms % 60000) / 1000), x = Math.floor(ms % 1000);++ t.textContent = m + ':' + String(s).padStart(2, '0') + '.' + String(x).padStart(3, '0');++ if (sp) sp.textContent = (1180 + Math.round(140 * Math.sin(ms / 700))).toLocaleString('en-US').replace(',', ' ');++ requestAnimationFrame(tick);++ };++ requestAnimationFrame(tick);++ }++++ // ── Watch: the TV ─────────────────────────────────────────++ var hlsLoading = null;++ function loadHls() {++ if (window.Hls) return Promise.resolve(window.Hls);++ return hlsLoading || (hlsLoading = new Promise(function (ok) {++ var s = document.createElement('script'); s.src = '/vendor/hls.light.min.js'; s.async = true;++ s.onload = function () { ok(window.Hls || null); }; s.onerror = function () { hlsLoading = null; ok(null); };++ document.head.appendChild(s);++ }));++ }++ function tv() {++ var root = document.querySelector('[data-sf-tv]');++ if (!root || root.dataset.ready) return;++ root.dataset.ready = '1';++ var screen = root.querySelector('[data-sf-screen]');++ var chs = Array.prototype.slice.call(root.querySelectorAll('.sf-ch'));++ var title = root.querySelector('[data-sf-title]'), why = root.querySelector('[data-sf-why]'), meta = root.querySelector('[data-sf-meta]');++ var kindPill = root.querySelector('.sf-tv-kind .sf-pill');++ var LABEL = { live: 'Live', clip: 'Clip', moment: 'AI moment' };++ var cur = 0, hls = null;++ function el(tag, cls) { var e = document.createElement(tag); if (cls) e.className = cls; return e; }++ function show(i, dir) {++ if (!chs.length) return;++ cur = (i + chs.length) % chs.length;++ var c = chs[cur], d = c.dataset;++ chs.forEach(function (x, n) { x.classList.toggle('is-on', n === cur); });++ if (hls) { try { hls.destroy(); } catch (e) { /* */ } hls = null; }++ screen.replaceChildren();++ screen.style.setProperty('--sf-dir', dir < 0 ? '-12px' : '12px');++ screen.classList.remove('is-changing'); void screen.offsetWidth; screen.classList.add('is-changing');++ if (d.sfPlayType === 'hls' && d.sfSrc) {++ var v = el('video', 'sf-tv-video'); v.playsInline = true; v.controls = true; v.autoplay = true; v.muted = !screen.dataset.sound;++ if (d.sfThumb) v.poster = d.sfThumb;++ v.setAttribute('data-ov-player', '');++ v.addEventListener('volumechange', function () { if (!v.muted) screen.dataset.sound = '1'; });++ screen.appendChild(v);++ if (v.canPlayType('application/vnd.apple.mpegurl')) { v.src = d.sfSrc; v.play().catch(function () {}); }++ else loadHls().then(function (Hls) {++ if (!Hls || !Hls.isSupported()) { v.src = d.sfSrc; return; }++ if (chs[cur] !== c) return;++ hls = new Hls({ maxBufferLength: 20 }); hls.loadSource(d.sfSrc); hls.attachMedia(v);++ hls.on(Hls.Events.MANIFEST_PARSED, function () { v.play().catch(function () {}); });++ });++ } else {++ if (d.sfThumb) { var img = el('img', 'sf-tv-poster'); img.src = d.sfThumb; img.alt = ''; screen.appendChild(img); }++ var a = el('a', 'sf-tv-out'); a.href = d.sfPage || '#';++ a.textContent = d.sfKind === 'live' ? 'Watch live on OpenVibe.Live' : 'Play on OpenVibe.Live';++ screen.appendChild(a);++ }++ if (title) title.textContent = (c.querySelector('.sf-ch-body b') || {}).textContent || '';++ if (why) why.textContent = d.sfWhy || '';++ if (kindPill) { kindPill.textContent = LABEL[d.sfKind] || ''; kindPill.className = 'sf-pill sf-pill--' + (d.sfKind || 'clip'); }++ if (meta) {++ meta.replaceChildren();++ if (d.sfChannel) { var l = el('a'); l.href = 'https://openvibe.live/@' + encodeURIComponent(d.sfChannel); l.textContent = '@' + d.sfChannel; meta.appendChild(l); }++ }++ if (c.scrollIntoView && window.innerWidth >= 980) c.scrollIntoView({ block: 'nearest', behavior: reduced() ? 'auto' : 'smooth' });++ }++ root.querySelector('[data-sf-prev]').addEventListener('click', function () { show(cur - 1, -1); });++ root.querySelector('[data-sf-next]').addEventListener('click', function () { show(cur + 1, 1); });++ chs.forEach(function (c, n) {++ c.querySelector('.sf-ch-link').addEventListener('click', function (e) { if (e.metaKey || e.ctrlKey || e.shiftKey) return; e.preventDefault(); show(n, n > cur ? 1 : -1); screen.scrollIntoView && window.innerWidth < 980 && screen.scrollIntoView({ block: 'start', behavior: reduced() ? 'auto' : 'smooth' }); });++ });++ document.addEventListener('keydown', function (e) {++ if (!document.body.contains(root)) return;++ var t = e.target; if (t && (t.tagName === 'INPUT' || t.tagName === 'TEXTAREA' || t.isContentEditable)) return;++ if (e.key === 'ArrowDown') { e.preventDefault(); show(cur + 1, 1); } else if (e.key === 'ArrowUp') { e.preventDefault(); show(cur - 1, -1); }++ });++ // A vertical swipe on the screen changes channel (the video's own controls keep their taps).++ var y0 = null, x0 = null;++ screen.addEventListener('touchstart', function (e) { var t = e.touches[0]; y0 = t.clientY; x0 = t.clientX; }, { passive: true });++ screen.addEventListener('touchend', function (e) {++ if (y0 == null) return; var t = e.changedTouches[0], dy = t.clientY - y0, dx = t.clientX - x0; y0 = null;++ if (Math.abs(dy) > 60 && Math.abs(dy) > Math.abs(dx) * 1.5) show(cur + (dy < 0 ? 1 : -1), dy < 0 ? 1 : -1);++ }, { passive: true });++ var first = chs[0];++ if (first && first.dataset.sfPlayType === 'hls') show(0, 1);++ }++++ // ── Web: start in the page ────────────────────────────────++ function webForm() {++ var f = document.querySelector('[data-sf-web-form]');++ if (!f || f.dataset.ready) return;++ f.dataset.ready = '1';++ var box = f.querySelector('textarea'), status = document.getElementById('sf-web-status'), btn = f.querySelector('button[type=submit]');++ document.querySelectorAll('[data-sf-example]').forEach(function (b) {++ b.addEventListener('click', function () { box.value = b.getAttribute('data-sf-example'); box.focus(); });++ });++ f.addEventListener('submit', function (e) {++ e.preventDefault();++ var q = box.value.trim();++ if (q.length < 3) { status.textContent = 'Say a little more: at least 3 characters.'; status.classList.add('is-bad'); return; }++ btn.disabled = true; status.classList.remove('is-bad'); status.textContent = 'Starting the agent…';++ fetch('/api/v1/web/tasks', { method: 'POST', credentials: 'same-origin', headers: { 'content-type': 'application/json', accept: 'application/json' }, body: JSON.stringify({ query: q }) })++ .then(function (r) { return r.json().catch(function () { return {}; }).then(function (b) { return { r: r, b: b }; }); })++ .then(function (x) {++ if (x.r.status === 401) { location.href = '/auth/login?next=%2Fweb'; return; }++ if (!x.r.ok || !x.b.page) { throw new Error(x.b.detail || x.b.title || 'That did not start. Try again in a moment.'); }++ status.textContent = 'Started. Taking you to the task…';++ location.href = x.b.page;++ })++ .catch(function (err) { btn.disabled = false; status.textContent = err.message; status.classList.add('is-bad'); });++ });++ }++++ function run() { runMotif(); tv(); webForm(); }++ if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', run); else run();++ document.addEventListener('ov:boost:load', run);++})();nnew file mode 100644@@@ -0,0 +1,2 @@++!function e(t){var r,i;r=this,i=function(){"use strict";function r(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,i=Array(t);r<t;r++)i[r]=e[r];return i}function i(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,g(i.key),i)}}function a(e,t,r){return t&&i(e.prototype,t),r&&i(e,r),Object.defineProperty(e,"prototype",{writable:!1}),e}function n(e,t){var i="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(i)return(i=i.call(e)).next.bind(i);if(Array.isArray(e)||(i=function(e,t){if(e){if("string"==typeof e)return r(e,t);var i={}.toString.call(e).slice(8,-1);return"Object"===i&&e.constructor&&(i=e.constructor.name),"Map"===i||"Set"===i?Array.from(e):"Arguments"===i||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i)?r(e,t):void 0}}(e))||t){i&&(e=i);var a=0;return function(){return a>=e.length?{done:!0}:{done:!1,value:e[a++]}}}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 s(e,t,r){return(t=g(t))in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function o(){return o=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var i in r)({}).hasOwnProperty.call(r,i)&&(e[i]=r[i])}return e},o.apply(null,arguments)}function l(e){return l=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(e){return e.__proto__||Object.getPrototypeOf(e)},l(e)}function u(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,c(e,t)}function d(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){})))}catch(e){}return(d=function(){return!!e})()}function h(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 f(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?h(Object(r),!0).forEach((function(t){s(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):h(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}function c(e,t){return c=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},c(e,t)}function g(e){var t=function(e,t){if("object"!=typeof e||!e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var i=r.call(e,t);if("object"!=typeof i)return i;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"==typeof t?t:t+""}function v(e){var t="function"==typeof Map?new Map:void 0;return v=function(e){if(null===e||!function(e){try{return-1!==Function.toString.call(e).indexOf("[native code]")}catch(t){return"function"==typeof e}}(e))return e;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,r)}function r(){return function(e,t,r){if(d())return Reflect.construct.apply(null,arguments);var i=[null];i.push.apply(i,t);var a=new(e.bind.apply(e,i));return r&&c(a,r.prototype),a}(e,arguments,l(this).constructor)}return r.prototype=Object.create(e.prototype,{constructor:{value:r,enumerable:!1,writable:!0,configurable:!0}}),c(r,e)},v(e)}function m(e){return e instanceof ArrayBuffer||"undefined"!=typeof SharedArrayBuffer&&e instanceof SharedArrayBuffer}var p="utf-16",y="utf-16be",E="utf-16le",T="utf-8";function S(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var L,R,A={exports:{}},b=(L||(L=1,function(e){var t=Object.prototype.hasOwnProperty,r="~";function i(){}function a(e,t,r){this.fn=e,this.context=t,this.once=r||!1}function n(e,t,i,n,s){if("function"!=typeof i)throw new TypeError("The listener must be a function");var o=new a(i,n||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,a=[];if(0===this._eventsCount)return a;for(i in e=this._events)t.call(e,i)&&a.push(r?i.slice(1):i);return Object.getOwnPropertySymbols?a.concat(Object.getOwnPropertySymbols(e)):a},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 a=0,n=i.length,s=new Array(n);a<n;a++)s[a]=i[a].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,a,n,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,a),!0;case 5:return d.fn.call(d.context,t,i,a,n),!0;case 6:return d.fn.call(d.context,t,i,a,n,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,a);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 n(this,e,t,r,!1)},o.prototype.once=function(e,t,r){return n(this,e,t,r,!0)},o.prototype.removeListener=function(e,t,i,a){var n=r?r+e:e;if(!this._events[n])return this;if(!t)return s(this,n),this;var o=this._events[n];if(o.fn)o.fn!==t||a&&!o.once||i&&o.context!==i||s(this,n);else{for(var l=0,u=[],d=o.length;l<d;l++)(o[l].fn!==t||a&&!o[l].once||i&&o[l].context!==i)&&u.push(o[l]);u.length?this._events[n]=1===u.length?u[0]:u:s(this,n)}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}(A)),A.exports),k=S(b),D={exports:{}},I=(R||(R=1,function(e,t){var r,i,a,n,s;r=/^(?=((?:[a-zA-Z0-9+\-.]+:)?))\1(?=((?:\/\/[^\/?#]*)?))\2(?=((?:(?:[^?#\/]*\/)*[^;?#\/]*)?))\3((?:;[^?#]*)?)(\?[^#]*)?(#[^]*)?$/,i=/^(?=([^\/?#]*))\1([^]*)$/,a=/(?:\/|^)\.(?=\/)/g,n=/(?:\/|^)\.\.\/(?!\.\.\/)[^\/]*(?=\/)/g,s={buildAbsoluteURL:function(e,t,r){if(r=r||{},e=e.trim(),!(t=t.trim())){if(!r.alwaysNormalize)return e;var a=s.parseURL(e);if(!a)throw new Error("Error trying to parse base URL.");return a.path=s.normalizePath(a.path),s.buildURLFromParts(a)}var n=s.parseURL(t);if(!n)throw new Error("Error trying to parse relative URL.");if(n.scheme)return r.alwaysNormalize?(n.path=s.normalizePath(n.path),s.buildURLFromParts(n)):t;var o=s.parseURL(e);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:n.netLoc,path:null,params:n.params,query:n.query,fragment:n.fragment};if(!n.netLoc&&(u.netLoc=o.netLoc,"/"!==n.path[0]))if(n.path){var d=o.path,h=d.substring(0,d.lastIndexOf("/")+1)+n.path;u.path=s.normalizePath(h)}else u.path=o.path,n.params||(u.params=o.params,n.query||(u.query=o.query));return null===u.path&&(u.path=r.alwaysNormalize?s.normalizePath(n.path):n.path),s.buildURLFromParts(u)},parseURL:function(e){var t=r.exec(e);return t?{scheme:t[1]||"",netLoc:t[2]||"",path:t[3]||"",params:t[4]||"",query:t[5]||"",fragment:t[6]||""}:null},normalizePath:function(e){for(e=e.split("").reverse().join("").replace(a,"");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=s}(D)),D.exports),_=Number.isFinite||function(e){return"number"==typeof e&&isFinite(e)},C=Number.isSafeInteger||function(e){return"number"==typeof e&&Math.abs(e)<=P},P=Number.MAX_SAFE_INTEGER||9007199254740991,x=function(e){return e.NETWORK_ERROR="networkError",e.MEDIA_ERROR="mediaError",e.KEY_SYSTEM_ERROR="keySystemError",e.MUX_ERROR="muxError",e.OTHER_ERROR="otherError",e}({}),w=function(e){return e.KEY_SYSTEM_NO_KEYS="keySystemNoKeys",e.KEY_SYSTEM_NO_ACCESS="keySystemNoAccess",e.KEY_SYSTEM_NO_SESSION="keySystemNoSession",e.KEY_SYSTEM_NO_CONFIGURED_LICENSE="keySystemNoConfiguredLicense",e.KEY_SYSTEM_LICENSE_REQUEST_FAILED="keySystemLicenseRequestFailed",e.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED="keySystemServerCertificateRequestFailed",e.KEY_SYSTEM_SERVER_CERTIFICATE_UPDATE_FAILED="keySystemServerCertificateUpdateFailed",e.KEY_SYSTEM_SESSION_UPDATE_FAILED="keySystemSessionUpdateFailed",e.KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED="keySystemStatusOutputRestricted",e.KEY_SYSTEM_STATUS_INTERNAL_ERROR="keySystemStatusInternalError",e.KEY_SYSTEM_DESTROY_MEDIA_KEYS_ERROR="keySystemDestroyMediaKeysError",e.KEY_SYSTEM_DESTROY_CLOSE_SESSION_ERROR="keySystemDestroyCloseSessionError",e.KEY_SYSTEM_DESTROY_REMOVE_SESSION_ERROR="keySystemDestroyRemoveSessionError",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.PLAYLIST_UNCHANGED_ERROR="playlistUnchangedError",e.LEVEL_LOAD_ERROR="levelLoadError",e.LEVEL_LOAD_TIMEOUT="levelLoadTimeOut",e.LEVEL_PARSING_ERROR="levelParsingError",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.FRAG_GAP="fragGap",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_APPEND_NO_PROGRESS="bufferAppendNoProgress",e.BUFFER_STALLED_ERROR="bufferStalledError",e.BUFFER_FULL_ERROR="bufferFullError",e.BUFFER_SEEK_OVER_HOLE="bufferSeekOverHole",e.BUFFER_NUDGE_ON_STALL="bufferNudgeOnStall",e.ASSET_LIST_LOAD_ERROR="assetListLoadError",e.ASSET_LIST_LOAD_TIMEOUT="assetListLoadTimeout",e.ASSET_LIST_PARSING_ERROR="assetListParsingError",e.INTERSTITIAL_ASSET_ITEM_ERROR="interstitialAssetItemError",e.INTERNAL_EXCEPTION="internalException",e.INTERNAL_ABORTED="aborted",e.ATTACH_MEDIA_ERROR="attachMediaError",e.MEDIA_SOURCE_REQUIRES_RESET="mediaSourceRequiresReset",e.UNKNOWN="unknown",e}({}),O=function(e){return e.MEDIA_ATTACHING="hlsMediaAttaching",e.MEDIA_ATTACHED="hlsMediaAttached",e.MEDIA_DETACHING="hlsMediaDetaching",e.MEDIA_DETACHED="hlsMediaDetached",e.MEDIA_ENDED="hlsMediaEnded",e.STALL_RESOLVED="hlsStallResolved",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.BUFFERED_TO_END="hlsBufferedToEnd",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.AUDIO_TRACK_UPDATED="hlsAudioTrackUpdated",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_TRACK_UPDATED="hlsSubtitleTrackUpdated",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.MAX_AUTO_LEVEL_UPDATED="hlsMaxAutoLevelUpdated",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",e.STEERING_MANIFEST_LOADED="hlsSteeringManifestLoaded",e.ASSET_LIST_LOADING="hlsAssetListLoading",e.ASSET_LIST_LOADED="hlsAssetListLoaded",e.INTERSTITIALS_UPDATED="hlsInterstitialsUpdated",e.INTERSTITIALS_BUFFERED_TO_BOUNDARY="hlsInterstitialsBufferedToBoundary",e.INTERSTITIAL_ASSET_PLAYER_CREATED="hlsInterstitialAssetPlayerCreated",e.INTERSTITIAL_STARTED="hlsInterstitialStarted",e.INTERSTITIAL_ASSET_STARTED="hlsInterstitialAssetStarted",e.INTERSTITIAL_ASSET_ENDED="hlsInterstitialAssetEnded",e.INTERSTITIAL_ASSET_ERROR="hlsInterstitialAssetError",e.INTERSTITIAL_ENDED="hlsInterstitialEnded",e.INTERSTITIALS_PRIMARY_RESUMED="hlsInterstitialsPrimaryResumed",e.PLAYOUT_LIMIT_REACHED="hlsPlayoutLimitReached",e.EVENT_CUE_ENTER="hlsEventCueEnter",e}({}),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}},F=function(e){return e.MANIFEST="manifest",e.LEVEL="level",e.AUDIO_TRACK="audioTrack",e.SUBTITLE_TRACK="subtitleTrack",e.MEDIA_FRAGMENT="media-fragment",e.KEY="key",e.STEERING_MANIFEST="steering-manifest",e.SERVER_CERTIFICATE="server-certificate",e.INTERSTITIAL_ASSET_LIST="interstitial-asset-list",e}({}),N=function(e){return e.MAIN="main",e.AUDIO="audio",e.SUBTITLE="subtitle",e}({}),B={AUDIO:"audio",VIDEO:"video",AUDIOVIDEO:"audiovideo"},U=function(){function e(e){var t,r,i;this._byteRange=null,this._url=null,this._stats=null,this._streams=null,this.base=void 0,this.relurl=void 0,"string"==typeof e&&(e={url:e}),this.base=e,(i=W(t=this,r="stats"))&&(i.enumerable=!0,Object.defineProperty(t,r,i))}var t=e.prototype;return t.setByteRange=function(e,t){var r,i=e.split("@",2);r=1===i.length?(null==t?void 0:t.byteRangeEndOffset)||0:parseInt(i[1]),this._byteRange=[r,parseInt(i[0])+r]},t.clearElementaryStreamInfo=function(){var e=this.elementaryStreams;e[B.AUDIO]=null,e[B.VIDEO]=null,e[B.AUDIOVIDEO]=null,this.url=null},a(e,[{key:"baseurl",get:function(){return this.base.url}},{key:"byteRange",get:function(){return null===this._byteRange?[]:this._byteRange}},{key:"byteRangeStartOffset",get:function(){return this.byteRange[0]}},{key:"byteRangeEndOffset",get:function(){return this.byteRange[1]}},{key:"elementaryStreams",get:function(){var e;return null===this._streams&&(this._streams=((e={})[B.AUDIO]=null,e[B.VIDEO]=null,e[B.AUDIOVIDEO]=null,e)),this._streams},set:function(e){this._streams=e}},{key:"hasStats",get:function(){return null!==this._stats}},{key:"hasStreams",get:function(){return null!==this._streams}},{key:"stats",get:function(){return null===this._stats&&(this._stats=new M),this._stats},set:function(e){this._stats=e}},{key:"url",get:function(){return!this._url&&this.relurl&&(this._url=I.buildAbsoluteURL(this.baseurl,this.relurl,{alwaysNormalize:!0})),this._url||""},set:function(e){this._url=e}}])}();function G(e){return"initSegment"!==e.sn}function V(e,t){return e.sn===(null==t?void 0:t.sn)&&e.level===t.level}function H(e,t){return e.sn===(null==t?void 0:t.sn)&&e.level===t.level&&e.cc===t.cc}var K=function(e){function t(t,r){var i;return(i=e.call(this,r)||this)._decryptdata=null,i._programDateTime=null,i._ref=null,i._bitrate=void 0,i.rawProgramDateTime=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.playlistOffset=0,i.deltaPTS=void 0,i.maxStartPTS=void 0,i.minEndPTS=void 0,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=t,i}u(t,e);var r=t.prototype;return r.addStart=function(e){this.setStart(this.start+e)},r.setStart=function(e){this.start=e,this._ref&&(this._ref.start=e)},r.setDuration=function(e){this.duration=e,this._ref&&(this._ref.duration=e)},r.setKeyFormat=function(e){var t=this.levelkeys;if(t){var r,i=t[e];!i||null!=(r=this._decryptdata)&&r.keyId||(this._decryptdata=i.getDecryptData(this.sn,t))}},r.abortRequests=function(){var e,t;null==(e=this.loader)||e.abort(),null==(t=this.keyLoader)||t.abort()},r.setElementaryStreamInfo=function(e,t,r,i,a,n){void 0===n&&(n=!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,a)):s[e]={startPTS:t,endPTS:r,startDTS:i,endDTS:a,partial:n}},a(t,[{key:"byteLength",get:function(){if(this.hasStats){var e=this.stats.total;if(e)return e}if(this.byteRange.length){var t=this.byteRange[0],r=this.byteRange[1];if(_(t)&&_(r))return r-t}return null}},{key:"bitrate",get:function(){return this.byteLength?8*this.byteLength/this.duration:this._bitrate?this._bitrate:null},set:function(e){this._bitrate=e}},{key:"decryptdata",get:function(){var e,t=this.levelkeys;if(!t||t.NONE)return null;if(t.identity)this._decryptdata||(this._decryptdata=t.identity.getDecryptData(this.sn));else if(null==(e=this._decryptdata)||!e.keyId){var r=Object.keys(t);if(1===r.length){var i=this._decryptdata=t[r[0]]||null;i&&(this._decryptdata=i.getDecryptData(this.sn,t))}}return this._decryptdata}},{key:"end",get:function(){return this.start+this.duration}},{key:"endProgramDateTime",get:function(){if(null===this.programDateTime)return null;var e=_(this.duration)?this.duration:0;return this.programDateTime+1e3*e}},{key:"encrypted",get:function(){var e;if(null!=(e=this._decryptdata)&&e.encrypted)return!0;if(this.levelkeys){var t,r=Object.keys(this.levelkeys),i=r.length;if(i>1||1===i&&null!=(t=this.levelkeys[r[0]])&&t.encrypted)return!0}return!1}},{key:"programDateTime",get:function(){return null===this._programDateTime&&this.rawProgramDateTime&&(this.programDateTime=Date.parse(this.rawProgramDateTime)),this._programDateTime},set:function(e){_(e)?this._programDateTime=e:this._programDateTime=this.rawProgramDateTime=null}},{key:"ref",get:function(){return G(this)?(this._ref||(this._ref={base:this.base,start:this.start,duration:this.duration,sn:this.sn,programDateTime:this.programDateTime}),this._ref):null}}])}(U),Y=function(e){function t(t,r,i,a,n){var s;(s=e.call(this,i)||this).fragOffset=0,s.duration=0,s.independent=!1,s.relurl=void 0,s.fragment=void 0,s.index=void 0,s.gap=!1,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=a;var o=t.enumeratedString("BYTERANGE");return o&&s.setByteRange(o,n),n&&(s.fragOffset=n.fragOffset+n.duration),s}return u(t,e),a(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||this.fragment.type===N.SUBTITLE&&this.stats.loading.end>0)}}])}(U);function W(e,t){var r=Object.getPrototypeOf(e);if(r){var i=Object.getOwnPropertyDescriptor(r,t);return i||W(r,t)}}function j(e){switch(e.details){case w.FRAG_LOAD_TIMEOUT:case w.KEY_LOAD_TIMEOUT:case w.LEVEL_LOAD_TIMEOUT:case w.MANIFEST_LOAD_TIMEOUT:case w.AUDIO_TRACK_LOAD_TIMEOUT:case w.SUBTITLE_TRACK_LOAD_TIMEOUT:case w.ASSET_LIST_LOAD_TIMEOUT:return!0}return!1}function q(e){return e.details.startsWith("key")}function X(e){return q(e)&&!!e.frag&&!e.frag.decryptdata}function z(e,t){var r=j(t);return e.default[(r?"timeout":"error")+"Retry"]}function Q(e,t){var r="linear"===e.backoff?1:Math.pow(2,t);return Math.min(r*e.retryDelayMs,e.maxRetryDelayMs)}function $(e){return f(f({},e),{errorRetry:null,timeoutRetry:null})}function Z(e,t,r,i){if(!e)return!1;var a=null==i?void 0:i.code,n=t<e.maxNumRetry&&(function(e){return J(e)||!!e&&(e<400||e>499)}(a)||!!r);return e.shouldRetry?e.shouldRetry(e,t,r,i,n):n}function J(e){return 0===e&&!1===navigator.onLine}function ee(e,t){return t>0&&e.loadError>0&&0!==e.loadErrorTime&&self.performance.now()-e.loadErrorTime>t}var te=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}(),re=function(){function e(e,t,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 te(e),this.fast_=new te(t),this.defaultTTFB_=i,this.ttfb_=new te(e)}var t=e.prototype;return t.update=function(e,t){var r=this.slow_,i=this.fast_,a=this.ttfb_;r.halfLife!==e&&(this.slow_=new te(e,r.getEstimate(),r.getTotalWeight())),i.halfLife!==t&&(this.fast_=new te(t,i.getEstimate(),i.getTotalWeight())),a.halfLife!==e&&(this.ttfb_=new te(e,a.getEstimate(),a.getTotalWeight()))},t.sample=function(e,t){if(!(t<=0)){var r=Math.min(this.minDelayMs_,t/1e5),i=8*t,a=Math.max(e,r)/1e3,n=i/a;this.fast_.sample(a,n),this.slow_.sample(a,n)}},t.sampleTTFB=function(e){var t=e/1e3,r=Math.sqrt(2)*Math.exp(-Math.pow(t,2)/2);this.ttfb_.sample(r,Math.max(e,5))},t.canEstimate=function(){return this.fast_.getTotalWeight()>=this.minWeight_},t.getEstimate=function(){return this.canEstimate()?Math.min(this.fast_.getEstimate(),this.slow_.getEstimate()):this.defaultEstimate_},t.getEstimateTTFB=function(){return this.ttfb_.getTotalWeight()>=this.minWeight_?this.ttfb_.getEstimate():this.defaultTTFB_},t.destroy=function(){},a(e,[{key:"defaultEstimate",get:function(){return this.defaultEstimate_}}])}(),ie=function(e,t){this.trace=void 0,this.debug=void 0,this.log=void 0,this.warn=void 0,this.info=void 0,this.error=void 0;var r="["+e+"]:";this.trace=ae,this.debug=t.debug.bind(null,r),this.log=t.log.bind(null,r),this.warn=t.warn.bind(null,r),this.info=t.info.bind(null,r),this.error=t.error.bind(null,r)},ae=function(){},ne={trace:ae,debug:ae,log:ae,warn:ae,info:ae,error:ae};function se(){return o({},ne)}function oe(e,t,r){return t[e]?t[e].bind(t):function(e,t){var r=self.console[e];return r?r.bind(self.console,(t?"["+t+"] ":"")+"["+e+"] >"):ae}(e,r)}var le=se();function ue(e,t,r){var i=se();if("object"==typeof console&&!0===e||"object"==typeof e){var a=["debug","log","info","warn","error"];a.forEach((function(t){i[t]=oe(t,e,r)}));try{i.log('Debug logs enabled for "'+t+'" in hls.js version 1.7.3')}catch(e){return se()}a.forEach((function(t){le[t]=oe(t,e)}))}else o(le,i);return i}var de,he,fe=le,ce=he?de:(he=1,de=void 0),ge=S(ce);function ve(e){if(void 0===e&&(e=!0),"undefined"!=typeof self)return(e||!self.MediaSource)&&self.ManagedMediaSource||self.MediaSource||self.WebKitMediaSource}function me(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}function pe(e,t){var r=0;return r=(127&e[t])<<21,r|=(127&e[t+1])<<14,r|=(127&e[t+2])<<7,r|=127&e[t+3]}function ye(e,t){return me(e,t)&&pe(e,t+6)+10<=e.length-t}function Ee(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}function Te(e,t){for(var r=t,i=0;me(e,t);)i+=10,i+=pe(e,t+6),Ee(e,t+10)&&(i+=10),t+=i;if(i>0)return e.subarray(r,r+i)}function Se(e,t,r){return void 0===t&&(t=0),void 0===r&&(r=1/0),function(e,t,r,i){var a,n=m(a=e)?a:a.buffer,s=1;"BYTES_PER_ELEMENT"in i&&(s=i.BYTES_PER_ELEMENT);var o,l=(o=e)&&m(o.buffer)&&void 0!==o.byteLength&&void 0!==o.byteOffset?e.byteOffset:0,u=(l+e.byteLength)/s,d=(l+t)/s,h=Math.floor(Math.max(0,Math.min(d,u)));return new i(n,h,Math.floor(Math.min(h+Math.max(r,0),u))-h)}(e,t,r,Uint8Array)}function Le(e,t){void 0===t&&(t=!1);var r=t?e.indexOf(0):e.length,i=function(e,t){var r;void 0===t&&(t={}),r=m(e)?new DataView(e):new DataView(e.buffer,e.byteOffset,e.byteLength);var i=0,a=t.encoding;if(!a){var n=r.getUint8(0),s=r.getUint8(1);239==n&&187==s&&191==r.getUint8(2)?(a=T,i=3):254==n&&255==s?(a=y,i=2):255==n&&254==s?(a=E,i=2):a=T}if("undefined"!=typeof TextDecoder)return new TextDecoder(a).decode(r);for(var o,l=r.byteLength,u=a!==y,d="";i<l;){switch(a){case T:if((o=r.getUint8(i))<128)i++;else if(o>=194&&o<=223)if(i+1<l){var h=r.getUint8(i+1);h>=128&&h<=191?(o=(31&o)<<6|63&h,i+=2):i++}else i++;else if(o>=224&&o<=239)if(i+2<=l-1){var f=r.getUint8(i+1),c=r.getUint8(i+2);f>=128&&f<=191&&c>=128&&c<=191?(o=(15&o)<<12|(63&f)<<6|63&c,i+=3):i++}else i++;else if(o>=240&&o<=244)if(i+3<=l-1){var g=r.getUint8(i+1),v=r.getUint8(i+2),S=r.getUint8(i+3);g>=128&&g<=191&&v>=128&&v<=191&&S>=128&&S<=191?(o=(7&o)<<18|(63&g)<<12|(63&v)<<6|63&S,i+=4):i++}else i++;else i++;break;case y:case p:case E:o=r.getUint16(i,u),i+=2}d+=String.fromCodePoint(o)}return d}(new DataView(e.buffer,e.byteOffset,r),{encoding:T});return t?i:i.replace(/\0/g,"")}function Re(e){var t={key:e.type,description:"",data:"",mimeType:null,pictureType:null};if(!(e.size<2))if(3===e.data[0]){var r=e.data.subarray(1).indexOf(0);if(-1!==r){var i=Le(Se(e.data,1,r)),a=e.data[2+r],n=e.data.subarray(3+r).indexOf(0);if(-1!==n){var s,o,l=Le(Se(e.data,3+r,n));return s="--\x3e"===i?Le(Se(e.data,4+r+n)):m(o=e.data.subarray(4+r+n))?o:0==o.byteOffset&&o.byteLength==o.buffer.byteLength?o.buffer:new Uint8Array(o).buffer,t.mimeType=i,t.pictureType=a,t.description=l,t.data=s,t}}}else console.log("Ignore frame with unrecognized character encoding")}function Ae(e){return"PRIV"===e.type?function(e){if(!(e.size<2)){var t=Le(e.data,!0),r=new Uint8Array(e.data.subarray(t.length+1));return{key:e.type,info:t,data:r.buffer}}}(e):"W"===e.type[0]?function(e){if("WXXX"===e.type){if(e.size<2)return;var t=1,r=Le(e.data.subarray(t),!0);t+=r.length+1;var i=Le(e.data.subarray(t));return{key:e.type,info:r,data:i}}var a=Le(e.data);return{key:e.type,info:"",data:a}}(e):"APIC"===e.type?Re(e):function(e){if(!(e.size<2)){if("TXXX"===e.type){var t=1,r=e.data,i=Le(r.subarray(t),!0);t+=i.length+1;var a=Le(r.subarray(t));return{key:e.type,info:i,data:a}}var n=Le(e.data.subarray(1));return{key:e.type,info:"",data:n}}}(e)}function be(e){var t=String.fromCharCode(e[0],e[1],e[2],e[3]),r=pe(e,4);return{type:t,size:r,data:e.subarray(10,10+r)}}var ke=10,De=10;function Ie(e){for(var t=0,r=[];me(e,t);){var i=pe(e,t+6);e[t+5]>>6&1&&(t+=ke);for(var a=(t+=ke)+i;t+De<a;){var n=be(e.subarray(t)),s=Ae(n);s&&r.push(s),t+=n.size+ke}Ee(e,t)&&(t+=ke)}return r}function _e(e){return e&&"PRIV"===e.key&&"com.apple.streaming.transportStreamTimestamp"===e.info}function Ce(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)}}function Pe(e){for(var t,r=n(Ie(e));!(t=r()).done;){var i=t.value;if(_e(i))return Ce(i)}}function xe(e){for(var t="",r=0;r<e.length;r++){var i=e[r].toString(16);i.length<2&&(i="0"+i),t+=i}return t}function we(e){return Uint8Array.from(e.replace(/^0x/,"").replace(/([\da-fA-F]{2}) ?/g,"0x$1 ").replace(/ +$/,"").split(" ")).buffer}function Oe(e){return String.fromCharCode.apply(null,e)}var Me,Fe=Math.pow(2,32)-1,Ne={avc1:1635148593,avcC:1635148611,hvc1:1752589105,hvcC:1752589123,btrt:1651798644,dinf:1684631142,dref:1685218662,esds:1702061171,free:1718773093,ftyp:1718909296,hdlr:1751411826,mdat:1835295092,mdhd:1835296868,mdia:1835297121,mfhd:1835427940,minf:1835626086,moof:1836019558,moov:1836019574,mp4a:1836069985,".mp3":778924083,dac3:1684103987,"ac-3":1633889587,mvex:1836475768,mvhd:1836476516,pasp:1885434736,sdtp:1935963248,stbl:1937007212,stco:1937007471,stsc:1937011555,stsd:1937011556,stsz:1937011578,stts:1937011827,tfdt:1952867444,tfhd:1952868452,traf:1953653094,trak:1953653099,trun:1953658222,trex:1953654136,tkhd:1953196132,vmhd:1986881636,smhd:1936549988},Be={video:1,audio:2,id3:3,text:4};function Ue(e,t){var r=e[t]<<8|e[t+1];return r<0?65536+r:r}function Ge(e,t){var r=e[t]<<24|e[t+1]<<16|e[t+2]<<8|e[t+3];return r<0?4294967296+r:r}function Ve(e,t){var r=Ge(e,t);return r*=Math.pow(2,32),r+=Ge(e,t+4)}function He(e,t){for(var r="",i=t;i<e.length;i++)if(r+=String.fromCharCode(e[i]),0===e[i])return r;return null}function Ke(e,t){return e[t]<<24|e[t+1]<<16|e[t+2]<<8|e[t+3]}function Ye(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 We(e,t){var r=[];return t.length?(je(e,t,0,0,e.byteLength,r),r):r}function je(e,t,r,i,a,n){var s=t[r];if(4===(null==s?void 0:s.length))for(var o=s.charCodeAt(0),l=s.charCodeAt(1),u=s.charCodeAt(2),d=s.charCodeAt(3),h=t.length-1,f=i;f<a;){var c=f+8<=a,g=f+4<=a?16777216*e[f]+(e[f+1]<<16|e[f+2]<<8|e[f+3]):Ge(e,f),v=g>1?f+g:a;if(c&&e[f+4]===o&&e[f+5]===l&&e[f+6]===u&&e[f+7]===d){var m=Math.min(v,a);r===h?n.push(e.subarray(f+8,m)):je(e,t,r+1,f+8,m,n)}f=v}}function qe(e){var t=[],r=e[0],i=8,a=Ge(e,i);i+=4;var n=0,s=0;0===r?(n=Ge(e,i),s=Ge(e,i+4),i+=8):(n=Ve(e,i),s=Ve(e,i+8),i+=16),i+=2;var o=e.length+s,l=Ue(e,i);i+=2;for(var u=0;u<l;u++){var d=i,h=Ge(e,d);d+=4;var f=2147483647&h;if(1==(2147483648&h)>>>31)return fe.warn("SIDX has hierarchical references (not supported)"),null;var c=Ge(e,d);d+=4,t.push({referenceSize:f,subsegmentDuration:c,info:{duration:c/a,start:o,end:o+f-1}}),o+=f,i=d+=4}return{earliestPresentationTime:n,timescale:a,version:r,referencesCount:l,references:t}}function Xe(e){for(var t=[],r=We(e,["moov","trak"]),i=0;i<r.length;i++){var a=r[i],n=We(a,["tkhd"])[0];if(n){var s=n[0],o=Ge(n,0===s?12:20),l=We(a,["mdia","mdhd"])[0];if(l){var u=Ge(l,0===(s=l[0])?12:20),d=We(a,["mdia","hdlr"])[0];if(d){var h=Oe(d.subarray(8,12)),c={soun:B.AUDIO,vide:B.VIDEO}[h],g=ze(We(a,["mdia","minf","stbl","stsd"])[0]);c?(t[o]={timescale:u,type:c,stsd:g},t[c]=f({timescale:u,id:o},g)):t[o]={timescale:u,type:h,stsd:g}}}}}return We(e,["moov","mvex","trex"]).forEach((function(e){var r=Ge(e,4),i=t[r];i&&(i.default={duration:Ge(e,12),sampleSize:Ge(e,16),flags:Ge(e,20)})})),t}function ze(e){var t,r=e.subarray(8),i=r.subarray(86),a=Oe(r.subarray(4,8)),n=a,s="enca"===a||"encv"===a;if(s){var o=We(r,[a])[0];We(o.subarray("enca"===a?28:78),["sinf"]).forEach((function(e){var t=We(e,["schm"])[0];if(t){var r=Oe(t.subarray(4,8));if("cbcs"===r||"cenc"===r){var i=We(e,["frma"])[0];i&&(n=Oe(i))}}}))}var l=n;switch(n){case"avc1":case"avc2":case"avc3":case"avc4":var u=We(i,["avcC"])[0];u&&u.length>3&&(n+="."+Ze(u[1])+Ze(u[2])+Ze(u[3]),t=Qe("avc1"===l?"dva1":"dvav",i));break;case"mp4a":var d=We(r,[a])[0],h=We(d.subarray(28),["esds"])[0];if(h&&h.length>7){var f=4;if(3!==h[f++])break;f=$e(h,f),f+=2;var c=h[f++];if(128&c&&(f+=2),64&c&&(f+=h[f++]),4!==h[f++])break;f=$e(h,f);var g=h[f++];if(64!==g)break;if(n+="."+Ze(g),f+=12,5!==h[f++])break;f=$e(h,f);var v=h[f++],m=(248&v)>>3;31===m&&(m+=1+((7&v)<<3)+((224&h[f])>>5)),n+="."+m}break;case"hvc1":case"hev1":var p=We(i,["hvcC"])[0];if(p&&p.length>12){var y=p[1],E=["","A","B","C"][y>>6],T=31&y,S=Ge(p,2),L=(32&y)>>5?"H":"L",R=p[12],A=p.subarray(6,12);n+="."+E+T,n+="."+function(e){for(var t=0,r=0;r<32;r++)t|=(e>>r&1)<<31-r;return t>>>0}(S).toString(16).toUpperCase(),n+="."+L+R;for(var b="",k=A.length;k--;){var D=A[k];(D||b)&&(b="."+D.toString(16).toUpperCase()+b)}n+=b}t=Qe("hev1"==l?"dvhe":"dvh1",i);break;case"dvh1":case"dvhe":case"dvav":case"dva1":case"dav1":n=Qe(n,i)||n;break;case"vp09":var I=We(i,["vpcC"])[0];if(I&&I.length>6){var _=I[4],C=I[5],P=I[6]>>4&15;n+="."+Je(_)+"."+Je(C)+"."+Je(P)}break;case"av01":var x=We(i,["av1C"])[0];if(x&&x.length>2){var w=x[1]>>>5,O=31&x[1],M=x[2]>>>7?"H":"M",F=(64&x[2])>>6,N=(32&x[2])>>5,B=2===w&&F?N?12:10:F?10:8,U=(16&x[2])>>4,G=(8&x[2])>>3,V=(4&x[2])>>2,H=3&x[2];n+="."+w+"."+Je(O)+M+"."+Je(B)+"."+U+"."+G+V+H+"."+Je(1)+"."+Je(1)+"."+Je(1)+".0",t=Qe("dav1",i)}}return{codec:n,encrypted:s,supplemental:t}}function Qe(e,t){var r=We(t,["dvvC"]),i=r.length?r[0]:We(t,["dvcC"])[0];if(i){var a=i[2]>>1&127,n=i[2]<<5&32|i[3]>>3&31;return e+"."+Je(a)+"."+Je(n)}}function $e(e,t){for(var r=t+5;128&e[t++]&&t<r;);return t}function Ze(e){return("0"+e.toString(16).toUpperCase()).slice(-2)}function Je(e){return(e<10?"0":"")+e}function et(e,t){We(e,["moov","trak"]).forEach((function(e){var r=We(e,["mdia","minf","stbl","stsd"])[0];if(r){var i=r.subarray(8),a=We(i,["enca"]),n=a.length>0;n||(a=We(i,["encv"])),a.forEach((function(e){We(n?e.subarray(28):e.subarray(78),["sinf"]).forEach((function(e){var r=function(e){var t=We(e,["schm"])[0];if(t){var r=Oe(t.subarray(4,8));if("cbcs"===r||"cenc"===r){var i=We(e,["schi","tenc"])[0];if(i)return i}}}(e);r&&t(r,n)}))}))}}))}function tt(e){return"undefined"==typeof TextDecoder?Le(e):(Me||(Me=new TextDecoder("utf-8"))).decode(e).replace(/\0/g,"")}function rt(e,t,r,i,a){for(var n={},s=!1,o=function(e){for(var t=e.byteLength,r=[],i=0;i<t;){var a=i+8<=t,n=i+4<=t?16777216*e[i]+(e[i+1]<<16|e[i+2]<<8|e[i+3]):Ge(e,i),s=n>1?i+n:t;if(a&&109===e[i+4]&&111===e[i+5]&&111===e[i+6]&&102===e[i+7])for(var o=Math.min(s,t),l=i+8;l<o;){var u=l+8<=o,d=l+4<=o?16777216*e[l]+(e[l+1]<<16|e[l+2]<<8|e[l+3]):Ge(e,l),h=d>1?l+d:o;if(u&&116===e[l+4]&&114===e[l+5]&&97===e[l+6]&&102===e[l+7]){for(var f=Math.min(h,o),c=void 0,g=void 0,v=[],m=l+8;m<f;){var p=m+8<=f,y=m+4<=f?16777216*e[m]+(e[m+1]<<16|e[m+2]<<8|e[m+3]):Ge(e,m),E=y>1?m+y:f;if(p&&116===e[m+4]){var T=Math.min(E,f);102===e[m+5]&&104===e[m+6]&&100===e[m+7]?g||(g=e.subarray(m+8,T)):102===e[m+5]&&100===e[m+6]&&116===e[m+7]?c||(c=e.subarray(m+8,T)):114===e[m+5]&&117===e[m+6]&&110===e[m+7]&&v.push(e.subarray(m+8,T))}m=E}r.push({moofOffset:e.byteOffset+i,tfdt:c,tfhd:g,truns:v})}l=h}i=s}return r}(e),l=e.byteLength,u=0;u<o.length;u++){var d=o[u],h=d.moofOffset,f=d.tfdt,c=d.tfhd,g=d.truns,v=Ge(c,4),m=t[v];if(m){var p=n[v]||(n[v]={start:NaN,duration:0,sampleCount:0,trun:[],timescale:m.timescale,type:m.type}),y=void 0;if(f){var E=f[0];y=Ge(f,4),1===E&&(y===Fe?r.warn("[mp4-demuxer]: Ignoring assumed invalid signed 64-bit track fragment decode time"):(y*=Fe+1,y+=Ge(f,8))),_(y)&&(!_(p.start)||y<p.start)&&(p.start=y),a&&_(y)&&(a.timeOffset=y/a.timescale)}var T=(null==a?void 0:a.trackId)===v&&m.type===B.VIDEO,S=m.default,L=16777215&Ge(c,0),R=(null==S?void 0:S.duration)||0,A=(null==S?void 0:S.sampleSize)||0,b=8;1&L&&(b+=8),2&L&&(b+=4),8&L&&(R=Ge(c,b),c.byteOffset,e.byteOffset,b+=4),16&L&&(A=Ge(c,b));var k=0,D=0!=(1&L),I=!D&&0!=(131072&L);D?(k=Ge(c,8),k*=Math.pow(2,32),k+=Ge(c,12)):I&&(k=h);for(var C=y||0,P=0,x=R,w=0;w<g.length;w++){var O=g[w],M=Ge(O,4),F=p.sampleCount;p.sampleCount+=M;var N=1&O[3],U=0,G=4&O[3],V=1&O[2],H=2&O[2],K=4&O[2],Y=8&O[2],W=8;N&&(U=Ke(O,W),W+=4),G&&(1&O[W+1]||void 0!==p.keyFrameIndex||(p.keyFrameIndex=F),W+=4);var j=k+U;if(0!==O[1]||6!==O[2]||1!==O[3]){for(var q=void 0,X=0;X<M;X++){V?(x=Ge(O,W),W+=4):x=R,H?(q=Ge(O,W),W+=4):q=A;var z=void 0;K&&(z=1&O[W+1]?1:0,W+=4);var Q=0;if(Y&&(Q=0===O[0]?Ge(O,W):Ke(O,W),W+=4),T){for(var $=j,Z=0;Z<q;){var J=Ge(e,$);$+=4,nt(a.isHEVCFlavor,e[$])&&st(e.subarray($,$+J),a.isHEVCFlavor?2:1,a.timeOffset+Q/a.timescale,a.samples),$+=J,Z+=J+4}a.timeOffset+=x/a.timescale}if((j+=q)<=l){0===z&&void 0===p.keyFrameIndex&&(p.keyFrameIndex=X,p.keyFrameStart=C);var ee=C+Q;(!_(p.ptsMin)||ee<p.ptsMin)&&(p.ptsMin=ee),(!_(p.ptsMax)||ee+x>p.ptsMax)&&(p.ptsMax=ee+x)}C+=x,P+=x}!P&&R&&(P+=R*M)}else{for(var te,re,ie=C,ae=j,ne=W,se=-1,oe=0,le=0,ue=null!=(te=null==a?void 0:a.timeOffset)?te:0,de=R/(null!=(re=null==a?void 0:a.timescale)?re:1),he=!0===(null==a?void 0:a.isHEVCFlavor),fe=he?2:1,ce=0;ce<M;ce++){var ge=0===O[W]?O[W+1]<<16|O[W+2]<<8|O[W+3]:Ge(O,W),ve=-1===se&&void 0===p.keyFrameIndex&&0==(1&O[W+5]);W+=8;var me=j+ge;if(T){if(ge>0){var pe=j;do{var ye=0===e[pe]?e[pe+1]<<16|e[pe+2]<<8|e[pe+3]:Ge(e,pe),Ee=e[pe+=4],Te=he?Ee>>1&63:31&Ee;(he?39===Te||40===Te:6===Te)&&st(e.subarray(pe,pe+ye),fe,ue,a.samples),pe+=ye}while(pe<me)}ue+=de}j=me,ve&&(se=ce,oe=j)}if(j<=l)le=M;else for(var Se=ae,Le=ne,Re=0;Re<M&&!((Se+=0===O[Le]?O[Le+1]<<16|O[Le+2]<<8|O[Le+3]:Ge(O,Le))>l);Re++)le=Re+1,Le+=8;if(T&&(a.timeOffset=ue),-1!==se&&oe<=l&&void 0===p.keyFrameIndex&&(p.keyFrameIndex=se,p.keyFrameStart=ie+se*R),le){(!_(p.ptsMin)||ie<p.ptsMin)&&(p.ptsMin=ie);var Ae=ie+le*R;(!_(p.ptsMax)||Ae>p.ptsMax)&&(p.ptsMax=Ae)}var be=R*M;C=ie+be,P+=be}}p.duration+=P,p.duration&&(s=!0)}}if(!s){for(var ke=1/0,De=0,Ie=We(e,["sidx"]),_e=0;_e<Ie.length;_e++){var Ce=qe(Ie[_e]);if(null!=Ce&&Ce.references){ke=Math.min(ke,Ce.earliestPresentationTime/Ce.timescale);var Pe=Ce.references.reduce((function(e,t){return e+t.info.duration||0}),0);De=Math.max(De,Pe+Ce.earliestPresentationTime/Ce.timescale)}}De&&_(De)&&Object.keys(n).forEach((function(e){n[e].duration||(n[e].duration=De*n[e].timescale-n[e].start)}))}return n}function it(e,t){var r=new Uint8Array(e.length+t.length);return r.set(e),r.set(t,e.length),r}function at(e){if(!e)return!1;var t=e.substring(0,4);return"hvc1"===t||"hev1"===t||"dvh1"===t||"dvhe"===t}function nt(e,t){if(e){var r=t>>1&63;return 39===r||40===r}return 6==(31&t)}function st(e,t,r,i){var a=ot(e),n=0;n+=t;for(var s=0,o=0,l=0;n<a.length;){s=0;do{if(n>=a.length)break;s+=l=a[n++]}while(255===l);o=0;do{if(n>=a.length)break;o+=l=a[n++]}while(255===l);var u=a.length-n,d=n;if(o<u)n+=o;else if(o>u){fe.error("Malformed SEI payload. "+o+" is too small, only "+u+" bytes left to parse.");break}if(4===s){if(181===a[d++]){var h=Ue(a,d);if(d+=2,49===h){var f=Ge(a,d);if(d+=4,1195456820===f){var c=a[d++];if(3===c){var g=a[d++],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]=a[d++]}i.push({type:c,payloadType:s,pts:r,bytes:p})}}}}}else if(5===s&&o>16){for(var E=[],T=0;T<16;T++){var S=a[d++].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,R=new Uint8Array(L),A=0;A<L;A++)R[A]=a[d++];i.push({payloadType:s,pts:r,uuid:E.join(""),userData:tt(R),userDataBytes:R})}}}function ot(e){for(var t,r=e.byteLength,i=e.indexOf(3,3);-1!==i;)0===e[i-1]&&0===e[i-2]&&(t||(t=[])).push(i),i=e.indexOf(3,i+1);if(!t)return e;for(var a=r-t.length,n=new Uint8Array(a),s=0,o=0,l=0;l<a;o++,l++)o===t[s]&&(o++,s++),n[l]=e[o];return n}var lt,ut,dt,ht=ft("userAgent");function ft(e){try{return navigator[e]||""}catch(e){}return""}function ct(){if(void 0===lt){var e=ht.match(/Chrome\/(\d+)/i);lt=e?parseInt(e[1]):0}return lt}ft("vendor");var gt={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,dav1:.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},image:{mjpg:1}};function vt(e,t){var r=gt[t];return!!r&&!!r[e.slice(0,4)]}function mt(e,t,r){return void 0===r&&(r=!0),!e.split(",").some((function(e){return!pt(e,t,r)}))}function pt(e,t,r){var i;void 0===r&&(r=!0);var a=ve(r);return null!=(i=null==a?void 0:a.isTypeSupported(yt(e,t)))&&i}function yt(e,t){return t+"/mp4;codecs="+e}function Et(e){if(e){var t=e.substring(0,4);return gt.video[t]}return 2}function Tt(e){var t=/\(Windows.+Firefox\//i.test(ht);return e.split(",").reduce((function(e,r){var i=t&&at(r)?9:gt.video[r];return i?(2*i+e)/(e?3:2):(gt.audio[r]+e)/(e?2:1)}),0)}var St={},Lt=/flac|opus|mp4a\.40\.34/i;function Rt(e,t){return void 0===t&&(t=!0),e.replace(Lt,(function(e){return function(e,t){if(void 0===t&&(t=!0),St[e])return St[e];for(var r={flac:["flac","fLaC","FLAC"],opus:["opus","Opus"],"mp4a.40.34":["mp3"]}[e],i=0;i<r.length;i++){var a;if(pt(r[i],"audio",t))return St[e]=r[i],r[i];if("mp3"===r[i]&&null!=(a=ve(t))&&a.isTypeSupported("audio/mpeg"))return""}return e}(e.toLowerCase(),t)}))}function At(e,t){if(e&&(e.length>4||-1!==["ac-3","ec-3","alac","fLaC","Opus"].indexOf(e))&&(bt(e,"audio")||bt(e,"video")))return e;if(t){var r=t.split(",");if(r.length>1){if(e)for(var i=r.length;i--;)if(r[i].substring(0,4)===e.substring(0,4))return r[i];return r[0]}}return t||e}function bt(e,t){return vt(e,t)&&pt(e,t)}function kt(e){var t=ve(e)||{isTypeSupported:function(){return!1}};return{mpeg:t.isTypeSupported("audio/mpeg"),mp3:t.isTypeSupported('audio/mp4; codecs="mp3"'),ac3:!1}}function Dt(e,t,r){var i=encodeURIComponent(t),a=i+"="+encodeURIComponent(r),n=!1,s=[];e.search.substring(1).split("&").forEach((function(e){e===i||e.startsWith(i+"=")?n||(s.push(a),n=!0):e&&s.push(e)})),n||s.push(a),e.search=s.join("&")}var It=["NONE","TYPE-0","TYPE-1",null],_t=["SDR","PQ","HLG"],Ct="",Pt="YES",xt="v2";function wt(e){var t=e.canSkipUntil,r=e.canSkipDateRanges,i=e.age;return t&&i<t/2?r?xt:Pt:Ct}var Ot=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&&Dt(t,"_HLS_msn",this.msn.toString()),void 0!==this.part&&Dt(t,"_HLS_part",this.part.toString()),this.skip&&Dt(t,"_HLS_skip",this.skip),t.href},e}(),Mt=function(){function e(e){if(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.supplemental=void 0,this.videoCodec=void 0,this.width=void 0,this.imageCodec=void 0,this.iframes=void 0,this.details=void 0,this.fragmentError=0,this.loadError=0,this.loadErrorTime=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=[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.frameRate=e.attrs.optionalFloat("FRAME-RATE",0),this._avgBitrate=e.attrs.decimalInteger("AVERAGE-BANDWIDTH"),this.audioCodec=e.audioCodec,this.videoCodec=e.videoCodec,"imageCodec"in e&&(this.imageCodec=e.imageCodec),this.codecSet=[e.videoCodec,e.audioCodec].filter((function(e){return!!e})).map((function(e){return e.substring(0,4)})).join(","),"supplemental"in e){var t;this.supplemental=e.supplemental;var r=null==(t=e.supplemental)?void 0:t.videoCodec;r&&r!==e.videoCodec&&(this.codecSet+=","+r.substring(0,4))}"iframes"in e&&e.iframes?this.iframes=!0:(this.addGroupId("audio",e.attrs.AUDIO),this.addGroupId("text",e.attrs.SUBTITLES))}var t=e.prototype;return t.hasAudioGroup=function(e){return Ft(this._audioGroups,e)},t.hasSubtitleGroup=function(e){return Ft(this._subtitleGroups,e)},t.addGroupId=function(e,t){if(t)if("audio"===e){var r=this._audioGroups;r||(r=this._audioGroups=[]),-1===r.indexOf(t)&&r.push(t)}else if("text"===e){var i=this._subtitleGroups;i||(i=this._subtitleGroups=[]),-1===i.indexOf(t)&&i.push(t)}},t.addFallback=function(){},a(e,[{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(e){}},{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 e;return null==(e=this.audioGroups)?void 0:e[0]}},{key:"textGroupId",get:function(){var e;return null==(e=this.subtitleGroups)?void 0:e[0]}}])}();function Ft(e,t){return!(!t||!e)&&-1!==e.indexOf(t)}function Nt(e,t){var r=!1,i=[];if(e&&(r="SDR"!==e,i=[e]),t){var a="SDR"!==(i=t.allowedVideoRanges||_t.slice(0)).join("")&&!t.videoCodec;(r=void 0!==t.preferHDR?t.preferHDR:a&&function(){if("function"==typeof matchMedia){var e=matchMedia("(dynamic-range: high)"),t=matchMedia("bad query");if(e.media!==t.media)return!0===e.matches}return!1}())||(i=["SDR"])}return{preferHDR:r,allowedVideoRanges:i}}var Bt=function(e,t){return JSON.stringify(e,function(e){var t=new WeakSet;return function(r,i){if(e&&(i=e(r,i)),"object"==typeof i&&null!==i){if(t.has(i))return;t.add(i)}return i}}(t))};function Ut(e,t){fe.log('[abr] start candidates with "'+e+'" ignored because '+t)}function Gt(e){return e.reduce((function(e,t){var r=e.groups[t.groupId];r||(r=e.groups[t.groupId]={tracks:[],channels:{2:0},hasDefault:!1,hasAutoSelect:!1}),r.tracks.push(t);var i=t.channels||"2";return r.channels[i]=(r.channels[i]||0)+1,r.hasDefault=r.hasDefault||t.default,r.hasAutoSelect=r.hasAutoSelect||t.autoselect,r.hasDefault&&(e.hasDefaultAudio=!0),r.hasAutoSelect&&(e.hasAutoSelectAudio=!0),e}),{hasDefaultAudio:!1,hasAutoSelectAudio:!1,groups:{}})}function Vt(e,t){var r;return!!e&&e!==(null==(r=t.loadLevelObj)?void 0:r.uri)}var Ht=function(e){function t(t){var r;return(r=e.call(this,"abr",t.logger)||this).hls=void 0,r.lastLevelLoadSec=0,r.lastLoadedFragLevel=-1,r.firstSelection=-1,r.firstAutoFloor=-1,r._nextAutoLevel=-1,r.nextAutoLevelKey="",r.audioTracksByGroup=null,r.codecTiers=null,r.timer=-1,r.fragCurrent=null,r.partCurrent=null,r.bitrateTestDelay=0,r.rebufferNotice=-1,r.supportedCache={},r.bwEstimator=void 0,r._abandonRulesCheck=function(e){var t,i=r,a=i.fragCurrent,n=i.partCurrent,s=i.hls;if(s){var o=s.autoLevelEnabled,l=s.media;if(a&&l){var u=performance.now(),d=n?n.stats:a.stats,h=n?n.duration:a.duration,f=u-d.loading.start,c=s.minAutoLevel,g=a.level,v=r._nextAutoLevel;if(d.aborted||d.loaded&&d.loaded===d.total||g<=c)return r.clearTimer(),void(r._nextAutoLevel=-1);if(o){var m=v>-1&&v!==g,p=!!e||m;if(p||!l.paused&&l.playbackRate&&l.readyState){var y=s.mainForwardBufferInfo;if(p||null!==y){var E=r.bwEstimator.getEstimateTTFB(),T=Math.abs(l.playbackRate);if(!(f<=Math.max(E,h/(2*T)*1e3))){var S=y?y.len/T:0,L=d.loading.first?d.loading.first-d.loading.start:-1,R=d.loaded&&L>-1,A=r.getBwEstimate(),b=s.levels,k=b[g],D=Math.max(d.loaded,Math.round(h*(a.bitrate||k.averageBitrate)/8)),I=R?f-L:f;I<1&&R&&(I=Math.min(f,8*d.loaded/A));var C=R?1e3*d.loaded/I:0,P=E/1e3,x=C?(D-d.loaded)/C:8*D/A+P;if(!(x<=S)){var w,M=C?8*C:A,F=!0===(null==(t=(null==e?void 0:e.details)||s.latestLevelDetails)?void 0:t.live),N=s.config.abrBandWidthUpFactor,B=Number.POSITIVE_INFINITY;for(w=g-1;w>c;w--){var U=b[w].maxBitrate,G=!b[w].details||F;if((B=r.getTimeToLoadFrag(P,M,h*U,G))<Math.min(S,h+P))break}if(!(B>=x||B>10*h)){R?r.bwEstimator.sample(f-Math.min(E,L),d.loaded):r.bwEstimator.sampleTTFB(f);var V=b[w].maxBitrate;r.getBwEstimate()*N>V&&(r.firstAutoFloor=Math.min(w,s.firstLevel),r.resetEstimator(V));var H=r.findBestLevel(V,c,w,0,S,1,1);H>-1&&(w=H),r.warn("Fragment "+a.sn+(n?" part "+n.index:"")+" of level "+g+" is loading too slowly;\n Fragment duration: "+a.duration.toFixed(3)+"\n Time to underbuffer: "+S.toFixed(3)+" s\n Estimated load time for current fragment: "+x.toFixed(3)+" s\n Estimated load time for down switch fragment: "+B.toFixed(3)+" s\n TTFB estimate: "+(0|L)+" ms\n Current BW estimate: "+(_(A)?0|A:"Unknown")+" bps\n New BW estimate: "+(0|r.getBwEstimate())+" bps\n Switching to level "+w+" @ "+(0|V)+" bps"),s.nextLoadLevel=s.nextAutoLevel=w,r.clearTimer();var K=function(){r.clearTimer();var e=r.hls;if(r.fragCurrent===a&&e.loadLevel===w&&w>0){var t=r.getStarvationDelay();if(r.warn("Aborting inflight request "+(w>0?"and switching down":"")+"\n Fragment duration: "+a.duration.toFixed(3)+" s\n Time to underbuffer: "+t.toFixed(3)+" s"),a.abortRequests(),r.fragCurrent=r.partCurrent=null,w>c){var i=r.findBestLevel(e.levels[c].bitrate,c,w,0,t,1,1);-1===i&&(i=c),e.nextLoadLevel=e.nextAutoLevel=i,r.firstAutoFloor=Math.min(i,e.firstLevel),r.resetEstimator(e.levels[i].bitrate)}}};m||x>2*B?K():r.timer=self.setInterval(K,1e3*B),s.trigger(O.FRAG_LOAD_EMERGENCY_ABORTED,{frag:a,part:n,stats:d})}}}}}}}}},r.hls=t,r.bwEstimator=r.initEstimator(),r.registerListeners(),r}u(t,e);var r=t.prototype;return r.resetEstimator=function(e){this.hls&&(e&&(this.log("setting initial bwe to "+e),this.hls.config.abrEwmaDefaultEstimate=e),this.firstSelection=-1,this.bwEstimator=this.initEstimator())},r.initEstimator=function(){var e=this.hls.config;return new re(e.abrEwmaSlowVoD,e.abrEwmaFastVoD,e.abrEwmaDefaultEstimate)},r.registerListeners=function(){var e=this.hls;e.on(O.MANIFEST_LOADING,this.onManifestLoading,this),e.on(O.FRAG_LOADING,this.onFragLoading,this),e.on(O.FRAG_LOADED,this.onFragLoaded,this),e.on(O.FRAG_BUFFERED,this.onFragBuffered,this),e.on(O.LEVEL_SWITCHING,this.onLevelSwitching,this),e.on(O.LEVEL_LOADED,this.onLevelLoaded,this),e.on(O.LEVELS_UPDATED,this.onLevelsUpdated,this),e.on(O.MAX_AUTO_LEVEL_UPDATED,this.onMaxAutoLevelUpdated,this),e.on(O.ERROR,this.onError,this)},r.unregisterListeners=function(){var e=this.hls;e&&(e.off(O.MANIFEST_LOADING,this.onManifestLoading,this),e.off(O.FRAG_LOADING,this.onFragLoading,this),e.off(O.FRAG_LOADED,this.onFragLoaded,this),e.off(O.FRAG_BUFFERED,this.onFragBuffered,this),e.off(O.LEVEL_SWITCHING,this.onLevelSwitching,this),e.off(O.LEVEL_LOADED,this.onLevelLoaded,this),e.off(O.LEVELS_UPDATED,this.onLevelsUpdated,this),e.off(O.MAX_AUTO_LEVEL_UPDATED,this.onMaxAutoLevelUpdated,this),e.off(O.ERROR,this.onError,this))},r.destroy=function(){this.unregisterListeners(),this.clearTimer(),this.hls=this._abandonRulesCheck=this.supportedCache=null,this.audioTracksByGroup=this.codecTiers=this.fragCurrent=this.partCurrent=null},r.onManifestLoading=function(e,t){this.lastLoadedFragLevel=-1,this.firstSelection=-1,this.firstAutoFloor=-1,this.lastLevelLoadSec=0,this.supportedCache={},this.fragCurrent=this.partCurrent=null,this.onLevelsUpdated(),this.clearTimer()},r.onLevelsUpdated=function(){this.lastLoadedFragLevel>-1&&this.fragCurrent&&(this.lastLoadedFragLevel=this.fragCurrent.level),this._nextAutoLevel=-1,this.onMaxAutoLevelUpdated(),this.codecTiers=null,this.audioTracksByGroup=null},r.onMaxAutoLevelUpdated=function(){this.firstSelection=-1,this.nextAutoLevelKey=""},r.onFragLoading=function(e,t){var r,i=t.frag;this.ignoreFragment(i)||(i.bitrateTest||(this.fragCurrent=i,this.partCurrent=null!=(r=t.part)?r:null),this.clearTimer(),this.timer=self.setInterval(this._abandonRulesCheck,100))},r.onLevelSwitching=function(e,t){this.clearTimer()},r.onError=function(e,t){if(!t.fatal)switch(t.details){case w.BUFFER_ADD_CODEC_ERROR:case w.BUFFER_APPEND_ERROR:this.lastLoadedFragLevel=-1,this.firstSelection=-1;break;case w.FRAG_LOAD_TIMEOUT:var r=t.frag,i=this.fragCurrent,a=this.partCurrent;if(r&&H(r,i)){var n=performance.now(),s=a?a.stats:r.stats,o=n-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)}}},r.getTimeToLoadFrag=function(e,t,r,i){return e+r/t+(i?e+this.lastLevelLoadSec:0)},r.onLevelLoaded=function(e,t){var r=this.hls.config,i=t.stats.loading,a=i.end-i.first;_(a)&&(this.lastLevelLoadSec=a/1e3),t.details.live?this.bwEstimator.update(r.abrEwmaSlowLive,r.abrEwmaFastLive):this.bwEstimator.update(r.abrEwmaSlowVoD,r.abrEwmaFastVoD),this.timer>-1&&this._abandonRulesCheck(t.levelInfo)},r.onFragLoaded=function(e,t){var r,i=t.frag,a=t.part,n=a?a.stats:i.stats;if(i.type===N.MAIN&&this.bwEstimator.sampleTTFB(n.loading.first-n.loading.start),!this.ignoreFragment(i)){if(this.clearTimer(),i.level===this._nextAutoLevel&&(this._nextAutoLevel=-1),this.firstSelection=-1,null!=(r=this.hls)&&r.config.abrMaxWithRealBitrate){var s=a?a.duration:i.duration,o=this.hls.levels[i.level],l=(o.loaded?o.loaded.bytes:0)+n.loaded,u=(o.loaded?o.loaded.duration:0)+s;o.loaded={bytes:l,duration:u},o.realBitrate=Math.round(8*l/u)}if(i.bitrateTest){var d={stats:n,frag:i,part:a,id:i.type};this.onFragBuffered(O.FRAG_BUFFERED,d),i.bitrateTest=!1}else this.lastLoadedFragLevel=i.level}},r.onFragBuffered=function(e,t){var r=t.frag,i=t.part,a=null!=i&&i.stats.loaded?i.stats:r.stats;if(!a.aborted&&!this.ignoreFragment(r)){var n=((r.bitrate||0)>=1e5?a.parsing.end:a.loading.end)-a.loading.start-Math.min(a.loading.first-a.loading.start,this.bwEstimator.getEstimateTTFB());this.bwEstimator.sample(n,a.loaded),a.bwEstimate=this.getBwEstimate(),r.bitrateTest?this.bitrateTestDelay=n/1e3:(this.bitrateTestDelay=0,this.firstAutoFloor>-1&&this.bwEstimator.canEstimate()&&(this.firstAutoFloor=-1))}},r.ignoreFragment=function(e){return e.type!==N.MAIN||"initSegment"===e.sn},r.clearTimer=function(){this.timer>-1&&(self.clearInterval(this.timer),this.timer=-1)},r.getAutoLevelKey=function(){return this.getBwEstimate()+"_"+this.getStarvationDelay().toFixed(2)},r.getNextABRAutoLevel=function(){var e=this.fragCurrent,t=this.partCurrent,r=this.hls;if(!r)return-1;if(r.levels.length<=1)return r.loadLevel;var i=r.maxAutoLevel,a=r.config,n=r.minAutoLevel,s=t?t.duration:e?e.duration:0,o=this.getBwEstimate(),l=this.getStarvationDelay(),u=a.abrBandWidthFactor,d=a.abrBandWidthUpFactor;if(l){var h=this.findBestLevel(o,n,i,l,0,u,d);if(h>=0)return this.rebufferNotice=-1,h}var f=s?Math.min(s,a.maxStarvationDelay):a.maxStarvationDelay;if(!l){var c=this.bitrateTestDelay;c&&(f=(s?Math.min(s,a.maxLoadingDelay):a.maxLoadingDelay)-c,this.info("bitrate test took "+Math.round(1e3*c)+"ms, set first fragment max fetchDuration to "+Math.round(1e3*f)+" ms"),u=d=1)}var g=this.findBestLevel(o,n,i,l,f,u,d);if(this.rebufferNotice!==g&&(this.rebufferNotice=g,this.info((l?"rebuffering expected":"buffer is empty")+", optimal quality level "+g)),g>-1)return g;var v=r.levels[n],m=r.loadLevelObj;return m&&(null==v?void 0:v.bitrate)<m.bitrate?n:r.loadLevel},r.getStarvationDelay=function(){var e=this.hls,t=null==e?void 0:e.media;if(!t)return 1/0;var r=t&&0!==t.playbackRate?Math.abs(t.playbackRate):1,i=e.mainForwardBufferInfo;return(i?i.len:0)/r},r.getBwEstimate=function(){return this.bwEstimator.getEstimate()},r.findBestLevel=function(e,t,r,i,a,n,s){var o=this,l=this.hls,u=this.lastLoadedFragLevel,d=this.fragCurrent,h=this.partCurrent;if(!l)return-1;var f=i+a,c=-1===u?l.firstLevel:u,g=l.levels,v=l.allAudioTracks,m=l.loadLevel,p=l.latestLevelDetails,y=l.config;if(1===g.length)return 0;var E,T=g[c],S=!(null==p||!p.live),L=-1===m||-1===u,R="SDR",A=(null==T?void 0:T.frameRate)||0,b=y.audioPreference,k=y.videoPreference,D=(this.audioTracksByGroup||(this.audioTracksByGroup=Gt(v)),-1);if(L){if(-1!==this.firstSelection)return this.firstSelection;var I=this.codecTiers||(this.codecTiers=function(e,t,r,i){return e.slice(r,i+1).reduce((function(e,t,r){if(!t.codecSet)return e;var i=t.audioGroups,a=e[t.codecSet];a||(e[t.codecSet]=a={minBitrate:1/0,minHeight:1/0,minFramerate:1/0,minIndex:r,maxScore:0,videoRanges:{SDR:0},channels:{2:0},hasDefaultAudio:!i,fragmentError:0}),a.minBitrate=Math.min(a.minBitrate,t.bitrate);var n=Math.min(t.height,t.width);return a.minHeight=Math.min(a.minHeight,n),a.minFramerate=Math.min(a.minFramerate,t.frameRate),a.minIndex=Math.min(a.minIndex,r),a.maxScore=Math.max(a.maxScore,t.score),a.fragmentError+=t.fragmentError,a.videoRanges[t.videoRange]=(a.videoRanges[t.videoRange]||0)+1,e}),{})}(g,0,t,r)),C=function(e,t,r,i,a){for(var n=Object.keys(e),s=null==i?void 0:i.channels,o=null==i?void 0:i.audioCodec,l=null==a?void 0:a.videoCodec,u=s&&2===parseInt(s),d=!1,h=!1,f=1/0,c=1/0,g=1/0,v=1/0,m=0,p=[],y=Nt(t,a),E=y.preferHDR,T=y.allowedVideoRanges,S=function(){var t=e[n[L]];d||(d=t.channels[2]>0),f=Math.min(f,t.minHeight),c=Math.min(c,t.minFramerate),g=Math.min(g,t.minBitrate),T.filter((function(e){return t.videoRanges[e]>0})).length>0&&(h=!0)},L=n.length;L--;)S();f=_(f)?f:0,c=_(c)?c:0;var R=Math.max(1080,f),A=Math.max(30,c);g=_(g)?g:r,r=Math.max(g,r),h||(t=void 0);var b=n.length>1;return{codecSet:n.reduce((function(t,i){var a=e[i];if(i===t)return t;if(p=h?T.filter((function(e){return a.videoRanges[e]>0})):[],b){if(a.minBitrate>r)return Ut(i,"min bitrate of "+a.minBitrate+" > current estimate of "+r),t;if(!a.hasDefaultAudio)return Ut(i,"no renditions with default or auto-select sound found"),t;if(o&&i.indexOf(o.substring(0,4))%5!=0)return Ut(i,'audio codec preference "'+o+'" not found'),t;if(s&&!u){if(!a.channels[s])return Ut(i,"no renditions with "+s+" channel sound found (channels options: "+Object.keys(a.channels)+")"),t}else if((!o||u)&&d&&0===a.channels[2])return Ut(i,"no renditions with stereo sound found"),t;if(a.minHeight>R)return Ut(i,"min resolution of "+a.minHeight+" > maximum of "+R),t;if(a.minFramerate>A)return Ut(i,"min framerate of "+a.minFramerate+" > maximum of "+A),t;if(!p.some((function(e){return a.videoRanges[e]>0})))return Ut(i,"no variants with VIDEO-RANGE of "+Bt(p)+" found"),t;if(l&&i.indexOf(l.substring(0,4))%5!=0)return Ut(i,'video codec preference "'+l+'" not found'),t;if(a.maxScore<m)return Ut(i,"max score of "+a.maxScore+" < selected max of "+m),t}return t&&(Tt(i)>=Tt(t)||a.fragmentError>e[t].fragmentError)?t:(v=a.minIndex,m=a.maxScore,i)}),void 0),videoRanges:p,preferHDR:E,minFramerate:c,minBitrate:g,minIndex:v}}(I,R,e,b,k),P=C.codecSet,x=C.videoRanges,w=C.minFramerate,O=C.minBitrate,M=C.minIndex,F=C.preferHDR;D=M,E=P,R=F?x[x.length-1]:x[0],A=w,e=Math.max(e,O),this.log("picked start tier "+Bt(C))}else E=null==T?void 0:T.codecSet,R=null==T?void 0:T.videoRange;for(var N,B=h?h.duration:d?d.duration:0,U=this.bwEstimator.getEstimateTTFB()/1e3,G=[],V=function(){var t,l=g[H],d=H>c;if(!l)return 0;if((E&&l.codecSet!==E||R&&l.videoRange!==R||d&&A>l.frameRate||!d&&A>0&&A<l.frameRate||null!=(t=l.supportedResult)&&null!=(t=t.decodingInfoResults)&&t.some((function(e){return!1===e.smooth})))&&(!L||H!==D))return G.push(H),0;var v,p=l.details,T=(h?null==p?void 0:p.partTarget:null==p?void 0:p.averagetargetduration)||B||1;v=d?s*e:n*e;var b=B&&i>=2*B&&0===a?l.averageBitrate:l.maxBitrate,k=o.getTimeToLoadFrag(U,v,b*T,!p||p.live);if(v>=b&&(H===u||0===l.loadError&&0===l.fragmentError||ee(l,y.errorPenaltyExpireMs))&&(k<=U||!_(k)||S&&!o.bitrateTestDelay||k<f)){var I=o.forcedAutoLevel;return H===m||-1!==I&&I===m||(G.length&&o.trace("Skipped level(s) "+G.join(",")+" of "+r+' max with CODECS and VIDEO-RANGE:"'+g[G[0]].codecs+'" '+g[G[0]].videoRange+'; not compatible with "'+E+'" '+R),o.info("switch candidate:"+c+"->"+H+" adjustedbw("+Math.round(v)+")-bitrate="+Math.round(v-b)+" ttfb:"+U.toFixed(1)+" avgDuration:"+T.toFixed(1)+" maxFetchDuration:"+f.toFixed(1)+" fetchDuration:"+k.toFixed(1)+" firstSelection:"+L+" codecSet:"+l.codecSet+" videoRange:"+l.videoRange+" hls.loadLevel:"+m)),L&&(o.firstSelection=H),{v:H}}},H=r;H>=t;H--)if(0!==(N=V())&&N)return N.v;return-1},r.deriveNextAutoLevel=function(e){if(!this.hls)return-1;var t=this.hls,r=t.maxAutoLevel,i=t.minAutoLevel;return Math.min(Math.max(e,i),r)},a(t,[{key:"firstAutoLevel",get:function(){var e=this.hls;if(!e)return 0;var t=e.maxAutoLevel,r=e.minAutoLevel,i=this.getBwEstimate(),a=this.getStarvationDelay(),n=e.config.maxStarvationDelay,s=this.findBestLevel(i,r,t,a/2,n,1,1);if(s>-1)return s;var o=e.firstLevel,l=-1===this.firstAutoFloor?1/0:this.firstAutoFloor,u=Math.min(Math.max(o,r),t,l);return this.warn("Could not find best starting auto level. Defaulting to first in playlist "+o+" clamped to "+u),u}},{key:"forcedAutoLevel",get:function(){return this.nextAutoLevelKey?-1:this._nextAutoLevel}},{key:"nextAutoLevel",get:function(){if(!this.hls)return-1;var e=this.forcedAutoLevel,t=this.bwEstimator.canEstimate(),r=this.lastLoadedFragLevel>-1;if(!(-1===e||t&&r&&this.nextAutoLevelKey!==this.getAutoLevelKey()))return e;var i=t&&r?this.getNextABRAutoLevel():this.firstAutoLevel;if(-1!==e){var a=this.hls.levels;if(a.length>Math.max(e,i)&&a[e].loadError<=a[i].loadError)return e}return this._nextAutoLevel=i,this.nextAutoLevelKey=this.getAutoLevelKey(),i},set:function(e){var t=this.deriveNextAutoLevel(e);this._nextAutoLevel!==t&&(this.nextAutoLevelKey="",this._nextAutoLevel=t)}}])}(ie),Kt=function(){function e(e){this.tracks=void 0,this.queues={video:[],audio:[],audiovideo:[]},this.tracks=e}var t=e.prototype;return t.destroy=function(){this.tracks=this.queues=null},t.append=function(e,t,r){if(null!==this.queues&&null!==this.tracks){var i=this.queues[t];i.push(e),1!==i.length||r||this.executeNext(t)}},t.appendBlocker=function(e){var t=this;return new Promise((function(r){var i={label:"async-blocker",execute:r,onStart:function(){},onComplete:function(){},onError:function(){}};t.append(i,e)}))},t.prependBlocker=function(e){var t=this;return new Promise((function(r){if(t.queues){var i={label:"async-blocker-prepend",execute:r,onStart:function(){},onComplete:function(){},onError:function(){}};t.queues[e].unshift(i)}}))},t.removeBlockers=function(){null!==this.queues&&[this.queues.video,this.queues.audio,this.queues.audiovideo].forEach((function(e){var t,r=null==(t=e[0])?void 0:t.label;"async-blocker"===r||"async-blocker-prepend"===r?(e[0].execute(),e.splice(0,1)):"block-audio"===r&&e.splice(0,1)}))},t.insertNext=function(e,t){var r;null!==this.queues&&(r=this.queues[t]).splice.apply(r,[1,0].concat(e))},t.unblockAudio=function(){var e;null!==this.queues&&"block-audio"===(null==(e=this.current("audio"))?void 0:e.label)&&this.shiftAndExecuteNext("audio")},t.audioBlocking=function(){return null!==this.queues&&this.queues.audio.some((function(e){return"block-audio"===e.label}))},t.executeNext=function(e){if(null!==this.queues&&null!==this.tracks){var t=this.queues[e];if(t.length){var r=t[0];try{r.execute()}catch(t){var i;if(r.onError(t),null===this.queues||null===this.tracks)return;var a=null==(i=this.tracks[e])?void 0:i.buffer;null!=a&&a.updating||this.shiftAndExecuteNext(e)}}}},t.shiftAndExecuteNext=function(e){null!==this.queues&&(this.queues[e].shift(),this.executeNext(e))},t.current=function(e){var t;return(null==(t=this.queues)?void 0:t[e][0])||null},t.toString=function(){var e=this.queues,t=this.tracks;return null===e||null===t?"<destroyed>":"\n"+this.list("video")+"\n"+this.list("audio")+"\n"+this.list("audiovideo")+"}"},t.list=function(e){var t,r;return null!=(t=this.queues)&&t[e]||null!=(r=this.tracks)&&r[e]?e+": ("+this.listSbInfo(e)+") "+this.listOps(e):""},t.listSbInfo=function(e){var t,r=null==(t=this.tracks)?void 0:t[e],i=null==r?void 0:r.buffer;return i?"SourceBuffer"+(i.updating?" updating":"")+(r.ended?" ended":"")+(r.ending?" ending":""):"none"},t.listOps=function(e){var t;return(null==(t=this.queues)?void 0:t[e].map((function(e){return e.label})).join(", "))||""},e}(),Yt=function(e,t){for(var r=0,i=e.length-1,a=null,n=null;r<=i;){var s=t(n=e[a=(r+i)/2|0]);if(s>0)r=a+1;else{if(!(s<0))return n;i=a-1}}return null};function Wt(e,t,r,i,a){void 0===r&&(r=0),void 0===i&&(i=0),void 0===a&&(a=.005);var n=null;if(e){n=t[1+e.sn-t[0].sn]||null;var s=e.endDTS-r;s>0&&s<15e-7&&(r+=15e-7),n&&e.level!==n.level&&n.end<=e.end&&(n=t[2+e.sn-t[0].sn]||null)}else 0===r&&0===t[0].start&&(n=t[0]);if(n&&((!e||e.level===n.level)&&0===jt(r,i,n)||function(e,t,r){if(0===(null==t?void 0:t.start)&&t.level<e.level&&(t.endPTS||0)>0){var i=t.tagList.reduce((function(e,t){return"INF"===t[0]&&(e+=parseFloat(t[1])),e}),r);return e.start<=i}return!1}(n,e,Math.min(a,i))))return n;var o=Yt(t,jt.bind(null,r,i));return!o||o===e&&n?n:o}function jt(e,t,r){if(void 0===e&&(e=0),void 0===t&&(t=0),r.start<=e&&r.start+r.duration>e)return 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 qt(e,t,r){var i=1e3*Math.min(t,r.duration+(r.deltaPTS?r.deltaPTS:0));return(r.endProgramDateTime||0)-i>e}var Xt=0,zt=2,Qt=3,$t=5,Zt=0,Jt=1,er=2,tr=4,rr=8,ir=16,ar=function(e){function t(t){var r;return(r=e.call(this,"error-controller",t.logger)||this).hls=void 0,r.playlistError=0,r.hls=t,r.registerListeners(),r}u(t,e);var r=t.prototype;return r.registerListeners=function(){var e=this.hls;e.on(O.ERROR,this.onError,this),e.on(O.MANIFEST_LOADING,this.onManifestLoading,this),e.on(O.LEVEL_UPDATED,this.onLevelUpdated,this)},r.unregisterListeners=function(){var e=this.hls;e&&(e.off(O.ERROR,this.onError,this),e.off(O.ERROR,this.onErrorOut,this),e.off(O.MANIFEST_LOADING,this.onManifestLoading,this),e.off(O.LEVEL_UPDATED,this.onLevelUpdated,this))},r.destroy=function(){this.unregisterListeners(),this.hls=null},r.startLoad=function(e){},r.stopLoad=function(){this.playlistError=0},r.getVariantLevelIndex=function(e){return(null==e?void 0:e.type)===N.MAIN?e.level:this.getVariantIndex()},r.getVariantIndex=function(){var e,t=this.hls,r=t.currentLevel;return null!=(e=t.loadLevelObj)&&e.details||-1===r?t.loadLevel:r},r.variantHasKey=function(e,t){if(e){var r;if(null!=(r=e.details)&&r.hasKey(t))return!0;var i=e.audioGroups;if(i)return this.hls.allAudioTracks.filter((function(e){return i.indexOf(e.groupId)>=0})).some((function(e){var r;return null==(r=e.details)?void 0:r.hasKey(t)}))}return!1},r.onManifestLoading=function(){this.playlistError=0},r.onLevelUpdated=function(){this.playlistError=0},r.onError=function(e,t){var r;if(!t.fatal){var i=this.hls,a=t.context;switch(t.details){case w.FRAG_LOAD_ERROR:case w.FRAG_LOAD_TIMEOUT:case w.KEY_LOAD_ERROR:case w.KEY_LOAD_TIMEOUT:return void(t.errorAction=this.getFragRetryOrSwitchAction(t));case w.FRAG_PARSING_ERROR:if(null!=(r=t.frag)&&r.gap)return void(t.errorAction=or());case w.FRAG_GAP:case w.FRAG_DECRYPT_ERROR:return t.errorAction=this.getFragRetryOrSwitchAction(t),void(t.errorAction.action=zt);case w.PLAYLIST_UNCHANGED_ERROR:case w.LEVEL_EMPTY_ERROR:case w.LEVEL_PARSING_ERROR:var n,s=t.parent===N.MAIN?t.level:i.loadLevel;return void(t.details===w.LEVEL_EMPTY_ERROR&&null!=(n=t.context)&&null!=(n=n.levelDetails)&&n.live?t.errorAction=this.getPlaylistRetryOrSwitchAction(t,s):(t.levelRetry=!1,t.errorAction=this.getLevelSwitchAction(t,s)));case w.LEVEL_LOAD_ERROR:case w.LEVEL_LOAD_TIMEOUT:return void("number"==typeof(null==a?void 0:a.level)&&(t.errorAction=this.getPlaylistRetryOrSwitchAction(t,a.level)));case w.AUDIO_TRACK_LOAD_ERROR:case w.AUDIO_TRACK_LOAD_TIMEOUT:case w.SUBTITLE_LOAD_ERROR:case w.SUBTITLE_TRACK_LOAD_TIMEOUT:if(a){var o=i.loadLevelObj;if(o&&(a.type===F.AUDIO_TRACK&&o.hasAudioGroup(a.groupId)||a.type===F.SUBTITLE_TRACK&&o.hasSubtitleGroup(a.groupId)))return t.errorAction=this.getPlaylistRetryOrSwitchAction(t,i.loadLevel),t.errorAction.action=zt,void(t.errorAction.flags=Jt)}return;case w.KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED:return void(t.errorAction={action:zt,flags:er});case w.KEY_SYSTEM_SESSION_UPDATE_FAILED:case w.KEY_SYSTEM_STATUS_INTERNAL_ERROR:case w.KEY_SYSTEM_NO_SESSION:return void(t.errorAction={action:zt,flags:tr});case w.BUFFER_ADD_CODEC_ERROR:case w.REMUX_ALLOC_ERROR:case w.BUFFER_APPEND_ERROR:return void(t.errorAction||(t.errorAction=this.getLevelSwitchAction(t,t.level)));case w.MEDIA_SOURCE_REQUIRES_RESET:return t.errorAction||(t.errorAction=this.getLevelSwitchAction(t,t.level)),void(t.errorAction.flags|=ir|rr);case w.INTERNAL_EXCEPTION:case w.BUFFER_APPENDING_ERROR:case w.BUFFER_APPEND_NO_PROGRESS:case w.BUFFER_FULL_ERROR:case w.LEVEL_SWITCH_ERROR:case w.BUFFER_STALLED_ERROR:case w.BUFFER_SEEK_OVER_HOLE:case w.BUFFER_NUDGE_ON_STALL:return void(t.errorAction=or())}t.type===x.KEY_SYSTEM_ERROR&&(t.levelRetry=!1,t.errorAction=or())}},r.getPlaylistRetryOrSwitchAction=function(e,t){var r=z(this.hls.config.playlistLoadPolicy,e),i=this.playlistError++;if(Z(r,i,j(e),e.response))return{action:$t,flags:Zt,retryConfig:r,retryCount:i};var a=this.getLevelSwitchAction(e,t);return r&&(a.retryConfig=r,a.retryCount=i),a},r.getFragRetryOrSwitchAction=function(e){var t=this.hls,r=this.getVariantLevelIndex(e.frag),i=t.levels[r],a=t.config,n=a.fragLoadPolicy,s=a.keyLoadPolicy,o=z(q(e)?s:n,e),l=t.levels.reduce((function(e,t){return e+t.fragmentError}),0);if(i&&(e.details!==w.FRAG_GAP&&i.fragmentError++,!X(e)&&Z(o,l,j(e),e.response)))return{action:$t,flags:Zt,retryConfig:o,retryCount:l};var u=this.getLevelSwitchAction(e,r);return o&&(u.retryConfig=o,u.retryCount=l),u},r.getLevelSwitchAction=function(e,t){var r=this.hls;null==t&&(t=r.loadLevel);var i=this.hls.levels[t];if(i){var a,n,s=e.details;i.loadError++,i.loadErrorTime=self.performance.now(),s===w.BUFFER_APPEND_ERROR&&i.fragmentError++;var o=-1,l=r.levels,u=r.loadLevel,d=r.minAutoLevel,h=r.maxAutoLevel;r.autoLevelEnabled||r.config.preserveManualLevelOnError||(r.loadLevel=-1);for(var f,c=null==(a=e.frag)?void 0:a.type,g=(c===N.AUDIO&&s===w.FRAG_PARSING_ERROR||"audio"===e.sourceBufferName&&nr(e))&&l.some((function(e){var t=e.audioCodec;return i.audioCodec!==t})),v="video"===e.sourceBufferName&&nr(e)&&l.some((function(e){var t=e.codecSet,r=e.audioCodec;return i.codecSet!==t&&i.audioCodec===r})),m=null!=(n=e.context)?n:{},p=m.type,y=m.groupId,E=function(){var t=(T+u)%l.length;if(t!==u&&t>=d&&t<=h&&(0===l[t].loadError||ee(l[t],r.config.errorPenaltyExpireMs))){var a,n,f=l[t];if(s===w.FRAG_GAP&&c===N.MAIN&&e.frag){var m=l[t].details;if(m){var E=Wt(e.frag,m.fragments,e.frag.start);if(null!=E&&E.gap)return 0}}else{if(p===F.AUDIO_TRACK&&f.hasAudioGroup(y)||p===F.SUBTITLE_TRACK&&f.hasSubtitleGroup(y))return 0;if(c===N.AUDIO&&null!=(a=i.audioGroups)&&a.some((function(e){return f.hasAudioGroup(e)}))||c===N.SUBTITLE&&null!=(n=i.subtitleGroups)&&n.some((function(e){return f.hasSubtitleGroup(e)}))||g&&i.audioCodec===f.audioCodec||v&&i.codecSet===f.codecSet||!g&&i.audioCodec!==f.audioCodec)return 0}return o=t,1}},T=l.length;T--&&(0===(f=E())||1!==f););if(o>-1&&r.loadLevel!==o)return e.levelRetry=!0,this.playlistError=0,{action:zt,flags:Zt,nextAutoLevel:o}}return{action:zt,flags:Jt}},r.onErrorOut=function(e,t){var r,i;switch(null==(r=t.errorAction)?void 0:r.action){case Xt:break;case zt:this.sendAlternateToPenaltyBox(t),t.errorAction.resolved||t.details===w.FRAG_GAP||t.details===w.PLAYLIST_UNCHANGED_ERROR||sr(t)||(t.fatal=!0)}((null==(i=t.errorAction)?void 0:i.flags)||0)&ir&&this.hls.recoverMediaError(),t.fatal&&this.hls.stopLoad()},r.sendAlternateToPenaltyBox=function(e){var t=this.hls,r=e.errorAction;if(r){var i=r.nextAutoLevel;if(r.flags===Zt)this.switchLevel(e,i);else if(r.flags&rr)for(var a=this.hls.levels,n=a.length;n--;)"SDR"!==a[n].videoRange?(a[n].fragmentError++,a[n].loadError++):void 0===i&&(i=n);else if(r.flags&er){var s=this.getVariantLevelIndex(e.frag),o=t.levels[s],l=null==o?void 0:o.attrs["HDCP-LEVEL"];r.hdcpLevel=l;var u="NONE"===l;l&&!u?(t.maxHdcpLevel=It[It.indexOf(l)-1],r.resolved=!0,this.warn('Restricting playback to HDCP-LEVEL of "'+t.maxHdcpLevel+'" or lower')):(u&&this.warn("HDCP policy resticted output with HDCP-LEVEL=NONE"),r.flags|=tr)}if(r.flags&tr){var d=e.decryptdata;if(d){for(var h=this.hls.levels,f=h.length,c=f;c--;){var g,v;this.variantHasKey(h[c],d)&&(this.log("Banned key found in level "+c+" ("+h[c].bitrate+'bps) or audio group "'+(null==(g=h[c].audioGroups)?void 0:g.join(","))+'" ('+(null==(v=e.frag)?void 0:v.type)+" fragment) "+xe(d.keyId||[])),h[c].fragmentError++,h[c].loadError++,this.log("Removing level "+c+" with key error ("+e.error+")"),this.hls.removeLevel(c))}var m=e.frag;if(this.hls.levels.length<f)r.resolved=!0;else if(m&&m.type!==N.MAIN){var p=m.decryptdata;p&&!d.matches(p)&&(r.resolved=!0)}}}r.resolved||this.switchLevel(e,i)}},r.switchLevel=function(e,t){if(void 0!==t&&e.errorAction&&(this.warn("switching to level "+t+" after "+e.details),this.hls.nextAutoLevel=t,e.errorAction.resolved=!0,this.hls.nextLoadLevel=this.hls.nextAutoLevel,e.details===w.BUFFER_ADD_CODEC_ERROR&&e.mimeType&&"audiovideo"!==e.sourceBufferName))for(var r=e.mimeType.replace(/^.+codecs=["']?([^"']+).*$/,"$1"),i=this.hls.levels,a=i.length;a--;)i[a][e.sourceBufferName+"Codec"]===r&&(this.log("Removing level "+a+" for "+e.details+' ("'+r+'" not supported)'),this.hls.removeLevel(a))},t}(ie);function nr(e){var t=e.details;return t===w.BUFFER_ADD_CODEC_ERROR||t===w.MEDIA_SOURCE_REQUIRES_RESET||t===w.BUFFER_APPEND_ERROR&&!sr(e)}function sr(e){return e.details===w.BUFFER_APPEND_ERROR&&("QuotaExceededError"===e.error.name||"InvalidStateError"===e.error.name)}function or(e){var t={action:Xt,flags:Zt};return e&&(t.resolved=!0),t}var lr=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.dateRangeTagCount=0,this.live=!0,this.iframesOnly=!1,this.requestScheduled=-1,this.ageHeader=0,this.advancedDateTime=void 0,this.updated=!0,this.advanced=!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.appliedTimelineOffset=void 0,this.fragments=[],this.encryptedFragments=[],this.dateRanges={},this.url=e}var t=e.prototype;return t.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.live,this.advanced=this.endSN>e.endSN||t>0||0===t&&r>0,this.updated||this.advanced?this.misses=0:this.misses=e.misses+1},t.hasKey=function(e){return this.encryptedFragments.some((function(t){var r=t.decryptdata;return r||(t.setKeyFormat(e.keyFormat),r=t.decryptdata),!!r&&e.matches(r)}))},a(e,[{key:"hasProgramDateTime",get:function(){return!!this.fragments.length&&_(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)&&e.length?this.partList[this.partList.length-1].end:this.fragmentEnd}},{key:"fragmentEnd",get:function(){return this.fragments.length?this.fragments[this.fragments.length-1].end:0}},{key:"fragmentStart",get:function(){return this.fragments.length?this.fragments[0].start: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)&&e.length?this.partList[this.partList.length-1].index:-1}},{key:"maxPartIndex",get:function(){var e=this.partList;if(e){var t=this.lastPartIndex;if(-1!==t){for(var r=e.length;r--;)if(e[r].index>t)return e[r].index;return t}}return 0}},{key:"lastPartSn",get:function(){var e;return null!=(e=this.partList)&&e.length?this.partList[this.partList.length-1].fragment.sn:this.endSN}},{key:"expired",get:function(){if(this.live&&this.age&&this.misses<3){var e=this.partEnd-this.fragmentStart;return this.age>Math.max(e,this.totalduration)+this.levelTargetDuration}return!1}}])}(),ur={length:0,start:function(){return 0},end:function(){return 0}},dr=function(){function e(){}return e.isBuffered=function(t,r){if(t)for(var i=e.getBuffered(t),a=i.length;a--;)if(r>=i.start(a)&&r<=i.end(a))return!0;return!1},e.bufferedRanges=function(t){if(t){var r=e.getBuffered(t);return e.timeRangesToArray(r)}return[]},e.timeRangesToArray=function(e){for(var t=[],r=0;r<e.length;r++)t.push({start:e.start(r),end:e.end(r)});return t},e.bufferInfo=function(t,r,i){if(t){var a=e.bufferedRanges(t);if(a.length)return e.bufferedInfo(a,r,i)}return{len:0,start:r,end:r,bufferedIndex:-1}},e.bufferedInfo=function(e,t,r){t=Math.max(0,t),e.length>1&&e.sort((function(e,t){return e.start-t.start||t.end-e.end}));var i=-1,a=[];if(r)for(var n=0;n<e.length;n++){t>=e[n].start&&t<=e[n].end&&(i=n);var s=a.length;if(s){var o=a[s-1].end;e[n].start-o<r?e[n].end>o&&(a[s-1].end=e[n].end):a.push(e[n])}else a.push(e[n])}else a=e;for(var l,u=0,d=t,h=t,f=0;f<a.length;f++){var c=a[f].start,g=a[f].end;if(-1===i&&t>=c&&t<=g&&(i=f),t+r>=c&&t<g)d=c,u=(h=g)-t;else if(t+r<c){l=c;break}}return{len:u,start:d||0,end:h||0,nextStart:l,buffered:e,bufferedIndex:i}},e.getBuffered=function(e){try{return e.buffered||ur}catch(e){return fe.log("failed to get media.buffered",e),ur}},e}();function hr(e,t,r){fr(e,t,r),e.addEventListener(t,r)}function fr(e,t,r){e.removeEventListener(t,r)}function cr(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}var gr=/(avc[1234]|hvc1|hev1|dvh[1e]|vp09|av01)(?:\.[^.,]+)+/,vr="HlsJsTrackRemovedError",mr=function(e){function t(t){var r;return(r=e.call(this,t)||this).name=vr,r}return u(t,e),t}(v(Error)),pr=function(e){function t(t,r){var i,a;return(i=e.call(this,"buffer-controller",t.logger)||this).hls=void 0,i.fragmentTracker=void 0,i.fragmentAppendProgress=Object.create(null),i.appendsWithoutProgress=Object.create(null),i.details=null,i._objectUrl=null,i.operationQueue=null,i.bufferCodecEventsTotal=0,i.media=null,i.mediaSource=null,i.lastMpegAudioChunk=null,i.lastVideoAppendEnd=0,i.appendSource=void 0,i.transferData=void 0,i.overrides=void 0,i.appendErrors={audio:0,video:0,audiovideo:0},i.appendError=void 0,i._quotaEvictionPending={},i.tracks={},i.sourceBuffers=[[null,null],[null,null]],i._onEndStreaming=function(e){var t;i.hls&&"open"===(null==(t=i.mediaSource)?void 0:t.readyState)&&i.media&&!i.media.seeking&&i.hls.pauseBuffering()},i._onStartStreaming=function(e){i.hls&&i.hls.resumeBuffering()},i._onMediaSourceOpen=function(e){var t=i,r=t.media,a=t.mediaSource;e&&i.log("Media source opened"),r&&a&&(fr(a,"sourceopen",i._onMediaSourceOpen),fr(r,"emptied",i._onMediaEmptied),i.updateDuration(),i.hls.trigger(O.MEDIA_ATTACHED,{media:r,mediaSource:a}),null!==i.mediaSource&&i.checkPendingTracks())},i._onMediaSourceClose=function(){if(i.log("Media source closed"),i.media){var e=i.media.error,t=i,r=t.appendError,a=t.appendErrors,n=!1;!r&&e&&(n=Math.max(++a.audio,++a.video,++a.audiovideo)>=i.hls.config.appendErrorMaxRetry);var s=new Error("MediaSource closed while media attached"+Tr(n,e));i.warn(s),i.hls.trigger(O.ERROR,o({fatal:n},r,{type:x.MEDIA_ERROR,details:w.MEDIA_SOURCE_REQUIRES_RESET,error:s}))}},i._onMediaSourceEnded=function(){i.log("Media source ended")},i._onMediaEmptied=function(){var e=i,t=e.mediaSrc,r=e._objectUrl;t!==r&&i.error("Media element src was set while attaching MediaSource ("+r+" > "+t+")")},i._onMediaError=function(){var e,t,r=i.media;r&&i.log("Media error (code: "+(null==(e=r.error)?void 0:e.code)+"): "+(null==(t=r.error)?void 0:t.message))},i.hls=t,i.fragmentTracker=r,i.appendSource=(a=ve(t.config.preferManagedMediaSource),"undefined"!=typeof self&&a===self.ManagedMediaSource),i.initTracks(),i.registerListeners(),i}u(t,e);var r=t.prototype;return r.hasSourceTypes=function(){return Object.keys(this.tracks).length>0},r.destroy=function(){this.unregisterListeners(),this.details=null,this.lastMpegAudioChunk=null,this.transferData=this.overrides=void 0,this.operationQueue&&(this.operationQueue.destroy(),this.operationQueue=null),this.hls=this.fragmentTracker=null,this._onMediaSourceOpen=this._onMediaSourceClose=null,this._onMediaSourceEnded=null,this._onStartStreaming=this._onEndStreaming=null,this._onMediaEmptied=this._onMediaError=null},r.registerListeners=function(){var e=this.hls;e.on(O.MEDIA_ATTACHING,this.onMediaAttaching,this),e.on(O.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(O.MANIFEST_LOADING,this.onManifestLoading,this),e.on(O.MANIFEST_PARSED,this.onManifestParsed,this),e.on(O.BUFFER_RESET,this.onBufferReset,this),e.on(O.BUFFER_APPENDING,this.onBufferAppending,this),e.on(O.BUFFER_CODECS,this.onBufferCodecs,this),e.on(O.BUFFER_EOS,this.onBufferEos,this),e.on(O.BUFFER_FLUSHING,this.onBufferFlushing,this),e.on(O.LEVEL_UPDATED,this.onLevelUpdated,this),e.on(O.FRAG_PARSED,this.onFragParsed,this),e.on(O.FRAG_CHANGED,this.onFragChanged,this),e.on(O.ERROR,this.onError,this)},r.unregisterListeners=function(){var e=this.hls;e.off(O.MEDIA_ATTACHING,this.onMediaAttaching,this),e.off(O.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(O.MANIFEST_LOADING,this.onManifestLoading,this),e.off(O.MANIFEST_PARSED,this.onManifestParsed,this),e.off(O.BUFFER_RESET,this.onBufferReset,this),e.off(O.BUFFER_APPENDING,this.onBufferAppending,this),e.off(O.BUFFER_CODECS,this.onBufferCodecs,this),e.off(O.BUFFER_EOS,this.onBufferEos,this),e.off(O.BUFFER_FLUSHING,this.onBufferFlushing,this),e.off(O.LEVEL_UPDATED,this.onLevelUpdated,this),e.off(O.FRAG_PARSED,this.onFragParsed,this),e.off(O.FRAG_CHANGED,this.onFragChanged,this),e.off(O.ERROR,this.onError,this)},r.transferMedia=function(){var e=this,t=this.media,r=this.mediaSource;if(!t)return null;var i={};if(this.operationQueue){var a=this.isUpdating();a||this.operationQueue.removeBlockers();var n=this.isQueued();(a||n)&&this.warn("Transfering MediaSource with"+(n?" operations in queue":"")+(a?" updating SourceBuffer(s)":"")+" "+this.operationQueue),this.operationQueue.destroy()}var s=this.transferData;return s&&!this.sourceBufferCount&&s.mediaSource===r?o(i,s.tracks):this.sourceBuffers.forEach((function(t){var r=t[0];r&&(i[r]=o({},e.tracks[r]),e.removeBuffer(r)),t[0]=t[1]=null})),{media:t,mediaSource:r,tracks:i}},r.initTracks=function(){this.resetAppendProgress(),this.sourceBuffers=[[null,null],[null,null]],this.tracks={},this.resetQueue(),this.lastMpegAudioChunk=null,this.lastVideoAppendEnd=0},r.onManifestLoading=function(){this.bufferCodecEventsTotal=0,this.details=null,this.resetAppendErrors(),this.resetAppendProgress()},r.onManifestParsed=function(e,t){var r,i=2;(t.audio&&!t.video||!t.altAudio)&&(i=1),this.bufferCodecEventsTotal=i,this.log(i+" bufferCodec event(s) expected."),null!=(r=this.transferData)&&r.mediaSource&&this.sourceBufferCount&&i&&this.bufferCreated()},r.onMediaAttaching=function(e,t){var r=this.media=t.media;this.transferData=this.overrides=void 0;var i=ve(this.appendSource);if(i){var a=!!t.mediaSource;(a||t.overrides)&&(this.transferData=t,this.overrides=t.overrides);var n=this.mediaSource=t.mediaSource||new i;if(this.assignMediaSource(n),a)this._objectUrl=r.src,this.attachTransferred();else{var s=this._objectUrl=self.URL.createObjectURL(n);if(this.appendSource)try{r.removeAttribute("src");var o=self.ManagedMediaSource;r.disableRemotePlayback=r.disableRemotePlayback||o&&n instanceof o,yr(r),function(e,t){var r=self.document.createElement("source");r.type="video/mp4",r.src=t,e.appendChild(r)}(r,s),r.load()}catch(e){r.src=s}else r.src=s}hr(r,"emptied",this._onMediaEmptied),hr(r,"error",this._onMediaError),this.appendSource&&hr(r,"seeking",this._onStartStreaming)}},r.assignMediaSource=function(e){var t,r;this.log(((null==(t=this.transferData)?void 0:t.mediaSource)===e?"transferred":"created")+" media source: "+(null==(r=e.constructor)?void 0:r.name)),hr(e,"sourceopen",this._onMediaSourceOpen),hr(e,"sourceended",this._onMediaSourceEnded),hr(e,"sourceclose",this._onMediaSourceClose),this.appendSource&&(hr(e,"startstreaming",this._onStartStreaming),hr(e,"endstreaming",this._onEndStreaming))},r.attachTransferred=function(){var e=this,t=this.media,r=this.transferData;if(r&&t){var i=this.tracks,a=r.tracks,n=a?Object.keys(a):null,s=n?n.length:0,o=function(){Promise.resolve().then((function(){e.media&&e.mediaSourceOpenOrEnded&&e._onMediaSourceOpen()}))};if(a&&n&&s){if(!this.tracksReady)return this.hls.config.startFragPrefetch=!0,void this.log("attachTransferred: waiting for SourceBuffer track info");if(this.log("attachTransferred: (bufferCodecEventsTotal "+this.bufferCodecEventsTotal+")\nrequired tracks: "+Bt(i,(function(e,t){return"initSegment"===e?void 0:t}))+";\ntransfer tracks: "+Bt(a,(function(e,t){return"initSegment"===e?void 0:t}))+"}"),!function(e,t){var r=Object.keys(e),i=Object.keys(t),a=r.length,n=i.length;return!a||!n||a===n&&!r.some((function(e){return-1===i.indexOf(e)}))}(a,i)){r.mediaSource=null,r.tracks=void 0;var l=t.currentTime,u=this.details,d=Math.max(l,(null==u?void 0:u.fragments[0].start)||0);return d-l>1?void this.log("attachTransferred: waiting for playback to reach new tracks start time "+l+" -> "+d):(this.warn('attachTransferred: resetting MediaSource for incompatible tracks ("'+Object.keys(a)+'"->"'+Object.keys(i)+'") start time: '+d+" currentTime: "+l),this.onMediaDetaching(O.MEDIA_DETACHING,{}),this.onMediaAttaching(O.MEDIA_ATTACHING,r),void(t.currentTime=d))}this.transferData=void 0,n.forEach((function(t){var r=t,i=a[r];if(i){var n=i.buffer;if(n){var s=e.fragmentTracker,o=i.id;if(s.hasFragments(o)||s.hasParts(o)){var l=dr.getBuffered(n);s.detectEvictedFragments(r,l,o,null,null,!0)}var u=Er(r),d=[r,n];e.sourceBuffers[u]=d,n.updating&&e.operationQueue&&e.operationQueue.prependBlocker(r),e.trackSourceBuffer(r,i)}}})),o(),this.bufferCreated()}else this.log("attachTransferred: MediaSource w/o SourceBuffers"),o()}},r.onMediaDetaching=function(e,t){var r=this,i=!!t.transferMedia;this.transferData=this.overrides=void 0;var a=this.media,n=this.mediaSource,s=this._objectUrl;if(n){if(this.log("media source "+(i?"transferring":"detaching")),i)this.sourceBuffers.forEach((function(e){var t=e[0];t&&r.removeBuffer(t)})),this.resetQueue();else{if(this.mediaSourceOpenOrEnded){var o="open"===n.readyState;try{for(var l=n.sourceBuffers,u=l.length;u--;)o&&l[u].abort(),n.removeSourceBuffer(l[u]);o&&n.endOfStream()}catch(e){this.warn("onMediaDetaching: "+e.message+" while calling endOfStream")}}this.sourceBufferCount&&this.onBufferReset()}fr(n,"sourceopen",this._onMediaSourceOpen),fr(n,"sourceended",this._onMediaSourceEnded),fr(n,"sourceclose",this._onMediaSourceClose),this.appendSource&&(fr(n,"startstreaming",this._onStartStreaming),fr(n,"endstreaming",this._onEndStreaming)),this.mediaSource=null,this._objectUrl=null}a&&(fr(a,"emptied",this._onMediaEmptied),fr(a,"error",this._onMediaError),fr(a,"seeking",this._onStartStreaming),i||(s&&self.URL.revokeObjectURL(s),this.mediaSrc===s?(a.removeAttribute("src"),this.appendSource&&yr(a),a.load()):this.warn("media|source.src was changed by a third party - skip cleanup")),this.media=null)},r.onBufferReset=function(){var e=this;this.sourceBuffers.forEach((function(t){var r=t[0];r&&e.resetBuffer(r)})),this.initTracks()},r.resetBuffer=function(e){var t,r=null==(t=this.tracks[e])?void 0:t.buffer;if(this.removeBuffer(e),r)try{var i;null!=(i=this.mediaSource)&&i.sourceBuffers.length&&this.mediaSource.removeSourceBuffer(r)}catch(t){this.warn("onBufferReset "+e,t)}delete this.tracks[e]},r.removeBuffer=function(e){this.removeBufferListeners(e),this.sourceBuffers[Er(e)]=[null,null];var t=this.tracks[e];t&&(this.clearBufferAppendTimeoutId(t),t.buffer=void 0)},r.resetQueue=function(){this.operationQueue&&this.operationQueue.destroy(),this.operationQueue=new Kt(this.tracks)},r.onBufferCodecs=function(e,t){var r,i=this,a=this.tracks,n=Object.keys(t);this.log('BUFFER_CODECS: "'+n+'" (current SB count '+this.sourceBufferCount+")");var s="audiovideo"in t&&(a.audio||a.video)||a.audiovideo&&("audio"in t||"video"in t),o=!s&&this.sourceBufferCount&&this.media&&n.some((function(e){return!a[e]}));s||o?this.warn('Unsupported transition between "'+Object.keys(a)+'" and "'+n+'" SourceBuffers'):(n.forEach((function(e){var r,n,s=t[e],o=s.id,l=s.codec,u=s.levelCodec,d=s.container,h=s.metadata,f=s.supplemental,c=a[e],g=null==(r=i.transferData)||null==(r=r.tracks)?void 0:r[e],v=null!=g&&g.buffer?g:c,m=(null==v?void 0:v.pendingCodec)||(null==v?void 0:v.codec),p=null==v?void 0:v.levelCodec;c||(c=a[e]={buffer:void 0,listeners:[],codec:l,supplemental:f,container:d,levelCodec:u,metadata:h,id:o});var y=At(m,p),E=null==y?void 0:y.replace(gr,"$1"),T=At(l,u),S=null==(n=T)?void 0:n.replace(gr,"$1");T&&y&&E!==S&&("audio"===e.slice(0,5)&&(T=Rt(T,i.appendSource)),i.log("switching codec "+m+" to "+T),T!==(c.pendingCodec||c.codec)&&(c.pendingCodec=T),c.container=d,i.appendChangeType(e,d,T))})),(this.tracksReady||this.sourceBufferCount)&&(t.tracks=this.sourceBufferTracks),this.sourceBufferCount||(this.bufferCodecEventsTotal>1&&!this.tracks.video&&!t.video&&"main"===(null==(r=t.audio)?void 0:r.id)&&(this.log("Main audio-only"),this.bufferCodecEventsTotal=1),this.mediaSourceOpenOrEnded&&this.checkPendingTracks()))},r.appendChangeType=function(e,t,r){var i=this,a=t+";codecs="+r,n={label:"change-type="+a,execute:function(){var n=i.tracks[e];if(n){var s=n.buffer;null!=s&&s.changeType&&(i.log("changing "+e+" sourceBuffer type to "+a),s.changeType(a),n.codec=r,n.container=t)}i.shiftAndExecuteNext(e)},onStart:function(){},onComplete:function(){},onError:function(t){i.warn("Failed to change "+e+" SourceBuffer type",t)}};this.append(n,e,this.isPending(this.tracks[e]))},r.blockAudio=function(e){var t,r=this,i=e.start,a=i+.05*e.duration;if(!0!==(null==(t=this.fragmentTracker.getAppendedFrag(i,N.MAIN))?void 0:t.gap)){var n={label:"block-audio",execute:function(){var e,t;(r.lastVideoAppendEnd>a||dr.isBuffered(null==(e=r.tracks.video)?void 0:e.buffer,a)||!0===(null==(t=r.fragmentTracker.getAppendedFrag(a,N.MAIN))?void 0:t.gap))&&r.unblockAudio()},onStart:function(){},onComplete:function(){},onError:function(e){r.warn("Error executing block-audio operation",e)}};this.append(n,"audio",!0)}},r.unblockAudio=function(){this.operationQueue&&this.operationQueue.unblockAudio()},r.onBufferAppending=function(e,t){var r=this,i=this.tracks,a=t.data,n=t.type,s=t.parent,o=t.frag,l=t.part,u=t.chunkMeta,d=t.offset,h=u.buffering[n],f=G(o)&&!l&&!o.gap&&!u.partial&&!u.iframe,c=null,g=!1,v=o.sn,m=o.cc,p=self.performance.now();h.start=p;var y=o.stats.buffering,E=l?l.stats.buffering:null;0===y.start&&(y.start=p),0===(null==E?void 0:E.start)&&(E.start=p);var T=i.audio,S=!1;"audio"===n&&"audio/mpeg"===(null==T?void 0:T.container)&&(S=!this.lastMpegAudioChunk||1===u.id||this.lastMpegAudioChunk.sn!==u.sn,this.lastMpegAudioChunk=u);var L=i.video,R=null==L?void 0:L.buffer;if(R&&"initSegment"!==v&&void 0!==d){var A=null==T?void 0:T.buffer,b=l||o;if("audio"===n&&"main"!==s&&!L.ending&&!L.ended&&A&&dr.getBuffered(A).length){var k=b.start+.05*b.duration,D=dr.getBuffered(R),I=this.currentOp("video");D.length||I?!dr.isBuffered(R,k)&&this.lastVideoAppendEnd<k&&this.blockAudio(b):this.blockAudio(b)}else if("video"===n){var C=b.end,P=this.lastVideoAppendEnd-C;this.lastVideoAppendEnd=C,P<0||P>b.duration?this.unblockAudio():this.isAudioBlocked()&&this.executeNext("audio")}}var M=(l||o).start,F={label:"append-"+n,execute:function(){var e;h.executeStart=self.performance.now();var t=null==(e=r.tracks[n])?void 0:e.buffer;t&&(f&&(c=dr.timeRangesToArray(dr.getBuffered(t)),g=Rr(Lr(c,o),o)),S?r.updateTimestampOffset(t,M,.1,n,v,m):void 0!==d&&_(d)&&r.updateTimestampOffset(t,d,1e-6,n,v,m)),r.appendExecutor(a,n)},onStart:function(){},onComplete:function(){r.clearBufferAppendTimeoutId(r.tracks[n]);var e=self.performance.now();h.executeEnd=h.end=e,0===y.first&&(y.first=e),0===(null==E?void 0:E.first)&&(E.first=e);var t={};if(r.sourceBuffers.forEach((function(e){var r=e[0],i=e[1];r&&(t[r]=dr.getBuffered(i))})),f){var i=r.getFragmentAppendProgress(o),a=t[n];if(a&&c){var d=Lr(dr.timeRangesToArray(a),o);i.progressed||(i.progressed=d-Lr(c,o)>.001);var v=g&&Rr(d,o);i.fullyBuffered[n]=(void 0===i.fullyBuffered[n]||i.fullyBuffered[n])&&v}}r.hls.trigger(O.BUFFER_APPENDED,{type:n,frag:o,part:l,chunkMeta:u,parent:s,timeRanges:t})},onError:function(e){var t;r.clearBufferAppendTimeoutId(r.tracks[n]);var i=e.code===DOMException.QUOTA_EXCEEDED_ERR||"QuotaExceededError"==e.name||"quota"in e;if(i&&!r._quotaEvictionPending[n]){var d=r.getBackBufferEvictionTarget(n,a.byteLength,o.type);if(d>0){r._quotaEvictionPending[n]=!0,r.log('QuotaExceededError on "'+n+'" append sn: '+v+" - evicting back buffer to "+d.toFixed(3)+"s and retrying");var h=r.getFlushOp(n,0,d),c=r.getClearEvictionPendingOp(n);return void r.insertNext([h,F,c],n)}r.warn('QuotaExceededError on "'+n+'" sn: '+v+" - no back buffer available to evict")}f&&(r.getFragmentAppendProgress(o).errored=!0);var g={type:x.MEDIA_ERROR,parent:s,details:w.BUFFER_APPEND_ERROR,sourceBufferName:n,frag:o,part:l,chunkMeta:u,error:e,err:e,fatal:!1},m=null==(t=r.media)?void 0:t.error;if(e.name!==vr||0!==r.sourceBufferCount||r.media&&0!==r.pendingTrackCount){var p;i&&(g.details=w.BUFFER_FULL_ERROR);var y=++r.appendErrors[n],E=r.hls.config.appendErrorMaxRetry;r.warn("Failed "+y+"/"+(E+1)+' times to append segment in "'+n+'" sourceBuffer with error: '+e.message),y>=E&&(g.fatal=!i);var T=null==(p=r.mediaSource)?void 0:p.readyState;("ended"===T||"closed"===T||m)&&(r.warn('MediaSource readyState "'+T+'" during SourceBuffer'+Tr(g.fatal,m)),g.details=w.MEDIA_SOURCE_REQUIRES_RESET)}else g.errorAction=or(!0);r.appendError=g,r.hls.trigger(O.ERROR,g),i&&r.hls&&r.trimBuffers(o.start,1)}};this.log('queuing "'+n+'" append sn: '+v+(l?" p: "+l.index:"")+" of "+s+" playlist "+o.level+" cc: "+m+" offset: "+d+" bytes: "+a.byteLength),this.append(F,n,this.isPending(this.tracks[n]))},r.getClearEvictionPendingOp=function(e){var t=this;return{label:"clear",execute:function(){t._quotaEvictionPending[e]=!1,t.shiftAndExecuteNext(e)},onStart:function(){},onComplete:function(){},onError:function(){}}},r.getFlushOp=function(e,t,r){var i=this;return this.log('queuing "'+e+'" remove '+t+"-"+r),{label:"remove",execute:function(){i.removeExecutor(e,t,r)},onStart:function(){},onComplete:function(){var a,n=null==(a=i.tracks[e])?void 0:a.buffer;i.log("Remove request "+t+"-"+r+" from "+e+" Source Buffer complete > buffer: "+(n?cr(dr.getBuffered(n)):"(detached)")),i.hls.trigger(O.BUFFER_FLUSHED,{type:e,start:t,end:r})},onError:function(a){i.warn("Failed to remove "+t+"-"+r+' from "'+e+'" SourceBuffer',a),i.hls.trigger(O.BUFFER_FLUSHED,{type:e,start:0,end:0,error:a})}}},r.onBufferFlushing=function(e,t){var r=this,i=t.type,a=t.startOffset,n=t.endOffset;i&&"audio"!==i||this.unblockAudio(),i?this.append(this.getFlushOp(i,a,n),i):this.sourceBuffers.forEach((function(e){var t=e[0];t&&r.append(r.getFlushOp(t,a,n),t)}))},r.onFragParsed=function(e,t){var r=this,i=t.frag,a=t.part,n=t.chunkMeta,s=[],o=a?a.elementaryStreams:i.elementaryStreams;o[B.AUDIOVIDEO]?s.push("audiovideo"):(o[B.AUDIO]&&s.push("audio"),o[B.VIDEO]&&s.push("video")),0===s.length&&this.log("Fragments must have at least one ElementaryStreamType set. "+i.type+" "+i.level+" "+(a?"part: "+a.index+" of ":"")+"sn: "+i.sn),this.blockBuffers((function(){var e=self.performance.now();i.stats.buffering.end=e,a&&(a.stats.buffering.end=e);var t=a?a.stats:i.stats;r.checkAppendProgress(i,a,n,s),r.hls&&(r.hls.trigger(O.FRAG_BUFFERED,{frag:i,part:a,stats:t,id:i.type,chunkMeta:n}),!a&&i.gap&&t.retry&&r.log("Nothing buffered for "+i.type+" level: "+i.level+" sn: "+i.sn+" retries "+t.retry))}),s).catch((function(e){r.warn("Fragment buffered callback "+e.message),r.stepOperationQueue(r.sourceBufferTypes)}))},r.onFragChanged=function(e,t){var r,i,a=this,n=null==(r=this.hls)?void 0:r.config;if(n){var s=n.backBufferLength,o=n.frontBufferFlushThreshold;this.trimBuffers(o,s,t.frag,t.previousFrag);var l=t.frag.elementaryStreams,u=this.appendErrors,d=null==(i=this.appendError)?void 0:i.sourceBufferName;Object.keys(l).forEach((function(e){l[e]&&(u[e]=0,e===d&&(a.appendError=void 0),"audio"===e||"video"===e?(u.audiovideo=0,"audiovideo"===d&&(a.appendError=void 0)):(u.audio=0,u.video=0,"audiovideo"!==d&&(a.appendError=void 0)))}))}},r.onBufferEos=function(e,t){var r,i=this;this.sourceBuffers.forEach((function(e){var r=e[0];if(r){var a=i.tracks[r];t.type&&t.type!==r||(a.ending=!0,a.ended||(a.ended=!0,i.log(r+" buffer reached EOS")))}}));var a=!1!==(null==(r=this.overrides)?void 0:r.endOfStream);this.sourceBufferCount>0&&!this.sourceBuffers.some((function(e){var t,r=e[0];return r&&!(null!=(t=i.tracks[r])&&t.ended)}))?(this.unblockAudio(),a?(this.log("Queueing EOS"),this.blockUntilOpen((function(){i.tracksEnded();var e=i.mediaSource;"open"===(null==e?void 0:e.readyState)?(i.log("Calling mediaSource.endOfStream()"),e.endOfStream(),i.hls.trigger(O.BUFFERED_TO_END,void 0)):e&&i.log("Could not call mediaSource.endOfStream(). mediaSource.readyState: "+e.readyState)}))):(this.tracksEnded(),this.hls.trigger(O.BUFFERED_TO_END,void 0))):"video"===t.type&&this.unblockAudio()},r.tracksEnded=function(){var e=this;this.sourceBuffers.forEach((function(t){var r=t[0];if(null!==r){var i=e.tracks[r];i&&(i.ending=!1)}}))},r.onLevelUpdated=function(e,t){var r=t.details;r.fragments.length&&(this.details=r,this.updateDuration())},r.updateDuration=function(){var e=this;this.blockUntilOpen((function(){var t=e.getDurationAndRange();t&&e.updateMediaSource(t)}))},r.onError=function(e,t){if(t.details===w.BUFFER_APPEND_ERROR&&t.frag){var r,i=null==(r=t.errorAction)?void 0:r.nextAutoLevel;_(i)&&i!==t.frag.level&&this.resetAppendErrors()}},r.getBackBufferEvictionTarget=function(e,t,r){var i=this.media;return i?this.fragmentTracker.getBackBufferEvictionEnd(i.currentTime,r,t):0},r.getFragmentAppendProgress=function(e){var t,r=Sr(e),i=this.fragmentAppendProgress[r];return(null==(t=i)?void 0:t.stats)!==e.stats&&(i={stats:e.stats,progressed:!1,errored:!1,fullyBuffered:Object.create(null)},this.fragmentAppendProgress[r]=i),i},r.checkAppendProgress=function(e,t,r,i){if(!(t||!G(e)||e.gap||r.iframe||r.partial)){var a=Sr(e),n=this.fragmentAppendProgress[a];delete this.fragmentAppendProgress[a];var s=e.stats.buffering;if(!s.start||s.first){var o=(null==n?void 0:n.stats)===e.stats?n:void 0;if(null==o||!o.errored){var l=i.length>0&&i.every((function(e){return!0===(null==o?void 0:o.fullyBuffered[e])}));if(null!=o&&o.progressed||l)delete this.appendsWithoutProgress[a];else if(o||0!==i.length){var u=(this.appendsWithoutProgress[a]||0)+1,d=this.hls;u>=d.config.appendErrorMaxRetry?delete this.appendsWithoutProgress[a]:this.appendsWithoutProgress[a]=u,this.warn("Fragment "+e.sn+" of "+e.type+" playlist "+e.level+" appended "+u+" time"+(u>1?"s":"")+" without buffered range growth"),d.trigger(O.ERROR,{type:x.MEDIA_ERROR,details:w.BUFFER_APPEND_NO_PROGRESS,fatal:!1,frag:e,chunkMeta:r,parent:e.type,appendsWithoutProgress:u,error:new Error("Fragment append did not increase buffered coverage ("+u+")")})}}}}},r.resetAppendProgress=function(){this.fragmentAppendProgress=Object.create(null),this.appendsWithoutProgress=Object.create(null)},r.resetAppendErrors=function(){this.appendErrors={audio:0,video:0,audiovideo:0},this.appendError=void 0},r.trimBuffers=function(e,t,r,i){var a=this.hls,n=this.details,s=this.media;if(s&&null!==n&&this.sourceBufferCount){var o=a.config,l=s.currentTime,u=n.levelTargetDuration;t=n.live&&null!==o.liveBackBufferLength?o.liveBackBufferLength:t;var d=-1/0;if(_(t)&&t>=0){var h=Math.max(t,u);d=Math.floor(l/u)*u-h}if(r){var f=this.getLoopBackBufferFlushEnd(r,null!=i?i:null);f>0&&(d=Math.max(d,f))}if(d>0&&this.flushBackBuffer(l,u,d),_(e)&&e>0){var c=Math.max(o.maxBufferLength,e),g=Math.max(c,u),v=Math.floor(l/u)*u+g;this.flushFrontBuffer(l,u,v)}}},r.getLoopBackBufferFlushEnd=function(e,t){var r,i=this.media;if(!1===(null==(r=this.hls)?void 0:r.config.loopBackBufferFlush)||null==i||!i.loop||!t||e.level<=t.level)return 0;var a=e.elementaryStreams,n=a.video,s=a.audiovideo;if(null!=n&&n.partial||null!=s&&s.partial)return 0;var o=this.getEarliestElementaryStreamStart(e)-.25;return o<=0?0:(this.log("Flushing lower quality back buffer for loop: level "+e.level+", range [0-"+o.toFixed(3)+"]"),o)},r.getEarliestElementaryStreamStart=function(e){var t=e.elementaryStreams,r=t.audio,i=t.video,a=t.audiovideo,n=e.start;return a?n=Math.min(n,a.startDTS):(r&&(n=Math.min(n,r.startDTS)),i&&(n=Math.min(n,i.startDTS))),n},r.flushBackBuffer=function(e,t,r){var i=this;this.sourceBuffers.forEach((function(e){var t=e[0],a=e[1];if(a){var n=dr.getBuffered(a);if(n.length>0&&r>n.start(0)){var s,o,l;i.hls.trigger(O.BACK_BUFFER_REACHED,{bufferEnd:r});var u=i.tracks[t];if(null!=(s=i.details)&&s.live)i.hls.trigger(O.LIVE_BACK_BUFFER_REACHED,{bufferEnd:r});else if(null!=u&&u.ended&&(null==(o=i.media)||!o.loop||!1===(null==(l=i.hls)?void 0:l.config.loopBackBufferFlush)))return void i.log("Cannot flush "+t+" back buffer while SourceBuffer is in ended state");i.hls.trigger(O.BUFFER_FLUSHING,{startOffset:0,endOffset:r,type:t})}}}))},r.flushFrontBuffer=function(e,t,r){var i=this;this.sourceBuffers.forEach((function(t){var a=t[0],n=t[1];if(n){var s=dr.getBuffered(n),o=s.length;if(o<2)return;var l=s.start(o-1),u=s.end(o-1);if(r>l||e>=l&&e<=u)return;i.hls.trigger(O.BUFFER_FLUSHING,{startOffset:l,endOffset:1/0,type:a})}}))},r.getDurationAndRange=function(){var e,t=this.details,r=this.mediaSource;if(!t||!this.media||"open"!==(null==r?void 0:r.readyState))return null;var i=t.edge;if(t.live&&this.hls.config.liveDurationInfinity){if(t.fragments.length&&r.setLiveSeekableRange){var a=Math.max(0,t.fragmentStart);return{duration:1/0,start:a,end:Math.max(a,i)}}return{duration:1/0}}var n=null==(e=this.overrides)?void 0:e.duration;if(n)return _(n)?{duration:n}:null;var s=this.media.duration;return i>(_(r.duration)?r.duration:0)&&i>s||!_(s)?{duration:i}:null},r.updateMediaSource=function(e){var t=e.duration,r=e.start,i=e.end,a=this.mediaSource;a&&this.media&&"open"===a.readyState&&(a.duration!==t&&(_(t)&&this.log("Updating MediaSource duration to "+t.toFixed(3)),a.duration=t),void 0!==r&&void 0!==i&&(this.log("MediaSource duration is set to "+a.duration+". Setting seekable range to "+r+"-"+i+"."),a.setLiveSeekableRange(r,i)))},r.checkPendingTracks=function(){var e=this.bufferCodecEventsTotal,t=this.pendingTrackCount,r=this.tracks;if(this.log("checkPendingTracks (pending: "+t+" codec events expected: "+e+") "+Bt(r)),this.tracksReady){var i,a=null==(i=this.transferData)?void 0:i.tracks;a&&Object.keys(a).length?this.attachTransferred():this.createSourceBuffers()}},r.bufferCreated=function(){var e=this;if(this.sourceBufferCount){var t={};this.sourceBuffers.forEach((function(r){var i=r[0],a=r[1];if(i){var n=e.tracks[i];t[i]={buffer:a,container:n.container,codec:n.codec,supplemental:n.supplemental,levelCodec:n.levelCodec,id:n.id,metadata:n.metadata}}})),this.hls.trigger(O.BUFFER_CREATED,{tracks:t}),this.log("SourceBuffers created. Running queue: "+this.operationQueue),this.sourceBuffers.forEach((function(t){var r=t[0];e.executeNext(r)}))}else{var r=new Error("could not create source buffer for media codec(s)");this.hls.trigger(O.ERROR,{type:x.MEDIA_ERROR,details:w.BUFFER_INCOMPATIBLE_CODECS_ERROR,fatal:!0,error:r,reason:r.message})}},r.createSourceBuffers=function(){var e=this.tracks,t=this.sourceBuffers,r=this.mediaSource;if(!r)throw new Error("createSourceBuffers called when mediaSource was null");for(var i in e){var a=i,n=e[a];if(this.isPending(n)){var s=this.getTrackCodec(n,a),o=n.container+";codecs="+s;n.codec=s,this.log("creating sourceBuffer("+o+")"+(this.currentOp(a)?" Queued":"")+" "+Bt(n));try{var l=r.addSourceBuffer(o),u=Er(a),d=[a,l];t[u]=d,n.buffer=l}catch(e){var h;return this.error("error while trying to add sourceBuffer: "+e.message),this.shiftAndExecuteNext(a),null==(h=this.operationQueue)||h.removeBlockers(),delete this.tracks[a],void this.hls.trigger(O.ERROR,{type:x.MEDIA_ERROR,details:w.BUFFER_ADD_CODEC_ERROR,fatal:!1,error:e,sourceBufferName:a,mimeType:o,parent:n.id})}this.trackSourceBuffer(a,n)}}this.bufferCreated()},r.clearBufferAppendTimeoutId=function(e){e&&(self.clearTimeout(e.bufferAppendTimeoutId),e.bufferAppendTimeoutId=void 0)},r.getTrackCodec=function(e,t){var r=e.supplemental,i=e.codec;r&&("video"===t||"audiovideo"===t)&&mt(r,"video")&&(i=function(e,t){var r=[];if(e)for(var i=e.split(","),a=0;a<i.length;a++)vt(i[a],"video")||r.push(i[a]);return t&&r.push(t),r.join(",")}(i,r));var a=At(i,e.levelCodec);return a?"audio"===t.slice(0,5)?Rt(a,this.appendSource):a:""},r.trackSourceBuffer=function(e,t){var r=this,i=t.buffer;if(i){var a=this.getTrackCodec(t,e);this.tracks[e]={buffer:i,codec:a,container:t.container,levelCodec:t.levelCodec,supplemental:t.supplemental,metadata:t.metadata,id:t.id,listeners:[]},this.removeBufferListeners(e),this.addBufferListener(e,"updatestart",this.onSBUpdateStart),this.addBufferListener(e,"updateend",this.onSBUpdateEnd),this.addBufferListener(e,"error",this.onSBUpdateError),this.appendSource&&this.addBufferListener(e,"bufferedchange",(function(e,t){var i=t.removedRanges;null!=i&&i.length&&(r.log(e+" buffer removed "+cr(i)),r.hls.trigger(O.BUFFER_FLUSHED,{type:e,start:i.start(0),end:i.end(i.length-1)}))}))}},r.onSBUpdateStart=function(e){var t=this.currentOp(e);t&&t.onStart()},r.onSBUpdateEnd=function(e){var t,r,i=this;if("closed"!==(null==(t=this.mediaSource)?void 0:t.readyState)){var a=this.currentOp(e);if(a){if(a.onComplete(),null==(r=this.tracks[e])||null==(r=r.buffer)?void 0:r.updating)return this.log(e+' SourceBuffer updating on "updateend"'),void this.blockUntilOpen((function(){i.shiftAndExecuteNext(e)}));this.shiftAndExecuteNext(e)}}else this.resetBuffer(e)},r.onSBUpdateError=function(e,t){var r,i=null==(r=this.mediaSource)?void 0:r.readyState,a=new Error(e+" SourceBuffer error. MediaSource readyState: "+i);this.error(""+a.message,t),this.hls.trigger(O.ERROR,{type:x.MEDIA_ERROR,details:w.BUFFER_APPENDING_ERROR,sourceBufferName:e,error:a,fatal:!1});var n=this.currentOp(e);n&&n.onError(a)},r.updateTimestampOffset=function(e,t,r,i,a,n){var s=t-e.timestampOffset;Math.abs(s)>=r&&(this.log("Updating "+i+" SourceBuffer timestampOffset to "+t+" (sn: "+a+" cc: "+n+")"),e.timestampOffset=t)},r.removeExecutor=function(e,t,r){var i=this.media,a=this.mediaSource,n=this.tracks[e],s=null==n?void 0:n.buffer;if(!i||!a||!s)throw new Error("Attempting to remove from the "+e+" SourceBuffer, but it does not exist");var o,l=_(i.duration)?i.duration:1/0,u=_(a.duration)?a.duration:1/0,d=Math.max(0,t);if(d>=u)return null==(o=this.currentOp(e))||o.onComplete(),void this.shiftAndExecuteNext(e);var h=Math.min(r,l,u);if(!(h>d)||n.ending&&!n.ended)throw new Error("Cannot remove "+(h<=d?"invalid range ("+d+" >= "+h+") ":"")+"from the "+e+" SourceBuffer"+(n.ending?" while track ending":""));n.ended=!1,this.log("Removing ["+d+","+h+"] from the "+e+" SourceBuffer"),s.remove(d,h)},r.appendExecutor=function(e,t){var r=this,i=this.tracks[t],a=null==i?void 0:i.buffer;if(!a)throw new mr("Attempting to append to the "+t+" SourceBuffer, but it does not exist");if(i.ending=!1,i.ended=!1,this.hls.config.appendTimeout!==1/0){var n=this.calculateAppendTimeoutTime(a);i.bufferAppendTimeoutId=self.setTimeout((function(){return r.appendTimeoutHandler(t,a,n)}),n)}a.appendBuffer(e)},r.appendTimeoutHandler=function(e,t,r){this.log("Received timeout after "+r+"ms for append on "+e+" source buffer. Aborting and triggering error.");try{t.abort()}catch(t){this.log("Failed to abort append on "+e+" source buffer after timeout.")}var i=this.currentOp(e);i&&i.onError(new Error(e+"-append-timeout"))},r.calculateAppendTimeoutTime=function(e){var t=10;this.details&&(t=this.details.levelTargetDuration);var r=2*t*1e3;if(null===this.media)return r;var i=dr.bufferInfo(e,this.media.currentTime,0);return i.len?(r=Math.max(1e3*i.len,r),Math.max(this.hls.config.appendTimeout,r)):r},r.blockUntilOpen=function(e){var t=this;if(this.isUpdating()||this.isQueued())this.blockBuffers(e).catch((function(e){t.warn("SourceBuffer blocked callback "+e),t.stepOperationQueue(t.sourceBufferTypes)}));else try{e()}catch(e){this.warn("Callback run without blocking "+this.operationQueue+" "+e)}},r.isUpdating=function(){return this.sourceBuffers.some((function(e){var t=e[0],r=e[1];return t&&r.updating}))},r.isQueued=function(){var e=this;return this.sourceBuffers.some((function(t){var r=t[0];return r&&!!e.currentOp(r)}))},r.isPending=function(e){return!!e&&!e.buffer},r.isAudioBlocked=function(){var e;return"block-audio"===(null==(e=this.currentOp("audio"))?void 0:e.label)},r.isAudioBlocking=function(){return!!this.operationQueue&&this.operationQueue.audioBlocking()},r.blockBuffers=function(e,t){var r=this;if(void 0===t&&(t=this.sourceBufferTypes),!t.length)return this.log("Blocking operation requested, but no SourceBuffers exist"),Promise.resolve().then(e);var i=this.operationQueue,a=2===t.length&&this.isAudioBlocking()?[this.appendBlocker("video")]:t.map((function(e){return r.appendBlocker(e)}));return Promise.all(a).then((function(t){i===r.operationQueue&&(e(),r.stepOperationQueue(r.sourceBufferTypes))}))},r.stepOperationQueue=function(e){var t=this;e.forEach((function(e){var r,i=null==(r=t.tracks[e])?void 0:r.buffer;!i||i.updating||"audio"===e&&t.isAudioBlocked()||t.shiftAndExecuteNext(e)}))},r.append=function(e,t,r){this.operationQueue&&this.operationQueue.append(e,t,r)},r.insertNext=function(e,t){this.operationQueue&&this.operationQueue.insertNext(e,t)},r.appendBlocker=function(e){if(this.operationQueue)return this.operationQueue.appendBlocker(e)},r.currentOp=function(e){return this.operationQueue?this.operationQueue.current(e):null},r.executeNext=function(e){e&&this.operationQueue&&this.operationQueue.executeNext(e)},r.shiftAndExecuteNext=function(e){this.operationQueue&&this.operationQueue.shiftAndExecuteNext(e)},r.addBufferListener=function(e,t,r){var i=this.tracks[e];if(i){var a=i.buffer;if(a){var n=r.bind(this,e);i.listeners.push({event:t,listener:n}),hr(a,t,n)}}},r.removeBufferListeners=function(e){var t=this.tracks[e];if(t){var r=t.buffer;r&&(t.listeners.forEach((function(e){fr(r,e.event,e.listener)})),t.listeners.length=0)}},a(t,[{key:"mediaSourceOpenOrEnded",get:function(){var e,t=null==(e=this.mediaSource)?void 0:e.readyState;return"open"===t||"ended"===t}},{key:"sourceBufferTracks",get:function(){var e=this;return Object.keys(this.tracks).reduce((function(t,r){var i=e.tracks[r];return t[r]={id:i.id,container:i.container,codec:i.codec,levelCodec:i.levelCodec},t}),{})}},{key:"bufferedToEnd",get:function(){var e=this;return this.sourceBufferCount>0&&!this.sourceBuffers.some((function(t){var r=t[0];if(r){var i=e.tracks[r];if(i)return!i.ended||i.ending}return!1}))}},{key:"tracksReady",get:function(){var e=this.pendingTrackCount;return e>0&&(e>=this.bufferCodecEventsTotal||this.isPending(this.tracks.audiovideo))}},{key:"mediaSrc",get:function(){var e,t,r=(null==(e=this.media)||null==(t=e.querySelector)?void 0:t.call(e,"source"))||this.media;return null==r?void 0:r.src}},{key:"pendingTrackCount",get:function(){var e=this;return Object.keys(this.tracks).reduce((function(t,r){return t+(e.isPending(e.tracks[r])?1:0)}),0)}},{key:"sourceBufferCount",get:function(){return this.sourceBuffers.reduce((function(e,t){return e+(t[0]?1:0)}),0)}},{key:"sourceBufferTypes",get:function(){return this.sourceBuffers.map((function(e){return e[0]})).filter((function(e){return!!e}))}}])}(ie);function yr(e){var t=e.querySelectorAll("source");[].slice.call(t).forEach((function(t){e.removeChild(t)}))}function Er(e){return"audio"===e?1:0}function Tr(e,t){return(e?"":" - triggering recovery")+(t?" with HTMLMediaElement Error: "+t.message:"")}function Sr(e){return e.type+"_"+e.sn+"_"+e.level}function Lr(e,t){for(var r=t.start,i=t.end,a=0,n=0;n<e.length;n++){var s=e[n];if(s.start>=i)break;var o=Math.max(r,s.start),l=Math.min(i,s.end);l>o&&(a+=l-o)}return a}function Rr(e,t){return t.duration-e<=.05}var Ar=function(){function e(e){this.hls=null,this.autoLevelCapping=void 0,this.media=void 0,this.restrictedLevels=void 0,this.timer=void 0,this.observer=void 0,this.clientRect=void 0,this.streamController=void 0,this.hls=e,this.autoLevelCapping=Number.POSITIVE_INFINITY,this.media=null,this.restrictedLevels=[],this.clientRect=null,this.registerListeners()}var t=e.prototype;return t.setStreamController=function(e){this.streamController=e},t.destroy=function(){this.hls&&this.unregisterListener(),(this.timer||this.observer)&&this.stopCapping(),this.media=this.clientRect=this.hls=null,this.streamController=void 0},t.registerListeners=function(){var e=this.hls;e&&(e.on(O.FPS_DROP_LEVEL_CAPPING,this.onFpsDropLevelCapping,this),e.on(O.MEDIA_ATTACHING,this.onMediaAttaching,this),e.on(O.MANIFEST_PARSED,this.onManifestParsed,this),e.on(O.LEVELS_UPDATED,this.onLevelsUpdated,this),e.on(O.BUFFER_CODECS,this.onBufferCodecs,this),e.on(O.MEDIA_DETACHING,this.onMediaDetaching,this))},t.unregisterListener=function(){var e=this.hls;e&&(e.off(O.FPS_DROP_LEVEL_CAPPING,this.onFpsDropLevelCapping,this),e.off(O.MEDIA_ATTACHING,this.onMediaAttaching,this),e.off(O.MANIFEST_PARSED,this.onManifestParsed,this),e.off(O.LEVELS_UPDATED,this.onLevelsUpdated,this),e.off(O.BUFFER_CODECS,this.onBufferCodecs,this),e.off(O.MEDIA_DETACHING,this.onMediaDetaching,this))},t.onFpsDropLevelCapping=function(e,t){if(this.hls){var r=this.hls.levels[t.droppedLevel];this.isLevelAllowed(r)&&this.restrictedLevels.push({bitrate:r.bitrate,height:r.height,width:r.width})}},t.onMediaAttaching=function(e,t){var r=t.media;this.clientRect=null,this.hls&&(r instanceof HTMLVideoElement?(this.media=r,this.hls.config.capLevelToPlayerSize&&this.observe()):this.media=null,(this.timer||this.observer)&&this.hls.levels.length>1&&this.detectPlayerSize())},t.onManifestParsed=function(e,t){this.restrictedLevels=[],t.video&&this.startCapping(),this.onLevelsUpdated()},t.onLevelsUpdated=function(){if(this.hls)if((this.timer||this.observer)&&(_(this.autoLevelCapping)||this.clientRect))this.detectPlayerSize();else if(void 0===this.observer&&void 0===this.timer){var e=this.hls.levels;e.length>1&&e.some((function(e){return!!e.videoCodec}))&&this.startCapping()}},t.onBufferCodecs=function(e,t){t.video&&this.startCapping()},t.onMediaDetaching=function(){this.stopCapping(),this.media=null},t.detectPlayerSize=function(){if(this.media){if(this.mediaHeight<=0||this.mediaWidth<=0||!this.hls)return void(this.clientRect=null);var e=this.hls.levels;if(e.length>1){var t=this.hls,r=this.getMaxLevel(e.length-1);r!==this.autoLevelCapping&&t.logger.log("Setting autoLevelCapping to "+r+": "+e[r].height+"p@"+e[r].bitrate+" for media "+this.mediaWidth+"x"+this.mediaHeight),t.autoLevelCapping=r,t.autoLevelEnabled&&t.autoLevelCapping>this.autoLevelCapping&&this.streamController&&this.streamController.nextLevelSwitch(),this.autoLevelCapping=t.autoLevelCapping}else 1===e.length&&this.stopCapping()}},t.getMaxLevel=function(t){var r=this;if(!this.hls)return-1;var i=this.hls.levels;if(!i.length)return-1;var a=i.filter((function(e,i){return r.isLevelAllowed(e)&&i<=t}));return this.observer||(this.clientRect=null),e.getMaxLevelByMediaSize(a,this.mediaWidth,this.mediaHeight)},t.observe=function(){var e=this,t=self.ResizeObserver;t&&(this.observer=new t((function(t){var r,i=null==(r=t[0])?void 0:r.contentRect;i&&(e.clientRect=i,e.detectPlayerSize())}))),this.observer&&this.media&&this.observer.observe(this.media)},t.startCapping=function(){var e;this.timer||this.observer||null==(e=this.hls)||!e.config.capLevelToPlayerSize||(self.clearInterval(this.timer),this.timer=void 0,this.autoLevelCapping=Number.POSITIVE_INFINITY,this.observe(),this.observer||(this.timer=self.setInterval(this.detectPlayerSize.bind(this),1e3)),this.detectPlayerSize())},t.stopCapping=function(){this.restrictedLevels=[],this.autoLevelCapping=Number.POSITIVE_INFINITY,this.timer&&(self.clearInterval(this.timer),this.timer=void 0),this.observer&&(this.observer.disconnect(),this.observer=void 0)},t.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},t.isLevelAllowed=function(e){return!this.restrictedLevels.some((function(t){return e.bitrate===t.bitrate&&e.width===t.width&&e.height===t.height}))},e.getMaxLevelByMediaSize=function(e,t,r){if(null==e||!e.length)return-1;for(var i,a,n=e.length-1,s=Math.max(t,r),o=0;o<e.length;o+=1){var l=e[o];if((l.width>=s||l.height>=s)&&(i=l,!(a=e[o+1])||i.width!==a.width||i.height!==a.height)){n=o;break}}return n},a(e,[{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)return e;var t=this.hls.config,r=t.ignoreDevicePixelRatio,i=t.maxDevicePixelRatio;if(!r)try{e=self.devicePixelRatio}catch(e){}return Math.min(e,i)}}])}(),br=/^(\d+)x(\d+)$/,kr=/(.+?)=(".*?"|.*?)(?:,|$)/g,Dr=function(){function e(t,r){"string"==typeof t&&(t=e.parseAttrList(t,r)),o(this,t)}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.enumeratedStringList=function(e,t){var r=this[e];return(r?r.split(/[ ,]+/):[]).reduce((function(e,t){return e[t.toLowerCase()]=!0,e}),t)},t.bool=function(e){return"YES"===this[e]},t.decimalResolution=function(e){var t=br.exec(this[e]);if(null!==t)return{width:parseInt(t[1],10),height:parseInt(t[2],10)}},e.parseAttrList=function(e,t){var r,i={};for(kr.lastIndex=0;null!==(r=kr.exec(e));){var a=r[1].trim(),n=r[2],s=0===n.indexOf('"')&&n.lastIndexOf('"')===n.length-1,o=!1;if(s)n=n.slice(1,-1);else switch(a){case"IV":case"SCTE35-CMD":case"SCTE35-IN":case"SCTE35-OUT":o=!0}if(t&&(s||o));else if(!o&&!s)switch(a){case"CLOSED-CAPTIONS":if("NONE"===n)break;case"ALLOWED-CPC":case"CLASS":case"ASSOC-LANGUAGE":case"AUDIO":case"BYTERANGE":case"CHANNELS":case"CHARACTERISTICS":case"CODECS":case"DATA-ID":case"END-DATE":case"GROUP-ID":case"ID":case"IMPORT":case"INSTREAM-ID":case"KEYFORMAT":case"KEYFORMATVERSIONS":case"LANGUAGE":case"NAME":case"PATHWAY-ID":case"QUERYPARAM":case"RECENTLY-REMOVED-DATERANGES":case"SERVER-URI":case"STABLE-RENDITION-ID":case"STABLE-VARIANT-ID":case"START-DATE":case"SUBTITLES":case"SUPPLEMENTAL-CODECS":case"URI":case"VALUE":case"VIDEO":case"X-ASSET-LIST":case"X-ASSET-URI":fe.warn(e+": attribute "+a+" is missing quotes")}i[a]=n}return i},a(e,[{key:"clientAttrs",get:function(){return Object.keys(this).filter((function(e){return"X-"===e.substring(0,2)}))}}])}();function Ir(e){return"SCTE35-OUT"===e||"SCTE35-IN"===e||"SCTE35-CMD"===e}var _r=function(){return a((function(e,t,r){var i;if(void 0===r&&(r=0),this.attr=void 0,this.tagAnchor=void 0,this.tagOrder=void 0,this._startDate=void 0,this._endDate=void 0,this._dateAtEnd=void 0,this._cue=void 0,this._badValueForSameId=void 0,this.tagAnchor=(null==t?void 0:t.tagAnchor)||null,this.tagOrder=null!=(i=null==t?void 0:t.tagOrder)?i:r,t){var a=t.attr;for(var n in a)if(Object.prototype.hasOwnProperty.call(e,n)&&e[n]!==a[n]){fe.warn('DATERANGE tag attribute: "'+n+'" does not match for tags with ID: "'+e.ID+'"'),this._badValueForSameId=n;break}e=o(new Dr({}),a,e)}if(this.attr=e,t?(this._startDate=t._startDate,this._cue=t._cue,this._endDate=t._endDate,this._dateAtEnd=t._dateAtEnd):this._startDate=new Date(e["START-DATE"]),"END-DATE"in e){var s=(null==t?void 0:t.endDate)||new Date(e["END-DATE"]);_(s.getTime())&&(this._endDate=s)}}),[{key:"id",get:function(){return this.attr.ID}},{key:"class",get:function(){return this.attr.CLASS}},{key:"cue",get:function(){var e=this._cue;return void 0===e?this._cue=this.attr.enumeratedStringList(this.attr.CUE?"CUE":"X-CUE",{pre:!1,post:!1,once:!1}):e}},{key:"startTime",get:function(){var e=this.tagAnchor;return null===e||null===e.programDateTime?(fe.warn('Expected tagAnchor Fragment with PDT set for DateRange "'+this.id+'": '+e),NaN):e.start+(this.startDate.getTime()-e.programDateTime)/1e3}},{key:"startDate",get:function(){return this._startDate}},{key:"endDate",get:function(){var e=this._endDate||this._dateAtEnd;if(e)return e;var t=this.duration;return null!==t?this._dateAtEnd=new Date(this._startDate.getTime()+1e3*t):null}},{key:"duration",get:function(){if("DURATION"in this.attr){var e=this.attr.decimalFloatingPoint("DURATION");if(_(e))return e}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:"isInterstitial",get:function(){return"com.apple.hls.interstitial"===this.class}},{key:"isValid",get:function(){return!!this.id&&!this._badValueForSameId&&_(this.startDate.getTime())&&(null===this.duration||this.duration>=0)&&(!this.endOnNext||!!this.class)&&(!this.attr.CUE||!this.cue.pre&&!this.cue.post||this.cue.pre!==this.cue.post)&&(!this.isInterstitial||"X-ASSET-URI"in this.attr||"X-ASSET-LIST"in this.attr)}},{key:"invalidReason",get:function(){return this.id?this._badValueForSameId?"Date range "+this._badValueForSameId+" mismatch":_(this.startDate.getTime())?!this.isInterstitial||"X-ASSET-URI"in this.attr||"X-ASSET-LIST"in this.attr?this.isValid?null:"Unknown (check DURATION(|END|NEXT), CLASS, and CUE)":"Interstitial Date range missing X-ASSET-(LIST|URI)":"Date range invalid start date":"Missing ID"}}])}();function Cr(e,t){return e.length===t.length&&!e.some((function(e,r){return e!==t[r]}))}function Pr(e,t){return!e&&!t||!(!e||!t)&&Cr(e,t)}var xr=0,wr=1;function Or(e){return"AES-128"===e||"AES-256"===e||"AES-256-CTR"===e}function Mr(e){switch(e){case"AES-128":case"AES-256":return xr;case"AES-256-CTR":return wr;default:throw new Error("invalid full segment method "+e)}}var Fr={},Nr=function(){function e(e,t,r,i,a,n){void 0===i&&(i=[1]),void 0===a&&(a=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.scheme=void 0,this.method=e,this.uri=t,this.keyFormat=r,this.keyFormatVersions=i,this.iv=a,this.encrypted=!!e&&"NONE"!==e,this.isCommonEncryption=this.encrypted&&!Or(e),null!=n&&n.startsWith("0x")&&(this.keyId=new Uint8Array(we(n)))}e.clearKeyUriToKeyIdMap=function(){Fr={}},e.setKeyIdForUri=function(e,t){Fr[e]=t},e.addKeyIdForUri=function(e){var t=Object.keys(Fr).length%Number.MAX_SAFE_INTEGER,r=new Uint8Array(16);return new DataView(r.buffer,12,4).setUint32(0,t),Fr[e]=r,r};var t=e.prototype;return t.matches=function(e){return e.uri===this.uri&&e.method===this.method&&e.encrypted===this.encrypted&&e.keyFormat===this.keyFormat&&Cr(e.keyFormatVersions,this.keyFormatVersions)&&Pr(e.iv,this.iv)&&Pr(e.keyId,this.keyId)},t.isSupported=function(){if(this.method){if(Or(this.method)||"NONE"===this.method)return!0;if("identity"===this.keyFormat)return"SAMPLE-AES"===this.method}return!1},t.getDecryptData=function(t,r){if(!this.encrypted||!this.uri)return null;if(Or(this.method)){var i=this.iv;return i||("number"!=typeof t&&(fe.warn('missing IV for initialization segment with method="'+this.method+'" - compliance issue'),t=0),i=function(e){for(var t=new Uint8Array(16),r=12;r<16;r++)t[r]=e>>8*(15-r)&255;return t}(t)),new e(this.method,this.uri,"identity",this.keyFormatVersions,i)}return this},e}(),Br=/#EXT-X-STREAM-INF:([^\r\n]*)(?:[\r\n](?:#[^\r\n]*)?)*([^\r\n]+)|#EXT-X-(I-FRAME-STREAM-INF|SESSION-DATA|SESSION-KEY|DEFINE|CONTENT-STEERING|START):([^\r\n]*)(?:[\r\n]+|$)/g,Ur=/#EXT-X-MEDIA:(.*)/g,Gr=/^#EXT(?:INF|-X-TARGETDURATION):/m,Vr=new RegExp([/#EXTINF:\s*(\d*(?:\.\d+)?)(?:,(.*)\s+)?/.source,/(?!#) *(\S[^\r\n]*)/.source,/#.*/.source].join("|"),"g"),Hr=new RegExp([/#EXT-X-(PROGRAM-DATE-TIME|BYTERANGE|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|I-FRAMES-ONLY)/.source,/(#)([^:]*):(.*)/.source,/(#)(.*)(?:.*)\r?\n?/.source].join("|")),Kr=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.resolve=function(e,t){return I.buildAbsoluteURL(t,e,{alwaysNormalize:!0})},e.isMediaPlaylist=function(e){return Gr.test(e)},e.parseMasterPlaylist=function(t,r){var i,a={contentSteering:null,levels:[],iframeVariants:[],playlistParsingError:null,sessionData:null,sessionKeys:null,startTimeOffset:null,variableList:null,hasVariableRefs:!1},n=[];if(Br.lastIndex=0,!t.startsWith("#EXTM3U"))return a.playlistParsingError=new Error("no EXTM3U delimiter"),a;for(;null!=(i=Br.exec(t));)if(i[1]){var s=Yr(new Dr(i[1],a),i[2],r,0,n);s&&a.levels.push(s)}else if(i[3]){var o=i[3],l=i[4];switch(o){case"I-FRAME-STREAM-INF":var u=new Dr(l,a),d=Yr(u,u.URI,r,0,n,!0);d&&a.iframeVariants.push(d);break;case"SESSION-DATA":var h=new Dr(l,a),f=h["DATA-ID"];f&&(null===a.sessionData&&(a.sessionData={}),a.sessionData[f]=h);break;case"SESSION-KEY":var c=qr(l,r,a);c.encrypted&&c.isSupported()?(null===a.sessionKeys&&(a.sessionKeys=[]),a.sessionKeys.push(c)):fe.warn('[Keys] Ignoring invalid EXT-X-SESSION-KEY tag: "'+l+'"');break;case"DEFINE":break;case"CONTENT-STEERING":var g=new Dr(l,a);a.contentSteering={uri:e.resolve(g["SERVER-URI"],r),pathwayId:g["PATHWAY-ID"]||"."};break;case"START":a.startTimeOffset=Xr(l)}}var v=n.length>0&&n.length<a.levels.length;return a.levels=v?n:a.levels,0===a.levels.length&&(a.playlistParsingError=new Error("no levels found in manifest")),a},e.parseMasterPlaylistMedia=function(t,r,i){var a,n={},s=i.levels,o={AUDIO:s.map((function(e){return{id:e.attrs.AUDIO,audioCodec:e.audioCodec}})),SUBTITLES:s.map((function(e){return{id:e.attrs.SUBTITLES,textCodec:e.textCodec}})),"CLOSED-CAPTIONS":[]},l=0;for(Ur.lastIndex=0;null!==(a=Ur.exec(t));){var u=new Dr(a[1],i),d=u.TYPE;if(d){var h=o[d],f=n[d]||[];n[d]=f;var c=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||c||"",type:d,default:u.bool("DEFAULT"),autoselect:u.bool("AUTOSELECT"),forced:u.bool("FORCED"),lang:c,url:u.URI?e.resolve(u.URI,r):""};if(g&&(y.assocLang=g),v&&(y.channels=v),m&&(y.characteristics=m),p&&(y.instreamId=p),null!=h&&h.length){var E=e.findGroup(h,y.groupId)||h[0];Qr(y,E,"audioCodec"),Qr(y,E,"textCodec")}f.push(y)}}return n},e.parseLevelPlaylist=function(e,t,r,i,a,n){var s,l,u,d,h,f={url:t},c=new lr(t),g=c.fragments,v=[],m=null,p=0,y=0,E=0,T=0,S=0,L=null,R=new K(i,f),A=-1,b=!1,k=null;if(Vr.lastIndex=0,c.m3u8=e,c.hasVariableRefs=!1,"#EXTM3U"!==(null==(s=Vr.exec(e))?void 0:s[0]))return c.playlistParsingError=new Error("Missing format identifier #EXTM3U"),c;for(;null!==(l=Vr.exec(e));){b&&(b=!1,(R=new K(i,f)).playlistOffset=E,R.setStart(E),R.sn=p,R.cc=T,S&&(R.bitrate=S),R.level=r,m&&(R.initSegment=m,m.rawProgramDateTime&&(R.rawProgramDateTime=m.rawProgramDateTime,m.rawProgramDateTime=null),k&&(R.setByteRange(k),k=null)));var D=l[1];if(D){R.duration=parseFloat(D);var I=(" "+l[2]).slice(1);R.title=I||null,R.tagList.push(I?["INF",D,I]:["INF",D])}else if(l[3]){if(_(R.duration)){var C;R.playlistOffset=E,R.setStart(E),d&&Jr(R,d,c),R.sn=p,R.level=r,R.cc=T;var P=(" "+l[3]).slice(1);R.relurl=P,$r(R,L,v),L=R,E+=R.duration,p++,y=0;var x=R.byteRange;if(2===x.length&&(R.bitrate=8*(x[1]-x[0])/R.duration|0),c.iframesOnly&&x[0]&&(null==(C=m)?void 0:C.cc)!==T){var w=new K(i,f);w.relurl=R.relurl,w.setByteRange(Math.min(x[0],1316)+"@0"),w.level=r,w.sn="initSegment",d&&(w.levelkeys=d,d.identity&&(w._decryptdata=d.identity.getDecryptData(0))),m=w,R.initSegment=m}g.push(R),b=!0}}else{if(!(l=l[0].match(Hr))){fe.warn("No matches on slow regex match for level playlist!");continue}for(u=1;u<l.length&&void 0===l[u];u++);var O=(" "+l[u]).slice(1),M=(" "+l[u+1]).slice(1),F=l[u+2]?(" "+l[u+2]).slice(1):null;switch(O){case"BYTERANGE":L?R.setByteRange(M,L):R.setByteRange(M);break;case"PROGRAM-DATE-TIME":R.rawProgramDateTime=M,R.tagList.push(["PROGRAM-DATE-TIME",M]),-1===A&&(A=g.length);break;case"PLAYLIST-TYPE":c.type&&ei(c,O,l),c.type=M.toUpperCase();break;case"MEDIA-SEQUENCE":0!==c.startSN?ei(c,O,l):null!==L&&ti(c,O,l),c.startSN=parseInt(M),p=c.startSN+c.skippedSegments;break;case"SKIP":c.skippedSegments&&ei(c,O,l);var N=new Dr(M,c),B=N.decimalInteger("SKIPPED-SEGMENTS");if(_(B)){c.skippedSegments+=B;for(var U=B;U--;)g.push(null);p+=B}var G=N.enumeratedString("RECENTLY-REMOVED-DATERANGES");G&&(c.recentlyRemovedDateranges=(c.recentlyRemovedDateranges||[]).concat(G.split("\t")));break;case"TARGETDURATION":0!==c.targetduration&&ei(c,O,l),c.targetduration=Math.max(parseInt(M),1);break;case"VERSION":null!==c.version&&ei(c,O,l),c.version=parseInt(M);break;case"INDEPENDENT-SEGMENTS":case"DEFINE":break;case"I-FRAMES-ONLY":c.iframesOnly=!0;break;case"ENDLIST":c.live||ei(c,O,l),c.live=!1;break;case"#":(M||F)&&R.tagList.push(F?[M,F]:[M]);break;case"DISCONTINUITY":T++,R.tagList.push(["DIS"]);break;case"GAP":R.gap=!0,R.tagList.push([O]);break;case"BITRATE":R.tagList.push([O,M]),S=1e3*parseInt(M),_(S)?R.bitrate=S:S=0;break;case"DATERANGE":var V=new Dr(M,c),H=new _r(V,c.dateRanges[V.ID],c.dateRangeTagCount);c.dateRangeTagCount++,H.isValid||c.skippedSegments?c.dateRanges[H.id]=H:fe.log("Ignoring invalid DATERANGE tag: "+H.invalidReason+": "+M),R.tagList.push(["EXT-X-DATERANGE",M]);break;case"DISCONTINUITY-SEQUENCE":0!==c.startCC&&ei(c,O,l),c.startCC=T=parseInt(M),g.forEach((function(e){return e&&(e.cc=T)}));break;case"KEY":var W=qr(M,t,c);if(W.isSupported()){if("NONE"===W.method){d=void 0;break}d||(d={});var j=d[W.keyFormat];null!=j&&j.matches(W)||(j&&(d=o({},d)),d[W.keyFormat]=W)}else fe.warn('[Keys] Ignoring unsupported EXT-X-KEY tag: "'+M+'" (light build)');break;case"START":c.startTimeOffset=Xr(M);break;case"MAP":var q=new Dr(M,c);if(R.duration){var X=new K(i,f);Zr(X,q,r,d),m=X,R.initSegment=m,m.rawProgramDateTime&&!R.rawProgramDateTime&&(R.rawProgramDateTime=m.rawProgramDateTime)}else{var z=R.byteRangeEndOffset;if(z){var Q=R.byteRangeStartOffset;k=z-Q+"@"+Q}else k=null;Zr(R,q,r,d),m=R,b=!0}m.cc=T;break;case"SERVER-CONTROL":h&&ei(c,O,l),h=new Dr(M),c.canBlockReload=h.bool("CAN-BLOCK-RELOAD"),c.canSkipUntil=h.optionalFloat("CAN-SKIP-UNTIL",0),c.canSkipDateRanges=c.canSkipUntil>0&&h.bool("CAN-SKIP-DATERANGES"),c.partHoldBack=h.optionalFloat("PART-HOLD-BACK",0),c.holdBack=h.optionalFloat("HOLD-BACK",0);break;case"PART-INF":c.partTarget&&ei(c,O,l);var $=new Dr(M);c.partTarget=$.decimalFloatingPoint("PART-TARGET");break;case"PART":var Z=c.partList;Z||(Z=c.partList=[]);var J=y>0?Z[Z.length-1]:void 0,ee=y++,te=new Dr(M,c),re=new Y(te,R,f,ee,J);Z.push(re),R.duration+=re.duration;break;case"PRELOAD-HINT":var ie=new Dr(M,c);c.preloadHint=ie;break;case"RENDITION-REPORT":var ae=new Dr(M,c);c.renditionReports=c.renditionReports||[],c.renditionReports.push(ae);break;default:fe.warn("line parsed but not handled: "+l)}}}L&&!L.relurl?(g.pop(),E-=L.duration,c.partList&&(c.fragmentHint=L)):c.partList&&($r(R,L,v),R.cc=T,c.fragmentHint=R,d&&Jr(R,d,c)),c.targetduration||(c.playlistParsingError=new Error("Missing Target Duration"));var ne=g.length,se=g[0],oe=g[ne-1];if((E+=c.skippedSegments*c.targetduration)>0&&ne&&oe){c.averagetargetduration=E/ne;var le=oe.sn;c.endSN="initSegment"!==le?le:0,c.live||(oe.endList=!0),A>0&&(function(e,t,r){if(r||(r=e[t]),r)for(var i=t;i--;){var a=e[i];if(!a)return;a.programDateTime=r.programDateTime-1e3*a.duration,r=a}}(g,A,A>ne-1?R:null),se&&v.unshift(se))}return c.fragmentHint&&(E+=c.fragmentHint.duration),c.totalduration=E,v.length&&c.dateRangeTagCount&&se&&Wr(v,c),c.endCC=T,c},e}();function Yr(e,t,r,i,a,n){var s;if(void 0===t)return null;var o=t,l={attrs:e,bitrate:e.decimalInteger("BANDWIDTH")||e.decimalInteger("AVERAGE-BANDWIDTH"),name:e.NAME,url:Kr.resolve(o,r)},u=e.decimalResolution("RESOLUTION");u&&(l.width=u.width,l.height=u.height),zr(e.CODECS,l);var d=e["SUPPLEMENTAL-CODECS"];return d&&(l.supplemental={},zr(d,l.supplemental)),null!=(s=l.unknownCodecs)&&s.length||a.push(l),n&&(l.iframes=!0),l}function Wr(e,t){var r=e.length;if(!r){if(!t.hasProgramDateTime)return;var i=t.fragments[t.fragments.length-1];e.push(i),r++}for(var a=e[r-1],n=t.live?1/0:t.totalduration,s=Object.keys(t.dateRanges),o=s.length;o--;){var l=t.dateRanges[s[o]],u=l.startDate.getTime();l.tagAnchor=a.ref;for(var d=r;d--;){var h;if((null==(h=e[d])?void 0:h.sn)<t.startSN)break;var f=jr(t,u,e,d,n);if(f){l.tagAnchor=f.ref;break}}}}function jr(e,t,r,i,a){var n=r[i];if(n){var s,o=n.programDateTime;if((t>=o||0===i)&&t<=o+1e3*(((null==(s=r[i+1])?void 0:s.start)||a)-n.start)){var l=n.sn-e.startSN,u=e.fragments;if(l>=0&&l<u.length){if(u.length>r.length)for(var d=r[i+1]||u[u.length-1],h=Math.min(d.sn-e.startSN,u.length-1);h>l;h--){var f=u[h].programDateTime;if(t>=f&&t<f+1e3*u[h].duration)return u[h]}return u[l]}}}return null}function qr(e,t,r){var i,a,n=new Dr(e,r),s=null!=(i=n.METHOD)?i:"",o=n.URI,l=n.hexadecimalInteger("IV"),u=n.KEYFORMATVERSIONS,d=null!=(a=n.KEYFORMAT)?a:"identity";o&&n.IV&&!l&&fe.error("Invalid IV: "+n.IV);var h=o?Kr.resolve(o,t):"",f=(u||"1").split("/").map(Number).filter(Number.isFinite);return new Nr(s,h,d,f,l,n.KEYID)}function Xr(e){var t=new Dr(e).decimalFloatingPoint("TIME-OFFSET");return _(t)?t:null}function zr(e,t){var r=(e||"").split(/[ ,]+/).filter((function(e){return e}));["video","audio","text","image"].forEach((function(e){var i=r.filter((function(t){return vt(t,e)}));i.length&&(t[e+"Codec"]=i.map((function(e){return e.split("/")[0]})).join(","),r=r.filter((function(e){return-1===i.indexOf(e)})))})),t.unknownCodecs=r}function Qr(e,t,r){var i=t[r];i&&(e[r]=i)}function $r(e,t,r){e.rawProgramDateTime?r.push(e):null!=t&&t.programDateTime&&(e.programDateTime=t.endProgramDateTime)}function Zr(e,t,r,i){e.relurl=t.URI,t.BYTERANGE&&e.setByteRange(t.BYTERANGE),e.level=r,e.sn="initSegment",i&&(e.levelkeys=i),e.initSegment=null}function Jr(e,t,r){e.levelkeys=t;var i=r.encryptedFragments;i.length&&i[i.length-1].levelkeys===t||!Object.keys(t).some((function(e){return t[e].isCommonEncryption}))||i.push(e)}function ei(e,t,r){e.playlistParsingError=new Error("#EXT-X-"+t+" must not appear more than once ("+r[0]+")")}function ti(e,t,r){e.playlistParsingError=new Error("#EXT-X-"+t+" must appear before the first Media Segment ("+r[0]+")")}function ri(e,t){var r=t.startPTS;if(_(r)){var i,a=0;t.sn>e.sn?(a=r-e.start,i=e):(a=e.start-r,i=t),i.duration!==a&&i.setDuration(a)}else t.sn>e.sn?e.cc===t.cc&&e.minEndPTS?t.setStart(e.start+(e.minEndPTS-e.start)):t.setStart(e.start+e.duration):t.setStart(Math.max(e.start-t.duration,0))}function ii(e,t,r,i,a,n,s,o){i-r<=0&&(o.warn("Fragment should have a positive duration",t),i=r+t.duration,n=a+t.duration);var l=r,u=i,d=t.startPTS,h=t.endPTS;if(_(d)){var f=Math.abs(d-r);e&&f>e.totalduration?o.warn("media timestamps and playlist times differ by "+f+"s for level "+t.level+" "+e.url):_(t.deltaPTS)?t.deltaPTS=Math.max(f,t.deltaPTS):t.deltaPTS=f,l=Math.max(r,d),r=Math.min(r,d),a=void 0!==t.startDTS?Math.min(a,t.startDTS):a,u=Math.min(i,h),i=Math.max(i,h),n=void 0!==t.endDTS?Math.max(n,t.endDTS):n}var c=r-t.start;0!==t.start&&t.setStart(r),t.setDuration(i-t.start),t.startPTS=r,t.maxStartPTS=l,t.startDTS=a,t.endPTS=i,t.minEndPTS=u,t.endDTS=n;var g,v=t.sn;if(!e||v<e.startSN||v>e.endSN)return 0;var m=v-e.startSN,p=e.fragments;for(p[m]=t,g=m;g>0;g--)ri(p[g],p[g-1]);for(g=m;g<p.length-1;g++)ri(p[g],p[g+1]);return e.fragmentHint&&ri(p[p.length-1],e.fragmentHint),e.PTSKnown=e.alignedSliding=!0,c}function ai(e,t,r){if(e!==t){for(var i,a=null,n=e.fragments,s=n.length-1;s>=0;s--){var l=n[s].initSegment;if(l){a=l;break}}e.fragmentHint&&delete e.fragmentHint.endPTS,function(e,t,r){for(var i,a=t.skippedSegments,n=Math.max(e.startSN,t.startSN)-t.startSN,s=(e.fragmentHint?1:0)+(a?t.endSN:Math.min(e.endSN,t.endSN))-t.startSN,o=t.startSN-e.startSN,l=t.fragmentHint?t.fragments.concat(t.fragmentHint):t.fragments,u=null!=(i=e.fragmentHint)&&i.duration?e.fragments.concat(e.fragmentHint):e.fragments,d=n;d<=s;d++){var h=u[o+d],f=l[d];if(a&&!f&&h&&(f=t.fragments[d]=h),h&&f){r(h,f,d,l);var c=h.relurl,g=f.relurl;if(c&&fi(c,g))return void(t.playlistParsingError=ni("media sequence mismatch "+f.sn+":",e,t,0,f));if(h.cc!==f.cc)return void(t.playlistParsingError=ni("discontinuity sequence mismatch ("+h.cc+"!="+f.cc+")",e,t,0,f))}}}(e,t,(function(e,r,n,s){if((!t.startCC||t.skippedSegments)&&r.cc!==e.cc){for(var o=e.cc-r.cc,l=n;l<s.length;l++)s[l].cc+=o;t.endCC=s[s.length-1].cc}_(e.startPTS)&&_(e.endPTS)&&(r.setStart(r.startPTS=e.startPTS),r.startDTS=e.startDTS,r.maxStartPTS=e.maxStartPTS,r.endPTS=e.endPTS,r.endDTS=e.endDTS,r.minEndPTS=e.minEndPTS,r.setDuration(e.endPTS-e.startPTS),r.duration&&(i=r),t.PTSKnown=t.alignedSliding=!0),e.hasStreams&&(r.elementaryStreams=e.elementaryStreams),r.loader=e.loader,e.hasStats&&(r.stats=e.stats),e.initSegment&&(r.initSegment=e.initSegment,a=e.initSegment)}));var u=t.fragments,d=t.fragmentHint?u.concat(t.fragmentHint):u;if(a&&d.forEach((function(e){var t;!e||e.initSegment&&e.initSegment.relurl!==(null==(t=a)?void 0:t.relurl)||(e.initSegment=a)})),t.skippedSegments){if(t.deltaUpdateFailed=u.some((function(e){return!e})),t.deltaUpdateFailed){r.warn("[level-helper] Previous playlist missing segments skipped in delta playlist");for(var h=t.skippedSegments;h--;)u.shift();t.startSN=u[0].sn}else{t.canSkipDateRanges&&(t.dateRanges=function(e,t,r){var i=t.dateRanges,a=t.recentlyRemovedDateranges,n=o({},e);a&&a.forEach((function(e){delete n[e]}));var s=Object.keys(n).length;return s?(Object.keys(i).forEach((function(e){var t=n[e],a=new _r(i[e].attr,t);a.isValid?(n[e]=a,t||(a.tagOrder+=s)):r.warn('Ignoring invalid Playlist Delta Update DATERANGE tag: "'+Bt(i[e].attr)+'"')})),n):i}(e.dateRanges,t,r));var f=e.fragments.filter((function(e){return e.rawProgramDateTime}));if(e.hasProgramDateTime&&!t.hasProgramDateTime)for(var c=1;c<d.length;c++)null===d[c].programDateTime&&$r(d[c],d[c-1],f);Wr(f,t)}t.endCC=u[u.length-1].cc}if(!t.startCC){var g,v=li(e,t.startSN-1);t.startCC=null!=(g=null==v?void 0:v.cc)?g:u[0].cc}(function(e,t,r){if(e&&t)for(var i=0,a=0,n=e.length;a<n;a++){var s=e[a],o=t[a+i];s&&o&&s.index===o.index&&s.fragment.sn===o.fragment.sn?r(s,o):i--}})(e.partList,t.partList,(function(e,t){t.elementaryStreams=e.elementaryStreams,t.stats=e.stats,t.gap=e.gap||t.gap})),i?(t.iframesOnly&&(i.type,N.MAIN),ii(t,i,i.startPTS,i.endPTS,i.startDTS,i.endDTS,0,r)):si(e,t),u.length&&(t.totalduration=t.edge-u[0].start),t.driftStartTime=e.driftStartTime,t.driftStart=e.driftStart;var m=t.advancedDateTime;if(t.advanced&&m){var p=t.edge;t.driftStart||(t.driftStartTime=m,t.driftStart=p),t.driftEndTime=m,t.driftEnd=p}else t.driftEndTime=e.driftEndTime,t.driftEnd=e.driftEnd,t.advancedDateTime=e.advancedDateTime;-1===t.requestScheduled&&(t.requestScheduled=e.requestScheduled)}}function ni(e,t,r,i,a){return new Error(e+" "+a.url+"\nPlaylist starting @"+t.startSN+"\n"+t.m3u8+"\n\nPlaylist starting @"+r.startSN+"\n"+r.m3u8)}function si(e,t,r,i){void 0===r&&(r=!0);var a=t.startSN+t.skippedSegments-e.startSN,n=e.fragments,s=a>=0,o=0;if(s&&a<n.length)o=n[a].start,null==i||i.log("Aligning playlists based on SN "+t.startSN+" (diff: "+o+")");else if(s&&t.startSN===e.endSN+1)o=e.fragmentEnd,null==i||i.log("Aligning playlists based on first/last SN "+t.startSN+" (diff: "+o+")");else if(s&&r)o=e.fragmentStart+a*t.levelTargetDuration;else{if(t.skippedSegments||0!==t.fragmentStart)return o;o=e.fragmentStart,null==i||i.log("Aligning playlists based on first SN "+t.startSN+" (diff: "+o+")")}return function(e,t){if(t){for(var r=e.fragments,i=e.skippedSegments;i<r.length;i++)r[i].addStart(t);e.fragmentHint&&e.fragmentHint.addStart(t)}}(t,o),o}function oi(e,t){void 0===t&&(t=1/0);var r=1e3*e.targetduration;if(e.updated){var i=e.fragments;if(i.length&&4*r>t){var a=1e3*i[i.length-1].duration;a>0&&a<r&&(r=a)}}else r/=2;return Math.round(r)}function li(e,t,r){var i;if(!e)return null;var a=e.fragments[t-e.startSN];return a||((null==(i=a=e.fragmentHint)?void 0:i.sn)===t?a:r&&t<e.startSN&&r.sn===t?r:null)}function ui(e,t,r){return e?di(e.partList,t,r):null}function di(e,t,r){if(e)for(var i=e.length;i--;){var a=e[i];if(a.index===r&&a.fragment.sn===t)return a}return null}function hi(e){e.forEach((function(e,t){var r;null==(r=e.details)||r.fragments.forEach((function(e){e.level=t,e.initSegment&&(e.initSegment.level=t)}))}))}function fi(e,t){return!(e===t||!t)&&ci(e)!==ci(t)}function ci(e){return e.replace(/\?[^?]*$/,"")}var gi=function(e){function t(t){var r;return(r=e.call(this,"content-steering",t.logger)||this).hls=void 0,r.loader=null,r.uri=null,r.pathwayId=".",r._pathwayPriority=null,r.timeToLoad=300,r.reloadTimer=-1,r.updated=0,r.started=!1,r.enabled=!0,r.levels=null,r.audioTracks=null,r.subtitleTracks=null,r.penalizedPathways={},r.hls=t,r.registerListeners(),r}u(t,e);var r=t.prototype;return r.registerListeners=function(){var e=this.hls;e.on(O.MANIFEST_LOADING,this.onManifestLoading,this),e.on(O.MANIFEST_LOADED,this.onManifestLoaded,this),e.on(O.MANIFEST_PARSED,this.onManifestParsed,this),e.on(O.ERROR,this.onError,this)},r.unregisterListeners=function(){var e=this.hls;e&&(e.off(O.MANIFEST_LOADING,this.onManifestLoading,this),e.off(O.MANIFEST_LOADED,this.onManifestLoaded,this),e.off(O.MANIFEST_PARSED,this.onManifestParsed,this),e.off(O.ERROR,this.onError,this))},r.pathways=function(){return(this.levels||[]).reduce((function(e,t){return-1===e.indexOf(t.pathwayId)&&e.push(t.pathwayId),e}),[])},r.startLoad=function(){if(this.started=!0,this.clearTimeout(),this.enabled&&this.uri){if(this.updated){var e=1e3*this.timeToLoad-(performance.now()-this.updated);if(e>0)return void this.scheduleRefresh(this.uri,e)}this.loadSteeringManifest(this.uri)}},r.stopLoad=function(){this.started=!1,this.loader&&(this.loader.destroy(),this.loader=null),this.clearTimeout()},r.clearTimeout=function(){-1!==this.reloadTimer&&(self.clearTimeout(this.reloadTimer),this.reloadTimer=-1)},r.destroy=function(){this.unregisterListeners(),this.stopLoad(),this.hls=null,this.levels=this.audioTracks=this.subtitleTracks=null},r.removeLevel=function(e){var t=this.levels;t&&(this.levels=t.filter((function(t){return t!==e})))},r.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},r.onManifestLoaded=function(e,t){var r=t.contentSteering;null!==r&&(this.pathwayId=r.pathwayId,this.uri=r.uri,this.started&&this.startLoad())},r.onManifestParsed=function(e,t){this.audioTracks=t.audioTracks,this.subtitleTracks=t.subtitleTracks},r.onError=function(e,t){var r=t.errorAction;if((null==r?void 0:r.action)===zt&&r.flags&Jt){var i=this.levels,a=this._pathwayPriority,n=this.pathwayId;if(t.context){var s=t.context,o=s.groupId,l=s.pathwayId,u=s.type;o&&i?n=this.getPathwayForGroupId(o,u,n):l&&(n=l)}n in this.penalizedPathways||(this.penalizedPathways[n]=performance.now()),!a&&i&&(a=this.pathways()),a&&a.length>1&&(this.updatePathwayPriority(a),r.resolved=this.pathwayId!==n,r.resolved||t.details===w.BUFFER_APPEND_ERROR&&!t.fatal||this.warn("Could not resolve "+t.details+' ("'+t.error.message+'") with content-steering for Pathway: '+n+" levels: "+(i?i.length:i)+" priorities: "+Bt(a)+" penalized: "+Bt(this.penalizedPathways)))}},r.filterParsedLevels=function(e){this.levels=e;var t=this.getLevelsForPathway(this.pathwayId);if(0===t.length){var r=e[0].pathwayId;this.log("No levels found in Pathway "+this.pathwayId+'. Setting initial Pathway to "'+r+'"'),t=this.getLevelsForPathway(r),this.pathwayId=r}return t.length!==e.length&&this.log("Found "+t.length+"/"+e.length+' levels in Pathway "'+this.pathwayId+'"'),t},r.getLevelsForPathway=function(e){return null===this.levels?[]:this.levels.filter((function(t){return e===t.pathwayId}))},r.updatePathwayPriority=function(e){var t;this._pathwayPriority=e;var r=this.penalizedPathways,i=performance.now();Object.keys(r).forEach((function(e){i-r[e]>3e5&&delete r[e]}));for(var a=0;a<e.length;a++){var n=e[a];if(!(n in r)){if(n===this.pathwayId)return;var s=this.hls.nextLoadLevel,o=this.hls.levels[s];if((t=this.getLevelsForPathway(n)).length>0){this.log('Setting Pathway to "'+n+'"'),this.pathwayId=n,hi(t),this.hls.trigger(O.LEVELS_UPDATED,{levels:t});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}}}},r.getPathwayForGroupId=function(e,t,r){for(var i=this.getLevelsForPathway(r).concat(this.levels||[]),a=0;a<i.length;a++)if(t===F.AUDIO_TRACK&&i[a].hasAudioGroup(e)||t===F.SUBTITLE_TRACK&&i[a].hasSubtitleGroup(e))return i[a].pathwayId;return r},r.clonePathways=function(e){var t=this,r=this.levels;if(r){var i={},a={};e.forEach((function(e){var n=e.ID,s=e["BASE-ID"],o=e["URI-REPLACEMENT"];if(!r.some((function(e){return e.pathwayId===n}))){var l=t.getLevelsForPathway(s).map((function(e){var t=new Dr(e.attrs);t["PATHWAY-ID"]=n;var r=t.AUDIO&&t.AUDIO+"_clone_"+n,s=t.SUBTITLES&&t.SUBTITLES+"_clone_"+n;r&&(i[t.AUDIO]=r,t.AUDIO=r),s&&(a[t.SUBTITLES]=s,t.SUBTITLES=s);var l=mi(e.uri,t["STABLE-VARIANT-ID"],"PER-VARIANT-URIS",o),u=new Mt({attrs:t,audioCodec:e.audioCodec,bitrate:e.bitrate,height:e.height,name:e.name,url:l,videoCodec:e.videoCodec,width:e.width});if(e.audioGroups)for(var d=1;d<e.audioGroups.length;d++)u.addGroupId("audio",e.audioGroups[d]+"_clone_"+n);if(e.subtitleGroups)for(var h=1;h<e.subtitleGroups.length;h++)u.addGroupId("text",e.subtitleGroups[h]+"_clone_"+n);return u}));r.push.apply(r,l),vi(t.audioTracks,i,o,n),vi(t.subtitleTracks,a,o,n)}}))}},r.loadSteeringManifest=function(e){var t,r=this,i=this.hls.config,a=i.loader;this.loader&&this.loader.destroy(),this.loader=new a(i);try{t=new self.URL(e)}catch(t){return this.enabled=!1,void this.log("Failed to parse Steering Manifest URI: "+e)}if("data:"!==t.protocol){var n=0|(this.hls.bandwidthEstimate||i.abrEwmaDefaultEstimate);Dt(t,"_HLS_pathway",this.pathwayId),Dt(t,"_HLS_throughput",""+n)}var s={type:F.STEERING_MANIFEST,responseType:"json",url:t.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},d={onSuccess:function(e,i,a,n){r.log('Loaded steering manifest: "'+t+'"');var s=e.data;if(1===(null==s?void 0: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,t).href}catch(e){return r.enabled=!1,void r.log("Failed to parse Steering Manifest RELOAD-URI: "+o)}r.scheduleRefresh(r.uri||a.url),l&&r.clonePathways(l);var d={steeringManifest:s,url:t.toString()};r.hls.trigger(O.STEERING_MANIFEST_LOADED,d),u&&r.updatePathwayPriority(u)}else r.log("Steering VERSION "+s.VERSION+" not supported!")},onError:function(e,t,i,a){if(r.log("Error loading steering manifest: "+e.code+" "+e.text+" ("+t.url+")"),r.stopLoad(),410===e.code)return r.enabled=!1,void r.log("Steering manifest "+t.url+" no longer available");var n=1e3*r.timeToLoad;if(429!==e.code)r.scheduleRefresh(r.uri||t.url,n);else{var s=r.loader;if("function"==typeof(null==s?void 0:s.getResponseHeader)){var o=s.getResponseHeader("Retry-After");o&&(n=1e3*parseFloat(o))}r.log("Steering manifest "+t.url+" rate limited")}},onTimeout:function(e,t,i){r.log("Timeout loading steering manifest ("+t.url+")"),r.scheduleRefresh(r.uri||t.url)}};this.log("Requesting steering manifest: "+t),this.loader.load(s,u,d)},r.scheduleRefresh=function(e,t){var r=this;void 0===t&&(t=1e3*this.timeToLoad),this.clearTimeout(),this.reloadTimer=self.setTimeout((function(){var t,i=null==(t=r.hls)?void 0:t.media;!i||i.ended?r.scheduleRefresh(e,1e3*r.timeToLoad):r.loadSteeringManifest(e)}),t)},a(t,[{key:"pathwayPriority",get:function(){return this._pathwayPriority},set:function(e){this.updatePathwayPriority(e)}}])}(ie);function vi(e,t,r,i){e&&Object.keys(t).forEach((function(a){var n=e.filter((function(e){return e.groupId===a})).map((function(e){var n=o({},e);return n.details=void 0,n.attrs=new Dr(n.attrs),n.url=n.attrs.URI=mi(e.url,e.attrs["STABLE-RENDITION-ID"],"PER-RENDITION-URIS",r),n.groupId=n.attrs["GROUP-ID"]=t[a],n.attrs["PATHWAY-ID"]=i,n}));e.push.apply(e,n)}))}function mi(e,t,r,i){if(t){var a=i[r];if(a){var n=a[t];if(n)return n}}var s=i.HOST,o=i.PARAMS,l=new self.URL(e);return s&&(l.hostname=s),o&&Object.keys(o).sort().forEach((function(e){e&&Dt(l,e,o[e])})),l.href}var pi=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(O.MEDIA_ATTACHING,this.onMediaAttaching,this),this.hls.on(O.MEDIA_DETACHING,this.onMediaDetaching,this)},t.unregisterListeners=function(){this.hls.off(O.MEDIA_ATTACHING,this.onMediaAttaching,this),this.hls.off(O.MEDIA_DETACHING,this.onMediaDetaching,this)},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.onMediaDetaching=function(){this.media=null},t.checkFPS=function(e,t,r){var i=performance.now();if(t){if(this.lastTime){var a=i-this.lastTime,n=r-this.lastDroppedFrames,s=t-this.lastDecodedFrames,o=1e3*n/a,l=this.hls;if(l.trigger(O.FPS_DROP,{currentDropped:n,currentDecoded:s,totalDroppedFrames:r}),o>0&&n>l.config.fpsDroppedMonitoringThreshold*s){var u=l.currentLevel;l.logger.warn("drop FPS ratio greater than max allowed value for currentLevel: "+u),u>0&&(-1===l.autoLevelCapping||l.autoLevelCapping>=u)&&(u-=1,l.trigger(O.FPS_DROP_LEVEL_CAPPING,{level:u,droppedLevel:l.currentLevel}),l.autoLevelCapping=u,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}(),yi="NOT_LOADED",Ei="APPENDING",Ti="PARTIAL",Si="OK",Li=function(){function e(e){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=e,this._registerListeners()}var t=e.prototype;return t._registerListeners=function(){var e=this.hls;e&&(e.on(O.MANIFEST_LOADING,this.onManifestLoading,this),e.on(O.BUFFER_APPENDED,this.onBufferAppended,this),e.on(O.FRAG_BUFFERED,this.onFragBuffered,this),e.on(O.FRAG_LOADED,this.onFragLoaded,this))},t._unregisterListeners=function(){var e=this.hls;e&&(e.off(O.MANIFEST_LOADING,this.onManifestLoading,this),e.off(O.BUFFER_APPENDED,this.onBufferAppended,this),e.off(O.FRAG_BUFFERED,this.onFragBuffered,this),e.off(O.FRAG_LOADED,this.onFragLoaded,this))},t.destroy=function(){this._unregisterListeners(),this.hls=this.fragments=this.activePartLists=this.endListFragments=this.timeRanges=null},t.getAppendedFrag=function(e,t){var r=this.activePartLists[t];if(r)for(var i=r.length;i--;){var a=r[i];if(!a)break;if(a.start<=e&&e<=a.end&&a.loaded)return a}return this.getBufferedFrag(e,t)},t.getBufferedFrag=function(e,t){return this.getFragAtPos(e,t,!0)},t.getFragAtPos=function(e,t,r){for(var i=this.fragments,a=Object.keys(i),n=a.length;n--;){var s=i[a[n]];if((null==s?void 0:s.body.type)===t&&(!r||s.buffered)){var o=s.body;if(o.start<=e&&e<=o.end)return o}}return null},t.detectEvictedFragments=function(e,t,r,i,a,n){var s=this;this.timeRanges&&(this.timeRanges[e]=t);var o=(null==a?void 0:a.fragment.sn)||-1;Object.keys(this.fragments).forEach((function(a){var l=s.fragments[a];if(l&&s.hls){var u=l.body;if(!(o>=u.sn))if(l.buffered||l.loaded&&!n){var d=l.range[e];if(d)if(0!==d.time.length){if(d.time.some((function(e){var r=!s.isTimeBuffered(e.startPTS,e.endPTS,t);return r&&s.removeFragment(u),r})),i&&i!==u&&ct()){var h=i.endPTS,f=u.startPTS;if(h&&f&&l.range.video){var c=f-h;c<0&&c>-.1&&(s.removeFragment(u),s.hls.trigger(O.BUFFER_FLUSHING,{startOffset:h+.004,endOffset:1/0,type:"video"}))}}}else s.removeFragment(u)}else u.type===r&&s.removeFragment(u)}}))},t.detectPartialFragments=function(e){var t=this,r=this.timeRanges,i=e.frag,a=e.part,n=e.id;if(r&&G(i)){var s=Ai(i),o=this.fragments[s];if(!(!o||o.buffered&&i.gap)){var l=!i.relurl;Object.keys(r).forEach((function(e){var s=i.elementaryStreams[e];if(s){var u=r[e],d=l||!0===s.partial;o.range[e]=t.getBufferedTimes(i,a,d,u),t.detectEvictedFragments(e,u,n,i,a)}})),o.loaded=null;var u=Object.keys(o.range);if(u.length){if(this.bufferedEnd(o,i),Ri(o)||this.removeParts(i.sn-1,i.type),!a){var d=Math.min(.004,i.duration);u.some((function(e){var r,a=null==(r=o.range[e])?void 0:r.time,n=0===a.length||1===a.length&&a[0].endPTS-a[0].startPTS<d;return n&&t.addAsGap(i),n}))}}else this.removeFragment(i)}}},t.addAsGap=function(e){e.gap=!0,this.removeFragment(e),this.fragBuffered(e,!0)},t.bufferedEnd=function(e,t){e.buffered=!0,(e.body.endList=t.endList||e.body.endList)&&!t.gap&&(this.endListFragments[e.body.type]=e)},t.removeParts=function(e,t){var r=this.activePartLists[t];r&&(this.activePartLists[t]=bi(r,(function(t){return t.fragment.sn>=e})))},t.fragBuffered=function(e,t){var r=Ai(e),i=this.fragments[r];return!i&&t&&(i=this.fragments[r]={body:e,appendedPTS:null,loaded:null,buffered:!1,range:Object.create(null)},e.gap&&(this.hasGaps=!0)),i&&(i.loaded=null,this.bufferedEnd(i,e)),i},t.getBufferedTimes=function(e,t,r,i){for(var a={time:[],partial:r},n=e.start,s=e.end,o=e.minEndPTS||s,l=e.maxStartPTS||n,u=0;u<i.length;u++){var d=i.start(u)-this.bufferPadding,h=i.end(u)+this.bufferPadding;if(l>=d&&o<=h){a.time.push({startPTS:Math.max(n,i.start(u)),endPTS:Math.min(s,i.end(u))});break}if(n<h&&s>d){var f=Math.max(n,i.start(u)),c=Math.min(s,i.end(u));c>f&&(a.partial=!0,a.time.push({startPTS:f,endPTS:c}))}else if(s<=d)break}return a},t.getPartialFragment=function(e){var t,r,i,a=null,n=0,s=this.bufferPadding,o=this.fragments;return Object.keys(o).forEach((function(l){var u=o[l];u&&Ri(u)&&(r=u.body.start-s,i=u.body.end+s,e>=r&&e<=i&&(t=Math.min(e-r,i-e),n<=t&&(a=u.body,n=t)))})),a},t.isEndListAppended=function(e){var t=this.endListFragments[e];return void 0!==t&&(t.buffered||Ri(t))},t.getState=function(e){var t=Ai(e),r=this.fragments[t];return r?r.buffered?Ri(r)?Ti:Si:Ei:yi},t.isTimeBuffered=function(e,t,r){for(var i,a,n=0;n<r.length;n++){if(i=r.start(n)-this.bufferPadding,a=r.end(n)+this.bufferPadding,e>=i&&t<=a)return!0;if(t<=i)return!1}return!1},t.onManifestLoading=function(){this.removeAllFragments()},t.onFragLoaded=function(e,t){if(G(t.frag)&&!t.frag.bitrateTest){var r=t.frag,i=t.part?null:t,a=Ai(r);this.fragments[a]={body:r,appendedPTS:null,loaded:i,buffered:!1,range:Object.create(null)}}},t.onBufferAppended=function(e,t){var r=t.frag,i=t.part,a=t.timeRanges;if(G(r)){var n=r.type;if(i){var s=this.activePartLists[n];s||(this.activePartLists[n]=s=[]),s.push(i)}this.timeRanges=a}},t.onFragBuffered=function(e,t){this.detectPartialFragments(t)},t.hasFragment=function(e){var t=Ai(e);return!!this.fragments[t]},t.hasFragments=function(e){var t=this.fragments,r=Object.keys(t);if(!e)return r.length>0;for(var i=r.length;i--;){var a=t[r[i]];if((null==a?void 0:a.body.type)===e)return!0}return!1},t.hasParts=function(e){var t;return!(null==(t=this.activePartLists[e])||!t.length)},t.getBackBufferEvictionEnd=function(e,t,r){for(var i=this.fragments,a=0,n=0,s=Object.keys(i),o=0;o<s.length;o++){var l=i[s[o]];if(l&&l.buffered&&l.body.type===t){var u=l.body;if(u.gap)return 0;var d=u.hasStats&&u.stats.loaded||u.byteLength||u.bitrate&&8*u.bitrate*u.duration;if(u.end<=e&&d&&(a+=d,n=Math.max(n,u.end),a>=r))return n}}return n>0?n:0},t.removeFragmentsInRange=function(e,t,r,i,a){var n=this;i&&!this.hasGaps||Object.keys(this.fragments).forEach((function(s){var o=n.fragments[s];if(o){var l=o.body;l.type!==r||i&&!l.gap||l.start<t&&l.end>e&&(o.buffered||a)&&n.removeFragment(l)}}))},t.removeFragment=function(e){var t=Ai(e);e.clearElementaryStreamInfo();var r=this.activePartLists[e.type];if(r){var i=e.sn;this.activePartLists[e.type]=bi(r,(function(e){return e.fragment.sn!==i}))}delete this.fragments[t],e.endList&&delete this.endListFragments[e.type]},t.removeAllFragments=function(){var e;this.fragments=Object.create(null),this.endListFragments=Object.create(null),this.activePartLists=Object.create(null),this.hasGaps=!1;var t=null==(e=this.hls)||null==(e=e.latestLevelDetails)?void 0:e.partList;t&&t.forEach((function(e){return e.clearElementaryStreamInfo()}))},e}();function Ri(e){var t,r,i,a;return e.buffered&&!!(e.body.gap||null!=(t=e.range.video)&&t.partial||null!=(r=e.range.audio)&&r.partial||null!=(i=e.range.audiovideo)&&i.partial||null!=(a=e.range.subs)&&a.partial)}function Ai(e){return e.type+"_"+e.level+"_"+e.sn}function bi(e,t){return e.filter((function(e){var r=t(e);return r||e.clearElementaryStreamInfo(),r}))}var ki=function(){function e(e,t,r){this.subtle=void 0,this.aesIV=void 0,this.aesMode=void 0,this.subtle=e,this.aesIV=t,this.aesMode=r}return e.prototype.decrypt=function(e,t){switch(this.aesMode){case xr:return this.subtle.decrypt({name:"AES-CBC",iv:this.aesIV},t,e);case wr:return this.subtle.decrypt({name:"AES-CTR",counter:this.aesIV,length:64},t,e);default:throw new Error("[AESCrypto] invalid aes mode "+this.aesMode)}},e}(),Di=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],a=r[1],n=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,g=0,v=0;for(v=0;v<256;v++)f[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,e[c]=m,t[m]=c;var p=f[c],y=f[p],E=f[y],T=257*f[m]^16843008*m;i[c]=T<<24|T>>>8,a[c]=T<<16|T>>>16,n[c]=T<<8|T>>>24,s[c]=T,T=16843009*E^65537*y^257*p^16843008*c,l[m]=T<<24|T>>>8,u[m]=T<<16|T>>>16,d[m]=T<<8|T>>>24,h[m]=T,c?(c=p^f[f[f[E^p]]],g^=f[f[g]]):c=g=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 a=this.keySize=t.length;if(4!==a&&6!==a&&8!==a)throw new Error("Invalid aes key size="+a);var n,s,o,l,u=this.ksRows=4*(a+6+1),d=this.keySchedule=new Uint32Array(u),h=this.invKeySchedule=new Uint32Array(u),f=this.sBox,c=this.rcon,g=this.invSubMix,v=g[0],m=g[1],p=g[2],y=g[3];for(n=0;n<u;n++)n<a?o=d[n]=t[n]:(l=o,n%a==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[n/a|0]<<24):a>6&&n%a==4&&(l=f[l>>>24]<<24|f[l>>>16&255]<<16|f[l>>>8&255]<<8|f[255&l]),d[n]=o=(d[n-a]^l)>>>0);for(s=0;s<u;s++)n=u-s,l=3&s?d[n]:d[n-4],h[s]=s<4||n<=4?l:v[f[l>>>24]]^m[f[l>>>16&255]]^p[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,a,n,s,o,l,u,d,h,f,c,g,v,m,p=this.keySize+6,y=this.invKeySchedule,E=this.invSBox,T=this.invSubMix,S=T[0],L=T[1],R=T[2],A=T[3],b=this.uint8ArrayToUint32Array_(r),k=b[0],D=b[1],I=b[2],_=b[3],C=new Int32Array(e),P=new Int32Array(C.length),x=this.networkToHostOrderSwap;t<C.length;){for(h=x(C[t]),f=x(C[t+1]),c=x(C[t+2]),g=x(C[t+3]),o=h^y[0],l=g^y[1],u=c^y[2],d=f^y[3],v=4,m=1;m<p;m++)i=S[o>>>24]^L[l>>16&255]^R[u>>8&255]^A[255&d]^y[v],a=S[l>>>24]^L[u>>16&255]^R[d>>8&255]^A[255&o]^y[v+1],n=S[u>>>24]^L[d>>16&255]^R[o>>8&255]^A[255&l]^y[v+2],s=S[d>>>24]^L[o>>16&255]^R[l>>8&255]^A[255&u]^y[v+3],o=i,l=a,u=n,d=s,v+=4;i=E[o>>>24]<<24^E[l>>16&255]<<16^E[u>>8&255]<<8^E[255&d]^y[v],a=E[l>>>24]<<24^E[u>>16&255]<<16^E[d>>8&255]<<8^E[255&o]^y[v+1],n=E[u>>>24]<<24^E[d>>16&255]<<16^E[o>>8&255]<<8^E[255&l]^y[v+2],s=E[d>>>24]<<24^E[o>>16&255]<<16^E[l>>8&255]<<8^E[255&u]^y[v+3],P[t]=x(i^k),P[t+1]=x(s^D),P[t+2]=x(n^I),P[t+3]=x(a^_),k=h,D=f,I=c,_=g,t+=4}return P.buffer},e}(),Ii=function(){function e(e,t,r){this.subtle=void 0,this.key=void 0,this.aesMode=void 0,this.subtle=e,this.key=t,this.aesMode=r}return e.prototype.expandKey=function(){var e=function(e){switch(e){case xr:return"AES-CBC";case wr:return"AES-CTR";default:throw new Error("[FastAESKey] invalid aes mode "+e)}}(this.aesMode);return this.subtle.importKey("raw",this.key,{name:e},!1,["encrypt","decrypt"])},e}(),_i=function(){function e(e,t){if(this.logEnabled=!0,this.decryptRange=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.enableSoftwareAES=void 0,this.enableSoftwareAES=e.enableSoftwareAES,!0!==t)try{var r=self.crypto;r&&(this.subtle=r.subtle||r.webkitSubtle)}catch(e){}this.useSoftware=!this.subtle}var t=e.prototype;return t.destroy=function(){this.subtle=null,this.softwareDecrypter=null,this.key=null,this.fastAesKey=null,this.remainderData=null,this.currentIV=null,this.currentResult=null},t.isSync=function(){return this.useSoftware},t.flush=function(){var e=this.currentResult,t=this.remainderData;if(!e||t)return this.reset(),null;var r=new Uint8Array(e);this.reset();var i,a,n,s=this.decryptRange;return s?0===s.start&&s.end>=r.length?r:r.slice(s.start,s.end):(a=(i=r).byteLength,(n=a&&new DataView(i.buffer).getUint8(a-1))?i.slice(0,a-n):i)},t.reset=function(){this.currentResult=null,this.currentIV=null,this.remainderData=null,this.softwareDecrypter&&(this.softwareDecrypter=null)},t.decrypt=function(e,t,r,i,a){var n=this;return this.decryptRange=a,this.useSoftware||a?new Promise((function(a,s){var o=ArrayBuffer.isView(e)?e:new Uint8Array(e);n.softwareDecrypt(o,t,r,i);var l=n.flush();l?a(l.buffer):s(new Error("[softwareDecrypt] Failed to decrypt data"))})):this.webCryptoDecrypt(new Uint8Array(e),t,r,i)},t.softwareDecrypt=function(e,t,r,i){var a=this.currentIV,n=this.currentResult,s=this.remainderData;if(i!==xr||16!==t.byteLength)return fe.warn("SoftwareDecrypt: can only handle AES-128-CBC"),null;this.logOnce("JS AES decrypt"),s&&(e=it(s,e),this.remainderData=null);var o=this.getValidChunk(e);if(!o.length)return null;a&&(r=a);var l=this.softwareDecrypter;l||(l=this.softwareDecrypter=new Di),l.expandKey(t);var u=n;return this.currentResult=l.decrypt(o.buffer,0,r),this.currentIV=o.slice(-16).buffer,u||null},t.webCryptoDecrypt=function(e,t,r,i){var a=this;if(this.key!==t||!this.fastAesKey){if(!this.subtle)return Promise.resolve(this.onWebCryptoError(e,t,r,i));this.key=t,this.fastAesKey=new Ii(this.subtle,t,i)}return this.fastAesKey.expandKey().then((function(t){return a.subtle?(a.logOnce("WebCrypto AES decrypt"),new ki(a.subtle,new Uint8Array(r),i).decrypt(e.buffer,t)):Promise.reject(new Error("web crypto not initialized"))})).catch((function(n){return fe.warn("[decrypter]: WebCrypto Error, disable WebCrypto API, "+n.name+": "+n.message),a.onWebCryptoError(e,t,r,i)}))},t.onWebCryptoError=function(e,t,r,i){var a=this.enableSoftwareAES;if(a){this.useSoftware=!0,this.logEnabled=!0,this.softwareDecrypt(e,t,r,i);var n=this.flush();if(n)return n.buffer}throw new Error("WebCrypto"+(a?" and softwareDecrypt":"")+": failed to decrypt data")},t.getValidChunk=function(e){var t=e,r=e.length-e.length%16;return r!==e.length&&(t=e.slice(0,r),this.remainderData=e.slice(r)),t},t.logOnce=function(e){this.logEnabled&&(fe.log("[decrypter]: "+e),this.logEnabled=!1)},e}(),Ci=Math.pow(2,17),Pi=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,r,i){var a=this,n=e.url;if(!n)return Promise.reject(new Mi({type:x.NETWORK_ERROR,details:w.FRAG_LOAD_ERROR,fatal:!1,frag:e,error:new Error("Fragment does not have a "+(n?"part list":"url")),networkDetails:null}));this.abort();var s=this.config,o=s.fLoader,l=s.loader;return new Promise((function(u,d){if(a.loader&&a.loader.destroy(),e.gap){if(e.tagList.some((function(e){return"GAP"===e[0]})))return void d(Oi(e));e.gap=!1}var h=a.loader=o?new o(s):new l(s),c=xi(e,null,t);e.loader=h;var g=$(s.fragLoadPolicy.default),v={loadPolicy:g,timeout:g.maxLoadTimeMs,maxRetry:0,retryDelay:0,maxRetryDelay:0,highWaterMark:"initSegment"===e.sn?1/0:Ci};h.stats.retry=e.stats.retry,e.stats=h.stats;var m={onSuccess:function(t,r,i,n){a.resetLoader(e,h);var s=t.data;i.resetIV&&e.decryptdata&&(e.decryptdata.iv=new Uint8Array(s.slice(0,16)),s=s.slice(16)),u({frag:e,part:null,payload:s,networkDetails:n})},onError:function(t,r,i,s){a.resetLoader(e,h),d(new Mi({type:x.NETWORK_ERROR,details:w.FRAG_LOAD_ERROR,fatal:!1,frag:e,response:f({url:n,data:void 0},t),error:new Error("HTTP Error "+t.code+" "+t.text),networkDetails:i,stats:s}))},onAbort:function(t,r,i){a.resetLoader(e,h),d(new Mi({type:x.NETWORK_ERROR,details:w.INTERNAL_ABORTED,fatal:!1,frag:e,error:new Error("Aborted"),networkDetails:i,stats:t}))},onTimeout:function(t,r,i){a.resetLoader(e,h),d(new Mi({type:x.NETWORK_ERROR,details:w.FRAG_LOAD_TIMEOUT,fatal:!1,frag:e,error:new Error("Timeout after "+v.timeout+"ms"),networkDetails:i,stats:t}))}};if(r){var p=a.gateProgress(r,i);m.onProgress=function(t,r,i,a){return p({frag:e,part:null,payload:i,networkDetails:a})}}h.load(c,v,m)}))},t.gateProgress=function(e,t){var r=e;if(t){var i=[],a=!1;t.then((function(){a=!0,i.forEach((function(t){return e(t)})),i.length=0})).catch((function(){})),r=function(t){a?e(t):i.push(t)}}return r},t.loadPart=function(e,t,r){var i=this;this.abort();var a=this.config,n=a.fLoader,s=a.loader;return new Promise((function(o,l){if(i.loader&&i.loader.destroy(),e.gap||t.gap)l(Oi(e,t));else{var u=i.loader=n?new n(a):new s(a),d=xi(e,t);e.loader=u;var h=$(a.fragLoadPolicy.default),c={loadPolicy:h,timeout:h.maxLoadTimeMs,maxRetry:0,retryDelay:0,maxRetryDelay:0,highWaterMark:Ci};t.stats=u.stats,u.load(d,c,{onSuccess:function(a,n,s,l){i.resetLoader(e,u),i.updateStatsFromPart(e,t);var d={frag:e,part:t,payload:a.data,networkDetails:l};r(d),o(d)},onError:function(r,a,n,s){i.resetLoader(e,u),l(new Mi({type:x.NETWORK_ERROR,details:w.FRAG_LOAD_ERROR,fatal:!1,frag:e,part:t,response:f({url:d.url,data:void 0},r),error:new Error("HTTP Error "+r.code+" "+r.text),networkDetails:n,stats:s}))},onAbort:function(r,a,n){e.stats.aborted=t.stats.aborted,i.resetLoader(e,u),l(new Mi({type:x.NETWORK_ERROR,details:w.INTERNAL_ABORTED,fatal:!1,frag:e,part:t,error:new Error("Aborted"),networkDetails:n,stats:r}))},onTimeout:function(r,a,n){i.resetLoader(e,u),l(new Mi({type:x.NETWORK_ERROR,details:w.FRAG_LOAD_TIMEOUT,fatal:!1,frag:e,part:t,error:new Error("Timeout after "+c.timeout+"ms"),networkDetails:n,stats:r}))}})}}))},t.updateStatsFromPart=function(e,t){var r=e.stats,i=t.stats,a=i.total;if(r.loaded+=i.loaded,a){var n=Math.round(e.duration/t.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},t.resetLoader=function(e,t){e.loader=null,e.url=null,this.loader===t&&(self.clearTimeout(this.partLoadTimeout),this.loader=null),t.destroy()},e}();function xi(e,t,r){void 0===t&&(t=null);var i=t||e,a={type:F.MEDIA_FRAGMENT,frag:e,part:t,responseType:"arraybuffer",url:i.url,headers:{},rangeStart:0,rangeEnd:0},n=wi(e,t,r);return n.resetIV&&(a.resetIV=!0),n.byteRange&&(a.rangeStart=n.byteRange.start,a.rangeEnd=n.byteRange.end),a}function wi(e,t,r){void 0===t&&(t=null);var i,a,n={},s=t||e,o=s.byteRangeStartOffset,l=s.byteRangeEndOffset;if(_(o)&&_(l))if("initSegment"!==e.sn&&!r||"AES-128"!==(a=null==(i=e.decryptdata)?void 0:i.method)&&"AES-256"!==a)n.byteRange={start:o,end:l};else{var u=16*Math.floor(o/16),d=16*Math.ceil(l/16);n.decryptRange={start:o-u,end:l-u},u>=16&&(n.resetIV=!0,u-=16),n.byteRange={start:u,end:d}}return n}function Oi(e,t){var r=new Error("GAP "+(e.gap?"tag":"attribute")+" found"),i={type:x.MEDIA_ERROR,details:w.FRAG_GAP,fatal:!1,frag:e,error:r,networkDetails:null};return t&&(i.part=t),(t||e).stats.aborted=!0,new Mi(i)}var Mi=function(e){function t(t){var r;return(r=e.call(this,t.error.message)||this).data=void 0,r.data=t,r}return u(t,e),t}(v(Error)),Fi=function(e){function t(t,r){var i;return(i=e.call(this,t,r)||this)._boundTick=void 0,i._tickTimer=null,i._tickInterval=null,i._tickCallCount=0,i._boundTick=i.tick.bind(i),i}u(t,e);var r=t.prototype;return r.destroy=function(){this.onHandlerDestroying(),this.onHandlerDestroyed()},r.onHandlerDestroying=function(){this.clearNextTick(),this.clearInterval()},r.onHandlerDestroyed=function(){},r.hasInterval=function(){return!!this._tickInterval},r.hasNextTick=function(){return!!this._tickTimer},r.setInterval=function(e){return!this._tickInterval&&(this._tickCallCount=0,this._tickInterval=self.setInterval(this._boundTick,e),!0)},r.clearInterval=function(){return!!this._tickInterval&&(self.clearInterval(this._tickInterval),this._tickInterval=null,!0)},r.clearNextTick=function(){return!!this._tickTimer&&(self.clearTimeout(this._tickTimer),this._tickTimer=null,!0)},r.tick=function(){this._tickCallCount++,1===this._tickCallCount?(this.doTick(),this._tickCallCount>1&&this.tickImmediate(),this._tickCallCount=0):(this.log("possible exception thrown in task-loop ("+this.constructor.name+".doTick)"),this._tickCallCount=0)},r.tickImmediate=function(){this.clearNextTick(),this._tickTimer=self.setTimeout(this._boundTick,0)},r.doTick=function(){},t}(ie),Ni=function(e,t,r,i,a,n,s,o,l){void 0===i&&(i=0),void 0===a&&(a=-1),void 0===n&&(n=!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.iframe=void 0,this.duration=void 0,this.decryptRange=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=a,this.partial=n,this.duration=s||0,this.decryptRange=l,this.iframe=o||!1};function Bi(e,t){for(var r=0,i=e.length;r<i;r++){var a;if((null==(a=e[r])?void 0:a.cc)===t)return e[r]}return null}function Ui(e,t){var r=e.start+t;e.setStart(r)}function Gi(e,t){for(var r=t.fragments,i=0,a=r.length;i<a;i++)Ui(r[i],e);t.fragmentHint&&Ui(t.fragmentHint,e),t.alignedSliding=!0}function Vi(e,t,r){e&&(function(e,t,r){if(function(e,t){return!!(e&&t.startCC<e.endCC&&t.endCC>e.startCC)}(t,e)){var i=Math.min(t.endCC,e.endCC),a=Bi(t.fragments,i),n=Bi(e.fragments,i);if(a&&n){var s=a.start-n.start;r.log("Aligning playlists using dicontinuity sequence "+i+" (diff: "+s+")"),Gi(s,e)}}}(t,e,r),t.alignedSliding||function(e,t,r){if(e.hasProgramDateTime&&t.hasProgramDateTime){var i,a,n=e.fragments,s=t.fragments;if(n.length&&s.length){var o=Math.min(t.endCC,e.endCC);t.startCC<o&&e.startCC<o&&(i=Bi(s,o),a=Bi(n,o)),i&&a||(a=Bi(n,(i=s[Math.floor(s.length/2)]||s.filter((function(e){return!!e}))[0]).cc)||n[Math.floor(n.length/2)]);var l=i.programDateTime,u=a.programDateTime;if(l&&u){var d=(u-l)/1e3;if(Math.abs(d)>Math.max(60,e.totalduration))r.log("Cannot align playlists using PDT without overlap ("+Math.abs(d)+" > "+e.totalduration+")");else{var h=d-(a.start-i.start);r.log("Aligning playlists using PDT (diff: "+h+")"),Gi(h,e)}}}}}(t,e,r),t.alignedSliding||t.skippedSegments||si(e,t,!1,r))}var Hi={STOPPED:"STOPPED",IDLE:"IDLE",KEY_LOADING:"KEY_LOADING",FRAG_LOADING:"FRAG_LOADING",FRAG_LOADING_WAITING_RETRY:"FRAG_LOADING_WAITING_RETRY",PARSING:"PARSING",PARSED:"PARSED",ENDED:"ENDED",ERROR:"ERROR",WAITING_LEVEL:"WAITING_LEVEL"},Ki=function(e){function t(t,r,i,a,n){var s;return(s=e.call(this,a,t.logger)||this).hls=void 0,s.fragPrevious=null,s.fragCurrent=null,s.fragPlaying=null,s.fragmentTracker=void 0,s.transmuxer=null,s._state=Hi.STOPPED,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=-1,s.startTimeOffset=null,s.retryDate=0,s.levels=null,s.fragmentLoader=void 0,s.initFragmentLoader=void 0,s.keyLoader=void 0,s.levelLastLoaded=null,s.startFragRequested=!1,s.decrypter=void 0,s.initPTS=[],s.buffering=!0,s.loadingParts=!1,s.loopSn=void 0,s.onMediaSeeking=function(){var e=s,t=e.config,r=e.fragCurrent,i=e.media,a=e.mediaBuffer,n=e.state,o=i?i.currentTime:0,l=dr.bufferInfo(a||i,o,t.maxBufferHole),u=l.len;if(s.log("Media seeking to "+o+", state: "+n+", "+(u?"in":"out of")+" buffer"),s.state===Hi.ENDED)s.resetLoadingState();else if(r){var d=t.maxFragLookUpTolerance,h=r.start-d,f=r.start+r.duration+d;if(!u||f<l.start||h>l.end){var c=o<h,g=o>f;(c||g)&&(!r.loader||!g&&s.isFragmentNearlyDownloaded(r)?s.fragPrevious=null:(s.log("Cancelling fragment load for seek (sn: "+r.sn+") - "+(c?"backward":"forward")+" seek"),r.abortRequests(),s.resetLoadingState()))}}if(i&&(s.fragmentTracker.removeFragmentsInRange(o,1/0,s.playlistType,!0),o>s.lastCurrentTime&&(s.lastCurrentTime=o),!s.loadingParts)){var v=Math.max(l.end,o),m=s.shouldLoadParts(s.getLevelDetails(),v);m&&(s.log("LL-Part loading ON after seeking to "+o+" with buffer @"+v),s.loadingParts=m)}var p=!dr.isBuffered(i,o);s.hls.hasEnoughToStart&&!p||(s.log("Setting "+(p?"startPosition":"nextLoadPosition")+" to "+o+" for seek without enough to start"),s.nextLoadPosition=o,p&&(s.startPosition=o)),u<1&&s.state===Hi.IDLE&&s.tickImmediate()},s.onMediaEnded=function(){s.log("setting startPosition to 0 because media ended"),s.startPosition=s.lastCurrentTime=0},s.playlistType=n,s.hls=t,s.fragmentLoader=new Pi(t.config),s.initFragmentLoader=new Pi(t.config),s.keyLoader=i,s.fragmentTracker=r,s.config=t.config,s.decrypter=new _i(t.config),s}u(t,e);var r=t.prototype;return r.registerListeners=function(){var e=this.hls;e.on(O.MEDIA_ATTACHED,this.onMediaAttached,this),e.on(O.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(O.MANIFEST_LOADING,this.onManifestLoading,this),e.on(O.MANIFEST_LOADED,this.onManifestLoaded,this),e.on(O.ERROR,this.onError,this)},r.unregisterListeners=function(){var e=this.hls;e.off(O.MEDIA_ATTACHED,this.onMediaAttached,this),e.off(O.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(O.MANIFEST_LOADING,this.onManifestLoading,this),e.off(O.MANIFEST_LOADED,this.onManifestLoaded,this),e.off(O.ERROR,this.onError,this)},r.doTick=function(){this.onTickEnd()},r.onTickEnd=function(){},r.startLoad=function(e){},r.stopLoad=function(){if(this.state!==Hi.STOPPED){this.fragmentLoader.abort(),this.initFragmentLoader.abort(),this.keyLoader.abort(this.playlistType);var e=this.fragCurrent;null!=e&&e.loader&&(e.abortRequests(),this.fragmentTracker.removeFragment(e)),this.resetTransmuxer(),this.fragCurrent=null,this.fragPrevious=null,this.clearInterval(),this.clearNextTick(),this.state=Hi.STOPPED}},r.pauseBuffering=function(){this.buffering=!1},r.resumeBuffering=function(){this.buffering=!0},r._streamEnded=function(e,t){if(t.live||!this.media)return!1;var r=e.end||0,i=this.config.timelineOffset||0;if(r<=i)return!1;var a=e.buffered;this.config.maxBufferHole&&a&&a.length>1&&(e=dr.bufferedInfo(a,e.start,0));var n=e.nextStart;if(n&&n>i&&n<t.edge)return!1;if(this.media.currentTime<e.start)return!1;var s=t.partList;if(null!=s&&s.length){var o=s[s.length-1];return dr.isBuffered(this.media,o.start+o.duration/2)}var l=t.fragments[t.fragments.length-1];return this.fragmentTracker.isEndListAppended(l.type)},r.getLevelDetails=function(){if(this.levels&&null!==this.levelLastLoaded)return this.levelLastLoaded.details},r.onMediaAttached=function(e,t){var r=this.media=this.mediaBuffer=t.media;hr(r,"seeking",this.onMediaSeeking),hr(r,"ended",this.onMediaEnded);var i=this.config;this.levels&&i.autoStartLoad&&this.state===Hi.STOPPED&&this.startLoad(i.startPosition)},r.onMediaDetaching=function(e,t){this.fragPlaying=null;var r=!!t.transferMedia,i=this.media;if(null!==i){if(i.ended&&(this.log("MSE detaching and video ended, reset startPosition"),this.startPosition=this.lastCurrentTime=0),fr(i,"seeking",this.onMediaSeeking),fr(i,"ended",this.onMediaEnded),this.media=this.mediaBuffer=null,this.loopSn=void 0,r)return this.resetLoadingState(),void this.resetTransmuxer();this.loadingParts=!1,this.fragmentTracker.removeAllFragments(),this.stopLoad()}},r.onManifestLoading=function(){this.initPTS=[],this.fragPlaying=this.levels=this.levelLastLoaded=this.fragCurrent=null,this.lastCurrentTime=0,this.startPosition=-1,this.startFragRequested=!1},r.onError=function(e,t){},r.onManifestLoaded=function(e,t){this.startTimeOffset=t.startTimeOffset},r.onHandlerDestroying=function(){this.stopLoad(),this.transmuxer&&(this.transmuxer.destroy(),this.transmuxer=null),e.prototype.onHandlerDestroying.call(this),this.hls=this.onMediaSeeking=this.onMediaEnded=null},r.onHandlerDestroyed=function(){this.state=Hi.STOPPED,this.fragmentLoader&&this.fragmentLoader.destroy(),this.initFragmentLoader&&this.initFragmentLoader.destroy(),this.keyLoader&&this.keyLoader.destroy(),this.decrypter&&this.decrypter.destroy(),this.hls=this.decrypter=this.keyLoader=null,this.fragmentLoader=this.initFragmentLoader=this.fragmentTracker=null,e.prototype.onHandlerDestroyed.call(this)},r.loadFragment=function(e,t,r){this.startFragRequested=!0,this._loadFragForPlayback(e,t,r)},r._loadFragForPlayback=function(e,t,r){var i=this;this._doFragLoad(e,t,r,(function(e){var t=e.frag;if(i.fragContextChanged(t))return i.warn(t.type+" sn: "+t.sn+(e.part?" part: "+e.part.index:"")+" of "+i.fragInfo(t,!1,e.part)+" was dropped during download."),void i.fragmentTracker.removeFragment(t);var r=++t.stats.chunkCount;i.log("load progress "+t.type+" sn: "+t.sn+(e.part?" part: "+e.part.index:"")+" of "+i.fragInfo(t,!1,e.part)+" chunk: "+r),i._handleFragmentLoadProgress(e)})).then((function(e){if(e){var t=i.state,r=e.frag;i.fragContextChanged(r)?(t===Hi.FRAG_LOADING||!i.fragCurrent&&t===Hi.PARSING)&&(i.fragmentTracker.removeFragment(r),i.state=Hi.IDLE):("payload"in e&&(i.log("Loaded "+r.type+" sn: "+r.sn+" of "+i.playlistLabel()+" "+r.level+" (bytes "+r.stats.loaded+")"),i.hls.trigger(O.FRAG_LOADED,e)),i._handleFragmentLoadComplete(e))}})).catch((function(t){i.state!==Hi.STOPPED&&i.state!==Hi.ERROR&&(i.log("Frag error "+e.type+" sn:"+e.sn+" cc:"+e.cc+": "+((null==t?void 0:t.message)||t)),i.resetFragmentLoading(e))}))},r.clearTrackerIfNeeded=function(e){var t,r=this.fragmentTracker;if(r.getState(e)===Ei){var i=e.type,a=this.getFwdBufferInfo(this.mediaBuffer,i),n=Math.max(e.duration,a?a.len:this.config.maxBufferLength),s=this.backtrackFragment;(1==(s?e.sn-s.sn:0)||this.reduceMaxBufferLength(n,e.duration))&&r.removeFragment(e)}else 0===(null==(t=this.mediaBuffer)?void 0:t.buffered.length)?r.removeAllFragments():r.hasParts(e.type)&&(r.detectPartialFragments({frag:e,part:null,stats:e.stats,id:e.type}),r.getState(e)===Ti&&r.removeFragment(e))},r.checkLiveUpdate=function(e){if(e.updated&&!e.live){var t=this.fragmentTracker,r=e.fragments[e.fragments.length-1];if(r.endList&&!t.isEndListAppended(this.playlistType)){var i=t.getPartialFragment(r.end);i&&(t.removeFragment(i),t.fragBuffered(r,!0))}t.detectPartialFragments({frag:r,part:null,stats:r.stats,id:r.type})}e.fragments[0]||(e.deltaUpdateFailed=!0)},r.waitForLive=function(e){var t=e.details;return(null==t?void 0:t.live)&&"EVENT"!==t.type&&(this.levelLastLoaded!==e||t.expired)},r.flushMainBuffer=function(e,t,r){if(void 0===r&&(r=null),e-t){var i={startOffset:e,endOffset:t,type:r};this.hls.trigger(O.BUFFER_FLUSHING,i)}},r._loadInitSegment=function(e){var t=this,r=this.hls;return this.initFragmentLoader.abort(),r.trigger(O.FRAG_LOADING,{frag:e,targetBufferTime:0}),this.initFragmentLoader.load(e).then((function(e){var r,i=e.frag;if(!t.levels||!H(i,null==(r=t.fragCurrent)?void 0:r.initSegment))throw new Mi({type:x.NETWORK_ERROR,details:w.INTERNAL_ABORTED,error:new Error("init load aborted"),fatal:!1,frag:i,networkDetails:null});return e})).then((function(e){var i=e.frag,a=e.payload,n=i.decryptdata;if(a&&a.byteLength>0&&null!=n&&n.key&&n.iv&&Or(n.method)){var s=self.performance.now(),o=wi(i,null,t.iframesOnly).decryptRange;return t.decrypter.decrypt(new Uint8Array(a),n.key.buffer,n.iv.buffer,Mr(n.method),o).catch((function(e){throw r.trigger(O.ERROR,{type:x.MEDIA_ERROR,details:w.FRAG_DECRYPT_ERROR,fatal:!1,error:e,reason:e.message,frag:i}),t.fragmentLoader.abort(),e})).then((function(a){var n=self.performance.now();return r.trigger(O.FRAG_DECRYPTED,{frag:i,part:null,payload:a,stats:{tstart:s,tdecrypt:n}}),e.payload=a,t.completeInitSegmentLoad(e)}))}return t.completeInitSegmentLoad(e)}))},r.completeInitSegmentLoad=function(e){var t=e.frag.stats;e.frag.data=new Uint8Array(e.payload),t.parsing.start=t.buffering.start=self.performance.now(),t.parsing.end=t.buffering.end=self.performance.now()},r.loadInitSegmentIfNeeded=function(e){var t,r=this,i=e.initSegment;if(!this.bitrateTest&&G(e)&&i&&!i.data)return(!i.encrypted||null!=(t=i.decryptdata)&&t.key?this._loadInitSegment(i):this.keyLoader.load(i).then((function(){return r._loadInitSegment(i)}))).catch((function(t){if(r.state===Hi.STOPPED||r.state===Hi.ERROR)throw t;throw"data"in t&&(t.data.frag=e,r.fragmentLoader.abort(),r.handleFragLoadError(t)),r.resetFragmentLoading(e),t}))},r.unhandledEncryptionError=function(e,t){var r,i,a=e.tracks;if(a&&!t.encrypted&&(null!=(r=a.audio)&&r.encrypted||null!=(i=a.video)&&i.encrypted)&&(!this.config.emeEnabled||!this.keyLoader.emeController)){var n=this.media,s=new Error("EME not supported (light build)");return this.warn(s.message),!(!n||n.mediaKeys)&&(this.hls.trigger(O.ERROR,{type:x.KEY_SYSTEM_ERROR,details:w.KEY_SYSTEM_NO_KEYS,fatal:!0,error:s,frag:t}),this.resetTransmuxer(),!0)}return!1},r.fragContextChanged=function(e){var t=this.fragCurrent;return!e||!H(e,t)},r.fragBufferedComplete=function(e,t){var r=this.mediaBuffer?this.mediaBuffer:this.media;if(this.log("Buffered "+e.type+" sn: "+e.sn+(t?" part: "+t.index:"")+" of "+this.fragInfo(e,!1,t)+" > buffer:"+(r?cr(dr.getBuffered(r)):"(detached)")+")"),G(e)){var i;if(e.type!==N.SUBTITLE){var a=e.elementaryStreams;if(!Object.keys(a).some((function(e){return!!a[e]})))return void(this.state=Hi.IDLE)}var n=null==(i=this.levels)?void 0:i[e.level];null!=n&&n.fragmentError&&(t||this.fragmentTracker.getState(e)!==Ti)&&(this.log("Resetting level fragment error count of "+n.fragmentError+" on frag buffered"),n.fragmentError=0,e.stats.retry=0)}this.state=Hi.IDLE},r._handleFragmentLoadComplete=function(e){var t=this.transmuxer;if(t){var r=e.frag,i=e.part,a=e.partsLoaded,n=!a||0===a.length||a.some((function(e){return!e})),s=wi(r,i).decryptRange,o=new Ni(r.level,r.sn,r.stats.chunkCount+1,0,i?i.index:-1,!n,r.duration,this.iframesOnly,s);this.log("load complete "+r.type+" sn: "+r.sn+(i?" part: "+i.index:"")+" of "+this.fragInfo(r,!1,i)),t.flush(o)}},r._handleFragmentLoadProgress=function(e){},r.loadKeyFor=function(e,t,r){var i,a=this,n=null;return!e.encrypted||null!=(i=e.decryptdata)&&i.key?e.encrypted||(n=this.keyLoader.loadClear(e,t.encryptedFragments,this.startFragRequested))&&this.log("[eme] blocking frag sn: "+e.sn+" load until media-keys acquired"):(this.log("Loading key for "+e.sn+" of ["+t.startSN+"-"+t.endSN+"], "+this.playlistLabel()+" "+e.level),this.state=Hi.KEY_LOADING,n=this.keyLoader.load(e,r).then((function(e){if(!a.fragContextChanged(e.frag))return a.hls.trigger(O.KEY_LOADED,e),a.state===Hi.KEY_LOADING&&(a.state=Hi.IDLE),e})),this.hls.trigger(O.KEY_LOADING,{frag:e})),n},r._doFragLoad=function(e,t,r,i){var a=this;void 0===r&&(r=null),this.fragCurrent=e;var n=t.details;if(!this.levels||!n)throw new Error("frag load aborted, missing level"+(n?"":" detail")+"s");var s,o=this.fragPrevious;if(!V(e,o)){var l=this.shouldLoadParts(t.details,e.end);l!==this.loadingParts&&(this.log("LL-Part loading "+(l?"ON":"OFF")+" loading sn "+(null==o?void 0:o.sn)+"->"+e.sn),this.loadingParts=l)}if(r=Math.max(e.start,r||0),this.loadingParts){var u=n.partList;if(u&&i){r>n.fragmentEnd&&n.fragmentHint&&(e=n.fragmentHint);var d=this.getNextPart(u,e,r);if(d>-1){var h=u[d];if(!V(e=this.fragCurrent=h.fragment,o)&&!this.shouldLoadParts(t.details,e.end))return this.log("LL-Part loading OFF @"+this.playhead+" next part: "+this.fragInfo(e,!1,h)),this.loadingParts=!1,Promise.resolve(null);if(!this.filterReplacedPrimary(h,t.details))return Promise.resolve(null);var f,c=this.loadKeyFor(e,n);if(this.fragContextChanged(e))return Promise.resolve(null);this.log("Loading "+e.type+" sn: "+e.sn+" part: "+h.index+" ("+d+"/"+(u.length-1)+") of "+this.fragInfo(e,!1,h)+" cc: "+e.cc+" ["+n.startSN+"-"+n.endSN+"], target: "+r+(h.byteRange.length?" range:"+h.byteRange.join("-"):"")),this.nextLoadPosition=h.start+h.duration,this.state=Hi.FRAG_LOADING;var g=this.loadInitSegmentIfNeeded(e);if(c||g){var v=!!c;f=Promise.all([c,g]).then((function(r){var n=r[0];return!v||n&&!a.fragContextChanged(n.frag)?a.doFragPartsLoad(e,h,t,i):null})).catch((function(e){return a.handleFragLoadError(e)}))}else f=this.doFragPartsLoad(e,h,t,i).catch((function(e){return a.handleFragLoadError(e)}));return this.hls.trigger(O.FRAG_LOADING,{frag:e,part:h,targetBufferTime:r}),null===this.fragCurrent?Promise.reject(new Error("frag load aborted, context changed in FRAG_LOADING parts")):f}if(!e.url||this.loadedEndOfParts(u,r))return Promise.resolve(null)}}if(this.loadingParts)this.log("LL-Part loading OFF after next part miss @"+r+" Check buffer at sn: "+e.sn+" loaded parts: "+(null==(s=n.partList)?void 0:s.filter((function(e){return e.loaded})).map((function(e){return"["+e.start+"-"+e.end+"]"})))),this.loadingParts=!1;else if(!e.url)return Promise.resolve(null);if(!this.filterReplacedPrimary(e,t.details))return Promise.resolve(null);var m,p=this.loadInitSegmentIfNeeded(e),y=this.loadKeyFor(e,n,p);if(this.fragContextChanged(e))return this.log("Context changed in KEY_LOADING sn: "+e.sn+" "+e.relurl+" > "+(null==(m=this.fragCurrent)?void 0:m.relurl)),null==p||p.catch((function(){return null})),null==y||y.catch((function(){return null})),Promise.resolve(null);this.log("Loading "+e.type+" sn: "+e.sn+" of "+this.fragInfo(e,!1)+" cc: "+e.cc+" ["+n.startSN+"-"+n.endSN+"], target: "+r+(e.byteRange.length?" range:"+e.byteRange.join("-"):"")),_(e.sn)&&!this.bitrateTest&&(this.nextLoadPosition=e.start+e.duration),this.state=Hi.FRAG_LOADING;var E,T=this.config.progressive&&e.type!==N.SUBTITLE;return E=T&&y?y.then((function(t){return!t||a.fragContextChanged(t.frag)?null:a.fragmentLoader.load(e,a.iframesOnly,i,p)})).catch((function(e){return a.handleFragLoadError(e)})):Promise.all([this.fragmentLoader.load(e,this.iframesOnly,T?i:void 0,T?p:void 0),y,p]).then((function(e){var t=e[0];return!T&&i&&i(t),t})).catch((function(e){return a.handleFragLoadError(e)})),this.hls.trigger(O.FRAG_LOADING,{frag:e,targetBufferTime:r}),null===this.fragCurrent?Promise.reject(new Error("frag load aborted, context changed in FRAG_LOADING")):Promise.all([E,p]).then((function(e){return e[0]}))},r.doFragPartsLoad=function(e,t,r,i){var a=this;return new Promise((function(n,s){var o,l=[],u=null==(o=r.details)?void 0:o.partList,d=function(t){a.fragmentLoader.loadPart(e,t,i).then((function(i){l[t.index]=i;var s=i.part;a.hls.trigger(O.FRAG_LOADED,i);var o=ui(r.details,e.sn,t.index+1)||di(u,e.sn,t.index+1);if(!o)return n({frag:e,part:s,partsLoaded:l});d(o)})).catch(s)};d(t)}))},r.handleFragLoadError=function(e){if("data"in e){var t=e.data;t.frag&&t.details===w.INTERNAL_ABORTED?this.handleFragLoadAborted(t.frag,t.part):t.frag&&t.type===x.KEY_SYSTEM_ERROR&&!t.fatal&&(t.frag.abortRequests(),this.resetStartWhenNotLoaded(),this.resetFragmentLoading(t.frag)),this.hls.trigger(O.ERROR,t)}else this.hls.trigger(O.ERROR,{type:x.OTHER_ERROR,details:w.INTERNAL_EXCEPTION,err:e,error:e,fatal:!0});return null},r._handleTransmuxerFlush=function(e){var t=this.getCurrentContext(e);if(t&&this.state===Hi.PARSING){var r=t.frag,i=t.part,a=t.level,n=self.performance.now();r.stats.parsing.end=n,i&&(i.stats.parsing.end=n);var s=this.getLevelDetails(),o=s&&r.sn>s.endSN||this.shouldLoadParts(s,r.end);o!==this.loadingParts&&(this.log("LL-Part loading "+(o?"ON":"OFF")+" after parsing segment ending @"+r.end),this.loadingParts=o),this.updateLevelTiming(r,i,a,e)}else this.fragCurrent||this.state===Hi.STOPPED||this.state===Hi.ERROR||(this.state=Hi.IDLE)},r.shouldLoadParts=function(e,t){if(this.config.lowLatencyMode){if(!e)return this.loadingParts;if(e.partList&&!this.iframesOnly){var r,i=e.partList[0];if(t>=i.end+((null==(r=e.fragmentHint)?void 0:r.duration)||0)&&this.playhead>i.start-i.fragment.duration)return!0}}return!1},r.getCurrentContext=function(e){var t=this.levels,r=this.fragCurrent,i=e.level,a=e.sn,n=e.part;if(null==t||!t[i])return this.warn("Levels object was unset while buffering fragment "+a+" of "+this.playlistLabel()+" "+i+". The current chunk will not be buffered."),null;var s=t[i],o=s.details,l=n>-1?ui(o,a,n):null,u=l?l.fragment:li(o,a,r);return u?(r&&r!==u&&(u.stats=r.stats),{frag:u,part:l,level:s}):null},r.bufferFragmentData=function(e,t,r,i,a){if(this.state===Hi.PARSING){G(t)&&!this.fragContextChanged(t)&&(this.fragPrevious=t);var n=e.data1,s=e.data2,o=n;if(s&&(o=it(n,s)),o.length){var l=this.initPTS[t.cc],u=l?-l.baseTime/l.timescale:void 0,d={type:e.type,frag:t,part:r,chunkMeta:i,offset:u,parent:t.type,data:o};if(this.hls.trigger(O.BUFFER_APPENDING,d),e.dropped&&e.independent&&!r){if(a)return;this.flushBufferGap(t)}}}},r.flushBufferGap=function(e){var t=this.media;if(t)if(dr.isBuffered(t,t.currentTime)){var r=t.currentTime,i=dr.bufferInfo(t,r,0),a=e.duration,n=Math.min(2*this.config.maxFragLookUpTolerance,.25*a),s=Math.max(Math.min(e.start-n,i.end-n),r+n);e.start-s>n&&this.flushMainBuffer(s,e.start)}else this.flushMainBuffer(0,e.start)},r.getFwdBufferInfo=function(e,t){var r,i=this.playhead,a=this.lastCurrentTime>i||null!=(r=this.media)&&r.paused?0:this.config.maxBufferHole;return this.getFwdBufferInfoAtPos(e,i,t,a)},r.getFwdBufferInfoAtPos=function(e,t,r,i){var a=dr.bufferInfo(e,t,i);if(0===a.len&&void 0!==a.nextStart){var n=this.fragmentTracker.getBufferedFrag(t,r);if(n&&(a.nextStart<=n.end||n.gap)){var s=Math.max(Math.min(a.nextStart,n.end)-t,i);return dr.bufferInfo(e,t,s)}}return a},r.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)},r.exceedsMaxBuffer=function(e,t,r){var i=e.nextStart;if(i&&r.start>i){var a=e.buffered;if(a){for(var n=e.len,s=e.bufferedIndex,o=a.length-1;o>s;o--)a[o].start<r.start&&(n+=a[o].end-a[o].start);return n>=t}}return!1},r.reduceMaxBufferLength=function(e,t){var r=this.config,i=Math.max(Math.min(e-t,r.maxBufferLength),t/2),a=Math.max(e-3*t,r.maxMaxBufferLength/2,i);return a>=i&&(r.maxMaxBufferLength=a,this.warn("Reduce max buffer length to "+a+"s"),!0)},r.getAppendedFrag=function(e){var t=this.fragmentTracker?this.fragmentTracker.getAppendedFrag(e,this.playlistType):null;return t&&"fragment"in t?t.fragment:t},r.getNextFragment=function(e,t){var r=t.fragments,i=r.length;if(!i)return null;var a=this.config,n=t.fragmentStart,s=a.lowLatencyMode&&!!t.partList,o=null;if(t.live){var l=a.initialLiveManifestSize;if(i<l)return this.warn("Not enough fragments to start playback (have: "+i+", need: "+l+")"),null;if(this.startFragRequested||!s||this.loadingParts||(this.log("LL-Part loading ON for initial live fragment"),this.loadingParts=!0),!t.PTSKnown&&!this.startFragRequested&&-1===this.startPosition||e<n){var u;o=this.getInitialLiveFragment(t);var d,h,f=this.config.startPosition,c=this.hls.startPosition,g=this.hls.liveSyncPosition,v=(null==(u=o)?void 0:u.start)||0;if(-1!==c&&c>=n?(d=c,h=c===f?"config":"next load start"):g?(d=g,h="live edge"):(d=e,h="buffer pos"),d<v&&(d=v,h="live frag start"),d<n&&(d=n,h="playlist start"),this.startPosition!=d||this.nextLoadPosition!=d){var m=t.live&&!this.filterReplacedPrimary(o,t)?-1:d;this.log("Setting startPosition to "+m+" next load "+d+" "+(-1===c?"":"(from "+f+") ")+"based on "+h+". live edge: "+g+" live frag start: "+v+" playlist start: "+n+" buffer pos: "+e),this.startPosition=m,this.nextLoadPosition=d}}}else e<=n&&(o=r[0]);if(!o){var p=this.loadingParts?t.partEnd:t.fragmentEnd;o=this.getFragmentAtPosition(e,p,t)}var y,E,T=this.filterReplacedPrimary(o,t);return!T&&o&&(y=t,(T=(E=o.sn)===y.endSN&&y.fragmentHint?y.fragmentHint:y.fragments[1+E-y.startSN]||null)&&(this.nextLoadPosition=T.start)),T},r.isLoopLoading=function(e,t){if(this.nextLoadPosition<=t)return!1;var r=this.fragmentTracker.getState(e);if(r===Si)return!0;if(r===Ti){if(e.gap)return!0;if(e.stats.retry>1)return!0}return!1},r.getNextFragmentLoopLoading=function(e,t,r,i,a){var n=null;if(e.gap&&(n=this.getNextFragment(this.nextLoadPosition,t))&&!n.gap&&r.nextStart){var s=this.getFwdBufferInfoAtPos(this.mediaBuffer?this.mediaBuffer:this.media,r.nextStart,i,0);if(null!==s&&r.len+s.len>=a){var o=n.sn;return this.loopSn!==o&&(this.log('buffer full after gaps in "'+i+'" playlist starting at sn: '+o),this.loopSn=o),null}}return this.loopSn=void 0,n},r.filterReplacedPrimary=function(e,t){return e?(this.config,e):e},r.getNextPart=function(e,t,r){for(var i=-1,a=!1,n=!0,s=0,o=e.length;s<o;s++){var l=e[s];if(n=n&&!l.independent,i>-1&&r<l.start)break;var u=l.loaded||l.gap;u?i=-1:(a||(l.independent||n)&&l.fragment===t)&&(i=s),a=u&&!l.gap}var d=e[i];return d&&d.fragment!==t&&this.log("Need buffer at "+r+" but next unloaded part starts at "+d.start+" (sn: "+t.sn+" -> "+d.fragment.sn+")"),i},r.loadedEndOfParts=function(e,t){for(var r,i=e.length;i--;){if(!(r=e[i]).loaded)return!1;if(t>r.start)return!0}return!1},r.getInitialLiveFragment=function(e){var t=e.fragments,r=this.fragPrevious,i=null;if(r){if(e.hasProgramDateTime&&(i=function(e,t,r){if(null===t||!Array.isArray(e)||!e.length||!_(t))return null;if(t<(e[0].programDateTime||0))return null;if(t>=(e[e.length-1].endProgramDateTime||0))return null;for(var i=0;i<e.length;++i){var a=e[i];if(qt(t,r,a))return a}return null}(t,r.endProgramDateTime,this.config.maxFragLookUpTolerance),i&&this.log("Live playlist, switching playlist, load frag with same PDT: "+r.programDateTime)),!i){var a=r.sn+1;if(a>=e.startSN&&a<=e.endSN){var n=t[a-e.startSN];r.cc===n.cc&&(i=n,this.log("Live playlist, switching playlist, load frag with next SN: "+i.sn))}i||(i=function(e,t,r){if(e&&e.startCC<=t&&e.endCC>=t){var i,a=e.fragments,n=e.fragmentHint;return n&&(a=a.concat(n)),Yt(a,(function(e){return e.cc<t?1:e.cc>t?-1:(i=e,e.end<=r?1:e.start>r?-1:0)})),i||null}return null}(e,r.cc,r.end),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?e.fragmentEnd:e.edge,e))}return i},r.getFragmentAtPosition=function(e,t,r){var i,a,n=this.config,s=this.fragPrevious,o=r.fragments,l=r.endSN,u=r.fragmentHint,d=n.maxFragLookUpTolerance,h=r.partList,f=!!(this.loadingParts&&null!=h&&h.length&&u);if(f&&!this.bitrateTest&&h[h.length-1].fragment.sn===u.sn&&(o=o.concat(u),l=u.sn),s&&this.fragmentTracker.getState(s)===yi&&(s=null),i=e<t?Wt(s,o,e,e<this.lastCurrentTime||e>t-d||null!=(a=this.media)&&a.paused||!this.startFragRequested?0:d):o[o.length-1]){var c=i.sn-r.startSN,g=this.fragmentTracker.getState(i);if((g===Si||g===Ti&&i.gap)&&(s=i),V(i,s)&&(!f||h[0].fragment.sn>i.sn||!r.live)){var v=o[c+1];i=i.sn<l&&this.fragmentTracker.getState(v)!==Si?v:null}}return i},r.alignPlaylists=function(e,t,r){var i=e.fragments.length;if(!i)return this.warn("No fragments in live playlist"),0;var a=e.fragmentStart,n=!t,s=e.alignedSliding&&_(a);if(n||!s&&!a){Vi(r,e,this);var o=e.fragmentStart;return this.log("Live playlist sliding: "+o+" start-sn: "+(t?t.startSN:"na")+"->"+e.startSN+" fragments: "+i),o}return a},r.waitForCdnTuneIn=function(e){return e.live&&e.canBlockReload&&e.partTarget&&e.tuneInGoal>Math.max(e.partHoldBack,3*e.partTarget)},r.setStartPosition=function(e,t){var r=this.startPosition;r<t&&(r=-1);var i=this.timelineOffset;if(-1===r){var a=null!==this.startTimeOffset,n=a?this.startTimeOffset:e.startTimeOffset;if(null!==n&&_(n))r=t+n,n<0&&(r+=e.edge),r=Math.min(Math.max(t,r),t+e.totalduration),this.log("Setting startPosition to "+r+" for start time offset "+n+" found in "+(a?"multivariant":"media")+" playlist"),this.startPosition=r;else if(e.live){var s=this.hls.liveSyncPosition;r=s||t,this.log("Setting startPosition to -1 to start at "+(s?"live edge":"playlist start")+" "+r),this.startPosition=-1}else this.log("setting startPosition to 0 by default"),this.startPosition=r=0;this.lastCurrentTime=r+i}this.nextLoadPosition=r+i},r.getLoadPosition=function(){var e,t=this.media,r=0;return null!=(e=this.hls)&&e.hasEnoughToStart&&t&&!this.iframesOnly?r=t.currentTime:this.nextLoadPosition>=0&&(r=this.nextLoadPosition),r},r.handleFragLoadAborted=function(e,t){this.transmuxer&&e.type===this.playlistType&&G(e)&&e.stats.aborted&&(this.log("Fragment "+e.sn+(t?" part "+t.index:"")+" of "+this.playlistLabel()+" "+e.level+" was aborted"),this.resetFragmentLoading(e))},r.resetFragmentLoading=function(e){this.fragCurrent&&(this.fragContextChanged(e)||this.state===Hi.FRAG_LOADING_WAITING_RETRY)||(this.state=Hi.IDLE)},r.onFragmentOrKeyLoadError=function(e,t){if(t.chunkMeta&&!t.frag){var r=this.getCurrentContext(t.chunkMeta);r&&(t.frag=r.frag)}var i=t.frag,a=t.part;if(i&&this.levels&&i.type===e)if(this.fragContextChanged(i)){var n;this.warn("Frag load error must match current frag to retry "+i.relurl+" > "+(null==(n=this.fragCurrent)?void 0:n.relurl))}else{var s=t.details===w.FRAG_GAP;s&&this.fragmentTracker.addAsGap(i);var o=t.errorAction;if(o){var l=o.action,u=o.flags,d=o.retryCount,h=void 0===d?0:d,f=o.retryConfig,c=!!f,g=c&&l===$t,v=c&&!o.resolved&&u&Jt,m=this.hls.latestLevelDetails,p=null==m?void 0:m.live;if(!g&&v&&G(i)&&p&&i.sn<m.endSN&&!X(t))this.resetFragmentErrors(e),a?a.gap=!0:this.treatAsGap(i),o.resolved=!0;else if((g||v)&&h<f.maxNumRetry){var y,E=J(null==(y=t.response)?void 0:y.code),T=Q(f,h);if(this.resetStartWhenNotLoaded(),this.retryDate=self.performance.now()+T,this.state=Hi.FRAG_LOADING_WAITING_RETRY,o.resolved=!0,E)return this.log("Waiting for connection (offline)"),this.retryDate=1/0,void(t.reason="offline");this.warn("Fragment "+i.sn+" of "+e+" "+i.level+" errored with "+t.details+", retrying loading "+(h+1)+"/"+f.maxNumRetry+" in "+T+"ms")}else if(f){if(this.resetFragmentErrors(e),!(h<f.maxNumRetry))return void this.warn(t.details+" reached or exceeded max retry ("+h+")");s||l===Qt||(o.resolved=!0)}else this.state=l===zt?Hi.WAITING_LEVEL:Hi.ERROR;this.tickImmediate()}else this.state=Hi.ERROR}},r.checkRetryDate=function(){var e=self.performance.now(),t=this.retryDate,r=t===1/0;(!t||e>=t||r&&!J(0))&&(r&&this.log("Connection restored (online)"),this.resetStartWhenNotLoaded(),this.state=Hi.IDLE)},r.reduceLengthAndFlushBuffer=function(e){if(this.state===Hi.PARSING||this.state===Hi.PARSED){var t=e.frag,r=e.parent,i=this.getFwdBufferInfo(this.mediaBuffer,r),a=i&&i.len>.5;a&&this.reduceMaxBufferLength(i.len,(null==t?void 0:t.duration)||10);var n=!a;return n?this.warn("Buffer full error while media.currentTime ("+this.playhead+") is not buffered, flush "+r+" buffer"):i.nextStart&&t&&i.nextStart>t.start&&this.flushMainBuffer(i.nextStart,Number.POSITIVE_INFINITY,r===N.AUDIO?r:void 0),t&&(this.fragmentTracker.removeFragment(t),this.nextLoadPosition=t.start),this.resetLoadingState(),n}return!1},r.resetFragmentErrors=function(e){e===N.AUDIO&&(this.fragCurrent=null),this.hls.hasEnoughToStart||(this.startFragRequested=!1),this.state!==Hi.STOPPED&&(this.state=Hi.IDLE)},r.afterBufferFlushed=function(e,t){if(e){var r=dr.getBuffered(e);this.fragmentTracker.detectEvictedFragments(t,r,this.playlistType),this.state===Hi.ENDED&&this.resetLoadingState()}},r.resetLoadingState=function(){this.log("Reset loading state"),this.fragCurrent=null,this.fragPrevious=null,this.state!==Hi.STOPPED&&(this.state=Hi.IDLE)},r.resetStartWhenNotLoaded=function(){if(!this.hls.hasEnoughToStart){this.startFragRequested=!1;var e=this.getLevelDetails();null!=e&&e.live?(this.log("resetting startPosition for live start"),this.startPosition=-1,this.setStartPosition(e,e.fragmentStart),this.resetLoadingState()):this.nextLoadPosition=this.startPosition}},r.resetWhenMissingContext=function(e){this.log("Loading context changed while buffering sn "+e.sn+" of "+this.playlistLabel()+" "+(-1===e.level?"<removed>":e.level)+". This chunk will not be buffered."),this.removeUnbufferedFrags(),this.resetStartWhenNotLoaded(),this.resetLoadingState()},r.removeUnbufferedFrags=function(e){void 0===e&&(e=0),this.fragmentTracker.removeFragmentsInRange(e,1/0,this.playlistType,!1,!0)},r.updateLevelTiming=function(e,t,r,i){var a=this,n=r.details;if(n){if(this.log("update level timing "+e.type+" sn: "+e.sn+(t?" part: "+t.index:"")+" of "+this.fragInfo(e,!1,t)),!Object.keys(e.elementaryStreams).reduce((function(t,s){var o=e.elementaryStreams[s];if(o){var l=o.endPTS-o.startPTS;if(l<=0)return a.warn("Could not parse fragment "+e.sn+" "+s+" duration reliably ("+l+")"),t||!1;var u=i.partial?0:ii(n,e,o.startPTS,o.endPTS,o.startDTS,o.endDTS,a.iframesOnly,a);return a.hls.trigger(O.LEVEL_PTS_UPDATED,{details:n,level:r,drift:u,type:s,frag:e,start:o.startPTS,end:o.endPTS}),!0}return t}),!1)){var s,o,l,u,d=null===(null==(s=this.transmuxer)?void 0:s.error),h=null!=(null==(o=this.transmuxer)?void 0:o.error),f=(null!=(l=null==(u=this.levels)?void 0:u.length)?l:0)>1;if((0===r.fragmentError||d&&(r.fragmentError<2||e.endList)||h&&f)&&this.treatAsGap(e,r),d){var c=new Error("Found no media in "+this.playlistLabel()+" "+e.level+" "+(t?"part: "+t.index+" of ":"")+"sn: "+e.sn+" at playlist time: "+e.start+". Resetting transmuxer to fallback to playlist timing");if(this.warn(c.message),this.hls.trigger(O.ERROR,{type:x.MEDIA_ERROR,details:w.FRAG_PARSING_ERROR,fatal:!1,error:c,frag:e,reason:"Found no media in msn "+e.sn+" of "+this.playlistLabel()+' "'+r.url+'"'}),!this.hls)return;this.resetTransmuxer()}}this.state=Hi.PARSED,this.log("Parsed "+e.type+" sn: "+e.sn+(t?" part: "+t.index:"")+" of "+this.fragInfo(e,!1,t)),this.hls.trigger(O.FRAG_PARSED,{frag:e,part:t,chunkMeta:i})}else this.warn("level.details undefined")},r.playlistLabel=function(){return this.playlistType+" playlist"},r.fragInfo=function(e,t,r){var i,a;return void 0===t&&(t=!0),this.playlistLabel()+" "+e.level+" ("+(r?"part":"frag")+":["+(null!=(i=t&&!r?e.startPTS:(r||e).start)?i:NaN)+"-"+(null!=(a=t&&!r?e.endPTS:(r||e).end)?a:NaN)+"]"+(r&&"main"===e.type?"INDEPENDENT="+(r.independent?"YES":"NO"):"")+")"},r.treatAsGap=function(e,t){t&&t.fragmentError++,this.fragmentTracker.addAsGap(e)},r.resetTransmuxer=function(){var e;null==(e=this.transmuxer)||e.reset()},r.isFragmentNearlyDownloaded=function(e){var t,r=null==(t=e.loader)?void 0:t.stats;if(!r)return!1;var i=r.loading.first>0,a=(r.total-r.loaded)/(this.hls.bandwidthEstimate||this.hls.config.abrEwmaDefaultEstimate);return i&&a<=.15},r.recoverWorkerError=function(e){"demuxerWorker"===e.event&&(this.fragmentTracker.removeAllFragments(),this.transmuxer&&(this.transmuxer.destroy(),this.transmuxer=null),this.resetStartWhenNotLoaded(),this.resetLoadingState())},r.calculateOptimalSwitchPoint=function(e,t){var r=0,i=this.hls,a=this.media,n=this.config,s=this.levels,o=this.playlistType,l=this.getLevelDetails();if(a&&!a.paused&&s){var u,d=o===N.AUDIO?ce.estimatedAudioBitrate(e.audioCodec,128e3):e.maxBitrate,h=1+i.ttfbEstimate/1e3,f=i.bandwidthEstimate*n.abrBandWidthUpFactor;r=h+d*(l&&(this.loadingParts?l.partTarget:l.averagetargetduration)||(null==(u=this.fragCurrent)?void 0:u.duration)||6)/f,e.details||(r+=h)}var c=this.playhead;return{fetchdelay:r,okToFlushForwardBuffer:!(null!=l&&l.live)||t.end-c>1.5*r}},r.scheduleTrackSwitch=function(e,t,r){var i=this.media,a=this.playlistType;if(i&&e){var n=r?this.getBufferedFrag(this.playhead+t):null;if(n){var s=this.followingBufferedFrag(n);if(s){var o;this.abortCurrentFrag();var l=s.maxStartPTS?s.maxStartPTS:s.start,u=s.duration,d=Math.max(n.end,l+Math.min(Math.max(u-this.config.maxFragLookUpTolerance,u*(this.couldBacktrack?.5:.125)),u*(this.couldBacktrack?.75:.25)));if(((null==(o=this.getLevelDetails())?void 0:o.fragmentStart)||0)>d)return;var h=a===N.MAIN?null:"audio";this.flushMainBuffer(d,Number.POSITIVE_INFINITY,h),this.cleanupBackBuffer()}}}},r.cleanupBackBuffer=function(){var e=this.media,t=this.playlistType;if(e){var r=this.getAppendedFrag(this.playhead);if(r&&r.start>1){var i=t===N.AUDIO;this.flushMainBuffer(0,r.start-(i?0:1),i?"audio":null)}}},r.getBufferedFrag=function(e){return this.fragmentTracker.getBufferedFrag(e,this.playlistType)},r.followingBufferedFrag=function(e){return e?this.getBufferedFrag(e.end+.5):null},r.abortCurrentFrag=function(){var e=this.fragCurrent;switch(this.fragCurrent=null,e&&(e.abortRequests(),this.fragmentTracker.removeFragment(e)),this.state){case Hi.KEY_LOADING:case Hi.FRAG_LOADING:case Hi.FRAG_LOADING_WAITING_RETRY:case Hi.PARSING:case Hi.PARSED:this.state=Hi.IDLE}this.nextLoadPosition=this.playhead},r.checkFragPlaying=function(){var e=this.media,t=null;if(e&&e.readyState>1&&!1===e.seeking){var r=e.currentTime;if(dr.isBuffered(e,r)?t=this.getAppendedFrag(r):dr.isBuffered(e,r+.1)&&(t=this.getAppendedFrag(r+.1)),t&&(this.backtrackFragment=void 0,!V(t,this.fragPlaying)))return this.fragPlaying=t,!0}return!1},r.getBufferOutput=function(){return null},r.nextLevelSwitch=function(){var e=this.levels,t=this.media,r=this.hls,i=this.config,a=this.playlistType;if(null!=t&&t.readyState&&e&&r&&i){var n=this.getBufferOutput(),s=this.getFwdBufferInfo(n,a);if(!s)return;var o=e[a===N.AUDIO?r.nextAudioTrack:r.nextLoadLevel],l=this.calculateOptimalSwitchPoint(o,s),u=l.fetchdelay,d=l.okToFlushForwardBuffer;this.scheduleTrackSwitch(s,u,d)}this.tickImmediate()},a(t,[{key:"startPositionValue",get:function(){var e=this.nextLoadPosition,t=this.startPosition;if(-1===t&&e){var r=this.getLevelDetails();return null!=r&&r.live?(this.hls.liveSyncPosition||r.fragmentStart)+this.timelineOffset:e}return t}},{key:"bufferingEnabled",get:function(){return this.buffering}},{key:"backtrackFragment",get:function(){},set:function(e){}},{key:"couldBacktrack",get:function(){return!1},set:function(e){}},{key:"inFlightFrag",get:function(){return{frag:this.fragCurrent,state:this.state}}},{key:"timelineOffset",get:function(){var e,t=this.config.timelineOffset;return t?(null==(e=this.getLevelDetails())?void 0:e.appliedTimelineOffset)||t:0}},{key:"primaryPrefetch",get:function(){return this.config,!1}},{key:"playhead",get:function(){var e;if(null!=(e=this.hls)&&e.hasEnoughToStart){var t,r=null==(t=this.media)?void 0:t.currentTime;return _(r)?r:this.lastCurrentTime}return this.getLoadPosition()}},{key:"iframesOnly",get:function(){var e;if(this.playlistType!==N.MAIN)return!1;var t=this.getLevelDetails();return t?t.iframesOnly:null==(e=this.levelLastLoaded)?void 0:e.iframes}},{key:"state",get:function(){return this._state},set:function(e){var t=this._state;t!==e&&(this._state=e,this.log(t+"->"+e))}}])}(Fi),Yi=function(e){function t(t,r){var i;return(i=e.call(this,"gap-controller",t.logger)||this).hls=void 0,i.fragmentTracker=void 0,i.media=null,i.mediaSource=void 0,i.nudgeRetry=0,i.skipRetry=0,i.stallReported=!1,i.stalled=null,i.moved=!1,i.seeking=!1,i.buffered={},i.lastCurrentTime=void 0,i.ended=0,i.waiting=0,i.onMediaPlaying=function(){i.ended=0,i.waiting=0},i.onMediaWaiting=function(){var e;null!=(e=i.media)&&e.seeking||(i.waiting=self.performance.now(),i.tick())},i.onMediaEnded=function(){var e;i.hls&&(i.ended=(null==(e=i.media)?void 0:e.currentTime)||1,i.hls.trigger(O.MEDIA_ENDED,{stalled:!1}))},i.hls=t,i.fragmentTracker=r,i.lastCurrentTime=i.getCurrentTime(),i.registerListeners(),i}u(t,e);var r=t.prototype;return r.registerListeners=function(){var e=this.hls;e&&(e.on(O.MEDIA_ATTACHED,this.onMediaAttached,this),e.on(O.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(O.BUFFER_APPENDED,this.onBufferAppended,this))},r.unregisterListeners=function(){var e=this.hls;e&&(e.off(O.MEDIA_ATTACHED,this.onMediaAttached,this),e.off(O.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(O.BUFFER_APPENDED,this.onBufferAppended,this))},r.destroy=function(){e.prototype.destroy.call(this),this.unregisterListeners(),this.media=this.hls=this.fragmentTracker=null,this.mediaSource=void 0},r.onMediaAttached=function(e,t){this.setInterval(100),this.mediaSource=t.mediaSource;var r=this.media=t.media;hr(r,"playing",this.onMediaPlaying),hr(r,"waiting",this.onMediaWaiting),hr(r,"ended",this.onMediaEnded)},r.onMediaDetaching=function(e,t){this.clearInterval();var r=this.media;r&&(fr(r,"playing",this.onMediaPlaying),fr(r,"waiting",this.onMediaWaiting),fr(r,"ended",this.onMediaEnded),this.media=null),this.mediaSource=void 0},r.onBufferAppended=function(e,t){this.buffered=t.timeRanges},r.getCurrentTime=function(){var e,t=(null==(e=this.hls)?void 0:e.config.timelineOffset)||0;return this.media?Math.max(this.media.currentTime,t):t},r.tick=function(){var e;if(null!=(e=this.media)&&e.readyState&&this.hasBuffered){var t=this.getCurrentTime();this.media.currentTime<t||(this.poll(t,this.lastCurrentTime),this.lastCurrentTime=t)}},r.poll=function(e,t){var r,i,a=null==(r=this.hls)?void 0:r.config;if(a){var n=this.media;if(n){var s=n.seeking,o=this.seeking&&!s,l=!this.seeking&&s,u=n.paused&&!s||n.ended||0===n.playbackRate;if(this.seeking=s,e!==t)return t&&(this.ended=0),this.moved=!0,s||(this.skipRetry=this.nudgeRetry=0,a.nudgeOnVideoHole&&!u&&e>t&&this.nudgeOnVideoHole(e,t)),void(0===this.waiting&&this.stallResolved(e));if(l||o)o&&this.stallResolved(e);else{if(u)return this.skipRetry=this.nudgeRetry=0,this.stallResolved(e),void(!this.ended&&n.ended&&this.hls&&(this.ended=e||1,this.hls.trigger(O.MEDIA_ENDED,{stalled:!1})));if(dr.getBuffered(n).length){var d=dr.bufferInfo(n,e,0),h=this.fragmentTracker;h&&this.hls&&Xi(0,e,h,this.hls);var f=d.nextStart||0;if(s&&h&&this.hls){var c=Wi(this.hls.inFlightFragments,e),g=d.len>2,v=!f||c||f-e>2&&!h.getPartialFragment(e);if(g||v)return;this.moved=!1}var m=null==(i=this.hls)?void 0:i.latestLevelDetails;if(!this.moved&&null!==this.stalled&&h){if(!(d.len>0||f))return;var p=Math.max(f,d.start||0)-e,y=null!=m&&m.live?2*m.targetduration:2,E=qi(e,h);if(p>0&&(p<=y||E))return void(n.paused||this._trySkipBufferHole(E))}var T=a.detectStallWithCurrentTimeMs,S=self.performance.now(),L=this.waiting,R=this.stalled;if(null===R){if(!(L>0&&S-L<T))return void(this.stalled=S);R=this.stalled=L}var A=S-R;if(!s&&(A>=T||L&&this.moved)&&this.hls){var b;if("ended"===(null==(b=this.mediaSource)?void 0:b.readyState)&&(null==m||!m.live)&&Math.abs(e-((null==m?void 0:m.edge)||0))<1){if(this.ended)return;return this.ended=e||1,void this.hls.trigger(O.MEDIA_ENDED,{stalled:!0})}if(this._reportStall(d),!this.media||!this.hls)return;if(n.currentTime!==e){var k=e;return e=n.currentTime,this.moved=!0,void this.log("currentTime changed "+k+" > "+e)}}var D=dr.bufferInfo(n,e,a.maxBufferHole);this._tryFixBufferStall(D,A,e)}else this.skipRetry=this.nudgeRetry=0}}}},r.stallResolved=function(e){var t=this.stalled;if(t&&this.hls&&(this.stalled=null,this.stallReported)){var r=self.performance.now()-t;this.log("playback not stuck anymore @"+e+", after "+Math.round(r)+"ms"),this.stallReported=!1,this.waiting=0,this.hls.trigger(O.STALL_RESOLVED,{})}},r.nudgeOnVideoHole=function(e,t){var r,i=this.buffered.video;if(this.hls&&this.media&&this.fragmentTracker&&null!=(r=this.buffered.audio)&&r.length&&i&&i.length>1&&e>i.end(0)){var a=dr.bufferedInfo(dr.timeRangesToArray(this.buffered.audio),e,0);if(a.len>1&&t>=a.start){var n=dr.timeRangesToArray(i),s=dr.bufferedInfo(n,t,0).bufferedIndex;if(s>-1&&s<n.length-1){var o=dr.bufferedInfo(n,e,0).bufferedIndex,l=n[s].end,u=n[s+1].start;if((-1===o||o>s)&&u-l<1&&e-l<2){var d=new Error("nudging playhead to flush pipeline after video hole. currentTime: "+e+" hole: "+l+" -> "+u+" buffered index: "+o);this.warn(d.message),this.media.currentTime+=1e-6;var h=qi(e,this.fragmentTracker);h&&"fragment"in h?h=h.fragment:h||(h=void 0);var f=dr.bufferInfo(this.media,e,0);this.hls.trigger(O.ERROR,{type:x.MEDIA_ERROR,details:w.BUFFER_SEEK_OVER_HOLE,fatal:!1,error:d,reason:d.message,frag:h,buffer:f.len,bufferInfo:f})}}}}},r._tryFixBufferStall=function(e,t,r){var i,a,n=this.fragmentTracker,s=this.media,o=null==(i=this.hls)?void 0:i.config;if(s&&n&&o){var l=null==(a=this.hls)?void 0:a.latestLevelDetails,u=qi(r,n);if((u||null!=l&&l.live&&r<l.fragmentStart)&&(this._trySkipBufferHole(u)||!this.media))return;var d=e.buffered,h=this.adjacentTraversal(e,r);(d&&d.length>1&&e.len>o.maxBufferHole||e.nextStart&&(e.nextStart-r<o.maxBufferHole||h))&&(t>1e3*o.highBufferWatchdogPeriod||this.waiting)&&(this.warn("Trying to nudge playhead over buffer-hole"),this._tryNudgeBuffer(e))}},r.adjacentTraversal=function(e,t){var r=this.fragmentTracker,i=e.nextStart;if(r&&i){var a=r.getFragAtPos(t,N.MAIN),n=r.getFragAtPos(i,N.MAIN);if(a&&n)return n.sn-a.sn<2}return!1},r._reportStall=function(e){var t=this.hls,r=this.media,i=this.stallReported,a=this.stalled;if(!i&&null!==a&&r&&t){this.stallReported=!0;var n=new Error("Playback stalling at @"+r.currentTime+" due to low buffer ("+Bt(e)+")");this.warn(n.message),t.trigger(O.ERROR,{type:x.MEDIA_ERROR,details:w.BUFFER_STALLED_ERROR,fatal:!1,error:n,buffer:e.len,bufferInfo:e,stalled:{start:a}})}},r._trySkipBufferHole=function(e){var t=this.fragmentTracker,r=this.media,i=this.hls,a=null==i?void 0:i.config;if(!r||!t||!a)return 0;var n=r.currentTime,s=dr.bufferInfo(r,n,0);Xi(0,n,t,i);var o=n<s.start?s.start:s.nextStart;if(o){var l=s.len<=a.maxBufferHole,u=s.len>0&&s.len<1&&r.readyState<3,d=o-n;if(d>0&&(l||u)){if(d>a.maxBufferHole){var h=!1;if(0===n){var f=t.getAppendedFrag(0,N.MAIN);f&&o<f.end&&(h=!0)}if(!h&&e){var c;if(null==(c=i.loadLevelObj)||!c.details)return 0;if(Wi(i.inFlightFragments,o))return 0;for(var g=!1,v=e.end;v<o;){var m=qi(v,t),p=m?m.duration:0;if(!(p>0)){g=!0;break}v+=p}if(g)return 0}}var y=a.nudgeMaxRetry,E=a.skipBufferHolePadding,T=++this.skipRetry>y,S=Math.max(o,n)+E;if(T||(this.warn("skipping hole, adjusting currentTime from "+n+" to "+S),this.moved=!0,r.currentTime=S),null==e||!e.gap||T){var L=new Error(T?"Playhead still not moving after seeking over buffer hole from "+n+" to "+S+" after "+a.nudgeMaxRetry+" attempts.":"fragment loaded with buffer holes, seeking from "+n+" to "+S),R={type:x.MEDIA_ERROR,details:w.BUFFER_SEEK_OVER_HOLE,fatal:T,error:L,reason:L.message,buffer:s.len,bufferInfo:s};e&&("fragment"in e?R.part=e:R.frag=e),i.trigger(O.ERROR,R)}return S}}return 0},r._tryNudgeBuffer=function(e){var t=this.hls,r=this.media,i=this.nudgeRetry,a=null==t?void 0:t.config;if(!r||!a)return 0;var n=r.currentTime;if(this.nudgeRetry++,i<a.nudgeMaxRetry){var s=n+(i+1)*a.nudgeOffset,o=new Error("Nudging 'currentTime' from "+n+" to "+s);this.warn(o.message),r.currentTime=s,t.trigger(O.ERROR,{type:x.MEDIA_ERROR,details:w.BUFFER_NUDGE_ON_STALL,error:o,fatal:!1,buffer:e.len,bufferInfo:e})}else{var l=new Error("Playhead still not moving while enough data buffered @"+n+" after "+a.nudgeMaxRetry+" nudges");this.error(l.message),t.trigger(O.ERROR,{type:x.MEDIA_ERROR,details:w.BUFFER_STALLED_ERROR,error:l,fatal:!0,buffer:e.len,bufferInfo:e})}},a(t,[{key:"hasBuffered",get:function(){return Object.keys(this.buffered).length>0}}])}(Fi);function Wi(e,t){var r=ji(e.main);if(r&&r.start<=t)return r;var i=ji(e.audio);return i&&i.start<=t?i:null}function ji(e){if(!e)return null;switch(e.state){case Hi.IDLE:case Hi.STOPPED:case Hi.ENDED:case Hi.ERROR:return null}return e.frag}function qi(e,t){return t.getAppendedFrag(e,N.MAIN)||t.getPartialFragment(e)}function Xi(e,t,r,i){i.latestLevelDetails,qi(t,r),i.config}var zi=function(e){return e.audioId3="org.id3",e.dateRange="com.apple.quicktime.HLS",e.emsg="https://aomedia.org/emsg/ID3",e.misbklv="urn:misb:KLV:bin:1910.1",e}({});function Qi(e,t,r,i){var a=e.mode;if("disabled"===a&&(e.mode="hidden"),e.cues&&e.cues.length>0)for(var n=function(e,t,r){var i=[],a=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,a=0,n=r;a<=n;)if(t<e[i=Math.floor((n+a)/2)].startTime)n=i-1;else{if(!(t>e[i].startTime&&a<r))return i;a=i+1}return e[a].startTime-t<t-e[n].startTime?a:n}(e,t);if(a>-1)for(var n=a,s=e.length;n<s;n++){var o=e[n];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<n.length;s++)i&&!i(n[s])||e.removeCue(n[s]);"disabled"===a&&(e.mode=a)}function $i(){if("undefined"!=typeof self)return self.VTTCue||self.TextTrackCue}function Zi(e,t,r,i,a){var n=new e(t,r,"");try{n.value=i,a&&(n.type=a)}catch(s){n=new e(t,r,Bt(a?f({type:a},i):i))}return n}var Ji=function(){var e=$i();try{e&&new e(0,Number.POSITIVE_INFINITY,"")}catch(e){return Number.MAX_VALUE}return Number.POSITIVE_INFINITY}(),ea=function(){function e(e){var t=this;this.hls=void 0,this.id3Track=null,this.media=null,this.dateRangeCuesAppended={},this.removeCues=!0,this.assetCue=void 0,this.onEventCueEnter=function(){t.hls&&t.hls.trigger(O.EVENT_CUE_ENTER,{})},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=this.onEventCueEnter=null},t._registerListeners=function(){var e=this.hls;e&&(e.on(O.MEDIA_ATTACHING,this.onMediaAttaching,this),e.on(O.MEDIA_ATTACHED,this.onMediaAttached,this),e.on(O.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(O.MANIFEST_LOADING,this.onManifestLoading,this),e.on(O.FRAG_PARSING_METADATA,this.onFragParsingMetadata,this),e.on(O.BUFFER_FLUSHING,this.onBufferFlushing,this),e.on(O.LEVEL_UPDATED,this.onLevelUpdated,this),e.on(O.LEVEL_PTS_UPDATED,this.onLevelPtsUpdated,this))},t._unregisterListeners=function(){var e=this.hls;e&&(e.off(O.MEDIA_ATTACHING,this.onMediaAttaching,this),e.off(O.MEDIA_ATTACHED,this.onMediaAttached,this),e.off(O.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(O.MANIFEST_LOADING,this.onManifestLoading,this),e.off(O.FRAG_PARSING_METADATA,this.onFragParsingMetadata,this),e.off(O.BUFFER_FLUSHING,this.onBufferFlushing,this),e.off(O.LEVEL_UPDATED,this.onLevelUpdated,this),e.off(O.LEVEL_PTS_UPDATED,this.onLevelPtsUpdated,this))},t.onMediaAttaching=function(e,t){this.media=t.media},t.onMediaAttached=function(){var e,t=null==(e=this.hls)?void 0:e.latestLevelDetails;t&&this.updateDateRangeCues(t)},t.onMediaDetaching=function(e,t){this.media=null,t.transferMedia||(this.id3Track&&(this.id3Track.remove(),this.id3Track=null),this.dateRangeCuesAppended={})},t.onManifestLoading=function(){this.dateRangeCuesAppended={}},t.createTrack=function(e){return r="metadata",i="id3",a="hidden",(n=(t=e).ownerDocument.createElement("track")).kind=r,n.label=i,n.src="data:,WEBVTT",n.track.mode=a,t.appendChild(n),n;var t,r,i,a,n},t.onFragParsingMetadata=function(e,t){if(this.media&&this.hls){var r=this.hls.config,i=r.enableEmsgMetadataCues,a=r.enableID3MetadataCues;if(i||a){var n=t.samples;this.id3Track||(this.id3Track=this.createTrack(this.media));var s=$i();if(s)for(var o=0;o<n.length;o++){var l=n[o].type;if((l!==zi.emsg||i)&&a){var u=Ie(n[o].data),d=n[o].pts,h=d+n[o].duration;h>Ji&&(h=Ji),h-d<=0&&(h=d+.25);for(var f=0;f<u.length;f++){var c=u[f];if(!_e(c)){this.updateId3CueEnds(d,l);var g=Zi(s,d,h,c,l);g&&this.id3Track.track.addCue(g)}}}}}}},t.updateId3CueEnds=function(e,t){var r,i=null==(r=this.id3Track)?void 0:r.track.cues;if(i)for(var a=i.length;a--;){var n=i[a];n.type===t&&n.startTime<e&&n.endTime===Ji&&(n.endTime=e)}},t.onBufferFlushing=function(e,t){var r=t.startOffset,i=t.endOffset,a=t.type,n=this.id3Track,s=this.hls;if(s){var o,l=s.config,u=l.enableEmsgMetadataCues,d=l.enableID3MetadataCues;n&&(u||d)&&(o="audio"===a?function(e){return e.type===zi.audioId3&&d}:"video"===a?function(e){return e.type===zi.emsg&&u}:function(e){return e.type===zi.audioId3&&d||e.type===zi.emsg&&u},Qi(n.track,r,i,o))}},t.onLevelUpdated=function(e,t){var r=t.details;this.updateDateRangeCues(r,!0)},t.onLevelPtsUpdated=function(e,t){Math.abs(t.drift)>.01&&this.updateDateRangeCues(t.details)},t.updateDateRangeCues=function(e,t){var r=this;if(this.hls&&this.media){var i=this.hls.config;i.assetPlayerId,i.timelineOffset;var a=i.enableDateRangeMetadataCues;if(i.interstitialsController,a){var n=$i();if(e.hasProgramDateTime){var s,o=this.id3Track,l=e.dateRanges,u=Object.keys(l),d=this.dateRangeCuesAppended;if(o&&t)if(null!=(s=o.track.cues)&&s.length)for(var h=Object.keys(d).filter((function(e){return-1===u.indexOf(e)})),f=function(){var e,t=h[c],i=null==(e=d[t])?void 0:e.cues;delete d[t],i&&Object.keys(i).forEach((function(e){var t=i[e];if(t){t.removeEventListener("enter",r.onEventCueEnter);try{o.track.removeCue(t)}catch(e){}}}))},c=h.length;c--;)f();else d=this.dateRangeCuesAppended={};var g=e.fragments[e.fragments.length-1];if(0!==u.length&&_(null==g?void 0:g.programDateTime)){this.id3Track||(this.id3Track=this.createTrack(this.media));for(var v=function(){var e=u[m],t=l[e],i=t.startTime,a=d[e],s=(null==a?void 0:a.cues)||{},o=(null==a?void 0:a.durationKnown)||!1,h=Ji,f=t.duration;if(t.endDate&&null!==f)h=i+f,o=!0;else if(t.endOnNext&&!o){var c=u.reduce((function(e,r){if(r!==t.id){var i=l[r];if(i.class===t.class&&i.startDate>t.startDate&&(!e||t.startDate<e.startDate))return i}return e}),null);c&&(h=c.startTime,o=!0)}for(var g,v=Object.keys(t.attr),p=0;p<v.length;p++){var y=v[p];if("ID"!==(g=y)&&"CLASS"!==g&&"CUE"!==g&&"START-DATE"!==g&&"DURATION"!==g&&"END-DATE"!==g&&"END-ON-NEXT"!==g){var E=s[y];if(E)!o||null!=a&&a.durationKnown?Math.abs(E.startTime-i)>.01&&(E.startTime=i,E.endTime=h):E.endTime=h;else if(n){var T=t.attr[y];Ir(y)&&(T=we(T));var S=Zi(n,i,h,{key:y,data:T},zi.dateRange);S&&(S.id=e,r.id3Track.track.addCue(S),s[y]=S)}}}d[e]={cues:s,dateRange:t,durationKnown:o}},m=0;m<u.length;m++)v()}}}}},e}(),ta=function(){function e(e){var t=this;this.hls=void 0,this.media=null,this.currentTime=0,this.stallCount=0,this._latency=null,this._targetLatencyUpdated=!1,this.onTimeupdate=function(){var e=t.media,r=t.levelDetails;if(e&&r&&t.hls){t.currentTime=e.currentTime;var i=t.hls.config,a=t.computeLatency();if(null!==a){t._latency=a;var n=i.lowLatencyMode,s=i.maxLiveSyncPlaybackRate;if(n&&1!==s&&r.live){var o=t.targetLatency;if(null!==o){var l=a-o;if(l<Math.min(t.maxLatency,o+r.targetduration)&&l>.05&&t.forwardBufferLength>1){var u=Math.min(2,Math.max(1,s)),d=Math.round(2/(1+Math.exp(-.75*l-t.edgeStalled))*20)/20,h=Math.min(u,Math.max(1,d));t.changeMediaPlaybackRate(e,h)}else 1!==e.playbackRate&&0!==e.playbackRate&&t.changeMediaPlaybackRate(e,1)}}}}},this.hls=e,this.registerListeners()}var t=e.prototype;return t.destroy=function(){this.unregisterListeners(),this.onMediaDetaching(),this.hls=null},t.registerListeners=function(){var e=this.hls;e&&(e.on(O.MEDIA_ATTACHED,this.onMediaAttached,this),e.on(O.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(O.MANIFEST_LOADING,this.onManifestLoading,this),e.on(O.LEVEL_UPDATED,this.onLevelUpdated,this),e.on(O.ERROR,this.onError,this),e.on(O.INTERSTITIAL_ASSET_STARTED,this.onAssetStarted,this))},t.unregisterListeners=function(){var e=this.hls;e&&(e.off(O.MEDIA_ATTACHED,this.onMediaAttached,this),e.off(O.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(O.MANIFEST_LOADING,this.onManifestLoading,this),e.off(O.LEVEL_UPDATED,this.onLevelUpdated,this),e.off(O.ERROR,this.onError,this),e.off(O.INTERSTITIAL_ASSET_STARTED,this.onAssetStarted,this))},t.onMediaAttached=function(e,t){this.media=t.media,this.media.addEventListener("timeupdate",this.onTimeupdate)},t.onMediaDetaching=function(){this.media&&(this.media.removeEventListener("timeupdate",this.onTimeupdate),this.media=null)},t.onManifestLoading=function(){this._latency=null,this.stallCount=0},t.onLevelUpdated=function(e,t){var r=t.details;r.advanced&&this.onTimeupdate(),!r.live&&this.media&&this.media.removeEventListener("timeupdate",this.onTimeupdate)},t.onAssetStarted=function(e,t){var r,i=this.hls,a=this.media||t.event.appendInPlace&&(null==i||null==(r=i.interstitialsManager)?void 0:r.playerQueue.reduce((function(e,t){return e||t.media}),null));i&&a&&a.playbackRate>1&&a.playbackRate<=i.config.maxLiveSyncPlaybackRate&&this.changeMediaPlaybackRate(a,1)},t.onError=function(e,t){var r;t.details===w.BUFFER_STALLED_ERROR&&(this.stallCount++,this.hls&&null!=(r=this.levelDetails)&&r.live&&this.hls.logger.warn("[latency-controller]: Stall detected, adjusting target latency"))},t.changeMediaPlaybackRate=function(e,t){var r,i;e.playbackRate!==t&&(null==(r=this.hls)||r.logger.debug("[latency-controller]: latency="+this.latency.toFixed(3)+", targetLatency="+(null==(i=this.targetLatency)?void 0:i.toFixed(3))+", forwardBufferLength="+this.forwardBufferLength.toFixed(3)+": adjusting playback rate from "+e.playbackRate+" to "+t),e.playbackRate=t)},t.estimateLiveEdge=function(){var e=this.levelDetails;return null===e?null:e.edge+e.age},t.computeLatency=function(){var e=this.estimateLiveEdge();return null===e?null:e-this.currentTime},a(e,[{key:"levelDetails",get:function(){var e;return(null==(e=this.hls)?void 0:e.latestLevelDetails)||null}},{key:"latency",get:function(){return this._latency||0}},{key:"maxLatency",get:function(){var e,t=null==(e=this.hls)?void 0:e.config;if(!t)return 0;if(void 0!==t.liveMaxLatencyDuration)return t.liveMaxLatencyDuration;var r=this.levelDetails;return r?t.liveMaxLatencyDurationCount*r.targetduration:0}},{key:"targetLatency",get:function(){var e=this.levelDetails;if(null===e||!this.hls)return null;var t=this.hls.config,r=e.holdBack,i=e.partHoldBack,a=e.targetduration,n=t.liveSyncDuration,s=t.liveSyncDurationCount,o=t.lowLatencyMode,l=this.hls.userConfig,u=o&&i||r;(this._targetLatencyUpdated||l.liveSyncDuration||l.liveSyncDurationCount||0===u)&&(u=void 0!==n?n:s*a);var d=a;return u+Math.min(this.stallCount*t.liveSyncOnStallIncrease,d)},set:function(e){this.hls&&(this.stallCount=0,this.hls.config.liveSyncDuration=e,this._targetLatencyUpdated=!0)}},{key:"liveSyncPosition",get:function(){var e=this.estimateLiveEdge(),t=this.targetLatency;if(null===e||null===t||!this.hls)return null;var r=this.levelDetails;if(null===r)return null;var i=r.edge,a=e-t-this.edgeStalled,n=i-r.totalduration,s=i-(this.hls.config.lowLatencyMode&&r.partTarget||r.targetduration);return Math.min(Math.max(n,a),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||!this.hls)return 0;var t=3*(this.hls.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}}])}();function ra(e,t){return 255===e[t]&&240==(246&e[t+1])}function ia(e,t){return 1&e[t+1]?7:9}function aa(e,t){return(3&e[t+3])<<11|e[t+4]<<3|(224&e[t+5])>>>5}function na(e,t){return t+1<e.length&&ra(e,t)}function sa(e,t){if(na(e,t)){var r=ia(e,t);if(t+r>=e.length)return!1;var i=aa(e,t);if(i<=r)return!1;var a=t+i;return a===e.length||na(e,a)}return!1}function oa(e,t,r,i,a){if(!e.samplerate){var n=function(e,t,r,i){var a=t[r+2],n=a>>2&15;if(!(n>12)){var s,o=1+(a>>6&3),l=t[r+3]>>6&3|(1&a)<<2,u=[96e3,88200,64e3,48e3,44100,32e3,24e3,22050,16e3,12e3,11025,8e3,7350][n],d=n;5!==o&&29!==o||(d-=3);var h=(14&d)>>1,f=(1&d)<<7|l<<3,c=0;if(1===o&&n>=6){var g=n-3;s=[(c=5)<<3|h,f|(14&g)>>1,(1&g)<<7|8,0]}else s=[o<<3|h,f];var v="mp4a.40."+(c||o);return fe.log("manifest codec:"+i+", parsed codec:"+v+", channels:"+l+", rate:"+u+" (ADTS object type:"+o+" sampling index:"+n+" esds config:["+s.join(",")+"])"),{config:s,samplerate:u,channelCount:l,codec:v,parsedCodec:v,manifestCodec:i}}var m=new Error("invalid ADTS sampling index:"+n);e.emit(O.ERROR,O.ERROR,{type:x.MEDIA_ERROR,details:w.FRAG_PARSING_ERROR,fatal:!0,error:m,reason:m.message})}(t,r,i,a);if(!n)return;o(e,n)}}function la(e){return 9216e4/e}function ua(e,t,r,i,a){var n,s=i+a*la(e.samplerate),o=function(e,t){var r=ia(e,t);if(t+r<=e.length){var i=aa(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?(n=new Uint8Array(d-u)).set(t.subarray(r+u,t.length),0):n=t.subarray(r+u,r+d);var f={unit:n,pts:s};return h||e.samples.push(f),{sample:f,length:d,missing:h}}var c=t.length-r;return(n=new Uint8Array(c)).set(t.subarray(r,t.length),0),{sample:{unit:n,pts:s},length:c,missing:-1}}function da(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 ha=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=it(this.cachedData,e),this.cachedData=null);var r,i=Te(e,0),a=i?i.length:0,n=this._audioTrack,s=this._id3Track,o=i?Pe(i):void 0,l=e.length;for((null===this.basePTS||0===this.frameIndex&&_(o))&&(this.basePTS=fa(o,t,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:zi.audioId3,duration:Number.POSITIVE_INFINITY});a<l;){if(this.canParse(e,a)){var u=this.appendFrame(n,e,a);u?(this.frameIndex++,this.lastPTS=u.sample.pts,r=a+=u.length):a=l}else ye(e,a)?(i=Te(e,a),s.samples.push({pts:this.lastPTS,dts:this.lastPTS,data:i,type:zi.audioId3,duration:Number.POSITIVE_INFINITY}),r=a+=i.length):a++;if(a===l&&r!==l){var d=e.slice(r);this.cachedData?this.cachedData=it(this.cachedData,d):this.cachedData=d}}return{audioTrack:n,videoTrack:da(),id3Track:s,textTrack:da()}},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:da(),id3Track:this._id3Track,textTrack:da()}},t.destroy=function(){this.cachedData=null,this._audioTrack=this._id3Track=void 0},e}(),fa=function(e,t,r){return _(e)?90*e:9e4*t+(r?9e4*r.baseTime/r.timescale:0)},ca=[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],ga=[44100,48e3,32e3,22050,24e3,16e3,11025,12e3,8e3],va=[[0,72,144,12],[0,0,0,0],[0,72,144,12],[0,144,144,12]],ma=[0,1,1,4];function pa(e,t,r,i,a){if(!(r+24>t.length)){var n=ya(t,r);if(n&&r+n.frameLength<=t.length){var s=i+a*(9e4*n.samplesPerFrame/n.sampleRate),o={unit:t.subarray(r,r+n.frameLength),pts:s,dts:s};return e.config=[],e.channelCount=n.channelCount,e.samplerate=n.sampleRate,e.samples.push(o),{sample:o,length:n.frameLength,missing:0}}}}function ya(e,t){var r=e[t+1]>>3&3,i=e[t+1]>>1&3,a=e[t+2]>>4&15,n=e[t+2]>>2&3;if(1!==r&&0!==a&&15!==a&&3!==n){var s=e[t+2]>>1&1,o=e[t+3]>>6,l=1e3*ca[14*(3===r?3-i:3===i?3:4)+a-1],u=ga[3*(3===r?0:2===r?1:2)+n],d=3===o?1:2,h=va[r][i],f=ma[i],c=8*h*f,g=Math.floor(h*l/u+s)*f,v=ct();return!!v&&v<=87&&2===i&&l>=224e3&&0===o&&(e[t+3]=128|e[t+3]),{sampleRate:u,channelCount:d,frameLength:g,samplesPerFrame:c}}}function Ea(e,t){return 255===e[t]&&224==(224&e[t+1])&&0!=(6&e[t+1])}function Ta(e,t){return t+1<e.length&&Ea(e,t)}function Sa(e,t){if(t+1<e.length&&Ea(e,t)){var r=ya(e,t),i=4;null!=r&&r.frameLength&&(i=r.frameLength);var a=t+i;return a===e.length||Ta(e,a)}return!1}var La=function(){function e(e,t,r){this.keyData=void 0,this.decrypter=void 0,this.keyData=r,this.decrypter=new _i(t,!0)}var t=e.prototype;return t.decryptBuffer=function(e){return this.decrypter.decrypt(e,this.keyData.key.buffer,this.keyData.iv.buffer,xr,{start:0,end:e.byteLength})},t.decryptAacSample=function(e,t,r){var i=this,a=e[t].unit;if(!(a.length<=16)){var n=a.subarray(16,a.length-a.length%16),s=n.buffer.slice(n.byteOffset,n.byteOffset+n.length);this.decryptBuffer(s).then((function(n){var s=new Uint8Array(n);a.set(s,16),i.decrypter.isSync()||i.decryptAacSamples(e,t+1,r)})).catch(r)}},t.decryptAacSamples=function(e,t,r){for(;;t++){if(t>=e.length)return void r();if(!(e[t].unit.length<32||(this.decryptAacSample(e,t,r),this.decrypter.isSync())))return}},t.getAvcEncryptedData=function(e){for(var t=16*Math.floor((e.length-48)/160)+16,r=new Int8Array(t),i=0,a=32;a<e.length-16;a+=160,i+=16)r.set(e.subarray(a,a+16),i);return r},t.getAvcDecryptedUnit=function(e,t){for(var r=new Uint8Array(t),i=0,a=32;a<e.length-16;a+=160,i+=16)e.set(r.subarray(i,i+16),a);return e},t.decryptAvcSample=function(e,t,r,i,a){var n=this,s=ot(a.data),o=this.getAvcEncryptedData(s);this.decryptBuffer(o.buffer).then((function(o){a.data=n.getAvcDecryptedUnit(s,o),n.decrypter.isSync()||n.decryptAvcSamples(e,t,r+1,i)})).catch(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 a=e[t].units;!(r>=a.length);r++){var n=a[r];if(!(n.data.length<=48||1!==n.type&&5!==n.type||(this.decryptAvcSample(e,t,r,i,n),this.decrypter.isSync())))return}}},e}(),Ra=function(e){function t(t,r){var i;return(i=e.call(this)||this).observer=void 0,i.config=void 0,i.sampleAes=null,i.observer=t,i.config=r,i}u(t,e);var r=t.prototype;return r.resetInitSegment=function(t,r,i,a){e.prototype.resetInitSegment.call(this,t,r,i,a),this._audioTrack={container:"audio/adts",type:"audio",id:2,pid:-1,sequenceNumber:0,segmentCodec:"aac",samples:[],manifestCodec:r,duration:a,inputTimeScale:9e4,dropped:0}},t.probe=function(e,t){if(!e)return!1;var r=Te(e,0),i=(null==r?void 0:r.length)||0;if(Sa(e,i))return!1;for(var a=e.length;i<a;i++)if(sa(e,i))return t.log("ADTS sync word found !"),!0;return!1},r.canParse=function(e,t){return function(e,t){return function(e,t){return t+5<e.length}(e,t)&&ra(e,t)&&aa(e,t)<=e.length-t}(e,t)},r.appendFrame=function(e,t,r){oa(e,this.observer,t,r,e.manifestCodec);var i=ua(e,t,r,this.basePTS,this.frameIndex);if(0===(null==i?void 0:i.missing))return i},r.demuxSampleAes=function(e,t,r){var i=this.demux(e,r),a=this.sampleAes=new La(this.observer,this.config,t);return new Promise((function(e){a.decryptAacSamples(i.audioTrack.samples,0,(function(){e(i)}))}))},t}(ha),Aa=function(e){function t(){return e.apply(this,arguments)||this}u(t,e);var r=t.prototype;return r.resetInitSegment=function(t,r,i,a){e.prototype.resetInitSegment.call(this,t,r,i,a),this._audioTrack={container:"audio/mpeg",type:"audio",id:2,pid:-1,sequenceNumber:0,segmentCodec:"mp3",samples:[],manifestCodec:r,duration:a,inputTimeScale:9e4,dropped:0}},t.probe=function(e){if(!e)return!1;var t=Te(e,0),r=(null==t?void 0:t.length)||0;if(t&&11===e[r]&&119===e[r+1]&&void 0!==Pe(t)&&function(e,t){var r=0,i=5;t+=i;for(var a=new Uint32Array(1),n=new Uint32Array(1),s=new Uint8Array(1);i>0;){s[0]=e[t];var o=Math.min(i,8),l=8-o;n[0]=4278190080>>>24+l<<l,a[0]=(s[0]&n[0])>>l,r=r?r<<o|a[0]:a[0],t+=1,i-=o}return r}(e,r)<=16)return!1;for(var i=e.length;r<i;r++)if(Sa(e,r))return fe.log("MPEG Audio sync word found !"),!0;return!1},r.canParse=function(e,t){return function(e,t){return Ea(e,t)&&4<=e.length-t}(e,t)},r.appendFrame=function(e,t,r){if(null!==this.basePTS)return pa(e,t,r,this.basePTS,this.frameIndex)},t}(ha),ba=/\/emsg[-/]ID3/i,ka=function(){function e(e,t,r,i){void 0===i&&(i=fe),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.initData=void 0,this.logger=void 0,this.config=t,this.logger=i}var t=e.prototype;return t.resetTimeStamp=function(){},t.resetInitSegment=function(e,t,r,i){var a=this.videoTrack=da("video",1),n=this.audioTrack=da("audio",1),s=this.txtTrack=da("text",1);if(this.id3Track=da("id3",1),this.initData=void 0,this.timeOffset=0,null!=e&&e.byteLength){var o=this.initData=Xe(e);if(o.video){var l=o.video,u=l.id,d=l.timescale,h=l.codec,f=l.supplemental;a.id=u,a.timescale=s.timescale=d,a.codec=h,a.supplemental=f}if(o.audio){var c=o.audio,g=c.id,v=c.timescale,m=c.codec;n.id=g,n.timescale=v,n.codec=m}s.id=Be.text,a.sampleDuration=0,a.duration=n.duration=i}},t.resetContiguity=function(){this.remainderData=null},e.probe=function(e){return function(e,t){for(var r=e.byteLength,i=0;i<r;){var a=Ge(e,i);if(a>8&&Ge(e,i+4)===Ne[t])return!0;i=a>1?i+a:r}return!1}(e,"moof")},t.demux=function(e,t,r){this.timeOffset=t;var i=e,a=this.videoTrack,n=this.txtTrack;if(this.config.progressive){this.remainderData&&(i=it(this.remainderData,e));var s=function(e){var t={valid:null,remainder:null},r=We(e,["moof"]);if(r.length<2)return t.remainder=e,t;var i=r[r.length-1];return t.valid=e.slice(0,i.byteOffset-8),t.remainder=e.slice(i.byteOffset-8),t}(i);this.remainderData=s.remainder,a.samples=s.valid||new Uint8Array}else a.samples=i;var o=this.extractID3Track(a,t),l=this.parseFragment(a,n,t,r);return{videoTrack:a,audioTrack:this.audioTrack,id3Track:o,textTrack:this.txtTrack,initData:this.initData,sampleData:l}},t.flush=function(e,t){var r=this.timeOffset,i=this.videoTrack,a=this.txtTrack;i.samples=this.remainderData||new Uint8Array,this.remainderData=null;var n=this.extractID3Track(i,this.timeOffset),s=this.parseFragment(i,a,r,t);return{videoTrack:i,audioTrack:da(),id3Track:n,textTrack:da(),initData:this.initData,sampleData:s}},t.parseFragment=function(e,t,r,i){var a=this.initData;if(a&&i){var n=function(e,t,r,i,a){var n=[];return{samples:n,sampleData:{includeSampleDetails:a,tracks:rt(t.samples,r,i,0,{isHEVCFlavor:at(t.codec),samples:n,timeOffset:e,timescale:t.timescale,trackId:t.id})}}}(r,e,a,this.logger,!1),s=n.samples,o=n.sampleData;return t.samples=s,o}t.samples=function(e,t){var r=[],i=t.samples,a=t.timescale,n=t.id,s=!1;return We(i,["moof"]).map((function(o){var l=o.byteOffset-8;We(o,["traf"]).map((function(o){var u=We(o,["tfdt"]).map((function(e){var t=e[0],r=Ge(e,4);return 1===t&&(r*=Math.pow(2,32),r+=Ge(e,8)),r/a}))[0];return void 0!==u&&(e=u),We(o,["tfhd"]).map((function(u){var d=Ge(u,4),h=16777215&Ge(u,0),f=0!=(1&h),c=0,g=0!=(2&h),v=0!=(8&h),m=0,p=0!=(16&h),y=0,E=0!=(32&h),T=!f&&0!=(131072&h),S=8;d===n&&(f?(c=Ge(u,S),S+=4,c*=Math.pow(2,32),c+=Ge(u,S),S+=4):T&&(c=l),g&&(S+=4),v&&(m=Ge(u,S),S+=4),p&&(y=Ge(u,S),S+=4),E&&(S+=4),"video"===t.type&&(s=at(t.codec)),We(o,["trun"]).map((function(n){var o=n[0],l=16777215&Ge(n,0),u=0!=(1&l),d=0,h=0!=(4&l),f=0!=(256&l),g=0,v=0!=(512&l),p=0,E=0!=(1024&l),T=0!=(2048&l),S=0,L=Ge(n,4),R=8;u&&(d=Ge(n,R),R+=4),h&&(R+=4);for(var A=c+d,b=0;b<L;b++){if(f?(g=Ge(n,R),R+=4):g=m,v?(p=Ge(n,R),R+=4):p=y,E&&(R+=4),T&&(S=0===o?Ge(n,R):Ke(n,R),R+=4),t.type===B.VIDEO)for(var k=0;k<p;){var D=Ge(i,A);nt(s,i[A+=4])&&st(i.subarray(A,A+D),s?2:1,e+S/a,r),A+=D,k+=D+4}e+=g/a}})))}))}))})),r}(r,e)},t.extractID3Track=function(e,t){var r=this,i=this.id3Track;if(e.samples.length){var a=We(e.samples,["emsg"]);a&&a.forEach((function(e){var a=function(e){var t,r=e[0],i="",a="",n=0,s=0,o=0,l=0,u=4;if(0===r){var d=He(e,u),h=null!==d?He(e,u+d.length):null;null!==d&&null!==h?(i=d,a=h,n=Ge(e,u+=d.length+h.length),s=Ge(e,u+=4),o=Ge(e,u+=4),l=Ge(e,u+=4),u+=4):(fe.warn("Unterminated "+(null===d?"scheme_id_uri":"value")+" in parsing emsg box: version 0 box ends inside a string, dropping all of its fields"),u=e.length)}else if(1===r){n=Ge(e,u);var f=Ge(e,u+=4),c=Ge(e,u+=4);u+=4,t=Math.pow(2,32)*f+c,C(t)||(t=Number.MAX_SAFE_INTEGER,fe.warn("Presentation time exceeds safe integer limit and wrapped to max safe integer in parsing emsg box")),o=Ge(e,u),l=Ge(e,u+=4);var g=He(e,u+=4),v=null!==g?He(e,u+g.length):null;null!==g&&(i=g,u+=g.length),null!==v?(a=v,u+=v.length):(fe.warn("Unterminated "+(null===g?"scheme_id_uri":"value")+" in parsing emsg box: version 1 box ends inside a string, dropping its "+(null===g?"strings and payload":"value and payload")),u=e.length)}return{schemeIdUri:i,value:a,timeScale:n,presentationTime:t,presentationTimeDelta:s,eventDuration:o,id:l,payload:e.subarray(u,e.byteLength)}}(e);if(ba.test(a.schemeIdUri)){var n=Da(a,t),s=4294967295===a.eventDuration?Number.POSITIVE_INFINITY:a.eventDuration/a.timeScale;s<=.001&&(s=Number.POSITIVE_INFINITY);var o=a.payload;i.samples.push({data:o,len:o.byteLength,dts:n,pts:n,type:zi.emsg,duration:s})}else if(r.config.enableEmsgKLVMetadata){var l=r.config.emsgKLVSchemaUri||zi.misbklv;if(a.schemeIdUri.startsWith(l)){var u=Da(a,t);i.samples.push({data:a.payload,len:a.payload.byteLength,dts:u,pts:u,type:zi.misbklv,duration:Number.POSITIVE_INFINITY})}}}))}return i},t.demuxSampleAes=function(e,t,r){return Promise.reject(new Error("The MP4 demuxer does not support SAMPLE-AES decryption"))},t.destroy=function(){this.config=null,this.remainderData=null,this.videoTrack=this.audioTrack=this.id3Track=this.txtTrack=void 0,this.initData=void 0},e}();function Da(e,t){return _(e.presentationTime)?e.presentationTime/e.timeScale:t+e.presentationTimeDelta/e.timeScale}var Ia=function(){function e(){this.VideoSample=null}var t=e.prototype;return t.createVideoSample=function(e,t,r){return{key:e,frame:!1,pts:t,dts:r,units:[],length:0}},t.getLastNalUnit=function(e){var t,r,i=this.VideoSample;if(i&&0!==i.units.length||(i=e[e.length-1]),null!=(t=i)&&t.units){var a=i.units;r=a[a.length-1]}return r},t.pushAccessUnit=function(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 a=r[i-1];e.pts=a.pts,e.dts=a.dts}t.samples.push(e)}},t.parseNALu=function(e,t,r){var i=t.byteLength;if(0===i)return[];var a=e.naluState||0,n=-1===a?0:a,s=[],o=0,l=0,u=-1,d=0;for(-1===a&&(u=0,d=this.getNALuType(t,0),l=1);l<i;){var h=t.indexOf(1,l);if(-1===h)break;l=h+1;for(var f=h;f>0&&0===t[f-1];)f--;if(!(h-f+(0===f?n:0)<2)){if(u>=0)s.push({data:t.subarray(u,f),type:d});else{var c=this.getLastNalUnit(e.samples);c&&(f>0?c.data=it(c.data,t.subarray(0,f)):n>0&&(c.data=c.data.subarray(0,c.data.byteLength-n)),c.state=0)}if(!(l<i)){o=-1,u=-1;break}u=l,d=this.getNALuType(t,l)}}if(-1!==o){for(var g=i;g>0&&0===t[g-1];)g--;o=i-g+(0===g?n:0)}if(u>=0)s.push({data:t.subarray(u),type:d,state:o});else if(0===s.length&&-1!==o){var v=this.getLastNalUnit(e.samples);v&&(v.data=it(v.data,t),v.state=o)}return e.naluState=o,s},e}(),_a=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),a=Math.min(4,t);if(0===a)throw new Error("no bytes available");i.set(e.subarray(r,r+a)),this.word=new DataView(i.buffer).getUint32(0),this.bitsAvailable=8*a,this.bytesAvailable-=a},t.skipBits=function(e){var t;e=Math.min(e,8*this.bytesAvailable+this.bitsAvailable),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;if(e>32&&fe.error("Cannot read more than 32 bits at a time"),this.bitsAvailable-=t,this.bitsAvailable>0)this.word<<=t;else{if(!(this.bytesAvailable>0))throw new Error("no bits available");this.loadWord()}return(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)},e}(),Ca=function(e){function t(){return e.apply(this,arguments)||this}u(t,e);var r=t.prototype;return r.parsePES=function(e,t,r,i,a){var n,s=this,o=a.iframe,l=this.parseNALu(e,r.data,i),u=this.VideoSample,d=!1;r.data=null,u&&l.length&&!e.audFound&&(this.pushAccessUnit(u,e),u=this.VideoSample=this.createVideoSample(!1,r.pts,r.dts)),l.forEach((function(i){var a,l;switch(i.type){case 1:if(o)break;var h=!1;n=!0;var f,c=i.data;if(d&&c.length>4){var g=s.readSliceType(c);2!==g&&4!==g&&7!==g&&9!==g||(h=!0)}h&&null!=(f=u)&&f.frame&&!u.key&&(s.pushAccessUnit(u,e),u=s.VideoSample=null),u||(u=s.VideoSample=s.createVideoSample(!0,r.pts,r.dts)),u.frame=!0,u.key=h;break;case 5:n=!0,null!=(a=u)&&a.frame&&!u.key&&(s.pushAccessUnit(u,e),u=s.VideoSample=null),u||(u=s.VideoSample=s.createVideoSample(!0,r.pts,r.dts)),u.key=!0,u.frame=!0;break;case 6:if(o)break;n=!0,st(i.data,1,r.pts,t.samples);break;case 7:var v,m;n=!0,d=!0;var p=i.data,y=s.readSPS(p);if(!e.sps||e.width!==y.width||e.height!==y.height||(null==(v=e.pixelRatio)?void 0:v[0])!==y.pixelRatio[0]||(null==(m=e.pixelRatio)?void 0:m[1])!==y.pixelRatio[1]){e.width=y.width,e.height=y.height,e.pixelRatio=y.pixelRatio,e.sps=[p];for(var E=p.subarray(1,4),T="avc1.",S=0;S<3;S++){var L=E[S].toString(16);L.length<2&&(L="0"+L),T+=L}e.codec=T}break;case 8:n=!0,e.pps=[i.data];break;case 9:n=!0,e.audFound=!0,null!=(l=u)&&l.frame&&(s.pushAccessUnit(u,e),u=null),u||(u=s.VideoSample=s.createVideoSample(!1,r.pts,r.dts));break;case 12:n=!0;break;default:n=!1}u&&n&&u.units.push(i)})),i&&u&&(this.pushAccessUnit(u,e),this.VideoSample=null)},r.getNALuType=function(e,t){return 31&e[t]},r.readSliceType=function(e){var t=new _a(e);return t.readUByte(),t.readUEG(),t.readUEG()},r.skipScalingList=function(e,t){for(var r=8,i=8,a=0;a<e;a++)0!==i&&(i=(r+t.readEG()+256)%256),r=0===i?r:i},r.readSPS=function(e){var t,r,i,a=new _a(e),n=0,s=0,o=0,l=0,u=a.readUByte.bind(a),d=a.readBits.bind(a),h=a.readUEG.bind(a),f=a.readBoolean.bind(a),c=a.skipBits.bind(a),g=a.skipEG.bind(a),v=a.skipUEG.bind(a),m=this.skipScalingList.bind(this);u();var p=u();if(d(5),c(3),u(),v(),100===p||110===p||122===p||244===p||44===p||83===p||86===p||118===p||128===p){var y=h();if(3===y&&c(1),v(),v(),c(1),f())for(r=3!==y?8:12,i=0;i<r;i++)f()&&m(i<6?16:64,a)}v();var E=h();if(0===E)h();else if(1===E)for(c(1),g(),g(),t=h(),i=0;i<t;i++)g();v(),c(1);var T=h(),S=h(),L=d(1);0===L&&c(1),c(1),f()&&(n=h(),s=h(),o=h(),l=h());var R=[1,1];if(f()&&f())switch(u()){case 1:R=[1,1];break;case 2:R=[12,11];break;case 3:R=[10,11];break;case 4:R=[16,11];break;case 5:R=[40,33];break;case 6:R=[24,11];break;case 7:R=[20,11];break;case 8:R=[32,11];break;case 9:R=[80,33];break;case 10:R=[18,11];break;case 11:R=[15,11];break;case 12:R=[64,33];break;case 13:R=[160,99];break;case 14:R=[4,3];break;case 15:R=[3,2];break;case 16:R=[2,1];break;case 255:R=[u()<<8|u(),u()<<8|u()]}return{width:Math.ceil(16*(T+1)-2*n-2*s),height:(2-L)*(S+1)*16-(L?2:4)*(o+l),pixelRatio:R}},t}(Ia),Pa=188,xa=function(){function e(e,t,r,i){this.logger=void 0,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._pmtId=-1,this._videoTrack=void 0,this._audioTrack=void 0,this._id3Track=void 0,this._txtTrack=void 0,this._klvPid=-1,this.aacOverFlow=null,this.remainderData=null,this.videoParser=void 0,this.videoIntegrityChecker=null,this.observer=e,this.config=t,this.typeSupported=r,this.logger=i,this.videoParser=null}e.probe=function(t,r){var i=e.syncOffset(t);return i>0&&r.warn("MPEG2-TS detected but first sync word found @ offset "+i),-1!==i},e.syncOffset=function(e){for(var t=e.length,r=Math.min(940,t-Pa)+1,i=0;i<r;){for(var a=!1,n=-1,s=0,o=i;o<t;o+=Pa){if(71!==e[o]||t-o!==Pa&&71!==e[o+Pa]){if(s)return-1;break}if(s++,-1===n&&0!==(n=o)&&(r=Math.min(n+18612,e.length-Pa)+1),a||(a=0===wa(e,o)),a&&s>1&&(0===n&&s>2||o+Pa>r))return n}i++}return-1},e.createTrack=function(e,t){return{container:"video"===e||"audio"===e?"video/mp2t":void 0,type:e,id:Be[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,a,n,s){this.pmtParsed=!1,this._pmtId=-1,this._videoTrack=e.createTrack("video"),this._videoTrack.duration=a,this.videoIntegrityChecker="skip"===this.config.handleMpegTsVideoIntegrityErrors?new Ga(this.logger):null,this._audioTrack=e.createTrack("audio",a),this._id3Track=e.createTrack("id3"),this._txtTrack=e.createTrack("text"),this._audioTrack.segmentCodec="aac",this.videoParser=null,this.aacOverFlow=null,this.remainderData=null,this.audioCodec=r,this.videoCodec=i,t&&this.demux(t,0,s,"SAMPLE-AES"===(null==n?void 0:n.method))},t.resetTimeStamp=function(){},t.resetContiguity=function(){var e=this._audioTrack,t=this._videoTrack,r=this._id3Track;e&&(e.pesData=null),t&&(t.pesData=null),r&&(r.pesData=null),this.aacOverFlow=null,this.remainderData=null},t.demux=function(t,r,i,a,n){var s;void 0===a&&(a=!1),void 0===n&&(n=!1),a||(this.sampleAes=null);var o=this._videoTrack,l=this.videoIntegrityChecker,u=this._audioTrack,d=this._id3Track,h=this._txtTrack,f=i.iframe,c=o.pid,g=o.pesData,v=u.pid,m=d.pid,p=this._klvPid,y=u.pesData,E=d.pesData,T=null,S=null,L=this.pmtParsed,R=this._pmtId,A=t.length;if(this.remainderData&&(A=(t=it(this.remainderData,t)).length,this.remainderData=null),A<Pa&&!n)return this.remainderData=t,{audioTrack:u,videoTrack:o,id3Track:d,textTrack:h};var b=Math.max(0,e.syncOffset(t));(A-=(A-b)%Pa)<t.byteLength&&!n&&(this.remainderData=new Uint8Array(t.buffer,A,t.buffer.byteLength-A));for(var k=0,D=b;D<A;D+=Pa)if(71===t[D]){var I=!!(64&t[D+1]),_=wa(t,D),C=void 0;if((48&t[D+3])>>4>1){if((C=D+5+t[D+4])===D+Pa)continue}else C=D+4;switch(_){case c:I&&(!g||null!=l&&l.isCorrupted||!(s=Ba(g,this.logger))||(this.readyVideoParser(o.segmentCodec),null!==this.videoParser&&this.videoParser.parsePES(o,h,s,!1,i)),g={data:[],size:0},null==l||l.reset(c)),null==l||l.handlePacket(t.subarray(D)),g&&(g.data.push(t.subarray(C,D+Pa)),g.size+=D+Pa-C);break;case v:if(f)break;if(I){if(y&&(s=Ba(y,this.logger)))switch(u.segmentCodec){case"aac":this.parseAACPES(u,s,i);break;case"mp3":this.parseMPEGPES(u,s)}y={data:[],size:0}}y&&(y.data.push(t.subarray(C,D+Pa)),y.size+=D+Pa-C);break;case m:if(f)break;I&&(E&&(s=Ba(E,this.logger))&&this.parseID3PES(d,s),E={data:[],size:0}),E&&(E.data.push(t.subarray(C,D+Pa)),E.size+=D+Pa-C);break;case p:if(f)break;I&&(T&&(s=Ba(T,this.logger))&&this.parseKlvPES(d,s),T={data:[],size:0}),T&&(T.data.push(t.subarray(C,D+Pa)),T.size+=D+Pa-C);break;case 0:I&&(C+=t[C]+1),R=this._pmtId=Oa(t,C);break;case R:I&&(C+=t[C]+1);var P=Ma(t,C,this.typeSupported,a,this.observer,this.logger,this.config,i);(c=P.videoPid)>0&&(o.pid=c,o.segmentCodec=P.segmentVideoCodec),(v=P.audioPid)>0&&(u.pid=v,u.segmentCodec=P.segmentAudioCodec),(m=P.id3Pid)>0&&(d.pid=m),(p=P.klvPid)>0&&(this._klvPid=p),null===S||L||(this.logger.warn("MPEG-TS PMT found at "+D+" after unknown PID '"+S+"'. Backtracking to sync byte @"+b+" to parse all TS packets."),S=null,D=b-188),L=this.pmtParsed=!0;break;case 17:case 8191:break;default:S=_}}else k++;k>0&&Fa(this.observer,new Error("Found "+k+" TS packet/s that do not start with 0x47"),i,void 0,this.logger),o.pesData=g,u.pesData=y,d.pesData=E;var x={audioTrack:u,videoTrack:o,id3Track:d,textTrack:h};return n&&this.extractRemainingSamples(x,i),x},t.flush=function(e,t){var r,i=this.remainderData;return this.remainderData=null,r=i?this.demux(i,0,t,!1,!0):{videoTrack:this._videoTrack,audioTrack:this._audioTrack,id3Track:this._id3Track,textTrack:this._txtTrack},this.extractRemainingSamples(r,t),this.sampleAes?this.decrypt(r,this.sampleAes):r},t.extractRemainingSamples=function(e,t){var r,i=e.audioTrack,a=e.videoTrack,n=e.id3Track,s=e.textTrack,o=a.pesData,l=i.pesData,u=n.pesData,d=this.videoIntegrityChecker;if(!o||null!=d&&d.isCorrupted||!(r=Ba(o,this.logger))?a.pesData=o:(this.readyVideoParser(a.segmentCodec),null!==this.videoParser&&(this.videoParser.parsePES(a,s,r,!0,t),a.pesData=null)),l&&(r=Ba(l,this.logger))){switch(i.segmentCodec){case"aac":this.parseAACPES(i,r,t);break;case"mp3":this.parseMPEGPES(i,r)}i.pesData=null}else null!=l&&l.size&&this.logger.log("last AAC PES packet truncated,might overlap between fragments"),i.pesData=l;u&&(r=Ba(u,this.logger))?(this.parseID3PES(n,r),n.pesData=null):n.pesData=u},t.demuxSampleAes=function(e,t,r,i){var a=this.demux(e,r,i,!0,!this.config.progressive),n=this.sampleAes=new La(this.observer,this.config,t);return this.decrypt(a,n)},t.readyVideoParser=function(e){null===this.videoParser&&"avc"===e&&(this.videoParser=new Ca)},t.decrypt=function(e,t){return new Promise((function(r){var i=e.audioTrack,a=e.videoTrack;i.samples&&"aac"===i.segmentCodec?t.decryptAacSamples(i.samples,0,(function(){a.samples?t.decryptAvcSamples(a.samples,0,0,(function(){r(e)})):r(e)})):a.samples&&t.decryptAvcSamples(a.samples,0,0,(function(){r(e)}))}))},t.destroy=function(){this.observer&&this.observer.removeAllListeners(),this.config=this.logger=this.observer=null,this.aacOverFlow=this.videoParser=this.remainderData=this.sampleAes=null,this._videoTrack=this._audioTrack=this._id3Track=this._txtTrack=void 0,this.videoIntegrityChecker=null},t.parseAACPES=function(e,t,r){var i,a,n,s=0,o=this.aacOverFlow,l=t.data;if(o){this.aacOverFlow=null;var u=o.missing,d=o.sample.unit.byteLength;if(-1===u)l=it(o.sample.unit,l);else{var h=d-u;o.sample.unit.set(l.subarray(0,u),h),e.samples.push(o.sample),s=o.missing}}for(i=s,a=l.length;i<a-1&&!na(l,i);i++);if(i!==s){var f,c=i<a-1;if(f=c?"AAC PES did not start with ADTS header,offset:"+i:"No ADTS header found in AAC PES",Fa(this.observer,new Error(f),r,c,this.logger),!c)return}if(oa(e,this.observer,l,i,this.audioCodec),void 0!==t.pts)n=t.pts;else{if(!o)return void this.logger.warn("[tsdemuxer]: AAC PES unknown PTS");var g=la(e.samplerate);n=o.sample.pts+g}for(var v,m=0;i<a;){if(i+=(v=ua(e,l,i,n,m)).length,v.missing){this.aacOverFlow=v;break}for(m++;i<a-1&&!na(l,i);i++);}},t.parseMPEGPES=function(e,t){var r=t.data,i=r.length,a=0,n=0,s=t.pts;if(void 0!==s)for(;n<i;)if(Ta(r,n)){var o=pa(e,r,n,s,a);if(!o)break;n+=o.length,a++}else n++;else this.logger.warn("[tsdemuxer]: MPEG PES unknown PTS")},t.parseAC3PES=function(e,t){},t.parseID3PES=function(e,t){if(void 0!==t.pts){var r=o({},t,{type:this._videoTrack?zi.emsg:zi.audioId3,duration:Number.POSITIVE_INFINITY});e.samples.push(r)}else this.logger.warn("[tsdemuxer]: ID3 PES unknown PTS")},t.parseKlvPES=function(e,t){var r,i=null!=(r=t.pts)?r:t.dts;if(void 0!==i){var a=t.data;if(a.length<16)this.logger.warn("[tsdemuxer]: KLV PES payload too short");else for(var n=0;n<a.length;){var s;if(n+16>a.length)break;var o=n;if((n+=16)>=a.length)break;var l=a[n];n+=1;var u=0;if(128&l){var d=127&l;if(0===d||d>4||n+d>a.length){this.logger.warn("[tsdemuxer]: Invalid KLV length encoding");break}for(var h=0;h<d;h++)u=u<<8|a[n+h];n+=d}else u=l;if(n+u>a.length){this.logger.warn("[tsdemuxer]: KLV value extends beyond PES payload");break}var f=n+u,c=a.subarray(o,f),g={data:c,len:c.byteLength,pts:i,dts:null!=(s=t.dts)?s:i,type:zi.misbklv,duration:Number.POSITIVE_INFINITY};e.samples.push(g),n=f}}else this.logger.warn("[tsdemuxer]: KLV PES unknown PTS and DTS")},e}();function wa(e,t){return((31&e[t+1])<<8)+e[t+2]}function Oa(e,t){return(31&e[t+10])<<8|e[t+11]}function Ma(e,t,r,i,a,n,s,o){var l={audioPid:-1,videoPid:-1,id3Pid:-1,klvPid:-1,segmentVideoCodec:"avc",segmentAudioCodec:"aac"},u=t+3+((15&e[t+1])<<8|e[t+2])-4;for(t+=12+((15&e[t+10])<<8|e[t+11]);t<u;){var d=wa(e,t),h=(15&e[t+3])<<8|e[t+4];switch(e[t]){case 207:if(!i){Na("ADTS AAC",n);break}case 15:-1===l.audioPid&&(l.audioPid=d);break;case 21:-1===l.id3Pid&&(l.id3Pid=d);break;case 219:if(!i){Na("H.264",n);break}case 27:-1===l.videoPid&&(l.videoPid=d);break;case 3:case 4:r.mpeg||r.mp3?-1===l.audioPid&&(l.audioPid=d,l.segmentAudioCodec="mp3"):n.log("MPEG audio found, not supported in this browser");break;case 193:if(!i){Na("AC-3",n);break}case 129:n.warn("AC-3 in M2TS support not included in build");break;case 6:if(h>0)for(var f=t+5,c=h;c>2;){switch(e[f]){case 106:-1===l.audioPid&&n.warn("AC-3 in M2TS support not included in build");break;case 5:-1===l.klvPid&&s.enableEmsgKLVMetadata&&(l.klvPid=d,n.log("KLV metadata PID found: "+d))}var g=e[f+1]+2;f+=g,c-=g}break;case 194:case 135:return Fa(a,new Error("Unsupported EC-3 in M2TS found"),o,void 0,n),l;case 36:return Fa(a,new Error("Unsupported HEVC in M2TS found"),o,void 0,n),l}t+=h+5}return l}function Fa(e,t,r,i,a){a.warn("parsing error: "+t.message),e.emit(O.ERROR,O.ERROR,{type:x.MEDIA_ERROR,details:w.FRAG_PARSING_ERROR,fatal:!1,levelRetry:i,chunkMeta:r,error:t,reason:t.message})}function Na(e,t){t.log(e+" with AES-128-CBC encryption found in unencrypted stream")}function Ba(e,t){var r,i,a,n,s,o=0,l=e.data;if(!e||0===e.size)return null;for(;l[0].length<19&&l.length>1;)l[0]=it(l[0],l[1]),l.splice(1,1);if(1===((r=l[0])[0]<<16)+(r[1]<<8)+r[2]){if((i=(r[4]<<8)+r[5])&&i>e.size-6)return null;var u=r[7];192&u&&(n=536870912*(14&r[9])+4194304*(255&r[10])+16384*(254&r[11])+128*(255&r[12])+(254&r[13])/2,64&u?n-(s=536870912*(14&r[14])+4194304*(255&r[15])+16384*(254&r[16])+128*(255&r[17])+(254&r[18])/2)>54e5&&(t.warn(Math.round((n-s)/9e4)+"s delta between PTS and DTS, align them"),n=s):s=n);var d=(a=r[8])+9;if(e.size<=d)return null;e.size-=d;for(var h=new Uint8Array(e.size),f=0,c=l.length;f<c;f++){var g=(r=l[f]).byteLength;if(d){if(d>g){d-=g;continue}r=r.subarray(d),g-=d,d=0}h.set(r,o),o+=g}return i&&(i-=a+3),{data:h,pts:n,dts:s,len:i}}return null}var Ua,Ga=function(){function e(e){this.logger=void 0,this.pid=0,this.lastContinuityCounter=-1,this.integrityState="ok",this.logger=e}var t=e.prototype;return t.reset=function(e){this.pid=e,this.lastContinuityCounter=-1,this.integrityState="ok"},t.handlePacket=function(e){if(!(e.byteLength<4)){var t=wa(e,0);if(t===this.pid){var r=(48&e[3])>>4;if(0!==r){var i=15&e[3],a=this.lastContinuityCounter;this.lastContinuityCounter=i;var n=0!=(1&r);if(!(0!=(2&r)&&0!=e[4]&&0!=(128&e[5])||a<0)){var s=n?a+1&15:a;return i!==s?(this.logger.warn("MPEG-TS Continuity Counter check failed for PID='"+t+"', CC="+i+", Expected-CC="+s+" Last-CC="+a),void(this.integrityState="cc-failed")):0!=(128&e[1])?(this.logger.warn("MPEG-TS Packet had TEI flag set for PID='"+t+"'"),void(this.integrityState="tei-bit")):void 0}}}else this.logger.debug("Packet PID mismatch, expected "+this.pid+" got "+t)}},a(e,[{key:"isCorrupted",get:function(){return"ok"!==this.integrityState}}])}(),Va=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}(),Ha={video: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]),audio: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])},Ka=new Uint8Array([0,0,0,0,0,0,0,0]),Ya=new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0]),Wa=new Uint8Array([0,0,0,1,0,0,0,0,0,0,0,0]),ja=new Uint8Array([0,0,0,0,0,0,0,0]),qa=new Uint8Array([0,0,0,0,0,0,0,1]),Xa=new Uint8Array([105,115,111,109]),za=new Uint8Array([97,118,99,49]),Qa=new Uint8Array([0,0,0,1]),$a=function(){function e(){}return e.box=function(e){for(var t=8,r=arguments.length,i=new Array(r>1?r-1:0),a=1;a<r;a++)i[a-1]=arguments[a];for(var n=i.length,s=n;n--;)t+=i[n].byteLength;var o=new Uint8Array(t);for(Ye(o,0,t),Ye(o,4,e),n=0,t=8;n<s;n++)o.set(i[n],t),t+=i[n].byteLength;return o},e.hdlr=function(t){return e.box(Ne.hdlr,Ha[t])},e.mdat=function(t){return e.box(Ne.mdat,t)},e.mdhd=function(t,r){r*=t;var i=Math.floor(r/(Fe+1)),a=Math.floor(r%(Fe+1));return e.box(Ne.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,a>>24,a>>16&255,a>>8&255,255&a,85,196,0,0]))},e.mdia=function(t){return e.box(Ne.mdia,e.mdhd(t.timescale||0,t.duration||0),e.hdlr(t.type),e.minf(t))},e.mfhd=function(t){return e.box(Ne.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(Ne.minf,e.box(Ne.smhd,ja),e.DINF,e.stbl(t)):e.box(Ne.minf,e.box(Ne.vmhd,Wa),e.DINF,e.stbl(t))},e.moof=function(t,r,i){return e.box(Ne.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,[Ne.moov,e.mvhd(t[0].timescale||0,t[0].duration||0)].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,[Ne.mvex].concat(i))},e.mvhd=function(t,r){r*=t;var i=Math.floor(r/(Fe+1)),a=Math.floor(r%(Fe+1)),n=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,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 e.box(Ne.mvhd,n)},e.sdtp=function(t){var r,i,a=t.samples||[],n=new Uint8Array(4+a.length);for(r=0;r<a.length;r++)i=a[r].flags,n[r+4]=i.dependsOn<<4|i.isDependedOn<<2|i.hasRedundancy;return e.box(Ne.sdtp,n)},e.stbl=function(t){return e.box(Ne.stbl,e.stsd(t),e.box(Ne.stts,Ka),e.box(Ne.stsc,Ka),e.box(Ne.stsz,Ya),e.box(Ne.stco,Ka))},e.avc1=function(t){var r,i,a,n=[],s=[];for(r=0;r<t.sps.length;r++)a=(i=t.sps[r]).byteLength,n.push(a>>>8&255),n.push(255&a),n=n.concat(Array.prototype.slice.call(i));for(r=0;r<t.pps.length;r++)a=(i=t.pps[r]).byteLength,s.push(a>>>8&255),s.push(255&a),s=s.concat(Array.prototype.slice.call(i));var o=e.box(Ne.avcC,new Uint8Array([1,n[3],n[4],n[5],255,224|t.sps.length].concat(n).concat([t.pps.length]).concat(s))),l=t.width,u=t.height,d=t.pixelRatio[0],h=t.pixelRatio[1];return e.box(Ne.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(Ne.btrt,new Uint8Array([0,28,156,128,0,45,198,192,0,45,198,192])),e.box(Ne.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,r=t.length;return new Uint8Array([0,0,0,0,3,23+r,0,1,0,4,15+r,64,21,0,0,0,0,0,0,0,0,0,0,0,5,r].concat(t,[6,1,2]))},e.audioStsd=function(e){var t=e.samplerate||0;return new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,e.channelCount||0,0,16,0,0,0,0,t>>8&255,255&t,0,0])},e.mp4a=function(t){return e.box(Ne.mp4a,e.audioStsd(t),e.box(Ne.esds,e.esds(t)))},e.mp3=function(t){return e.box(Ne[".mp3"],e.audioStsd(t))},e.ac3=function(t){return e.box(Ne["ac-3"],e.audioStsd(t),e.box(Ne.dac3,t.config))},e.stsd=function(t){var r=t.segmentCodec;if("audio"===t.type){if("aac"===r)return e.box(Ne.stsd,qa,e.mp4a(t));if("mp3"===r&&"mp3"===t.codec)return e.box(Ne.stsd,qa,e.mp3(t))}else{if(!t.pps||!t.sps)throw new Error("video track missing pps or sps");if("avc"===r)return e.box(Ne.stsd,qa,e.avc1(t))}throw new Error("unsupported "+t.type+" segment codec ("+r+"/"+t.codec+")")},e.tkhd=function(t){var r=t.id,i=(t.duration||0)*(t.timescale||0),a=t.width||0,n=t.height||0,s=Math.floor(i/(Fe+1)),o=Math.floor(i%(Fe+1));return e.box(Ne.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]))},e.traf=function(t,r){var i=e.sdtp(t),a=t.id,n=Math.floor(r/(Fe+1)),s=Math.floor(r%(Fe+1));return e.box(Ne.traf,e.box(Ne.tfhd,new Uint8Array([0,0,0,0,a>>24,a>>16&255,a>>8&255,255&a])),e.box(Ne.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])),e.trun(t,i.length+16+20+8+16+8+8),i)},e.trak=function(t){return t.duration=t.duration||4294967295,e.box(Ne.trak,e.tkhd(t),e.mdia(t))},e.trex=function(t){var r=t.id;return e.box(Ne.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,a,n,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++)n=(a=u[i]).duration,s=a.size,o=a.flags,l=a.cts,f.set([n>>>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 e.box(Ne.trun,f)},e.initSegment=function(t){var r=e.moov(t);return it(e.FTYP,r)},e.hvc1=function(e){return new Uint8Array},e}();function Za(e,t){return function(e,t,r,i){var a=e*t*r;return Math.round(a)}(e,1e3,1/9e4)}function Ja(e){var t=e.baseTime,r=e.timescale;return t/r+" ("+t+"/"+r+") trackId: "+e.trackId}Ua=$a,$a.FTYP=Ua.box(Ne.ftyp,Xa,Qa,Xa,za),$a.DINF=Ua.box(Ne.dinf,Ua.box(Ne.dref,new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,12,117,114,108,32,0,0,0,1])));var en=8589934592;function tn(e,t,r,i){return{duration:t,size:r,cts:i,flags:{isLeading:0,isDependedOn:0,hasRedundancy:0,degradPrio:0,dependsOn:e?2:1,isNonSync:e?0:1,paddingValue:0}}}var rn=function(e){function t(t,r,i,a){var n;return(n=e.call(this,"mp4-remuxer",a)||this).observer=void 0,n.config=void 0,n.typeSupported=void 0,n.ISGenerated=!1,n._initPTS=null,n._initDTS=null,n.nextVideoTs=null,n.nextAudioTs=null,n.videoSampleDuration=null,n.isAudioContiguous=!1,n.isVideoContiguous=!1,n.videoTrackConfig=void 0,n.observer=t,n.config=r,n.typeSupported=i,n.ISGenerated=!1,n}u(t,e);var r=t.prototype;return r.destroy=function(){this.config=this.videoTrackConfig=this._initPTS=this._initDTS=null},r.resetTimeStamp=function(e){var t=this._initPTS;t&&e&&e.trackId===t.trackId&&e.baseTime===t.baseTime&&e.timescale===t.timescale||this.log("Reset initPTS: "+(t?Ja(t):t)+" > "+(e?Ja(e):e)),this._initPTS=this._initDTS=e},r.resetNextTimestamp=function(){this.log("reset next timestamp"),this.isVideoContiguous=!1,this.isAudioContiguous=!1},r.resetInitSegment=function(){this.log("ISGenerated flag reset"),this.ISGenerated=!1,this.videoTrackConfig=void 0},r.getVideoStartPts=function(e){var t=!1,r=e[0].pts,i=e.reduce((function(e,i){var a=i.pts,n=a-e;return n<-4294967296&&(t=!0,n=(a=an(a,r))-e),n>0?e:a}),r);return t&&this.debug("PTS rollover detected"),i},r.remux=function(e,t,r,i,a,n,s,o,l){var u,d,h,f,c,g,v=a,m=a,p=!l.iframe&&e.pid>-1,y=t.pid>-1,E=t.samples.length,T=e.samples.length>0,S=s&&E>0||E>1;if((!p||T)&&(!y||S)||this.ISGenerated||s){if(this.ISGenerated){var L,R,A,b,k=this.videoTrackConfig;(k&&(t.width!==k.width||t.height!==k.height||(null==(L=t.pixelRatio)?void 0:L[0])!==(null==(R=k.pixelRatio)?void 0:R[0])||(null==(A=t.pixelRatio)?void 0:A[1])!==(null==(b=k.pixelRatio)?void 0:b[1]))||!k&&S||null===this.nextAudioTs&&T)&&this.resetInitSegment()}this.ISGenerated||(h=this.generateIS(e,t,a,n));var D,I=this.isVideoContiguous,_=-1;if(S&&(_=function(e){for(var t=0;t<e.length;t++)if(e[t].key)return t;return-1}(t.samples),!I&&this.config.forceKeyFrameOnDiscontinuity))if(g=!0,_>0){this.warn("Dropped "+_+" out of "+E+" video samples due to a missing keyframe");var C=this.getVideoStartPts(t.samples);t.samples=t.samples.slice(_),t.dropped+=_,D=m+=(t.samples[0].pts-C)/t.inputTimeScale}else-1===_&&(this.warn("No keyframe found out of "+E+" video samples"),g=!1);if(this.ISGenerated){if(T&&S&&a){var P=this.getVideoStartPts(t.samples),x=(an(e.samples[0].pts,P)-P)/t.inputTimeScale;v+=Math.max(0,x),m+=Math.max(0,-x)}if(T){if(e.samplerate||(this.warn("regenerate InitSegment as audio detected"),h=this.generateIS(e,t,a,n)),d=this.remuxAudio(e,v,this.isAudioContiguous,n,y||S||o===N.AUDIO?m:void 0,l),S){var w=d?d.endPTS-d.startPTS:0;t.inputTimeScale||(this.warn("regenerate InitSegment as video detected"),h=this.generateIS(e,t,a,n)),u=this.remuxVideo(t,m,I,w,l)}}else S&&(u=this.remuxVideo(t,m,I,0,l));u&&(u.firstKeyFrame=_,u.independent=-1!==_,u.firstKeyFramePTS=D)}}return this.ISGenerated&&this._initPTS&&this._initDTS&&(r.samples.length&&(c=nn(r,a,this._initPTS,this._initDTS)),i.samples.length&&(f=sn(i,a,this._initPTS))),{audio:d,video:u,initSegment:h,independent:g,text:f,id3:c}},r.computeInitPts=function(e,t,r,i){var a=Math.round(r*t),n=an(e,a);if(n<a)for(this.log("Adjusting PTS for rollover in timeline near "+(a-n)/t+" "+i);n<a;)n+=en;return n-a},r.generateIS=function(e,t,r,i){var a,n,s,o=e.samples,l=t.samples,u=this.typeSupported,d={},h=this._initPTS,f=!h||i,c="audio/mp4",g=-1;if(f&&(a=n=1/0),e.config&&o.length){switch(e.timescale=e.samplerate,e.segmentCodec){case"mp3":u.mpeg?(c="audio/mpeg",e.codec=""):u.mp3&&(e.codec="mp3");break;case"ac3":e.codec="ac-3"}if(d.audio={id:"audio",container:c,codec:e.codec,initSegment:"mp3"===e.segmentCodec&&u.mpeg?new Uint8Array(0):$a.initSegment([e]),metadata:{channelCount:e.channelCount}},f){g=e.id,s=e.inputTimeScale;var v=this.computeInitPts(o[0].pts,s,r,"audio");(!h||s!==h.timescale||0===r&&v<h.baseTime)&&(a=n=v)}}if(t.sps&&t.pps&&l.length){if(t.timescale=t.inputTimeScale,d.video={id:"main",container:"video/mp4",codec:t.codec,initSegment:$a.initSegment([t]),metadata:{width:t.width,height:t.height}},f){g=t.id,s=t.inputTimeScale;var m=this.getVideoStartPts(l),p=an(l[0].dts,m),y=this.computeInitPts(p,s,r,"video"),E=this.computeInitPts(m,s,r,"video");(!h||s!==h.timescale||_(a)||0===r&&E<h.baseTime)&&(n=Math.min(n,y),a=Math.min(a,y))}this.videoTrackConfig={width:t.width,height:t.height,pixelRatio:t.pixelRatio}}if(Object.keys(d).length)return this.ISGenerated=!0,f&&_(a)?(h&&this.warn("Timestamps at playlist time: "+(i?"":"~")+r+" "+a/s+" != initPTS: "+h.baseTime/h.timescale+" ("+h.baseTime+"/"+h.timescale+") trackId: "+h.trackId),this.log("Found initPTS at playlist time: "+r+" offset: "+a/s+" ("+a+"/"+s+") trackId: "+g),this._initPTS={baseTime:a,timescale:s,trackId:g},this._initDTS={baseTime:n,timescale:s,trackId:g}):a=s=void 0,{tracks:d,initPTS:a,timescale:s,trackId:g}},r.remuxVideo=function(e,t,r,i,a){var n,s,l=e.inputTimeScale,u=e.samples,d=[],h=u.length,f=this._initPTS,c=f.baseTime*l/f.timescale,g=this.nextVideoTs,v=8,m=this.videoSampleDuration,p=Number.POSITIVE_INFINITY,y=Number.NEGATIVE_INFINITY,E=!1;if(!r||null===g){var T=c+t*l,S=u[0].pts-an(u[0].dts,u[0].pts);ct()&&null!==g&&Math.abs(T-S-(g+c))<15e3?r=!0:g=T-S-c}for(var L=g+c,R=0;R<h;R++){var A=u[R];A.pts=an(A.pts,L),A.dts=an(A.dts,L),A.dts<u[R>0?R-1:R].dts&&(E=!0)}E&&u.sort((function(e,t){var r=e.dts-t.dts,i=e.pts-t.pts;return r||i})),n=u[0].dts;var b=(s=u[u.length-1].dts)-n,k=b?Math.round(b/(h-1)):m||e.inputTimeScale/30;if(r){var D=n-L,I=D>k,_=D<-1;if((I||_)&&(I?this.warn((e.segmentCodec||"").toUpperCase()+": "+Za(D)+" ms ("+D+"dts) hole between fragments detected at "+t.toFixed(3)):this.warn((e.segmentCodec||"").toUpperCase()+": "+Za(-D)+" ms ("+D+"dts) overlapping between fragments detected at "+t.toFixed(3)),!_||L>=u[0].pts||ct())){n=L;var C=u[0].pts-D;if(I)u[0].dts=n,u[0].pts=C;else for(var P=!0,M=0;M<u.length&&!(u[M].dts>C&&P);M++){var F=u[M].pts;if(u[M].dts-=D,u[M].pts-=D,M<u.length-1){var N=u[M+1].pts;P=N<=u[M].pts==N<=F}}this.log("Video: Initial PTS/DTS adjusted: "+Za(C)+"/"+Za(n)+", delta: "+Za(D)+" ms")}}for(var B=0,U=0,G=n=Math.max(0,n),V=0;V<h;V++){for(var H=u[V],K=H.units,Y=K.length,W=0,j=0;j<Y;j++)W+=K[j].data.length;U+=W,B+=Y,H.length=W,H.dts<G?(H.dts=G,G+=k/4|0||1):G=H.dts,p=Math.min(H.pts,p),y=Math.max(H.pts,y)}s=u[h-1].dts;var q,X=U+4*B+8;try{q=new Uint8Array(X)}catch(e){return void this.observer.emit(O.ERROR,O.ERROR,{type:x.MUX_ERROR,details:w.REMUX_ALLOC_ERROR,fatal:!1,chunkMeta:a,error:e,bytes:X,reason:"fail allocating video mdat "+X})}Ye(q,0,X),Ye(q,4,Ne.mdat);for(var z=!1,Q=Number.POSITIVE_INFINITY,$=Number.POSITIVE_INFINITY,Z=Number.NEGATIVE_INFINITY,J=Number.NEGATIVE_INFINITY,ee=0;ee<h;ee++){for(var te=u[ee],re=te.units,ie=0,ae=0,ne=re.length;ae<ne;ae++){var se=re[ae],oe=se.data,le=se.data.byteLength;Ye(q,v,le),v+=4,q.set(oe,v),v+=le,ie+=4+le}var ue=void 0;if(ee<h-1)m=u[ee+1].dts-te.dts,ue=u[ee+1].pts-te.pts;else{var de=this.config,he=ee>0?te.dts-u[ee-1].dts:k;if(ue=ee>0?te.pts-u[ee-1].pts:k,de.stretchShortVideoTrack&&null!==this.nextAudioTs){var fe=Math.floor(de.maxBufferHole*l),ce=(i?p+i*l:this.nextAudioTs+c)-te.pts;ce>fe?((m=ce-he)<0?m=he:z=!0,this.log("It is approximately "+ce/90+" ms to the next segment; using duration "+m/90+" ms for the last video frame.")):m=he}else m=he}var ge=Math.round(te.pts-te.dts);if(Q=Math.min(Q,m),Z=Math.max(Z,m),$=Math.min($,ue),J=Math.max(J,ue),!r&&0===ee&&ge>m){var ve=Math.round(ge/k)*k;ve-ge==1&&(this.log("pad first CTS "+ge+" -> "+ve+" of sn: "+a.sn),ge=ve)}d.push(tn(te.key,m,ie,ge))}if(d.length){var me=ct();if(me){if(me<70){var pe=d[0].flags;pe.dependsOn=2,pe.isNonSync=0}}else if(function(){if(void 0===dt){var e=ht.match(/Safari\/(\d+)/i);dt=e?parseInt(e[1]):0}return dt}()&&J-$<Z-Q&&k/Z<.025&&0===d[0].cts){this.warn("Found irregular gaps in sample duration. Using PTS instead of DTS to determine MP4 sample duration.");for(var ye=n,Ee=0,Te=d.length;Ee<Te;Ee++){var Se=ye+d[Ee].duration,Le=ye+d[Ee].cts;if(Ee<Te-1){var Re=Se+d[Ee+1].cts;d[Ee].duration=Re-Le}else d[Ee].duration=Ee?d[Ee-1].duration:k;d[Ee].cts=0,ye=Se}}}var Ae=s+(m=z||!m?k:m);this.nextVideoTs=g=Ae-c,this.videoSampleDuration=m,this.isVideoContiguous=!0;var be={data1:$a.moof(a.sn,n,o(e,{samples:d})),data2:q,startPTS:(p-c)/l,endPTS:(y+m-c)/l,startDTS:(n-c)/l,endDTS:g/l,type:"video",hasAudio:!1,hasVideo:!0,nb:d.length,dropped:e.dropped};return e.samples=[],e.dropped=0,be},r.getSamplesPerFrame=function(e){switch(e.segmentCodec){case"mp3":return 1152;case"ac3":return 1536;default:return 1024}},r.remuxAudio=function(e,t,r,i,a,n){var s=e.inputTimeScale,l=s/(e.samplerate?e.samplerate:s),u=this.getSamplesPerFrame(e),d=u*l,h=this._initPTS,f="mp3"===e.segmentCodec&&this.typeSupported.mpeg,c=[],g=void 0!==a,v=e.samples,m=f?0:8,p=this.nextAudioTs||-1,y=h.baseTime*s/h.timescale,E=y+t*s;if(this.isAudioContiguous=r=r||v.length&&p>0&&(i&&Math.abs(E-(p+y))<9e3||Math.abs(an(v[0].pts,E)-(p+y))<20*d),v.forEach((function(e){e.pts=an(e.pts,E)})),!r||p<0){var T=v.length;if(v=v.filter((function(e){return e.pts>=0})),T!==v.length&&this.warn("Removed "+(v.length-T)+" of "+T+" samples (initPTS "+y+" / "+s+")"),!v.length)return;p=0===a?0:i&&!g?Math.max(0,E-y):v[0].pts-y}if("aac"===e.segmentCodec)for(var S=this.config.maxAudioFramesDrift,L=0,R=p+y;L<v.length;L++){var A=v[L],b=A.pts,k=b-R,D=Math.abs(1e3*k/s);if(k<=-S*d&&g)0===L&&(this.warn("Audio frame @ "+(b/s).toFixed(3)+"s overlaps marker by "+Math.round(1e3*k/s)+" ms."),this.nextAudioTs=p=b-y,R=b);else if(k>=S*d&&D<1e4&&g){var I=Math.round(k/d);for(R=b-I*d;R<0&&I&&d;)I--,R+=d;0===L&&(this.nextAudioTs=p=R-y),this.warn("Injecting "+I+" audio frames @ "+((R-y)/s).toFixed(3)+"s due to "+Math.round(1e3*k/s)+" ms gap.");for(var _=0;_<I;_++){var C=Va.getSilentFrame(e.parsedCodec||e.manifestCodec||e.codec,e.channelCount);C||(this.log("Unable to get silent frame for given audio codec; duplicating last frame instead."),C=A.unit.subarray()),v.splice(L,0,{unit:C,pts:R}),R+=d,L++}}A.pts=R,R+=d}for(var P,M=null,F=null,N=0,B=v.length;B--;)N+=v[B].unit.byteLength;for(var U=0,G=v.length;U<G;U++){var V=v[U],H=V.unit,K=V.pts;if(null!==F)c[U-1].duration=Math.round((K-F)/l);else{if(r&&"aac"===e.segmentCodec&&(K=p+y),M=K,!(N>0))return;N+=m;try{P=new Uint8Array(N)}catch(e){return void this.observer.emit(O.ERROR,O.ERROR,{type:x.MUX_ERROR,details:w.REMUX_ALLOC_ERROR,fatal:!1,chunkMeta:n,error:e,bytes:N,reason:"fail allocating audio mdat "+N})}f||(Ye(P,0,N),Ye(P,4,Ne.mdat))}P&&P.set(H,m);var Y=H.byteLength;m+=Y,c.push(tn(!0,u,Y,0)),F=K}var W=c.length;if(W){var j=c[c.length-1];p=F-y,this.nextAudioTs=p+l*j.duration;var q=f?new Uint8Array(0):$a.moof(e.sequenceNumber++,M/l,o({},e,{samples:c}));e.samples=[];var X=(M-y)/s,z=this.nextAudioTs/s,Q={data1:q,data2:P,startPTS:X,endPTS:z,startDTS:X,endDTS:z,type:"audio",hasAudio:!0,hasVideo:!1,nb:W};return this.isAudioContiguous=!0,Q}},t}(ie);function an(e,t){var r;if(null===t)return e;for(r=t<e?-en:en;Math.abs(e-t)>4294967296;)e+=r;return e}function nn(e,t,r,i){var a=e.samples.length;if(a){for(var n=e.inputTimeScale,s=0;s<a;s++){var o=e.samples[s];o.pts=an(o.pts-r.baseTime*n/r.timescale,t*n)/n,o.dts=an(o.dts-i.baseTime*n/i.timescale,t*n)/n}var l=e.samples;return e.samples=[],{samples:l}}}function sn(e,t,r){var i=e.samples.length;if(i){for(var a=e.inputTimeScale,n=0;n<i;n++){var s=e.samples[n];s.pts=an(s.pts-r.baseTime*a/r.timescale,t*a)/a}e.samples.sort((function(e,t){return e.pts-t.pts}));var o=e.samples;return e.samples=[],{samples:o}}}var on,ln=function(e){function t(t,r,i,a){var n;return(n=e.call(this,"passthrough-remuxer",a)||this).observer=void 0,n.emitInitSegment=!1,n.audioCodec=void 0,n.videoCodec=void 0,n.initData=void 0,n.initPTS=null,n.initTracks=void 0,n.lastEndTime=null,n.isVideoContiguous=!1,n.videoOnlyRemux=!1,n.decryptdata=null,n.pendingInitSegment=void 0,n.observer=t,n}u(t,e);var r=t.prototype;return r.destroy=function(){this.observer&&this.observer.removeAllListeners(),this.observer=null},r.resetTimeStamp=function(e){this.lastEndTime=null;var t=this.initPTS;t&&e&&t.baseTime===e.baseTime&&t.timescale===e.timescale||(this.initPTS=e)},r.resetNextTimestamp=function(){this.isVideoContiguous=!1,this.lastEndTime=null},r.resetInitSegment=function(e,t,r,i){this.audioCodec=t,this.videoCodec=r,this.decryptdata=i,this.pendingInitSegment=e,this.videoOnlyRemux=!1,this.initTracks=void 0,this.initData=void 0,this.emitInitSegment=!0},r.generateInitSegment=function(e,t,r){this.videoOnlyRemux=!1;var i=this.audioCodec,a=this.videoCodec;if(null==e||!e.byteLength)return this.initTracks=void 0,void(this.initData=void 0);var n=this.initData=r||Xe(e),s=n.audio,o=n.video;if(t)!function(e,t){if(e&&t){var r=t.keyId;16===(null==r?void 0:r.byteLength)&&t.isCommonEncryption&&et(e,(function(e,t){var i=e.subarray(8,24);i.some((function(e){return 0!==e}))||(fe.log("[eme] Patching keyId in 'enc"+(t?"a":"v")+">sinf>>tenc' box: "+xe(i)+" -> "+xe(r)),e.set(r,8))}))}}(e,t);else{var l=s||o;null!=l&&l.encrypted&&this.warn('Init segment with encrypted track with has no key ("'+l.codec+'")!')}s&&(i=dn(s,B.AUDIO,this)),o&&(a=dn(o,B.VIDEO,this));var u={};s&&o?u.audiovideo={container:"video/mp4",codec:i+","+a,supplemental:o.supplemental,encrypted:o.encrypted,initSegment:e,id:"main"}:s?u.audio={container:"audio/mp4",codec:i,encrypted:s.encrypted,initSegment:e,id:"audio"}:o?u.video={container:"video/mp4",codec:a,supplemental:o.supplemental,encrypted:o.encrypted,initSegment:e,id:"main"}:this.warn("initSegment does not contain moov or trak boxes."),this.initTracks=u},r.remux=function(e,t,r,i,a,n,s,o,l,u,d){var h,f,c=this.initPTS,g=this.lastEndTime,v={audio:void 0,video:void 0,text:void 0,id3:r,initSegment:void 0};_(g)||(g=this.lastEndTime=a||0);var m=t.samples;if(!m.length)return v;var p=this.pendingInitSegment;p&&(this.pendingInitSegment=void 0,this.generateInitSegment(p,this.decryptdata,u));var y={initPTS:void 0,timescale:void 0,trackId:void 0},E=this.initData;if(null!=(h=E)&&h.length||(this.generateInitSegment(m),E=this.initData),null==(f=E)||!f.length)return this.warn("Failed to generate initSegment."),v;this.emitInitSegment&&(y.tracks=this.initTracks,v.initSegment=y,this.emitInitSegment=!1);var T=d?d.tracks:function(e,t,r,i,a){return rt(e,t,i)}(m,E,0,this),S=E.audio?T[E.audio.id]:null,L=E.video?T[E.video.id]:null,R=!!E.audio,A=!!E.video,b="";R&&(b+="audio"),A&&(b+="video");var k,D,I=un(L,1/0),C=un(S,1/0),P=un(L,0,!0),x=un(S,0,!0);L&&S&&E.audio&&(C>P||I>x)&&this.warn("audio and video track sample timestamps do not overlap. v: "+I+"-"+P+" a: "+C+"-"+x+"}",L,S);var w=!!S&&(!L||!c&&C<I||!!c&&c.trackId===E.audio.id),O=w?S:L;if(!O)return this.log("No media samples found in "+o+" "+l.level+" "+(-1===l.part?"":"part: "+l.part+" of")+" sn: "+l.sn+" at playlist time: "+a),v;var M=m;D=w?x-C:P-I;var F=O.timescale,N=O.start-a*F,B=w?E.audio.id:E.video.id;if(k=O.start/F,(n||!c)&&(function(e,t,r,i){if(null===e)return!0;var a=Math.max(i,1),n=t-e.baseTime/e.timescale;return Math.abs(n-r)>=a}(c,k,a,D)||F!==c.timescale)){var U=!1,G=w?"audio":"video";if(c){var V=0!==a?k/a:1+k/1;U=k>=0&&Math.abs(1-V)<.001,this.log(G+" timestamps in track "+B+" at playlist time: "+(n?"":"~")+a+" maps to "+k+" with initPTS: "+c.baseTime/c.timescale+" ("+(N/F-c.baseTime/c.timescale)+"s diff) ("+b+") drift estimate: "+V+" "+(U?"(ignoring drift)":"remapping timestamps (initPTS)"))}U||(this.log("Found initPTS in "+G+" track "+B+" at playlist time: "+a+" offset: "+(k-a)+" ("+N+"/"+F+")"),c=null,y.initPTS=N,y.timescale=F,y.trackId=B)}c?(y.initPTS=c.baseTime,y.timescale=c.timescale,y.trackId=c.trackId):(y.timescale&&void 0!==y.trackId&&void 0!==y.initPTS||(this.warn("Could not set initPTS"),y.initPTS=k,y.timescale=1,y.trackId=-1),this.initPTS=c={baseTime:y.initPTS,timescale:y.timescale,trackId:y.trackId});var H=k-c.baseTime/c.timescale,K=H+D,Y=A&&void 0!==(null==O?void 0:O.ptsMin)?O.ptsMin/O.timescale-c.baseTime/c.timescale:H,W=A&&null!=O&&O.ptsMax?O.ptsMax/O.timescale-c.baseTime/c.timescale:K;D>0?this.lastEndTime=K:(this.warn("Duration parsed from mp4 should be greater than zero"),this.resetNextTimestamp());var j={data1:M,data2:void 0,startPTS:Y,startDTS:H,endPTS:W,endDTS:K,type:b,hasAudio:R,hasVideo:A,nb:1,dropped:0,encrypted:!!E.audio&&E.audio.encrypted||!!E.video&&E.video.encrypted};v.audio=R&&!A?j:void 0,v.video=A?j:void 0;var q=this.isVideoContiguous,X=null==L?void 0:L.sampleCount;if(X){var z=L.keyFrameIndex,Q=-1!==z;j.nb=X,j.dropped=0===z||q?0:Q?z:X,j.independent=Q,j.firstKeyFrame=z,Q&&L.keyFrameStart&&(j.firstKeyFramePTS=(L.keyFrameStart-c.baseTime)/c.timescale),q||(v.independent=Q),this.isVideoContiguous||(this.isVideoContiguous=Q),j.dropped&&this.warn("fmp4 does not start with IDR: firstIDR "+z+"/"+X+" dropped: "+j.dropped+" start: "+(j.firstKeyFramePTS||"NA"))}return v.initSegment=y,v.id3=nn(r,a,c,c),i.samples.length&&(v.text=sn(i,a,c)),v},t}(ie);function un(e,t,r){return void 0===r&&(r=!1),void 0!==(null==e?void 0:e.start)?(e.start+(r?e.duration:0))/e.timescale:t}function dn(e,t,r){var i=e.codec;return i&&i.length>4?i:t===B.AUDIO?"ec-3"===i||"ac-3"===i||"alac"===i?i:"fLaC"===i||"Opus"===i?Rt(i,!1):(r.warn('Unhandled audio codec "'+i+'" in mp4 MAP'),i||"mp4a"):"mjpg"===i?i:(r.warn('Unhandled video codec "'+i+'" in mp4 MAP'),i||"avc1")}try{on=self.performance.now.bind(self.performance)}catch(e){on=Date.now}var hn=[{demux:ka,remux:ln},{demux:xa,remux:rn},{demux:Ra,remux:rn},{demux:Aa,remux:rn}],fn=function(){function e(e,t,r,i,a,n){this.asyncResult=!1,this.logger=void 0,this.observer=void 0,this.typeSupported=void 0,this.config=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.id=a,this.logger=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 a=this;if(!this.observer)return cn(r);var n=r.transmuxing;n.executeStart=on();var s=new Uint8Array(e),o=this.currentTransmuxState,l=this.transmuxConfig;i&&(this.currentTransmuxState=i);var u=i||o,d=u.contiguous,h=u.discontinuity,f=u.trackSwitch,c=u.accurateTimeOffset,g=u.timeOffset,v=u.initSegmentChange,m=l.audioCodec,p=l.videoCodec,y=l.defaultInitPts,E=l.duration,T=l.initSegmentData,S=function(e,t){var r=null;return e.byteLength>0&&null!=(null==t?void 0:t.key)&&null!==t.iv&&null!=t.method&&(r=t),r}(s,t);if(S&&Or(S.method)){var L=this.getDecrypter(),R=Mr(S.method);return this.asyncResult=!0,this.decryptionPromise=L.decrypt(s,S.key.buffer,S.iv.buffer,R,r.decryptRange).then((function(e){var t=a.push(e,null,r);return a.decryptionPromise=null,t})),this.decryptionPromise}var A=this.needsProbing(h,f);if(A){var b=r.iframe&&T&&xa.probe(T,this.logger)?T:s,k=this.configureTransmuxer(b);if(k)return this.logger.warn("[transmuxer] "+k.message),this.observer.emit(O.ERROR,O.ERROR,{type:x.MEDIA_ERROR,details:w.FRAG_PARSING_ERROR,fatal:!1,error:k,reason:k.message}),n.executeEnd=on(),cn(r)}(h||f||v||A)&&this.resetInitSegment(T,m,p,E,t,r),(h||v||A)&&this.resetInitialTimestamp(y),d||this.resetContiguity();var D=this.transmux(s,S,g,c,r);this.asyncResult=gn(D);var I=this.currentTransmuxState;return I.contiguous=!0,I.discontinuity=!1,I.trackSwitch=!1,n.executeEnd=on(),D},t.flush=function(e){var t=this,r=e.transmuxing;r.executeStart=on();var i=this.decrypter,a=this.currentTransmuxState,n=this.decryptionPromise;if(n)return this.asyncResult=!0,n.then((function(){return t.flush(e)}));var s=[],o=a.timeOffset;if(i){var l=i.flush();l&&s.push(this.push(l.buffer,null,e))}var u=this.demuxer,d=this.remuxer;if(!u||!d){r.executeEnd=on();var h=[cn(e)];return this.asyncResult?Promise.resolve(h):h}var f=u.flush(o,e);return gn(f)?(this.asyncResult=!0,f.then((function(r){return t.flushRemux(s,r,e),s}))):(this.flushRemux(s,f,e),this.asyncResult?Promise.resolve(s):s)},t.flushRemux=function(e,t,r){if(this.remuxer){var i=t.audioTrack,a=t.videoTrack,n=t.id3Track,s=t.textTrack,o=this.currentTransmuxState,l=o.accurateTimeOffset,u=o.timeOffset;this.logger.log("[transmuxer.ts]: Flushed "+this.id+" sn: "+r.sn+(r.part>-1?" part: "+r.part:"")+" of "+this.id+" playlist "+r.level);var d=this.remuxer.remux(i,a,n,s,u,l,!0,this.id,r,t.initData,t.sampleData);e.push({remuxResult:d,chunkMeta:r}),r.transmuxing.executeEnd=on()}},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,a,n){var s=this.demuxer,o=this.remuxer;s&&o&&(s.resetInitSegment(e,t,r,i,a,n),o.resetInitSegment(e,t,r,a))},t.destroy=function(){this.demuxer&&(this.demuxer.destroy(),this.demuxer=void 0),this.remuxer&&(this.remuxer.destroy(),this.remuxer=void 0),this.observer&&this.observer.removeAllListeners(),this.decryptionPromise=null,this.decrypter=this.observer=void 0,this.logger=this.config=this.probe=void 0},t.transmux=function(e,t,r,i,a){return"SAMPLE-AES"===(null==t?void 0:t.method)?this.transmuxSampleAes(e,t,r,i,a):this.transmuxUnencrypted(e,r,i,a)},t.transmuxUnencrypted=function(e,t,r,i){var a=this.demuxer,n=this.remuxer;if(!a||!n)return cn(i);var s=a.demux(e,t,i,!1,!this.config.progressive),o=s.audioTrack,l=s.videoTrack,u=s.id3Track,d=s.textTrack;return{remuxResult:n.remux(o,l,u,d,t,r,!1,this.id,i,s.initData,s.sampleData),chunkMeta:i}},t.transmuxSampleAes=function(e,t,r,i,a){var n=this;return this.demuxer?this.demuxer.demuxSampleAes(e,t,r,a).then((function(e){return{remuxResult:n.remuxer.remux(e.audioTrack,e.videoTrack,e.id3Track,e.textTrack,r,i,!1,n.id,a,e.initData,e.sampleData),chunkMeta:a}})):Promise.reject(new Error("no demuxer"))},t.configureTransmuxer=function(e){var t=this.config,r=this.observer,i=this.typeSupported;if(r){var a;try{for(var n=0,s=hn.length;n<s;n++){var o;if(null!=(o=hn[n].demux)&&o.probe(e,this.logger)){a=hn[n];break}}}catch(e){return e}if(!a)return new Error("Failed to find demuxer by probing fragment data");var l=this.demuxer,u=this.remuxer,d=a.demux,h=a.remux;l&&l instanceof d||(this.logger.log("Using "+d.name),this.demuxer=new d(r,t,i,this.logger),this.probe=d.probe),u&&u instanceof h||(this.logger.log("Using "+h.name),this.remuxer=new h(r,t,i,this.logger))}},t.needsProbing=function(e,t){return!this.demuxer||!this.remuxer||e||t},t.getDecrypter=function(){var e=this.decrypter;return e||(e=this.decrypter=new _i(this.config)),e},e}(),cn=function(e){return{remuxResult:{},chunkMeta:e}};function gn(e){return"then"in e&&e.then instanceof Function}var vn=function(e,t,r,i,a){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=a||null},mn=function(e,t,r,i,a,n){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=a,this.initSegmentChange=n},pn=[];function yn(e,t,r){if(!((i=t.remuxResult).audio||i.video||i.text||i.id3||i.initSegment))return!1;var i,a=[],n=t.remuxResult,s=n.audio,o=n.video;return s&&En(a,s),o&&En(a,o),e.postMessage({event:"transmuxComplete",data:t,instanceNo:r},a),!0}function En(e,t){t.data1&&e.push(t.data1.buffer),t.data2&&e.push(t.data2.buffer)}function Tn(e,t,r,i){t.reduce((function(t,r){return yn(e,r,i)||t}),!1)||e.postMessage({event:"transmuxComplete",data:t[0],instanceNo:i}),e.postMessage({event:"flush",data:r,instanceNo:i})}function Sn(e,t,r){self.postMessage({event:e,data:t,instanceNo:r})}void 0!==t&&t&&self.addEventListener("message",(function(e){var t=e.data,r=t.instanceNo;if(void 0!==r){var i=pn[r];if("reset"===t.cmd&&(delete pn[t.resetNo],i&&i.destroy(),t.cmd="init"),"init"===t.cmd){var a=JSON.parse(t.config),n=new k;n.on(O.FRAG_DECRYPTED,(function(e,t){return Sn(e,t,r)})),n.on(O.ERROR,(function(e,t){return Sn(e,t,r)}));var s=ue(a.debug,t.id);return function(e,t){var r=function(r){e[r]=function(){var e=Array.prototype.join.call(arguments," ");Sn("workerLog",{logType:r,message:e},t)}};for(var i in e)r(i)}(s,r),pn[r]=new fn(n,t.typeSupported,a,"",t.id,s),void Sn("init",null,r)}if(i)switch(t.cmd){case"configure":i.configure(t.config);break;case"demux":var o=i.push(t.data,t.decryptdata,t.chunkMeta,t.state);gn(o)?o.then((function(e){yn(self,e,r)})).catch((function(e){Sn(O.ERROR,{instanceNo:r,type:x.MEDIA_ERROR,details:w.FRAG_PARSING_ERROR,chunkMeta:t.chunkMeta,fatal:!1,error:e,err:e,reason:"transmuxer-worker push error"},r)})):yn(self,o,r);break;case"flush":var l=t.chunkMeta,u=i.flush(l);gn(u)?u.then((function(e){Tn(self,e,l,r)})).catch((function(e){Sn(O.ERROR,{type:x.MEDIA_ERROR,details:w.FRAG_PARSING_ERROR,chunkMeta:t.chunkMeta,fatal:!1,error:e,err:e,reason:"transmuxer-worker flush error"},r)})):Tn(self,u,l,r)}}}));var Ln="1.7.3",Rn={},An=0,bn=function(){function t(t,r,i,a){var n=this;this.error=null,this.hls=void 0,this.id=void 0,this.instanceNo=An++,this.observer=void 0,this.frag=null,this.part=null,this.useWorker=void 0,this.workerContext=null,this.transmuxer=null,this.onTransmuxComplete=void 0,this.onFlush=void 0,this.onWorkerMessage=function(e){var t=e.data,r=n.hls;if(r&&null!=t&&t.event&&t.instanceNo===n.instanceNo)switch(t.event){case"init":var i,a=null==(i=n.workerContext)?void 0:i.objectURL;a&&self.URL.revokeObjectURL(a);break;case"transmuxComplete":n.handleTransmuxComplete(t.data);break;case"flush":n.onFlush(t.data);break;case"workerLog":r.logger[t.data.logType]&&r.logger[t.data.logType](t.data.message);break;default:t.data=t.data||{},t.data.frag=n.frag,t.data.part=n.part,t.data.id=n.id,r.trigger(t.event,t.data)}},this.onWorkerError=function(e){if(n.hls){var t=new Error(e.message+" ("+e.filename+":"+e.lineno+")");n.hls.config.enableWorker=!1,n.hls.logger.warn('Error in "'+n.id+'" Web Worker, fallback to inline'),n.hls.trigger(O.ERROR,{type:x.OTHER_ERROR,details:w.INTERNAL_EXCEPTION,fatal:!1,event:"demuxerWorker",error:t})}};var s=t.config;this.hls=t,this.id=r,this.useWorker=!!s.enableWorker,this.onTransmuxComplete=i,this.onFlush=a;var o=function(e,t){(t=t||{}).frag=n.frag||void 0,e===O.ERROR&&(t.parent=n.id,t.part=n.part,n.error=t.error),n.hls.trigger(e,t)};this.observer=new k,this.observer.on(O.FRAG_DECRYPTED,o),this.observer.on(O.ERROR,o);var l=kt(s.preferManagedMediaSource);if(this.useWorker&&"undefined"!=typeof Worker){var u=this.hls.logger;s.workerPath;try{s.workerPath?(u.log("loading Web Worker "+s.workerPath+' for "'+r+'"'),this.workerContext=function(e){var t=Rn[e];if(t)return t.clientCount++,t;var r=new self.URL(e,self.location.href).href,i={worker:new self.Worker(r),scriptURL:r,clientCount:1};return Rn[e]=i,i}(s.workerPath)):(u.log('injecting Web Worker for "'+r+'"'),this.workerContext=function(){var t=Rn[Ln];if(t)return t.clientCount++,t;var r=new self.Blob(["var exports={};var module={exports:exports};function define(f){f()};define.amd=true;("+e.toString()+")(true);"],{type:"text/javascript"}),i=self.URL.createObjectURL(r),a={worker:new self.Worker(i),objectURL:i,clientCount:1};return Rn[Ln]=a,a}());var d=this.workerContext.worker;d.addEventListener("message",this.onWorkerMessage),d.addEventListener("error",this.onWorkerError),d.postMessage({instanceNo:this.instanceNo,cmd:"init",typeSupported:l,id:r,config:Bt(s)})}catch(e){u.warn('Error setting up "'+r+'" Web Worker, fallback to inline',e),this.terminateWorker(),this.error=null,this.transmuxer=new fn(this.observer,l,s,"",r,t.logger)}}else this.transmuxer=new fn(this.observer,l,s,"",r,t.logger)}var r=t.prototype;return r.reset=function(){if(this.frag=null,this.part=null,this.workerContext){var e=this.instanceNo;this.instanceNo=An++;var t=this.hls.config,r=kt(t.preferManagedMediaSource);this.workerContext.worker.postMessage({instanceNo:this.instanceNo,cmd:"reset",resetNo:e,typeSupported:r,id:this.id,config:Bt(t)})}},r.terminateWorker=function(){if(this.workerContext){var e=this.workerContext.worker;this.workerContext=null,e.removeEventListener("message",this.onWorkerMessage),e.removeEventListener("error",this.onWorkerError),function(e){var t=Rn[e||Ln];if(t&&1==t.clientCount--){var r=t.worker,i=t.objectURL;delete Rn[e||Ln],i&&self.URL.revokeObjectURL(i),r.terminate()}}(this.hls.config.workerPath)}},r.destroy=function(){if(this.workerContext)this.terminateWorker(),this.onWorkerMessage=this.onWorkerError=null;else{var e=this.transmuxer;e&&(e.destroy(),this.transmuxer=null)}var t=this.observer;t&&t.removeAllListeners(),this.frag=null,this.part=null,this.observer=null,this.hls=null},r.push=function(e,t,r,i,a,n,s,o,l,u){var d,h,f=this;l.transmuxing.start=self.performance.now();var c=this.instanceNo,g=this.transmuxer,v=n?n.start:a.start,m=a.decryptdata,p=this.frag,y=!p||a.cc!==p.cc,E=!p||l.level!==p.level,T=p?l.sn-p.sn:-1,S=this.part?l.part-this.part.index:-1,L=0===T&&l.id>1&&l.id===(null==p?void 0:p.stats.chunkCount),R=!E&&(1===T||0===T&&(1===S||L&&S<=0)),A=self.performance.now();(E||T||0===a.stats.parsing.start)&&(a.stats.parsing.start=A),!n||!S&&R||(n.stats.parsing.start=A);var b=!(p&&(null==(d=a.initSegment)?void 0:d.url)===(null==(h=p.initSegment)?void 0:h.url)),k=new mn(y,R,o,E,v,b);if(!R||y||b){this.hls.logger.log("[transmuxer-interface]: Starting new transmux session for "+a.type+" sn: "+l.sn+(l.part>-1?" part: "+l.part:"")+" "+this.id+" playlist: "+l.level+" id: "+l.id+"\n discontinuity: "+y+"\n trackSwitch: "+E+"\n contiguous: "+R+"\n accurateTimeOffset: "+o+"\n timeOffset: "+v+"\n initSegmentChange: "+b);var D=new vn(r,i,t,s,u);this.configureTransmuxer(D)}if(this.frag=a,this.part=n,this.workerContext)this.workerContext.worker.postMessage({instanceNo:c,cmd:"demux",data:e,decryptdata:m,chunkMeta:l,state:k},e instanceof ArrayBuffer?[e]:[]);else if(g){var I=g.push(e,m,l,k);gn(I)?I.then((function(e){f.handleTransmuxComplete(e)})).catch((function(e){f.transmuxerError(e,l,"transmuxer-interface push error")})):this.handleTransmuxComplete(I)}},r.flush=function(e){var t=this;e.transmuxing.start=self.performance.now();var r=this.instanceNo,i=this.transmuxer;if(this.workerContext)this.workerContext.worker.postMessage({instanceNo:r,cmd:"flush",chunkMeta:e});else if(i){var a=i.flush(e);gn(a)?a.then((function(r){t.handleFlushResult(r,e)})).catch((function(r){t.transmuxerError(r,e,"transmuxer-interface flush error")})):this.handleFlushResult(a,e)}},r.transmuxerError=function(e,t,r){this.hls&&(this.error=e,this.hls.trigger(O.ERROR,{type:x.MEDIA_ERROR,details:w.FRAG_PARSING_ERROR,chunkMeta:t,frag:this.frag||void 0,part:this.part||void 0,fatal:!1,error:e,err:e,reason:r}))},r.handleFlushResult=function(e,t){var r=this;e.forEach((function(e){r.handleTransmuxComplete(e)})),this.onFlush(t)},r.configureTransmuxer=function(e){var t=this.instanceNo,r=this.transmuxer;this.workerContext?this.workerContext.worker.postMessage({instanceNo:t,cmd:"configure",config:e}):r&&r.configure(e)},r.handleTransmuxComplete=function(e){e.chunkMeta.transmuxing.end=self.performance.now(),this.onTransmuxComplete(e)},t}();function kn(){return self.SourceBuffer||self.WebKitSourceBuffer}function Dn(){if(!ve())return!1;var e=kn();return!e||e.prototype&&"function"==typeof e.prototype.appendBuffer&&"function"==typeof e.prototype.remove}var In=0,_n=1,Cn=2,Pn=function(e){function t(t,r,i){var a;return(a=e.call(this,t,r,i,"stream-controller",N.MAIN)||this).audioCodecSwap=!1,a.level=-1,a._forceStartLoad=!1,a._hasEnoughToStart=!1,a.altAudio=In,a.audioOnly=!1,a._couldBacktrack=!1,a._backtrackFragment=void 0,a.audioCodecSwitch=!1,a.videoBuffer=null,a.onMediaPlaying=function(){a.tick()},a.onMediaSeeked=function(){var e=a.media,t=e?e.currentTime:null;if(null!==t&&_(t)&&(a.log("Media seeked to "+t),a.getBufferedFrag(t))){var r=a.getFwdBufferInfoAtPos(e,t,N.MAIN,0);r&&0!==r.len?a.tick():a.log("Main buffer empty at "+t+' on "seeked" event: '+(r?r.len:r))}},a.registerListeners(),a}u(t,e);var r=t.prototype;return r.registerListeners=function(){e.prototype.registerListeners.call(this);var t=this.hls;t.on(O.MANIFEST_PARSED,this.onManifestParsed,this),t.on(O.LEVEL_LOADING,this.onLevelLoading,this),t.on(O.LEVEL_LOADED,this.onLevelLoaded,this),t.on(O.FRAG_LOAD_EMERGENCY_ABORTED,this.onFragLoadEmergencyAborted,this),t.on(O.AUDIO_TRACK_SWITCHING,this.onAudioTrackSwitching,this),t.on(O.AUDIO_TRACK_SWITCHED,this.onAudioTrackSwitched,this),t.on(O.BUFFER_CREATED,this.onBufferCreated,this),t.on(O.BUFFER_FLUSHED,this.onBufferFlushed,this),t.on(O.LEVELS_UPDATED,this.onLevelsUpdated,this),t.on(O.FRAG_BUFFERED,this.onFragBuffered,this)},r.unregisterListeners=function(){e.prototype.unregisterListeners.call(this);var t=this.hls;t.off(O.MANIFEST_PARSED,this.onManifestParsed,this),t.off(O.LEVEL_LOADED,this.onLevelLoaded,this),t.off(O.FRAG_LOAD_EMERGENCY_ABORTED,this.onFragLoadEmergencyAborted,this),t.off(O.AUDIO_TRACK_SWITCHING,this.onAudioTrackSwitching,this),t.off(O.AUDIO_TRACK_SWITCHED,this.onAudioTrackSwitched,this),t.off(O.BUFFER_CREATED,this.onBufferCreated,this),t.off(O.BUFFER_FLUSHED,this.onBufferFlushed,this),t.off(O.LEVELS_UPDATED,this.onLevelsUpdated,this),t.off(O.FRAG_BUFFERED,this.onFragBuffered,this)},r.onHandlerDestroying=function(){this.onMediaPlaying=this.onMediaSeeked=null,this.unregisterListeners(),e.prototype.onHandlerDestroying.call(this)},r.startLoad=function(e,t){if(this.levels){var r=this.lastCurrentTime,i=this.hls;if(this.stopLoad(),this.setInterval(100),this.level=-1,!this.startFragRequested){var a=i.startLevel;-1===a&&(i.config.testBandwidth&&this.levels.length>1?(a=0,this.bitrateTest=!0):a=i.firstAutoLevel),i.nextLoadLevel=a,this.level=i.loadLevel,this._hasEnoughToStart=!!t}r>0&&-1===e&&!t&&this.initPTS.length&&(this.log("Override startPosition with lastCurrentTime @"+r),e=r),this.state=Hi.IDLE,this.nextLoadPosition=this.lastCurrentTime=e+this.timelineOffset,this.startPosition=t?-1:e,t||this.fragmentTracker.hasFragments(this.playlistType)||(this._hasEnoughToStart=this.startFragRequested=!1),this.tick()}else this._forceStartLoad=!0,this.state=Hi.STOPPED},r.stopLoad=function(){this._forceStartLoad=!1,e.prototype.stopLoad.call(this)},r.doTick=function(){switch(this.state){case Hi.WAITING_LEVEL:var e=this.levels,t=this.level,r=null==e?void 0:e[t],i=null==r?void 0:r.details;if(i&&(!i.live||this.levelLastLoaded===r&&!this.waitForLive(r))){if(this.waitForCdnTuneIn(i))break;this.state=Hi.IDLE;break}if(this.hls.nextLoadLevel!==this.level){this.state=Hi.IDLE;break}break;case Hi.FRAG_LOADING_WAITING_RETRY:this.checkRetryDate()}this.state===Hi.IDLE&&this.doTickIdle(),this.onTickEnd()},r.onTickEnd=function(){var t;e.prototype.onTickEnd.call(this),null!=(t=this.media)&&t.readyState&&!1===this.media.seeking&&(this.lastCurrentTime=this.media.currentTime),this.checkFragmentChanged()},r.doTickIdle=function(){var e=this.hls,t=this.levelLastLoaded,r=this.levels,i=this.media;if(null!==t&&(i||this.primaryPrefetch||!this.startFragRequested&&e.config.startFragPrefetch)&&(!this.altAudio||!this.audioOnly)){var a=this.buffering?e.nextLoadLevel:e.loadLevel;if(null!=r&&r[a]){var n=r[a],s=this.getMainFwdBufferInfo();if(null!==s){var o=this.getLevelDetails();if(o&&this._streamEnded(s,o)){var l={type:this.targetBufferType()};return this.hls.trigger(O.BUFFER_EOS,l),void(this.state=Hi.ENDED)}if(this.buffering){e.loadLevel!==a&&-1===e.manualLevel&&this.log("Adapting to level "+a+" from level "+this.level),this.level=e.nextLoadLevel=a;var u=n.details;if(!u||this.state===Hi.WAITING_LEVEL||this.waitForLive(n))return this.level=a,this.state=Hi.WAITING_LEVEL,void(this.startFragRequested=!1);var d=s.len,h=this.getMaxBufferLength(n.maxBitrate);if(!(d>=h)){this.backtrackFragment&&this.backtrackFragment.start>s.end&&(this.backtrackFragment=void 0);var f=this.backtrackFragment?this.backtrackFragment.start:s.end,c=this.getNextFragment(f,u);if(this.couldBacktrack&&!this.fragPrevious&&c&&G(c)&&this.fragmentTracker.getState(c)!==Si){var g,v=(null!=(g=this.backtrackFragment)?g:c).sn-u.startSN,m=u.fragments[v-1];m&&c.cc===m.cc&&(c=m,this.fragmentTracker.removeFragment(m))}else this.backtrackFragment&&s.len&&(this.backtrackFragment=void 0);if(c&&this.isLoopLoading(c,f)){if(!c.gap){var p=this.audioOnly&&!this.altAudio?B.AUDIO:B.VIDEO,y=(p===B.VIDEO?this.videoBuffer:this.mediaBuffer)||this.media;y&&this.afterBufferFlushed(y,p)}c=this.getNextFragmentLoopLoading(c,u,s,N.MAIN,h)}c&&(this.exceedsMaxBuffer(s,h,c)||this.loadFragment(c,n,f))}}}}}},r.loadFragment=function(t,r,i){var a=this.fragmentTracker.getState(t);a===yi||a===Ti?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,r)):e.prototype.loadFragment.call(this,t,r,i):this.clearTrackerIfNeeded(t)},r.immediateLevelSwitch=function(){var t;this.abortCurrentFrag(),this.flushMainBuffer(0,Number.POSITIVE_INFINITY),this.altAudio!==In&&((null==(t=this.getLevelDetails())?void 0:t.fragmentStart)||0)>this.lastCurrentTime&&e.prototype.flushMainBuffer.call(this,0,Number.POSITIVE_INFINITY,"audio")},r.getBufferOutput=function(){return this.mediaBuffer&&this.altAudio===Cn?this.mediaBuffer:this.media},r.checkFragmentChanged=function(){var e=this.fragPlaying;if(!this.checkFragPlaying())return!1;var t=this.fragPlaying;if(t){var r=t.level;this.hls.trigger(O.FRAG_CHANGED,{frag:t,previousFrag:e}),(null==e?void 0:e.level)!==r&&this.hls.trigger(O.LEVEL_SWITCHED,{level:r})}return!0},r.abortCurrentFrag=function(){this.backtrackFragment=void 0,e.prototype.abortCurrentFrag.call(this)},r.flushMainBuffer=function(t,r){e.prototype.flushMainBuffer.call(this,t,r,this.targetBufferType())},r.targetBufferType=function(){return this.altAudio!==Cn||this.audioOnly?null:"video"},r.onMediaAttached=function(t,r){e.prototype.onMediaAttached.call(this,t,r);var i=r.media;hr(i,"playing",this.onMediaPlaying),hr(i,"seeked",this.onMediaSeeked)},r.onMediaDetaching=function(t,r){var i=this.media;i&&(fr(i,"playing",this.onMediaPlaying),fr(i,"seeked",this.onMediaSeeked)),this.videoBuffer=null,e.prototype.onMediaDetaching.call(this,t,r),r.transferMedia||(this._hasEnoughToStart=!1)},r.onManifestLoading=function(){e.prototype.onManifestLoading.call(this),this.log("Trigger BUFFER_RESET"),this.hls.trigger(O.BUFFER_RESET,void 0),this.couldBacktrack=!1,this.backtrackFragment=void 0,this.altAudio=In,this.audioOnly=!1},r.onManifestParsed=function(e,t){for(var r,i,a=!1,n=!1,s=0;s<t.levels.length;s++){var o=t.levels[s].audioCodec;o&&(a=a||-1!==o.indexOf("mp4a.40.2"),n=n||-1!==o.indexOf("mp4a.40.5"))}this.audioCodecSwitch=a&&n&&!("function"==typeof(null==(i=kn())||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=t.levels,this.startFragRequested=!1},r.onLevelLoading=function(e,t){if(this.levels&&this.state===Hi.IDLE){var r=t.levelInfo;(!r.details||r.details.live&&(this.levelLastLoaded!==r||r.details.expired)||this.waitForCdnTuneIn(r.details))&&(this.state=Hi.WAITING_LEVEL)}},r.onLevelLoaded=function(e,t){var r,i=this,a=this.levels,n=this.startFragRequested,s=t.level,o=t.details,l=o.totalduration;if(a){this.log("Level "+s+" loaded ["+o.startSN+","+o.endSN+"]"+(o.lastPartSn?"[part-"+o.lastPartSn+"-"+o.lastPartIndex+"]":"")+", cc ["+o.startCC+", "+o.endCC+"] duration:"+l);var u=t.levelInfo,d=this.fragCurrent;!d||this.state!==Hi.FRAG_LOADING&&this.state!==Hi.FRAG_LOADING_WAITING_RETRY||d.level!==t.level&&d.loader&&this.abortCurrentFrag();var h=0;if(o.live||null!=(r=u.details)&&r.live){var f;if(this.checkLiveUpdate(o),o.deltaUpdateFailed)return;h=this.alignPlaylists(o,u.details,null==(f=this.levelLastLoaded)?void 0:f.details)}if(u.details=o,this.levelLastLoaded=u,n||this.setStartPosition(o,h),this.hls.trigger(O.LEVEL_UPDATED,{details:o,level:s}),this.state===Hi.WAITING_LEVEL){if(this.waitForCdnTuneIn(o))return;this.state=Hi.IDLE}n&&o.live&&this.synchronizeToLiveEdge(o),this.initPTS.some((function(e,t){if(t>=o.startCC)return!0;delete i.initPTS[t]})),this.tick()}else this.warn("Levels were reset while loading level "+s)},r.synchronizeToLiveEdge=function(e){var t=this.config,r=this.media;if(r){var i=this.hls.liveSyncPosition,a=this.playhead,n=e.fragmentStart,s=e.edge,o=a>=n-t.maxFragLookUpTolerance&&a<=s;if(null!==i&&r.duration>i&&(a<i||!o)){var l=void 0!==t.liveMaxLatencyDuration?t.liveMaxLatencyDuration:t.liveMaxLatencyDurationCount*e.targetduration;if((!o&&r.readyState<4||a<s-l)&&(this._hasEnoughToStart||(this.nextLoadPosition=i),r.readyState))if(this.warn("Playback: "+a+" is located too far from the end of live sliding playlist: "+s+", reset currentTime to : "+i),"buffered"===this.config.liveSyncMode){var u,d=dr.bufferInfo(r,i,0);if(null==(u=d.buffered)||!u.length)return void(r.currentTime=i);if(d.start<=a)return void(r.currentTime=i);var h=dr.bufferedInfo(d.buffered,a,0).nextStart;h&&(r.currentTime=h)}else r.currentTime=i}}},r._handleFragmentLoadProgress=function(e){var t,r=e.frag,i=e.part,a=e.payload,n=this.levels;if(n){var s=n[r.level];if(s){var 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,d=null==(t=r.initSegment)?void 0:t.data,h=this._getAudioCodec(s),f=this.transmuxer=this.transmuxer||new bn(this.hls,N.MAIN,this._handleTransmuxComplete.bind(this),this._handleTransmuxerFlush.bind(this)),c=i?i.index:-1,g=-1!==c,v=wi(r,i,this.iframesOnly),m=v.decryptRange,p=v.byteRange,y=new Ni(r.level,r.sn,r.stats.chunkCount,p?p.end-p.start:a.byteLength,c,g,r.duration,this.iframesOnly,m),E=this.initPTS[r.cc];f.push(a,d,h,l,r,i,o.totalduration,u,y,E)}else this.warn("Level "+r.level+" not found on progress")}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(e,t){var r=this,i=this.hls,a=this.altAudio!==In;if(Vt(t.url,i))this.altAudio=_n;else{if(this.mediaBuffer!==this.media){this.log("Switching on main audio, use media.buffered to schedule main fragment loading"),this.mediaBuffer=this.media;var n=this.fragCurrent;n&&(this.log("Switching to main audio track, cancel main fragment load"),n.abortRequests(),this.fragmentTracker.removeFragment(n)),this.resetTransmuxer(),this.resetLoadingState()}else this.audioOnly&&this.resetTransmuxer();if(a)return this.altAudio=In,this.fragmentTracker.removeAllFragments(),i.once(O.BUFFER_FLUSHED,(function(){r.hls&&r.hls.trigger(O.AUDIO_TRACK_SWITCHED,t)})),void i.trigger(O.BUFFER_FLUSHING,{startOffset:0,endOffset:Number.POSITIVE_INFINITY,type:null});i.trigger(O.AUDIO_TRACK_SWITCHED,t)}},r.onAudioTrackSwitched=function(e,t){var r=Vt(t.url,this.hls);if(r){var i=this.videoBuffer;i&&this.mediaBuffer!==i&&(this.log("Switching on alternate audio, use video.buffered to schedule main fragment loading"),this.mediaBuffer=i)}this.altAudio=r?Cn:In,this.tick()},r.onBufferCreated=function(e,t){var r,i,a=t.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},r.onFragBuffered=function(e,t){var r=t.frag,i=t.part,a=r.type===N.MAIN;if(a){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===Hi.PARSED&&(this.state=Hi.IDLE));var n=!1;if(G(r)&&(n=!!r.gap&&!r.tagList.some((function(e){return"GAP"===e[0]}))),this.fragBufferedComplete(r,i),n){var s;r.stats.retry++;var o=null==(s=this.levels)?void 0:s[r.level];o&&o.fragmentError++}}var l=this.media;l&&(!this._hasEnoughToStart&&dr.getBuffered(l).length&&(this._hasEnoughToStart=!0,this.seekToStartPos()),a&&this.tick())},r.onError=function(e,t){var r;if(t.fatal)this.state=Hi.ERROR;else switch(t.details){case w.FRAG_GAP:case w.FRAG_PARSING_ERROR:case w.FRAG_DECRYPT_ERROR:case w.FRAG_LOAD_ERROR:case w.FRAG_LOAD_TIMEOUT:case w.KEY_LOAD_ERROR:case w.KEY_LOAD_TIMEOUT:this.onFragmentOrKeyLoadError(N.MAIN,t);break;case w.LEVEL_LOAD_ERROR:case w.LEVEL_LOAD_TIMEOUT:case w.LEVEL_PARSING_ERROR:t.levelRetry||this.state!==Hi.WAITING_LEVEL||(null==(r=t.context)?void 0:r.type)!==F.LEVEL||(this.state=Hi.IDLE);break;case w.BUFFER_APPEND_NO_PROGRESS:if("main"!==t.parent)return;t.frag&&(t.appendsWithoutProgress||0)>=this.config.appendErrorMaxRetry&&(this.warn("Marking fragment "+t.frag.sn+" of level "+t.frag.level+" as a gap after "+t.appendsWithoutProgress+" appends without buffered range growth, to prevent loop loading"),this.fragmentTracker.addAsGap(t.frag));break;case w.BUFFER_ADD_CODEC_ERROR:case w.BUFFER_APPEND_ERROR:if("main"!==t.parent)return;this.reduceLengthAndFlushBuffer(t)&&this.resetLoadingState();break;case w.BUFFER_FULL_ERROR:if("main"!==t.parent)return;this.reduceLengthAndFlushBuffer(t)&&(!this.config.interstitialsController&&this.config.assetPlayerId?this._hasEnoughToStart=!0:this.flushMainBuffer(0,Number.POSITIVE_INFINITY));break;case w.INTERNAL_EXCEPTION:this.recoverWorkerError(t)}},r.onFragLoadEmergencyAborted=function(){this.state=Hi.IDLE,this._hasEnoughToStart||(this.startFragRequested=!1,this.nextLoadPosition=this.lastCurrentTime),this.tickImmediate()},r.onBufferFlushed=function(e,t){var r=t.type;if(r!==B.AUDIO||!this.altAudio){var i=(r===B.VIDEO?this.videoBuffer:this.mediaBuffer)||this.media;i&&(this.afterBufferFlushed(i,r),this.tick())}},r.onLevelsUpdated=function(e,t){this.level>-1&&this.fragCurrent&&(this.level=this.fragCurrent.level,-1===this.level&&this.resetWhenMissingContext(this.fragCurrent)),this.levels=t.levels},r.swapAudioCodec=function(){this.audioCodecSwap=!this.audioCodecSwap},r.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=this.timelineOffset;i&&r&&(r+=i);var a=this.getLevelDetails(),n=dr.getBuffered(e),s=n.length?n.start(0):0,o=s-r,l=Math.max(this.config.maxBufferHole,this.config.maxFragLookUpTolerance);(this.config.startOnSegmentBoundary||o>0&&(o<l||this.loadingParts&&o<2*((null==a?void 0:a.partTarget)||0)))&&(this.log("adjusting start position by "+o+" to match buffer start"),r+=o,this.startPosition=r),t<r&&(this.log("seek to target start position "+r+" from current time "+t+" buffer start "+s),e.currentTime=r)}}},r._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},r._loadBitrateTestFrag=function(e,t){var r=this;e.bitrateTest=!0,this._doFragLoad(e,t).then((function(e){var i=r.hls,a=null==e?void 0:e.frag;if(a&&!r.fragContextChanged(a)){t.fragmentError=0,r.state=Hi.IDLE,r.startFragRequested=!1,r.bitrateTest=!1;var n=a.stats;n.parsing.start=n.parsing.end=n.buffering.start=n.buffering.end=self.performance.now(),i.trigger(O.FRAG_LOADED,e),a.bitrateTest=!1}})).catch((function(t){r.state!==Hi.STOPPED&&r.state!==Hi.ERROR&&(r.warn(t),r.resetFragmentLoading(e))}))},r._handleTransmuxComplete=function(e){var t=this.playlistType,r=this.hls,i=e.remuxResult,a=e.chunkMeta,n=this.getCurrentContext(a);if(n){var s=n.frag,o=n.part,l=n.level,u=i.video,d=i.text,h=i.id3,f=i.initSegment,c=l.details,g=this.altAudio?void 0:i.audio;if(this.fragContextChanged(s))this.fragmentTracker.removeFragment(s);else{if(this.state=Hi.PARSING,f){var v=f.tracks;if(v){var m=s.initSegment||s;if(this.unhandledEncryptionError(f,s))return;this._bufferInitSegment(l,v,m,a),r.trigger(O.FRAG_PARSING_INIT_SEGMENT,{frag:m,id:t,tracks:v})}var p=f.initPTS,y=f.timescale,E=this.initPTS[s.cc];if(_(p)&&(!E||E.baseTime!==p||E.timescale!==y)){var T=f.trackId;this.initPTS[s.cc]={baseTime:p,timescale:y,trackId:T};var S=this.initPTS.slice(0);r.trigger(O.INIT_PTS_FOUND,{timestampOffsets:S,frag:s,id:t,initPTS:p,timescale:y,trackId:T})}}if(u&&c){g&&"audiovideo"===u.type&&this.logMuxedErr(s);var L=c.fragments[s.sn-1-c.startSN],R=s.sn===c.startSN,A=!L||s.cc>L.cc;if(!1!==i.independent){var b=u.startPTS,k=u.endPTS,D=u.startDTS,I=u.endDTS;if(o)o.elementaryStreams[u.type]={startPTS:b,endPTS:k,startDTS:D,endDTS:I};else if(u.firstKeyFrame&&u.independent&&1===a.id&&!A&&(this.couldBacktrack=!0),u.dropped&&u.independent){var C=this.getMainFwdBufferInfo(),P=(C?C.end:this.getLoadPosition())+this.config.maxBufferHole,x=u.firstKeyFramePTS?u.firstKeyFramePTS:b;if(!R&&P<x-this.config.maxBufferHole&&!A)return void this.backtrack(s);A&&(s.gap=!0),s.setElementaryStreamInfo(u.type,s.start,k,s.start,I,!0)}else R&&b-(c.appliedTimelineOffset||0)>2&&(s.gap=!0);s.setElementaryStreamInfo(u.type,b,k,D,I),this.backtrackFragment&&(this.backtrackFragment=s),this.bufferFragmentData(u,s,o,a,R||A)}else{if(!R&&!A)return void this.backtrack(s);s.gap=!0}}if(g){var w=g.startPTS,M=g.endPTS,F=g.startDTS,N=g.endDTS;o&&(o.elementaryStreams[B.AUDIO]={startPTS:w,endPTS:M,startDTS:F,endDTS:N}),s.setElementaryStreamInfo(B.AUDIO,w,M,F,N),this.bufferFragmentData(g,s,o,a)}if(c&&null!=h&&h.samples.length){var U={id:t,frag:s,details:c,samples:h.samples};r.trigger(O.FRAG_PARSING_METADATA,U)}if(c&&d){var G={id:t,frag:s,details:c,samples:d.samples};r.trigger(O.FRAG_PARSING_USERDATA,G)}}}else this.resetWhenMissingContext(a)},r.logMuxedErr=function(e){this.warn((G(e)?"Media":"Init")+" segment with muxed audiovideo where only video expected: "+e.url)},r._bufferInitSegment=function(e,t,r,i){var a=this;if(this.state===Hi.PARSING){this.audioOnly=!!t.audio&&!t.video,this.altAudio&&!this.audioOnly&&(delete t.audio,t.audiovideo&&this.logMuxedErr(r));var n=t.audio,s=t.video,o=t.audiovideo;if(n){var l=e.audioCodec,u=At(n.codec,l);if("mp4a"===u&&(u="mp4a.40.5"),this.audioCodecSwitch){u&&(u=-1!==u.indexOf("mp4a.40.5")?"mp4a.40.2":"mp4a.40.5");var d=n.metadata;d&&"channelCount"in d&&1!==(d.channelCount||1)&&!function(){if(void 0===ut){var e=ht.match(/Firefox\/(\d+)/i);ut=e?parseInt(e[1]):0}return ut}()&&(u="mp4a.40.5")}u&&-1!==u.indexOf("mp4a.40.5")&&/Android/.test(ht)&&"audio/mpeg"!==n.container&&(u="mp4a.40.2",this.log("Android: force audio codec to "+u)),l&&l!==u&&this.log('Swapping manifest audio codec "'+l+'" for "'+u+'"'),n.levelCodec=u,n.id=N.MAIN,this.log("Init audio buffer, container:"+n.container+", codecs[selected/level/parsed]=["+(u||"")+"/"+(l||"")+"/"+n.codec+"]"),delete t.audiovideo}if(s){s.levelCodec=e.videoCodec,s.id=N.MAIN;var h=s.codec;if(4===(null==h?void 0:h.length))switch(h){case"hvc1":case"hev1":s.codec="hvc1.1.6.L120.90";break;case"av01":s.codec="av01.0.04M.08";break;case"avc1":s.codec="avc1.42e01e"}this.log("Init video buffer, container:"+s.container+", codecs[level/parsed]=["+(e.videoCodec||"")+"/"+h+"]"+(s.codec!==h?" parsed-corrected="+s.codec:"")+(s.supplemental?" supplemental="+s.supplemental:"")),delete t.audiovideo}o&&(this.iframesOnly&&this.logMuxedErr(r),this.log("Init audiovideo buffer, container:"+o.container+", codecs[level/parsed]=["+e.codecs+"/"+o.codec+"]"),delete t.video,delete t.audio);var f=Object.keys(t);if(f.length){if(this.hls.trigger(O.BUFFER_CODECS,t),!this.hls)return;f.forEach((function(e){var n=t[e].initSegment;null!=n&&n.byteLength&&a.hls.trigger(O.BUFFER_APPENDING,{type:e,data:n,frag:r,part:null,chunkMeta:i,parent:r.type})}))}this.tickImmediate()}},r.getMainFwdBufferInfo=function(){var e=this.getBufferOutput();return this.getFwdBufferInfo(e,N.MAIN)},r.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=Hi.IDLE},a(t,[{key:"backtrackFragment",get:function(){return this._backtrackFragment},set:function(e){this._backtrackFragment=e}},{key:"couldBacktrack",get:function(){return this._couldBacktrack},set:function(e){this._couldBacktrack=e}},{key:"hasEnoughToStart",get:function(){return this._hasEnoughToStart}},{key:"maxBufferLength",get:function(){var e=this.levels,t=this.level,r=null==e?void 0:e[t];return r?this.getMaxBufferLength(r.maxBitrate):this.config.maxBufferLength}},{key:"nextLevel",get:function(){var e=this.nextBufferedFrag;return e?e.level:-1}},{key:"currentFrag",get:function(){var e;if(this.fragPlaying)return this.fragPlaying;var t=(null==(e=this.media)?void 0:e.currentTime)||this.lastCurrentTime;return _(t)?this.getAppendedFrag(t):null}},{key:"currentProgramDateTime",get:function(){var e,t=(null==(e=this.media)?void 0:e.currentTime)||this.lastCurrentTime;if(_(t)){var r=this.getLevelDetails(),i=this.currentFrag||(r?Wt(null,r.fragments,t):null);if(i){var a=i.programDateTime;if(null!==a){var n=a+1e3*(t-i.start);return new Date(n)}}}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}}])}(Ki),xn=function(){function e(){this.requestTimeout=void 0,this.retryTimeout=void 0,this.retryDelay=0,this.config=null,this.callbacks=null,this.context=null,this.stats=new M}var t=e.prototype;return t.destroy=function(){this.callbacks=this.context=this.config=null,this.abortInternal(),this.stats=null},t.load=function(e,t,r){var i=this.stats;if(i.loading.start)throw new Error("Loader can only be used once.");i.loading.start=self.performance.now(),this.context=e,this.config=t,this.callbacks=r,this.loadInternal()},t.abort=function(){var e;this.abortInternal(),null!=(e=this.callbacks)&&e.onAbort&&this.callbacks.onAbort(this.stats,this.context,this.getNetworkDetails())},t.loadtimeout=function(){if(this.config){var e=this.config.loadPolicy.timeoutRetry;if(Z(e,this.stats.retry,!0))this.retry(e);else{var t;fe.warn("timeout while loading "+(null==(t=this.context)?void 0:t.url));var r=this.callbacks;r&&(this.abortInternal(),r.onTimeout(this.stats,this.context,this.getNetworkDetails()))}}},t.retry=function(e){var t=this,r=this.context,i=this.stats;this.retryDelay=Q(e,i.retry),i.retry++,fe.warn((status?"HTTP Status "+status:"Timeout")+" while loading "+(null==r?void 0:r.url)+", retrying "+i.retry+"/"+e.maxNumRetry+" in "+this.retryDelay+"ms"),this.abortInternal(),this.resetInternalLoader(),self.clearTimeout(this.retryTimeout),this.retryTimeout=self.setTimeout((function(){return t.loadInternal()}),this.retryDelay)},e}(),wn=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,a=0;a<e.length;a++){var n=e[a];r.set(n,i),i+=n.length}return r}(t,r),this.reset(),e):new Uint8Array(0)},t.reset=function(){this.chunks.length=0,this.dataLength=0},e}(),On=/(\d+)-(\d+)\/(\d+)/,Mn=function(e){function t(t){var r;return(r=e.call(this)||this).fetchSetup=void 0,r.request=null,r.response=null,r.controller=null,r.fetchSetup=t.fetchSetup||Fn,r}u(t,e);var r=t.prototype;return r.destroy=function(){this.request=null,e.prototype.destroy.call(this),this.response=null,this.controller=null,this.fetchSetup=null},r.abortInternal=function(){self.clearTimeout(this.retryTimeout),this.controller&&!this.stats.loading.end&&(this.stats.aborted=!0,this.controller.abort())},r.getNetworkDetails=function(){return this.response},r.resetInternalLoader=function(){this.response=null},r.loadInternal=function(){var e=this,t=this.config,r=this.context,i=this.stats;if(t&&r){i.loading.first=0,i.loaded=0,i.aborted=!1,this.controller=new self.AbortController;var a=function(e,t){var r={method:"GET",mode:"cors",credentials:"same-origin",signal:t,headers:new self.Headers(o({},e.headers))};return e.rangeEnd&&r.headers.set("Range","bytes="+e.rangeStart+"-"+String(e.rangeEnd-1)),r}(r,this.controller.signal),n="arraybuffer"===r.responseType,s=n?"byteLength":"length",l=t.loadPolicy,u=l.maxTimeToFirstByteMs,d=l.maxLoadTimeMs;this.request=this.fetchSetup(r,a),self.clearTimeout(this.requestTimeout),t.timeout=u&&_(u)?u:d,this.requestTimeout=self.setTimeout((function(){e.loadtimeout()}),t.timeout),(gn(this.request)?this.request.then(self.fetch):self.fetch(this.request)).then((function(a){var s;e.response=a;var o=Math.max(self.performance.now(),i.loading.start);if(self.clearTimeout(e.requestTimeout),t.timeout=d,e.requestTimeout=self.setTimeout((function(){e.loadtimeout()}),d-(o-i.loading.start)),!a.ok){var l=a.status,u=a.statusText;throw new Nn(u||"fetch, bad network response",l,a)}i.loading.first=o,i.total=function(e){var t=e.get("Content-Range");if(t){var r=function(e){var t=On.exec(e);if(t)return parseInt(t[2])-parseInt(t[1])+1}(t);if(_(r))return r}var i=e.get("Content-Length");if(i)return parseInt(i)}(a.headers)||i.total;var h=null==(s=e.callbacks)?void 0:s.onProgress;return h&&_(t.highWaterMark)?e.loadProgressively(a,i,r,t.highWaterMark,h):n?a.arrayBuffer():"json"===r.responseType?a.json():a.text()})).then((function(a){var n,o,l=e.response;if(!l)throw new Error("loader destroyed");self.clearTimeout(e.requestTimeout),i.loading.end=Math.max(self.performance.now(),i.loading.first);var u=a[s];u&&(i.loaded=i.total=u);var d={url:l.url,data:a,code:l.status},h=null==(n=e.callbacks)?void 0:n.onProgress;h&&!_(t.highWaterMark)&&h(i,r,a,l),null==(o=e.callbacks)||o.onSuccess(d,i,r,l)})).catch((function(a){if(self.clearTimeout(e.requestTimeout),!i.aborted){var n,s=a&&a.code||0,o=a?a.message:null,l=t.loadPolicy.errorRetry;Z(l,i.retry,!1,{url:r.url,data:void 0,code:s})?e.retry(l):(fe.error(s+" while loading "+r.url),null==(n=e.callbacks)||n.onError({code:s,text:o},r,a?a.details:null,i))}}))}},r.getCacheAge=function(){var e=null;if(this.response){var t=this.response.headers.get("age");e=t?parseFloat(t):null}return e},r.getResponseHeader=function(e){return this.response?this.response.headers.get(e):null},r.loadProgressively=function(e,t,r,i,a){void 0===i&&(i=0);var n=new wn,s=e.body.getReader(),o=function(){return s.read().then((function(s){if(s.done)return n.dataLength&&a(t,r,n.flush().buffer,e),Promise.resolve(new ArrayBuffer(0));var l=s.value,u=l.length;return t.loaded+=u,u<i||n.dataLength?(n.push(l),n.dataLength>=i&&a(t,r,n.flush().buffer,e)):a(t,r,l.buffer,e),o()})).catch((function(){return Promise.reject()}))};return o()},t}(xn);function Fn(e,t){return new self.Request(e.url,t)}var Nn=function(e){function t(t,r,i){var a;return(a=e.call(this,t)||this).code=void 0,a.details=void 0,a.code=r,a.details=i,a}return u(t,e),t}(v(Error)),Bn=/^age:\s*[\d.]+\s*$/im,Un=function(e){function t(t){var r;return(r=e.call(this)||this).xhrSetup=void 0,r.loader=null,r.xhrSetup=t.xhrSetup||null,r}u(t,e);var r=t.prototype;return r.destroy=function(){e.prototype.destroy.call(this),this.loader=null,this.xhrSetup=null},r.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()))},r.getNetworkDetails=function(){return this.loader},r.resetInternalLoader=function(){this.loader=null},r.loadInternal=function(){var e=this,t=this.config,r=this.context;if(t&&r){var i=this.loader=new self.XMLHttpRequest,a=this.stats;a.loading.first=0,a.loaded=0,a.aborted=!1;var n=this.xhrSetup;n?Promise.resolve().then((function(){if(e.loader===i&&!e.stats.aborted)return n.call(e,i,r.url,r)})).catch((function(t){if(e.loader===i&&!e.stats.aborted)return i.open("GET",r.url,!0),n.call(e,i,r.url,r)})).then((function(){e.loader!==i||e.stats.aborted||e.openAndSendXhr(i,r,t)})).catch((function(t){var n;null==(n=e.callbacks)||n.onError({code:i.status,text:t.message},r,i,a)})):this.openAndSendXhr(i,r,t)}},r.openAndSendXhr=function(e,t,r){e.readyState||e.open("GET",t.url,!0);var i=t.headers,a=r.loadPolicy,n=a.maxTimeToFirstByteMs,s=a.maxLoadTimeMs;if(i)for(var o in i)e.setRequestHeader(o,i[o]);t.rangeEnd&&e.setRequestHeader("Range","bytes="+t.rangeStart+"-"+(t.rangeEnd-1)),e.onreadystatechange=this.readystatechange.bind(this),e.onprogress=this.loadprogress.bind(this),e.responseType=t.responseType,self.clearTimeout(this.requestTimeout),r.timeout=n&&_(n)?n:s,this.requestTimeout=self.setTimeout(this.loadtimeout.bind(this),r.timeout),e.send()},r.readystatechange=function(){var e=this.context,t=this.loader,r=this.stats;if(e&&t){var i=t.readyState,a=this.config;if(!r.aborted&&i>=2&&(0===r.loading.first&&(r.loading.first=Math.max(self.performance.now(),r.loading.start),a.timeout!==a.loadPolicy.maxLoadTimeMs&&(self.clearTimeout(this.requestTimeout),a.timeout=a.loadPolicy.maxLoadTimeMs,this.requestTimeout=self.setTimeout(this.loadtimeout.bind(this),a.loadPolicy.maxLoadTimeMs-(r.loading.first-r.loading.start)))),4===i)){self.clearTimeout(this.requestTimeout),t.onreadystatechange=null,t.onprogress=null;var n=t.status,s="text"===t.responseType?t.responseText:null;if(n>=200&&n<300){var o=null!=s?s:t.response;if(null!=o){var l,u;r.loading.end=Math.max(self.performance.now(),r.loading.first);var d="arraybuffer"===t.responseType?o.byteLength:o.length;r.loaded=r.total=d,r.bwEstimate=8e3*r.total/(r.loading.end-r.loading.first);var h=null==(l=this.callbacks)?void 0:l.onProgress;h&&h(r,e,o,t);var f={url:t.responseURL,data:o,code:n};return e.rangeEnd&&d!==e.rangeEnd-e.rangeStart&&fe.warn("Payload length "+d+" does not match requested Range: bytes="+e.rangeStart+"-"+(e.rangeEnd-1)),void(null==(u=this.callbacks)||u.onSuccess(f,r,e,t))}}var c,g=a.loadPolicy.errorRetry;Z(g,r.retry,!1,{url:e.url,data:void 0,code:n})?this.retry(g):(fe.error(n+" while loading "+e.url),null==(c=this.callbacks)||c.onError({code:n,text:t.statusText},e,t,r))}}},r.loadprogress=function(e){var t=this.stats;t.loaded=e.loaded,e.lengthComputable&&(t.total=e.total)},r.getCacheAge=function(){var e=null;if(this.loader&&Bn.test(this.loader.getAllResponseHeaders())){var t=this.loader.getResponseHeader("age");e=t?parseFloat(t):null}return e},r.getResponseHeader=function(e){return this.loader&&new RegExp("^"+e+":\\s*[\\d.]+\\s*$","im").test(this.loader.getAllResponseHeaders())?this.loader.getResponseHeader(e):null},t}(xn),Gn={maxTimeToFirstByteMs:8e3,maxLoadTimeMs:2e4,timeoutRetry:null,errorRetry:null},Vn=f(f({autoStartLoad:!0,startPosition:-1,defaultAudioCodec:void 0,debug:!1,capLevelOnFPSDrop:!1,capLevelToPlayerSize:!1,ignoreDevicePixelRatio:!1,maxDevicePixelRatio:Number.POSITIVE_INFINITY,preferManagedMediaSource:!1,initialLiveManifestSize:1,maxBufferLength:30,backBufferLength:1/0,frontBufferFlushThreshold:1/0,loopBackBufferFlush:void 0,startOnSegmentBoundary:!1,nextAudioTrackBufferFlushForwardOffset:.25,maxBufferSize:6e7,maxFragLookUpTolerance:.25,maxBufferHole:.1,detectStallWithCurrentTimeMs:1250,highBufferWatchdogPeriod:2,nudgeOffset:.1,nudgeMaxRetry:3,nudgeOnVideoHole:!0,skipBufferHolePadding:.1,liveSyncMode:"edge",liveSyncDurationCount:3,liveSyncOnStallIncrease:1,liveMaxLatencyDurationCount:1/0,liveMaxUnchangedPlaylistRefresh: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,appendTimeout:1/0,ignorePlaylistParsingErrors:!1,loader:Un,fLoader:void 0,pLoader:void 0,xhrSetup:void 0,licenseXhrSetup:void 0,licenseResponseCallback:void 0,stretchShortVideoTrack:!1,maxAudioFramesDrift:1,forceKeyFrameOnDiscontinuity:!0,handleMpegTsVideoIntegrityErrors:"process",abrEwmaFastLive:3,abrEwmaSlowLive:9,abrEwmaFastVoD:3,abrEwmaSlowVoD:9,abrEwmaDefaultEstimate:5e5,abrEwmaDefaultEstimateMax:5e6,abrBandWidthFactor:.95,abrBandWidthUpFactor:.7,abrMaxWithRealBitrate:!1,abrSwitchInterval:0,maxStarvationDelay:4,maxLoadingDelay:4,minAutoBitrate:0,emeEnabled:!1,widevineLicenseUrl:void 0,drmSystems:{},drmSystemOptions:{},requestMediaKeySystemAccessFunc:null,requireKeySystemAccessOnStart:!1,testBandwidth:!0,progressive:!1,lowLatencyMode:!0,cmcd:void 0,enableDateRangeMetadataCues:!0,enableEmsgMetadataCues:!0,enableEmsgKLVMetadata:!1,enableID3MetadataCues:!0,emsgKLVSchemaUri:void 0,enableInterstitialPlayback:!1,interstitialAppendInPlace:!0,interstitialLiveLookAhead:10,iframeCacheLimit:2097152,useMediaCapabilities:!1,preserveManualLevelOnError:!1,errorPenaltyExpireMs:0,certLoadPolicy:{default:Gn},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}}},interstitialAssetListLoadPolicy:{default:Gn},manifestLoadingTimeOut:1e4,manifestLoadingMaxRetry:1,manifestLoadingRetryDelay:1e3,manifestLoadingMaxRetryTimeout:64e3,levelLoadingTimeOut:1e4,levelLoadingMaxRetry:4,levelLoadingRetryDelay:1e3,levelLoadingMaxRetryTimeout:64e3,fragLoadingTimeOut:2e4,fragLoadingMaxRetry:6,fragLoadingRetryDelay:1e3,fragLoadingMaxRetryTimeout:64e3,streamController:Pn,abrController:Ht,bufferController:pr,capLevelController:Ar,errorController:ar,fpsController:pi,id3TrackController:ea,gapController:Yi,latencyController:ta},{cueHandler:ge,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,contentSteeringController:gi,iframeController:void 0,interstitialsController:void 0});function Hn(e,t,r){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"');if(void 0!==t.liveMaxUnchangedPlaylistRefresh){var i=t.liveMaxUnchangedPlaylistRefresh,a=(n=i,s=2,o=1/0,Math.max(s,Math.min(n,o)));a!==i&&r.warn('hls.js config: "liveMaxUnchangedPlaylistRefresh" clamped from '+i+" to "+a+"."),t.liveMaxUnchangedPlaylistRefresh=a}var n,s,o,l=Kn(e),u=["TimeOut","MaxRetry","RetryDelay","MaxRetryTimeout"];return["manifest","level","frag"].forEach((function(e){var i=("level"===e?"playlist":e)+"LoadPolicy",a=void 0===t[i],n=[];u.forEach((function(r){var s=e+"Loading"+r,o=t[s];if(void 0!==o&&a){n.push(s);var u=l[i].default;switch(t[i]={default:u},r){case"TimeOut":u.maxLoadTimeMs=o,u.maxTimeToFirstByteMs=o;break;case"MaxRetry":u.errorRetry.maxNumRetry=o,u.timeoutRetry.maxNumRetry=o;break;case"RetryDelay":u.errorRetry.retryDelayMs=o,u.timeoutRetry.retryDelayMs=o;break;case"MaxRetryTimeout":u.errorRetry.maxRetryDelayMs=o,u.timeoutRetry.maxRetryDelayMs=o}}})),n.length&&r.warn('hls.js config: "'+n.join('", "')+'" setting(s) are deprecated, use "'+i+'": '+Bt(t[i]))})),f(f({},l),t)}function Kn(e){return e&&"object"==typeof e?Array.isArray(e)?e.map(Kn):Object.keys(e).reduce((function(t,r){return t[r]=Kn(e[r]),t}),{}):e}function Yn(e,t){var r=e.loader;r!==Mn&&r!==Un?(t.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=Mn,e.progressive=!0,e.enableSoftwareAES=!0,t.log("[config]: Progressive streaming enabled, using FetchLoader"))}var Wn=function(e){function t(t,r){var i;return(i=e.call(this,r,t.logger)||this).hls=void 0,i.canLoad=!1,i.timer=-1,i.loadingSync=!1,i.hls=t,i}u(t,e);var r=t.prototype;return r.destroy=function(){this.clearTimer(),this.hls=null},r.clearTimer=function(){-1!==this.timer&&(self.clearTimeout(this.timer),this.timer=-1)},r.startLoad=function(){this.canLoad=!0,this.loadPlaylist()},r.stopLoad=function(){this.canLoad=!1,this.clearTimer()},r.switchParams=function(e,t,r){var i=null==t?void 0:t.renditionReports;if(i){for(var a=-1,n=0;n<i.length;n++){var s=i[n],o=void 0;try{o=new self.URL(s.URI,t.url).href}catch(e){this.warn("Could not construct new URL for Rendition Report: "+e),o=s.URI||""}if(o===e){a=n;break}o===e.substring(0,o.length)&&(a=n)}if(-1!==a){var l=i[a],u=parseInt(l["LAST-MSN"])||t.lastPartSn,d=parseInt(l["LAST-PART"])||t.lastPartIndex;if(this.hls.config.lowLatencyMode){var h=Math.min(t.age-t.partTarget,t.targetduration);d>=0&&h>t.partTarget&&(d+=1)}var f=r&&wt(r);return new Ot(u,d>=0?d:void 0,f)}}},r.loadPlaylist=function(e){this.clearTimer()},r.loadingPlaylist=function(e,t){this.clearTimer()},r.shouldLoadPlaylist=function(e){return this.canLoad&&!!e&&!!e.url&&(!e.details||e.details.live)},r.getUrlWithDirectives=function(e,t){if(t)try{return t.addDirectives(e)}catch(e){this.warn("Could not construct new URL with HLS Delivery Directives: "+e)}return e},r.playlistLoaded=function(e,t,r){var i=t.details,a=t.stats,n=i.fragments,s=self.performance.now(),o=a.loading.first?Math.max(0,Math.floor(s-a.loading.first)):0;i.advancedDateTime=Date.now()-o;var l=this.hls.config.timelineOffset;if(l!==i.appliedTimelineOffset){var u=Math.max(l||0,0);i.appliedTimelineOffset=u,n.forEach((function(e){null==e||e.setStart(e.playlistOffset+u)}))}if(i.live||null!=r&&r.live){var d,h="levelInfo"in t?t.levelInfo:t.track;i.reloaded(r);var f=null==(d=n[n.length-1])?void 0:d.type;if(i.misses>=this.hls.config.liveMaxUnchangedPlaylistRefresh){var c=new Error(f+" playlist "+h.id+" hit max allowed unchanged reloads.");this.warn(c);var g=t.networkDetails,v=t.context;return void this.hls.trigger(O.ERROR,{type:x.NETWORK_ERROR,details:w.PLAYLIST_UNCHANGED_ERROR,fatal:!1,url:i.url,error:c,reason:c.message,level:t.level,parent:f,context:v,networkDetails:g,stats:a})}if(r){ai(r,i,this);var m=i.playlistParsingError;if(m){this.warn(m);var p=this.hls;if(!p.config.ignorePlaylistParsingErrors){var y=t.networkDetails;return void p.trigger(O.ERROR,{type:x.NETWORK_ERROR,details:w.LEVEL_PARSING_ERROR,fatal:!1,url:i.url,error:m,reason:m.message,level:t.level,parent:f,networkDetails:y,stats:a})}i.playlistParsingError=null}}-1===i.requestScheduled&&(i.requestScheduled=a.loading.start);var E=this.hls.mainForwardBufferInfo,T=E?E.end-E.len:0,S=oi(i,1e3*(i.edge-T));i.requestScheduled+S<s?i.requestScheduled=s:i.requestScheduled<=s&&(i.requestScheduled+=S);var L,R=t.deliveryDirectives;if(this.log("live playlist "+e+" "+(i.advanced?"REFRESHED "+i.lastPartSn+" - "+i.lastPartIndex:i.updated?"UPDATED":"MISSED (received "+i.lastPartSn+" - "+i.lastPartIndex+(R?" | requested "+R.msn+" - "+R.part:"")+")")),!this.canLoad||!i.live)return;var A=void 0,b=void 0;if(i.canBlockReload&&i.advanced){var k=this.hls.config.lowLatencyMode,D=i.lastPartSn,I=i.endSN,_=i.lastPartIndex,C=D===I;-1!==_?C?(A=I+1,b=k?0:_):(A=D,b=k?_+1:i.maxPartIndex):A=I+1;var P=i.age,M=P+i.ageHeader,F=Math.min(M-i.partTarget,1.5*i.targetduration);if(F>0){if(M>3*i.targetduration)this.log("Playlist last advanced "+P.toFixed(2)+"s ago. Omitting segment and part directives."),A=void 0,b=void 0;else if(null!=r&&r.tuneInGoal&&M-i.partTarget>r.tuneInGoal)this.warn("CDN Tune-in goal increased from: "+r.tuneInGoal+" to: "+F+" with playlist age: "+i.age),F=0;else{var N=Math.floor(F/i.targetduration);A+=N,void 0!==b&&(b+=Math.round(F%i.targetduration/i.partTarget)),this.log("CDN Tune-in age: "+i.ageHeader+"s last advanced "+P.toFixed(2)+"s goal: "+F+" skip sn "+N+" to part "+b)}i.tuneInGoal=F}if(L=this.getDeliveryDirectives(i,t.deliveryDirectives,A,b),k||!C)return i.requestScheduled=s,void this.loadingPlaylist(h,L)}else(i.canBlockReload||i.canSkipUntil)&&(L=this.getDeliveryDirectives(i,t.deliveryDirectives,A,b));L&&void 0!==A&&i.canBlockReload&&(i.requestScheduled=a.loading.first+Math.max(S-2*o,S/2)),this.scheduleLoading(h,L,i)}else this.clearTimer()},r.scheduleLoading=function(e,t,r){var i=this,a=r||e.details;if(a){var n=self.performance.now(),s=a.requestScheduled;if(n>=s&&!this.loadingSync)return this.loadingSync=!0,this.loadingPlaylist(e,t),void(this.loadingSync=!1);var o=Math.max(s-n,0);this.log("reload live playlist "+(e.name||e.bitrate+"bps")+" in "+Math.round(o)+" ms"),this.clearTimer(),this.timer=self.setTimeout((function(){return i.loadingPlaylist(e,t)}),o)}else this.loadingPlaylist(e,t)},r.getDeliveryDirectives=function(e,t,r,i){var a=wt(e);return null!=t&&t.skip&&e.deltaUpdateFailed&&(r=t.msn,i=t.part,a=Ct),new Ot(r,i,a)},r.checkRetry=function(e){var t=this,r=e.details,i=j(e),a=e.errorAction,n=a||{},s=n.action,o=n.retryCount,l=void 0===o?0:o,u=n.retryConfig,d=!!a&&!!u&&(s===$t||!a.resolved&&s===zt);if(d){var h;if(l>=u.maxNumRetry)return!1;if(i&&null!=(h=e.context)&&h.deliveryDirectives)this.warn("Retrying playlist loading "+(l+1)+"/"+u.maxNumRetry+' after "'+r+'" without delivery-directives'),this.loadPlaylist();else{var f,c=J(null==(f=e.response)?void 0:f.code);if(this.clearTimer(),c)this.log("Waiting for connection (offline)"),e.reason="offline",this.timer=self.setTimeout((function(){return t.checkOfflineStatus()}),1e3);else{var g=Q(u,l);this.warn("Retrying playlist loading "+(l+1)+"/"+u.maxNumRetry+' after "'+r+'" in '+g+"ms"),this.timer=self.setTimeout((function(){return t.loadPlaylist()}),g)}}e.levelRetry=!0,a.resolved=!0}return d},r.checkOfflineStatus=function(){var e=this;this.clearTimer(),J(0)?this.timer=self.setTimeout((function(){return e.checkOfflineStatus()}),1e3):(this.log("Connection restored (online)"),this.loadPlaylist())},t}(ie),jn=function(e){function t(t,r){var i;return(i=e.call(this,t,"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.lastABRSwitchTime=-1,i._iframeVariants=[],i.onParsedComplete=void 0,i.steering=r,i._registerListeners(),i}u(t,e);var r=t.prototype;return r._registerListeners=function(){var e=this.hls;e.on(O.MANIFEST_LOADING,this.onManifestLoading,this),e.on(O.MANIFEST_LOADED,this.onManifestLoaded,this),e.on(O.LEVEL_LOADED,this.onLevelLoaded,this),e.on(O.LEVELS_UPDATED,this.onLevelsUpdated,this),e.on(O.FRAG_BUFFERED,this.onFragBuffered,this),e.on(O.ERROR,this.onError,this)},r._unregisterListeners=function(){var e=this.hls;e.off(O.MANIFEST_LOADING,this.onManifestLoading,this),e.off(O.MANIFEST_LOADED,this.onManifestLoaded,this),e.off(O.LEVEL_LOADED,this.onLevelLoaded,this),e.off(O.LEVELS_UPDATED,this.onLevelsUpdated,this),e.off(O.FRAG_BUFFERED,this.onFragBuffered,this),e.off(O.ERROR,this.onError,this)},r.destroy=function(){this._unregisterListeners(),this.steering=null,this.resetLevels(),e.prototype.destroy.call(this)},r.stopLoad=function(){this._levels.forEach((function(e){e.loadError=0,e.fragmentError=0})),e.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,this._iframeVariants=[]},r.onManifestLoading=function(e,t){this.resetLevels()},r.onManifestLoaded=function(e,t){var r=this,i=this.hls.config.preferManagedMediaSource,a=[],n={},s={},o=!1,l=!1,u=!1,d=!1;t.levels.forEach((function(e){var t=e.attrs,h=zn(e,i),f=e.audioCodec,c=e.videoCodec,g=e.imageCodec,v=e.width,m=e.height;if(h){o||(o=!(!v||!m)),l||(l=!!c),u||(u=!!f),d||(d=!!g);var p=t.CODECS,y=t["FRAME-RATE"],E=t["HDCP-LEVEL"],T=t["PATHWAY-ID"],S=t.RESOLUTION,L=t["VIDEO-RANGE"],R=(T||".")+"-"+e.bitrate+"-"+S+"-"+y+"-"+p+"-"+L+"-"+E;if(n[R])if(n[R].uri===e.url||e.attrs["PATHWAY-ID"])e.iframes||(n[R].addGroupId("audio",t.AUDIO),n[R].addGroupId("text",t.SUBTITLES));else{var A=s[R]+=1;e.attrs["PATHWAY-ID"]=new Array(A+1).join(".");var b=r.createLevel(e);n[R]=b,a.push(b)}else{var k=r.createLevel(e);n[R]=k,s[R]=1,a.push(k)}}else r.log('Some or all CODECS not supported "'+t.CODECS+'"')})),this.filterAndSortMediaOptions(a,t,o,l,u,d)},r.createLevel=function(e){var t=new Mt(e),r=e.supplemental;if(null!=r&&r.videoCodec&&!Xn(r.videoCodec,this.hls.config.preferManagedMediaSource)){var i=new Error('SUPPLEMENTAL-CODECS not supported "'+r.videoCodec+'"');this.log(i.message),t.supportedResult=ce.getUnsupportedResult(i,[])}return t},r.filterAndSortMediaOptions=function(e,t,r,i,a,n){var s,o=this,l=[],u=[],d=e,h=(null==(s=t.stats)?void 0:s.parsing)||{},f=this.hls.config.preferManagedMediaSource;if((r||i)&&a&&(d=d.filter((function(e){var t,r=e.videoCodec,i=e.videoRange,a=e.width,n=e.height;return(!!r||!(!a||!n))&&!!(t=i)&&_t.indexOf(t)>-1}))),i&&n&&(d=d.filter((function(e){return!e.imageCodec}))),0===d.length)return Promise.resolve().then((function(){if(o.hls){var e="no level with compatible codecs found in manifest",r=e;t.levels.length&&(r="one or more CODECS in variant not supported: "+Bt(t.levels.map((function(e){return e.attrs.CODECS})).filter((function(e,t,r){return r.indexOf(e)===t}))),o.warn(r),e+=" ("+r+")");var i=new Error(e);o.hls.trigger(O.ERROR,{type:x.MEDIA_ERROR,details:w.MANIFEST_INCOMPATIBLE_CODECS_ERROR,fatal:!0,url:t.url,error:i,reason:r})}})),void(h.end=performance.now());t.audioTracks&&Qn(l=t.audioTracks.filter((function(e){return!e.audioCodec||qn(e.audioCodec,f)}))),t.subtitles&&Qn(u=t.subtitles);var c=d.slice(0);d.sort((function(e,t){if(e.attrs["HDCP-LEVEL"]!==t.attrs["HDCP-LEVEL"])return(e.attrs["HDCP-LEVEL"]||"")>(t.attrs["HDCP-LEVEL"]||"")?1:-1;if(r&&e.height!==t.height)return e.height-t.height;if(e.frameRate!==t.frameRate)return e.frameRate-t.frameRate;if(e.videoRange!==t.videoRange)return _t.indexOf(e.videoRange)-_t.indexOf(t.videoRange);if(e.videoCodec!==t.videoCodec){var i=Et(e.videoCodec),a=Et(t.videoCodec);if(i!==a)return a-i}if(e.uri===t.uri&&e.codecSet!==t.codecSet){var n=Tt(e.codecSet),s=Tt(t.codecSet);if(n!==s)return s-n}return e.averageBitrate!==t.averageBitrate?e.averageBitrate-t.averageBitrate:0}));var g=c[0];if(this.steering&&(d=this.steering.filterParsedLevels(d)).length!==c.length)for(var v=0;v<c.length;v++)if(c[v].pathwayId===d[0].pathwayId){g=c[v];break}this._levels=d;for(var m=0;m<d.length;m++)if(d[m]===g){var p;this._firstLevel=m;var y=g.bitrate,E=this.hls.bandwidthEstimate;if(this.log("manifest loaded, "+d.length+" level(s) found, first bitrate: "+y),void 0===(null==(p=this.hls.userConfig)?void 0:p.abrEwmaDefaultEstimate)){var T=Math.min(y,this.hls.config.abrEwmaDefaultEstimateMax);T>E&&E===this.hls.abrEwmaDefaultEstimate&&(this.hls.bandwidthEstimate=T)}break}var S=t.iframeVariants.filter((function(e){return zn(e,f)}));this._iframeVariants=S;var L=a&&!i,R=this.hls.config,A=!(!R.audioStreamController||!R.audioTrackController),b={levels:d,audioTracks:l,subtitleTracks:u,iframeVariants:S,sessionData:t.sessionData,sessionKeys:t.sessionKeys,firstLevel:this._firstLevel,stats:t.stats,audio:a,video:i,altAudio:A&&!L&&l.some((function(e){return!!e.url}))};h.end=performance.now(),this.hls.trigger(O.MANIFEST_PARSED,b)},r.onError=function(e,t){!t.fatal&&t.context&&t.context.type===F.LEVEL&&t.context.level===this.level&&this.checkRetry(t)},r.onFragBuffered=function(e,t){var r=t.frag;if(r.type===N.MAIN){var i=r.elementaryStreams;if(!Object.keys(i).some((function(e){return!!i[e]})))return;var a=this._levels[r.level];null!=a&&a.loadError&&(this.log("Resetting level error count of "+a.loadError+" on frag buffered"),a.loadError=0)}},r.onLevelLoaded=function(e,t){var r,i,a=t.level,n=t.details,s=t.levelInfo;if(!s)return this.warn("Invalid level index "+a),void(null!=(i=t.deliveryDirectives)&&i.skip&&(n.deltaUpdateFailed=!0));if(s===this.currentLevel||t.withoutMultiVariant){0===s.fragmentError&&(s.loadError=0);var o=s.details;o===t.details&&o.advanced&&(o=void 0),this.playlistLoaded(a,t,o)}else null!=(r=t.deliveryDirectives)&&r.skip&&(n.deltaUpdateFailed=!0)},r.loadPlaylist=function(t){e.prototype.loadPlaylist.call(this),this.shouldLoadPlaylist(this.currentLevel)&&this.scheduleLoading(this.currentLevel,t)},r.loadingPlaylist=function(t,r){e.prototype.loadingPlaylist.call(this,t,r);var i=this.getUrlWithDirectives(t.uri,r),a=this.currentLevelIndex,n=t.attrs["PATHWAY-ID"],s=t.details,o=null==s?void 0:s.age;this.log("Loading level index "+a+(void 0!==(null==r?void 0:r.msn)?" at sn "+r.msn+" part "+r.part:"")+(n?" Pathway "+n:"")+(o&&s.live?" age "+o.toFixed(1)+(s.type&&" "+s.type||""):"")+" "+i),this.hls.trigger(O.LEVEL_LOADING,{url:i,level:a,levelInfo:t,pathwayId:t.attrs["PATHWAY-ID"],id:0,deliveryDirectives:r||null})},r.removeLevel=function(e){var t,r=this;if(1!==this._levels.length){var i=this._levels.filter((function(t,i){return i!==e||(r.steering&&r.steering.removeLevel(t),t===r.currentLevel&&(r.currentLevel=null,r.currentLevelIndex=-1,t.details&&t.details.fragments.forEach((function(e){return e.level=-1}))),!1)}));hi(i),this._levels=i,this.currentLevelIndex>-1&&null!=(t=this.currentLevel)&&t.details&&(this.currentLevelIndex=this.currentLevel.details.fragments[0].level),this.manualLevelIndex>-1&&(this.manualLevelIndex=this.currentLevelIndex);var a=i.length-1;this._firstLevel=Math.min(this._firstLevel,a),this._startLevel&&(this._startLevel=Math.min(this._startLevel,a)),this.hls.trigger(O.LEVELS_UPDATED,{levels:i})}},r.onLevelsUpdated=function(e,t){var r=t.levels;this._levels=r},r.checkMaxAutoUpdated=function(){var e=this.hls,t=e.autoLevelCapping,r=e.maxAutoLevel,i=e.maxHdcpLevel;this._maxAutoLevel!==r&&(this._maxAutoLevel=r,this.hls.trigger(O.MAX_AUTO_LEVEL_UPDATED,{autoLevelCapping:t,levels:this.levels,maxAutoLevel:r,minAutoLevel:this.hls.minAutoLevel,maxHdcpLevel:i}))},a(t,[{key:"iframeVariants",get:function(){return 0===this._iframeVariants.length?null:this._iframeVariants}},{key:"levels",get:function(){return 0===this._levels.length?null:this._levels}},{key:"loadLevelObj",get:function(){return this.currentLevel}},{key:"level",get:function(){return this.currentLevelIndex},set:function(e){var t=this._levels;if(0!==t.length){if(e<0||e>=t.length){var r=new Error("invalid level idx"),i=e<0;if(this.hls.trigger(O.ERROR,{type:x.OTHER_ERROR,details:w.LEVEL_SWITCH_ERROR,level:e,fatal:i,error:r,reason:r.message}),i)return;e=Math.min(e,t.length-1)}var a=this.currentLevelIndex,n=this.currentLevel,s=n?n.attrs["PATHWAY-ID"]:void 0,o=t[e],l=o.attrs["PATHWAY-ID"];if(this.currentLevelIndex=e,this.currentLevel=o,a!==e||!n||s!==l){this.log("Switching to level "+e+" ("+(o.height?o.height+"p ":"")+(o.videoRange?o.videoRange+" ":"")+(o.codecSet?o.codecSet+" ":"")+"@"+o.bitrate+")"+(l?" with Pathway "+l:"")+" from level "+a+(s?" with Pathway "+s:""));var u={level:e,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(O.LEVEL_SWITCHING,u);var d=o.details;if(!d||d.live){var h=this.switchParams(o.uri,null==n?void 0:n.details,d);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.hls.firstAutoLevel}return this._startLevel},set:function(e){this._startLevel=e}},{key:"pathways",get:function(){return this.steering?this.steering.pathways():[]}},{key:"pathwayPriority",get:function(){return this.steering?this.steering.pathwayPriority:null},set:function(e){if(this.steering){var t=this.steering.pathways(),r=e.filter((function(e){return-1!==t.indexOf(e)}));if(e.length<1)return void this.warn("pathwayPriority "+e+" should contain at least one pathway from list: "+t);this.steering.pathwayPriority=r}}},{key:"nextLoadLevel",get:function(){return-1!==this.manualLevelIndex?this.manualLevelIndex:this.hls.nextAutoLevel},set:function(e){var t=this.currentLevelIndex;if(-1===this.manualLevelIndex&&e!==t&&-1!==e){var r=this.hls.config.abrSwitchInterval;if(r>0){var i=performance.now(),a=i-this.lastABRSwitchTime,n=1e3*r;if(this.lastABRSwitchTime>-1&&a<n)return void this.warn("Preventing ABR level switch: "+t+" -> "+e+" ("+Math.round(a)+"ms < "+n+"ms / "+r+"s)");this.lastABRSwitchTime=i,this.log("Allowing ABR level switch: "+t+" -> "+e+" ("+Math.round(a)+"ms >= "+n+"ms / "+r+"s)")}}this.level=e,-1===this.manualLevelIndex&&(this.hls.nextAutoLevel=e)}}])}(Wn);function qn(e,t){return mt(e,"audio",t)}function Xn(e,t){return mt(e,"video",t)}function zn(e,t){var r=e.audioCodec,i=e.videoCodec;r&&(e.audioCodec=r=Rt(r,t)||void 0),i&&(i=e.videoCodec=function(e){for(var t=e.split(","),r=0;r<t.length;r++){var i=t[r].split(".");i.length>2&&"avc1"===i[0]&&(t[r]="avc1."+parseInt(i[1]).toString(16)+("000"+parseInt(i[2]).toString(16)).slice(-4))}return t.join(",")}(i));var a=e.unknownCodecs;return!((null==a?void 0:a.length)||r&&!qn(r,t)||i&&!Xn(i,t))}function Qn(e){var t={};e.forEach((function(e){var r=e.groupId||"";e.id=t[r]=t[r]||0,t[r]++}))}var $n=function(e){function t(t,r){var i;return(i=e.call(this,"key-loader",r)||this).config=void 0,i.keyLoaderInfo={},i.emeController=null,i.config=t,i}u(t,e);var r=t.prototype;return r.abort=function(e){for(var t in this.keyLoaderInfo){var r=this.keyLoaderInfo[t].loader;if(r){var i;if(e&&e!==(null==(i=r.context)?void 0:i.frag.type))return;r.abort()}}},r.destroy=function(){for(var e in this.keyLoaderInfo){var t=this.keyLoaderInfo[e].loader;t&&t.destroy()}this.emeController=null,this.keyLoaderInfo={}},r.loadClear=function(e,t,r){return this.emeController?this.emeController.loadClear(e,t,r):null},r.load=function(e,t){var r=this;return!e.decryptdata&&e.encrypted&&this.emeController&&this.config.emeEnabled?this.emeController.selectKeySystemFormat(e).then((function(i){return r.loadInternal(e,t,i)})):this.loadInternal(e,t)},r.loadInternal=function(e,t,r){var i=this,a=e.decryptdata;if(!a){var n=new Error(r?"Expected frag.decryptdata to be defined after setting format "+r:"Missing decryption data on fragment in onKeyLoading (emeEnabled with controller: "+(this.emeController&&this.config.emeEnabled)+")");return Promise.reject(Zn(e,w.KEY_LOAD_ERROR,n))}var s=e;switch(a.method){case"SAMPLE-AES":case"SAMPLE-AES-CENC":case"SAMPLE-AES-CTR":return"identity"===a.keyFormat?this.loadKeyHTTP(s):t?t.then((function(){return i.loadKeyEME(s)})):this.loadKeyEME(s);case"AES-128":case"AES-256":case"AES-256-CTR":return this.loadKeyHTTP(s);default:return Promise.reject(Zn(e,w.KEY_LOAD_ERROR,new Error('Key supplied with unsupported METHOD: "'+a.method+'"')))}},r.loadKeyEME=function(e){if(this.emeController&&this.config.emeEnabled){var t;if(!e.decryptdata.keyId&&null!=(t=e.initSegment)&&t.data){var r=function(e){var t=[];return et(e,(function(e){return t.push(e.subarray(8,24))})),t}(e.initSegment.data);if(r.length){var i=r[0],a=e.decryptdata.uri;if(i.some((function(e){return 0!==e})))this.log("Using keyId found in init segment "+xe(i)+" keyUri: "+a),Nr.setKeyIdForUri(a,i);else{var n=Nr.addKeyIdForUri(a);this.log("Patching empty keyId with "+xe(n)+" keyUri: "+a),i=n}e.decryptdata.keyId=i}}return this.emeController.loadKey(e).then((function(){return{frag:e,keyInfo:{decryptdata:e.decryptdata}}}))}return Promise.reject(Zn(e,w.KEY_LOAD_ERROR,new Error("emeEnabled with controller: "+(this.emeController&&this.config.emeEnabled)+")")))},r.loadKeyHTTP=function(e){var t=this,r=e.decryptdata,i=r.uri;if(!i)return Promise.reject(Zn(e,w.KEY_LOAD_ERROR,new Error('Invalid key URI: "'+i+'"')));var a=this.keyLoaderInfo[i];if(a){if(a.decryptdata.key)return r.key=a.decryptdata.key,a.keyLoadPromise=null,Promise.resolve({frag:e,keyInfo:a});if(a.keyLoadPromise)return a.keyLoadPromise.then((function(t){return r.key=a.decryptdata.key,f(f({},t),{},{frag:e})}))}this.log("Loading"+(r.keyId?" keyId: "+xe(r.keyId):"")+" URI: "+r.uri+" from "+e.type+" "+e.level);var n=this.config,s=new(0,n.loader)(n);e.keyLoader=s;var o=this.keyLoaderInfo[i]={decryptdata:r,keyLoadPromise:null,loader:s};return o.keyLoadPromise=new Promise((function(r,a){var l={type:F.KEY,keyInfo:o,frag:e,responseType:"arraybuffer",url:i},u=n.keyLoadPolicy.default,d={loadPolicy:u,timeout:u.maxLoadTimeMs,maxRetry:0,retryDelay:0,maxRetryDelay:0},h={onSuccess:function(e,i,n,s){var o=n.frag,l=n.keyInfo,u=n.url;if(!o.decryptdata||l!==t.keyLoaderInfo[u])return a(Zn(o,w.KEY_LOAD_ERROR,new Error("after key load, decryptdata unset or changed"),s));l.decryptdata.key=o.decryptdata.key=new Uint8Array(e.data),o.keyLoader=l.loader=l.keyLoadPromise=null,r({frag:o,keyInfo:l})},onError:function(r,n,s,o){t.resetLoader(n),a(Zn(e,w.KEY_LOAD_ERROR,new Error("HTTP Error "+r.code+" loading key "+r.text),s,f({url:i,data:void 0},r)))},onTimeout:function(r,i,n){t.resetLoader(i),a(Zn(e,w.KEY_LOAD_TIMEOUT,new Error("key loading timed out"),n))},onAbort:function(r,i,n){t.resetLoader(i),a(Zn(e,w.INTERNAL_ABORTED,new Error("key loading aborted"),n))}};s.load(l,d,h)}))},r.resetLoader=function(e){var t=e.frag,r=e.keyInfo,i=e.url,a=r.loader;t.keyLoader===a&&(t.keyLoader=r.loader=r.keyLoadPromise=null),delete this.keyLoaderInfo[i],a&&a.destroy()},t}(ie);function Zn(e,t,r,i,a){return void 0===t&&(t=w.KEY_LOAD_ERROR),new Mi({type:x.NETWORK_ERROR,details:t,fatal:!1,frag:e,response:a,error:r,networkDetails:i||null})}function Jn(e){switch(e.type){case F.AUDIO_TRACK:return N.AUDIO;case F.SUBTITLE_TRACK:return N.SUBTITLE;default:return N.MAIN}}function es(e,t){var r=e.url;return void 0!==r&&0!==r.indexOf("data:")||(r=t.url),r}var ts=function(){function e(e){this.hls=void 0,this.loaders=Object.create(null),this.variableList=null,this.onManifestLoaded=this.checkAutostartLoad,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(O.MANIFEST_LOADING,this.onManifestLoading,this),e.on(O.LEVEL_LOADING,this.onLevelLoading,this),e.on(O.AUDIO_TRACK_LOADING,this.onAudioTrackLoading,this),e.on(O.SUBTITLE_TRACK_LOADING,this.onSubtitleTrackLoading,this),e.on(O.LEVELS_UPDATED,this.onLevelsUpdated,this)},t.unregisterListeners=function(){var e=this.hls;e.off(O.MANIFEST_LOADING,this.onManifestLoading,this),e.off(O.LEVEL_LOADING,this.onLevelLoading,this),e.off(O.AUDIO_TRACK_LOADING,this.onAudioTrackLoading,this),e.off(O.SUBTITLE_TRACK_LOADING,this.onSubtitleTrackLoading,this),e.off(O.LEVELS_UPDATED,this.onLevelsUpdated,this)},t.createInternalLoader=function(e){var t=this.hls.config,r=t.pLoader,i=t.loader,a=new(r||i)(t);return this.loaders[e.type]=a,a},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.variableList=null,this.unregisterListeners(),this.destroyInternalLoaders()},t.onManifestLoading=function(e,t){var r=t.url;this.variableList=null,this.load({id:null,level:0,responseType:"text",type:F.MANIFEST,url:r,deliveryDirectives:null,levelOrTrack:null})},t.onLevelLoading=function(e,t){var r=t.id,i=t.level,a=t.pathwayId,n=t.url,s=t.deliveryDirectives,o=t.levelInfo;this.load({id:r,level:i,pathwayId:a,responseType:"text",type:F.LEVEL,url:n,deliveryDirectives:s,levelOrTrack:o})},t.onAudioTrackLoading=function(e,t){var r=t.id,i=t.groupId,a=t.url,n=t.deliveryDirectives,s=t.track;this.load({id:r,groupId:i,level:null,responseType:"text",type:F.AUDIO_TRACK,url:a,deliveryDirectives:n,levelOrTrack:s})},t.onSubtitleTrackLoading=function(e,t){var r=t.id,i=t.groupId,a=t.url,n=t.deliveryDirectives,s=t.track;this.load({id:r,groupId:i,level:null,responseType:"text",type:F.SUBTITLE_TRACK,url:a,deliveryDirectives:n,levelOrTrack:s})},t.onLevelsUpdated=function(e,t){var r=this.loaders[F.LEVEL];if(r){var i=r.context;i&&!t.levels.some((function(e){return e===i.levelOrTrack}))&&(r.abort(),delete this.loaders[F.LEVEL])}},t.load=function(e){var t,r,i,a=this,n=this.hls.config,s=this.getInternalLoader(e);if(s){var l=this.hls.logger,u=s.context;if((null==u?void 0:u.levelOrTrack)===e.levelOrTrack&&(u.url===e.url||u.deliveryDirectives&&!e.deliveryDirectives))return void(u.url===e.url?l.log("[playlist-loader]: ignore "+e.url+" ongoing request"):l.log("[playlist-loader]: ignore "+e.url+" in favor of "+u.url));l.log("[playlist-loader]: aborting previous loader for type: "+e.type),s.abort()}if(r=e.type===F.MANIFEST?n.manifestLoadPolicy.default:o({},n.playlistLoadPolicy.default,{timeoutRetry:null,errorRetry:null}),s=this.createInternalLoader(e),_(null==(t=e.deliveryDirectives)?void 0:t.part)&&(e.type===F.LEVEL&&null!==e.level?i=this.hls.levels[e.level].details:e.type===F.AUDIO_TRACK&&null!==e.id?i=this.hls.audioTracks[e.id].details:e.type===F.SUBTITLE_TRACK&&null!==e.id&&(i=this.hls.subtitleTracks[e.id].details),i)){var d=i.partTarget,h=i.targetduration;if(d&&h){var f=1e3*Math.max(3*d,.8*h);r=o({},r,{maxTimeToFirstByteMs:Math.min(f,r.maxTimeToFirstByteMs),maxLoadTimeMs:Math.min(f,r.maxTimeToFirstByteMs)})}}var c=r.errorRetry||r.timeoutRetry||{},g={loadPolicy:r,timeout:r.maxLoadTimeMs,maxRetry:c.maxNumRetry||0,retryDelay:c.retryDelayMs||0,maxRetryDelay:c.maxRetryDelayMs||0},v={onSuccess:function(e,t,r,i){var n=a.getInternalLoader(r);a.resetInternalLoader(r.type);var s=e.data;t.parsing.start=performance.now(),Kr.isMediaPlaylist(s)||r.type!==F.MANIFEST?a.handleTrackOrLevelPlaylist(e,t,r,i||null,n):a.handleMasterPlaylist(e,t,r,i)},onError:function(e,t,r,i){a.handleNetworkError(t,r,!1,e,i)},onTimeout:function(e,t,r){a.handleNetworkError(t,r,!0,void 0,e)}};s.load(e,g,v)},t.checkAutostartLoad=function(){if(this.hls){var e=this.hls,t=e.config,r=t.autoStartLoad,i=t.startPosition,a=e.forceStartLoad;(r||a)&&(this.hls.logger.log((r?"auto":"force")+" startLoad with configured startPosition "+i),this.hls.startLoad(i))}},t.handleMasterPlaylist=function(e,t,r,i){var a=this,n=this.hls,s=e.data,o=es(e,r),l=Kr.parseMasterPlaylist(s,o);if(l.playlistParsingError)return t.parsing.end=performance.now(),void this.handleManifestParsingError(e,r,l.playlistParsingError,i,t);var u=l.contentSteering,d=l.levels,h=l.iframeVariants,f=l.sessionData,c=l.sessionKeys,g=l.startTimeOffset,v=l.variableList;this.variableList=v,d.forEach((function(e){var t=e.unknownCodecs;if(t)for(var r=a.hls.config.preferManagedMediaSource,i=e.audioCodec,n=e.videoCodec,s=t.length;s--;){var o=t[s];mt(o,"audio",r)?(e.audioCodec=i=i?i+","+o:o,gt.audio[i.substring(0,4)]=2,t.splice(s,1)):mt(o,"video",r)&&(e.videoCodec=n=n?n+","+o:o,gt.video[n.substring(0,4)]=2,t.splice(s,1))}}));var m=Kr.parseMasterPlaylistMedia(s,o,l),p=m.AUDIO,y=void 0===p?[]:p,E=m.SUBTITLES,T=m["CLOSED-CAPTIONS"];y.length&&(y.some((function(e){return!e.url}))||!d[0].audioCodec||d[0].attrs.AUDIO||(this.hls.logger.log("[playlist-loader]: audio codec signaled in quality level, but no embedded audio track signaled, create one"),y.unshift({type:"main",name:"main",groupId:"main",default:!1,autoselect:!1,forced:!1,id:-1,attrs:new Dr({}),bitrate:0,url:""}))),n.trigger(O.MANIFEST_LOADED,{levels:d,audioTracks:y,subtitles:E,iframeVariants:h,captions:T,contentSteering:u,url:o,stats:t,networkDetails:i,sessionData:f,sessionKeys:c,startTimeOffset:g,variableList:v})},t.handleTrackOrLevelPlaylist=function(e,t,r,i,a){var n=this.hls,s=r.id,o=r.level,l=r.type,u=es(e,r),d=_(o)?o:_(s)?s:0,h=Jn(r),f=Kr.parseLevelPlaylist(e.data,u,d,h,0,this.variableList);if(l===F.MANIFEST){var c={attrs:new Dr({}),bitrate:0,details:f,name:"",url:u};f.requestScheduled=t.loading.start+oi(f,0),n.trigger(O.MANIFEST_LOADED,{levels:[c],audioTracks:[],iframeVariants:[],url:u,stats:t,networkDetails:i,sessionData:null,sessionKeys:null,contentSteering:null,startTimeOffset:null,variableList:null})}t.parsing.end=performance.now(),r.levelDetails=f,this.handlePlaylistLoaded(f,e,t,r,i,a)},t.handleManifestParsingError=function(e,t,r,i,a){this.hls.trigger(O.ERROR,{type:x.NETWORK_ERROR,details:w.MANIFEST_PARSING_ERROR,fatal:t.type===F.MANIFEST,url:e.url,err:r,error:r,reason:r.message,response:e,context:t,networkDetails:i,stats:a})},t.handleNetworkError=function(e,t,r,i,a){void 0===r&&(r=!1);var n="A network "+(r?"timeout":"error"+(i?" (status "+i.code+")":""))+" occurred while loading "+e.type;e.type===F.LEVEL?n+=": "+e.level+" id: "+e.id:e.type!==F.AUDIO_TRACK&&e.type!==F.SUBTITLE_TRACK||(n+=" id: "+e.id+' group-id: "'+e.groupId+'"');var s=new Error(n);this.hls.logger.warn("[playlist-loader]: "+n);var o=w.UNKNOWN,l=!1,u=this.getInternalLoader(e);switch(e.type){case F.MANIFEST:o=r?w.MANIFEST_LOAD_TIMEOUT:w.MANIFEST_LOAD_ERROR,l=!0;break;case F.LEVEL:o=r?w.LEVEL_LOAD_TIMEOUT:w.LEVEL_LOAD_ERROR,l=!1;break;case F.AUDIO_TRACK:o=r?w.AUDIO_TRACK_LOAD_TIMEOUT:w.AUDIO_TRACK_LOAD_ERROR,l=!1;break;case F.SUBTITLE_TRACK:o=r?w.SUBTITLE_TRACK_LOAD_TIMEOUT:w.SUBTITLE_LOAD_ERROR,l=!1}u&&this.resetInternalLoader(e.type);var d={type:x.NETWORK_ERROR,details:o,fatal:l,url:e.url,loader:u,context:e,error:s,networkDetails:t,stats:a};if(i){var h=e.url;t&&"url"in t&&(h=t.url),d.response=f({url:h,data:void 0},i)}this.hls.trigger(O.ERROR,d)},t.handlePlaylistLoaded=function(e,t,r,i,a,n){var s=this.hls,o=i.type,l=i.level,u=i.levelOrTrack,d=i.id,h=i.groupId,f=i.deliveryDirectives,c=es(t,i),g=Jn(i),v="number"==typeof i.level&&g===N.MAIN?l:void 0,m=e.playlistParsingError;if(m){if(this.hls.logger.warn(m+" "+e.url),!s.config.ignorePlaylistParsingErrors)return void s.trigger(O.ERROR,{type:x.NETWORK_ERROR,details:w.LEVEL_PARSING_ERROR,fatal:!1,url:c,error:m,reason:m.message,response:t,context:i,level:v,parent:g,networkDetails:a,stats:r});e.playlistParsingError=null}if(e.fragments.length)switch(e.live&&n&&(n.getCacheAge&&(e.ageHeader=n.getCacheAge()||0),n.getCacheAge&&!isNaN(e.ageHeader)||(e.ageHeader=0)),o){case F.MANIFEST:case F.LEVEL:if(v)if(u){if(u!==s.levels[v]){var p=s.levels.indexOf(u);p>-1&&(v=p)}}else v=0;s.trigger(O.LEVEL_LOADED,{details:e,levelInfo:u||s.levels[0],level:v||0,id:d||0,stats:r,networkDetails:a,deliveryDirectives:f,withoutMultiVariant:o===F.MANIFEST,context:i});break;case F.AUDIO_TRACK:s.trigger(O.AUDIO_TRACK_LOADED,{details:e,track:u,id:d||0,groupId:h||"",stats:r,networkDetails:a,deliveryDirectives:f,context:i});break;case F.SUBTITLE_TRACK:s.trigger(O.SUBTITLE_TRACK_LOADED,{details:e,track:u,id:d||0,groupId:h||"",stats:r,networkDetails:a,deliveryDirectives:f,context:i})}else{var y=e.playlistParsingError=new Error("No Segments found in Playlist");s.trigger(O.ERROR,{type:x.NETWORK_ERROR,details:w.LEVEL_EMPTY_ERROR,fatal:!1,url:c,error:y,reason:y.message,response:t,context:i,level:v,parent:g,networkDetails:a,stats:r})}},e}(),rs=function(){function e(t){void 0===t&&(t={}),this.config=void 0,this.userConfig=void 0,this.logger=void 0,this._url=null,this.streamController=void 0,this.coreComponents=void 0,this.networkControllers=void 0,this._emitter=new k,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.audioStreamController=void 0,this.subtititleStreamController=void 0,this.audioTrackController=void 0,this.subtitleTrackController=void 0,this.interstitialsController=void 0,this.iframeController=void 0,this.gapController=void 0,this.emeController=void 0,this.cmcdController=void 0,this._media=null,this._sessionId=void 0,this.triggeringException=void 0,this.started=!1;var r=this.logger=ue(t.debug||!1,"Hls instance",t.loggerId||t.assetPlayerId),i=this.config=Hn(e.DefaultConfig,t,r);this.userConfig=t,i.progressive&&Yn(i,r);var a=i.streamController,n=i.abrController,s=i.bufferController,o=i.capLevelController,l=i.errorController,u=i.fpsController,d=i.id3TrackController,h=i.iframeController,f=i.gapController,c=new l(this),g=this.abrController=new n(this),v=new Li(this),m=i.interstitialsController,p=m?this.interstitialsController=new m(this,e):null,y=s?this.bufferController=new s(this,v):null,E=o?this.capLevelController=new o(this):null,T=u?new u(this):null,S=i.cmcdController,L=S?this.cmcdController=new S(this):null,R=new ts(this),A=i.contentSteeringController,b=A?new A(this):null,D=this.levelController=new jn(this,b),I=d?new d(this):void 0,_=new $n(this.config,this.logger),C=this.streamController=new a(this,v,_),P=this.gapController=f?new f(this,v):void 0;E&&E.setStreamController(C);var x=[R,D,C];p&&x.splice(1,0,p),b&&x.splice(1,0,b),this.networkControllers=x;var w=[g];y&&w.push(y),P&&w.push(P),E&&w.push(E),T&&(T.setStreamController(C),w.push(T)),I&&w.push(I),w.push(v),this.audioTrackController=this.createController(i.audioTrackController,x);var M=i.audioStreamController;M&&x.push(this.audioStreamController=new M(this,v,_)),this.subtitleTrackController=this.createController(i.subtitleTrackController,x);var F=i.subtitleStreamController;F&&x.push(this.subtititleStreamController=new F(this,v,_)),this.createController(i.timelineController,w),_.emeController=this.emeController=this.createController(i.emeController,w),L&&w.push(L),this.latencyController=this.createController(i.latencyController,w),this.coreComponents=w,this.iframeController=h?new h(this,e):void 0,x.push(c);var N=c.onErrorOut;"function"==typeof N&&this.on(O.ERROR,N,c),this.on(O.MANIFEST_LOADED,R.onManifestLoaded,R)}e.isMSESupported=function(){return Dn()},e.isSupported=function(){return function(){if(!Dn())return!1;var e=ve();return"function"==typeof(null==e?void 0:e.isTypeSupported)&&(["avc1.42E01E,mp4a.40.2","av01.0.01M.08","vp09.00.50.08"].some((function(t){return e.isTypeSupported(yt(t,"video"))}))||["mp4a.40.2","fLaC"].some((function(t){return e.isTypeSupported(yt(t,"audio"))})))}()},e.getMediaSource=function(){return ve()};var t=e.prototype;return t.createController=function(e,t){if(e){var r=new e(this);return t&&t.push(r),r}return null},t.on=function(e,t,r){void 0===r&&(r=this),this._emitter.on(e,t,r)},t.once=function(e,t,r){void 0===r&&(r=this),this._emitter.once(e,t,r)},t.removeAllListeners=function(e){this._emitter.removeAllListeners(e)},t.off=function(e,t,r,i){void 0===r&&(r=this),this._emitter.off(e,t,r,i)},t.listeners=function(e){return this._emitter.listeners(e)},t.emit=function(e,t,r){return this._emitter.emit(e,t,r)},t.trigger=function(e,t){if(this.config.debug)return this.emit(e,e,t);try{return this.emit(e,e,t)}catch(t){if(this.logger.error("An internal error happened while handling event "+e+'. Error message: "'+t.message+'". Here is a stacktrace:',t),!this.triggeringException){this.triggeringException=!0;var r=e===O.ERROR;this.trigger(O.ERROR,{type:x.OTHER_ERROR,details:w.INTERNAL_EXCEPTION,fatal:r,event:e,error:t}),this.triggeringException=!1}}return!1},t.listenerCount=function(e){return this._emitter.listenerCount(e)},t.destroy=function(){this.logger.log("destroy"),this.trigger(O.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,this.iframeController=void 0;var e=this.config;e.xhrSetup=e.fetchSetup=void 0,this.userConfig=null},t.attachMedia=function(e){if(!e||"media"in e&&!e.media){var t=new Error("attachMedia failed: invalid argument ("+e+")");this.trigger(O.ERROR,{type:x.OTHER_ERROR,details:w.ATTACH_MEDIA_ERROR,fatal:!0,error:t})}else{this.logger.log("attachMedia"),this._media&&(this.logger.warn("media must be detached before attaching"),this.detachMedia());var r="media"in e,i=r?e.media:e,a=r?e:{media:i};this._media=i,this.trigger(O.MEDIA_ATTACHING,a)}},t.detachMedia=function(){this.logger.log("detachMedia");var e={};this.trigger(O.MEDIA_DETACHING,e),this._media=null,this.trigger(O.MEDIA_DETACHED,e)},t.transferMedia=function(){var e;this._media=null;var t=(null==(e=this.bufferController)?void 0:e.transferMedia())||null,r={transferMedia:t};return this.trigger(O.MEDIA_DETACHING,r),this.trigger(O.MEDIA_DETACHED,r),t},t.loadSource=function(e){var t;this.stopLoad();var r=this.media,i=this._url,a=this._url=I.buildAbsoluteURL(self.location.href,e,{alwaysNormalize:!0});this._autoLevelCapping=-1,this._maxHdcpLevel=null,this.logger.log("loadSource:"+a),r&&i&&(i!==a||null!=(t=this.bufferController)&&t.hasSourceTypes())&&(this.detachMedia(),this.attachMedia(r)),this.trigger(O.MANIFEST_LOADING,{url:e})},t.startLoad=function(e,t){void 0===e&&(e=-1),this.logger.log("startLoad("+e+(t?", <skip seek to start>":"")+")"),this.started=!0,this.resumeBuffering();for(var r=0;r<this.networkControllers.length&&(this.networkControllers[r].startLoad(e,t),this.started&&this.networkControllers);r++);},t.stopLoad=function(){this.logger.log("stopLoad"),this.started=!1;for(var e=0;e<this.networkControllers.length&&(this.networkControllers[e].stopLoad(),!this.started&&this.networkControllers);e++);},t.resumeBuffering=function(){this.bufferingEnabled||(this.logger.log("resume buffering"),this.networkControllers.forEach((function(e){e.resumeBuffering&&e.resumeBuffering()})))},t.pauseBuffering=function(){this.bufferingEnabled&&(this.logger.log("pause buffering"),this.networkControllers.forEach((function(e){e.pauseBuffering&&e.pauseBuffering()})))},t.swapAudioCodec=function(){this.logger.log("swapAudioCodec"),this.streamController.swapAudioCodec()},t.recoverMediaError=function(){this.logger.log("recoverMediaError");var e=this._media,t=this.started,r=null==e?void 0:e.currentTime;this.detachMedia(),e&&(this.attachMedia(e),t&&(r?this.startLoad(r):this.config.autoStartLoad||this.startLoad()))},t.removeLevel=function(e){this.levelController.removeLevel(e)},t.setAudioOption=function(e){var t;return(null==(t=this.audioTrackController)?void 0:t.setAudioOption(e))||null},t.setSubtitleOption=function(e){var t;return(null==(t=this.subtitleTrackController)?void 0:t.setSubtitleOption(e))||null},t.createIFramePlayer=function(e){return null},t.createImageIFramePlayer=function(e){return null},t.getMediaDecodingInfo=function(e,t){void 0===t&&(t=this.allAudioTracks);var r=Gt(t);return ce.getMediaDecodingInfoPromise(e,r,navigator.mediaCapabilities)},a(e,[{key:"url",get:function(){return this._url}},{key:"hasEnoughToStart",get:function(){return this.streamController.hasEnoughToStart}},{key:"startPosition",get:function(){return this.streamController.startPositionValue}},{key:"loadingEnabled",get:function(){return this.started&&this.streamController.state!==Hi.STOPPED}},{key:"bufferingEnabled",get:function(){return this.streamController.bufferingEnabled}},{key:"inFlightFragments",get:function(){var e,t=((e={})[N.MAIN]=this.streamController.inFlightFrag,e);return this.audioStreamController&&(t[N.AUDIO]=this.audioStreamController.inFlightFrag),this.subtititleStreamController&&(t[N.SUBTITLE]=this.subtititleStreamController.inFlightFrag),t}},{key:"sessionId",get:function(){var e=this._sessionId;return e||(e=this._sessionId=function(){try{return crypto.randomUUID()}catch(i){try{var e=URL.createObjectURL(new Blob),t=e.toString();return URL.revokeObjectURL(e),t.slice(t.lastIndexOf("/")+1)}catch(e){var r=(new Date).getTime();return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(function(e){var t=(r+16*Math.random())%16|0;return r=Math.floor(r/16),("x"==e?t:3&t|8).toString(16)}))}}}()),e}},{key:"levels",get:function(){var e=this.levelController.levels;return e||[]}},{key:"latestLevelDetails",get:function(){return this.streamController.getLevelDetails()||null}},{key:"loadLevelObj",get:function(){return this.levelController.loadLevelObj}},{key:"currentLevel",get:function(){return this.streamController.currentLevel},set:function(e){this.logger.log("set currentLevel:"+e),this.levelController.manualLevel=e,this.streamController.immediateLevelSwitch()}},{key:"nextLevel",get:function(){return this.streamController.nextLevel},set:function(e){this.logger.log("set nextLevel:"+e),this.levelController.manualLevel=e,this.streamController.nextLevelSwitch()}},{key:"loadLevel",get:function(){return this.levelController.level},set:function(e){this.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){this.logger.log("set firstLevel:"+e),this.levelController.firstLevel=e}},{key:"startLevel",get:function(){var e=this.levelController.startLevel;return-1===e&&this.abrController.forcedAutoLevel>-1?this.abrController.forcedAutoLevel:e},set:function(e){this.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=this.capLevelController,r=!!e;t&&r!==this.config.capLevelToPlayerSize&&(r?t.startCapping():(t.stopCapping(),this.autoLevelCapping=-1,this.streamController.nextLevelSwitch()),this.config.capLevelToPlayerSize=r)}},{key:"autoLevelCapping",get:function(){return this._autoLevelCapping},set:function(e){this._autoLevelCapping!==e&&(this.logger.log("set autoLevelCapping:"+e),this._autoLevelCapping=e,this.levelController.checkMaxAutoUpdated())}},{key:"bandwidthEstimate",get:function(){var e=this.abrController.bwEstimator;return e?e.getEstimate():NaN},set:function(e){this.abrController.resetEstimator(e)}},{key:"abrEwmaDefaultEstimate",get:function(){var e=this.abrController.bwEstimator;return e?e.defaultEstimate:NaN}},{key:"ttfbEstimate",get:function(){var e=this.abrController.bwEstimator;return e?e.getEstimateTTFB():NaN}},{key:"maxHdcpLevel",get:function(){return this._maxHdcpLevel},set:function(e){(function(e){return It.indexOf(e)>-1})(e)&&this._maxHdcpLevel!==e&&(this._maxHdcpLevel=e,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 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,t=this.levels,r=this.autoLevelCapping,i=this.maxHdcpLevel;if(e=-1===r&&null!=t&&t.length?t.length-1:r,i)for(var a=e;a--;){var n=t[a].attrs["HDCP-LEVEL"];if(n&&n<=i)return a}return e}},{key:"firstAutoLevel",get:function(){return this.abrController.firstAutoLevel}},{key:"nextAutoLevel",get:function(){return this.abrController.nextAutoLevel},set:function(e){this.abrController.nextAutoLevel=e}},{key:"playingDate",get:function(){return this.streamController.currentProgramDateTime}},{key:"mainForwardBufferInfo",get:function(){return this.streamController.getMainFwdBufferInfo()}},{key:"audioForwardBufferInfo",get:function(){var e;return(null==(e=this.audioStreamController)?void 0:e.getFwdBufferInfo())||null}},{key:"maxBufferLength",get:function(){return this.streamController.maxBufferLength}},{key:"allAudioTracks",get:function(){var e=this.audioTrackController;return e?e.allAudioTracks:[]}},{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:"nextAudioTrack",get:function(){var e,t;return null!=(e=null==(t=this.audioStreamController)?void 0:t.nextAudioTrack)?e:-1},set:function(e){var t=this.audioTrackController;t&&(t.nextAudioTrack=e)}},{key:"allSubtitleTracks",get:function(){var e=this.subtitleTrackController;return e?e.allSubtitleTracks:[]}},{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(){var e,t;return null!=(e=null==(t=this.latencyController)?void 0:t.liveSyncPosition)?e:null}},{key:"latency",get:function(){var e;return(null==(e=this.latencyController)?void 0:e.latency)||0}},{key:"maxLatency",get:function(){var e;return(null==(e=this.latencyController)?void 0:e.maxLatency)||0}},{key:"targetLatency",get:function(){var e;return(null==(e=this.latencyController)?void 0:e.targetLatency)||null},set:function(e){this.latencyController&&(this.latencyController.targetLatency=e)}},{key:"drift",get:function(){var e;return(null==(e=this.latencyController)?void 0:e.drift)||null}},{key:"forceStartLoad",get:function(){return this.streamController.forceStartLoad}},{key:"pathways",get:function(){return this.levelController.pathways}},{key:"pathwayPriority",get:function(){return this.levelController.pathwayPriority},set:function(e){this.levelController.pathwayPriority=e}},{key:"bufferedToEnd",get:function(){var e;return!(null==(e=this.bufferController)||!e.bufferedToEnd)}},{key:"interstitialsManager",get:function(){return null}},{key:"iframeVariants",get:function(){var e=this.levelController.iframeVariants;return e||[]}}],[{key:"version",get:function(){return Ln}},{key:"Events",get:function(){return O}},{key:"MetadataSchema",get:function(){return zi}},{key:"ErrorTypes",get:function(){return x}},{key:"ErrorDetails",get:function(){return w}},{key:"DefaultConfig",get:function(){return e.defaultConfig?e.defaultConfig:Vn},set:function(t){e.defaultConfig=t}}])}();return rs.defaultConfig=void 0,rs},"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);++//# sourceMappingURL=hls.light.min.js.mapnnew file mode 100644@@@ -0,0 +1,167 @@++'use strict';++++/**++ * OpenVibe.Surf — Express app factory; server/index.js listens, tests build their own instance with mock upstreams.++ *++ * /, /ride, /ride/:map, /watch, /web, /developers the pages (http/pages.js)++ * /web/go the no-JavaScript task form++ * /api/v1/watch/feed, /api/v1/ride/maps… Watch and Ride (http/api.js)++ * /api/v1/web/tasks Surf with Vibe (http/api.js)++ * /auth/* Network SSO with PKCE (auth/sso.js)++ * /api/health, /api/ready, /release.json liveness, truthful readiness, release metadata++ * /robots.txt, /sitemap.xml, /llms.txt crawl artifacts (http/discovery.js)++ *++ * Stateless: no database, no Valkey, no Events. The dependencies are OpenVibe.Network (sign-in), OpenVibe.Live++ * (Watch), OpenVibe.Games (Ride) and OpenVibe.Actor (Web); the pages degrade honestly without any of them.++ */++const path = require('path');++const express = require('express');++const helmet = require('helmet');++const cookieParser = require('cookie-parser');++const rateLimit = require('express-rate-limit');++const contracts = require('openvibe-contracts');++const cache = require('openvibe-shared/cache-policy');++const releaseLib = require('openvibe-shared/release');++const metricsLib = require('openvibe-shared/metrics');++const serve = require('openvibe-shared/serve');++++const configLib = require('./config');++const { createKeyStore } = require('./auth/keys');++const { createSso } = require('./auth/sso');++const { createWatchFeed } = require('./watch/feed');++const { createRide } = require('./ride/games');++const { createWeb } = require('./web/tasks');++const { createApi } = require('./http/api');++const { createPageRoutes } = require('./http/pages');++const { createDiscoveryRoutes } = require('./http/discovery');++const { createServiceReadiness } = require('./observability');++const { PUBLIC_DIR, assetVersion } = require('./assets');++const views = require('./views');++++const VERSION = require('../package.json').version;++++/**++ * opts: config, fetchImpl, log, now, release++ * A test passes a fetchImpl that talks to in-process Network/Live/Games/Actor stand-ins.++ */++function createApp(opts = {}) {++ const config = opts.config || configLib.load();++ const log = opts.log || console;++ const fetchImpl = opts.fetchImpl || globalThis.fetch;++ const now = opts.now || (() => Date.now());++++ const keys = createKeyStore({ config, fetchImpl, log });++ const sso = createSso({ config, keys, fetchImpl, log, now });++ const feed = createWatchFeed({ config, fetchImpl, log, now });++ const ride = createRide({ config, fetchImpl, log, now });++ const web = createWeb({ config, fetchImpl, log });++ const ctx = { config, keys, sso, feed, ride, web, log };++++ const release = opts.release || releaseLib.createRelease({ service: 'surf', root: path.join(__dirname, '..') });++ views.setRelease(release.release);++ views.configure({ baseUrl: config.baseUrl });++++ const app = express();++ app.disable('x-powered-by');++ app.set('trust proxy', config.trustProxy);++ app.locals.ctx = ctx;++++ const metrics = metricsLib.instrument(app, { service: 'surf', release: release.release });++ app.locals.metrics = metrics.registry;++ release.mount(app, { registry: metrics.registry });++++ // ── Machine endpoints ───────────────────────────────────++ app.get('/api/health', (_req, res) => res.json({ status: 'ok', service: 'openvibe-surf', version: VERSION }));++ const readiness = createServiceReadiness({ config, feed, ride, keys, release: release.release, fetchImpl });++ app.get('/api/ready', readiness.handler);++++ app.use(contracts.http.middleware());++ app.use(helmet({++ contentSecurityPolicy: {++ directives: {++ defaultSrc: ["'self'"],++ // The OpenVibe Frame (theme-loader, navbar, footer) comes from the Network; the inline init is ours.++ scriptSrc: ["'self'", "'unsafe-inline'", 'https://openvibe.network', 'https://static.cloudflareinsights.com'],++ styleSrc: ["'self'", "'unsafe-inline'", 'https://openvibe.network', 'https://fonts.googleapis.com', 'https://cdnjs.cloudflare.com'],++ fontSrc: ["'self'", 'data:', 'https://fonts.gstatic.com', 'https://cdnjs.cloudflare.com'],++ // Card thumbnails and avatars are https images on the media edge; the catalogue may carry any https image.++ imgSrc: ["'self'", 'data:', 'https:'],++ // The footer's release line hears OpenVibe.Events; Cloudflare's beacon reports page timings.++ // hls.js fetches Media's playlists and segments, which may redirect to R2 or B2 (as openvibe.video's CSP).++ connectSrc: ["'self'", 'https://openvibe.network', 'https://cloudflareinsights.com', 'https://openvibe.events', 'https://openvibe.media', 'https://edge.openvibe.media', 'https://*.r2.cloudflarestorage.com', 'https://*.backblazeb2.com'],++ // Ride embeds a Games map; Watch may embed Live's player when Live has one.++ frameSrc: ["'self'", 'https://openvibe.games', 'https://openvibe.live'],++ // HLS clips play from the media edge (and a blob: URL a player may build).++ mediaSrc: ["'self'", 'https://openvibe.media', 'https://edge.openvibe.media', 'https://*.r2.cloudflarestorage.com', 'https://*.backblazeb2.com', 'blob:'],++ frameAncestors: ["'none'"],++ objectSrc: ["'none'"],++ baseUri: ["'self'"],++ formAction: ["'self'", 'https://openvibe.network'],++ },++ },++ frameguard: { action: 'deny' },++ crossOriginEmbedderPolicy: false,++ crossOriginResourcePolicy: { policy: 'same-site' },++ referrerPolicy: { policy: 'strict-origin-when-cross-origin' },++ }));++ app.use(cookieParser());++++ // ── Per-IP budget for the API and sign-in (120 a minute) ─++ const limiter = rateLimit({++ windowMs: 60_000,++ limit: config.rateLimitPerMinute || 120,++ standardHeaders: true,++ legacyHeaders: false,++ handler: (req, res) => contracts.http.sendProblem(res, 429, 'rate_limited', {++ detail: 'too many requests from this address; retry shortly',++ ctx: req.ov,++ }),++ });++ app.use(['/api', '/auth'], limiter);++++ // ── Who is signed in, then sign-in itself ───────────────++ app.use(sso.middleware());++ app.use('/auth', sso.routes());++++ // ── Static assets (content-hashed ?v= → immutable) ──────++ app.use('/shared', serve.handler());++ const setAssetHeaders = (res, filePath) => {++ const rel = path.relative(PUBLIC_DIR, filePath).split(path.sep).join('/');++ const v = res.req && res.req.query && res.req.query.v;++ res.setHeader('Cache-Control', cache.assetHeaders(rel, { hashed: Boolean(v) && v === assetVersion(rel) }));++ };++ app.use('/css', express.static(path.join(PUBLIC_DIR, 'css'), { index: false, redirect: false, setHeaders: setAssetHeaders }));++ app.use('/js', express.static(path.join(PUBLIC_DIR, 'js'), { index: false, redirect: false, setHeaders: setAssetHeaders }));++ // hls.js 1.7.3 light (the same file openvibe.video serves), loaded by js/surf.js only where a browser cannot play HLS.++ app.use('/vendor', express.static(path.join(PUBLIC_DIR, 'vendor'), { index: false, redirect: false, setHeaders: setAssetHeaders }));++++ // ── API: Watch, Ride and Web ────────────────────────────++ app.use('/api', express.json({ limit: '32kb' }));++ app.use('/api', createApi(ctx));++ app.use('/api', (req, res) => contracts.http.sendProblem(res, 404, 'route.not_found', {++ detail: 'No such API route.',++ ctx: req.ov,++ }));++++ // ── Crawl artifacts ─────────────────────────────────────++ app.use(createDiscoveryRoutes(ctx));++++ // ── Pages (and the no-JavaScript task form) ─────────────++ app.use(createPageRoutes(ctx));++++ // eslint-disable-next-line no-unused-vars++ app.use((err, req, res, _next) => {++ // Never log request bodies (a query is personal, and a token can be in a header).++ log.error('[OpenVibe.Surf]', err && err.message ? err.message.slice(0, 300) : err);++ if (res.headersSent) return;++ res.set('Cache-Control', 'no-store');++ if (err && err.type === 'entity.parse.failed') return contracts.http.sendProblem(res, 400, 'request.malformed_json', { ctx: req.ov });++ if (err && err.type === 'entity.too.large') return req.path.startsWith('/api/') ? contracts.http.sendProblem(res, 413, 'request.too_large', { ctx: req.ov }) : res.status(413).type('text/plain').send('That request was too large.');++ if (req.path.startsWith('/api/')) return contracts.http.sendProblem(res, 500, 'internal.error', { detail: 'Internal error', ctx: req.ov });++ return res.status(500).type('text/plain').send('Something went wrong on our side. Try again in a moment.');++ });++++ return { app, ctx };++}++++module.exports = { createApp };nnew file mode 100644@@@ -0,0 +1,30 @@++'use strict';++++/**++ * Content-addressed URLs for this site's own assets: /css/surf.css?v=<first 10 hex of SHA-256>. The static++ * middleware treats a matching ?v= as immutable (openvibe-shared/cache-policy); any other URL gets the short++ * public window, because those bytes may change under the same URL.++ */++const crypto = require('crypto');++const fs = require('fs');++const path = require('path');++++const PUBLIC_DIR = path.join(__dirname, '..', 'public');++const hashes = new Map();++++function assetVersion(rel) {++ const key = String(rel || '').replace(/^\/+/, '');++ if (hashes.has(key)) return hashes.get(key);++ let version = 'dev';++ try {++ version = crypto.createHash('sha256').update(fs.readFileSync(path.join(PUBLIC_DIR, key))).digest('hex').slice(0, 10);++ } catch {++ /* a missing asset stays at a stable placeholder */++ }++ hashes.set(key, version);++ return version;++}++++const asset = (rel) => `/${String(rel || '').replace(/^\/+/, '')}?v=${assetVersion(rel)}`;++++module.exports = { PUBLIC_DIR, assetVersion, asset };nnew file mode 100644@@@ -0,0 +1,110 @@++'use strict';++++/**++ * The Network's RS256 signing key, fetched once from GET /api/.well-known/jwks on the configured internal URL and++ * kept fresh by openvibe-sdk/auth's JWKS client. Tokens are verified offline against it: the person's session token++ * (sign-in).++ *++ * The SDK keeps one client per URL: the last good keys through outages, exponential backoff, a rotation honoured at++ * once, unknown-kid floods throttled, the refresh timer unref'd. This module is a thin shim so the rest of++ * OpenVibe.Surf (the readiness check, the SSO middleware) can keep talking to `keys.get()` / `keys.ensure()` /++ * `keys.loaded()`, while verification goes through the SDK's verifyUserToken / verifyAppToken with `jwks: <url>`.++ *++ * Nothing the SDK says about a failed key fetch leaves this module: its message names the internal JWKS URL and the++ * fetch error, so token.no_key answers 'signing key not loaded yet' (and an error without a code 'token does not++ * verify'). Reasons about the token itself (audience, expiry, signature) are kept. Every rejection is logged.++ */++const sdk = require('openvibe-sdk/auth');++++/** Public reasons. The SDK's own text (the JWKS URL, the fetch error) never goes over the wire. */++const KEY_UNAVAILABLE = 'signing key not loaded yet';++const DOES_NOT_VERIFY = 'token does not verify';++++/**++ * What a caller may be told. Only token.no_key carries the SDK's fetch text (the internal JWKS URL and the connect++ * error); every other code's message is about the token itself ('not for openvibe.surf', 'expired'), which a++ * developer needs, so it is kept.++ */++function publicReason(err) {++ const code = err && err.code;++ if (!code || code === 'token.no_key') return code ? KEY_UNAVAILABLE : DOES_NOT_VERIFY;++ return (err.message && String(err.message).slice(0, 200)) || DOES_NOT_VERIFY;++}++++/** Resolve the JWKS URL for the configured Network (internal first, then public). */++function jwksUrl(config) {++ return (config.networkInternalUrl || config.networkUrl) + '/api/.well-known/jwks';++}++++/**++ * `keys` is what app.js hands the rest of the service: a facade over the SDK's jwksClient. A synchronous caller++ * wants a PEM, so we mirror the cached first key as PEM — refreshed every time the SDK gives us keys (ensure(),++ * verifyUser(), verifyApp()).++ */++function createKeyStore({ config, fetchImpl = globalThis.fetch, log = console }) {++ const url = jwksUrl(config);++ const client = sdk.jwksClient(url, { fetch: fetchImpl, log });++ let mirror = null; // { pem, count } — the first cached key as PEM++++ async function refreshMirror() {++ try {++ const ks = await client.keys();++ const first = ks && ks[0];++ mirror = first ? { pem: first.key.export({ type: 'spki', format: 'pem' }), count: ks.length } : null;++ } catch (err) { log.warn(`[OpenVibe.Surf] JWKS mirror refresh failed: ${err && err.message || err}`); mirror = null; }++ }++ /** First fetch (or wait out the backoff window). Refreshes the mirror. */++ async function ensure() { await refreshMirror(); }++ /** Cached public key as PEM — for openvibe-contracts' verifyServiceToken (synchronous). */++ function get() { return mirror ? mirror.pem : null; }++ function loaded() { return Boolean(mirror); }++++ /** The SDK's `now` is a NUMBER of milliseconds (it does Math.floor(now / 1000)); a function disables every expiry check. */++ const asMs = (v) => (typeof v === 'function' ? v() : (typeof v === 'number' ? v : Date.now()));++++ /** Verify a user (session) token via the SDK. Returns { ok, claims } | { ok: false, reason, code, expired }. */++ async function verifyUser(token, { issuer, audience, now = Date.now() } = {}) {++ try {++ const claims = await sdk.verifyUserToken(token, { jwks: url, issuer, audience, now: asMs(now), log });++ await refreshMirror();++ return { ok: true, claims };++ } catch (err) {++ await refreshMirror();++ const code = err && err.code;++ log.warn(`[OpenVibe.Surf] user token rejected (${code || 'error'}):`, (err && err.message) || err);++ return { ok: false, reason: publicReason(err), code, expired: code === 'token.expired' };++ }++ }++ /** Verify an app token. Same shape and same fixed reasons. */++ async function verifyApp(token, { issuer, audience, acceptSandbox = true, now = Date.now() } = {}) {++ try {++ const claims = await sdk.verifyAppToken(token, { jwks: url, issuer, audience, acceptSandbox, now: asMs(now), log });++ await refreshMirror();++ return { ok: true, claims };++ } catch (err) {++ await refreshMirror();++ const code = err && err.code;++ log.warn(`[OpenVibe.Surf] app token rejected (${code || 'error'}):`, (err && err.message) || err);++ return { ok: false, reason: publicReason(err), code };++ }++ }++++ /**++ * The PEM for the key a token's header names (a rotation publishes two keys: the first one is not always it), for++ * openvibe-contracts' synchronous guard. The SDK refetches for an unknown kid (at most every 30 s). Null when no++ * key is loaded; a token that names no kid gets the first key.++ */++ async function pemForToken(token) {++ let kid = null;++ try { kid = JSON.parse(Buffer.from(String(token).split('.')[0], 'base64url').toString('utf8')).kid || null; } catch { /* the guard refuses it */ }++ try {++ const ks = await client.keysForKid(kid);++ const k = (kid && ks.find((x) => x.kid === kid)) || ks[0];++ return k ? k.key.export({ type: 'spki', format: 'pem' }) : null;++ } catch { return null; }++ }++++ return { ensure, get, loaded, status: client.status, verifyUser, verifyApp, pemForToken, client };++}++++module.exports = { createKeyStore, jwksUrl };nnew file mode 100644@@@ -0,0 +1,282 @@++'use strict';++++/**++ * Sign-in with OpenVibe.Network: OAuth 2 authorization code with PKCE (S256), as OAuth client `surf`.++ *++ * GET /auth/login → Network /oauth/authorize?…&code_challenge=…&code_challenge_method=S256++ * (?silent=1 adds prompt=none; ?next= a same-site path)++ * GET /auth/callback → state check, server-side code exchange (client secret + code_verifier)++ * GET /auth/logout → clear cookies, revoke the refresh token (best effort)++ * GET /auth/me → { user } for the shared navbar (it cannot read the httpOnly cookie); { user: null } for a guest++ *++ * The person's Network access token lives in an httpOnly cookie (surf_at) no script can read: with it, a script++ * could act as them. The refresh token (surf_rt) is httpOnly too. Neither is ever stored server-side, rendered++ * or logged.++ */++const crypto = require('crypto');++const express = require('express');++const cache = require('openvibe-shared/cache-policy');++++const ACCESS_COOKIE = 'surf_at';++const REFRESH_COOKIE = 'surf_rt';++const HINT_COOKIE = 'ov_sso_hint';++const FLOW_COOKIE = 'surf_oauth';++++function pkcePair() {++ const verifier = crypto.randomBytes(32).toString('base64url'); // 43 characters++ const challenge = crypto.createHash('sha256').update(verifier).digest('base64url');++ return { verifier, challenge };++}++++/** This site's own origins: BASE_URL (the one origin OpenVibe.Surf is served from). */++function ownOrigins(config) {++ const out = new Set();++ for (const u of [config && config.baseUrl]) {++ try { if (u) out.add(new URL(u).origin); } catch { /* not a URL */ }++ }++ return out;++}++++/**++ * Same-site relative paths only (never protocol-relative, never another origin). An absolute URL on one of this++ * site's own origins counts as its path: the shared navbar's silent sign-in sends the whole page address, and a++ * person signed in quietly should land on the page they asked for.++ */++function sanitizeNext(next, config) {++ // Browsers drop tab and newline characters from a URL and read a backslash as "/": "/<TAB>/evil.com" would++ // leave the site. A next with any control character or backslash goes home.++ if (typeof next === 'string' && /[\u0000-\u001f\u007f\\]/.test(next)) return '/';++ if (typeof next === 'string' && /^https?:\/\//i.test(next)) {++ let u = null;++ try { u = new URL(next); } catch { return '/'; }++ if (!ownOrigins(config).has(u.origin)) return '/';++ next = `${u.pathname}${u.search}${u.hash}`;++ }++ if (typeof next !== 'string' || !/^\/(?!\/|\\)/.test(next) || next.length > 500) return '/';++ return next;++}++++function authorizeUrl(config, { state, challenge, silent = false }) {++ const q = new URLSearchParams({++ response_type: 'code',++ client_id: config.oauth.clientId,++ redirect_uri: config.oauth.redirectUri,++ scope: config.oauth.scope,++ state,++ code_challenge: challenge,++ code_challenge_method: 'S256',++ });++ if (silent) q.set('prompt', 'none');++ return `${config.networkPublicUrl || config.networkUrl}/oauth/authorize?${q.toString()}`;++}++++function createSso({ config, keys, fetchImpl = globalThis.fetch, now = () => Date.now(), log = console }) {++ const cookieBase = () => ({ sameSite: 'lax', secure: config.cookies.secure, httpOnly: true, path: '/' });++++ function setSession(res, data) {++ res.cookie(ACCESS_COOKIE, data.access_token, { ...cookieBase(), maxAge: 24 * 60 * 60 * 1000 });++ if (data.refresh_token) res.cookie(REFRESH_COOKIE, data.refresh_token, { ...cookieBase(), maxAge: 30 * 24 * 60 * 60 * 1000 });++ res.cookie(HINT_COOKIE, 'account', { sameSite: 'lax', secure: config.cookies.secure, httpOnly: false, path: '/', maxAge: 365 * 24 * 60 * 60 * 1000 });++ }++ function clearSession(res) {++ res.clearCookie(ACCESS_COOKIE, cookieBase());++ res.clearCookie(REFRESH_COOKIE, cookieBase());++ }++++ /** POST the Network token endpoint (internal URL). Throws { status, error } on refusal. */++ async function tokenGrant(fields) {++ const body = new URLSearchParams({ client_id: config.oauth.clientId, client_secret: config.oauth.clientSecret, ...fields });++ let res;++ try {++ res = await fetchImpl(`${config.networkInternalUrl || config.networkUrl}/oauth/token`, {++ method: 'POST',++ headers: { 'Content-Type': 'application/x-www-form-urlencoded', Accept: 'application/json' },++ body, signal: AbortSignal.timeout(10_000),++ });++ } catch (err) {++ throw Object.assign(new Error('OpenVibe.Network did not answer'), { status: 502, error: err && err.name === 'TimeoutError' ? 'timeout' : 'unreachable' });++ }++ const data = await res.json().catch(() => null);++ if (!res.ok || !data || !data.access_token) {++ throw Object.assign(new Error((data && (data.error_description || data.error)) || `token endpoint ${res.status}`),++ { status: res.status, error: (data && data.error) || 'invalid_grant' });++ }++ return data;++ }++++ /**++ * An OpenVibe.Surf session is a Network SESSION token: audience openvibe.network and a person. The Network++ * signs FedCM assertions (audience = any owned-zone origin that asked), app/service tokens and++ * internal tokens with the same key and issuer; none of them may become a session here.++ */++ async function verifySession(token) {++ const v = await keys.verifyUser(token, { issuer: config.networkIssuer || config.networkPublicUrl || config.networkUrl, audience: config.oauth.sessionAudience, now: now() });++ const c = v.claims;++ if (c && (c.typ === 'fedcm' || c.actor_type !== undefined)) return { ok: false, reason: 'not a session token' };++ return v;++ }++++ function viewerFromClaims(claims, token) {++ const subject = typeof claims.subject_id === 'string' ? claims.subject_id : null;++ const role = typeof claims.role === 'string' ? claims.role : 'user';++ return {++ kind: 'user',++ subject,++ username: typeof claims.username === 'string' ? claims.username.slice(0, 64) : null,++ displayName: typeof claims.display_name === 'string' ? claims.display_name.slice(0, 80) : (typeof claims.username === 'string' ? claims.username.slice(0, 64) : 'you'),++ role,++ // The navbar's avatar and colour (Network's token claims; the avatar is an https image on openvibe.media).++ avatarUrl: typeof claims.avatar_url === 'string' && /^https:\/\/[^\s"'<>]{1,500}$/.test(claims.avatar_url) ? claims.avatar_url : null,++ profileColor: typeof claims.profile_color === 'string' && /^#[0-9a-fA-F]{3,8}$/.test(claims.profile_color) ? claims.profile_color : null,++ // The person's Network access token: used for server-side Network calls, never rendered.++ token,++ };++ }++++ /** One refresh per request, however many calls in it see a 401 (refresh tokens rotate). */++ async function refresh(req, res) {++ if (!req.sessionRefresh) req.sessionRefresh = await doRefresh(req, res);++ return req.sessionRefresh;++ }++++ async function doRefresh(req, res) {++ const rt = req.cookies && req.cookies[REFRESH_COOKIE];++ if (!rt) return null;++ try {++ const data = await tokenGrant({ grant_type: 'refresh_token', refresh_token: rt });++ setSession(res, data);++ const v = await verifySession(data.access_token);++ return v.ok ? viewerFromClaims(v.claims, data.access_token) : null;++ } catch (err) {++ if (err.status && err.status < 500) clearSession(res);++ return null;++ }++ }++++ /** req.viewer for every page: the signed-in person, refreshed when their token expired. */++ function middleware() {++ return async (req, res, next) => {++ req.viewer = { kind: 'anonymous' };++ const token = req.cookies && req.cookies[ACCESS_COOKIE];++ if (!token) return next();++ await keys.ensure();++ const v = await verifySession(token);++ if (v.ok) { req.viewer = viewerFromClaims(v.claims, token); return next(); }++ if (v.expired) {++ const fresh = await refresh(req, res);++ if (fresh) { req.viewer = fresh; return next(); }++ }++ if (keys.loaded()) clearSession(res);++ next();++ };++ }++++ /**++ * Run fn(token) as the viewer; when the Network answers 401 (expired or revoked session), refresh++ * once and run it again. Other failures are returned to the caller unchanged.++ */++ async function asViewer(req, res, fn) {++ try {++ return await fn(req.viewer.token);++ } catch (err) {++ if (!err || err.status !== 401) throw err;++ const fresh = await refresh(req, res);++ if (!fresh) throw err;++ req.viewer = fresh;++ return fn(fresh.token);++ }++ }++++ function routes() {++ const r = express.Router();++ const flowOpts = () => ({ ...cookieBase(), path: '/auth', maxAge: 10 * 60 * 1000 });++++ r.get('/login', (req, res) => {++ const silent = Boolean(req.query.silent) && req.query.silent !== '0';++ const state = crypto.randomBytes(16).toString('hex');++ const { verifier, challenge } = pkcePair();++ res.cookie(FLOW_COOKIE, JSON.stringify({ state, verifier, next: sanitizeNext(req.query.next, config), silent }), flowOpts());++ res.set('Cache-Control', cache.htmlHeaders({ private: true }));++ res.redirect(authorizeUrl(config, { state, challenge, silent }));++ });++++ r.get('/callback', async (req, res) => {++ res.set('Cache-Control', cache.htmlHeaders({ private: true }));++ let flow = null;++ try { flow = JSON.parse(req.cookies[FLOW_COOKIE] || 'null'); } catch { flow = null; }++ res.clearCookie(FLOW_COOKIE, { ...cookieBase(), path: '/auth' });++ const next = flow ? sanitizeNext(flow.next, config) : '/';++ const { code, state, error } = req.query;++ if (error) {++ if ((flow && flow.silent) || error === 'login_required' || error === 'interaction_required') return res.redirect(next);++ return res.status(400).type('text/plain').send(`Sign-in was not completed (${String(error).slice(0, 60)}).`);++ }++ if (!flow || typeof flow.state !== 'string' || typeof flow.verifier !== 'string' || typeof state !== 'string'++ || state.length !== flow.state.length || !crypto.timingSafeEqual(Buffer.from(state), Buffer.from(flow.state))) {++ return res.status(400).type('text/plain').send('Sign-in state did not match. Please try signing in again.');++ }++ if (!code) return res.status(400).type('text/plain').send('The Network sent no authorization code.');++ try {++ const data = await tokenGrant({ grant_type: 'authorization_code', code: String(code), redirect_uri: config.oauth.redirectUri, code_verifier: flow.verifier });++ setSession(res, data);++ // No Clear-Site-Data here or on sign-out: Chrome holds such an answer (and the cookies it sets) until the++ // cache is cleared, which on phones left people on the account chooser with no session.++ return res.redirect(next);++ } catch (err) {++ log.warn('[OpenVibe.Surf] sign-in exchange failed:', err.status || '', err.error || '');++ return res.status(err.status && err.status < 500 ? 400 : 502).type('text/plain')++ .send(err.status && err.status < 500 ? 'The Network refused the sign-in code. Please sign in again.' : 'Sign-in failed: OpenVibe.Network did not answer. Please try again.');++ }++ });++++ // Sign-out ends the session, so another site must not be able to trigger it (a cross-site++ // <img src=/auth/logout> or link). A same-origin navigation or form, or the address bar++ // (Sec-Fetch-Site none), signs out directly; anything else gets a confirm button (POST).++ const sameOriginRequest = (req) => {++ const site = String(req.get('sec-fetch-site') || '');++ if (site) return site === 'same-origin' || (site === 'none' && req.method === 'GET');++ const origin = req.get('origin');++ if (origin) return origin === `${req.protocol}://${req.get('host')}`;++ return req.method === 'GET'; // no fetch metadata and no Origin: an old browser navigating++ };++ r.get('/logout', (req, res, next) => {++ if (sameOriginRequest(req)) return next();++ res.set('Cache-Control', cache.htmlHeaders({ private: true }));++ const q = req.query.next ? `?next=${encodeURIComponent(sanitizeNext(req.query.next, config))}` : '';++ res.type('html').send(`<!doctype html><meta charset="utf-8"><title>Sign out</title><meta name="viewport" content="width=device-width,initial-scale=1">`++ + `<form method="post" action="/auth/logout${q}" style="font:16px system-ui;margin:3em auto;max-width:24em;text-align:center">`++ + `<p>Sign out of OpenVibe.Surf?</p><button type="submit">Sign out</button> <a href="/">Cancel</a></form>`);++ });++ r.post('/logout', (req, res, next) => (sameOriginRequest(req) ? next() : res.status(403).type('text/plain').send('Sign-out must come from this site.')));++ r.all('/logout', async (req, res) => {++ const rt = req.cookies && req.cookies[REFRESH_COOKIE];++ if (rt) {++ fetchImpl(`${config.networkInternalUrl || config.networkUrl}/oauth/revoke`, {++ method: 'POST', headers: { 'Content-Type': 'application/json' },++ body: JSON.stringify({ client_id: config.oauth.clientId, client_secret: config.oauth.clientSecret, token: rt }),++ signal: AbortSignal.timeout(3000),++ }).catch(() => {});++ }++ clearSession(res);++ res.cookie(HINT_COOKIE, 'guest', { sameSite: 'lax', secure: config.cookies.secure, httpOnly: false, path: '/', maxAge: 365 * 24 * 60 * 60 * 1000 });++ res.redirect(303, sanitizeNext(req.query.next, config));++ });++++ r.get('/me', (req, res) => {++ res.set('Cache-Control', cache.htmlHeaders({ private: true }));++ // No session at all (a guest: no access or refresh cookie) is signed out, not an error: the shared++ // navbar asks this on every page view, and a 401 logged a console error on each. A session cookie that++ // is present but invalid or expired still answers 401.++ const c = req.cookies || {};++ if (!c[ACCESS_COOKIE] && !c[REFRESH_COOKIE] && !req.get('authorization')) return res.json({ user: null });++ const v = req.viewer;++ if (!v || v.kind !== 'user') return res.status(401).json({ error: 'Not signed in' });++ res.json({ user: { username: v.username, display_name: v.displayName, subject_id: v.subject, role: v.role, avatar_url: v.avatarUrl || null, profile_color: v.profileColor || null } });++ });++ return r;++ }++++ return { middleware, routes, asViewer, tokenGrant, pkcePair };++}++++module.exports = { createSso, pkcePair, sanitizeNext, ownOrigins, authorizeUrl, ACCESS_COOKIE, REFRESH_COOKIE };nnew file mode 100644@@@ -0,0 +1,75 @@++'use strict';++++/**++ * OpenVibe.Surf configuration. Every value comes from the environment (production:++ * /etc/openvibe/surf.env, see .env.example). Only environment variable NAMES appear in code and docs; secrets are++ * never logged.++ *++ * load(env) is pure so tests can build a config without touching process.env.++ */++require('dotenv').config();++++const trim = (s) => String(s || '').replace(/\/+$/, '');++const int = (v, def) => (Number.isFinite(parseInt(v, 10)) ? parseInt(v, 10) : def);++const atLeast = (v, min, def) => Math.max(min, int(v, def));++++function load(env = process.env) {++ const nodeEnv = env.NODE_ENV || 'development';++ const isProduction = nodeEnv === 'production';++ const port = int(env.PORT, 5110);++ const baseUrl = trim(env.BASE_URL || (isProduction ? 'https://openvibe.surf' : `http://localhost:${port}`));++ // The Network's public origin is where a browser is sent to authorize. Its internal origin is where this++ // service exchanges the code and fetches the JWKS; neither a person's browser nor their token talks to it++ // directly.++ const networkPublicUrl = trim(env.OV_NETWORK_URL || 'https://openvibe.network');++ const networkInternalUrl = trim(env.OV_NETWORK_INTERNAL_URL || 'http://127.0.0.1:4000');++++ return {++ service: 'surf',++ name: 'OpenVibe.Surf',++ port,++ host: env.HOST || '127.0.0.1',++ nodeEnv,++ isProduction,++ baseUrl,++ trustProxy: env.TRUST_PROXY != null ? Number(env.TRUST_PROXY) : 2,++++ // OpenVibe.Network: sign-in (OAuth client `surf`, PKCE S256) and its JWKS.++ networkUrl: networkInternalUrl,++ networkPublicUrl,++ networkInternalUrl,++ networkIssuer: trim(env.OV_NETWORK_ISSUER || networkPublicUrl),++++ // The three rooms this front joins. `public` origins are what a person's browser is sent to; the internal++ // origins are what this process may call. A person's Network token only ever goes to the configured Actor.++ liveInternalUrl: trim(env.OV_LIVE_INTERNAL_URL || 'http://127.0.0.1:3000'),++ livePublicUrl: trim(env.SURF_LIVE_PUBLIC_URL || 'https://openvibe.live'),++ gamesInternalUrl: trim(env.OV_GAMES_INTERNAL_URL || 'http://127.0.0.1:8000'),++ gamesPublicUrl: trim(env.SURF_GAMES_PUBLIC_URL || 'https://openvibe.games'),++ actorInternalUrl: trim(env.OV_ACTOR_INTERNAL_URL || 'http://127.0.0.1:4950'),++ actorPublicUrl: trim(env.SURF_ACTOR_PUBLIC_URL || 'https://openvibe.actor'),++++ // Watch: the merged feed is cached 30 s; each Live source gets 2 s before it is left out.++ watchCacheMs: Math.max(0, int(env.SURF_WATCH_CACHE_MS, 30_000)),++ watchSourceTimeoutMs: atLeast(env.SURF_WATCH_SOURCE_TIMEOUT_MS, 200, 2000),++ // Ride: maps are cached 60 s; Games has a little longer than one Live source to answer.++ rideCacheMs: Math.max(0, int(env.SURF_RIDE_CACHE_MS, 60_000)),++ gamesTimeoutMs: atLeast(env.SURF_GAMES_TIMEOUT_MS, 200, 4000),++ // Web: Actor creates a task quickly, but a cold allowance check may take a moment.++ actorTimeoutMs: atLeast(env.SURF_ACTOR_TIMEOUT_MS, 200, 15_000),++++ oauth: {++ clientId: env.OV_OAUTH_CLIENT_ID || 'surf',++ clientSecret: env.OV_OAUTH_CLIENT_SECRET || '',++ redirectUri: env.OV_OAUTH_REDIRECT_URI || `${baseUrl}/auth/callback`,++ scope: 'profile',++ sessionAudience: env.OV_SESSION_AUDIENCE || 'openvibe.network',++ },++ cookies: { secure: env.COOKIE_SECURE ? env.COOKIE_SECURE === 'true' : isProduction },++++ // One per-IP budget for /api and /auth (express-rate-limit, one 60 s window).++ rateLimitPerMinute: Math.max(1, int(env.SURF_RATE_LIMIT_PER_MIN, 120)),++ };++}++++module.exports = { load };nnew file mode 100644@@@ -0,0 +1,113 @@++'use strict';++++/**++ * /api/v1 — the three Surfs:++ *++ * GET /api/v1/watch/feed?topic=all|live|clips|moments&cursor= the merged Live feed, cached 30 s, public++ * GET /api/v1/ride/maps Games' surf maps, cached 60 s, public++ * GET /api/v1/ride/maps/:id one map and its records++ * POST /api/v1/web/tasks Surf with Vibe: one Actor task as the person++ *++ * The Watch and Ride reads are public and cacheable. A browser's POST to /api/v1/web is a write: it must be++ * signed in and come from this site's own Origin, it carries the person's token to Actor as a Bearer, and it is++ * never cached. Actor's 4xx refusal keeps its status and its words; an Actor that does not answer is this++ * service's 502.++ */++const { http } = require('openvibe-contracts');++const { asyncRouter } = require('./router');++++function ownOrigin(config) {++ try { return new URL(config.baseUrl).origin; } catch { return null; }++}++++function createApi(ctx) {++ const { config, feed, ride, web, log } = ctx;++ const r = asyncRouter();++ const origin = ownOrigin(config);++++ const problem = (req, res, status, code, detail) => http.sendProblem(res, status, code, { detail, ctx: req.ov });++++ // ── Watch: the merged Live feed ─────────────────────────++ r.get('/v1/watch/feed', async (req, res) => {++ try {++ const out = await feed.feed(req.query.topic || 'all', req.query.cursor || null);++ res.set('Cache-Control', 'public, max-age=30');++ return res.json(out);++ } catch (err) {++ if (err && err.code === 'watch.invalid_topic') return problem(req, res, 400, err.code, err.message);++ if (err && err.code === 'watch.invalid_cursor') return problem(req, res, 400, err.code, err.message);++ log.warn('[OpenVibe.Surf] watch feed failed:', (err && err.message) || err);++ return problem(req, res, 502, 'watch.unavailable', 'OpenVibe.Live did not answer. Try again in a moment.');++ }++ });++++ // ── Ride: the Games map catalogue ───────────────────────++ r.get('/v1/ride/maps', async (req, res) => {++ try {++ const out = await ride.maps();++ res.set('Cache-Control', 'public, max-age=60');++ return res.json(out);++ } catch (err) {++ log.warn('[OpenVibe.Surf] ride maps failed:', (err && err.message) || err);++ return problem(req, res, 502, 'games.unavailable', 'OpenVibe.Games did not answer. Try again in a moment.');++ }++ });++++ r.get('/v1/ride/maps/:id', async (req, res) => {++ try {++ const out = await ride.map(req.params.id);++ res.set('Cache-Control', 'public, max-age=60');++ return res.json(out);++ } catch (err) {++ if (err && err.status === 400) return problem(req, res, 400, err.code || 'ride.invalid_map', err.message);++ log.warn('[OpenVibe.Surf] ride map failed:', (err && err.message) || err);++ return problem(req, res, 502, 'games.unavailable', 'OpenVibe.Games did not answer. Try again in a moment.');++ }++ });++++ // ── Web: Surf with Vibe ─────────────────────────────────++ // A person's task is never cacheable, not even the 401/403 that refuses it.++ r.use('/v1/web', (_req, res, next) => { res.set('Cache-Control', 'private, no-store'); next(); });++++ // The person's Network token, from the httpOnly cookie only (never a header a script could set). The SSO++ // middleware has already verified it and, when the cookie had expired, refreshed it: prefer that token so an++ // expired surf_at never reaches Actor.++ function requireToken(req, res, next) {++ const viewer = req.viewer;++ const token = viewer && viewer.kind === 'user' ? viewer.token : null;++ if (!token) return problem(req, res, 401, 'token.required', 'Sign in at openvibe.surf to surf the web with Vibe.');++ req.webToken = token;++ return next();++ }++++ // A browser POST to this route must come from this site itself: another site cannot make a signed-in visitor++ // spend their Actor allowance.++ function requireOrigin(req, res, next) {++ const sent = String(req.get('origin') || '');++ if (!origin || sent !== origin) return problem(req, res, 403, 'web.cross_site', 'Surf with Vibe must be started from openvibe.surf itself.');++ return next();++ }++++ r.post('/v1/web/tasks', requireToken, requireOrigin, async (req, res) => {++ const body = req.body && typeof req.body === 'object' ? req.body : {};++ const query = body.query;++ if (!web.validQuery(query)) return problem(req, res, 400, 'web.invalid_query', 'query must be 3 to 500 characters.');++ try {++ const out = await web.createTask({++ token: req.webToken,++ query,++ idempotencyKey: req.get('idempotency-key') || null,++ });++ res.set('Location', out.page);++ return res.status(201).json(out);++ } catch (err) {++ if (err && err.status >= 400 && err.status < 500) return problem(req, res, err.status, err.code || 'actor.refused', err.detail || err.message);++ log.warn('[OpenVibe.Surf] web task failed:', (err && err.message) || err);++ return problem(req, res, 502, (err && err.code) || 'actor.unavailable', (err && err.message) || 'OpenVibe.Actor did not answer. Try again in a moment.');++ }++ });++++ return r;++}++++module.exports = { createApi, ownOrigin };nnew file mode 100644@@@ -0,0 +1,100 @@++'use strict';++++/**++ * Crawl artifacts for openvibe.surf, built with openvibe-shared/seo: robots.txt, sitemap.xml and llms.txt.++ * The public pages are for search engines and AI crawlers; the API, sign-in and the form that creates a task are++ * not. The sitemap adds one page per surf map while Games lists them; a Games failure leaves the static pages in++ * place rather than failing the crawl artifact.++ */++const fs = require('fs');++const path = require('path');++const seo = require('openvibe-shared/seo');++const { asyncRouter } = require('./router');++++const SITE_NAME = 'OpenVibe.Surf';++const DESCRIPTION = 'Ride maps, surf the web, surf what\'s on. OpenVibe.Surf is the Surf room of the OpenVibe network: ride CS-style surf maps in the browser (OpenVibe.Games), lean back and surf live streams, clips and AI moments (OpenVibe.Live), and let an agent browse the web for you on a private desk (OpenVibe.Actor).';++const DISALLOW = ['/auth/', '/api/', '/web/go'];++++function dayOf(ts) {++ const m = String(ts == null ? '' : ts).match(/^(\d{4}-\d{2}-\d{2})/);++ return m ? m[1] : null;++}++function siteUpdated() {++ try { return dayOf(JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'STATUS.json'), 'utf8')).updated); } catch { return null; }++}++++/** Every public page, in the order a crawler should read them. */++function publicPages(maps = []) {++ const static_ = [++ { path: '/', changefreq: 'weekly', priority: 1.0, title: 'OpenVibe.Surf', text: 'Ride CS-style surf maps in the browser, watch live streams, clips and AI moments, and surf the web with an agent.' },++ { path: '/ride', changefreq: 'weekly', priority: 0.9, title: 'Ride', text: 'The OpenVibe.Games surf map catalogue, with each map’s stages, records and in-browser embed.' },++ { path: '/watch', changefreq: 'hourly', priority: 0.9, title: 'Watch', text: 'Live streams, clips people cut and the AI moments of the week, merged into one lean-back feed from OpenVibe.Live.' },++ { path: '/web', changefreq: 'monthly', priority: 0.8, title: 'Surf with Vibe', text: 'Ask an agent to browse the web for you: it searches, reads pages, keeps its sources and finishes with a cited report.' },++ { path: '/developers', changefreq: 'monthly', priority: 0.5, title: 'The Surf API', text: 'The Watch feed, the Ride maps proxy and the Web task route.' },++ ];++ const mapPages = (Array.isArray(maps) ? maps : []).filter((m) => m && m.id).map((m) => ({++ path: `/ride/${encodeURIComponent(String(m.id))}`,++ changefreq: 'monthly',++ priority: 0.6,++ title: String(m.name || m.id),++ text: `${String(m.name || m.id)}: a ${String(m.tier === 1 ? 'beginner' : m.tier === 2 ? 'intermediate' : 'advanced')} surf map with ${m.stages == null ? 'several' : String(m.stages)} stages${m.records != null ? ` and ${String(m.records)} recorded runs` : ''}.`,++ }));++ return [...static_, ...mapPages];++}++++function createDiscoveryRoutes(ctx) {++ const { config, ride } = ctx;++ const r = asyncRouter();++ const site = String(config.baseUrl).replace(/\/+$/, '');++ const abs = (p) => `${site}${p}`;++ const TEXT = 'public, max-age=3600';++++ async function maps() {++ try { const list = await ride.maps(); return list.maps || []; } catch { return []; }++ }++++ r.get('/robots.txt', async (_req, res) => {++ res.type('text/plain').set('Cache-Control', TEXT).send(++ '# openvibe.surf: the public pages are for search and AI crawlers; sign-in, the API and the task form are not.\n'++ + seo.robotsTxt({ sitemaps: [abs('/sitemap.xml')], disallow: DISALLOW }));++ });++++ r.get('/llms.txt', async (_req, res) => {++ res.type('text/plain').set('Cache-Control', TEXT).send(seo.llmsTxt({++ name: SITE_NAME,++ summary: DESCRIPTION,++ details: 'The pages here are server-rendered and readable without JavaScript. Ride is a proxy to OpenVibe.Games, Watch merges OpenVibe.Live’s live, clip and moment feeds, and Web creates one OpenVibe.Actor task with the signed-in person’s own Network token.',++ sections: [++ { title: 'Start here', links: [++ { title: 'OpenVibe.Surf', url: abs('/'), note: 'the three surfs' },++ { title: 'Ride', url: abs('/ride'), note: 'CS-style surf maps from OpenVibe.Games' },++ { title: 'Watch', url: abs('/watch'), note: 'live streams, clips and AI moments from OpenVibe.Live' },++ { title: 'Surf with Vibe', url: abs('/web'), note: 'an agent browses the web for you and cites its sources' },++ { title: 'For developers', url: abs('/developers'), note: 'the Watch, Ride and Web endpoints' },++ ] },++ { title: 'Machine-readable', links: [++ { title: 'The Watch feed (JSON)', url: abs('/api/v1/watch/feed?topic=all'), note: 'public, cached 30 seconds' },++ { title: 'The Ride maps (JSON)', url: abs('/api/v1/ride/maps'), note: 'public, cached 60 seconds' },++ { title: 'Sitemap', url: abs('/sitemap.xml') },++ { title: 'Release metadata (JSON)', url: abs('/release.json') },++ ] },++ { title: 'Elsewhere', links: [++ { title: 'OpenVibe.Live', url: 'https://openvibe.live', note: 'what Watch surfs' },++ { title: 'OpenVibe.Games', url: 'https://openvibe.games', note: 'what Ride plays' },++ { title: 'OpenVibe.Actor', url: 'https://openvibe.actor', note: 'who browses the web for Web' },++ ] },++ ],++ }));++ });++++ r.get('/sitemap.xml', async (_req, res) => {++ const pages = publicPages(await maps());++ const lastmod = siteUpdated();++ const urls = pages.map((e) => ({ loc: abs(e.path), ...(lastmod ? { lastmod } : {}), changefreq: e.changefreq, priority: e.priority }));++ res.type('application/xml').set('Cache-Control', TEXT).send(seo.sitemapXml(urls));++ });++++ return r;++}++++module.exports = { createDiscoveryRoutes, publicPages, DESCRIPTION, SITE_NAME, DISALLOW };nnew file mode 100644@@@ -0,0 +1,119 @@++'use strict';++++/**++ * The public pages:++ * / the front door: Ride, Watch, Web++ * /ride the maps list and the embed of the first map++ * /ride/:map one map: name, tier, stages, records, the embed++ * /watch the merged Live feed as a list with thumbnails and play links++ * /web the Surf with Vibe form and what it does++ * /web/go?q= the no-JavaScript form: create the task, 303 to its Actor page++ * /developers the API, for people and agents++ *++ * Every page is server-rendered and complete without JavaScript. A page for a signed-in person is private and++ * never cached; a public page gets the short 60-second window. `me` comes from the session the SSO middleware++ * already resolved — a guest never causes a Network call to find out they are a guest.++ */++const views = require('../views');++const { asyncRouter } = require('./router');++const { send } = require('../render/layout');++const { ACCESS_COOKIE } = require('../auth/sso');++const express = require('express');++const { ownOrigin } = require('./api');++++const TOPICS = new Set(['all', 'live', 'clips', 'moments']);++++function meOf(viewer) {++ if (!viewer || viewer.kind !== 'user') return { signedIn: false, user: null };++ return {++ signedIn: true,++ user: {++ subject_id: viewer.subject || null,++ username: viewer.username || null,++ display_name: viewer.displayName || null,++ role: viewer.role || 'user',++ avatar_url: viewer.avatarUrl || null,++ profile_color: viewer.profileColor || null,++ },++ };++}++++function createPageRoutes(ctx) {++ const { feed, ride, web, log, config } = ctx;++ const r = asyncRouter();++ // An upstream never throws in a page: a failure degrades to the page without its data.++ const safe = (fn) => Promise.resolve().then(fn).catch((err) => {++ log.warn('[OpenVibe.Surf] page upstream failed:', (err && err.message) || err);++ return null;++ });++ const page = (res, status, html, viewer) => send(res, status, html, { viewer });++++ r.get('/', (req, res) => page(res, 200, views.home(), req.viewer));++++ r.get('/ride', async (req, res) => {++ const out = await safe(() => ride.maps());++ return page(res, 200, views.ride({ maps: out ? out.maps : [], games: out ? out.games : 'unavailable' }), req.viewer);++ });++++ r.get('/ride/:map', async (req, res) => {++ const id = String(req.params.map || '').slice(0, 64);++ const out = await safe(() => ride.map(id));++ return page(res, 200, views.map({++ id,++ map: out ? out.map : { id, embed_url: ride.embedUrl(id) },++ top: out ? out.top : [],++ games: out ? out.games : 'unavailable',++ }), req.viewer);++ });++++ r.get('/watch', async (req, res) => {++ const topic = TOPICS.has(String(req.query.topic || '')) ? String(req.query.topic) : 'all';++ let out = null;++ try {++ out = await feed.feed(topic, req.query.cursor || null);++ } catch (err) {++ // A stale or hand-edited cursor is not a page-level failure: show the first page instead of a 400.++ if (err && err.code === 'watch.invalid_cursor') {++ out = await safe(() => feed.feed(topic, null));++ } else {++ log.warn('[OpenVibe.Surf] watch page failed:', (err && err.message) || err);++ }++ }++ return page(res, 200, views.watch({ feed: out, topic }), req.viewer);++ });++++ r.get('/web', (req, res) => page(res, 200, views.web({ me: meOf(req.viewer), query: typeof req.query.q === 'string' ? req.query.q.slice(0, 500) : '' }), req.viewer));++++ // The no-JavaScript form: a POST from this site that creates one task and goes to it (303). The query is in the++ // body, never in a URL (so never in a log); another site's form is refused by its Origin.++ r.get('/web/go', (req, res) => res.redirect(303, '/web'));++ r.post('/web/go', express.urlencoded({ extended: false, limit: '8kb' }), async (req, res) => {++ res.set('Cache-Control', 'private, no-store');++ const origin = String(req.get('origin') || '');++ const site = String(req.get('sec-fetch-site') || '');++ if ((origin && origin !== ownOrigin(config)) || (site && site !== 'same-origin' && site !== 'none')) {++ return res.status(403).type('text/plain').send('Surf with Vibe starts from openvibe.surf itself.');++ }++ const query = typeof (req.body || {}).q === 'string' ? req.body.q.trim() : '';++ if (!web.validQuery(query)) return page(res, 400, views.web({ me: meOf(req.viewer), query }), req.viewer);++ const token = req.viewer && req.viewer.kind === 'user' ? (req.viewer.token || (req.cookies && req.cookies[ACCESS_COOKIE])) : null;++ if (!token) return res.redirect(303, '/auth/login?next=%2Fweb');++ try {++ const out = await web.createTask({ token, query, idempotencyKey: null });++ return res.redirect(303, out.page);++ } catch (err) {++ if (err && err.status >= 400 && err.status < 500) {++ return res.status(err.status).type('text/plain').send(`OpenVibe.Actor could not take that task: ${String(err.detail || err.message || 'refused').slice(0, 400)}`);++ }++ log.warn('[OpenVibe.Surf] web form failed:', (err && err.message) || err);++ return res.status(502).type('text/plain').send('OpenVibe.Actor did not answer. Go back to /web and try again in a moment.');++ }++ });++++ r.get('/developers', (req, res) => page(res, 200, views.developers(), req.viewer));++++ r.use((req, res) => page(res, 404, views.notFound(), req.viewer));++ return r;++}++++module.exports = { createPageRoutes, meOf };nnew file mode 100644@@@ -0,0 +1,23 @@++'use strict';++++/**++ * express.Router() whose get/post handlers may be async: a rejected promise goes to next(err) instead of++ * becoming an unhandled rejection. (The same helper OpenVibe.Design, OpenVibe.Help and OpenVibe.Domains carry.)++ */++const express = require('express');++++function asyncRouter() {++ const r = express.Router();++ for (const method of ['get', 'post', 'put']) {++ const orig = r[method].bind(r);++ r[method] = (path, ...handlers) => orig(path, ...handlers.map((h) => (h.length === 4 ? h : (req, res, next) => {++ try {++ const out = h(req, res, next);++ if (out && typeof out.catch === 'function') out.catch(next);++ } catch (err) { next(err); }++ })));++ }++ return r;++}++++module.exports = { asyncRouter };nnew file mode 100644@@@ -0,0 +1,46 @@++'use strict';++++/**++ * OpenVibe.Surf — process entry. `node server/index.js` listens on PORT (5110) behind nginx (deploy/).++ * Stateless: the HTTP server and the JWKS refresher are what stop.++ */++const { createApp } = require('./app');++const { gracefulStop } = require('openvibe-sdk/service');++++/**++ * The process stop (openvibe-sdk/service): stop taking connections, drain the requests in flight, stop the JWKS++ * refresher, exit. Exported so a test can inject `exit` and `signals: false`.++ */++function createLifecycle({ server, ctx, exit, signals } = {}) {++ return gracefulStop({++ name: 'OpenVibe.Surf',++ server,++ deadlineExitCode: 0,++ exit,++ signals,++ deadlineMs: 10_000,++ close: [() => ctx.keys.client.stop()],++ });++}++++async function start() {++ const { app, ctx } = await createApp();++ const { config } = ctx;++ const server = app.listen(config.port, config.host, () => {++ console.log(`[OpenVibe.Surf] ${config.nodeEnv} on http://${config.host}:${config.port} → ${config.baseUrl} (network ${config.networkUrl}, live ${config.liveInternalUrl}, games ${config.gamesInternalUrl}, actor ${config.actorInternalUrl})`);++ // Load the Network's signing key now, not on the first sign-in, so /api/ready reports sign-in as it is.++ ctx.keys.ensure().catch(() => {});++ });++ server.keepAliveTimeout = 65_000;++ createLifecycle({ server, ctx });++ return { server, ctx };++}++++if (require.main === module) {++ start().catch((err) => {++ console.error('[OpenVibe.Surf] failed to start:', err);++ process.exit(1);++ });++}++++module.exports = { start, createLifecycle };nnew file mode 100644@@@ -0,0 +1,62 @@++'use strict';++++/**++ * Truthful readiness for GET /api/ready (openvibe-shared/ready). There is no database: the service is stateless.++ *++ * network optional OpenVibe.Network answers its health route++ * network_jwks optional the Network signing key is loaded; without it nobody can sign in++ * oauth_client optional OV_OAUTH_CLIENT_SECRET is set (sign-in needs it)++ * live optional OpenVibe.Live answers++ * games optional OpenVibe.Games answers (a 404 for the new surf routes still means it is up)++ * actor optional OpenVibe.Actor answers; Web cannot create a task without it++ *++ * No check is required: if an upstream does not answer, the process still serves the pages and degrades honestly,++ * so the answer is 200 with status "degraded" — never 503.++ */++const { createReadiness } = require('openvibe-shared/ready');++++function createServiceReadiness({ config, feed, ride, keys = null, release = null, fetchImpl = globalThis.fetch }) {++ return createReadiness({++ service: 'surf',++ release,++ checks: [++ {++ name: 'network',++ required: false,++ check: async () => {++ const base = config.networkInternalUrl || config.networkUrl;++ const fetchOnce = (p) => fetchImpl(`${base}${p}`, {++ headers: { Accept: 'application/json' },++ signal: AbortSignal.timeout(2000),++ });++ let res = await fetchOnce('/api/health');++ // An older Network (and a test stand-in) may not serve /api/health; the JWKS route it must serve++ // is the dependency that matters to sign-in.++ if (res.status === 404) res = await fetchOnce('/api/.well-known/jwks');++ return res.ok ? true : `OpenVibe.Network answered ${res.status}`;++ },++ },++ {++ name: 'network_jwks',++ required: false,++ check: () => (keys && keys.loaded() ? true : 'Network signing key not loaded yet: sign-in is unavailable'),++ },++ { name: 'oauth_client', required: false, check: () => (config.oauth.clientSecret ? true : 'OV_OAUTH_CLIENT_SECRET unset: sign-in cannot complete') },++ { name: 'live', required: false, check: () => feed.probe() },++ { name: 'games', required: false, check: () => ride.probe() },++ {++ name: 'actor',++ required: false,++ check: async () => {++ const res = await fetchImpl(`${config.actorInternalUrl}/api/v1/agents`, {++ headers: { Accept: 'application/json' },++ signal: AbortSignal.timeout(2000),++ });++ return res.status < 500 ? true : `OpenVibe.Actor answered ${res.status}`;++ },++ },++ ],++ });++}++++module.exports = { createServiceReadiness };nnew file mode 100644@@@ -0,0 +1,98 @@++'use strict';++++/**++ * Page shell for OpenVibe.Surf. Every page is server-rendered through openvibe-shared/shell and is complete++ * without JavaScript: the OpenVibe Frame (theme, navbar, footer) comes from the shared shell and the room's accent++ * from openvibe-shared/rooms. The views themselves are the v1 placeholders (server/views/index.js): stable `sf-`++ * ids and classes, semantic HTML, readable with no script.++ *++ * - <head>: the shared app icon, the room's stylesheets, this site's content-hashed surf.css++ * - the navbar's session probe (/auth/me), sign-in and sign-out URLs and silent sign-in, as every site's++ * - surf.js on every page (deferred), for the main session's progressive enhancement++ */++const shell = require('openvibe-shared/shell');++const ovServe = require('openvibe-shared/serve');++const appIcon = require('openvibe-shared/app-icon');++const kit = require('openvibe-shared/kit');++const rooms = require('openvibe-shared/rooms');++const { asset, assetVersion } = require('../assets');++++const NAME = 'OpenVibe.Surf';++const NETWORK = 'https://openvibe.network';++const ROOM = rooms.get('surf');++const esc = kit.esc;++++const NAV = [++ { label: 'Home', href: '/', icon: 'fa-house' },++ { label: 'Ride', href: '/ride', icon: 'fa-flag-checkered' },++ { label: 'Watch', href: '/watch', icon: 'fa-tv' },++ { label: 'Web', href: '/web', icon: 'fa-compass' },++ { label: 'Developers', href: '/developers', icon: 'fa-code', more: true },++];++++let RELEASE = 'dev';++let BASE = 'https://openvibe.surf';++function setRelease(id) { if (id) RELEASE = String(id); }++function configure({ baseUrl } = {}) { if (baseUrl) BASE = String(baseUrl).replace(/\/+$/, ''); }++const baseUrl = () => BASE;++const canonical = (p) => `${BASE}${p || '/'}`;++++/** The page around a body: the shared shell with the room's stylesheets and behaviour. */++function layout({ title, description, canonical, path, robots = 'index, follow', body, active = '/', scripts = ['js/surf.js'], jsonLd = [] } = {}) {++ const pathNow = path || active || '/';++ canonical = canonical || (path ? `${BASE}${path}` : undefined);++ const navbar = {++ service: 'surf',++ apiBase: NETWORK,++ links: NAV.map((l) => ({ ...l, active: l.href === active || (l.href !== '/' && String(active).startsWith(`${l.href}/`)) })),++ appBar: [{ label: 'Ride', href: '/ride', icon: 'fa-flag-checkered' }, { label: 'Watch', href: '/watch', icon: 'fa-tv' }],++ history: { type: 'page', title: title || NAME },++ silentLogin: `${BASE}/auth/login?silent=1&next={url}`,++ sessionUrl: '/auth/me',++ loginUrl: '/auth/login?next={path}',++ logoutUrl: '/auth/logout?next={path}',++ };++ const footer = { service: 'surf', variant: 'full', mount: '#ov-footer', brandName: NAME };++ return shell.page({++ name: NAME,++ lang: 'en',++ title: title || `${NAME}: ${ROOM.line}`,++ titleSuffix: title ? ` · ${NAME}` : '',++ siteName: NAME,++ description: description || `${ROOM.line}. Ride CS-style surf maps in the browser, surf what is live on OpenVibe.Live, and let an agent browse the web for you.`,++ canonical,++ robots,++ jsonLd,++ home: '/',++ navLinks: NAV,++ navbar,++ footer,++ head: `${appIcon.headTags({ site: 'surf' })}++<link rel="stylesheet" href="${esc(ovServe.url('kit.css'))}">++<link rel="stylesheet" href="${esc(ovServe.url('room.css'))}">++<link rel="stylesheet" href="${esc(ovServe.url('showcase.css'))}">++<link rel="stylesheet" href="${esc(asset('css/surf.css'))}">++<script src="${esc(ovServe.url('kit-web.js'))}" defer></script>++<script src="${esc(ovServe.url('motion.js'))}" defer></script>++${(scripts || []).map((rel) => `<script src="${esc(asset(rel))}" defer></script>`).join('\n')}++<meta name="ov-boost" content="surf@${esc(RELEASE)}">`,++ body: `<a class="skip" href="#main">Skip to content</a>++<div id="navbar-mount"></div>++<main id="main" class="sf ov-room" style="${rooms.style('surf')}">${body || ''}</main>++<script>window.__OV_PAGE = ${JSON.stringify({ navbar, footer, path: pathNow }).replace(/</g, '\\u003c')};++document.addEventListener('DOMContentLoaded', function () { try { if (window.OpenVibeFooter) OpenVibeFooter.init(window.__OV_PAGE.footer); } catch (e) { /* the Frame is optional */ } });</script>`,++ });++}++++/**++ * Send a page; a page rendered for a signed-in person is private and never cached. A public page gets the short++ * 60-second public window the main session asked for, with Vary: Cookie because the frame differs by session.++ */++function send(res, status, html, { viewer } = {}) {++ const personal = viewer && viewer.kind === 'user';++ res.set('Cache-Control', personal ? 'private, no-store' : 'public, max-age=60');++ res.set('Vary', 'Cookie');++ res.status(status).type('html').send(html);++}++++module.exports = { layout, send, setRelease, configure, baseUrl, canonical, NAME, NETWORK, ROOM, NAV, esc, asset, assetVersion };nnew file mode 100644@@@ -0,0 +1,118 @@++'use strict';++++/**++ * Ride: the OpenVibe.Games surf map catalogue, proxied and cached for 60 s.++ *++ * GET /api/v1/ride/maps → { maps: [...], games: 'ready'|'not_ready' }++ * GET /api/v1/ride/maps/:id → { map, top, games: 'ready'|'not_ready' }++ *++ * Games is adding GET /api/surf/maps and GET /api/surf/maps/:id now. Until those routes exist Games answers 404:++ * that is "not ready", not an error, so the list is empty and the answer says so. A timeout or a 5xx is an++ * error (the caller answers 502); a failure is never cached, so the next request asks again.++ *++ * Every map carries `embed_url`: https://openvibe.games/play/surf?map=<id>&embed=1 (SURF_GAMES_PUBLIC_URL).++ */++const EMBED_PATH = '/play/surf';++++function clean(id) {++ return String(id == null ? '' : id).trim().slice(0, 64);++}++++function createRide({ config, fetchImpl = globalThis.fetch, log = console, now = () => Date.now() }) {++ const ttl = config.rideCacheMs == null ? 60_000 : config.rideCacheMs;++ let listCache = null; // { at, value }++ const mapCache = new Map(); // id -> { at, value }++++ const publicUrl = String(config.gamesPublicUrl || 'https://openvibe.games').replace(/\/+$/, '');++ const embedUrl = (id) => `${publicUrl}${EMBED_PATH}?map=${encodeURIComponent(clean(id))}&embed=1`;++++ function withEmbed(map) {++ if (!map || typeof map !== 'object') return map;++ const id = clean(map.id);++ return id ? { ...map, embed_url: embedUrl(id) } : map;++ }++++ async function request(path) {++ let res;++ try {++ res = await fetchImpl(`${config.gamesInternalUrl}${path}`, {++ headers: { Accept: 'application/json' },++ signal: AbortSignal.timeout(config.gamesTimeoutMs || 4000),++ });++ } catch (err) {++ const timedOut = err && (err.name === 'TimeoutError' || err.name === 'AbortError');++ throw Object.assign(new Error(timedOut ? 'OpenVibe.Games timed out' : 'OpenVibe.Games did not answer'), { status: 502, code: 'games.unavailable' });++ }++ const text = await res.text().catch(() => '');++ let data = null;++ try { data = text ? JSON.parse(text) : null; } catch { data = null; }++ return { status: res.status, ok: res.ok, data, text };++ }++++ function notReadyList() { return { maps: [], games: 'not_ready' }; }++ function notReadyMap(id) {++ return {++ map: { id: clean(id), name: null, tier: null, stages: null, author: null, records: null, wrMs: null, wrHolder: null, embed_url: embedUrl(id) },++ top: [],++ games: 'not_ready',++ };++ }++++ /** The map catalogue, from memory while it is under 60 s old. A 404 from Games is a cached "not ready". */++ async function maps({ fresh = false } = {}) {++ if (!fresh && listCache && now() - listCache.at < ttl) return listCache.value;++ const r = await request('/api/surf/maps');++ if (r.status === 404) {++ const value = notReadyList();++ listCache = { at: now(), value };++ return value;++ }++ if (!r.ok || !r.data || !Array.isArray(r.data.maps)) {++ throw Object.assign(new Error(`OpenVibe.Games catalogue answered ${r.status}`), { status: 502, code: 'games.unavailable', response: r });++ }++ const value = { maps: r.data.maps.filter(Boolean).map(withEmbed), games: 'ready' };++ listCache = { at: now(), value };++ return value;++ }++++ /** One map: Games' answer with the embed URL added, or a "not ready" shell that still carries the embed. */++ async function map(id, { fresh = false } = {}) {++ const key = clean(id);++ if (!key) throw Object.assign(new Error('a map id is required'), { status: 400, code: 'ride.invalid_map' });++ const hit = mapCache.get(key);++ if (!fresh && hit && now() - hit.at < ttl) return hit.value;++ const r = await request(`/api/surf/maps/${encodeURIComponent(key)}`);++ if (r.status === 404) {++ const value = notReadyMap(key);++ mapCache.set(key, { at: now(), value });++ if (mapCache.size > 200) mapCache.delete(mapCache.keys().next().value);++ return value;++ }++ if (!r.ok || !r.data || !r.data.map) {++ throw Object.assign(new Error(`OpenVibe.Games map answered ${r.status}`), { status: 502, code: 'games.unavailable', response: r });++ }++ const value = { map: withEmbed(r.data.map), top: Array.isArray(r.data.top) ? r.data.top : [], games: 'ready' };++ mapCache.set(key, { at: now(), value });++ if (mapCache.size > 200) mapCache.delete(mapCache.keys().next().value);++ return value;++ }++++ /** Readiness: Games is reachable when it answers anything below 500. A 404 still means the process is up. */++ async function probe() {++ try {++ const res = await fetchImpl(`${config.gamesInternalUrl}/api/surf/maps`, {++ headers: { Accept: 'application/json' },++ signal: AbortSignal.timeout(config.gamesTimeoutMs || 4000),++ });++ return res.status < 500 ? true : `OpenVibe.Games answered ${res.status}`;++ } catch (err) {++ return err && (err.name === 'TimeoutError' || err.name === 'AbortError') ? 'OpenVibe.Games timed out' : 'OpenVibe.Games did not answer';++ }++ }++++ function clear() { listCache = null; mapCache.clear(); }++++ return { maps, map, probe, clear, embedUrl, get cached() { return listCache; } };++}++++module.exports = { createRide, embedUrl: (config, id) => `${String(config.gamesPublicUrl || 'https://openvibe.games').replace(/\/+$/, '')}${EMBED_PATH}?map=${encodeURIComponent(clean(id))}&embed=1` };nnew file mode 100644@@@ -0,0 +1,236 @@++'use strict';++/**++ * The Surf room's pages (plan 2026-10-28 §5.5, v1): one room, three surfs. Server-rendered and whole without++ * JavaScript; public/js/surf.js adds the TV (HLS in the page, ↑ ↓ and swipe to change channel), the Web form's++ * in-page start, and the home's ticking timer.++ *++ * home() the front door: the run motif, three tiles under one Start, scenes, questions++ * ride({ maps, games }) every map as a card, and the first one to play++ * map({ id, map, top, games }) one map: tier, stages, author, the game, its fastest runs++ * watch({ feed, topic }) the TV: what is on now, the guide (up next), topics++ * web({ me, query }) Surf with Vibe: ask, and an agent browses for you on a private desk++ * developers() the API++ * notFound()++ */++const layoutLib = require('../render/layout');++const kit = require('openvibe-shared/kit');++const sc = require('openvibe-shared/showcase');++const rooms = require('openvibe-shared/rooms');++++const { layout, esc } = layoutLib;++const ROOM = rooms.get('surf');++++const TIER = { 1: 'Beginner', 2: 'Intermediate', 3: 'Hard', 4: 'Advanced', 5: 'Expert', 6: 'Elite' };++const tierLabel = (t) => (TIER[t] ? `Tier ${t} · ${TIER[t]}` : t ? `Tier ${t}` : 'Unrated');++const idSafe = (v) => String(v == null ? '' : v).replace(/[^A-Za-z0-9_-]+/g, '-').replace(/^-+|-+$/g, '');++/** 61234 → "1:01.234"; 9870 → "9.870". */++function runTime(v) {++ const n = Number(v);++ if (!Number.isFinite(n) || n < 0 || v === null) return null;++ const m = Math.floor(n / 60000), s = Math.floor((n % 60000) / 1000), x = String(Math.round(n % 1000)).padStart(3, '0');++ return m ? `${m}:${String(s).padStart(2, '0')}.${x}` : `${s}.${x}`;++}++const clipLen = (v) => { const n = Number(v); if (!Number.isFinite(n) || v === null) return null; const m = Math.floor(n / 60), s = Math.round(n % 60); return m ? `${m}:${String(s).padStart(2, '0')}` : `0:${String(s).padStart(2, '0')}`; };++const signedInName = (me) => (me && me.signedIn && me.user && (me.user.display_name || me.user.username)) || null;++++/** The run motif: a first-person view down two ramps, speed lines, the timer and the speed. */++function runMotif() {++ const lines = Array.from({ length: 9 }, (_, i) => `<i style="--i:${i}"></i>`).join('');++ return `<div class="sf-run" aria-hidden="true" data-sf-run>++<svg class="sf-run-ramps" viewBox="0 0 400 260" preserveAspectRatio="none"><defs><linearGradient id="sf-rampL" x1="0" x2="1"><stop offset="0" stop-color="currentColor" stop-opacity=".55"/><stop offset="1" stop-color="currentColor" stop-opacity=".12"/></linearGradient><linearGradient id="sf-rampR" x1="1" x2="0"><stop offset="0" stop-color="currentColor" stop-opacity=".55"/><stop offset="1" stop-color="currentColor" stop-opacity=".12"/></linearGradient></defs>++<path d="M0 260 L170 112 L178 118 L40 260 Z" fill="url(#sf-rampL)"/><path d="M400 260 L230 112 L222 118 L360 260 Z" fill="url(#sf-rampR)"/>++<path d="M170 112 L200 96 L230 112" fill="none" stroke="currentColor" stroke-opacity=".35" stroke-width="2"/></svg>++<span class="sf-run-speed">${lines}</span>++<span class="sf-run-hud"><b data-sf-timer>0:12.384</b><small><span data-sf-speed>1 284</span> u/s · stage 2/4</small></span>++</div>`;++}++++function home() {++ const tiles = `<ul class="sf-tiles" id="sf-tiles">++<li class="sf-tile" id="sf-tile-ride" data-sf-surf="ride"><a href="/ride"><span class="sf-tile-ic">${kit.icon('game')}</span><b>Ride</b><span>CS-style surf in the browser: ramps, a millisecond timer, your ghost and the record.</span><span class="sf-tile-go">Ride a map${kit.icon('chevron')}</span></a></li>++<li class="sf-tile" id="sf-tile-web" data-sf-surf="web"><a href="/web"><span class="sf-tile-ic">${kit.icon('globe')}</span><b>Web</b><span>Surf with Vibe: say what you want, an agent browses for you and reports back with its sources.</span><span class="sf-tile-go">Ask Vibe${kit.icon('chevron')}</span></a></li>++<li class="sf-tile" id="sf-tile-watch" data-sf-surf="watch"><a href="/watch"><span class="sf-tile-ic">${kit.icon('play')}</span><b>Watch</b><span>Lean back: live streams, clips and the week's AI moments, one swipe or ↑ ↓ to change channel.</span><span class="sf-tile-go">Start watching${kit.icon('chevron')}</span></a></li>++</ul>`;++ // The tiles sit right under the hero (sc.room's own html slot comes after the scenes), so they go in before the scenes.++ const room = sc.room({++ room: 'surf',++ hero: {++ title: ROOM.line,++ lede: 'Three surfs in one room. Ride Counter-Strike-style surf maps right in the browser and race the clock, let an agent surf the web for you and bring back what it found, or lean back and surf what is on across OpenVibe.',++ start: { href: '/ride', label: 'Start riding' },++ motif: runMotif(),++ },++ scenes: [++ { title: 'Press play, you are on the ramp', text: 'Air-strafe into the ramp, hold your speed, hit the next one. The timer counts to the millisecond, your best run rides beside you as a ghost, and every finished run lands on the map\'s board.',++ html: `<div class="sf-demo-run"><div class="sf-demo-hud"><span>Stage 3/4</span><b>0:41.207</b><span class="is-good">−0.384</span></div><div class="sf-demo-bar"><i style="width:72%"></i></div><p>Personal best 0:41.591 · World record 0:38.902</p></div>`,++ action: { href: '/ride', label: 'Pick a map' } },++ { title: 'Surf the web with Vibe', text: 'Say what you want: a cheap flight and three things to do, or what people say about a shoe. The agent searches, reads the pages that matter on a private desk, keeps its sources, and finishes with a report you can check.',++ html: `<ol class="sf-demo-steps"><li class="is-done">Searched the web for “Lisbon in March, cheap flights”</li><li class="is-done">Read 4 pages · compared prices on 2 sites</li><li class="is-now">Writing the report with its sources…</li></ol>`,++ action: { href: '/web', label: 'Ask Vibe' } },++ { title: 'Lean back and surf what is on', text: 'Live streams first, then the clips people cut and the AI moments of the week, each saying why it is on. Swipe on a phone or press ↑ ↓ on a keyboard.',++ html: `<div class="sf-demo-tv"><div class="sf-demo-screen"><span class="sf-pill-live">LIVE</span><span>goosely · Night owls: Seattle walk</span></div><ul><li><b>Up next</b> Top AI moment this week</li><li>New clip · Robot build, part 2</li></ul></div>`,++ action: { href: '/watch', label: 'Start watching' } },++ ],++ explore: { title: 'More on Surf', items: [++ { title: 'Ride', text: 'Maps, the timer, records', href: '/ride', icon: 'ov:games' },++ { title: 'Surf with Vibe', text: 'An agent browses for you', href: '/web', icon: 'ov:actor' },++ { title: 'The API', text: 'Feeds, maps and tasks as JSON', href: '/developers', icon: 'ov:code' },++ ] },++ faq: { items: [++ ['Do I need to install anything to ride?', 'No. The maps run in your browser on OpenVibe.Games: a keyboard and mouse (pointer lock) or a gamepad; phones get a touch ramp trainer.'],++ ['Does the agent use my accounts?', 'No. It browses on its own private desk, stops at logins and anything that would spend money, and hands over to you. Its steps and sources stay in your Actor tasks.'],++ ['What plays in Watch?', 'OpenVibe.Live: who is live now, the clips people cut and the week\'s AI moments. Live streams open on their channel; clips and moments play right here.'],++ ] },++ });++ const at = room.indexOf('<section class="sc-room-scenes"');++ const body = at < 0 ? room.replace(/<\/div>$/, `${tiles}</div>`) : room.slice(0, at) + tiles + room.slice(at);++ return layout({ title: null, path: '/', body: `<div class="sf-wrap">${body}</div>`, active: '/' });++}++++function mapCard(m) {++ const id = String(m.id || '');++ const wr = runTime(m.wrMs);++ return `<li class="sf-map" id="sf-map-${esc(idSafe(id))}" data-sf-map-id="${esc(id)}" data-tier="${esc(m.tier || '')}">++<a class="sf-map-link" href="/ride/${encodeURIComponent(id)}">++<span class="sf-map-art" aria-hidden="true"><i></i><i></i><i></i></span>++<span class="sf-map-body"><span class="sf-tier">${esc(tierLabel(m.tier))}</span><b>${esc(m.name || id || 'Surf map')}</b>++<span class="sf-map-meta">${m.stages == null ? '' : `${esc(m.stages)} stage${Number(m.stages) === 1 ? '' : 's'}`}${m.records != null ? ` · ${esc(m.records)} run${Number(m.records) === 1 ? '' : 's'}` : ''}</span>++<span class="sf-wr">${wr ? `Record <b>${esc(wr)}</b>${m.wrHolder ? ` · ${esc(m.wrHolder)}` : ''}` : 'No record yet: set the first one'}</span></span>++</a></li>`;++}++++function gameFrame(m, name) {++ if (!m || !m.embed_url) return '';++ return `<div class="sf-game" id="sf-embed" data-sf-embed data-sf-map-id="${esc(m.id || '')}">++<iframe src="${esc(m.embed_url)}" title="${esc(`${name}: play in OpenVibe.Games`)}" loading="lazy" allow="fullscreen; gamepad; pointer-lock" referrerpolicy="strict-origin-when-cross-origin"></iframe>++</div><p class="sf-fine sf-game-help">Click the game to start: <kbd>W</kbd><kbd>A</kbd><kbd>S</kbd><kbd>D</kbd> and the mouse to strafe, <kbd>R</kbd> to restart, <kbd>G</kbd> for your ghost. <a href="${esc(m.embed_url.replace(/[?&]embed=1/, ''))}">Open it full screen</a></p>`;++}++++const gamesNote = (games) => (games === 'not_ready' ? 'OpenVibe.Games is building the surf maps right now. They appear here the moment they are ready.'++ : games === 'unavailable' ? 'OpenVibe.Games did not answer just now. Try again in a moment.' : 'No maps are listed right now.');++++function ride({ maps = [], games = 'ready' } = {}) {++ const list = Array.isArray(maps) ? maps : [];++ const first = list[0] || null;++ const body = `<div class="sf-wrap sf-page-pad">++<header class="sf-page-h"><p class="sf-kicker">Ride</p><h1>Pick a ramp</h1>++<p class="sf-lede">Counter-Strike-style surf in the browser. Each map has stages, a millisecond timer and a board of the fastest runs${first ? '; the first one is ready to play below' : ''}.</p></header>++<section id="sf-ride" aria-labelledby="sf-maps-h"><h2 id="sf-maps-h" class="sf-sr">Maps</h2>++${list.length ? `<ul class="sf-maps" id="sf-maps">${list.map(mapCard).join('')}</ul>` : `<div class="sf-empty" id="sf-maps-empty">${kit.icon('game')}<p>${esc(gamesNote(games))}</p></div>`}++</section>++${first ? `<section id="sf-ride-embed" aria-labelledby="sf-embed-h" class="sf-play"><div class="sf-head-row"><h2 id="sf-embed-h">${esc(first.name || 'Play')}</h2><a class="sf-more" href="/ride/${encodeURIComponent(first.id)}">Records${kit.icon('chevron')}</a></div>${gameFrame(first, first.name || 'Surf map')}</section>` : ''}++</div>`;++ return layout({ title: 'Ride', description: 'Counter-Strike-style surf maps in the browser: stages, a millisecond timer, ghosts and records.', path: '/ride', body, active: '/ride' });++}++++function map({ id, map: m = null, top = [], games = 'ready' } = {}) {++ const one = m || { id };++ const name = one.name || String(id || '');++ const rows = (Array.isArray(top) ? top : []).map((r, i) => `<tr${i === 0 ? ' class="is-wr"' : ''}><td>${esc(r.rank == null ? i + 1 : r.rank)}</td><td>${esc(r.name || '')}</td><td class="sf-time">${esc(runTime(r.timeMs) || '')}</td></tr>`).join('');++ const body = `<div class="sf-wrap sf-page-pad">++<header class="sf-page-h sf-map-h" id="sf-map" data-sf-map-id="${esc(id || one.id || '')}"><p class="sf-kicker"><a href="/ride">Ride</a> · ${esc(tierLabel(one.tier))}</p><h1>${esc(name)}</h1>++<p class="sf-lede">${one.stages == null ? '' : `${esc(one.stages)} stage${Number(one.stages) === 1 ? '' : 's'}`}${one.records != null ? ` · ${esc(one.records)} run${Number(one.records) === 1 ? '' : 's'}` : ''}${one.author ? ` · by ${esc(one.author)}` : ''}${one.wrMs != null ? ` · record <b>${esc(runTime(one.wrMs))}</b>${one.wrHolder ? ` by ${esc(one.wrHolder)}` : ''}` : ''}</p></header>++${games === 'not_ready' ? `<p class="sf-note" id="sf-ride-not-ready">${esc(gamesNote(games))}</p>` : ''}++<div class="sf-map-grid">++<section id="sf-map-embed" aria-label="Play">${gameFrame(one, name) || `<div class="sf-empty">${kit.icon('game')}<p>This map cannot be played here right now.</p></div>`}</section>++<section id="sf-map-records" class="sf-card" aria-labelledby="sf-records-h"><h2 id="sf-records-h">${kit.icon('trophy')}Fastest runs</h2>++${rows ? `<table class="sf-board" id="sf-top"><thead><tr><th scope="col">#</th><th scope="col">Rider</th><th scope="col">Time</th></tr></thead><tbody>${rows}</tbody></table>`++ : `<p class="sf-fine" id="sf-top-empty">${games === 'ready' ? 'No runs yet. Finish it once and you hold the record.' : esc(gamesNote(games))}</p>`}++</section></div>++</div>`;++ return layout({ title: `${name}: Ride`, description: `${name}: a surf map you ride in the browser, with stages, a millisecond timer and records.`, path: `/ride/${encodeURIComponent(id || one.id || '')}`, body, active: '/ride' });++}++++function watch({ feed = null, topic = 'all' } = {}) {++ const items = feed && Array.isArray(feed.items) ? feed.items : [];++ const tabs = [['all', 'All'], ['live', 'Live'], ['clips', 'Clips'], ['moments', 'AI moments']].map(([id, label]) =>++ `<a class="sf-topic" href="/watch?topic=${id}"${id === topic ? ' aria-current="page"' : ''}>${label}</a>`).join('');++ const first = items[0] || null;++ const kindOf = (it) => (it.kind === 'moment' ? 'moment' : it.kind === 'clip' ? 'clip' : 'live');++ const label = { live: 'Live', clip: 'Clip', moment: 'AI moment' };++ const stage = first ? (() => {++ const k = kindOf(first);++ const p = first.play || {};++ const media = p.type === 'hls' && p.src++ ? `<video class="sf-tv-video" data-sf-video playsinline controls preload="none" poster="${esc(first.thumbnail || '')}" src="${esc(p.src)}"></video>`++ : `${first.thumbnail ? `<img class="sf-tv-poster" src="${esc(first.thumbnail)}" alt="" loading="eager">` : '<span class="sf-tv-poster"></span>'}<a class="sf-tv-out" href="${esc(p.page || '#')}">${kit.icon('play')}${k === 'live' ? 'Watch live on OpenVibe.Live' : 'Play on OpenVibe.Live'}</a>`;++ return `<div class="sf-tv-main"><div class="sf-tv-screen" data-sf-screen>${media}</div>++<div class="sf-tv-info"><p class="sf-tv-kind"><span class="sf-pill sf-pill--${esc(k)}">${esc(label[k])}</span><span data-sf-why>${esc(first.why || '')}</span></p>++<h2 data-sf-title>${esc(first.title || '')}</h2>++<p class="sf-tv-meta" data-sf-meta>${first.channel && first.channel.username ? `<a href="https://openvibe.live/@${encodeURIComponent(first.channel.username)}">@${esc(first.channel.username)}</a>` : ''}</p>++<p class="sf-tv-keys"><button type="button" class="ovk-btn ovk-btn--quiet ovk-btn--small" data-sf-prev aria-label="Previous channel">${kit.icon('up')}</button><button type="button" class="ovk-btn ovk-btn--quiet ovk-btn--small" data-sf-next aria-label="Next channel">${kit.icon('down')}</button><span class="sf-fine">Swipe, or press <kbd>↑</kbd> <kbd>↓</kbd></span></p></div></div>`;++ })() : '';++ const rows = items.map((it, i) => {++ const k = kindOf(it);++ const p = it.play || {};++ return `<li class="sf-ch${i === 0 ? ' is-on' : ''}" id="sf-item-${esc(idSafe(it.key))}" data-sf-i="${i}" data-sf-kind="${esc(k)}" data-sf-play-type="${esc(p.type || '')}"${p.src ? ` data-sf-src="${esc(p.src)}"` : ''} data-sf-page="${esc(p.page || '')}" data-sf-thumb="${esc(it.thumbnail || '')}" data-sf-why="${esc(it.why || '')}" data-sf-channel="${esc((it.channel && it.channel.username) || '')}">++<a class="sf-ch-link" href="${esc(p.page || '#')}">++<span class="sf-ch-thumb">${it.thumbnail ? `<img src="${esc(it.thumbnail)}" alt="" loading="lazy" decoding="async">` : ''}${it.duration_seconds != null ? `<span class="sf-ch-len">${esc(clipLen(it.duration_seconds))}</span>` : ''}${k === 'live' ? '<span class="sf-ch-live">LIVE</span>' : ''}</span>++<span class="sf-ch-body"><b>${esc(it.title || '')}</b><small>${it.channel && it.channel.username ? `@${esc(it.channel.username)} · ` : ''}${esc(it.why || label[k])}</small></span>++</a></li>`;++ }).join('');++ const missing = feed && feed.sources ? Object.entries(feed.sources).filter(([, v]) => v !== 'ok').map(([k]) => k) : [];++ const body = `<div class="sf-wrap sf-page-pad">++<header class="sf-page-h sf-watch-h"><div><p class="sf-kicker">Watch</p><h1>Surf what's on</h1></div><nav class="sf-topics" id="sf-topics" aria-label="What to watch">${tabs}</nav></header>++${missing.length ? `<p class="sf-note" id="sf-sources">Some of OpenVibe.Live did not answer (${esc(missing.join(', '))}); the rest is here.</p>` : ''}++${items.length ? `<div class="sf-tv" id="sf-tv" data-sf-tv>${stage}<aside class="sf-guide" aria-labelledby="sf-guide-h"><h2 id="sf-guide-h">Up next</h2><ol class="sf-chs" id="sf-watch-feed">${rows}</ol>++${feed && feed.next ? `<a class="sf-more" id="sf-watch-next" href="/watch?topic=${esc(topic)}&cursor=${esc(feed.next)}">More${kit.icon('chevron')}</a>` : ''}</aside></div>`++ : `<div class="sf-empty" id="sf-watch-empty">${kit.icon('play')}<p>${feed ? 'Nothing on this channel right now. Try another, or come back soon.' : 'OpenVibe.Live did not answer. Try again in a moment.'}</p></div>`}++</div>`;++ return layout({ title: 'Watch', description: 'Lean-back channel surfing over OpenVibe.Live: who is live, clips and the week\'s AI moments in one feed.', path: '/watch', body, active: '/watch' });++}++++const EXAMPLES = ['A cheap flight to Lisbon in March and three things to do', 'What do runners say about the Pegasus 42?', 'Compare three hosting plans under $10 a month'];++++function web({ me = {}, query = '' } = {}) {++ const who = signedInName(me);++ const body = `<div class="sf-wrap sf-page-pad sf-narrow">++<header class="sf-page-h"><p class="sf-kicker">Web · Surf with Vibe</p><h1>Tell it what to find</h1>++<p class="sf-lede">An agent surfs the web for you: it searches, reads the pages that matter on a private desk when a page needs a real browser, keeps its sources, and finishes with a short report that cites them.</p></header>++<section id="sf-web" class="sf-card" aria-labelledby="sf-web-h"><h2 id="sf-web-h" class="sf-sr">What should Vibe find?</h2>++<form class="sf-web-form" id="sf-web-form" data-sf-web-form action="/web/go" method="post">++<label for="sf-web-query" class="sf-sr">What should Vibe find?</label>++<textarea id="sf-web-query" name="q" rows="3" minlength="3" maxlength="500" required spellcheck="true" placeholder="e.g. a cheap flight to Lisbon in March and three things to do">${esc(query)}</textarea>++<div class="sf-web-row"><span class="sf-fine" id="sf-web-who">${who ? `Runs as <b>${esc(who)}</b>, on your OpenVibe allowance.` : 'You will be asked to sign in first.'}</span>++<button class="ovk-btn ovk-btn--primary" id="sf-web-submit" type="submit">${kit.icon('globe')}Surf it</button></div>++</form>++<p class="sf-status" id="sf-web-status" role="status"></p>++<div class="sf-examples" aria-label="Try one">${EXAMPLES.map((x) => `<button type="button" class="sf-ex" data-sf-example="${esc(x)}">${esc(x)}</button>`).join('')}</div>++</section>++<section id="sf-web-how" aria-labelledby="sf-web-how-h"><h2 id="sf-web-how-h">What happens next</h2>++<ol class="sf-how">++<li><b>It searches.</b><span>The web first through OpenVibe's Search Fabric, so it starts with what is known and cited.</span></li>++<li><b>It reads.</b><span>It opens the pages that matter, and a private desk when a page needs a real browser. You can watch the desk live and take over.</span></li>++<li><b>It reports.</b><span>A short answer with every source it used, in your Actor tasks and in the Action Center.</span></li>++</ol>++<p class="sf-fine">It never signs in or pays for you: at a login, a captcha or a checkout it stops and hands over. OpenVibe.Surf keeps neither your question nor the report; the task is yours at OpenVibe.Actor.</p></section>++</div>`;++ return layout({ title: 'Surf with Vibe', description: 'Ask an agent to surf the web for you: it searches, reads pages on a private desk when needed, keeps its sources and reports back with citations.', path: '/web', body, active: '/web' });++}++++function developers() {++ const rows = [++ ['GET /api/v1/watch/feed?topic=all|live|clips|moments&cursor=', 'The merged OpenVibe.Live feed: who is live (by viewers), then clips and AI moments by freshness, never two from one channel in a row. Each item says why it is on. Cached 30 s; a source that did not answer is named in <code>sources</code>.'],++ ['GET /api/v1/ride/maps', 'The surf maps from OpenVibe.Games: id, name, tier, stages, author, runs, the record. Cached 60 s.'],++ ['GET /api/v1/ride/maps/:id', 'One map, its fastest runs and its <code>embed_url</code>.'],++ ['POST /api/v1/web/tasks', 'Signed in, from this site: <code>{ query }</code> (3–500 characters) starts an OpenVibe.Actor task as you and answers <code>{ task_id, page }</code>.'],++ ];++ const body = `<div class="sf-wrap sf-page-pad">++<header class="sf-page-h"><p class="sf-kicker">For developers</p><h1>The Surf API</h1>++<p class="sf-lede">Watch and Ride are public reads; Web starts a task at OpenVibe.Actor as the signed-in person. Errors are RFC 9457 <code>application/problem+json</code>.</p></header>++<dl class="sf-eps">${rows.map(([ep, what]) => `<div><dt><code>${ep}</code></dt><dd>${what}</dd></div>`).join('')}</dl>++</div>`;++ return layout({ title: 'The Surf API', path: '/developers', body, active: '/developers' });++}++++function notFound() {++ const body = `<div class="sf-wrap sf-page-pad sf-narrow"><div class="sf-empty" id="sf-not-found">${kit.icon('game')}<h1>Wiped out</h1><p>There is nothing at this address. Try <a href="/ride">a map</a>, <a href="/watch">what's on</a> or <a href="/web">asking Vibe</a>.</p></div></div>`;++ return layout({ title: 'Not found', robots: 'noindex, follow', body, active: '/' });++}++++module.exports = {++ home, ride, map, watch, web, developers, notFound, runTime,++ layout,++ setRelease: layoutLib.setRelease,++ configure: layoutLib.configure,++};nnew file mode 100644@@@ -0,0 +1,312 @@++'use strict';++++/**++ * The Watch feed: OpenVibe.Live's three surfaces merged into one list.++ *++ * GET /api/v1/watch/feed?topic=all|live|clips|moments&cursor=++ *++ * Sources (all on OV_LIVE_INTERNAL_URL):++ * live GET /api/streams → { streams: [...] }++ * clips GET /api/content/feed?type=clips&sort=new… → { items: [...], next }++ * moments GET /api/content/moments?type=clips&sort=top… → { items: [...], next }++ *++ * One shape for every card:++ * { key, kind, title, channel: { username, display_name, avatar_url }, thumbnail,++ * play: { type: 'live'|'hls', src, page }, duration_seconds, viewers, why }++ *++ * Live streams come first, by viewers. Clips and moments then interleave by freshness. Finally no two cards from++ * the same channel sit next to each other when another channel is available. The merged page is cached 30 s;++ * every Live source is given 2 s and a source that fails is left out and named in `sources`.++ */++const DEFAULT_TOPIC = 'all';++const TOPICS = ['all', 'live', 'clips', 'moments'];++const SOURCE_NAMES = ['live', 'clips', 'moments'];++const CLIP_PATH = '/api/content/feed';++const MOMENT_PATH = '/api/content/moments';++++class WatchError extends Error {++ constructor(status, code, message) {++ super(message);++ this.status = status;++ this.code = code;++ }++}++++const clean = (v, max = 140) => {++ const s = String(v == null ? '' : v).replace(/\s+/g, ' ').trim();++ if (!max || s.length <= max) return s;++ return `${s.slice(0, max - 1).replace(/\s+\S*$/, '')}…`;++};++const num = (v) => (Number.isFinite(Number(v)) ? Number(v) : 0);++const numOrNull = (v) => (Number.isFinite(Number(v)) && v !== null && v !== '' ? Number(v) : null);++const fresh = (item) => {++ const n = Date.parse(String(item && item.created_at || ''));++ return Number.isFinite(n) ? n : 0;++};++++/** An absolute http(s) URL, or null. Media is the only place cards point for pictures and playlists. */++function mediaUrl(v) {++ if (typeof v !== 'string' || !v) return null;++ try {++ const u = new URL(v);++ return u.protocol === 'https:' || u.protocol === 'http:' ? u.toString() : null;++ } catch { return null; }++}++++/** A card's page: on OpenVibe.Live, or on openvibe.video where Live's clips and moments open now. */++const VIDEO_ORIGINS = new Set(['https://openvibe.video']);++function livePage(livePublicUrl, href) {++ if (typeof href !== 'string' || !href) return null;++ try {++ const base = new URL(`${livePublicUrl}/`);++ const u = new URL(href, base);++ return u.origin === base.origin || VIDEO_ORIGINS.has(u.origin) ? u.toString() : null;++ } catch { return null; }++}++++function channelOf(c) {++ if (typeof c === 'string' && c) c = { username: c, display_name: c };++ const ch = c && typeof c === 'object' ? c : {};++ const username = typeof ch.username === 'string' && ch.username ? ch.username.slice(0, 64) : null;++ return {++ username,++ display_name: clean(ch.display_name || username || 'Unknown channel', 80) || 'Unknown channel',++ avatar_url: /^https:\/\/[^\s"'<>]{1,500}$/.test(String(ch.avatar_url || '')) ? String(ch.avatar_url) : null,++ };++}++++function idOf(item, kind) {++ if (item && (typeof item.id === 'string' || typeof item.id === 'number')) return String(item.id);++ if (item && typeof item.key === 'string' && item.key) return item.key.includes(':') ? item.key.slice(item.key.indexOf(':') + 1) : item.key;++ return null;++}++++function sortLive(items) {++ return [...items].sort((a, b) => (b.viewers - a.viewers) || String(a.channel.username || '').localeCompare(String(b.channel.username || '')));++}++function sortFresh(items) {++ return [...items].sort((a, b) => fresh(b) - fresh(a));++}++++/**++ * Moments and clips interleaved by freshness: the freshest overall goes first, then the other kind, then back,++ * each side keeping its own newest-first order. When one side runs out the rest of the other follows.++ */++function interleave(moments, clips) {++ const a = sortFresh(moments);++ const b = sortFresh(clips);++ const out = [];++ while (a.length || b.length) {++ let takeMoment;++ if (!a.length) takeMoment = false;++ else if (!b.length) takeMoment = true;++ else if (!out.length) takeMoment = fresh(a[0]) >= fresh(b[0]);++ else takeMoment = out[out.length - 1].kind !== 'moment';++ out.push(takeMoment ? a.shift() : b.shift());++ }++ return out;++}++++/**++ * Never two cards from the same channel in a row when another channel is available: when the next card repeats++ * the previous channel, pull the nearest later card from a different channel forward. Order otherwise stands.++ */++function separateChannels(items) {++ const pool = [...items];++ const out = [];++ while (pool.length) {++ let at = 0;++ const prev = out[out.length - 1];++ if (prev && prev.channel.username && pool[0].channel.username === prev.channel.username) {++ const other = pool.findIndex((it) => it.channel.username !== prev.channel.username);++ if (other > 0) at = other;++ }++ out.push(pool.splice(at, 1)[0]);++ }++ return out;++}++++function rank(source, topic) {++ if (topic === 'live') return separateChannels(sortLive(source.live));++ if (topic === 'clips') return separateChannels(sortFresh(source.clips));++ if (topic === 'moments') return separateChannels(sortFresh(source.moments));++ // Live stays first, always: separate channels within the live block and within the content block, then fix++ // only the boundary between the two by pulling a content card from another channel forward.++ const live = separateChannels(sortLive(source.live));++ let content = separateChannels(interleave(source.moments, source.clips));++ if (live.length && content.length) {++ const prev = live[live.length - 1];++ if (prev.channel.username && content[0].channel.username === prev.channel.username) {++ const other = content.findIndex((it) => it.channel.username !== prev.channel.username);++ if (other > 0) {++ content.unshift(content.splice(other, 1)[0]);++ content = separateChannels(content);++ }++ }++ }++ return [...live, ...content];++}++++const encodeCursor = (value) => Buffer.from(JSON.stringify(value), 'utf8').toString('base64url');++++function decodeCursor(raw) {++ if (!raw) return {};++ let parsed = null;++ try { parsed = JSON.parse(Buffer.from(String(raw), 'base64url').toString('utf8')); } catch { parsed = null; }++ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) throw new WatchError(400, 'watch.invalid_cursor', 'That cursor is not one this feed made.');++ const out = {};++ if (typeof parsed.clips === 'string' && parsed.clips) out.clips = parsed.clips;++ if (typeof parsed.moments === 'string' && parsed.moments) out.moments = parsed.moments;++ return out;++}++++function sourcesOf(topic) {++ if (topic === 'all') return [...SOURCE_NAMES];++ return [topic];++}++++function createWatchFeed({ config, fetchImpl = globalThis.fetch, log = console, now = () => Date.now() }) {++ const cache = new Map();++ const ttl = config.watchCacheMs == null ? 30_000 : config.watchCacheMs;++ const sourceTimeout = config.watchSourceTimeoutMs || 2000;++++ async function getJson(path) {++ let res;++ try {++ res = await fetchImpl(`${config.liveInternalUrl}${path}`, {++ headers: { Accept: 'application/json' },++ signal: AbortSignal.timeout(sourceTimeout),++ });++ } catch (err) {++ const timedOut = err && (err.name === 'TimeoutError' || err.name === 'AbortError');++ throw Object.assign(new Error(timedOut ? 'OpenVibe.Live timed out' : 'OpenVibe.Live did not answer'), { source: timedOut ? 'timeout' : 'error' });++ }++ if (!res.ok) throw Object.assign(new Error(`OpenVibe.Live answered ${res.status}`), { source: 'error' });++ let data = null;++ try { data = await res.json(); } catch { data = null; }++ if (!data || typeof data !== 'object') throw Object.assign(new Error('OpenVibe.Live answered without JSON'), { source: 'error' });++ return data;++ }++++ async function loadLive() {++ const data = await getJson('/api/streams');++ const streams = Array.isArray(data.streams) ? data.streams : [];++ const items = streams.filter((s) => s && typeof s.username === 'string' && s.username).map((s) => {++ const viewers = num(s.total_viewer_count != null ? s.total_viewer_count : (s.viewer_count != null ? s.viewer_count : s.viewers));++ return {++ key: `live:${s.username}`,++ kind: 'live',++ title: clean(s.title, 140) || 'Live now',++ channel: channelOf({ username: s.username, display_name: s.display_name, avatar_url: s.avatar_url }),++ thumbnail: mediaUrl(s.thumbnail_url || s.thumbnail),++ play: { type: 'live', src: null, page: livePage(config.livePublicUrl, `/@${encodeURIComponent(s.username)}`) },++ duration_seconds: numOrNull(s.duration_seconds),++ viewers,++ why: `Live now · ${viewers} watching`,++ created_at: s.started_at || null,++ };++ });++ return { items, next: null };++ }++++ async function loadContent(kind, path, query, cursor) {++ const sourcePath = cursor++ ? `${path}?${new URLSearchParams({ ...query, cursor }).toString()}`++ : `${path}?${new URLSearchParams(query).toString()}`;++ const data = await getJson(sourcePath);++ const rows = Array.isArray(data.items) ? data.items : [];++ const items = [];++ for (const it of rows) {++ if (!it || typeof it !== 'object') continue;++ const id = idOf(it, kind);++ const src = mediaUrl(it.preview_url);++ const page = livePage(config.livePublicUrl, it.href);++ if (!id || !src || !page) continue; // a card without a playable preview is not a Watch card++ items.push({++ key: `${kind}:${id}`,++ kind,++ title: clean(it.title, 140) || (kind === 'clip' ? 'Clip' : 'Moment'),++ channel: channelOf(it.channel),++ thumbnail: mediaUrl(it.thumbnail_url || it.thumbnail),++ play: { type: 'hls', src, page },++ duration_seconds: numOrNull(it.duration_seconds),++ viewers: null,++ why: kind === 'clip' ? 'New clip' : 'Top AI moment this week',++ created_at: it.created_at || null,++ });++ }++ return { items, next: typeof data.next === 'string' && data.next ? data.next : null };++ }++++ /**++ * One merged page. `cursor` is this feed's opaque cursor (it carries each Live source's cursor). A bad cursor++ * or topic is a WatchError; a failing source never is — it is left out and named.++ */++ async function feed(topic = DEFAULT_TOPIC, cursorRaw = null) {++ const name = String(topic || DEFAULT_TOPIC);++ if (!TOPICS.includes(name)) throw new WatchError(400, 'watch.invalid_topic', 'topic must be all, live, clips or moments.');++ const cursor = decodeCursor(cursorRaw);++ const key = `${name}|${cursorRaw || ''}`;++ const hit = cache.get(key);++ if (hit && now() - hit.at < ttl) return hit.value;++++ const wanted = sourcesOf(name);++ const includeLive = wanted.includes('live') && !(name === 'all' && cursorRaw);++ const jobs = [];++ const status = {};++ const value = { live: [], clips: [], moments: [] };++ const nexts = { clips: null, moments: null };++++ if (includeLive) jobs.push(loadLive().then((out) => { value.live = out.items; status.live = 'ok'; }, (err) => { status.live = (err && err.source) || 'error'; log.warn('[OpenVibe.Surf] watch live source failed:', (err && err.message) || err); }));++ else if (wanted.includes('live')) status.live = 'ok';++ if (wanted.includes('clips')) jobs.push(loadContent('clip', CLIP_PATH, { type: 'clips', sort: 'new', window: 'week' }, cursor.clips)++ .then((out) => { value.clips = out.items; nexts.clips = out.next; status.clips = 'ok'; }, (err) => { status.clips = (err && err.source) || 'error'; log.warn('[OpenVibe.Surf] watch clips source failed:', (err && err.message) || err); }));++ if (wanted.includes('moments')) jobs.push(loadContent('moment', MOMENT_PATH, { type: 'clips', sort: 'top', window: 'week' }, cursor.moments)++ .then((out) => { value.moments = out.items; nexts.moments = out.next; status.moments = 'ok'; }, (err) => { status.moments = (err && err.source) || 'error'; log.warn('[OpenVibe.Surf] watch moments source failed:', (err && err.message) || err); }));++++ await Promise.all(jobs);++++ const failed = Object.keys(status).filter((k) => status[k] !== 'ok');++ // Paging is per Live source; a failed source has no next page until it answers again.++ const nextParts = {};++ if (nexts.clips) nextParts.clips = nexts.clips;++ if (nexts.moments) nextParts.moments = nexts.moments;++ const result = {++ topic: name,++ items: rank(value, name).map(({ created_at, ...card }) => card),++ next: Object.keys(nextParts).length ? encodeCursor(nextParts) : null,++ sources: status,++ partial: failed.length > 0,++ };++ // A partial page is not kept: the next request should ask the missing source again. A complete page is++ // cached for the whole 30 s.++ if (!failed.length) {++ cache.set(key, { at: now(), value: result });++ if (cache.size > 100) cache.delete(cache.keys().next().value);++ }++ return result;++ }++++ /** Readiness: is Live answering at all? Any 2xx counts; 404 is a service that is not Live. */++ async function probe() {++ try {++ const res = await fetchImpl(`${config.liveInternalUrl}/api/streams`, {++ headers: { Accept: 'application/json' },++ signal: AbortSignal.timeout(sourceTimeout),++ });++ return res.ok ? true : `OpenVibe.Live answered ${res.status}`;++ } catch (err) {++ return err && (err.name === 'TimeoutError' || err.name === 'AbortError') ? 'OpenVibe.Live timed out' : 'OpenVibe.Live did not answer';++ }++ }++++ function clear() { cache.clear(); }++++ return { feed, probe, clear, get cached() { return cache; } };++}++++module.exports = {++ createWatchFeed, WatchError, TOPICS, SOURCE_NAMES,++ // Exported for the tests that pin the ordering rules.++ interleave, separateChannels, sortLive, sortFresh, decodeCursor, encodeCursor,++};nnew file mode 100644@@@ -0,0 +1,93 @@++'use strict';++++/**++ * Surf with Vibe: ask OpenVibe.Actor to browse the web on a private desk.++ *++ * A task is created with the person's OWN Network access token from the httpOnly `surf_at` cookie as a Bearer.++ * Actor accepts a person's session token at POST /api/v1/tasks (server/http/principal.js); the task then belongs++ * to them (their allowance, their task list), not to OpenVibe.Surf. The token is only ever put in the++ * Authorization header of the request to the configured Actor origin, and is never logged or rendered.++ *++ * The task text is a short standing instruction plus the person's query: browse with its tools (search_web,++ * read_page, a desk if a page needs it), keep its sources, and finish with a cited report. The request carries an++ * Idempotency-Key (and Actor's body `idempotency_key`: the same value), so a retried form does not start a second++ * task.++ *++ * Actor's 4xx answer (401, 403, 429 allowance/budget, 422) is a refusal the person can read: its status and detail++ * pass through. A timeout or a 5xx is this service's 502.++ */++const crypto = require('crypto');++++const MIN_QUERY = 3;++const MAX_QUERY = 500;++const IDEMPOTENCY = /^[A-Za-z0-9._:-]{8,128}$/;++const PREFIX = 'Surf the web for me with your tools. Use search_web to find pages, read_page to read them, and a desk when a page needs one. Keep every source you use and finish with a short report that cites each source.';++++function validQuery(value) {++ if (typeof value !== 'string') return false;++ const q = value.trim();++ return q.length >= MIN_QUERY && q.length <= MAX_QUERY;++}++++/** The instruction Actor receives: the standing browsing brief, then the person's words unchanged. */++function taskText(query) {++ return `${PREFIX}\n\n${String(query == null ? '' : query).trim()}`;++}++++function newIdempotencyKey() {++ return `surf:web:${crypto.randomBytes(16).toString('hex')}`;++}++++function createWeb({ config, fetchImpl = globalThis.fetch, log = console }) {++ /**++ * Create one task for the person whose token this is. Resolves { task_id, page } or throws++ * { status, code, detail, actor } — a 4xx from Actor keeps its status and detail.++ */++ async function createTask({ token, query, idempotencyKey = null } = {}) {++ if (!token) throw Object.assign(new Error('Sign in to surf the web.'), { status: 401, code: 'token.required' });++ if (!validQuery(query)) throw Object.assign(new Error('query must be 3 to 500 characters.'), { status: 400, code: 'web.invalid_query' });++ const key = IDEMPOTENCY.test(String(idempotencyKey || '')) ? String(idempotencyKey) : newIdempotencyKey();++ let res;++ try {++ res = await fetchImpl(`${config.actorInternalUrl}/api/v1/tasks`, {++ method: 'POST',++ headers: {++ Accept: 'application/json',++ 'Content-Type': 'application/json',++ Authorization: `Bearer ${token}`,++ 'Idempotency-Key': key,++ },++ body: JSON.stringify({ task: taskText(query), idempotency_key: key }),++ signal: AbortSignal.timeout(config.actorTimeoutMs || 15_000),++ });++ } catch (err) {++ const timedOut = err && (err.name === 'TimeoutError' || err.name === 'AbortError');++ log.warn('[OpenVibe.Surf] Actor did not answer:', timedOut ? 'timeout' : ((err && err.message) || err));++ throw Object.assign(new Error('OpenVibe.Actor did not answer. Try again in a moment.'), { status: 502, code: 'actor.unavailable' });++ }++ const text = await res.text().catch(() => '');++ let data = null;++ try { data = text ? JSON.parse(text) : null; } catch { data = null; }++ if (!res.ok) {++ // Actor's problem is written for the person: carry its status and words through, never its body verbatim++ // (it may echo request detail; the detail is what a person can act on).++ const detail = String((data && (data.detail || data.title || data.error)) || text || `OpenVibe.Actor answered ${res.status}`).slice(0, 400);++ throw Object.assign(new Error(detail), {++ status: res.status >= 400 && res.status < 500 ? res.status : 502,++ code: (data && data.code) || 'actor.refused',++ detail,++ actor: data,++ });++ }++ const id = data && typeof data.id === 'string' ? data.id : null;++ if (!id) {++ log.warn('[OpenVibe.Surf] Actor answered without a task id');++ throw Object.assign(new Error('OpenVibe.Actor answered without a task id.'), { status: 502, code: 'actor.invalid_response' });++ }++ return { task_id: id, page: `${String(config.actorPublicUrl).replace(/\/+$/, '')}/tasks/${encodeURIComponent(id)}` };++ }++++ return { createTask, validQuery };++}++++module.exports = { createWeb, taskText, validQuery, newIdempotencyKey, PREFIX, MIN_QUERY, MAX_QUERY };nnew file mode 100644@@@ -0,0 +1,44 @@++'use strict';++/** The crawl artifacts: robots.txt, sitemap.xml (the public pages and the Ride maps) and llms.txt. */++const assert = require('assert');++const { boot, check, done } = require('./helpers/boot');++++(async () => {++ const t = await boot();++++ await check('robots.txt names the sitemap and keeps crawlers out of the API, sign-in and the task form', async () => {++ const r = await t.get('/robots.txt');++ assert.strictEqual(r.status, 200);++ assert.match(r.headers.get('content-type'), /text\/plain/);++ assert.match(r.text, /Sitemap: https:\/\/openvibe\.surf\/sitemap\.xml/);++ assert.match(r.text, /Disallow: \/api\//);++ assert.match(r.text, /Disallow: \/auth\//);++ assert.match(r.text, /Disallow: \/web\/go/);++ });++++ await check('sitemap.xml lists the five public pages and the maps Games lists', async () => {++ const r = await t.get('/sitemap.xml');++ assert.strictEqual(r.status, 200);++ assert.match(r.headers.get('content-type'), /application\/xml/);++ for (const path of ['/', '/ride', '/watch', '/web', '/developers']) {++ assert.ok(r.text.includes(`<loc>https://openvibe.surf${path}</loc>`), `sitemap has ${path}`);++ }++ assert.match(r.text, /<loc>https:\/\/openvibe\.surf\/ride\/surf_lagoon<\/loc>/);++ assert.match(r.text, /<lastmod>2026-/);++ });++++ await check('llms.txt describes the room, its three surfs and the machine-readable routes', async () => {++ const r = await t.get('/llms.txt');++ assert.strictEqual(r.status, 200);++ assert.match(r.text, /# OpenVibe\.Surf/);++ assert.match(r.text, /Ride maps, surf the web, surf what's on/i);++ assert.match(r.text, /https:\/\/openvibe\.surf\/api\/v1\/watch\/feed\?topic=all/);++ assert.match(r.text, /https:\/\/openvibe\.surf\/api\/v1\/ride\/maps/);++ assert.match(r.text, /https:\/\/openvibe\.live/);++ assert.match(r.text, /https:\/\/openvibe\.games/);++ assert.match(r.text, /https:\/\/openvibe\.actor/);++ });++++ await t.close();++ done();++})().catch((err) => { console.error(err); process.exit(1); });nnew file mode 100644@@@ -0,0 +1,165 @@++'use strict';++/**++ * Watch: the merge and its order (live streams by viewers, moments and clips interleaved by freshness, never two++ * cards from one channel in a row), the one item shape, a failing source left out and named, and the 30 s cache.++ */++const assert = require('assert');++const { boot, check, done } = require('./helpers/boot');++++const kinds = (feed) => feed.items.map((i) => i.kind);++const channels = (feed) => feed.items.map((i) => i.channel.username);++++(async () => {++ const t = await boot();++++ await check('GET /api/v1/watch/feed merges the three sources into the one shape', async () => {++ t.ctx.feed.clear();++ const r = await t.get('/api/v1/watch/feed?topic=all');++ assert.strictEqual(r.status, 200, r.text);++ assert.strictEqual(r.headers.get('cache-control'), 'public, max-age=30');++ const b = r.json();++ assert.strictEqual(b.topic, 'all');++ assert.deepStrictEqual(b.sources, { live: 'ok', clips: 'ok', moments: 'ok' });++ assert.strictEqual(b.partial, false);++ assert.deepStrictEqual(kinds(b), ['live', 'live', 'live', 'moment', 'clip', 'clip'], 'lives first, then moments/clips interleaved by freshness');++ assert.deepStrictEqual(channels(b).slice(0, 3), ['bo', 'ada', 'cy'], 'live streams by viewers');++ for (const item of b.items) {++ assert.deepStrictEqual(Object.keys(item).sort(), ['channel', 'duration_seconds', 'key', 'kind', 'play', 'thumbnail', 'title', 'viewers', 'why'].sort());++ assert.deepStrictEqual(Object.keys(item.channel).sort(), ['avatar_url', 'display_name', 'username']);++ assert.deepStrictEqual(Object.keys(item.play).sort(), ['page', 'src', 'type']);++ assert.match(item.key, /^(live|clip|moment):/);++ assert.match(item.play.page, /^https:\/\/openvibe\.(live|video)\//, 'a page on Live or on openvibe.video');++ }++ const live = b.items[0];++ assert.strictEqual(live.key, 'live:bo');++ assert.strictEqual(live.play.type, 'live');++ assert.strictEqual(live.play.src, null);++ assert.strictEqual(live.play.page, 'https://openvibe.live/@bo');++ assert.strictEqual(live.viewers, 38);++ assert.strictEqual(live.why, 'Live now · 38 watching');++ const moment = b.items.find((i) => i.kind === 'moment');++ assert.strictEqual(moment.play.type, 'hls');++ assert.strictEqual(moment.play.src, 'https://openvibe.media/c/201/master.m3u8');++ assert.strictEqual(moment.play.page, 'https://openvibe.video/c/201', 'clips and moments open on openvibe.video');++ assert.strictEqual(moment.why, 'Top AI moment this week');++ const clip = b.items.find((i) => i.kind === 'clip');++ assert.strictEqual(clip.why, 'New clip');++ assert.strictEqual(clip.duration_seconds, 31);++ });++++ await check('moments and clips alternate by freshness, and no channel runs twice', async () => {++ t.ctx.feed.clear();++ t.upstreams.state.streams = [++ { username: 'bo', display_name: 'Bo', title: 'Bo live', viewer_count: 38, total_viewer_count: 38 },++ { username: 'ada', display_name: 'Ada', title: 'Ada live', viewer_count: 12, total_viewer_count: 12 },++ ];++ t.upstreams.state.moments = [++ { id: 1, href: '/clip/1', title: 'Moment one', created_at: '2026-10-28T10:00:00.000Z', channel: { username: 'mira' }, preview_url: 'https://openvibe.media/c/1/master.m3u8', thumbnail_url: 'https://openvibe.media/c/1.jpg' },++ { id: 2, href: '/clip/2', title: 'Moment two', created_at: '2026-10-28T08:00:00.000Z', channel: { username: 'noa' }, preview_url: 'https://openvibe.media/c/2/master.m3u8', thumbnail_url: 'https://openvibe.media/c/2.jpg' },++ ];++ t.upstreams.state.clips = [++ { id: 11, href: '/clip/11', title: 'Clip one', created_at: '2026-10-28T09:00:00.000Z', channel: { username: 'ada' }, preview_url: 'https://openvibe.media/c/11/master.m3u8' },++ { id: 12, href: '/clip/12', title: 'Clip two', created_at: '2026-10-28T07:00:00.000Z', channel: { username: 'bo' }, preview_url: 'https://openvibe.media/c/12/master.m3u8' },++ ];++ const b = (await t.get('/api/v1/watch/feed?topic=all')).json();++ assert.deepStrictEqual(kinds(b), ['live', 'live', 'moment', 'clip', 'moment', 'clip']);++ assert.deepStrictEqual(channels(b), ['bo', 'ada', 'mira', 'ada', 'noa', 'bo']);++ const names = channels(b);++ for (let i = 1; i < names.length; i++) assert.notStrictEqual(names[i], names[i - 1], `no channel twice in a row at ${i}`);++ });++++ await check('a card from the same channel as the previous one is moved down', async () => {++ t.ctx.feed.clear();++ t.upstreams.state.streams = [{ username: 'cy', display_name: 'Cy', title: 'Cy live', viewer_count: 5, total_viewer_count: 5 }];++ t.upstreams.state.moments = [{ id: 21, href: '/clip/21', title: 'Cy moment', created_at: '2026-10-28T10:00:00.000Z', channel: { username: 'cy' }, preview_url: 'https://openvibe.media/c/21/master.m3u8' }];++ t.upstreams.state.clips = [++ { id: 31, href: '/clip/31', title: 'Ada clip', created_at: '2026-10-28T09:00:00.000Z', channel: { username: 'ada' }, preview_url: 'https://openvibe.media/c/31/master.m3u8' },++ { id: 32, href: '/clip/32', title: 'Bo clip', created_at: '2026-10-28T08:00:00.000Z', channel: { username: 'bo' }, preview_url: 'https://openvibe.media/c/32/master.m3u8' },++ ];++ const b = (await t.get('/api/v1/watch/feed?topic=all')).json();++ assert.strictEqual(b.items.length, 4);++ assert.strictEqual(b.items[0].channel.username, 'cy', 'the live card stays first');++ assert.notStrictEqual(b.items[1].channel.username, 'cy', 'the next card is another channel');++ const names = channels(b);++ for (let i = 1; i < names.length; i++) assert.notStrictEqual(names[i], names[i - 1], `no channel twice in a row at ${i}`);++ });++++ await check('a failing source is left out and named in sources', async () => {++ t.ctx.feed.clear();++ t.upstreams.state.fail.moments = 500;++ const r = await t.get('/api/v1/watch/feed?topic=all');++ t.upstreams.state.fail.moments = null;++ assert.strictEqual(r.status, 200, 'the feed still answers');++ const b = r.json();++ assert.ok(!b.items.some((i) => i.kind === 'moment'), 'no moment cards');++ assert.ok(b.items.some((i) => i.kind === 'clip'), 'clips still there');++ assert.strictEqual(b.sources.moments, 'error');++ assert.strictEqual(b.sources.clips, 'ok');++ assert.strictEqual(b.partial, true);++ const recovered = await t.get('/api/v1/watch/feed?topic=all');++ assert.strictEqual(recovered.json().sources.moments, 'ok', 'a partial page is not cached: the next request retries');++ assert.ok(recovered.json().items.some((i) => i.kind === 'moment'));++ });++++ await check('a slow source times out after its 2 s budget (500 ms in this test) and is named', async () => {++ t.ctx.feed.clear();++ t.upstreams.state.delay.streams = 900; // boot sets the source budget to 500 ms++ const started = Date.now();++ const r = await t.get('/api/v1/watch/feed?topic=live');++ t.upstreams.state.delay.streams = 0;++ assert.strictEqual(r.status, 200);++ assert.strictEqual(r.json().sources.live, 'timeout');++ assert.ok(Date.now() - started < 1500, 'it did not wait for the slow source');++ assert.deepStrictEqual(r.json().items, []);++ });++++ await check('the merged feed is cached for 30 s', async () => {++ t.ctx.feed.clear();++ t.upstreams.resetCounts();++ await t.get('/api/v1/watch/feed?topic=all');++ const once = { ...t.upstreams.state.counts };++ assert.strictEqual(once.streams, 1);++ assert.strictEqual(once.clips, 1);++ assert.strictEqual(once.moments, 1);++ await t.get('/api/v1/watch/feed?topic=all');++ assert.deepStrictEqual({ ...t.upstreams.state.counts }, once, 'the second read is the cached page');++ t.clock.advance(30_001);++ await t.get('/api/v1/watch/feed?topic=all');++ assert.strictEqual(t.upstreams.state.counts.streams, once.streams + 1, 'after 30 s it asks again');++ });++++ await check('topic filters only fetch what they need, and a cursor pages the Live feeds', async () => {++ t.ctx.feed.clear();++ t.upstreams.resetCounts();++ const live = await t.get('/api/v1/watch/feed?topic=live');++ assert.deepStrictEqual(kinds(live.json()), ['live']);++ assert.strictEqual(t.upstreams.state.counts.clips, 0);++ assert.strictEqual(t.upstreams.state.counts.moments, 0);++++ t.ctx.feed.clear();++ t.upstreams.state.clipsNext = 'clip-page-2';++ t.upstreams.state.momentsNext = 'moment-page-2';++ const first = (await t.get('/api/v1/watch/feed?topic=all')).json();++ assert.ok(first.next, 'a next cursor when either source has more');++ t.upstreams.resetCounts();++ const second = await t.get(`/api/v1/watch/feed?topic=all&cursor=${encodeURIComponent(first.next)}`);++ assert.strictEqual(second.status, 200, second.text);++ assert.strictEqual(t.upstreams.state.counts.streams, 0, 'live streams are not repeated on page two');++ const clips = t.upstreams.lastFor('/api/content/feed');++ const moments = t.upstreams.lastFor('/api/content/moments');++ assert.strictEqual(clips.query.cursor, 'clip-page-2');++ assert.strictEqual(moments.query.cursor, 'moment-page-2');++ });++++ await check('a bad topic or cursor is a 400 problem', async () => {++ const badTopic = await t.get('/api/v1/watch/feed?topic=nope');++ assert.strictEqual(badTopic.status, 400);++ assert.strictEqual(badTopic.json().code, 'watch.invalid_topic');++ const badCursor = await t.get('/api/v1/watch/feed?topic=all&cursor=not-a-cursor');++ assert.strictEqual(badCursor.status, 400);++ assert.strictEqual(badCursor.json().code, 'watch.invalid_cursor');++ });++++ await t.close();++ done();++})().catch((err) => { console.error(err); process.exit(1); });nnew file mode 100644@@@ -0,0 +1,106 @@++'use strict';++/**++ * Boots OpenVibe.Surf on a random port against an in-process Network (test/helpers/network.js) and in-process++ * Live/Games/Actor stand-ins (test/helpers/upstreams.js), then returns a small HTTP client. Every test file gets++ * its own process, servers and app.++ *++ * const t = await boot();++ * t.get(path, { as: user, cookie, json, form, headers, method })++ * t.signIn(user) → cookie header value (surf_at = a Network user token, as after /auth/callback)++ * t.clock.advance(31_000) → move the feed/ride cache clock++ * t.logs(), t.close()++ */++const http = require('http');++const { startNetwork } = require('./network');++const { startUpstreams } = require('./upstreams');++++async function boot(opts = {}) {++ const network = await startNetwork();++ const upstreams = await startUpstreams();++ const env = {++ NODE_ENV: 'test', PORT: '0', BASE_URL: 'https://openvibe.surf', TRUST_PROXY: '1',++ OV_NETWORK_URL: network.url, OV_NETWORK_INTERNAL_URL: network.url, OV_NETWORK_ISSUER: network.url,++ OV_OAUTH_CLIENT_ID: 'surf', OV_OAUTH_CLIENT_SECRET: 'surf-secret', COOKIE_SECURE: 'false',++ SURF_RATE_LIMIT_PER_MIN: '100000',++ OV_LIVE_INTERNAL_URL: upstreams.url,++ OV_GAMES_INTERNAL_URL: upstreams.url,++ OV_ACTOR_INTERNAL_URL: upstreams.url,++ SURF_GAMES_PUBLIC_URL: 'https://openvibe.games',++ SURF_LIVE_PUBLIC_URL: 'https://openvibe.live',++ SURF_ACTOR_PUBLIC_URL: 'https://openvibe.actor',++ SURF_WATCH_SOURCE_TIMEOUT_MS: '500',++ SURF_GAMES_TIMEOUT_MS: '1000',++ SURF_ACTOR_TIMEOUT_MS: '1000',++ ...(opts.env || {}),++ };++ for (const [k, v] of Object.entries(opts.env || {})) if (v === null) delete env[k];++++ const clock = {++ t: Date.now(),++ now() { return this.t; },++ advance(ms) { this.t += ms; },++ };++++ const logLines = [];++ const log = {++ log: (...a) => logLines.push(a.map(String).join(' ')),++ info: (...a) => logLines.push(a.map(String).join(' ')),++ warn: (...a) => logLines.push(a.map(String).join(' ')),++ error: (...a) => logLines.push(a.map(String).join(' ')),++ };++++ const configLib = require('../../server/config');++ const { createApp } = require('../../server/app');++ const config = configLib.load(env);++ const built = createApp({ config, log, now: () => clock.t });++ await built.ctx.keys.ensure();++ const server = await new Promise((resolve) => { const s = http.createServer(built.app); s.listen(0, '127.0.0.1', () => resolve(s)); });++ const base = `http://127.0.0.1:${server.address().port}`;++++ const signIn = (user) => `surf_at=${network.userToken(user)}`;++++ async function get(p, o = {}) {++ const headers = { ...(o.headers || {}) };++ if (o.as) headers.cookie = [signIn(o.as), headers.cookie].filter(Boolean).join('; ');++ if (o.cookie) headers.cookie = o.cookie;++ let body = o.body;++ if (o.json !== undefined) { body = JSON.stringify(o.json); headers['content-type'] = 'application/json'; }++ if (o.form !== undefined) { body = new URLSearchParams(o.form).toString(); headers['content-type'] = 'application/x-www-form-urlencoded'; }++ const res = await fetch(base + p, { method: o.method || (body ? 'POST' : 'GET'), headers, body, redirect: 'manual' });++ const text = Buffer.from(await res.arrayBuffer()).toString('utf8');++ return {++ status: res.status,++ headers: res.headers,++ text,++ json() { return JSON.parse(text); },++ };++ }++++ return {++ base, network, upstreams, clock, config, ctx: built.ctx, get, signIn,++ logs: () => logLines.join('\n'),++ async close() {++ await new Promise((r) => server.close(r));++ built.ctx.keys.client.stop();++ await network.close();++ await upstreams.close();++ },++ };++}++++let failures = 0;++async function check(name, fn) {++ try {++ await fn();++ process.stdout.write(` ✓ ${name}\n`);++ } catch (e) {++ failures++;++ process.stdout.write(` ✗ ${name}\n ${(e.stack || String(e)).split('\n').slice(0, 10).join('\n ')}\n`);++ }++}++function done() {++ process.stdout.write(failures ? `\n${failures} failed\n` : '\nall passed\n');++ process.exit(failures ? 1 : 0);++}++++module.exports = { boot, check, done };nnew file mode 100644@@@ -0,0 +1,110 @@++'use strict';++/**++ * An in-process stand-in for OpenVibe.Network: a real RS256 key pair and JWKS, /oauth/token with PKCE S256,++ * /oauth/revoke and an /api/health route. Every request is recorded so a test can assert what was (and was not)++ * called. setDown(true) makes every answer a 503.++ */++const http = require('http');++const crypto = require('crypto');++++const b64 = (o) => Buffer.from(JSON.stringify(o)).toString('base64url');++function signJwt(claims, privateKey) {++ const input = `${b64({ alg: 'RS256', typ: 'JWT' })}.${b64(claims)}`;++ return `${input}.${crypto.sign('RSA-SHA256', Buffer.from(input), privateKey).toString('base64url')}`;++}++++function listen(handler) {++ return new Promise((resolve) => {++ const sockets = new Set();++ const server = http.createServer((req, res) => {++ const chunks = [];++ req.on('data', (c) => chunks.push(c));++ req.on('end', () => {++ const raw = Buffer.concat(chunks);++ const json = (status, obj, headers = {}) => {++ res.writeHead(status, { 'Content-Type': 'application/json', ...headers });++ res.end(JSON.stringify(obj));++ };++ Promise.resolve(handler(req, raw, json, res)).catch((err) => json(500, { error: String(err && err.message) }));++ });++ });++ server.on('connection', (s) => { sockets.add(s); s.on('close', () => sockets.delete(s)); });++ server.listen(0, '127.0.0.1', () => resolve({++ server,++ url: `http://127.0.0.1:${server.address().port}`,++ close: () => new Promise((r) => { for (const s of sockets) s.destroy(); server.close(r); }),++ }));++ });++}++++async function startNetwork() {++ const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', { modulusLength: 2048 });++ const publicPem = publicKey.export({ type: 'spki', format: 'pem' });++ const privatePem = privateKey.export({ type: 'pkcs8', format: 'pem' });++ const st = {++ users: new Map(), codes: new Map(), refresh: new Map(),++ down: false, requests: [], tokenRequests: [],++ };++ let issuer = null;++ let nextUserId = 1;++++ function addUser(username, { role = 'user' } = {}) {++ const u = { id: nextUserId++, subject: `usr_${crypto.randomBytes(12).toString('hex')}`, username, display_name: username, role };++ st.users.set(u.id, u);++ return u;++ }++ function userToken(u, { ttl = 3600 } = {}) {++ const now = Math.floor(Date.now() / 1000);++ return signJwt({ iss: issuer, sub: u.id, subject_id: u.subject, username: u.username, display_name: u.display_name, role: u.role, aud: ['openvibe.network'], iat: now, exp: now + ttl }, privatePem);++ }++++ const srv = await listen(async (req, raw, json) => {++ const url = new URL(req.url, 'http://x');++ const text = raw.toString('utf8');++ let body = {};++ try { body = text ? JSON.parse(text) : {}; } catch { body = {}; }++ st.requests.push({ method: req.method, path: url.pathname, authorization: req.headers.authorization || null, body: text });++ if (st.down) return json(503, { error: 'Network is restarting' });++++ if (url.pathname === '/api/health') return json(200, { status: 'ok', service: 'openvibe-network' });++ if (url.pathname === '/api/.well-known/jwks') return json(200, { public_key: publicPem, algorithm: 'RS256' });++ if (url.pathname === '/oauth/revoke') return json(200, {});++ if (url.pathname === '/oauth/token' && req.method === 'POST') {++ const form = Object.fromEntries(new URLSearchParams(text));++ st.tokenRequests.push(form);++ if (form.client_id !== 'surf' || form.client_secret !== 'surf-secret') return json(401, { error: 'invalid_client', error_description: 'bad client secret' });++ if (form.grant_type === 'authorization_code') {++ const c = st.codes.get(form.code);++ if (!c) return json(400, { error: 'invalid_grant', error_description: 'unknown code' });++ st.codes.delete(form.code);++ const challenge = crypto.createHash('sha256').update(String(form.code_verifier || '')).digest('base64url');++ if (challenge !== c.challenge) return json(400, { error: 'invalid_grant', error_description: 'PKCE verification failed' });++ const rt = `rt_${crypto.randomBytes(12).toString('hex')}`;++ st.refresh.set(rt, c.user);++ return json(200, { access_token: userToken(c.user), refresh_token: rt, token_type: 'Bearer' });++ }++ if (form.grant_type === 'refresh_token') {++ const u = st.refresh.get(form.refresh_token);++ if (!u) return json(400, { error: 'invalid_grant', error_description: 'refresh token rejected' });++ st.refresh.delete(form.refresh_token);++ const rt = `rt_${crypto.randomBytes(12).toString('hex')}`;++ st.refresh.set(rt, u);++ return json(200, { access_token: userToken(u), refresh_token: rt, token_type: 'Bearer' });++ }++ return json(400, { error: 'unsupported_grant_type' });++ }++ return json(404, { error: 'not found' });++ });++ issuer = srv.url;++++ return {++ url: srv.url, publicPem, privatePem, state: st,++ requests: st.requests, tokenRequests: st.tokenRequests,++ addUser, userToken,++ issueCode(user, challenge) { const code = `code_${crypto.randomBytes(8).toString('hex')}`; st.codes.set(code, { user, challenge }); return code; },++ setDown(v) { st.down = Boolean(v); },++ close: srv.close,++ };++}++++module.exports = { startNetwork, signJwt };nnew file mode 100644@@@ -0,0 +1,143 @@++'use strict';++/**++ * In-process stand-ins for OpenVibe.Live, OpenVibe.Games and OpenVibe.Actor, on one HTTP server. The boot helper++ * points OV_LIVE_INTERNAL_URL, OV_GAMES_INTERNAL_URL and OV_ACTOR_INTERNAL_URL at it.++ *++ * A test changes state.fail.<source> (an HTTP status) or state.delay.<source> (ms), state.gamesNotFound, or++ * state.actor.status, and reads state.requests / state.counts to see what the app called.++ */++const http = require('http');++++const DEFAULT_STREAMS = [++ { id: 1, username: 'ada', display_name: 'Ada', title: 'Night drive through the city', viewer_count: 12, total_viewer_count: 12, thumbnail_url: 'https://openvibe.media/live/ada.jpg', started_at: '2026-10-28T10:00:00.000Z' },++ { id: 2, username: 'bo', display_name: 'Bo', title: 'Surf practice, all day', viewer_count: 38, total_viewer_count: 38, thumbnail_url: 'https://openvibe.media/live/bo.jpg', started_at: '2026-10-28T09:00:00.000Z' },++ { id: 3, username: 'cy', display_name: 'Cy', title: 'Chat picks the route', viewer_count: 5, total_viewer_count: 5, thumbnail_url: 'https://openvibe.media/live/cy.jpg', started_at: '2026-10-28T08:00:00.000Z' },++];++const DEFAULT_CLIPS = [++ { key: 'clip:101', id: 101, href: '/clip/101', title: 'Kickflip over the pier', created_at: '2026-10-28T09:00:00.000Z', channel: { username: 'ada', display_name: 'Ada', avatar_url: null }, thumbnail_url: 'https://openvibe.media/clips/101.jpg', preview_url: 'https://openvibe.media/c/101/master.m3u8', duration_seconds: 31, ai_label: null },++ { key: 'clip:102', id: 102, href: '/clip/102', title: 'The long way down', created_at: '2026-10-28T07:00:00.000Z', channel: { username: 'bo', display_name: 'Bo', avatar_url: null }, thumbnail_url: 'https://openvibe.media/clips/102.jpg', preview_url: 'https://openvibe.media/c/102/master.m3u8', duration_seconds: 44, ai_label: null },++];++const DEFAULT_MOMENTS = [++ { key: 'clip:201', id: 201, href: 'https://openvibe.video/c/201', title: 'The wave that ate the camera', created_at: '2026-10-28T10:00:00.000Z', channel: { username: 'mira', display_name: 'Mira', avatar_url: null }, thumbnail_url: 'https://openvibe.media/clips/201.jpg', preview_url: 'https://openvibe.media/c/201/master.m3u8', duration_seconds: 18, ai_label: 'Auto-clip' },++];++const DEFAULT_MAPS = [++ { id: 'surf_lagoon', name: 'Surf Lagoon', tier: 1, stages: 3, author: 'OpenVibe', records: 5, wrMs: 61234, wrHolder: 'ada' },++ { id: 'surf_canyon', name: 'Surf Canyon', tier: 2, stages: 4, author: 'OpenVibe', records: 2, wrMs: 73456, wrHolder: 'bo' },++ { id: 'surf_orbit', name: 'Surf Orbit', tier: 4, stages: 6, author: 'OpenVibe', records: 0, wrMs: null, wrHolder: null },++];++const DEFAULT_TOP = {++ surf_lagoon: [{ rank: 1, name: 'ada', timeMs: 61234, runId: null }, { rank: 2, name: 'bo', timeMs: 62345, runId: null }],++ surf_canyon: [{ rank: 1, name: 'bo', timeMs: 73456, runId: null }],++ surf_orbit: [],++};++++function listen(handler) {++ return new Promise((resolve) => {++ const sockets = new Set();++ const server = http.createServer((req, res) => {++ const chunks = [];++ req.on('data', (c) => chunks.push(c));++ req.on('end', () => {++ const raw = Buffer.concat(chunks).toString('utf8');++ const json = (status, obj, headers = {}) => {++ res.writeHead(status, { 'Content-Type': 'application/json', ...headers });++ res.end(JSON.stringify(obj));++ };++ Promise.resolve(handler(req, raw, json, res)).catch((err) => json(500, { error: String(err && err.message) }));++ });++ });++ server.on('connection', (s) => { sockets.add(s); s.on('close', () => sockets.delete(s)); });++ server.listen(0, '127.0.0.1', () => resolve({++ server,++ url: `http://127.0.0.1:${server.address().port}`,++ close: () => new Promise((r) => { for (const s of sockets) s.destroy(); server.close(r); }),++ }));++ });++}++++async function startUpstreams() {++ const state = {++ streams: DEFAULT_STREAMS.map((s) => ({ ...s })),++ clips: DEFAULT_CLIPS.map((s) => ({ ...s })),++ moments: DEFAULT_MOMENTS.map((s) => ({ ...s })),++ clipsNext: null,++ momentsNext: null,++ maps: DEFAULT_MAPS.map((m) => ({ ...m })),++ top: JSON.parse(JSON.stringify(DEFAULT_TOP)),++ gamesNotFound: false,++ gamesStatus: null,++ fail: { streams: null, clips: null, moments: null },++ delay: { streams: 0, clips: 0, moments: 0 },++ actor: { status: 201, id: 'tsk_01J8SURFEXAMPLE00000000', problem: null },++ requests: [],++ counts: { streams: 0, clips: 0, moments: 0, maps: 0, map: 0, actor: 0 },++ };++++ const srv = await listen(async (req, raw, json) => {++ const url = new URL(req.url, 'http://x');++ const p = url.pathname;++ const entry = { method: req.method, path: p, query: Object.fromEntries(url.searchParams), headers: { ...req.headers }, body: raw };++ state.requests.push(entry);++++ const fail = (name) => {++ if (!state.fail[name]) return false;++ json(state.fail[name], { error: 'upstream failed' });++ return true;++ };++++ if (p === '/api/streams') {++ state.counts.streams++;++ if (state.delay.streams) await new Promise((r) => setTimeout(r, state.delay.streams));++ if (fail('streams')) return;++ return json(200, { streams: state.streams });++ }++ if (p === '/api/content/feed') {++ state.counts.clips++;++ if (state.delay.clips) await new Promise((r) => setTimeout(r, state.delay.clips));++ if (fail('clips')) return;++ return json(200, { feed: 'content', type: 'clips', sort: 'new', window: 'week', items: state.clips, next: state.clipsNext, sources: { clips: 'ok' }, partial: false });++ }++ if (p === '/api/content/moments') {++ state.counts.moments++;++ if (state.delay.moments) await new Promise((r) => setTimeout(r, state.delay.moments));++ if (fail('moments')) return;++ return json(200, { feed: 'moments', type: 'clips', sort: 'top', window: 'week', items: state.moments, next: state.momentsNext, sources: { aiclips: 'ok' }, partial: false });++ }++ if (p === '/api/surf/maps') {++ state.counts.maps++;++ if (state.gamesStatus) return json(state.gamesStatus, { error: 'games failed' });++ if (state.gamesNotFound) return json(404, { error: 'not_found' });++ return json(200, { maps: state.maps });++ }++ const one = /^\/api\/surf\/maps\/([^/]+)$/.exec(p);++ if (one) {++ state.counts.map++;++ if (state.gamesStatus) return json(state.gamesStatus, { error: 'games failed' });++ if (state.gamesNotFound) return json(404, { error: 'not_found' });++ const id = decodeURIComponent(one[1]);++ const map = state.maps.find((m) => m.id === id);++ if (!map) return json(404, { error: 'unknown_map' });++ return json(200, { map, top: state.top[id] || [] });++ }++ if (p === '/api/v1/agents' && req.method === 'GET') return json(200, { agents: [] });++ if (p === '/api/v1/tasks' && req.method === 'POST') {++ state.counts.actor++;++ if (state.actor.status >= 400) {++ return json(state.actor.status, state.actor.problem || { type: 'about:blank', title: 'Refused', status: state.actor.status, code: 'actor.refused', detail: 'OpenVibe.Actor refused this task.' });++ }++ return json(state.actor.status, { id: state.actor.id, state: 'queued', task: 'the task' });++ }++ return json(404, { error: 'not found' });++ });++++ return {++ url: srv.url,++ state,++ close: srv.close,++ resetCounts() { for (const k of Object.keys(state.counts)) state.counts[k] = 0; },++ requestsFor(path) { return state.requests.filter((r) => r.path === path); },++ lastFor(path) { return state.requests.filter((r) => r.path === path).pop() || null; },++ };++}++++module.exports = { startUpstreams, DEFAULT_STREAMS, DEFAULT_CLIPS, DEFAULT_MOMENTS, DEFAULT_MAPS, DEFAULT_TOP };nnew file mode 100644@@@ -0,0 +1,168 @@++'use strict';++/**++ * The pages and the plumbing: /, /ride, /ride/:map, /watch, /web, /developers, the 404; public vs signed-in cache++ * policy; the CSP the embeds need; health and truthful readiness; the release manifest and content-hashed assets.++ */++const assert = require('assert');++const { boot, check, done } = require('./helpers/boot');++const { assetVersion } = require('../server/assets');++++(async () => {++ const t = await boot();++ const user = t.network.addUser('kim');++++ await check('the home page has the three tiles and is publicly cacheable', async () => {++ const r = await t.get('/');++ assert.strictEqual(r.status, 200);++ assert.match(r.text, /id="sf-tile-ride"/);++ assert.match(r.text, /id="sf-tile-watch"/);++ assert.match(r.text, /id="sf-tile-web"/);++ assert.match(r.text, /href="\/ride"/);++ assert.match(r.text, /href="\/watch"/);++ assert.match(r.text, /href="\/web"/);++ assert.match(r.text, /<main id="main"/);++ assert.strictEqual(r.headers.get('cache-control'), 'public, max-age=60');++ assert.match(r.headers.get('vary'), /Cookie/i);++ });++++ await check('/ride lists the maps and embeds the first', async () => {++ const r = await t.get('/ride');++ assert.strictEqual(r.status, 200, r.text);++ assert.match(r.text, /id="sf-maps"/);++ assert.match(r.text, /href="\/ride\/surf_lagoon"/);++ assert.match(r.text, /Surf Lagoon/);++ assert.match(r.text, /id="sf-embed"/);++ assert.match(r.text, /src="https:\/\/openvibe\.games\/play\/surf\?map=surf_lagoon&embed=1"/);++ assert.strictEqual(r.headers.get('cache-control'), 'public, max-age=60');++ });++++ await check('/ride/:map shows the name, tier, stages, records and the embed', async () => {++ const r = await t.get('/ride/surf_lagoon');++ assert.strictEqual(r.status, 200, r.text);++ assert.match(r.text, /Surf Lagoon/);++ assert.match(r.text, /Beginner/);++ assert.match(r.text, /3 stages/);++ assert.match(r.text, /5 runs/);++ assert.match(r.text, /1:01\.234/);++ assert.match(r.text, /id="sf-top"/);++ assert.match(r.text, /ada/);++ assert.match(r.text, /src="https:\/\/openvibe\.games\/play\/surf\?map=surf_lagoon&embed=1"/);++ });++++ await check('/ride/:map still embeds when Games has not added its routes yet', async () => {++ t.ctx.ride.clear();++ t.upstreams.state.gamesNotFound = true;++ const r = await t.get('/ride/surf_lagoon');++ t.upstreams.state.gamesNotFound = false;++ t.ctx.ride.clear();++ assert.strictEqual(r.status, 200, r.text);++ assert.match(r.text, /id="sf-ride-not-ready"/);++ assert.match(r.text, /src="https:\/\/openvibe\.games\/play\/surf\?map=surf_lagoon&embed=1"/);++ });++++ await check('/watch is the feed as a list with thumbnails, play links and HLS hooks', async () => {++ t.ctx.feed.clear();++ const r = await t.get('/watch');++ assert.strictEqual(r.status, 200, r.text);++ assert.match(r.text, /id="sf-watch-feed"/);++ assert.match(r.text, /class="sf-ch is-on"[^>]*data-sf-kind="live"/, 'the guide, live first and on');++ assert.match(r.text, /data-sf-tv/, 'the TV');++ assert.match(r.text, /https:\/\/openvibe\.media\/live\/bo\.jpg/);++ assert.match(r.text, /href="https:\/\/openvibe\.live\/@bo"/);++ assert.match(r.text, /data-sf-play-type="hls"/);++ assert.match(r.text, /data-sf-src="https:\/\/openvibe\.media\/c\/201\/master\.m3u8"/);++ assert.match(r.text, /Live now · 38 watching/);++ assert.match(r.text, /Top AI moment this week/);++ assert.match(r.text, /New clip/);++ assert.match(r.text, /id="sf-topics"/);++ assert.strictEqual(r.headers.get('cache-control'), 'public, max-age=60');++ });++++ await check('/watch?topic=live only lists live streams', async () => {++ t.ctx.feed.clear();++ const r = await t.get('/watch?topic=live');++ assert.strictEqual(r.status, 200);++ assert.match(r.text, /data-sf-kind="live"/);++ assert.ok(!r.text.includes('data-sf-kind="clip"'), 'no clips on the live topic');++ assert.ok(!r.text.includes('data-sf-kind="moment"'), 'no moments on the live topic');++ });++++ await check('/web is the Surf with Vibe form and what it does', async () => {++ const r = await t.get('/web');++ assert.strictEqual(r.status, 200, r.text);++ assert.match(r.text, /id="sf-web-form"/);++ assert.match(r.text, /action="\/web\/go"/);++ assert.match(r.text, /id="sf-web-query"/);++ assert.match(r.text, /minlength="3"/);++ assert.match(r.text, /maxlength="500"/);++ assert.match(r.text, /method="post"/, 'the plain form posts: the query is never in a URL');++ assert.match(r.text, /asked to sign in/);++ assert.match(r.text, /Search Fabric/);++ assert.match(r.text, /private desk/);++ assert.strictEqual(r.headers.get('cache-control'), 'public, max-age=60');++ const me = await t.get('/web', { as: user });++ assert.strictEqual(me.status, 200);++ assert.match(me.text, /Runs as <b>kim<\/b>/);++ assert.strictEqual(me.headers.get('cache-control'), 'private, no-store', 'a signed-in page is never cached');++ });++++ await check('/developers and the 404', async () => {++ const dev = await t.get('/developers');++ assert.strictEqual(dev.status, 200);++ assert.match(dev.text, /GET \/api\/v1\/watch\/feed/);++ assert.match(dev.text, /POST \/api\/v1\/web\/tasks/);++ const gone = await t.get('/not-a-page');++ assert.strictEqual(gone.status, 404);++ assert.match(gone.text, /id="sf-not-found"/);++ assert.match(gone.text, /Wiped out/);++ });++++ await check('the CSP allows the Games embed, a Live player and the media edge', async () => {++ const r = await t.get('/');++ const csp = r.headers.get('content-security-policy') || '';++ assert.match(csp, /frame-src 'self' https:\/\/openvibe\.games https:\/\/openvibe\.live/);++ assert.match(csp, /media-src 'self' https:\/\/openvibe\.media https:\/\/edge\.openvibe\.media https:\/\/\*\.r2\.cloudflarestorage\.com https:\/\/\*\.backblazeb2\.com blob:/);++ assert.match(csp, /connect-src [^;]*https:\/\/openvibe\.media/, 'hls.js fetches Media\'s playlists and segments');++ assert.match(csp, /connect-src 'self' https:\/\/openvibe\.network https:\/\/cloudflareinsights\.com https:\/\/openvibe\.events/);++ });++++ await check('health and readiness are honest; a Network outage is degraded, not down', async () => {++ const health = await t.get('/api/health');++ assert.strictEqual(health.status, 200);++ assert.strictEqual(health.json().status, 'ok');++ assert.strictEqual(health.json().service, 'openvibe-surf');++ assert.strictEqual(health.json().version, '0.1.0');++ const ready = await t.get('/api/ready');++ assert.strictEqual(ready.status, 200, ready.text);++ assert.strictEqual(ready.json().ready, true);++ assert.strictEqual(ready.json().checks.network.status, 'ok');++ t.network.setDown(true);++ const degraded = await t.get('/api/ready');++ t.network.setDown(false);++ assert.strictEqual(degraded.status, 200, 'the process still serves');++ assert.strictEqual(degraded.json().ready, true);++ assert.strictEqual(degraded.json().status, 'degraded');++ assert.ok(degraded.json().degraded.includes('network'));++ });++++ await check('the release manifest and content-hashed static assets', async () => {++ const rel = await t.get('/release.json');++ assert.strictEqual(rel.status, 200);++ assert.strictEqual(rel.json().service, 'surf');++ assert.match(rel.headers.get('content-type'), /application\/json/);++ const css = await t.get('/css/surf.css');++ assert.strictEqual(css.status, 200);++ assert.match(css.text, /OpenVibe\.Surf: the room's own pieces/);++ const hashed = await t.get(`/css/surf.css?v=${assetVersion('css/surf.css')}`);++ assert.strictEqual(hashed.status, 200);++ assert.strictEqual(hashed.headers.get('cache-control'), 'public, max-age=31536000, immutable');++ const js = await t.get(`/js/surf.js?v=${assetVersion('js/surf.js')}`);++ assert.strictEqual(js.status, 200);++ assert.match(js.headers.get('content-type'), /javascript/);++ const shared = await t.get('/shared/room.css');++ assert.strictEqual(shared.status, 200);++ });++++ await t.close();++ done();++})().catch((err) => { console.error(err); process.exit(1); });nnew file mode 100644@@@ -0,0 +1,94 @@++'use strict';++/**++ * Ride: the Games proxy (the maps list and one map), its 60 s cache, Games 404 → an empty list with++ * games: 'not_ready', a Games failure → 502, and the map's embed URL.++ */++const assert = require('assert');++const { boot, check, done } = require('./helpers/boot');++++(async () => {++ const t = await boot();++++ await check('GET /api/v1/ride/maps proxies Games and adds each map’s embed URL', async () => {++ t.ctx.ride.clear();++ t.upstreams.resetCounts();++ const r = await t.get('/api/v1/ride/maps');++ assert.strictEqual(r.status, 200, r.text);++ assert.strictEqual(r.headers.get('cache-control'), 'public, max-age=60');++ const b = r.json();++ assert.strictEqual(b.games, 'ready');++ assert.strictEqual(b.maps.length, 3);++ assert.strictEqual(b.maps[0].id, 'surf_lagoon');++ assert.strictEqual(b.maps[0].name, 'Surf Lagoon');++ assert.strictEqual(b.maps[0].tier, 1);++ assert.strictEqual(b.maps[0].stages, 3);++ assert.strictEqual(b.maps[0].embed_url, 'https://openvibe.games/play/surf?map=surf_lagoon&embed=1');++ assert.strictEqual(t.upstreams.state.counts.maps, 1);++ });++++ await check('the list is cached for 60 s', async () => {++ t.upstreams.resetCounts();++ await t.get('/api/v1/ride/maps');++ assert.strictEqual(t.upstreams.state.counts.maps, 0, 'the second read is the cached list');++ t.clock.advance(60_001);++ await t.get('/api/v1/ride/maps');++ assert.strictEqual(t.upstreams.state.counts.maps, 1, 'after 60 s it asks Games again');++ });++++ await check('GET /api/v1/ride/maps/:id returns the map, its records and the embed', async () => {++ t.ctx.ride.clear();++ t.upstreams.resetCounts();++ const r = await t.get('/api/v1/ride/maps/surf_lagoon');++ assert.strictEqual(r.status, 200, r.text);++ assert.strictEqual(r.headers.get('cache-control'), 'public, max-age=60');++ const b = r.json();++ assert.strictEqual(b.games, 'ready');++ assert.strictEqual(b.map.id, 'surf_lagoon');++ assert.strictEqual(b.map.records, 5);++ assert.strictEqual(b.map.wrMs, 61234);++ assert.strictEqual(b.map.embed_url, 'https://openvibe.games/play/surf?map=surf_lagoon&embed=1');++ assert.deepStrictEqual(b.top[0], { rank: 1, name: 'ada', timeMs: 61234, runId: null });++ const again = await t.get('/api/v1/ride/maps/surf_lagoon');++ assert.strictEqual(again.status, 200);++ assert.strictEqual(t.upstreams.state.counts.map, 1, 'the map is cached too');++ });++++ await check('Games 404 means not_ready: an empty list and a map shell that still embeds', async () => {++ t.ctx.ride.clear();++ t.upstreams.state.gamesNotFound = true;++ const list = await t.get('/api/v1/ride/maps');++ assert.strictEqual(list.status, 200, list.text);++ assert.deepStrictEqual(list.json(), { maps: [], games: 'not_ready' });++ assert.strictEqual(list.headers.get('cache-control'), 'public, max-age=60');++ const one = await t.get('/api/v1/ride/maps/surf_lagoon');++ assert.strictEqual(one.status, 200, one.text);++ const b = one.json();++ assert.strictEqual(b.games, 'not_ready');++ assert.strictEqual(b.map.id, 'surf_lagoon');++ assert.strictEqual(b.map.embed_url, 'https://openvibe.games/play/surf?map=surf_lagoon&embed=1');++ assert.deepStrictEqual(b.top, []);++ t.upstreams.state.gamesNotFound = false;++ });++++ await check('a Games failure is a 502 problem, and a failure is never cached', async () => {++ t.ctx.ride.clear();++ t.upstreams.state.gamesStatus = 503;++ const r = await t.get('/api/v1/ride/maps');++ assert.strictEqual(r.status, 502, r.text);++ assert.match(r.headers.get('content-type'), /application\/problem\+json/);++ assert.strictEqual(r.json().code, 'games.unavailable');++ t.upstreams.state.gamesStatus = null;++ const ok = await t.get('/api/v1/ride/maps');++ assert.strictEqual(ok.status, 200, 'the next request asks again');++ assert.strictEqual(ok.json().games, 'ready');++ });++++ await check('a blank map id is a 400', async () => {++ const r = await t.get('/api/v1/ride/maps/%20');++ assert.strictEqual(r.status, 400);++ assert.strictEqual(r.json().code, 'ride.invalid_map');++ });++++ await t.close();++ done();++})().catch((err) => { console.error(err); process.exit(1); });nnew file mode 100644@@@ -0,0 +1,20 @@++#!/usr/bin/env node++/**++ * Runs every test/*.test.js in its own process and fails if any fails. Each file boots OpenVibe.Surf against++ * in-process Network/Live/Games/Actor stand-ins (test/helpers/); none needs the internet or a running site.++ *++ * npm test # everything++ * npm test -- watch # only files whose name contains one of the words++ * npm test -- --strict # a skipped test fails the run too++ */++'use strict';++const { run } = require('openvibe-shared/test-runner');++++// VERBOSE=1 also prints every file's output, then the summary again.++run({ dir: __dirname, timeoutMs: 60_000, pad: 34, parallel: 1 }).then((r) => {++ if (process.env.VERBOSE) {++ for (const t of r.results) console.log(`\n── ${t.file} ──\n${t.output.trimEnd()}`);++ console.log(`\n${r.summary}`);++ }++ process.exit(r.exitCode);++}, (err) => { console.error(err); process.exit(1); });nnew file mode 100644@@@ -0,0 +1,128 @@++'use strict';++/**++ * Sign-in: sanitizeNext (open-redirect regressions), the login redirect (PKCE S256, state, client id, redirect++ * URI), the callback (state mismatch refused, httpOnly cookies), /auth/me signed out and in, and sign-out.++ */++const assert = require('assert');++const { boot, check, done } = require('./helpers/boot');++const { sanitizeNext } = require('../server/auth/sso');++++const config = { baseUrl: 'https://openvibe.surf' };++const cookies = (r) => (typeof r.headers.getSetCookie === 'function' ? r.headers.getSetCookie() : [r.headers.get('set-cookie') || '']);++const cookieNamed = (r, name) => {++ for (const c of cookies(r)) {++ const m = new RegExp(`(?:^|[,\\s])${name}=([^;]+)`).exec(c);++ if (m) return `${name}=${m[1]}`;++ }++ return null;++};++++(async () => {++ // ── sanitizeNext: same-site paths only ──++ for (const v of ['//evil.com', '/\\evil.com', '\\\\evil.com', 'https://evil.com', 'http:evil.com', '/\t/evil.com',++ '/\n/evil.com', '\t//evil.com', ' //evil.com', '/\\/evil.com', 'https:/\\evil.com', '///evil.com',++ 'javascript:alert(1)', null, '', 12]) {++ assert.strictEqual(sanitizeNext(v, config), '/', `${JSON.stringify(v)} goes home`);++ }++ assert.strictEqual(sanitizeNext('/watch?topic=live', config), '/watch?topic=live');++ assert.strictEqual(sanitizeNext('https://openvibe.surf/ride/surf_lagoon?x=1#c', config), '/ride/surf_lagoon?x=1#c');++ for (const v of ['https://evil.com/a', 'https://openvibe.surf.evil.com/a', 'https://[email protected]/', 'http://openvibe.surf/a']) {++ assert.strictEqual(sanitizeNext(v, config), '/', v);++ }++++ const t = await boot();++ const user = t.network.addUser('kim');++++ await check('login redirects to Network with state, client id surf and an S256 challenge', async () => {++ const r = await t.get('/auth/login?next=/web');++ assert.strictEqual(r.status, 302);++ const loc = new URL(r.headers.get('location'));++ assert.strictEqual(loc.origin + loc.pathname, `${t.network.url}/oauth/authorize`);++ assert.strictEqual(loc.searchParams.get('client_id'), 'surf');++ assert.strictEqual(loc.searchParams.get('response_type'), 'code');++ assert.strictEqual(loc.searchParams.get('redirect_uri'), 'https://openvibe.surf/auth/callback');++ assert.strictEqual(loc.searchParams.get('scope'), 'profile');++ assert.strictEqual(loc.searchParams.get('code_challenge_method'), 'S256');++ assert.match(loc.searchParams.get('code_challenge'), /^[A-Za-z0-9_-]{43}$/);++ assert.match(loc.searchParams.get('state'), /^[0-9a-f]{32}$/);++ const flow = cookieNamed(r, 'surf_oauth');++ assert.ok(flow, 'the flow cookie is set');++ assert.match(cookies(r).find((c) => c.includes('surf_oauth=')), /Path=\/auth.*HttpOnly/i);++ const payload = JSON.parse(decodeURIComponent(flow.split('=')[1]));++ assert.strictEqual(payload.next, '/web');++ assert.strictEqual(payload.silent, false);++ });++++ await check('silent sign-in adds prompt=none and next= only keeps this site', async () => {++ const r = await t.get('/auth/login?silent=1&next=' + encodeURIComponent('https://evil.example/x'));++ const loc = new URL(r.headers.get('location'));++ assert.strictEqual(loc.searchParams.get('prompt'), 'none');++ const flow = JSON.parse(decodeURIComponent(cookieNamed(r, 'surf_oauth').split('=')[1]));++ assert.strictEqual(flow.next, '/', 'another origin goes home');++ const own = await t.get('/auth/login?next=' + encodeURIComponent('https://openvibe.surf/ride/surf_lagoon?x=1'));++ const ownFlow = JSON.parse(decodeURIComponent(cookieNamed(own, 'surf_oauth').split('=')[1]));++ assert.strictEqual(ownFlow.next, '/ride/surf_lagoon?x=1');++ });++++ await check('the callback exchanges the code and sets httpOnly surf_at/surf_rt cookies', async () => {++ const login = await t.get('/auth/login?next=/web');++ const loc = new URL(login.headers.get('location'));++ const flowCookie = cookieNamed(login, 'surf_oauth');++ const code = t.network.issueCode(user, loc.searchParams.get('code_challenge'));++ const r = await t.get(`/auth/callback?code=${code}&state=${loc.searchParams.get('state')}`, { cookie: flowCookie });++ assert.strictEqual(r.status, 302, r.text);++ assert.strictEqual(r.headers.get('location'), '/web');++ const all = cookies(r).join('\n');++ assert.match(all, /surf_at=[^;]+;.*HttpOnly/i);++ assert.match(all, /surf_rt=[^;]+;.*HttpOnly/i);++ assert.ok(!/SameSite=None/i.test(all), 'SameSite=Lax, not None');++ const at = cookieNamed(r, 'surf_at');++ assert.ok(at, 'the access cookie is set');++ const me = await t.get('/auth/me', { cookie: at });++ assert.strictEqual(me.status, 200);++ assert.strictEqual(me.json().user.username, 'kim');++ assert.ok(!me.text.includes(at.split('=')[1]), '/auth/me never returns the token');++ const tokenRequest = t.network.tokenRequests[t.network.tokenRequests.length - 1];++ assert.strictEqual(tokenRequest.client_id, 'surf');++ assert.strictEqual(tokenRequest.grant_type, 'authorization_code');++ assert.strictEqual(tokenRequest.redirect_uri, 'https://openvibe.surf/auth/callback');++ assert.ok(tokenRequest.code_verifier, 'the verifier is sent server-side');++ });++++ await check('a wrong state or no flow cookie is refused', async () => {++ const login = await t.get('/auth/login');++ const loc = new URL(login.headers.get('location'));++ const flowCookie = cookieNamed(login, 'surf_oauth');++ const code = t.network.issueCode(user, loc.searchParams.get('code_challenge'));++ const bad = await t.get(`/auth/callback?code=${code}&state=${'0'.repeat(32)}`, { cookie: flowCookie });++ assert.strictEqual(bad.status, 400);++ const noCookie = await t.get(`/auth/callback?code=${code}&state=${loc.searchParams.get('state')}`);++ assert.strictEqual(noCookie.status, 400);++ const none = await t.get('/auth/callback?state=x');++ assert.strictEqual(none.status, 400);++ });++++ await check('/auth/me is null for a guest and 401 for a cookie that does not verify', async () => {++ const guest = await t.get('/auth/me');++ assert.strictEqual(guest.status, 200);++ assert.deepStrictEqual(guest.json(), { user: null });++ const bad = await t.get('/auth/me', { cookie: 'surf_at=not-a-token' });++ assert.strictEqual(bad.status, 401);++ assert.strictEqual(bad.json().error, 'Not signed in');++ });++++ await check('sign-out clears the cookies and returns to next', async () => {++ const r = await t.get('/auth/logout?next=%2Fwatch', { headers: { 'sec-fetch-site': 'same-origin' } });++ assert.strictEqual(r.status, 303);++ assert.strictEqual(r.headers.get('location'), '/watch');++ assert.match(cookies(r).join('\n'), /surf_at=;/);++ });++++ await check('the token is never logged', async () => {++ const token = t.network.userToken(user);++ assert.ok(!t.logs().includes(token), 'no access token in the logs');++ });++++ await t.close();++ done();++})().catch((err) => { console.error(err); process.exit(1); });nnew file mode 100644@@@ -0,0 +1,139 @@++'use strict';++/**++ * Web: signed out 401, cross-site 403, the Actor call carries the person's Bearer and an Idempotency-Key and the++ * prefixed browsing brief, Actor's refusal passes through in words, and the plain form (POST /web/go, this site only) 303s to the Actor task page.++ */++const assert = require('assert');++const { boot, check, done } = require('./helpers/boot');++const { PREFIX, taskText } = require('../server/web/tasks');++++const ORIGIN = 'https://openvibe.surf';++++(async () => {++ const t = await boot();++ const user = t.network.addUser('kim');++ const token = t.network.userToken(user);++ const signedIn = `surf_at=${token}`;++++ await check('POST /api/v1/web/tasks is a 401 problem when signed out', async () => {++ const r = await t.get('/api/v1/web/tasks', { method: 'POST', json: { query: 'who runs the mail for openvibe.network' }, headers: { origin: ORIGIN } });++ assert.strictEqual(r.status, 401, r.text);++ assert.match(r.headers.get('content-type'), /application\/problem\+json/);++ assert.strictEqual(r.json().code, 'token.required');++ assert.strictEqual(r.headers.get('cache-control'), 'private, no-store');++ });++++ await check('a cross-site Origin is 403 and never reaches Actor', async () => {++ t.upstreams.resetCounts();++ const r = await t.get('/api/v1/web/tasks', {++ cookie: signedIn, method: 'POST', json: { query: 'who runs the mail for openvibe.network' }, headers: { origin: 'https://evil.example' },++ });++ assert.strictEqual(r.status, 403, r.text);++ assert.strictEqual(r.json().code, 'web.cross_site');++ assert.strictEqual(r.headers.get('cache-control'), 'private, no-store');++ assert.strictEqual(t.upstreams.state.counts.actor, 0);++ const missing = await t.get('/api/v1/web/tasks', { cookie: signedIn, method: 'POST', json: { query: 'who runs the mail for openvibe.network' } });++ assert.strictEqual(missing.status, 403, 'no Origin is not this site');++ });++++ await check('the task carries the person’s Bearer, an Idempotency-Key and the prefixed browsing brief', async () => {++ t.upstreams.resetCounts();++ const query = 'who runs the mail for openvibe.network and is SPF set up?';++ const r = await t.get('/api/v1/web/tasks', {++ cookie: signedIn, method: 'POST', json: { query }, headers: { origin: ORIGIN },++ });++ assert.strictEqual(r.status, 201, r.text);++ const b = r.json();++ assert.strictEqual(b.task_id, 'tsk_01J8SURFEXAMPLE00000000');++ assert.strictEqual(b.page, 'https://openvibe.actor/tasks/tsk_01J8SURFEXAMPLE00000000');++ assert.strictEqual(r.headers.get('cache-control'), 'private, no-store');++ assert.strictEqual(t.upstreams.state.counts.actor, 1);++++ const sent = t.upstreams.lastFor('/api/v1/tasks');++ assert.strictEqual(sent.headers.authorization, `Bearer ${token}`);++ assert.match(sent.headers['idempotency-key'] || '', /^surf:web:[0-9a-f]{32}$/);++ const body = JSON.parse(sent.body);++ assert.strictEqual(body.task, taskText(query));++ assert.ok(body.task.startsWith(PREFIX), 'the standing brief comes first');++ assert.ok(body.task.endsWith(query), 'the person’s words are unchanged at the end');++ assert.match(body.task, /search_web/);++ assert.match(body.task, /read_page/);++ assert.match(body.task, /desk/);++ assert.match(body.task, /cites each source/);++ assert.strictEqual(body.idempotency_key, sent.headers['idempotency-key']);++ assert.ok(!t.logs().includes(token), 'the person’s token is never logged');++ assert.ok(!r.text.includes(token), 'the token is never echoed');++ });++++ await check('a caller-supplied Idempotency-Key is passed through when it is valid', async () => {++ const r = await t.get('/api/v1/web/tasks', {++ cookie: signedIn, method: 'POST', json: { query: 'find a good surf forecast' }, headers: { origin: ORIGIN, 'idempotency-key': 'surf:web:caller-key-123' },++ });++ assert.strictEqual(r.status, 201, r.text);++ const sent = t.upstreams.lastFor('/api/v1/tasks');++ assert.strictEqual(sent.headers['idempotency-key'], 'surf:web:caller-key-123');++ assert.strictEqual(JSON.parse(sent.body).idempotency_key, 'surf:web:caller-key-123');++ });++++ await check('a short, long or missing query is a 400 problem', async () => {++ for (const query of ['ab', 'x'.repeat(501), '', null]) {++ const r = await t.get('/api/v1/web/tasks', { cookie: signedIn, method: 'POST', json: { query }, headers: { origin: ORIGIN } });++ assert.strictEqual(r.status, 400, String(query));++ assert.strictEqual(r.json().code, 'web.invalid_query');++ }++ });++++ await check('Actor’s refusal passes its status and words through', async () => {++ t.upstreams.state.actor.status = 429;++ t.upstreams.state.actor.problem = {++ type: 'about:blank', title: 'Too many tasks', status: 429, code: 'actor.allowance.exhausted',++ detail: 'The free tier runs 5 tasks a day; it resets at midnight UTC.',++ };++ const r = await t.get('/api/v1/web/tasks', { cookie: signedIn, method: 'POST', json: { query: 'surf the latest AI news' }, headers: { origin: ORIGIN } });++ t.upstreams.state.actor.status = 201;++ t.upstreams.state.actor.problem = null;++ assert.strictEqual(r.status, 429, r.text);++ const b = r.json();++ assert.strictEqual(b.code, 'actor.allowance.exhausted');++ assert.match(b.detail, /free tier runs 5 tasks a day/);++ assert.strictEqual(b.status, 429);++ });++++ const own = { origin: 'https://openvibe.surf', 'sec-fetch-site': 'same-origin' };++ await check('POST /web/go (the plain form) creates the task and 303s to the Actor task page; the query is never in a URL', async () => {++ t.upstreams.resetCounts();++ const r = await t.get('/web/go', { cookie: signedIn, form: { q: 'find the best surf spots in Portugal' }, headers: own });++ assert.strictEqual(r.status, 303, r.text);++ assert.strictEqual(r.headers.get('location'), 'https://openvibe.actor/tasks/tsk_01J8SURFEXAMPLE00000000');++ assert.strictEqual(r.headers.get('cache-control'), 'private, no-store');++ const sent = t.upstreams.lastFor('/api/v1/tasks');++ assert.strictEqual(sent.headers.authorization, `Bearer ${token}`);++ assert.ok(JSON.parse(sent.body).task.includes('find the best surf spots in Portugal'));++ const get = await t.get('/web/go?q=find%20the%20best%20surf%20spots', { cookie: signedIn });++ assert.deepStrictEqual([get.status, get.headers.get('location')], [303, '/web'], 'a GET starts nothing: it goes back to the form');++ });++++ await check('POST /web/go sends a guest to sign in, refuses a bad query and another site', async () => {++ const guest = await t.get('/web/go', { form: { q: 'find the best surf spots' }, headers: own });++ assert.strictEqual(guest.status, 303);++ assert.strictEqual(guest.headers.get('location'), '/auth/login?next=%2Fweb');++ const bad = await t.get('/web/go', { cookie: signedIn, form: { q: 'ab' }, headers: own });++ assert.strictEqual(bad.status, 400);++ assert.match(bad.text, /id="sf-web-form"/, 'the form again, with what was typed');++ const cross = await t.get('/web/go', { cookie: signedIn, form: { q: 'find the best surf spots' }, headers: { origin: 'https://evil.example', 'sec-fetch-site': 'cross-site' } });++ assert.strictEqual(cross.status, 403);++ });++++ await check('POST /web/go passes Actor’s refusal through in words', async () => {++ t.upstreams.state.actor.status = 429;++ t.upstreams.state.actor.problem = { code: 'actor.allowance.exhausted', detail: 'The free tier runs 5 tasks a day; it resets at midnight UTC.' };++ const r = await t.get('/web/go', { cookie: signedIn, form: { q: 'find the best surf spots' }, headers: own });++ t.upstreams.state.actor.status = 201;++ t.upstreams.state.actor.problem = null;++ assert.strictEqual(r.status, 429);++ assert.match(r.text, /free tier runs 5 tasks a day/);++ });++++ await t.close();++ done();++})().catch((err) => { console.error(err); process.exit(1); });
new file mode 100644
new file mode 100644
@@ -0,0 +1,38 @@
@@ -0,0 +1,38 @@
+# OpenVibe.Surf — environment (production: /etc/openvibe/surf.env). Names only; never commit values.
+
+NODE_ENV=production
+PORT=5110
+HOST=127.0.0.1
+BASE_URL=https://openvibe.surf
+TRUST_PROXY=2
+
+# OpenVibe.Network: sign-in (OAuth client `surf`, PKCE S256) and its JWKS.
+OV_NETWORK_INTERNAL_URL=http://127.0.0.1:4000
+OV_OAUTH_CLIENT_ID=surf
+OV_OAUTH_CLIENT_SECRET=
+# The browser is sent to the public origin; the token exchange and the JWKS use the internal one above.
+# OV_NETWORK_URL=https://openvibe.network
+# OV_NETWORK_ISSUER=https://openvibe.network
+# OV_OAUTH_REDIRECT_URI=https://openvibe.surf/auth/callback
+# OV_SESSION_AUDIENCE=openvibe.network
+
+# The three rooms this front joins. Internal origins are what this process calls; public origins are what a person's
+# browser and the page links use.
+OV_LIVE_INTERNAL_URL=http://127.0.0.1:3000
+OV_GAMES_INTERNAL_URL=http://127.0.0.1:8000
+OV_ACTOR_INTERNAL_URL=http://127.0.0.1:4950
+SURF_GAMES_PUBLIC_URL=https://openvibe.games
+# SURF_LIVE_PUBLIC_URL=https://openvibe.live
+# SURF_ACTOR_PUBLIC_URL=https://openvibe.actor
+
+# Optional: the Watch merge (30 s cache, 2 s per Live source), the Ride cache (60 s) and Games timeout, the Actor
+# timeout, the per-IP limit.
+# SURF_WATCH_CACHE_MS=30000
+# SURF_WATCH_SOURCE_TIMEOUT_MS=2000
+# SURF_RIDE_CACHE_MS=60000
+# SURF_GAMES_TIMEOUT_MS=4000
+# SURF_ACTOR_TIMEOUT_MS=15000
+# SURF_RATE_LIMIT_PER_MIN=120
+
+# optional: defaults to true in production
+# COOKIE_SECURE=true
new file mode 100644
new file mode 100644
@@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
+name: ci
+on:
+ push:
+ branches: [main]
+ pull_request:
+jobs:
+ # install, node --check and npm test: the shared test job (Track Q).
+ test:
+ uses: OpenVibers/OpenVibe.Shared/.github/workflows/test.yml@423fe519b5fe9594cc81b670e5ba5cb9f21f6176
+ with:
+ syntax-dirs: server test
+ # No database, no Valkey, no Events: every test runs against in-process Network/Live/Games/Actor stand-ins.
+ test: OV_ALLOW_SUITE=1 npm test
+ # Secret scan (gitleaks) and dependency audit, shared by every OpenVibers repo (Track Q).
+ security:
+ uses: OpenVibers/OpenVibe.Shared/.github/workflows/security.yml@423fe519b5fe9594cc81b670e5ba5cb9f21f6176
new file mode 100644
new file mode 100644
@@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
+node_modules/
+data/
+dist/
+.env
+.env.*
+!.env.example
+*.log
+*.pem
+*.key
+.npm-cache/
+.ref/
+.npm-cache
new file mode 100644
new file mode 100644
@@ -0,0 +1,41 @@
@@ -0,0 +1,41 @@
+# Changelog
+
+What changed in OpenVibe.Surf, newest first. Each site also publishes its patch notes at /updates.
+
+## 0.1.0 — 2026-10-28
+
+The service skeleton: three surfs, one room.
+
+- **Sign-in with OpenVibe.Network** (`server/auth/sso.js`, OAuth client `surf`): authorization code with PKCE
+ S256, a state check, a server-side code exchange, httpOnly `surf_at` and `surf_rt` cookies (SameSite=Lax,
+ Secure in production), the `surf_oauth` flow cookie, and `sanitizeNext` (this site's own origin only, no
+ control characters or backslashes). `/auth/login`, `/auth/callback`, `/auth/logout`, `/auth/me`.
+- **Watch** (`server/watch/feed.js`, `GET /api/v1/watch/feed`): OpenVibe.Live's live streams
+ (`GET /api/streams`), clips (`GET /api/content/feed?type=clips&sort=new&window=week`) and AI moments
+ (`GET /api/content/moments?type=clips&sort=top&window=week`) merged into one item shape. Live streams first by
+ viewers, then moments and clips interleaved by freshness, never two cards from the same channel in a row. The
+ merged feed is cached 30 s; each Live source gets 2 s and a failing one is left out and named in `sources`. The
+ topic filter, an opaque per-source cursor and `public, max-age=30` are included.
+- **Ride** (`server/ride/games.js`, `GET /api/v1/ride/maps`, `/api/v1/ride/maps/:id`): a proxy to OpenVibe.Games'
+ `/api/surf/maps` routes, cached 60 s, each map carrying its
+ `https://openvibe.games/play/surf?map=<id>&embed=1` embed URL. While Games' new routes answer 404 the list is
+ empty and `games` is `'not_ready'`; a Games failure is a 502 and is never cached.
+- **Web** (`server/web/tasks.js`, `POST /api/v1/web/tasks`, `GET /web/go`): Surf with Vibe. A signed-in,
+ same-origin request creates one OpenVibe.Actor task with the person's own Network token from `surf_at` as a
+ Bearer, an `Idempotency-Key`, and a short browsing brief (search_web, read_page, a desk when a page needs one,
+ keep sources, finish with a cited report). Answers `{ task_id, page }`; Actor's 4xx refusal keeps its status
+ and words. `/web/go` is the same task as a no-JavaScript form that 303s to the Actor task page.
+- **Pages** (`server/http/pages.js`, `server/views/index.js`): `/`, `/ride`, `/ride/:map`, `/watch`, `/web`,
+ `/developers` and a 404, all server-rendered through the OpenVibe Frame and complete without JavaScript, with
+ stable `sf-` ids and classes. Public pages are `public, max-age=60` with `Vary: Cookie`; a page rendered for a
+ signed-in person is `private, no-store`. `public/css/surf.css` and `public/js/surf.js` are one-comment
+ placeholders for the main session.
+- **Plumbing** (`server/app.js`): helmet with the network's CSP (the Games embed, a possible Live player, the
+ media edge for HLS), content-hashed `/css` and `/js`, `/api/health`, `/api/ready` (degraded, never 503, when an
+ optional upstream is down), `/release.json`, `/release-metrics`, `/shared/*`, `/robots.txt`, `/sitemap.xml` and
+ `/llms.txt`, a 120/min per-IP limit on `/api` and `/auth`, and a graceful stop.
+- **Deploy:** `deploy/systemd/openvibe-surf.service` (port 5110, `/etc/openvibe/surf.env`) and
+ `deploy/nginx/openvibe.surf.conf` (TLS for openvibe.surf, www → apex, the Watch/Ride read zone, the forms zone
+ for `/web/go` and `/api/v1/web/`, `/auth/me` on the API zone, `/internal` and `/metrics` 404).
+- **Tests:** `test/feed.test.js`, `test/ride.test.js`, `test/web.test.js`, `test/sso.test.js`,
+ `test/pages.test.js` and `test/discovery.test.js`, each against in-process Network/Live/Games/Actor stand-ins.
new file mode 100644
new file mode 100644
@@ -0,0 +1,661 @@
@@ -0,0 +1,661 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<https://www.gnu.org/licenses/>.
new file mode 100644
new file mode 100644
@@ -0,0 +1,221 @@
@@ -0,0 +1,221 @@
+# OpenVibe.Surf
+
+> Ride maps, surf the web, surf what's on.
+
+**Status:** v1, the three-surf front (v0.1.0, see [STATUS.json](STATUS.json)).
+**Domain:** `openvibe.surf` · **Port:** 5110 · **Service id:** `surf` · **Env prefix:** `SURF`
+**License:** AGPL-3.0-only (the same as every OpenVibe service).
+
+OpenVibe.Surf is the Surf room. One room, three surfs:
+
+- **Ride** — CS-style surf in the browser, served by OpenVibe.Games. The map catalogue is proxied from Games and
+ every map plays in an embed (`https://openvibe.games/play/surf?map=<id>&embed=1`).
+- **Watch** — lean-back channel surfing over OpenVibe.Live: live streams, clips people cut and the AI moments of
+ the week, merged into one feed with one card shape.
+- **Web** — *Surf with Vibe*: tell an agent what you are looking for and it browses the web for you on a private
+ desk through OpenVibe.Actor, keeps its sources and finishes with a cited report.
+
+## What it is
+
+- **Identity:** service id `surf`, name OpenVibe.Surf, port 5110, host 127.0.0.1. It stores no accounts, no
+ database and no tokens: the person's Network session lives in httpOnly cookies.
+- **Sign-in:** an OAuth 2 client of OpenVibe.Network (`surf`), authorization code with PKCE S256, a state check
+ and a server-side code exchange.
+- **Stateless:** no database, no Valkey, no Events. The only state is a 30 s Watch feed cache, a 60 s Ride cache
+ and the SDK's JWKS cache, all in memory.
+
+## Watch
+
+`GET /api/v1/watch/feed?topic=all|live|clips|moments&cursor=`
+
+Merges three public OpenVibe.Live reads (`OV_LIVE_INTERNAL_URL`, default `http://127.0.0.1:3000`):
+
+| Source | Live route | What it gives |
+|---|---|---|
+| live | `GET /api/streams` | `{ streams: [...] }` with `username`, `display_name`, `avatar_url`, `title`, `viewer_count`, `thumbnail_url` |
+| clips | `GET /api/content/feed?type=clips&sort=new&window=week` | `items` with `href`, `thumbnail_url`, `preview_url`, `title`, `channel`, `duration_seconds` |
+| moments | `GET /api/content/moments?type=clips&sort=top&window=week` | the same shape, AI-cut clips |
+
+Every card is one shape:
+
+```json
+{
+ "key": "live:bo",
+ "kind": "live",
+ "title": "Surf practice, all day",
+ "channel": { "username": "bo", "display_name": "Bo", "avatar_url": null },
+ "thumbnail": "https://openvibe.media/live/bo.jpg",
+ "play": { "type": "live", "src": null, "page": "https://openvibe.live/@bo" },
+ "duration_seconds": null,
+ "viewers": 38,
+ "why": "Live now · 38 watching"
+}
+```
+
+- A clip or moment has `play.type: "hls"`, `play.src` = its `preview_url` (an HLS master playlist on
+ `openvibe.media`: Media 302s `/c/<id>` to `master.m3u8`) and `play.page` = its Live page. A live stream has
+ `play.type: "live"`, `src: null` and `page` = `https://openvibe.live/@<username>` (Live has no embeddable
+ player yet).
+- **Ranking:** live streams first, by viewers; then moments and clips interleaved by freshness. Finally, no two
+ cards from the same channel sit next to each other when another channel is available.
+- **Cache:** a complete merged page for a topic/cursor is cached 30 s (`SURF_WATCH_CACHE_MS`); a partial page is
+ not kept, so the next request retries the missing source. Each Live source gets 2 s
+ (`SURF_WATCH_SOURCE_TIMEOUT_MS`); a source that fails or times out is left out, named in `sources`
+ (`{ live: "ok", clips: "ok", moments: "error" }`) and `partial` is true.
+- **Cursor:** opaque; it carries each Live feed's own `next` cursor. Page two does not repeat live streams.
+- **Cache-Control:** `public, max-age=30`.
+
+## Ride
+
+`GET /api/v1/ride/maps` and `GET /api/v1/ride/maps/:id`
+
+Proxy to OpenVibe.Games (`OV_GAMES_INTERNAL_URL`, default `http://127.0.0.1:8000`), cached 60 s
+(`SURF_RIDE_CACHE_MS`). The routes `/api/surf/maps` and `/api/surf/maps/:id` are being added to Games now. Until
+they answer, Games gives 404: the list is `{ "maps": [], "games": "not_ready" }` and a map detail is a shell that
+still carries the embed (`{ "map": { "id": … }, "top": [], "games": "not_ready" }`). A Games timeout or 5xx is a
+`502 games.unavailable` problem and is never cached.
+
+Each map carries `embed_url`:
+`https://openvibe.games/play/surf?map=<id>&embed=1` (`SURF_GAMES_PUBLIC_URL`).
+
+## Web — Surf with Vibe
+
+`POST /api/v1/web/tasks` (signed in; this site's own `Origin`) with `{ "query": "…" }` (3–500 characters).
+
+The person's own Network access token from the httpOnly `surf_at` cookie is sent to OpenVibe.Actor
+(`OV_ACTOR_INTERNAL_URL`, default `http://127.0.0.1:4950`) as a Bearer on `POST /api/v1/tasks`. Actor accepts a
+person's session token there; the task is theirs — their allowance and their task list — not OpenVibe.Surf's.
+
+The task text is a short standing brief plus the query unchanged:
+
+> Surf the web for me with your tools. Use search_web to find pages, read_page to read them, and a desk when a
+> page needs one. Keep every source you use and finish with a short report that cites each source.
+
+The request carries an `Idempotency-Key` header (and Actor's body `idempotency_key`, the same value), so a retried
+form does not start a second task. The answer is `{ "task_id": "tsk_…", "page": "https://openvibe.actor/tasks/tsk_…" }`
+(201, `Location` set). Actor's 4xx refusal (401, 403, 429 allowance/budget, 422) keeps its status and its words;
+an Actor that does not answer is a `502 actor.unavailable`.
+
+`GET /web/go?q=…` is the same task as a plain form for no-JavaScript: it creates the task and **303**s to the
+Actor task page. A guest is sent to sign in; a bad query is a 400; an Actor refusal keeps its status and prints
+its words. It is `private, no-store`.
+
+## Pages
+
+| Route | What it is |
+|---|---|
+| `/` | the front door: three tiles, Ride, Web, Watch |
+| `/ride` | the maps list and the embed of the first map |
+| `/ride/:map` | one map: name, tier, stages, records if Games answers, the embed |
+| `/watch` | the feed as a list with each item's thumbnail and a play link; data hooks for the HLS `<video>` |
+| `/web` | the Surf with Vibe form and what it does |
+| `/developers` | the API, for people and agents |
+| anything else | the 404 page |
+
+All of them are server-rendered through the OpenVibe Frame (`openvibe-shared/shell`) and readable without
+JavaScript, with stable `sf-` ids and classes. A page rendered for a signed-in person is `private, no-store`; a
+public page is `public, max-age=60` with `Vary: Cookie`. `public/css/surf.css` and `public/js/surf.js` are
+one-comment placeholders: the main session designs them and adds the HLS player.
+
+## API
+
+| Route | Auth | Cache | What it does |
+|---|---|---|---|
+| `GET /api/v1/watch/feed` | none | `public, max-age=30` | the merged Live feed |
+| `GET /api/v1/ride/maps` | none | `public, max-age=60` | the Games surf map catalogue |
+| `GET /api/v1/ride/maps/:id` | none | `public, max-age=60` | one map and its fastest runs |
+| `POST /api/v1/web/tasks` | cookie + Origin | `private, no-store` | create one Actor task as the person |
+| `GET /web/go` | cookie | `private, no-store` | the no-JavaScript task form, 303 to Actor |
+
+Errors are RFC 9457 `application/problem+json`. A blank map id is 400; a bad Watch topic or cursor is 400. The
+only outbound person token is the `Authorization: Bearer` on the request to Actor.
+
+Plumbing: `GET /api/health`, `GET /api/ready` (200 `ready` or `degraded`; Network, JWKS, the OAuth client, Live,
+Games and Actor are optional checks), `GET /release.json`, `POST /release-metrics`, `/shared/*` (the pinned Frame),
+`/css/*` and `/js/*` (content-hashed `?v=` → immutable), `GET /robots.txt`, `GET /sitemap.xml` (the public pages
+and the Ride maps), `GET /llms.txt` and `GET /metrics` (loopback only).
+
+## Sign-in
+
+`server/auth/sso.js` is OpenVibe.Design's module with this site's names:
+
+- `GET /auth/login?next=/web[&silent=1]` mints a state and a PKCE S256 verifier/challenge, stores them (and the
+ sanitized `next`) in the httpOnly `surf_oauth` flow cookie scoped to `/auth`, and redirects to
+ `${OV_NETWORK_URL}/oauth/authorize` with `client_id=surf` and `redirect_uri=${BASE_URL}/auth/callback`.
+- `GET /auth/callback` checks the state in constant time, exchanges the code at the Network's internal
+ `/oauth/token` with the client secret and the code verifier, and sets `surf_at` (24 h) and `surf_rt` (30 d):
+ httpOnly, SameSite=Lax, Secure in production, Path=/.
+- `GET /auth/logout` clears them and revokes the refresh token (best effort); `GET /auth/me` answers `{ user }` or
+ `{ user: null }` for the shared navbar. A FedCM assertion or an app/service token never becomes a session.
+- `sanitizeNext` accepts same-site relative paths only. An absolute URL on this site's own origin counts as its
+ path; another origin, `//evil`, a control character or a backslash goes to `/`.
+
+The Network signs the access token (RS256); `server/auth/keys.js` fetches and refreshes its JWKS from
+`${OV_NETWORK_INTERNAL_URL}/api/.well-known/jwks` through `openvibe-sdk/auth`.
+
+## Configuration
+
+See [.env.example](.env.example). Required in production: `BASE_URL` and `OV_OAUTH_CLIENT_SECRET` (the `surf`
+OAuth client on the Network).
+
+| Variable | Default | What |
+|---|---|---|
+| `OV_NETWORK_INTERNAL_URL` | `http://127.0.0.1:4000` | sign-in token exchange and JWKS |
+| `OV_NETWORK_URL` | `https://openvibe.network` | where a browser is sent to authorize |
+| `OV_NETWORK_ISSUER` | `OV_NETWORK_URL` | the token issuer |
+| `OV_LIVE_INTERNAL_URL` | `http://127.0.0.1:3000` | Watch's three Live reads |
+| `OV_GAMES_INTERNAL_URL` | `http://127.0.0.1:8000` | Ride's map reads |
+| `OV_ACTOR_INTERNAL_URL` | `http://127.0.0.1:4950` | Web's task creation |
+| `SURF_GAMES_PUBLIC_URL` | `https://openvibe.games` | the embed origin and `embed_url` |
+| `SURF_LIVE_PUBLIC_URL` | `https://openvibe.live` | card page links |
+| `SURF_ACTOR_PUBLIC_URL` | `https://openvibe.actor` | the task page a person is sent to |
+| `SURF_WATCH_CACHE_MS` | `30000` | the merged Watch feed cache |
+| `SURF_WATCH_SOURCE_TIMEOUT_MS` | `2000` | one Live source's budget |
+| `SURF_RIDE_CACHE_MS` | `60000` | the Ride cache |
+| `SURF_GAMES_TIMEOUT_MS` | `4000` | one Games read's budget |
+| `SURF_ACTOR_TIMEOUT_MS` | `15000` | one Actor task creation's budget |
+| `SURF_RATE_LIMIT_PER_MIN` | `120` | per-IP `/api` and `/auth` budget |
+| `COOKIE_SECURE` | true in production | Secure cookies |
+
+## How to run
+
+```bash
+npm install
+npm run dev # http://localhost:5110
+OV_ALLOW_SUITE=1 npm test
+```
+
+`npm test` runs every `test/*.test.js` in its own process with in-process stand-ins
+(`test/helpers/network.js`, `test/helpers/upstreams.js`): a real RS256 key pair and the OAuth token endpoint for
+Network, and recorded Live, Games and Actor routes. Nothing calls the real internet. `OV_ALLOW_SUITE=1` is the
+suite opt-in the shared test runner expects.
+
+## Deploy (for the lead)
+
+- **Deploy:** `sudo ovhost deploy surf` on the host (checkout at `/opt/openvibe.surf`, unit
+ `openvibe-surf.service` on 127.0.0.1:5110, env `/etc/openvibe/surf.env`).
+- **nginx:** [deploy/nginx/openvibe.surf.conf](deploy/nginx/openvibe.surf.conf), installed with
+ `ov-vhost-install`. `/api` is reads only; `/api/v1/web/` and `/web/go` are the forms zone; `/auth` is GET only;
+ `/auth/me` gets the API budget; `/internal` and `/metrics` are 404.
+- **Readiness:** `/api/ready` reports `degraded` (HTTP 200) when Network, Live or Games does not answer; the
+ process still serves the pages and the crawl artifacts.
+
+## Security (threat notes)
+
+Reporting a vulnerability: [SECURITY.md](SECURITY.md).
+
+- Session tokens are httpOnly cookies; no script can read them and nothing renders or logs them. The only place
+ they go is the `Authorization` header of a request to the configured Actor origin.
+- A Web task must come from this site's own origin (`Origin`), so another site cannot make a signed-in visitor
+ spend their Actor allowance. `/web/go` refuses a cross-site navigation when the browser says it is one.
+- `next` after sign-in is this site's own path only; control characters and backslashes are refused because
+ browsers drop or rewrite them.
+- Watch's card fields are allow-listed and escaped; only `http(s)` URLs become `href`/`src`. Actor's error
+ detail is trimmed and no token or request body is logged.
+- Secrets live only in the environment file; only variable names appear in code and docs, and no secret or token
+ is logged.
+
+---
+
+Part of the [OpenVibe network](https://openvibe.network). Built in the open by
+[OpenVibers](https://github.com/OpenVibers).
new file mode 100644
new file mode 100644
@@ -0,0 +1,22 @@
@@ -0,0 +1,22 @@
+# Security policy
+
+OpenVibe is run in the open, and we want to hear about security problems before anyone else does.
+
+## Reporting a vulnerability
+
+Email **[email protected]** with "security" in the subject. Please include what you found, where (URL,
+repository, file), how to reproduce it, and what an attacker could do with it. Do not open a public GitHub issue for
+a vulnerability, and do not test against other people's accounts, data or streams.
+
+We reply within 7 days, keep you updated while we fix it, and credit you when the fix ships if you would like.
+
+## Scope
+
+Every OpenVibe service and site (openvibe.surf, openvibe.design, openvibe.live, openvibe.network,
+openvibe.tools, openvibe.media, openvibe.community, openvibe.games and the other openvibe.* domains) and every
+repository under github.com/OpenVibers. Machine-readable contact details are at `/.well-known/security.txt` on
+each site.
+
+## Supported versions
+
+Only the current `main` branch, which is what runs in production, receives fixes.
new file mode 100644
new file mode 100644
@@ -0,0 +1,53 @@
@@ -0,0 +1,53 @@
+{
+ "repository": "OpenVibers/OpenVibe.Surf",
+ "version": "0.1.0",
+ "stage": "v1",
+ "code": true,
+ "wave": "W20",
+ "plan": "OpenVibe.Surf: one room with three surfs. Ride is CS-style surf in the browser served by OpenVibe.Games; Watch is lean-back channel surfing over OpenVibe.Live's live streams, clips and AI moments; Web is Surf with Vibe, where an agent browses for you on a private desk through OpenVibe.Actor.",
+ "runtime": "node 22, express 4, openvibe-shared/shell; stateless (no database, no Valkey, no Events)",
+ "port": "5110",
+ "domain": "openvibe.surf",
+ "contracts": "openvibe-contracts v0.230.0",
+ "sdk": "openvibe-sdk v0.70.0",
+ "shared": "openvibe-shared v4.31.0",
+ "packages": [
+ "openvibe-contracts v0.230.0",
+ "openvibe-sdk v0.70.0",
+ "openvibe-shared v4.31.0"
+ ],
+ "summary": "The service skeleton for openvibe.surf: Network sign-in as OAuth client `surf` (PKCE S256, httpOnly surf_at/surf_rt cookies), the Watch feed merged from OpenVibe.Live (live streams by viewers, then clips and moments interleaved by freshness, no same-channel runs, cached 30 s, each source given 2 s, failures named in `sources`), the Ride proxy to OpenVibe.Games' /api/surf/maps (cached 60 s, games: 'not_ready' while Games' new routes answer 404, an embed URL per map), and Surf with Vibe: a signed-in, same-origin POST that creates one OpenVibe.Actor task with the person's own Network token as a Bearer, an Idempotency-Key and a cited browsing brief, plus /web/go as the no-JavaScript form. The pages are placeholders for the main session: stable sf- ids and classes, semantic HTML, readable without JavaScript.",
+ "works": [
+ "Sign-in with OpenVibe.Network: OAuth 2 authorization code, PKCE S256, state check, server-side exchange, httpOnly surf_at/surf_rt cookies, sanitizeNext (own origins only, no control characters or backslashes)",
+ "Watch GET /api/v1/watch/feed?topic=all|live|clips|moments&cursor=: merges OpenVibe.Live's /api/streams, /api/content/feed?type=clips and /api/content/moments?type=clips into one item shape, ranks live by viewers then moments/clips by freshness with no channel twice in a row, caches 30 s, 2 s per source, a failing source left out and named in sources, public max-age=30",
+ "Ride GET /api/v1/ride/maps and /api/v1/ride/maps/:id: proxies OpenVibe.Games' /api/surf/maps routes, adds each map's embed_url (https://openvibe.games/play/surf?map=<id>&embed=1), caches 60 s, Games 404 → empty list/map shell with games: 'not_ready'",
+ "Web POST /api/v1/web/tasks: signed in, same-origin, query 3–500 characters, sends the person's surf_at token to OpenVibe.Actor as a Bearer with an Idempotency-Key and the prefixed browsing brief, answers { task_id, page }, and passes Actor's 4xx refusal through in words",
+ "GET /web/go?q=: the same task as a plain form for no-JavaScript: 303 to the Actor task page, sign-in redirect for a guest, Actor's refusal in plain words",
+ "Server-rendered pages: / (three tiles), /ride (maps list and first embed), /ride/:map (name, tier, stages, records, embed), /watch (feed list with thumbnails and play links, HLS hooks for the main session's video), /web (the form), /developers and a 404; public max-age=60 with Vary: Cookie, signed-in private, no-store",
+ "Crawl artifacts (robots.txt, sitemap.xml with the public pages and Ride maps, llms.txt), /api/health, /api/ready (degraded when Network/Live/Games/Actor do not answer), /release.json, /release-metrics, /shared/*",
+ "Helmet with the network's CSP (Games and Live frames, openvibe.media HLS), per-IP 120/min on /api and /auth, content-hashed /css and /js, graceful stop",
+ "Deploy files: openvibe-surf.service, nginx vhost for openvibe.surf on 127.0.0.1:5110 with the forms zone, /auth/me on the API zone and /internal + /metrics 404",
+ "Tests: feed merge/order/shape/failing source/30 s cache/cursor, ride proxy/cache/not_ready/failure, web 401/403/Actor Bearer + Idempotency-Key + prefixed text/refusal/redirect, SSO (Design's cases), pages/cache headers/CSP, discovery"
+ ],
+ "notYet": [
+ "The pages' design and public/css/surf.css + public/js/surf.js: the main session writes them; today the views are semantic placeholders and the assets are one-comment files (the HLS <video> player is the main session's script)",
+ "OpenVibe.Games' GET /api/surf/maps and /api/surf/maps/:id are being added in parallel; until they ship, Ride answers games: 'not_ready'",
+ "A Surf service manifest and capabilities in OpenVibe.Contracts: this service has no capability of its own and no other service calls it",
+ "Deployment: the deploy files are written, but the service is not deployed yet"
+ ],
+ "capabilitiesRegistered": false,
+ "serviceManifestRegistered": false,
+ "deployed": false,
+ "placeholderInSites": false,
+ "features": [
+ "Network sign-in (PKCE)",
+ "Watch merged Live feed",
+ "Ride Games maps proxy",
+ "Surf with Vibe via OpenVibe.Actor",
+ "server-rendered pages",
+ "crawl artifacts",
+ "limits and readiness",
+ "deploy files"
+ ],
+ "updated": "2026-10-28"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,127 @@
@@ -0,0 +1,127 @@
+## ═══════════════════════════════════════════════════════════════
+## openvibe.surf — Nginx configuration (REFERENCE)
+##
+## Deployed to /etc/nginx/sites-available/openvibe.surf.conf (ov-vhost-install) — keep them in sync and reload
+## nginx after changing it. The Node app (port 5110, systemd unit openvibe-surf) renders every page and serves the
+## Watch/Ride/Web API; nginx terminates TLS, redirects www → apex, rate-limits the API, sign-in and the task forms,
+## and compresses text.
+##
+## No proxy_cache: the app decides cacheability per response (public 30 s for the Watch feed, 60 s for Ride and the
+## pages; private, no-store for a page or answer rendered for a signed-in person). Never log request bodies here.
+## ═══════════════════════════════════════════════════════════════
+
+limit_req_zone $binary_remote_addr zone=surf_auth:10m rate=10r/m;
+limit_req_zone $binary_remote_addr zone=surf_api:10m rate=5r/s;
+# Starting an Actor task is a form, not a read: a person types, so a small budget is plenty. Both the JSON route and
+# the no-JavaScript form take it, so neither is a way around the other.
+limit_req_zone $binary_remote_addr zone=surf_forms:10m rate=20r/m;
+
+server {
+ listen 80;
+ listen [::]:80;
+ server_name openvibe.surf www.openvibe.surf;
+
+ location /.well-known/acme-challenge/ { root /var/www/certbot; }
+ location / { return 301 https://openvibe.surf$request_uri; }
+}
+
+server {
+ listen 443 ssl;
+ listen [::]:443 ssl;
+ http2 on;
+ server_name www.openvibe.surf;
+
+ ssl_certificate /etc/letsencrypt/live/openvibe.surf/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/openvibe.surf/privkey.pem;
+ ssl_protocols TLSv1.2 TLSv1.3;
+
+ return 301 https://openvibe.surf$request_uri;
+}
+
+server {
+ listen 443 ssl;
+ listen [::]:443 ssl;
+ http2 on;
+ server_name openvibe.surf;
+
+ ssl_certificate /etc/letsencrypt/live/openvibe.surf/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/openvibe.surf/privkey.pem;
+ ssl_protocols TLSv1.2 TLSv1.3;
+
+ access_log /var/log/nginx/openvibe.surf.access.log;
+ error_log /var/log/nginx/openvibe.surf.error.log;
+
+ # A query is at most 500 characters; 32 KB leaves room for headers.
+ client_max_body_size 32k;
+
+ add_header X-Content-Type-Options nosniff always;
+ add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
+
+ gzip on;
+ gzip_proxied any;
+ gzip_vary on;
+ gzip_comp_level 5;
+ gzip_min_length 1024;
+ gzip_types text/css application/javascript text/javascript application/json application/problem+json image/svg+xml text/plain application/xml;
+
+ proxy_http_version 1.1;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $remote_addr;
+ proxy_set_header CF-Connecting-IP $remote_addr;
+ proxy_set_header X-Forwarded-Proto $scheme;
+
+ # ── The task form and the JSON route it shares: GET /web/go, POST /api/v1/web/tasks ──
+ # A person's query is a write, so these have their own small budget. Nothing else under /api/v1/web is exposed.
+ location = /web/go {
+ limit_except GET { deny all; }
+ limit_req zone=surf_forms burst=5 nodelay;
+ limit_req_status 429;
+ proxy_pass http://127.0.0.1:5110;
+ }
+
+ location ^~ /api/v1/web/ {
+ limit_except GET POST { deny all; }
+ limit_req zone=surf_forms burst=5 nodelay;
+ limit_req_status 429;
+ proxy_pass http://127.0.0.1:5110;
+ }
+
+ # ── The Watch and Ride API: reads only ──────────────────
+ location /api/ {
+ limit_except GET { deny all; }
+ limit_req zone=surf_api burst=20 nodelay;
+ limit_req_status 429;
+ proxy_pass http://127.0.0.1:5110;
+ }
+
+ # ── Sign-in: GET only ───────────────────────────────────
+ # /auth/login, /auth/callback, /auth/me and /auth/logout are all GETs here. The app's POST /auth/logout confirm
+ # is defence in depth and is not exposed by nginx; a same-origin navigation signs out directly.
+ # The navbar's session probe runs on every page view: it gets the API's budget, not sign-in's.
+ location = /auth/me {
+ limit_req zone=surf_api burst=30 nodelay;
+ limit_req_status 429;
+ proxy_pass http://127.0.0.1:5110;
+ }
+
+ location /auth/ {
+ limit_except GET { deny all; }
+ limit_req zone=surf_auth burst=10 nodelay;
+ limit_req_status 429;
+ proxy_pass http://127.0.0.1:5110;
+ }
+
+ # ── Prometheus metrics: host-local scrapers only (http://127.0.0.1:5110/metrics) ──
+ location ~* ^/metrics(/|$) { return 404; }
+ location ~* ^/internal(/|$) { return 404; }
+
+ # ── Pages, discovery, static assets, release.json ──────
+ location / {
+ proxy_pass http://127.0.0.1:5110;
+ }
+
+ location ~ /\.(?!well-known) {
+ deny all;
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,30 @@
@@ -0,0 +1,30 @@
+[Unit]
+Description=OpenVibe.Surf — Ride maps, surf the web, surf what's on.
+# Network (sign-in, JWKS) should be up first, but OpenVibe.Surf degrades honestly without it: the pages, Watch and
+# Ride serve, and sign-in waits for the signing key (see /api/ready). Live, Games and Actor are optional per request.
+After=network.target
+Wants=openvibe-network.service
+
+[Service]
+Type=simple
+User=ubuntu
+Group=ubuntu
+WorkingDirectory=/opt/openvibe.surf
+EnvironmentFile=/etc/openvibe/surf.env
+Environment=NODE_ENV=production
+Environment=PORT=5110
+Environment=PATH=/usr/local/bin:/usr/bin:/bin
+ExecStart=/usr/bin/env node /opt/openvibe.surf/server/index.js
+Restart=always
+RestartSec=5
+KillSignal=SIGTERM
+# In-flight requests get the 10 s drain.
+TimeoutStopSec=15
+NoNewPrivileges=true
+PrivateTmp=true
+ProtectSystem=strict
+ProtectHome=true
+# Stateless: the code tree is read-only and nothing here writes to disk.
+
+[Install]
+WantedBy=multi-user.target
new file mode 100644
new file mode 100644
@@ -0,0 +1,1143 @@
@@ -0,0 +1,1143 @@
+{
+ "name": "openvibe-surf",
+ "version": "0.1.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "openvibe-surf",
+ "version": "0.1.0",
+ "license": "AGPL-3.0-only",
+ "dependencies": {
+ "cookie-parser": "^1.4.7",
+ "dotenv": "^16.4.7",
+ "express": "^4.21.2",
+ "express-rate-limit": "^7.5.0",
+ "helmet": "^8.1.0",
+ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.230.0",
+ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.70.0",
+ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.32.0"
+ },
+ "engines": {
+ "node": ">=22.0.0"
+ }
+ },
+ "node_modules/accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "8.20.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
+ "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ajv-formats": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz",
+ "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "ajv": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
+ "license": "MIT"
+ },
+ "node_modules/body-parser": {
+ "version": "1.20.9",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.9.tgz",
+ "integrity": "sha512-FISIQxV5u91Y9gvuFq3q/tA/qTqLJn3AhRcEvyozyhJTh5OBFFtv827AdHU7Rbyct4r+Ba1/99oFa1Y3yuFsdw==",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "content-type": "~1.0.5",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "~1.2.0",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.4.24",
+ "on-finished": "~2.4.1",
+ "qs": "~6.16.0",
+ "raw-body": "~2.5.3",
+ "type-is": "~1.6.18",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/content-disposition": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/content-type": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+ "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie-parser": {
+ "version": "1.4.7",
+ "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz",
+ "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==",
+ "license": "MIT",
+ "dependencies": {
+ "cookie": "0.7.2",
+ "cookie-signature": "1.0.6"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/cookie-signature": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/destroy": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "16.6.1",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
+ "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "license": "MIT"
+ },
+ "node_modules/encodeurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
+ "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "license": "MIT"
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/express": {
+ "version": "4.22.3",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.22.3.tgz",
+ "integrity": "sha512-Bdcs4+3qlpVlx2NRn6fgX2Ue2/gGRaPeawebgclM0ERSCqDpA+owF1fdPwjJUTAJWMTuAaxjDf+hzb0/4eKvvw==",
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "~1.3.8",
+ "array-flatten": "1.1.1",
+ "body-parser": "~1.20.5",
+ "content-disposition": "~0.5.4",
+ "content-type": "~1.0.4",
+ "cookie": "~0.7.1",
+ "cookie-signature": "~1.0.6",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "~1.3.1",
+ "fresh": "~0.5.2",
+ "http-errors": "~2.0.0",
+ "merge-descriptors": "1.0.3",
+ "methods": "~1.1.2",
+ "on-finished": "~2.4.1",
+ "parseurl": "~1.3.3",
+ "path-to-regexp": "~0.1.13",
+ "proxy-addr": "~2.0.7",
+ "qs": "~6.16.0",
+ "range-parser": "~1.2.1",
+ "safe-buffer": "5.2.1",
+ "send": "~0.19.0",
+ "serve-static": "~1.16.2",
+ "setprototypeof": "1.2.0",
+ "statuses": "~2.0.1",
+ "type-is": "~1.6.18",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/express-rate-limit": {
+ "version": "7.5.1",
+ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz",
+ "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/express-rate-limit"
+ },
+ "peerDependencies": {
+ "express": ">= 4.11"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "license": "MIT"
+ },
+ "node_modules/fast-uri": {
+ "version": "3.1.9",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.9.tgz",
+ "integrity": "sha512-hS/0Wt3Ya043oKXs0sIxrMpKQITXhMLIrVAKKXi4gozGC9v7YRWVnXIZzjNPTO02M9BdxLCWqK1yRXgNziVyJQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/finalhandler": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",
+ "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.4.1",
+ "parseurl": "~1.3.3",
+ "statuses": "~2.0.2",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
+ "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/helmet": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/helmet/-/helmet-8.3.0.tgz",
+ "integrity": "sha512-Qgpiaws3Sm30Av8Eah6sjMCZZwjlBu+E68rhpCWBshY1lb09HtLwj5GviX0OyQIn+ulUS0iX0AxN5n3tLZzz1w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/EvanHahn"
+ }
+ },
+ "node_modules/http-errors": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
+ "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
+ "license": "MIT",
+ "dependencies": {
+ "depd": "~2.0.0",
+ "inherits": "~2.0.4",
+ "setprototypeof": "~1.2.0",
+ "statuses": "~2.0.2",
+ "toidentifier": "~1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "license": "ISC"
+ },
+ "node_modules/ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "license": "MIT"
+ },
+ "node_modules/jsonwebtoken": {
+ "version": "9.0.3",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz",
+ "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==",
+ "license": "MIT",
+ "dependencies": {
+ "jws": "^4.0.1",
+ "lodash.includes": "^4.3.0",
+ "lodash.isboolean": "^3.0.3",
+ "lodash.isinteger": "^4.0.4",
+ "lodash.isnumber": "^3.0.3",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.once": "^4.0.0",
+ "ms": "^2.1.1",
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">=12",
+ "npm": ">=6"
+ }
+ },
+ "node_modules/jsonwebtoken/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/jwa": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
+ "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
+ "license": "MIT",
+ "dependencies": {
+ "buffer-equal-constant-time": "^1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/jws": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
+ "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
+ "license": "MIT",
+ "dependencies": {
+ "jwa": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/lodash.includes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+ "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isboolean": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+ "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isinteger": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+ "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isnumber": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+ "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isstring": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.once": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
+ "license": "MIT"
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/merge-descriptors": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
+ "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "license": "MIT",
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "license": "MIT"
+ },
+ "node_modules/negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/on-finished": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.2.tgz",
+ "integrity": "sha512-ATWUv387i+nLD49EiDoOft5scf32Vsdz3ktrQJG9bi7jRFmaXTssvyvj1EmeKVVgvesbqUtvQQvaS2TpTukAZQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/openvibe-contracts": {
+ "version": "0.230.0",
+ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.230.0",
+ "integrity": "sha512-wvzSYV0GKfHV6UnEKtqDAXkrl3yimdWe/KHM0jeU0JWkrRo93zGNAZ2fRPwATBeihITVE0b973dM899U/YOzgw==",
+ "license": "AGPL-3.0-only",
+ "dependencies": {
+ "ajv": "^8.17.1",
+ "ajv-formats": "^3.0.1"
+ },
+ "bin": {
+ "openvibe-contracts-check": "bin/check-service.js"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/openvibe-sdk": {
+ "version": "0.70.0",
+ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.70.0",
+ "integrity": "sha512-iMNxlnRm0fmyj60nHiC/lKAaG3JAqiJgu42lShnkby4yF4dQJRG3G0trrqOoULZKBalrYHb/gjauRwNQYN/tog==",
+ "license": "MIT",
+ "bin": {
+ "openvibe-download": "bin/openvibe-download.js"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "peerDependencies": {
+ "@electric-sql/pglite": "^0.5.8",
+ "iovalkey": "^0.4.0",
+ "pg": "^8.23.0"
+ },
+ "peerDependenciesMeta": {
+ "@electric-sql/pglite": {
+ "optional": true
+ },
+ "iovalkey": {
+ "optional": true
+ },
+ "pg": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/openvibe-shared": {
+ "version": "4.32.0",
+ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.32.0",
+ "integrity": "sha512-D4P2ron5WiE0Qzyvfnp6Fh7JFjTZcoxkKlB+6nq+ceZewbInPXyPDcur+XwtINKuTIdC7lRu3EZDSFWfZQv+Nw==",
+ "license": "MIT",
+ "dependencies": {
+ "jsonwebtoken": "^9.0.0"
+ },
+ "bin": {
+ "openvibe-build-assets": "scripts/build-assets.js"
+ }
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/path-to-regexp": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz",
+ "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==",
+ "license": "MIT"
+ },
+ "node_modules/proxy-addr": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.8.tgz",
+ "integrity": "sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==",
+ "license": "MIT",
+ "dependencies": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.16.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.2.tgz",
+ "integrity": "sha512-VYdnB5S7/0IeXzBacUtwNAaM6d7Y/PgYnNC7bJCI6meqA/OgnsQbZMtVW8Dt2D1PHgbHGtYmhSoEIgvt7xZ2ZA==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "es-define-property": "^1.0.1",
+ "side-channel": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "2.5.3",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
+ "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.4.24",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "license": "MIT"
+ },
+ "node_modules/semver": {
+ "version": "7.8.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/send": {
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz",
+ "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "~0.5.2",
+ "http-errors": "~2.0.1",
+ "mime": "1.6.0",
+ "ms": "2.1.3",
+ "on-finished": "~2.4.1",
+ "range-parser": "~1.2.1",
+ "statuses": "~2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/send/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/serve-static": {
+ "version": "1.16.3",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz",
+ "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==",
+ "license": "MIT",
+ "dependencies": {
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.3",
+ "send": "~0.19.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "license": "ISC"
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz",
+ "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.4",
+ "side-channel-list": "^1.0.1",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
+ "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/statuses": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+ "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "license": "MIT",
+ "dependencies": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,30 @@
@@ -0,0 +1,30 @@
+{
+ "name": "openvibe-surf",
+ "version": "0.1.0",
+ "private": true,
+ "description": "OpenVibe.Surf — Ride maps, surf the web, surf what's on.",
+ "main": "server/index.js",
+ "scripts": {
+ "start": "node server/index.js",
+ "dev": "NODE_ENV=development node server/index.js",
+ "test": "node test/run.js"
+ },
+ "keywords": [
+ "openvibe"
+ ],
+ "author": "OpenVibers",
+ "license": "AGPL-3.0-only",
+ "dependencies": {
+ "cookie-parser": "^1.4.7",
+ "dotenv": "^16.4.7",
+ "express": "^4.21.2",
+ "express-rate-limit": "^7.5.0",
+ "helmet": "^8.1.0",
+ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.230.0",
+ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.70.0",
+ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.32.0"
+ },
+ "engines": {
+ "node": ">=22.0.0"
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,174 @@
@@ -0,0 +1,174 @@
+/* OpenVibe.Surf: the room's own pieces, on the shared room kit (kit.css, room.css, showcase.css). The page wears the
+ person's theme; --room-* is Surf's cyan (rooms.js). Phone first: the TV stacks (screen, then the guide), the map
+ page stacks (game, then records); from 980 px they sit side by side. */
+*, *::before, *::after { box-sizing: border-box; }
+html, body { margin: 0; background: var(--bg-primary, #0a0f1c); color: var(--text-primary, #e6edf7); font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; }
+.skip { position: absolute; left: -9999px; }
+.skip:focus { left: 12px; top: 12px; z-index: 99; padding: 8px 12px; border-radius: 10px; background: var(--bg-card, #0f1729); color: inherit; }
+.sf-wrap { max-width: 1240px; margin: 0 auto; padding: 0 16px 64px; }
+.sf-page-pad { padding-top: clamp(20px, 4vw, 40px); }
+.sf-narrow { max-width: 760px; }
+.sf a:not([class]) { color: var(--room-strong); }
+.sf h2 { margin: 0 0 10px; font-size: 1.05rem; font-weight: 800; letter-spacing: -.01em; }
+:where(.sf) .ovk-i { fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
+.sf-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
+.sf-kicker { margin: 0 0 4px; font-size: .74rem; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--room-strong); }
+.sf-kicker a { color: inherit; }
+.sf-lede { margin: 8px 0 0; max-width: 700px; color: var(--text-secondary, #96a7c2); line-height: 1.55; }
+.sf-fine { color: var(--text-muted, #7386a3); font-size: .84rem; line-height: 1.5; }
+.sf-page-h { margin-bottom: 20px; }
+/* Filled buttons wear the room's blue, as the home page's Start does. */
+.sf .ovk-btn--primary { background: var(--room-fill); color: var(--room-on); }
+.sf-page-h h1 { margin: 0; font-size: clamp(1.7rem, 4.2vw, 2.4rem); font-weight: 800; letter-spacing: -.03em; line-height: 1.1; }
+.sf-head-row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
+.sf-more { display: inline-flex; align-items: center; gap: 2px; font-weight: 700; font-size: .9rem; text-decoration: none; color: var(--room-strong); }
+.sf-more .ovk-i { width: 1em; height: 1em; }
+.sf section + section { margin-top: 28px; }
+.sf-note { margin: 16px 0; padding: 10px 14px; border-radius: 12px; font-size: .9rem; color: var(--text-secondary, #96a7c2); background: color-mix(in srgb, var(--warning, #f5a524) 12%, transparent); }
+.sf-empty { display: grid; justify-items: center; gap: 10px; padding: 36px 20px; border-radius: 18px; border: 1px dashed var(--border, #1f2d47); text-align: center; color: var(--text-secondary, #96a7c2); }
+.sf-empty > .ovk-i { width: 40px; height: 40px; color: var(--room-accent); stroke-width: 1.6; }
+.sf-empty p { margin: 0; max-width: 460px; line-height: 1.5; }
+.sf-empty h1 { margin: 0; font-size: 1.6rem; font-weight: 800; color: var(--text-primary, #e6edf7); }
+.sf-card { padding: 16px; border-radius: 18px; border: 1px solid var(--border, #1f2d47); background: var(--bg-secondary, #101828); }
+.sf-card h2 { display: flex; align-items: center; gap: 8px; }
+.sf-card h2 .ovk-i { width: 18px; height: 18px; color: var(--room-strong); }
+kbd { display: inline-block; min-width: 1.6em; padding: 0 5px; border-radius: 6px; border: 1px solid var(--border, #1f2d47); border-bottom-width: 2px; background: var(--bg-card, #0f1729); font: 700 .78rem/1.5 ui-monospace, SFMono-Regular, Menlo, monospace; text-align: center; color: var(--text-primary, #e6edf7); }
+
+/* The home's run motif: a first-person run down two ramps, speed lines, the timer. */
+.sf-run { position: relative; width: min(100%, 440px); aspect-ratio: 400 / 260; margin: 0 auto; border-radius: 22px; overflow: hidden; color: var(--room-accent);
+ background: radial-gradient(120% 90% at 50% 35%, color-mix(in srgb, var(--room-accent) 22%, transparent), transparent 60%), linear-gradient(180deg, color-mix(in srgb, var(--room-accent) 8%, var(--bg-secondary, #101828)), var(--bg-primary, #0a0f1c));
+ border: 1px solid color-mix(in srgb, var(--room-accent) 30%, var(--border, #1f2d47)); }
+.sf-run-ramps { position: absolute; inset: 0; width: 100%; height: 100%; }
+.sf-run-speed { position: absolute; inset: 0; }
+.sf-run-speed i { position: absolute; left: 50%; top: 42%; width: 2px; height: 30%; border-radius: 2px; background: linear-gradient(to bottom, transparent, color-mix(in srgb, var(--text-primary, #fff) 55%, transparent));
+ transform-origin: 50% 0; transform: rotate(calc(var(--i) * 40deg - 160deg)) translateY(40%); opacity: 0; }
+html.ov-motion .sf-run-speed i { animation: sf-speed 1.1s linear infinite; animation-delay: calc(var(--i) * -.12s); }
+@keyframes sf-speed { 0% { opacity: 0; transform: rotate(calc(var(--i) * 40deg - 160deg)) translateY(10%) scaleY(.4); } 30% { opacity: .9; } 100% { opacity: 0; transform: rotate(calc(var(--i) * 40deg - 160deg)) translateY(120%) scaleY(1.3); } }
+.sf-run-hud { position: absolute; left: 50%; bottom: 10%; translate: -50% 0; display: grid; justify-items: center; gap: 2px; padding: 8px 16px; border-radius: 14px; background: rgba(0, 0, 0, .55); color: #fff; }
+.sf-run-hud b { font: 800 1.6rem/1 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
+.sf-run-hud small { font-size: .72rem; font-weight: 700; opacity: .8; font-variant-numeric: tabular-nums; }
+/* On a phone the room's picture is a faint corner mark behind the title: the ramps alone, no timer to read through. */
+@media (max-width: 759px) { .sc-room-pic .sf-run-hud, .sc-room-pic .sf-run-speed { display: none; } }
+
+/* Three tiles under one Start. */
+.sf-tiles { list-style: none; margin: 4px 0 0; padding: 0; display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); }
+.sf-tile a { display: grid; gap: 8px; height: 100%; padding: 18px; border-radius: 20px; border: 1px solid var(--border, #1f2d47); background: var(--bg-card, #0f1729); color: inherit; text-decoration: none; transition: border-color .15s, transform .15s; }
+.sf-tile a:hover, .sf-tile a:focus-visible { border-color: var(--room-accent); transform: translateY(-2px); outline: none; }
+.sf-tile-ic { display: grid; place-items: center; width: 44px; height: 44px; border-radius: 14px; background: var(--room-fill); color: var(--room-on); }
+.sf-tile-ic .ovk-i { width: 22px; height: 22px; }
+.sf-tile b { font-size: 1.25rem; font-weight: 800; letter-spacing: -.01em; }
+.sf-tile span:not(.sf-tile-ic):not(.sf-tile-go) { color: var(--text-secondary, #96a7c2); line-height: 1.5; font-size: .95rem; }
+.sf-tile-go { display: inline-flex; align-items: center; gap: 2px; margin-top: 4px; font-weight: 800; color: var(--room-strong); }
+.sf-tile-go .ovk-i { width: 1em; height: 1em; }
+@media (prefers-reduced-motion: reduce) { .sf-tile a { transition: none; } .sf-tile a:hover { transform: none; } }
+
+/* Scenes. */
+.sf-demo-run { display: grid; gap: 10px; padding: 16px; border-radius: 18px; background: #0b0f17; color: #e9f2ff; border: 1px solid color-mix(in srgb, var(--room-accent) 35%, transparent); }
+.sf-demo-hud { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; font-weight: 700; font-size: .85rem; }
+.sf-demo-hud b { font: 800 1.8rem/1 ui-monospace, SFMono-Regular, Menlo, monospace; font-variant-numeric: tabular-nums; }
+.sf-demo-hud .is-good { color: #4ade80; font-variant-numeric: tabular-nums; }
+.sf-demo-bar { height: 6px; border-radius: 6px; background: rgba(255, 255, 255, .12); overflow: hidden; }
+.sf-demo-bar i { display: block; height: 100%; background: #00bef6; border-radius: inherit; }
+.sf-demo-run p { margin: 0; font-size: .8rem; opacity: .75; }
+.sf-demo-steps { list-style: none; margin: 0; padding: 14px; display: grid; gap: 8px; border-radius: 18px; background: var(--bg-card, #0f1729); border: 1px solid var(--border, #1f2d47); }
+.sf-demo-steps li { display: flex; align-items: center; gap: 10px; font-size: .9rem; color: var(--text-secondary, #96a7c2); }
+.sf-demo-steps li::before { content: ""; flex: none; width: 10px; height: 10px; border-radius: 50%; border: 2px solid var(--text-muted, #7386a3); }
+.sf-demo-steps li.is-done::before { background: var(--success, #22c55e); border-color: var(--success, #22c55e); }
+.sf-demo-steps li.is-now { color: var(--text-primary, #e6edf7); font-weight: 650; }
+.sf-demo-steps li.is-now::before { border-color: var(--room-accent); border-right-color: transparent; animation: sf-spin .9s linear infinite; }
+@keyframes sf-spin { to { transform: rotate(360deg); } }
+.sf-demo-tv { display: grid; gap: 8px; padding: 10px; border-radius: 18px; background: var(--bg-card, #0f1729); border: 1px solid var(--border, #1f2d47); }
+.sf-demo-screen { position: relative; display: flex; align-items: flex-end; gap: 8px; aspect-ratio: 16 / 6; max-height: 210px; padding: 12px; border-radius: 12px; color: #fff; font-weight: 700; font-size: .85rem;
+ background: radial-gradient(120% 120% at 70% 30%, color-mix(in srgb, var(--room-accent) 45%, transparent), transparent 60%), linear-gradient(160deg, #1e293b, #0b1020); }
+.sf-demo-tv ul { list-style: none; margin: 0; padding: 0 4px; display: grid; gap: 4px; font-size: .82rem; color: var(--text-secondary, #96a7c2); }
+.sf-pill-live { padding: 1px 7px; border-radius: 5px; background: var(--live-red, #e11d48); color: #fff; font-size: .68rem; font-weight: 800; }
+
+/* Ride: map cards. */
+.sf-maps { list-style: none; margin: 16px 0 0; padding: 0; display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr)); }
+.sf-map-link { display: grid; grid-template-columns: 84px minmax(0, 1fr); gap: 14px; align-items: center; padding: 12px; border-radius: 18px; border: 1px solid var(--border, #1f2d47); background: var(--bg-card, #0f1729); color: inherit; text-decoration: none; transition: border-color .15s; }
+.sf-map-link:hover, .sf-map-link:focus-visible { border-color: var(--room-accent); outline: none; }
+.sf-map-art { position: relative; aspect-ratio: 1; border-radius: 14px; overflow: hidden; background: linear-gradient(180deg, color-mix(in srgb, var(--room-accent) 18%, var(--bg-secondary, #101828)), var(--bg-primary, #0a0f1c)); }
+.sf-map-art i { position: absolute; bottom: 0; width: 46%; height: 70%; background: linear-gradient(to top, color-mix(in srgb, var(--room-accent) 70%, transparent), color-mix(in srgb, var(--room-accent) 15%, transparent)); clip-path: polygon(0 100%, 100% 0, 100% 100%); }
+.sf-map-art i:nth-child(1) { left: -6%; } .sf-map-art i:nth-child(2) { left: 30%; height: 50%; opacity: .7; transform: scaleX(-1); } .sf-map-art i:nth-child(3) { left: 60%; height: 85%; opacity: .55; }
+.sf-map[data-tier="4"] .sf-map-art i, .sf-map[data-tier="5"] .sf-map-art i { height: 92%; }
+.sf-map-body { display: grid; gap: 3px; min-width: 0; }
+.sf-map-body > b { font-size: 1.05rem; font-weight: 800; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+.sf-tier { width: fit-content; padding: 1px 8px; border-radius: 999px; font-size: .7rem; font-weight: 800; letter-spacing: .02em; background: color-mix(in srgb, var(--room-accent) 16%, transparent); color: var(--room-strong); }
+.sf-map-meta { font-size: .84rem; color: var(--text-muted, #7386a3); }
+.sf-wr { font-size: .84rem; color: var(--text-secondary, #96a7c2); }
+.sf-wr b { font-variant-numeric: tabular-nums; color: var(--text-primary, #e6edf7); }
+.sf-play { margin-top: 28px; }
+.sf-game { position: relative; aspect-ratio: 16 / 9; border-radius: 18px; overflow: hidden; background: #000; border: 1px solid var(--border, #1f2d47); }
+.sf-game iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
+.sf-game-help { margin-top: 8px; }
+.sf-map-grid { display: grid; gap: 16px; margin-top: 20px; grid-template-columns: minmax(0, 1fr); }
+@media (min-width: 980px) { .sf-map-grid { grid-template-columns: minmax(0, 1fr) 320px; align-items: start; } }
+.sf-board { width: 100%; border-collapse: collapse; font-size: .9rem; }
+.sf-board th { text-align: left; font-size: .72rem; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; color: var(--text-muted, #7386a3); padding: 0 6px 8px; }
+.sf-board td { padding: 8px 6px; border-top: 1px solid var(--border, #1f2d47); }
+.sf-board .sf-time { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-variant-numeric: tabular-nums; text-align: right; }
+.sf-board th:last-child { text-align: right; }
+.sf-board .is-wr td { font-weight: 800; color: var(--room-strong); }
+
+/* Watch: the TV and the guide. */
+.sf-watch-h { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 12px; }
+.sf-topics { display: flex; gap: 4px; padding: 3px; border-radius: 999px; background: var(--bg-card, #0f1729); border: 1px solid var(--border, #1f2d47); overflow-x: auto; scrollbar-width: none; max-width: 100%; }
+.sf-topic { flex: none; padding: 6px 14px; border-radius: 999px; font-size: .86rem; font-weight: 700; text-decoration: none; color: var(--text-secondary, #96a7c2); }
+.sf-topic[aria-current="page"] { background: var(--room-fill); color: var(--room-on); }
+.sf-tv { display: grid; gap: 16px; margin-top: 18px; grid-template-columns: minmax(0, 1fr); }
+@media (min-width: 980px) { .sf-tv { grid-template-columns: minmax(0, 1fr) 340px; align-items: start; } .sf-guide { position: sticky; top: calc(var(--ovnav-real-h, 56px) + 12px); max-height: calc(100vh - var(--ovnav-real-h, 56px) - 24px); overflow-y: auto; } }
+.sf-tv-main { min-width: 0; }
+.sf-tv-screen { position: relative; aspect-ratio: 16 / 9; border-radius: 18px; overflow: hidden; background: #000; touch-action: pan-x; }
+.sf-tv-video, .sf-tv-poster { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; background: #000; }
+.sf-tv-poster { object-fit: cover; filter: brightness(.6); }
+.sf-tv-out { position: absolute; left: 50%; top: 50%; translate: -50% -50%; display: inline-flex; align-items: center; gap: 8px; padding: 12px 18px; border-radius: 999px; background: var(--room-fill); color: var(--room-on); font-weight: 800; text-decoration: none; white-space: nowrap; box-shadow: 0 10px 30px rgba(0, 0, 0, .4); }
+.sf-tv-out .ovk-i { width: 18px; height: 18px; }
+.sf-tv-screen.is-changing { animation: sf-change .28s ease; }
+@keyframes sf-change { 0% { opacity: .2; transform: translateY(var(--sf-dir, 12px)); } 100% { opacity: 1; transform: none; } }
+.sf-tv-info { display: grid; gap: 6px; margin-top: 12px; }
+.sf-tv-info h2 { margin: 0; font-size: clamp(1.1rem, 2.6vw, 1.45rem); line-height: 1.25; }
+.sf-tv-kind { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 0; font-size: .85rem; color: var(--text-muted, #7386a3); }
+.sf-pill { padding: 2px 9px; border-radius: 999px; font-size: .72rem; font-weight: 800; background: color-mix(in srgb, var(--room-accent) 16%, transparent); color: var(--room-strong); }
+.sf-pill--live { background: var(--live-red, #e11d48); color: #fff; }
+.sf-tv-meta { margin: 0; font-size: .9rem; }
+.sf-tv-meta a { color: var(--text-secondary, #96a7c2); text-decoration: none; font-weight: 650; }
+.sf-tv-keys { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 4px 0 0; }
+.sf-tv-keys .ovk-i { width: 18px; height: 18px; }
+.sf-guide h2 { font-size: .8rem; letter-spacing: .07em; text-transform: uppercase; color: var(--text-muted, #7386a3); }
+.sf-chs { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
+.sf-ch-link { display: grid; grid-template-columns: 112px minmax(0, 1fr); gap: 10px; align-items: center; padding: 6px; border-radius: 14px; color: inherit; text-decoration: none; border: 1px solid transparent; }
+.sf-ch-link:hover, .sf-ch-link:focus-visible { background: var(--bg-card, #0f1729); outline: none; }
+.sf-ch.is-on .sf-ch-link { background: var(--bg-card, #0f1729); border-color: var(--room-accent); }
+.sf-ch-thumb { position: relative; aspect-ratio: 16 / 9; border-radius: 10px; overflow: hidden; background: var(--bg-tertiary, #1a2233); }
+.sf-ch-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
+.sf-ch-len, .sf-ch-live { position: absolute; right: 4px; bottom: 4px; padding: 0 5px; border-radius: 4px; font-size: .66rem; font-weight: 800; background: rgba(0, 0, 0, .7); color: #fff; font-variant-numeric: tabular-nums; }
+.sf-ch-live { left: 4px; right: auto; top: 4px; bottom: auto; background: var(--live-red, #e11d48); }
+.sf-ch-body { display: grid; gap: 2px; min-width: 0; }
+.sf-ch-body b { font-size: .88rem; line-height: 1.3; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
+.sf-ch-body small { font-size: .76rem; color: var(--text-muted, #7386a3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+.sf-guide .sf-more { margin-top: 10px; }
+
+/* Web: Surf with Vibe. */
+.sf-web-form { display: grid; gap: 10px; }
+.sf-web-form textarea { width: 100%; min-height: 96px; padding: 12px 14px; border-radius: 14px; border: 1px solid var(--border, #1f2d47); background: var(--bg-input, #0d1322); color: var(--text-primary, #e6edf7); font: inherit; font-size: 1.02rem; line-height: 1.5; resize: vertical; }
+.sf-web-form textarea:focus { outline: 2px solid var(--room-accent); outline-offset: 1px; border-color: transparent; }
+.sf-web-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px; }
+.sf-web-row .ovk-btn .ovk-i { width: 1.05em; height: 1.05em; }
+.sf-status { min-height: 1.4em; margin: 8px 0 0; font-size: .9rem; color: var(--text-secondary, #96a7c2); }
+.sf-status.is-bad { color: var(--danger-text, #f87171); }
+.sf-examples { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
+.sf-ex { padding: 6px 12px; border-radius: 999px; border: 1px solid var(--border, #1f2d47); background: transparent; color: var(--text-secondary, #96a7c2); font: inherit; font-size: .84rem; cursor: pointer; text-align: left; }
+.sf-ex:hover, .sf-ex:focus-visible { border-color: var(--room-accent); color: var(--text-primary, #e6edf7); outline: none; }
+.sf-how { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; counter-reset: how; }
+.sf-how li { position: relative; display: grid; gap: 2px; padding: 12px 14px 12px 52px; border-radius: 14px; background: var(--bg-card, #0f1729); border: 1px solid var(--border, #1f2d47); counter-increment: how; }
+.sf-how li::before { content: counter(how); position: absolute; left: 14px; top: 12px; display: grid; place-items: center; width: 26px; height: 26px; border-radius: 50%; background: var(--room-fill); color: var(--room-on); font-weight: 800; font-size: .85rem; }
+.sf-how span { color: var(--text-secondary, #96a7c2); line-height: 1.5; font-size: .92rem; }
+
+/* The API. */
+.sf-eps { display: grid; gap: 10px; margin: 20px 0 0; }
+.sf-eps > div { padding: 12px 14px; border-radius: 14px; background: var(--bg-card, #0f1729); border: 1px solid var(--border, #1f2d47); }
+.sf-eps dt code { font: 600 .88rem/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--room-strong); overflow-wrap: anywhere; }
+.sf-eps dd { margin: 6px 0 0; color: var(--text-secondary, #96a7c2); line-height: 1.5; }
+
+@media (prefers-reduced-motion: reduce) { .sf-run-speed i, .sf-demo-steps li.is-now::before, .sf-tv-screen.is-changing { animation: none !important; } }
+html[data-ov-motion=reduced] .sf-run-speed i { animation: none !important; }
new file mode 100644
new file mode 100644
@@ -0,0 +1,134 @@
@@ -0,0 +1,134 @@
+/* OpenVibe.Surf: what the pages are complete without.
+ * - Watch: the TV plays clips and moments in the page (native HLS, else hls.js from /vendor, loaded only then);
+ * ↑ ↓, the buttons, a swipe on the screen or a tap in the guide change channel; live streams open on Live.
+ * - Web: the form starts the task in the page (POST /api/v1/web/tasks) and goes to it; examples fill the box.
+ * - Home: the run motif's timer and speed tick. */
+(function () {
+ 'use strict';
+ var reduced = function () { try { return matchMedia('(prefers-reduced-motion: reduce)').matches || document.documentElement.dataset.ovMotion === 'reduced'; } catch (e) { return false; } };
+
+ // ── Home: the run's timer ─────────────────────────────────
+ function runMotif() {
+ var t = document.querySelector('[data-sf-timer]'), sp = document.querySelector('[data-sf-speed]');
+ if (!t || t.dataset.ticking || reduced()) return;
+ t.dataset.ticking = '1';
+ var start = performance.now() - 12384;
+ var tick = function () {
+ if (!document.body.contains(t)) return;
+ var ms = (performance.now() - start) % 59999, m = Math.floor(ms / 60000), s = Math.floor((ms % 60000) / 1000), x = Math.floor(ms % 1000);
+ t.textContent = m + ':' + String(s).padStart(2, '0') + '.' + String(x).padStart(3, '0');
+ if (sp) sp.textContent = (1180 + Math.round(140 * Math.sin(ms / 700))).toLocaleString('en-US').replace(',', ' ');
+ requestAnimationFrame(tick);
+ };
+ requestAnimationFrame(tick);
+ }
+
+ // ── Watch: the TV ─────────────────────────────────────────
+ var hlsLoading = null;
+ function loadHls() {
+ if (window.Hls) return Promise.resolve(window.Hls);
+ return hlsLoading || (hlsLoading = new Promise(function (ok) {
+ var s = document.createElement('script'); s.src = '/vendor/hls.light.min.js'; s.async = true;
+ s.onload = function () { ok(window.Hls || null); }; s.onerror = function () { hlsLoading = null; ok(null); };
+ document.head.appendChild(s);
+ }));
+ }
+ function tv() {
+ var root = document.querySelector('[data-sf-tv]');
+ if (!root || root.dataset.ready) return;
+ root.dataset.ready = '1';
+ var screen = root.querySelector('[data-sf-screen]');
+ var chs = Array.prototype.slice.call(root.querySelectorAll('.sf-ch'));
+ var title = root.querySelector('[data-sf-title]'), why = root.querySelector('[data-sf-why]'), meta = root.querySelector('[data-sf-meta]');
+ var kindPill = root.querySelector('.sf-tv-kind .sf-pill');
+ var LABEL = { live: 'Live', clip: 'Clip', moment: 'AI moment' };
+ var cur = 0, hls = null;
+ function el(tag, cls) { var e = document.createElement(tag); if (cls) e.className = cls; return e; }
+ function show(i, dir) {
+ if (!chs.length) return;
+ cur = (i + chs.length) % chs.length;
+ var c = chs[cur], d = c.dataset;
+ chs.forEach(function (x, n) { x.classList.toggle('is-on', n === cur); });
+ if (hls) { try { hls.destroy(); } catch (e) { /* */ } hls = null; }
+ screen.replaceChildren();
+ screen.style.setProperty('--sf-dir', dir < 0 ? '-12px' : '12px');
+ screen.classList.remove('is-changing'); void screen.offsetWidth; screen.classList.add('is-changing');
+ if (d.sfPlayType === 'hls' && d.sfSrc) {
+ var v = el('video', 'sf-tv-video'); v.playsInline = true; v.controls = true; v.autoplay = true; v.muted = !screen.dataset.sound;
+ if (d.sfThumb) v.poster = d.sfThumb;
+ v.setAttribute('data-ov-player', '');
+ v.addEventListener('volumechange', function () { if (!v.muted) screen.dataset.sound = '1'; });
+ screen.appendChild(v);
+ if (v.canPlayType('application/vnd.apple.mpegurl')) { v.src = d.sfSrc; v.play().catch(function () {}); }
+ else loadHls().then(function (Hls) {
+ if (!Hls || !Hls.isSupported()) { v.src = d.sfSrc; return; }
+ if (chs[cur] !== c) return;
+ hls = new Hls({ maxBufferLength: 20 }); hls.loadSource(d.sfSrc); hls.attachMedia(v);
+ hls.on(Hls.Events.MANIFEST_PARSED, function () { v.play().catch(function () {}); });
+ });
+ } else {
+ if (d.sfThumb) { var img = el('img', 'sf-tv-poster'); img.src = d.sfThumb; img.alt = ''; screen.appendChild(img); }
+ var a = el('a', 'sf-tv-out'); a.href = d.sfPage || '#';
+ a.textContent = d.sfKind === 'live' ? 'Watch live on OpenVibe.Live' : 'Play on OpenVibe.Live';
+ screen.appendChild(a);
+ }
+ if (title) title.textContent = (c.querySelector('.sf-ch-body b') || {}).textContent || '';
+ if (why) why.textContent = d.sfWhy || '';
+ if (kindPill) { kindPill.textContent = LABEL[d.sfKind] || ''; kindPill.className = 'sf-pill sf-pill--' + (d.sfKind || 'clip'); }
+ if (meta) {
+ meta.replaceChildren();
+ if (d.sfChannel) { var l = el('a'); l.href = 'https://openvibe.live/@' + encodeURIComponent(d.sfChannel); l.textContent = '@' + d.sfChannel; meta.appendChild(l); }
+ }
+ if (c.scrollIntoView && window.innerWidth >= 980) c.scrollIntoView({ block: 'nearest', behavior: reduced() ? 'auto' : 'smooth' });
+ }
+ root.querySelector('[data-sf-prev]').addEventListener('click', function () { show(cur - 1, -1); });
+ root.querySelector('[data-sf-next]').addEventListener('click', function () { show(cur + 1, 1); });
+ chs.forEach(function (c, n) {
+ c.querySelector('.sf-ch-link').addEventListener('click', function (e) { if (e.metaKey || e.ctrlKey || e.shiftKey) return; e.preventDefault(); show(n, n > cur ? 1 : -1); screen.scrollIntoView && window.innerWidth < 980 && screen.scrollIntoView({ block: 'start', behavior: reduced() ? 'auto' : 'smooth' }); });
+ });
+ document.addEventListener('keydown', function (e) {
+ if (!document.body.contains(root)) return;
+ var t = e.target; if (t && (t.tagName === 'INPUT' || t.tagName === 'TEXTAREA' || t.isContentEditable)) return;
+ if (e.key === 'ArrowDown') { e.preventDefault(); show(cur + 1, 1); } else if (e.key === 'ArrowUp') { e.preventDefault(); show(cur - 1, -1); }
+ });
+ // A vertical swipe on the screen changes channel (the video's own controls keep their taps).
+ var y0 = null, x0 = null;
+ screen.addEventListener('touchstart', function (e) { var t = e.touches[0]; y0 = t.clientY; x0 = t.clientX; }, { passive: true });
+ screen.addEventListener('touchend', function (e) {
+ if (y0 == null) return; var t = e.changedTouches[0], dy = t.clientY - y0, dx = t.clientX - x0; y0 = null;
+ if (Math.abs(dy) > 60 && Math.abs(dy) > Math.abs(dx) * 1.5) show(cur + (dy < 0 ? 1 : -1), dy < 0 ? 1 : -1);
+ }, { passive: true });
+ var first = chs[0];
+ if (first && first.dataset.sfPlayType === 'hls') show(0, 1);
+ }
+
+ // ── Web: start in the page ────────────────────────────────
+ function webForm() {
+ var f = document.querySelector('[data-sf-web-form]');
+ if (!f || f.dataset.ready) return;
+ f.dataset.ready = '1';
+ var box = f.querySelector('textarea'), status = document.getElementById('sf-web-status'), btn = f.querySelector('button[type=submit]');
+ document.querySelectorAll('[data-sf-example]').forEach(function (b) {
+ b.addEventListener('click', function () { box.value = b.getAttribute('data-sf-example'); box.focus(); });
+ });
+ f.addEventListener('submit', function (e) {
+ e.preventDefault();
+ var q = box.value.trim();
+ if (q.length < 3) { status.textContent = 'Say a little more: at least 3 characters.'; status.classList.add('is-bad'); return; }
+ btn.disabled = true; status.classList.remove('is-bad'); status.textContent = 'Starting the agent…';
+ fetch('/api/v1/web/tasks', { method: 'POST', credentials: 'same-origin', headers: { 'content-type': 'application/json', accept: 'application/json' }, body: JSON.stringify({ query: q }) })
+ .then(function (r) { return r.json().catch(function () { return {}; }).then(function (b) { return { r: r, b: b }; }); })
+ .then(function (x) {
+ if (x.r.status === 401) { location.href = '/auth/login?next=%2Fweb'; return; }
+ if (!x.r.ok || !x.b.page) { throw new Error(x.b.detail || x.b.title || 'That did not start. Try again in a moment.'); }
+ status.textContent = 'Started. Taking you to the task…';
+ location.href = x.b.page;
+ })
+ .catch(function (err) { btn.disabled = false; status.textContent = err.message; status.classList.add('is-bad'); });
+ });
+ }
+
+ function run() { runMotif(); tv(); webForm(); }
+ if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', run); else run();
+ document.addEventListener('ov:boost:load', run);
+})();
new file mode 100644
new file mode 100644
@@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
+!function e(t){var r,i;r=this,i=function(){"use strict";function r(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,i=Array(t);r<t;r++)i[r]=e[r];return i}function i(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,g(i.key),i)}}function a(e,t,r){return t&&i(e.prototype,t),r&&i(e,r),Object.defineProperty(e,"prototype",{writable:!1}),e}function n(e,t){var i="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(i)return(i=i.call(e)).next.bind(i);if(Array.isArray(e)||(i=function(e,t){if(e){if("string"==typeof e)return r(e,t);var i={}.toString.call(e).slice(8,-1);return"Object"===i&&e.constructor&&(i=e.constructor.name),"Map"===i||"Set"===i?Array.from(e):"Arguments"===i||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i)?r(e,t):void 0}}(e))||t){i&&(e=i);var a=0;return function(){return a>=e.length?{done:!0}:{done:!1,value:e[a++]}}}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 s(e,t,r){return(t=g(t))in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function o(){return o=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var i in r)({}).hasOwnProperty.call(r,i)&&(e[i]=r[i])}return e},o.apply(null,arguments)}function l(e){return l=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(e){return e.__proto__||Object.getPrototypeOf(e)},l(e)}function u(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,c(e,t)}function d(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){})))}catch(e){}return(d=function(){return!!e})()}function h(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 f(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?h(Object(r),!0).forEach((function(t){s(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):h(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}function c(e,t){return c=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},c(e,t)}function g(e){var t=function(e,t){if("object"!=typeof e||!e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var i=r.call(e,t);if("object"!=typeof i)return i;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"==typeof t?t:t+""}function v(e){var t="function"==typeof Map?new Map:void 0;return v=function(e){if(null===e||!function(e){try{return-1!==Function.toString.call(e).indexOf("[native code]")}catch(t){return"function"==typeof e}}(e))return e;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,r)}function r(){return function(e,t,r){if(d())return Reflect.construct.apply(null,arguments);var i=[null];i.push.apply(i,t);var a=new(e.bind.apply(e,i));return r&&c(a,r.prototype),a}(e,arguments,l(this).constructor)}return r.prototype=Object.create(e.prototype,{constructor:{value:r,enumerable:!1,writable:!0,configurable:!0}}),c(r,e)},v(e)}function m(e){return e instanceof ArrayBuffer||"undefined"!=typeof SharedArrayBuffer&&e instanceof SharedArrayBuffer}var p="utf-16",y="utf-16be",E="utf-16le",T="utf-8";function S(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var L,R,A={exports:{}},b=(L||(L=1,function(e){var t=Object.prototype.hasOwnProperty,r="~";function i(){}function a(e,t,r){this.fn=e,this.context=t,this.once=r||!1}function n(e,t,i,n,s){if("function"!=typeof i)throw new TypeError("The listener must be a function");var o=new a(i,n||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,a=[];if(0===this._eventsCount)return a;for(i in e=this._events)t.call(e,i)&&a.push(r?i.slice(1):i);return Object.getOwnPropertySymbols?a.concat(Object.getOwnPropertySymbols(e)):a},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 a=0,n=i.length,s=new Array(n);a<n;a++)s[a]=i[a].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,a,n,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,a),!0;case 5:return d.fn.call(d.context,t,i,a,n),!0;case 6:return d.fn.call(d.context,t,i,a,n,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,a);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 n(this,e,t,r,!1)},o.prototype.once=function(e,t,r){return n(this,e,t,r,!0)},o.prototype.removeListener=function(e,t,i,a){var n=r?r+e:e;if(!this._events[n])return this;if(!t)return s(this,n),this;var o=this._events[n];if(o.fn)o.fn!==t||a&&!o.once||i&&o.context!==i||s(this,n);else{for(var l=0,u=[],d=o.length;l<d;l++)(o[l].fn!==t||a&&!o[l].once||i&&o[l].context!==i)&&u.push(o[l]);u.length?this._events[n]=1===u.length?u[0]:u:s(this,n)}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}(A)),A.exports),k=S(b),D={exports:{}},I=(R||(R=1,function(e,t){var r,i,a,n,s;r=/^(?=((?:[a-zA-Z0-9+\-.]+:)?))\1(?=((?:\/\/[^\/?#]*)?))\2(?=((?:(?:[^?#\/]*\/)*[^;?#\/]*)?))\3((?:;[^?#]*)?)(\?[^#]*)?(#[^]*)?$/,i=/^(?=([^\/?#]*))\1([^]*)$/,a=/(?:\/|^)\.(?=\/)/g,n=/(?:\/|^)\.\.\/(?!\.\.\/)[^\/]*(?=\/)/g,s={buildAbsoluteURL:function(e,t,r){if(r=r||{},e=e.trim(),!(t=t.trim())){if(!r.alwaysNormalize)return e;var a=s.parseURL(e);if(!a)throw new Error("Error trying to parse base URL.");return a.path=s.normalizePath(a.path),s.buildURLFromParts(a)}var n=s.parseURL(t);if(!n)throw new Error("Error trying to parse relative URL.");if(n.scheme)return r.alwaysNormalize?(n.path=s.normalizePath(n.path),s.buildURLFromParts(n)):t;var o=s.parseURL(e);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:n.netLoc,path:null,params:n.params,query:n.query,fragment:n.fragment};if(!n.netLoc&&(u.netLoc=o.netLoc,"/"!==n.path[0]))if(n.path){var d=o.path,h=d.substring(0,d.lastIndexOf("/")+1)+n.path;u.path=s.normalizePath(h)}else u.path=o.path,n.params||(u.params=o.params,n.query||(u.query=o.query));return null===u.path&&(u.path=r.alwaysNormalize?s.normalizePath(n.path):n.path),s.buildURLFromParts(u)},parseURL:function(e){var t=r.exec(e);return t?{scheme:t[1]||"",netLoc:t[2]||"",path:t[3]||"",params:t[4]||"",query:t[5]||"",fragment:t[6]||""}:null},normalizePath:function(e){for(e=e.split("").reverse().join("").replace(a,"");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=s}(D)),D.exports),_=Number.isFinite||function(e){return"number"==typeof e&&isFinite(e)},C=Number.isSafeInteger||function(e){return"number"==typeof e&&Math.abs(e)<=P},P=Number.MAX_SAFE_INTEGER||9007199254740991,x=function(e){return e.NETWORK_ERROR="networkError",e.MEDIA_ERROR="mediaError",e.KEY_SYSTEM_ERROR="keySystemError",e.MUX_ERROR="muxError",e.OTHER_ERROR="otherError",e}({}),w=function(e){return e.KEY_SYSTEM_NO_KEYS="keySystemNoKeys",e.KEY_SYSTEM_NO_ACCESS="keySystemNoAccess",e.KEY_SYSTEM_NO_SESSION="keySystemNoSession",e.KEY_SYSTEM_NO_CONFIGURED_LICENSE="keySystemNoConfiguredLicense",e.KEY_SYSTEM_LICENSE_REQUEST_FAILED="keySystemLicenseRequestFailed",e.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED="keySystemServerCertificateRequestFailed",e.KEY_SYSTEM_SERVER_CERTIFICATE_UPDATE_FAILED="keySystemServerCertificateUpdateFailed",e.KEY_SYSTEM_SESSION_UPDATE_FAILED="keySystemSessionUpdateFailed",e.KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED="keySystemStatusOutputRestricted",e.KEY_SYSTEM_STATUS_INTERNAL_ERROR="keySystemStatusInternalError",e.KEY_SYSTEM_DESTROY_MEDIA_KEYS_ERROR="keySystemDestroyMediaKeysError",e.KEY_SYSTEM_DESTROY_CLOSE_SESSION_ERROR="keySystemDestroyCloseSessionError",e.KEY_SYSTEM_DESTROY_REMOVE_SESSION_ERROR="keySystemDestroyRemoveSessionError",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.PLAYLIST_UNCHANGED_ERROR="playlistUnchangedError",e.LEVEL_LOAD_ERROR="levelLoadError",e.LEVEL_LOAD_TIMEOUT="levelLoadTimeOut",e.LEVEL_PARSING_ERROR="levelParsingError",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.FRAG_GAP="fragGap",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_APPEND_NO_PROGRESS="bufferAppendNoProgress",e.BUFFER_STALLED_ERROR="bufferStalledError",e.BUFFER_FULL_ERROR="bufferFullError",e.BUFFER_SEEK_OVER_HOLE="bufferSeekOverHole",e.BUFFER_NUDGE_ON_STALL="bufferNudgeOnStall",e.ASSET_LIST_LOAD_ERROR="assetListLoadError",e.ASSET_LIST_LOAD_TIMEOUT="assetListLoadTimeout",e.ASSET_LIST_PARSING_ERROR="assetListParsingError",e.INTERSTITIAL_ASSET_ITEM_ERROR="interstitialAssetItemError",e.INTERNAL_EXCEPTION="internalException",e.INTERNAL_ABORTED="aborted",e.ATTACH_MEDIA_ERROR="attachMediaError",e.MEDIA_SOURCE_REQUIRES_RESET="mediaSourceRequiresReset",e.UNKNOWN="unknown",e}({}),O=function(e){return e.MEDIA_ATTACHING="hlsMediaAttaching",e.MEDIA_ATTACHED="hlsMediaAttached",e.MEDIA_DETACHING="hlsMediaDetaching",e.MEDIA_DETACHED="hlsMediaDetached",e.MEDIA_ENDED="hlsMediaEnded",e.STALL_RESOLVED="hlsStallResolved",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.BUFFERED_TO_END="hlsBufferedToEnd",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.AUDIO_TRACK_UPDATED="hlsAudioTrackUpdated",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_TRACK_UPDATED="hlsSubtitleTrackUpdated",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.MAX_AUTO_LEVEL_UPDATED="hlsMaxAutoLevelUpdated",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",e.STEERING_MANIFEST_LOADED="hlsSteeringManifestLoaded",e.ASSET_LIST_LOADING="hlsAssetListLoading",e.ASSET_LIST_LOADED="hlsAssetListLoaded",e.INTERSTITIALS_UPDATED="hlsInterstitialsUpdated",e.INTERSTITIALS_BUFFERED_TO_BOUNDARY="hlsInterstitialsBufferedToBoundary",e.INTERSTITIAL_ASSET_PLAYER_CREATED="hlsInterstitialAssetPlayerCreated",e.INTERSTITIAL_STARTED="hlsInterstitialStarted",e.INTERSTITIAL_ASSET_STARTED="hlsInterstitialAssetStarted",e.INTERSTITIAL_ASSET_ENDED="hlsInterstitialAssetEnded",e.INTERSTITIAL_ASSET_ERROR="hlsInterstitialAssetError",e.INTERSTITIAL_ENDED="hlsInterstitialEnded",e.INTERSTITIALS_PRIMARY_RESUMED="hlsInterstitialsPrimaryResumed",e.PLAYOUT_LIMIT_REACHED="hlsPlayoutLimitReached",e.EVENT_CUE_ENTER="hlsEventCueEnter",e}({}),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}},F=function(e){return e.MANIFEST="manifest",e.LEVEL="level",e.AUDIO_TRACK="audioTrack",e.SUBTITLE_TRACK="subtitleTrack",e.MEDIA_FRAGMENT="media-fragment",e.KEY="key",e.STEERING_MANIFEST="steering-manifest",e.SERVER_CERTIFICATE="server-certificate",e.INTERSTITIAL_ASSET_LIST="interstitial-asset-list",e}({}),N=function(e){return e.MAIN="main",e.AUDIO="audio",e.SUBTITLE="subtitle",e}({}),B={AUDIO:"audio",VIDEO:"video",AUDIOVIDEO:"audiovideo"},U=function(){function e(e){var t,r,i;this._byteRange=null,this._url=null,this._stats=null,this._streams=null,this.base=void 0,this.relurl=void 0,"string"==typeof e&&(e={url:e}),this.base=e,(i=W(t=this,r="stats"))&&(i.enumerable=!0,Object.defineProperty(t,r,i))}var t=e.prototype;return t.setByteRange=function(e,t){var r,i=e.split("@",2);r=1===i.length?(null==t?void 0:t.byteRangeEndOffset)||0:parseInt(i[1]),this._byteRange=[r,parseInt(i[0])+r]},t.clearElementaryStreamInfo=function(){var e=this.elementaryStreams;e[B.AUDIO]=null,e[B.VIDEO]=null,e[B.AUDIOVIDEO]=null,this.url=null},a(e,[{key:"baseurl",get:function(){return this.base.url}},{key:"byteRange",get:function(){return null===this._byteRange?[]:this._byteRange}},{key:"byteRangeStartOffset",get:function(){return this.byteRange[0]}},{key:"byteRangeEndOffset",get:function(){return this.byteRange[1]}},{key:"elementaryStreams",get:function(){var e;return null===this._streams&&(this._streams=((e={})[B.AUDIO]=null,e[B.VIDEO]=null,e[B.AUDIOVIDEO]=null,e)),this._streams},set:function(e){this._streams=e}},{key:"hasStats",get:function(){return null!==this._stats}},{key:"hasStreams",get:function(){return null!==this._streams}},{key:"stats",get:function(){return null===this._stats&&(this._stats=new M),this._stats},set:function(e){this._stats=e}},{key:"url",get:function(){return!this._url&&this.relurl&&(this._url=I.buildAbsoluteURL(this.baseurl,this.relurl,{alwaysNormalize:!0})),this._url||""},set:function(e){this._url=e}}])}();function G(e){return"initSegment"!==e.sn}function V(e,t){return e.sn===(null==t?void 0:t.sn)&&e.level===t.level}function H(e,t){return e.sn===(null==t?void 0:t.sn)&&e.level===t.level&&e.cc===t.cc}var K=function(e){function t(t,r){var i;return(i=e.call(this,r)||this)._decryptdata=null,i._programDateTime=null,i._ref=null,i._bitrate=void 0,i.rawProgramDateTime=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.playlistOffset=0,i.deltaPTS=void 0,i.maxStartPTS=void 0,i.minEndPTS=void 0,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=t,i}u(t,e);var r=t.prototype;return r.addStart=function(e){this.setStart(this.start+e)},r.setStart=function(e){this.start=e,this._ref&&(this._ref.start=e)},r.setDuration=function(e){this.duration=e,this._ref&&(this._ref.duration=e)},r.setKeyFormat=function(e){var t=this.levelkeys;if(t){var r,i=t[e];!i||null!=(r=this._decryptdata)&&r.keyId||(this._decryptdata=i.getDecryptData(this.sn,t))}},r.abortRequests=function(){var e,t;null==(e=this.loader)||e.abort(),null==(t=this.keyLoader)||t.abort()},r.setElementaryStreamInfo=function(e,t,r,i,a,n){void 0===n&&(n=!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,a)):s[e]={startPTS:t,endPTS:r,startDTS:i,endDTS:a,partial:n}},a(t,[{key:"byteLength",get:function(){if(this.hasStats){var e=this.stats.total;if(e)return e}if(this.byteRange.length){var t=this.byteRange[0],r=this.byteRange[1];if(_(t)&&_(r))return r-t}return null}},{key:"bitrate",get:function(){return this.byteLength?8*this.byteLength/this.duration:this._bitrate?this._bitrate:null},set:function(e){this._bitrate=e}},{key:"decryptdata",get:function(){var e,t=this.levelkeys;if(!t||t.NONE)return null;if(t.identity)this._decryptdata||(this._decryptdata=t.identity.getDecryptData(this.sn));else if(null==(e=this._decryptdata)||!e.keyId){var r=Object.keys(t);if(1===r.length){var i=this._decryptdata=t[r[0]]||null;i&&(this._decryptdata=i.getDecryptData(this.sn,t))}}return this._decryptdata}},{key:"end",get:function(){return this.start+this.duration}},{key:"endProgramDateTime",get:function(){if(null===this.programDateTime)return null;var e=_(this.duration)?this.duration:0;return this.programDateTime+1e3*e}},{key:"encrypted",get:function(){var e;if(null!=(e=this._decryptdata)&&e.encrypted)return!0;if(this.levelkeys){var t,r=Object.keys(this.levelkeys),i=r.length;if(i>1||1===i&&null!=(t=this.levelkeys[r[0]])&&t.encrypted)return!0}return!1}},{key:"programDateTime",get:function(){return null===this._programDateTime&&this.rawProgramDateTime&&(this.programDateTime=Date.parse(this.rawProgramDateTime)),this._programDateTime},set:function(e){_(e)?this._programDateTime=e:this._programDateTime=this.rawProgramDateTime=null}},{key:"ref",get:function(){return G(this)?(this._ref||(this._ref={base:this.base,start:this.start,duration:this.duration,sn:this.sn,programDateTime:this.programDateTime}),this._ref):null}}])}(U),Y=function(e){function t(t,r,i,a,n){var s;(s=e.call(this,i)||this).fragOffset=0,s.duration=0,s.independent=!1,s.relurl=void 0,s.fragment=void 0,s.index=void 0,s.gap=!1,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=a;var o=t.enumeratedString("BYTERANGE");return o&&s.setByteRange(o,n),n&&(s.fragOffset=n.fragOffset+n.duration),s}return u(t,e),a(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||this.fragment.type===N.SUBTITLE&&this.stats.loading.end>0)}}])}(U);function W(e,t){var r=Object.getPrototypeOf(e);if(r){var i=Object.getOwnPropertyDescriptor(r,t);return i||W(r,t)}}function j(e){switch(e.details){case w.FRAG_LOAD_TIMEOUT:case w.KEY_LOAD_TIMEOUT:case w.LEVEL_LOAD_TIMEOUT:case w.MANIFEST_LOAD_TIMEOUT:case w.AUDIO_TRACK_LOAD_TIMEOUT:case w.SUBTITLE_TRACK_LOAD_TIMEOUT:case w.ASSET_LIST_LOAD_TIMEOUT:return!0}return!1}function q(e){return e.details.startsWith("key")}function X(e){return q(e)&&!!e.frag&&!e.frag.decryptdata}function z(e,t){var r=j(t);return e.default[(r?"timeout":"error")+"Retry"]}function Q(e,t){var r="linear"===e.backoff?1:Math.pow(2,t);return Math.min(r*e.retryDelayMs,e.maxRetryDelayMs)}function $(e){return f(f({},e),{errorRetry:null,timeoutRetry:null})}function Z(e,t,r,i){if(!e)return!1;var a=null==i?void 0:i.code,n=t<e.maxNumRetry&&(function(e){return J(e)||!!e&&(e<400||e>499)}(a)||!!r);return e.shouldRetry?e.shouldRetry(e,t,r,i,n):n}function J(e){return 0===e&&!1===navigator.onLine}function ee(e,t){return t>0&&e.loadError>0&&0!==e.loadErrorTime&&self.performance.now()-e.loadErrorTime>t}var te=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}(),re=function(){function e(e,t,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 te(e),this.fast_=new te(t),this.defaultTTFB_=i,this.ttfb_=new te(e)}var t=e.prototype;return t.update=function(e,t){var r=this.slow_,i=this.fast_,a=this.ttfb_;r.halfLife!==e&&(this.slow_=new te(e,r.getEstimate(),r.getTotalWeight())),i.halfLife!==t&&(this.fast_=new te(t,i.getEstimate(),i.getTotalWeight())),a.halfLife!==e&&(this.ttfb_=new te(e,a.getEstimate(),a.getTotalWeight()))},t.sample=function(e,t){if(!(t<=0)){var r=Math.min(this.minDelayMs_,t/1e5),i=8*t,a=Math.max(e,r)/1e3,n=i/a;this.fast_.sample(a,n),this.slow_.sample(a,n)}},t.sampleTTFB=function(e){var t=e/1e3,r=Math.sqrt(2)*Math.exp(-Math.pow(t,2)/2);this.ttfb_.sample(r,Math.max(e,5))},t.canEstimate=function(){return this.fast_.getTotalWeight()>=this.minWeight_},t.getEstimate=function(){return this.canEstimate()?Math.min(this.fast_.getEstimate(),this.slow_.getEstimate()):this.defaultEstimate_},t.getEstimateTTFB=function(){return this.ttfb_.getTotalWeight()>=this.minWeight_?this.ttfb_.getEstimate():this.defaultTTFB_},t.destroy=function(){},a(e,[{key:"defaultEstimate",get:function(){return this.defaultEstimate_}}])}(),ie=function(e,t){this.trace=void 0,this.debug=void 0,this.log=void 0,this.warn=void 0,this.info=void 0,this.error=void 0;var r="["+e+"]:";this.trace=ae,this.debug=t.debug.bind(null,r),this.log=t.log.bind(null,r),this.warn=t.warn.bind(null,r),this.info=t.info.bind(null,r),this.error=t.error.bind(null,r)},ae=function(){},ne={trace:ae,debug:ae,log:ae,warn:ae,info:ae,error:ae};function se(){return o({},ne)}function oe(e,t,r){return t[e]?t[e].bind(t):function(e,t){var r=self.console[e];return r?r.bind(self.console,(t?"["+t+"] ":"")+"["+e+"] >"):ae}(e,r)}var le=se();function ue(e,t,r){var i=se();if("object"==typeof console&&!0===e||"object"==typeof e){var a=["debug","log","info","warn","error"];a.forEach((function(t){i[t]=oe(t,e,r)}));try{i.log('Debug logs enabled for "'+t+'" in hls.js version 1.7.3')}catch(e){return se()}a.forEach((function(t){le[t]=oe(t,e)}))}else o(le,i);return i}var de,he,fe=le,ce=he?de:(he=1,de=void 0),ge=S(ce);function ve(e){if(void 0===e&&(e=!0),"undefined"!=typeof self)return(e||!self.MediaSource)&&self.ManagedMediaSource||self.MediaSource||self.WebKitMediaSource}function me(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}function pe(e,t){var r=0;return r=(127&e[t])<<21,r|=(127&e[t+1])<<14,r|=(127&e[t+2])<<7,r|=127&e[t+3]}function ye(e,t){return me(e,t)&&pe(e,t+6)+10<=e.length-t}function Ee(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}function Te(e,t){for(var r=t,i=0;me(e,t);)i+=10,i+=pe(e,t+6),Ee(e,t+10)&&(i+=10),t+=i;if(i>0)return e.subarray(r,r+i)}function Se(e,t,r){return void 0===t&&(t=0),void 0===r&&(r=1/0),function(e,t,r,i){var a,n=m(a=e)?a:a.buffer,s=1;"BYTES_PER_ELEMENT"in i&&(s=i.BYTES_PER_ELEMENT);var o,l=(o=e)&&m(o.buffer)&&void 0!==o.byteLength&&void 0!==o.byteOffset?e.byteOffset:0,u=(l+e.byteLength)/s,d=(l+t)/s,h=Math.floor(Math.max(0,Math.min(d,u)));return new i(n,h,Math.floor(Math.min(h+Math.max(r,0),u))-h)}(e,t,r,Uint8Array)}function Le(e,t){void 0===t&&(t=!1);var r=t?e.indexOf(0):e.length,i=function(e,t){var r;void 0===t&&(t={}),r=m(e)?new DataView(e):new DataView(e.buffer,e.byteOffset,e.byteLength);var i=0,a=t.encoding;if(!a){var n=r.getUint8(0),s=r.getUint8(1);239==n&&187==s&&191==r.getUint8(2)?(a=T,i=3):254==n&&255==s?(a=y,i=2):255==n&&254==s?(a=E,i=2):a=T}if("undefined"!=typeof TextDecoder)return new TextDecoder(a).decode(r);for(var o,l=r.byteLength,u=a!==y,d="";i<l;){switch(a){case T:if((o=r.getUint8(i))<128)i++;else if(o>=194&&o<=223)if(i+1<l){var h=r.getUint8(i+1);h>=128&&h<=191?(o=(31&o)<<6|63&h,i+=2):i++}else i++;else if(o>=224&&o<=239)if(i+2<=l-1){var f=r.getUint8(i+1),c=r.getUint8(i+2);f>=128&&f<=191&&c>=128&&c<=191?(o=(15&o)<<12|(63&f)<<6|63&c,i+=3):i++}else i++;else if(o>=240&&o<=244)if(i+3<=l-1){var g=r.getUint8(i+1),v=r.getUint8(i+2),S=r.getUint8(i+3);g>=128&&g<=191&&v>=128&&v<=191&&S>=128&&S<=191?(o=(7&o)<<18|(63&g)<<12|(63&v)<<6|63&S,i+=4):i++}else i++;else i++;break;case y:case p:case E:o=r.getUint16(i,u),i+=2}d+=String.fromCodePoint(o)}return d}(new DataView(e.buffer,e.byteOffset,r),{encoding:T});return t?i:i.replace(/\0/g,"")}function Re(e){var t={key:e.type,description:"",data:"",mimeType:null,pictureType:null};if(!(e.size<2))if(3===e.data[0]){var r=e.data.subarray(1).indexOf(0);if(-1!==r){var i=Le(Se(e.data,1,r)),a=e.data[2+r],n=e.data.subarray(3+r).indexOf(0);if(-1!==n){var s,o,l=Le(Se(e.data,3+r,n));return s="--\x3e"===i?Le(Se(e.data,4+r+n)):m(o=e.data.subarray(4+r+n))?o:0==o.byteOffset&&o.byteLength==o.buffer.byteLength?o.buffer:new Uint8Array(o).buffer,t.mimeType=i,t.pictureType=a,t.description=l,t.data=s,t}}}else console.log("Ignore frame with unrecognized character encoding")}function Ae(e){return"PRIV"===e.type?function(e){if(!(e.size<2)){var t=Le(e.data,!0),r=new Uint8Array(e.data.subarray(t.length+1));return{key:e.type,info:t,data:r.buffer}}}(e):"W"===e.type[0]?function(e){if("WXXX"===e.type){if(e.size<2)return;var t=1,r=Le(e.data.subarray(t),!0);t+=r.length+1;var i=Le(e.data.subarray(t));return{key:e.type,info:r,data:i}}var a=Le(e.data);return{key:e.type,info:"",data:a}}(e):"APIC"===e.type?Re(e):function(e){if(!(e.size<2)){if("TXXX"===e.type){var t=1,r=e.data,i=Le(r.subarray(t),!0);t+=i.length+1;var a=Le(r.subarray(t));return{key:e.type,info:i,data:a}}var n=Le(e.data.subarray(1));return{key:e.type,info:"",data:n}}}(e)}function be(e){var t=String.fromCharCode(e[0],e[1],e[2],e[3]),r=pe(e,4);return{type:t,size:r,data:e.subarray(10,10+r)}}var ke=10,De=10;function Ie(e){for(var t=0,r=[];me(e,t);){var i=pe(e,t+6);e[t+5]>>6&1&&(t+=ke);for(var a=(t+=ke)+i;t+De<a;){var n=be(e.subarray(t)),s=Ae(n);s&&r.push(s),t+=n.size+ke}Ee(e,t)&&(t+=ke)}return r}function _e(e){return e&&"PRIV"===e.key&&"com.apple.streaming.transportStreamTimestamp"===e.info}function Ce(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)}}function Pe(e){for(var t,r=n(Ie(e));!(t=r()).done;){var i=t.value;if(_e(i))return Ce(i)}}function xe(e){for(var t="",r=0;r<e.length;r++){var i=e[r].toString(16);i.length<2&&(i="0"+i),t+=i}return t}function we(e){return Uint8Array.from(e.replace(/^0x/,"").replace(/([\da-fA-F]{2}) ?/g,"0x$1 ").replace(/ +$/,"").split(" ")).buffer}function Oe(e){return String.fromCharCode.apply(null,e)}var Me,Fe=Math.pow(2,32)-1,Ne={avc1:1635148593,avcC:1635148611,hvc1:1752589105,hvcC:1752589123,btrt:1651798644,dinf:1684631142,dref:1685218662,esds:1702061171,free:1718773093,ftyp:1718909296,hdlr:1751411826,mdat:1835295092,mdhd:1835296868,mdia:1835297121,mfhd:1835427940,minf:1835626086,moof:1836019558,moov:1836019574,mp4a:1836069985,".mp3":778924083,dac3:1684103987,"ac-3":1633889587,mvex:1836475768,mvhd:1836476516,pasp:1885434736,sdtp:1935963248,stbl:1937007212,stco:1937007471,stsc:1937011555,stsd:1937011556,stsz:1937011578,stts:1937011827,tfdt:1952867444,tfhd:1952868452,traf:1953653094,trak:1953653099,trun:1953658222,trex:1953654136,tkhd:1953196132,vmhd:1986881636,smhd:1936549988},Be={video:1,audio:2,id3:3,text:4};function Ue(e,t){var r=e[t]<<8|e[t+1];return r<0?65536+r:r}function Ge(e,t){var r=e[t]<<24|e[t+1]<<16|e[t+2]<<8|e[t+3];return r<0?4294967296+r:r}function Ve(e,t){var r=Ge(e,t);return r*=Math.pow(2,32),r+=Ge(e,t+4)}function He(e,t){for(var r="",i=t;i<e.length;i++)if(r+=String.fromCharCode(e[i]),0===e[i])return r;return null}function Ke(e,t){return e[t]<<24|e[t+1]<<16|e[t+2]<<8|e[t+3]}function Ye(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 We(e,t){var r=[];return t.length?(je(e,t,0,0,e.byteLength,r),r):r}function je(e,t,r,i,a,n){var s=t[r];if(4===(null==s?void 0:s.length))for(var o=s.charCodeAt(0),l=s.charCodeAt(1),u=s.charCodeAt(2),d=s.charCodeAt(3),h=t.length-1,f=i;f<a;){var c=f+8<=a,g=f+4<=a?16777216*e[f]+(e[f+1]<<16|e[f+2]<<8|e[f+3]):Ge(e,f),v=g>1?f+g:a;if(c&&e[f+4]===o&&e[f+5]===l&&e[f+6]===u&&e[f+7]===d){var m=Math.min(v,a);r===h?n.push(e.subarray(f+8,m)):je(e,t,r+1,f+8,m,n)}f=v}}function qe(e){var t=[],r=e[0],i=8,a=Ge(e,i);i+=4;var n=0,s=0;0===r?(n=Ge(e,i),s=Ge(e,i+4),i+=8):(n=Ve(e,i),s=Ve(e,i+8),i+=16),i+=2;var o=e.length+s,l=Ue(e,i);i+=2;for(var u=0;u<l;u++){var d=i,h=Ge(e,d);d+=4;var f=2147483647&h;if(1==(2147483648&h)>>>31)return fe.warn("SIDX has hierarchical references (not supported)"),null;var c=Ge(e,d);d+=4,t.push({referenceSize:f,subsegmentDuration:c,info:{duration:c/a,start:o,end:o+f-1}}),o+=f,i=d+=4}return{earliestPresentationTime:n,timescale:a,version:r,referencesCount:l,references:t}}function Xe(e){for(var t=[],r=We(e,["moov","trak"]),i=0;i<r.length;i++){var a=r[i],n=We(a,["tkhd"])[0];if(n){var s=n[0],o=Ge(n,0===s?12:20),l=We(a,["mdia","mdhd"])[0];if(l){var u=Ge(l,0===(s=l[0])?12:20),d=We(a,["mdia","hdlr"])[0];if(d){var h=Oe(d.subarray(8,12)),c={soun:B.AUDIO,vide:B.VIDEO}[h],g=ze(We(a,["mdia","minf","stbl","stsd"])[0]);c?(t[o]={timescale:u,type:c,stsd:g},t[c]=f({timescale:u,id:o},g)):t[o]={timescale:u,type:h,stsd:g}}}}}return We(e,["moov","mvex","trex"]).forEach((function(e){var r=Ge(e,4),i=t[r];i&&(i.default={duration:Ge(e,12),sampleSize:Ge(e,16),flags:Ge(e,20)})})),t}function ze(e){var t,r=e.subarray(8),i=r.subarray(86),a=Oe(r.subarray(4,8)),n=a,s="enca"===a||"encv"===a;if(s){var o=We(r,[a])[0];We(o.subarray("enca"===a?28:78),["sinf"]).forEach((function(e){var t=We(e,["schm"])[0];if(t){var r=Oe(t.subarray(4,8));if("cbcs"===r||"cenc"===r){var i=We(e,["frma"])[0];i&&(n=Oe(i))}}}))}var l=n;switch(n){case"avc1":case"avc2":case"avc3":case"avc4":var u=We(i,["avcC"])[0];u&&u.length>3&&(n+="."+Ze(u[1])+Ze(u[2])+Ze(u[3]),t=Qe("avc1"===l?"dva1":"dvav",i));break;case"mp4a":var d=We(r,[a])[0],h=We(d.subarray(28),["esds"])[0];if(h&&h.length>7){var f=4;if(3!==h[f++])break;f=$e(h,f),f+=2;var c=h[f++];if(128&c&&(f+=2),64&c&&(f+=h[f++]),4!==h[f++])break;f=$e(h,f);var g=h[f++];if(64!==g)break;if(n+="."+Ze(g),f+=12,5!==h[f++])break;f=$e(h,f);var v=h[f++],m=(248&v)>>3;31===m&&(m+=1+((7&v)<<3)+((224&h[f])>>5)),n+="."+m}break;case"hvc1":case"hev1":var p=We(i,["hvcC"])[0];if(p&&p.length>12){var y=p[1],E=["","A","B","C"][y>>6],T=31&y,S=Ge(p,2),L=(32&y)>>5?"H":"L",R=p[12],A=p.subarray(6,12);n+="."+E+T,n+="."+function(e){for(var t=0,r=0;r<32;r++)t|=(e>>r&1)<<31-r;return t>>>0}(S).toString(16).toUpperCase(),n+="."+L+R;for(var b="",k=A.length;k--;){var D=A[k];(D||b)&&(b="."+D.toString(16).toUpperCase()+b)}n+=b}t=Qe("hev1"==l?"dvhe":"dvh1",i);break;case"dvh1":case"dvhe":case"dvav":case"dva1":case"dav1":n=Qe(n,i)||n;break;case"vp09":var I=We(i,["vpcC"])[0];if(I&&I.length>6){var _=I[4],C=I[5],P=I[6]>>4&15;n+="."+Je(_)+"."+Je(C)+"."+Je(P)}break;case"av01":var x=We(i,["av1C"])[0];if(x&&x.length>2){var w=x[1]>>>5,O=31&x[1],M=x[2]>>>7?"H":"M",F=(64&x[2])>>6,N=(32&x[2])>>5,B=2===w&&F?N?12:10:F?10:8,U=(16&x[2])>>4,G=(8&x[2])>>3,V=(4&x[2])>>2,H=3&x[2];n+="."+w+"."+Je(O)+M+"."+Je(B)+"."+U+"."+G+V+H+"."+Je(1)+"."+Je(1)+"."+Je(1)+".0",t=Qe("dav1",i)}}return{codec:n,encrypted:s,supplemental:t}}function Qe(e,t){var r=We(t,["dvvC"]),i=r.length?r[0]:We(t,["dvcC"])[0];if(i){var a=i[2]>>1&127,n=i[2]<<5&32|i[3]>>3&31;return e+"."+Je(a)+"."+Je(n)}}function $e(e,t){for(var r=t+5;128&e[t++]&&t<r;);return t}function Ze(e){return("0"+e.toString(16).toUpperCase()).slice(-2)}function Je(e){return(e<10?"0":"")+e}function et(e,t){We(e,["moov","trak"]).forEach((function(e){var r=We(e,["mdia","minf","stbl","stsd"])[0];if(r){var i=r.subarray(8),a=We(i,["enca"]),n=a.length>0;n||(a=We(i,["encv"])),a.forEach((function(e){We(n?e.subarray(28):e.subarray(78),["sinf"]).forEach((function(e){var r=function(e){var t=We(e,["schm"])[0];if(t){var r=Oe(t.subarray(4,8));if("cbcs"===r||"cenc"===r){var i=We(e,["schi","tenc"])[0];if(i)return i}}}(e);r&&t(r,n)}))}))}}))}function tt(e){return"undefined"==typeof TextDecoder?Le(e):(Me||(Me=new TextDecoder("utf-8"))).decode(e).replace(/\0/g,"")}function rt(e,t,r,i,a){for(var n={},s=!1,o=function(e){for(var t=e.byteLength,r=[],i=0;i<t;){var a=i+8<=t,n=i+4<=t?16777216*e[i]+(e[i+1]<<16|e[i+2]<<8|e[i+3]):Ge(e,i),s=n>1?i+n:t;if(a&&109===e[i+4]&&111===e[i+5]&&111===e[i+6]&&102===e[i+7])for(var o=Math.min(s,t),l=i+8;l<o;){var u=l+8<=o,d=l+4<=o?16777216*e[l]+(e[l+1]<<16|e[l+2]<<8|e[l+3]):Ge(e,l),h=d>1?l+d:o;if(u&&116===e[l+4]&&114===e[l+5]&&97===e[l+6]&&102===e[l+7]){for(var f=Math.min(h,o),c=void 0,g=void 0,v=[],m=l+8;m<f;){var p=m+8<=f,y=m+4<=f?16777216*e[m]+(e[m+1]<<16|e[m+2]<<8|e[m+3]):Ge(e,m),E=y>1?m+y:f;if(p&&116===e[m+4]){var T=Math.min(E,f);102===e[m+5]&&104===e[m+6]&&100===e[m+7]?g||(g=e.subarray(m+8,T)):102===e[m+5]&&100===e[m+6]&&116===e[m+7]?c||(c=e.subarray(m+8,T)):114===e[m+5]&&117===e[m+6]&&110===e[m+7]&&v.push(e.subarray(m+8,T))}m=E}r.push({moofOffset:e.byteOffset+i,tfdt:c,tfhd:g,truns:v})}l=h}i=s}return r}(e),l=e.byteLength,u=0;u<o.length;u++){var d=o[u],h=d.moofOffset,f=d.tfdt,c=d.tfhd,g=d.truns,v=Ge(c,4),m=t[v];if(m){var p=n[v]||(n[v]={start:NaN,duration:0,sampleCount:0,trun:[],timescale:m.timescale,type:m.type}),y=void 0;if(f){var E=f[0];y=Ge(f,4),1===E&&(y===Fe?r.warn("[mp4-demuxer]: Ignoring assumed invalid signed 64-bit track fragment decode time"):(y*=Fe+1,y+=Ge(f,8))),_(y)&&(!_(p.start)||y<p.start)&&(p.start=y),a&&_(y)&&(a.timeOffset=y/a.timescale)}var T=(null==a?void 0:a.trackId)===v&&m.type===B.VIDEO,S=m.default,L=16777215&Ge(c,0),R=(null==S?void 0:S.duration)||0,A=(null==S?void 0:S.sampleSize)||0,b=8;1&L&&(b+=8),2&L&&(b+=4),8&L&&(R=Ge(c,b),c.byteOffset,e.byteOffset,b+=4),16&L&&(A=Ge(c,b));var k=0,D=0!=(1&L),I=!D&&0!=(131072&L);D?(k=Ge(c,8),k*=Math.pow(2,32),k+=Ge(c,12)):I&&(k=h);for(var C=y||0,P=0,x=R,w=0;w<g.length;w++){var O=g[w],M=Ge(O,4),F=p.sampleCount;p.sampleCount+=M;var N=1&O[3],U=0,G=4&O[3],V=1&O[2],H=2&O[2],K=4&O[2],Y=8&O[2],W=8;N&&(U=Ke(O,W),W+=4),G&&(1&O[W+1]||void 0!==p.keyFrameIndex||(p.keyFrameIndex=F),W+=4);var j=k+U;if(0!==O[1]||6!==O[2]||1!==O[3]){for(var q=void 0,X=0;X<M;X++){V?(x=Ge(O,W),W+=4):x=R,H?(q=Ge(O,W),W+=4):q=A;var z=void 0;K&&(z=1&O[W+1]?1:0,W+=4);var Q=0;if(Y&&(Q=0===O[0]?Ge(O,W):Ke(O,W),W+=4),T){for(var $=j,Z=0;Z<q;){var J=Ge(e,$);$+=4,nt(a.isHEVCFlavor,e[$])&&st(e.subarray($,$+J),a.isHEVCFlavor?2:1,a.timeOffset+Q/a.timescale,a.samples),$+=J,Z+=J+4}a.timeOffset+=x/a.timescale}if((j+=q)<=l){0===z&&void 0===p.keyFrameIndex&&(p.keyFrameIndex=X,p.keyFrameStart=C);var ee=C+Q;(!_(p.ptsMin)||ee<p.ptsMin)&&(p.ptsMin=ee),(!_(p.ptsMax)||ee+x>p.ptsMax)&&(p.ptsMax=ee+x)}C+=x,P+=x}!P&&R&&(P+=R*M)}else{for(var te,re,ie=C,ae=j,ne=W,se=-1,oe=0,le=0,ue=null!=(te=null==a?void 0:a.timeOffset)?te:0,de=R/(null!=(re=null==a?void 0:a.timescale)?re:1),he=!0===(null==a?void 0:a.isHEVCFlavor),fe=he?2:1,ce=0;ce<M;ce++){var ge=0===O[W]?O[W+1]<<16|O[W+2]<<8|O[W+3]:Ge(O,W),ve=-1===se&&void 0===p.keyFrameIndex&&0==(1&O[W+5]);W+=8;var me=j+ge;if(T){if(ge>0){var pe=j;do{var ye=0===e[pe]?e[pe+1]<<16|e[pe+2]<<8|e[pe+3]:Ge(e,pe),Ee=e[pe+=4],Te=he?Ee>>1&63:31&Ee;(he?39===Te||40===Te:6===Te)&&st(e.subarray(pe,pe+ye),fe,ue,a.samples),pe+=ye}while(pe<me)}ue+=de}j=me,ve&&(se=ce,oe=j)}if(j<=l)le=M;else for(var Se=ae,Le=ne,Re=0;Re<M&&!((Se+=0===O[Le]?O[Le+1]<<16|O[Le+2]<<8|O[Le+3]:Ge(O,Le))>l);Re++)le=Re+1,Le+=8;if(T&&(a.timeOffset=ue),-1!==se&&oe<=l&&void 0===p.keyFrameIndex&&(p.keyFrameIndex=se,p.keyFrameStart=ie+se*R),le){(!_(p.ptsMin)||ie<p.ptsMin)&&(p.ptsMin=ie);var Ae=ie+le*R;(!_(p.ptsMax)||Ae>p.ptsMax)&&(p.ptsMax=Ae)}var be=R*M;C=ie+be,P+=be}}p.duration+=P,p.duration&&(s=!0)}}if(!s){for(var ke=1/0,De=0,Ie=We(e,["sidx"]),_e=0;_e<Ie.length;_e++){var Ce=qe(Ie[_e]);if(null!=Ce&&Ce.references){ke=Math.min(ke,Ce.earliestPresentationTime/Ce.timescale);var Pe=Ce.references.reduce((function(e,t){return e+t.info.duration||0}),0);De=Math.max(De,Pe+Ce.earliestPresentationTime/Ce.timescale)}}De&&_(De)&&Object.keys(n).forEach((function(e){n[e].duration||(n[e].duration=De*n[e].timescale-n[e].start)}))}return n}function it(e,t){var r=new Uint8Array(e.length+t.length);return r.set(e),r.set(t,e.length),r}function at(e){if(!e)return!1;var t=e.substring(0,4);return"hvc1"===t||"hev1"===t||"dvh1"===t||"dvhe"===t}function nt(e,t){if(e){var r=t>>1&63;return 39===r||40===r}return 6==(31&t)}function st(e,t,r,i){var a=ot(e),n=0;n+=t;for(var s=0,o=0,l=0;n<a.length;){s=0;do{if(n>=a.length)break;s+=l=a[n++]}while(255===l);o=0;do{if(n>=a.length)break;o+=l=a[n++]}while(255===l);var u=a.length-n,d=n;if(o<u)n+=o;else if(o>u){fe.error("Malformed SEI payload. "+o+" is too small, only "+u+" bytes left to parse.");break}if(4===s){if(181===a[d++]){var h=Ue(a,d);if(d+=2,49===h){var f=Ge(a,d);if(d+=4,1195456820===f){var c=a[d++];if(3===c){var g=a[d++],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]=a[d++]}i.push({type:c,payloadType:s,pts:r,bytes:p})}}}}}else if(5===s&&o>16){for(var E=[],T=0;T<16;T++){var S=a[d++].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,R=new Uint8Array(L),A=0;A<L;A++)R[A]=a[d++];i.push({payloadType:s,pts:r,uuid:E.join(""),userData:tt(R),userDataBytes:R})}}}function ot(e){for(var t,r=e.byteLength,i=e.indexOf(3,3);-1!==i;)0===e[i-1]&&0===e[i-2]&&(t||(t=[])).push(i),i=e.indexOf(3,i+1);if(!t)return e;for(var a=r-t.length,n=new Uint8Array(a),s=0,o=0,l=0;l<a;o++,l++)o===t[s]&&(o++,s++),n[l]=e[o];return n}var lt,ut,dt,ht=ft("userAgent");function ft(e){try{return navigator[e]||""}catch(e){}return""}function ct(){if(void 0===lt){var e=ht.match(/Chrome\/(\d+)/i);lt=e?parseInt(e[1]):0}return lt}ft("vendor");var gt={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,dav1:.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},image:{mjpg:1}};function vt(e,t){var r=gt[t];return!!r&&!!r[e.slice(0,4)]}function mt(e,t,r){return void 0===r&&(r=!0),!e.split(",").some((function(e){return!pt(e,t,r)}))}function pt(e,t,r){var i;void 0===r&&(r=!0);var a=ve(r);return null!=(i=null==a?void 0:a.isTypeSupported(yt(e,t)))&&i}function yt(e,t){return t+"/mp4;codecs="+e}function Et(e){if(e){var t=e.substring(0,4);return gt.video[t]}return 2}function Tt(e){var t=/\(Windows.+Firefox\//i.test(ht);return e.split(",").reduce((function(e,r){var i=t&&at(r)?9:gt.video[r];return i?(2*i+e)/(e?3:2):(gt.audio[r]+e)/(e?2:1)}),0)}var St={},Lt=/flac|opus|mp4a\.40\.34/i;function Rt(e,t){return void 0===t&&(t=!0),e.replace(Lt,(function(e){return function(e,t){if(void 0===t&&(t=!0),St[e])return St[e];for(var r={flac:["flac","fLaC","FLAC"],opus:["opus","Opus"],"mp4a.40.34":["mp3"]}[e],i=0;i<r.length;i++){var a;if(pt(r[i],"audio",t))return St[e]=r[i],r[i];if("mp3"===r[i]&&null!=(a=ve(t))&&a.isTypeSupported("audio/mpeg"))return""}return e}(e.toLowerCase(),t)}))}function At(e,t){if(e&&(e.length>4||-1!==["ac-3","ec-3","alac","fLaC","Opus"].indexOf(e))&&(bt(e,"audio")||bt(e,"video")))return e;if(t){var r=t.split(",");if(r.length>1){if(e)for(var i=r.length;i--;)if(r[i].substring(0,4)===e.substring(0,4))return r[i];return r[0]}}return t||e}function bt(e,t){return vt(e,t)&&pt(e,t)}function kt(e){var t=ve(e)||{isTypeSupported:function(){return!1}};return{mpeg:t.isTypeSupported("audio/mpeg"),mp3:t.isTypeSupported('audio/mp4; codecs="mp3"'),ac3:!1}}function Dt(e,t,r){var i=encodeURIComponent(t),a=i+"="+encodeURIComponent(r),n=!1,s=[];e.search.substring(1).split("&").forEach((function(e){e===i||e.startsWith(i+"=")?n||(s.push(a),n=!0):e&&s.push(e)})),n||s.push(a),e.search=s.join("&")}var It=["NONE","TYPE-0","TYPE-1",null],_t=["SDR","PQ","HLG"],Ct="",Pt="YES",xt="v2";function wt(e){var t=e.canSkipUntil,r=e.canSkipDateRanges,i=e.age;return t&&i<t/2?r?xt:Pt:Ct}var Ot=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&&Dt(t,"_HLS_msn",this.msn.toString()),void 0!==this.part&&Dt(t,"_HLS_part",this.part.toString()),this.skip&&Dt(t,"_HLS_skip",this.skip),t.href},e}(),Mt=function(){function e(e){if(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.supplemental=void 0,this.videoCodec=void 0,this.width=void 0,this.imageCodec=void 0,this.iframes=void 0,this.details=void 0,this.fragmentError=0,this.loadError=0,this.loadErrorTime=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=[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.frameRate=e.attrs.optionalFloat("FRAME-RATE",0),this._avgBitrate=e.attrs.decimalInteger("AVERAGE-BANDWIDTH"),this.audioCodec=e.audioCodec,this.videoCodec=e.videoCodec,"imageCodec"in e&&(this.imageCodec=e.imageCodec),this.codecSet=[e.videoCodec,e.audioCodec].filter((function(e){return!!e})).map((function(e){return e.substring(0,4)})).join(","),"supplemental"in e){var t;this.supplemental=e.supplemental;var r=null==(t=e.supplemental)?void 0:t.videoCodec;r&&r!==e.videoCodec&&(this.codecSet+=","+r.substring(0,4))}"iframes"in e&&e.iframes?this.iframes=!0:(this.addGroupId("audio",e.attrs.AUDIO),this.addGroupId("text",e.attrs.SUBTITLES))}var t=e.prototype;return t.hasAudioGroup=function(e){return Ft(this._audioGroups,e)},t.hasSubtitleGroup=function(e){return Ft(this._subtitleGroups,e)},t.addGroupId=function(e,t){if(t)if("audio"===e){var r=this._audioGroups;r||(r=this._audioGroups=[]),-1===r.indexOf(t)&&r.push(t)}else if("text"===e){var i=this._subtitleGroups;i||(i=this._subtitleGroups=[]),-1===i.indexOf(t)&&i.push(t)}},t.addFallback=function(){},a(e,[{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(e){}},{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 e;return null==(e=this.audioGroups)?void 0:e[0]}},{key:"textGroupId",get:function(){var e;return null==(e=this.subtitleGroups)?void 0:e[0]}}])}();function Ft(e,t){return!(!t||!e)&&-1!==e.indexOf(t)}function Nt(e,t){var r=!1,i=[];if(e&&(r="SDR"!==e,i=[e]),t){var a="SDR"!==(i=t.allowedVideoRanges||_t.slice(0)).join("")&&!t.videoCodec;(r=void 0!==t.preferHDR?t.preferHDR:a&&function(){if("function"==typeof matchMedia){var e=matchMedia("(dynamic-range: high)"),t=matchMedia("bad query");if(e.media!==t.media)return!0===e.matches}return!1}())||(i=["SDR"])}return{preferHDR:r,allowedVideoRanges:i}}var Bt=function(e,t){return JSON.stringify(e,function(e){var t=new WeakSet;return function(r,i){if(e&&(i=e(r,i)),"object"==typeof i&&null!==i){if(t.has(i))return;t.add(i)}return i}}(t))};function Ut(e,t){fe.log('[abr] start candidates with "'+e+'" ignored because '+t)}function Gt(e){return e.reduce((function(e,t){var r=e.groups[t.groupId];r||(r=e.groups[t.groupId]={tracks:[],channels:{2:0},hasDefault:!1,hasAutoSelect:!1}),r.tracks.push(t);var i=t.channels||"2";return r.channels[i]=(r.channels[i]||0)+1,r.hasDefault=r.hasDefault||t.default,r.hasAutoSelect=r.hasAutoSelect||t.autoselect,r.hasDefault&&(e.hasDefaultAudio=!0),r.hasAutoSelect&&(e.hasAutoSelectAudio=!0),e}),{hasDefaultAudio:!1,hasAutoSelectAudio:!1,groups:{}})}function Vt(e,t){var r;return!!e&&e!==(null==(r=t.loadLevelObj)?void 0:r.uri)}var Ht=function(e){function t(t){var r;return(r=e.call(this,"abr",t.logger)||this).hls=void 0,r.lastLevelLoadSec=0,r.lastLoadedFragLevel=-1,r.firstSelection=-1,r.firstAutoFloor=-1,r._nextAutoLevel=-1,r.nextAutoLevelKey="",r.audioTracksByGroup=null,r.codecTiers=null,r.timer=-1,r.fragCurrent=null,r.partCurrent=null,r.bitrateTestDelay=0,r.rebufferNotice=-1,r.supportedCache={},r.bwEstimator=void 0,r._abandonRulesCheck=function(e){var t,i=r,a=i.fragCurrent,n=i.partCurrent,s=i.hls;if(s){var o=s.autoLevelEnabled,l=s.media;if(a&&l){var u=performance.now(),d=n?n.stats:a.stats,h=n?n.duration:a.duration,f=u-d.loading.start,c=s.minAutoLevel,g=a.level,v=r._nextAutoLevel;if(d.aborted||d.loaded&&d.loaded===d.total||g<=c)return r.clearTimer(),void(r._nextAutoLevel=-1);if(o){var m=v>-1&&v!==g,p=!!e||m;if(p||!l.paused&&l.playbackRate&&l.readyState){var y=s.mainForwardBufferInfo;if(p||null!==y){var E=r.bwEstimator.getEstimateTTFB(),T=Math.abs(l.playbackRate);if(!(f<=Math.max(E,h/(2*T)*1e3))){var S=y?y.len/T:0,L=d.loading.first?d.loading.first-d.loading.start:-1,R=d.loaded&&L>-1,A=r.getBwEstimate(),b=s.levels,k=b[g],D=Math.max(d.loaded,Math.round(h*(a.bitrate||k.averageBitrate)/8)),I=R?f-L:f;I<1&&R&&(I=Math.min(f,8*d.loaded/A));var C=R?1e3*d.loaded/I:0,P=E/1e3,x=C?(D-d.loaded)/C:8*D/A+P;if(!(x<=S)){var w,M=C?8*C:A,F=!0===(null==(t=(null==e?void 0:e.details)||s.latestLevelDetails)?void 0:t.live),N=s.config.abrBandWidthUpFactor,B=Number.POSITIVE_INFINITY;for(w=g-1;w>c;w--){var U=b[w].maxBitrate,G=!b[w].details||F;if((B=r.getTimeToLoadFrag(P,M,h*U,G))<Math.min(S,h+P))break}if(!(B>=x||B>10*h)){R?r.bwEstimator.sample(f-Math.min(E,L),d.loaded):r.bwEstimator.sampleTTFB(f);var V=b[w].maxBitrate;r.getBwEstimate()*N>V&&(r.firstAutoFloor=Math.min(w,s.firstLevel),r.resetEstimator(V));var H=r.findBestLevel(V,c,w,0,S,1,1);H>-1&&(w=H),r.warn("Fragment "+a.sn+(n?" part "+n.index:"")+" of level "+g+" is loading too slowly;\n Fragment duration: "+a.duration.toFixed(3)+"\n Time to underbuffer: "+S.toFixed(3)+" s\n Estimated load time for current fragment: "+x.toFixed(3)+" s\n Estimated load time for down switch fragment: "+B.toFixed(3)+" s\n TTFB estimate: "+(0|L)+" ms\n Current BW estimate: "+(_(A)?0|A:"Unknown")+" bps\n New BW estimate: "+(0|r.getBwEstimate())+" bps\n Switching to level "+w+" @ "+(0|V)+" bps"),s.nextLoadLevel=s.nextAutoLevel=w,r.clearTimer();var K=function(){r.clearTimer();var e=r.hls;if(r.fragCurrent===a&&e.loadLevel===w&&w>0){var t=r.getStarvationDelay();if(r.warn("Aborting inflight request "+(w>0?"and switching down":"")+"\n Fragment duration: "+a.duration.toFixed(3)+" s\n Time to underbuffer: "+t.toFixed(3)+" s"),a.abortRequests(),r.fragCurrent=r.partCurrent=null,w>c){var i=r.findBestLevel(e.levels[c].bitrate,c,w,0,t,1,1);-1===i&&(i=c),e.nextLoadLevel=e.nextAutoLevel=i,r.firstAutoFloor=Math.min(i,e.firstLevel),r.resetEstimator(e.levels[i].bitrate)}}};m||x>2*B?K():r.timer=self.setInterval(K,1e3*B),s.trigger(O.FRAG_LOAD_EMERGENCY_ABORTED,{frag:a,part:n,stats:d})}}}}}}}}},r.hls=t,r.bwEstimator=r.initEstimator(),r.registerListeners(),r}u(t,e);var r=t.prototype;return r.resetEstimator=function(e){this.hls&&(e&&(this.log("setting initial bwe to "+e),this.hls.config.abrEwmaDefaultEstimate=e),this.firstSelection=-1,this.bwEstimator=this.initEstimator())},r.initEstimator=function(){var e=this.hls.config;return new re(e.abrEwmaSlowVoD,e.abrEwmaFastVoD,e.abrEwmaDefaultEstimate)},r.registerListeners=function(){var e=this.hls;e.on(O.MANIFEST_LOADING,this.onManifestLoading,this),e.on(O.FRAG_LOADING,this.onFragLoading,this),e.on(O.FRAG_LOADED,this.onFragLoaded,this),e.on(O.FRAG_BUFFERED,this.onFragBuffered,this),e.on(O.LEVEL_SWITCHING,this.onLevelSwitching,this),e.on(O.LEVEL_LOADED,this.onLevelLoaded,this),e.on(O.LEVELS_UPDATED,this.onLevelsUpdated,this),e.on(O.MAX_AUTO_LEVEL_UPDATED,this.onMaxAutoLevelUpdated,this),e.on(O.ERROR,this.onError,this)},r.unregisterListeners=function(){var e=this.hls;e&&(e.off(O.MANIFEST_LOADING,this.onManifestLoading,this),e.off(O.FRAG_LOADING,this.onFragLoading,this),e.off(O.FRAG_LOADED,this.onFragLoaded,this),e.off(O.FRAG_BUFFERED,this.onFragBuffered,this),e.off(O.LEVEL_SWITCHING,this.onLevelSwitching,this),e.off(O.LEVEL_LOADED,this.onLevelLoaded,this),e.off(O.LEVELS_UPDATED,this.onLevelsUpdated,this),e.off(O.MAX_AUTO_LEVEL_UPDATED,this.onMaxAutoLevelUpdated,this),e.off(O.ERROR,this.onError,this))},r.destroy=function(){this.unregisterListeners(),this.clearTimer(),this.hls=this._abandonRulesCheck=this.supportedCache=null,this.audioTracksByGroup=this.codecTiers=this.fragCurrent=this.partCurrent=null},r.onManifestLoading=function(e,t){this.lastLoadedFragLevel=-1,this.firstSelection=-1,this.firstAutoFloor=-1,this.lastLevelLoadSec=0,this.supportedCache={},this.fragCurrent=this.partCurrent=null,this.onLevelsUpdated(),this.clearTimer()},r.onLevelsUpdated=function(){this.lastLoadedFragLevel>-1&&this.fragCurrent&&(this.lastLoadedFragLevel=this.fragCurrent.level),this._nextAutoLevel=-1,this.onMaxAutoLevelUpdated(),this.codecTiers=null,this.audioTracksByGroup=null},r.onMaxAutoLevelUpdated=function(){this.firstSelection=-1,this.nextAutoLevelKey=""},r.onFragLoading=function(e,t){var r,i=t.frag;this.ignoreFragment(i)||(i.bitrateTest||(this.fragCurrent=i,this.partCurrent=null!=(r=t.part)?r:null),this.clearTimer(),this.timer=self.setInterval(this._abandonRulesCheck,100))},r.onLevelSwitching=function(e,t){this.clearTimer()},r.onError=function(e,t){if(!t.fatal)switch(t.details){case w.BUFFER_ADD_CODEC_ERROR:case w.BUFFER_APPEND_ERROR:this.lastLoadedFragLevel=-1,this.firstSelection=-1;break;case w.FRAG_LOAD_TIMEOUT:var r=t.frag,i=this.fragCurrent,a=this.partCurrent;if(r&&H(r,i)){var n=performance.now(),s=a?a.stats:r.stats,o=n-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)}}},r.getTimeToLoadFrag=function(e,t,r,i){return e+r/t+(i?e+this.lastLevelLoadSec:0)},r.onLevelLoaded=function(e,t){var r=this.hls.config,i=t.stats.loading,a=i.end-i.first;_(a)&&(this.lastLevelLoadSec=a/1e3),t.details.live?this.bwEstimator.update(r.abrEwmaSlowLive,r.abrEwmaFastLive):this.bwEstimator.update(r.abrEwmaSlowVoD,r.abrEwmaFastVoD),this.timer>-1&&this._abandonRulesCheck(t.levelInfo)},r.onFragLoaded=function(e,t){var r,i=t.frag,a=t.part,n=a?a.stats:i.stats;if(i.type===N.MAIN&&this.bwEstimator.sampleTTFB(n.loading.first-n.loading.start),!this.ignoreFragment(i)){if(this.clearTimer(),i.level===this._nextAutoLevel&&(this._nextAutoLevel=-1),this.firstSelection=-1,null!=(r=this.hls)&&r.config.abrMaxWithRealBitrate){var s=a?a.duration:i.duration,o=this.hls.levels[i.level],l=(o.loaded?o.loaded.bytes:0)+n.loaded,u=(o.loaded?o.loaded.duration:0)+s;o.loaded={bytes:l,duration:u},o.realBitrate=Math.round(8*l/u)}if(i.bitrateTest){var d={stats:n,frag:i,part:a,id:i.type};this.onFragBuffered(O.FRAG_BUFFERED,d),i.bitrateTest=!1}else this.lastLoadedFragLevel=i.level}},r.onFragBuffered=function(e,t){var r=t.frag,i=t.part,a=null!=i&&i.stats.loaded?i.stats:r.stats;if(!a.aborted&&!this.ignoreFragment(r)){var n=((r.bitrate||0)>=1e5?a.parsing.end:a.loading.end)-a.loading.start-Math.min(a.loading.first-a.loading.start,this.bwEstimator.getEstimateTTFB());this.bwEstimator.sample(n,a.loaded),a.bwEstimate=this.getBwEstimate(),r.bitrateTest?this.bitrateTestDelay=n/1e3:(this.bitrateTestDelay=0,this.firstAutoFloor>-1&&this.bwEstimator.canEstimate()&&(this.firstAutoFloor=-1))}},r.ignoreFragment=function(e){return e.type!==N.MAIN||"initSegment"===e.sn},r.clearTimer=function(){this.timer>-1&&(self.clearInterval(this.timer),this.timer=-1)},r.getAutoLevelKey=function(){return this.getBwEstimate()+"_"+this.getStarvationDelay().toFixed(2)},r.getNextABRAutoLevel=function(){var e=this.fragCurrent,t=this.partCurrent,r=this.hls;if(!r)return-1;if(r.levels.length<=1)return r.loadLevel;var i=r.maxAutoLevel,a=r.config,n=r.minAutoLevel,s=t?t.duration:e?e.duration:0,o=this.getBwEstimate(),l=this.getStarvationDelay(),u=a.abrBandWidthFactor,d=a.abrBandWidthUpFactor;if(l){var h=this.findBestLevel(o,n,i,l,0,u,d);if(h>=0)return this.rebufferNotice=-1,h}var f=s?Math.min(s,a.maxStarvationDelay):a.maxStarvationDelay;if(!l){var c=this.bitrateTestDelay;c&&(f=(s?Math.min(s,a.maxLoadingDelay):a.maxLoadingDelay)-c,this.info("bitrate test took "+Math.round(1e3*c)+"ms, set first fragment max fetchDuration to "+Math.round(1e3*f)+" ms"),u=d=1)}var g=this.findBestLevel(o,n,i,l,f,u,d);if(this.rebufferNotice!==g&&(this.rebufferNotice=g,this.info((l?"rebuffering expected":"buffer is empty")+", optimal quality level "+g)),g>-1)return g;var v=r.levels[n],m=r.loadLevelObj;return m&&(null==v?void 0:v.bitrate)<m.bitrate?n:r.loadLevel},r.getStarvationDelay=function(){var e=this.hls,t=null==e?void 0:e.media;if(!t)return 1/0;var r=t&&0!==t.playbackRate?Math.abs(t.playbackRate):1,i=e.mainForwardBufferInfo;return(i?i.len:0)/r},r.getBwEstimate=function(){return this.bwEstimator.getEstimate()},r.findBestLevel=function(e,t,r,i,a,n,s){var o=this,l=this.hls,u=this.lastLoadedFragLevel,d=this.fragCurrent,h=this.partCurrent;if(!l)return-1;var f=i+a,c=-1===u?l.firstLevel:u,g=l.levels,v=l.allAudioTracks,m=l.loadLevel,p=l.latestLevelDetails,y=l.config;if(1===g.length)return 0;var E,T=g[c],S=!(null==p||!p.live),L=-1===m||-1===u,R="SDR",A=(null==T?void 0:T.frameRate)||0,b=y.audioPreference,k=y.videoPreference,D=(this.audioTracksByGroup||(this.audioTracksByGroup=Gt(v)),-1);if(L){if(-1!==this.firstSelection)return this.firstSelection;var I=this.codecTiers||(this.codecTiers=function(e,t,r,i){return e.slice(r,i+1).reduce((function(e,t,r){if(!t.codecSet)return e;var i=t.audioGroups,a=e[t.codecSet];a||(e[t.codecSet]=a={minBitrate:1/0,minHeight:1/0,minFramerate:1/0,minIndex:r,maxScore:0,videoRanges:{SDR:0},channels:{2:0},hasDefaultAudio:!i,fragmentError:0}),a.minBitrate=Math.min(a.minBitrate,t.bitrate);var n=Math.min(t.height,t.width);return a.minHeight=Math.min(a.minHeight,n),a.minFramerate=Math.min(a.minFramerate,t.frameRate),a.minIndex=Math.min(a.minIndex,r),a.maxScore=Math.max(a.maxScore,t.score),a.fragmentError+=t.fragmentError,a.videoRanges[t.videoRange]=(a.videoRanges[t.videoRange]||0)+1,e}),{})}(g,0,t,r)),C=function(e,t,r,i,a){for(var n=Object.keys(e),s=null==i?void 0:i.channels,o=null==i?void 0:i.audioCodec,l=null==a?void 0:a.videoCodec,u=s&&2===parseInt(s),d=!1,h=!1,f=1/0,c=1/0,g=1/0,v=1/0,m=0,p=[],y=Nt(t,a),E=y.preferHDR,T=y.allowedVideoRanges,S=function(){var t=e[n[L]];d||(d=t.channels[2]>0),f=Math.min(f,t.minHeight),c=Math.min(c,t.minFramerate),g=Math.min(g,t.minBitrate),T.filter((function(e){return t.videoRanges[e]>0})).length>0&&(h=!0)},L=n.length;L--;)S();f=_(f)?f:0,c=_(c)?c:0;var R=Math.max(1080,f),A=Math.max(30,c);g=_(g)?g:r,r=Math.max(g,r),h||(t=void 0);var b=n.length>1;return{codecSet:n.reduce((function(t,i){var a=e[i];if(i===t)return t;if(p=h?T.filter((function(e){return a.videoRanges[e]>0})):[],b){if(a.minBitrate>r)return Ut(i,"min bitrate of "+a.minBitrate+" > current estimate of "+r),t;if(!a.hasDefaultAudio)return Ut(i,"no renditions with default or auto-select sound found"),t;if(o&&i.indexOf(o.substring(0,4))%5!=0)return Ut(i,'audio codec preference "'+o+'" not found'),t;if(s&&!u){if(!a.channels[s])return Ut(i,"no renditions with "+s+" channel sound found (channels options: "+Object.keys(a.channels)+")"),t}else if((!o||u)&&d&&0===a.channels[2])return Ut(i,"no renditions with stereo sound found"),t;if(a.minHeight>R)return Ut(i,"min resolution of "+a.minHeight+" > maximum of "+R),t;if(a.minFramerate>A)return Ut(i,"min framerate of "+a.minFramerate+" > maximum of "+A),t;if(!p.some((function(e){return a.videoRanges[e]>0})))return Ut(i,"no variants with VIDEO-RANGE of "+Bt(p)+" found"),t;if(l&&i.indexOf(l.substring(0,4))%5!=0)return Ut(i,'video codec preference "'+l+'" not found'),t;if(a.maxScore<m)return Ut(i,"max score of "+a.maxScore+" < selected max of "+m),t}return t&&(Tt(i)>=Tt(t)||a.fragmentError>e[t].fragmentError)?t:(v=a.minIndex,m=a.maxScore,i)}),void 0),videoRanges:p,preferHDR:E,minFramerate:c,minBitrate:g,minIndex:v}}(I,R,e,b,k),P=C.codecSet,x=C.videoRanges,w=C.minFramerate,O=C.minBitrate,M=C.minIndex,F=C.preferHDR;D=M,E=P,R=F?x[x.length-1]:x[0],A=w,e=Math.max(e,O),this.log("picked start tier "+Bt(C))}else E=null==T?void 0:T.codecSet,R=null==T?void 0:T.videoRange;for(var N,B=h?h.duration:d?d.duration:0,U=this.bwEstimator.getEstimateTTFB()/1e3,G=[],V=function(){var t,l=g[H],d=H>c;if(!l)return 0;if((E&&l.codecSet!==E||R&&l.videoRange!==R||d&&A>l.frameRate||!d&&A>0&&A<l.frameRate||null!=(t=l.supportedResult)&&null!=(t=t.decodingInfoResults)&&t.some((function(e){return!1===e.smooth})))&&(!L||H!==D))return G.push(H),0;var v,p=l.details,T=(h?null==p?void 0:p.partTarget:null==p?void 0:p.averagetargetduration)||B||1;v=d?s*e:n*e;var b=B&&i>=2*B&&0===a?l.averageBitrate:l.maxBitrate,k=o.getTimeToLoadFrag(U,v,b*T,!p||p.live);if(v>=b&&(H===u||0===l.loadError&&0===l.fragmentError||ee(l,y.errorPenaltyExpireMs))&&(k<=U||!_(k)||S&&!o.bitrateTestDelay||k<f)){var I=o.forcedAutoLevel;return H===m||-1!==I&&I===m||(G.length&&o.trace("Skipped level(s) "+G.join(",")+" of "+r+' max with CODECS and VIDEO-RANGE:"'+g[G[0]].codecs+'" '+g[G[0]].videoRange+'; not compatible with "'+E+'" '+R),o.info("switch candidate:"+c+"->"+H+" adjustedbw("+Math.round(v)+")-bitrate="+Math.round(v-b)+" ttfb:"+U.toFixed(1)+" avgDuration:"+T.toFixed(1)+" maxFetchDuration:"+f.toFixed(1)+" fetchDuration:"+k.toFixed(1)+" firstSelection:"+L+" codecSet:"+l.codecSet+" videoRange:"+l.videoRange+" hls.loadLevel:"+m)),L&&(o.firstSelection=H),{v:H}}},H=r;H>=t;H--)if(0!==(N=V())&&N)return N.v;return-1},r.deriveNextAutoLevel=function(e){if(!this.hls)return-1;var t=this.hls,r=t.maxAutoLevel,i=t.minAutoLevel;return Math.min(Math.max(e,i),r)},a(t,[{key:"firstAutoLevel",get:function(){var e=this.hls;if(!e)return 0;var t=e.maxAutoLevel,r=e.minAutoLevel,i=this.getBwEstimate(),a=this.getStarvationDelay(),n=e.config.maxStarvationDelay,s=this.findBestLevel(i,r,t,a/2,n,1,1);if(s>-1)return s;var o=e.firstLevel,l=-1===this.firstAutoFloor?1/0:this.firstAutoFloor,u=Math.min(Math.max(o,r),t,l);return this.warn("Could not find best starting auto level. Defaulting to first in playlist "+o+" clamped to "+u),u}},{key:"forcedAutoLevel",get:function(){return this.nextAutoLevelKey?-1:this._nextAutoLevel}},{key:"nextAutoLevel",get:function(){if(!this.hls)return-1;var e=this.forcedAutoLevel,t=this.bwEstimator.canEstimate(),r=this.lastLoadedFragLevel>-1;if(!(-1===e||t&&r&&this.nextAutoLevelKey!==this.getAutoLevelKey()))return e;var i=t&&r?this.getNextABRAutoLevel():this.firstAutoLevel;if(-1!==e){var a=this.hls.levels;if(a.length>Math.max(e,i)&&a[e].loadError<=a[i].loadError)return e}return this._nextAutoLevel=i,this.nextAutoLevelKey=this.getAutoLevelKey(),i},set:function(e){var t=this.deriveNextAutoLevel(e);this._nextAutoLevel!==t&&(this.nextAutoLevelKey="",this._nextAutoLevel=t)}}])}(ie),Kt=function(){function e(e){this.tracks=void 0,this.queues={video:[],audio:[],audiovideo:[]},this.tracks=e}var t=e.prototype;return t.destroy=function(){this.tracks=this.queues=null},t.append=function(e,t,r){if(null!==this.queues&&null!==this.tracks){var i=this.queues[t];i.push(e),1!==i.length||r||this.executeNext(t)}},t.appendBlocker=function(e){var t=this;return new Promise((function(r){var i={label:"async-blocker",execute:r,onStart:function(){},onComplete:function(){},onError:function(){}};t.append(i,e)}))},t.prependBlocker=function(e){var t=this;return new Promise((function(r){if(t.queues){var i={label:"async-blocker-prepend",execute:r,onStart:function(){},onComplete:function(){},onError:function(){}};t.queues[e].unshift(i)}}))},t.removeBlockers=function(){null!==this.queues&&[this.queues.video,this.queues.audio,this.queues.audiovideo].forEach((function(e){var t,r=null==(t=e[0])?void 0:t.label;"async-blocker"===r||"async-blocker-prepend"===r?(e[0].execute(),e.splice(0,1)):"block-audio"===r&&e.splice(0,1)}))},t.insertNext=function(e,t){var r;null!==this.queues&&(r=this.queues[t]).splice.apply(r,[1,0].concat(e))},t.unblockAudio=function(){var e;null!==this.queues&&"block-audio"===(null==(e=this.current("audio"))?void 0:e.label)&&this.shiftAndExecuteNext("audio")},t.audioBlocking=function(){return null!==this.queues&&this.queues.audio.some((function(e){return"block-audio"===e.label}))},t.executeNext=function(e){if(null!==this.queues&&null!==this.tracks){var t=this.queues[e];if(t.length){var r=t[0];try{r.execute()}catch(t){var i;if(r.onError(t),null===this.queues||null===this.tracks)return;var a=null==(i=this.tracks[e])?void 0:i.buffer;null!=a&&a.updating||this.shiftAndExecuteNext(e)}}}},t.shiftAndExecuteNext=function(e){null!==this.queues&&(this.queues[e].shift(),this.executeNext(e))},t.current=function(e){var t;return(null==(t=this.queues)?void 0:t[e][0])||null},t.toString=function(){var e=this.queues,t=this.tracks;return null===e||null===t?"<destroyed>":"\n"+this.list("video")+"\n"+this.list("audio")+"\n"+this.list("audiovideo")+"}"},t.list=function(e){var t,r;return null!=(t=this.queues)&&t[e]||null!=(r=this.tracks)&&r[e]?e+": ("+this.listSbInfo(e)+") "+this.listOps(e):""},t.listSbInfo=function(e){var t,r=null==(t=this.tracks)?void 0:t[e],i=null==r?void 0:r.buffer;return i?"SourceBuffer"+(i.updating?" updating":"")+(r.ended?" ended":"")+(r.ending?" ending":""):"none"},t.listOps=function(e){var t;return(null==(t=this.queues)?void 0:t[e].map((function(e){return e.label})).join(", "))||""},e}(),Yt=function(e,t){for(var r=0,i=e.length-1,a=null,n=null;r<=i;){var s=t(n=e[a=(r+i)/2|0]);if(s>0)r=a+1;else{if(!(s<0))return n;i=a-1}}return null};function Wt(e,t,r,i,a){void 0===r&&(r=0),void 0===i&&(i=0),void 0===a&&(a=.005);var n=null;if(e){n=t[1+e.sn-t[0].sn]||null;var s=e.endDTS-r;s>0&&s<15e-7&&(r+=15e-7),n&&e.level!==n.level&&n.end<=e.end&&(n=t[2+e.sn-t[0].sn]||null)}else 0===r&&0===t[0].start&&(n=t[0]);if(n&&((!e||e.level===n.level)&&0===jt(r,i,n)||function(e,t,r){if(0===(null==t?void 0:t.start)&&t.level<e.level&&(t.endPTS||0)>0){var i=t.tagList.reduce((function(e,t){return"INF"===t[0]&&(e+=parseFloat(t[1])),e}),r);return e.start<=i}return!1}(n,e,Math.min(a,i))))return n;var o=Yt(t,jt.bind(null,r,i));return!o||o===e&&n?n:o}function jt(e,t,r){if(void 0===e&&(e=0),void 0===t&&(t=0),r.start<=e&&r.start+r.duration>e)return 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 qt(e,t,r){var i=1e3*Math.min(t,r.duration+(r.deltaPTS?r.deltaPTS:0));return(r.endProgramDateTime||0)-i>e}var Xt=0,zt=2,Qt=3,$t=5,Zt=0,Jt=1,er=2,tr=4,rr=8,ir=16,ar=function(e){function t(t){var r;return(r=e.call(this,"error-controller",t.logger)||this).hls=void 0,r.playlistError=0,r.hls=t,r.registerListeners(),r}u(t,e);var r=t.prototype;return r.registerListeners=function(){var e=this.hls;e.on(O.ERROR,this.onError,this),e.on(O.MANIFEST_LOADING,this.onManifestLoading,this),e.on(O.LEVEL_UPDATED,this.onLevelUpdated,this)},r.unregisterListeners=function(){var e=this.hls;e&&(e.off(O.ERROR,this.onError,this),e.off(O.ERROR,this.onErrorOut,this),e.off(O.MANIFEST_LOADING,this.onManifestLoading,this),e.off(O.LEVEL_UPDATED,this.onLevelUpdated,this))},r.destroy=function(){this.unregisterListeners(),this.hls=null},r.startLoad=function(e){},r.stopLoad=function(){this.playlistError=0},r.getVariantLevelIndex=function(e){return(null==e?void 0:e.type)===N.MAIN?e.level:this.getVariantIndex()},r.getVariantIndex=function(){var e,t=this.hls,r=t.currentLevel;return null!=(e=t.loadLevelObj)&&e.details||-1===r?t.loadLevel:r},r.variantHasKey=function(e,t){if(e){var r;if(null!=(r=e.details)&&r.hasKey(t))return!0;var i=e.audioGroups;if(i)return this.hls.allAudioTracks.filter((function(e){return i.indexOf(e.groupId)>=0})).some((function(e){var r;return null==(r=e.details)?void 0:r.hasKey(t)}))}return!1},r.onManifestLoading=function(){this.playlistError=0},r.onLevelUpdated=function(){this.playlistError=0},r.onError=function(e,t){var r;if(!t.fatal){var i=this.hls,a=t.context;switch(t.details){case w.FRAG_LOAD_ERROR:case w.FRAG_LOAD_TIMEOUT:case w.KEY_LOAD_ERROR:case w.KEY_LOAD_TIMEOUT:return void(t.errorAction=this.getFragRetryOrSwitchAction(t));case w.FRAG_PARSING_ERROR:if(null!=(r=t.frag)&&r.gap)return void(t.errorAction=or());case w.FRAG_GAP:case w.FRAG_DECRYPT_ERROR:return t.errorAction=this.getFragRetryOrSwitchAction(t),void(t.errorAction.action=zt);case w.PLAYLIST_UNCHANGED_ERROR:case w.LEVEL_EMPTY_ERROR:case w.LEVEL_PARSING_ERROR:var n,s=t.parent===N.MAIN?t.level:i.loadLevel;return void(t.details===w.LEVEL_EMPTY_ERROR&&null!=(n=t.context)&&null!=(n=n.levelDetails)&&n.live?t.errorAction=this.getPlaylistRetryOrSwitchAction(t,s):(t.levelRetry=!1,t.errorAction=this.getLevelSwitchAction(t,s)));case w.LEVEL_LOAD_ERROR:case w.LEVEL_LOAD_TIMEOUT:return void("number"==typeof(null==a?void 0:a.level)&&(t.errorAction=this.getPlaylistRetryOrSwitchAction(t,a.level)));case w.AUDIO_TRACK_LOAD_ERROR:case w.AUDIO_TRACK_LOAD_TIMEOUT:case w.SUBTITLE_LOAD_ERROR:case w.SUBTITLE_TRACK_LOAD_TIMEOUT:if(a){var o=i.loadLevelObj;if(o&&(a.type===F.AUDIO_TRACK&&o.hasAudioGroup(a.groupId)||a.type===F.SUBTITLE_TRACK&&o.hasSubtitleGroup(a.groupId)))return t.errorAction=this.getPlaylistRetryOrSwitchAction(t,i.loadLevel),t.errorAction.action=zt,void(t.errorAction.flags=Jt)}return;case w.KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED:return void(t.errorAction={action:zt,flags:er});case w.KEY_SYSTEM_SESSION_UPDATE_FAILED:case w.KEY_SYSTEM_STATUS_INTERNAL_ERROR:case w.KEY_SYSTEM_NO_SESSION:return void(t.errorAction={action:zt,flags:tr});case w.BUFFER_ADD_CODEC_ERROR:case w.REMUX_ALLOC_ERROR:case w.BUFFER_APPEND_ERROR:return void(t.errorAction||(t.errorAction=this.getLevelSwitchAction(t,t.level)));case w.MEDIA_SOURCE_REQUIRES_RESET:return t.errorAction||(t.errorAction=this.getLevelSwitchAction(t,t.level)),void(t.errorAction.flags|=ir|rr);case w.INTERNAL_EXCEPTION:case w.BUFFER_APPENDING_ERROR:case w.BUFFER_APPEND_NO_PROGRESS:case w.BUFFER_FULL_ERROR:case w.LEVEL_SWITCH_ERROR:case w.BUFFER_STALLED_ERROR:case w.BUFFER_SEEK_OVER_HOLE:case w.BUFFER_NUDGE_ON_STALL:return void(t.errorAction=or())}t.type===x.KEY_SYSTEM_ERROR&&(t.levelRetry=!1,t.errorAction=or())}},r.getPlaylistRetryOrSwitchAction=function(e,t){var r=z(this.hls.config.playlistLoadPolicy,e),i=this.playlistError++;if(Z(r,i,j(e),e.response))return{action:$t,flags:Zt,retryConfig:r,retryCount:i};var a=this.getLevelSwitchAction(e,t);return r&&(a.retryConfig=r,a.retryCount=i),a},r.getFragRetryOrSwitchAction=function(e){var t=this.hls,r=this.getVariantLevelIndex(e.frag),i=t.levels[r],a=t.config,n=a.fragLoadPolicy,s=a.keyLoadPolicy,o=z(q(e)?s:n,e),l=t.levels.reduce((function(e,t){return e+t.fragmentError}),0);if(i&&(e.details!==w.FRAG_GAP&&i.fragmentError++,!X(e)&&Z(o,l,j(e),e.response)))return{action:$t,flags:Zt,retryConfig:o,retryCount:l};var u=this.getLevelSwitchAction(e,r);return o&&(u.retryConfig=o,u.retryCount=l),u},r.getLevelSwitchAction=function(e,t){var r=this.hls;null==t&&(t=r.loadLevel);var i=this.hls.levels[t];if(i){var a,n,s=e.details;i.loadError++,i.loadErrorTime=self.performance.now(),s===w.BUFFER_APPEND_ERROR&&i.fragmentError++;var o=-1,l=r.levels,u=r.loadLevel,d=r.minAutoLevel,h=r.maxAutoLevel;r.autoLevelEnabled||r.config.preserveManualLevelOnError||(r.loadLevel=-1);for(var f,c=null==(a=e.frag)?void 0:a.type,g=(c===N.AUDIO&&s===w.FRAG_PARSING_ERROR||"audio"===e.sourceBufferName&&nr(e))&&l.some((function(e){var t=e.audioCodec;return i.audioCodec!==t})),v="video"===e.sourceBufferName&&nr(e)&&l.some((function(e){var t=e.codecSet,r=e.audioCodec;return i.codecSet!==t&&i.audioCodec===r})),m=null!=(n=e.context)?n:{},p=m.type,y=m.groupId,E=function(){var t=(T+u)%l.length;if(t!==u&&t>=d&&t<=h&&(0===l[t].loadError||ee(l[t],r.config.errorPenaltyExpireMs))){var a,n,f=l[t];if(s===w.FRAG_GAP&&c===N.MAIN&&e.frag){var m=l[t].details;if(m){var E=Wt(e.frag,m.fragments,e.frag.start);if(null!=E&&E.gap)return 0}}else{if(p===F.AUDIO_TRACK&&f.hasAudioGroup(y)||p===F.SUBTITLE_TRACK&&f.hasSubtitleGroup(y))return 0;if(c===N.AUDIO&&null!=(a=i.audioGroups)&&a.some((function(e){return f.hasAudioGroup(e)}))||c===N.SUBTITLE&&null!=(n=i.subtitleGroups)&&n.some((function(e){return f.hasSubtitleGroup(e)}))||g&&i.audioCodec===f.audioCodec||v&&i.codecSet===f.codecSet||!g&&i.audioCodec!==f.audioCodec)return 0}return o=t,1}},T=l.length;T--&&(0===(f=E())||1!==f););if(o>-1&&r.loadLevel!==o)return e.levelRetry=!0,this.playlistError=0,{action:zt,flags:Zt,nextAutoLevel:o}}return{action:zt,flags:Jt}},r.onErrorOut=function(e,t){var r,i;switch(null==(r=t.errorAction)?void 0:r.action){case Xt:break;case zt:this.sendAlternateToPenaltyBox(t),t.errorAction.resolved||t.details===w.FRAG_GAP||t.details===w.PLAYLIST_UNCHANGED_ERROR||sr(t)||(t.fatal=!0)}((null==(i=t.errorAction)?void 0:i.flags)||0)&ir&&this.hls.recoverMediaError(),t.fatal&&this.hls.stopLoad()},r.sendAlternateToPenaltyBox=function(e){var t=this.hls,r=e.errorAction;if(r){var i=r.nextAutoLevel;if(r.flags===Zt)this.switchLevel(e,i);else if(r.flags&rr)for(var a=this.hls.levels,n=a.length;n--;)"SDR"!==a[n].videoRange?(a[n].fragmentError++,a[n].loadError++):void 0===i&&(i=n);else if(r.flags&er){var s=this.getVariantLevelIndex(e.frag),o=t.levels[s],l=null==o?void 0:o.attrs["HDCP-LEVEL"];r.hdcpLevel=l;var u="NONE"===l;l&&!u?(t.maxHdcpLevel=It[It.indexOf(l)-1],r.resolved=!0,this.warn('Restricting playback to HDCP-LEVEL of "'+t.maxHdcpLevel+'" or lower')):(u&&this.warn("HDCP policy resticted output with HDCP-LEVEL=NONE"),r.flags|=tr)}if(r.flags&tr){var d=e.decryptdata;if(d){for(var h=this.hls.levels,f=h.length,c=f;c--;){var g,v;this.variantHasKey(h[c],d)&&(this.log("Banned key found in level "+c+" ("+h[c].bitrate+'bps) or audio group "'+(null==(g=h[c].audioGroups)?void 0:g.join(","))+'" ('+(null==(v=e.frag)?void 0:v.type)+" fragment) "+xe(d.keyId||[])),h[c].fragmentError++,h[c].loadError++,this.log("Removing level "+c+" with key error ("+e.error+")"),this.hls.removeLevel(c))}var m=e.frag;if(this.hls.levels.length<f)r.resolved=!0;else if(m&&m.type!==N.MAIN){var p=m.decryptdata;p&&!d.matches(p)&&(r.resolved=!0)}}}r.resolved||this.switchLevel(e,i)}},r.switchLevel=function(e,t){if(void 0!==t&&e.errorAction&&(this.warn("switching to level "+t+" after "+e.details),this.hls.nextAutoLevel=t,e.errorAction.resolved=!0,this.hls.nextLoadLevel=this.hls.nextAutoLevel,e.details===w.BUFFER_ADD_CODEC_ERROR&&e.mimeType&&"audiovideo"!==e.sourceBufferName))for(var r=e.mimeType.replace(/^.+codecs=["']?([^"']+).*$/,"$1"),i=this.hls.levels,a=i.length;a--;)i[a][e.sourceBufferName+"Codec"]===r&&(this.log("Removing level "+a+" for "+e.details+' ("'+r+'" not supported)'),this.hls.removeLevel(a))},t}(ie);function nr(e){var t=e.details;return t===w.BUFFER_ADD_CODEC_ERROR||t===w.MEDIA_SOURCE_REQUIRES_RESET||t===w.BUFFER_APPEND_ERROR&&!sr(e)}function sr(e){return e.details===w.BUFFER_APPEND_ERROR&&("QuotaExceededError"===e.error.name||"InvalidStateError"===e.error.name)}function or(e){var t={action:Xt,flags:Zt};return e&&(t.resolved=!0),t}var lr=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.dateRangeTagCount=0,this.live=!0,this.iframesOnly=!1,this.requestScheduled=-1,this.ageHeader=0,this.advancedDateTime=void 0,this.updated=!0,this.advanced=!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.appliedTimelineOffset=void 0,this.fragments=[],this.encryptedFragments=[],this.dateRanges={},this.url=e}var t=e.prototype;return t.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.live,this.advanced=this.endSN>e.endSN||t>0||0===t&&r>0,this.updated||this.advanced?this.misses=0:this.misses=e.misses+1},t.hasKey=function(e){return this.encryptedFragments.some((function(t){var r=t.decryptdata;return r||(t.setKeyFormat(e.keyFormat),r=t.decryptdata),!!r&&e.matches(r)}))},a(e,[{key:"hasProgramDateTime",get:function(){return!!this.fragments.length&&_(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)&&e.length?this.partList[this.partList.length-1].end:this.fragmentEnd}},{key:"fragmentEnd",get:function(){return this.fragments.length?this.fragments[this.fragments.length-1].end:0}},{key:"fragmentStart",get:function(){return this.fragments.length?this.fragments[0].start: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)&&e.length?this.partList[this.partList.length-1].index:-1}},{key:"maxPartIndex",get:function(){var e=this.partList;if(e){var t=this.lastPartIndex;if(-1!==t){for(var r=e.length;r--;)if(e[r].index>t)return e[r].index;return t}}return 0}},{key:"lastPartSn",get:function(){var e;return null!=(e=this.partList)&&e.length?this.partList[this.partList.length-1].fragment.sn:this.endSN}},{key:"expired",get:function(){if(this.live&&this.age&&this.misses<3){var e=this.partEnd-this.fragmentStart;return this.age>Math.max(e,this.totalduration)+this.levelTargetDuration}return!1}}])}(),ur={length:0,start:function(){return 0},end:function(){return 0}},dr=function(){function e(){}return e.isBuffered=function(t,r){if(t)for(var i=e.getBuffered(t),a=i.length;a--;)if(r>=i.start(a)&&r<=i.end(a))return!0;return!1},e.bufferedRanges=function(t){if(t){var r=e.getBuffered(t);return e.timeRangesToArray(r)}return[]},e.timeRangesToArray=function(e){for(var t=[],r=0;r<e.length;r++)t.push({start:e.start(r),end:e.end(r)});return t},e.bufferInfo=function(t,r,i){if(t){var a=e.bufferedRanges(t);if(a.length)return e.bufferedInfo(a,r,i)}return{len:0,start:r,end:r,bufferedIndex:-1}},e.bufferedInfo=function(e,t,r){t=Math.max(0,t),e.length>1&&e.sort((function(e,t){return e.start-t.start||t.end-e.end}));var i=-1,a=[];if(r)for(var n=0;n<e.length;n++){t>=e[n].start&&t<=e[n].end&&(i=n);var s=a.length;if(s){var o=a[s-1].end;e[n].start-o<r?e[n].end>o&&(a[s-1].end=e[n].end):a.push(e[n])}else a.push(e[n])}else a=e;for(var l,u=0,d=t,h=t,f=0;f<a.length;f++){var c=a[f].start,g=a[f].end;if(-1===i&&t>=c&&t<=g&&(i=f),t+r>=c&&t<g)d=c,u=(h=g)-t;else if(t+r<c){l=c;break}}return{len:u,start:d||0,end:h||0,nextStart:l,buffered:e,bufferedIndex:i}},e.getBuffered=function(e){try{return e.buffered||ur}catch(e){return fe.log("failed to get media.buffered",e),ur}},e}();function hr(e,t,r){fr(e,t,r),e.addEventListener(t,r)}function fr(e,t,r){e.removeEventListener(t,r)}function cr(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}var gr=/(avc[1234]|hvc1|hev1|dvh[1e]|vp09|av01)(?:\.[^.,]+)+/,vr="HlsJsTrackRemovedError",mr=function(e){function t(t){var r;return(r=e.call(this,t)||this).name=vr,r}return u(t,e),t}(v(Error)),pr=function(e){function t(t,r){var i,a;return(i=e.call(this,"buffer-controller",t.logger)||this).hls=void 0,i.fragmentTracker=void 0,i.fragmentAppendProgress=Object.create(null),i.appendsWithoutProgress=Object.create(null),i.details=null,i._objectUrl=null,i.operationQueue=null,i.bufferCodecEventsTotal=0,i.media=null,i.mediaSource=null,i.lastMpegAudioChunk=null,i.lastVideoAppendEnd=0,i.appendSource=void 0,i.transferData=void 0,i.overrides=void 0,i.appendErrors={audio:0,video:0,audiovideo:0},i.appendError=void 0,i._quotaEvictionPending={},i.tracks={},i.sourceBuffers=[[null,null],[null,null]],i._onEndStreaming=function(e){var t;i.hls&&"open"===(null==(t=i.mediaSource)?void 0:t.readyState)&&i.media&&!i.media.seeking&&i.hls.pauseBuffering()},i._onStartStreaming=function(e){i.hls&&i.hls.resumeBuffering()},i._onMediaSourceOpen=function(e){var t=i,r=t.media,a=t.mediaSource;e&&i.log("Media source opened"),r&&a&&(fr(a,"sourceopen",i._onMediaSourceOpen),fr(r,"emptied",i._onMediaEmptied),i.updateDuration(),i.hls.trigger(O.MEDIA_ATTACHED,{media:r,mediaSource:a}),null!==i.mediaSource&&i.checkPendingTracks())},i._onMediaSourceClose=function(){if(i.log("Media source closed"),i.media){var e=i.media.error,t=i,r=t.appendError,a=t.appendErrors,n=!1;!r&&e&&(n=Math.max(++a.audio,++a.video,++a.audiovideo)>=i.hls.config.appendErrorMaxRetry);var s=new Error("MediaSource closed while media attached"+Tr(n,e));i.warn(s),i.hls.trigger(O.ERROR,o({fatal:n},r,{type:x.MEDIA_ERROR,details:w.MEDIA_SOURCE_REQUIRES_RESET,error:s}))}},i._onMediaSourceEnded=function(){i.log("Media source ended")},i._onMediaEmptied=function(){var e=i,t=e.mediaSrc,r=e._objectUrl;t!==r&&i.error("Media element src was set while attaching MediaSource ("+r+" > "+t+")")},i._onMediaError=function(){var e,t,r=i.media;r&&i.log("Media error (code: "+(null==(e=r.error)?void 0:e.code)+"): "+(null==(t=r.error)?void 0:t.message))},i.hls=t,i.fragmentTracker=r,i.appendSource=(a=ve(t.config.preferManagedMediaSource),"undefined"!=typeof self&&a===self.ManagedMediaSource),i.initTracks(),i.registerListeners(),i}u(t,e);var r=t.prototype;return r.hasSourceTypes=function(){return Object.keys(this.tracks).length>0},r.destroy=function(){this.unregisterListeners(),this.details=null,this.lastMpegAudioChunk=null,this.transferData=this.overrides=void 0,this.operationQueue&&(this.operationQueue.destroy(),this.operationQueue=null),this.hls=this.fragmentTracker=null,this._onMediaSourceOpen=this._onMediaSourceClose=null,this._onMediaSourceEnded=null,this._onStartStreaming=this._onEndStreaming=null,this._onMediaEmptied=this._onMediaError=null},r.registerListeners=function(){var e=this.hls;e.on(O.MEDIA_ATTACHING,this.onMediaAttaching,this),e.on(O.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(O.MANIFEST_LOADING,this.onManifestLoading,this),e.on(O.MANIFEST_PARSED,this.onManifestParsed,this),e.on(O.BUFFER_RESET,this.onBufferReset,this),e.on(O.BUFFER_APPENDING,this.onBufferAppending,this),e.on(O.BUFFER_CODECS,this.onBufferCodecs,this),e.on(O.BUFFER_EOS,this.onBufferEos,this),e.on(O.BUFFER_FLUSHING,this.onBufferFlushing,this),e.on(O.LEVEL_UPDATED,this.onLevelUpdated,this),e.on(O.FRAG_PARSED,this.onFragParsed,this),e.on(O.FRAG_CHANGED,this.onFragChanged,this),e.on(O.ERROR,this.onError,this)},r.unregisterListeners=function(){var e=this.hls;e.off(O.MEDIA_ATTACHING,this.onMediaAttaching,this),e.off(O.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(O.MANIFEST_LOADING,this.onManifestLoading,this),e.off(O.MANIFEST_PARSED,this.onManifestParsed,this),e.off(O.BUFFER_RESET,this.onBufferReset,this),e.off(O.BUFFER_APPENDING,this.onBufferAppending,this),e.off(O.BUFFER_CODECS,this.onBufferCodecs,this),e.off(O.BUFFER_EOS,this.onBufferEos,this),e.off(O.BUFFER_FLUSHING,this.onBufferFlushing,this),e.off(O.LEVEL_UPDATED,this.onLevelUpdated,this),e.off(O.FRAG_PARSED,this.onFragParsed,this),e.off(O.FRAG_CHANGED,this.onFragChanged,this),e.off(O.ERROR,this.onError,this)},r.transferMedia=function(){var e=this,t=this.media,r=this.mediaSource;if(!t)return null;var i={};if(this.operationQueue){var a=this.isUpdating();a||this.operationQueue.removeBlockers();var n=this.isQueued();(a||n)&&this.warn("Transfering MediaSource with"+(n?" operations in queue":"")+(a?" updating SourceBuffer(s)":"")+" "+this.operationQueue),this.operationQueue.destroy()}var s=this.transferData;return s&&!this.sourceBufferCount&&s.mediaSource===r?o(i,s.tracks):this.sourceBuffers.forEach((function(t){var r=t[0];r&&(i[r]=o({},e.tracks[r]),e.removeBuffer(r)),t[0]=t[1]=null})),{media:t,mediaSource:r,tracks:i}},r.initTracks=function(){this.resetAppendProgress(),this.sourceBuffers=[[null,null],[null,null]],this.tracks={},this.resetQueue(),this.lastMpegAudioChunk=null,this.lastVideoAppendEnd=0},r.onManifestLoading=function(){this.bufferCodecEventsTotal=0,this.details=null,this.resetAppendErrors(),this.resetAppendProgress()},r.onManifestParsed=function(e,t){var r,i=2;(t.audio&&!t.video||!t.altAudio)&&(i=1),this.bufferCodecEventsTotal=i,this.log(i+" bufferCodec event(s) expected."),null!=(r=this.transferData)&&r.mediaSource&&this.sourceBufferCount&&i&&this.bufferCreated()},r.onMediaAttaching=function(e,t){var r=this.media=t.media;this.transferData=this.overrides=void 0;var i=ve(this.appendSource);if(i){var a=!!t.mediaSource;(a||t.overrides)&&(this.transferData=t,this.overrides=t.overrides);var n=this.mediaSource=t.mediaSource||new i;if(this.assignMediaSource(n),a)this._objectUrl=r.src,this.attachTransferred();else{var s=this._objectUrl=self.URL.createObjectURL(n);if(this.appendSource)try{r.removeAttribute("src");var o=self.ManagedMediaSource;r.disableRemotePlayback=r.disableRemotePlayback||o&&n instanceof o,yr(r),function(e,t){var r=self.document.createElement("source");r.type="video/mp4",r.src=t,e.appendChild(r)}(r,s),r.load()}catch(e){r.src=s}else r.src=s}hr(r,"emptied",this._onMediaEmptied),hr(r,"error",this._onMediaError),this.appendSource&&hr(r,"seeking",this._onStartStreaming)}},r.assignMediaSource=function(e){var t,r;this.log(((null==(t=this.transferData)?void 0:t.mediaSource)===e?"transferred":"created")+" media source: "+(null==(r=e.constructor)?void 0:r.name)),hr(e,"sourceopen",this._onMediaSourceOpen),hr(e,"sourceended",this._onMediaSourceEnded),hr(e,"sourceclose",this._onMediaSourceClose),this.appendSource&&(hr(e,"startstreaming",this._onStartStreaming),hr(e,"endstreaming",this._onEndStreaming))},r.attachTransferred=function(){var e=this,t=this.media,r=this.transferData;if(r&&t){var i=this.tracks,a=r.tracks,n=a?Object.keys(a):null,s=n?n.length:0,o=function(){Promise.resolve().then((function(){e.media&&e.mediaSourceOpenOrEnded&&e._onMediaSourceOpen()}))};if(a&&n&&s){if(!this.tracksReady)return this.hls.config.startFragPrefetch=!0,void this.log("attachTransferred: waiting for SourceBuffer track info");if(this.log("attachTransferred: (bufferCodecEventsTotal "+this.bufferCodecEventsTotal+")\nrequired tracks: "+Bt(i,(function(e,t){return"initSegment"===e?void 0:t}))+";\ntransfer tracks: "+Bt(a,(function(e,t){return"initSegment"===e?void 0:t}))+"}"),!function(e,t){var r=Object.keys(e),i=Object.keys(t),a=r.length,n=i.length;return!a||!n||a===n&&!r.some((function(e){return-1===i.indexOf(e)}))}(a,i)){r.mediaSource=null,r.tracks=void 0;var l=t.currentTime,u=this.details,d=Math.max(l,(null==u?void 0:u.fragments[0].start)||0);return d-l>1?void this.log("attachTransferred: waiting for playback to reach new tracks start time "+l+" -> "+d):(this.warn('attachTransferred: resetting MediaSource for incompatible tracks ("'+Object.keys(a)+'"->"'+Object.keys(i)+'") start time: '+d+" currentTime: "+l),this.onMediaDetaching(O.MEDIA_DETACHING,{}),this.onMediaAttaching(O.MEDIA_ATTACHING,r),void(t.currentTime=d))}this.transferData=void 0,n.forEach((function(t){var r=t,i=a[r];if(i){var n=i.buffer;if(n){var s=e.fragmentTracker,o=i.id;if(s.hasFragments(o)||s.hasParts(o)){var l=dr.getBuffered(n);s.detectEvictedFragments(r,l,o,null,null,!0)}var u=Er(r),d=[r,n];e.sourceBuffers[u]=d,n.updating&&e.operationQueue&&e.operationQueue.prependBlocker(r),e.trackSourceBuffer(r,i)}}})),o(),this.bufferCreated()}else this.log("attachTransferred: MediaSource w/o SourceBuffers"),o()}},r.onMediaDetaching=function(e,t){var r=this,i=!!t.transferMedia;this.transferData=this.overrides=void 0;var a=this.media,n=this.mediaSource,s=this._objectUrl;if(n){if(this.log("media source "+(i?"transferring":"detaching")),i)this.sourceBuffers.forEach((function(e){var t=e[0];t&&r.removeBuffer(t)})),this.resetQueue();else{if(this.mediaSourceOpenOrEnded){var o="open"===n.readyState;try{for(var l=n.sourceBuffers,u=l.length;u--;)o&&l[u].abort(),n.removeSourceBuffer(l[u]);o&&n.endOfStream()}catch(e){this.warn("onMediaDetaching: "+e.message+" while calling endOfStream")}}this.sourceBufferCount&&this.onBufferReset()}fr(n,"sourceopen",this._onMediaSourceOpen),fr(n,"sourceended",this._onMediaSourceEnded),fr(n,"sourceclose",this._onMediaSourceClose),this.appendSource&&(fr(n,"startstreaming",this._onStartStreaming),fr(n,"endstreaming",this._onEndStreaming)),this.mediaSource=null,this._objectUrl=null}a&&(fr(a,"emptied",this._onMediaEmptied),fr(a,"error",this._onMediaError),fr(a,"seeking",this._onStartStreaming),i||(s&&self.URL.revokeObjectURL(s),this.mediaSrc===s?(a.removeAttribute("src"),this.appendSource&&yr(a),a.load()):this.warn("media|source.src was changed by a third party - skip cleanup")),this.media=null)},r.onBufferReset=function(){var e=this;this.sourceBuffers.forEach((function(t){var r=t[0];r&&e.resetBuffer(r)})),this.initTracks()},r.resetBuffer=function(e){var t,r=null==(t=this.tracks[e])?void 0:t.buffer;if(this.removeBuffer(e),r)try{var i;null!=(i=this.mediaSource)&&i.sourceBuffers.length&&this.mediaSource.removeSourceBuffer(r)}catch(t){this.warn("onBufferReset "+e,t)}delete this.tracks[e]},r.removeBuffer=function(e){this.removeBufferListeners(e),this.sourceBuffers[Er(e)]=[null,null];var t=this.tracks[e];t&&(this.clearBufferAppendTimeoutId(t),t.buffer=void 0)},r.resetQueue=function(){this.operationQueue&&this.operationQueue.destroy(),this.operationQueue=new Kt(this.tracks)},r.onBufferCodecs=function(e,t){var r,i=this,a=this.tracks,n=Object.keys(t);this.log('BUFFER_CODECS: "'+n+'" (current SB count '+this.sourceBufferCount+")");var s="audiovideo"in t&&(a.audio||a.video)||a.audiovideo&&("audio"in t||"video"in t),o=!s&&this.sourceBufferCount&&this.media&&n.some((function(e){return!a[e]}));s||o?this.warn('Unsupported transition between "'+Object.keys(a)+'" and "'+n+'" SourceBuffers'):(n.forEach((function(e){var r,n,s=t[e],o=s.id,l=s.codec,u=s.levelCodec,d=s.container,h=s.metadata,f=s.supplemental,c=a[e],g=null==(r=i.transferData)||null==(r=r.tracks)?void 0:r[e],v=null!=g&&g.buffer?g:c,m=(null==v?void 0:v.pendingCodec)||(null==v?void 0:v.codec),p=null==v?void 0:v.levelCodec;c||(c=a[e]={buffer:void 0,listeners:[],codec:l,supplemental:f,container:d,levelCodec:u,metadata:h,id:o});var y=At(m,p),E=null==y?void 0:y.replace(gr,"$1"),T=At(l,u),S=null==(n=T)?void 0:n.replace(gr,"$1");T&&y&&E!==S&&("audio"===e.slice(0,5)&&(T=Rt(T,i.appendSource)),i.log("switching codec "+m+" to "+T),T!==(c.pendingCodec||c.codec)&&(c.pendingCodec=T),c.container=d,i.appendChangeType(e,d,T))})),(this.tracksReady||this.sourceBufferCount)&&(t.tracks=this.sourceBufferTracks),this.sourceBufferCount||(this.bufferCodecEventsTotal>1&&!this.tracks.video&&!t.video&&"main"===(null==(r=t.audio)?void 0:r.id)&&(this.log("Main audio-only"),this.bufferCodecEventsTotal=1),this.mediaSourceOpenOrEnded&&this.checkPendingTracks()))},r.appendChangeType=function(e,t,r){var i=this,a=t+";codecs="+r,n={label:"change-type="+a,execute:function(){var n=i.tracks[e];if(n){var s=n.buffer;null!=s&&s.changeType&&(i.log("changing "+e+" sourceBuffer type to "+a),s.changeType(a),n.codec=r,n.container=t)}i.shiftAndExecuteNext(e)},onStart:function(){},onComplete:function(){},onError:function(t){i.warn("Failed to change "+e+" SourceBuffer type",t)}};this.append(n,e,this.isPending(this.tracks[e]))},r.blockAudio=function(e){var t,r=this,i=e.start,a=i+.05*e.duration;if(!0!==(null==(t=this.fragmentTracker.getAppendedFrag(i,N.MAIN))?void 0:t.gap)){var n={label:"block-audio",execute:function(){var e,t;(r.lastVideoAppendEnd>a||dr.isBuffered(null==(e=r.tracks.video)?void 0:e.buffer,a)||!0===(null==(t=r.fragmentTracker.getAppendedFrag(a,N.MAIN))?void 0:t.gap))&&r.unblockAudio()},onStart:function(){},onComplete:function(){},onError:function(e){r.warn("Error executing block-audio operation",e)}};this.append(n,"audio",!0)}},r.unblockAudio=function(){this.operationQueue&&this.operationQueue.unblockAudio()},r.onBufferAppending=function(e,t){var r=this,i=this.tracks,a=t.data,n=t.type,s=t.parent,o=t.frag,l=t.part,u=t.chunkMeta,d=t.offset,h=u.buffering[n],f=G(o)&&!l&&!o.gap&&!u.partial&&!u.iframe,c=null,g=!1,v=o.sn,m=o.cc,p=self.performance.now();h.start=p;var y=o.stats.buffering,E=l?l.stats.buffering:null;0===y.start&&(y.start=p),0===(null==E?void 0:E.start)&&(E.start=p);var T=i.audio,S=!1;"audio"===n&&"audio/mpeg"===(null==T?void 0:T.container)&&(S=!this.lastMpegAudioChunk||1===u.id||this.lastMpegAudioChunk.sn!==u.sn,this.lastMpegAudioChunk=u);var L=i.video,R=null==L?void 0:L.buffer;if(R&&"initSegment"!==v&&void 0!==d){var A=null==T?void 0:T.buffer,b=l||o;if("audio"===n&&"main"!==s&&!L.ending&&!L.ended&&A&&dr.getBuffered(A).length){var k=b.start+.05*b.duration,D=dr.getBuffered(R),I=this.currentOp("video");D.length||I?!dr.isBuffered(R,k)&&this.lastVideoAppendEnd<k&&this.blockAudio(b):this.blockAudio(b)}else if("video"===n){var C=b.end,P=this.lastVideoAppendEnd-C;this.lastVideoAppendEnd=C,P<0||P>b.duration?this.unblockAudio():this.isAudioBlocked()&&this.executeNext("audio")}}var M=(l||o).start,F={label:"append-"+n,execute:function(){var e;h.executeStart=self.performance.now();var t=null==(e=r.tracks[n])?void 0:e.buffer;t&&(f&&(c=dr.timeRangesToArray(dr.getBuffered(t)),g=Rr(Lr(c,o),o)),S?r.updateTimestampOffset(t,M,.1,n,v,m):void 0!==d&&_(d)&&r.updateTimestampOffset(t,d,1e-6,n,v,m)),r.appendExecutor(a,n)},onStart:function(){},onComplete:function(){r.clearBufferAppendTimeoutId(r.tracks[n]);var e=self.performance.now();h.executeEnd=h.end=e,0===y.first&&(y.first=e),0===(null==E?void 0:E.first)&&(E.first=e);var t={};if(r.sourceBuffers.forEach((function(e){var r=e[0],i=e[1];r&&(t[r]=dr.getBuffered(i))})),f){var i=r.getFragmentAppendProgress(o),a=t[n];if(a&&c){var d=Lr(dr.timeRangesToArray(a),o);i.progressed||(i.progressed=d-Lr(c,o)>.001);var v=g&&Rr(d,o);i.fullyBuffered[n]=(void 0===i.fullyBuffered[n]||i.fullyBuffered[n])&&v}}r.hls.trigger(O.BUFFER_APPENDED,{type:n,frag:o,part:l,chunkMeta:u,parent:s,timeRanges:t})},onError:function(e){var t;r.clearBufferAppendTimeoutId(r.tracks[n]);var i=e.code===DOMException.QUOTA_EXCEEDED_ERR||"QuotaExceededError"==e.name||"quota"in e;if(i&&!r._quotaEvictionPending[n]){var d=r.getBackBufferEvictionTarget(n,a.byteLength,o.type);if(d>0){r._quotaEvictionPending[n]=!0,r.log('QuotaExceededError on "'+n+'" append sn: '+v+" - evicting back buffer to "+d.toFixed(3)+"s and retrying");var h=r.getFlushOp(n,0,d),c=r.getClearEvictionPendingOp(n);return void r.insertNext([h,F,c],n)}r.warn('QuotaExceededError on "'+n+'" sn: '+v+" - no back buffer available to evict")}f&&(r.getFragmentAppendProgress(o).errored=!0);var g={type:x.MEDIA_ERROR,parent:s,details:w.BUFFER_APPEND_ERROR,sourceBufferName:n,frag:o,part:l,chunkMeta:u,error:e,err:e,fatal:!1},m=null==(t=r.media)?void 0:t.error;if(e.name!==vr||0!==r.sourceBufferCount||r.media&&0!==r.pendingTrackCount){var p;i&&(g.details=w.BUFFER_FULL_ERROR);var y=++r.appendErrors[n],E=r.hls.config.appendErrorMaxRetry;r.warn("Failed "+y+"/"+(E+1)+' times to append segment in "'+n+'" sourceBuffer with error: '+e.message),y>=E&&(g.fatal=!i);var T=null==(p=r.mediaSource)?void 0:p.readyState;("ended"===T||"closed"===T||m)&&(r.warn('MediaSource readyState "'+T+'" during SourceBuffer'+Tr(g.fatal,m)),g.details=w.MEDIA_SOURCE_REQUIRES_RESET)}else g.errorAction=or(!0);r.appendError=g,r.hls.trigger(O.ERROR,g),i&&r.hls&&r.trimBuffers(o.start,1)}};this.log('queuing "'+n+'" append sn: '+v+(l?" p: "+l.index:"")+" of "+s+" playlist "+o.level+" cc: "+m+" offset: "+d+" bytes: "+a.byteLength),this.append(F,n,this.isPending(this.tracks[n]))},r.getClearEvictionPendingOp=function(e){var t=this;return{label:"clear",execute:function(){t._quotaEvictionPending[e]=!1,t.shiftAndExecuteNext(e)},onStart:function(){},onComplete:function(){},onError:function(){}}},r.getFlushOp=function(e,t,r){var i=this;return this.log('queuing "'+e+'" remove '+t+"-"+r),{label:"remove",execute:function(){i.removeExecutor(e,t,r)},onStart:function(){},onComplete:function(){var a,n=null==(a=i.tracks[e])?void 0:a.buffer;i.log("Remove request "+t+"-"+r+" from "+e+" Source Buffer complete > buffer: "+(n?cr(dr.getBuffered(n)):"(detached)")),i.hls.trigger(O.BUFFER_FLUSHED,{type:e,start:t,end:r})},onError:function(a){i.warn("Failed to remove "+t+"-"+r+' from "'+e+'" SourceBuffer',a),i.hls.trigger(O.BUFFER_FLUSHED,{type:e,start:0,end:0,error:a})}}},r.onBufferFlushing=function(e,t){var r=this,i=t.type,a=t.startOffset,n=t.endOffset;i&&"audio"!==i||this.unblockAudio(),i?this.append(this.getFlushOp(i,a,n),i):this.sourceBuffers.forEach((function(e){var t=e[0];t&&r.append(r.getFlushOp(t,a,n),t)}))},r.onFragParsed=function(e,t){var r=this,i=t.frag,a=t.part,n=t.chunkMeta,s=[],o=a?a.elementaryStreams:i.elementaryStreams;o[B.AUDIOVIDEO]?s.push("audiovideo"):(o[B.AUDIO]&&s.push("audio"),o[B.VIDEO]&&s.push("video")),0===s.length&&this.log("Fragments must have at least one ElementaryStreamType set. "+i.type+" "+i.level+" "+(a?"part: "+a.index+" of ":"")+"sn: "+i.sn),this.blockBuffers((function(){var e=self.performance.now();i.stats.buffering.end=e,a&&(a.stats.buffering.end=e);var t=a?a.stats:i.stats;r.checkAppendProgress(i,a,n,s),r.hls&&(r.hls.trigger(O.FRAG_BUFFERED,{frag:i,part:a,stats:t,id:i.type,chunkMeta:n}),!a&&i.gap&&t.retry&&r.log("Nothing buffered for "+i.type+" level: "+i.level+" sn: "+i.sn+" retries "+t.retry))}),s).catch((function(e){r.warn("Fragment buffered callback "+e.message),r.stepOperationQueue(r.sourceBufferTypes)}))},r.onFragChanged=function(e,t){var r,i,a=this,n=null==(r=this.hls)?void 0:r.config;if(n){var s=n.backBufferLength,o=n.frontBufferFlushThreshold;this.trimBuffers(o,s,t.frag,t.previousFrag);var l=t.frag.elementaryStreams,u=this.appendErrors,d=null==(i=this.appendError)?void 0:i.sourceBufferName;Object.keys(l).forEach((function(e){l[e]&&(u[e]=0,e===d&&(a.appendError=void 0),"audio"===e||"video"===e?(u.audiovideo=0,"audiovideo"===d&&(a.appendError=void 0)):(u.audio=0,u.video=0,"audiovideo"!==d&&(a.appendError=void 0)))}))}},r.onBufferEos=function(e,t){var r,i=this;this.sourceBuffers.forEach((function(e){var r=e[0];if(r){var a=i.tracks[r];t.type&&t.type!==r||(a.ending=!0,a.ended||(a.ended=!0,i.log(r+" buffer reached EOS")))}}));var a=!1!==(null==(r=this.overrides)?void 0:r.endOfStream);this.sourceBufferCount>0&&!this.sourceBuffers.some((function(e){var t,r=e[0];return r&&!(null!=(t=i.tracks[r])&&t.ended)}))?(this.unblockAudio(),a?(this.log("Queueing EOS"),this.blockUntilOpen((function(){i.tracksEnded();var e=i.mediaSource;"open"===(null==e?void 0:e.readyState)?(i.log("Calling mediaSource.endOfStream()"),e.endOfStream(),i.hls.trigger(O.BUFFERED_TO_END,void 0)):e&&i.log("Could not call mediaSource.endOfStream(). mediaSource.readyState: "+e.readyState)}))):(this.tracksEnded(),this.hls.trigger(O.BUFFERED_TO_END,void 0))):"video"===t.type&&this.unblockAudio()},r.tracksEnded=function(){var e=this;this.sourceBuffers.forEach((function(t){var r=t[0];if(null!==r){var i=e.tracks[r];i&&(i.ending=!1)}}))},r.onLevelUpdated=function(e,t){var r=t.details;r.fragments.length&&(this.details=r,this.updateDuration())},r.updateDuration=function(){var e=this;this.blockUntilOpen((function(){var t=e.getDurationAndRange();t&&e.updateMediaSource(t)}))},r.onError=function(e,t){if(t.details===w.BUFFER_APPEND_ERROR&&t.frag){var r,i=null==(r=t.errorAction)?void 0:r.nextAutoLevel;_(i)&&i!==t.frag.level&&this.resetAppendErrors()}},r.getBackBufferEvictionTarget=function(e,t,r){var i=this.media;return i?this.fragmentTracker.getBackBufferEvictionEnd(i.currentTime,r,t):0},r.getFragmentAppendProgress=function(e){var t,r=Sr(e),i=this.fragmentAppendProgress[r];return(null==(t=i)?void 0:t.stats)!==e.stats&&(i={stats:e.stats,progressed:!1,errored:!1,fullyBuffered:Object.create(null)},this.fragmentAppendProgress[r]=i),i},r.checkAppendProgress=function(e,t,r,i){if(!(t||!G(e)||e.gap||r.iframe||r.partial)){var a=Sr(e),n=this.fragmentAppendProgress[a];delete this.fragmentAppendProgress[a];var s=e.stats.buffering;if(!s.start||s.first){var o=(null==n?void 0:n.stats)===e.stats?n:void 0;if(null==o||!o.errored){var l=i.length>0&&i.every((function(e){return!0===(null==o?void 0:o.fullyBuffered[e])}));if(null!=o&&o.progressed||l)delete this.appendsWithoutProgress[a];else if(o||0!==i.length){var u=(this.appendsWithoutProgress[a]||0)+1,d=this.hls;u>=d.config.appendErrorMaxRetry?delete this.appendsWithoutProgress[a]:this.appendsWithoutProgress[a]=u,this.warn("Fragment "+e.sn+" of "+e.type+" playlist "+e.level+" appended "+u+" time"+(u>1?"s":"")+" without buffered range growth"),d.trigger(O.ERROR,{type:x.MEDIA_ERROR,details:w.BUFFER_APPEND_NO_PROGRESS,fatal:!1,frag:e,chunkMeta:r,parent:e.type,appendsWithoutProgress:u,error:new Error("Fragment append did not increase buffered coverage ("+u+")")})}}}}},r.resetAppendProgress=function(){this.fragmentAppendProgress=Object.create(null),this.appendsWithoutProgress=Object.create(null)},r.resetAppendErrors=function(){this.appendErrors={audio:0,video:0,audiovideo:0},this.appendError=void 0},r.trimBuffers=function(e,t,r,i){var a=this.hls,n=this.details,s=this.media;if(s&&null!==n&&this.sourceBufferCount){var o=a.config,l=s.currentTime,u=n.levelTargetDuration;t=n.live&&null!==o.liveBackBufferLength?o.liveBackBufferLength:t;var d=-1/0;if(_(t)&&t>=0){var h=Math.max(t,u);d=Math.floor(l/u)*u-h}if(r){var f=this.getLoopBackBufferFlushEnd(r,null!=i?i:null);f>0&&(d=Math.max(d,f))}if(d>0&&this.flushBackBuffer(l,u,d),_(e)&&e>0){var c=Math.max(o.maxBufferLength,e),g=Math.max(c,u),v=Math.floor(l/u)*u+g;this.flushFrontBuffer(l,u,v)}}},r.getLoopBackBufferFlushEnd=function(e,t){var r,i=this.media;if(!1===(null==(r=this.hls)?void 0:r.config.loopBackBufferFlush)||null==i||!i.loop||!t||e.level<=t.level)return 0;var a=e.elementaryStreams,n=a.video,s=a.audiovideo;if(null!=n&&n.partial||null!=s&&s.partial)return 0;var o=this.getEarliestElementaryStreamStart(e)-.25;return o<=0?0:(this.log("Flushing lower quality back buffer for loop: level "+e.level+", range [0-"+o.toFixed(3)+"]"),o)},r.getEarliestElementaryStreamStart=function(e){var t=e.elementaryStreams,r=t.audio,i=t.video,a=t.audiovideo,n=e.start;return a?n=Math.min(n,a.startDTS):(r&&(n=Math.min(n,r.startDTS)),i&&(n=Math.min(n,i.startDTS))),n},r.flushBackBuffer=function(e,t,r){var i=this;this.sourceBuffers.forEach((function(e){var t=e[0],a=e[1];if(a){var n=dr.getBuffered(a);if(n.length>0&&r>n.start(0)){var s,o,l;i.hls.trigger(O.BACK_BUFFER_REACHED,{bufferEnd:r});var u=i.tracks[t];if(null!=(s=i.details)&&s.live)i.hls.trigger(O.LIVE_BACK_BUFFER_REACHED,{bufferEnd:r});else if(null!=u&&u.ended&&(null==(o=i.media)||!o.loop||!1===(null==(l=i.hls)?void 0:l.config.loopBackBufferFlush)))return void i.log("Cannot flush "+t+" back buffer while SourceBuffer is in ended state");i.hls.trigger(O.BUFFER_FLUSHING,{startOffset:0,endOffset:r,type:t})}}}))},r.flushFrontBuffer=function(e,t,r){var i=this;this.sourceBuffers.forEach((function(t){var a=t[0],n=t[1];if(n){var s=dr.getBuffered(n),o=s.length;if(o<2)return;var l=s.start(o-1),u=s.end(o-1);if(r>l||e>=l&&e<=u)return;i.hls.trigger(O.BUFFER_FLUSHING,{startOffset:l,endOffset:1/0,type:a})}}))},r.getDurationAndRange=function(){var e,t=this.details,r=this.mediaSource;if(!t||!this.media||"open"!==(null==r?void 0:r.readyState))return null;var i=t.edge;if(t.live&&this.hls.config.liveDurationInfinity){if(t.fragments.length&&r.setLiveSeekableRange){var a=Math.max(0,t.fragmentStart);return{duration:1/0,start:a,end:Math.max(a,i)}}return{duration:1/0}}var n=null==(e=this.overrides)?void 0:e.duration;if(n)return _(n)?{duration:n}:null;var s=this.media.duration;return i>(_(r.duration)?r.duration:0)&&i>s||!_(s)?{duration:i}:null},r.updateMediaSource=function(e){var t=e.duration,r=e.start,i=e.end,a=this.mediaSource;a&&this.media&&"open"===a.readyState&&(a.duration!==t&&(_(t)&&this.log("Updating MediaSource duration to "+t.toFixed(3)),a.duration=t),void 0!==r&&void 0!==i&&(this.log("MediaSource duration is set to "+a.duration+". Setting seekable range to "+r+"-"+i+"."),a.setLiveSeekableRange(r,i)))},r.checkPendingTracks=function(){var e=this.bufferCodecEventsTotal,t=this.pendingTrackCount,r=this.tracks;if(this.log("checkPendingTracks (pending: "+t+" codec events expected: "+e+") "+Bt(r)),this.tracksReady){var i,a=null==(i=this.transferData)?void 0:i.tracks;a&&Object.keys(a).length?this.attachTransferred():this.createSourceBuffers()}},r.bufferCreated=function(){var e=this;if(this.sourceBufferCount){var t={};this.sourceBuffers.forEach((function(r){var i=r[0],a=r[1];if(i){var n=e.tracks[i];t[i]={buffer:a,container:n.container,codec:n.codec,supplemental:n.supplemental,levelCodec:n.levelCodec,id:n.id,metadata:n.metadata}}})),this.hls.trigger(O.BUFFER_CREATED,{tracks:t}),this.log("SourceBuffers created. Running queue: "+this.operationQueue),this.sourceBuffers.forEach((function(t){var r=t[0];e.executeNext(r)}))}else{var r=new Error("could not create source buffer for media codec(s)");this.hls.trigger(O.ERROR,{type:x.MEDIA_ERROR,details:w.BUFFER_INCOMPATIBLE_CODECS_ERROR,fatal:!0,error:r,reason:r.message})}},r.createSourceBuffers=function(){var e=this.tracks,t=this.sourceBuffers,r=this.mediaSource;if(!r)throw new Error("createSourceBuffers called when mediaSource was null");for(var i in e){var a=i,n=e[a];if(this.isPending(n)){var s=this.getTrackCodec(n,a),o=n.container+";codecs="+s;n.codec=s,this.log("creating sourceBuffer("+o+")"+(this.currentOp(a)?" Queued":"")+" "+Bt(n));try{var l=r.addSourceBuffer(o),u=Er(a),d=[a,l];t[u]=d,n.buffer=l}catch(e){var h;return this.error("error while trying to add sourceBuffer: "+e.message),this.shiftAndExecuteNext(a),null==(h=this.operationQueue)||h.removeBlockers(),delete this.tracks[a],void this.hls.trigger(O.ERROR,{type:x.MEDIA_ERROR,details:w.BUFFER_ADD_CODEC_ERROR,fatal:!1,error:e,sourceBufferName:a,mimeType:o,parent:n.id})}this.trackSourceBuffer(a,n)}}this.bufferCreated()},r.clearBufferAppendTimeoutId=function(e){e&&(self.clearTimeout(e.bufferAppendTimeoutId),e.bufferAppendTimeoutId=void 0)},r.getTrackCodec=function(e,t){var r=e.supplemental,i=e.codec;r&&("video"===t||"audiovideo"===t)&&mt(r,"video")&&(i=function(e,t){var r=[];if(e)for(var i=e.split(","),a=0;a<i.length;a++)vt(i[a],"video")||r.push(i[a]);return t&&r.push(t),r.join(",")}(i,r));var a=At(i,e.levelCodec);return a?"audio"===t.slice(0,5)?Rt(a,this.appendSource):a:""},r.trackSourceBuffer=function(e,t){var r=this,i=t.buffer;if(i){var a=this.getTrackCodec(t,e);this.tracks[e]={buffer:i,codec:a,container:t.container,levelCodec:t.levelCodec,supplemental:t.supplemental,metadata:t.metadata,id:t.id,listeners:[]},this.removeBufferListeners(e),this.addBufferListener(e,"updatestart",this.onSBUpdateStart),this.addBufferListener(e,"updateend",this.onSBUpdateEnd),this.addBufferListener(e,"error",this.onSBUpdateError),this.appendSource&&this.addBufferListener(e,"bufferedchange",(function(e,t){var i=t.removedRanges;null!=i&&i.length&&(r.log(e+" buffer removed "+cr(i)),r.hls.trigger(O.BUFFER_FLUSHED,{type:e,start:i.start(0),end:i.end(i.length-1)}))}))}},r.onSBUpdateStart=function(e){var t=this.currentOp(e);t&&t.onStart()},r.onSBUpdateEnd=function(e){var t,r,i=this;if("closed"!==(null==(t=this.mediaSource)?void 0:t.readyState)){var a=this.currentOp(e);if(a){if(a.onComplete(),null==(r=this.tracks[e])||null==(r=r.buffer)?void 0:r.updating)return this.log(e+' SourceBuffer updating on "updateend"'),void this.blockUntilOpen((function(){i.shiftAndExecuteNext(e)}));this.shiftAndExecuteNext(e)}}else this.resetBuffer(e)},r.onSBUpdateError=function(e,t){var r,i=null==(r=this.mediaSource)?void 0:r.readyState,a=new Error(e+" SourceBuffer error. MediaSource readyState: "+i);this.error(""+a.message,t),this.hls.trigger(O.ERROR,{type:x.MEDIA_ERROR,details:w.BUFFER_APPENDING_ERROR,sourceBufferName:e,error:a,fatal:!1});var n=this.currentOp(e);n&&n.onError(a)},r.updateTimestampOffset=function(e,t,r,i,a,n){var s=t-e.timestampOffset;Math.abs(s)>=r&&(this.log("Updating "+i+" SourceBuffer timestampOffset to "+t+" (sn: "+a+" cc: "+n+")"),e.timestampOffset=t)},r.removeExecutor=function(e,t,r){var i=this.media,a=this.mediaSource,n=this.tracks[e],s=null==n?void 0:n.buffer;if(!i||!a||!s)throw new Error("Attempting to remove from the "+e+" SourceBuffer, but it does not exist");var o,l=_(i.duration)?i.duration:1/0,u=_(a.duration)?a.duration:1/0,d=Math.max(0,t);if(d>=u)return null==(o=this.currentOp(e))||o.onComplete(),void this.shiftAndExecuteNext(e);var h=Math.min(r,l,u);if(!(h>d)||n.ending&&!n.ended)throw new Error("Cannot remove "+(h<=d?"invalid range ("+d+" >= "+h+") ":"")+"from the "+e+" SourceBuffer"+(n.ending?" while track ending":""));n.ended=!1,this.log("Removing ["+d+","+h+"] from the "+e+" SourceBuffer"),s.remove(d,h)},r.appendExecutor=function(e,t){var r=this,i=this.tracks[t],a=null==i?void 0:i.buffer;if(!a)throw new mr("Attempting to append to the "+t+" SourceBuffer, but it does not exist");if(i.ending=!1,i.ended=!1,this.hls.config.appendTimeout!==1/0){var n=this.calculateAppendTimeoutTime(a);i.bufferAppendTimeoutId=self.setTimeout((function(){return r.appendTimeoutHandler(t,a,n)}),n)}a.appendBuffer(e)},r.appendTimeoutHandler=function(e,t,r){this.log("Received timeout after "+r+"ms for append on "+e+" source buffer. Aborting and triggering error.");try{t.abort()}catch(t){this.log("Failed to abort append on "+e+" source buffer after timeout.")}var i=this.currentOp(e);i&&i.onError(new Error(e+"-append-timeout"))},r.calculateAppendTimeoutTime=function(e){var t=10;this.details&&(t=this.details.levelTargetDuration);var r=2*t*1e3;if(null===this.media)return r;var i=dr.bufferInfo(e,this.media.currentTime,0);return i.len?(r=Math.max(1e3*i.len,r),Math.max(this.hls.config.appendTimeout,r)):r},r.blockUntilOpen=function(e){var t=this;if(this.isUpdating()||this.isQueued())this.blockBuffers(e).catch((function(e){t.warn("SourceBuffer blocked callback "+e),t.stepOperationQueue(t.sourceBufferTypes)}));else try{e()}catch(e){this.warn("Callback run without blocking "+this.operationQueue+" "+e)}},r.isUpdating=function(){return this.sourceBuffers.some((function(e){var t=e[0],r=e[1];return t&&r.updating}))},r.isQueued=function(){var e=this;return this.sourceBuffers.some((function(t){var r=t[0];return r&&!!e.currentOp(r)}))},r.isPending=function(e){return!!e&&!e.buffer},r.isAudioBlocked=function(){var e;return"block-audio"===(null==(e=this.currentOp("audio"))?void 0:e.label)},r.isAudioBlocking=function(){return!!this.operationQueue&&this.operationQueue.audioBlocking()},r.blockBuffers=function(e,t){var r=this;if(void 0===t&&(t=this.sourceBufferTypes),!t.length)return this.log("Blocking operation requested, but no SourceBuffers exist"),Promise.resolve().then(e);var i=this.operationQueue,a=2===t.length&&this.isAudioBlocking()?[this.appendBlocker("video")]:t.map((function(e){return r.appendBlocker(e)}));return Promise.all(a).then((function(t){i===r.operationQueue&&(e(),r.stepOperationQueue(r.sourceBufferTypes))}))},r.stepOperationQueue=function(e){var t=this;e.forEach((function(e){var r,i=null==(r=t.tracks[e])?void 0:r.buffer;!i||i.updating||"audio"===e&&t.isAudioBlocked()||t.shiftAndExecuteNext(e)}))},r.append=function(e,t,r){this.operationQueue&&this.operationQueue.append(e,t,r)},r.insertNext=function(e,t){this.operationQueue&&this.operationQueue.insertNext(e,t)},r.appendBlocker=function(e){if(this.operationQueue)return this.operationQueue.appendBlocker(e)},r.currentOp=function(e){return this.operationQueue?this.operationQueue.current(e):null},r.executeNext=function(e){e&&this.operationQueue&&this.operationQueue.executeNext(e)},r.shiftAndExecuteNext=function(e){this.operationQueue&&this.operationQueue.shiftAndExecuteNext(e)},r.addBufferListener=function(e,t,r){var i=this.tracks[e];if(i){var a=i.buffer;if(a){var n=r.bind(this,e);i.listeners.push({event:t,listener:n}),hr(a,t,n)}}},r.removeBufferListeners=function(e){var t=this.tracks[e];if(t){var r=t.buffer;r&&(t.listeners.forEach((function(e){fr(r,e.event,e.listener)})),t.listeners.length=0)}},a(t,[{key:"mediaSourceOpenOrEnded",get:function(){var e,t=null==(e=this.mediaSource)?void 0:e.readyState;return"open"===t||"ended"===t}},{key:"sourceBufferTracks",get:function(){var e=this;return Object.keys(this.tracks).reduce((function(t,r){var i=e.tracks[r];return t[r]={id:i.id,container:i.container,codec:i.codec,levelCodec:i.levelCodec},t}),{})}},{key:"bufferedToEnd",get:function(){var e=this;return this.sourceBufferCount>0&&!this.sourceBuffers.some((function(t){var r=t[0];if(r){var i=e.tracks[r];if(i)return!i.ended||i.ending}return!1}))}},{key:"tracksReady",get:function(){var e=this.pendingTrackCount;return e>0&&(e>=this.bufferCodecEventsTotal||this.isPending(this.tracks.audiovideo))}},{key:"mediaSrc",get:function(){var e,t,r=(null==(e=this.media)||null==(t=e.querySelector)?void 0:t.call(e,"source"))||this.media;return null==r?void 0:r.src}},{key:"pendingTrackCount",get:function(){var e=this;return Object.keys(this.tracks).reduce((function(t,r){return t+(e.isPending(e.tracks[r])?1:0)}),0)}},{key:"sourceBufferCount",get:function(){return this.sourceBuffers.reduce((function(e,t){return e+(t[0]?1:0)}),0)}},{key:"sourceBufferTypes",get:function(){return this.sourceBuffers.map((function(e){return e[0]})).filter((function(e){return!!e}))}}])}(ie);function yr(e){var t=e.querySelectorAll("source");[].slice.call(t).forEach((function(t){e.removeChild(t)}))}function Er(e){return"audio"===e?1:0}function Tr(e,t){return(e?"":" - triggering recovery")+(t?" with HTMLMediaElement Error: "+t.message:"")}function Sr(e){return e.type+"_"+e.sn+"_"+e.level}function Lr(e,t){for(var r=t.start,i=t.end,a=0,n=0;n<e.length;n++){var s=e[n];if(s.start>=i)break;var o=Math.max(r,s.start),l=Math.min(i,s.end);l>o&&(a+=l-o)}return a}function Rr(e,t){return t.duration-e<=.05}var Ar=function(){function e(e){this.hls=null,this.autoLevelCapping=void 0,this.media=void 0,this.restrictedLevels=void 0,this.timer=void 0,this.observer=void 0,this.clientRect=void 0,this.streamController=void 0,this.hls=e,this.autoLevelCapping=Number.POSITIVE_INFINITY,this.media=null,this.restrictedLevels=[],this.clientRect=null,this.registerListeners()}var t=e.prototype;return t.setStreamController=function(e){this.streamController=e},t.destroy=function(){this.hls&&this.unregisterListener(),(this.timer||this.observer)&&this.stopCapping(),this.media=this.clientRect=this.hls=null,this.streamController=void 0},t.registerListeners=function(){var e=this.hls;e&&(e.on(O.FPS_DROP_LEVEL_CAPPING,this.onFpsDropLevelCapping,this),e.on(O.MEDIA_ATTACHING,this.onMediaAttaching,this),e.on(O.MANIFEST_PARSED,this.onManifestParsed,this),e.on(O.LEVELS_UPDATED,this.onLevelsUpdated,this),e.on(O.BUFFER_CODECS,this.onBufferCodecs,this),e.on(O.MEDIA_DETACHING,this.onMediaDetaching,this))},t.unregisterListener=function(){var e=this.hls;e&&(e.off(O.FPS_DROP_LEVEL_CAPPING,this.onFpsDropLevelCapping,this),e.off(O.MEDIA_ATTACHING,this.onMediaAttaching,this),e.off(O.MANIFEST_PARSED,this.onManifestParsed,this),e.off(O.LEVELS_UPDATED,this.onLevelsUpdated,this),e.off(O.BUFFER_CODECS,this.onBufferCodecs,this),e.off(O.MEDIA_DETACHING,this.onMediaDetaching,this))},t.onFpsDropLevelCapping=function(e,t){if(this.hls){var r=this.hls.levels[t.droppedLevel];this.isLevelAllowed(r)&&this.restrictedLevels.push({bitrate:r.bitrate,height:r.height,width:r.width})}},t.onMediaAttaching=function(e,t){var r=t.media;this.clientRect=null,this.hls&&(r instanceof HTMLVideoElement?(this.media=r,this.hls.config.capLevelToPlayerSize&&this.observe()):this.media=null,(this.timer||this.observer)&&this.hls.levels.length>1&&this.detectPlayerSize())},t.onManifestParsed=function(e,t){this.restrictedLevels=[],t.video&&this.startCapping(),this.onLevelsUpdated()},t.onLevelsUpdated=function(){if(this.hls)if((this.timer||this.observer)&&(_(this.autoLevelCapping)||this.clientRect))this.detectPlayerSize();else if(void 0===this.observer&&void 0===this.timer){var e=this.hls.levels;e.length>1&&e.some((function(e){return!!e.videoCodec}))&&this.startCapping()}},t.onBufferCodecs=function(e,t){t.video&&this.startCapping()},t.onMediaDetaching=function(){this.stopCapping(),this.media=null},t.detectPlayerSize=function(){if(this.media){if(this.mediaHeight<=0||this.mediaWidth<=0||!this.hls)return void(this.clientRect=null);var e=this.hls.levels;if(e.length>1){var t=this.hls,r=this.getMaxLevel(e.length-1);r!==this.autoLevelCapping&&t.logger.log("Setting autoLevelCapping to "+r+": "+e[r].height+"p@"+e[r].bitrate+" for media "+this.mediaWidth+"x"+this.mediaHeight),t.autoLevelCapping=r,t.autoLevelEnabled&&t.autoLevelCapping>this.autoLevelCapping&&this.streamController&&this.streamController.nextLevelSwitch(),this.autoLevelCapping=t.autoLevelCapping}else 1===e.length&&this.stopCapping()}},t.getMaxLevel=function(t){var r=this;if(!this.hls)return-1;var i=this.hls.levels;if(!i.length)return-1;var a=i.filter((function(e,i){return r.isLevelAllowed(e)&&i<=t}));return this.observer||(this.clientRect=null),e.getMaxLevelByMediaSize(a,this.mediaWidth,this.mediaHeight)},t.observe=function(){var e=this,t=self.ResizeObserver;t&&(this.observer=new t((function(t){var r,i=null==(r=t[0])?void 0:r.contentRect;i&&(e.clientRect=i,e.detectPlayerSize())}))),this.observer&&this.media&&this.observer.observe(this.media)},t.startCapping=function(){var e;this.timer||this.observer||null==(e=this.hls)||!e.config.capLevelToPlayerSize||(self.clearInterval(this.timer),this.timer=void 0,this.autoLevelCapping=Number.POSITIVE_INFINITY,this.observe(),this.observer||(this.timer=self.setInterval(this.detectPlayerSize.bind(this),1e3)),this.detectPlayerSize())},t.stopCapping=function(){this.restrictedLevels=[],this.autoLevelCapping=Number.POSITIVE_INFINITY,this.timer&&(self.clearInterval(this.timer),this.timer=void 0),this.observer&&(this.observer.disconnect(),this.observer=void 0)},t.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},t.isLevelAllowed=function(e){return!this.restrictedLevels.some((function(t){return e.bitrate===t.bitrate&&e.width===t.width&&e.height===t.height}))},e.getMaxLevelByMediaSize=function(e,t,r){if(null==e||!e.length)return-1;for(var i,a,n=e.length-1,s=Math.max(t,r),o=0;o<e.length;o+=1){var l=e[o];if((l.width>=s||l.height>=s)&&(i=l,!(a=e[o+1])||i.width!==a.width||i.height!==a.height)){n=o;break}}return n},a(e,[{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)return e;var t=this.hls.config,r=t.ignoreDevicePixelRatio,i=t.maxDevicePixelRatio;if(!r)try{e=self.devicePixelRatio}catch(e){}return Math.min(e,i)}}])}(),br=/^(\d+)x(\d+)$/,kr=/(.+?)=(".*?"|.*?)(?:,|$)/g,Dr=function(){function e(t,r){"string"==typeof t&&(t=e.parseAttrList(t,r)),o(this,t)}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.enumeratedStringList=function(e,t){var r=this[e];return(r?r.split(/[ ,]+/):[]).reduce((function(e,t){return e[t.toLowerCase()]=!0,e}),t)},t.bool=function(e){return"YES"===this[e]},t.decimalResolution=function(e){var t=br.exec(this[e]);if(null!==t)return{width:parseInt(t[1],10),height:parseInt(t[2],10)}},e.parseAttrList=function(e,t){var r,i={};for(kr.lastIndex=0;null!==(r=kr.exec(e));){var a=r[1].trim(),n=r[2],s=0===n.indexOf('"')&&n.lastIndexOf('"')===n.length-1,o=!1;if(s)n=n.slice(1,-1);else switch(a){case"IV":case"SCTE35-CMD":case"SCTE35-IN":case"SCTE35-OUT":o=!0}if(t&&(s||o));else if(!o&&!s)switch(a){case"CLOSED-CAPTIONS":if("NONE"===n)break;case"ALLOWED-CPC":case"CLASS":case"ASSOC-LANGUAGE":case"AUDIO":case"BYTERANGE":case"CHANNELS":case"CHARACTERISTICS":case"CODECS":case"DATA-ID":case"END-DATE":case"GROUP-ID":case"ID":case"IMPORT":case"INSTREAM-ID":case"KEYFORMAT":case"KEYFORMATVERSIONS":case"LANGUAGE":case"NAME":case"PATHWAY-ID":case"QUERYPARAM":case"RECENTLY-REMOVED-DATERANGES":case"SERVER-URI":case"STABLE-RENDITION-ID":case"STABLE-VARIANT-ID":case"START-DATE":case"SUBTITLES":case"SUPPLEMENTAL-CODECS":case"URI":case"VALUE":case"VIDEO":case"X-ASSET-LIST":case"X-ASSET-URI":fe.warn(e+": attribute "+a+" is missing quotes")}i[a]=n}return i},a(e,[{key:"clientAttrs",get:function(){return Object.keys(this).filter((function(e){return"X-"===e.substring(0,2)}))}}])}();function Ir(e){return"SCTE35-OUT"===e||"SCTE35-IN"===e||"SCTE35-CMD"===e}var _r=function(){return a((function(e,t,r){var i;if(void 0===r&&(r=0),this.attr=void 0,this.tagAnchor=void 0,this.tagOrder=void 0,this._startDate=void 0,this._endDate=void 0,this._dateAtEnd=void 0,this._cue=void 0,this._badValueForSameId=void 0,this.tagAnchor=(null==t?void 0:t.tagAnchor)||null,this.tagOrder=null!=(i=null==t?void 0:t.tagOrder)?i:r,t){var a=t.attr;for(var n in a)if(Object.prototype.hasOwnProperty.call(e,n)&&e[n]!==a[n]){fe.warn('DATERANGE tag attribute: "'+n+'" does not match for tags with ID: "'+e.ID+'"'),this._badValueForSameId=n;break}e=o(new Dr({}),a,e)}if(this.attr=e,t?(this._startDate=t._startDate,this._cue=t._cue,this._endDate=t._endDate,this._dateAtEnd=t._dateAtEnd):this._startDate=new Date(e["START-DATE"]),"END-DATE"in e){var s=(null==t?void 0:t.endDate)||new Date(e["END-DATE"]);_(s.getTime())&&(this._endDate=s)}}),[{key:"id",get:function(){return this.attr.ID}},{key:"class",get:function(){return this.attr.CLASS}},{key:"cue",get:function(){var e=this._cue;return void 0===e?this._cue=this.attr.enumeratedStringList(this.attr.CUE?"CUE":"X-CUE",{pre:!1,post:!1,once:!1}):e}},{key:"startTime",get:function(){var e=this.tagAnchor;return null===e||null===e.programDateTime?(fe.warn('Expected tagAnchor Fragment with PDT set for DateRange "'+this.id+'": '+e),NaN):e.start+(this.startDate.getTime()-e.programDateTime)/1e3}},{key:"startDate",get:function(){return this._startDate}},{key:"endDate",get:function(){var e=this._endDate||this._dateAtEnd;if(e)return e;var t=this.duration;return null!==t?this._dateAtEnd=new Date(this._startDate.getTime()+1e3*t):null}},{key:"duration",get:function(){if("DURATION"in this.attr){var e=this.attr.decimalFloatingPoint("DURATION");if(_(e))return e}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:"isInterstitial",get:function(){return"com.apple.hls.interstitial"===this.class}},{key:"isValid",get:function(){return!!this.id&&!this._badValueForSameId&&_(this.startDate.getTime())&&(null===this.duration||this.duration>=0)&&(!this.endOnNext||!!this.class)&&(!this.attr.CUE||!this.cue.pre&&!this.cue.post||this.cue.pre!==this.cue.post)&&(!this.isInterstitial||"X-ASSET-URI"in this.attr||"X-ASSET-LIST"in this.attr)}},{key:"invalidReason",get:function(){return this.id?this._badValueForSameId?"Date range "+this._badValueForSameId+" mismatch":_(this.startDate.getTime())?!this.isInterstitial||"X-ASSET-URI"in this.attr||"X-ASSET-LIST"in this.attr?this.isValid?null:"Unknown (check DURATION(|END|NEXT), CLASS, and CUE)":"Interstitial Date range missing X-ASSET-(LIST|URI)":"Date range invalid start date":"Missing ID"}}])}();function Cr(e,t){return e.length===t.length&&!e.some((function(e,r){return e!==t[r]}))}function Pr(e,t){return!e&&!t||!(!e||!t)&&Cr(e,t)}var xr=0,wr=1;function Or(e){return"AES-128"===e||"AES-256"===e||"AES-256-CTR"===e}function Mr(e){switch(e){case"AES-128":case"AES-256":return xr;case"AES-256-CTR":return wr;default:throw new Error("invalid full segment method "+e)}}var Fr={},Nr=function(){function e(e,t,r,i,a,n){void 0===i&&(i=[1]),void 0===a&&(a=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.scheme=void 0,this.method=e,this.uri=t,this.keyFormat=r,this.keyFormatVersions=i,this.iv=a,this.encrypted=!!e&&"NONE"!==e,this.isCommonEncryption=this.encrypted&&!Or(e),null!=n&&n.startsWith("0x")&&(this.keyId=new Uint8Array(we(n)))}e.clearKeyUriToKeyIdMap=function(){Fr={}},e.setKeyIdForUri=function(e,t){Fr[e]=t},e.addKeyIdForUri=function(e){var t=Object.keys(Fr).length%Number.MAX_SAFE_INTEGER,r=new Uint8Array(16);return new DataView(r.buffer,12,4).setUint32(0,t),Fr[e]=r,r};var t=e.prototype;return t.matches=function(e){return e.uri===this.uri&&e.method===this.method&&e.encrypted===this.encrypted&&e.keyFormat===this.keyFormat&&Cr(e.keyFormatVersions,this.keyFormatVersions)&&Pr(e.iv,this.iv)&&Pr(e.keyId,this.keyId)},t.isSupported=function(){if(this.method){if(Or(this.method)||"NONE"===this.method)return!0;if("identity"===this.keyFormat)return"SAMPLE-AES"===this.method}return!1},t.getDecryptData=function(t,r){if(!this.encrypted||!this.uri)return null;if(Or(this.method)){var i=this.iv;return i||("number"!=typeof t&&(fe.warn('missing IV for initialization segment with method="'+this.method+'" - compliance issue'),t=0),i=function(e){for(var t=new Uint8Array(16),r=12;r<16;r++)t[r]=e>>8*(15-r)&255;return t}(t)),new e(this.method,this.uri,"identity",this.keyFormatVersions,i)}return this},e}(),Br=/#EXT-X-STREAM-INF:([^\r\n]*)(?:[\r\n](?:#[^\r\n]*)?)*([^\r\n]+)|#EXT-X-(I-FRAME-STREAM-INF|SESSION-DATA|SESSION-KEY|DEFINE|CONTENT-STEERING|START):([^\r\n]*)(?:[\r\n]+|$)/g,Ur=/#EXT-X-MEDIA:(.*)/g,Gr=/^#EXT(?:INF|-X-TARGETDURATION):/m,Vr=new RegExp([/#EXTINF:\s*(\d*(?:\.\d+)?)(?:,(.*)\s+)?/.source,/(?!#) *(\S[^\r\n]*)/.source,/#.*/.source].join("|"),"g"),Hr=new RegExp([/#EXT-X-(PROGRAM-DATE-TIME|BYTERANGE|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|I-FRAMES-ONLY)/.source,/(#)([^:]*):(.*)/.source,/(#)(.*)(?:.*)\r?\n?/.source].join("|")),Kr=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.resolve=function(e,t){return I.buildAbsoluteURL(t,e,{alwaysNormalize:!0})},e.isMediaPlaylist=function(e){return Gr.test(e)},e.parseMasterPlaylist=function(t,r){var i,a={contentSteering:null,levels:[],iframeVariants:[],playlistParsingError:null,sessionData:null,sessionKeys:null,startTimeOffset:null,variableList:null,hasVariableRefs:!1},n=[];if(Br.lastIndex=0,!t.startsWith("#EXTM3U"))return a.playlistParsingError=new Error("no EXTM3U delimiter"),a;for(;null!=(i=Br.exec(t));)if(i[1]){var s=Yr(new Dr(i[1],a),i[2],r,0,n);s&&a.levels.push(s)}else if(i[3]){var o=i[3],l=i[4];switch(o){case"I-FRAME-STREAM-INF":var u=new Dr(l,a),d=Yr(u,u.URI,r,0,n,!0);d&&a.iframeVariants.push(d);break;case"SESSION-DATA":var h=new Dr(l,a),f=h["DATA-ID"];f&&(null===a.sessionData&&(a.sessionData={}),a.sessionData[f]=h);break;case"SESSION-KEY":var c=qr(l,r,a);c.encrypted&&c.isSupported()?(null===a.sessionKeys&&(a.sessionKeys=[]),a.sessionKeys.push(c)):fe.warn('[Keys] Ignoring invalid EXT-X-SESSION-KEY tag: "'+l+'"');break;case"DEFINE":break;case"CONTENT-STEERING":var g=new Dr(l,a);a.contentSteering={uri:e.resolve(g["SERVER-URI"],r),pathwayId:g["PATHWAY-ID"]||"."};break;case"START":a.startTimeOffset=Xr(l)}}var v=n.length>0&&n.length<a.levels.length;return a.levels=v?n:a.levels,0===a.levels.length&&(a.playlistParsingError=new Error("no levels found in manifest")),a},e.parseMasterPlaylistMedia=function(t,r,i){var a,n={},s=i.levels,o={AUDIO:s.map((function(e){return{id:e.attrs.AUDIO,audioCodec:e.audioCodec}})),SUBTITLES:s.map((function(e){return{id:e.attrs.SUBTITLES,textCodec:e.textCodec}})),"CLOSED-CAPTIONS":[]},l=0;for(Ur.lastIndex=0;null!==(a=Ur.exec(t));){var u=new Dr(a[1],i),d=u.TYPE;if(d){var h=o[d],f=n[d]||[];n[d]=f;var c=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||c||"",type:d,default:u.bool("DEFAULT"),autoselect:u.bool("AUTOSELECT"),forced:u.bool("FORCED"),lang:c,url:u.URI?e.resolve(u.URI,r):""};if(g&&(y.assocLang=g),v&&(y.channels=v),m&&(y.characteristics=m),p&&(y.instreamId=p),null!=h&&h.length){var E=e.findGroup(h,y.groupId)||h[0];Qr(y,E,"audioCodec"),Qr(y,E,"textCodec")}f.push(y)}}return n},e.parseLevelPlaylist=function(e,t,r,i,a,n){var s,l,u,d,h,f={url:t},c=new lr(t),g=c.fragments,v=[],m=null,p=0,y=0,E=0,T=0,S=0,L=null,R=new K(i,f),A=-1,b=!1,k=null;if(Vr.lastIndex=0,c.m3u8=e,c.hasVariableRefs=!1,"#EXTM3U"!==(null==(s=Vr.exec(e))?void 0:s[0]))return c.playlistParsingError=new Error("Missing format identifier #EXTM3U"),c;for(;null!==(l=Vr.exec(e));){b&&(b=!1,(R=new K(i,f)).playlistOffset=E,R.setStart(E),R.sn=p,R.cc=T,S&&(R.bitrate=S),R.level=r,m&&(R.initSegment=m,m.rawProgramDateTime&&(R.rawProgramDateTime=m.rawProgramDateTime,m.rawProgramDateTime=null),k&&(R.setByteRange(k),k=null)));var D=l[1];if(D){R.duration=parseFloat(D);var I=(" "+l[2]).slice(1);R.title=I||null,R.tagList.push(I?["INF",D,I]:["INF",D])}else if(l[3]){if(_(R.duration)){var C;R.playlistOffset=E,R.setStart(E),d&&Jr(R,d,c),R.sn=p,R.level=r,R.cc=T;var P=(" "+l[3]).slice(1);R.relurl=P,$r(R,L,v),L=R,E+=R.duration,p++,y=0;var x=R.byteRange;if(2===x.length&&(R.bitrate=8*(x[1]-x[0])/R.duration|0),c.iframesOnly&&x[0]&&(null==(C=m)?void 0:C.cc)!==T){var w=new K(i,f);w.relurl=R.relurl,w.setByteRange(Math.min(x[0],1316)+"@0"),w.level=r,w.sn="initSegment",d&&(w.levelkeys=d,d.identity&&(w._decryptdata=d.identity.getDecryptData(0))),m=w,R.initSegment=m}g.push(R),b=!0}}else{if(!(l=l[0].match(Hr))){fe.warn("No matches on slow regex match for level playlist!");continue}for(u=1;u<l.length&&void 0===l[u];u++);var O=(" "+l[u]).slice(1),M=(" "+l[u+1]).slice(1),F=l[u+2]?(" "+l[u+2]).slice(1):null;switch(O){case"BYTERANGE":L?R.setByteRange(M,L):R.setByteRange(M);break;case"PROGRAM-DATE-TIME":R.rawProgramDateTime=M,R.tagList.push(["PROGRAM-DATE-TIME",M]),-1===A&&(A=g.length);break;case"PLAYLIST-TYPE":c.type&&ei(c,O,l),c.type=M.toUpperCase();break;case"MEDIA-SEQUENCE":0!==c.startSN?ei(c,O,l):null!==L&&ti(c,O,l),c.startSN=parseInt(M),p=c.startSN+c.skippedSegments;break;case"SKIP":c.skippedSegments&&ei(c,O,l);var N=new Dr(M,c),B=N.decimalInteger("SKIPPED-SEGMENTS");if(_(B)){c.skippedSegments+=B;for(var U=B;U--;)g.push(null);p+=B}var G=N.enumeratedString("RECENTLY-REMOVED-DATERANGES");G&&(c.recentlyRemovedDateranges=(c.recentlyRemovedDateranges||[]).concat(G.split("\t")));break;case"TARGETDURATION":0!==c.targetduration&&ei(c,O,l),c.targetduration=Math.max(parseInt(M),1);break;case"VERSION":null!==c.version&&ei(c,O,l),c.version=parseInt(M);break;case"INDEPENDENT-SEGMENTS":case"DEFINE":break;case"I-FRAMES-ONLY":c.iframesOnly=!0;break;case"ENDLIST":c.live||ei(c,O,l),c.live=!1;break;case"#":(M||F)&&R.tagList.push(F?[M,F]:[M]);break;case"DISCONTINUITY":T++,R.tagList.push(["DIS"]);break;case"GAP":R.gap=!0,R.tagList.push([O]);break;case"BITRATE":R.tagList.push([O,M]),S=1e3*parseInt(M),_(S)?R.bitrate=S:S=0;break;case"DATERANGE":var V=new Dr(M,c),H=new _r(V,c.dateRanges[V.ID],c.dateRangeTagCount);c.dateRangeTagCount++,H.isValid||c.skippedSegments?c.dateRanges[H.id]=H:fe.log("Ignoring invalid DATERANGE tag: "+H.invalidReason+": "+M),R.tagList.push(["EXT-X-DATERANGE",M]);break;case"DISCONTINUITY-SEQUENCE":0!==c.startCC&&ei(c,O,l),c.startCC=T=parseInt(M),g.forEach((function(e){return e&&(e.cc=T)}));break;case"KEY":var W=qr(M,t,c);if(W.isSupported()){if("NONE"===W.method){d=void 0;break}d||(d={});var j=d[W.keyFormat];null!=j&&j.matches(W)||(j&&(d=o({},d)),d[W.keyFormat]=W)}else fe.warn('[Keys] Ignoring unsupported EXT-X-KEY tag: "'+M+'" (light build)');break;case"START":c.startTimeOffset=Xr(M);break;case"MAP":var q=new Dr(M,c);if(R.duration){var X=new K(i,f);Zr(X,q,r,d),m=X,R.initSegment=m,m.rawProgramDateTime&&!R.rawProgramDateTime&&(R.rawProgramDateTime=m.rawProgramDateTime)}else{var z=R.byteRangeEndOffset;if(z){var Q=R.byteRangeStartOffset;k=z-Q+"@"+Q}else k=null;Zr(R,q,r,d),m=R,b=!0}m.cc=T;break;case"SERVER-CONTROL":h&&ei(c,O,l),h=new Dr(M),c.canBlockReload=h.bool("CAN-BLOCK-RELOAD"),c.canSkipUntil=h.optionalFloat("CAN-SKIP-UNTIL",0),c.canSkipDateRanges=c.canSkipUntil>0&&h.bool("CAN-SKIP-DATERANGES"),c.partHoldBack=h.optionalFloat("PART-HOLD-BACK",0),c.holdBack=h.optionalFloat("HOLD-BACK",0);break;case"PART-INF":c.partTarget&&ei(c,O,l);var $=new Dr(M);c.partTarget=$.decimalFloatingPoint("PART-TARGET");break;case"PART":var Z=c.partList;Z||(Z=c.partList=[]);var J=y>0?Z[Z.length-1]:void 0,ee=y++,te=new Dr(M,c),re=new Y(te,R,f,ee,J);Z.push(re),R.duration+=re.duration;break;case"PRELOAD-HINT":var ie=new Dr(M,c);c.preloadHint=ie;break;case"RENDITION-REPORT":var ae=new Dr(M,c);c.renditionReports=c.renditionReports||[],c.renditionReports.push(ae);break;default:fe.warn("line parsed but not handled: "+l)}}}L&&!L.relurl?(g.pop(),E-=L.duration,c.partList&&(c.fragmentHint=L)):c.partList&&($r(R,L,v),R.cc=T,c.fragmentHint=R,d&&Jr(R,d,c)),c.targetduration||(c.playlistParsingError=new Error("Missing Target Duration"));var ne=g.length,se=g[0],oe=g[ne-1];if((E+=c.skippedSegments*c.targetduration)>0&&ne&&oe){c.averagetargetduration=E/ne;var le=oe.sn;c.endSN="initSegment"!==le?le:0,c.live||(oe.endList=!0),A>0&&(function(e,t,r){if(r||(r=e[t]),r)for(var i=t;i--;){var a=e[i];if(!a)return;a.programDateTime=r.programDateTime-1e3*a.duration,r=a}}(g,A,A>ne-1?R:null),se&&v.unshift(se))}return c.fragmentHint&&(E+=c.fragmentHint.duration),c.totalduration=E,v.length&&c.dateRangeTagCount&&se&&Wr(v,c),c.endCC=T,c},e}();function Yr(e,t,r,i,a,n){var s;if(void 0===t)return null;var o=t,l={attrs:e,bitrate:e.decimalInteger("BANDWIDTH")||e.decimalInteger("AVERAGE-BANDWIDTH"),name:e.NAME,url:Kr.resolve(o,r)},u=e.decimalResolution("RESOLUTION");u&&(l.width=u.width,l.height=u.height),zr(e.CODECS,l);var d=e["SUPPLEMENTAL-CODECS"];return d&&(l.supplemental={},zr(d,l.supplemental)),null!=(s=l.unknownCodecs)&&s.length||a.push(l),n&&(l.iframes=!0),l}function Wr(e,t){var r=e.length;if(!r){if(!t.hasProgramDateTime)return;var i=t.fragments[t.fragments.length-1];e.push(i),r++}for(var a=e[r-1],n=t.live?1/0:t.totalduration,s=Object.keys(t.dateRanges),o=s.length;o--;){var l=t.dateRanges[s[o]],u=l.startDate.getTime();l.tagAnchor=a.ref;for(var d=r;d--;){var h;if((null==(h=e[d])?void 0:h.sn)<t.startSN)break;var f=jr(t,u,e,d,n);if(f){l.tagAnchor=f.ref;break}}}}function jr(e,t,r,i,a){var n=r[i];if(n){var s,o=n.programDateTime;if((t>=o||0===i)&&t<=o+1e3*(((null==(s=r[i+1])?void 0:s.start)||a)-n.start)){var l=n.sn-e.startSN,u=e.fragments;if(l>=0&&l<u.length){if(u.length>r.length)for(var d=r[i+1]||u[u.length-1],h=Math.min(d.sn-e.startSN,u.length-1);h>l;h--){var f=u[h].programDateTime;if(t>=f&&t<f+1e3*u[h].duration)return u[h]}return u[l]}}}return null}function qr(e,t,r){var i,a,n=new Dr(e,r),s=null!=(i=n.METHOD)?i:"",o=n.URI,l=n.hexadecimalInteger("IV"),u=n.KEYFORMATVERSIONS,d=null!=(a=n.KEYFORMAT)?a:"identity";o&&n.IV&&!l&&fe.error("Invalid IV: "+n.IV);var h=o?Kr.resolve(o,t):"",f=(u||"1").split("/").map(Number).filter(Number.isFinite);return new Nr(s,h,d,f,l,n.KEYID)}function Xr(e){var t=new Dr(e).decimalFloatingPoint("TIME-OFFSET");return _(t)?t:null}function zr(e,t){var r=(e||"").split(/[ ,]+/).filter((function(e){return e}));["video","audio","text","image"].forEach((function(e){var i=r.filter((function(t){return vt(t,e)}));i.length&&(t[e+"Codec"]=i.map((function(e){return e.split("/")[0]})).join(","),r=r.filter((function(e){return-1===i.indexOf(e)})))})),t.unknownCodecs=r}function Qr(e,t,r){var i=t[r];i&&(e[r]=i)}function $r(e,t,r){e.rawProgramDateTime?r.push(e):null!=t&&t.programDateTime&&(e.programDateTime=t.endProgramDateTime)}function Zr(e,t,r,i){e.relurl=t.URI,t.BYTERANGE&&e.setByteRange(t.BYTERANGE),e.level=r,e.sn="initSegment",i&&(e.levelkeys=i),e.initSegment=null}function Jr(e,t,r){e.levelkeys=t;var i=r.encryptedFragments;i.length&&i[i.length-1].levelkeys===t||!Object.keys(t).some((function(e){return t[e].isCommonEncryption}))||i.push(e)}function ei(e,t,r){e.playlistParsingError=new Error("#EXT-X-"+t+" must not appear more than once ("+r[0]+")")}function ti(e,t,r){e.playlistParsingError=new Error("#EXT-X-"+t+" must appear before the first Media Segment ("+r[0]+")")}function ri(e,t){var r=t.startPTS;if(_(r)){var i,a=0;t.sn>e.sn?(a=r-e.start,i=e):(a=e.start-r,i=t),i.duration!==a&&i.setDuration(a)}else t.sn>e.sn?e.cc===t.cc&&e.minEndPTS?t.setStart(e.start+(e.minEndPTS-e.start)):t.setStart(e.start+e.duration):t.setStart(Math.max(e.start-t.duration,0))}function ii(e,t,r,i,a,n,s,o){i-r<=0&&(o.warn("Fragment should have a positive duration",t),i=r+t.duration,n=a+t.duration);var l=r,u=i,d=t.startPTS,h=t.endPTS;if(_(d)){var f=Math.abs(d-r);e&&f>e.totalduration?o.warn("media timestamps and playlist times differ by "+f+"s for level "+t.level+" "+e.url):_(t.deltaPTS)?t.deltaPTS=Math.max(f,t.deltaPTS):t.deltaPTS=f,l=Math.max(r,d),r=Math.min(r,d),a=void 0!==t.startDTS?Math.min(a,t.startDTS):a,u=Math.min(i,h),i=Math.max(i,h),n=void 0!==t.endDTS?Math.max(n,t.endDTS):n}var c=r-t.start;0!==t.start&&t.setStart(r),t.setDuration(i-t.start),t.startPTS=r,t.maxStartPTS=l,t.startDTS=a,t.endPTS=i,t.minEndPTS=u,t.endDTS=n;var g,v=t.sn;if(!e||v<e.startSN||v>e.endSN)return 0;var m=v-e.startSN,p=e.fragments;for(p[m]=t,g=m;g>0;g--)ri(p[g],p[g-1]);for(g=m;g<p.length-1;g++)ri(p[g],p[g+1]);return e.fragmentHint&&ri(p[p.length-1],e.fragmentHint),e.PTSKnown=e.alignedSliding=!0,c}function ai(e,t,r){if(e!==t){for(var i,a=null,n=e.fragments,s=n.length-1;s>=0;s--){var l=n[s].initSegment;if(l){a=l;break}}e.fragmentHint&&delete e.fragmentHint.endPTS,function(e,t,r){for(var i,a=t.skippedSegments,n=Math.max(e.startSN,t.startSN)-t.startSN,s=(e.fragmentHint?1:0)+(a?t.endSN:Math.min(e.endSN,t.endSN))-t.startSN,o=t.startSN-e.startSN,l=t.fragmentHint?t.fragments.concat(t.fragmentHint):t.fragments,u=null!=(i=e.fragmentHint)&&i.duration?e.fragments.concat(e.fragmentHint):e.fragments,d=n;d<=s;d++){var h=u[o+d],f=l[d];if(a&&!f&&h&&(f=t.fragments[d]=h),h&&f){r(h,f,d,l);var c=h.relurl,g=f.relurl;if(c&&fi(c,g))return void(t.playlistParsingError=ni("media sequence mismatch "+f.sn+":",e,t,0,f));if(h.cc!==f.cc)return void(t.playlistParsingError=ni("discontinuity sequence mismatch ("+h.cc+"!="+f.cc+")",e,t,0,f))}}}(e,t,(function(e,r,n,s){if((!t.startCC||t.skippedSegments)&&r.cc!==e.cc){for(var o=e.cc-r.cc,l=n;l<s.length;l++)s[l].cc+=o;t.endCC=s[s.length-1].cc}_(e.startPTS)&&_(e.endPTS)&&(r.setStart(r.startPTS=e.startPTS),r.startDTS=e.startDTS,r.maxStartPTS=e.maxStartPTS,r.endPTS=e.endPTS,r.endDTS=e.endDTS,r.minEndPTS=e.minEndPTS,r.setDuration(e.endPTS-e.startPTS),r.duration&&(i=r),t.PTSKnown=t.alignedSliding=!0),e.hasStreams&&(r.elementaryStreams=e.elementaryStreams),r.loader=e.loader,e.hasStats&&(r.stats=e.stats),e.initSegment&&(r.initSegment=e.initSegment,a=e.initSegment)}));var u=t.fragments,d=t.fragmentHint?u.concat(t.fragmentHint):u;if(a&&d.forEach((function(e){var t;!e||e.initSegment&&e.initSegment.relurl!==(null==(t=a)?void 0:t.relurl)||(e.initSegment=a)})),t.skippedSegments){if(t.deltaUpdateFailed=u.some((function(e){return!e})),t.deltaUpdateFailed){r.warn("[level-helper] Previous playlist missing segments skipped in delta playlist");for(var h=t.skippedSegments;h--;)u.shift();t.startSN=u[0].sn}else{t.canSkipDateRanges&&(t.dateRanges=function(e,t,r){var i=t.dateRanges,a=t.recentlyRemovedDateranges,n=o({},e);a&&a.forEach((function(e){delete n[e]}));var s=Object.keys(n).length;return s?(Object.keys(i).forEach((function(e){var t=n[e],a=new _r(i[e].attr,t);a.isValid?(n[e]=a,t||(a.tagOrder+=s)):r.warn('Ignoring invalid Playlist Delta Update DATERANGE tag: "'+Bt(i[e].attr)+'"')})),n):i}(e.dateRanges,t,r));var f=e.fragments.filter((function(e){return e.rawProgramDateTime}));if(e.hasProgramDateTime&&!t.hasProgramDateTime)for(var c=1;c<d.length;c++)null===d[c].programDateTime&&$r(d[c],d[c-1],f);Wr(f,t)}t.endCC=u[u.length-1].cc}if(!t.startCC){var g,v=li(e,t.startSN-1);t.startCC=null!=(g=null==v?void 0:v.cc)?g:u[0].cc}(function(e,t,r){if(e&&t)for(var i=0,a=0,n=e.length;a<n;a++){var s=e[a],o=t[a+i];s&&o&&s.index===o.index&&s.fragment.sn===o.fragment.sn?r(s,o):i--}})(e.partList,t.partList,(function(e,t){t.elementaryStreams=e.elementaryStreams,t.stats=e.stats,t.gap=e.gap||t.gap})),i?(t.iframesOnly&&(i.type,N.MAIN),ii(t,i,i.startPTS,i.endPTS,i.startDTS,i.endDTS,0,r)):si(e,t),u.length&&(t.totalduration=t.edge-u[0].start),t.driftStartTime=e.driftStartTime,t.driftStart=e.driftStart;var m=t.advancedDateTime;if(t.advanced&&m){var p=t.edge;t.driftStart||(t.driftStartTime=m,t.driftStart=p),t.driftEndTime=m,t.driftEnd=p}else t.driftEndTime=e.driftEndTime,t.driftEnd=e.driftEnd,t.advancedDateTime=e.advancedDateTime;-1===t.requestScheduled&&(t.requestScheduled=e.requestScheduled)}}function ni(e,t,r,i,a){return new Error(e+" "+a.url+"\nPlaylist starting @"+t.startSN+"\n"+t.m3u8+"\n\nPlaylist starting @"+r.startSN+"\n"+r.m3u8)}function si(e,t,r,i){void 0===r&&(r=!0);var a=t.startSN+t.skippedSegments-e.startSN,n=e.fragments,s=a>=0,o=0;if(s&&a<n.length)o=n[a].start,null==i||i.log("Aligning playlists based on SN "+t.startSN+" (diff: "+o+")");else if(s&&t.startSN===e.endSN+1)o=e.fragmentEnd,null==i||i.log("Aligning playlists based on first/last SN "+t.startSN+" (diff: "+o+")");else if(s&&r)o=e.fragmentStart+a*t.levelTargetDuration;else{if(t.skippedSegments||0!==t.fragmentStart)return o;o=e.fragmentStart,null==i||i.log("Aligning playlists based on first SN "+t.startSN+" (diff: "+o+")")}return function(e,t){if(t){for(var r=e.fragments,i=e.skippedSegments;i<r.length;i++)r[i].addStart(t);e.fragmentHint&&e.fragmentHint.addStart(t)}}(t,o),o}function oi(e,t){void 0===t&&(t=1/0);var r=1e3*e.targetduration;if(e.updated){var i=e.fragments;if(i.length&&4*r>t){var a=1e3*i[i.length-1].duration;a>0&&a<r&&(r=a)}}else r/=2;return Math.round(r)}function li(e,t,r){var i;if(!e)return null;var a=e.fragments[t-e.startSN];return a||((null==(i=a=e.fragmentHint)?void 0:i.sn)===t?a:r&&t<e.startSN&&r.sn===t?r:null)}function ui(e,t,r){return e?di(e.partList,t,r):null}function di(e,t,r){if(e)for(var i=e.length;i--;){var a=e[i];if(a.index===r&&a.fragment.sn===t)return a}return null}function hi(e){e.forEach((function(e,t){var r;null==(r=e.details)||r.fragments.forEach((function(e){e.level=t,e.initSegment&&(e.initSegment.level=t)}))}))}function fi(e,t){return!(e===t||!t)&&ci(e)!==ci(t)}function ci(e){return e.replace(/\?[^?]*$/,"")}var gi=function(e){function t(t){var r;return(r=e.call(this,"content-steering",t.logger)||this).hls=void 0,r.loader=null,r.uri=null,r.pathwayId=".",r._pathwayPriority=null,r.timeToLoad=300,r.reloadTimer=-1,r.updated=0,r.started=!1,r.enabled=!0,r.levels=null,r.audioTracks=null,r.subtitleTracks=null,r.penalizedPathways={},r.hls=t,r.registerListeners(),r}u(t,e);var r=t.prototype;return r.registerListeners=function(){var e=this.hls;e.on(O.MANIFEST_LOADING,this.onManifestLoading,this),e.on(O.MANIFEST_LOADED,this.onManifestLoaded,this),e.on(O.MANIFEST_PARSED,this.onManifestParsed,this),e.on(O.ERROR,this.onError,this)},r.unregisterListeners=function(){var e=this.hls;e&&(e.off(O.MANIFEST_LOADING,this.onManifestLoading,this),e.off(O.MANIFEST_LOADED,this.onManifestLoaded,this),e.off(O.MANIFEST_PARSED,this.onManifestParsed,this),e.off(O.ERROR,this.onError,this))},r.pathways=function(){return(this.levels||[]).reduce((function(e,t){return-1===e.indexOf(t.pathwayId)&&e.push(t.pathwayId),e}),[])},r.startLoad=function(){if(this.started=!0,this.clearTimeout(),this.enabled&&this.uri){if(this.updated){var e=1e3*this.timeToLoad-(performance.now()-this.updated);if(e>0)return void this.scheduleRefresh(this.uri,e)}this.loadSteeringManifest(this.uri)}},r.stopLoad=function(){this.started=!1,this.loader&&(this.loader.destroy(),this.loader=null),this.clearTimeout()},r.clearTimeout=function(){-1!==this.reloadTimer&&(self.clearTimeout(this.reloadTimer),this.reloadTimer=-1)},r.destroy=function(){this.unregisterListeners(),this.stopLoad(),this.hls=null,this.levels=this.audioTracks=this.subtitleTracks=null},r.removeLevel=function(e){var t=this.levels;t&&(this.levels=t.filter((function(t){return t!==e})))},r.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},r.onManifestLoaded=function(e,t){var r=t.contentSteering;null!==r&&(this.pathwayId=r.pathwayId,this.uri=r.uri,this.started&&this.startLoad())},r.onManifestParsed=function(e,t){this.audioTracks=t.audioTracks,this.subtitleTracks=t.subtitleTracks},r.onError=function(e,t){var r=t.errorAction;if((null==r?void 0:r.action)===zt&&r.flags&Jt){var i=this.levels,a=this._pathwayPriority,n=this.pathwayId;if(t.context){var s=t.context,o=s.groupId,l=s.pathwayId,u=s.type;o&&i?n=this.getPathwayForGroupId(o,u,n):l&&(n=l)}n in this.penalizedPathways||(this.penalizedPathways[n]=performance.now()),!a&&i&&(a=this.pathways()),a&&a.length>1&&(this.updatePathwayPriority(a),r.resolved=this.pathwayId!==n,r.resolved||t.details===w.BUFFER_APPEND_ERROR&&!t.fatal||this.warn("Could not resolve "+t.details+' ("'+t.error.message+'") with content-steering for Pathway: '+n+" levels: "+(i?i.length:i)+" priorities: "+Bt(a)+" penalized: "+Bt(this.penalizedPathways)))}},r.filterParsedLevels=function(e){this.levels=e;var t=this.getLevelsForPathway(this.pathwayId);if(0===t.length){var r=e[0].pathwayId;this.log("No levels found in Pathway "+this.pathwayId+'. Setting initial Pathway to "'+r+'"'),t=this.getLevelsForPathway(r),this.pathwayId=r}return t.length!==e.length&&this.log("Found "+t.length+"/"+e.length+' levels in Pathway "'+this.pathwayId+'"'),t},r.getLevelsForPathway=function(e){return null===this.levels?[]:this.levels.filter((function(t){return e===t.pathwayId}))},r.updatePathwayPriority=function(e){var t;this._pathwayPriority=e;var r=this.penalizedPathways,i=performance.now();Object.keys(r).forEach((function(e){i-r[e]>3e5&&delete r[e]}));for(var a=0;a<e.length;a++){var n=e[a];if(!(n in r)){if(n===this.pathwayId)return;var s=this.hls.nextLoadLevel,o=this.hls.levels[s];if((t=this.getLevelsForPathway(n)).length>0){this.log('Setting Pathway to "'+n+'"'),this.pathwayId=n,hi(t),this.hls.trigger(O.LEVELS_UPDATED,{levels:t});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}}}},r.getPathwayForGroupId=function(e,t,r){for(var i=this.getLevelsForPathway(r).concat(this.levels||[]),a=0;a<i.length;a++)if(t===F.AUDIO_TRACK&&i[a].hasAudioGroup(e)||t===F.SUBTITLE_TRACK&&i[a].hasSubtitleGroup(e))return i[a].pathwayId;return r},r.clonePathways=function(e){var t=this,r=this.levels;if(r){var i={},a={};e.forEach((function(e){var n=e.ID,s=e["BASE-ID"],o=e["URI-REPLACEMENT"];if(!r.some((function(e){return e.pathwayId===n}))){var l=t.getLevelsForPathway(s).map((function(e){var t=new Dr(e.attrs);t["PATHWAY-ID"]=n;var r=t.AUDIO&&t.AUDIO+"_clone_"+n,s=t.SUBTITLES&&t.SUBTITLES+"_clone_"+n;r&&(i[t.AUDIO]=r,t.AUDIO=r),s&&(a[t.SUBTITLES]=s,t.SUBTITLES=s);var l=mi(e.uri,t["STABLE-VARIANT-ID"],"PER-VARIANT-URIS",o),u=new Mt({attrs:t,audioCodec:e.audioCodec,bitrate:e.bitrate,height:e.height,name:e.name,url:l,videoCodec:e.videoCodec,width:e.width});if(e.audioGroups)for(var d=1;d<e.audioGroups.length;d++)u.addGroupId("audio",e.audioGroups[d]+"_clone_"+n);if(e.subtitleGroups)for(var h=1;h<e.subtitleGroups.length;h++)u.addGroupId("text",e.subtitleGroups[h]+"_clone_"+n);return u}));r.push.apply(r,l),vi(t.audioTracks,i,o,n),vi(t.subtitleTracks,a,o,n)}}))}},r.loadSteeringManifest=function(e){var t,r=this,i=this.hls.config,a=i.loader;this.loader&&this.loader.destroy(),this.loader=new a(i);try{t=new self.URL(e)}catch(t){return this.enabled=!1,void this.log("Failed to parse Steering Manifest URI: "+e)}if("data:"!==t.protocol){var n=0|(this.hls.bandwidthEstimate||i.abrEwmaDefaultEstimate);Dt(t,"_HLS_pathway",this.pathwayId),Dt(t,"_HLS_throughput",""+n)}var s={type:F.STEERING_MANIFEST,responseType:"json",url:t.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},d={onSuccess:function(e,i,a,n){r.log('Loaded steering manifest: "'+t+'"');var s=e.data;if(1===(null==s?void 0: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,t).href}catch(e){return r.enabled=!1,void r.log("Failed to parse Steering Manifest RELOAD-URI: "+o)}r.scheduleRefresh(r.uri||a.url),l&&r.clonePathways(l);var d={steeringManifest:s,url:t.toString()};r.hls.trigger(O.STEERING_MANIFEST_LOADED,d),u&&r.updatePathwayPriority(u)}else r.log("Steering VERSION "+s.VERSION+" not supported!")},onError:function(e,t,i,a){if(r.log("Error loading steering manifest: "+e.code+" "+e.text+" ("+t.url+")"),r.stopLoad(),410===e.code)return r.enabled=!1,void r.log("Steering manifest "+t.url+" no longer available");var n=1e3*r.timeToLoad;if(429!==e.code)r.scheduleRefresh(r.uri||t.url,n);else{var s=r.loader;if("function"==typeof(null==s?void 0:s.getResponseHeader)){var o=s.getResponseHeader("Retry-After");o&&(n=1e3*parseFloat(o))}r.log("Steering manifest "+t.url+" rate limited")}},onTimeout:function(e,t,i){r.log("Timeout loading steering manifest ("+t.url+")"),r.scheduleRefresh(r.uri||t.url)}};this.log("Requesting steering manifest: "+t),this.loader.load(s,u,d)},r.scheduleRefresh=function(e,t){var r=this;void 0===t&&(t=1e3*this.timeToLoad),this.clearTimeout(),this.reloadTimer=self.setTimeout((function(){var t,i=null==(t=r.hls)?void 0:t.media;!i||i.ended?r.scheduleRefresh(e,1e3*r.timeToLoad):r.loadSteeringManifest(e)}),t)},a(t,[{key:"pathwayPriority",get:function(){return this._pathwayPriority},set:function(e){this.updatePathwayPriority(e)}}])}(ie);function vi(e,t,r,i){e&&Object.keys(t).forEach((function(a){var n=e.filter((function(e){return e.groupId===a})).map((function(e){var n=o({},e);return n.details=void 0,n.attrs=new Dr(n.attrs),n.url=n.attrs.URI=mi(e.url,e.attrs["STABLE-RENDITION-ID"],"PER-RENDITION-URIS",r),n.groupId=n.attrs["GROUP-ID"]=t[a],n.attrs["PATHWAY-ID"]=i,n}));e.push.apply(e,n)}))}function mi(e,t,r,i){if(t){var a=i[r];if(a){var n=a[t];if(n)return n}}var s=i.HOST,o=i.PARAMS,l=new self.URL(e);return s&&(l.hostname=s),o&&Object.keys(o).sort().forEach((function(e){e&&Dt(l,e,o[e])})),l.href}var pi=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(O.MEDIA_ATTACHING,this.onMediaAttaching,this),this.hls.on(O.MEDIA_DETACHING,this.onMediaDetaching,this)},t.unregisterListeners=function(){this.hls.off(O.MEDIA_ATTACHING,this.onMediaAttaching,this),this.hls.off(O.MEDIA_DETACHING,this.onMediaDetaching,this)},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.onMediaDetaching=function(){this.media=null},t.checkFPS=function(e,t,r){var i=performance.now();if(t){if(this.lastTime){var a=i-this.lastTime,n=r-this.lastDroppedFrames,s=t-this.lastDecodedFrames,o=1e3*n/a,l=this.hls;if(l.trigger(O.FPS_DROP,{currentDropped:n,currentDecoded:s,totalDroppedFrames:r}),o>0&&n>l.config.fpsDroppedMonitoringThreshold*s){var u=l.currentLevel;l.logger.warn("drop FPS ratio greater than max allowed value for currentLevel: "+u),u>0&&(-1===l.autoLevelCapping||l.autoLevelCapping>=u)&&(u-=1,l.trigger(O.FPS_DROP_LEVEL_CAPPING,{level:u,droppedLevel:l.currentLevel}),l.autoLevelCapping=u,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}(),yi="NOT_LOADED",Ei="APPENDING",Ti="PARTIAL",Si="OK",Li=function(){function e(e){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=e,this._registerListeners()}var t=e.prototype;return t._registerListeners=function(){var e=this.hls;e&&(e.on(O.MANIFEST_LOADING,this.onManifestLoading,this),e.on(O.BUFFER_APPENDED,this.onBufferAppended,this),e.on(O.FRAG_BUFFERED,this.onFragBuffered,this),e.on(O.FRAG_LOADED,this.onFragLoaded,this))},t._unregisterListeners=function(){var e=this.hls;e&&(e.off(O.MANIFEST_LOADING,this.onManifestLoading,this),e.off(O.BUFFER_APPENDED,this.onBufferAppended,this),e.off(O.FRAG_BUFFERED,this.onFragBuffered,this),e.off(O.FRAG_LOADED,this.onFragLoaded,this))},t.destroy=function(){this._unregisterListeners(),this.hls=this.fragments=this.activePartLists=this.endListFragments=this.timeRanges=null},t.getAppendedFrag=function(e,t){var r=this.activePartLists[t];if(r)for(var i=r.length;i--;){var a=r[i];if(!a)break;if(a.start<=e&&e<=a.end&&a.loaded)return a}return this.getBufferedFrag(e,t)},t.getBufferedFrag=function(e,t){return this.getFragAtPos(e,t,!0)},t.getFragAtPos=function(e,t,r){for(var i=this.fragments,a=Object.keys(i),n=a.length;n--;){var s=i[a[n]];if((null==s?void 0:s.body.type)===t&&(!r||s.buffered)){var o=s.body;if(o.start<=e&&e<=o.end)return o}}return null},t.detectEvictedFragments=function(e,t,r,i,a,n){var s=this;this.timeRanges&&(this.timeRanges[e]=t);var o=(null==a?void 0:a.fragment.sn)||-1;Object.keys(this.fragments).forEach((function(a){var l=s.fragments[a];if(l&&s.hls){var u=l.body;if(!(o>=u.sn))if(l.buffered||l.loaded&&!n){var d=l.range[e];if(d)if(0!==d.time.length){if(d.time.some((function(e){var r=!s.isTimeBuffered(e.startPTS,e.endPTS,t);return r&&s.removeFragment(u),r})),i&&i!==u&&ct()){var h=i.endPTS,f=u.startPTS;if(h&&f&&l.range.video){var c=f-h;c<0&&c>-.1&&(s.removeFragment(u),s.hls.trigger(O.BUFFER_FLUSHING,{startOffset:h+.004,endOffset:1/0,type:"video"}))}}}else s.removeFragment(u)}else u.type===r&&s.removeFragment(u)}}))},t.detectPartialFragments=function(e){var t=this,r=this.timeRanges,i=e.frag,a=e.part,n=e.id;if(r&&G(i)){var s=Ai(i),o=this.fragments[s];if(!(!o||o.buffered&&i.gap)){var l=!i.relurl;Object.keys(r).forEach((function(e){var s=i.elementaryStreams[e];if(s){var u=r[e],d=l||!0===s.partial;o.range[e]=t.getBufferedTimes(i,a,d,u),t.detectEvictedFragments(e,u,n,i,a)}})),o.loaded=null;var u=Object.keys(o.range);if(u.length){if(this.bufferedEnd(o,i),Ri(o)||this.removeParts(i.sn-1,i.type),!a){var d=Math.min(.004,i.duration);u.some((function(e){var r,a=null==(r=o.range[e])?void 0:r.time,n=0===a.length||1===a.length&&a[0].endPTS-a[0].startPTS<d;return n&&t.addAsGap(i),n}))}}else this.removeFragment(i)}}},t.addAsGap=function(e){e.gap=!0,this.removeFragment(e),this.fragBuffered(e,!0)},t.bufferedEnd=function(e,t){e.buffered=!0,(e.body.endList=t.endList||e.body.endList)&&!t.gap&&(this.endListFragments[e.body.type]=e)},t.removeParts=function(e,t){var r=this.activePartLists[t];r&&(this.activePartLists[t]=bi(r,(function(t){return t.fragment.sn>=e})))},t.fragBuffered=function(e,t){var r=Ai(e),i=this.fragments[r];return!i&&t&&(i=this.fragments[r]={body:e,appendedPTS:null,loaded:null,buffered:!1,range:Object.create(null)},e.gap&&(this.hasGaps=!0)),i&&(i.loaded=null,this.bufferedEnd(i,e)),i},t.getBufferedTimes=function(e,t,r,i){for(var a={time:[],partial:r},n=e.start,s=e.end,o=e.minEndPTS||s,l=e.maxStartPTS||n,u=0;u<i.length;u++){var d=i.start(u)-this.bufferPadding,h=i.end(u)+this.bufferPadding;if(l>=d&&o<=h){a.time.push({startPTS:Math.max(n,i.start(u)),endPTS:Math.min(s,i.end(u))});break}if(n<h&&s>d){var f=Math.max(n,i.start(u)),c=Math.min(s,i.end(u));c>f&&(a.partial=!0,a.time.push({startPTS:f,endPTS:c}))}else if(s<=d)break}return a},t.getPartialFragment=function(e){var t,r,i,a=null,n=0,s=this.bufferPadding,o=this.fragments;return Object.keys(o).forEach((function(l){var u=o[l];u&&Ri(u)&&(r=u.body.start-s,i=u.body.end+s,e>=r&&e<=i&&(t=Math.min(e-r,i-e),n<=t&&(a=u.body,n=t)))})),a},t.isEndListAppended=function(e){var t=this.endListFragments[e];return void 0!==t&&(t.buffered||Ri(t))},t.getState=function(e){var t=Ai(e),r=this.fragments[t];return r?r.buffered?Ri(r)?Ti:Si:Ei:yi},t.isTimeBuffered=function(e,t,r){for(var i,a,n=0;n<r.length;n++){if(i=r.start(n)-this.bufferPadding,a=r.end(n)+this.bufferPadding,e>=i&&t<=a)return!0;if(t<=i)return!1}return!1},t.onManifestLoading=function(){this.removeAllFragments()},t.onFragLoaded=function(e,t){if(G(t.frag)&&!t.frag.bitrateTest){var r=t.frag,i=t.part?null:t,a=Ai(r);this.fragments[a]={body:r,appendedPTS:null,loaded:i,buffered:!1,range:Object.create(null)}}},t.onBufferAppended=function(e,t){var r=t.frag,i=t.part,a=t.timeRanges;if(G(r)){var n=r.type;if(i){var s=this.activePartLists[n];s||(this.activePartLists[n]=s=[]),s.push(i)}this.timeRanges=a}},t.onFragBuffered=function(e,t){this.detectPartialFragments(t)},t.hasFragment=function(e){var t=Ai(e);return!!this.fragments[t]},t.hasFragments=function(e){var t=this.fragments,r=Object.keys(t);if(!e)return r.length>0;for(var i=r.length;i--;){var a=t[r[i]];if((null==a?void 0:a.body.type)===e)return!0}return!1},t.hasParts=function(e){var t;return!(null==(t=this.activePartLists[e])||!t.length)},t.getBackBufferEvictionEnd=function(e,t,r){for(var i=this.fragments,a=0,n=0,s=Object.keys(i),o=0;o<s.length;o++){var l=i[s[o]];if(l&&l.buffered&&l.body.type===t){var u=l.body;if(u.gap)return 0;var d=u.hasStats&&u.stats.loaded||u.byteLength||u.bitrate&&8*u.bitrate*u.duration;if(u.end<=e&&d&&(a+=d,n=Math.max(n,u.end),a>=r))return n}}return n>0?n:0},t.removeFragmentsInRange=function(e,t,r,i,a){var n=this;i&&!this.hasGaps||Object.keys(this.fragments).forEach((function(s){var o=n.fragments[s];if(o){var l=o.body;l.type!==r||i&&!l.gap||l.start<t&&l.end>e&&(o.buffered||a)&&n.removeFragment(l)}}))},t.removeFragment=function(e){var t=Ai(e);e.clearElementaryStreamInfo();var r=this.activePartLists[e.type];if(r){var i=e.sn;this.activePartLists[e.type]=bi(r,(function(e){return e.fragment.sn!==i}))}delete this.fragments[t],e.endList&&delete this.endListFragments[e.type]},t.removeAllFragments=function(){var e;this.fragments=Object.create(null),this.endListFragments=Object.create(null),this.activePartLists=Object.create(null),this.hasGaps=!1;var t=null==(e=this.hls)||null==(e=e.latestLevelDetails)?void 0:e.partList;t&&t.forEach((function(e){return e.clearElementaryStreamInfo()}))},e}();function Ri(e){var t,r,i,a;return e.buffered&&!!(e.body.gap||null!=(t=e.range.video)&&t.partial||null!=(r=e.range.audio)&&r.partial||null!=(i=e.range.audiovideo)&&i.partial||null!=(a=e.range.subs)&&a.partial)}function Ai(e){return e.type+"_"+e.level+"_"+e.sn}function bi(e,t){return e.filter((function(e){var r=t(e);return r||e.clearElementaryStreamInfo(),r}))}var ki=function(){function e(e,t,r){this.subtle=void 0,this.aesIV=void 0,this.aesMode=void 0,this.subtle=e,this.aesIV=t,this.aesMode=r}return e.prototype.decrypt=function(e,t){switch(this.aesMode){case xr:return this.subtle.decrypt({name:"AES-CBC",iv:this.aesIV},t,e);case wr:return this.subtle.decrypt({name:"AES-CTR",counter:this.aesIV,length:64},t,e);default:throw new Error("[AESCrypto] invalid aes mode "+this.aesMode)}},e}(),Di=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],a=r[1],n=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,g=0,v=0;for(v=0;v<256;v++)f[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,e[c]=m,t[m]=c;var p=f[c],y=f[p],E=f[y],T=257*f[m]^16843008*m;i[c]=T<<24|T>>>8,a[c]=T<<16|T>>>16,n[c]=T<<8|T>>>24,s[c]=T,T=16843009*E^65537*y^257*p^16843008*c,l[m]=T<<24|T>>>8,u[m]=T<<16|T>>>16,d[m]=T<<8|T>>>24,h[m]=T,c?(c=p^f[f[f[E^p]]],g^=f[f[g]]):c=g=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 a=this.keySize=t.length;if(4!==a&&6!==a&&8!==a)throw new Error("Invalid aes key size="+a);var n,s,o,l,u=this.ksRows=4*(a+6+1),d=this.keySchedule=new Uint32Array(u),h=this.invKeySchedule=new Uint32Array(u),f=this.sBox,c=this.rcon,g=this.invSubMix,v=g[0],m=g[1],p=g[2],y=g[3];for(n=0;n<u;n++)n<a?o=d[n]=t[n]:(l=o,n%a==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[n/a|0]<<24):a>6&&n%a==4&&(l=f[l>>>24]<<24|f[l>>>16&255]<<16|f[l>>>8&255]<<8|f[255&l]),d[n]=o=(d[n-a]^l)>>>0);for(s=0;s<u;s++)n=u-s,l=3&s?d[n]:d[n-4],h[s]=s<4||n<=4?l:v[f[l>>>24]]^m[f[l>>>16&255]]^p[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,a,n,s,o,l,u,d,h,f,c,g,v,m,p=this.keySize+6,y=this.invKeySchedule,E=this.invSBox,T=this.invSubMix,S=T[0],L=T[1],R=T[2],A=T[3],b=this.uint8ArrayToUint32Array_(r),k=b[0],D=b[1],I=b[2],_=b[3],C=new Int32Array(e),P=new Int32Array(C.length),x=this.networkToHostOrderSwap;t<C.length;){for(h=x(C[t]),f=x(C[t+1]),c=x(C[t+2]),g=x(C[t+3]),o=h^y[0],l=g^y[1],u=c^y[2],d=f^y[3],v=4,m=1;m<p;m++)i=S[o>>>24]^L[l>>16&255]^R[u>>8&255]^A[255&d]^y[v],a=S[l>>>24]^L[u>>16&255]^R[d>>8&255]^A[255&o]^y[v+1],n=S[u>>>24]^L[d>>16&255]^R[o>>8&255]^A[255&l]^y[v+2],s=S[d>>>24]^L[o>>16&255]^R[l>>8&255]^A[255&u]^y[v+3],o=i,l=a,u=n,d=s,v+=4;i=E[o>>>24]<<24^E[l>>16&255]<<16^E[u>>8&255]<<8^E[255&d]^y[v],a=E[l>>>24]<<24^E[u>>16&255]<<16^E[d>>8&255]<<8^E[255&o]^y[v+1],n=E[u>>>24]<<24^E[d>>16&255]<<16^E[o>>8&255]<<8^E[255&l]^y[v+2],s=E[d>>>24]<<24^E[o>>16&255]<<16^E[l>>8&255]<<8^E[255&u]^y[v+3],P[t]=x(i^k),P[t+1]=x(s^D),P[t+2]=x(n^I),P[t+3]=x(a^_),k=h,D=f,I=c,_=g,t+=4}return P.buffer},e}(),Ii=function(){function e(e,t,r){this.subtle=void 0,this.key=void 0,this.aesMode=void 0,this.subtle=e,this.key=t,this.aesMode=r}return e.prototype.expandKey=function(){var e=function(e){switch(e){case xr:return"AES-CBC";case wr:return"AES-CTR";default:throw new Error("[FastAESKey] invalid aes mode "+e)}}(this.aesMode);return this.subtle.importKey("raw",this.key,{name:e},!1,["encrypt","decrypt"])},e}(),_i=function(){function e(e,t){if(this.logEnabled=!0,this.decryptRange=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.enableSoftwareAES=void 0,this.enableSoftwareAES=e.enableSoftwareAES,!0!==t)try{var r=self.crypto;r&&(this.subtle=r.subtle||r.webkitSubtle)}catch(e){}this.useSoftware=!this.subtle}var t=e.prototype;return t.destroy=function(){this.subtle=null,this.softwareDecrypter=null,this.key=null,this.fastAesKey=null,this.remainderData=null,this.currentIV=null,this.currentResult=null},t.isSync=function(){return this.useSoftware},t.flush=function(){var e=this.currentResult,t=this.remainderData;if(!e||t)return this.reset(),null;var r=new Uint8Array(e);this.reset();var i,a,n,s=this.decryptRange;return s?0===s.start&&s.end>=r.length?r:r.slice(s.start,s.end):(a=(i=r).byteLength,(n=a&&new DataView(i.buffer).getUint8(a-1))?i.slice(0,a-n):i)},t.reset=function(){this.currentResult=null,this.currentIV=null,this.remainderData=null,this.softwareDecrypter&&(this.softwareDecrypter=null)},t.decrypt=function(e,t,r,i,a){var n=this;return this.decryptRange=a,this.useSoftware||a?new Promise((function(a,s){var o=ArrayBuffer.isView(e)?e:new Uint8Array(e);n.softwareDecrypt(o,t,r,i);var l=n.flush();l?a(l.buffer):s(new Error("[softwareDecrypt] Failed to decrypt data"))})):this.webCryptoDecrypt(new Uint8Array(e),t,r,i)},t.softwareDecrypt=function(e,t,r,i){var a=this.currentIV,n=this.currentResult,s=this.remainderData;if(i!==xr||16!==t.byteLength)return fe.warn("SoftwareDecrypt: can only handle AES-128-CBC"),null;this.logOnce("JS AES decrypt"),s&&(e=it(s,e),this.remainderData=null);var o=this.getValidChunk(e);if(!o.length)return null;a&&(r=a);var l=this.softwareDecrypter;l||(l=this.softwareDecrypter=new Di),l.expandKey(t);var u=n;return this.currentResult=l.decrypt(o.buffer,0,r),this.currentIV=o.slice(-16).buffer,u||null},t.webCryptoDecrypt=function(e,t,r,i){var a=this;if(this.key!==t||!this.fastAesKey){if(!this.subtle)return Promise.resolve(this.onWebCryptoError(e,t,r,i));this.key=t,this.fastAesKey=new Ii(this.subtle,t,i)}return this.fastAesKey.expandKey().then((function(t){return a.subtle?(a.logOnce("WebCrypto AES decrypt"),new ki(a.subtle,new Uint8Array(r),i).decrypt(e.buffer,t)):Promise.reject(new Error("web crypto not initialized"))})).catch((function(n){return fe.warn("[decrypter]: WebCrypto Error, disable WebCrypto API, "+n.name+": "+n.message),a.onWebCryptoError(e,t,r,i)}))},t.onWebCryptoError=function(e,t,r,i){var a=this.enableSoftwareAES;if(a){this.useSoftware=!0,this.logEnabled=!0,this.softwareDecrypt(e,t,r,i);var n=this.flush();if(n)return n.buffer}throw new Error("WebCrypto"+(a?" and softwareDecrypt":"")+": failed to decrypt data")},t.getValidChunk=function(e){var t=e,r=e.length-e.length%16;return r!==e.length&&(t=e.slice(0,r),this.remainderData=e.slice(r)),t},t.logOnce=function(e){this.logEnabled&&(fe.log("[decrypter]: "+e),this.logEnabled=!1)},e}(),Ci=Math.pow(2,17),Pi=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,r,i){var a=this,n=e.url;if(!n)return Promise.reject(new Mi({type:x.NETWORK_ERROR,details:w.FRAG_LOAD_ERROR,fatal:!1,frag:e,error:new Error("Fragment does not have a "+(n?"part list":"url")),networkDetails:null}));this.abort();var s=this.config,o=s.fLoader,l=s.loader;return new Promise((function(u,d){if(a.loader&&a.loader.destroy(),e.gap){if(e.tagList.some((function(e){return"GAP"===e[0]})))return void d(Oi(e));e.gap=!1}var h=a.loader=o?new o(s):new l(s),c=xi(e,null,t);e.loader=h;var g=$(s.fragLoadPolicy.default),v={loadPolicy:g,timeout:g.maxLoadTimeMs,maxRetry:0,retryDelay:0,maxRetryDelay:0,highWaterMark:"initSegment"===e.sn?1/0:Ci};h.stats.retry=e.stats.retry,e.stats=h.stats;var m={onSuccess:function(t,r,i,n){a.resetLoader(e,h);var s=t.data;i.resetIV&&e.decryptdata&&(e.decryptdata.iv=new Uint8Array(s.slice(0,16)),s=s.slice(16)),u({frag:e,part:null,payload:s,networkDetails:n})},onError:function(t,r,i,s){a.resetLoader(e,h),d(new Mi({type:x.NETWORK_ERROR,details:w.FRAG_LOAD_ERROR,fatal:!1,frag:e,response:f({url:n,data:void 0},t),error:new Error("HTTP Error "+t.code+" "+t.text),networkDetails:i,stats:s}))},onAbort:function(t,r,i){a.resetLoader(e,h),d(new Mi({type:x.NETWORK_ERROR,details:w.INTERNAL_ABORTED,fatal:!1,frag:e,error:new Error("Aborted"),networkDetails:i,stats:t}))},onTimeout:function(t,r,i){a.resetLoader(e,h),d(new Mi({type:x.NETWORK_ERROR,details:w.FRAG_LOAD_TIMEOUT,fatal:!1,frag:e,error:new Error("Timeout after "+v.timeout+"ms"),networkDetails:i,stats:t}))}};if(r){var p=a.gateProgress(r,i);m.onProgress=function(t,r,i,a){return p({frag:e,part:null,payload:i,networkDetails:a})}}h.load(c,v,m)}))},t.gateProgress=function(e,t){var r=e;if(t){var i=[],a=!1;t.then((function(){a=!0,i.forEach((function(t){return e(t)})),i.length=0})).catch((function(){})),r=function(t){a?e(t):i.push(t)}}return r},t.loadPart=function(e,t,r){var i=this;this.abort();var a=this.config,n=a.fLoader,s=a.loader;return new Promise((function(o,l){if(i.loader&&i.loader.destroy(),e.gap||t.gap)l(Oi(e,t));else{var u=i.loader=n?new n(a):new s(a),d=xi(e,t);e.loader=u;var h=$(a.fragLoadPolicy.default),c={loadPolicy:h,timeout:h.maxLoadTimeMs,maxRetry:0,retryDelay:0,maxRetryDelay:0,highWaterMark:Ci};t.stats=u.stats,u.load(d,c,{onSuccess:function(a,n,s,l){i.resetLoader(e,u),i.updateStatsFromPart(e,t);var d={frag:e,part:t,payload:a.data,networkDetails:l};r(d),o(d)},onError:function(r,a,n,s){i.resetLoader(e,u),l(new Mi({type:x.NETWORK_ERROR,details:w.FRAG_LOAD_ERROR,fatal:!1,frag:e,part:t,response:f({url:d.url,data:void 0},r),error:new Error("HTTP Error "+r.code+" "+r.text),networkDetails:n,stats:s}))},onAbort:function(r,a,n){e.stats.aborted=t.stats.aborted,i.resetLoader(e,u),l(new Mi({type:x.NETWORK_ERROR,details:w.INTERNAL_ABORTED,fatal:!1,frag:e,part:t,error:new Error("Aborted"),networkDetails:n,stats:r}))},onTimeout:function(r,a,n){i.resetLoader(e,u),l(new Mi({type:x.NETWORK_ERROR,details:w.FRAG_LOAD_TIMEOUT,fatal:!1,frag:e,part:t,error:new Error("Timeout after "+c.timeout+"ms"),networkDetails:n,stats:r}))}})}}))},t.updateStatsFromPart=function(e,t){var r=e.stats,i=t.stats,a=i.total;if(r.loaded+=i.loaded,a){var n=Math.round(e.duration/t.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},t.resetLoader=function(e,t){e.loader=null,e.url=null,this.loader===t&&(self.clearTimeout(this.partLoadTimeout),this.loader=null),t.destroy()},e}();function xi(e,t,r){void 0===t&&(t=null);var i=t||e,a={type:F.MEDIA_FRAGMENT,frag:e,part:t,responseType:"arraybuffer",url:i.url,headers:{},rangeStart:0,rangeEnd:0},n=wi(e,t,r);return n.resetIV&&(a.resetIV=!0),n.byteRange&&(a.rangeStart=n.byteRange.start,a.rangeEnd=n.byteRange.end),a}function wi(e,t,r){void 0===t&&(t=null);var i,a,n={},s=t||e,o=s.byteRangeStartOffset,l=s.byteRangeEndOffset;if(_(o)&&_(l))if("initSegment"!==e.sn&&!r||"AES-128"!==(a=null==(i=e.decryptdata)?void 0:i.method)&&"AES-256"!==a)n.byteRange={start:o,end:l};else{var u=16*Math.floor(o/16),d=16*Math.ceil(l/16);n.decryptRange={start:o-u,end:l-u},u>=16&&(n.resetIV=!0,u-=16),n.byteRange={start:u,end:d}}return n}function Oi(e,t){var r=new Error("GAP "+(e.gap?"tag":"attribute")+" found"),i={type:x.MEDIA_ERROR,details:w.FRAG_GAP,fatal:!1,frag:e,error:r,networkDetails:null};return t&&(i.part=t),(t||e).stats.aborted=!0,new Mi(i)}var Mi=function(e){function t(t){var r;return(r=e.call(this,t.error.message)||this).data=void 0,r.data=t,r}return u(t,e),t}(v(Error)),Fi=function(e){function t(t,r){var i;return(i=e.call(this,t,r)||this)._boundTick=void 0,i._tickTimer=null,i._tickInterval=null,i._tickCallCount=0,i._boundTick=i.tick.bind(i),i}u(t,e);var r=t.prototype;return r.destroy=function(){this.onHandlerDestroying(),this.onHandlerDestroyed()},r.onHandlerDestroying=function(){this.clearNextTick(),this.clearInterval()},r.onHandlerDestroyed=function(){},r.hasInterval=function(){return!!this._tickInterval},r.hasNextTick=function(){return!!this._tickTimer},r.setInterval=function(e){return!this._tickInterval&&(this._tickCallCount=0,this._tickInterval=self.setInterval(this._boundTick,e),!0)},r.clearInterval=function(){return!!this._tickInterval&&(self.clearInterval(this._tickInterval),this._tickInterval=null,!0)},r.clearNextTick=function(){return!!this._tickTimer&&(self.clearTimeout(this._tickTimer),this._tickTimer=null,!0)},r.tick=function(){this._tickCallCount++,1===this._tickCallCount?(this.doTick(),this._tickCallCount>1&&this.tickImmediate(),this._tickCallCount=0):(this.log("possible exception thrown in task-loop ("+this.constructor.name+".doTick)"),this._tickCallCount=0)},r.tickImmediate=function(){this.clearNextTick(),this._tickTimer=self.setTimeout(this._boundTick,0)},r.doTick=function(){},t}(ie),Ni=function(e,t,r,i,a,n,s,o,l){void 0===i&&(i=0),void 0===a&&(a=-1),void 0===n&&(n=!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.iframe=void 0,this.duration=void 0,this.decryptRange=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=a,this.partial=n,this.duration=s||0,this.decryptRange=l,this.iframe=o||!1};function Bi(e,t){for(var r=0,i=e.length;r<i;r++){var a;if((null==(a=e[r])?void 0:a.cc)===t)return e[r]}return null}function Ui(e,t){var r=e.start+t;e.setStart(r)}function Gi(e,t){for(var r=t.fragments,i=0,a=r.length;i<a;i++)Ui(r[i],e);t.fragmentHint&&Ui(t.fragmentHint,e),t.alignedSliding=!0}function Vi(e,t,r){e&&(function(e,t,r){if(function(e,t){return!!(e&&t.startCC<e.endCC&&t.endCC>e.startCC)}(t,e)){var i=Math.min(t.endCC,e.endCC),a=Bi(t.fragments,i),n=Bi(e.fragments,i);if(a&&n){var s=a.start-n.start;r.log("Aligning playlists using dicontinuity sequence "+i+" (diff: "+s+")"),Gi(s,e)}}}(t,e,r),t.alignedSliding||function(e,t,r){if(e.hasProgramDateTime&&t.hasProgramDateTime){var i,a,n=e.fragments,s=t.fragments;if(n.length&&s.length){var o=Math.min(t.endCC,e.endCC);t.startCC<o&&e.startCC<o&&(i=Bi(s,o),a=Bi(n,o)),i&&a||(a=Bi(n,(i=s[Math.floor(s.length/2)]||s.filter((function(e){return!!e}))[0]).cc)||n[Math.floor(n.length/2)]);var l=i.programDateTime,u=a.programDateTime;if(l&&u){var d=(u-l)/1e3;if(Math.abs(d)>Math.max(60,e.totalduration))r.log("Cannot align playlists using PDT without overlap ("+Math.abs(d)+" > "+e.totalduration+")");else{var h=d-(a.start-i.start);r.log("Aligning playlists using PDT (diff: "+h+")"),Gi(h,e)}}}}}(t,e,r),t.alignedSliding||t.skippedSegments||si(e,t,!1,r))}var Hi={STOPPED:"STOPPED",IDLE:"IDLE",KEY_LOADING:"KEY_LOADING",FRAG_LOADING:"FRAG_LOADING",FRAG_LOADING_WAITING_RETRY:"FRAG_LOADING_WAITING_RETRY",PARSING:"PARSING",PARSED:"PARSED",ENDED:"ENDED",ERROR:"ERROR",WAITING_LEVEL:"WAITING_LEVEL"},Ki=function(e){function t(t,r,i,a,n){var s;return(s=e.call(this,a,t.logger)||this).hls=void 0,s.fragPrevious=null,s.fragCurrent=null,s.fragPlaying=null,s.fragmentTracker=void 0,s.transmuxer=null,s._state=Hi.STOPPED,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=-1,s.startTimeOffset=null,s.retryDate=0,s.levels=null,s.fragmentLoader=void 0,s.initFragmentLoader=void 0,s.keyLoader=void 0,s.levelLastLoaded=null,s.startFragRequested=!1,s.decrypter=void 0,s.initPTS=[],s.buffering=!0,s.loadingParts=!1,s.loopSn=void 0,s.onMediaSeeking=function(){var e=s,t=e.config,r=e.fragCurrent,i=e.media,a=e.mediaBuffer,n=e.state,o=i?i.currentTime:0,l=dr.bufferInfo(a||i,o,t.maxBufferHole),u=l.len;if(s.log("Media seeking to "+o+", state: "+n+", "+(u?"in":"out of")+" buffer"),s.state===Hi.ENDED)s.resetLoadingState();else if(r){var d=t.maxFragLookUpTolerance,h=r.start-d,f=r.start+r.duration+d;if(!u||f<l.start||h>l.end){var c=o<h,g=o>f;(c||g)&&(!r.loader||!g&&s.isFragmentNearlyDownloaded(r)?s.fragPrevious=null:(s.log("Cancelling fragment load for seek (sn: "+r.sn+") - "+(c?"backward":"forward")+" seek"),r.abortRequests(),s.resetLoadingState()))}}if(i&&(s.fragmentTracker.removeFragmentsInRange(o,1/0,s.playlistType,!0),o>s.lastCurrentTime&&(s.lastCurrentTime=o),!s.loadingParts)){var v=Math.max(l.end,o),m=s.shouldLoadParts(s.getLevelDetails(),v);m&&(s.log("LL-Part loading ON after seeking to "+o+" with buffer @"+v),s.loadingParts=m)}var p=!dr.isBuffered(i,o);s.hls.hasEnoughToStart&&!p||(s.log("Setting "+(p?"startPosition":"nextLoadPosition")+" to "+o+" for seek without enough to start"),s.nextLoadPosition=o,p&&(s.startPosition=o)),u<1&&s.state===Hi.IDLE&&s.tickImmediate()},s.onMediaEnded=function(){s.log("setting startPosition to 0 because media ended"),s.startPosition=s.lastCurrentTime=0},s.playlistType=n,s.hls=t,s.fragmentLoader=new Pi(t.config),s.initFragmentLoader=new Pi(t.config),s.keyLoader=i,s.fragmentTracker=r,s.config=t.config,s.decrypter=new _i(t.config),s}u(t,e);var r=t.prototype;return r.registerListeners=function(){var e=this.hls;e.on(O.MEDIA_ATTACHED,this.onMediaAttached,this),e.on(O.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(O.MANIFEST_LOADING,this.onManifestLoading,this),e.on(O.MANIFEST_LOADED,this.onManifestLoaded,this),e.on(O.ERROR,this.onError,this)},r.unregisterListeners=function(){var e=this.hls;e.off(O.MEDIA_ATTACHED,this.onMediaAttached,this),e.off(O.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(O.MANIFEST_LOADING,this.onManifestLoading,this),e.off(O.MANIFEST_LOADED,this.onManifestLoaded,this),e.off(O.ERROR,this.onError,this)},r.doTick=function(){this.onTickEnd()},r.onTickEnd=function(){},r.startLoad=function(e){},r.stopLoad=function(){if(this.state!==Hi.STOPPED){this.fragmentLoader.abort(),this.initFragmentLoader.abort(),this.keyLoader.abort(this.playlistType);var e=this.fragCurrent;null!=e&&e.loader&&(e.abortRequests(),this.fragmentTracker.removeFragment(e)),this.resetTransmuxer(),this.fragCurrent=null,this.fragPrevious=null,this.clearInterval(),this.clearNextTick(),this.state=Hi.STOPPED}},r.pauseBuffering=function(){this.buffering=!1},r.resumeBuffering=function(){this.buffering=!0},r._streamEnded=function(e,t){if(t.live||!this.media)return!1;var r=e.end||0,i=this.config.timelineOffset||0;if(r<=i)return!1;var a=e.buffered;this.config.maxBufferHole&&a&&a.length>1&&(e=dr.bufferedInfo(a,e.start,0));var n=e.nextStart;if(n&&n>i&&n<t.edge)return!1;if(this.media.currentTime<e.start)return!1;var s=t.partList;if(null!=s&&s.length){var o=s[s.length-1];return dr.isBuffered(this.media,o.start+o.duration/2)}var l=t.fragments[t.fragments.length-1];return this.fragmentTracker.isEndListAppended(l.type)},r.getLevelDetails=function(){if(this.levels&&null!==this.levelLastLoaded)return this.levelLastLoaded.details},r.onMediaAttached=function(e,t){var r=this.media=this.mediaBuffer=t.media;hr(r,"seeking",this.onMediaSeeking),hr(r,"ended",this.onMediaEnded);var i=this.config;this.levels&&i.autoStartLoad&&this.state===Hi.STOPPED&&this.startLoad(i.startPosition)},r.onMediaDetaching=function(e,t){this.fragPlaying=null;var r=!!t.transferMedia,i=this.media;if(null!==i){if(i.ended&&(this.log("MSE detaching and video ended, reset startPosition"),this.startPosition=this.lastCurrentTime=0),fr(i,"seeking",this.onMediaSeeking),fr(i,"ended",this.onMediaEnded),this.media=this.mediaBuffer=null,this.loopSn=void 0,r)return this.resetLoadingState(),void this.resetTransmuxer();this.loadingParts=!1,this.fragmentTracker.removeAllFragments(),this.stopLoad()}},r.onManifestLoading=function(){this.initPTS=[],this.fragPlaying=this.levels=this.levelLastLoaded=this.fragCurrent=null,this.lastCurrentTime=0,this.startPosition=-1,this.startFragRequested=!1},r.onError=function(e,t){},r.onManifestLoaded=function(e,t){this.startTimeOffset=t.startTimeOffset},r.onHandlerDestroying=function(){this.stopLoad(),this.transmuxer&&(this.transmuxer.destroy(),this.transmuxer=null),e.prototype.onHandlerDestroying.call(this),this.hls=this.onMediaSeeking=this.onMediaEnded=null},r.onHandlerDestroyed=function(){this.state=Hi.STOPPED,this.fragmentLoader&&this.fragmentLoader.destroy(),this.initFragmentLoader&&this.initFragmentLoader.destroy(),this.keyLoader&&this.keyLoader.destroy(),this.decrypter&&this.decrypter.destroy(),this.hls=this.decrypter=this.keyLoader=null,this.fragmentLoader=this.initFragmentLoader=this.fragmentTracker=null,e.prototype.onHandlerDestroyed.call(this)},r.loadFragment=function(e,t,r){this.startFragRequested=!0,this._loadFragForPlayback(e,t,r)},r._loadFragForPlayback=function(e,t,r){var i=this;this._doFragLoad(e,t,r,(function(e){var t=e.frag;if(i.fragContextChanged(t))return i.warn(t.type+" sn: "+t.sn+(e.part?" part: "+e.part.index:"")+" of "+i.fragInfo(t,!1,e.part)+" was dropped during download."),void i.fragmentTracker.removeFragment(t);var r=++t.stats.chunkCount;i.log("load progress "+t.type+" sn: "+t.sn+(e.part?" part: "+e.part.index:"")+" of "+i.fragInfo(t,!1,e.part)+" chunk: "+r),i._handleFragmentLoadProgress(e)})).then((function(e){if(e){var t=i.state,r=e.frag;i.fragContextChanged(r)?(t===Hi.FRAG_LOADING||!i.fragCurrent&&t===Hi.PARSING)&&(i.fragmentTracker.removeFragment(r),i.state=Hi.IDLE):("payload"in e&&(i.log("Loaded "+r.type+" sn: "+r.sn+" of "+i.playlistLabel()+" "+r.level+" (bytes "+r.stats.loaded+")"),i.hls.trigger(O.FRAG_LOADED,e)),i._handleFragmentLoadComplete(e))}})).catch((function(t){i.state!==Hi.STOPPED&&i.state!==Hi.ERROR&&(i.log("Frag error "+e.type+" sn:"+e.sn+" cc:"+e.cc+": "+((null==t?void 0:t.message)||t)),i.resetFragmentLoading(e))}))},r.clearTrackerIfNeeded=function(e){var t,r=this.fragmentTracker;if(r.getState(e)===Ei){var i=e.type,a=this.getFwdBufferInfo(this.mediaBuffer,i),n=Math.max(e.duration,a?a.len:this.config.maxBufferLength),s=this.backtrackFragment;(1==(s?e.sn-s.sn:0)||this.reduceMaxBufferLength(n,e.duration))&&r.removeFragment(e)}else 0===(null==(t=this.mediaBuffer)?void 0:t.buffered.length)?r.removeAllFragments():r.hasParts(e.type)&&(r.detectPartialFragments({frag:e,part:null,stats:e.stats,id:e.type}),r.getState(e)===Ti&&r.removeFragment(e))},r.checkLiveUpdate=function(e){if(e.updated&&!e.live){var t=this.fragmentTracker,r=e.fragments[e.fragments.length-1];if(r.endList&&!t.isEndListAppended(this.playlistType)){var i=t.getPartialFragment(r.end);i&&(t.removeFragment(i),t.fragBuffered(r,!0))}t.detectPartialFragments({frag:r,part:null,stats:r.stats,id:r.type})}e.fragments[0]||(e.deltaUpdateFailed=!0)},r.waitForLive=function(e){var t=e.details;return(null==t?void 0:t.live)&&"EVENT"!==t.type&&(this.levelLastLoaded!==e||t.expired)},r.flushMainBuffer=function(e,t,r){if(void 0===r&&(r=null),e-t){var i={startOffset:e,endOffset:t,type:r};this.hls.trigger(O.BUFFER_FLUSHING,i)}},r._loadInitSegment=function(e){var t=this,r=this.hls;return this.initFragmentLoader.abort(),r.trigger(O.FRAG_LOADING,{frag:e,targetBufferTime:0}),this.initFragmentLoader.load(e).then((function(e){var r,i=e.frag;if(!t.levels||!H(i,null==(r=t.fragCurrent)?void 0:r.initSegment))throw new Mi({type:x.NETWORK_ERROR,details:w.INTERNAL_ABORTED,error:new Error("init load aborted"),fatal:!1,frag:i,networkDetails:null});return e})).then((function(e){var i=e.frag,a=e.payload,n=i.decryptdata;if(a&&a.byteLength>0&&null!=n&&n.key&&n.iv&&Or(n.method)){var s=self.performance.now(),o=wi(i,null,t.iframesOnly).decryptRange;return t.decrypter.decrypt(new Uint8Array(a),n.key.buffer,n.iv.buffer,Mr(n.method),o).catch((function(e){throw r.trigger(O.ERROR,{type:x.MEDIA_ERROR,details:w.FRAG_DECRYPT_ERROR,fatal:!1,error:e,reason:e.message,frag:i}),t.fragmentLoader.abort(),e})).then((function(a){var n=self.performance.now();return r.trigger(O.FRAG_DECRYPTED,{frag:i,part:null,payload:a,stats:{tstart:s,tdecrypt:n}}),e.payload=a,t.completeInitSegmentLoad(e)}))}return t.completeInitSegmentLoad(e)}))},r.completeInitSegmentLoad=function(e){var t=e.frag.stats;e.frag.data=new Uint8Array(e.payload),t.parsing.start=t.buffering.start=self.performance.now(),t.parsing.end=t.buffering.end=self.performance.now()},r.loadInitSegmentIfNeeded=function(e){var t,r=this,i=e.initSegment;if(!this.bitrateTest&&G(e)&&i&&!i.data)return(!i.encrypted||null!=(t=i.decryptdata)&&t.key?this._loadInitSegment(i):this.keyLoader.load(i).then((function(){return r._loadInitSegment(i)}))).catch((function(t){if(r.state===Hi.STOPPED||r.state===Hi.ERROR)throw t;throw"data"in t&&(t.data.frag=e,r.fragmentLoader.abort(),r.handleFragLoadError(t)),r.resetFragmentLoading(e),t}))},r.unhandledEncryptionError=function(e,t){var r,i,a=e.tracks;if(a&&!t.encrypted&&(null!=(r=a.audio)&&r.encrypted||null!=(i=a.video)&&i.encrypted)&&(!this.config.emeEnabled||!this.keyLoader.emeController)){var n=this.media,s=new Error("EME not supported (light build)");return this.warn(s.message),!(!n||n.mediaKeys)&&(this.hls.trigger(O.ERROR,{type:x.KEY_SYSTEM_ERROR,details:w.KEY_SYSTEM_NO_KEYS,fatal:!0,error:s,frag:t}),this.resetTransmuxer(),!0)}return!1},r.fragContextChanged=function(e){var t=this.fragCurrent;return!e||!H(e,t)},r.fragBufferedComplete=function(e,t){var r=this.mediaBuffer?this.mediaBuffer:this.media;if(this.log("Buffered "+e.type+" sn: "+e.sn+(t?" part: "+t.index:"")+" of "+this.fragInfo(e,!1,t)+" > buffer:"+(r?cr(dr.getBuffered(r)):"(detached)")+")"),G(e)){var i;if(e.type!==N.SUBTITLE){var a=e.elementaryStreams;if(!Object.keys(a).some((function(e){return!!a[e]})))return void(this.state=Hi.IDLE)}var n=null==(i=this.levels)?void 0:i[e.level];null!=n&&n.fragmentError&&(t||this.fragmentTracker.getState(e)!==Ti)&&(this.log("Resetting level fragment error count of "+n.fragmentError+" on frag buffered"),n.fragmentError=0,e.stats.retry=0)}this.state=Hi.IDLE},r._handleFragmentLoadComplete=function(e){var t=this.transmuxer;if(t){var r=e.frag,i=e.part,a=e.partsLoaded,n=!a||0===a.length||a.some((function(e){return!e})),s=wi(r,i).decryptRange,o=new Ni(r.level,r.sn,r.stats.chunkCount+1,0,i?i.index:-1,!n,r.duration,this.iframesOnly,s);this.log("load complete "+r.type+" sn: "+r.sn+(i?" part: "+i.index:"")+" of "+this.fragInfo(r,!1,i)),t.flush(o)}},r._handleFragmentLoadProgress=function(e){},r.loadKeyFor=function(e,t,r){var i,a=this,n=null;return!e.encrypted||null!=(i=e.decryptdata)&&i.key?e.encrypted||(n=this.keyLoader.loadClear(e,t.encryptedFragments,this.startFragRequested))&&this.log("[eme] blocking frag sn: "+e.sn+" load until media-keys acquired"):(this.log("Loading key for "+e.sn+" of ["+t.startSN+"-"+t.endSN+"], "+this.playlistLabel()+" "+e.level),this.state=Hi.KEY_LOADING,n=this.keyLoader.load(e,r).then((function(e){if(!a.fragContextChanged(e.frag))return a.hls.trigger(O.KEY_LOADED,e),a.state===Hi.KEY_LOADING&&(a.state=Hi.IDLE),e})),this.hls.trigger(O.KEY_LOADING,{frag:e})),n},r._doFragLoad=function(e,t,r,i){var a=this;void 0===r&&(r=null),this.fragCurrent=e;var n=t.details;if(!this.levels||!n)throw new Error("frag load aborted, missing level"+(n?"":" detail")+"s");var s,o=this.fragPrevious;if(!V(e,o)){var l=this.shouldLoadParts(t.details,e.end);l!==this.loadingParts&&(this.log("LL-Part loading "+(l?"ON":"OFF")+" loading sn "+(null==o?void 0:o.sn)+"->"+e.sn),this.loadingParts=l)}if(r=Math.max(e.start,r||0),this.loadingParts){var u=n.partList;if(u&&i){r>n.fragmentEnd&&n.fragmentHint&&(e=n.fragmentHint);var d=this.getNextPart(u,e,r);if(d>-1){var h=u[d];if(!V(e=this.fragCurrent=h.fragment,o)&&!this.shouldLoadParts(t.details,e.end))return this.log("LL-Part loading OFF @"+this.playhead+" next part: "+this.fragInfo(e,!1,h)),this.loadingParts=!1,Promise.resolve(null);if(!this.filterReplacedPrimary(h,t.details))return Promise.resolve(null);var f,c=this.loadKeyFor(e,n);if(this.fragContextChanged(e))return Promise.resolve(null);this.log("Loading "+e.type+" sn: "+e.sn+" part: "+h.index+" ("+d+"/"+(u.length-1)+") of "+this.fragInfo(e,!1,h)+" cc: "+e.cc+" ["+n.startSN+"-"+n.endSN+"], target: "+r+(h.byteRange.length?" range:"+h.byteRange.join("-"):"")),this.nextLoadPosition=h.start+h.duration,this.state=Hi.FRAG_LOADING;var g=this.loadInitSegmentIfNeeded(e);if(c||g){var v=!!c;f=Promise.all([c,g]).then((function(r){var n=r[0];return!v||n&&!a.fragContextChanged(n.frag)?a.doFragPartsLoad(e,h,t,i):null})).catch((function(e){return a.handleFragLoadError(e)}))}else f=this.doFragPartsLoad(e,h,t,i).catch((function(e){return a.handleFragLoadError(e)}));return this.hls.trigger(O.FRAG_LOADING,{frag:e,part:h,targetBufferTime:r}),null===this.fragCurrent?Promise.reject(new Error("frag load aborted, context changed in FRAG_LOADING parts")):f}if(!e.url||this.loadedEndOfParts(u,r))return Promise.resolve(null)}}if(this.loadingParts)this.log("LL-Part loading OFF after next part miss @"+r+" Check buffer at sn: "+e.sn+" loaded parts: "+(null==(s=n.partList)?void 0:s.filter((function(e){return e.loaded})).map((function(e){return"["+e.start+"-"+e.end+"]"})))),this.loadingParts=!1;else if(!e.url)return Promise.resolve(null);if(!this.filterReplacedPrimary(e,t.details))return Promise.resolve(null);var m,p=this.loadInitSegmentIfNeeded(e),y=this.loadKeyFor(e,n,p);if(this.fragContextChanged(e))return this.log("Context changed in KEY_LOADING sn: "+e.sn+" "+e.relurl+" > "+(null==(m=this.fragCurrent)?void 0:m.relurl)),null==p||p.catch((function(){return null})),null==y||y.catch((function(){return null})),Promise.resolve(null);this.log("Loading "+e.type+" sn: "+e.sn+" of "+this.fragInfo(e,!1)+" cc: "+e.cc+" ["+n.startSN+"-"+n.endSN+"], target: "+r+(e.byteRange.length?" range:"+e.byteRange.join("-"):"")),_(e.sn)&&!this.bitrateTest&&(this.nextLoadPosition=e.start+e.duration),this.state=Hi.FRAG_LOADING;var E,T=this.config.progressive&&e.type!==N.SUBTITLE;return E=T&&y?y.then((function(t){return!t||a.fragContextChanged(t.frag)?null:a.fragmentLoader.load(e,a.iframesOnly,i,p)})).catch((function(e){return a.handleFragLoadError(e)})):Promise.all([this.fragmentLoader.load(e,this.iframesOnly,T?i:void 0,T?p:void 0),y,p]).then((function(e){var t=e[0];return!T&&i&&i(t),t})).catch((function(e){return a.handleFragLoadError(e)})),this.hls.trigger(O.FRAG_LOADING,{frag:e,targetBufferTime:r}),null===this.fragCurrent?Promise.reject(new Error("frag load aborted, context changed in FRAG_LOADING")):Promise.all([E,p]).then((function(e){return e[0]}))},r.doFragPartsLoad=function(e,t,r,i){var a=this;return new Promise((function(n,s){var o,l=[],u=null==(o=r.details)?void 0:o.partList,d=function(t){a.fragmentLoader.loadPart(e,t,i).then((function(i){l[t.index]=i;var s=i.part;a.hls.trigger(O.FRAG_LOADED,i);var o=ui(r.details,e.sn,t.index+1)||di(u,e.sn,t.index+1);if(!o)return n({frag:e,part:s,partsLoaded:l});d(o)})).catch(s)};d(t)}))},r.handleFragLoadError=function(e){if("data"in e){var t=e.data;t.frag&&t.details===w.INTERNAL_ABORTED?this.handleFragLoadAborted(t.frag,t.part):t.frag&&t.type===x.KEY_SYSTEM_ERROR&&!t.fatal&&(t.frag.abortRequests(),this.resetStartWhenNotLoaded(),this.resetFragmentLoading(t.frag)),this.hls.trigger(O.ERROR,t)}else this.hls.trigger(O.ERROR,{type:x.OTHER_ERROR,details:w.INTERNAL_EXCEPTION,err:e,error:e,fatal:!0});return null},r._handleTransmuxerFlush=function(e){var t=this.getCurrentContext(e);if(t&&this.state===Hi.PARSING){var r=t.frag,i=t.part,a=t.level,n=self.performance.now();r.stats.parsing.end=n,i&&(i.stats.parsing.end=n);var s=this.getLevelDetails(),o=s&&r.sn>s.endSN||this.shouldLoadParts(s,r.end);o!==this.loadingParts&&(this.log("LL-Part loading "+(o?"ON":"OFF")+" after parsing segment ending @"+r.end),this.loadingParts=o),this.updateLevelTiming(r,i,a,e)}else this.fragCurrent||this.state===Hi.STOPPED||this.state===Hi.ERROR||(this.state=Hi.IDLE)},r.shouldLoadParts=function(e,t){if(this.config.lowLatencyMode){if(!e)return this.loadingParts;if(e.partList&&!this.iframesOnly){var r,i=e.partList[0];if(t>=i.end+((null==(r=e.fragmentHint)?void 0:r.duration)||0)&&this.playhead>i.start-i.fragment.duration)return!0}}return!1},r.getCurrentContext=function(e){var t=this.levels,r=this.fragCurrent,i=e.level,a=e.sn,n=e.part;if(null==t||!t[i])return this.warn("Levels object was unset while buffering fragment "+a+" of "+this.playlistLabel()+" "+i+". The current chunk will not be buffered."),null;var s=t[i],o=s.details,l=n>-1?ui(o,a,n):null,u=l?l.fragment:li(o,a,r);return u?(r&&r!==u&&(u.stats=r.stats),{frag:u,part:l,level:s}):null},r.bufferFragmentData=function(e,t,r,i,a){if(this.state===Hi.PARSING){G(t)&&!this.fragContextChanged(t)&&(this.fragPrevious=t);var n=e.data1,s=e.data2,o=n;if(s&&(o=it(n,s)),o.length){var l=this.initPTS[t.cc],u=l?-l.baseTime/l.timescale:void 0,d={type:e.type,frag:t,part:r,chunkMeta:i,offset:u,parent:t.type,data:o};if(this.hls.trigger(O.BUFFER_APPENDING,d),e.dropped&&e.independent&&!r){if(a)return;this.flushBufferGap(t)}}}},r.flushBufferGap=function(e){var t=this.media;if(t)if(dr.isBuffered(t,t.currentTime)){var r=t.currentTime,i=dr.bufferInfo(t,r,0),a=e.duration,n=Math.min(2*this.config.maxFragLookUpTolerance,.25*a),s=Math.max(Math.min(e.start-n,i.end-n),r+n);e.start-s>n&&this.flushMainBuffer(s,e.start)}else this.flushMainBuffer(0,e.start)},r.getFwdBufferInfo=function(e,t){var r,i=this.playhead,a=this.lastCurrentTime>i||null!=(r=this.media)&&r.paused?0:this.config.maxBufferHole;return this.getFwdBufferInfoAtPos(e,i,t,a)},r.getFwdBufferInfoAtPos=function(e,t,r,i){var a=dr.bufferInfo(e,t,i);if(0===a.len&&void 0!==a.nextStart){var n=this.fragmentTracker.getBufferedFrag(t,r);if(n&&(a.nextStart<=n.end||n.gap)){var s=Math.max(Math.min(a.nextStart,n.end)-t,i);return dr.bufferInfo(e,t,s)}}return a},r.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)},r.exceedsMaxBuffer=function(e,t,r){var i=e.nextStart;if(i&&r.start>i){var a=e.buffered;if(a){for(var n=e.len,s=e.bufferedIndex,o=a.length-1;o>s;o--)a[o].start<r.start&&(n+=a[o].end-a[o].start);return n>=t}}return!1},r.reduceMaxBufferLength=function(e,t){var r=this.config,i=Math.max(Math.min(e-t,r.maxBufferLength),t/2),a=Math.max(e-3*t,r.maxMaxBufferLength/2,i);return a>=i&&(r.maxMaxBufferLength=a,this.warn("Reduce max buffer length to "+a+"s"),!0)},r.getAppendedFrag=function(e){var t=this.fragmentTracker?this.fragmentTracker.getAppendedFrag(e,this.playlistType):null;return t&&"fragment"in t?t.fragment:t},r.getNextFragment=function(e,t){var r=t.fragments,i=r.length;if(!i)return null;var a=this.config,n=t.fragmentStart,s=a.lowLatencyMode&&!!t.partList,o=null;if(t.live){var l=a.initialLiveManifestSize;if(i<l)return this.warn("Not enough fragments to start playback (have: "+i+", need: "+l+")"),null;if(this.startFragRequested||!s||this.loadingParts||(this.log("LL-Part loading ON for initial live fragment"),this.loadingParts=!0),!t.PTSKnown&&!this.startFragRequested&&-1===this.startPosition||e<n){var u;o=this.getInitialLiveFragment(t);var d,h,f=this.config.startPosition,c=this.hls.startPosition,g=this.hls.liveSyncPosition,v=(null==(u=o)?void 0:u.start)||0;if(-1!==c&&c>=n?(d=c,h=c===f?"config":"next load start"):g?(d=g,h="live edge"):(d=e,h="buffer pos"),d<v&&(d=v,h="live frag start"),d<n&&(d=n,h="playlist start"),this.startPosition!=d||this.nextLoadPosition!=d){var m=t.live&&!this.filterReplacedPrimary(o,t)?-1:d;this.log("Setting startPosition to "+m+" next load "+d+" "+(-1===c?"":"(from "+f+") ")+"based on "+h+". live edge: "+g+" live frag start: "+v+" playlist start: "+n+" buffer pos: "+e),this.startPosition=m,this.nextLoadPosition=d}}}else e<=n&&(o=r[0]);if(!o){var p=this.loadingParts?t.partEnd:t.fragmentEnd;o=this.getFragmentAtPosition(e,p,t)}var y,E,T=this.filterReplacedPrimary(o,t);return!T&&o&&(y=t,(T=(E=o.sn)===y.endSN&&y.fragmentHint?y.fragmentHint:y.fragments[1+E-y.startSN]||null)&&(this.nextLoadPosition=T.start)),T},r.isLoopLoading=function(e,t){if(this.nextLoadPosition<=t)return!1;var r=this.fragmentTracker.getState(e);if(r===Si)return!0;if(r===Ti){if(e.gap)return!0;if(e.stats.retry>1)return!0}return!1},r.getNextFragmentLoopLoading=function(e,t,r,i,a){var n=null;if(e.gap&&(n=this.getNextFragment(this.nextLoadPosition,t))&&!n.gap&&r.nextStart){var s=this.getFwdBufferInfoAtPos(this.mediaBuffer?this.mediaBuffer:this.media,r.nextStart,i,0);if(null!==s&&r.len+s.len>=a){var o=n.sn;return this.loopSn!==o&&(this.log('buffer full after gaps in "'+i+'" playlist starting at sn: '+o),this.loopSn=o),null}}return this.loopSn=void 0,n},r.filterReplacedPrimary=function(e,t){return e?(this.config,e):e},r.getNextPart=function(e,t,r){for(var i=-1,a=!1,n=!0,s=0,o=e.length;s<o;s++){var l=e[s];if(n=n&&!l.independent,i>-1&&r<l.start)break;var u=l.loaded||l.gap;u?i=-1:(a||(l.independent||n)&&l.fragment===t)&&(i=s),a=u&&!l.gap}var d=e[i];return d&&d.fragment!==t&&this.log("Need buffer at "+r+" but next unloaded part starts at "+d.start+" (sn: "+t.sn+" -> "+d.fragment.sn+")"),i},r.loadedEndOfParts=function(e,t){for(var r,i=e.length;i--;){if(!(r=e[i]).loaded)return!1;if(t>r.start)return!0}return!1},r.getInitialLiveFragment=function(e){var t=e.fragments,r=this.fragPrevious,i=null;if(r){if(e.hasProgramDateTime&&(i=function(e,t,r){if(null===t||!Array.isArray(e)||!e.length||!_(t))return null;if(t<(e[0].programDateTime||0))return null;if(t>=(e[e.length-1].endProgramDateTime||0))return null;for(var i=0;i<e.length;++i){var a=e[i];if(qt(t,r,a))return a}return null}(t,r.endProgramDateTime,this.config.maxFragLookUpTolerance),i&&this.log("Live playlist, switching playlist, load frag with same PDT: "+r.programDateTime)),!i){var a=r.sn+1;if(a>=e.startSN&&a<=e.endSN){var n=t[a-e.startSN];r.cc===n.cc&&(i=n,this.log("Live playlist, switching playlist, load frag with next SN: "+i.sn))}i||(i=function(e,t,r){if(e&&e.startCC<=t&&e.endCC>=t){var i,a=e.fragments,n=e.fragmentHint;return n&&(a=a.concat(n)),Yt(a,(function(e){return e.cc<t?1:e.cc>t?-1:(i=e,e.end<=r?1:e.start>r?-1:0)})),i||null}return null}(e,r.cc,r.end),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?e.fragmentEnd:e.edge,e))}return i},r.getFragmentAtPosition=function(e,t,r){var i,a,n=this.config,s=this.fragPrevious,o=r.fragments,l=r.endSN,u=r.fragmentHint,d=n.maxFragLookUpTolerance,h=r.partList,f=!!(this.loadingParts&&null!=h&&h.length&&u);if(f&&!this.bitrateTest&&h[h.length-1].fragment.sn===u.sn&&(o=o.concat(u),l=u.sn),s&&this.fragmentTracker.getState(s)===yi&&(s=null),i=e<t?Wt(s,o,e,e<this.lastCurrentTime||e>t-d||null!=(a=this.media)&&a.paused||!this.startFragRequested?0:d):o[o.length-1]){var c=i.sn-r.startSN,g=this.fragmentTracker.getState(i);if((g===Si||g===Ti&&i.gap)&&(s=i),V(i,s)&&(!f||h[0].fragment.sn>i.sn||!r.live)){var v=o[c+1];i=i.sn<l&&this.fragmentTracker.getState(v)!==Si?v:null}}return i},r.alignPlaylists=function(e,t,r){var i=e.fragments.length;if(!i)return this.warn("No fragments in live playlist"),0;var a=e.fragmentStart,n=!t,s=e.alignedSliding&&_(a);if(n||!s&&!a){Vi(r,e,this);var o=e.fragmentStart;return this.log("Live playlist sliding: "+o+" start-sn: "+(t?t.startSN:"na")+"->"+e.startSN+" fragments: "+i),o}return a},r.waitForCdnTuneIn=function(e){return e.live&&e.canBlockReload&&e.partTarget&&e.tuneInGoal>Math.max(e.partHoldBack,3*e.partTarget)},r.setStartPosition=function(e,t){var r=this.startPosition;r<t&&(r=-1);var i=this.timelineOffset;if(-1===r){var a=null!==this.startTimeOffset,n=a?this.startTimeOffset:e.startTimeOffset;if(null!==n&&_(n))r=t+n,n<0&&(r+=e.edge),r=Math.min(Math.max(t,r),t+e.totalduration),this.log("Setting startPosition to "+r+" for start time offset "+n+" found in "+(a?"multivariant":"media")+" playlist"),this.startPosition=r;else if(e.live){var s=this.hls.liveSyncPosition;r=s||t,this.log("Setting startPosition to -1 to start at "+(s?"live edge":"playlist start")+" "+r),this.startPosition=-1}else this.log("setting startPosition to 0 by default"),this.startPosition=r=0;this.lastCurrentTime=r+i}this.nextLoadPosition=r+i},r.getLoadPosition=function(){var e,t=this.media,r=0;return null!=(e=this.hls)&&e.hasEnoughToStart&&t&&!this.iframesOnly?r=t.currentTime:this.nextLoadPosition>=0&&(r=this.nextLoadPosition),r},r.handleFragLoadAborted=function(e,t){this.transmuxer&&e.type===this.playlistType&&G(e)&&e.stats.aborted&&(this.log("Fragment "+e.sn+(t?" part "+t.index:"")+" of "+this.playlistLabel()+" "+e.level+" was aborted"),this.resetFragmentLoading(e))},r.resetFragmentLoading=function(e){this.fragCurrent&&(this.fragContextChanged(e)||this.state===Hi.FRAG_LOADING_WAITING_RETRY)||(this.state=Hi.IDLE)},r.onFragmentOrKeyLoadError=function(e,t){if(t.chunkMeta&&!t.frag){var r=this.getCurrentContext(t.chunkMeta);r&&(t.frag=r.frag)}var i=t.frag,a=t.part;if(i&&this.levels&&i.type===e)if(this.fragContextChanged(i)){var n;this.warn("Frag load error must match current frag to retry "+i.relurl+" > "+(null==(n=this.fragCurrent)?void 0:n.relurl))}else{var s=t.details===w.FRAG_GAP;s&&this.fragmentTracker.addAsGap(i);var o=t.errorAction;if(o){var l=o.action,u=o.flags,d=o.retryCount,h=void 0===d?0:d,f=o.retryConfig,c=!!f,g=c&&l===$t,v=c&&!o.resolved&&u&Jt,m=this.hls.latestLevelDetails,p=null==m?void 0:m.live;if(!g&&v&&G(i)&&p&&i.sn<m.endSN&&!X(t))this.resetFragmentErrors(e),a?a.gap=!0:this.treatAsGap(i),o.resolved=!0;else if((g||v)&&h<f.maxNumRetry){var y,E=J(null==(y=t.response)?void 0:y.code),T=Q(f,h);if(this.resetStartWhenNotLoaded(),this.retryDate=self.performance.now()+T,this.state=Hi.FRAG_LOADING_WAITING_RETRY,o.resolved=!0,E)return this.log("Waiting for connection (offline)"),this.retryDate=1/0,void(t.reason="offline");this.warn("Fragment "+i.sn+" of "+e+" "+i.level+" errored with "+t.details+", retrying loading "+(h+1)+"/"+f.maxNumRetry+" in "+T+"ms")}else if(f){if(this.resetFragmentErrors(e),!(h<f.maxNumRetry))return void this.warn(t.details+" reached or exceeded max retry ("+h+")");s||l===Qt||(o.resolved=!0)}else this.state=l===zt?Hi.WAITING_LEVEL:Hi.ERROR;this.tickImmediate()}else this.state=Hi.ERROR}},r.checkRetryDate=function(){var e=self.performance.now(),t=this.retryDate,r=t===1/0;(!t||e>=t||r&&!J(0))&&(r&&this.log("Connection restored (online)"),this.resetStartWhenNotLoaded(),this.state=Hi.IDLE)},r.reduceLengthAndFlushBuffer=function(e){if(this.state===Hi.PARSING||this.state===Hi.PARSED){var t=e.frag,r=e.parent,i=this.getFwdBufferInfo(this.mediaBuffer,r),a=i&&i.len>.5;a&&this.reduceMaxBufferLength(i.len,(null==t?void 0:t.duration)||10);var n=!a;return n?this.warn("Buffer full error while media.currentTime ("+this.playhead+") is not buffered, flush "+r+" buffer"):i.nextStart&&t&&i.nextStart>t.start&&this.flushMainBuffer(i.nextStart,Number.POSITIVE_INFINITY,r===N.AUDIO?r:void 0),t&&(this.fragmentTracker.removeFragment(t),this.nextLoadPosition=t.start),this.resetLoadingState(),n}return!1},r.resetFragmentErrors=function(e){e===N.AUDIO&&(this.fragCurrent=null),this.hls.hasEnoughToStart||(this.startFragRequested=!1),this.state!==Hi.STOPPED&&(this.state=Hi.IDLE)},r.afterBufferFlushed=function(e,t){if(e){var r=dr.getBuffered(e);this.fragmentTracker.detectEvictedFragments(t,r,this.playlistType),this.state===Hi.ENDED&&this.resetLoadingState()}},r.resetLoadingState=function(){this.log("Reset loading state"),this.fragCurrent=null,this.fragPrevious=null,this.state!==Hi.STOPPED&&(this.state=Hi.IDLE)},r.resetStartWhenNotLoaded=function(){if(!this.hls.hasEnoughToStart){this.startFragRequested=!1;var e=this.getLevelDetails();null!=e&&e.live?(this.log("resetting startPosition for live start"),this.startPosition=-1,this.setStartPosition(e,e.fragmentStart),this.resetLoadingState()):this.nextLoadPosition=this.startPosition}},r.resetWhenMissingContext=function(e){this.log("Loading context changed while buffering sn "+e.sn+" of "+this.playlistLabel()+" "+(-1===e.level?"<removed>":e.level)+". This chunk will not be buffered."),this.removeUnbufferedFrags(),this.resetStartWhenNotLoaded(),this.resetLoadingState()},r.removeUnbufferedFrags=function(e){void 0===e&&(e=0),this.fragmentTracker.removeFragmentsInRange(e,1/0,this.playlistType,!1,!0)},r.updateLevelTiming=function(e,t,r,i){var a=this,n=r.details;if(n){if(this.log("update level timing "+e.type+" sn: "+e.sn+(t?" part: "+t.index:"")+" of "+this.fragInfo(e,!1,t)),!Object.keys(e.elementaryStreams).reduce((function(t,s){var o=e.elementaryStreams[s];if(o){var l=o.endPTS-o.startPTS;if(l<=0)return a.warn("Could not parse fragment "+e.sn+" "+s+" duration reliably ("+l+")"),t||!1;var u=i.partial?0:ii(n,e,o.startPTS,o.endPTS,o.startDTS,o.endDTS,a.iframesOnly,a);return a.hls.trigger(O.LEVEL_PTS_UPDATED,{details:n,level:r,drift:u,type:s,frag:e,start:o.startPTS,end:o.endPTS}),!0}return t}),!1)){var s,o,l,u,d=null===(null==(s=this.transmuxer)?void 0:s.error),h=null!=(null==(o=this.transmuxer)?void 0:o.error),f=(null!=(l=null==(u=this.levels)?void 0:u.length)?l:0)>1;if((0===r.fragmentError||d&&(r.fragmentError<2||e.endList)||h&&f)&&this.treatAsGap(e,r),d){var c=new Error("Found no media in "+this.playlistLabel()+" "+e.level+" "+(t?"part: "+t.index+" of ":"")+"sn: "+e.sn+" at playlist time: "+e.start+". Resetting transmuxer to fallback to playlist timing");if(this.warn(c.message),this.hls.trigger(O.ERROR,{type:x.MEDIA_ERROR,details:w.FRAG_PARSING_ERROR,fatal:!1,error:c,frag:e,reason:"Found no media in msn "+e.sn+" of "+this.playlistLabel()+' "'+r.url+'"'}),!this.hls)return;this.resetTransmuxer()}}this.state=Hi.PARSED,this.log("Parsed "+e.type+" sn: "+e.sn+(t?" part: "+t.index:"")+" of "+this.fragInfo(e,!1,t)),this.hls.trigger(O.FRAG_PARSED,{frag:e,part:t,chunkMeta:i})}else this.warn("level.details undefined")},r.playlistLabel=function(){return this.playlistType+" playlist"},r.fragInfo=function(e,t,r){var i,a;return void 0===t&&(t=!0),this.playlistLabel()+" "+e.level+" ("+(r?"part":"frag")+":["+(null!=(i=t&&!r?e.startPTS:(r||e).start)?i:NaN)+"-"+(null!=(a=t&&!r?e.endPTS:(r||e).end)?a:NaN)+"]"+(r&&"main"===e.type?"INDEPENDENT="+(r.independent?"YES":"NO"):"")+")"},r.treatAsGap=function(e,t){t&&t.fragmentError++,this.fragmentTracker.addAsGap(e)},r.resetTransmuxer=function(){var e;null==(e=this.transmuxer)||e.reset()},r.isFragmentNearlyDownloaded=function(e){var t,r=null==(t=e.loader)?void 0:t.stats;if(!r)return!1;var i=r.loading.first>0,a=(r.total-r.loaded)/(this.hls.bandwidthEstimate||this.hls.config.abrEwmaDefaultEstimate);return i&&a<=.15},r.recoverWorkerError=function(e){"demuxerWorker"===e.event&&(this.fragmentTracker.removeAllFragments(),this.transmuxer&&(this.transmuxer.destroy(),this.transmuxer=null),this.resetStartWhenNotLoaded(),this.resetLoadingState())},r.calculateOptimalSwitchPoint=function(e,t){var r=0,i=this.hls,a=this.media,n=this.config,s=this.levels,o=this.playlistType,l=this.getLevelDetails();if(a&&!a.paused&&s){var u,d=o===N.AUDIO?ce.estimatedAudioBitrate(e.audioCodec,128e3):e.maxBitrate,h=1+i.ttfbEstimate/1e3,f=i.bandwidthEstimate*n.abrBandWidthUpFactor;r=h+d*(l&&(this.loadingParts?l.partTarget:l.averagetargetduration)||(null==(u=this.fragCurrent)?void 0:u.duration)||6)/f,e.details||(r+=h)}var c=this.playhead;return{fetchdelay:r,okToFlushForwardBuffer:!(null!=l&&l.live)||t.end-c>1.5*r}},r.scheduleTrackSwitch=function(e,t,r){var i=this.media,a=this.playlistType;if(i&&e){var n=r?this.getBufferedFrag(this.playhead+t):null;if(n){var s=this.followingBufferedFrag(n);if(s){var o;this.abortCurrentFrag();var l=s.maxStartPTS?s.maxStartPTS:s.start,u=s.duration,d=Math.max(n.end,l+Math.min(Math.max(u-this.config.maxFragLookUpTolerance,u*(this.couldBacktrack?.5:.125)),u*(this.couldBacktrack?.75:.25)));if(((null==(o=this.getLevelDetails())?void 0:o.fragmentStart)||0)>d)return;var h=a===N.MAIN?null:"audio";this.flushMainBuffer(d,Number.POSITIVE_INFINITY,h),this.cleanupBackBuffer()}}}},r.cleanupBackBuffer=function(){var e=this.media,t=this.playlistType;if(e){var r=this.getAppendedFrag(this.playhead);if(r&&r.start>1){var i=t===N.AUDIO;this.flushMainBuffer(0,r.start-(i?0:1),i?"audio":null)}}},r.getBufferedFrag=function(e){return this.fragmentTracker.getBufferedFrag(e,this.playlistType)},r.followingBufferedFrag=function(e){return e?this.getBufferedFrag(e.end+.5):null},r.abortCurrentFrag=function(){var e=this.fragCurrent;switch(this.fragCurrent=null,e&&(e.abortRequests(),this.fragmentTracker.removeFragment(e)),this.state){case Hi.KEY_LOADING:case Hi.FRAG_LOADING:case Hi.FRAG_LOADING_WAITING_RETRY:case Hi.PARSING:case Hi.PARSED:this.state=Hi.IDLE}this.nextLoadPosition=this.playhead},r.checkFragPlaying=function(){var e=this.media,t=null;if(e&&e.readyState>1&&!1===e.seeking){var r=e.currentTime;if(dr.isBuffered(e,r)?t=this.getAppendedFrag(r):dr.isBuffered(e,r+.1)&&(t=this.getAppendedFrag(r+.1)),t&&(this.backtrackFragment=void 0,!V(t,this.fragPlaying)))return this.fragPlaying=t,!0}return!1},r.getBufferOutput=function(){return null},r.nextLevelSwitch=function(){var e=this.levels,t=this.media,r=this.hls,i=this.config,a=this.playlistType;if(null!=t&&t.readyState&&e&&r&&i){var n=this.getBufferOutput(),s=this.getFwdBufferInfo(n,a);if(!s)return;var o=e[a===N.AUDIO?r.nextAudioTrack:r.nextLoadLevel],l=this.calculateOptimalSwitchPoint(o,s),u=l.fetchdelay,d=l.okToFlushForwardBuffer;this.scheduleTrackSwitch(s,u,d)}this.tickImmediate()},a(t,[{key:"startPositionValue",get:function(){var e=this.nextLoadPosition,t=this.startPosition;if(-1===t&&e){var r=this.getLevelDetails();return null!=r&&r.live?(this.hls.liveSyncPosition||r.fragmentStart)+this.timelineOffset:e}return t}},{key:"bufferingEnabled",get:function(){return this.buffering}},{key:"backtrackFragment",get:function(){},set:function(e){}},{key:"couldBacktrack",get:function(){return!1},set:function(e){}},{key:"inFlightFrag",get:function(){return{frag:this.fragCurrent,state:this.state}}},{key:"timelineOffset",get:function(){var e,t=this.config.timelineOffset;return t?(null==(e=this.getLevelDetails())?void 0:e.appliedTimelineOffset)||t:0}},{key:"primaryPrefetch",get:function(){return this.config,!1}},{key:"playhead",get:function(){var e;if(null!=(e=this.hls)&&e.hasEnoughToStart){var t,r=null==(t=this.media)?void 0:t.currentTime;return _(r)?r:this.lastCurrentTime}return this.getLoadPosition()}},{key:"iframesOnly",get:function(){var e;if(this.playlistType!==N.MAIN)return!1;var t=this.getLevelDetails();return t?t.iframesOnly:null==(e=this.levelLastLoaded)?void 0:e.iframes}},{key:"state",get:function(){return this._state},set:function(e){var t=this._state;t!==e&&(this._state=e,this.log(t+"->"+e))}}])}(Fi),Yi=function(e){function t(t,r){var i;return(i=e.call(this,"gap-controller",t.logger)||this).hls=void 0,i.fragmentTracker=void 0,i.media=null,i.mediaSource=void 0,i.nudgeRetry=0,i.skipRetry=0,i.stallReported=!1,i.stalled=null,i.moved=!1,i.seeking=!1,i.buffered={},i.lastCurrentTime=void 0,i.ended=0,i.waiting=0,i.onMediaPlaying=function(){i.ended=0,i.waiting=0},i.onMediaWaiting=function(){var e;null!=(e=i.media)&&e.seeking||(i.waiting=self.performance.now(),i.tick())},i.onMediaEnded=function(){var e;i.hls&&(i.ended=(null==(e=i.media)?void 0:e.currentTime)||1,i.hls.trigger(O.MEDIA_ENDED,{stalled:!1}))},i.hls=t,i.fragmentTracker=r,i.lastCurrentTime=i.getCurrentTime(),i.registerListeners(),i}u(t,e);var r=t.prototype;return r.registerListeners=function(){var e=this.hls;e&&(e.on(O.MEDIA_ATTACHED,this.onMediaAttached,this),e.on(O.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(O.BUFFER_APPENDED,this.onBufferAppended,this))},r.unregisterListeners=function(){var e=this.hls;e&&(e.off(O.MEDIA_ATTACHED,this.onMediaAttached,this),e.off(O.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(O.BUFFER_APPENDED,this.onBufferAppended,this))},r.destroy=function(){e.prototype.destroy.call(this),this.unregisterListeners(),this.media=this.hls=this.fragmentTracker=null,this.mediaSource=void 0},r.onMediaAttached=function(e,t){this.setInterval(100),this.mediaSource=t.mediaSource;var r=this.media=t.media;hr(r,"playing",this.onMediaPlaying),hr(r,"waiting",this.onMediaWaiting),hr(r,"ended",this.onMediaEnded)},r.onMediaDetaching=function(e,t){this.clearInterval();var r=this.media;r&&(fr(r,"playing",this.onMediaPlaying),fr(r,"waiting",this.onMediaWaiting),fr(r,"ended",this.onMediaEnded),this.media=null),this.mediaSource=void 0},r.onBufferAppended=function(e,t){this.buffered=t.timeRanges},r.getCurrentTime=function(){var e,t=(null==(e=this.hls)?void 0:e.config.timelineOffset)||0;return this.media?Math.max(this.media.currentTime,t):t},r.tick=function(){var e;if(null!=(e=this.media)&&e.readyState&&this.hasBuffered){var t=this.getCurrentTime();this.media.currentTime<t||(this.poll(t,this.lastCurrentTime),this.lastCurrentTime=t)}},r.poll=function(e,t){var r,i,a=null==(r=this.hls)?void 0:r.config;if(a){var n=this.media;if(n){var s=n.seeking,o=this.seeking&&!s,l=!this.seeking&&s,u=n.paused&&!s||n.ended||0===n.playbackRate;if(this.seeking=s,e!==t)return t&&(this.ended=0),this.moved=!0,s||(this.skipRetry=this.nudgeRetry=0,a.nudgeOnVideoHole&&!u&&e>t&&this.nudgeOnVideoHole(e,t)),void(0===this.waiting&&this.stallResolved(e));if(l||o)o&&this.stallResolved(e);else{if(u)return this.skipRetry=this.nudgeRetry=0,this.stallResolved(e),void(!this.ended&&n.ended&&this.hls&&(this.ended=e||1,this.hls.trigger(O.MEDIA_ENDED,{stalled:!1})));if(dr.getBuffered(n).length){var d=dr.bufferInfo(n,e,0),h=this.fragmentTracker;h&&this.hls&&Xi(0,e,h,this.hls);var f=d.nextStart||0;if(s&&h&&this.hls){var c=Wi(this.hls.inFlightFragments,e),g=d.len>2,v=!f||c||f-e>2&&!h.getPartialFragment(e);if(g||v)return;this.moved=!1}var m=null==(i=this.hls)?void 0:i.latestLevelDetails;if(!this.moved&&null!==this.stalled&&h){if(!(d.len>0||f))return;var p=Math.max(f,d.start||0)-e,y=null!=m&&m.live?2*m.targetduration:2,E=qi(e,h);if(p>0&&(p<=y||E))return void(n.paused||this._trySkipBufferHole(E))}var T=a.detectStallWithCurrentTimeMs,S=self.performance.now(),L=this.waiting,R=this.stalled;if(null===R){if(!(L>0&&S-L<T))return void(this.stalled=S);R=this.stalled=L}var A=S-R;if(!s&&(A>=T||L&&this.moved)&&this.hls){var b;if("ended"===(null==(b=this.mediaSource)?void 0:b.readyState)&&(null==m||!m.live)&&Math.abs(e-((null==m?void 0:m.edge)||0))<1){if(this.ended)return;return this.ended=e||1,void this.hls.trigger(O.MEDIA_ENDED,{stalled:!0})}if(this._reportStall(d),!this.media||!this.hls)return;if(n.currentTime!==e){var k=e;return e=n.currentTime,this.moved=!0,void this.log("currentTime changed "+k+" > "+e)}}var D=dr.bufferInfo(n,e,a.maxBufferHole);this._tryFixBufferStall(D,A,e)}else this.skipRetry=this.nudgeRetry=0}}}},r.stallResolved=function(e){var t=this.stalled;if(t&&this.hls&&(this.stalled=null,this.stallReported)){var r=self.performance.now()-t;this.log("playback not stuck anymore @"+e+", after "+Math.round(r)+"ms"),this.stallReported=!1,this.waiting=0,this.hls.trigger(O.STALL_RESOLVED,{})}},r.nudgeOnVideoHole=function(e,t){var r,i=this.buffered.video;if(this.hls&&this.media&&this.fragmentTracker&&null!=(r=this.buffered.audio)&&r.length&&i&&i.length>1&&e>i.end(0)){var a=dr.bufferedInfo(dr.timeRangesToArray(this.buffered.audio),e,0);if(a.len>1&&t>=a.start){var n=dr.timeRangesToArray(i),s=dr.bufferedInfo(n,t,0).bufferedIndex;if(s>-1&&s<n.length-1){var o=dr.bufferedInfo(n,e,0).bufferedIndex,l=n[s].end,u=n[s+1].start;if((-1===o||o>s)&&u-l<1&&e-l<2){var d=new Error("nudging playhead to flush pipeline after video hole. currentTime: "+e+" hole: "+l+" -> "+u+" buffered index: "+o);this.warn(d.message),this.media.currentTime+=1e-6;var h=qi(e,this.fragmentTracker);h&&"fragment"in h?h=h.fragment:h||(h=void 0);var f=dr.bufferInfo(this.media,e,0);this.hls.trigger(O.ERROR,{type:x.MEDIA_ERROR,details:w.BUFFER_SEEK_OVER_HOLE,fatal:!1,error:d,reason:d.message,frag:h,buffer:f.len,bufferInfo:f})}}}}},r._tryFixBufferStall=function(e,t,r){var i,a,n=this.fragmentTracker,s=this.media,o=null==(i=this.hls)?void 0:i.config;if(s&&n&&o){var l=null==(a=this.hls)?void 0:a.latestLevelDetails,u=qi(r,n);if((u||null!=l&&l.live&&r<l.fragmentStart)&&(this._trySkipBufferHole(u)||!this.media))return;var d=e.buffered,h=this.adjacentTraversal(e,r);(d&&d.length>1&&e.len>o.maxBufferHole||e.nextStart&&(e.nextStart-r<o.maxBufferHole||h))&&(t>1e3*o.highBufferWatchdogPeriod||this.waiting)&&(this.warn("Trying to nudge playhead over buffer-hole"),this._tryNudgeBuffer(e))}},r.adjacentTraversal=function(e,t){var r=this.fragmentTracker,i=e.nextStart;if(r&&i){var a=r.getFragAtPos(t,N.MAIN),n=r.getFragAtPos(i,N.MAIN);if(a&&n)return n.sn-a.sn<2}return!1},r._reportStall=function(e){var t=this.hls,r=this.media,i=this.stallReported,a=this.stalled;if(!i&&null!==a&&r&&t){this.stallReported=!0;var n=new Error("Playback stalling at @"+r.currentTime+" due to low buffer ("+Bt(e)+")");this.warn(n.message),t.trigger(O.ERROR,{type:x.MEDIA_ERROR,details:w.BUFFER_STALLED_ERROR,fatal:!1,error:n,buffer:e.len,bufferInfo:e,stalled:{start:a}})}},r._trySkipBufferHole=function(e){var t=this.fragmentTracker,r=this.media,i=this.hls,a=null==i?void 0:i.config;if(!r||!t||!a)return 0;var n=r.currentTime,s=dr.bufferInfo(r,n,0);Xi(0,n,t,i);var o=n<s.start?s.start:s.nextStart;if(o){var l=s.len<=a.maxBufferHole,u=s.len>0&&s.len<1&&r.readyState<3,d=o-n;if(d>0&&(l||u)){if(d>a.maxBufferHole){var h=!1;if(0===n){var f=t.getAppendedFrag(0,N.MAIN);f&&o<f.end&&(h=!0)}if(!h&&e){var c;if(null==(c=i.loadLevelObj)||!c.details)return 0;if(Wi(i.inFlightFragments,o))return 0;for(var g=!1,v=e.end;v<o;){var m=qi(v,t),p=m?m.duration:0;if(!(p>0)){g=!0;break}v+=p}if(g)return 0}}var y=a.nudgeMaxRetry,E=a.skipBufferHolePadding,T=++this.skipRetry>y,S=Math.max(o,n)+E;if(T||(this.warn("skipping hole, adjusting currentTime from "+n+" to "+S),this.moved=!0,r.currentTime=S),null==e||!e.gap||T){var L=new Error(T?"Playhead still not moving after seeking over buffer hole from "+n+" to "+S+" after "+a.nudgeMaxRetry+" attempts.":"fragment loaded with buffer holes, seeking from "+n+" to "+S),R={type:x.MEDIA_ERROR,details:w.BUFFER_SEEK_OVER_HOLE,fatal:T,error:L,reason:L.message,buffer:s.len,bufferInfo:s};e&&("fragment"in e?R.part=e:R.frag=e),i.trigger(O.ERROR,R)}return S}}return 0},r._tryNudgeBuffer=function(e){var t=this.hls,r=this.media,i=this.nudgeRetry,a=null==t?void 0:t.config;if(!r||!a)return 0;var n=r.currentTime;if(this.nudgeRetry++,i<a.nudgeMaxRetry){var s=n+(i+1)*a.nudgeOffset,o=new Error("Nudging 'currentTime' from "+n+" to "+s);this.warn(o.message),r.currentTime=s,t.trigger(O.ERROR,{type:x.MEDIA_ERROR,details:w.BUFFER_NUDGE_ON_STALL,error:o,fatal:!1,buffer:e.len,bufferInfo:e})}else{var l=new Error("Playhead still not moving while enough data buffered @"+n+" after "+a.nudgeMaxRetry+" nudges");this.error(l.message),t.trigger(O.ERROR,{type:x.MEDIA_ERROR,details:w.BUFFER_STALLED_ERROR,error:l,fatal:!0,buffer:e.len,bufferInfo:e})}},a(t,[{key:"hasBuffered",get:function(){return Object.keys(this.buffered).length>0}}])}(Fi);function Wi(e,t){var r=ji(e.main);if(r&&r.start<=t)return r;var i=ji(e.audio);return i&&i.start<=t?i:null}function ji(e){if(!e)return null;switch(e.state){case Hi.IDLE:case Hi.STOPPED:case Hi.ENDED:case Hi.ERROR:return null}return e.frag}function qi(e,t){return t.getAppendedFrag(e,N.MAIN)||t.getPartialFragment(e)}function Xi(e,t,r,i){i.latestLevelDetails,qi(t,r),i.config}var zi=function(e){return e.audioId3="org.id3",e.dateRange="com.apple.quicktime.HLS",e.emsg="https://aomedia.org/emsg/ID3",e.misbklv="urn:misb:KLV:bin:1910.1",e}({});function Qi(e,t,r,i){var a=e.mode;if("disabled"===a&&(e.mode="hidden"),e.cues&&e.cues.length>0)for(var n=function(e,t,r){var i=[],a=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,a=0,n=r;a<=n;)if(t<e[i=Math.floor((n+a)/2)].startTime)n=i-1;else{if(!(t>e[i].startTime&&a<r))return i;a=i+1}return e[a].startTime-t<t-e[n].startTime?a:n}(e,t);if(a>-1)for(var n=a,s=e.length;n<s;n++){var o=e[n];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<n.length;s++)i&&!i(n[s])||e.removeCue(n[s]);"disabled"===a&&(e.mode=a)}function $i(){if("undefined"!=typeof self)return self.VTTCue||self.TextTrackCue}function Zi(e,t,r,i,a){var n=new e(t,r,"");try{n.value=i,a&&(n.type=a)}catch(s){n=new e(t,r,Bt(a?f({type:a},i):i))}return n}var Ji=function(){var e=$i();try{e&&new e(0,Number.POSITIVE_INFINITY,"")}catch(e){return Number.MAX_VALUE}return Number.POSITIVE_INFINITY}(),ea=function(){function e(e){var t=this;this.hls=void 0,this.id3Track=null,this.media=null,this.dateRangeCuesAppended={},this.removeCues=!0,this.assetCue=void 0,this.onEventCueEnter=function(){t.hls&&t.hls.trigger(O.EVENT_CUE_ENTER,{})},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=this.onEventCueEnter=null},t._registerListeners=function(){var e=this.hls;e&&(e.on(O.MEDIA_ATTACHING,this.onMediaAttaching,this),e.on(O.MEDIA_ATTACHED,this.onMediaAttached,this),e.on(O.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(O.MANIFEST_LOADING,this.onManifestLoading,this),e.on(O.FRAG_PARSING_METADATA,this.onFragParsingMetadata,this),e.on(O.BUFFER_FLUSHING,this.onBufferFlushing,this),e.on(O.LEVEL_UPDATED,this.onLevelUpdated,this),e.on(O.LEVEL_PTS_UPDATED,this.onLevelPtsUpdated,this))},t._unregisterListeners=function(){var e=this.hls;e&&(e.off(O.MEDIA_ATTACHING,this.onMediaAttaching,this),e.off(O.MEDIA_ATTACHED,this.onMediaAttached,this),e.off(O.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(O.MANIFEST_LOADING,this.onManifestLoading,this),e.off(O.FRAG_PARSING_METADATA,this.onFragParsingMetadata,this),e.off(O.BUFFER_FLUSHING,this.onBufferFlushing,this),e.off(O.LEVEL_UPDATED,this.onLevelUpdated,this),e.off(O.LEVEL_PTS_UPDATED,this.onLevelPtsUpdated,this))},t.onMediaAttaching=function(e,t){this.media=t.media},t.onMediaAttached=function(){var e,t=null==(e=this.hls)?void 0:e.latestLevelDetails;t&&this.updateDateRangeCues(t)},t.onMediaDetaching=function(e,t){this.media=null,t.transferMedia||(this.id3Track&&(this.id3Track.remove(),this.id3Track=null),this.dateRangeCuesAppended={})},t.onManifestLoading=function(){this.dateRangeCuesAppended={}},t.createTrack=function(e){return r="metadata",i="id3",a="hidden",(n=(t=e).ownerDocument.createElement("track")).kind=r,n.label=i,n.src="data:,WEBVTT",n.track.mode=a,t.appendChild(n),n;var t,r,i,a,n},t.onFragParsingMetadata=function(e,t){if(this.media&&this.hls){var r=this.hls.config,i=r.enableEmsgMetadataCues,a=r.enableID3MetadataCues;if(i||a){var n=t.samples;this.id3Track||(this.id3Track=this.createTrack(this.media));var s=$i();if(s)for(var o=0;o<n.length;o++){var l=n[o].type;if((l!==zi.emsg||i)&&a){var u=Ie(n[o].data),d=n[o].pts,h=d+n[o].duration;h>Ji&&(h=Ji),h-d<=0&&(h=d+.25);for(var f=0;f<u.length;f++){var c=u[f];if(!_e(c)){this.updateId3CueEnds(d,l);var g=Zi(s,d,h,c,l);g&&this.id3Track.track.addCue(g)}}}}}}},t.updateId3CueEnds=function(e,t){var r,i=null==(r=this.id3Track)?void 0:r.track.cues;if(i)for(var a=i.length;a--;){var n=i[a];n.type===t&&n.startTime<e&&n.endTime===Ji&&(n.endTime=e)}},t.onBufferFlushing=function(e,t){var r=t.startOffset,i=t.endOffset,a=t.type,n=this.id3Track,s=this.hls;if(s){var o,l=s.config,u=l.enableEmsgMetadataCues,d=l.enableID3MetadataCues;n&&(u||d)&&(o="audio"===a?function(e){return e.type===zi.audioId3&&d}:"video"===a?function(e){return e.type===zi.emsg&&u}:function(e){return e.type===zi.audioId3&&d||e.type===zi.emsg&&u},Qi(n.track,r,i,o))}},t.onLevelUpdated=function(e,t){var r=t.details;this.updateDateRangeCues(r,!0)},t.onLevelPtsUpdated=function(e,t){Math.abs(t.drift)>.01&&this.updateDateRangeCues(t.details)},t.updateDateRangeCues=function(e,t){var r=this;if(this.hls&&this.media){var i=this.hls.config;i.assetPlayerId,i.timelineOffset;var a=i.enableDateRangeMetadataCues;if(i.interstitialsController,a){var n=$i();if(e.hasProgramDateTime){var s,o=this.id3Track,l=e.dateRanges,u=Object.keys(l),d=this.dateRangeCuesAppended;if(o&&t)if(null!=(s=o.track.cues)&&s.length)for(var h=Object.keys(d).filter((function(e){return-1===u.indexOf(e)})),f=function(){var e,t=h[c],i=null==(e=d[t])?void 0:e.cues;delete d[t],i&&Object.keys(i).forEach((function(e){var t=i[e];if(t){t.removeEventListener("enter",r.onEventCueEnter);try{o.track.removeCue(t)}catch(e){}}}))},c=h.length;c--;)f();else d=this.dateRangeCuesAppended={};var g=e.fragments[e.fragments.length-1];if(0!==u.length&&_(null==g?void 0:g.programDateTime)){this.id3Track||(this.id3Track=this.createTrack(this.media));for(var v=function(){var e=u[m],t=l[e],i=t.startTime,a=d[e],s=(null==a?void 0:a.cues)||{},o=(null==a?void 0:a.durationKnown)||!1,h=Ji,f=t.duration;if(t.endDate&&null!==f)h=i+f,o=!0;else if(t.endOnNext&&!o){var c=u.reduce((function(e,r){if(r!==t.id){var i=l[r];if(i.class===t.class&&i.startDate>t.startDate&&(!e||t.startDate<e.startDate))return i}return e}),null);c&&(h=c.startTime,o=!0)}for(var g,v=Object.keys(t.attr),p=0;p<v.length;p++){var y=v[p];if("ID"!==(g=y)&&"CLASS"!==g&&"CUE"!==g&&"START-DATE"!==g&&"DURATION"!==g&&"END-DATE"!==g&&"END-ON-NEXT"!==g){var E=s[y];if(E)!o||null!=a&&a.durationKnown?Math.abs(E.startTime-i)>.01&&(E.startTime=i,E.endTime=h):E.endTime=h;else if(n){var T=t.attr[y];Ir(y)&&(T=we(T));var S=Zi(n,i,h,{key:y,data:T},zi.dateRange);S&&(S.id=e,r.id3Track.track.addCue(S),s[y]=S)}}}d[e]={cues:s,dateRange:t,durationKnown:o}},m=0;m<u.length;m++)v()}}}}},e}(),ta=function(){function e(e){var t=this;this.hls=void 0,this.media=null,this.currentTime=0,this.stallCount=0,this._latency=null,this._targetLatencyUpdated=!1,this.onTimeupdate=function(){var e=t.media,r=t.levelDetails;if(e&&r&&t.hls){t.currentTime=e.currentTime;var i=t.hls.config,a=t.computeLatency();if(null!==a){t._latency=a;var n=i.lowLatencyMode,s=i.maxLiveSyncPlaybackRate;if(n&&1!==s&&r.live){var o=t.targetLatency;if(null!==o){var l=a-o;if(l<Math.min(t.maxLatency,o+r.targetduration)&&l>.05&&t.forwardBufferLength>1){var u=Math.min(2,Math.max(1,s)),d=Math.round(2/(1+Math.exp(-.75*l-t.edgeStalled))*20)/20,h=Math.min(u,Math.max(1,d));t.changeMediaPlaybackRate(e,h)}else 1!==e.playbackRate&&0!==e.playbackRate&&t.changeMediaPlaybackRate(e,1)}}}}},this.hls=e,this.registerListeners()}var t=e.prototype;return t.destroy=function(){this.unregisterListeners(),this.onMediaDetaching(),this.hls=null},t.registerListeners=function(){var e=this.hls;e&&(e.on(O.MEDIA_ATTACHED,this.onMediaAttached,this),e.on(O.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(O.MANIFEST_LOADING,this.onManifestLoading,this),e.on(O.LEVEL_UPDATED,this.onLevelUpdated,this),e.on(O.ERROR,this.onError,this),e.on(O.INTERSTITIAL_ASSET_STARTED,this.onAssetStarted,this))},t.unregisterListeners=function(){var e=this.hls;e&&(e.off(O.MEDIA_ATTACHED,this.onMediaAttached,this),e.off(O.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(O.MANIFEST_LOADING,this.onManifestLoading,this),e.off(O.LEVEL_UPDATED,this.onLevelUpdated,this),e.off(O.ERROR,this.onError,this),e.off(O.INTERSTITIAL_ASSET_STARTED,this.onAssetStarted,this))},t.onMediaAttached=function(e,t){this.media=t.media,this.media.addEventListener("timeupdate",this.onTimeupdate)},t.onMediaDetaching=function(){this.media&&(this.media.removeEventListener("timeupdate",this.onTimeupdate),this.media=null)},t.onManifestLoading=function(){this._latency=null,this.stallCount=0},t.onLevelUpdated=function(e,t){var r=t.details;r.advanced&&this.onTimeupdate(),!r.live&&this.media&&this.media.removeEventListener("timeupdate",this.onTimeupdate)},t.onAssetStarted=function(e,t){var r,i=this.hls,a=this.media||t.event.appendInPlace&&(null==i||null==(r=i.interstitialsManager)?void 0:r.playerQueue.reduce((function(e,t){return e||t.media}),null));i&&a&&a.playbackRate>1&&a.playbackRate<=i.config.maxLiveSyncPlaybackRate&&this.changeMediaPlaybackRate(a,1)},t.onError=function(e,t){var r;t.details===w.BUFFER_STALLED_ERROR&&(this.stallCount++,this.hls&&null!=(r=this.levelDetails)&&r.live&&this.hls.logger.warn("[latency-controller]: Stall detected, adjusting target latency"))},t.changeMediaPlaybackRate=function(e,t){var r,i;e.playbackRate!==t&&(null==(r=this.hls)||r.logger.debug("[latency-controller]: latency="+this.latency.toFixed(3)+", targetLatency="+(null==(i=this.targetLatency)?void 0:i.toFixed(3))+", forwardBufferLength="+this.forwardBufferLength.toFixed(3)+": adjusting playback rate from "+e.playbackRate+" to "+t),e.playbackRate=t)},t.estimateLiveEdge=function(){var e=this.levelDetails;return null===e?null:e.edge+e.age},t.computeLatency=function(){var e=this.estimateLiveEdge();return null===e?null:e-this.currentTime},a(e,[{key:"levelDetails",get:function(){var e;return(null==(e=this.hls)?void 0:e.latestLevelDetails)||null}},{key:"latency",get:function(){return this._latency||0}},{key:"maxLatency",get:function(){var e,t=null==(e=this.hls)?void 0:e.config;if(!t)return 0;if(void 0!==t.liveMaxLatencyDuration)return t.liveMaxLatencyDuration;var r=this.levelDetails;return r?t.liveMaxLatencyDurationCount*r.targetduration:0}},{key:"targetLatency",get:function(){var e=this.levelDetails;if(null===e||!this.hls)return null;var t=this.hls.config,r=e.holdBack,i=e.partHoldBack,a=e.targetduration,n=t.liveSyncDuration,s=t.liveSyncDurationCount,o=t.lowLatencyMode,l=this.hls.userConfig,u=o&&i||r;(this._targetLatencyUpdated||l.liveSyncDuration||l.liveSyncDurationCount||0===u)&&(u=void 0!==n?n:s*a);var d=a;return u+Math.min(this.stallCount*t.liveSyncOnStallIncrease,d)},set:function(e){this.hls&&(this.stallCount=0,this.hls.config.liveSyncDuration=e,this._targetLatencyUpdated=!0)}},{key:"liveSyncPosition",get:function(){var e=this.estimateLiveEdge(),t=this.targetLatency;if(null===e||null===t||!this.hls)return null;var r=this.levelDetails;if(null===r)return null;var i=r.edge,a=e-t-this.edgeStalled,n=i-r.totalduration,s=i-(this.hls.config.lowLatencyMode&&r.partTarget||r.targetduration);return Math.min(Math.max(n,a),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||!this.hls)return 0;var t=3*(this.hls.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}}])}();function ra(e,t){return 255===e[t]&&240==(246&e[t+1])}function ia(e,t){return 1&e[t+1]?7:9}function aa(e,t){return(3&e[t+3])<<11|e[t+4]<<3|(224&e[t+5])>>>5}function na(e,t){return t+1<e.length&&ra(e,t)}function sa(e,t){if(na(e,t)){var r=ia(e,t);if(t+r>=e.length)return!1;var i=aa(e,t);if(i<=r)return!1;var a=t+i;return a===e.length||na(e,a)}return!1}function oa(e,t,r,i,a){if(!e.samplerate){var n=function(e,t,r,i){var a=t[r+2],n=a>>2&15;if(!(n>12)){var s,o=1+(a>>6&3),l=t[r+3]>>6&3|(1&a)<<2,u=[96e3,88200,64e3,48e3,44100,32e3,24e3,22050,16e3,12e3,11025,8e3,7350][n],d=n;5!==o&&29!==o||(d-=3);var h=(14&d)>>1,f=(1&d)<<7|l<<3,c=0;if(1===o&&n>=6){var g=n-3;s=[(c=5)<<3|h,f|(14&g)>>1,(1&g)<<7|8,0]}else s=[o<<3|h,f];var v="mp4a.40."+(c||o);return fe.log("manifest codec:"+i+", parsed codec:"+v+", channels:"+l+", rate:"+u+" (ADTS object type:"+o+" sampling index:"+n+" esds config:["+s.join(",")+"])"),{config:s,samplerate:u,channelCount:l,codec:v,parsedCodec:v,manifestCodec:i}}var m=new Error("invalid ADTS sampling index:"+n);e.emit(O.ERROR,O.ERROR,{type:x.MEDIA_ERROR,details:w.FRAG_PARSING_ERROR,fatal:!0,error:m,reason:m.message})}(t,r,i,a);if(!n)return;o(e,n)}}function la(e){return 9216e4/e}function ua(e,t,r,i,a){var n,s=i+a*la(e.samplerate),o=function(e,t){var r=ia(e,t);if(t+r<=e.length){var i=aa(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?(n=new Uint8Array(d-u)).set(t.subarray(r+u,t.length),0):n=t.subarray(r+u,r+d);var f={unit:n,pts:s};return h||e.samples.push(f),{sample:f,length:d,missing:h}}var c=t.length-r;return(n=new Uint8Array(c)).set(t.subarray(r,t.length),0),{sample:{unit:n,pts:s},length:c,missing:-1}}function da(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 ha=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=it(this.cachedData,e),this.cachedData=null);var r,i=Te(e,0),a=i?i.length:0,n=this._audioTrack,s=this._id3Track,o=i?Pe(i):void 0,l=e.length;for((null===this.basePTS||0===this.frameIndex&&_(o))&&(this.basePTS=fa(o,t,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:zi.audioId3,duration:Number.POSITIVE_INFINITY});a<l;){if(this.canParse(e,a)){var u=this.appendFrame(n,e,a);u?(this.frameIndex++,this.lastPTS=u.sample.pts,r=a+=u.length):a=l}else ye(e,a)?(i=Te(e,a),s.samples.push({pts:this.lastPTS,dts:this.lastPTS,data:i,type:zi.audioId3,duration:Number.POSITIVE_INFINITY}),r=a+=i.length):a++;if(a===l&&r!==l){var d=e.slice(r);this.cachedData?this.cachedData=it(this.cachedData,d):this.cachedData=d}}return{audioTrack:n,videoTrack:da(),id3Track:s,textTrack:da()}},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:da(),id3Track:this._id3Track,textTrack:da()}},t.destroy=function(){this.cachedData=null,this._audioTrack=this._id3Track=void 0},e}(),fa=function(e,t,r){return _(e)?90*e:9e4*t+(r?9e4*r.baseTime/r.timescale:0)},ca=[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],ga=[44100,48e3,32e3,22050,24e3,16e3,11025,12e3,8e3],va=[[0,72,144,12],[0,0,0,0],[0,72,144,12],[0,144,144,12]],ma=[0,1,1,4];function pa(e,t,r,i,a){if(!(r+24>t.length)){var n=ya(t,r);if(n&&r+n.frameLength<=t.length){var s=i+a*(9e4*n.samplesPerFrame/n.sampleRate),o={unit:t.subarray(r,r+n.frameLength),pts:s,dts:s};return e.config=[],e.channelCount=n.channelCount,e.samplerate=n.sampleRate,e.samples.push(o),{sample:o,length:n.frameLength,missing:0}}}}function ya(e,t){var r=e[t+1]>>3&3,i=e[t+1]>>1&3,a=e[t+2]>>4&15,n=e[t+2]>>2&3;if(1!==r&&0!==a&&15!==a&&3!==n){var s=e[t+2]>>1&1,o=e[t+3]>>6,l=1e3*ca[14*(3===r?3-i:3===i?3:4)+a-1],u=ga[3*(3===r?0:2===r?1:2)+n],d=3===o?1:2,h=va[r][i],f=ma[i],c=8*h*f,g=Math.floor(h*l/u+s)*f,v=ct();return!!v&&v<=87&&2===i&&l>=224e3&&0===o&&(e[t+3]=128|e[t+3]),{sampleRate:u,channelCount:d,frameLength:g,samplesPerFrame:c}}}function Ea(e,t){return 255===e[t]&&224==(224&e[t+1])&&0!=(6&e[t+1])}function Ta(e,t){return t+1<e.length&&Ea(e,t)}function Sa(e,t){if(t+1<e.length&&Ea(e,t)){var r=ya(e,t),i=4;null!=r&&r.frameLength&&(i=r.frameLength);var a=t+i;return a===e.length||Ta(e,a)}return!1}var La=function(){function e(e,t,r){this.keyData=void 0,this.decrypter=void 0,this.keyData=r,this.decrypter=new _i(t,!0)}var t=e.prototype;return t.decryptBuffer=function(e){return this.decrypter.decrypt(e,this.keyData.key.buffer,this.keyData.iv.buffer,xr,{start:0,end:e.byteLength})},t.decryptAacSample=function(e,t,r){var i=this,a=e[t].unit;if(!(a.length<=16)){var n=a.subarray(16,a.length-a.length%16),s=n.buffer.slice(n.byteOffset,n.byteOffset+n.length);this.decryptBuffer(s).then((function(n){var s=new Uint8Array(n);a.set(s,16),i.decrypter.isSync()||i.decryptAacSamples(e,t+1,r)})).catch(r)}},t.decryptAacSamples=function(e,t,r){for(;;t++){if(t>=e.length)return void r();if(!(e[t].unit.length<32||(this.decryptAacSample(e,t,r),this.decrypter.isSync())))return}},t.getAvcEncryptedData=function(e){for(var t=16*Math.floor((e.length-48)/160)+16,r=new Int8Array(t),i=0,a=32;a<e.length-16;a+=160,i+=16)r.set(e.subarray(a,a+16),i);return r},t.getAvcDecryptedUnit=function(e,t){for(var r=new Uint8Array(t),i=0,a=32;a<e.length-16;a+=160,i+=16)e.set(r.subarray(i,i+16),a);return e},t.decryptAvcSample=function(e,t,r,i,a){var n=this,s=ot(a.data),o=this.getAvcEncryptedData(s);this.decryptBuffer(o.buffer).then((function(o){a.data=n.getAvcDecryptedUnit(s,o),n.decrypter.isSync()||n.decryptAvcSamples(e,t,r+1,i)})).catch(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 a=e[t].units;!(r>=a.length);r++){var n=a[r];if(!(n.data.length<=48||1!==n.type&&5!==n.type||(this.decryptAvcSample(e,t,r,i,n),this.decrypter.isSync())))return}}},e}(),Ra=function(e){function t(t,r){var i;return(i=e.call(this)||this).observer=void 0,i.config=void 0,i.sampleAes=null,i.observer=t,i.config=r,i}u(t,e);var r=t.prototype;return r.resetInitSegment=function(t,r,i,a){e.prototype.resetInitSegment.call(this,t,r,i,a),this._audioTrack={container:"audio/adts",type:"audio",id:2,pid:-1,sequenceNumber:0,segmentCodec:"aac",samples:[],manifestCodec:r,duration:a,inputTimeScale:9e4,dropped:0}},t.probe=function(e,t){if(!e)return!1;var r=Te(e,0),i=(null==r?void 0:r.length)||0;if(Sa(e,i))return!1;for(var a=e.length;i<a;i++)if(sa(e,i))return t.log("ADTS sync word found !"),!0;return!1},r.canParse=function(e,t){return function(e,t){return function(e,t){return t+5<e.length}(e,t)&&ra(e,t)&&aa(e,t)<=e.length-t}(e,t)},r.appendFrame=function(e,t,r){oa(e,this.observer,t,r,e.manifestCodec);var i=ua(e,t,r,this.basePTS,this.frameIndex);if(0===(null==i?void 0:i.missing))return i},r.demuxSampleAes=function(e,t,r){var i=this.demux(e,r),a=this.sampleAes=new La(this.observer,this.config,t);return new Promise((function(e){a.decryptAacSamples(i.audioTrack.samples,0,(function(){e(i)}))}))},t}(ha),Aa=function(e){function t(){return e.apply(this,arguments)||this}u(t,e);var r=t.prototype;return r.resetInitSegment=function(t,r,i,a){e.prototype.resetInitSegment.call(this,t,r,i,a),this._audioTrack={container:"audio/mpeg",type:"audio",id:2,pid:-1,sequenceNumber:0,segmentCodec:"mp3",samples:[],manifestCodec:r,duration:a,inputTimeScale:9e4,dropped:0}},t.probe=function(e){if(!e)return!1;var t=Te(e,0),r=(null==t?void 0:t.length)||0;if(t&&11===e[r]&&119===e[r+1]&&void 0!==Pe(t)&&function(e,t){var r=0,i=5;t+=i;for(var a=new Uint32Array(1),n=new Uint32Array(1),s=new Uint8Array(1);i>0;){s[0]=e[t];var o=Math.min(i,8),l=8-o;n[0]=4278190080>>>24+l<<l,a[0]=(s[0]&n[0])>>l,r=r?r<<o|a[0]:a[0],t+=1,i-=o}return r}(e,r)<=16)return!1;for(var i=e.length;r<i;r++)if(Sa(e,r))return fe.log("MPEG Audio sync word found !"),!0;return!1},r.canParse=function(e,t){return function(e,t){return Ea(e,t)&&4<=e.length-t}(e,t)},r.appendFrame=function(e,t,r){if(null!==this.basePTS)return pa(e,t,r,this.basePTS,this.frameIndex)},t}(ha),ba=/\/emsg[-/]ID3/i,ka=function(){function e(e,t,r,i){void 0===i&&(i=fe),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.initData=void 0,this.logger=void 0,this.config=t,this.logger=i}var t=e.prototype;return t.resetTimeStamp=function(){},t.resetInitSegment=function(e,t,r,i){var a=this.videoTrack=da("video",1),n=this.audioTrack=da("audio",1),s=this.txtTrack=da("text",1);if(this.id3Track=da("id3",1),this.initData=void 0,this.timeOffset=0,null!=e&&e.byteLength){var o=this.initData=Xe(e);if(o.video){var l=o.video,u=l.id,d=l.timescale,h=l.codec,f=l.supplemental;a.id=u,a.timescale=s.timescale=d,a.codec=h,a.supplemental=f}if(o.audio){var c=o.audio,g=c.id,v=c.timescale,m=c.codec;n.id=g,n.timescale=v,n.codec=m}s.id=Be.text,a.sampleDuration=0,a.duration=n.duration=i}},t.resetContiguity=function(){this.remainderData=null},e.probe=function(e){return function(e,t){for(var r=e.byteLength,i=0;i<r;){var a=Ge(e,i);if(a>8&&Ge(e,i+4)===Ne[t])return!0;i=a>1?i+a:r}return!1}(e,"moof")},t.demux=function(e,t,r){this.timeOffset=t;var i=e,a=this.videoTrack,n=this.txtTrack;if(this.config.progressive){this.remainderData&&(i=it(this.remainderData,e));var s=function(e){var t={valid:null,remainder:null},r=We(e,["moof"]);if(r.length<2)return t.remainder=e,t;var i=r[r.length-1];return t.valid=e.slice(0,i.byteOffset-8),t.remainder=e.slice(i.byteOffset-8),t}(i);this.remainderData=s.remainder,a.samples=s.valid||new Uint8Array}else a.samples=i;var o=this.extractID3Track(a,t),l=this.parseFragment(a,n,t,r);return{videoTrack:a,audioTrack:this.audioTrack,id3Track:o,textTrack:this.txtTrack,initData:this.initData,sampleData:l}},t.flush=function(e,t){var r=this.timeOffset,i=this.videoTrack,a=this.txtTrack;i.samples=this.remainderData||new Uint8Array,this.remainderData=null;var n=this.extractID3Track(i,this.timeOffset),s=this.parseFragment(i,a,r,t);return{videoTrack:i,audioTrack:da(),id3Track:n,textTrack:da(),initData:this.initData,sampleData:s}},t.parseFragment=function(e,t,r,i){var a=this.initData;if(a&&i){var n=function(e,t,r,i,a){var n=[];return{samples:n,sampleData:{includeSampleDetails:a,tracks:rt(t.samples,r,i,0,{isHEVCFlavor:at(t.codec),samples:n,timeOffset:e,timescale:t.timescale,trackId:t.id})}}}(r,e,a,this.logger,!1),s=n.samples,o=n.sampleData;return t.samples=s,o}t.samples=function(e,t){var r=[],i=t.samples,a=t.timescale,n=t.id,s=!1;return We(i,["moof"]).map((function(o){var l=o.byteOffset-8;We(o,["traf"]).map((function(o){var u=We(o,["tfdt"]).map((function(e){var t=e[0],r=Ge(e,4);return 1===t&&(r*=Math.pow(2,32),r+=Ge(e,8)),r/a}))[0];return void 0!==u&&(e=u),We(o,["tfhd"]).map((function(u){var d=Ge(u,4),h=16777215&Ge(u,0),f=0!=(1&h),c=0,g=0!=(2&h),v=0!=(8&h),m=0,p=0!=(16&h),y=0,E=0!=(32&h),T=!f&&0!=(131072&h),S=8;d===n&&(f?(c=Ge(u,S),S+=4,c*=Math.pow(2,32),c+=Ge(u,S),S+=4):T&&(c=l),g&&(S+=4),v&&(m=Ge(u,S),S+=4),p&&(y=Ge(u,S),S+=4),E&&(S+=4),"video"===t.type&&(s=at(t.codec)),We(o,["trun"]).map((function(n){var o=n[0],l=16777215&Ge(n,0),u=0!=(1&l),d=0,h=0!=(4&l),f=0!=(256&l),g=0,v=0!=(512&l),p=0,E=0!=(1024&l),T=0!=(2048&l),S=0,L=Ge(n,4),R=8;u&&(d=Ge(n,R),R+=4),h&&(R+=4);for(var A=c+d,b=0;b<L;b++){if(f?(g=Ge(n,R),R+=4):g=m,v?(p=Ge(n,R),R+=4):p=y,E&&(R+=4),T&&(S=0===o?Ge(n,R):Ke(n,R),R+=4),t.type===B.VIDEO)for(var k=0;k<p;){var D=Ge(i,A);nt(s,i[A+=4])&&st(i.subarray(A,A+D),s?2:1,e+S/a,r),A+=D,k+=D+4}e+=g/a}})))}))}))})),r}(r,e)},t.extractID3Track=function(e,t){var r=this,i=this.id3Track;if(e.samples.length){var a=We(e.samples,["emsg"]);a&&a.forEach((function(e){var a=function(e){var t,r=e[0],i="",a="",n=0,s=0,o=0,l=0,u=4;if(0===r){var d=He(e,u),h=null!==d?He(e,u+d.length):null;null!==d&&null!==h?(i=d,a=h,n=Ge(e,u+=d.length+h.length),s=Ge(e,u+=4),o=Ge(e,u+=4),l=Ge(e,u+=4),u+=4):(fe.warn("Unterminated "+(null===d?"scheme_id_uri":"value")+" in parsing emsg box: version 0 box ends inside a string, dropping all of its fields"),u=e.length)}else if(1===r){n=Ge(e,u);var f=Ge(e,u+=4),c=Ge(e,u+=4);u+=4,t=Math.pow(2,32)*f+c,C(t)||(t=Number.MAX_SAFE_INTEGER,fe.warn("Presentation time exceeds safe integer limit and wrapped to max safe integer in parsing emsg box")),o=Ge(e,u),l=Ge(e,u+=4);var g=He(e,u+=4),v=null!==g?He(e,u+g.length):null;null!==g&&(i=g,u+=g.length),null!==v?(a=v,u+=v.length):(fe.warn("Unterminated "+(null===g?"scheme_id_uri":"value")+" in parsing emsg box: version 1 box ends inside a string, dropping its "+(null===g?"strings and payload":"value and payload")),u=e.length)}return{schemeIdUri:i,value:a,timeScale:n,presentationTime:t,presentationTimeDelta:s,eventDuration:o,id:l,payload:e.subarray(u,e.byteLength)}}(e);if(ba.test(a.schemeIdUri)){var n=Da(a,t),s=4294967295===a.eventDuration?Number.POSITIVE_INFINITY:a.eventDuration/a.timeScale;s<=.001&&(s=Number.POSITIVE_INFINITY);var o=a.payload;i.samples.push({data:o,len:o.byteLength,dts:n,pts:n,type:zi.emsg,duration:s})}else if(r.config.enableEmsgKLVMetadata){var l=r.config.emsgKLVSchemaUri||zi.misbklv;if(a.schemeIdUri.startsWith(l)){var u=Da(a,t);i.samples.push({data:a.payload,len:a.payload.byteLength,dts:u,pts:u,type:zi.misbklv,duration:Number.POSITIVE_INFINITY})}}}))}return i},t.demuxSampleAes=function(e,t,r){return Promise.reject(new Error("The MP4 demuxer does not support SAMPLE-AES decryption"))},t.destroy=function(){this.config=null,this.remainderData=null,this.videoTrack=this.audioTrack=this.id3Track=this.txtTrack=void 0,this.initData=void 0},e}();function Da(e,t){return _(e.presentationTime)?e.presentationTime/e.timeScale:t+e.presentationTimeDelta/e.timeScale}var Ia=function(){function e(){this.VideoSample=null}var t=e.prototype;return t.createVideoSample=function(e,t,r){return{key:e,frame:!1,pts:t,dts:r,units:[],length:0}},t.getLastNalUnit=function(e){var t,r,i=this.VideoSample;if(i&&0!==i.units.length||(i=e[e.length-1]),null!=(t=i)&&t.units){var a=i.units;r=a[a.length-1]}return r},t.pushAccessUnit=function(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 a=r[i-1];e.pts=a.pts,e.dts=a.dts}t.samples.push(e)}},t.parseNALu=function(e,t,r){var i=t.byteLength;if(0===i)return[];var a=e.naluState||0,n=-1===a?0:a,s=[],o=0,l=0,u=-1,d=0;for(-1===a&&(u=0,d=this.getNALuType(t,0),l=1);l<i;){var h=t.indexOf(1,l);if(-1===h)break;l=h+1;for(var f=h;f>0&&0===t[f-1];)f--;if(!(h-f+(0===f?n:0)<2)){if(u>=0)s.push({data:t.subarray(u,f),type:d});else{var c=this.getLastNalUnit(e.samples);c&&(f>0?c.data=it(c.data,t.subarray(0,f)):n>0&&(c.data=c.data.subarray(0,c.data.byteLength-n)),c.state=0)}if(!(l<i)){o=-1,u=-1;break}u=l,d=this.getNALuType(t,l)}}if(-1!==o){for(var g=i;g>0&&0===t[g-1];)g--;o=i-g+(0===g?n:0)}if(u>=0)s.push({data:t.subarray(u),type:d,state:o});else if(0===s.length&&-1!==o){var v=this.getLastNalUnit(e.samples);v&&(v.data=it(v.data,t),v.state=o)}return e.naluState=o,s},e}(),_a=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),a=Math.min(4,t);if(0===a)throw new Error("no bytes available");i.set(e.subarray(r,r+a)),this.word=new DataView(i.buffer).getUint32(0),this.bitsAvailable=8*a,this.bytesAvailable-=a},t.skipBits=function(e){var t;e=Math.min(e,8*this.bytesAvailable+this.bitsAvailable),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;if(e>32&&fe.error("Cannot read more than 32 bits at a time"),this.bitsAvailable-=t,this.bitsAvailable>0)this.word<<=t;else{if(!(this.bytesAvailable>0))throw new Error("no bits available");this.loadWord()}return(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)},e}(),Ca=function(e){function t(){return e.apply(this,arguments)||this}u(t,e);var r=t.prototype;return r.parsePES=function(e,t,r,i,a){var n,s=this,o=a.iframe,l=this.parseNALu(e,r.data,i),u=this.VideoSample,d=!1;r.data=null,u&&l.length&&!e.audFound&&(this.pushAccessUnit(u,e),u=this.VideoSample=this.createVideoSample(!1,r.pts,r.dts)),l.forEach((function(i){var a,l;switch(i.type){case 1:if(o)break;var h=!1;n=!0;var f,c=i.data;if(d&&c.length>4){var g=s.readSliceType(c);2!==g&&4!==g&&7!==g&&9!==g||(h=!0)}h&&null!=(f=u)&&f.frame&&!u.key&&(s.pushAccessUnit(u,e),u=s.VideoSample=null),u||(u=s.VideoSample=s.createVideoSample(!0,r.pts,r.dts)),u.frame=!0,u.key=h;break;case 5:n=!0,null!=(a=u)&&a.frame&&!u.key&&(s.pushAccessUnit(u,e),u=s.VideoSample=null),u||(u=s.VideoSample=s.createVideoSample(!0,r.pts,r.dts)),u.key=!0,u.frame=!0;break;case 6:if(o)break;n=!0,st(i.data,1,r.pts,t.samples);break;case 7:var v,m;n=!0,d=!0;var p=i.data,y=s.readSPS(p);if(!e.sps||e.width!==y.width||e.height!==y.height||(null==(v=e.pixelRatio)?void 0:v[0])!==y.pixelRatio[0]||(null==(m=e.pixelRatio)?void 0:m[1])!==y.pixelRatio[1]){e.width=y.width,e.height=y.height,e.pixelRatio=y.pixelRatio,e.sps=[p];for(var E=p.subarray(1,4),T="avc1.",S=0;S<3;S++){var L=E[S].toString(16);L.length<2&&(L="0"+L),T+=L}e.codec=T}break;case 8:n=!0,e.pps=[i.data];break;case 9:n=!0,e.audFound=!0,null!=(l=u)&&l.frame&&(s.pushAccessUnit(u,e),u=null),u||(u=s.VideoSample=s.createVideoSample(!1,r.pts,r.dts));break;case 12:n=!0;break;default:n=!1}u&&n&&u.units.push(i)})),i&&u&&(this.pushAccessUnit(u,e),this.VideoSample=null)},r.getNALuType=function(e,t){return 31&e[t]},r.readSliceType=function(e){var t=new _a(e);return t.readUByte(),t.readUEG(),t.readUEG()},r.skipScalingList=function(e,t){for(var r=8,i=8,a=0;a<e;a++)0!==i&&(i=(r+t.readEG()+256)%256),r=0===i?r:i},r.readSPS=function(e){var t,r,i,a=new _a(e),n=0,s=0,o=0,l=0,u=a.readUByte.bind(a),d=a.readBits.bind(a),h=a.readUEG.bind(a),f=a.readBoolean.bind(a),c=a.skipBits.bind(a),g=a.skipEG.bind(a),v=a.skipUEG.bind(a),m=this.skipScalingList.bind(this);u();var p=u();if(d(5),c(3),u(),v(),100===p||110===p||122===p||244===p||44===p||83===p||86===p||118===p||128===p){var y=h();if(3===y&&c(1),v(),v(),c(1),f())for(r=3!==y?8:12,i=0;i<r;i++)f()&&m(i<6?16:64,a)}v();var E=h();if(0===E)h();else if(1===E)for(c(1),g(),g(),t=h(),i=0;i<t;i++)g();v(),c(1);var T=h(),S=h(),L=d(1);0===L&&c(1),c(1),f()&&(n=h(),s=h(),o=h(),l=h());var R=[1,1];if(f()&&f())switch(u()){case 1:R=[1,1];break;case 2:R=[12,11];break;case 3:R=[10,11];break;case 4:R=[16,11];break;case 5:R=[40,33];break;case 6:R=[24,11];break;case 7:R=[20,11];break;case 8:R=[32,11];break;case 9:R=[80,33];break;case 10:R=[18,11];break;case 11:R=[15,11];break;case 12:R=[64,33];break;case 13:R=[160,99];break;case 14:R=[4,3];break;case 15:R=[3,2];break;case 16:R=[2,1];break;case 255:R=[u()<<8|u(),u()<<8|u()]}return{width:Math.ceil(16*(T+1)-2*n-2*s),height:(2-L)*(S+1)*16-(L?2:4)*(o+l),pixelRatio:R}},t}(Ia),Pa=188,xa=function(){function e(e,t,r,i){this.logger=void 0,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._pmtId=-1,this._videoTrack=void 0,this._audioTrack=void 0,this._id3Track=void 0,this._txtTrack=void 0,this._klvPid=-1,this.aacOverFlow=null,this.remainderData=null,this.videoParser=void 0,this.videoIntegrityChecker=null,this.observer=e,this.config=t,this.typeSupported=r,this.logger=i,this.videoParser=null}e.probe=function(t,r){var i=e.syncOffset(t);return i>0&&r.warn("MPEG2-TS detected but first sync word found @ offset "+i),-1!==i},e.syncOffset=function(e){for(var t=e.length,r=Math.min(940,t-Pa)+1,i=0;i<r;){for(var a=!1,n=-1,s=0,o=i;o<t;o+=Pa){if(71!==e[o]||t-o!==Pa&&71!==e[o+Pa]){if(s)return-1;break}if(s++,-1===n&&0!==(n=o)&&(r=Math.min(n+18612,e.length-Pa)+1),a||(a=0===wa(e,o)),a&&s>1&&(0===n&&s>2||o+Pa>r))return n}i++}return-1},e.createTrack=function(e,t){return{container:"video"===e||"audio"===e?"video/mp2t":void 0,type:e,id:Be[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,a,n,s){this.pmtParsed=!1,this._pmtId=-1,this._videoTrack=e.createTrack("video"),this._videoTrack.duration=a,this.videoIntegrityChecker="skip"===this.config.handleMpegTsVideoIntegrityErrors?new Ga(this.logger):null,this._audioTrack=e.createTrack("audio",a),this._id3Track=e.createTrack("id3"),this._txtTrack=e.createTrack("text"),this._audioTrack.segmentCodec="aac",this.videoParser=null,this.aacOverFlow=null,this.remainderData=null,this.audioCodec=r,this.videoCodec=i,t&&this.demux(t,0,s,"SAMPLE-AES"===(null==n?void 0:n.method))},t.resetTimeStamp=function(){},t.resetContiguity=function(){var e=this._audioTrack,t=this._videoTrack,r=this._id3Track;e&&(e.pesData=null),t&&(t.pesData=null),r&&(r.pesData=null),this.aacOverFlow=null,this.remainderData=null},t.demux=function(t,r,i,a,n){var s;void 0===a&&(a=!1),void 0===n&&(n=!1),a||(this.sampleAes=null);var o=this._videoTrack,l=this.videoIntegrityChecker,u=this._audioTrack,d=this._id3Track,h=this._txtTrack,f=i.iframe,c=o.pid,g=o.pesData,v=u.pid,m=d.pid,p=this._klvPid,y=u.pesData,E=d.pesData,T=null,S=null,L=this.pmtParsed,R=this._pmtId,A=t.length;if(this.remainderData&&(A=(t=it(this.remainderData,t)).length,this.remainderData=null),A<Pa&&!n)return this.remainderData=t,{audioTrack:u,videoTrack:o,id3Track:d,textTrack:h};var b=Math.max(0,e.syncOffset(t));(A-=(A-b)%Pa)<t.byteLength&&!n&&(this.remainderData=new Uint8Array(t.buffer,A,t.buffer.byteLength-A));for(var k=0,D=b;D<A;D+=Pa)if(71===t[D]){var I=!!(64&t[D+1]),_=wa(t,D),C=void 0;if((48&t[D+3])>>4>1){if((C=D+5+t[D+4])===D+Pa)continue}else C=D+4;switch(_){case c:I&&(!g||null!=l&&l.isCorrupted||!(s=Ba(g,this.logger))||(this.readyVideoParser(o.segmentCodec),null!==this.videoParser&&this.videoParser.parsePES(o,h,s,!1,i)),g={data:[],size:0},null==l||l.reset(c)),null==l||l.handlePacket(t.subarray(D)),g&&(g.data.push(t.subarray(C,D+Pa)),g.size+=D+Pa-C);break;case v:if(f)break;if(I){if(y&&(s=Ba(y,this.logger)))switch(u.segmentCodec){case"aac":this.parseAACPES(u,s,i);break;case"mp3":this.parseMPEGPES(u,s)}y={data:[],size:0}}y&&(y.data.push(t.subarray(C,D+Pa)),y.size+=D+Pa-C);break;case m:if(f)break;I&&(E&&(s=Ba(E,this.logger))&&this.parseID3PES(d,s),E={data:[],size:0}),E&&(E.data.push(t.subarray(C,D+Pa)),E.size+=D+Pa-C);break;case p:if(f)break;I&&(T&&(s=Ba(T,this.logger))&&this.parseKlvPES(d,s),T={data:[],size:0}),T&&(T.data.push(t.subarray(C,D+Pa)),T.size+=D+Pa-C);break;case 0:I&&(C+=t[C]+1),R=this._pmtId=Oa(t,C);break;case R:I&&(C+=t[C]+1);var P=Ma(t,C,this.typeSupported,a,this.observer,this.logger,this.config,i);(c=P.videoPid)>0&&(o.pid=c,o.segmentCodec=P.segmentVideoCodec),(v=P.audioPid)>0&&(u.pid=v,u.segmentCodec=P.segmentAudioCodec),(m=P.id3Pid)>0&&(d.pid=m),(p=P.klvPid)>0&&(this._klvPid=p),null===S||L||(this.logger.warn("MPEG-TS PMT found at "+D+" after unknown PID '"+S+"'. Backtracking to sync byte @"+b+" to parse all TS packets."),S=null,D=b-188),L=this.pmtParsed=!0;break;case 17:case 8191:break;default:S=_}}else k++;k>0&&Fa(this.observer,new Error("Found "+k+" TS packet/s that do not start with 0x47"),i,void 0,this.logger),o.pesData=g,u.pesData=y,d.pesData=E;var x={audioTrack:u,videoTrack:o,id3Track:d,textTrack:h};return n&&this.extractRemainingSamples(x,i),x},t.flush=function(e,t){var r,i=this.remainderData;return this.remainderData=null,r=i?this.demux(i,0,t,!1,!0):{videoTrack:this._videoTrack,audioTrack:this._audioTrack,id3Track:this._id3Track,textTrack:this._txtTrack},this.extractRemainingSamples(r,t),this.sampleAes?this.decrypt(r,this.sampleAes):r},t.extractRemainingSamples=function(e,t){var r,i=e.audioTrack,a=e.videoTrack,n=e.id3Track,s=e.textTrack,o=a.pesData,l=i.pesData,u=n.pesData,d=this.videoIntegrityChecker;if(!o||null!=d&&d.isCorrupted||!(r=Ba(o,this.logger))?a.pesData=o:(this.readyVideoParser(a.segmentCodec),null!==this.videoParser&&(this.videoParser.parsePES(a,s,r,!0,t),a.pesData=null)),l&&(r=Ba(l,this.logger))){switch(i.segmentCodec){case"aac":this.parseAACPES(i,r,t);break;case"mp3":this.parseMPEGPES(i,r)}i.pesData=null}else null!=l&&l.size&&this.logger.log("last AAC PES packet truncated,might overlap between fragments"),i.pesData=l;u&&(r=Ba(u,this.logger))?(this.parseID3PES(n,r),n.pesData=null):n.pesData=u},t.demuxSampleAes=function(e,t,r,i){var a=this.demux(e,r,i,!0,!this.config.progressive),n=this.sampleAes=new La(this.observer,this.config,t);return this.decrypt(a,n)},t.readyVideoParser=function(e){null===this.videoParser&&"avc"===e&&(this.videoParser=new Ca)},t.decrypt=function(e,t){return new Promise((function(r){var i=e.audioTrack,a=e.videoTrack;i.samples&&"aac"===i.segmentCodec?t.decryptAacSamples(i.samples,0,(function(){a.samples?t.decryptAvcSamples(a.samples,0,0,(function(){r(e)})):r(e)})):a.samples&&t.decryptAvcSamples(a.samples,0,0,(function(){r(e)}))}))},t.destroy=function(){this.observer&&this.observer.removeAllListeners(),this.config=this.logger=this.observer=null,this.aacOverFlow=this.videoParser=this.remainderData=this.sampleAes=null,this._videoTrack=this._audioTrack=this._id3Track=this._txtTrack=void 0,this.videoIntegrityChecker=null},t.parseAACPES=function(e,t,r){var i,a,n,s=0,o=this.aacOverFlow,l=t.data;if(o){this.aacOverFlow=null;var u=o.missing,d=o.sample.unit.byteLength;if(-1===u)l=it(o.sample.unit,l);else{var h=d-u;o.sample.unit.set(l.subarray(0,u),h),e.samples.push(o.sample),s=o.missing}}for(i=s,a=l.length;i<a-1&&!na(l,i);i++);if(i!==s){var f,c=i<a-1;if(f=c?"AAC PES did not start with ADTS header,offset:"+i:"No ADTS header found in AAC PES",Fa(this.observer,new Error(f),r,c,this.logger),!c)return}if(oa(e,this.observer,l,i,this.audioCodec),void 0!==t.pts)n=t.pts;else{if(!o)return void this.logger.warn("[tsdemuxer]: AAC PES unknown PTS");var g=la(e.samplerate);n=o.sample.pts+g}for(var v,m=0;i<a;){if(i+=(v=ua(e,l,i,n,m)).length,v.missing){this.aacOverFlow=v;break}for(m++;i<a-1&&!na(l,i);i++);}},t.parseMPEGPES=function(e,t){var r=t.data,i=r.length,a=0,n=0,s=t.pts;if(void 0!==s)for(;n<i;)if(Ta(r,n)){var o=pa(e,r,n,s,a);if(!o)break;n+=o.length,a++}else n++;else this.logger.warn("[tsdemuxer]: MPEG PES unknown PTS")},t.parseAC3PES=function(e,t){},t.parseID3PES=function(e,t){if(void 0!==t.pts){var r=o({},t,{type:this._videoTrack?zi.emsg:zi.audioId3,duration:Number.POSITIVE_INFINITY});e.samples.push(r)}else this.logger.warn("[tsdemuxer]: ID3 PES unknown PTS")},t.parseKlvPES=function(e,t){var r,i=null!=(r=t.pts)?r:t.dts;if(void 0!==i){var a=t.data;if(a.length<16)this.logger.warn("[tsdemuxer]: KLV PES payload too short");else for(var n=0;n<a.length;){var s;if(n+16>a.length)break;var o=n;if((n+=16)>=a.length)break;var l=a[n];n+=1;var u=0;if(128&l){var d=127&l;if(0===d||d>4||n+d>a.length){this.logger.warn("[tsdemuxer]: Invalid KLV length encoding");break}for(var h=0;h<d;h++)u=u<<8|a[n+h];n+=d}else u=l;if(n+u>a.length){this.logger.warn("[tsdemuxer]: KLV value extends beyond PES payload");break}var f=n+u,c=a.subarray(o,f),g={data:c,len:c.byteLength,pts:i,dts:null!=(s=t.dts)?s:i,type:zi.misbklv,duration:Number.POSITIVE_INFINITY};e.samples.push(g),n=f}}else this.logger.warn("[tsdemuxer]: KLV PES unknown PTS and DTS")},e}();function wa(e,t){return((31&e[t+1])<<8)+e[t+2]}function Oa(e,t){return(31&e[t+10])<<8|e[t+11]}function Ma(e,t,r,i,a,n,s,o){var l={audioPid:-1,videoPid:-1,id3Pid:-1,klvPid:-1,segmentVideoCodec:"avc",segmentAudioCodec:"aac"},u=t+3+((15&e[t+1])<<8|e[t+2])-4;for(t+=12+((15&e[t+10])<<8|e[t+11]);t<u;){var d=wa(e,t),h=(15&e[t+3])<<8|e[t+4];switch(e[t]){case 207:if(!i){Na("ADTS AAC",n);break}case 15:-1===l.audioPid&&(l.audioPid=d);break;case 21:-1===l.id3Pid&&(l.id3Pid=d);break;case 219:if(!i){Na("H.264",n);break}case 27:-1===l.videoPid&&(l.videoPid=d);break;case 3:case 4:r.mpeg||r.mp3?-1===l.audioPid&&(l.audioPid=d,l.segmentAudioCodec="mp3"):n.log("MPEG audio found, not supported in this browser");break;case 193:if(!i){Na("AC-3",n);break}case 129:n.warn("AC-3 in M2TS support not included in build");break;case 6:if(h>0)for(var f=t+5,c=h;c>2;){switch(e[f]){case 106:-1===l.audioPid&&n.warn("AC-3 in M2TS support not included in build");break;case 5:-1===l.klvPid&&s.enableEmsgKLVMetadata&&(l.klvPid=d,n.log("KLV metadata PID found: "+d))}var g=e[f+1]+2;f+=g,c-=g}break;case 194:case 135:return Fa(a,new Error("Unsupported EC-3 in M2TS found"),o,void 0,n),l;case 36:return Fa(a,new Error("Unsupported HEVC in M2TS found"),o,void 0,n),l}t+=h+5}return l}function Fa(e,t,r,i,a){a.warn("parsing error: "+t.message),e.emit(O.ERROR,O.ERROR,{type:x.MEDIA_ERROR,details:w.FRAG_PARSING_ERROR,fatal:!1,levelRetry:i,chunkMeta:r,error:t,reason:t.message})}function Na(e,t){t.log(e+" with AES-128-CBC encryption found in unencrypted stream")}function Ba(e,t){var r,i,a,n,s,o=0,l=e.data;if(!e||0===e.size)return null;for(;l[0].length<19&&l.length>1;)l[0]=it(l[0],l[1]),l.splice(1,1);if(1===((r=l[0])[0]<<16)+(r[1]<<8)+r[2]){if((i=(r[4]<<8)+r[5])&&i>e.size-6)return null;var u=r[7];192&u&&(n=536870912*(14&r[9])+4194304*(255&r[10])+16384*(254&r[11])+128*(255&r[12])+(254&r[13])/2,64&u?n-(s=536870912*(14&r[14])+4194304*(255&r[15])+16384*(254&r[16])+128*(255&r[17])+(254&r[18])/2)>54e5&&(t.warn(Math.round((n-s)/9e4)+"s delta between PTS and DTS, align them"),n=s):s=n);var d=(a=r[8])+9;if(e.size<=d)return null;e.size-=d;for(var h=new Uint8Array(e.size),f=0,c=l.length;f<c;f++){var g=(r=l[f]).byteLength;if(d){if(d>g){d-=g;continue}r=r.subarray(d),g-=d,d=0}h.set(r,o),o+=g}return i&&(i-=a+3),{data:h,pts:n,dts:s,len:i}}return null}var Ua,Ga=function(){function e(e){this.logger=void 0,this.pid=0,this.lastContinuityCounter=-1,this.integrityState="ok",this.logger=e}var t=e.prototype;return t.reset=function(e){this.pid=e,this.lastContinuityCounter=-1,this.integrityState="ok"},t.handlePacket=function(e){if(!(e.byteLength<4)){var t=wa(e,0);if(t===this.pid){var r=(48&e[3])>>4;if(0!==r){var i=15&e[3],a=this.lastContinuityCounter;this.lastContinuityCounter=i;var n=0!=(1&r);if(!(0!=(2&r)&&0!=e[4]&&0!=(128&e[5])||a<0)){var s=n?a+1&15:a;return i!==s?(this.logger.warn("MPEG-TS Continuity Counter check failed for PID='"+t+"', CC="+i+", Expected-CC="+s+" Last-CC="+a),void(this.integrityState="cc-failed")):0!=(128&e[1])?(this.logger.warn("MPEG-TS Packet had TEI flag set for PID='"+t+"'"),void(this.integrityState="tei-bit")):void 0}}}else this.logger.debug("Packet PID mismatch, expected "+this.pid+" got "+t)}},a(e,[{key:"isCorrupted",get:function(){return"ok"!==this.integrityState}}])}(),Va=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}(),Ha={video: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]),audio: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])},Ka=new Uint8Array([0,0,0,0,0,0,0,0]),Ya=new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0]),Wa=new Uint8Array([0,0,0,1,0,0,0,0,0,0,0,0]),ja=new Uint8Array([0,0,0,0,0,0,0,0]),qa=new Uint8Array([0,0,0,0,0,0,0,1]),Xa=new Uint8Array([105,115,111,109]),za=new Uint8Array([97,118,99,49]),Qa=new Uint8Array([0,0,0,1]),$a=function(){function e(){}return e.box=function(e){for(var t=8,r=arguments.length,i=new Array(r>1?r-1:0),a=1;a<r;a++)i[a-1]=arguments[a];for(var n=i.length,s=n;n--;)t+=i[n].byteLength;var o=new Uint8Array(t);for(Ye(o,0,t),Ye(o,4,e),n=0,t=8;n<s;n++)o.set(i[n],t),t+=i[n].byteLength;return o},e.hdlr=function(t){return e.box(Ne.hdlr,Ha[t])},e.mdat=function(t){return e.box(Ne.mdat,t)},e.mdhd=function(t,r){r*=t;var i=Math.floor(r/(Fe+1)),a=Math.floor(r%(Fe+1));return e.box(Ne.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,a>>24,a>>16&255,a>>8&255,255&a,85,196,0,0]))},e.mdia=function(t){return e.box(Ne.mdia,e.mdhd(t.timescale||0,t.duration||0),e.hdlr(t.type),e.minf(t))},e.mfhd=function(t){return e.box(Ne.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(Ne.minf,e.box(Ne.smhd,ja),e.DINF,e.stbl(t)):e.box(Ne.minf,e.box(Ne.vmhd,Wa),e.DINF,e.stbl(t))},e.moof=function(t,r,i){return e.box(Ne.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,[Ne.moov,e.mvhd(t[0].timescale||0,t[0].duration||0)].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,[Ne.mvex].concat(i))},e.mvhd=function(t,r){r*=t;var i=Math.floor(r/(Fe+1)),a=Math.floor(r%(Fe+1)),n=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,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 e.box(Ne.mvhd,n)},e.sdtp=function(t){var r,i,a=t.samples||[],n=new Uint8Array(4+a.length);for(r=0;r<a.length;r++)i=a[r].flags,n[r+4]=i.dependsOn<<4|i.isDependedOn<<2|i.hasRedundancy;return e.box(Ne.sdtp,n)},e.stbl=function(t){return e.box(Ne.stbl,e.stsd(t),e.box(Ne.stts,Ka),e.box(Ne.stsc,Ka),e.box(Ne.stsz,Ya),e.box(Ne.stco,Ka))},e.avc1=function(t){var r,i,a,n=[],s=[];for(r=0;r<t.sps.length;r++)a=(i=t.sps[r]).byteLength,n.push(a>>>8&255),n.push(255&a),n=n.concat(Array.prototype.slice.call(i));for(r=0;r<t.pps.length;r++)a=(i=t.pps[r]).byteLength,s.push(a>>>8&255),s.push(255&a),s=s.concat(Array.prototype.slice.call(i));var o=e.box(Ne.avcC,new Uint8Array([1,n[3],n[4],n[5],255,224|t.sps.length].concat(n).concat([t.pps.length]).concat(s))),l=t.width,u=t.height,d=t.pixelRatio[0],h=t.pixelRatio[1];return e.box(Ne.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(Ne.btrt,new Uint8Array([0,28,156,128,0,45,198,192,0,45,198,192])),e.box(Ne.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,r=t.length;return new Uint8Array([0,0,0,0,3,23+r,0,1,0,4,15+r,64,21,0,0,0,0,0,0,0,0,0,0,0,5,r].concat(t,[6,1,2]))},e.audioStsd=function(e){var t=e.samplerate||0;return new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,e.channelCount||0,0,16,0,0,0,0,t>>8&255,255&t,0,0])},e.mp4a=function(t){return e.box(Ne.mp4a,e.audioStsd(t),e.box(Ne.esds,e.esds(t)))},e.mp3=function(t){return e.box(Ne[".mp3"],e.audioStsd(t))},e.ac3=function(t){return e.box(Ne["ac-3"],e.audioStsd(t),e.box(Ne.dac3,t.config))},e.stsd=function(t){var r=t.segmentCodec;if("audio"===t.type){if("aac"===r)return e.box(Ne.stsd,qa,e.mp4a(t));if("mp3"===r&&"mp3"===t.codec)return e.box(Ne.stsd,qa,e.mp3(t))}else{if(!t.pps||!t.sps)throw new Error("video track missing pps or sps");if("avc"===r)return e.box(Ne.stsd,qa,e.avc1(t))}throw new Error("unsupported "+t.type+" segment codec ("+r+"/"+t.codec+")")},e.tkhd=function(t){var r=t.id,i=(t.duration||0)*(t.timescale||0),a=t.width||0,n=t.height||0,s=Math.floor(i/(Fe+1)),o=Math.floor(i%(Fe+1));return e.box(Ne.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]))},e.traf=function(t,r){var i=e.sdtp(t),a=t.id,n=Math.floor(r/(Fe+1)),s=Math.floor(r%(Fe+1));return e.box(Ne.traf,e.box(Ne.tfhd,new Uint8Array([0,0,0,0,a>>24,a>>16&255,a>>8&255,255&a])),e.box(Ne.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])),e.trun(t,i.length+16+20+8+16+8+8),i)},e.trak=function(t){return t.duration=t.duration||4294967295,e.box(Ne.trak,e.tkhd(t),e.mdia(t))},e.trex=function(t){var r=t.id;return e.box(Ne.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,a,n,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++)n=(a=u[i]).duration,s=a.size,o=a.flags,l=a.cts,f.set([n>>>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 e.box(Ne.trun,f)},e.initSegment=function(t){var r=e.moov(t);return it(e.FTYP,r)},e.hvc1=function(e){return new Uint8Array},e}();function Za(e,t){return function(e,t,r,i){var a=e*t*r;return Math.round(a)}(e,1e3,1/9e4)}function Ja(e){var t=e.baseTime,r=e.timescale;return t/r+" ("+t+"/"+r+") trackId: "+e.trackId}Ua=$a,$a.FTYP=Ua.box(Ne.ftyp,Xa,Qa,Xa,za),$a.DINF=Ua.box(Ne.dinf,Ua.box(Ne.dref,new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,12,117,114,108,32,0,0,0,1])));var en=8589934592;function tn(e,t,r,i){return{duration:t,size:r,cts:i,flags:{isLeading:0,isDependedOn:0,hasRedundancy:0,degradPrio:0,dependsOn:e?2:1,isNonSync:e?0:1,paddingValue:0}}}var rn=function(e){function t(t,r,i,a){var n;return(n=e.call(this,"mp4-remuxer",a)||this).observer=void 0,n.config=void 0,n.typeSupported=void 0,n.ISGenerated=!1,n._initPTS=null,n._initDTS=null,n.nextVideoTs=null,n.nextAudioTs=null,n.videoSampleDuration=null,n.isAudioContiguous=!1,n.isVideoContiguous=!1,n.videoTrackConfig=void 0,n.observer=t,n.config=r,n.typeSupported=i,n.ISGenerated=!1,n}u(t,e);var r=t.prototype;return r.destroy=function(){this.config=this.videoTrackConfig=this._initPTS=this._initDTS=null},r.resetTimeStamp=function(e){var t=this._initPTS;t&&e&&e.trackId===t.trackId&&e.baseTime===t.baseTime&&e.timescale===t.timescale||this.log("Reset initPTS: "+(t?Ja(t):t)+" > "+(e?Ja(e):e)),this._initPTS=this._initDTS=e},r.resetNextTimestamp=function(){this.log("reset next timestamp"),this.isVideoContiguous=!1,this.isAudioContiguous=!1},r.resetInitSegment=function(){this.log("ISGenerated flag reset"),this.ISGenerated=!1,this.videoTrackConfig=void 0},r.getVideoStartPts=function(e){var t=!1,r=e[0].pts,i=e.reduce((function(e,i){var a=i.pts,n=a-e;return n<-4294967296&&(t=!0,n=(a=an(a,r))-e),n>0?e:a}),r);return t&&this.debug("PTS rollover detected"),i},r.remux=function(e,t,r,i,a,n,s,o,l){var u,d,h,f,c,g,v=a,m=a,p=!l.iframe&&e.pid>-1,y=t.pid>-1,E=t.samples.length,T=e.samples.length>0,S=s&&E>0||E>1;if((!p||T)&&(!y||S)||this.ISGenerated||s){if(this.ISGenerated){var L,R,A,b,k=this.videoTrackConfig;(k&&(t.width!==k.width||t.height!==k.height||(null==(L=t.pixelRatio)?void 0:L[0])!==(null==(R=k.pixelRatio)?void 0:R[0])||(null==(A=t.pixelRatio)?void 0:A[1])!==(null==(b=k.pixelRatio)?void 0:b[1]))||!k&&S||null===this.nextAudioTs&&T)&&this.resetInitSegment()}this.ISGenerated||(h=this.generateIS(e,t,a,n));var D,I=this.isVideoContiguous,_=-1;if(S&&(_=function(e){for(var t=0;t<e.length;t++)if(e[t].key)return t;return-1}(t.samples),!I&&this.config.forceKeyFrameOnDiscontinuity))if(g=!0,_>0){this.warn("Dropped "+_+" out of "+E+" video samples due to a missing keyframe");var C=this.getVideoStartPts(t.samples);t.samples=t.samples.slice(_),t.dropped+=_,D=m+=(t.samples[0].pts-C)/t.inputTimeScale}else-1===_&&(this.warn("No keyframe found out of "+E+" video samples"),g=!1);if(this.ISGenerated){if(T&&S&&a){var P=this.getVideoStartPts(t.samples),x=(an(e.samples[0].pts,P)-P)/t.inputTimeScale;v+=Math.max(0,x),m+=Math.max(0,-x)}if(T){if(e.samplerate||(this.warn("regenerate InitSegment as audio detected"),h=this.generateIS(e,t,a,n)),d=this.remuxAudio(e,v,this.isAudioContiguous,n,y||S||o===N.AUDIO?m:void 0,l),S){var w=d?d.endPTS-d.startPTS:0;t.inputTimeScale||(this.warn("regenerate InitSegment as video detected"),h=this.generateIS(e,t,a,n)),u=this.remuxVideo(t,m,I,w,l)}}else S&&(u=this.remuxVideo(t,m,I,0,l));u&&(u.firstKeyFrame=_,u.independent=-1!==_,u.firstKeyFramePTS=D)}}return this.ISGenerated&&this._initPTS&&this._initDTS&&(r.samples.length&&(c=nn(r,a,this._initPTS,this._initDTS)),i.samples.length&&(f=sn(i,a,this._initPTS))),{audio:d,video:u,initSegment:h,independent:g,text:f,id3:c}},r.computeInitPts=function(e,t,r,i){var a=Math.round(r*t),n=an(e,a);if(n<a)for(this.log("Adjusting PTS for rollover in timeline near "+(a-n)/t+" "+i);n<a;)n+=en;return n-a},r.generateIS=function(e,t,r,i){var a,n,s,o=e.samples,l=t.samples,u=this.typeSupported,d={},h=this._initPTS,f=!h||i,c="audio/mp4",g=-1;if(f&&(a=n=1/0),e.config&&o.length){switch(e.timescale=e.samplerate,e.segmentCodec){case"mp3":u.mpeg?(c="audio/mpeg",e.codec=""):u.mp3&&(e.codec="mp3");break;case"ac3":e.codec="ac-3"}if(d.audio={id:"audio",container:c,codec:e.codec,initSegment:"mp3"===e.segmentCodec&&u.mpeg?new Uint8Array(0):$a.initSegment([e]),metadata:{channelCount:e.channelCount}},f){g=e.id,s=e.inputTimeScale;var v=this.computeInitPts(o[0].pts,s,r,"audio");(!h||s!==h.timescale||0===r&&v<h.baseTime)&&(a=n=v)}}if(t.sps&&t.pps&&l.length){if(t.timescale=t.inputTimeScale,d.video={id:"main",container:"video/mp4",codec:t.codec,initSegment:$a.initSegment([t]),metadata:{width:t.width,height:t.height}},f){g=t.id,s=t.inputTimeScale;var m=this.getVideoStartPts(l),p=an(l[0].dts,m),y=this.computeInitPts(p,s,r,"video"),E=this.computeInitPts(m,s,r,"video");(!h||s!==h.timescale||_(a)||0===r&&E<h.baseTime)&&(n=Math.min(n,y),a=Math.min(a,y))}this.videoTrackConfig={width:t.width,height:t.height,pixelRatio:t.pixelRatio}}if(Object.keys(d).length)return this.ISGenerated=!0,f&&_(a)?(h&&this.warn("Timestamps at playlist time: "+(i?"":"~")+r+" "+a/s+" != initPTS: "+h.baseTime/h.timescale+" ("+h.baseTime+"/"+h.timescale+") trackId: "+h.trackId),this.log("Found initPTS at playlist time: "+r+" offset: "+a/s+" ("+a+"/"+s+") trackId: "+g),this._initPTS={baseTime:a,timescale:s,trackId:g},this._initDTS={baseTime:n,timescale:s,trackId:g}):a=s=void 0,{tracks:d,initPTS:a,timescale:s,trackId:g}},r.remuxVideo=function(e,t,r,i,a){var n,s,l=e.inputTimeScale,u=e.samples,d=[],h=u.length,f=this._initPTS,c=f.baseTime*l/f.timescale,g=this.nextVideoTs,v=8,m=this.videoSampleDuration,p=Number.POSITIVE_INFINITY,y=Number.NEGATIVE_INFINITY,E=!1;if(!r||null===g){var T=c+t*l,S=u[0].pts-an(u[0].dts,u[0].pts);ct()&&null!==g&&Math.abs(T-S-(g+c))<15e3?r=!0:g=T-S-c}for(var L=g+c,R=0;R<h;R++){var A=u[R];A.pts=an(A.pts,L),A.dts=an(A.dts,L),A.dts<u[R>0?R-1:R].dts&&(E=!0)}E&&u.sort((function(e,t){var r=e.dts-t.dts,i=e.pts-t.pts;return r||i})),n=u[0].dts;var b=(s=u[u.length-1].dts)-n,k=b?Math.round(b/(h-1)):m||e.inputTimeScale/30;if(r){var D=n-L,I=D>k,_=D<-1;if((I||_)&&(I?this.warn((e.segmentCodec||"").toUpperCase()+": "+Za(D)+" ms ("+D+"dts) hole between fragments detected at "+t.toFixed(3)):this.warn((e.segmentCodec||"").toUpperCase()+": "+Za(-D)+" ms ("+D+"dts) overlapping between fragments detected at "+t.toFixed(3)),!_||L>=u[0].pts||ct())){n=L;var C=u[0].pts-D;if(I)u[0].dts=n,u[0].pts=C;else for(var P=!0,M=0;M<u.length&&!(u[M].dts>C&&P);M++){var F=u[M].pts;if(u[M].dts-=D,u[M].pts-=D,M<u.length-1){var N=u[M+1].pts;P=N<=u[M].pts==N<=F}}this.log("Video: Initial PTS/DTS adjusted: "+Za(C)+"/"+Za(n)+", delta: "+Za(D)+" ms")}}for(var B=0,U=0,G=n=Math.max(0,n),V=0;V<h;V++){for(var H=u[V],K=H.units,Y=K.length,W=0,j=0;j<Y;j++)W+=K[j].data.length;U+=W,B+=Y,H.length=W,H.dts<G?(H.dts=G,G+=k/4|0||1):G=H.dts,p=Math.min(H.pts,p),y=Math.max(H.pts,y)}s=u[h-1].dts;var q,X=U+4*B+8;try{q=new Uint8Array(X)}catch(e){return void this.observer.emit(O.ERROR,O.ERROR,{type:x.MUX_ERROR,details:w.REMUX_ALLOC_ERROR,fatal:!1,chunkMeta:a,error:e,bytes:X,reason:"fail allocating video mdat "+X})}Ye(q,0,X),Ye(q,4,Ne.mdat);for(var z=!1,Q=Number.POSITIVE_INFINITY,$=Number.POSITIVE_INFINITY,Z=Number.NEGATIVE_INFINITY,J=Number.NEGATIVE_INFINITY,ee=0;ee<h;ee++){for(var te=u[ee],re=te.units,ie=0,ae=0,ne=re.length;ae<ne;ae++){var se=re[ae],oe=se.data,le=se.data.byteLength;Ye(q,v,le),v+=4,q.set(oe,v),v+=le,ie+=4+le}var ue=void 0;if(ee<h-1)m=u[ee+1].dts-te.dts,ue=u[ee+1].pts-te.pts;else{var de=this.config,he=ee>0?te.dts-u[ee-1].dts:k;if(ue=ee>0?te.pts-u[ee-1].pts:k,de.stretchShortVideoTrack&&null!==this.nextAudioTs){var fe=Math.floor(de.maxBufferHole*l),ce=(i?p+i*l:this.nextAudioTs+c)-te.pts;ce>fe?((m=ce-he)<0?m=he:z=!0,this.log("It is approximately "+ce/90+" ms to the next segment; using duration "+m/90+" ms for the last video frame.")):m=he}else m=he}var ge=Math.round(te.pts-te.dts);if(Q=Math.min(Q,m),Z=Math.max(Z,m),$=Math.min($,ue),J=Math.max(J,ue),!r&&0===ee&&ge>m){var ve=Math.round(ge/k)*k;ve-ge==1&&(this.log("pad first CTS "+ge+" -> "+ve+" of sn: "+a.sn),ge=ve)}d.push(tn(te.key,m,ie,ge))}if(d.length){var me=ct();if(me){if(me<70){var pe=d[0].flags;pe.dependsOn=2,pe.isNonSync=0}}else if(function(){if(void 0===dt){var e=ht.match(/Safari\/(\d+)/i);dt=e?parseInt(e[1]):0}return dt}()&&J-$<Z-Q&&k/Z<.025&&0===d[0].cts){this.warn("Found irregular gaps in sample duration. Using PTS instead of DTS to determine MP4 sample duration.");for(var ye=n,Ee=0,Te=d.length;Ee<Te;Ee++){var Se=ye+d[Ee].duration,Le=ye+d[Ee].cts;if(Ee<Te-1){var Re=Se+d[Ee+1].cts;d[Ee].duration=Re-Le}else d[Ee].duration=Ee?d[Ee-1].duration:k;d[Ee].cts=0,ye=Se}}}var Ae=s+(m=z||!m?k:m);this.nextVideoTs=g=Ae-c,this.videoSampleDuration=m,this.isVideoContiguous=!0;var be={data1:$a.moof(a.sn,n,o(e,{samples:d})),data2:q,startPTS:(p-c)/l,endPTS:(y+m-c)/l,startDTS:(n-c)/l,endDTS:g/l,type:"video",hasAudio:!1,hasVideo:!0,nb:d.length,dropped:e.dropped};return e.samples=[],e.dropped=0,be},r.getSamplesPerFrame=function(e){switch(e.segmentCodec){case"mp3":return 1152;case"ac3":return 1536;default:return 1024}},r.remuxAudio=function(e,t,r,i,a,n){var s=e.inputTimeScale,l=s/(e.samplerate?e.samplerate:s),u=this.getSamplesPerFrame(e),d=u*l,h=this._initPTS,f="mp3"===e.segmentCodec&&this.typeSupported.mpeg,c=[],g=void 0!==a,v=e.samples,m=f?0:8,p=this.nextAudioTs||-1,y=h.baseTime*s/h.timescale,E=y+t*s;if(this.isAudioContiguous=r=r||v.length&&p>0&&(i&&Math.abs(E-(p+y))<9e3||Math.abs(an(v[0].pts,E)-(p+y))<20*d),v.forEach((function(e){e.pts=an(e.pts,E)})),!r||p<0){var T=v.length;if(v=v.filter((function(e){return e.pts>=0})),T!==v.length&&this.warn("Removed "+(v.length-T)+" of "+T+" samples (initPTS "+y+" / "+s+")"),!v.length)return;p=0===a?0:i&&!g?Math.max(0,E-y):v[0].pts-y}if("aac"===e.segmentCodec)for(var S=this.config.maxAudioFramesDrift,L=0,R=p+y;L<v.length;L++){var A=v[L],b=A.pts,k=b-R,D=Math.abs(1e3*k/s);if(k<=-S*d&&g)0===L&&(this.warn("Audio frame @ "+(b/s).toFixed(3)+"s overlaps marker by "+Math.round(1e3*k/s)+" ms."),this.nextAudioTs=p=b-y,R=b);else if(k>=S*d&&D<1e4&&g){var I=Math.round(k/d);for(R=b-I*d;R<0&&I&&d;)I--,R+=d;0===L&&(this.nextAudioTs=p=R-y),this.warn("Injecting "+I+" audio frames @ "+((R-y)/s).toFixed(3)+"s due to "+Math.round(1e3*k/s)+" ms gap.");for(var _=0;_<I;_++){var C=Va.getSilentFrame(e.parsedCodec||e.manifestCodec||e.codec,e.channelCount);C||(this.log("Unable to get silent frame for given audio codec; duplicating last frame instead."),C=A.unit.subarray()),v.splice(L,0,{unit:C,pts:R}),R+=d,L++}}A.pts=R,R+=d}for(var P,M=null,F=null,N=0,B=v.length;B--;)N+=v[B].unit.byteLength;for(var U=0,G=v.length;U<G;U++){var V=v[U],H=V.unit,K=V.pts;if(null!==F)c[U-1].duration=Math.round((K-F)/l);else{if(r&&"aac"===e.segmentCodec&&(K=p+y),M=K,!(N>0))return;N+=m;try{P=new Uint8Array(N)}catch(e){return void this.observer.emit(O.ERROR,O.ERROR,{type:x.MUX_ERROR,details:w.REMUX_ALLOC_ERROR,fatal:!1,chunkMeta:n,error:e,bytes:N,reason:"fail allocating audio mdat "+N})}f||(Ye(P,0,N),Ye(P,4,Ne.mdat))}P&&P.set(H,m);var Y=H.byteLength;m+=Y,c.push(tn(!0,u,Y,0)),F=K}var W=c.length;if(W){var j=c[c.length-1];p=F-y,this.nextAudioTs=p+l*j.duration;var q=f?new Uint8Array(0):$a.moof(e.sequenceNumber++,M/l,o({},e,{samples:c}));e.samples=[];var X=(M-y)/s,z=this.nextAudioTs/s,Q={data1:q,data2:P,startPTS:X,endPTS:z,startDTS:X,endDTS:z,type:"audio",hasAudio:!0,hasVideo:!1,nb:W};return this.isAudioContiguous=!0,Q}},t}(ie);function an(e,t){var r;if(null===t)return e;for(r=t<e?-en:en;Math.abs(e-t)>4294967296;)e+=r;return e}function nn(e,t,r,i){var a=e.samples.length;if(a){for(var n=e.inputTimeScale,s=0;s<a;s++){var o=e.samples[s];o.pts=an(o.pts-r.baseTime*n/r.timescale,t*n)/n,o.dts=an(o.dts-i.baseTime*n/i.timescale,t*n)/n}var l=e.samples;return e.samples=[],{samples:l}}}function sn(e,t,r){var i=e.samples.length;if(i){for(var a=e.inputTimeScale,n=0;n<i;n++){var s=e.samples[n];s.pts=an(s.pts-r.baseTime*a/r.timescale,t*a)/a}e.samples.sort((function(e,t){return e.pts-t.pts}));var o=e.samples;return e.samples=[],{samples:o}}}var on,ln=function(e){function t(t,r,i,a){var n;return(n=e.call(this,"passthrough-remuxer",a)||this).observer=void 0,n.emitInitSegment=!1,n.audioCodec=void 0,n.videoCodec=void 0,n.initData=void 0,n.initPTS=null,n.initTracks=void 0,n.lastEndTime=null,n.isVideoContiguous=!1,n.videoOnlyRemux=!1,n.decryptdata=null,n.pendingInitSegment=void 0,n.observer=t,n}u(t,e);var r=t.prototype;return r.destroy=function(){this.observer&&this.observer.removeAllListeners(),this.observer=null},r.resetTimeStamp=function(e){this.lastEndTime=null;var t=this.initPTS;t&&e&&t.baseTime===e.baseTime&&t.timescale===e.timescale||(this.initPTS=e)},r.resetNextTimestamp=function(){this.isVideoContiguous=!1,this.lastEndTime=null},r.resetInitSegment=function(e,t,r,i){this.audioCodec=t,this.videoCodec=r,this.decryptdata=i,this.pendingInitSegment=e,this.videoOnlyRemux=!1,this.initTracks=void 0,this.initData=void 0,this.emitInitSegment=!0},r.generateInitSegment=function(e,t,r){this.videoOnlyRemux=!1;var i=this.audioCodec,a=this.videoCodec;if(null==e||!e.byteLength)return this.initTracks=void 0,void(this.initData=void 0);var n=this.initData=r||Xe(e),s=n.audio,o=n.video;if(t)!function(e,t){if(e&&t){var r=t.keyId;16===(null==r?void 0:r.byteLength)&&t.isCommonEncryption&&et(e,(function(e,t){var i=e.subarray(8,24);i.some((function(e){return 0!==e}))||(fe.log("[eme] Patching keyId in 'enc"+(t?"a":"v")+">sinf>>tenc' box: "+xe(i)+" -> "+xe(r)),e.set(r,8))}))}}(e,t);else{var l=s||o;null!=l&&l.encrypted&&this.warn('Init segment with encrypted track with has no key ("'+l.codec+'")!')}s&&(i=dn(s,B.AUDIO,this)),o&&(a=dn(o,B.VIDEO,this));var u={};s&&o?u.audiovideo={container:"video/mp4",codec:i+","+a,supplemental:o.supplemental,encrypted:o.encrypted,initSegment:e,id:"main"}:s?u.audio={container:"audio/mp4",codec:i,encrypted:s.encrypted,initSegment:e,id:"audio"}:o?u.video={container:"video/mp4",codec:a,supplemental:o.supplemental,encrypted:o.encrypted,initSegment:e,id:"main"}:this.warn("initSegment does not contain moov or trak boxes."),this.initTracks=u},r.remux=function(e,t,r,i,a,n,s,o,l,u,d){var h,f,c=this.initPTS,g=this.lastEndTime,v={audio:void 0,video:void 0,text:void 0,id3:r,initSegment:void 0};_(g)||(g=this.lastEndTime=a||0);var m=t.samples;if(!m.length)return v;var p=this.pendingInitSegment;p&&(this.pendingInitSegment=void 0,this.generateInitSegment(p,this.decryptdata,u));var y={initPTS:void 0,timescale:void 0,trackId:void 0},E=this.initData;if(null!=(h=E)&&h.length||(this.generateInitSegment(m),E=this.initData),null==(f=E)||!f.length)return this.warn("Failed to generate initSegment."),v;this.emitInitSegment&&(y.tracks=this.initTracks,v.initSegment=y,this.emitInitSegment=!1);var T=d?d.tracks:function(e,t,r,i,a){return rt(e,t,i)}(m,E,0,this),S=E.audio?T[E.audio.id]:null,L=E.video?T[E.video.id]:null,R=!!E.audio,A=!!E.video,b="";R&&(b+="audio"),A&&(b+="video");var k,D,I=un(L,1/0),C=un(S,1/0),P=un(L,0,!0),x=un(S,0,!0);L&&S&&E.audio&&(C>P||I>x)&&this.warn("audio and video track sample timestamps do not overlap. v: "+I+"-"+P+" a: "+C+"-"+x+"}",L,S);var w=!!S&&(!L||!c&&C<I||!!c&&c.trackId===E.audio.id),O=w?S:L;if(!O)return this.log("No media samples found in "+o+" "+l.level+" "+(-1===l.part?"":"part: "+l.part+" of")+" sn: "+l.sn+" at playlist time: "+a),v;var M=m;D=w?x-C:P-I;var F=O.timescale,N=O.start-a*F,B=w?E.audio.id:E.video.id;if(k=O.start/F,(n||!c)&&(function(e,t,r,i){if(null===e)return!0;var a=Math.max(i,1),n=t-e.baseTime/e.timescale;return Math.abs(n-r)>=a}(c,k,a,D)||F!==c.timescale)){var U=!1,G=w?"audio":"video";if(c){var V=0!==a?k/a:1+k/1;U=k>=0&&Math.abs(1-V)<.001,this.log(G+" timestamps in track "+B+" at playlist time: "+(n?"":"~")+a+" maps to "+k+" with initPTS: "+c.baseTime/c.timescale+" ("+(N/F-c.baseTime/c.timescale)+"s diff) ("+b+") drift estimate: "+V+" "+(U?"(ignoring drift)":"remapping timestamps (initPTS)"))}U||(this.log("Found initPTS in "+G+" track "+B+" at playlist time: "+a+" offset: "+(k-a)+" ("+N+"/"+F+")"),c=null,y.initPTS=N,y.timescale=F,y.trackId=B)}c?(y.initPTS=c.baseTime,y.timescale=c.timescale,y.trackId=c.trackId):(y.timescale&&void 0!==y.trackId&&void 0!==y.initPTS||(this.warn("Could not set initPTS"),y.initPTS=k,y.timescale=1,y.trackId=-1),this.initPTS=c={baseTime:y.initPTS,timescale:y.timescale,trackId:y.trackId});var H=k-c.baseTime/c.timescale,K=H+D,Y=A&&void 0!==(null==O?void 0:O.ptsMin)?O.ptsMin/O.timescale-c.baseTime/c.timescale:H,W=A&&null!=O&&O.ptsMax?O.ptsMax/O.timescale-c.baseTime/c.timescale:K;D>0?this.lastEndTime=K:(this.warn("Duration parsed from mp4 should be greater than zero"),this.resetNextTimestamp());var j={data1:M,data2:void 0,startPTS:Y,startDTS:H,endPTS:W,endDTS:K,type:b,hasAudio:R,hasVideo:A,nb:1,dropped:0,encrypted:!!E.audio&&E.audio.encrypted||!!E.video&&E.video.encrypted};v.audio=R&&!A?j:void 0,v.video=A?j:void 0;var q=this.isVideoContiguous,X=null==L?void 0:L.sampleCount;if(X){var z=L.keyFrameIndex,Q=-1!==z;j.nb=X,j.dropped=0===z||q?0:Q?z:X,j.independent=Q,j.firstKeyFrame=z,Q&&L.keyFrameStart&&(j.firstKeyFramePTS=(L.keyFrameStart-c.baseTime)/c.timescale),q||(v.independent=Q),this.isVideoContiguous||(this.isVideoContiguous=Q),j.dropped&&this.warn("fmp4 does not start with IDR: firstIDR "+z+"/"+X+" dropped: "+j.dropped+" start: "+(j.firstKeyFramePTS||"NA"))}return v.initSegment=y,v.id3=nn(r,a,c,c),i.samples.length&&(v.text=sn(i,a,c)),v},t}(ie);function un(e,t,r){return void 0===r&&(r=!1),void 0!==(null==e?void 0:e.start)?(e.start+(r?e.duration:0))/e.timescale:t}function dn(e,t,r){var i=e.codec;return i&&i.length>4?i:t===B.AUDIO?"ec-3"===i||"ac-3"===i||"alac"===i?i:"fLaC"===i||"Opus"===i?Rt(i,!1):(r.warn('Unhandled audio codec "'+i+'" in mp4 MAP'),i||"mp4a"):"mjpg"===i?i:(r.warn('Unhandled video codec "'+i+'" in mp4 MAP'),i||"avc1")}try{on=self.performance.now.bind(self.performance)}catch(e){on=Date.now}var hn=[{demux:ka,remux:ln},{demux:xa,remux:rn},{demux:Ra,remux:rn},{demux:Aa,remux:rn}],fn=function(){function e(e,t,r,i,a,n){this.asyncResult=!1,this.logger=void 0,this.observer=void 0,this.typeSupported=void 0,this.config=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.id=a,this.logger=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 a=this;if(!this.observer)return cn(r);var n=r.transmuxing;n.executeStart=on();var s=new Uint8Array(e),o=this.currentTransmuxState,l=this.transmuxConfig;i&&(this.currentTransmuxState=i);var u=i||o,d=u.contiguous,h=u.discontinuity,f=u.trackSwitch,c=u.accurateTimeOffset,g=u.timeOffset,v=u.initSegmentChange,m=l.audioCodec,p=l.videoCodec,y=l.defaultInitPts,E=l.duration,T=l.initSegmentData,S=function(e,t){var r=null;return e.byteLength>0&&null!=(null==t?void 0:t.key)&&null!==t.iv&&null!=t.method&&(r=t),r}(s,t);if(S&&Or(S.method)){var L=this.getDecrypter(),R=Mr(S.method);return this.asyncResult=!0,this.decryptionPromise=L.decrypt(s,S.key.buffer,S.iv.buffer,R,r.decryptRange).then((function(e){var t=a.push(e,null,r);return a.decryptionPromise=null,t})),this.decryptionPromise}var A=this.needsProbing(h,f);if(A){var b=r.iframe&&T&&xa.probe(T,this.logger)?T:s,k=this.configureTransmuxer(b);if(k)return this.logger.warn("[transmuxer] "+k.message),this.observer.emit(O.ERROR,O.ERROR,{type:x.MEDIA_ERROR,details:w.FRAG_PARSING_ERROR,fatal:!1,error:k,reason:k.message}),n.executeEnd=on(),cn(r)}(h||f||v||A)&&this.resetInitSegment(T,m,p,E,t,r),(h||v||A)&&this.resetInitialTimestamp(y),d||this.resetContiguity();var D=this.transmux(s,S,g,c,r);this.asyncResult=gn(D);var I=this.currentTransmuxState;return I.contiguous=!0,I.discontinuity=!1,I.trackSwitch=!1,n.executeEnd=on(),D},t.flush=function(e){var t=this,r=e.transmuxing;r.executeStart=on();var i=this.decrypter,a=this.currentTransmuxState,n=this.decryptionPromise;if(n)return this.asyncResult=!0,n.then((function(){return t.flush(e)}));var s=[],o=a.timeOffset;if(i){var l=i.flush();l&&s.push(this.push(l.buffer,null,e))}var u=this.demuxer,d=this.remuxer;if(!u||!d){r.executeEnd=on();var h=[cn(e)];return this.asyncResult?Promise.resolve(h):h}var f=u.flush(o,e);return gn(f)?(this.asyncResult=!0,f.then((function(r){return t.flushRemux(s,r,e),s}))):(this.flushRemux(s,f,e),this.asyncResult?Promise.resolve(s):s)},t.flushRemux=function(e,t,r){if(this.remuxer){var i=t.audioTrack,a=t.videoTrack,n=t.id3Track,s=t.textTrack,o=this.currentTransmuxState,l=o.accurateTimeOffset,u=o.timeOffset;this.logger.log("[transmuxer.ts]: Flushed "+this.id+" sn: "+r.sn+(r.part>-1?" part: "+r.part:"")+" of "+this.id+" playlist "+r.level);var d=this.remuxer.remux(i,a,n,s,u,l,!0,this.id,r,t.initData,t.sampleData);e.push({remuxResult:d,chunkMeta:r}),r.transmuxing.executeEnd=on()}},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,a,n){var s=this.demuxer,o=this.remuxer;s&&o&&(s.resetInitSegment(e,t,r,i,a,n),o.resetInitSegment(e,t,r,a))},t.destroy=function(){this.demuxer&&(this.demuxer.destroy(),this.demuxer=void 0),this.remuxer&&(this.remuxer.destroy(),this.remuxer=void 0),this.observer&&this.observer.removeAllListeners(),this.decryptionPromise=null,this.decrypter=this.observer=void 0,this.logger=this.config=this.probe=void 0},t.transmux=function(e,t,r,i,a){return"SAMPLE-AES"===(null==t?void 0:t.method)?this.transmuxSampleAes(e,t,r,i,a):this.transmuxUnencrypted(e,r,i,a)},t.transmuxUnencrypted=function(e,t,r,i){var a=this.demuxer,n=this.remuxer;if(!a||!n)return cn(i);var s=a.demux(e,t,i,!1,!this.config.progressive),o=s.audioTrack,l=s.videoTrack,u=s.id3Track,d=s.textTrack;return{remuxResult:n.remux(o,l,u,d,t,r,!1,this.id,i,s.initData,s.sampleData),chunkMeta:i}},t.transmuxSampleAes=function(e,t,r,i,a){var n=this;return this.demuxer?this.demuxer.demuxSampleAes(e,t,r,a).then((function(e){return{remuxResult:n.remuxer.remux(e.audioTrack,e.videoTrack,e.id3Track,e.textTrack,r,i,!1,n.id,a,e.initData,e.sampleData),chunkMeta:a}})):Promise.reject(new Error("no demuxer"))},t.configureTransmuxer=function(e){var t=this.config,r=this.observer,i=this.typeSupported;if(r){var a;try{for(var n=0,s=hn.length;n<s;n++){var o;if(null!=(o=hn[n].demux)&&o.probe(e,this.logger)){a=hn[n];break}}}catch(e){return e}if(!a)return new Error("Failed to find demuxer by probing fragment data");var l=this.demuxer,u=this.remuxer,d=a.demux,h=a.remux;l&&l instanceof d||(this.logger.log("Using "+d.name),this.demuxer=new d(r,t,i,this.logger),this.probe=d.probe),u&&u instanceof h||(this.logger.log("Using "+h.name),this.remuxer=new h(r,t,i,this.logger))}},t.needsProbing=function(e,t){return!this.demuxer||!this.remuxer||e||t},t.getDecrypter=function(){var e=this.decrypter;return e||(e=this.decrypter=new _i(this.config)),e},e}(),cn=function(e){return{remuxResult:{},chunkMeta:e}};function gn(e){return"then"in e&&e.then instanceof Function}var vn=function(e,t,r,i,a){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=a||null},mn=function(e,t,r,i,a,n){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=a,this.initSegmentChange=n},pn=[];function yn(e,t,r){if(!((i=t.remuxResult).audio||i.video||i.text||i.id3||i.initSegment))return!1;var i,a=[],n=t.remuxResult,s=n.audio,o=n.video;return s&&En(a,s),o&&En(a,o),e.postMessage({event:"transmuxComplete",data:t,instanceNo:r},a),!0}function En(e,t){t.data1&&e.push(t.data1.buffer),t.data2&&e.push(t.data2.buffer)}function Tn(e,t,r,i){t.reduce((function(t,r){return yn(e,r,i)||t}),!1)||e.postMessage({event:"transmuxComplete",data:t[0],instanceNo:i}),e.postMessage({event:"flush",data:r,instanceNo:i})}function Sn(e,t,r){self.postMessage({event:e,data:t,instanceNo:r})}void 0!==t&&t&&self.addEventListener("message",(function(e){var t=e.data,r=t.instanceNo;if(void 0!==r){var i=pn[r];if("reset"===t.cmd&&(delete pn[t.resetNo],i&&i.destroy(),t.cmd="init"),"init"===t.cmd){var a=JSON.parse(t.config),n=new k;n.on(O.FRAG_DECRYPTED,(function(e,t){return Sn(e,t,r)})),n.on(O.ERROR,(function(e,t){return Sn(e,t,r)}));var s=ue(a.debug,t.id);return function(e,t){var r=function(r){e[r]=function(){var e=Array.prototype.join.call(arguments," ");Sn("workerLog",{logType:r,message:e},t)}};for(var i in e)r(i)}(s,r),pn[r]=new fn(n,t.typeSupported,a,"",t.id,s),void Sn("init",null,r)}if(i)switch(t.cmd){case"configure":i.configure(t.config);break;case"demux":var o=i.push(t.data,t.decryptdata,t.chunkMeta,t.state);gn(o)?o.then((function(e){yn(self,e,r)})).catch((function(e){Sn(O.ERROR,{instanceNo:r,type:x.MEDIA_ERROR,details:w.FRAG_PARSING_ERROR,chunkMeta:t.chunkMeta,fatal:!1,error:e,err:e,reason:"transmuxer-worker push error"},r)})):yn(self,o,r);break;case"flush":var l=t.chunkMeta,u=i.flush(l);gn(u)?u.then((function(e){Tn(self,e,l,r)})).catch((function(e){Sn(O.ERROR,{type:x.MEDIA_ERROR,details:w.FRAG_PARSING_ERROR,chunkMeta:t.chunkMeta,fatal:!1,error:e,err:e,reason:"transmuxer-worker flush error"},r)})):Tn(self,u,l,r)}}}));var Ln="1.7.3",Rn={},An=0,bn=function(){function t(t,r,i,a){var n=this;this.error=null,this.hls=void 0,this.id=void 0,this.instanceNo=An++,this.observer=void 0,this.frag=null,this.part=null,this.useWorker=void 0,this.workerContext=null,this.transmuxer=null,this.onTransmuxComplete=void 0,this.onFlush=void 0,this.onWorkerMessage=function(e){var t=e.data,r=n.hls;if(r&&null!=t&&t.event&&t.instanceNo===n.instanceNo)switch(t.event){case"init":var i,a=null==(i=n.workerContext)?void 0:i.objectURL;a&&self.URL.revokeObjectURL(a);break;case"transmuxComplete":n.handleTransmuxComplete(t.data);break;case"flush":n.onFlush(t.data);break;case"workerLog":r.logger[t.data.logType]&&r.logger[t.data.logType](t.data.message);break;default:t.data=t.data||{},t.data.frag=n.frag,t.data.part=n.part,t.data.id=n.id,r.trigger(t.event,t.data)}},this.onWorkerError=function(e){if(n.hls){var t=new Error(e.message+" ("+e.filename+":"+e.lineno+")");n.hls.config.enableWorker=!1,n.hls.logger.warn('Error in "'+n.id+'" Web Worker, fallback to inline'),n.hls.trigger(O.ERROR,{type:x.OTHER_ERROR,details:w.INTERNAL_EXCEPTION,fatal:!1,event:"demuxerWorker",error:t})}};var s=t.config;this.hls=t,this.id=r,this.useWorker=!!s.enableWorker,this.onTransmuxComplete=i,this.onFlush=a;var o=function(e,t){(t=t||{}).frag=n.frag||void 0,e===O.ERROR&&(t.parent=n.id,t.part=n.part,n.error=t.error),n.hls.trigger(e,t)};this.observer=new k,this.observer.on(O.FRAG_DECRYPTED,o),this.observer.on(O.ERROR,o);var l=kt(s.preferManagedMediaSource);if(this.useWorker&&"undefined"!=typeof Worker){var u=this.hls.logger;s.workerPath;try{s.workerPath?(u.log("loading Web Worker "+s.workerPath+' for "'+r+'"'),this.workerContext=function(e){var t=Rn[e];if(t)return t.clientCount++,t;var r=new self.URL(e,self.location.href).href,i={worker:new self.Worker(r),scriptURL:r,clientCount:1};return Rn[e]=i,i}(s.workerPath)):(u.log('injecting Web Worker for "'+r+'"'),this.workerContext=function(){var t=Rn[Ln];if(t)return t.clientCount++,t;var r=new self.Blob(["var exports={};var module={exports:exports};function define(f){f()};define.amd=true;("+e.toString()+")(true);"],{type:"text/javascript"}),i=self.URL.createObjectURL(r),a={worker:new self.Worker(i),objectURL:i,clientCount:1};return Rn[Ln]=a,a}());var d=this.workerContext.worker;d.addEventListener("message",this.onWorkerMessage),d.addEventListener("error",this.onWorkerError),d.postMessage({instanceNo:this.instanceNo,cmd:"init",typeSupported:l,id:r,config:Bt(s)})}catch(e){u.warn('Error setting up "'+r+'" Web Worker, fallback to inline',e),this.terminateWorker(),this.error=null,this.transmuxer=new fn(this.observer,l,s,"",r,t.logger)}}else this.transmuxer=new fn(this.observer,l,s,"",r,t.logger)}var r=t.prototype;return r.reset=function(){if(this.frag=null,this.part=null,this.workerContext){var e=this.instanceNo;this.instanceNo=An++;var t=this.hls.config,r=kt(t.preferManagedMediaSource);this.workerContext.worker.postMessage({instanceNo:this.instanceNo,cmd:"reset",resetNo:e,typeSupported:r,id:this.id,config:Bt(t)})}},r.terminateWorker=function(){if(this.workerContext){var e=this.workerContext.worker;this.workerContext=null,e.removeEventListener("message",this.onWorkerMessage),e.removeEventListener("error",this.onWorkerError),function(e){var t=Rn[e||Ln];if(t&&1==t.clientCount--){var r=t.worker,i=t.objectURL;delete Rn[e||Ln],i&&self.URL.revokeObjectURL(i),r.terminate()}}(this.hls.config.workerPath)}},r.destroy=function(){if(this.workerContext)this.terminateWorker(),this.onWorkerMessage=this.onWorkerError=null;else{var e=this.transmuxer;e&&(e.destroy(),this.transmuxer=null)}var t=this.observer;t&&t.removeAllListeners(),this.frag=null,this.part=null,this.observer=null,this.hls=null},r.push=function(e,t,r,i,a,n,s,o,l,u){var d,h,f=this;l.transmuxing.start=self.performance.now();var c=this.instanceNo,g=this.transmuxer,v=n?n.start:a.start,m=a.decryptdata,p=this.frag,y=!p||a.cc!==p.cc,E=!p||l.level!==p.level,T=p?l.sn-p.sn:-1,S=this.part?l.part-this.part.index:-1,L=0===T&&l.id>1&&l.id===(null==p?void 0:p.stats.chunkCount),R=!E&&(1===T||0===T&&(1===S||L&&S<=0)),A=self.performance.now();(E||T||0===a.stats.parsing.start)&&(a.stats.parsing.start=A),!n||!S&&R||(n.stats.parsing.start=A);var b=!(p&&(null==(d=a.initSegment)?void 0:d.url)===(null==(h=p.initSegment)?void 0:h.url)),k=new mn(y,R,o,E,v,b);if(!R||y||b){this.hls.logger.log("[transmuxer-interface]: Starting new transmux session for "+a.type+" sn: "+l.sn+(l.part>-1?" part: "+l.part:"")+" "+this.id+" playlist: "+l.level+" id: "+l.id+"\n discontinuity: "+y+"\n trackSwitch: "+E+"\n contiguous: "+R+"\n accurateTimeOffset: "+o+"\n timeOffset: "+v+"\n initSegmentChange: "+b);var D=new vn(r,i,t,s,u);this.configureTransmuxer(D)}if(this.frag=a,this.part=n,this.workerContext)this.workerContext.worker.postMessage({instanceNo:c,cmd:"demux",data:e,decryptdata:m,chunkMeta:l,state:k},e instanceof ArrayBuffer?[e]:[]);else if(g){var I=g.push(e,m,l,k);gn(I)?I.then((function(e){f.handleTransmuxComplete(e)})).catch((function(e){f.transmuxerError(e,l,"transmuxer-interface push error")})):this.handleTransmuxComplete(I)}},r.flush=function(e){var t=this;e.transmuxing.start=self.performance.now();var r=this.instanceNo,i=this.transmuxer;if(this.workerContext)this.workerContext.worker.postMessage({instanceNo:r,cmd:"flush",chunkMeta:e});else if(i){var a=i.flush(e);gn(a)?a.then((function(r){t.handleFlushResult(r,e)})).catch((function(r){t.transmuxerError(r,e,"transmuxer-interface flush error")})):this.handleFlushResult(a,e)}},r.transmuxerError=function(e,t,r){this.hls&&(this.error=e,this.hls.trigger(O.ERROR,{type:x.MEDIA_ERROR,details:w.FRAG_PARSING_ERROR,chunkMeta:t,frag:this.frag||void 0,part:this.part||void 0,fatal:!1,error:e,err:e,reason:r}))},r.handleFlushResult=function(e,t){var r=this;e.forEach((function(e){r.handleTransmuxComplete(e)})),this.onFlush(t)},r.configureTransmuxer=function(e){var t=this.instanceNo,r=this.transmuxer;this.workerContext?this.workerContext.worker.postMessage({instanceNo:t,cmd:"configure",config:e}):r&&r.configure(e)},r.handleTransmuxComplete=function(e){e.chunkMeta.transmuxing.end=self.performance.now(),this.onTransmuxComplete(e)},t}();function kn(){return self.SourceBuffer||self.WebKitSourceBuffer}function Dn(){if(!ve())return!1;var e=kn();return!e||e.prototype&&"function"==typeof e.prototype.appendBuffer&&"function"==typeof e.prototype.remove}var In=0,_n=1,Cn=2,Pn=function(e){function t(t,r,i){var a;return(a=e.call(this,t,r,i,"stream-controller",N.MAIN)||this).audioCodecSwap=!1,a.level=-1,a._forceStartLoad=!1,a._hasEnoughToStart=!1,a.altAudio=In,a.audioOnly=!1,a._couldBacktrack=!1,a._backtrackFragment=void 0,a.audioCodecSwitch=!1,a.videoBuffer=null,a.onMediaPlaying=function(){a.tick()},a.onMediaSeeked=function(){var e=a.media,t=e?e.currentTime:null;if(null!==t&&_(t)&&(a.log("Media seeked to "+t),a.getBufferedFrag(t))){var r=a.getFwdBufferInfoAtPos(e,t,N.MAIN,0);r&&0!==r.len?a.tick():a.log("Main buffer empty at "+t+' on "seeked" event: '+(r?r.len:r))}},a.registerListeners(),a}u(t,e);var r=t.prototype;return r.registerListeners=function(){e.prototype.registerListeners.call(this);var t=this.hls;t.on(O.MANIFEST_PARSED,this.onManifestParsed,this),t.on(O.LEVEL_LOADING,this.onLevelLoading,this),t.on(O.LEVEL_LOADED,this.onLevelLoaded,this),t.on(O.FRAG_LOAD_EMERGENCY_ABORTED,this.onFragLoadEmergencyAborted,this),t.on(O.AUDIO_TRACK_SWITCHING,this.onAudioTrackSwitching,this),t.on(O.AUDIO_TRACK_SWITCHED,this.onAudioTrackSwitched,this),t.on(O.BUFFER_CREATED,this.onBufferCreated,this),t.on(O.BUFFER_FLUSHED,this.onBufferFlushed,this),t.on(O.LEVELS_UPDATED,this.onLevelsUpdated,this),t.on(O.FRAG_BUFFERED,this.onFragBuffered,this)},r.unregisterListeners=function(){e.prototype.unregisterListeners.call(this);var t=this.hls;t.off(O.MANIFEST_PARSED,this.onManifestParsed,this),t.off(O.LEVEL_LOADED,this.onLevelLoaded,this),t.off(O.FRAG_LOAD_EMERGENCY_ABORTED,this.onFragLoadEmergencyAborted,this),t.off(O.AUDIO_TRACK_SWITCHING,this.onAudioTrackSwitching,this),t.off(O.AUDIO_TRACK_SWITCHED,this.onAudioTrackSwitched,this),t.off(O.BUFFER_CREATED,this.onBufferCreated,this),t.off(O.BUFFER_FLUSHED,this.onBufferFlushed,this),t.off(O.LEVELS_UPDATED,this.onLevelsUpdated,this),t.off(O.FRAG_BUFFERED,this.onFragBuffered,this)},r.onHandlerDestroying=function(){this.onMediaPlaying=this.onMediaSeeked=null,this.unregisterListeners(),e.prototype.onHandlerDestroying.call(this)},r.startLoad=function(e,t){if(this.levels){var r=this.lastCurrentTime,i=this.hls;if(this.stopLoad(),this.setInterval(100),this.level=-1,!this.startFragRequested){var a=i.startLevel;-1===a&&(i.config.testBandwidth&&this.levels.length>1?(a=0,this.bitrateTest=!0):a=i.firstAutoLevel),i.nextLoadLevel=a,this.level=i.loadLevel,this._hasEnoughToStart=!!t}r>0&&-1===e&&!t&&this.initPTS.length&&(this.log("Override startPosition with lastCurrentTime @"+r),e=r),this.state=Hi.IDLE,this.nextLoadPosition=this.lastCurrentTime=e+this.timelineOffset,this.startPosition=t?-1:e,t||this.fragmentTracker.hasFragments(this.playlistType)||(this._hasEnoughToStart=this.startFragRequested=!1),this.tick()}else this._forceStartLoad=!0,this.state=Hi.STOPPED},r.stopLoad=function(){this._forceStartLoad=!1,e.prototype.stopLoad.call(this)},r.doTick=function(){switch(this.state){case Hi.WAITING_LEVEL:var e=this.levels,t=this.level,r=null==e?void 0:e[t],i=null==r?void 0:r.details;if(i&&(!i.live||this.levelLastLoaded===r&&!this.waitForLive(r))){if(this.waitForCdnTuneIn(i))break;this.state=Hi.IDLE;break}if(this.hls.nextLoadLevel!==this.level){this.state=Hi.IDLE;break}break;case Hi.FRAG_LOADING_WAITING_RETRY:this.checkRetryDate()}this.state===Hi.IDLE&&this.doTickIdle(),this.onTickEnd()},r.onTickEnd=function(){var t;e.prototype.onTickEnd.call(this),null!=(t=this.media)&&t.readyState&&!1===this.media.seeking&&(this.lastCurrentTime=this.media.currentTime),this.checkFragmentChanged()},r.doTickIdle=function(){var e=this.hls,t=this.levelLastLoaded,r=this.levels,i=this.media;if(null!==t&&(i||this.primaryPrefetch||!this.startFragRequested&&e.config.startFragPrefetch)&&(!this.altAudio||!this.audioOnly)){var a=this.buffering?e.nextLoadLevel:e.loadLevel;if(null!=r&&r[a]){var n=r[a],s=this.getMainFwdBufferInfo();if(null!==s){var o=this.getLevelDetails();if(o&&this._streamEnded(s,o)){var l={type:this.targetBufferType()};return this.hls.trigger(O.BUFFER_EOS,l),void(this.state=Hi.ENDED)}if(this.buffering){e.loadLevel!==a&&-1===e.manualLevel&&this.log("Adapting to level "+a+" from level "+this.level),this.level=e.nextLoadLevel=a;var u=n.details;if(!u||this.state===Hi.WAITING_LEVEL||this.waitForLive(n))return this.level=a,this.state=Hi.WAITING_LEVEL,void(this.startFragRequested=!1);var d=s.len,h=this.getMaxBufferLength(n.maxBitrate);if(!(d>=h)){this.backtrackFragment&&this.backtrackFragment.start>s.end&&(this.backtrackFragment=void 0);var f=this.backtrackFragment?this.backtrackFragment.start:s.end,c=this.getNextFragment(f,u);if(this.couldBacktrack&&!this.fragPrevious&&c&&G(c)&&this.fragmentTracker.getState(c)!==Si){var g,v=(null!=(g=this.backtrackFragment)?g:c).sn-u.startSN,m=u.fragments[v-1];m&&c.cc===m.cc&&(c=m,this.fragmentTracker.removeFragment(m))}else this.backtrackFragment&&s.len&&(this.backtrackFragment=void 0);if(c&&this.isLoopLoading(c,f)){if(!c.gap){var p=this.audioOnly&&!this.altAudio?B.AUDIO:B.VIDEO,y=(p===B.VIDEO?this.videoBuffer:this.mediaBuffer)||this.media;y&&this.afterBufferFlushed(y,p)}c=this.getNextFragmentLoopLoading(c,u,s,N.MAIN,h)}c&&(this.exceedsMaxBuffer(s,h,c)||this.loadFragment(c,n,f))}}}}}},r.loadFragment=function(t,r,i){var a=this.fragmentTracker.getState(t);a===yi||a===Ti?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,r)):e.prototype.loadFragment.call(this,t,r,i):this.clearTrackerIfNeeded(t)},r.immediateLevelSwitch=function(){var t;this.abortCurrentFrag(),this.flushMainBuffer(0,Number.POSITIVE_INFINITY),this.altAudio!==In&&((null==(t=this.getLevelDetails())?void 0:t.fragmentStart)||0)>this.lastCurrentTime&&e.prototype.flushMainBuffer.call(this,0,Number.POSITIVE_INFINITY,"audio")},r.getBufferOutput=function(){return this.mediaBuffer&&this.altAudio===Cn?this.mediaBuffer:this.media},r.checkFragmentChanged=function(){var e=this.fragPlaying;if(!this.checkFragPlaying())return!1;var t=this.fragPlaying;if(t){var r=t.level;this.hls.trigger(O.FRAG_CHANGED,{frag:t,previousFrag:e}),(null==e?void 0:e.level)!==r&&this.hls.trigger(O.LEVEL_SWITCHED,{level:r})}return!0},r.abortCurrentFrag=function(){this.backtrackFragment=void 0,e.prototype.abortCurrentFrag.call(this)},r.flushMainBuffer=function(t,r){e.prototype.flushMainBuffer.call(this,t,r,this.targetBufferType())},r.targetBufferType=function(){return this.altAudio!==Cn||this.audioOnly?null:"video"},r.onMediaAttached=function(t,r){e.prototype.onMediaAttached.call(this,t,r);var i=r.media;hr(i,"playing",this.onMediaPlaying),hr(i,"seeked",this.onMediaSeeked)},r.onMediaDetaching=function(t,r){var i=this.media;i&&(fr(i,"playing",this.onMediaPlaying),fr(i,"seeked",this.onMediaSeeked)),this.videoBuffer=null,e.prototype.onMediaDetaching.call(this,t,r),r.transferMedia||(this._hasEnoughToStart=!1)},r.onManifestLoading=function(){e.prototype.onManifestLoading.call(this),this.log("Trigger BUFFER_RESET"),this.hls.trigger(O.BUFFER_RESET,void 0),this.couldBacktrack=!1,this.backtrackFragment=void 0,this.altAudio=In,this.audioOnly=!1},r.onManifestParsed=function(e,t){for(var r,i,a=!1,n=!1,s=0;s<t.levels.length;s++){var o=t.levels[s].audioCodec;o&&(a=a||-1!==o.indexOf("mp4a.40.2"),n=n||-1!==o.indexOf("mp4a.40.5"))}this.audioCodecSwitch=a&&n&&!("function"==typeof(null==(i=kn())||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=t.levels,this.startFragRequested=!1},r.onLevelLoading=function(e,t){if(this.levels&&this.state===Hi.IDLE){var r=t.levelInfo;(!r.details||r.details.live&&(this.levelLastLoaded!==r||r.details.expired)||this.waitForCdnTuneIn(r.details))&&(this.state=Hi.WAITING_LEVEL)}},r.onLevelLoaded=function(e,t){var r,i=this,a=this.levels,n=this.startFragRequested,s=t.level,o=t.details,l=o.totalduration;if(a){this.log("Level "+s+" loaded ["+o.startSN+","+o.endSN+"]"+(o.lastPartSn?"[part-"+o.lastPartSn+"-"+o.lastPartIndex+"]":"")+", cc ["+o.startCC+", "+o.endCC+"] duration:"+l);var u=t.levelInfo,d=this.fragCurrent;!d||this.state!==Hi.FRAG_LOADING&&this.state!==Hi.FRAG_LOADING_WAITING_RETRY||d.level!==t.level&&d.loader&&this.abortCurrentFrag();var h=0;if(o.live||null!=(r=u.details)&&r.live){var f;if(this.checkLiveUpdate(o),o.deltaUpdateFailed)return;h=this.alignPlaylists(o,u.details,null==(f=this.levelLastLoaded)?void 0:f.details)}if(u.details=o,this.levelLastLoaded=u,n||this.setStartPosition(o,h),this.hls.trigger(O.LEVEL_UPDATED,{details:o,level:s}),this.state===Hi.WAITING_LEVEL){if(this.waitForCdnTuneIn(o))return;this.state=Hi.IDLE}n&&o.live&&this.synchronizeToLiveEdge(o),this.initPTS.some((function(e,t){if(t>=o.startCC)return!0;delete i.initPTS[t]})),this.tick()}else this.warn("Levels were reset while loading level "+s)},r.synchronizeToLiveEdge=function(e){var t=this.config,r=this.media;if(r){var i=this.hls.liveSyncPosition,a=this.playhead,n=e.fragmentStart,s=e.edge,o=a>=n-t.maxFragLookUpTolerance&&a<=s;if(null!==i&&r.duration>i&&(a<i||!o)){var l=void 0!==t.liveMaxLatencyDuration?t.liveMaxLatencyDuration:t.liveMaxLatencyDurationCount*e.targetduration;if((!o&&r.readyState<4||a<s-l)&&(this._hasEnoughToStart||(this.nextLoadPosition=i),r.readyState))if(this.warn("Playback: "+a+" is located too far from the end of live sliding playlist: "+s+", reset currentTime to : "+i),"buffered"===this.config.liveSyncMode){var u,d=dr.bufferInfo(r,i,0);if(null==(u=d.buffered)||!u.length)return void(r.currentTime=i);if(d.start<=a)return void(r.currentTime=i);var h=dr.bufferedInfo(d.buffered,a,0).nextStart;h&&(r.currentTime=h)}else r.currentTime=i}}},r._handleFragmentLoadProgress=function(e){var t,r=e.frag,i=e.part,a=e.payload,n=this.levels;if(n){var s=n[r.level];if(s){var 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,d=null==(t=r.initSegment)?void 0:t.data,h=this._getAudioCodec(s),f=this.transmuxer=this.transmuxer||new bn(this.hls,N.MAIN,this._handleTransmuxComplete.bind(this),this._handleTransmuxerFlush.bind(this)),c=i?i.index:-1,g=-1!==c,v=wi(r,i,this.iframesOnly),m=v.decryptRange,p=v.byteRange,y=new Ni(r.level,r.sn,r.stats.chunkCount,p?p.end-p.start:a.byteLength,c,g,r.duration,this.iframesOnly,m),E=this.initPTS[r.cc];f.push(a,d,h,l,r,i,o.totalduration,u,y,E)}else this.warn("Level "+r.level+" not found on progress")}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(e,t){var r=this,i=this.hls,a=this.altAudio!==In;if(Vt(t.url,i))this.altAudio=_n;else{if(this.mediaBuffer!==this.media){this.log("Switching on main audio, use media.buffered to schedule main fragment loading"),this.mediaBuffer=this.media;var n=this.fragCurrent;n&&(this.log("Switching to main audio track, cancel main fragment load"),n.abortRequests(),this.fragmentTracker.removeFragment(n)),this.resetTransmuxer(),this.resetLoadingState()}else this.audioOnly&&this.resetTransmuxer();if(a)return this.altAudio=In,this.fragmentTracker.removeAllFragments(),i.once(O.BUFFER_FLUSHED,(function(){r.hls&&r.hls.trigger(O.AUDIO_TRACK_SWITCHED,t)})),void i.trigger(O.BUFFER_FLUSHING,{startOffset:0,endOffset:Number.POSITIVE_INFINITY,type:null});i.trigger(O.AUDIO_TRACK_SWITCHED,t)}},r.onAudioTrackSwitched=function(e,t){var r=Vt(t.url,this.hls);if(r){var i=this.videoBuffer;i&&this.mediaBuffer!==i&&(this.log("Switching on alternate audio, use video.buffered to schedule main fragment loading"),this.mediaBuffer=i)}this.altAudio=r?Cn:In,this.tick()},r.onBufferCreated=function(e,t){var r,i,a=t.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},r.onFragBuffered=function(e,t){var r=t.frag,i=t.part,a=r.type===N.MAIN;if(a){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===Hi.PARSED&&(this.state=Hi.IDLE));var n=!1;if(G(r)&&(n=!!r.gap&&!r.tagList.some((function(e){return"GAP"===e[0]}))),this.fragBufferedComplete(r,i),n){var s;r.stats.retry++;var o=null==(s=this.levels)?void 0:s[r.level];o&&o.fragmentError++}}var l=this.media;l&&(!this._hasEnoughToStart&&dr.getBuffered(l).length&&(this._hasEnoughToStart=!0,this.seekToStartPos()),a&&this.tick())},r.onError=function(e,t){var r;if(t.fatal)this.state=Hi.ERROR;else switch(t.details){case w.FRAG_GAP:case w.FRAG_PARSING_ERROR:case w.FRAG_DECRYPT_ERROR:case w.FRAG_LOAD_ERROR:case w.FRAG_LOAD_TIMEOUT:case w.KEY_LOAD_ERROR:case w.KEY_LOAD_TIMEOUT:this.onFragmentOrKeyLoadError(N.MAIN,t);break;case w.LEVEL_LOAD_ERROR:case w.LEVEL_LOAD_TIMEOUT:case w.LEVEL_PARSING_ERROR:t.levelRetry||this.state!==Hi.WAITING_LEVEL||(null==(r=t.context)?void 0:r.type)!==F.LEVEL||(this.state=Hi.IDLE);break;case w.BUFFER_APPEND_NO_PROGRESS:if("main"!==t.parent)return;t.frag&&(t.appendsWithoutProgress||0)>=this.config.appendErrorMaxRetry&&(this.warn("Marking fragment "+t.frag.sn+" of level "+t.frag.level+" as a gap after "+t.appendsWithoutProgress+" appends without buffered range growth, to prevent loop loading"),this.fragmentTracker.addAsGap(t.frag));break;case w.BUFFER_ADD_CODEC_ERROR:case w.BUFFER_APPEND_ERROR:if("main"!==t.parent)return;this.reduceLengthAndFlushBuffer(t)&&this.resetLoadingState();break;case w.BUFFER_FULL_ERROR:if("main"!==t.parent)return;this.reduceLengthAndFlushBuffer(t)&&(!this.config.interstitialsController&&this.config.assetPlayerId?this._hasEnoughToStart=!0:this.flushMainBuffer(0,Number.POSITIVE_INFINITY));break;case w.INTERNAL_EXCEPTION:this.recoverWorkerError(t)}},r.onFragLoadEmergencyAborted=function(){this.state=Hi.IDLE,this._hasEnoughToStart||(this.startFragRequested=!1,this.nextLoadPosition=this.lastCurrentTime),this.tickImmediate()},r.onBufferFlushed=function(e,t){var r=t.type;if(r!==B.AUDIO||!this.altAudio){var i=(r===B.VIDEO?this.videoBuffer:this.mediaBuffer)||this.media;i&&(this.afterBufferFlushed(i,r),this.tick())}},r.onLevelsUpdated=function(e,t){this.level>-1&&this.fragCurrent&&(this.level=this.fragCurrent.level,-1===this.level&&this.resetWhenMissingContext(this.fragCurrent)),this.levels=t.levels},r.swapAudioCodec=function(){this.audioCodecSwap=!this.audioCodecSwap},r.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=this.timelineOffset;i&&r&&(r+=i);var a=this.getLevelDetails(),n=dr.getBuffered(e),s=n.length?n.start(0):0,o=s-r,l=Math.max(this.config.maxBufferHole,this.config.maxFragLookUpTolerance);(this.config.startOnSegmentBoundary||o>0&&(o<l||this.loadingParts&&o<2*((null==a?void 0:a.partTarget)||0)))&&(this.log("adjusting start position by "+o+" to match buffer start"),r+=o,this.startPosition=r),t<r&&(this.log("seek to target start position "+r+" from current time "+t+" buffer start "+s),e.currentTime=r)}}},r._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},r._loadBitrateTestFrag=function(e,t){var r=this;e.bitrateTest=!0,this._doFragLoad(e,t).then((function(e){var i=r.hls,a=null==e?void 0:e.frag;if(a&&!r.fragContextChanged(a)){t.fragmentError=0,r.state=Hi.IDLE,r.startFragRequested=!1,r.bitrateTest=!1;var n=a.stats;n.parsing.start=n.parsing.end=n.buffering.start=n.buffering.end=self.performance.now(),i.trigger(O.FRAG_LOADED,e),a.bitrateTest=!1}})).catch((function(t){r.state!==Hi.STOPPED&&r.state!==Hi.ERROR&&(r.warn(t),r.resetFragmentLoading(e))}))},r._handleTransmuxComplete=function(e){var t=this.playlistType,r=this.hls,i=e.remuxResult,a=e.chunkMeta,n=this.getCurrentContext(a);if(n){var s=n.frag,o=n.part,l=n.level,u=i.video,d=i.text,h=i.id3,f=i.initSegment,c=l.details,g=this.altAudio?void 0:i.audio;if(this.fragContextChanged(s))this.fragmentTracker.removeFragment(s);else{if(this.state=Hi.PARSING,f){var v=f.tracks;if(v){var m=s.initSegment||s;if(this.unhandledEncryptionError(f,s))return;this._bufferInitSegment(l,v,m,a),r.trigger(O.FRAG_PARSING_INIT_SEGMENT,{frag:m,id:t,tracks:v})}var p=f.initPTS,y=f.timescale,E=this.initPTS[s.cc];if(_(p)&&(!E||E.baseTime!==p||E.timescale!==y)){var T=f.trackId;this.initPTS[s.cc]={baseTime:p,timescale:y,trackId:T};var S=this.initPTS.slice(0);r.trigger(O.INIT_PTS_FOUND,{timestampOffsets:S,frag:s,id:t,initPTS:p,timescale:y,trackId:T})}}if(u&&c){g&&"audiovideo"===u.type&&this.logMuxedErr(s);var L=c.fragments[s.sn-1-c.startSN],R=s.sn===c.startSN,A=!L||s.cc>L.cc;if(!1!==i.independent){var b=u.startPTS,k=u.endPTS,D=u.startDTS,I=u.endDTS;if(o)o.elementaryStreams[u.type]={startPTS:b,endPTS:k,startDTS:D,endDTS:I};else if(u.firstKeyFrame&&u.independent&&1===a.id&&!A&&(this.couldBacktrack=!0),u.dropped&&u.independent){var C=this.getMainFwdBufferInfo(),P=(C?C.end:this.getLoadPosition())+this.config.maxBufferHole,x=u.firstKeyFramePTS?u.firstKeyFramePTS:b;if(!R&&P<x-this.config.maxBufferHole&&!A)return void this.backtrack(s);A&&(s.gap=!0),s.setElementaryStreamInfo(u.type,s.start,k,s.start,I,!0)}else R&&b-(c.appliedTimelineOffset||0)>2&&(s.gap=!0);s.setElementaryStreamInfo(u.type,b,k,D,I),this.backtrackFragment&&(this.backtrackFragment=s),this.bufferFragmentData(u,s,o,a,R||A)}else{if(!R&&!A)return void this.backtrack(s);s.gap=!0}}if(g){var w=g.startPTS,M=g.endPTS,F=g.startDTS,N=g.endDTS;o&&(o.elementaryStreams[B.AUDIO]={startPTS:w,endPTS:M,startDTS:F,endDTS:N}),s.setElementaryStreamInfo(B.AUDIO,w,M,F,N),this.bufferFragmentData(g,s,o,a)}if(c&&null!=h&&h.samples.length){var U={id:t,frag:s,details:c,samples:h.samples};r.trigger(O.FRAG_PARSING_METADATA,U)}if(c&&d){var G={id:t,frag:s,details:c,samples:d.samples};r.trigger(O.FRAG_PARSING_USERDATA,G)}}}else this.resetWhenMissingContext(a)},r.logMuxedErr=function(e){this.warn((G(e)?"Media":"Init")+" segment with muxed audiovideo where only video expected: "+e.url)},r._bufferInitSegment=function(e,t,r,i){var a=this;if(this.state===Hi.PARSING){this.audioOnly=!!t.audio&&!t.video,this.altAudio&&!this.audioOnly&&(delete t.audio,t.audiovideo&&this.logMuxedErr(r));var n=t.audio,s=t.video,o=t.audiovideo;if(n){var l=e.audioCodec,u=At(n.codec,l);if("mp4a"===u&&(u="mp4a.40.5"),this.audioCodecSwitch){u&&(u=-1!==u.indexOf("mp4a.40.5")?"mp4a.40.2":"mp4a.40.5");var d=n.metadata;d&&"channelCount"in d&&1!==(d.channelCount||1)&&!function(){if(void 0===ut){var e=ht.match(/Firefox\/(\d+)/i);ut=e?parseInt(e[1]):0}return ut}()&&(u="mp4a.40.5")}u&&-1!==u.indexOf("mp4a.40.5")&&/Android/.test(ht)&&"audio/mpeg"!==n.container&&(u="mp4a.40.2",this.log("Android: force audio codec to "+u)),l&&l!==u&&this.log('Swapping manifest audio codec "'+l+'" for "'+u+'"'),n.levelCodec=u,n.id=N.MAIN,this.log("Init audio buffer, container:"+n.container+", codecs[selected/level/parsed]=["+(u||"")+"/"+(l||"")+"/"+n.codec+"]"),delete t.audiovideo}if(s){s.levelCodec=e.videoCodec,s.id=N.MAIN;var h=s.codec;if(4===(null==h?void 0:h.length))switch(h){case"hvc1":case"hev1":s.codec="hvc1.1.6.L120.90";break;case"av01":s.codec="av01.0.04M.08";break;case"avc1":s.codec="avc1.42e01e"}this.log("Init video buffer, container:"+s.container+", codecs[level/parsed]=["+(e.videoCodec||"")+"/"+h+"]"+(s.codec!==h?" parsed-corrected="+s.codec:"")+(s.supplemental?" supplemental="+s.supplemental:"")),delete t.audiovideo}o&&(this.iframesOnly&&this.logMuxedErr(r),this.log("Init audiovideo buffer, container:"+o.container+", codecs[level/parsed]=["+e.codecs+"/"+o.codec+"]"),delete t.video,delete t.audio);var f=Object.keys(t);if(f.length){if(this.hls.trigger(O.BUFFER_CODECS,t),!this.hls)return;f.forEach((function(e){var n=t[e].initSegment;null!=n&&n.byteLength&&a.hls.trigger(O.BUFFER_APPENDING,{type:e,data:n,frag:r,part:null,chunkMeta:i,parent:r.type})}))}this.tickImmediate()}},r.getMainFwdBufferInfo=function(){var e=this.getBufferOutput();return this.getFwdBufferInfo(e,N.MAIN)},r.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=Hi.IDLE},a(t,[{key:"backtrackFragment",get:function(){return this._backtrackFragment},set:function(e){this._backtrackFragment=e}},{key:"couldBacktrack",get:function(){return this._couldBacktrack},set:function(e){this._couldBacktrack=e}},{key:"hasEnoughToStart",get:function(){return this._hasEnoughToStart}},{key:"maxBufferLength",get:function(){var e=this.levels,t=this.level,r=null==e?void 0:e[t];return r?this.getMaxBufferLength(r.maxBitrate):this.config.maxBufferLength}},{key:"nextLevel",get:function(){var e=this.nextBufferedFrag;return e?e.level:-1}},{key:"currentFrag",get:function(){var e;if(this.fragPlaying)return this.fragPlaying;var t=(null==(e=this.media)?void 0:e.currentTime)||this.lastCurrentTime;return _(t)?this.getAppendedFrag(t):null}},{key:"currentProgramDateTime",get:function(){var e,t=(null==(e=this.media)?void 0:e.currentTime)||this.lastCurrentTime;if(_(t)){var r=this.getLevelDetails(),i=this.currentFrag||(r?Wt(null,r.fragments,t):null);if(i){var a=i.programDateTime;if(null!==a){var n=a+1e3*(t-i.start);return new Date(n)}}}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}}])}(Ki),xn=function(){function e(){this.requestTimeout=void 0,this.retryTimeout=void 0,this.retryDelay=0,this.config=null,this.callbacks=null,this.context=null,this.stats=new M}var t=e.prototype;return t.destroy=function(){this.callbacks=this.context=this.config=null,this.abortInternal(),this.stats=null},t.load=function(e,t,r){var i=this.stats;if(i.loading.start)throw new Error("Loader can only be used once.");i.loading.start=self.performance.now(),this.context=e,this.config=t,this.callbacks=r,this.loadInternal()},t.abort=function(){var e;this.abortInternal(),null!=(e=this.callbacks)&&e.onAbort&&this.callbacks.onAbort(this.stats,this.context,this.getNetworkDetails())},t.loadtimeout=function(){if(this.config){var e=this.config.loadPolicy.timeoutRetry;if(Z(e,this.stats.retry,!0))this.retry(e);else{var t;fe.warn("timeout while loading "+(null==(t=this.context)?void 0:t.url));var r=this.callbacks;r&&(this.abortInternal(),r.onTimeout(this.stats,this.context,this.getNetworkDetails()))}}},t.retry=function(e){var t=this,r=this.context,i=this.stats;this.retryDelay=Q(e,i.retry),i.retry++,fe.warn((status?"HTTP Status "+status:"Timeout")+" while loading "+(null==r?void 0:r.url)+", retrying "+i.retry+"/"+e.maxNumRetry+" in "+this.retryDelay+"ms"),this.abortInternal(),this.resetInternalLoader(),self.clearTimeout(this.retryTimeout),this.retryTimeout=self.setTimeout((function(){return t.loadInternal()}),this.retryDelay)},e}(),wn=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,a=0;a<e.length;a++){var n=e[a];r.set(n,i),i+=n.length}return r}(t,r),this.reset(),e):new Uint8Array(0)},t.reset=function(){this.chunks.length=0,this.dataLength=0},e}(),On=/(\d+)-(\d+)\/(\d+)/,Mn=function(e){function t(t){var r;return(r=e.call(this)||this).fetchSetup=void 0,r.request=null,r.response=null,r.controller=null,r.fetchSetup=t.fetchSetup||Fn,r}u(t,e);var r=t.prototype;return r.destroy=function(){this.request=null,e.prototype.destroy.call(this),this.response=null,this.controller=null,this.fetchSetup=null},r.abortInternal=function(){self.clearTimeout(this.retryTimeout),this.controller&&!this.stats.loading.end&&(this.stats.aborted=!0,this.controller.abort())},r.getNetworkDetails=function(){return this.response},r.resetInternalLoader=function(){this.response=null},r.loadInternal=function(){var e=this,t=this.config,r=this.context,i=this.stats;if(t&&r){i.loading.first=0,i.loaded=0,i.aborted=!1,this.controller=new self.AbortController;var a=function(e,t){var r={method:"GET",mode:"cors",credentials:"same-origin",signal:t,headers:new self.Headers(o({},e.headers))};return e.rangeEnd&&r.headers.set("Range","bytes="+e.rangeStart+"-"+String(e.rangeEnd-1)),r}(r,this.controller.signal),n="arraybuffer"===r.responseType,s=n?"byteLength":"length",l=t.loadPolicy,u=l.maxTimeToFirstByteMs,d=l.maxLoadTimeMs;this.request=this.fetchSetup(r,a),self.clearTimeout(this.requestTimeout),t.timeout=u&&_(u)?u:d,this.requestTimeout=self.setTimeout((function(){e.loadtimeout()}),t.timeout),(gn(this.request)?this.request.then(self.fetch):self.fetch(this.request)).then((function(a){var s;e.response=a;var o=Math.max(self.performance.now(),i.loading.start);if(self.clearTimeout(e.requestTimeout),t.timeout=d,e.requestTimeout=self.setTimeout((function(){e.loadtimeout()}),d-(o-i.loading.start)),!a.ok){var l=a.status,u=a.statusText;throw new Nn(u||"fetch, bad network response",l,a)}i.loading.first=o,i.total=function(e){var t=e.get("Content-Range");if(t){var r=function(e){var t=On.exec(e);if(t)return parseInt(t[2])-parseInt(t[1])+1}(t);if(_(r))return r}var i=e.get("Content-Length");if(i)return parseInt(i)}(a.headers)||i.total;var h=null==(s=e.callbacks)?void 0:s.onProgress;return h&&_(t.highWaterMark)?e.loadProgressively(a,i,r,t.highWaterMark,h):n?a.arrayBuffer():"json"===r.responseType?a.json():a.text()})).then((function(a){var n,o,l=e.response;if(!l)throw new Error("loader destroyed");self.clearTimeout(e.requestTimeout),i.loading.end=Math.max(self.performance.now(),i.loading.first);var u=a[s];u&&(i.loaded=i.total=u);var d={url:l.url,data:a,code:l.status},h=null==(n=e.callbacks)?void 0:n.onProgress;h&&!_(t.highWaterMark)&&h(i,r,a,l),null==(o=e.callbacks)||o.onSuccess(d,i,r,l)})).catch((function(a){if(self.clearTimeout(e.requestTimeout),!i.aborted){var n,s=a&&a.code||0,o=a?a.message:null,l=t.loadPolicy.errorRetry;Z(l,i.retry,!1,{url:r.url,data:void 0,code:s})?e.retry(l):(fe.error(s+" while loading "+r.url),null==(n=e.callbacks)||n.onError({code:s,text:o},r,a?a.details:null,i))}}))}},r.getCacheAge=function(){var e=null;if(this.response){var t=this.response.headers.get("age");e=t?parseFloat(t):null}return e},r.getResponseHeader=function(e){return this.response?this.response.headers.get(e):null},r.loadProgressively=function(e,t,r,i,a){void 0===i&&(i=0);var n=new wn,s=e.body.getReader(),o=function(){return s.read().then((function(s){if(s.done)return n.dataLength&&a(t,r,n.flush().buffer,e),Promise.resolve(new ArrayBuffer(0));var l=s.value,u=l.length;return t.loaded+=u,u<i||n.dataLength?(n.push(l),n.dataLength>=i&&a(t,r,n.flush().buffer,e)):a(t,r,l.buffer,e),o()})).catch((function(){return Promise.reject()}))};return o()},t}(xn);function Fn(e,t){return new self.Request(e.url,t)}var Nn=function(e){function t(t,r,i){var a;return(a=e.call(this,t)||this).code=void 0,a.details=void 0,a.code=r,a.details=i,a}return u(t,e),t}(v(Error)),Bn=/^age:\s*[\d.]+\s*$/im,Un=function(e){function t(t){var r;return(r=e.call(this)||this).xhrSetup=void 0,r.loader=null,r.xhrSetup=t.xhrSetup||null,r}u(t,e);var r=t.prototype;return r.destroy=function(){e.prototype.destroy.call(this),this.loader=null,this.xhrSetup=null},r.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()))},r.getNetworkDetails=function(){return this.loader},r.resetInternalLoader=function(){this.loader=null},r.loadInternal=function(){var e=this,t=this.config,r=this.context;if(t&&r){var i=this.loader=new self.XMLHttpRequest,a=this.stats;a.loading.first=0,a.loaded=0,a.aborted=!1;var n=this.xhrSetup;n?Promise.resolve().then((function(){if(e.loader===i&&!e.stats.aborted)return n.call(e,i,r.url,r)})).catch((function(t){if(e.loader===i&&!e.stats.aborted)return i.open("GET",r.url,!0),n.call(e,i,r.url,r)})).then((function(){e.loader!==i||e.stats.aborted||e.openAndSendXhr(i,r,t)})).catch((function(t){var n;null==(n=e.callbacks)||n.onError({code:i.status,text:t.message},r,i,a)})):this.openAndSendXhr(i,r,t)}},r.openAndSendXhr=function(e,t,r){e.readyState||e.open("GET",t.url,!0);var i=t.headers,a=r.loadPolicy,n=a.maxTimeToFirstByteMs,s=a.maxLoadTimeMs;if(i)for(var o in i)e.setRequestHeader(o,i[o]);t.rangeEnd&&e.setRequestHeader("Range","bytes="+t.rangeStart+"-"+(t.rangeEnd-1)),e.onreadystatechange=this.readystatechange.bind(this),e.onprogress=this.loadprogress.bind(this),e.responseType=t.responseType,self.clearTimeout(this.requestTimeout),r.timeout=n&&_(n)?n:s,this.requestTimeout=self.setTimeout(this.loadtimeout.bind(this),r.timeout),e.send()},r.readystatechange=function(){var e=this.context,t=this.loader,r=this.stats;if(e&&t){var i=t.readyState,a=this.config;if(!r.aborted&&i>=2&&(0===r.loading.first&&(r.loading.first=Math.max(self.performance.now(),r.loading.start),a.timeout!==a.loadPolicy.maxLoadTimeMs&&(self.clearTimeout(this.requestTimeout),a.timeout=a.loadPolicy.maxLoadTimeMs,this.requestTimeout=self.setTimeout(this.loadtimeout.bind(this),a.loadPolicy.maxLoadTimeMs-(r.loading.first-r.loading.start)))),4===i)){self.clearTimeout(this.requestTimeout),t.onreadystatechange=null,t.onprogress=null;var n=t.status,s="text"===t.responseType?t.responseText:null;if(n>=200&&n<300){var o=null!=s?s:t.response;if(null!=o){var l,u;r.loading.end=Math.max(self.performance.now(),r.loading.first);var d="arraybuffer"===t.responseType?o.byteLength:o.length;r.loaded=r.total=d,r.bwEstimate=8e3*r.total/(r.loading.end-r.loading.first);var h=null==(l=this.callbacks)?void 0:l.onProgress;h&&h(r,e,o,t);var f={url:t.responseURL,data:o,code:n};return e.rangeEnd&&d!==e.rangeEnd-e.rangeStart&&fe.warn("Payload length "+d+" does not match requested Range: bytes="+e.rangeStart+"-"+(e.rangeEnd-1)),void(null==(u=this.callbacks)||u.onSuccess(f,r,e,t))}}var c,g=a.loadPolicy.errorRetry;Z(g,r.retry,!1,{url:e.url,data:void 0,code:n})?this.retry(g):(fe.error(n+" while loading "+e.url),null==(c=this.callbacks)||c.onError({code:n,text:t.statusText},e,t,r))}}},r.loadprogress=function(e){var t=this.stats;t.loaded=e.loaded,e.lengthComputable&&(t.total=e.total)},r.getCacheAge=function(){var e=null;if(this.loader&&Bn.test(this.loader.getAllResponseHeaders())){var t=this.loader.getResponseHeader("age");e=t?parseFloat(t):null}return e},r.getResponseHeader=function(e){return this.loader&&new RegExp("^"+e+":\\s*[\\d.]+\\s*$","im").test(this.loader.getAllResponseHeaders())?this.loader.getResponseHeader(e):null},t}(xn),Gn={maxTimeToFirstByteMs:8e3,maxLoadTimeMs:2e4,timeoutRetry:null,errorRetry:null},Vn=f(f({autoStartLoad:!0,startPosition:-1,defaultAudioCodec:void 0,debug:!1,capLevelOnFPSDrop:!1,capLevelToPlayerSize:!1,ignoreDevicePixelRatio:!1,maxDevicePixelRatio:Number.POSITIVE_INFINITY,preferManagedMediaSource:!1,initialLiveManifestSize:1,maxBufferLength:30,backBufferLength:1/0,frontBufferFlushThreshold:1/0,loopBackBufferFlush:void 0,startOnSegmentBoundary:!1,nextAudioTrackBufferFlushForwardOffset:.25,maxBufferSize:6e7,maxFragLookUpTolerance:.25,maxBufferHole:.1,detectStallWithCurrentTimeMs:1250,highBufferWatchdogPeriod:2,nudgeOffset:.1,nudgeMaxRetry:3,nudgeOnVideoHole:!0,skipBufferHolePadding:.1,liveSyncMode:"edge",liveSyncDurationCount:3,liveSyncOnStallIncrease:1,liveMaxLatencyDurationCount:1/0,liveMaxUnchangedPlaylistRefresh: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,appendTimeout:1/0,ignorePlaylistParsingErrors:!1,loader:Un,fLoader:void 0,pLoader:void 0,xhrSetup:void 0,licenseXhrSetup:void 0,licenseResponseCallback:void 0,stretchShortVideoTrack:!1,maxAudioFramesDrift:1,forceKeyFrameOnDiscontinuity:!0,handleMpegTsVideoIntegrityErrors:"process",abrEwmaFastLive:3,abrEwmaSlowLive:9,abrEwmaFastVoD:3,abrEwmaSlowVoD:9,abrEwmaDefaultEstimate:5e5,abrEwmaDefaultEstimateMax:5e6,abrBandWidthFactor:.95,abrBandWidthUpFactor:.7,abrMaxWithRealBitrate:!1,abrSwitchInterval:0,maxStarvationDelay:4,maxLoadingDelay:4,minAutoBitrate:0,emeEnabled:!1,widevineLicenseUrl:void 0,drmSystems:{},drmSystemOptions:{},requestMediaKeySystemAccessFunc:null,requireKeySystemAccessOnStart:!1,testBandwidth:!0,progressive:!1,lowLatencyMode:!0,cmcd:void 0,enableDateRangeMetadataCues:!0,enableEmsgMetadataCues:!0,enableEmsgKLVMetadata:!1,enableID3MetadataCues:!0,emsgKLVSchemaUri:void 0,enableInterstitialPlayback:!1,interstitialAppendInPlace:!0,interstitialLiveLookAhead:10,iframeCacheLimit:2097152,useMediaCapabilities:!1,preserveManualLevelOnError:!1,errorPenaltyExpireMs:0,certLoadPolicy:{default:Gn},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}}},interstitialAssetListLoadPolicy:{default:Gn},manifestLoadingTimeOut:1e4,manifestLoadingMaxRetry:1,manifestLoadingRetryDelay:1e3,manifestLoadingMaxRetryTimeout:64e3,levelLoadingTimeOut:1e4,levelLoadingMaxRetry:4,levelLoadingRetryDelay:1e3,levelLoadingMaxRetryTimeout:64e3,fragLoadingTimeOut:2e4,fragLoadingMaxRetry:6,fragLoadingRetryDelay:1e3,fragLoadingMaxRetryTimeout:64e3,streamController:Pn,abrController:Ht,bufferController:pr,capLevelController:Ar,errorController:ar,fpsController:pi,id3TrackController:ea,gapController:Yi,latencyController:ta},{cueHandler:ge,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,contentSteeringController:gi,iframeController:void 0,interstitialsController:void 0});function Hn(e,t,r){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"');if(void 0!==t.liveMaxUnchangedPlaylistRefresh){var i=t.liveMaxUnchangedPlaylistRefresh,a=(n=i,s=2,o=1/0,Math.max(s,Math.min(n,o)));a!==i&&r.warn('hls.js config: "liveMaxUnchangedPlaylistRefresh" clamped from '+i+" to "+a+"."),t.liveMaxUnchangedPlaylistRefresh=a}var n,s,o,l=Kn(e),u=["TimeOut","MaxRetry","RetryDelay","MaxRetryTimeout"];return["manifest","level","frag"].forEach((function(e){var i=("level"===e?"playlist":e)+"LoadPolicy",a=void 0===t[i],n=[];u.forEach((function(r){var s=e+"Loading"+r,o=t[s];if(void 0!==o&&a){n.push(s);var u=l[i].default;switch(t[i]={default:u},r){case"TimeOut":u.maxLoadTimeMs=o,u.maxTimeToFirstByteMs=o;break;case"MaxRetry":u.errorRetry.maxNumRetry=o,u.timeoutRetry.maxNumRetry=o;break;case"RetryDelay":u.errorRetry.retryDelayMs=o,u.timeoutRetry.retryDelayMs=o;break;case"MaxRetryTimeout":u.errorRetry.maxRetryDelayMs=o,u.timeoutRetry.maxRetryDelayMs=o}}})),n.length&&r.warn('hls.js config: "'+n.join('", "')+'" setting(s) are deprecated, use "'+i+'": '+Bt(t[i]))})),f(f({},l),t)}function Kn(e){return e&&"object"==typeof e?Array.isArray(e)?e.map(Kn):Object.keys(e).reduce((function(t,r){return t[r]=Kn(e[r]),t}),{}):e}function Yn(e,t){var r=e.loader;r!==Mn&&r!==Un?(t.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=Mn,e.progressive=!0,e.enableSoftwareAES=!0,t.log("[config]: Progressive streaming enabled, using FetchLoader"))}var Wn=function(e){function t(t,r){var i;return(i=e.call(this,r,t.logger)||this).hls=void 0,i.canLoad=!1,i.timer=-1,i.loadingSync=!1,i.hls=t,i}u(t,e);var r=t.prototype;return r.destroy=function(){this.clearTimer(),this.hls=null},r.clearTimer=function(){-1!==this.timer&&(self.clearTimeout(this.timer),this.timer=-1)},r.startLoad=function(){this.canLoad=!0,this.loadPlaylist()},r.stopLoad=function(){this.canLoad=!1,this.clearTimer()},r.switchParams=function(e,t,r){var i=null==t?void 0:t.renditionReports;if(i){for(var a=-1,n=0;n<i.length;n++){var s=i[n],o=void 0;try{o=new self.URL(s.URI,t.url).href}catch(e){this.warn("Could not construct new URL for Rendition Report: "+e),o=s.URI||""}if(o===e){a=n;break}o===e.substring(0,o.length)&&(a=n)}if(-1!==a){var l=i[a],u=parseInt(l["LAST-MSN"])||t.lastPartSn,d=parseInt(l["LAST-PART"])||t.lastPartIndex;if(this.hls.config.lowLatencyMode){var h=Math.min(t.age-t.partTarget,t.targetduration);d>=0&&h>t.partTarget&&(d+=1)}var f=r&&wt(r);return new Ot(u,d>=0?d:void 0,f)}}},r.loadPlaylist=function(e){this.clearTimer()},r.loadingPlaylist=function(e,t){this.clearTimer()},r.shouldLoadPlaylist=function(e){return this.canLoad&&!!e&&!!e.url&&(!e.details||e.details.live)},r.getUrlWithDirectives=function(e,t){if(t)try{return t.addDirectives(e)}catch(e){this.warn("Could not construct new URL with HLS Delivery Directives: "+e)}return e},r.playlistLoaded=function(e,t,r){var i=t.details,a=t.stats,n=i.fragments,s=self.performance.now(),o=a.loading.first?Math.max(0,Math.floor(s-a.loading.first)):0;i.advancedDateTime=Date.now()-o;var l=this.hls.config.timelineOffset;if(l!==i.appliedTimelineOffset){var u=Math.max(l||0,0);i.appliedTimelineOffset=u,n.forEach((function(e){null==e||e.setStart(e.playlistOffset+u)}))}if(i.live||null!=r&&r.live){var d,h="levelInfo"in t?t.levelInfo:t.track;i.reloaded(r);var f=null==(d=n[n.length-1])?void 0:d.type;if(i.misses>=this.hls.config.liveMaxUnchangedPlaylistRefresh){var c=new Error(f+" playlist "+h.id+" hit max allowed unchanged reloads.");this.warn(c);var g=t.networkDetails,v=t.context;return void this.hls.trigger(O.ERROR,{type:x.NETWORK_ERROR,details:w.PLAYLIST_UNCHANGED_ERROR,fatal:!1,url:i.url,error:c,reason:c.message,level:t.level,parent:f,context:v,networkDetails:g,stats:a})}if(r){ai(r,i,this);var m=i.playlistParsingError;if(m){this.warn(m);var p=this.hls;if(!p.config.ignorePlaylistParsingErrors){var y=t.networkDetails;return void p.trigger(O.ERROR,{type:x.NETWORK_ERROR,details:w.LEVEL_PARSING_ERROR,fatal:!1,url:i.url,error:m,reason:m.message,level:t.level,parent:f,networkDetails:y,stats:a})}i.playlistParsingError=null}}-1===i.requestScheduled&&(i.requestScheduled=a.loading.start);var E=this.hls.mainForwardBufferInfo,T=E?E.end-E.len:0,S=oi(i,1e3*(i.edge-T));i.requestScheduled+S<s?i.requestScheduled=s:i.requestScheduled<=s&&(i.requestScheduled+=S);var L,R=t.deliveryDirectives;if(this.log("live playlist "+e+" "+(i.advanced?"REFRESHED "+i.lastPartSn+" - "+i.lastPartIndex:i.updated?"UPDATED":"MISSED (received "+i.lastPartSn+" - "+i.lastPartIndex+(R?" | requested "+R.msn+" - "+R.part:"")+")")),!this.canLoad||!i.live)return;var A=void 0,b=void 0;if(i.canBlockReload&&i.advanced){var k=this.hls.config.lowLatencyMode,D=i.lastPartSn,I=i.endSN,_=i.lastPartIndex,C=D===I;-1!==_?C?(A=I+1,b=k?0:_):(A=D,b=k?_+1:i.maxPartIndex):A=I+1;var P=i.age,M=P+i.ageHeader,F=Math.min(M-i.partTarget,1.5*i.targetduration);if(F>0){if(M>3*i.targetduration)this.log("Playlist last advanced "+P.toFixed(2)+"s ago. Omitting segment and part directives."),A=void 0,b=void 0;else if(null!=r&&r.tuneInGoal&&M-i.partTarget>r.tuneInGoal)this.warn("CDN Tune-in goal increased from: "+r.tuneInGoal+" to: "+F+" with playlist age: "+i.age),F=0;else{var N=Math.floor(F/i.targetduration);A+=N,void 0!==b&&(b+=Math.round(F%i.targetduration/i.partTarget)),this.log("CDN Tune-in age: "+i.ageHeader+"s last advanced "+P.toFixed(2)+"s goal: "+F+" skip sn "+N+" to part "+b)}i.tuneInGoal=F}if(L=this.getDeliveryDirectives(i,t.deliveryDirectives,A,b),k||!C)return i.requestScheduled=s,void this.loadingPlaylist(h,L)}else(i.canBlockReload||i.canSkipUntil)&&(L=this.getDeliveryDirectives(i,t.deliveryDirectives,A,b));L&&void 0!==A&&i.canBlockReload&&(i.requestScheduled=a.loading.first+Math.max(S-2*o,S/2)),this.scheduleLoading(h,L,i)}else this.clearTimer()},r.scheduleLoading=function(e,t,r){var i=this,a=r||e.details;if(a){var n=self.performance.now(),s=a.requestScheduled;if(n>=s&&!this.loadingSync)return this.loadingSync=!0,this.loadingPlaylist(e,t),void(this.loadingSync=!1);var o=Math.max(s-n,0);this.log("reload live playlist "+(e.name||e.bitrate+"bps")+" in "+Math.round(o)+" ms"),this.clearTimer(),this.timer=self.setTimeout((function(){return i.loadingPlaylist(e,t)}),o)}else this.loadingPlaylist(e,t)},r.getDeliveryDirectives=function(e,t,r,i){var a=wt(e);return null!=t&&t.skip&&e.deltaUpdateFailed&&(r=t.msn,i=t.part,a=Ct),new Ot(r,i,a)},r.checkRetry=function(e){var t=this,r=e.details,i=j(e),a=e.errorAction,n=a||{},s=n.action,o=n.retryCount,l=void 0===o?0:o,u=n.retryConfig,d=!!a&&!!u&&(s===$t||!a.resolved&&s===zt);if(d){var h;if(l>=u.maxNumRetry)return!1;if(i&&null!=(h=e.context)&&h.deliveryDirectives)this.warn("Retrying playlist loading "+(l+1)+"/"+u.maxNumRetry+' after "'+r+'" without delivery-directives'),this.loadPlaylist();else{var f,c=J(null==(f=e.response)?void 0:f.code);if(this.clearTimer(),c)this.log("Waiting for connection (offline)"),e.reason="offline",this.timer=self.setTimeout((function(){return t.checkOfflineStatus()}),1e3);else{var g=Q(u,l);this.warn("Retrying playlist loading "+(l+1)+"/"+u.maxNumRetry+' after "'+r+'" in '+g+"ms"),this.timer=self.setTimeout((function(){return t.loadPlaylist()}),g)}}e.levelRetry=!0,a.resolved=!0}return d},r.checkOfflineStatus=function(){var e=this;this.clearTimer(),J(0)?this.timer=self.setTimeout((function(){return e.checkOfflineStatus()}),1e3):(this.log("Connection restored (online)"),this.loadPlaylist())},t}(ie),jn=function(e){function t(t,r){var i;return(i=e.call(this,t,"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.lastABRSwitchTime=-1,i._iframeVariants=[],i.onParsedComplete=void 0,i.steering=r,i._registerListeners(),i}u(t,e);var r=t.prototype;return r._registerListeners=function(){var e=this.hls;e.on(O.MANIFEST_LOADING,this.onManifestLoading,this),e.on(O.MANIFEST_LOADED,this.onManifestLoaded,this),e.on(O.LEVEL_LOADED,this.onLevelLoaded,this),e.on(O.LEVELS_UPDATED,this.onLevelsUpdated,this),e.on(O.FRAG_BUFFERED,this.onFragBuffered,this),e.on(O.ERROR,this.onError,this)},r._unregisterListeners=function(){var e=this.hls;e.off(O.MANIFEST_LOADING,this.onManifestLoading,this),e.off(O.MANIFEST_LOADED,this.onManifestLoaded,this),e.off(O.LEVEL_LOADED,this.onLevelLoaded,this),e.off(O.LEVELS_UPDATED,this.onLevelsUpdated,this),e.off(O.FRAG_BUFFERED,this.onFragBuffered,this),e.off(O.ERROR,this.onError,this)},r.destroy=function(){this._unregisterListeners(),this.steering=null,this.resetLevels(),e.prototype.destroy.call(this)},r.stopLoad=function(){this._levels.forEach((function(e){e.loadError=0,e.fragmentError=0})),e.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,this._iframeVariants=[]},r.onManifestLoading=function(e,t){this.resetLevels()},r.onManifestLoaded=function(e,t){var r=this,i=this.hls.config.preferManagedMediaSource,a=[],n={},s={},o=!1,l=!1,u=!1,d=!1;t.levels.forEach((function(e){var t=e.attrs,h=zn(e,i),f=e.audioCodec,c=e.videoCodec,g=e.imageCodec,v=e.width,m=e.height;if(h){o||(o=!(!v||!m)),l||(l=!!c),u||(u=!!f),d||(d=!!g);var p=t.CODECS,y=t["FRAME-RATE"],E=t["HDCP-LEVEL"],T=t["PATHWAY-ID"],S=t.RESOLUTION,L=t["VIDEO-RANGE"],R=(T||".")+"-"+e.bitrate+"-"+S+"-"+y+"-"+p+"-"+L+"-"+E;if(n[R])if(n[R].uri===e.url||e.attrs["PATHWAY-ID"])e.iframes||(n[R].addGroupId("audio",t.AUDIO),n[R].addGroupId("text",t.SUBTITLES));else{var A=s[R]+=1;e.attrs["PATHWAY-ID"]=new Array(A+1).join(".");var b=r.createLevel(e);n[R]=b,a.push(b)}else{var k=r.createLevel(e);n[R]=k,s[R]=1,a.push(k)}}else r.log('Some or all CODECS not supported "'+t.CODECS+'"')})),this.filterAndSortMediaOptions(a,t,o,l,u,d)},r.createLevel=function(e){var t=new Mt(e),r=e.supplemental;if(null!=r&&r.videoCodec&&!Xn(r.videoCodec,this.hls.config.preferManagedMediaSource)){var i=new Error('SUPPLEMENTAL-CODECS not supported "'+r.videoCodec+'"');this.log(i.message),t.supportedResult=ce.getUnsupportedResult(i,[])}return t},r.filterAndSortMediaOptions=function(e,t,r,i,a,n){var s,o=this,l=[],u=[],d=e,h=(null==(s=t.stats)?void 0:s.parsing)||{},f=this.hls.config.preferManagedMediaSource;if((r||i)&&a&&(d=d.filter((function(e){var t,r=e.videoCodec,i=e.videoRange,a=e.width,n=e.height;return(!!r||!(!a||!n))&&!!(t=i)&&_t.indexOf(t)>-1}))),i&&n&&(d=d.filter((function(e){return!e.imageCodec}))),0===d.length)return Promise.resolve().then((function(){if(o.hls){var e="no level with compatible codecs found in manifest",r=e;t.levels.length&&(r="one or more CODECS in variant not supported: "+Bt(t.levels.map((function(e){return e.attrs.CODECS})).filter((function(e,t,r){return r.indexOf(e)===t}))),o.warn(r),e+=" ("+r+")");var i=new Error(e);o.hls.trigger(O.ERROR,{type:x.MEDIA_ERROR,details:w.MANIFEST_INCOMPATIBLE_CODECS_ERROR,fatal:!0,url:t.url,error:i,reason:r})}})),void(h.end=performance.now());t.audioTracks&&Qn(l=t.audioTracks.filter((function(e){return!e.audioCodec||qn(e.audioCodec,f)}))),t.subtitles&&Qn(u=t.subtitles);var c=d.slice(0);d.sort((function(e,t){if(e.attrs["HDCP-LEVEL"]!==t.attrs["HDCP-LEVEL"])return(e.attrs["HDCP-LEVEL"]||"")>(t.attrs["HDCP-LEVEL"]||"")?1:-1;if(r&&e.height!==t.height)return e.height-t.height;if(e.frameRate!==t.frameRate)return e.frameRate-t.frameRate;if(e.videoRange!==t.videoRange)return _t.indexOf(e.videoRange)-_t.indexOf(t.videoRange);if(e.videoCodec!==t.videoCodec){var i=Et(e.videoCodec),a=Et(t.videoCodec);if(i!==a)return a-i}if(e.uri===t.uri&&e.codecSet!==t.codecSet){var n=Tt(e.codecSet),s=Tt(t.codecSet);if(n!==s)return s-n}return e.averageBitrate!==t.averageBitrate?e.averageBitrate-t.averageBitrate:0}));var g=c[0];if(this.steering&&(d=this.steering.filterParsedLevels(d)).length!==c.length)for(var v=0;v<c.length;v++)if(c[v].pathwayId===d[0].pathwayId){g=c[v];break}this._levels=d;for(var m=0;m<d.length;m++)if(d[m]===g){var p;this._firstLevel=m;var y=g.bitrate,E=this.hls.bandwidthEstimate;if(this.log("manifest loaded, "+d.length+" level(s) found, first bitrate: "+y),void 0===(null==(p=this.hls.userConfig)?void 0:p.abrEwmaDefaultEstimate)){var T=Math.min(y,this.hls.config.abrEwmaDefaultEstimateMax);T>E&&E===this.hls.abrEwmaDefaultEstimate&&(this.hls.bandwidthEstimate=T)}break}var S=t.iframeVariants.filter((function(e){return zn(e,f)}));this._iframeVariants=S;var L=a&&!i,R=this.hls.config,A=!(!R.audioStreamController||!R.audioTrackController),b={levels:d,audioTracks:l,subtitleTracks:u,iframeVariants:S,sessionData:t.sessionData,sessionKeys:t.sessionKeys,firstLevel:this._firstLevel,stats:t.stats,audio:a,video:i,altAudio:A&&!L&&l.some((function(e){return!!e.url}))};h.end=performance.now(),this.hls.trigger(O.MANIFEST_PARSED,b)},r.onError=function(e,t){!t.fatal&&t.context&&t.context.type===F.LEVEL&&t.context.level===this.level&&this.checkRetry(t)},r.onFragBuffered=function(e,t){var r=t.frag;if(r.type===N.MAIN){var i=r.elementaryStreams;if(!Object.keys(i).some((function(e){return!!i[e]})))return;var a=this._levels[r.level];null!=a&&a.loadError&&(this.log("Resetting level error count of "+a.loadError+" on frag buffered"),a.loadError=0)}},r.onLevelLoaded=function(e,t){var r,i,a=t.level,n=t.details,s=t.levelInfo;if(!s)return this.warn("Invalid level index "+a),void(null!=(i=t.deliveryDirectives)&&i.skip&&(n.deltaUpdateFailed=!0));if(s===this.currentLevel||t.withoutMultiVariant){0===s.fragmentError&&(s.loadError=0);var o=s.details;o===t.details&&o.advanced&&(o=void 0),this.playlistLoaded(a,t,o)}else null!=(r=t.deliveryDirectives)&&r.skip&&(n.deltaUpdateFailed=!0)},r.loadPlaylist=function(t){e.prototype.loadPlaylist.call(this),this.shouldLoadPlaylist(this.currentLevel)&&this.scheduleLoading(this.currentLevel,t)},r.loadingPlaylist=function(t,r){e.prototype.loadingPlaylist.call(this,t,r);var i=this.getUrlWithDirectives(t.uri,r),a=this.currentLevelIndex,n=t.attrs["PATHWAY-ID"],s=t.details,o=null==s?void 0:s.age;this.log("Loading level index "+a+(void 0!==(null==r?void 0:r.msn)?" at sn "+r.msn+" part "+r.part:"")+(n?" Pathway "+n:"")+(o&&s.live?" age "+o.toFixed(1)+(s.type&&" "+s.type||""):"")+" "+i),this.hls.trigger(O.LEVEL_LOADING,{url:i,level:a,levelInfo:t,pathwayId:t.attrs["PATHWAY-ID"],id:0,deliveryDirectives:r||null})},r.removeLevel=function(e){var t,r=this;if(1!==this._levels.length){var i=this._levels.filter((function(t,i){return i!==e||(r.steering&&r.steering.removeLevel(t),t===r.currentLevel&&(r.currentLevel=null,r.currentLevelIndex=-1,t.details&&t.details.fragments.forEach((function(e){return e.level=-1}))),!1)}));hi(i),this._levels=i,this.currentLevelIndex>-1&&null!=(t=this.currentLevel)&&t.details&&(this.currentLevelIndex=this.currentLevel.details.fragments[0].level),this.manualLevelIndex>-1&&(this.manualLevelIndex=this.currentLevelIndex);var a=i.length-1;this._firstLevel=Math.min(this._firstLevel,a),this._startLevel&&(this._startLevel=Math.min(this._startLevel,a)),this.hls.trigger(O.LEVELS_UPDATED,{levels:i})}},r.onLevelsUpdated=function(e,t){var r=t.levels;this._levels=r},r.checkMaxAutoUpdated=function(){var e=this.hls,t=e.autoLevelCapping,r=e.maxAutoLevel,i=e.maxHdcpLevel;this._maxAutoLevel!==r&&(this._maxAutoLevel=r,this.hls.trigger(O.MAX_AUTO_LEVEL_UPDATED,{autoLevelCapping:t,levels:this.levels,maxAutoLevel:r,minAutoLevel:this.hls.minAutoLevel,maxHdcpLevel:i}))},a(t,[{key:"iframeVariants",get:function(){return 0===this._iframeVariants.length?null:this._iframeVariants}},{key:"levels",get:function(){return 0===this._levels.length?null:this._levels}},{key:"loadLevelObj",get:function(){return this.currentLevel}},{key:"level",get:function(){return this.currentLevelIndex},set:function(e){var t=this._levels;if(0!==t.length){if(e<0||e>=t.length){var r=new Error("invalid level idx"),i=e<0;if(this.hls.trigger(O.ERROR,{type:x.OTHER_ERROR,details:w.LEVEL_SWITCH_ERROR,level:e,fatal:i,error:r,reason:r.message}),i)return;e=Math.min(e,t.length-1)}var a=this.currentLevelIndex,n=this.currentLevel,s=n?n.attrs["PATHWAY-ID"]:void 0,o=t[e],l=o.attrs["PATHWAY-ID"];if(this.currentLevelIndex=e,this.currentLevel=o,a!==e||!n||s!==l){this.log("Switching to level "+e+" ("+(o.height?o.height+"p ":"")+(o.videoRange?o.videoRange+" ":"")+(o.codecSet?o.codecSet+" ":"")+"@"+o.bitrate+")"+(l?" with Pathway "+l:"")+" from level "+a+(s?" with Pathway "+s:""));var u={level:e,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(O.LEVEL_SWITCHING,u);var d=o.details;if(!d||d.live){var h=this.switchParams(o.uri,null==n?void 0:n.details,d);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.hls.firstAutoLevel}return this._startLevel},set:function(e){this._startLevel=e}},{key:"pathways",get:function(){return this.steering?this.steering.pathways():[]}},{key:"pathwayPriority",get:function(){return this.steering?this.steering.pathwayPriority:null},set:function(e){if(this.steering){var t=this.steering.pathways(),r=e.filter((function(e){return-1!==t.indexOf(e)}));if(e.length<1)return void this.warn("pathwayPriority "+e+" should contain at least one pathway from list: "+t);this.steering.pathwayPriority=r}}},{key:"nextLoadLevel",get:function(){return-1!==this.manualLevelIndex?this.manualLevelIndex:this.hls.nextAutoLevel},set:function(e){var t=this.currentLevelIndex;if(-1===this.manualLevelIndex&&e!==t&&-1!==e){var r=this.hls.config.abrSwitchInterval;if(r>0){var i=performance.now(),a=i-this.lastABRSwitchTime,n=1e3*r;if(this.lastABRSwitchTime>-1&&a<n)return void this.warn("Preventing ABR level switch: "+t+" -> "+e+" ("+Math.round(a)+"ms < "+n+"ms / "+r+"s)");this.lastABRSwitchTime=i,this.log("Allowing ABR level switch: "+t+" -> "+e+" ("+Math.round(a)+"ms >= "+n+"ms / "+r+"s)")}}this.level=e,-1===this.manualLevelIndex&&(this.hls.nextAutoLevel=e)}}])}(Wn);function qn(e,t){return mt(e,"audio",t)}function Xn(e,t){return mt(e,"video",t)}function zn(e,t){var r=e.audioCodec,i=e.videoCodec;r&&(e.audioCodec=r=Rt(r,t)||void 0),i&&(i=e.videoCodec=function(e){for(var t=e.split(","),r=0;r<t.length;r++){var i=t[r].split(".");i.length>2&&"avc1"===i[0]&&(t[r]="avc1."+parseInt(i[1]).toString(16)+("000"+parseInt(i[2]).toString(16)).slice(-4))}return t.join(",")}(i));var a=e.unknownCodecs;return!((null==a?void 0:a.length)||r&&!qn(r,t)||i&&!Xn(i,t))}function Qn(e){var t={};e.forEach((function(e){var r=e.groupId||"";e.id=t[r]=t[r]||0,t[r]++}))}var $n=function(e){function t(t,r){var i;return(i=e.call(this,"key-loader",r)||this).config=void 0,i.keyLoaderInfo={},i.emeController=null,i.config=t,i}u(t,e);var r=t.prototype;return r.abort=function(e){for(var t in this.keyLoaderInfo){var r=this.keyLoaderInfo[t].loader;if(r){var i;if(e&&e!==(null==(i=r.context)?void 0:i.frag.type))return;r.abort()}}},r.destroy=function(){for(var e in this.keyLoaderInfo){var t=this.keyLoaderInfo[e].loader;t&&t.destroy()}this.emeController=null,this.keyLoaderInfo={}},r.loadClear=function(e,t,r){return this.emeController?this.emeController.loadClear(e,t,r):null},r.load=function(e,t){var r=this;return!e.decryptdata&&e.encrypted&&this.emeController&&this.config.emeEnabled?this.emeController.selectKeySystemFormat(e).then((function(i){return r.loadInternal(e,t,i)})):this.loadInternal(e,t)},r.loadInternal=function(e,t,r){var i=this,a=e.decryptdata;if(!a){var n=new Error(r?"Expected frag.decryptdata to be defined after setting format "+r:"Missing decryption data on fragment in onKeyLoading (emeEnabled with controller: "+(this.emeController&&this.config.emeEnabled)+")");return Promise.reject(Zn(e,w.KEY_LOAD_ERROR,n))}var s=e;switch(a.method){case"SAMPLE-AES":case"SAMPLE-AES-CENC":case"SAMPLE-AES-CTR":return"identity"===a.keyFormat?this.loadKeyHTTP(s):t?t.then((function(){return i.loadKeyEME(s)})):this.loadKeyEME(s);case"AES-128":case"AES-256":case"AES-256-CTR":return this.loadKeyHTTP(s);default:return Promise.reject(Zn(e,w.KEY_LOAD_ERROR,new Error('Key supplied with unsupported METHOD: "'+a.method+'"')))}},r.loadKeyEME=function(e){if(this.emeController&&this.config.emeEnabled){var t;if(!e.decryptdata.keyId&&null!=(t=e.initSegment)&&t.data){var r=function(e){var t=[];return et(e,(function(e){return t.push(e.subarray(8,24))})),t}(e.initSegment.data);if(r.length){var i=r[0],a=e.decryptdata.uri;if(i.some((function(e){return 0!==e})))this.log("Using keyId found in init segment "+xe(i)+" keyUri: "+a),Nr.setKeyIdForUri(a,i);else{var n=Nr.addKeyIdForUri(a);this.log("Patching empty keyId with "+xe(n)+" keyUri: "+a),i=n}e.decryptdata.keyId=i}}return this.emeController.loadKey(e).then((function(){return{frag:e,keyInfo:{decryptdata:e.decryptdata}}}))}return Promise.reject(Zn(e,w.KEY_LOAD_ERROR,new Error("emeEnabled with controller: "+(this.emeController&&this.config.emeEnabled)+")")))},r.loadKeyHTTP=function(e){var t=this,r=e.decryptdata,i=r.uri;if(!i)return Promise.reject(Zn(e,w.KEY_LOAD_ERROR,new Error('Invalid key URI: "'+i+'"')));var a=this.keyLoaderInfo[i];if(a){if(a.decryptdata.key)return r.key=a.decryptdata.key,a.keyLoadPromise=null,Promise.resolve({frag:e,keyInfo:a});if(a.keyLoadPromise)return a.keyLoadPromise.then((function(t){return r.key=a.decryptdata.key,f(f({},t),{},{frag:e})}))}this.log("Loading"+(r.keyId?" keyId: "+xe(r.keyId):"")+" URI: "+r.uri+" from "+e.type+" "+e.level);var n=this.config,s=new(0,n.loader)(n);e.keyLoader=s;var o=this.keyLoaderInfo[i]={decryptdata:r,keyLoadPromise:null,loader:s};return o.keyLoadPromise=new Promise((function(r,a){var l={type:F.KEY,keyInfo:o,frag:e,responseType:"arraybuffer",url:i},u=n.keyLoadPolicy.default,d={loadPolicy:u,timeout:u.maxLoadTimeMs,maxRetry:0,retryDelay:0,maxRetryDelay:0},h={onSuccess:function(e,i,n,s){var o=n.frag,l=n.keyInfo,u=n.url;if(!o.decryptdata||l!==t.keyLoaderInfo[u])return a(Zn(o,w.KEY_LOAD_ERROR,new Error("after key load, decryptdata unset or changed"),s));l.decryptdata.key=o.decryptdata.key=new Uint8Array(e.data),o.keyLoader=l.loader=l.keyLoadPromise=null,r({frag:o,keyInfo:l})},onError:function(r,n,s,o){t.resetLoader(n),a(Zn(e,w.KEY_LOAD_ERROR,new Error("HTTP Error "+r.code+" loading key "+r.text),s,f({url:i,data:void 0},r)))},onTimeout:function(r,i,n){t.resetLoader(i),a(Zn(e,w.KEY_LOAD_TIMEOUT,new Error("key loading timed out"),n))},onAbort:function(r,i,n){t.resetLoader(i),a(Zn(e,w.INTERNAL_ABORTED,new Error("key loading aborted"),n))}};s.load(l,d,h)}))},r.resetLoader=function(e){var t=e.frag,r=e.keyInfo,i=e.url,a=r.loader;t.keyLoader===a&&(t.keyLoader=r.loader=r.keyLoadPromise=null),delete this.keyLoaderInfo[i],a&&a.destroy()},t}(ie);function Zn(e,t,r,i,a){return void 0===t&&(t=w.KEY_LOAD_ERROR),new Mi({type:x.NETWORK_ERROR,details:t,fatal:!1,frag:e,response:a,error:r,networkDetails:i||null})}function Jn(e){switch(e.type){case F.AUDIO_TRACK:return N.AUDIO;case F.SUBTITLE_TRACK:return N.SUBTITLE;default:return N.MAIN}}function es(e,t){var r=e.url;return void 0!==r&&0!==r.indexOf("data:")||(r=t.url),r}var ts=function(){function e(e){this.hls=void 0,this.loaders=Object.create(null),this.variableList=null,this.onManifestLoaded=this.checkAutostartLoad,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(O.MANIFEST_LOADING,this.onManifestLoading,this),e.on(O.LEVEL_LOADING,this.onLevelLoading,this),e.on(O.AUDIO_TRACK_LOADING,this.onAudioTrackLoading,this),e.on(O.SUBTITLE_TRACK_LOADING,this.onSubtitleTrackLoading,this),e.on(O.LEVELS_UPDATED,this.onLevelsUpdated,this)},t.unregisterListeners=function(){var e=this.hls;e.off(O.MANIFEST_LOADING,this.onManifestLoading,this),e.off(O.LEVEL_LOADING,this.onLevelLoading,this),e.off(O.AUDIO_TRACK_LOADING,this.onAudioTrackLoading,this),e.off(O.SUBTITLE_TRACK_LOADING,this.onSubtitleTrackLoading,this),e.off(O.LEVELS_UPDATED,this.onLevelsUpdated,this)},t.createInternalLoader=function(e){var t=this.hls.config,r=t.pLoader,i=t.loader,a=new(r||i)(t);return this.loaders[e.type]=a,a},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.variableList=null,this.unregisterListeners(),this.destroyInternalLoaders()},t.onManifestLoading=function(e,t){var r=t.url;this.variableList=null,this.load({id:null,level:0,responseType:"text",type:F.MANIFEST,url:r,deliveryDirectives:null,levelOrTrack:null})},t.onLevelLoading=function(e,t){var r=t.id,i=t.level,a=t.pathwayId,n=t.url,s=t.deliveryDirectives,o=t.levelInfo;this.load({id:r,level:i,pathwayId:a,responseType:"text",type:F.LEVEL,url:n,deliveryDirectives:s,levelOrTrack:o})},t.onAudioTrackLoading=function(e,t){var r=t.id,i=t.groupId,a=t.url,n=t.deliveryDirectives,s=t.track;this.load({id:r,groupId:i,level:null,responseType:"text",type:F.AUDIO_TRACK,url:a,deliveryDirectives:n,levelOrTrack:s})},t.onSubtitleTrackLoading=function(e,t){var r=t.id,i=t.groupId,a=t.url,n=t.deliveryDirectives,s=t.track;this.load({id:r,groupId:i,level:null,responseType:"text",type:F.SUBTITLE_TRACK,url:a,deliveryDirectives:n,levelOrTrack:s})},t.onLevelsUpdated=function(e,t){var r=this.loaders[F.LEVEL];if(r){var i=r.context;i&&!t.levels.some((function(e){return e===i.levelOrTrack}))&&(r.abort(),delete this.loaders[F.LEVEL])}},t.load=function(e){var t,r,i,a=this,n=this.hls.config,s=this.getInternalLoader(e);if(s){var l=this.hls.logger,u=s.context;if((null==u?void 0:u.levelOrTrack)===e.levelOrTrack&&(u.url===e.url||u.deliveryDirectives&&!e.deliveryDirectives))return void(u.url===e.url?l.log("[playlist-loader]: ignore "+e.url+" ongoing request"):l.log("[playlist-loader]: ignore "+e.url+" in favor of "+u.url));l.log("[playlist-loader]: aborting previous loader for type: "+e.type),s.abort()}if(r=e.type===F.MANIFEST?n.manifestLoadPolicy.default:o({},n.playlistLoadPolicy.default,{timeoutRetry:null,errorRetry:null}),s=this.createInternalLoader(e),_(null==(t=e.deliveryDirectives)?void 0:t.part)&&(e.type===F.LEVEL&&null!==e.level?i=this.hls.levels[e.level].details:e.type===F.AUDIO_TRACK&&null!==e.id?i=this.hls.audioTracks[e.id].details:e.type===F.SUBTITLE_TRACK&&null!==e.id&&(i=this.hls.subtitleTracks[e.id].details),i)){var d=i.partTarget,h=i.targetduration;if(d&&h){var f=1e3*Math.max(3*d,.8*h);r=o({},r,{maxTimeToFirstByteMs:Math.min(f,r.maxTimeToFirstByteMs),maxLoadTimeMs:Math.min(f,r.maxTimeToFirstByteMs)})}}var c=r.errorRetry||r.timeoutRetry||{},g={loadPolicy:r,timeout:r.maxLoadTimeMs,maxRetry:c.maxNumRetry||0,retryDelay:c.retryDelayMs||0,maxRetryDelay:c.maxRetryDelayMs||0},v={onSuccess:function(e,t,r,i){var n=a.getInternalLoader(r);a.resetInternalLoader(r.type);var s=e.data;t.parsing.start=performance.now(),Kr.isMediaPlaylist(s)||r.type!==F.MANIFEST?a.handleTrackOrLevelPlaylist(e,t,r,i||null,n):a.handleMasterPlaylist(e,t,r,i)},onError:function(e,t,r,i){a.handleNetworkError(t,r,!1,e,i)},onTimeout:function(e,t,r){a.handleNetworkError(t,r,!0,void 0,e)}};s.load(e,g,v)},t.checkAutostartLoad=function(){if(this.hls){var e=this.hls,t=e.config,r=t.autoStartLoad,i=t.startPosition,a=e.forceStartLoad;(r||a)&&(this.hls.logger.log((r?"auto":"force")+" startLoad with configured startPosition "+i),this.hls.startLoad(i))}},t.handleMasterPlaylist=function(e,t,r,i){var a=this,n=this.hls,s=e.data,o=es(e,r),l=Kr.parseMasterPlaylist(s,o);if(l.playlistParsingError)return t.parsing.end=performance.now(),void this.handleManifestParsingError(e,r,l.playlistParsingError,i,t);var u=l.contentSteering,d=l.levels,h=l.iframeVariants,f=l.sessionData,c=l.sessionKeys,g=l.startTimeOffset,v=l.variableList;this.variableList=v,d.forEach((function(e){var t=e.unknownCodecs;if(t)for(var r=a.hls.config.preferManagedMediaSource,i=e.audioCodec,n=e.videoCodec,s=t.length;s--;){var o=t[s];mt(o,"audio",r)?(e.audioCodec=i=i?i+","+o:o,gt.audio[i.substring(0,4)]=2,t.splice(s,1)):mt(o,"video",r)&&(e.videoCodec=n=n?n+","+o:o,gt.video[n.substring(0,4)]=2,t.splice(s,1))}}));var m=Kr.parseMasterPlaylistMedia(s,o,l),p=m.AUDIO,y=void 0===p?[]:p,E=m.SUBTITLES,T=m["CLOSED-CAPTIONS"];y.length&&(y.some((function(e){return!e.url}))||!d[0].audioCodec||d[0].attrs.AUDIO||(this.hls.logger.log("[playlist-loader]: audio codec signaled in quality level, but no embedded audio track signaled, create one"),y.unshift({type:"main",name:"main",groupId:"main",default:!1,autoselect:!1,forced:!1,id:-1,attrs:new Dr({}),bitrate:0,url:""}))),n.trigger(O.MANIFEST_LOADED,{levels:d,audioTracks:y,subtitles:E,iframeVariants:h,captions:T,contentSteering:u,url:o,stats:t,networkDetails:i,sessionData:f,sessionKeys:c,startTimeOffset:g,variableList:v})},t.handleTrackOrLevelPlaylist=function(e,t,r,i,a){var n=this.hls,s=r.id,o=r.level,l=r.type,u=es(e,r),d=_(o)?o:_(s)?s:0,h=Jn(r),f=Kr.parseLevelPlaylist(e.data,u,d,h,0,this.variableList);if(l===F.MANIFEST){var c={attrs:new Dr({}),bitrate:0,details:f,name:"",url:u};f.requestScheduled=t.loading.start+oi(f,0),n.trigger(O.MANIFEST_LOADED,{levels:[c],audioTracks:[],iframeVariants:[],url:u,stats:t,networkDetails:i,sessionData:null,sessionKeys:null,contentSteering:null,startTimeOffset:null,variableList:null})}t.parsing.end=performance.now(),r.levelDetails=f,this.handlePlaylistLoaded(f,e,t,r,i,a)},t.handleManifestParsingError=function(e,t,r,i,a){this.hls.trigger(O.ERROR,{type:x.NETWORK_ERROR,details:w.MANIFEST_PARSING_ERROR,fatal:t.type===F.MANIFEST,url:e.url,err:r,error:r,reason:r.message,response:e,context:t,networkDetails:i,stats:a})},t.handleNetworkError=function(e,t,r,i,a){void 0===r&&(r=!1);var n="A network "+(r?"timeout":"error"+(i?" (status "+i.code+")":""))+" occurred while loading "+e.type;e.type===F.LEVEL?n+=": "+e.level+" id: "+e.id:e.type!==F.AUDIO_TRACK&&e.type!==F.SUBTITLE_TRACK||(n+=" id: "+e.id+' group-id: "'+e.groupId+'"');var s=new Error(n);this.hls.logger.warn("[playlist-loader]: "+n);var o=w.UNKNOWN,l=!1,u=this.getInternalLoader(e);switch(e.type){case F.MANIFEST:o=r?w.MANIFEST_LOAD_TIMEOUT:w.MANIFEST_LOAD_ERROR,l=!0;break;case F.LEVEL:o=r?w.LEVEL_LOAD_TIMEOUT:w.LEVEL_LOAD_ERROR,l=!1;break;case F.AUDIO_TRACK:o=r?w.AUDIO_TRACK_LOAD_TIMEOUT:w.AUDIO_TRACK_LOAD_ERROR,l=!1;break;case F.SUBTITLE_TRACK:o=r?w.SUBTITLE_TRACK_LOAD_TIMEOUT:w.SUBTITLE_LOAD_ERROR,l=!1}u&&this.resetInternalLoader(e.type);var d={type:x.NETWORK_ERROR,details:o,fatal:l,url:e.url,loader:u,context:e,error:s,networkDetails:t,stats:a};if(i){var h=e.url;t&&"url"in t&&(h=t.url),d.response=f({url:h,data:void 0},i)}this.hls.trigger(O.ERROR,d)},t.handlePlaylistLoaded=function(e,t,r,i,a,n){var s=this.hls,o=i.type,l=i.level,u=i.levelOrTrack,d=i.id,h=i.groupId,f=i.deliveryDirectives,c=es(t,i),g=Jn(i),v="number"==typeof i.level&&g===N.MAIN?l:void 0,m=e.playlistParsingError;if(m){if(this.hls.logger.warn(m+" "+e.url),!s.config.ignorePlaylistParsingErrors)return void s.trigger(O.ERROR,{type:x.NETWORK_ERROR,details:w.LEVEL_PARSING_ERROR,fatal:!1,url:c,error:m,reason:m.message,response:t,context:i,level:v,parent:g,networkDetails:a,stats:r});e.playlistParsingError=null}if(e.fragments.length)switch(e.live&&n&&(n.getCacheAge&&(e.ageHeader=n.getCacheAge()||0),n.getCacheAge&&!isNaN(e.ageHeader)||(e.ageHeader=0)),o){case F.MANIFEST:case F.LEVEL:if(v)if(u){if(u!==s.levels[v]){var p=s.levels.indexOf(u);p>-1&&(v=p)}}else v=0;s.trigger(O.LEVEL_LOADED,{details:e,levelInfo:u||s.levels[0],level:v||0,id:d||0,stats:r,networkDetails:a,deliveryDirectives:f,withoutMultiVariant:o===F.MANIFEST,context:i});break;case F.AUDIO_TRACK:s.trigger(O.AUDIO_TRACK_LOADED,{details:e,track:u,id:d||0,groupId:h||"",stats:r,networkDetails:a,deliveryDirectives:f,context:i});break;case F.SUBTITLE_TRACK:s.trigger(O.SUBTITLE_TRACK_LOADED,{details:e,track:u,id:d||0,groupId:h||"",stats:r,networkDetails:a,deliveryDirectives:f,context:i})}else{var y=e.playlistParsingError=new Error("No Segments found in Playlist");s.trigger(O.ERROR,{type:x.NETWORK_ERROR,details:w.LEVEL_EMPTY_ERROR,fatal:!1,url:c,error:y,reason:y.message,response:t,context:i,level:v,parent:g,networkDetails:a,stats:r})}},e}(),rs=function(){function e(t){void 0===t&&(t={}),this.config=void 0,this.userConfig=void 0,this.logger=void 0,this._url=null,this.streamController=void 0,this.coreComponents=void 0,this.networkControllers=void 0,this._emitter=new k,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.audioStreamController=void 0,this.subtititleStreamController=void 0,this.audioTrackController=void 0,this.subtitleTrackController=void 0,this.interstitialsController=void 0,this.iframeController=void 0,this.gapController=void 0,this.emeController=void 0,this.cmcdController=void 0,this._media=null,this._sessionId=void 0,this.triggeringException=void 0,this.started=!1;var r=this.logger=ue(t.debug||!1,"Hls instance",t.loggerId||t.assetPlayerId),i=this.config=Hn(e.DefaultConfig,t,r);this.userConfig=t,i.progressive&&Yn(i,r);var a=i.streamController,n=i.abrController,s=i.bufferController,o=i.capLevelController,l=i.errorController,u=i.fpsController,d=i.id3TrackController,h=i.iframeController,f=i.gapController,c=new l(this),g=this.abrController=new n(this),v=new Li(this),m=i.interstitialsController,p=m?this.interstitialsController=new m(this,e):null,y=s?this.bufferController=new s(this,v):null,E=o?this.capLevelController=new o(this):null,T=u?new u(this):null,S=i.cmcdController,L=S?this.cmcdController=new S(this):null,R=new ts(this),A=i.contentSteeringController,b=A?new A(this):null,D=this.levelController=new jn(this,b),I=d?new d(this):void 0,_=new $n(this.config,this.logger),C=this.streamController=new a(this,v,_),P=this.gapController=f?new f(this,v):void 0;E&&E.setStreamController(C);var x=[R,D,C];p&&x.splice(1,0,p),b&&x.splice(1,0,b),this.networkControllers=x;var w=[g];y&&w.push(y),P&&w.push(P),E&&w.push(E),T&&(T.setStreamController(C),w.push(T)),I&&w.push(I),w.push(v),this.audioTrackController=this.createController(i.audioTrackController,x);var M=i.audioStreamController;M&&x.push(this.audioStreamController=new M(this,v,_)),this.subtitleTrackController=this.createController(i.subtitleTrackController,x);var F=i.subtitleStreamController;F&&x.push(this.subtititleStreamController=new F(this,v,_)),this.createController(i.timelineController,w),_.emeController=this.emeController=this.createController(i.emeController,w),L&&w.push(L),this.latencyController=this.createController(i.latencyController,w),this.coreComponents=w,this.iframeController=h?new h(this,e):void 0,x.push(c);var N=c.onErrorOut;"function"==typeof N&&this.on(O.ERROR,N,c),this.on(O.MANIFEST_LOADED,R.onManifestLoaded,R)}e.isMSESupported=function(){return Dn()},e.isSupported=function(){return function(){if(!Dn())return!1;var e=ve();return"function"==typeof(null==e?void 0:e.isTypeSupported)&&(["avc1.42E01E,mp4a.40.2","av01.0.01M.08","vp09.00.50.08"].some((function(t){return e.isTypeSupported(yt(t,"video"))}))||["mp4a.40.2","fLaC"].some((function(t){return e.isTypeSupported(yt(t,"audio"))})))}()},e.getMediaSource=function(){return ve()};var t=e.prototype;return t.createController=function(e,t){if(e){var r=new e(this);return t&&t.push(r),r}return null},t.on=function(e,t,r){void 0===r&&(r=this),this._emitter.on(e,t,r)},t.once=function(e,t,r){void 0===r&&(r=this),this._emitter.once(e,t,r)},t.removeAllListeners=function(e){this._emitter.removeAllListeners(e)},t.off=function(e,t,r,i){void 0===r&&(r=this),this._emitter.off(e,t,r,i)},t.listeners=function(e){return this._emitter.listeners(e)},t.emit=function(e,t,r){return this._emitter.emit(e,t,r)},t.trigger=function(e,t){if(this.config.debug)return this.emit(e,e,t);try{return this.emit(e,e,t)}catch(t){if(this.logger.error("An internal error happened while handling event "+e+'. Error message: "'+t.message+'". Here is a stacktrace:',t),!this.triggeringException){this.triggeringException=!0;var r=e===O.ERROR;this.trigger(O.ERROR,{type:x.OTHER_ERROR,details:w.INTERNAL_EXCEPTION,fatal:r,event:e,error:t}),this.triggeringException=!1}}return!1},t.listenerCount=function(e){return this._emitter.listenerCount(e)},t.destroy=function(){this.logger.log("destroy"),this.trigger(O.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,this.iframeController=void 0;var e=this.config;e.xhrSetup=e.fetchSetup=void 0,this.userConfig=null},t.attachMedia=function(e){if(!e||"media"in e&&!e.media){var t=new Error("attachMedia failed: invalid argument ("+e+")");this.trigger(O.ERROR,{type:x.OTHER_ERROR,details:w.ATTACH_MEDIA_ERROR,fatal:!0,error:t})}else{this.logger.log("attachMedia"),this._media&&(this.logger.warn("media must be detached before attaching"),this.detachMedia());var r="media"in e,i=r?e.media:e,a=r?e:{media:i};this._media=i,this.trigger(O.MEDIA_ATTACHING,a)}},t.detachMedia=function(){this.logger.log("detachMedia");var e={};this.trigger(O.MEDIA_DETACHING,e),this._media=null,this.trigger(O.MEDIA_DETACHED,e)},t.transferMedia=function(){var e;this._media=null;var t=(null==(e=this.bufferController)?void 0:e.transferMedia())||null,r={transferMedia:t};return this.trigger(O.MEDIA_DETACHING,r),this.trigger(O.MEDIA_DETACHED,r),t},t.loadSource=function(e){var t;this.stopLoad();var r=this.media,i=this._url,a=this._url=I.buildAbsoluteURL(self.location.href,e,{alwaysNormalize:!0});this._autoLevelCapping=-1,this._maxHdcpLevel=null,this.logger.log("loadSource:"+a),r&&i&&(i!==a||null!=(t=this.bufferController)&&t.hasSourceTypes())&&(this.detachMedia(),this.attachMedia(r)),this.trigger(O.MANIFEST_LOADING,{url:e})},t.startLoad=function(e,t){void 0===e&&(e=-1),this.logger.log("startLoad("+e+(t?", <skip seek to start>":"")+")"),this.started=!0,this.resumeBuffering();for(var r=0;r<this.networkControllers.length&&(this.networkControllers[r].startLoad(e,t),this.started&&this.networkControllers);r++);},t.stopLoad=function(){this.logger.log("stopLoad"),this.started=!1;for(var e=0;e<this.networkControllers.length&&(this.networkControllers[e].stopLoad(),!this.started&&this.networkControllers);e++);},t.resumeBuffering=function(){this.bufferingEnabled||(this.logger.log("resume buffering"),this.networkControllers.forEach((function(e){e.resumeBuffering&&e.resumeBuffering()})))},t.pauseBuffering=function(){this.bufferingEnabled&&(this.logger.log("pause buffering"),this.networkControllers.forEach((function(e){e.pauseBuffering&&e.pauseBuffering()})))},t.swapAudioCodec=function(){this.logger.log("swapAudioCodec"),this.streamController.swapAudioCodec()},t.recoverMediaError=function(){this.logger.log("recoverMediaError");var e=this._media,t=this.started,r=null==e?void 0:e.currentTime;this.detachMedia(),e&&(this.attachMedia(e),t&&(r?this.startLoad(r):this.config.autoStartLoad||this.startLoad()))},t.removeLevel=function(e){this.levelController.removeLevel(e)},t.setAudioOption=function(e){var t;return(null==(t=this.audioTrackController)?void 0:t.setAudioOption(e))||null},t.setSubtitleOption=function(e){var t;return(null==(t=this.subtitleTrackController)?void 0:t.setSubtitleOption(e))||null},t.createIFramePlayer=function(e){return null},t.createImageIFramePlayer=function(e){return null},t.getMediaDecodingInfo=function(e,t){void 0===t&&(t=this.allAudioTracks);var r=Gt(t);return ce.getMediaDecodingInfoPromise(e,r,navigator.mediaCapabilities)},a(e,[{key:"url",get:function(){return this._url}},{key:"hasEnoughToStart",get:function(){return this.streamController.hasEnoughToStart}},{key:"startPosition",get:function(){return this.streamController.startPositionValue}},{key:"loadingEnabled",get:function(){return this.started&&this.streamController.state!==Hi.STOPPED}},{key:"bufferingEnabled",get:function(){return this.streamController.bufferingEnabled}},{key:"inFlightFragments",get:function(){var e,t=((e={})[N.MAIN]=this.streamController.inFlightFrag,e);return this.audioStreamController&&(t[N.AUDIO]=this.audioStreamController.inFlightFrag),this.subtititleStreamController&&(t[N.SUBTITLE]=this.subtititleStreamController.inFlightFrag),t}},{key:"sessionId",get:function(){var e=this._sessionId;return e||(e=this._sessionId=function(){try{return crypto.randomUUID()}catch(i){try{var e=URL.createObjectURL(new Blob),t=e.toString();return URL.revokeObjectURL(e),t.slice(t.lastIndexOf("/")+1)}catch(e){var r=(new Date).getTime();return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(function(e){var t=(r+16*Math.random())%16|0;return r=Math.floor(r/16),("x"==e?t:3&t|8).toString(16)}))}}}()),e}},{key:"levels",get:function(){var e=this.levelController.levels;return e||[]}},{key:"latestLevelDetails",get:function(){return this.streamController.getLevelDetails()||null}},{key:"loadLevelObj",get:function(){return this.levelController.loadLevelObj}},{key:"currentLevel",get:function(){return this.streamController.currentLevel},set:function(e){this.logger.log("set currentLevel:"+e),this.levelController.manualLevel=e,this.streamController.immediateLevelSwitch()}},{key:"nextLevel",get:function(){return this.streamController.nextLevel},set:function(e){this.logger.log("set nextLevel:"+e),this.levelController.manualLevel=e,this.streamController.nextLevelSwitch()}},{key:"loadLevel",get:function(){return this.levelController.level},set:function(e){this.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){this.logger.log("set firstLevel:"+e),this.levelController.firstLevel=e}},{key:"startLevel",get:function(){var e=this.levelController.startLevel;return-1===e&&this.abrController.forcedAutoLevel>-1?this.abrController.forcedAutoLevel:e},set:function(e){this.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=this.capLevelController,r=!!e;t&&r!==this.config.capLevelToPlayerSize&&(r?t.startCapping():(t.stopCapping(),this.autoLevelCapping=-1,this.streamController.nextLevelSwitch()),this.config.capLevelToPlayerSize=r)}},{key:"autoLevelCapping",get:function(){return this._autoLevelCapping},set:function(e){this._autoLevelCapping!==e&&(this.logger.log("set autoLevelCapping:"+e),this._autoLevelCapping=e,this.levelController.checkMaxAutoUpdated())}},{key:"bandwidthEstimate",get:function(){var e=this.abrController.bwEstimator;return e?e.getEstimate():NaN},set:function(e){this.abrController.resetEstimator(e)}},{key:"abrEwmaDefaultEstimate",get:function(){var e=this.abrController.bwEstimator;return e?e.defaultEstimate:NaN}},{key:"ttfbEstimate",get:function(){var e=this.abrController.bwEstimator;return e?e.getEstimateTTFB():NaN}},{key:"maxHdcpLevel",get:function(){return this._maxHdcpLevel},set:function(e){(function(e){return It.indexOf(e)>-1})(e)&&this._maxHdcpLevel!==e&&(this._maxHdcpLevel=e,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 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,t=this.levels,r=this.autoLevelCapping,i=this.maxHdcpLevel;if(e=-1===r&&null!=t&&t.length?t.length-1:r,i)for(var a=e;a--;){var n=t[a].attrs["HDCP-LEVEL"];if(n&&n<=i)return a}return e}},{key:"firstAutoLevel",get:function(){return this.abrController.firstAutoLevel}},{key:"nextAutoLevel",get:function(){return this.abrController.nextAutoLevel},set:function(e){this.abrController.nextAutoLevel=e}},{key:"playingDate",get:function(){return this.streamController.currentProgramDateTime}},{key:"mainForwardBufferInfo",get:function(){return this.streamController.getMainFwdBufferInfo()}},{key:"audioForwardBufferInfo",get:function(){var e;return(null==(e=this.audioStreamController)?void 0:e.getFwdBufferInfo())||null}},{key:"maxBufferLength",get:function(){return this.streamController.maxBufferLength}},{key:"allAudioTracks",get:function(){var e=this.audioTrackController;return e?e.allAudioTracks:[]}},{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:"nextAudioTrack",get:function(){var e,t;return null!=(e=null==(t=this.audioStreamController)?void 0:t.nextAudioTrack)?e:-1},set:function(e){var t=this.audioTrackController;t&&(t.nextAudioTrack=e)}},{key:"allSubtitleTracks",get:function(){var e=this.subtitleTrackController;return e?e.allSubtitleTracks:[]}},{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(){var e,t;return null!=(e=null==(t=this.latencyController)?void 0:t.liveSyncPosition)?e:null}},{key:"latency",get:function(){var e;return(null==(e=this.latencyController)?void 0:e.latency)||0}},{key:"maxLatency",get:function(){var e;return(null==(e=this.latencyController)?void 0:e.maxLatency)||0}},{key:"targetLatency",get:function(){var e;return(null==(e=this.latencyController)?void 0:e.targetLatency)||null},set:function(e){this.latencyController&&(this.latencyController.targetLatency=e)}},{key:"drift",get:function(){var e;return(null==(e=this.latencyController)?void 0:e.drift)||null}},{key:"forceStartLoad",get:function(){return this.streamController.forceStartLoad}},{key:"pathways",get:function(){return this.levelController.pathways}},{key:"pathwayPriority",get:function(){return this.levelController.pathwayPriority},set:function(e){this.levelController.pathwayPriority=e}},{key:"bufferedToEnd",get:function(){var e;return!(null==(e=this.bufferController)||!e.bufferedToEnd)}},{key:"interstitialsManager",get:function(){return null}},{key:"iframeVariants",get:function(){var e=this.levelController.iframeVariants;return e||[]}}],[{key:"version",get:function(){return Ln}},{key:"Events",get:function(){return O}},{key:"MetadataSchema",get:function(){return zi}},{key:"ErrorTypes",get:function(){return x}},{key:"ErrorDetails",get:function(){return w}},{key:"DefaultConfig",get:function(){return e.defaultConfig?e.defaultConfig:Vn},set:function(t){e.defaultConfig=t}}])}();return rs.defaultConfig=void 0,rs},"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);
+//# sourceMappingURL=hls.light.min.js.map
new file mode 100644
new file mode 100644
@@ -0,0 +1,167 @@
@@ -0,0 +1,167 @@
+'use strict';
+
+/**
+ * OpenVibe.Surf — Express app factory; server/index.js listens, tests build their own instance with mock upstreams.
+ *
+ * /, /ride, /ride/:map, /watch, /web, /developers the pages (http/pages.js)
+ * /web/go the no-JavaScript task form
+ * /api/v1/watch/feed, /api/v1/ride/maps… Watch and Ride (http/api.js)
+ * /api/v1/web/tasks Surf with Vibe (http/api.js)
+ * /auth/* Network SSO with PKCE (auth/sso.js)
+ * /api/health, /api/ready, /release.json liveness, truthful readiness, release metadata
+ * /robots.txt, /sitemap.xml, /llms.txt crawl artifacts (http/discovery.js)
+ *
+ * Stateless: no database, no Valkey, no Events. The dependencies are OpenVibe.Network (sign-in), OpenVibe.Live
+ * (Watch), OpenVibe.Games (Ride) and OpenVibe.Actor (Web); the pages degrade honestly without any of them.
+ */
+const path = require('path');
+const express = require('express');
+const helmet = require('helmet');
+const cookieParser = require('cookie-parser');
+const rateLimit = require('express-rate-limit');
+const contracts = require('openvibe-contracts');
+const cache = require('openvibe-shared/cache-policy');
+const releaseLib = require('openvibe-shared/release');
+const metricsLib = require('openvibe-shared/metrics');
+const serve = require('openvibe-shared/serve');
+
+const configLib = require('./config');
+const { createKeyStore } = require('./auth/keys');
+const { createSso } = require('./auth/sso');
+const { createWatchFeed } = require('./watch/feed');
+const { createRide } = require('./ride/games');
+const { createWeb } = require('./web/tasks');
+const { createApi } = require('./http/api');
+const { createPageRoutes } = require('./http/pages');
+const { createDiscoveryRoutes } = require('./http/discovery');
+const { createServiceReadiness } = require('./observability');
+const { PUBLIC_DIR, assetVersion } = require('./assets');
+const views = require('./views');
+
+const VERSION = require('../package.json').version;
+
+/**
+ * opts: config, fetchImpl, log, now, release
+ * A test passes a fetchImpl that talks to in-process Network/Live/Games/Actor stand-ins.
+ */
+function createApp(opts = {}) {
+ const config = opts.config || configLib.load();
+ const log = opts.log || console;
+ const fetchImpl = opts.fetchImpl || globalThis.fetch;
+ const now = opts.now || (() => Date.now());
+
+ const keys = createKeyStore({ config, fetchImpl, log });
+ const sso = createSso({ config, keys, fetchImpl, log, now });
+ const feed = createWatchFeed({ config, fetchImpl, log, now });
+ const ride = createRide({ config, fetchImpl, log, now });
+ const web = createWeb({ config, fetchImpl, log });
+ const ctx = { config, keys, sso, feed, ride, web, log };
+
+ const release = opts.release || releaseLib.createRelease({ service: 'surf', root: path.join(__dirname, '..') });
+ views.setRelease(release.release);
+ views.configure({ baseUrl: config.baseUrl });
+
+ const app = express();
+ app.disable('x-powered-by');
+ app.set('trust proxy', config.trustProxy);
+ app.locals.ctx = ctx;
+
+ const metrics = metricsLib.instrument(app, { service: 'surf', release: release.release });
+ app.locals.metrics = metrics.registry;
+ release.mount(app, { registry: metrics.registry });
+
+ // ── Machine endpoints ───────────────────────────────────
+ app.get('/api/health', (_req, res) => res.json({ status: 'ok', service: 'openvibe-surf', version: VERSION }));
+ const readiness = createServiceReadiness({ config, feed, ride, keys, release: release.release, fetchImpl });
+ app.get('/api/ready', readiness.handler);
+
+ app.use(contracts.http.middleware());
+ app.use(helmet({
+ contentSecurityPolicy: {
+ directives: {
+ defaultSrc: ["'self'"],
+ // The OpenVibe Frame (theme-loader, navbar, footer) comes from the Network; the inline init is ours.
+ scriptSrc: ["'self'", "'unsafe-inline'", 'https://openvibe.network', 'https://static.cloudflareinsights.com'],
+ styleSrc: ["'self'", "'unsafe-inline'", 'https://openvibe.network', 'https://fonts.googleapis.com', 'https://cdnjs.cloudflare.com'],
+ fontSrc: ["'self'", 'data:', 'https://fonts.gstatic.com', 'https://cdnjs.cloudflare.com'],
+ // Card thumbnails and avatars are https images on the media edge; the catalogue may carry any https image.
+ imgSrc: ["'self'", 'data:', 'https:'],
+ // The footer's release line hears OpenVibe.Events; Cloudflare's beacon reports page timings.
+ // hls.js fetches Media's playlists and segments, which may redirect to R2 or B2 (as openvibe.video's CSP).
+ connectSrc: ["'self'", 'https://openvibe.network', 'https://cloudflareinsights.com', 'https://openvibe.events', 'https://openvibe.media', 'https://edge.openvibe.media', 'https://*.r2.cloudflarestorage.com', 'https://*.backblazeb2.com'],
+ // Ride embeds a Games map; Watch may embed Live's player when Live has one.
+ frameSrc: ["'self'", 'https://openvibe.games', 'https://openvibe.live'],
+ // HLS clips play from the media edge (and a blob: URL a player may build).
+ mediaSrc: ["'self'", 'https://openvibe.media', 'https://edge.openvibe.media', 'https://*.r2.cloudflarestorage.com', 'https://*.backblazeb2.com', 'blob:'],
+ frameAncestors: ["'none'"],
+ objectSrc: ["'none'"],
+ baseUri: ["'self'"],
+ formAction: ["'self'", 'https://openvibe.network'],
+ },
+ },
+ frameguard: { action: 'deny' },
+ crossOriginEmbedderPolicy: false,
+ crossOriginResourcePolicy: { policy: 'same-site' },
+ referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
+ }));
+ app.use(cookieParser());
+
+ // ── Per-IP budget for the API and sign-in (120 a minute) ─
+ const limiter = rateLimit({
+ windowMs: 60_000,
+ limit: config.rateLimitPerMinute || 120,
+ standardHeaders: true,
+ legacyHeaders: false,
+ handler: (req, res) => contracts.http.sendProblem(res, 429, 'rate_limited', {
+ detail: 'too many requests from this address; retry shortly',
+ ctx: req.ov,
+ }),
+ });
+ app.use(['/api', '/auth'], limiter);
+
+ // ── Who is signed in, then sign-in itself ───────────────
+ app.use(sso.middleware());
+ app.use('/auth', sso.routes());
+
+ // ── Static assets (content-hashed ?v= → immutable) ──────
+ app.use('/shared', serve.handler());
+ const setAssetHeaders = (res, filePath) => {
+ const rel = path.relative(PUBLIC_DIR, filePath).split(path.sep).join('/');
+ const v = res.req && res.req.query && res.req.query.v;
+ res.setHeader('Cache-Control', cache.assetHeaders(rel, { hashed: Boolean(v) && v === assetVersion(rel) }));
+ };
+ app.use('/css', express.static(path.join(PUBLIC_DIR, 'css'), { index: false, redirect: false, setHeaders: setAssetHeaders }));
+ app.use('/js', express.static(path.join(PUBLIC_DIR, 'js'), { index: false, redirect: false, setHeaders: setAssetHeaders }));
+ // hls.js 1.7.3 light (the same file openvibe.video serves), loaded by js/surf.js only where a browser cannot play HLS.
+ app.use('/vendor', express.static(path.join(PUBLIC_DIR, 'vendor'), { index: false, redirect: false, setHeaders: setAssetHeaders }));
+
+ // ── API: Watch, Ride and Web ────────────────────────────
+ app.use('/api', express.json({ limit: '32kb' }));
+ app.use('/api', createApi(ctx));
+ app.use('/api', (req, res) => contracts.http.sendProblem(res, 404, 'route.not_found', {
+ detail: 'No such API route.',
+ ctx: req.ov,
+ }));
+
+ // ── Crawl artifacts ─────────────────────────────────────
+ app.use(createDiscoveryRoutes(ctx));
+
+ // ── Pages (and the no-JavaScript task form) ─────────────
+ app.use(createPageRoutes(ctx));
+
+ // eslint-disable-next-line no-unused-vars
+ app.use((err, req, res, _next) => {
+ // Never log request bodies (a query is personal, and a token can be in a header).
+ log.error('[OpenVibe.Surf]', err && err.message ? err.message.slice(0, 300) : err);
+ if (res.headersSent) return;
+ res.set('Cache-Control', 'no-store');
+ if (err && err.type === 'entity.parse.failed') return contracts.http.sendProblem(res, 400, 'request.malformed_json', { ctx: req.ov });
+ if (err && err.type === 'entity.too.large') return req.path.startsWith('/api/') ? contracts.http.sendProblem(res, 413, 'request.too_large', { ctx: req.ov }) : res.status(413).type('text/plain').send('That request was too large.');
+ if (req.path.startsWith('/api/')) return contracts.http.sendProblem(res, 500, 'internal.error', { detail: 'Internal error', ctx: req.ov });
+ return res.status(500).type('text/plain').send('Something went wrong on our side. Try again in a moment.');
+ });
+
+ return { app, ctx };
+}
+
+module.exports = { createApp };
new file mode 100644
new file mode 100644
@@ -0,0 +1,30 @@
@@ -0,0 +1,30 @@
+'use strict';
+
+/**
+ * Content-addressed URLs for this site's own assets: /css/surf.css?v=<first 10 hex of SHA-256>. The static
+ * middleware treats a matching ?v= as immutable (openvibe-shared/cache-policy); any other URL gets the short
+ * public window, because those bytes may change under the same URL.
+ */
+const crypto = require('crypto');
+const fs = require('fs');
+const path = require('path');
+
+const PUBLIC_DIR = path.join(__dirname, '..', 'public');
+const hashes = new Map();
+
+function assetVersion(rel) {
+ const key = String(rel || '').replace(/^\/+/, '');
+ if (hashes.has(key)) return hashes.get(key);
+ let version = 'dev';
+ try {
+ version = crypto.createHash('sha256').update(fs.readFileSync(path.join(PUBLIC_DIR, key))).digest('hex').slice(0, 10);
+ } catch {
+ /* a missing asset stays at a stable placeholder */
+ }
+ hashes.set(key, version);
+ return version;
+}
+
+const asset = (rel) => `/${String(rel || '').replace(/^\/+/, '')}?v=${assetVersion(rel)}`;
+
+module.exports = { PUBLIC_DIR, assetVersion, asset };
new file mode 100644
new file mode 100644
@@ -0,0 +1,110 @@
@@ -0,0 +1,110 @@
+'use strict';
+
+/**
+ * The Network's RS256 signing key, fetched once from GET /api/.well-known/jwks on the configured internal URL and
+ * kept fresh by openvibe-sdk/auth's JWKS client. Tokens are verified offline against it: the person's session token
+ * (sign-in).
+ *
+ * The SDK keeps one client per URL: the last good keys through outages, exponential backoff, a rotation honoured at
+ * once, unknown-kid floods throttled, the refresh timer unref'd. This module is a thin shim so the rest of
+ * OpenVibe.Surf (the readiness check, the SSO middleware) can keep talking to `keys.get()` / `keys.ensure()` /
+ * `keys.loaded()`, while verification goes through the SDK's verifyUserToken / verifyAppToken with `jwks: <url>`.
+ *
+ * Nothing the SDK says about a failed key fetch leaves this module: its message names the internal JWKS URL and the
+ * fetch error, so token.no_key answers 'signing key not loaded yet' (and an error without a code 'token does not
+ * verify'). Reasons about the token itself (audience, expiry, signature) are kept. Every rejection is logged.
+ */
+const sdk = require('openvibe-sdk/auth');
+
+/** Public reasons. The SDK's own text (the JWKS URL, the fetch error) never goes over the wire. */
+const KEY_UNAVAILABLE = 'signing key not loaded yet';
+const DOES_NOT_VERIFY = 'token does not verify';
+
+/**
+ * What a caller may be told. Only token.no_key carries the SDK's fetch text (the internal JWKS URL and the connect
+ * error); every other code's message is about the token itself ('not for openvibe.surf', 'expired'), which a
+ * developer needs, so it is kept.
+ */
+function publicReason(err) {
+ const code = err && err.code;
+ if (!code || code === 'token.no_key') return code ? KEY_UNAVAILABLE : DOES_NOT_VERIFY;
+ return (err.message && String(err.message).slice(0, 200)) || DOES_NOT_VERIFY;
+}
+
+/** Resolve the JWKS URL for the configured Network (internal first, then public). */
+function jwksUrl(config) {
+ return (config.networkInternalUrl || config.networkUrl) + '/api/.well-known/jwks';
+}
+
+/**
+ * `keys` is what app.js hands the rest of the service: a facade over the SDK's jwksClient. A synchronous caller
+ * wants a PEM, so we mirror the cached first key as PEM — refreshed every time the SDK gives us keys (ensure(),
+ * verifyUser(), verifyApp()).
+ */
+function createKeyStore({ config, fetchImpl = globalThis.fetch, log = console }) {
+ const url = jwksUrl(config);
+ const client = sdk.jwksClient(url, { fetch: fetchImpl, log });
+ let mirror = null; // { pem, count } — the first cached key as PEM
+
+ async function refreshMirror() {
+ try {
+ const ks = await client.keys();
+ const first = ks && ks[0];
+ mirror = first ? { pem: first.key.export({ type: 'spki', format: 'pem' }), count: ks.length } : null;
+ } catch (err) { log.warn(`[OpenVibe.Surf] JWKS mirror refresh failed: ${err && err.message || err}`); mirror = null; }
+ }
+ /** First fetch (or wait out the backoff window). Refreshes the mirror. */
+ async function ensure() { await refreshMirror(); }
+ /** Cached public key as PEM — for openvibe-contracts' verifyServiceToken (synchronous). */
+ function get() { return mirror ? mirror.pem : null; }
+ function loaded() { return Boolean(mirror); }
+
+ /** The SDK's `now` is a NUMBER of milliseconds (it does Math.floor(now / 1000)); a function disables every expiry check. */
+ const asMs = (v) => (typeof v === 'function' ? v() : (typeof v === 'number' ? v : Date.now()));
+
+ /** Verify a user (session) token via the SDK. Returns { ok, claims } | { ok: false, reason, code, expired }. */
+ async function verifyUser(token, { issuer, audience, now = Date.now() } = {}) {
+ try {
+ const claims = await sdk.verifyUserToken(token, { jwks: url, issuer, audience, now: asMs(now), log });
+ await refreshMirror();
+ return { ok: true, claims };
+ } catch (err) {
+ await refreshMirror();
+ const code = err && err.code;
+ log.warn(`[OpenVibe.Surf] user token rejected (${code || 'error'}):`, (err && err.message) || err);
+ return { ok: false, reason: publicReason(err), code, expired: code === 'token.expired' };
+ }
+ }
+ /** Verify an app token. Same shape and same fixed reasons. */
+ async function verifyApp(token, { issuer, audience, acceptSandbox = true, now = Date.now() } = {}) {
+ try {
+ const claims = await sdk.verifyAppToken(token, { jwks: url, issuer, audience, acceptSandbox, now: asMs(now), log });
+ await refreshMirror();
+ return { ok: true, claims };
+ } catch (err) {
+ await refreshMirror();
+ const code = err && err.code;
+ log.warn(`[OpenVibe.Surf] app token rejected (${code || 'error'}):`, (err && err.message) || err);
+ return { ok: false, reason: publicReason(err), code };
+ }
+ }
+
+ /**
+ * The PEM for the key a token's header names (a rotation publishes two keys: the first one is not always it), for
+ * openvibe-contracts' synchronous guard. The SDK refetches for an unknown kid (at most every 30 s). Null when no
+ * key is loaded; a token that names no kid gets the first key.
+ */
+ async function pemForToken(token) {
+ let kid = null;
+ try { kid = JSON.parse(Buffer.from(String(token).split('.')[0], 'base64url').toString('utf8')).kid || null; } catch { /* the guard refuses it */ }
+ try {
+ const ks = await client.keysForKid(kid);
+ const k = (kid && ks.find((x) => x.kid === kid)) || ks[0];
+ return k ? k.key.export({ type: 'spki', format: 'pem' }) : null;
+ } catch { return null; }
+ }
+
+ return { ensure, get, loaded, status: client.status, verifyUser, verifyApp, pemForToken, client };
+}
+
+module.exports = { createKeyStore, jwksUrl };
new file mode 100644
new file mode 100644
@@ -0,0 +1,282 @@
@@ -0,0 +1,282 @@
+'use strict';
+
+/**
+ * Sign-in with OpenVibe.Network: OAuth 2 authorization code with PKCE (S256), as OAuth client `surf`.
+ *
+ * GET /auth/login → Network /oauth/authorize?…&code_challenge=…&code_challenge_method=S256
+ * (?silent=1 adds prompt=none; ?next= a same-site path)
+ * GET /auth/callback → state check, server-side code exchange (client secret + code_verifier)
+ * GET /auth/logout → clear cookies, revoke the refresh token (best effort)
+ * GET /auth/me → { user } for the shared navbar (it cannot read the httpOnly cookie); { user: null } for a guest
+ *
+ * The person's Network access token lives in an httpOnly cookie (surf_at) no script can read: with it, a script
+ * could act as them. The refresh token (surf_rt) is httpOnly too. Neither is ever stored server-side, rendered
+ * or logged.
+ */
+const crypto = require('crypto');
+const express = require('express');
+const cache = require('openvibe-shared/cache-policy');
+
+const ACCESS_COOKIE = 'surf_at';
+const REFRESH_COOKIE = 'surf_rt';
+const HINT_COOKIE = 'ov_sso_hint';
+const FLOW_COOKIE = 'surf_oauth';
+
+function pkcePair() {
+ const verifier = crypto.randomBytes(32).toString('base64url'); // 43 characters
+ const challenge = crypto.createHash('sha256').update(verifier).digest('base64url');
+ return { verifier, challenge };
+}
+
+/** This site's own origins: BASE_URL (the one origin OpenVibe.Surf is served from). */
+function ownOrigins(config) {
+ const out = new Set();
+ for (const u of [config && config.baseUrl]) {
+ try { if (u) out.add(new URL(u).origin); } catch { /* not a URL */ }
+ }
+ return out;
+}
+
+/**
+ * Same-site relative paths only (never protocol-relative, never another origin). An absolute URL on one of this
+ * site's own origins counts as its path: the shared navbar's silent sign-in sends the whole page address, and a
+ * person signed in quietly should land on the page they asked for.
+ */
+function sanitizeNext(next, config) {
+ // Browsers drop tab and newline characters from a URL and read a backslash as "/": "/<TAB>/evil.com" would
+ // leave the site. A next with any control character or backslash goes home.
+ if (typeof next === 'string' && /[\u0000-\u001f\u007f\\]/.test(next)) return '/';
+ if (typeof next === 'string' && /^https?:\/\//i.test(next)) {
+ let u = null;
+ try { u = new URL(next); } catch { return '/'; }
+ if (!ownOrigins(config).has(u.origin)) return '/';
+ next = `${u.pathname}${u.search}${u.hash}`;
+ }
+ if (typeof next !== 'string' || !/^\/(?!\/|\\)/.test(next) || next.length > 500) return '/';
+ return next;
+}
+
+function authorizeUrl(config, { state, challenge, silent = false }) {
+ const q = new URLSearchParams({
+ response_type: 'code',
+ client_id: config.oauth.clientId,
+ redirect_uri: config.oauth.redirectUri,
+ scope: config.oauth.scope,
+ state,
+ code_challenge: challenge,
+ code_challenge_method: 'S256',
+ });
+ if (silent) q.set('prompt', 'none');
+ return `${config.networkPublicUrl || config.networkUrl}/oauth/authorize?${q.toString()}`;
+}
+
+function createSso({ config, keys, fetchImpl = globalThis.fetch, now = () => Date.now(), log = console }) {
+ const cookieBase = () => ({ sameSite: 'lax', secure: config.cookies.secure, httpOnly: true, path: '/' });
+
+ function setSession(res, data) {
+ res.cookie(ACCESS_COOKIE, data.access_token, { ...cookieBase(), maxAge: 24 * 60 * 60 * 1000 });
+ if (data.refresh_token) res.cookie(REFRESH_COOKIE, data.refresh_token, { ...cookieBase(), maxAge: 30 * 24 * 60 * 60 * 1000 });
+ res.cookie(HINT_COOKIE, 'account', { sameSite: 'lax', secure: config.cookies.secure, httpOnly: false, path: '/', maxAge: 365 * 24 * 60 * 60 * 1000 });
+ }
+ function clearSession(res) {
+ res.clearCookie(ACCESS_COOKIE, cookieBase());
+ res.clearCookie(REFRESH_COOKIE, cookieBase());
+ }
+
+ /** POST the Network token endpoint (internal URL). Throws { status, error } on refusal. */
+ async function tokenGrant(fields) {
+ const body = new URLSearchParams({ client_id: config.oauth.clientId, client_secret: config.oauth.clientSecret, ...fields });
+ let res;
+ try {
+ res = await fetchImpl(`${config.networkInternalUrl || config.networkUrl}/oauth/token`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded', Accept: 'application/json' },
+ body, signal: AbortSignal.timeout(10_000),
+ });
+ } catch (err) {
+ throw Object.assign(new Error('OpenVibe.Network did not answer'), { status: 502, error: err && err.name === 'TimeoutError' ? 'timeout' : 'unreachable' });
+ }
+ const data = await res.json().catch(() => null);
+ if (!res.ok || !data || !data.access_token) {
+ throw Object.assign(new Error((data && (data.error_description || data.error)) || `token endpoint ${res.status}`),
+ { status: res.status, error: (data && data.error) || 'invalid_grant' });
+ }
+ return data;
+ }
+
+ /**
+ * An OpenVibe.Surf session is a Network SESSION token: audience openvibe.network and a person. The Network
+ * signs FedCM assertions (audience = any owned-zone origin that asked), app/service tokens and
+ * internal tokens with the same key and issuer; none of them may become a session here.
+ */
+ async function verifySession(token) {
+ const v = await keys.verifyUser(token, { issuer: config.networkIssuer || config.networkPublicUrl || config.networkUrl, audience: config.oauth.sessionAudience, now: now() });
+ const c = v.claims;
+ if (c && (c.typ === 'fedcm' || c.actor_type !== undefined)) return { ok: false, reason: 'not a session token' };
+ return v;
+ }
+
+ function viewerFromClaims(claims, token) {
+ const subject = typeof claims.subject_id === 'string' ? claims.subject_id : null;
+ const role = typeof claims.role === 'string' ? claims.role : 'user';
+ return {
+ kind: 'user',
+ subject,
+ username: typeof claims.username === 'string' ? claims.username.slice(0, 64) : null,
+ displayName: typeof claims.display_name === 'string' ? claims.display_name.slice(0, 80) : (typeof claims.username === 'string' ? claims.username.slice(0, 64) : 'you'),
+ role,
+ // The navbar's avatar and colour (Network's token claims; the avatar is an https image on openvibe.media).
+ avatarUrl: typeof claims.avatar_url === 'string' && /^https:\/\/[^\s"'<>]{1,500}$/.test(claims.avatar_url) ? claims.avatar_url : null,
+ profileColor: typeof claims.profile_color === 'string' && /^#[0-9a-fA-F]{3,8}$/.test(claims.profile_color) ? claims.profile_color : null,
+ // The person's Network access token: used for server-side Network calls, never rendered.
+ token,
+ };
+ }
+
+ /** One refresh per request, however many calls in it see a 401 (refresh tokens rotate). */
+ async function refresh(req, res) {
+ if (!req.sessionRefresh) req.sessionRefresh = await doRefresh(req, res);
+ return req.sessionRefresh;
+ }
+
+ async function doRefresh(req, res) {
+ const rt = req.cookies && req.cookies[REFRESH_COOKIE];
+ if (!rt) return null;
+ try {
+ const data = await tokenGrant({ grant_type: 'refresh_token', refresh_token: rt });
+ setSession(res, data);
+ const v = await verifySession(data.access_token);
+ return v.ok ? viewerFromClaims(v.claims, data.access_token) : null;
+ } catch (err) {
+ if (err.status && err.status < 500) clearSession(res);
+ return null;
+ }
+ }
+
+ /** req.viewer for every page: the signed-in person, refreshed when their token expired. */
+ function middleware() {
+ return async (req, res, next) => {
+ req.viewer = { kind: 'anonymous' };
+ const token = req.cookies && req.cookies[ACCESS_COOKIE];
+ if (!token) return next();
+ await keys.ensure();
+ const v = await verifySession(token);
+ if (v.ok) { req.viewer = viewerFromClaims(v.claims, token); return next(); }
+ if (v.expired) {
+ const fresh = await refresh(req, res);
+ if (fresh) { req.viewer = fresh; return next(); }
+ }
+ if (keys.loaded()) clearSession(res);
+ next();
+ };
+ }
+
+ /**
+ * Run fn(token) as the viewer; when the Network answers 401 (expired or revoked session), refresh
+ * once and run it again. Other failures are returned to the caller unchanged.
+ */
+ async function asViewer(req, res, fn) {
+ try {
+ return await fn(req.viewer.token);
+ } catch (err) {
+ if (!err || err.status !== 401) throw err;
+ const fresh = await refresh(req, res);
+ if (!fresh) throw err;
+ req.viewer = fresh;
+ return fn(fresh.token);
+ }
+ }
+
+ function routes() {
+ const r = express.Router();
+ const flowOpts = () => ({ ...cookieBase(), path: '/auth', maxAge: 10 * 60 * 1000 });
+
+ r.get('/login', (req, res) => {
+ const silent = Boolean(req.query.silent) && req.query.silent !== '0';
+ const state = crypto.randomBytes(16).toString('hex');
+ const { verifier, challenge } = pkcePair();
+ res.cookie(FLOW_COOKIE, JSON.stringify({ state, verifier, next: sanitizeNext(req.query.next, config), silent }), flowOpts());
+ res.set('Cache-Control', cache.htmlHeaders({ private: true }));
+ res.redirect(authorizeUrl(config, { state, challenge, silent }));
+ });
+
+ r.get('/callback', async (req, res) => {
+ res.set('Cache-Control', cache.htmlHeaders({ private: true }));
+ let flow = null;
+ try { flow = JSON.parse(req.cookies[FLOW_COOKIE] || 'null'); } catch { flow = null; }
+ res.clearCookie(FLOW_COOKIE, { ...cookieBase(), path: '/auth' });
+ const next = flow ? sanitizeNext(flow.next, config) : '/';
+ const { code, state, error } = req.query;
+ if (error) {
+ if ((flow && flow.silent) || error === 'login_required' || error === 'interaction_required') return res.redirect(next);
+ return res.status(400).type('text/plain').send(`Sign-in was not completed (${String(error).slice(0, 60)}).`);
+ }
+ if (!flow || typeof flow.state !== 'string' || typeof flow.verifier !== 'string' || typeof state !== 'string'
+ || state.length !== flow.state.length || !crypto.timingSafeEqual(Buffer.from(state), Buffer.from(flow.state))) {
+ return res.status(400).type('text/plain').send('Sign-in state did not match. Please try signing in again.');
+ }
+ if (!code) return res.status(400).type('text/plain').send('The Network sent no authorization code.');
+ try {
+ const data = await tokenGrant({ grant_type: 'authorization_code', code: String(code), redirect_uri: config.oauth.redirectUri, code_verifier: flow.verifier });
+ setSession(res, data);
+ // No Clear-Site-Data here or on sign-out: Chrome holds such an answer (and the cookies it sets) until the
+ // cache is cleared, which on phones left people on the account chooser with no session.
+ return res.redirect(next);
+ } catch (err) {
+ log.warn('[OpenVibe.Surf] sign-in exchange failed:', err.status || '', err.error || '');
+ return res.status(err.status && err.status < 500 ? 400 : 502).type('text/plain')
+ .send(err.status && err.status < 500 ? 'The Network refused the sign-in code. Please sign in again.' : 'Sign-in failed: OpenVibe.Network did not answer. Please try again.');
+ }
+ });
+
+ // Sign-out ends the session, so another site must not be able to trigger it (a cross-site
+ // <img src=/auth/logout> or link). A same-origin navigation or form, or the address bar
+ // (Sec-Fetch-Site none), signs out directly; anything else gets a confirm button (POST).
+ const sameOriginRequest = (req) => {
+ const site = String(req.get('sec-fetch-site') || '');
+ if (site) return site === 'same-origin' || (site === 'none' && req.method === 'GET');
+ const origin = req.get('origin');
+ if (origin) return origin === `${req.protocol}://${req.get('host')}`;
+ return req.method === 'GET'; // no fetch metadata and no Origin: an old browser navigating
+ };
+ r.get('/logout', (req, res, next) => {
+ if (sameOriginRequest(req)) return next();
+ res.set('Cache-Control', cache.htmlHeaders({ private: true }));
+ const q = req.query.next ? `?next=${encodeURIComponent(sanitizeNext(req.query.next, config))}` : '';
+ res.type('html').send(`<!doctype html><meta charset="utf-8"><title>Sign out</title><meta name="viewport" content="width=device-width,initial-scale=1">`
+ + `<form method="post" action="/auth/logout${q}" style="font:16px system-ui;margin:3em auto;max-width:24em;text-align:center">`
+ + `<p>Sign out of OpenVibe.Surf?</p><button type="submit">Sign out</button> <a href="/">Cancel</a></form>`);
+ });
+ r.post('/logout', (req, res, next) => (sameOriginRequest(req) ? next() : res.status(403).type('text/plain').send('Sign-out must come from this site.')));
+ r.all('/logout', async (req, res) => {
+ const rt = req.cookies && req.cookies[REFRESH_COOKIE];
+ if (rt) {
+ fetchImpl(`${config.networkInternalUrl || config.networkUrl}/oauth/revoke`, {
+ method: 'POST', headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ client_id: config.oauth.clientId, client_secret: config.oauth.clientSecret, token: rt }),
+ signal: AbortSignal.timeout(3000),
+ }).catch(() => {});
+ }
+ clearSession(res);
+ res.cookie(HINT_COOKIE, 'guest', { sameSite: 'lax', secure: config.cookies.secure, httpOnly: false, path: '/', maxAge: 365 * 24 * 60 * 60 * 1000 });
+ res.redirect(303, sanitizeNext(req.query.next, config));
+ });
+
+ r.get('/me', (req, res) => {
+ res.set('Cache-Control', cache.htmlHeaders({ private: true }));
+ // No session at all (a guest: no access or refresh cookie) is signed out, not an error: the shared
+ // navbar asks this on every page view, and a 401 logged a console error on each. A session cookie that
+ // is present but invalid or expired still answers 401.
+ const c = req.cookies || {};
+ if (!c[ACCESS_COOKIE] && !c[REFRESH_COOKIE] && !req.get('authorization')) return res.json({ user: null });
+ const v = req.viewer;
+ if (!v || v.kind !== 'user') return res.status(401).json({ error: 'Not signed in' });
+ res.json({ user: { username: v.username, display_name: v.displayName, subject_id: v.subject, role: v.role, avatar_url: v.avatarUrl || null, profile_color: v.profileColor || null } });
+ });
+ return r;
+ }
+
+ return { middleware, routes, asViewer, tokenGrant, pkcePair };
+}
+
+module.exports = { createSso, pkcePair, sanitizeNext, ownOrigins, authorizeUrl, ACCESS_COOKIE, REFRESH_COOKIE };
new file mode 100644
new file mode 100644
@@ -0,0 +1,75 @@
@@ -0,0 +1,75 @@
+'use strict';
+
+/**
+ * OpenVibe.Surf configuration. Every value comes from the environment (production:
+ * /etc/openvibe/surf.env, see .env.example). Only environment variable NAMES appear in code and docs; secrets are
+ * never logged.
+ *
+ * load(env) is pure so tests can build a config without touching process.env.
+ */
+require('dotenv').config();
+
+const trim = (s) => String(s || '').replace(/\/+$/, '');
+const int = (v, def) => (Number.isFinite(parseInt(v, 10)) ? parseInt(v, 10) : def);
+const atLeast = (v, min, def) => Math.max(min, int(v, def));
+
+function load(env = process.env) {
+ const nodeEnv = env.NODE_ENV || 'development';
+ const isProduction = nodeEnv === 'production';
+ const port = int(env.PORT, 5110);
+ const baseUrl = trim(env.BASE_URL || (isProduction ? 'https://openvibe.surf' : `http://localhost:${port}`));
+ // The Network's public origin is where a browser is sent to authorize. Its internal origin is where this
+ // service exchanges the code and fetches the JWKS; neither a person's browser nor their token talks to it
+ // directly.
+ const networkPublicUrl = trim(env.OV_NETWORK_URL || 'https://openvibe.network');
+ const networkInternalUrl = trim(env.OV_NETWORK_INTERNAL_URL || 'http://127.0.0.1:4000');
+
+ return {
+ service: 'surf',
+ name: 'OpenVibe.Surf',
+ port,
+ host: env.HOST || '127.0.0.1',
+ nodeEnv,
+ isProduction,
+ baseUrl,
+ trustProxy: env.TRUST_PROXY != null ? Number(env.TRUST_PROXY) : 2,
+
+ // OpenVibe.Network: sign-in (OAuth client `surf`, PKCE S256) and its JWKS.
+ networkUrl: networkInternalUrl,
+ networkPublicUrl,
+ networkInternalUrl,
+ networkIssuer: trim(env.OV_NETWORK_ISSUER || networkPublicUrl),
+
+ // The three rooms this front joins. `public` origins are what a person's browser is sent to; the internal
+ // origins are what this process may call. A person's Network token only ever goes to the configured Actor.
+ liveInternalUrl: trim(env.OV_LIVE_INTERNAL_URL || 'http://127.0.0.1:3000'),
+ livePublicUrl: trim(env.SURF_LIVE_PUBLIC_URL || 'https://openvibe.live'),
+ gamesInternalUrl: trim(env.OV_GAMES_INTERNAL_URL || 'http://127.0.0.1:8000'),
+ gamesPublicUrl: trim(env.SURF_GAMES_PUBLIC_URL || 'https://openvibe.games'),
+ actorInternalUrl: trim(env.OV_ACTOR_INTERNAL_URL || 'http://127.0.0.1:4950'),
+ actorPublicUrl: trim(env.SURF_ACTOR_PUBLIC_URL || 'https://openvibe.actor'),
+
+ // Watch: the merged feed is cached 30 s; each Live source gets 2 s before it is left out.
+ watchCacheMs: Math.max(0, int(env.SURF_WATCH_CACHE_MS, 30_000)),
+ watchSourceTimeoutMs: atLeast(env.SURF_WATCH_SOURCE_TIMEOUT_MS, 200, 2000),
+ // Ride: maps are cached 60 s; Games has a little longer than one Live source to answer.
+ rideCacheMs: Math.max(0, int(env.SURF_RIDE_CACHE_MS, 60_000)),
+ gamesTimeoutMs: atLeast(env.SURF_GAMES_TIMEOUT_MS, 200, 4000),
+ // Web: Actor creates a task quickly, but a cold allowance check may take a moment.
+ actorTimeoutMs: atLeast(env.SURF_ACTOR_TIMEOUT_MS, 200, 15_000),
+
+ oauth: {
+ clientId: env.OV_OAUTH_CLIENT_ID || 'surf',
+ clientSecret: env.OV_OAUTH_CLIENT_SECRET || '',
+ redirectUri: env.OV_OAUTH_REDIRECT_URI || `${baseUrl}/auth/callback`,
+ scope: 'profile',
+ sessionAudience: env.OV_SESSION_AUDIENCE || 'openvibe.network',
+ },
+ cookies: { secure: env.COOKIE_SECURE ? env.COOKIE_SECURE === 'true' : isProduction },
+
+ // One per-IP budget for /api and /auth (express-rate-limit, one 60 s window).
+ rateLimitPerMinute: Math.max(1, int(env.SURF_RATE_LIMIT_PER_MIN, 120)),
+ };
+}
+
+module.exports = { load };
new file mode 100644
new file mode 100644
@@ -0,0 +1,113 @@
@@ -0,0 +1,113 @@
+'use strict';
+
+/**
+ * /api/v1 — the three Surfs:
+ *
+ * GET /api/v1/watch/feed?topic=all|live|clips|moments&cursor= the merged Live feed, cached 30 s, public
+ * GET /api/v1/ride/maps Games' surf maps, cached 60 s, public
+ * GET /api/v1/ride/maps/:id one map and its records
+ * POST /api/v1/web/tasks Surf with Vibe: one Actor task as the person
+ *
+ * The Watch and Ride reads are public and cacheable. A browser's POST to /api/v1/web is a write: it must be
+ * signed in and come from this site's own Origin, it carries the person's token to Actor as a Bearer, and it is
+ * never cached. Actor's 4xx refusal keeps its status and its words; an Actor that does not answer is this
+ * service's 502.
+ */
+const { http } = require('openvibe-contracts');
+const { asyncRouter } = require('./router');
+
+function ownOrigin(config) {
+ try { return new URL(config.baseUrl).origin; } catch { return null; }
+}
+
+function createApi(ctx) {
+ const { config, feed, ride, web, log } = ctx;
+ const r = asyncRouter();
+ const origin = ownOrigin(config);
+
+ const problem = (req, res, status, code, detail) => http.sendProblem(res, status, code, { detail, ctx: req.ov });
+
+ // ── Watch: the merged Live feed ─────────────────────────
+ r.get('/v1/watch/feed', async (req, res) => {
+ try {
+ const out = await feed.feed(req.query.topic || 'all', req.query.cursor || null);
+ res.set('Cache-Control', 'public, max-age=30');
+ return res.json(out);
+ } catch (err) {
+ if (err && err.code === 'watch.invalid_topic') return problem(req, res, 400, err.code, err.message);
+ if (err && err.code === 'watch.invalid_cursor') return problem(req, res, 400, err.code, err.message);
+ log.warn('[OpenVibe.Surf] watch feed failed:', (err && err.message) || err);
+ return problem(req, res, 502, 'watch.unavailable', 'OpenVibe.Live did not answer. Try again in a moment.');
+ }
+ });
+
+ // ── Ride: the Games map catalogue ───────────────────────
+ r.get('/v1/ride/maps', async (req, res) => {
+ try {
+ const out = await ride.maps();
+ res.set('Cache-Control', 'public, max-age=60');
+ return res.json(out);
+ } catch (err) {
+ log.warn('[OpenVibe.Surf] ride maps failed:', (err && err.message) || err);
+ return problem(req, res, 502, 'games.unavailable', 'OpenVibe.Games did not answer. Try again in a moment.');
+ }
+ });
+
+ r.get('/v1/ride/maps/:id', async (req, res) => {
+ try {
+ const out = await ride.map(req.params.id);
+ res.set('Cache-Control', 'public, max-age=60');
+ return res.json(out);
+ } catch (err) {
+ if (err && err.status === 400) return problem(req, res, 400, err.code || 'ride.invalid_map', err.message);
+ log.warn('[OpenVibe.Surf] ride map failed:', (err && err.message) || err);
+ return problem(req, res, 502, 'games.unavailable', 'OpenVibe.Games did not answer. Try again in a moment.');
+ }
+ });
+
+ // ── Web: Surf with Vibe ─────────────────────────────────
+ // A person's task is never cacheable, not even the 401/403 that refuses it.
+ r.use('/v1/web', (_req, res, next) => { res.set('Cache-Control', 'private, no-store'); next(); });
+
+ // The person's Network token, from the httpOnly cookie only (never a header a script could set). The SSO
+ // middleware has already verified it and, when the cookie had expired, refreshed it: prefer that token so an
+ // expired surf_at never reaches Actor.
+ function requireToken(req, res, next) {
+ const viewer = req.viewer;
+ const token = viewer && viewer.kind === 'user' ? viewer.token : null;
+ if (!token) return problem(req, res, 401, 'token.required', 'Sign in at openvibe.surf to surf the web with Vibe.');
+ req.webToken = token;
+ return next();
+ }
+
+ // A browser POST to this route must come from this site itself: another site cannot make a signed-in visitor
+ // spend their Actor allowance.
+ function requireOrigin(req, res, next) {
+ const sent = String(req.get('origin') || '');
+ if (!origin || sent !== origin) return problem(req, res, 403, 'web.cross_site', 'Surf with Vibe must be started from openvibe.surf itself.');
+ return next();
+ }
+
+ r.post('/v1/web/tasks', requireToken, requireOrigin, async (req, res) => {
+ const body = req.body && typeof req.body === 'object' ? req.body : {};
+ const query = body.query;
+ if (!web.validQuery(query)) return problem(req, res, 400, 'web.invalid_query', 'query must be 3 to 500 characters.');
+ try {
+ const out = await web.createTask({
+ token: req.webToken,
+ query,
+ idempotencyKey: req.get('idempotency-key') || null,
+ });
+ res.set('Location', out.page);
+ return res.status(201).json(out);
+ } catch (err) {
+ if (err && err.status >= 400 && err.status < 500) return problem(req, res, err.status, err.code || 'actor.refused', err.detail || err.message);
+ log.warn('[OpenVibe.Surf] web task failed:', (err && err.message) || err);
+ return problem(req, res, 502, (err && err.code) || 'actor.unavailable', (err && err.message) || 'OpenVibe.Actor did not answer. Try again in a moment.');
+ }
+ });
+
+ return r;
+}
+
+module.exports = { createApi, ownOrigin };
new file mode 100644
new file mode 100644
@@ -0,0 +1,100 @@
@@ -0,0 +1,100 @@
+'use strict';
+
+/**
+ * Crawl artifacts for openvibe.surf, built with openvibe-shared/seo: robots.txt, sitemap.xml and llms.txt.
+ * The public pages are for search engines and AI crawlers; the API, sign-in and the form that creates a task are
+ * not. The sitemap adds one page per surf map while Games lists them; a Games failure leaves the static pages in
+ * place rather than failing the crawl artifact.
+ */
+const fs = require('fs');
+const path = require('path');
+const seo = require('openvibe-shared/seo');
+const { asyncRouter } = require('./router');
+
+const SITE_NAME = 'OpenVibe.Surf';
+const DESCRIPTION = 'Ride maps, surf the web, surf what\'s on. OpenVibe.Surf is the Surf room of the OpenVibe network: ride CS-style surf maps in the browser (OpenVibe.Games), lean back and surf live streams, clips and AI moments (OpenVibe.Live), and let an agent browse the web for you on a private desk (OpenVibe.Actor).';
+const DISALLOW = ['/auth/', '/api/', '/web/go'];
+
+function dayOf(ts) {
+ const m = String(ts == null ? '' : ts).match(/^(\d{4}-\d{2}-\d{2})/);
+ return m ? m[1] : null;
+}
+function siteUpdated() {
+ try { return dayOf(JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'STATUS.json'), 'utf8')).updated); } catch { return null; }
+}
+
+/** Every public page, in the order a crawler should read them. */
+function publicPages(maps = []) {
+ const static_ = [
+ { path: '/', changefreq: 'weekly', priority: 1.0, title: 'OpenVibe.Surf', text: 'Ride CS-style surf maps in the browser, watch live streams, clips and AI moments, and surf the web with an agent.' },
+ { path: '/ride', changefreq: 'weekly', priority: 0.9, title: 'Ride', text: 'The OpenVibe.Games surf map catalogue, with each map’s stages, records and in-browser embed.' },
+ { path: '/watch', changefreq: 'hourly', priority: 0.9, title: 'Watch', text: 'Live streams, clips people cut and the AI moments of the week, merged into one lean-back feed from OpenVibe.Live.' },
+ { path: '/web', changefreq: 'monthly', priority: 0.8, title: 'Surf with Vibe', text: 'Ask an agent to browse the web for you: it searches, reads pages, keeps its sources and finishes with a cited report.' },
+ { path: '/developers', changefreq: 'monthly', priority: 0.5, title: 'The Surf API', text: 'The Watch feed, the Ride maps proxy and the Web task route.' },
+ ];
+ const mapPages = (Array.isArray(maps) ? maps : []).filter((m) => m && m.id).map((m) => ({
+ path: `/ride/${encodeURIComponent(String(m.id))}`,
+ changefreq: 'monthly',
+ priority: 0.6,
+ title: String(m.name || m.id),
+ text: `${String(m.name || m.id)}: a ${String(m.tier === 1 ? 'beginner' : m.tier === 2 ? 'intermediate' : 'advanced')} surf map with ${m.stages == null ? 'several' : String(m.stages)} stages${m.records != null ? ` and ${String(m.records)} recorded runs` : ''}.`,
+ }));
+ return [...static_, ...mapPages];
+}
+
+function createDiscoveryRoutes(ctx) {
+ const { config, ride } = ctx;
+ const r = asyncRouter();
+ const site = String(config.baseUrl).replace(/\/+$/, '');
+ const abs = (p) => `${site}${p}`;
+ const TEXT = 'public, max-age=3600';
+
+ async function maps() {
+ try { const list = await ride.maps(); return list.maps || []; } catch { return []; }
+ }
+
+ r.get('/robots.txt', async (_req, res) => {
+ res.type('text/plain').set('Cache-Control', TEXT).send(
+ '# openvibe.surf: the public pages are for search and AI crawlers; sign-in, the API and the task form are not.\n'
+ + seo.robotsTxt({ sitemaps: [abs('/sitemap.xml')], disallow: DISALLOW }));
+ });
+
+ r.get('/llms.txt', async (_req, res) => {
+ res.type('text/plain').set('Cache-Control', TEXT).send(seo.llmsTxt({
+ name: SITE_NAME,
+ summary: DESCRIPTION,
+ details: 'The pages here are server-rendered and readable without JavaScript. Ride is a proxy to OpenVibe.Games, Watch merges OpenVibe.Live’s live, clip and moment feeds, and Web creates one OpenVibe.Actor task with the signed-in person’s own Network token.',
+ sections: [
+ { title: 'Start here', links: [
+ { title: 'OpenVibe.Surf', url: abs('/'), note: 'the three surfs' },
+ { title: 'Ride', url: abs('/ride'), note: 'CS-style surf maps from OpenVibe.Games' },
+ { title: 'Watch', url: abs('/watch'), note: 'live streams, clips and AI moments from OpenVibe.Live' },
+ { title: 'Surf with Vibe', url: abs('/web'), note: 'an agent browses the web for you and cites its sources' },
+ { title: 'For developers', url: abs('/developers'), note: 'the Watch, Ride and Web endpoints' },
+ ] },
+ { title: 'Machine-readable', links: [
+ { title: 'The Watch feed (JSON)', url: abs('/api/v1/watch/feed?topic=all'), note: 'public, cached 30 seconds' },
+ { title: 'The Ride maps (JSON)', url: abs('/api/v1/ride/maps'), note: 'public, cached 60 seconds' },
+ { title: 'Sitemap', url: abs('/sitemap.xml') },
+ { title: 'Release metadata (JSON)', url: abs('/release.json') },
+ ] },
+ { title: 'Elsewhere', links: [
+ { title: 'OpenVibe.Live', url: 'https://openvibe.live', note: 'what Watch surfs' },
+ { title: 'OpenVibe.Games', url: 'https://openvibe.games', note: 'what Ride plays' },
+ { title: 'OpenVibe.Actor', url: 'https://openvibe.actor', note: 'who browses the web for Web' },
+ ] },
+ ],
+ }));
+ });
+
+ r.get('/sitemap.xml', async (_req, res) => {
+ const pages = publicPages(await maps());
+ const lastmod = siteUpdated();
+ const urls = pages.map((e) => ({ loc: abs(e.path), ...(lastmod ? { lastmod } : {}), changefreq: e.changefreq, priority: e.priority }));
+ res.type('application/xml').set('Cache-Control', TEXT).send(seo.sitemapXml(urls));
+ });
+
+ return r;
+}
+
+module.exports = { createDiscoveryRoutes, publicPages, DESCRIPTION, SITE_NAME, DISALLOW };
new file mode 100644
new file mode 100644
@@ -0,0 +1,119 @@
@@ -0,0 +1,119 @@
+'use strict';
+
+/**
+ * The public pages:
+ * / the front door: Ride, Watch, Web
+ * /ride the maps list and the embed of the first map
+ * /ride/:map one map: name, tier, stages, records, the embed
+ * /watch the merged Live feed as a list with thumbnails and play links
+ * /web the Surf with Vibe form and what it does
+ * /web/go?q= the no-JavaScript form: create the task, 303 to its Actor page
+ * /developers the API, for people and agents
+ *
+ * Every page is server-rendered and complete without JavaScript. A page for a signed-in person is private and
+ * never cached; a public page gets the short 60-second window. `me` comes from the session the SSO middleware
+ * already resolved — a guest never causes a Network call to find out they are a guest.
+ */
+const views = require('../views');
+const { asyncRouter } = require('./router');
+const { send } = require('../render/layout');
+const { ACCESS_COOKIE } = require('../auth/sso');
+const express = require('express');
+const { ownOrigin } = require('./api');
+
+const TOPICS = new Set(['all', 'live', 'clips', 'moments']);
+
+function meOf(viewer) {
+ if (!viewer || viewer.kind !== 'user') return { signedIn: false, user: null };
+ return {
+ signedIn: true,
+ user: {
+ subject_id: viewer.subject || null,
+ username: viewer.username || null,
+ display_name: viewer.displayName || null,
+ role: viewer.role || 'user',
+ avatar_url: viewer.avatarUrl || null,
+ profile_color: viewer.profileColor || null,
+ },
+ };
+}
+
+function createPageRoutes(ctx) {
+ const { feed, ride, web, log, config } = ctx;
+ const r = asyncRouter();
+ // An upstream never throws in a page: a failure degrades to the page without its data.
+ const safe = (fn) => Promise.resolve().then(fn).catch((err) => {
+ log.warn('[OpenVibe.Surf] page upstream failed:', (err && err.message) || err);
+ return null;
+ });
+ const page = (res, status, html, viewer) => send(res, status, html, { viewer });
+
+ r.get('/', (req, res) => page(res, 200, views.home(), req.viewer));
+
+ r.get('/ride', async (req, res) => {
+ const out = await safe(() => ride.maps());
+ return page(res, 200, views.ride({ maps: out ? out.maps : [], games: out ? out.games : 'unavailable' }), req.viewer);
+ });
+
+ r.get('/ride/:map', async (req, res) => {
+ const id = String(req.params.map || '').slice(0, 64);
+ const out = await safe(() => ride.map(id));
+ return page(res, 200, views.map({
+ id,
+ map: out ? out.map : { id, embed_url: ride.embedUrl(id) },
+ top: out ? out.top : [],
+ games: out ? out.games : 'unavailable',
+ }), req.viewer);
+ });
+
+ r.get('/watch', async (req, res) => {
+ const topic = TOPICS.has(String(req.query.topic || '')) ? String(req.query.topic) : 'all';
+ let out = null;
+ try {
+ out = await feed.feed(topic, req.query.cursor || null);
+ } catch (err) {
+ // A stale or hand-edited cursor is not a page-level failure: show the first page instead of a 400.
+ if (err && err.code === 'watch.invalid_cursor') {
+ out = await safe(() => feed.feed(topic, null));
+ } else {
+ log.warn('[OpenVibe.Surf] watch page failed:', (err && err.message) || err);
+ }
+ }
+ return page(res, 200, views.watch({ feed: out, topic }), req.viewer);
+ });
+
+ r.get('/web', (req, res) => page(res, 200, views.web({ me: meOf(req.viewer), query: typeof req.query.q === 'string' ? req.query.q.slice(0, 500) : '' }), req.viewer));
+
+ // The no-JavaScript form: a POST from this site that creates one task and goes to it (303). The query is in the
+ // body, never in a URL (so never in a log); another site's form is refused by its Origin.
+ r.get('/web/go', (req, res) => res.redirect(303, '/web'));
+ r.post('/web/go', express.urlencoded({ extended: false, limit: '8kb' }), async (req, res) => {
+ res.set('Cache-Control', 'private, no-store');
+ const origin = String(req.get('origin') || '');
+ const site = String(req.get('sec-fetch-site') || '');
+ if ((origin && origin !== ownOrigin(config)) || (site && site !== 'same-origin' && site !== 'none')) {
+ return res.status(403).type('text/plain').send('Surf with Vibe starts from openvibe.surf itself.');
+ }
+ const query = typeof (req.body || {}).q === 'string' ? req.body.q.trim() : '';
+ if (!web.validQuery(query)) return page(res, 400, views.web({ me: meOf(req.viewer), query }), req.viewer);
+ const token = req.viewer && req.viewer.kind === 'user' ? (req.viewer.token || (req.cookies && req.cookies[ACCESS_COOKIE])) : null;
+ if (!token) return res.redirect(303, '/auth/login?next=%2Fweb');
+ try {
+ const out = await web.createTask({ token, query, idempotencyKey: null });
+ return res.redirect(303, out.page);
+ } catch (err) {
+ if (err && err.status >= 400 && err.status < 500) {
+ return res.status(err.status).type('text/plain').send(`OpenVibe.Actor could not take that task: ${String(err.detail || err.message || 'refused').slice(0, 400)}`);
+ }
+ log.warn('[OpenVibe.Surf] web form failed:', (err && err.message) || err);
+ return res.status(502).type('text/plain').send('OpenVibe.Actor did not answer. Go back to /web and try again in a moment.');
+ }
+ });
+
+ r.get('/developers', (req, res) => page(res, 200, views.developers(), req.viewer));
+
+ r.use((req, res) => page(res, 404, views.notFound(), req.viewer));
+ return r;
+}
+
+module.exports = { createPageRoutes, meOf };
new file mode 100644
new file mode 100644
@@ -0,0 +1,23 @@
@@ -0,0 +1,23 @@
+'use strict';
+
+/**
+ * express.Router() whose get/post handlers may be async: a rejected promise goes to next(err) instead of
+ * becoming an unhandled rejection. (The same helper OpenVibe.Design, OpenVibe.Help and OpenVibe.Domains carry.)
+ */
+const express = require('express');
+
+function asyncRouter() {
+ const r = express.Router();
+ for (const method of ['get', 'post', 'put']) {
+ const orig = r[method].bind(r);
+ r[method] = (path, ...handlers) => orig(path, ...handlers.map((h) => (h.length === 4 ? h : (req, res, next) => {
+ try {
+ const out = h(req, res, next);
+ if (out && typeof out.catch === 'function') out.catch(next);
+ } catch (err) { next(err); }
+ })));
+ }
+ return r;
+}
+
+module.exports = { asyncRouter };
new file mode 100644
new file mode 100644
@@ -0,0 +1,46 @@
@@ -0,0 +1,46 @@
+'use strict';
+
+/**
+ * OpenVibe.Surf — process entry. `node server/index.js` listens on PORT (5110) behind nginx (deploy/).
+ * Stateless: the HTTP server and the JWKS refresher are what stop.
+ */
+const { createApp } = require('./app');
+const { gracefulStop } = require('openvibe-sdk/service');
+
+/**
+ * The process stop (openvibe-sdk/service): stop taking connections, drain the requests in flight, stop the JWKS
+ * refresher, exit. Exported so a test can inject `exit` and `signals: false`.
+ */
+function createLifecycle({ server, ctx, exit, signals } = {}) {
+ return gracefulStop({
+ name: 'OpenVibe.Surf',
+ server,
+ deadlineExitCode: 0,
+ exit,
+ signals,
+ deadlineMs: 10_000,
+ close: [() => ctx.keys.client.stop()],
+ });
+}
+
+async function start() {
+ const { app, ctx } = await createApp();
+ const { config } = ctx;
+ const server = app.listen(config.port, config.host, () => {
+ console.log(`[OpenVibe.Surf] ${config.nodeEnv} on http://${config.host}:${config.port} → ${config.baseUrl} (network ${config.networkUrl}, live ${config.liveInternalUrl}, games ${config.gamesInternalUrl}, actor ${config.actorInternalUrl})`);
+ // Load the Network's signing key now, not on the first sign-in, so /api/ready reports sign-in as it is.
+ ctx.keys.ensure().catch(() => {});
+ });
+ server.keepAliveTimeout = 65_000;
+ createLifecycle({ server, ctx });
+ return { server, ctx };
+}
+
+if (require.main === module) {
+ start().catch((err) => {
+ console.error('[OpenVibe.Surf] failed to start:', err);
+ process.exit(1);
+ });
+}
+
+module.exports = { start, createLifecycle };
new file mode 100644
new file mode 100644
@@ -0,0 +1,62 @@
@@ -0,0 +1,62 @@
+'use strict';
+
+/**
+ * Truthful readiness for GET /api/ready (openvibe-shared/ready). There is no database: the service is stateless.
+ *
+ * network optional OpenVibe.Network answers its health route
+ * network_jwks optional the Network signing key is loaded; without it nobody can sign in
+ * oauth_client optional OV_OAUTH_CLIENT_SECRET is set (sign-in needs it)
+ * live optional OpenVibe.Live answers
+ * games optional OpenVibe.Games answers (a 404 for the new surf routes still means it is up)
+ * actor optional OpenVibe.Actor answers; Web cannot create a task without it
+ *
+ * No check is required: if an upstream does not answer, the process still serves the pages and degrades honestly,
+ * so the answer is 200 with status "degraded" — never 503.
+ */
+const { createReadiness } = require('openvibe-shared/ready');
+
+function createServiceReadiness({ config, feed, ride, keys = null, release = null, fetchImpl = globalThis.fetch }) {
+ return createReadiness({
+ service: 'surf',
+ release,
+ checks: [
+ {
+ name: 'network',
+ required: false,
+ check: async () => {
+ const base = config.networkInternalUrl || config.networkUrl;
+ const fetchOnce = (p) => fetchImpl(`${base}${p}`, {
+ headers: { Accept: 'application/json' },
+ signal: AbortSignal.timeout(2000),
+ });
+ let res = await fetchOnce('/api/health');
+ // An older Network (and a test stand-in) may not serve /api/health; the JWKS route it must serve
+ // is the dependency that matters to sign-in.
+ if (res.status === 404) res = await fetchOnce('/api/.well-known/jwks');
+ return res.ok ? true : `OpenVibe.Network answered ${res.status}`;
+ },
+ },
+ {
+ name: 'network_jwks',
+ required: false,
+ check: () => (keys && keys.loaded() ? true : 'Network signing key not loaded yet: sign-in is unavailable'),
+ },
+ { name: 'oauth_client', required: false, check: () => (config.oauth.clientSecret ? true : 'OV_OAUTH_CLIENT_SECRET unset: sign-in cannot complete') },
+ { name: 'live', required: false, check: () => feed.probe() },
+ { name: 'games', required: false, check: () => ride.probe() },
+ {
+ name: 'actor',
+ required: false,
+ check: async () => {
+ const res = await fetchImpl(`${config.actorInternalUrl}/api/v1/agents`, {
+ headers: { Accept: 'application/json' },
+ signal: AbortSignal.timeout(2000),
+ });
+ return res.status < 500 ? true : `OpenVibe.Actor answered ${res.status}`;
+ },
+ },
+ ],
+ });
+}
+
+module.exports = { createServiceReadiness };
new file mode 100644
new file mode 100644
@@ -0,0 +1,98 @@
@@ -0,0 +1,98 @@
+'use strict';
+
+/**
+ * Page shell for OpenVibe.Surf. Every page is server-rendered through openvibe-shared/shell and is complete
+ * without JavaScript: the OpenVibe Frame (theme, navbar, footer) comes from the shared shell and the room's accent
+ * from openvibe-shared/rooms. The views themselves are the v1 placeholders (server/views/index.js): stable `sf-`
+ * ids and classes, semantic HTML, readable with no script.
+ *
+ * - <head>: the shared app icon, the room's stylesheets, this site's content-hashed surf.css
+ * - the navbar's session probe (/auth/me), sign-in and sign-out URLs and silent sign-in, as every site's
+ * - surf.js on every page (deferred), for the main session's progressive enhancement
+ */
+const shell = require('openvibe-shared/shell');
+const ovServe = require('openvibe-shared/serve');
+const appIcon = require('openvibe-shared/app-icon');
+const kit = require('openvibe-shared/kit');
+const rooms = require('openvibe-shared/rooms');
+const { asset, assetVersion } = require('../assets');
+
+const NAME = 'OpenVibe.Surf';
+const NETWORK = 'https://openvibe.network';
+const ROOM = rooms.get('surf');
+const esc = kit.esc;
+
+const NAV = [
+ { label: 'Home', href: '/', icon: 'fa-house' },
+ { label: 'Ride', href: '/ride', icon: 'fa-flag-checkered' },
+ { label: 'Watch', href: '/watch', icon: 'fa-tv' },
+ { label: 'Web', href: '/web', icon: 'fa-compass' },
+ { label: 'Developers', href: '/developers', icon: 'fa-code', more: true },
+];
+
+let RELEASE = 'dev';
+let BASE = 'https://openvibe.surf';
+function setRelease(id) { if (id) RELEASE = String(id); }
+function configure({ baseUrl } = {}) { if (baseUrl) BASE = String(baseUrl).replace(/\/+$/, ''); }
+const baseUrl = () => BASE;
+const canonical = (p) => `${BASE}${p || '/'}`;
+
+/** The page around a body: the shared shell with the room's stylesheets and behaviour. */
+function layout({ title, description, canonical, path, robots = 'index, follow', body, active = '/', scripts = ['js/surf.js'], jsonLd = [] } = {}) {
+ const pathNow = path || active || '/';
+ canonical = canonical || (path ? `${BASE}${path}` : undefined);
+ const navbar = {
+ service: 'surf',
+ apiBase: NETWORK,
+ links: NAV.map((l) => ({ ...l, active: l.href === active || (l.href !== '/' && String(active).startsWith(`${l.href}/`)) })),
+ appBar: [{ label: 'Ride', href: '/ride', icon: 'fa-flag-checkered' }, { label: 'Watch', href: '/watch', icon: 'fa-tv' }],
+ history: { type: 'page', title: title || NAME },
+ silentLogin: `${BASE}/auth/login?silent=1&next={url}`,
+ sessionUrl: '/auth/me',
+ loginUrl: '/auth/login?next={path}',
+ logoutUrl: '/auth/logout?next={path}',
+ };
+ const footer = { service: 'surf', variant: 'full', mount: '#ov-footer', brandName: NAME };
+ return shell.page({
+ name: NAME,
+ lang: 'en',
+ title: title || `${NAME}: ${ROOM.line}`,
+ titleSuffix: title ? ` · ${NAME}` : '',
+ siteName: NAME,
+ description: description || `${ROOM.line}. Ride CS-style surf maps in the browser, surf what is live on OpenVibe.Live, and let an agent browse the web for you.`,
+ canonical,
+ robots,
+ jsonLd,
+ home: '/',
+ navLinks: NAV,
+ navbar,
+ footer,
+ head: `${appIcon.headTags({ site: 'surf' })}
+<link rel="stylesheet" href="${esc(ovServe.url('kit.css'))}">
+<link rel="stylesheet" href="${esc(ovServe.url('room.css'))}">
+<link rel="stylesheet" href="${esc(ovServe.url('showcase.css'))}">
+<link rel="stylesheet" href="${esc(asset('css/surf.css'))}">
+<script src="${esc(ovServe.url('kit-web.js'))}" defer></script>
+<script src="${esc(ovServe.url('motion.js'))}" defer></script>
+${(scripts || []).map((rel) => `<script src="${esc(asset(rel))}" defer></script>`).join('\n')}
+<meta name="ov-boost" content="surf@${esc(RELEASE)}">`,
+ body: `<a class="skip" href="#main">Skip to content</a>
+<div id="navbar-mount"></div>
+<main id="main" class="sf ov-room" style="${rooms.style('surf')}">${body || ''}</main>
+<script>window.__OV_PAGE = ${JSON.stringify({ navbar, footer, path: pathNow }).replace(/</g, '\\u003c')};
+document.addEventListener('DOMContentLoaded', function () { try { if (window.OpenVibeFooter) OpenVibeFooter.init(window.__OV_PAGE.footer); } catch (e) { /* the Frame is optional */ } });</script>`,
+ });
+}
+
+/**
+ * Send a page; a page rendered for a signed-in person is private and never cached. A public page gets the short
+ * 60-second public window the main session asked for, with Vary: Cookie because the frame differs by session.
+ */
+function send(res, status, html, { viewer } = {}) {
+ const personal = viewer && viewer.kind === 'user';
+ res.set('Cache-Control', personal ? 'private, no-store' : 'public, max-age=60');
+ res.set('Vary', 'Cookie');
+ res.status(status).type('html').send(html);
+}
+
+module.exports = { layout, send, setRelease, configure, baseUrl, canonical, NAME, NETWORK, ROOM, NAV, esc, asset, assetVersion };
new file mode 100644
new file mode 100644
@@ -0,0 +1,118 @@
@@ -0,0 +1,118 @@
+'use strict';
+
+/**
+ * Ride: the OpenVibe.Games surf map catalogue, proxied and cached for 60 s.
+ *
+ * GET /api/v1/ride/maps → { maps: [...], games: 'ready'|'not_ready' }
+ * GET /api/v1/ride/maps/:id → { map, top, games: 'ready'|'not_ready' }
+ *
+ * Games is adding GET /api/surf/maps and GET /api/surf/maps/:id now. Until those routes exist Games answers 404:
+ * that is "not ready", not an error, so the list is empty and the answer says so. A timeout or a 5xx is an
+ * error (the caller answers 502); a failure is never cached, so the next request asks again.
+ *
+ * Every map carries `embed_url`: https://openvibe.games/play/surf?map=<id>&embed=1 (SURF_GAMES_PUBLIC_URL).
+ */
+const EMBED_PATH = '/play/surf';
+
+function clean(id) {
+ return String(id == null ? '' : id).trim().slice(0, 64);
+}
+
+function createRide({ config, fetchImpl = globalThis.fetch, log = console, now = () => Date.now() }) {
+ const ttl = config.rideCacheMs == null ? 60_000 : config.rideCacheMs;
+ let listCache = null; // { at, value }
+ const mapCache = new Map(); // id -> { at, value }
+
+ const publicUrl = String(config.gamesPublicUrl || 'https://openvibe.games').replace(/\/+$/, '');
+ const embedUrl = (id) => `${publicUrl}${EMBED_PATH}?map=${encodeURIComponent(clean(id))}&embed=1`;
+
+ function withEmbed(map) {
+ if (!map || typeof map !== 'object') return map;
+ const id = clean(map.id);
+ return id ? { ...map, embed_url: embedUrl(id) } : map;
+ }
+
+ async function request(path) {
+ let res;
+ try {
+ res = await fetchImpl(`${config.gamesInternalUrl}${path}`, {
+ headers: { Accept: 'application/json' },
+ signal: AbortSignal.timeout(config.gamesTimeoutMs || 4000),
+ });
+ } catch (err) {
+ const timedOut = err && (err.name === 'TimeoutError' || err.name === 'AbortError');
+ throw Object.assign(new Error(timedOut ? 'OpenVibe.Games timed out' : 'OpenVibe.Games did not answer'), { status: 502, code: 'games.unavailable' });
+ }
+ const text = await res.text().catch(() => '');
+ let data = null;
+ try { data = text ? JSON.parse(text) : null; } catch { data = null; }
+ return { status: res.status, ok: res.ok, data, text };
+ }
+
+ function notReadyList() { return { maps: [], games: 'not_ready' }; }
+ function notReadyMap(id) {
+ return {
+ map: { id: clean(id), name: null, tier: null, stages: null, author: null, records: null, wrMs: null, wrHolder: null, embed_url: embedUrl(id) },
+ top: [],
+ games: 'not_ready',
+ };
+ }
+
+ /** The map catalogue, from memory while it is under 60 s old. A 404 from Games is a cached "not ready". */
+ async function maps({ fresh = false } = {}) {
+ if (!fresh && listCache && now() - listCache.at < ttl) return listCache.value;
+ const r = await request('/api/surf/maps');
+ if (r.status === 404) {
+ const value = notReadyList();
+ listCache = { at: now(), value };
+ return value;
+ }
+ if (!r.ok || !r.data || !Array.isArray(r.data.maps)) {
+ throw Object.assign(new Error(`OpenVibe.Games catalogue answered ${r.status}`), { status: 502, code: 'games.unavailable', response: r });
+ }
+ const value = { maps: r.data.maps.filter(Boolean).map(withEmbed), games: 'ready' };
+ listCache = { at: now(), value };
+ return value;
+ }
+
+ /** One map: Games' answer with the embed URL added, or a "not ready" shell that still carries the embed. */
+ async function map(id, { fresh = false } = {}) {
+ const key = clean(id);
+ if (!key) throw Object.assign(new Error('a map id is required'), { status: 400, code: 'ride.invalid_map' });
+ const hit = mapCache.get(key);
+ if (!fresh && hit && now() - hit.at < ttl) return hit.value;
+ const r = await request(`/api/surf/maps/${encodeURIComponent(key)}`);
+ if (r.status === 404) {
+ const value = notReadyMap(key);
+ mapCache.set(key, { at: now(), value });
+ if (mapCache.size > 200) mapCache.delete(mapCache.keys().next().value);
+ return value;
+ }
+ if (!r.ok || !r.data || !r.data.map) {
+ throw Object.assign(new Error(`OpenVibe.Games map answered ${r.status}`), { status: 502, code: 'games.unavailable', response: r });
+ }
+ const value = { map: withEmbed(r.data.map), top: Array.isArray(r.data.top) ? r.data.top : [], games: 'ready' };
+ mapCache.set(key, { at: now(), value });
+ if (mapCache.size > 200) mapCache.delete(mapCache.keys().next().value);
+ return value;
+ }
+
+ /** Readiness: Games is reachable when it answers anything below 500. A 404 still means the process is up. */
+ async function probe() {
+ try {
+ const res = await fetchImpl(`${config.gamesInternalUrl}/api/surf/maps`, {
+ headers: { Accept: 'application/json' },
+ signal: AbortSignal.timeout(config.gamesTimeoutMs || 4000),
+ });
+ return res.status < 500 ? true : `OpenVibe.Games answered ${res.status}`;
+ } catch (err) {
+ return err && (err.name === 'TimeoutError' || err.name === 'AbortError') ? 'OpenVibe.Games timed out' : 'OpenVibe.Games did not answer';
+ }
+ }
+
+ function clear() { listCache = null; mapCache.clear(); }
+
+ return { maps, map, probe, clear, embedUrl, get cached() { return listCache; } };
+}
+
+module.exports = { createRide, embedUrl: (config, id) => `${String(config.gamesPublicUrl || 'https://openvibe.games').replace(/\/+$/, '')}${EMBED_PATH}?map=${encodeURIComponent(clean(id))}&embed=1` };
new file mode 100644
new file mode 100644
@@ -0,0 +1,236 @@
@@ -0,0 +1,236 @@
+'use strict';
+/**
+ * The Surf room's pages (plan 2026-10-28 §5.5, v1): one room, three surfs. Server-rendered and whole without
+ * JavaScript; public/js/surf.js adds the TV (HLS in the page, ↑ ↓ and swipe to change channel), the Web form's
+ * in-page start, and the home's ticking timer.
+ *
+ * home() the front door: the run motif, three tiles under one Start, scenes, questions
+ * ride({ maps, games }) every map as a card, and the first one to play
+ * map({ id, map, top, games }) one map: tier, stages, author, the game, its fastest runs
+ * watch({ feed, topic }) the TV: what is on now, the guide (up next), topics
+ * web({ me, query }) Surf with Vibe: ask, and an agent browses for you on a private desk
+ * developers() the API
+ * notFound()
+ */
+const layoutLib = require('../render/layout');
+const kit = require('openvibe-shared/kit');
+const sc = require('openvibe-shared/showcase');
+const rooms = require('openvibe-shared/rooms');
+
+const { layout, esc } = layoutLib;
+const ROOM = rooms.get('surf');
+
+const TIER = { 1: 'Beginner', 2: 'Intermediate', 3: 'Hard', 4: 'Advanced', 5: 'Expert', 6: 'Elite' };
+const tierLabel = (t) => (TIER[t] ? `Tier ${t} · ${TIER[t]}` : t ? `Tier ${t}` : 'Unrated');
+const idSafe = (v) => String(v == null ? '' : v).replace(/[^A-Za-z0-9_-]+/g, '-').replace(/^-+|-+$/g, '');
+/** 61234 → "1:01.234"; 9870 → "9.870". */
+function runTime(v) {
+ const n = Number(v);
+ if (!Number.isFinite(n) || n < 0 || v === null) return null;
+ const m = Math.floor(n / 60000), s = Math.floor((n % 60000) / 1000), x = String(Math.round(n % 1000)).padStart(3, '0');
+ return m ? `${m}:${String(s).padStart(2, '0')}.${x}` : `${s}.${x}`;
+}
+const clipLen = (v) => { const n = Number(v); if (!Number.isFinite(n) || v === null) return null; const m = Math.floor(n / 60), s = Math.round(n % 60); return m ? `${m}:${String(s).padStart(2, '0')}` : `0:${String(s).padStart(2, '0')}`; };
+const signedInName = (me) => (me && me.signedIn && me.user && (me.user.display_name || me.user.username)) || null;
+
+/** The run motif: a first-person view down two ramps, speed lines, the timer and the speed. */
+function runMotif() {
+ const lines = Array.from({ length: 9 }, (_, i) => `<i style="--i:${i}"></i>`).join('');
+ return `<div class="sf-run" aria-hidden="true" data-sf-run>
+<svg class="sf-run-ramps" viewBox="0 0 400 260" preserveAspectRatio="none"><defs><linearGradient id="sf-rampL" x1="0" x2="1"><stop offset="0" stop-color="currentColor" stop-opacity=".55"/><stop offset="1" stop-color="currentColor" stop-opacity=".12"/></linearGradient><linearGradient id="sf-rampR" x1="1" x2="0"><stop offset="0" stop-color="currentColor" stop-opacity=".55"/><stop offset="1" stop-color="currentColor" stop-opacity=".12"/></linearGradient></defs>
+<path d="M0 260 L170 112 L178 118 L40 260 Z" fill="url(#sf-rampL)"/><path d="M400 260 L230 112 L222 118 L360 260 Z" fill="url(#sf-rampR)"/>
+<path d="M170 112 L200 96 L230 112" fill="none" stroke="currentColor" stroke-opacity=".35" stroke-width="2"/></svg>
+<span class="sf-run-speed">${lines}</span>
+<span class="sf-run-hud"><b data-sf-timer>0:12.384</b><small><span data-sf-speed>1 284</span> u/s · stage 2/4</small></span>
+</div>`;
+}
+
+function home() {
+ const tiles = `<ul class="sf-tiles" id="sf-tiles">
+<li class="sf-tile" id="sf-tile-ride" data-sf-surf="ride"><a href="/ride"><span class="sf-tile-ic">${kit.icon('game')}</span><b>Ride</b><span>CS-style surf in the browser: ramps, a millisecond timer, your ghost and the record.</span><span class="sf-tile-go">Ride a map${kit.icon('chevron')}</span></a></li>
+<li class="sf-tile" id="sf-tile-web" data-sf-surf="web"><a href="/web"><span class="sf-tile-ic">${kit.icon('globe')}</span><b>Web</b><span>Surf with Vibe: say what you want, an agent browses for you and reports back with its sources.</span><span class="sf-tile-go">Ask Vibe${kit.icon('chevron')}</span></a></li>
+<li class="sf-tile" id="sf-tile-watch" data-sf-surf="watch"><a href="/watch"><span class="sf-tile-ic">${kit.icon('play')}</span><b>Watch</b><span>Lean back: live streams, clips and the week's AI moments, one swipe or ↑ ↓ to change channel.</span><span class="sf-tile-go">Start watching${kit.icon('chevron')}</span></a></li>
+</ul>`;
+ // The tiles sit right under the hero (sc.room's own html slot comes after the scenes), so they go in before the scenes.
+ const room = sc.room({
+ room: 'surf',
+ hero: {
+ title: ROOM.line,
+ lede: 'Three surfs in one room. Ride Counter-Strike-style surf maps right in the browser and race the clock, let an agent surf the web for you and bring back what it found, or lean back and surf what is on across OpenVibe.',
+ start: { href: '/ride', label: 'Start riding' },
+ motif: runMotif(),
+ },
+ scenes: [
+ { title: 'Press play, you are on the ramp', text: 'Air-strafe into the ramp, hold your speed, hit the next one. The timer counts to the millisecond, your best run rides beside you as a ghost, and every finished run lands on the map\'s board.',
+ html: `<div class="sf-demo-run"><div class="sf-demo-hud"><span>Stage 3/4</span><b>0:41.207</b><span class="is-good">−0.384</span></div><div class="sf-demo-bar"><i style="width:72%"></i></div><p>Personal best 0:41.591 · World record 0:38.902</p></div>`,
+ action: { href: '/ride', label: 'Pick a map' } },
+ { title: 'Surf the web with Vibe', text: 'Say what you want: a cheap flight and three things to do, or what people say about a shoe. The agent searches, reads the pages that matter on a private desk, keeps its sources, and finishes with a report you can check.',
+ html: `<ol class="sf-demo-steps"><li class="is-done">Searched the web for “Lisbon in March, cheap flights”</li><li class="is-done">Read 4 pages · compared prices on 2 sites</li><li class="is-now">Writing the report with its sources…</li></ol>`,
+ action: { href: '/web', label: 'Ask Vibe' } },
+ { title: 'Lean back and surf what is on', text: 'Live streams first, then the clips people cut and the AI moments of the week, each saying why it is on. Swipe on a phone or press ↑ ↓ on a keyboard.',
+ html: `<div class="sf-demo-tv"><div class="sf-demo-screen"><span class="sf-pill-live">LIVE</span><span>goosely · Night owls: Seattle walk</span></div><ul><li><b>Up next</b> Top AI moment this week</li><li>New clip · Robot build, part 2</li></ul></div>`,
+ action: { href: '/watch', label: 'Start watching' } },
+ ],
+ explore: { title: 'More on Surf', items: [
+ { title: 'Ride', text: 'Maps, the timer, records', href: '/ride', icon: 'ov:games' },
+ { title: 'Surf with Vibe', text: 'An agent browses for you', href: '/web', icon: 'ov:actor' },
+ { title: 'The API', text: 'Feeds, maps and tasks as JSON', href: '/developers', icon: 'ov:code' },
+ ] },
+ faq: { items: [
+ ['Do I need to install anything to ride?', 'No. The maps run in your browser on OpenVibe.Games: a keyboard and mouse (pointer lock) or a gamepad; phones get a touch ramp trainer.'],
+ ['Does the agent use my accounts?', 'No. It browses on its own private desk, stops at logins and anything that would spend money, and hands over to you. Its steps and sources stay in your Actor tasks.'],
+ ['What plays in Watch?', 'OpenVibe.Live: who is live now, the clips people cut and the week\'s AI moments. Live streams open on their channel; clips and moments play right here.'],
+ ] },
+ });
+ const at = room.indexOf('<section class="sc-room-scenes"');
+ const body = at < 0 ? room.replace(/<\/div>$/, `${tiles}</div>`) : room.slice(0, at) + tiles + room.slice(at);
+ return layout({ title: null, path: '/', body: `<div class="sf-wrap">${body}</div>`, active: '/' });
+}
+
+function mapCard(m) {
+ const id = String(m.id || '');
+ const wr = runTime(m.wrMs);
+ return `<li class="sf-map" id="sf-map-${esc(idSafe(id))}" data-sf-map-id="${esc(id)}" data-tier="${esc(m.tier || '')}">
+<a class="sf-map-link" href="/ride/${encodeURIComponent(id)}">
+<span class="sf-map-art" aria-hidden="true"><i></i><i></i><i></i></span>
+<span class="sf-map-body"><span class="sf-tier">${esc(tierLabel(m.tier))}</span><b>${esc(m.name || id || 'Surf map')}</b>
+<span class="sf-map-meta">${m.stages == null ? '' : `${esc(m.stages)} stage${Number(m.stages) === 1 ? '' : 's'}`}${m.records != null ? ` · ${esc(m.records)} run${Number(m.records) === 1 ? '' : 's'}` : ''}</span>
+<span class="sf-wr">${wr ? `Record <b>${esc(wr)}</b>${m.wrHolder ? ` · ${esc(m.wrHolder)}` : ''}` : 'No record yet: set the first one'}</span></span>
+</a></li>`;
+}
+
+function gameFrame(m, name) {
+ if (!m || !m.embed_url) return '';
+ return `<div class="sf-game" id="sf-embed" data-sf-embed data-sf-map-id="${esc(m.id || '')}">
+<iframe src="${esc(m.embed_url)}" title="${esc(`${name}: play in OpenVibe.Games`)}" loading="lazy" allow="fullscreen; gamepad; pointer-lock" referrerpolicy="strict-origin-when-cross-origin"></iframe>
+</div><p class="sf-fine sf-game-help">Click the game to start: <kbd>W</kbd><kbd>A</kbd><kbd>S</kbd><kbd>D</kbd> and the mouse to strafe, <kbd>R</kbd> to restart, <kbd>G</kbd> for your ghost. <a href="${esc(m.embed_url.replace(/[?&]embed=1/, ''))}">Open it full screen</a></p>`;
+}
+
+const gamesNote = (games) => (games === 'not_ready' ? 'OpenVibe.Games is building the surf maps right now. They appear here the moment they are ready.'
+ : games === 'unavailable' ? 'OpenVibe.Games did not answer just now. Try again in a moment.' : 'No maps are listed right now.');
+
+function ride({ maps = [], games = 'ready' } = {}) {
+ const list = Array.isArray(maps) ? maps : [];
+ const first = list[0] || null;
+ const body = `<div class="sf-wrap sf-page-pad">
+<header class="sf-page-h"><p class="sf-kicker">Ride</p><h1>Pick a ramp</h1>
+<p class="sf-lede">Counter-Strike-style surf in the browser. Each map has stages, a millisecond timer and a board of the fastest runs${first ? '; the first one is ready to play below' : ''}.</p></header>
+<section id="sf-ride" aria-labelledby="sf-maps-h"><h2 id="sf-maps-h" class="sf-sr">Maps</h2>
+${list.length ? `<ul class="sf-maps" id="sf-maps">${list.map(mapCard).join('')}</ul>` : `<div class="sf-empty" id="sf-maps-empty">${kit.icon('game')}<p>${esc(gamesNote(games))}</p></div>`}
+</section>
+${first ? `<section id="sf-ride-embed" aria-labelledby="sf-embed-h" class="sf-play"><div class="sf-head-row"><h2 id="sf-embed-h">${esc(first.name || 'Play')}</h2><a class="sf-more" href="/ride/${encodeURIComponent(first.id)}">Records${kit.icon('chevron')}</a></div>${gameFrame(first, first.name || 'Surf map')}</section>` : ''}
+</div>`;
+ return layout({ title: 'Ride', description: 'Counter-Strike-style surf maps in the browser: stages, a millisecond timer, ghosts and records.', path: '/ride', body, active: '/ride' });
+}
+
+function map({ id, map: m = null, top = [], games = 'ready' } = {}) {
+ const one = m || { id };
+ const name = one.name || String(id || '');
+ const rows = (Array.isArray(top) ? top : []).map((r, i) => `<tr${i === 0 ? ' class="is-wr"' : ''}><td>${esc(r.rank == null ? i + 1 : r.rank)}</td><td>${esc(r.name || '')}</td><td class="sf-time">${esc(runTime(r.timeMs) || '')}</td></tr>`).join('');
+ const body = `<div class="sf-wrap sf-page-pad">
+<header class="sf-page-h sf-map-h" id="sf-map" data-sf-map-id="${esc(id || one.id || '')}"><p class="sf-kicker"><a href="/ride">Ride</a> · ${esc(tierLabel(one.tier))}</p><h1>${esc(name)}</h1>
+<p class="sf-lede">${one.stages == null ? '' : `${esc(one.stages)} stage${Number(one.stages) === 1 ? '' : 's'}`}${one.records != null ? ` · ${esc(one.records)} run${Number(one.records) === 1 ? '' : 's'}` : ''}${one.author ? ` · by ${esc(one.author)}` : ''}${one.wrMs != null ? ` · record <b>${esc(runTime(one.wrMs))}</b>${one.wrHolder ? ` by ${esc(one.wrHolder)}` : ''}` : ''}</p></header>
+${games === 'not_ready' ? `<p class="sf-note" id="sf-ride-not-ready">${esc(gamesNote(games))}</p>` : ''}
+<div class="sf-map-grid">
+<section id="sf-map-embed" aria-label="Play">${gameFrame(one, name) || `<div class="sf-empty">${kit.icon('game')}<p>This map cannot be played here right now.</p></div>`}</section>
+<section id="sf-map-records" class="sf-card" aria-labelledby="sf-records-h"><h2 id="sf-records-h">${kit.icon('trophy')}Fastest runs</h2>
+${rows ? `<table class="sf-board" id="sf-top"><thead><tr><th scope="col">#</th><th scope="col">Rider</th><th scope="col">Time</th></tr></thead><tbody>${rows}</tbody></table>`
+ : `<p class="sf-fine" id="sf-top-empty">${games === 'ready' ? 'No runs yet. Finish it once and you hold the record.' : esc(gamesNote(games))}</p>`}
+</section></div>
+</div>`;
+ return layout({ title: `${name}: Ride`, description: `${name}: a surf map you ride in the browser, with stages, a millisecond timer and records.`, path: `/ride/${encodeURIComponent(id || one.id || '')}`, body, active: '/ride' });
+}
+
+function watch({ feed = null, topic = 'all' } = {}) {
+ const items = feed && Array.isArray(feed.items) ? feed.items : [];
+ const tabs = [['all', 'All'], ['live', 'Live'], ['clips', 'Clips'], ['moments', 'AI moments']].map(([id, label]) =>
+ `<a class="sf-topic" href="/watch?topic=${id}"${id === topic ? ' aria-current="page"' : ''}>${label}</a>`).join('');
+ const first = items[0] || null;
+ const kindOf = (it) => (it.kind === 'moment' ? 'moment' : it.kind === 'clip' ? 'clip' : 'live');
+ const label = { live: 'Live', clip: 'Clip', moment: 'AI moment' };
+ const stage = first ? (() => {
+ const k = kindOf(first);
+ const p = first.play || {};
+ const media = p.type === 'hls' && p.src
+ ? `<video class="sf-tv-video" data-sf-video playsinline controls preload="none" poster="${esc(first.thumbnail || '')}" src="${esc(p.src)}"></video>`
+ : `${first.thumbnail ? `<img class="sf-tv-poster" src="${esc(first.thumbnail)}" alt="" loading="eager">` : '<span class="sf-tv-poster"></span>'}<a class="sf-tv-out" href="${esc(p.page || '#')}">${kit.icon('play')}${k === 'live' ? 'Watch live on OpenVibe.Live' : 'Play on OpenVibe.Live'}</a>`;
+ return `<div class="sf-tv-main"><div class="sf-tv-screen" data-sf-screen>${media}</div>
+<div class="sf-tv-info"><p class="sf-tv-kind"><span class="sf-pill sf-pill--${esc(k)}">${esc(label[k])}</span><span data-sf-why>${esc(first.why || '')}</span></p>
+<h2 data-sf-title>${esc(first.title || '')}</h2>
+<p class="sf-tv-meta" data-sf-meta>${first.channel && first.channel.username ? `<a href="https://openvibe.live/@${encodeURIComponent(first.channel.username)}">@${esc(first.channel.username)}</a>` : ''}</p>
+<p class="sf-tv-keys"><button type="button" class="ovk-btn ovk-btn--quiet ovk-btn--small" data-sf-prev aria-label="Previous channel">${kit.icon('up')}</button><button type="button" class="ovk-btn ovk-btn--quiet ovk-btn--small" data-sf-next aria-label="Next channel">${kit.icon('down')}</button><span class="sf-fine">Swipe, or press <kbd>↑</kbd> <kbd>↓</kbd></span></p></div></div>`;
+ })() : '';
+ const rows = items.map((it, i) => {
+ const k = kindOf(it);
+ const p = it.play || {};
+ return `<li class="sf-ch${i === 0 ? ' is-on' : ''}" id="sf-item-${esc(idSafe(it.key))}" data-sf-i="${i}" data-sf-kind="${esc(k)}" data-sf-play-type="${esc(p.type || '')}"${p.src ? ` data-sf-src="${esc(p.src)}"` : ''} data-sf-page="${esc(p.page || '')}" data-sf-thumb="${esc(it.thumbnail || '')}" data-sf-why="${esc(it.why || '')}" data-sf-channel="${esc((it.channel && it.channel.username) || '')}">
+<a class="sf-ch-link" href="${esc(p.page || '#')}">
+<span class="sf-ch-thumb">${it.thumbnail ? `<img src="${esc(it.thumbnail)}" alt="" loading="lazy" decoding="async">` : ''}${it.duration_seconds != null ? `<span class="sf-ch-len">${esc(clipLen(it.duration_seconds))}</span>` : ''}${k === 'live' ? '<span class="sf-ch-live">LIVE</span>' : ''}</span>
+<span class="sf-ch-body"><b>${esc(it.title || '')}</b><small>${it.channel && it.channel.username ? `@${esc(it.channel.username)} · ` : ''}${esc(it.why || label[k])}</small></span>
+</a></li>`;
+ }).join('');
+ const missing = feed && feed.sources ? Object.entries(feed.sources).filter(([, v]) => v !== 'ok').map(([k]) => k) : [];
+ const body = `<div class="sf-wrap sf-page-pad">
+<header class="sf-page-h sf-watch-h"><div><p class="sf-kicker">Watch</p><h1>Surf what's on</h1></div><nav class="sf-topics" id="sf-topics" aria-label="What to watch">${tabs}</nav></header>
+${missing.length ? `<p class="sf-note" id="sf-sources">Some of OpenVibe.Live did not answer (${esc(missing.join(', '))}); the rest is here.</p>` : ''}
+${items.length ? `<div class="sf-tv" id="sf-tv" data-sf-tv>${stage}<aside class="sf-guide" aria-labelledby="sf-guide-h"><h2 id="sf-guide-h">Up next</h2><ol class="sf-chs" id="sf-watch-feed">${rows}</ol>
+${feed && feed.next ? `<a class="sf-more" id="sf-watch-next" href="/watch?topic=${esc(topic)}&cursor=${esc(feed.next)}">More${kit.icon('chevron')}</a>` : ''}</aside></div>`
+ : `<div class="sf-empty" id="sf-watch-empty">${kit.icon('play')}<p>${feed ? 'Nothing on this channel right now. Try another, or come back soon.' : 'OpenVibe.Live did not answer. Try again in a moment.'}</p></div>`}
+</div>`;
+ return layout({ title: 'Watch', description: 'Lean-back channel surfing over OpenVibe.Live: who is live, clips and the week\'s AI moments in one feed.', path: '/watch', body, active: '/watch' });
+}
+
+const EXAMPLES = ['A cheap flight to Lisbon in March and three things to do', 'What do runners say about the Pegasus 42?', 'Compare three hosting plans under $10 a month'];
+
+function web({ me = {}, query = '' } = {}) {
+ const who = signedInName(me);
+ const body = `<div class="sf-wrap sf-page-pad sf-narrow">
+<header class="sf-page-h"><p class="sf-kicker">Web · Surf with Vibe</p><h1>Tell it what to find</h1>
+<p class="sf-lede">An agent surfs the web for you: it searches, reads the pages that matter on a private desk when a page needs a real browser, keeps its sources, and finishes with a short report that cites them.</p></header>
+<section id="sf-web" class="sf-card" aria-labelledby="sf-web-h"><h2 id="sf-web-h" class="sf-sr">What should Vibe find?</h2>
+<form class="sf-web-form" id="sf-web-form" data-sf-web-form action="/web/go" method="post">
+<label for="sf-web-query" class="sf-sr">What should Vibe find?</label>
+<textarea id="sf-web-query" name="q" rows="3" minlength="3" maxlength="500" required spellcheck="true" placeholder="e.g. a cheap flight to Lisbon in March and three things to do">${esc(query)}</textarea>
+<div class="sf-web-row"><span class="sf-fine" id="sf-web-who">${who ? `Runs as <b>${esc(who)}</b>, on your OpenVibe allowance.` : 'You will be asked to sign in first.'}</span>
+<button class="ovk-btn ovk-btn--primary" id="sf-web-submit" type="submit">${kit.icon('globe')}Surf it</button></div>
+</form>
+<p class="sf-status" id="sf-web-status" role="status"></p>
+<div class="sf-examples" aria-label="Try one">${EXAMPLES.map((x) => `<button type="button" class="sf-ex" data-sf-example="${esc(x)}">${esc(x)}</button>`).join('')}</div>
+</section>
+<section id="sf-web-how" aria-labelledby="sf-web-how-h"><h2 id="sf-web-how-h">What happens next</h2>
+<ol class="sf-how">
+<li><b>It searches.</b><span>The web first through OpenVibe's Search Fabric, so it starts with what is known and cited.</span></li>
+<li><b>It reads.</b><span>It opens the pages that matter, and a private desk when a page needs a real browser. You can watch the desk live and take over.</span></li>
+<li><b>It reports.</b><span>A short answer with every source it used, in your Actor tasks and in the Action Center.</span></li>
+</ol>
+<p class="sf-fine">It never signs in or pays for you: at a login, a captcha or a checkout it stops and hands over. OpenVibe.Surf keeps neither your question nor the report; the task is yours at OpenVibe.Actor.</p></section>
+</div>`;
+ return layout({ title: 'Surf with Vibe', description: 'Ask an agent to surf the web for you: it searches, reads pages on a private desk when needed, keeps its sources and reports back with citations.', path: '/web', body, active: '/web' });
+}
+
+function developers() {
+ const rows = [
+ ['GET /api/v1/watch/feed?topic=all|live|clips|moments&cursor=', 'The merged OpenVibe.Live feed: who is live (by viewers), then clips and AI moments by freshness, never two from one channel in a row. Each item says why it is on. Cached 30 s; a source that did not answer is named in <code>sources</code>.'],
+ ['GET /api/v1/ride/maps', 'The surf maps from OpenVibe.Games: id, name, tier, stages, author, runs, the record. Cached 60 s.'],
+ ['GET /api/v1/ride/maps/:id', 'One map, its fastest runs and its <code>embed_url</code>.'],
+ ['POST /api/v1/web/tasks', 'Signed in, from this site: <code>{ query }</code> (3–500 characters) starts an OpenVibe.Actor task as you and answers <code>{ task_id, page }</code>.'],
+ ];
+ const body = `<div class="sf-wrap sf-page-pad">
+<header class="sf-page-h"><p class="sf-kicker">For developers</p><h1>The Surf API</h1>
+<p class="sf-lede">Watch and Ride are public reads; Web starts a task at OpenVibe.Actor as the signed-in person. Errors are RFC 9457 <code>application/problem+json</code>.</p></header>
+<dl class="sf-eps">${rows.map(([ep, what]) => `<div><dt><code>${ep}</code></dt><dd>${what}</dd></div>`).join('')}</dl>
+</div>`;
+ return layout({ title: 'The Surf API', path: '/developers', body, active: '/developers' });
+}
+
+function notFound() {
+ const body = `<div class="sf-wrap sf-page-pad sf-narrow"><div class="sf-empty" id="sf-not-found">${kit.icon('game')}<h1>Wiped out</h1><p>There is nothing at this address. Try <a href="/ride">a map</a>, <a href="/watch">what's on</a> or <a href="/web">asking Vibe</a>.</p></div></div>`;
+ return layout({ title: 'Not found', robots: 'noindex, follow', body, active: '/' });
+}
+
+module.exports = {
+ home, ride, map, watch, web, developers, notFound, runTime,
+ layout,
+ setRelease: layoutLib.setRelease,
+ configure: layoutLib.configure,
+};
new file mode 100644
new file mode 100644
@@ -0,0 +1,312 @@
@@ -0,0 +1,312 @@
+'use strict';
+
+/**
+ * The Watch feed: OpenVibe.Live's three surfaces merged into one list.
+ *
+ * GET /api/v1/watch/feed?topic=all|live|clips|moments&cursor=
+ *
+ * Sources (all on OV_LIVE_INTERNAL_URL):
+ * live GET /api/streams → { streams: [...] }
+ * clips GET /api/content/feed?type=clips&sort=new… → { items: [...], next }
+ * moments GET /api/content/moments?type=clips&sort=top… → { items: [...], next }
+ *
+ * One shape for every card:
+ * { key, kind, title, channel: { username, display_name, avatar_url }, thumbnail,
+ * play: { type: 'live'|'hls', src, page }, duration_seconds, viewers, why }
+ *
+ * Live streams come first, by viewers. Clips and moments then interleave by freshness. Finally no two cards from
+ * the same channel sit next to each other when another channel is available. The merged page is cached 30 s;
+ * every Live source is given 2 s and a source that fails is left out and named in `sources`.
+ */
+const DEFAULT_TOPIC = 'all';
+const TOPICS = ['all', 'live', 'clips', 'moments'];
+const SOURCE_NAMES = ['live', 'clips', 'moments'];
+const CLIP_PATH = '/api/content/feed';
+const MOMENT_PATH = '/api/content/moments';
+
+class WatchError extends Error {
+ constructor(status, code, message) {
+ super(message);
+ this.status = status;
+ this.code = code;
+ }
+}
+
+const clean = (v, max = 140) => {
+ const s = String(v == null ? '' : v).replace(/\s+/g, ' ').trim();
+ if (!max || s.length <= max) return s;
+ return `${s.slice(0, max - 1).replace(/\s+\S*$/, '')}…`;
+};
+const num = (v) => (Number.isFinite(Number(v)) ? Number(v) : 0);
+const numOrNull = (v) => (Number.isFinite(Number(v)) && v !== null && v !== '' ? Number(v) : null);
+const fresh = (item) => {
+ const n = Date.parse(String(item && item.created_at || ''));
+ return Number.isFinite(n) ? n : 0;
+};
+
+/** An absolute http(s) URL, or null. Media is the only place cards point for pictures and playlists. */
+function mediaUrl(v) {
+ if (typeof v !== 'string' || !v) return null;
+ try {
+ const u = new URL(v);
+ return u.protocol === 'https:' || u.protocol === 'http:' ? u.toString() : null;
+ } catch { return null; }
+}
+
+/** A card's page: on OpenVibe.Live, or on openvibe.video where Live's clips and moments open now. */
+const VIDEO_ORIGINS = new Set(['https://openvibe.video']);
+function livePage(livePublicUrl, href) {
+ if (typeof href !== 'string' || !href) return null;
+ try {
+ const base = new URL(`${livePublicUrl}/`);
+ const u = new URL(href, base);
+ return u.origin === base.origin || VIDEO_ORIGINS.has(u.origin) ? u.toString() : null;
+ } catch { return null; }
+}
+
+function channelOf(c) {
+ if (typeof c === 'string' && c) c = { username: c, display_name: c };
+ const ch = c && typeof c === 'object' ? c : {};
+ const username = typeof ch.username === 'string' && ch.username ? ch.username.slice(0, 64) : null;
+ return {
+ username,
+ display_name: clean(ch.display_name || username || 'Unknown channel', 80) || 'Unknown channel',
+ avatar_url: /^https:\/\/[^\s"'<>]{1,500}$/.test(String(ch.avatar_url || '')) ? String(ch.avatar_url) : null,
+ };
+}
+
+function idOf(item, kind) {
+ if (item && (typeof item.id === 'string' || typeof item.id === 'number')) return String(item.id);
+ if (item && typeof item.key === 'string' && item.key) return item.key.includes(':') ? item.key.slice(item.key.indexOf(':') + 1) : item.key;
+ return null;
+}
+
+function sortLive(items) {
+ return [...items].sort((a, b) => (b.viewers - a.viewers) || String(a.channel.username || '').localeCompare(String(b.channel.username || '')));
+}
+function sortFresh(items) {
+ return [...items].sort((a, b) => fresh(b) - fresh(a));
+}
+
+/**
+ * Moments and clips interleaved by freshness: the freshest overall goes first, then the other kind, then back,
+ * each side keeping its own newest-first order. When one side runs out the rest of the other follows.
+ */
+function interleave(moments, clips) {
+ const a = sortFresh(moments);
+ const b = sortFresh(clips);
+ const out = [];
+ while (a.length || b.length) {
+ let takeMoment;
+ if (!a.length) takeMoment = false;
+ else if (!b.length) takeMoment = true;
+ else if (!out.length) takeMoment = fresh(a[0]) >= fresh(b[0]);
+ else takeMoment = out[out.length - 1].kind !== 'moment';
+ out.push(takeMoment ? a.shift() : b.shift());
+ }
+ return out;
+}
+
+/**
+ * Never two cards from the same channel in a row when another channel is available: when the next card repeats
+ * the previous channel, pull the nearest later card from a different channel forward. Order otherwise stands.
+ */
+function separateChannels(items) {
+ const pool = [...items];
+ const out = [];
+ while (pool.length) {
+ let at = 0;
+ const prev = out[out.length - 1];
+ if (prev && prev.channel.username && pool[0].channel.username === prev.channel.username) {
+ const other = pool.findIndex((it) => it.channel.username !== prev.channel.username);
+ if (other > 0) at = other;
+ }
+ out.push(pool.splice(at, 1)[0]);
+ }
+ return out;
+}
+
+function rank(source, topic) {
+ if (topic === 'live') return separateChannels(sortLive(source.live));
+ if (topic === 'clips') return separateChannels(sortFresh(source.clips));
+ if (topic === 'moments') return separateChannels(sortFresh(source.moments));
+ // Live stays first, always: separate channels within the live block and within the content block, then fix
+ // only the boundary between the two by pulling a content card from another channel forward.
+ const live = separateChannels(sortLive(source.live));
+ let content = separateChannels(interleave(source.moments, source.clips));
+ if (live.length && content.length) {
+ const prev = live[live.length - 1];
+ if (prev.channel.username && content[0].channel.username === prev.channel.username) {
+ const other = content.findIndex((it) => it.channel.username !== prev.channel.username);
+ if (other > 0) {
+ content.unshift(content.splice(other, 1)[0]);
+ content = separateChannels(content);
+ }
+ }
+ }
+ return [...live, ...content];
+}
+
+const encodeCursor = (value) => Buffer.from(JSON.stringify(value), 'utf8').toString('base64url');
+
+function decodeCursor(raw) {
+ if (!raw) return {};
+ let parsed = null;
+ try { parsed = JSON.parse(Buffer.from(String(raw), 'base64url').toString('utf8')); } catch { parsed = null; }
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) throw new WatchError(400, 'watch.invalid_cursor', 'That cursor is not one this feed made.');
+ const out = {};
+ if (typeof parsed.clips === 'string' && parsed.clips) out.clips = parsed.clips;
+ if (typeof parsed.moments === 'string' && parsed.moments) out.moments = parsed.moments;
+ return out;
+}
+
+function sourcesOf(topic) {
+ if (topic === 'all') return [...SOURCE_NAMES];
+ return [topic];
+}
+
+function createWatchFeed({ config, fetchImpl = globalThis.fetch, log = console, now = () => Date.now() }) {
+ const cache = new Map();
+ const ttl = config.watchCacheMs == null ? 30_000 : config.watchCacheMs;
+ const sourceTimeout = config.watchSourceTimeoutMs || 2000;
+
+ async function getJson(path) {
+ let res;
+ try {
+ res = await fetchImpl(`${config.liveInternalUrl}${path}`, {
+ headers: { Accept: 'application/json' },
+ signal: AbortSignal.timeout(sourceTimeout),
+ });
+ } catch (err) {
+ const timedOut = err && (err.name === 'TimeoutError' || err.name === 'AbortError');
+ throw Object.assign(new Error(timedOut ? 'OpenVibe.Live timed out' : 'OpenVibe.Live did not answer'), { source: timedOut ? 'timeout' : 'error' });
+ }
+ if (!res.ok) throw Object.assign(new Error(`OpenVibe.Live answered ${res.status}`), { source: 'error' });
+ let data = null;
+ try { data = await res.json(); } catch { data = null; }
+ if (!data || typeof data !== 'object') throw Object.assign(new Error('OpenVibe.Live answered without JSON'), { source: 'error' });
+ return data;
+ }
+
+ async function loadLive() {
+ const data = await getJson('/api/streams');
+ const streams = Array.isArray(data.streams) ? data.streams : [];
+ const items = streams.filter((s) => s && typeof s.username === 'string' && s.username).map((s) => {
+ const viewers = num(s.total_viewer_count != null ? s.total_viewer_count : (s.viewer_count != null ? s.viewer_count : s.viewers));
+ return {
+ key: `live:${s.username}`,
+ kind: 'live',
+ title: clean(s.title, 140) || 'Live now',
+ channel: channelOf({ username: s.username, display_name: s.display_name, avatar_url: s.avatar_url }),
+ thumbnail: mediaUrl(s.thumbnail_url || s.thumbnail),
+ play: { type: 'live', src: null, page: livePage(config.livePublicUrl, `/@${encodeURIComponent(s.username)}`) },
+ duration_seconds: numOrNull(s.duration_seconds),
+ viewers,
+ why: `Live now · ${viewers} watching`,
+ created_at: s.started_at || null,
+ };
+ });
+ return { items, next: null };
+ }
+
+ async function loadContent(kind, path, query, cursor) {
+ const sourcePath = cursor
+ ? `${path}?${new URLSearchParams({ ...query, cursor }).toString()}`
+ : `${path}?${new URLSearchParams(query).toString()}`;
+ const data = await getJson(sourcePath);
+ const rows = Array.isArray(data.items) ? data.items : [];
+ const items = [];
+ for (const it of rows) {
+ if (!it || typeof it !== 'object') continue;
+ const id = idOf(it, kind);
+ const src = mediaUrl(it.preview_url);
+ const page = livePage(config.livePublicUrl, it.href);
+ if (!id || !src || !page) continue; // a card without a playable preview is not a Watch card
+ items.push({
+ key: `${kind}:${id}`,
+ kind,
+ title: clean(it.title, 140) || (kind === 'clip' ? 'Clip' : 'Moment'),
+ channel: channelOf(it.channel),
+ thumbnail: mediaUrl(it.thumbnail_url || it.thumbnail),
+ play: { type: 'hls', src, page },
+ duration_seconds: numOrNull(it.duration_seconds),
+ viewers: null,
+ why: kind === 'clip' ? 'New clip' : 'Top AI moment this week',
+ created_at: it.created_at || null,
+ });
+ }
+ return { items, next: typeof data.next === 'string' && data.next ? data.next : null };
+ }
+
+ /**
+ * One merged page. `cursor` is this feed's opaque cursor (it carries each Live source's cursor). A bad cursor
+ * or topic is a WatchError; a failing source never is — it is left out and named.
+ */
+ async function feed(topic = DEFAULT_TOPIC, cursorRaw = null) {
+ const name = String(topic || DEFAULT_TOPIC);
+ if (!TOPICS.includes(name)) throw new WatchError(400, 'watch.invalid_topic', 'topic must be all, live, clips or moments.');
+ const cursor = decodeCursor(cursorRaw);
+ const key = `${name}|${cursorRaw || ''}`;
+ const hit = cache.get(key);
+ if (hit && now() - hit.at < ttl) return hit.value;
+
+ const wanted = sourcesOf(name);
+ const includeLive = wanted.includes('live') && !(name === 'all' && cursorRaw);
+ const jobs = [];
+ const status = {};
+ const value = { live: [], clips: [], moments: [] };
+ const nexts = { clips: null, moments: null };
+
+ if (includeLive) jobs.push(loadLive().then((out) => { value.live = out.items; status.live = 'ok'; }, (err) => { status.live = (err && err.source) || 'error'; log.warn('[OpenVibe.Surf] watch live source failed:', (err && err.message) || err); }));
+ else if (wanted.includes('live')) status.live = 'ok';
+ if (wanted.includes('clips')) jobs.push(loadContent('clip', CLIP_PATH, { type: 'clips', sort: 'new', window: 'week' }, cursor.clips)
+ .then((out) => { value.clips = out.items; nexts.clips = out.next; status.clips = 'ok'; }, (err) => { status.clips = (err && err.source) || 'error'; log.warn('[OpenVibe.Surf] watch clips source failed:', (err && err.message) || err); }));
+ if (wanted.includes('moments')) jobs.push(loadContent('moment', MOMENT_PATH, { type: 'clips', sort: 'top', window: 'week' }, cursor.moments)
+ .then((out) => { value.moments = out.items; nexts.moments = out.next; status.moments = 'ok'; }, (err) => { status.moments = (err && err.source) || 'error'; log.warn('[OpenVibe.Surf] watch moments source failed:', (err && err.message) || err); }));
+
+ await Promise.all(jobs);
+
+ const failed = Object.keys(status).filter((k) => status[k] !== 'ok');
+ // Paging is per Live source; a failed source has no next page until it answers again.
+ const nextParts = {};
+ if (nexts.clips) nextParts.clips = nexts.clips;
+ if (nexts.moments) nextParts.moments = nexts.moments;
+ const result = {
+ topic: name,
+ items: rank(value, name).map(({ created_at, ...card }) => card),
+ next: Object.keys(nextParts).length ? encodeCursor(nextParts) : null,
+ sources: status,
+ partial: failed.length > 0,
+ };
+ // A partial page is not kept: the next request should ask the missing source again. A complete page is
+ // cached for the whole 30 s.
+ if (!failed.length) {
+ cache.set(key, { at: now(), value: result });
+ if (cache.size > 100) cache.delete(cache.keys().next().value);
+ }
+ return result;
+ }
+
+ /** Readiness: is Live answering at all? Any 2xx counts; 404 is a service that is not Live. */
+ async function probe() {
+ try {
+ const res = await fetchImpl(`${config.liveInternalUrl}/api/streams`, {
+ headers: { Accept: 'application/json' },
+ signal: AbortSignal.timeout(sourceTimeout),
+ });
+ return res.ok ? true : `OpenVibe.Live answered ${res.status}`;
+ } catch (err) {
+ return err && (err.name === 'TimeoutError' || err.name === 'AbortError') ? 'OpenVibe.Live timed out' : 'OpenVibe.Live did not answer';
+ }
+ }
+
+ function clear() { cache.clear(); }
+
+ return { feed, probe, clear, get cached() { return cache; } };
+}
+
+module.exports = {
+ createWatchFeed, WatchError, TOPICS, SOURCE_NAMES,
+ // Exported for the tests that pin the ordering rules.
+ interleave, separateChannels, sortLive, sortFresh, decodeCursor, encodeCursor,
+};
new file mode 100644
new file mode 100644
@@ -0,0 +1,93 @@
@@ -0,0 +1,93 @@
+'use strict';
+
+/**
+ * Surf with Vibe: ask OpenVibe.Actor to browse the web on a private desk.
+ *
+ * A task is created with the person's OWN Network access token from the httpOnly `surf_at` cookie as a Bearer.
+ * Actor accepts a person's session token at POST /api/v1/tasks (server/http/principal.js); the task then belongs
+ * to them (their allowance, their task list), not to OpenVibe.Surf. The token is only ever put in the
+ * Authorization header of the request to the configured Actor origin, and is never logged or rendered.
+ *
+ * The task text is a short standing instruction plus the person's query: browse with its tools (search_web,
+ * read_page, a desk if a page needs it), keep its sources, and finish with a cited report. The request carries an
+ * Idempotency-Key (and Actor's body `idempotency_key`: the same value), so a retried form does not start a second
+ * task.
+ *
+ * Actor's 4xx answer (401, 403, 429 allowance/budget, 422) is a refusal the person can read: its status and detail
+ * pass through. A timeout or a 5xx is this service's 502.
+ */
+const crypto = require('crypto');
+
+const MIN_QUERY = 3;
+const MAX_QUERY = 500;
+const IDEMPOTENCY = /^[A-Za-z0-9._:-]{8,128}$/;
+const PREFIX = 'Surf the web for me with your tools. Use search_web to find pages, read_page to read them, and a desk when a page needs one. Keep every source you use and finish with a short report that cites each source.';
+
+function validQuery(value) {
+ if (typeof value !== 'string') return false;
+ const q = value.trim();
+ return q.length >= MIN_QUERY && q.length <= MAX_QUERY;
+}
+
+/** The instruction Actor receives: the standing browsing brief, then the person's words unchanged. */
+function taskText(query) {
+ return `${PREFIX}\n\n${String(query == null ? '' : query).trim()}`;
+}
+
+function newIdempotencyKey() {
+ return `surf:web:${crypto.randomBytes(16).toString('hex')}`;
+}
+
+function createWeb({ config, fetchImpl = globalThis.fetch, log = console }) {
+ /**
+ * Create one task for the person whose token this is. Resolves { task_id, page } or throws
+ * { status, code, detail, actor } — a 4xx from Actor keeps its status and detail.
+ */
+ async function createTask({ token, query, idempotencyKey = null } = {}) {
+ if (!token) throw Object.assign(new Error('Sign in to surf the web.'), { status: 401, code: 'token.required' });
+ if (!validQuery(query)) throw Object.assign(new Error('query must be 3 to 500 characters.'), { status: 400, code: 'web.invalid_query' });
+ const key = IDEMPOTENCY.test(String(idempotencyKey || '')) ? String(idempotencyKey) : newIdempotencyKey();
+ let res;
+ try {
+ res = await fetchImpl(`${config.actorInternalUrl}/api/v1/tasks`, {
+ method: 'POST',
+ headers: {
+ Accept: 'application/json',
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${token}`,
+ 'Idempotency-Key': key,
+ },
+ body: JSON.stringify({ task: taskText(query), idempotency_key: key }),
+ signal: AbortSignal.timeout(config.actorTimeoutMs || 15_000),
+ });
+ } catch (err) {
+ const timedOut = err && (err.name === 'TimeoutError' || err.name === 'AbortError');
+ log.warn('[OpenVibe.Surf] Actor did not answer:', timedOut ? 'timeout' : ((err && err.message) || err));
+ throw Object.assign(new Error('OpenVibe.Actor did not answer. Try again in a moment.'), { status: 502, code: 'actor.unavailable' });
+ }
+ const text = await res.text().catch(() => '');
+ let data = null;
+ try { data = text ? JSON.parse(text) : null; } catch { data = null; }
+ if (!res.ok) {
+ // Actor's problem is written for the person: carry its status and words through, never its body verbatim
+ // (it may echo request detail; the detail is what a person can act on).
+ const detail = String((data && (data.detail || data.title || data.error)) || text || `OpenVibe.Actor answered ${res.status}`).slice(0, 400);
+ throw Object.assign(new Error(detail), {
+ status: res.status >= 400 && res.status < 500 ? res.status : 502,
+ code: (data && data.code) || 'actor.refused',
+ detail,
+ actor: data,
+ });
+ }
+ const id = data && typeof data.id === 'string' ? data.id : null;
+ if (!id) {
+ log.warn('[OpenVibe.Surf] Actor answered without a task id');
+ throw Object.assign(new Error('OpenVibe.Actor answered without a task id.'), { status: 502, code: 'actor.invalid_response' });
+ }
+ return { task_id: id, page: `${String(config.actorPublicUrl).replace(/\/+$/, '')}/tasks/${encodeURIComponent(id)}` };
+ }
+
+ return { createTask, validQuery };
+}
+
+module.exports = { createWeb, taskText, validQuery, newIdempotencyKey, PREFIX, MIN_QUERY, MAX_QUERY };
new file mode 100644
new file mode 100644
@@ -0,0 +1,44 @@
@@ -0,0 +1,44 @@
+'use strict';
+/** The crawl artifacts: robots.txt, sitemap.xml (the public pages and the Ride maps) and llms.txt. */
+const assert = require('assert');
+const { boot, check, done } = require('./helpers/boot');
+
+(async () => {
+ const t = await boot();
+
+ await check('robots.txt names the sitemap and keeps crawlers out of the API, sign-in and the task form', async () => {
+ const r = await t.get('/robots.txt');
+ assert.strictEqual(r.status, 200);
+ assert.match(r.headers.get('content-type'), /text\/plain/);
+ assert.match(r.text, /Sitemap: https:\/\/openvibe\.surf\/sitemap\.xml/);
+ assert.match(r.text, /Disallow: \/api\//);
+ assert.match(r.text, /Disallow: \/auth\//);
+ assert.match(r.text, /Disallow: \/web\/go/);
+ });
+
+ await check('sitemap.xml lists the five public pages and the maps Games lists', async () => {
+ const r = await t.get('/sitemap.xml');
+ assert.strictEqual(r.status, 200);
+ assert.match(r.headers.get('content-type'), /application\/xml/);
+ for (const path of ['/', '/ride', '/watch', '/web', '/developers']) {
+ assert.ok(r.text.includes(`<loc>https://openvibe.surf${path}</loc>`), `sitemap has ${path}`);
+ }
+ assert.match(r.text, /<loc>https:\/\/openvibe\.surf\/ride\/surf_lagoon<\/loc>/);
+ assert.match(r.text, /<lastmod>2026-/);
+ });
+
+ await check('llms.txt describes the room, its three surfs and the machine-readable routes', async () => {
+ const r = await t.get('/llms.txt');
+ assert.strictEqual(r.status, 200);
+ assert.match(r.text, /# OpenVibe\.Surf/);
+ assert.match(r.text, /Ride maps, surf the web, surf what's on/i);
+ assert.match(r.text, /https:\/\/openvibe\.surf\/api\/v1\/watch\/feed\?topic=all/);
+ assert.match(r.text, /https:\/\/openvibe\.surf\/api\/v1\/ride\/maps/);
+ assert.match(r.text, /https:\/\/openvibe\.live/);
+ assert.match(r.text, /https:\/\/openvibe\.games/);
+ assert.match(r.text, /https:\/\/openvibe\.actor/);
+ });
+
+ await t.close();
+ done();
+})().catch((err) => { console.error(err); process.exit(1); });
new file mode 100644
new file mode 100644
@@ -0,0 +1,165 @@
@@ -0,0 +1,165 @@
+'use strict';
+/**
+ * Watch: the merge and its order (live streams by viewers, moments and clips interleaved by freshness, never two
+ * cards from one channel in a row), the one item shape, a failing source left out and named, and the 30 s cache.
+ */
+const assert = require('assert');
+const { boot, check, done } = require('./helpers/boot');
+
+const kinds = (feed) => feed.items.map((i) => i.kind);
+const channels = (feed) => feed.items.map((i) => i.channel.username);
+
+(async () => {
+ const t = await boot();
+
+ await check('GET /api/v1/watch/feed merges the three sources into the one shape', async () => {
+ t.ctx.feed.clear();
+ const r = await t.get('/api/v1/watch/feed?topic=all');
+ assert.strictEqual(r.status, 200, r.text);
+ assert.strictEqual(r.headers.get('cache-control'), 'public, max-age=30');
+ const b = r.json();
+ assert.strictEqual(b.topic, 'all');
+ assert.deepStrictEqual(b.sources, { live: 'ok', clips: 'ok', moments: 'ok' });
+ assert.strictEqual(b.partial, false);
+ assert.deepStrictEqual(kinds(b), ['live', 'live', 'live', 'moment', 'clip', 'clip'], 'lives first, then moments/clips interleaved by freshness');
+ assert.deepStrictEqual(channels(b).slice(0, 3), ['bo', 'ada', 'cy'], 'live streams by viewers');
+ for (const item of b.items) {
+ assert.deepStrictEqual(Object.keys(item).sort(), ['channel', 'duration_seconds', 'key', 'kind', 'play', 'thumbnail', 'title', 'viewers', 'why'].sort());
+ assert.deepStrictEqual(Object.keys(item.channel).sort(), ['avatar_url', 'display_name', 'username']);
+ assert.deepStrictEqual(Object.keys(item.play).sort(), ['page', 'src', 'type']);
+ assert.match(item.key, /^(live|clip|moment):/);
+ assert.match(item.play.page, /^https:\/\/openvibe\.(live|video)\//, 'a page on Live or on openvibe.video');
+ }
+ const live = b.items[0];
+ assert.strictEqual(live.key, 'live:bo');
+ assert.strictEqual(live.play.type, 'live');
+ assert.strictEqual(live.play.src, null);
+ assert.strictEqual(live.play.page, 'https://openvibe.live/@bo');
+ assert.strictEqual(live.viewers, 38);
+ assert.strictEqual(live.why, 'Live now · 38 watching');
+ const moment = b.items.find((i) => i.kind === 'moment');
+ assert.strictEqual(moment.play.type, 'hls');
+ assert.strictEqual(moment.play.src, 'https://openvibe.media/c/201/master.m3u8');
+ assert.strictEqual(moment.play.page, 'https://openvibe.video/c/201', 'clips and moments open on openvibe.video');
+ assert.strictEqual(moment.why, 'Top AI moment this week');
+ const clip = b.items.find((i) => i.kind === 'clip');
+ assert.strictEqual(clip.why, 'New clip');
+ assert.strictEqual(clip.duration_seconds, 31);
+ });
+
+ await check('moments and clips alternate by freshness, and no channel runs twice', async () => {
+ t.ctx.feed.clear();
+ t.upstreams.state.streams = [
+ { username: 'bo', display_name: 'Bo', title: 'Bo live', viewer_count: 38, total_viewer_count: 38 },
+ { username: 'ada', display_name: 'Ada', title: 'Ada live', viewer_count: 12, total_viewer_count: 12 },
+ ];
+ t.upstreams.state.moments = [
+ { id: 1, href: '/clip/1', title: 'Moment one', created_at: '2026-10-28T10:00:00.000Z', channel: { username: 'mira' }, preview_url: 'https://openvibe.media/c/1/master.m3u8', thumbnail_url: 'https://openvibe.media/c/1.jpg' },
+ { id: 2, href: '/clip/2', title: 'Moment two', created_at: '2026-10-28T08:00:00.000Z', channel: { username: 'noa' }, preview_url: 'https://openvibe.media/c/2/master.m3u8', thumbnail_url: 'https://openvibe.media/c/2.jpg' },
+ ];
+ t.upstreams.state.clips = [
+ { id: 11, href: '/clip/11', title: 'Clip one', created_at: '2026-10-28T09:00:00.000Z', channel: { username: 'ada' }, preview_url: 'https://openvibe.media/c/11/master.m3u8' },
+ { id: 12, href: '/clip/12', title: 'Clip two', created_at: '2026-10-28T07:00:00.000Z', channel: { username: 'bo' }, preview_url: 'https://openvibe.media/c/12/master.m3u8' },
+ ];
+ const b = (await t.get('/api/v1/watch/feed?topic=all')).json();
+ assert.deepStrictEqual(kinds(b), ['live', 'live', 'moment', 'clip', 'moment', 'clip']);
+ assert.deepStrictEqual(channels(b), ['bo', 'ada', 'mira', 'ada', 'noa', 'bo']);
+ const names = channels(b);
+ for (let i = 1; i < names.length; i++) assert.notStrictEqual(names[i], names[i - 1], `no channel twice in a row at ${i}`);
+ });
+
+ await check('a card from the same channel as the previous one is moved down', async () => {
+ t.ctx.feed.clear();
+ t.upstreams.state.streams = [{ username: 'cy', display_name: 'Cy', title: 'Cy live', viewer_count: 5, total_viewer_count: 5 }];
+ t.upstreams.state.moments = [{ id: 21, href: '/clip/21', title: 'Cy moment', created_at: '2026-10-28T10:00:00.000Z', channel: { username: 'cy' }, preview_url: 'https://openvibe.media/c/21/master.m3u8' }];
+ t.upstreams.state.clips = [
+ { id: 31, href: '/clip/31', title: 'Ada clip', created_at: '2026-10-28T09:00:00.000Z', channel: { username: 'ada' }, preview_url: 'https://openvibe.media/c/31/master.m3u8' },
+ { id: 32, href: '/clip/32', title: 'Bo clip', created_at: '2026-10-28T08:00:00.000Z', channel: { username: 'bo' }, preview_url: 'https://openvibe.media/c/32/master.m3u8' },
+ ];
+ const b = (await t.get('/api/v1/watch/feed?topic=all')).json();
+ assert.strictEqual(b.items.length, 4);
+ assert.strictEqual(b.items[0].channel.username, 'cy', 'the live card stays first');
+ assert.notStrictEqual(b.items[1].channel.username, 'cy', 'the next card is another channel');
+ const names = channels(b);
+ for (let i = 1; i < names.length; i++) assert.notStrictEqual(names[i], names[i - 1], `no channel twice in a row at ${i}`);
+ });
+
+ await check('a failing source is left out and named in sources', async () => {
+ t.ctx.feed.clear();
+ t.upstreams.state.fail.moments = 500;
+ const r = await t.get('/api/v1/watch/feed?topic=all');
+ t.upstreams.state.fail.moments = null;
+ assert.strictEqual(r.status, 200, 'the feed still answers');
+ const b = r.json();
+ assert.ok(!b.items.some((i) => i.kind === 'moment'), 'no moment cards');
+ assert.ok(b.items.some((i) => i.kind === 'clip'), 'clips still there');
+ assert.strictEqual(b.sources.moments, 'error');
+ assert.strictEqual(b.sources.clips, 'ok');
+ assert.strictEqual(b.partial, true);
+ const recovered = await t.get('/api/v1/watch/feed?topic=all');
+ assert.strictEqual(recovered.json().sources.moments, 'ok', 'a partial page is not cached: the next request retries');
+ assert.ok(recovered.json().items.some((i) => i.kind === 'moment'));
+ });
+
+ await check('a slow source times out after its 2 s budget (500 ms in this test) and is named', async () => {
+ t.ctx.feed.clear();
+ t.upstreams.state.delay.streams = 900; // boot sets the source budget to 500 ms
+ const started = Date.now();
+ const r = await t.get('/api/v1/watch/feed?topic=live');
+ t.upstreams.state.delay.streams = 0;
+ assert.strictEqual(r.status, 200);
+ assert.strictEqual(r.json().sources.live, 'timeout');
+ assert.ok(Date.now() - started < 1500, 'it did not wait for the slow source');
+ assert.deepStrictEqual(r.json().items, []);
+ });
+
+ await check('the merged feed is cached for 30 s', async () => {
+ t.ctx.feed.clear();
+ t.upstreams.resetCounts();
+ await t.get('/api/v1/watch/feed?topic=all');
+ const once = { ...t.upstreams.state.counts };
+ assert.strictEqual(once.streams, 1);
+ assert.strictEqual(once.clips, 1);
+ assert.strictEqual(once.moments, 1);
+ await t.get('/api/v1/watch/feed?topic=all');
+ assert.deepStrictEqual({ ...t.upstreams.state.counts }, once, 'the second read is the cached page');
+ t.clock.advance(30_001);
+ await t.get('/api/v1/watch/feed?topic=all');
+ assert.strictEqual(t.upstreams.state.counts.streams, once.streams + 1, 'after 30 s it asks again');
+ });
+
+ await check('topic filters only fetch what they need, and a cursor pages the Live feeds', async () => {
+ t.ctx.feed.clear();
+ t.upstreams.resetCounts();
+ const live = await t.get('/api/v1/watch/feed?topic=live');
+ assert.deepStrictEqual(kinds(live.json()), ['live']);
+ assert.strictEqual(t.upstreams.state.counts.clips, 0);
+ assert.strictEqual(t.upstreams.state.counts.moments, 0);
+
+ t.ctx.feed.clear();
+ t.upstreams.state.clipsNext = 'clip-page-2';
+ t.upstreams.state.momentsNext = 'moment-page-2';
+ const first = (await t.get('/api/v1/watch/feed?topic=all')).json();
+ assert.ok(first.next, 'a next cursor when either source has more');
+ t.upstreams.resetCounts();
+ const second = await t.get(`/api/v1/watch/feed?topic=all&cursor=${encodeURIComponent(first.next)}`);
+ assert.strictEqual(second.status, 200, second.text);
+ assert.strictEqual(t.upstreams.state.counts.streams, 0, 'live streams are not repeated on page two');
+ const clips = t.upstreams.lastFor('/api/content/feed');
+ const moments = t.upstreams.lastFor('/api/content/moments');
+ assert.strictEqual(clips.query.cursor, 'clip-page-2');
+ assert.strictEqual(moments.query.cursor, 'moment-page-2');
+ });
+
+ await check('a bad topic or cursor is a 400 problem', async () => {
+ const badTopic = await t.get('/api/v1/watch/feed?topic=nope');
+ assert.strictEqual(badTopic.status, 400);
+ assert.strictEqual(badTopic.json().code, 'watch.invalid_topic');
+ const badCursor = await t.get('/api/v1/watch/feed?topic=all&cursor=not-a-cursor');
+ assert.strictEqual(badCursor.status, 400);
+ assert.strictEqual(badCursor.json().code, 'watch.invalid_cursor');
+ });
+
+ await t.close();
+ done();
+})().catch((err) => { console.error(err); process.exit(1); });
new file mode 100644
new file mode 100644
@@ -0,0 +1,106 @@
@@ -0,0 +1,106 @@
+'use strict';
+/**
+ * Boots OpenVibe.Surf on a random port against an in-process Network (test/helpers/network.js) and in-process
+ * Live/Games/Actor stand-ins (test/helpers/upstreams.js), then returns a small HTTP client. Every test file gets
+ * its own process, servers and app.
+ *
+ * const t = await boot();
+ * t.get(path, { as: user, cookie, json, form, headers, method })
+ * t.signIn(user) → cookie header value (surf_at = a Network user token, as after /auth/callback)
+ * t.clock.advance(31_000) → move the feed/ride cache clock
+ * t.logs(), t.close()
+ */
+const http = require('http');
+const { startNetwork } = require('./network');
+const { startUpstreams } = require('./upstreams');
+
+async function boot(opts = {}) {
+ const network = await startNetwork();
+ const upstreams = await startUpstreams();
+ const env = {
+ NODE_ENV: 'test', PORT: '0', BASE_URL: 'https://openvibe.surf', TRUST_PROXY: '1',
+ OV_NETWORK_URL: network.url, OV_NETWORK_INTERNAL_URL: network.url, OV_NETWORK_ISSUER: network.url,
+ OV_OAUTH_CLIENT_ID: 'surf', OV_OAUTH_CLIENT_SECRET: 'surf-secret', COOKIE_SECURE: 'false',
+ SURF_RATE_LIMIT_PER_MIN: '100000',
+ OV_LIVE_INTERNAL_URL: upstreams.url,
+ OV_GAMES_INTERNAL_URL: upstreams.url,
+ OV_ACTOR_INTERNAL_URL: upstreams.url,
+ SURF_GAMES_PUBLIC_URL: 'https://openvibe.games',
+ SURF_LIVE_PUBLIC_URL: 'https://openvibe.live',
+ SURF_ACTOR_PUBLIC_URL: 'https://openvibe.actor',
+ SURF_WATCH_SOURCE_TIMEOUT_MS: '500',
+ SURF_GAMES_TIMEOUT_MS: '1000',
+ SURF_ACTOR_TIMEOUT_MS: '1000',
+ ...(opts.env || {}),
+ };
+ for (const [k, v] of Object.entries(opts.env || {})) if (v === null) delete env[k];
+
+ const clock = {
+ t: Date.now(),
+ now() { return this.t; },
+ advance(ms) { this.t += ms; },
+ };
+
+ const logLines = [];
+ const log = {
+ log: (...a) => logLines.push(a.map(String).join(' ')),
+ info: (...a) => logLines.push(a.map(String).join(' ')),
+ warn: (...a) => logLines.push(a.map(String).join(' ')),
+ error: (...a) => logLines.push(a.map(String).join(' ')),
+ };
+
+ const configLib = require('../../server/config');
+ const { createApp } = require('../../server/app');
+ const config = configLib.load(env);
+ const built = createApp({ config, log, now: () => clock.t });
+ await built.ctx.keys.ensure();
+ const server = await new Promise((resolve) => { const s = http.createServer(built.app); s.listen(0, '127.0.0.1', () => resolve(s)); });
+ const base = `http://127.0.0.1:${server.address().port}`;
+
+ const signIn = (user) => `surf_at=${network.userToken(user)}`;
+
+ async function get(p, o = {}) {
+ const headers = { ...(o.headers || {}) };
+ if (o.as) headers.cookie = [signIn(o.as), headers.cookie].filter(Boolean).join('; ');
+ if (o.cookie) headers.cookie = o.cookie;
+ let body = o.body;
+ if (o.json !== undefined) { body = JSON.stringify(o.json); headers['content-type'] = 'application/json'; }
+ if (o.form !== undefined) { body = new URLSearchParams(o.form).toString(); headers['content-type'] = 'application/x-www-form-urlencoded'; }
+ const res = await fetch(base + p, { method: o.method || (body ? 'POST' : 'GET'), headers, body, redirect: 'manual' });
+ const text = Buffer.from(await res.arrayBuffer()).toString('utf8');
+ return {
+ status: res.status,
+ headers: res.headers,
+ text,
+ json() { return JSON.parse(text); },
+ };
+ }
+
+ return {
+ base, network, upstreams, clock, config, ctx: built.ctx, get, signIn,
+ logs: () => logLines.join('\n'),
+ async close() {
+ await new Promise((r) => server.close(r));
+ built.ctx.keys.client.stop();
+ await network.close();
+ await upstreams.close();
+ },
+ };
+}
+
+let failures = 0;
+async function check(name, fn) {
+ try {
+ await fn();
+ process.stdout.write(` ✓ ${name}\n`);
+ } catch (e) {
+ failures++;
+ process.stdout.write(` ✗ ${name}\n ${(e.stack || String(e)).split('\n').slice(0, 10).join('\n ')}\n`);
+ }
+}
+function done() {
+ process.stdout.write(failures ? `\n${failures} failed\n` : '\nall passed\n');
+ process.exit(failures ? 1 : 0);
+}
+
+module.exports = { boot, check, done };
new file mode 100644
new file mode 100644
@@ -0,0 +1,110 @@
@@ -0,0 +1,110 @@
+'use strict';
+/**
+ * An in-process stand-in for OpenVibe.Network: a real RS256 key pair and JWKS, /oauth/token with PKCE S256,
+ * /oauth/revoke and an /api/health route. Every request is recorded so a test can assert what was (and was not)
+ * called. setDown(true) makes every answer a 503.
+ */
+const http = require('http');
+const crypto = require('crypto');
+
+const b64 = (o) => Buffer.from(JSON.stringify(o)).toString('base64url');
+function signJwt(claims, privateKey) {
+ const input = `${b64({ alg: 'RS256', typ: 'JWT' })}.${b64(claims)}`;
+ return `${input}.${crypto.sign('RSA-SHA256', Buffer.from(input), privateKey).toString('base64url')}`;
+}
+
+function listen(handler) {
+ return new Promise((resolve) => {
+ const sockets = new Set();
+ const server = http.createServer((req, res) => {
+ const chunks = [];
+ req.on('data', (c) => chunks.push(c));
+ req.on('end', () => {
+ const raw = Buffer.concat(chunks);
+ const json = (status, obj, headers = {}) => {
+ res.writeHead(status, { 'Content-Type': 'application/json', ...headers });
+ res.end(JSON.stringify(obj));
+ };
+ Promise.resolve(handler(req, raw, json, res)).catch((err) => json(500, { error: String(err && err.message) }));
+ });
+ });
+ server.on('connection', (s) => { sockets.add(s); s.on('close', () => sockets.delete(s)); });
+ server.listen(0, '127.0.0.1', () => resolve({
+ server,
+ url: `http://127.0.0.1:${server.address().port}`,
+ close: () => new Promise((r) => { for (const s of sockets) s.destroy(); server.close(r); }),
+ }));
+ });
+}
+
+async function startNetwork() {
+ const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', { modulusLength: 2048 });
+ const publicPem = publicKey.export({ type: 'spki', format: 'pem' });
+ const privatePem = privateKey.export({ type: 'pkcs8', format: 'pem' });
+ const st = {
+ users: new Map(), codes: new Map(), refresh: new Map(),
+ down: false, requests: [], tokenRequests: [],
+ };
+ let issuer = null;
+ let nextUserId = 1;
+
+ function addUser(username, { role = 'user' } = {}) {
+ const u = { id: nextUserId++, subject: `usr_${crypto.randomBytes(12).toString('hex')}`, username, display_name: username, role };
+ st.users.set(u.id, u);
+ return u;
+ }
+ function userToken(u, { ttl = 3600 } = {}) {
+ const now = Math.floor(Date.now() / 1000);
+ return signJwt({ iss: issuer, sub: u.id, subject_id: u.subject, username: u.username, display_name: u.display_name, role: u.role, aud: ['openvibe.network'], iat: now, exp: now + ttl }, privatePem);
+ }
+
+ const srv = await listen(async (req, raw, json) => {
+ const url = new URL(req.url, 'http://x');
+ const text = raw.toString('utf8');
+ let body = {};
+ try { body = text ? JSON.parse(text) : {}; } catch { body = {}; }
+ st.requests.push({ method: req.method, path: url.pathname, authorization: req.headers.authorization || null, body: text });
+ if (st.down) return json(503, { error: 'Network is restarting' });
+
+ if (url.pathname === '/api/health') return json(200, { status: 'ok', service: 'openvibe-network' });
+ if (url.pathname === '/api/.well-known/jwks') return json(200, { public_key: publicPem, algorithm: 'RS256' });
+ if (url.pathname === '/oauth/revoke') return json(200, {});
+ if (url.pathname === '/oauth/token' && req.method === 'POST') {
+ const form = Object.fromEntries(new URLSearchParams(text));
+ st.tokenRequests.push(form);
+ if (form.client_id !== 'surf' || form.client_secret !== 'surf-secret') return json(401, { error: 'invalid_client', error_description: 'bad client secret' });
+ if (form.grant_type === 'authorization_code') {
+ const c = st.codes.get(form.code);
+ if (!c) return json(400, { error: 'invalid_grant', error_description: 'unknown code' });
+ st.codes.delete(form.code);
+ const challenge = crypto.createHash('sha256').update(String(form.code_verifier || '')).digest('base64url');
+ if (challenge !== c.challenge) return json(400, { error: 'invalid_grant', error_description: 'PKCE verification failed' });
+ const rt = `rt_${crypto.randomBytes(12).toString('hex')}`;
+ st.refresh.set(rt, c.user);
+ return json(200, { access_token: userToken(c.user), refresh_token: rt, token_type: 'Bearer' });
+ }
+ if (form.grant_type === 'refresh_token') {
+ const u = st.refresh.get(form.refresh_token);
+ if (!u) return json(400, { error: 'invalid_grant', error_description: 'refresh token rejected' });
+ st.refresh.delete(form.refresh_token);
+ const rt = `rt_${crypto.randomBytes(12).toString('hex')}`;
+ st.refresh.set(rt, u);
+ return json(200, { access_token: userToken(u), refresh_token: rt, token_type: 'Bearer' });
+ }
+ return json(400, { error: 'unsupported_grant_type' });
+ }
+ return json(404, { error: 'not found' });
+ });
+ issuer = srv.url;
+
+ return {
+ url: srv.url, publicPem, privatePem, state: st,
+ requests: st.requests, tokenRequests: st.tokenRequests,
+ addUser, userToken,
+ issueCode(user, challenge) { const code = `code_${crypto.randomBytes(8).toString('hex')}`; st.codes.set(code, { user, challenge }); return code; },
+ setDown(v) { st.down = Boolean(v); },
+ close: srv.close,
+ };
+}
+
+module.exports = { startNetwork, signJwt };
new file mode 100644
new file mode 100644
@@ -0,0 +1,143 @@
@@ -0,0 +1,143 @@
+'use strict';
+/**
+ * In-process stand-ins for OpenVibe.Live, OpenVibe.Games and OpenVibe.Actor, on one HTTP server. The boot helper
+ * points OV_LIVE_INTERNAL_URL, OV_GAMES_INTERNAL_URL and OV_ACTOR_INTERNAL_URL at it.
+ *
+ * A test changes state.fail.<source> (an HTTP status) or state.delay.<source> (ms), state.gamesNotFound, or
+ * state.actor.status, and reads state.requests / state.counts to see what the app called.
+ */
+const http = require('http');
+
+const DEFAULT_STREAMS = [
+ { id: 1, username: 'ada', display_name: 'Ada', title: 'Night drive through the city', viewer_count: 12, total_viewer_count: 12, thumbnail_url: 'https://openvibe.media/live/ada.jpg', started_at: '2026-10-28T10:00:00.000Z' },
+ { id: 2, username: 'bo', display_name: 'Bo', title: 'Surf practice, all day', viewer_count: 38, total_viewer_count: 38, thumbnail_url: 'https://openvibe.media/live/bo.jpg', started_at: '2026-10-28T09:00:00.000Z' },
+ { id: 3, username: 'cy', display_name: 'Cy', title: 'Chat picks the route', viewer_count: 5, total_viewer_count: 5, thumbnail_url: 'https://openvibe.media/live/cy.jpg', started_at: '2026-10-28T08:00:00.000Z' },
+];
+const DEFAULT_CLIPS = [
+ { key: 'clip:101', id: 101, href: '/clip/101', title: 'Kickflip over the pier', created_at: '2026-10-28T09:00:00.000Z', channel: { username: 'ada', display_name: 'Ada', avatar_url: null }, thumbnail_url: 'https://openvibe.media/clips/101.jpg', preview_url: 'https://openvibe.media/c/101/master.m3u8', duration_seconds: 31, ai_label: null },
+ { key: 'clip:102', id: 102, href: '/clip/102', title: 'The long way down', created_at: '2026-10-28T07:00:00.000Z', channel: { username: 'bo', display_name: 'Bo', avatar_url: null }, thumbnail_url: 'https://openvibe.media/clips/102.jpg', preview_url: 'https://openvibe.media/c/102/master.m3u8', duration_seconds: 44, ai_label: null },
+];
+const DEFAULT_MOMENTS = [
+ { key: 'clip:201', id: 201, href: 'https://openvibe.video/c/201', title: 'The wave that ate the camera', created_at: '2026-10-28T10:00:00.000Z', channel: { username: 'mira', display_name: 'Mira', avatar_url: null }, thumbnail_url: 'https://openvibe.media/clips/201.jpg', preview_url: 'https://openvibe.media/c/201/master.m3u8', duration_seconds: 18, ai_label: 'Auto-clip' },
+];
+const DEFAULT_MAPS = [
+ { id: 'surf_lagoon', name: 'Surf Lagoon', tier: 1, stages: 3, author: 'OpenVibe', records: 5, wrMs: 61234, wrHolder: 'ada' },
+ { id: 'surf_canyon', name: 'Surf Canyon', tier: 2, stages: 4, author: 'OpenVibe', records: 2, wrMs: 73456, wrHolder: 'bo' },
+ { id: 'surf_orbit', name: 'Surf Orbit', tier: 4, stages: 6, author: 'OpenVibe', records: 0, wrMs: null, wrHolder: null },
+];
+const DEFAULT_TOP = {
+ surf_lagoon: [{ rank: 1, name: 'ada', timeMs: 61234, runId: null }, { rank: 2, name: 'bo', timeMs: 62345, runId: null }],
+ surf_canyon: [{ rank: 1, name: 'bo', timeMs: 73456, runId: null }],
+ surf_orbit: [],
+};
+
+function listen(handler) {
+ return new Promise((resolve) => {
+ const sockets = new Set();
+ const server = http.createServer((req, res) => {
+ const chunks = [];
+ req.on('data', (c) => chunks.push(c));
+ req.on('end', () => {
+ const raw = Buffer.concat(chunks).toString('utf8');
+ const json = (status, obj, headers = {}) => {
+ res.writeHead(status, { 'Content-Type': 'application/json', ...headers });
+ res.end(JSON.stringify(obj));
+ };
+ Promise.resolve(handler(req, raw, json, res)).catch((err) => json(500, { error: String(err && err.message) }));
+ });
+ });
+ server.on('connection', (s) => { sockets.add(s); s.on('close', () => sockets.delete(s)); });
+ server.listen(0, '127.0.0.1', () => resolve({
+ server,
+ url: `http://127.0.0.1:${server.address().port}`,
+ close: () => new Promise((r) => { for (const s of sockets) s.destroy(); server.close(r); }),
+ }));
+ });
+}
+
+async function startUpstreams() {
+ const state = {
+ streams: DEFAULT_STREAMS.map((s) => ({ ...s })),
+ clips: DEFAULT_CLIPS.map((s) => ({ ...s })),
+ moments: DEFAULT_MOMENTS.map((s) => ({ ...s })),
+ clipsNext: null,
+ momentsNext: null,
+ maps: DEFAULT_MAPS.map((m) => ({ ...m })),
+ top: JSON.parse(JSON.stringify(DEFAULT_TOP)),
+ gamesNotFound: false,
+ gamesStatus: null,
+ fail: { streams: null, clips: null, moments: null },
+ delay: { streams: 0, clips: 0, moments: 0 },
+ actor: { status: 201, id: 'tsk_01J8SURFEXAMPLE00000000', problem: null },
+ requests: [],
+ counts: { streams: 0, clips: 0, moments: 0, maps: 0, map: 0, actor: 0 },
+ };
+
+ const srv = await listen(async (req, raw, json) => {
+ const url = new URL(req.url, 'http://x');
+ const p = url.pathname;
+ const entry = { method: req.method, path: p, query: Object.fromEntries(url.searchParams), headers: { ...req.headers }, body: raw };
+ state.requests.push(entry);
+
+ const fail = (name) => {
+ if (!state.fail[name]) return false;
+ json(state.fail[name], { error: 'upstream failed' });
+ return true;
+ };
+
+ if (p === '/api/streams') {
+ state.counts.streams++;
+ if (state.delay.streams) await new Promise((r) => setTimeout(r, state.delay.streams));
+ if (fail('streams')) return;
+ return json(200, { streams: state.streams });
+ }
+ if (p === '/api/content/feed') {
+ state.counts.clips++;
+ if (state.delay.clips) await new Promise((r) => setTimeout(r, state.delay.clips));
+ if (fail('clips')) return;
+ return json(200, { feed: 'content', type: 'clips', sort: 'new', window: 'week', items: state.clips, next: state.clipsNext, sources: { clips: 'ok' }, partial: false });
+ }
+ if (p === '/api/content/moments') {
+ state.counts.moments++;
+ if (state.delay.moments) await new Promise((r) => setTimeout(r, state.delay.moments));
+ if (fail('moments')) return;
+ return json(200, { feed: 'moments', type: 'clips', sort: 'top', window: 'week', items: state.moments, next: state.momentsNext, sources: { aiclips: 'ok' }, partial: false });
+ }
+ if (p === '/api/surf/maps') {
+ state.counts.maps++;
+ if (state.gamesStatus) return json(state.gamesStatus, { error: 'games failed' });
+ if (state.gamesNotFound) return json(404, { error: 'not_found' });
+ return json(200, { maps: state.maps });
+ }
+ const one = /^\/api\/surf\/maps\/([^/]+)$/.exec(p);
+ if (one) {
+ state.counts.map++;
+ if (state.gamesStatus) return json(state.gamesStatus, { error: 'games failed' });
+ if (state.gamesNotFound) return json(404, { error: 'not_found' });
+ const id = decodeURIComponent(one[1]);
+ const map = state.maps.find((m) => m.id === id);
+ if (!map) return json(404, { error: 'unknown_map' });
+ return json(200, { map, top: state.top[id] || [] });
+ }
+ if (p === '/api/v1/agents' && req.method === 'GET') return json(200, { agents: [] });
+ if (p === '/api/v1/tasks' && req.method === 'POST') {
+ state.counts.actor++;
+ if (state.actor.status >= 400) {
+ return json(state.actor.status, state.actor.problem || { type: 'about:blank', title: 'Refused', status: state.actor.status, code: 'actor.refused', detail: 'OpenVibe.Actor refused this task.' });
+ }
+ return json(state.actor.status, { id: state.actor.id, state: 'queued', task: 'the task' });
+ }
+ return json(404, { error: 'not found' });
+ });
+
+ return {
+ url: srv.url,
+ state,
+ close: srv.close,
+ resetCounts() { for (const k of Object.keys(state.counts)) state.counts[k] = 0; },
+ requestsFor(path) { return state.requests.filter((r) => r.path === path); },
+ lastFor(path) { return state.requests.filter((r) => r.path === path).pop() || null; },
+ };
+}
+
+module.exports = { startUpstreams, DEFAULT_STREAMS, DEFAULT_CLIPS, DEFAULT_MOMENTS, DEFAULT_MAPS, DEFAULT_TOP };
new file mode 100644
new file mode 100644
@@ -0,0 +1,168 @@
@@ -0,0 +1,168 @@
+'use strict';
+/**
+ * The pages and the plumbing: /, /ride, /ride/:map, /watch, /web, /developers, the 404; public vs signed-in cache
+ * policy; the CSP the embeds need; health and truthful readiness; the release manifest and content-hashed assets.
+ */
+const assert = require('assert');
+const { boot, check, done } = require('./helpers/boot');
+const { assetVersion } = require('../server/assets');
+
+(async () => {
+ const t = await boot();
+ const user = t.network.addUser('kim');
+
+ await check('the home page has the three tiles and is publicly cacheable', async () => {
+ const r = await t.get('/');
+ assert.strictEqual(r.status, 200);
+ assert.match(r.text, /id="sf-tile-ride"/);
+ assert.match(r.text, /id="sf-tile-watch"/);
+ assert.match(r.text, /id="sf-tile-web"/);
+ assert.match(r.text, /href="\/ride"/);
+ assert.match(r.text, /href="\/watch"/);
+ assert.match(r.text, /href="\/web"/);
+ assert.match(r.text, /<main id="main"/);
+ assert.strictEqual(r.headers.get('cache-control'), 'public, max-age=60');
+ assert.match(r.headers.get('vary'), /Cookie/i);
+ });
+
+ await check('/ride lists the maps and embeds the first', async () => {
+ const r = await t.get('/ride');
+ assert.strictEqual(r.status, 200, r.text);
+ assert.match(r.text, /id="sf-maps"/);
+ assert.match(r.text, /href="\/ride\/surf_lagoon"/);
+ assert.match(r.text, /Surf Lagoon/);
+ assert.match(r.text, /id="sf-embed"/);
+ assert.match(r.text, /src="https:\/\/openvibe\.games\/play\/surf\?map=surf_lagoon&embed=1"/);
+ assert.strictEqual(r.headers.get('cache-control'), 'public, max-age=60');
+ });
+
+ await check('/ride/:map shows the name, tier, stages, records and the embed', async () => {
+ const r = await t.get('/ride/surf_lagoon');
+ assert.strictEqual(r.status, 200, r.text);
+ assert.match(r.text, /Surf Lagoon/);
+ assert.match(r.text, /Beginner/);
+ assert.match(r.text, /3 stages/);
+ assert.match(r.text, /5 runs/);
+ assert.match(r.text, /1:01\.234/);
+ assert.match(r.text, /id="sf-top"/);
+ assert.match(r.text, /ada/);
+ assert.match(r.text, /src="https:\/\/openvibe\.games\/play\/surf\?map=surf_lagoon&embed=1"/);
+ });
+
+ await check('/ride/:map still embeds when Games has not added its routes yet', async () => {
+ t.ctx.ride.clear();
+ t.upstreams.state.gamesNotFound = true;
+ const r = await t.get('/ride/surf_lagoon');
+ t.upstreams.state.gamesNotFound = false;
+ t.ctx.ride.clear();
+ assert.strictEqual(r.status, 200, r.text);
+ assert.match(r.text, /id="sf-ride-not-ready"/);
+ assert.match(r.text, /src="https:\/\/openvibe\.games\/play\/surf\?map=surf_lagoon&embed=1"/);
+ });
+
+ await check('/watch is the feed as a list with thumbnails, play links and HLS hooks', async () => {
+ t.ctx.feed.clear();
+ const r = await t.get('/watch');
+ assert.strictEqual(r.status, 200, r.text);
+ assert.match(r.text, /id="sf-watch-feed"/);
+ assert.match(r.text, /class="sf-ch is-on"[^>]*data-sf-kind="live"/, 'the guide, live first and on');
+ assert.match(r.text, /data-sf-tv/, 'the TV');
+ assert.match(r.text, /https:\/\/openvibe\.media\/live\/bo\.jpg/);
+ assert.match(r.text, /href="https:\/\/openvibe\.live\/@bo"/);
+ assert.match(r.text, /data-sf-play-type="hls"/);
+ assert.match(r.text, /data-sf-src="https:\/\/openvibe\.media\/c\/201\/master\.m3u8"/);
+ assert.match(r.text, /Live now · 38 watching/);
+ assert.match(r.text, /Top AI moment this week/);
+ assert.match(r.text, /New clip/);
+ assert.match(r.text, /id="sf-topics"/);
+ assert.strictEqual(r.headers.get('cache-control'), 'public, max-age=60');
+ });
+
+ await check('/watch?topic=live only lists live streams', async () => {
+ t.ctx.feed.clear();
+ const r = await t.get('/watch?topic=live');
+ assert.strictEqual(r.status, 200);
+ assert.match(r.text, /data-sf-kind="live"/);
+ assert.ok(!r.text.includes('data-sf-kind="clip"'), 'no clips on the live topic');
+ assert.ok(!r.text.includes('data-sf-kind="moment"'), 'no moments on the live topic');
+ });
+
+ await check('/web is the Surf with Vibe form and what it does', async () => {
+ const r = await t.get('/web');
+ assert.strictEqual(r.status, 200, r.text);
+ assert.match(r.text, /id="sf-web-form"/);
+ assert.match(r.text, /action="\/web\/go"/);
+ assert.match(r.text, /id="sf-web-query"/);
+ assert.match(r.text, /minlength="3"/);
+ assert.match(r.text, /maxlength="500"/);
+ assert.match(r.text, /method="post"/, 'the plain form posts: the query is never in a URL');
+ assert.match(r.text, /asked to sign in/);
+ assert.match(r.text, /Search Fabric/);
+ assert.match(r.text, /private desk/);
+ assert.strictEqual(r.headers.get('cache-control'), 'public, max-age=60');
+ const me = await t.get('/web', { as: user });
+ assert.strictEqual(me.status, 200);
+ assert.match(me.text, /Runs as <b>kim<\/b>/);
+ assert.strictEqual(me.headers.get('cache-control'), 'private, no-store', 'a signed-in page is never cached');
+ });
+
+ await check('/developers and the 404', async () => {
+ const dev = await t.get('/developers');
+ assert.strictEqual(dev.status, 200);
+ assert.match(dev.text, /GET \/api\/v1\/watch\/feed/);
+ assert.match(dev.text, /POST \/api\/v1\/web\/tasks/);
+ const gone = await t.get('/not-a-page');
+ assert.strictEqual(gone.status, 404);
+ assert.match(gone.text, /id="sf-not-found"/);
+ assert.match(gone.text, /Wiped out/);
+ });
+
+ await check('the CSP allows the Games embed, a Live player and the media edge', async () => {
+ const r = await t.get('/');
+ const csp = r.headers.get('content-security-policy') || '';
+ assert.match(csp, /frame-src 'self' https:\/\/openvibe\.games https:\/\/openvibe\.live/);
+ assert.match(csp, /media-src 'self' https:\/\/openvibe\.media https:\/\/edge\.openvibe\.media https:\/\/\*\.r2\.cloudflarestorage\.com https:\/\/\*\.backblazeb2\.com blob:/);
+ assert.match(csp, /connect-src [^;]*https:\/\/openvibe\.media/, 'hls.js fetches Media\'s playlists and segments');
+ assert.match(csp, /connect-src 'self' https:\/\/openvibe\.network https:\/\/cloudflareinsights\.com https:\/\/openvibe\.events/);
+ });
+
+ await check('health and readiness are honest; a Network outage is degraded, not down', async () => {
+ const health = await t.get('/api/health');
+ assert.strictEqual(health.status, 200);
+ assert.strictEqual(health.json().status, 'ok');
+ assert.strictEqual(health.json().service, 'openvibe-surf');
+ assert.strictEqual(health.json().version, '0.1.0');
+ const ready = await t.get('/api/ready');
+ assert.strictEqual(ready.status, 200, ready.text);
+ assert.strictEqual(ready.json().ready, true);
+ assert.strictEqual(ready.json().checks.network.status, 'ok');
+ t.network.setDown(true);
+ const degraded = await t.get('/api/ready');
+ t.network.setDown(false);
+ assert.strictEqual(degraded.status, 200, 'the process still serves');
+ assert.strictEqual(degraded.json().ready, true);
+ assert.strictEqual(degraded.json().status, 'degraded');
+ assert.ok(degraded.json().degraded.includes('network'));
+ });
+
+ await check('the release manifest and content-hashed static assets', async () => {
+ const rel = await t.get('/release.json');
+ assert.strictEqual(rel.status, 200);
+ assert.strictEqual(rel.json().service, 'surf');
+ assert.match(rel.headers.get('content-type'), /application\/json/);
+ const css = await t.get('/css/surf.css');
+ assert.strictEqual(css.status, 200);
+ assert.match(css.text, /OpenVibe\.Surf: the room's own pieces/);
+ const hashed = await t.get(`/css/surf.css?v=${assetVersion('css/surf.css')}`);
+ assert.strictEqual(hashed.status, 200);
+ assert.strictEqual(hashed.headers.get('cache-control'), 'public, max-age=31536000, immutable');
+ const js = await t.get(`/js/surf.js?v=${assetVersion('js/surf.js')}`);
+ assert.strictEqual(js.status, 200);
+ assert.match(js.headers.get('content-type'), /javascript/);
+ const shared = await t.get('/shared/room.css');
+ assert.strictEqual(shared.status, 200);
+ });
+
+ await t.close();
+ done();
+})().catch((err) => { console.error(err); process.exit(1); });
new file mode 100644
new file mode 100644
@@ -0,0 +1,94 @@
@@ -0,0 +1,94 @@
+'use strict';
+/**
+ * Ride: the Games proxy (the maps list and one map), its 60 s cache, Games 404 → an empty list with
+ * games: 'not_ready', a Games failure → 502, and the map's embed URL.
+ */
+const assert = require('assert');
+const { boot, check, done } = require('./helpers/boot');
+
+(async () => {
+ const t = await boot();
+
+ await check('GET /api/v1/ride/maps proxies Games and adds each map’s embed URL', async () => {
+ t.ctx.ride.clear();
+ t.upstreams.resetCounts();
+ const r = await t.get('/api/v1/ride/maps');
+ assert.strictEqual(r.status, 200, r.text);
+ assert.strictEqual(r.headers.get('cache-control'), 'public, max-age=60');
+ const b = r.json();
+ assert.strictEqual(b.games, 'ready');
+ assert.strictEqual(b.maps.length, 3);
+ assert.strictEqual(b.maps[0].id, 'surf_lagoon');
+ assert.strictEqual(b.maps[0].name, 'Surf Lagoon');
+ assert.strictEqual(b.maps[0].tier, 1);
+ assert.strictEqual(b.maps[0].stages, 3);
+ assert.strictEqual(b.maps[0].embed_url, 'https://openvibe.games/play/surf?map=surf_lagoon&embed=1');
+ assert.strictEqual(t.upstreams.state.counts.maps, 1);
+ });
+
+ await check('the list is cached for 60 s', async () => {
+ t.upstreams.resetCounts();
+ await t.get('/api/v1/ride/maps');
+ assert.strictEqual(t.upstreams.state.counts.maps, 0, 'the second read is the cached list');
+ t.clock.advance(60_001);
+ await t.get('/api/v1/ride/maps');
+ assert.strictEqual(t.upstreams.state.counts.maps, 1, 'after 60 s it asks Games again');
+ });
+
+ await check('GET /api/v1/ride/maps/:id returns the map, its records and the embed', async () => {
+ t.ctx.ride.clear();
+ t.upstreams.resetCounts();
+ const r = await t.get('/api/v1/ride/maps/surf_lagoon');
+ assert.strictEqual(r.status, 200, r.text);
+ assert.strictEqual(r.headers.get('cache-control'), 'public, max-age=60');
+ const b = r.json();
+ assert.strictEqual(b.games, 'ready');
+ assert.strictEqual(b.map.id, 'surf_lagoon');
+ assert.strictEqual(b.map.records, 5);
+ assert.strictEqual(b.map.wrMs, 61234);
+ assert.strictEqual(b.map.embed_url, 'https://openvibe.games/play/surf?map=surf_lagoon&embed=1');
+ assert.deepStrictEqual(b.top[0], { rank: 1, name: 'ada', timeMs: 61234, runId: null });
+ const again = await t.get('/api/v1/ride/maps/surf_lagoon');
+ assert.strictEqual(again.status, 200);
+ assert.strictEqual(t.upstreams.state.counts.map, 1, 'the map is cached too');
+ });
+
+ await check('Games 404 means not_ready: an empty list and a map shell that still embeds', async () => {
+ t.ctx.ride.clear();
+ t.upstreams.state.gamesNotFound = true;
+ const list = await t.get('/api/v1/ride/maps');
+ assert.strictEqual(list.status, 200, list.text);
+ assert.deepStrictEqual(list.json(), { maps: [], games: 'not_ready' });
+ assert.strictEqual(list.headers.get('cache-control'), 'public, max-age=60');
+ const one = await t.get('/api/v1/ride/maps/surf_lagoon');
+ assert.strictEqual(one.status, 200, one.text);
+ const b = one.json();
+ assert.strictEqual(b.games, 'not_ready');
+ assert.strictEqual(b.map.id, 'surf_lagoon');
+ assert.strictEqual(b.map.embed_url, 'https://openvibe.games/play/surf?map=surf_lagoon&embed=1');
+ assert.deepStrictEqual(b.top, []);
+ t.upstreams.state.gamesNotFound = false;
+ });
+
+ await check('a Games failure is a 502 problem, and a failure is never cached', async () => {
+ t.ctx.ride.clear();
+ t.upstreams.state.gamesStatus = 503;
+ const r = await t.get('/api/v1/ride/maps');
+ assert.strictEqual(r.status, 502, r.text);
+ assert.match(r.headers.get('content-type'), /application\/problem\+json/);
+ assert.strictEqual(r.json().code, 'games.unavailable');
+ t.upstreams.state.gamesStatus = null;
+ const ok = await t.get('/api/v1/ride/maps');
+ assert.strictEqual(ok.status, 200, 'the next request asks again');
+ assert.strictEqual(ok.json().games, 'ready');
+ });
+
+ await check('a blank map id is a 400', async () => {
+ const r = await t.get('/api/v1/ride/maps/%20');
+ assert.strictEqual(r.status, 400);
+ assert.strictEqual(r.json().code, 'ride.invalid_map');
+ });
+
+ await t.close();
+ done();
+})().catch((err) => { console.error(err); process.exit(1); });
new file mode 100644
new file mode 100644
@@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
+#!/usr/bin/env node
+/**
+ * Runs every test/*.test.js in its own process and fails if any fails. Each file boots OpenVibe.Surf against
+ * in-process Network/Live/Games/Actor stand-ins (test/helpers/); none needs the internet or a running site.
+ *
+ * npm test # everything
+ * npm test -- watch # only files whose name contains one of the words
+ * npm test -- --strict # a skipped test fails the run too
+ */
+'use strict';
+const { run } = require('openvibe-shared/test-runner');
+
+// VERBOSE=1 also prints every file's output, then the summary again.
+run({ dir: __dirname, timeoutMs: 60_000, pad: 34, parallel: 1 }).then((r) => {
+ if (process.env.VERBOSE) {
+ for (const t of r.results) console.log(`\n── ${t.file} ──\n${t.output.trimEnd()}`);
+ console.log(`\n${r.summary}`);
+ }
+ process.exit(r.exitCode);
+}, (err) => { console.error(err); process.exit(1); });
new file mode 100644
new file mode 100644
@@ -0,0 +1,128 @@
@@ -0,0 +1,128 @@
+'use strict';
+/**
+ * Sign-in: sanitizeNext (open-redirect regressions), the login redirect (PKCE S256, state, client id, redirect
+ * URI), the callback (state mismatch refused, httpOnly cookies), /auth/me signed out and in, and sign-out.
+ */
+const assert = require('assert');
+const { boot, check, done } = require('./helpers/boot');
+const { sanitizeNext } = require('../server/auth/sso');
+
+const config = { baseUrl: 'https://openvibe.surf' };
+const cookies = (r) => (typeof r.headers.getSetCookie === 'function' ? r.headers.getSetCookie() : [r.headers.get('set-cookie') || '']);
+const cookieNamed = (r, name) => {
+ for (const c of cookies(r)) {
+ const m = new RegExp(`(?:^|[,\\s])${name}=([^;]+)`).exec(c);
+ if (m) return `${name}=${m[1]}`;
+ }
+ return null;
+};
+
+(async () => {
+ // ── sanitizeNext: same-site paths only ──
+ for (const v of ['//evil.com', '/\\evil.com', '\\\\evil.com', 'https://evil.com', 'http:evil.com', '/\t/evil.com',
+ '/\n/evil.com', '\t//evil.com', ' //evil.com', '/\\/evil.com', 'https:/\\evil.com', '///evil.com',
+ 'javascript:alert(1)', null, '', 12]) {
+ assert.strictEqual(sanitizeNext(v, config), '/', `${JSON.stringify(v)} goes home`);
+ }
+ assert.strictEqual(sanitizeNext('/watch?topic=live', config), '/watch?topic=live');
+ assert.strictEqual(sanitizeNext('https://openvibe.surf/ride/surf_lagoon?x=1#c', config), '/ride/surf_lagoon?x=1#c');
+ for (const v of ['https://evil.com/a', 'https://openvibe.surf.evil.com/a', 'https://[email protected]/', 'http://openvibe.surf/a']) {
+ assert.strictEqual(sanitizeNext(v, config), '/', v);
+ }
+
+ const t = await boot();
+ const user = t.network.addUser('kim');
+
+ await check('login redirects to Network with state, client id surf and an S256 challenge', async () => {
+ const r = await t.get('/auth/login?next=/web');
+ assert.strictEqual(r.status, 302);
+ const loc = new URL(r.headers.get('location'));
+ assert.strictEqual(loc.origin + loc.pathname, `${t.network.url}/oauth/authorize`);
+ assert.strictEqual(loc.searchParams.get('client_id'), 'surf');
+ assert.strictEqual(loc.searchParams.get('response_type'), 'code');
+ assert.strictEqual(loc.searchParams.get('redirect_uri'), 'https://openvibe.surf/auth/callback');
+ assert.strictEqual(loc.searchParams.get('scope'), 'profile');
+ assert.strictEqual(loc.searchParams.get('code_challenge_method'), 'S256');
+ assert.match(loc.searchParams.get('code_challenge'), /^[A-Za-z0-9_-]{43}$/);
+ assert.match(loc.searchParams.get('state'), /^[0-9a-f]{32}$/);
+ const flow = cookieNamed(r, 'surf_oauth');
+ assert.ok(flow, 'the flow cookie is set');
+ assert.match(cookies(r).find((c) => c.includes('surf_oauth=')), /Path=\/auth.*HttpOnly/i);
+ const payload = JSON.parse(decodeURIComponent(flow.split('=')[1]));
+ assert.strictEqual(payload.next, '/web');
+ assert.strictEqual(payload.silent, false);
+ });
+
+ await check('silent sign-in adds prompt=none and next= only keeps this site', async () => {
+ const r = await t.get('/auth/login?silent=1&next=' + encodeURIComponent('https://evil.example/x'));
+ const loc = new URL(r.headers.get('location'));
+ assert.strictEqual(loc.searchParams.get('prompt'), 'none');
+ const flow = JSON.parse(decodeURIComponent(cookieNamed(r, 'surf_oauth').split('=')[1]));
+ assert.strictEqual(flow.next, '/', 'another origin goes home');
+ const own = await t.get('/auth/login?next=' + encodeURIComponent('https://openvibe.surf/ride/surf_lagoon?x=1'));
+ const ownFlow = JSON.parse(decodeURIComponent(cookieNamed(own, 'surf_oauth').split('=')[1]));
+ assert.strictEqual(ownFlow.next, '/ride/surf_lagoon?x=1');
+ });
+
+ await check('the callback exchanges the code and sets httpOnly surf_at/surf_rt cookies', async () => {
+ const login = await t.get('/auth/login?next=/web');
+ const loc = new URL(login.headers.get('location'));
+ const flowCookie = cookieNamed(login, 'surf_oauth');
+ const code = t.network.issueCode(user, loc.searchParams.get('code_challenge'));
+ const r = await t.get(`/auth/callback?code=${code}&state=${loc.searchParams.get('state')}`, { cookie: flowCookie });
+ assert.strictEqual(r.status, 302, r.text);
+ assert.strictEqual(r.headers.get('location'), '/web');
+ const all = cookies(r).join('\n');
+ assert.match(all, /surf_at=[^;]+;.*HttpOnly/i);
+ assert.match(all, /surf_rt=[^;]+;.*HttpOnly/i);
+ assert.ok(!/SameSite=None/i.test(all), 'SameSite=Lax, not None');
+ const at = cookieNamed(r, 'surf_at');
+ assert.ok(at, 'the access cookie is set');
+ const me = await t.get('/auth/me', { cookie: at });
+ assert.strictEqual(me.status, 200);
+ assert.strictEqual(me.json().user.username, 'kim');
+ assert.ok(!me.text.includes(at.split('=')[1]), '/auth/me never returns the token');
+ const tokenRequest = t.network.tokenRequests[t.network.tokenRequests.length - 1];
+ assert.strictEqual(tokenRequest.client_id, 'surf');
+ assert.strictEqual(tokenRequest.grant_type, 'authorization_code');
+ assert.strictEqual(tokenRequest.redirect_uri, 'https://openvibe.surf/auth/callback');
+ assert.ok(tokenRequest.code_verifier, 'the verifier is sent server-side');
+ });
+
+ await check('a wrong state or no flow cookie is refused', async () => {
+ const login = await t.get('/auth/login');
+ const loc = new URL(login.headers.get('location'));
+ const flowCookie = cookieNamed(login, 'surf_oauth');
+ const code = t.network.issueCode(user, loc.searchParams.get('code_challenge'));
+ const bad = await t.get(`/auth/callback?code=${code}&state=${'0'.repeat(32)}`, { cookie: flowCookie });
+ assert.strictEqual(bad.status, 400);
+ const noCookie = await t.get(`/auth/callback?code=${code}&state=${loc.searchParams.get('state')}`);
+ assert.strictEqual(noCookie.status, 400);
+ const none = await t.get('/auth/callback?state=x');
+ assert.strictEqual(none.status, 400);
+ });
+
+ await check('/auth/me is null for a guest and 401 for a cookie that does not verify', async () => {
+ const guest = await t.get('/auth/me');
+ assert.strictEqual(guest.status, 200);
+ assert.deepStrictEqual(guest.json(), { user: null });
+ const bad = await t.get('/auth/me', { cookie: 'surf_at=not-a-token' });
+ assert.strictEqual(bad.status, 401);
+ assert.strictEqual(bad.json().error, 'Not signed in');
+ });
+
+ await check('sign-out clears the cookies and returns to next', async () => {
+ const r = await t.get('/auth/logout?next=%2Fwatch', { headers: { 'sec-fetch-site': 'same-origin' } });
+ assert.strictEqual(r.status, 303);
+ assert.strictEqual(r.headers.get('location'), '/watch');
+ assert.match(cookies(r).join('\n'), /surf_at=;/);
+ });
+
+ await check('the token is never logged', async () => {
+ const token = t.network.userToken(user);
+ assert.ok(!t.logs().includes(token), 'no access token in the logs');
+ });
+
+ await t.close();
+ done();
+})().catch((err) => { console.error(err); process.exit(1); });
new file mode 100644
new file mode 100644
@@ -0,0 +1,139 @@
@@ -0,0 +1,139 @@
+'use strict';
+/**
+ * Web: signed out 401, cross-site 403, the Actor call carries the person's Bearer and an Idempotency-Key and the
+ * prefixed browsing brief, Actor's refusal passes through in words, and the plain form (POST /web/go, this site only) 303s to the Actor task page.
+ */
+const assert = require('assert');
+const { boot, check, done } = require('./helpers/boot');
+const { PREFIX, taskText } = require('../server/web/tasks');
+
+const ORIGIN = 'https://openvibe.surf';
+
+(async () => {
+ const t = await boot();
+ const user = t.network.addUser('kim');
+ const token = t.network.userToken(user);
+ const signedIn = `surf_at=${token}`;
+
+ await check('POST /api/v1/web/tasks is a 401 problem when signed out', async () => {
+ const r = await t.get('/api/v1/web/tasks', { method: 'POST', json: { query: 'who runs the mail for openvibe.network' }, headers: { origin: ORIGIN } });
+ assert.strictEqual(r.status, 401, r.text);
+ assert.match(r.headers.get('content-type'), /application\/problem\+json/);
+ assert.strictEqual(r.json().code, 'token.required');
+ assert.strictEqual(r.headers.get('cache-control'), 'private, no-store');
+ });
+
+ await check('a cross-site Origin is 403 and never reaches Actor', async () => {
+ t.upstreams.resetCounts();
+ const r = await t.get('/api/v1/web/tasks', {
+ cookie: signedIn, method: 'POST', json: { query: 'who runs the mail for openvibe.network' }, headers: { origin: 'https://evil.example' },
+ });
+ assert.strictEqual(r.status, 403, r.text);
+ assert.strictEqual(r.json().code, 'web.cross_site');
+ assert.strictEqual(r.headers.get('cache-control'), 'private, no-store');
+ assert.strictEqual(t.upstreams.state.counts.actor, 0);
+ const missing = await t.get('/api/v1/web/tasks', { cookie: signedIn, method: 'POST', json: { query: 'who runs the mail for openvibe.network' } });
+ assert.strictEqual(missing.status, 403, 'no Origin is not this site');
+ });
+
+ await check('the task carries the person’s Bearer, an Idempotency-Key and the prefixed browsing brief', async () => {
+ t.upstreams.resetCounts();
+ const query = 'who runs the mail for openvibe.network and is SPF set up?';
+ const r = await t.get('/api/v1/web/tasks', {
+ cookie: signedIn, method: 'POST', json: { query }, headers: { origin: ORIGIN },
+ });
+ assert.strictEqual(r.status, 201, r.text);
+ const b = r.json();
+ assert.strictEqual(b.task_id, 'tsk_01J8SURFEXAMPLE00000000');
+ assert.strictEqual(b.page, 'https://openvibe.actor/tasks/tsk_01J8SURFEXAMPLE00000000');
+ assert.strictEqual(r.headers.get('cache-control'), 'private, no-store');
+ assert.strictEqual(t.upstreams.state.counts.actor, 1);
+
+ const sent = t.upstreams.lastFor('/api/v1/tasks');
+ assert.strictEqual(sent.headers.authorization, `Bearer ${token}`);
+ assert.match(sent.headers['idempotency-key'] || '', /^surf:web:[0-9a-f]{32}$/);
+ const body = JSON.parse(sent.body);
+ assert.strictEqual(body.task, taskText(query));
+ assert.ok(body.task.startsWith(PREFIX), 'the standing brief comes first');
+ assert.ok(body.task.endsWith(query), 'the person’s words are unchanged at the end');
+ assert.match(body.task, /search_web/);
+ assert.match(body.task, /read_page/);
+ assert.match(body.task, /desk/);
+ assert.match(body.task, /cites each source/);
+ assert.strictEqual(body.idempotency_key, sent.headers['idempotency-key']);
+ assert.ok(!t.logs().includes(token), 'the person’s token is never logged');
+ assert.ok(!r.text.includes(token), 'the token is never echoed');
+ });
+
+ await check('a caller-supplied Idempotency-Key is passed through when it is valid', async () => {
+ const r = await t.get('/api/v1/web/tasks', {
+ cookie: signedIn, method: 'POST', json: { query: 'find a good surf forecast' }, headers: { origin: ORIGIN, 'idempotency-key': 'surf:web:caller-key-123' },
+ });
+ assert.strictEqual(r.status, 201, r.text);
+ const sent = t.upstreams.lastFor('/api/v1/tasks');
+ assert.strictEqual(sent.headers['idempotency-key'], 'surf:web:caller-key-123');
+ assert.strictEqual(JSON.parse(sent.body).idempotency_key, 'surf:web:caller-key-123');
+ });
+
+ await check('a short, long or missing query is a 400 problem', async () => {
+ for (const query of ['ab', 'x'.repeat(501), '', null]) {
+ const r = await t.get('/api/v1/web/tasks', { cookie: signedIn, method: 'POST', json: { query }, headers: { origin: ORIGIN } });
+ assert.strictEqual(r.status, 400, String(query));
+ assert.strictEqual(r.json().code, 'web.invalid_query');
+ }
+ });
+
+ await check('Actor’s refusal passes its status and words through', async () => {
+ t.upstreams.state.actor.status = 429;
+ t.upstreams.state.actor.problem = {
+ type: 'about:blank', title: 'Too many tasks', status: 429, code: 'actor.allowance.exhausted',
+ detail: 'The free tier runs 5 tasks a day; it resets at midnight UTC.',
+ };
+ const r = await t.get('/api/v1/web/tasks', { cookie: signedIn, method: 'POST', json: { query: 'surf the latest AI news' }, headers: { origin: ORIGIN } });
+ t.upstreams.state.actor.status = 201;
+ t.upstreams.state.actor.problem = null;
+ assert.strictEqual(r.status, 429, r.text);
+ const b = r.json();
+ assert.strictEqual(b.code, 'actor.allowance.exhausted');
+ assert.match(b.detail, /free tier runs 5 tasks a day/);
+ assert.strictEqual(b.status, 429);
+ });
+
+ const own = { origin: 'https://openvibe.surf', 'sec-fetch-site': 'same-origin' };
+ await check('POST /web/go (the plain form) creates the task and 303s to the Actor task page; the query is never in a URL', async () => {
+ t.upstreams.resetCounts();
+ const r = await t.get('/web/go', { cookie: signedIn, form: { q: 'find the best surf spots in Portugal' }, headers: own });
+ assert.strictEqual(r.status, 303, r.text);
+ assert.strictEqual(r.headers.get('location'), 'https://openvibe.actor/tasks/tsk_01J8SURFEXAMPLE00000000');
+ assert.strictEqual(r.headers.get('cache-control'), 'private, no-store');
+ const sent = t.upstreams.lastFor('/api/v1/tasks');
+ assert.strictEqual(sent.headers.authorization, `Bearer ${token}`);
+ assert.ok(JSON.parse(sent.body).task.includes('find the best surf spots in Portugal'));
+ const get = await t.get('/web/go?q=find%20the%20best%20surf%20spots', { cookie: signedIn });
+ assert.deepStrictEqual([get.status, get.headers.get('location')], [303, '/web'], 'a GET starts nothing: it goes back to the form');
+ });
+
+ await check('POST /web/go sends a guest to sign in, refuses a bad query and another site', async () => {
+ const guest = await t.get('/web/go', { form: { q: 'find the best surf spots' }, headers: own });
+ assert.strictEqual(guest.status, 303);
+ assert.strictEqual(guest.headers.get('location'), '/auth/login?next=%2Fweb');
+ const bad = await t.get('/web/go', { cookie: signedIn, form: { q: 'ab' }, headers: own });
+ assert.strictEqual(bad.status, 400);
+ assert.match(bad.text, /id="sf-web-form"/, 'the form again, with what was typed');
+ const cross = await t.get('/web/go', { cookie: signedIn, form: { q: 'find the best surf spots' }, headers: { origin: 'https://evil.example', 'sec-fetch-site': 'cross-site' } });
+ assert.strictEqual(cross.status, 403);
+ });
+
+ await check('POST /web/go passes Actor’s refusal through in words', async () => {
+ t.upstreams.state.actor.status = 429;
+ t.upstreams.state.actor.problem = { code: 'actor.allowance.exhausted', detail: 'The free tier runs 5 tasks a day; it resets at midnight UTC.' };
+ const r = await t.get('/web/go', { cookie: signedIn, form: { q: 'find the best surf spots' }, headers: own });
+ t.upstreams.state.actor.status = 201;
+ t.upstreams.state.actor.problem = null;
+ assert.strictEqual(r.status, 429);
+ assert.match(r.text, /free tier runs 5 tasks a day/);
+ });
+
+ await t.close();
+ done();
+})().catch((err) => { console.error(err); process.exit(1); });