diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..a510da8b989fb92930f409f719890c61bb17fdcb
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,61 @@
+FROM golang:bookworm as builder
+WORKDIR /go/src
+EXPOSE 8080
+
+# Install git first as it is required for go mod tidy
+RUN apt-get update && apt-get install -y git build-essential
+
+# Copy everything first
+COPY go.mod go.sum ./
+COPY warp.go server.go ./
+
+# Resolution ambiguity fix: Explicitly fetch the main module and tidy
+RUN go mod download && go mod tidy
+
+# Build binaries
+RUN CGO_ENABLED=0 GOOS=linux \
+ go build -a -installsuffix cgo -ldflags '-s' -o warp warp.go && \
+ go build -a -installsuffix cgo -ldflags '-s' -o server server.go
+
+FROM ubuntu:22.04
+
+# Copy binaries
+COPY --from=builder /go/src/warp /usr/local/bin/
+COPY --from=builder /go/src/server /usr/local/bin/
+COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
+
+COPY entrypoint.sh /usr/local/bin/
+
+# Install dependencies and Deno
+RUN apt-get update && apt-get install -y --no-install-recommends \
+ bash \
+ curl \
+ ca-certificates \
+ unzip \
+ ffmpeg \
+ && rm -rf /var/lib/apt/lists/* \
+ && curl -fsSL https://deno.land/x/install/install.sh | sh \
+ && mv /root/.deno/bin/deno /usr/local/bin/deno \
+ && chmod +x /usr/local/bin/entrypoint.sh
+
+# Copy Deno App
+WORKDIR /app
+COPY deno.json deno.lock compile.env grafana_dashboard.json ./
+COPY config ./config
+COPY src ./src
+
+ENV DAEMON_MODE false
+ENV PROXY_UP ""
+ENV PROXY_PORT "8080"
+ENV PROXY_USER ""
+ENV PROXY_PASS ""
+ENV WIREGUARD_UP ""
+ENV WIREGUARD_CONFIG ""
+ENV WIREGUARD_INTERFACE_PRIVATE_KEY ""
+ENV WIREGUARD_INTERFACE_DNS "1.1.1.1"
+ENV WIREGUARD_INTERFACE_ADDRESS ""
+ENV WIREGUARD_PEER_PUBLIC_KEY ""
+ENV WIREGUARD_PEER_ALLOWED_IPS "0.0.0.0/0"
+ENV WIREGUARD_PEER_ENDPOINT ""
+
+ENTRYPOINT [ "entrypoint.sh" ]
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..1ce875873d862b9c6b8583c6be32a864921ba551
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,661 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ 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.
+
+
+ Copyright (C)
+
+ 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 .
+
+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
+.
\ No newline at end of file
diff --git a/compile.env b/compile.env
new file mode 100644
index 0000000000000000000000000000000000000000..816eabc33734629fa9fd596174406fb5d5914855
--- /dev/null
+++ b/compile.env
@@ -0,0 +1 @@
+DENO_COMPILED=true
diff --git a/config/config.example.toml b/config/config.example.toml
new file mode 100644
index 0000000000000000000000000000000000000000..4bf2c36a0ee80a294346f49bf8cb250dc9903faa
--- /dev/null
+++ b/config/config.example.toml
@@ -0,0 +1,80 @@
+#####
+# The configuration options listed below are able to be enabled as needed.
+# The values in this example are the defaults. Some values can alternatively
+# be set using an environment variable.
+#
+# In order to enable an option, make sure you uncomment both the option
+# and the block header for the section it belongs to. Any other commented
+# options will continue to use default values.
+# See https://toml.io/en/ for details on the configuration format.
+#####
+
+# [server]
+# port = 8282 # env variable: PORT
+# host = "127.0.0.1" # env variable: HOST
+# # Listens to a unix socket on instead of a TCP socket
+# use_unix_socket = false # env variable: SERVER_USE_UNIX_SOCKET
+# unix_socket_path = "/tmp/invidious-companion.sock" # env variable: SERVER_UNIX_SOCKET_PATH
+# # Base path Invidious companion will serve from
+# base_path = "/companion" # env variable: SERVER_BASE_PATH
+# # secret key needs to be exactly 16 characters long
+# secret_key = "CHANGE_ME" # env variable: SERVER_SECRET_KEY
+# verify_requests = false # env variable: SERVER_VERIFY_REQUESTS
+# encrypt_query_params = false # env variable: SERVER_ENCRYPT_QUERY_PARAMS
+# enable_metrics = false # env variable: SERVER_ENABLE_METRICS
+
+# [cache]
+# enabled = true # env variable: CACHE_ENABLED
+# # will get cached in /var/tmp/youtubei.js if you specify /var/tmp
+# # you need to change the --allow-write from deno run too
+# directory = "/var/tmp" # env variable: CACHE_DIRECTORY
+
+# [networking]
+## Proxy type supported: https://docs.deno.com/api/deno/~/Deno.Proxy
+# #proxy = "" # env variable: PROXY
+# # Enable automatic proxy fetching from antpeak.com (free proxies, auto-rotates when failed)
+# # When enabled, ignores the `proxy` setting above and fetches proxies automatically
+# auto_proxy = false # env variable: NETWORKING_AUTO_PROXY
+# # IPv6 rotation settings - allows sending requests with unique IPv6 addresses
+# # This requires IPv6 setup: https://github.com/iv-org/invidious-companion/wiki/How-to-send-IPv6-requests-with-a-new-IPv6-address-for-each-request-on-a-server-with-a-whole-IPv6-range
+# # Randomizes all bits after the block prefix (e.g., /32 randomizes bits 33-128)
+# #ipv6_block = "2001:db8::/32" # env variable: NETWORKING_IPV6_BLOCK
+
+# [networking.videoplayback]
+# # Enable YouTube new video format UMP
+# ump = false # env variable: NETWORKING_VIDEOPLAYBACK_UMP
+# # size of chunks to request from google servers for rate limiting reductions
+# video_fetch_chunk_size_mb = 5 # env variable: NETWORKING_VIDEOPLAYBACK_VIDEO_FETCH_CHUNK_SIZE_MB
+
+###
+# Network call timeouts when talking to YouTube.
+# Needed in order to ensure Deno closes hanging connections
+###
+# [networking.fetch]
+# timeout_ms = 30000 # env variable: NETWORKING_FETCH_TIMEOUT_MS
+
+###
+# Network call retries when talking to YouTube, using
+# https://docs.deno.com/examples/exponential_backoff/
+###
+# [networking.fetch.retry]
+# # enable retries on calls to YouTube
+# enabled = false # env variable: NETWORKING_FETCH_RETRY_ENABLED
+# # max number of times to retry
+# times = 1 # env variable: NETWORKING_FETCH_RETRY_TIMES
+# # minimum wait after first call (ms)
+# initial_debounce = 0 # env variable: NETWORKING_FETCH_RETRY_INITIAL_DEBOUNCE
+# # how much to back off after each retry (multiplier of initial_debounce)
+# debounce_multiplier = 0 # env variable: NETWORKING_FETCH_RETRY_DEBOUNCE_MULTIPLIER
+
+# [jobs]
+
+# [jobs.youtube_session]
+# # whether to generate PO tokens
+# po_token_enabled = true # env variable: JOBS_YOUTUBE_SESSION_PO_TOKEN_ENABLED
+# # frequency of PO token refresh in cron format
+# frequency = "*/5 * * * *" # env variable: JOBS_YOUTUBE_SESSION_FREQUENCY
+
+# [youtube_session]
+# oauth_enabled = false # env variable: YOUTUBE_SESSION_OAUTH_ENABLED
+# cookies = "" # env variable: YOUTUBE_SESSION_COOKIES
diff --git a/config/config.toml b/config/config.toml
new file mode 100644
index 0000000000000000000000000000000000000000..0e83eddd1de92aeb7380f13713c0450d345013f6
--- /dev/null
+++ b/config/config.toml
@@ -0,0 +1,60 @@
+#####
+# Invidious Companion Configuration
+#
+# See https://toml.io/en/ for details on the configuration format.
+#####
+
+[server]
+port = 7860 # env: PORT
+host = "0.0.0.0" # env: HOST
+secret_key = "0123456789abcdef" # env: SERVER_SECRET_KEY
+
+# Optional Server Settings
+# use_unix_socket = false # env: SERVER_USE_UNIX_SOCKET
+# unix_socket_path = "/tmp/invidious-companion.sock"
+base_path = "/" # env: SERVER_BASE_PATH
+# verify_requests = false # env: SERVER_VERIFY_REQUESTS
+# encrypt_query_params = false # env: SERVER_ENCRYPT_QUERY_PARAMS
+enable_metrics = true # env: SERVER_ENABLE_METRICS
+
+[cache]
+enabled = false # env: CACHE_ENABLED
+# directory = "/var/tmp" # env: CACHE_DIRECTORY
+
+[networking]
+# Auto Proxy Settings
+# auto_proxy: enable automatic proxy fetching (rotates on failure)
+auto_proxy = false # env: NETWORKING_AUTO_PROXY
+
+# VPN Source: Which service to use for auto_proxy
+# 1 = AntPeak (Default)
+# 2 = Urban VPN
+# 3 = Custom Proxy API (self hosted)
+vpn_source = 2 # env: NETWORKING_VPN_SOURCE
+
+# Manual Proxy (overrides auto_proxy if set)
+ proxy = "http://127.0.0.1:8080"
+
+# IPv6 Rotation
+# ipv6_block = "2001:db8::/32" # env: NETWORKING_IPV6_BLOCK
+
+[networking.videoplayback]
+ump = false # env: NETWORKING_VIDEOPLAYBACK_UMP
+video_fetch_chunk_size_mb = 5 # env: NETWORKING_VIDEOPLAYBACK_VIDEO_FETCH_CHUNK_SIZE_MB
+
+[networking.fetch]
+# timeout_ms = 30000 # env: NETWORKING_FETCH_TIMEOUT_MS
+
+[networking.fetch.retry]
+# enabled = false # env: NETWORKING_FETCH_RETRY_ENABLED
+# times = 1 # env: NETWORKING_FETCH_RETRY_TIMES
+# initial_debounce = 0
+# debounce_multiplier = 0
+
+[jobs.youtube_session]
+po_token_enabled = true # env: JOBS_YOUTUBE_SESSION_PO_TOKEN_ENABLED
+frequency = "*/5 * * * *" # env: JOBS_YOUTUBE_SESSION_FREQUENCY
+
+[youtube_session]
+# oauth_enabled = true # env: YOUTUBE_SESSION_OAUTH_ENABLED
+# cookies = "" # env: YOUTUBE_SESSION_COOKIES
diff --git a/deno.json b/deno.json
new file mode 100644
index 0000000000000000000000000000000000000000..f3da7e7f2526a24cc6ceb06b9a942661557b8645
--- /dev/null
+++ b/deno.json
@@ -0,0 +1,47 @@
+{
+ "tasks": {
+ "dev": "deno run --allow-import=github.com:443,jsr.io:443,cdn.jsdelivr.net:443,esm.sh:443,deno.land:443 --allow-net --allow-env --allow-sys=hostname --allow-read=.,/var/tmp/youtubei.js,/tmp/invidious-companion.sock,/tmp/mp3-downloads --allow-write=/var/tmp/youtubei.js,/tmp/invidious-companion.sock,/tmp/mp3-downloads --allow-run=ffmpeg --watch src/main.ts",
+ "compile": "deno compile --include ./src/lib/helpers/youtubePlayerReq.ts --include ./src/lib/helpers/getFetchClient.ts --output invidious_companion --allow-import=github.com:443,jsr.io:443,cdn.jsdelivr.net:443,esm.sh:443,deno.land:443 --allow-net --allow-env --allow-read --allow-sys=hostname --allow-write=/var/tmp/youtubei.js,/tmp/invidious-companion.sock,/tmp/mp3-downloads --allow-run=ffmpeg src/main.ts --_version_date=\"$(git log -1 --format=%ci | awk '{print $1}' | sed s/-/./g)\" --_version_commit=\"$(git rev-list HEAD --max-count=1 --abbrev-commit)\"",
+ "test": "deno test --allow-import=github.com:443,jsr.io:443,cdn.jsdelivr.net:443,esm.sh:443,deno.land:443 --allow-net --allow-env --allow-sys=hostname --allow-read=.,/var/tmp/youtubei.js,/tmp/invidious-companion.sock --allow-write=/var/tmp/youtubei.js",
+ "format": "deno fmt src/**",
+ "check": "deno check src/**",
+ "lint": "deno lint src/**"
+ },
+ "imports": {
+ "@std/cli": "jsr:@std/cli@^1.0.17",
+ "hono": "jsr:@hono/hono@4.7.4",
+ "@std/toml": "jsr:@std/toml@1.0.2",
+ "prom-client": "https://esm.sh/prom-client@15.1.3?pin=v135",
+ "youtubei.js": "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno.ts",
+ "youtubei.js/Utils": "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/utils/Utils.ts",
+ "youtubei.js/NavigationEndpoint": "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/NavigationEndpoint.ts",
+ "youtubei.js/PlayerCaptionsTracklist": "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PlayerCaptionsTracklist.ts",
+ "youtubei.js/TabbedFeed": "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/core/mixins/TabbedFeed.ts",
+ "jsdom": "npm:jsdom@26.1.0",
+ "bgutils": "https://esm.sh/bgutils-js@3.2.0",
+ "estree": "https://esm.sh/@types/estree@1.0.6",
+ "youtubePlayerReq": "./src/lib/helpers/youtubePlayerReq.ts",
+ "getFetchClient": "./src/lib/helpers/getFetchClient.ts",
+ "googlevideo": "jsr:@luanrt/googlevideo@2.0.0",
+ "meriyah": "npm:meriyah@6.1.4",
+ "crypto/": "https://deno.land/x/crypto@v0.11.0/",
+ "@std/encoding/base64": "jsr:@std/encoding@1.0.7/base64",
+ "@std/async": "jsr:@std/async@1.0.11",
+ "@std/fs": "jsr:@std/fs@1.0.14",
+ "@std/path": "jsr:@std/path@1.0.8",
+ "brotli": "https://deno.land/x/brotli@0.1.7/mod.ts",
+ "zod": "https://deno.land/x/zod@v3.24.2/mod.ts",
+ "canvas": "./src/lib/extra/emptyExport.ts",
+ "bufferutil": "./src/lib/extra/emptyExport.ts",
+ "utf-8-validate": "./src/lib/extra/emptyExport.ts"
+ },
+ "unstable": [
+ "cron",
+ "kv",
+ "http",
+ "temporal"
+ ],
+ "fmt": {
+ "indentWidth": 4
+ }
+}
\ No newline at end of file
diff --git a/deno.lock b/deno.lock
new file mode 100644
index 0000000000000000000000000000000000000000..43b9594237e7fa775f6f1bbcb1da33d7c2039fda
--- /dev/null
+++ b/deno.lock
@@ -0,0 +1,1208 @@
+{
+ "version": "5",
+ "specifiers": {
+ "jsr:@hono/hono@4.7.4": "4.7.4",
+ "jsr:@std/assert@1.0.12": "1.0.12",
+ "jsr:@std/async@1.0.11": "1.0.11",
+ "jsr:@std/cli@^1.0.17": "1.0.24",
+ "jsr:@std/collections@^1.0.9": "1.1.3",
+ "jsr:@std/encoding@1.0.7": "1.0.7",
+ "jsr:@std/fs@1.0.14": "1.0.14",
+ "jsr:@std/internal@^1.0.6": "1.0.12",
+ "jsr:@std/toml@1.0.2": "1.0.2",
+ "npm:jsdom@26.1.0": "26.1.0",
+ "npm:meriyah@6.1.4": "6.1.4"
+ },
+ "jsr": {
+ "@hono/hono@4.7.4": {
+ "integrity": "c03c9cbe0fbfc4e51f3fee6502a7903aa4f9ef7c2c98635607b15eee14258825"
+ },
+ "@std/assert@1.0.12": {
+ "integrity": "08009f0926dda9cbd8bef3a35d3b6a4b964b0ab5c3e140a4e0351fbf34af5b9a",
+ "dependencies": [
+ "jsr:@std/internal"
+ ]
+ },
+ "@std/async@1.0.11": {
+ "integrity": "eee0d3405275506638a9c8efaa849cf0d35873120c69b7caa1309c9a9e5b6f85"
+ },
+ "@std/cli@1.0.24": {
+ "integrity": "b655a5beb26aa94f98add6bc8889f5fb9bc3ee2cc3fc954e151201f4c4200a5e"
+ },
+ "@std/collections@1.1.3": {
+ "integrity": "bf8b0818886df6a32b64c7d3b037a425111f28278d69fd0995aeb62777c986b0"
+ },
+ "@std/encoding@1.0.7": {
+ "integrity": "f631247c1698fef289f2de9e2a33d571e46133b38d042905e3eac3715030a82d"
+ },
+ "@std/fs@1.0.14": {
+ "integrity": "1e84bf0b95fe08f41f1f4aea9717bbf29f45408a56ce073b0114474ce1c9fccf"
+ },
+ "@std/internal@1.0.12": {
+ "integrity": "972a634fd5bc34b242024402972cd5143eac68d8dffaca5eaa4dba30ce17b027"
+ },
+ "@std/toml@1.0.2": {
+ "integrity": "5892ba489c5b512265a384238a8fe8dddbbb9498b4b210ef1b9f0336a423a39b",
+ "dependencies": [
+ "jsr:@std/collections"
+ ]
+ }
+ },
+ "npm": {
+ "@asamuzakjp/css-color@3.2.0_@csstools+css-parser-algorithms@3.0.5__@csstools+css-tokenizer@3.0.4_@csstools+css-tokenizer@3.0.4": {
+ "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==",
+ "dependencies": [
+ "@csstools/css-calc",
+ "@csstools/css-color-parser",
+ "@csstools/css-parser-algorithms",
+ "@csstools/css-tokenizer",
+ "lru-cache"
+ ]
+ },
+ "@csstools/color-helpers@5.1.0": {
+ "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA=="
+ },
+ "@csstools/css-calc@2.1.4_@csstools+css-parser-algorithms@3.0.5__@csstools+css-tokenizer@3.0.4_@csstools+css-tokenizer@3.0.4": {
+ "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==",
+ "dependencies": [
+ "@csstools/css-parser-algorithms",
+ "@csstools/css-tokenizer"
+ ]
+ },
+ "@csstools/css-color-parser@3.1.0_@csstools+css-parser-algorithms@3.0.5__@csstools+css-tokenizer@3.0.4_@csstools+css-tokenizer@3.0.4": {
+ "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==",
+ "dependencies": [
+ "@csstools/color-helpers",
+ "@csstools/css-calc",
+ "@csstools/css-parser-algorithms",
+ "@csstools/css-tokenizer"
+ ]
+ },
+ "@csstools/css-parser-algorithms@3.0.5_@csstools+css-tokenizer@3.0.4": {
+ "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==",
+ "dependencies": [
+ "@csstools/css-tokenizer"
+ ]
+ },
+ "@csstools/css-tokenizer@3.0.4": {
+ "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw=="
+ },
+ "agent-base@7.1.4": {
+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="
+ },
+ "cssstyle@4.6.0": {
+ "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==",
+ "dependencies": [
+ "@asamuzakjp/css-color",
+ "rrweb-cssom"
+ ]
+ },
+ "data-urls@5.0.0": {
+ "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==",
+ "dependencies": [
+ "whatwg-mimetype",
+ "whatwg-url"
+ ]
+ },
+ "debug@4.4.3": {
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dependencies": [
+ "ms"
+ ]
+ },
+ "decimal.js@10.6.0": {
+ "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg=="
+ },
+ "entities@6.0.1": {
+ "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="
+ },
+ "html-encoding-sniffer@4.0.0": {
+ "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==",
+ "dependencies": [
+ "whatwg-encoding"
+ ]
+ },
+ "http-proxy-agent@7.0.2": {
+ "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
+ "dependencies": [
+ "agent-base",
+ "debug"
+ ]
+ },
+ "https-proxy-agent@7.0.6": {
+ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
+ "dependencies": [
+ "agent-base",
+ "debug"
+ ]
+ },
+ "iconv-lite@0.6.3": {
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dependencies": [
+ "safer-buffer"
+ ]
+ },
+ "is-potential-custom-element-name@1.0.1": {
+ "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="
+ },
+ "jsdom@26.1.0": {
+ "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==",
+ "dependencies": [
+ "cssstyle",
+ "data-urls",
+ "decimal.js",
+ "html-encoding-sniffer",
+ "http-proxy-agent",
+ "https-proxy-agent",
+ "is-potential-custom-element-name",
+ "nwsapi",
+ "parse5",
+ "rrweb-cssom",
+ "saxes",
+ "symbol-tree",
+ "tough-cookie",
+ "w3c-xmlserializer",
+ "webidl-conversions",
+ "whatwg-encoding",
+ "whatwg-mimetype",
+ "whatwg-url",
+ "ws",
+ "xml-name-validator"
+ ]
+ },
+ "lru-cache@10.4.3": {
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="
+ },
+ "meriyah@6.1.4": {
+ "integrity": "sha512-Sz8FzjzI0kN13GK/6MVEsVzMZEPvOhnmmI1lU5+/1cGOiK3QUahntrNNtdVeihrO7t9JpoH75iMNXg6R6uWflQ=="
+ },
+ "ms@2.1.3": {
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
+ },
+ "nwsapi@2.2.22": {
+ "integrity": "sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ=="
+ },
+ "parse5@7.3.0": {
+ "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
+ "dependencies": [
+ "entities"
+ ]
+ },
+ "punycode@2.3.1": {
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="
+ },
+ "rrweb-cssom@0.8.0": {
+ "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw=="
+ },
+ "safer-buffer@2.1.2": {
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+ },
+ "saxes@6.0.0": {
+ "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
+ "dependencies": [
+ "xmlchars"
+ ]
+ },
+ "symbol-tree@3.2.4": {
+ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="
+ },
+ "tldts-core@6.1.86": {
+ "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA=="
+ },
+ "tldts@6.1.86": {
+ "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==",
+ "dependencies": [
+ "tldts-core"
+ ],
+ "bin": true
+ },
+ "tough-cookie@5.1.2": {
+ "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==",
+ "dependencies": [
+ "tldts"
+ ]
+ },
+ "tr46@5.1.1": {
+ "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
+ "dependencies": [
+ "punycode"
+ ]
+ },
+ "w3c-xmlserializer@5.0.0": {
+ "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
+ "dependencies": [
+ "xml-name-validator"
+ ]
+ },
+ "webidl-conversions@7.0.0": {
+ "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="
+ },
+ "whatwg-encoding@3.1.1": {
+ "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
+ "dependencies": [
+ "iconv-lite"
+ ]
+ },
+ "whatwg-mimetype@4.0.0": {
+ "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="
+ },
+ "whatwg-url@14.2.0": {
+ "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
+ "dependencies": [
+ "tr46",
+ "webidl-conversions"
+ ]
+ },
+ "ws@8.18.3": {
+ "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="
+ },
+ "xml-name-validator@5.0.0": {
+ "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg=="
+ },
+ "xmlchars@2.2.0": {
+ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
+ }
+ },
+ "redirects": {
+ "https://esm.sh/@asamuzakjp/css-color@^3.2.0?target=denonext": "https://esm.sh/@asamuzakjp/css-color@3.2.0?target=denonext",
+ "https://esm.sh/@csstools/color-helpers@^5.1.0?target=denonext": "https://esm.sh/@csstools/color-helpers@5.1.0?target=denonext",
+ "https://esm.sh/@csstools/css-calc@^2.1.3?target=denonext": "https://esm.sh/@csstools/css-calc@2.1.4?target=denonext",
+ "https://esm.sh/@csstools/css-calc@^2.1.4?target=denonext": "https://esm.sh/@csstools/css-calc@2.1.4?target=denonext",
+ "https://esm.sh/@csstools/css-color-parser@^3.0.9?target=denonext": "https://esm.sh/@csstools/css-color-parser@3.1.0?target=denonext",
+ "https://esm.sh/@csstools/css-parser-algorithms@^3.0.4?target=denonext": "https://esm.sh/@csstools/css-parser-algorithms@3.0.5?target=denonext",
+ "https://esm.sh/@csstools/css-parser-algorithms@^3.0.5?target=denonext": "https://esm.sh/@csstools/css-parser-algorithms@3.0.5?target=denonext",
+ "https://esm.sh/@csstools/css-tokenizer@^3.0.3?target=denonext": "https://esm.sh/@csstools/css-tokenizer@3.0.4?target=denonext",
+ "https://esm.sh/@csstools/css-tokenizer@^3.0.4?target=denonext": "https://esm.sh/@csstools/css-tokenizer@3.0.4?target=denonext",
+ "https://esm.sh/@opentelemetry/api@^1.4.0?target=denonext": "https://esm.sh/@opentelemetry/api@1.9.0?target=denonext",
+ "https://esm.sh/agent-base@^7.1.0?target=denonext": "https://esm.sh/agent-base@7.1.4?target=denonext",
+ "https://esm.sh/agent-base@^7.1.2?target=denonext": "https://esm.sh/agent-base@7.1.4?target=denonext",
+ "https://esm.sh/asynckit@^0.4.0?target=denonext": "https://esm.sh/asynckit@0.4.0?target=denonext",
+ "https://esm.sh/combined-stream@^1.0.8?target=denonext": "https://esm.sh/combined-stream@1.0.8?target=denonext",
+ "https://esm.sh/cssstyle@^4.2.1?target=denonext": "https://esm.sh/cssstyle@4.6.0?target=denonext",
+ "https://esm.sh/data-urls@^5.0.0?target=denonext": "https://esm.sh/data-urls@5.0.0?target=denonext",
+ "https://esm.sh/debug@4?target=denonext": "https://esm.sh/debug@4.4.3?target=denonext",
+ "https://esm.sh/debug@^4.3.4?target=denonext": "https://esm.sh/debug@4.4.3?target=denonext",
+ "https://esm.sh/decimal.js@^10.4.3?target=denonext": "https://esm.sh/decimal.js@10.6.0?target=denonext",
+ "https://esm.sh/decimal.js@^10.5.0?target=denonext": "https://esm.sh/decimal.js@10.6.0?target=denonext",
+ "https://esm.sh/delayed-stream@~1.0.0?target=denonext": "https://esm.sh/delayed-stream@1.0.0?target=denonext",
+ "https://esm.sh/entities@^6.0.0/decode?target=denonext": "https://esm.sh/entities@6.0.1/decode?target=denonext",
+ "https://esm.sh/entities@^6.0.0/escape?target=denonext": "https://esm.sh/entities@6.0.1/escape?target=denonext",
+ "https://esm.sh/form-data@^4.0.1?target=denonext": "https://esm.sh/form-data@4.0.5?target=denonext",
+ "https://esm.sh/html-encoding-sniffer@^4.0.0?target=denonext": "https://esm.sh/html-encoding-sniffer@4.0.0?target=denonext",
+ "https://esm.sh/http-proxy-agent@^7.0.2?target=denonext": "https://esm.sh/http-proxy-agent@7.0.2?target=denonext",
+ "https://esm.sh/https-proxy-agent@^7.0.6?target=denonext": "https://esm.sh/https-proxy-agent@7.0.6?target=denonext",
+ "https://esm.sh/is-potential-custom-element-name@^1.0.1?target=denonext": "https://esm.sh/is-potential-custom-element-name@1.0.1?target=denonext",
+ "https://esm.sh/lru-cache@^10.4.3?target=denonext": "https://esm.sh/lru-cache@10.4.3?target=denonext",
+ "https://esm.sh/mime-types@^2.1.12?target=denonext": "https://esm.sh/mime-types@2.1.35?target=denonext",
+ "https://esm.sh/ms@^2.1.3?target=denonext": "https://esm.sh/ms@2.1.3?target=denonext",
+ "https://esm.sh/nwsapi@^2.2.16?target=denonext": "https://esm.sh/nwsapi@2.2.22?target=denonext",
+ "https://esm.sh/parse5@^7.2.1?target=denonext": "https://esm.sh/parse5@7.3.0?target=denonext",
+ "https://esm.sh/punycode@^2.3.1?target=denonext": "https://esm.sh/punycode@2.3.1?target=denonext",
+ "https://esm.sh/rrweb-cssom@^0.8.0?target=denonext": "https://esm.sh/rrweb-cssom@0.8.0?target=denonext",
+ "https://esm.sh/safer-buffer@^2.1.2?target=denonext": "https://esm.sh/safer-buffer@2.1.2?target=denonext",
+ "https://esm.sh/saxes@^6.0.0?target=denonext": "https://esm.sh/saxes@6.0.0?target=denonext",
+ "https://esm.sh/supports-color?target=denonext": "https://esm.sh/supports-color@10.2.2?target=denonext",
+ "https://esm.sh/symbol-tree@^3.2.4?target=denonext": "https://esm.sh/symbol-tree@3.2.4?target=denonext",
+ "https://esm.sh/tdigest@^0.1.1?target=denonext": "https://esm.sh/tdigest@0.1.2?target=denonext",
+ "https://esm.sh/tldts-core@^6.1.86?target=denonext": "https://esm.sh/tldts-core@6.1.86?target=denonext",
+ "https://esm.sh/tldts@^6.1.32?target=denonext": "https://esm.sh/tldts@6.1.86?target=denonext",
+ "https://esm.sh/tough-cookie@^5.0.0?target=denonext": "https://esm.sh/tough-cookie@5.1.2?target=denonext",
+ "https://esm.sh/tough-cookie@^5.1.1?target=denonext": "https://esm.sh/tough-cookie@5.1.2?target=denonext",
+ "https://esm.sh/tr46@^5.1.0?target=denonext": "https://esm.sh/tr46@5.1.1?target=denonext",
+ "https://esm.sh/w3c-xmlserializer@^5.0.0?target=denonext": "https://esm.sh/w3c-xmlserializer@5.0.0?target=denonext",
+ "https://esm.sh/webidl-conversions@^7.0.0?target=denonext": "https://esm.sh/webidl-conversions@7.0.0?target=denonext",
+ "https://esm.sh/whatwg-encoding@^3.1.1?target=denonext": "https://esm.sh/whatwg-encoding@3.1.1?target=denonext",
+ "https://esm.sh/whatwg-mimetype@^4.0.0?target=denonext": "https://esm.sh/whatwg-mimetype@4.0.0?target=denonext",
+ "https://esm.sh/whatwg-url@^14.0.0?target=denonext": "https://esm.sh/whatwg-url@14.2.0?target=denonext",
+ "https://esm.sh/whatwg-url@^14.1.0/webidl2js-wrapper?target=denonext": "https://esm.sh/whatwg-url@14.2.0/webidl2js-wrapper?target=denonext",
+ "https://esm.sh/whatwg-url@^14.1.0?target=denonext": "https://esm.sh/whatwg-url@14.2.0?target=denonext",
+ "https://esm.sh/whatwg-url@^14.1.1/webidl2js-wrapper?target=denonext": "https://esm.sh/whatwg-url@14.2.0/webidl2js-wrapper?target=denonext",
+ "https://esm.sh/whatwg-url@^14.1.1?target=denonext": "https://esm.sh/whatwg-url@14.2.0?target=denonext",
+ "https://esm.sh/ws@^8.18.0?external=bufferutil,utf-8-validate&target=denonext": "https://esm.sh/ws@8.18.3?external=bufferutil,utf-8-validate&target=denonext",
+ "https://esm.sh/xml-name-validator@^5.0.0?target=denonext": "https://esm.sh/xml-name-validator@5.0.0?target=denonext",
+ "https://esm.sh/xmlchars@^2.2.0/xml/1.0/ed5?target=denonext": "https://esm.sh/xmlchars@2.2.0/xml/1.0/ed5?target=denonext",
+ "https://esm.sh/xmlchars@^2.2.0/xml/1.1/ed2?target=denonext": "https://esm.sh/xmlchars@2.2.0/xml/1.1/ed2?target=denonext",
+ "https://esm.sh/xmlchars@^2.2.0/xmlns/1.0/ed3?target=denonext": "https://esm.sh/xmlchars@2.2.0/xmlns/1.0/ed3?target=denonext"
+ },
+ "remote": {
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno.ts": "f913bfdb3c66b2330fa8b531bd1e291437b836c9fbf002b9ae044bf14e1f397c",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/package.json": "37a3751c6d6b7bacf9eee43a5e2f42e003a950b23f30927f27033a375650fd12",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/protos/generated/misc/common.ts": "c71cd3a460e035b46422cce349a9b23cff83bed357eec94fba50a99ba292957c",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/protos/generated/misc/params.ts": "4bd8bf67bda1f606ef94ded2fe5f755b55067c5c67bef3c77cb388d2a5867641",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/protos/generated/youtube/api/pfiinnertube/attestation_response_data.ts": "0f4f2580e4995117155455877d192aadfeda85e4012dbd4b184d0fba35a5c068",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/protos/generated/youtube/api/pfiinnertube/capability_info.ts": "cdc7a1921f93e019e6e7ab67da5a3af57cc8f75c1a52e5321eeee79155f2f5dd",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/protos/generated/youtube/api/pfiinnertube/client_info.ts": "76d8d517701ac987d32f6c8405bd0fa828d5dbd449761c94afc340918fa107b8",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/protos/generated/youtube/api/pfiinnertube/innertube_context.ts": "cb207febdc98a7a9a6dd32a721cbf404673516cbcfd4b0de450fe0bdb1a49f78",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/protos/generated/youtube/api/pfiinnertube/metadata_update_request.ts": "c8c13395883bedae170a06e86623d2c63c96eee2a2e55710d9e211307b0131e5",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/protos/generated/youtube/api/pfiinnertube/request_info.ts": "4f6ce95cb60398755450a74b7c5aad7014d680f679762b823582a59ed9acd650",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/protos/generated/youtube/api/pfiinnertube/third_party_info.ts": "7f61b26270ea5740d1aee22ee2498e31046d18d7a4d285fbc1ca0470b39c55f3",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/protos/generated/youtube/api/pfiinnertube/user_info.ts": "5833c2998d1f0611581621a20d487ccb74ebb09eb170efbffb90a5b0a1faab41",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/Innertube.ts": "b9b370f2ee04025edfaa3c6693b3b94bb6059c6a25e7799ee62911b1f22d873e",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/core/Actions.ts": "e7be1d70d26a2a43ef3440cbbaf4e4842298a78c25b3b3a7eae00913567f3629",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/core/OAuth2.ts": "6f622a6cd6628908a333a5a59e7563e03784ebbce04f58d2e13df26a14a92061",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/core/Player.ts": "fdaa46834890d2e3c2f54f542b90eb32b229e45ed64e39f0fcf00cbe25fdbd25",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/core/Session.ts": "f98ebe5db92cb1fc3b7a7335c631f4ca5c504988f7b2838796ca16ea981cf333",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/core/clients/Kids.ts": "33dabfcb41bbe51ef1507a50b4e0fe1269d0e2f681e60d5defa96932af10d320",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/core/clients/Music.ts": "98347d692141561ff5a87999b6f6b65c06c830f40a733706898552463630aa88",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/core/clients/Studio.ts": "5cd99ce2024c625c4b21f2aca0a2e45c945e2cd2f2f955647725aa8b5a738806",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/core/clients/index.ts": "b2606d625fea9aabe4747c4f01de63e3b599374a8160997623bd7efa9c3eff76",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/core/index.ts": "d96c02d9789a16157680cc7f1adfc7387ffc8743db6c0f4f0bd343b6af4ecf64",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/core/managers/AccountManager.ts": "05c569396982fb67d2a77308031039120e69974b58d7cd7520c5a9663ede7c40",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/core/managers/InteractionManager.ts": "ffb7c8c575b5dc4889c1af110f49a8ed479f13d7596875faca23828488598fb4",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/core/managers/PlaylistManager.ts": "d1d1b4582e0f6e0c6e0ec23592bd148605058ae1e17ef4472ea021f37c3225e1",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/core/managers/index.ts": "5beecdce8cda61ea8c9ae837d44ccc0f0be51cad4301c6cec10e7f74aa07410d",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/core/mixins/Feed.ts": "f2f844c2ae4fba8377b08f4662299bf7722cb28cc167edb284709280004a89ea",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/core/mixins/FilterableFeed.ts": "4719386e06883717daf2a679ee6ffd0ef9a6e66bed6f12e31e50a5214647d1d7",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/core/mixins/MediaInfo.ts": "f9825dde9dd5bc37048bb09e50e0f97feead3915f4c681e421cf7b630396fdad",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/core/mixins/TabbedFeed.ts": "8fa623bb087ba50de395d76e77ee73eefa6afeba15b43834a13f064fd198a53f",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/core/mixins/index.ts": "fe6111ac42722b605f58435c6c23d6ad95fd5a977808f7d47c3a77a0e4bb219e",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/AboutChannel.ts": "b7098bf0ce19c25025f8ea9296c850078e422ffed01e66233fbd521415be5ea4",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/AboutChannelView.ts": "f9a033c58c1a72e202bd8812828ee18818afed88d46c67fcc427651f91f4f966",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/AccountChannel.ts": "42d25e5d0b3b33dbbd44d5e42333ad8af2180a908d60e165999a0d22ae1f698d",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/AccountItem.ts": "b8ae58db28eeed79c637b9dc628ff86af2a4431804a7cac241fc694e5d4abced",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/AccountItemSection.ts": "9397913f6f7dedf4ca6df0c1388558e97822fc3306a51ec0fb3f2166d56e0728",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/AccountItemSectionHeader.ts": "6653606bb157013a896a931d5afae0c04111f476fc8156e0726b04bd985cff19",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/AccountSectionList.ts": "278b0c7c1562e0c133a001a0c9aeb77fc6ac0a1325e773c241becff42f212b33",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ActiveAccountHeader.ts": "d97c132a1cf4d50af67437592b712c4e5d13ee3c0fdb0ba0e1af07dc158657b2",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/AddToPlaylist.ts": "0442b49349483d03246ef476d5814039e8b120770ea7f4e9a8d677bae04881bb",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/Alert.ts": "951317e55a6824da1ec614792b833c2c101341d3a8bec7378c37bc41a50507c0",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/AlertWithButton.ts": "5052f31ccc17ae1867a6c581d89bf2a2b2010d262b2bb585408dd4bfb4ce53d2",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/AnimatedThumbnailOverlayView.ts": "5db12d3e7de1d401f3a010296da60c1bd5ae526680936e1d07f609745128365a",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/AttributionView.ts": "02a1e4a9f71f64945c2f392220f5cc87fa2ba11d6fb21d803746c90572273f74",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/AudioOnlyPlayability.ts": "4bceb72fd82087d6607fe19e497769b8aec58037848ca136a759abd12ba8203c",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/AutomixPreviewVideo.ts": "324f69168e2fd3878f86c4c2b6cca1babd199607c8e63858e7e2f6e9dcc582d8",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/AvatarStackView.ts": "1bafc5095d5b6bd45766c64669346136530b58ae144ff1e9e6cb012cc3a37c71",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/AvatarView.ts": "8f2d0c5bcd46969be7f09ab60c2d4ef1867c9b68b1606e57d9c66116cdb73e83",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/BackgroundPromo.ts": "6e63fbfd84689656bf0fff071cc4ba085312e8d7c4c212527b6adac635b087d4",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/BackstageImage.ts": "70e8d61a8288658a3b85e541e80515be8543ba7f888e0ebd57d20dfc4686afbe",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/BackstagePost.ts": "17b84179958182a23245d115b6078e8422aae02aecb63a2755850bfef0090037",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/BackstagePostThread.ts": "8c1060ae74df090b896be6675e2862bbdac34b88bc0f2e8902ea77e3bc0c94b2",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/BadgeView.ts": "1b8519ecb191026b7b4e89ca5eaaad9f6eae924d9b81c41cd370aaf9932a4489",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/BrowseFeedActions.ts": "31cdbc4eed104c1404b66db108449c6f2dcae2d99441116f0e5c698778f897ea",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/BrowserMediaSession.ts": "8acf89acb338340f6fda58cc93cba64324d7e3c7c475a292848e0e559714a792",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/Button.ts": "bde5b357993e6b964ea5c3d66ebfb8675d908c189db3d6c5bfb2db1bb750ba98",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ButtonCardView.ts": "4141e92c1d5287a8ea68c6af185e60b3fd1979348d9d9c9ce5ddbd82c76cf0f8",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ButtonView.ts": "f7e0cd870e764368a9d5342e8153faba4a152dc0981a9cf1cb1f755585fa6956",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/C4TabbedHeader.ts": "f0931752407327d5394ba50671ca551ad30729dae2027b9073b8cbc4549d975c",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/CallToActionButton.ts": "6ba73d975c67478b30a7f15a3c2b40c475324faaf70f6988cf0cea62ce996f55",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/Card.ts": "0bb00ec52f69c90035cf02a26d70a225ec18e35a7e5a289afb10d17f9e64efe3",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/CardCollection.ts": "0f02c3bd5be6229fda3e351173feb842d699df39da8dacd39c906f4f45a199f6",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/CarouselHeader.ts": "7aac27476062e708fa75a8022b95414ed896407d2b1a24f25be1390683418b39",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/CarouselItem.ts": "a0d5da435a191a94b07ed156c65b80c51ddef3d02e582e4643cd3bd931fdf690",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/CarouselItemView.ts": "477349bfea82b81929c676c1cd8b587c3c489711522e4f7a492389b0e2080149",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/CarouselLockup.ts": "7d2d2cc744d7fe4c6728be720539ca77623ce7f045fbd32e4ceb0a7ddeb0f8a1",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/CarouselTitleView.ts": "1c37c201d6a62638c2a5902f153ff8150c4251051e673a6a7b264dc99240a619",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/Channel.ts": "aa770ee9464546899aa6e87f23c60ff5fddb0456b3978dc348a69e7ac6888852",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ChannelAboutFullMetadata.ts": "f417bf2920a7b21362ce7e92f3bc2caa63df92de66ce320b6b80d870bee5dc72",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ChannelAgeGate.ts": "2981c34ea2b790380e4241072c32a5ae8f28699f6b65d54afb3fe03cd23e8ed1",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ChannelExternalLinkView.ts": "0464d1afaca0ceb5bfefc9eee5f8b284434c766aab043d2429839a92f6a17680",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ChannelFeaturedContent.ts": "7855e3eaab1d674715bd4d71240c31cd398d0beeb7c4cfba29413f45667ca6dd",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ChannelHeaderLinks.ts": "c1f2470548d874c99325e843a41db9a8360ccb4041f76cfcdc0149e7ba6de609",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ChannelHeaderLinksView.ts": "11800e5d680fcb33f8952dad1a880e4056bd1a28936e6c1bae16a90214ce8101",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ChannelMetadata.ts": "fd1050bb5d5159b6c7f9a6e92a9a04adfc32bf504066d32bc691e617c63fd420",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ChannelMobileHeader.ts": "0b521d7cfbe3eb096638ca916af4a4b4b865d6b5c30910c5627b21d0b51f8089",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ChannelOptions.ts": "7fb3f9589d5ca8c708c56d429373cfc266858c60f338b91a18f03c2fe24cd414",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ChannelOwnerEmptyState.ts": "b747b29b2eca1d26f3b5cdcef458bdae6275945aa59c5a549c7173d0723cd673",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ChannelSubMenu.ts": "bb6640f3d1f27079d0e57b4351e0a16c3163bf61a4300666e4cd886dadf74f3e",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ChannelSwitcherHeader.ts": "352660758c5515ca1621a98ba8b6c91eb739e757473240d86993b42d3b43c070",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ChannelSwitcherPage.ts": "f937bc413eb3da1f8b31b1ab65934021fe63baf8993ecb251d73289b886df7b5",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ChannelTagline.ts": "8479718dc0eeec61848387a7129af35dc69ce6a3cb1505ba101b736a819e24ff",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ChannelThumbnailWithLink.ts": "e335f0a5890ce9a5c01be37d82c1d4ead14b763cf11156e48cf142aaa7056d94",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ChannelVideoPlayer.ts": "c0fca466db8fe642014564969d59153750f7ac59238338ec7878e6847aa41160",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/Chapter.ts": "e51baa5d63b638aae64483a57e4780bc3804fb528b0322fffac5941257c36168",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ChildVideo.ts": "1dac31a72cf3acb8ff98b756a595827b3cb368df0390c39427042137641c8bfb",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ChipBarView.ts": "a9d004a98369853836c82ec322199a0ea76c7cb1e339766bff26b1c25c20b9af",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ChipCloud.ts": "23653511a7f6fd1f4afe8a7819fbcf7357ae8c485ea654b627b2c01cd02faa76",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ChipCloudChip.ts": "523050ad614b0cbda3bf965549d4a15f3f5097b0c3003eb5c5b0a3d71d7f09af",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ChipView.ts": "4be3ae041ec848208c9526939cb079565263e7e81e49367e7f49a49da120a24c",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ClientSideToggleMenuItem.ts": "505b0fa14d1e287dfda459d1e42a0277ae56b00fb1170af0dd3cae74d93329b6",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ClipAdState.ts": "12a62df6a13800bef4722c5b19c15dd6879563575fb5c6421f8ce395ad2a8716",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ClipCreation.ts": "5649fbabd461e635b10316bd04016f21a933b5c9ed4ab7465a33c2963c57f994",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ClipCreationScrubber.ts": "c19b58a3fc18ef835e95ee9ec3236135e5291d7f03fffbf5f0b2eae6f59acfa0",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ClipCreationTextInput.ts": "5dc8b6d33b273e48607fbc7bb628c8dbce186dacf215465e95249940f0fe7a6b",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ClipSection.ts": "2dcdadb4c69cabe2592d549b8010b917b24c1ad092ba4b9f5524c45d451fd753",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/CollaboratorInfoCardContent.ts": "216b4b6c033cabad5ca335c61e7a44d8f1ded772eb6b7fb4f0f2301ad00dc910",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/CollageHeroImage.ts": "37c580d4eada8dda7171703dab6a7e23aa42a44a32b5a65ffc28c7cec5dae71c",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/CollectionThumbnailView.ts": "724bdfe69e5be7f7e90ca3d1b1bb0a7ff97762622dc7538203baafc4c84bfa01",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/CompactChannel.ts": "0d4eec78fad9ae30658b5ad8ef45322aca0285493372e6a1c650f3301be48b27",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/CompactLink.ts": "94b196062427e1fcf0806eecb9b37e63206571794427990ddf1c22ce49b72ecf",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/CompactMix.ts": "2821cf9ebec785f8ea40734b068f879604ec29ec8e0aa8ca4120c606a41c7f32",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/CompactMovie.ts": "749f8a5901cca3508bc2bc3681535695455150b949885eaae80b787faa2b24ba",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/CompactPlaylist.ts": "6cf130b855ec292a3947d51b303d225f22828271212a0e24d5716f80f9e8aafa",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/CompactStation.ts": "bbad99d1c357248ee6f2ca67fe12923c91278c5fe6055104d38abbe929c434e8",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/CompactVideo.ts": "c79343ee77d6ab4a29c40a1f199b88ac4c2767ae06abbec9def9aedd60a4ebfa",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/CompositeVideoPrimaryInfo.ts": "9568102d033a1d42b3eb8196f80b64d631cb856b7c49a11d7fa2e61b81490151",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ConfirmDialog.ts": "6294c06165cfc99cf54b7e8e979d9b5159e6663fed2649c590ffb5e07a3abf0c",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ContentMetadataView.ts": "91d3f66082fd507a9e79f8d0fc21530fc27e8522ad649fc24c35742927efc671",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ContentPreviewImageView.ts": "e4da54a4fea4525900d28eb7080fa436e0a2baaddd606102c6b2e2a2ffd45a25",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ContinuationItem.ts": "f6f2a06ada2632dbde7c68cb20278c4117a9215041427f713728a22491ae23f6",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ConversationBar.ts": "2e56f0d3579a980a886a9a41ea14e3bb30e08a33291ed9f3a15721f4a2366008",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/CopyLink.ts": "8107ad4c58f932083e535836776eefd9d013713fa2eb7ab371e5226dec5aa77e",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/CreatePlaylistDialog.ts": "701377af34472057d5a186a3e5a81ebb05a4302dc2be505802dc9891afc3c4ea",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/CreatePlaylistDialogFormView.ts": "8f2a2ad363bd0ed7b8d7ddf3c642f646db0c16cc789df73ec0cf76f25fc63184",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/DecoratedAvatarView.ts": "bb7e384855ffb53fc32db5de9f9205353a865398be3f1b7896122397a29b81e3",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/DecoratedPlayerBar.ts": "35e2d82bb8f963186555ef7f3ca1af6f91f0516486f85aa57d0a196abf43c6bc",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/DefaultPromoPanel.ts": "9f8634c0a96780f41ef1f9a3dd3c759bc4259689f6db17e0250b2f781eea7b6c",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/DescriptionPreviewView.ts": "15c7dfdba75962a0ce3ce25b9f0756e63d7378a4939426f0d1c0c06e1189b108",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/DialogHeaderView.ts": "e9b837eba94f9be4607127c3f1c61e60421a2554849d19f625646c842c90ac10",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/DialogView.ts": "a6b3a835bb0b2040c009e4eaef99166356ac7c79d51b6d69ae0b2e808754b141",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/DidYouMean.ts": "5308ce98e58b582a3c66dcf126a4768560f37392461f2af4d9fed7c234ec4293",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/DislikeButtonView.ts": "2442308846dbea9951ba058a3a0d94f35e5b82ab72427cbb0588460de35f3bb0",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/DismissableDialog.ts": "c2dfe0ba877cf77c8450856097b6f41da96a80bb270c1882c51639ec5d02c2a4",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/DismissableDialogContentSection.ts": "52a40f67d199cf09579b548662cce9bb64aa8dab60118a359229e086787ce9f4",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/DownloadButton.ts": "626215d98c883ff413f360bc5a73886bd82448f12112bb7d93d4a0d97e5fe5f2",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/Dropdown.ts": "629b2c3fa8be96687da953600f716ea2c43e496ec4e3eac43823b9820725131b",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/DropdownItem.ts": "e8a2e1271585e9ff92d786805d0a5f2a04df61a3ed79305f4d671837cfcc3fbf",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/DropdownView.ts": "de15b2da121fbe20d5a1ab8d6cdea98df8d3f43702452215d86f1e5af3dc6fb6",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/DynamicTextView.ts": "d4b17d7f4a9177dcee184175f9efb2ff0fa5ec2d4fdd6a6aae8036fee54614af",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/Element.ts": "9d70e34c3eac0d0ed56d606af2b36ca753cfbc8773907a2531d096797e2cd5c5",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/EmergencyOnebox.ts": "ccbdca9bde02258204d31cb2d42fbb41c681a9eab333c50ac4d32ee5e941ac4c",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/EmojiPickerCategory.ts": "1e8bf2dd4efa0f31ee48856fd89345739eb467b37e3b5427338a489e9a61f051",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/EmojiPickerCategoryButton.ts": "5ac0f99885ea9271ef697ca7f473f92e6c3cb62708237cd63a8beb1fb8ee1b45",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/EmojiPickerUpsellCategory.ts": "c8733c75bdb0b4b882cd75cb3d8761914a467ce22204b2b201a9d010db36aed8",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/EndScreenPlaylist.ts": "03d94d438a12217d9825b3b701190429d6d55517600681db70bb2756a7cc70ae",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/EndScreenVideo.ts": "825d14928f8b8fb2a3f50129d2b09c2017389d10f87252ef2f825f3fe87b52c1",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/Endscreen.ts": "eb032970230d659bd7a9dc5f67ce22634e3eacfcebab77532b87dc16208caad8",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/EndscreenElement.ts": "7f818d9bf05f71e9146eab5a920ed7d0b5289d9340d5f18f826d9a9c3d049dfd",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/EngagementPanelSectionList.ts": "1a427eda3d67c7d8a92e76de23623c0a7279be84b52c578d5efd4867afbfbed3",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/EngagementPanelTitleHeader.ts": "bf926834cd5b88cba79563e1bc91281c12c5480d1a02d76102dc77c0b0768ff7",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/EomSettingsDisclaimer.ts": "382c4c3636bf0ddc3d21418c2e4258fa42c57f3a6163b8a6b58fe5d2737554bc",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ExpandableMetadata.ts": "4694e05eee544657f82373c333a31380ecaa565878cad267cab88eeba50cce03",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ExpandableTab.ts": "0a5c20c83ba4caafb94143bbd642aa15e54dd6901b4a9697bb0b89349cbe22f8",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ExpandableVideoDescriptionBody.ts": "7f4319878bac8cfe124b37501a478dd291fdf9c1fa5aedf56c6d532261cc5faf",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ExpandedShelfContents.ts": "70158f2bbeccde3c9391c1775eaa72bda650e612aac483e7ac378ff789fd5f40",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/Factoid.ts": "10e575dec4ee77d1bbbb1773f884eff18345b19f2d37c461d4bddfbbb006904b",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/FancyDismissibleDialog.ts": "532d40d8a4eab04e71fe039ac5266c473b6dce82b2bd051fb1afe8fbe3fe1d19",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/FeedFilterChipBar.ts": "9b1e137d49d921e47e03510b983df85bc41cd8fc70ef9ff6262676b75061c9ad",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/FeedNudge.ts": "d403105fc57b192c86286ea12e70d9534dd7673e43401f690be8c2af3c6f8afd",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/FeedTabbedHeader.ts": "94883a1832a3ecf8bb2694da1485d428fcae844075df71c7e6876bfded2b2fae",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/FlexibleActionsView.ts": "c9387583935e5e83eeae15b45ae833d4ad1839ba38e0ae11455a34088452f52e",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/Form.ts": "13495b764dfea51be19e67f859beeca9251409b920da650f47bd1678381332af",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/FormFooterView.ts": "4d4191dbed740fec50ddf99cf82c7191e3eca701d4879339328fad02927cddda",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/FormPopup.ts": "0acaff45c42727ddafb1bdcec73c37576eea675ee895425156cd1b117dc36fcc",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/GameCard.ts": "f26e1b085a3ffba41649242299ec399b159d693e78c8323dea9c1d4b9e820b07",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/GameDetails.ts": "4fe76f47c0f703a54b134fa53d8172b12222c18519011ac74c790c4e4ca2ea90",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/Grid.ts": "3079a3b9979f91396fefe3d60de1bd5531fb1976f128fb3fd8636b89f5a1581e",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/GridChannel.ts": "087979fac219af5c1fa8311551f9e185274caf413442a269287848410536ff9e",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/GridHeader.ts": "cde855d3a46427c9f25a7a862e6d1e17e41f65a8edd8e345cd13c79e739a3779",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/GridMix.ts": "2784966f9213937254159e2e2dabbd4382d3ed5b2ac9c8de16c1a202700d3cea",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/GridMovie.ts": "4c26f9e0aafdb97531ec5241c601a0b20cc8f95197b4709d9b5fcc9c633416b1",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/GridPlaylist.ts": "268ab90959525f6226a4a7c33293202169af947f670a68006780687acfa34d51",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/GridShelfView.ts": "47ba089207569e936d63d156fc75f097b8c11575b9d5c405d7ec7f8b52ed46b7",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/GridShow.ts": "6200fe2a21403ab1ed5554fb9af21ff258ada2318f29cb7ddb897ac4ce588971",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/GridVideo.ts": "05a43570137ccc6a9c83ca6753e883971ffbb7b5c8eaec74ab4828fac3ca2cfd",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/GuideCollapsibleEntry.ts": "6898c1aa7dee2b9403dc818e0af4cbd5f11a97d7493931c1673e8afdd4a41e49",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/GuideCollapsibleSectionEntry.ts": "a71b258a5f7479b3c41494f1b02e8bcab804146c5048028d9775873afab4380e",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/GuideDownloadsEntry.ts": "1b53dd6b41ca9d78788c74c896ed1375ea9b55c24d366e6a371a81bd5137ac22",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/GuideEntry.ts": "a0d8caecf5941882a20e4ea792e3483b97ad59b17c7f7b9e9b0d304c6ea58a6b",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/GuideSection.ts": "cdf4f493e16c64365c7272f34c3122c99e3cf782adfc19396119002c142143af",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/GuideSubscriptionsSection.ts": "796eb3a997dcde33e0321bbe593cfe4069ca411cc0e4e31d15beecac518240ea",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/HashtagHeader.ts": "d0044124e6532d0fc0d11f77041639b33a3166096abea360f347382bf2bb2bc5",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/HashtagTile.ts": "9b45df9a3d1659dfca9afd3ebeeec33384d51f143f4b42fec18c588bf4abac32",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/HeatMarker.ts": "fe898140319d4ff66f6581306312c2fa7c913173651f89057054f63b9d9f37fe",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/Heatmap.ts": "ab08f9ad9f85d8dc464ec2edae32e4a53ceb40c2b8a94bddd9d96f59389cf759",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/HeroPlaylistThumbnail.ts": "9b9420bd31f5585a46f0cb1fc50c252dd5e8806236df9fbb3788323fb00c9146",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/HighlightsCarousel.ts": "422a74b2661f37cd135162a23e12eac8366af21192bf2435878f3809faa10c92",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/HistorySuggestion.ts": "58167e2cd26403a3262e3a800217f593a5dc134d22ba56ed5e2869e4e172fd0d",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/HorizontalCardList.ts": "1f46e177d6dceea2d76c7718bc6e95767e5265a204bc33031b321a2f42ebc044",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/HorizontalList.ts": "0cdf416e04ee0d1e2f5b47281ebee779d40db7a7834769dc4c4a001a52e5a722",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/HorizontalMovieList.ts": "d665e1b8e581a35eb468b6bb7da29e2f6be8dfcd8ad0e94f479be2b5c95410d0",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/HowThisWasMadeSectionView.ts": "b3755a41ba6842667b781f47f70b3bb8baf010381fc41005f77bcb38a07d7a7b",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/HypePointsFactoid.ts": "816bc2c452e42e7a3eb08af447f5609be4b786793184c0c0b5e24a6abbf1d0b1",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/IconLink.ts": "cd28bc7d49261a1a32cb487cdcb7793cde54d0def660f854ae2f1dbf90a02142",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ImageBannerView.ts": "b0e207b2af2cea727d0a7e6e585d8b2f21fc6c1ea0c57c86d1369ef64681d494",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/IncludingResultsFor.ts": "c87f357d7893309940f9464cd957560c6b8f10bc786a786e582d5d6912a15e9a",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/InfoPanelContainer.ts": "e7f4ce71b83ca597bc11d271e6c2d660c93be1635ea4d558930c8451b2b240bd",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/InfoPanelContent.ts": "20de88f6e063c8a46026843b6fc4c3e2cfaf6665d2e06a8516f5af0488f73a03",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/InfoRow.ts": "ff209cf7f39b77977c2c6533b6251159a49b5f611e0c9cc6f78820991635ba1c",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/InteractiveTabbedHeader.ts": "f0172b719a2fd78723f4b20138086e3a55a07eb8b191b43a605ac2e0d4c3a6c6",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ItemSection.ts": "5825fb36d6891f71ba5febc3fb74fcdc95e98a3906bc591952cae834d3d7dbf9",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ItemSectionHeader.ts": "cdf8ceab39376cae9cde4c4bc22799e6111dd0b3bb6c87f0ae546db824a3bb6b",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ItemSectionTab.ts": "f86071543a11f2732a7d9d28689818445f5326fc176e674f252d75a927496c13",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ItemSectionTabbedHeader.ts": "8b497bc42bb577ba2b30617f862ac7f373cca8cca93eb5650d9fd30595182676",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/LikeButton.ts": "8ce35e04195d331f0cfd91a9802ef67faa402b82f3e7cb58e7fb80a6ee486ce7",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/LikeButtonView.ts": "6925a9427f918f4119e28f43cff021b248693c37ed2690709d18c169d4f3ffec",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ListItemView.ts": "e7c677a4d61ec20abb53c5396652b0efe11502092e9bff355640b2ecb69617b6",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ListView.ts": "54fc990768a589fe43918fadf8caab9a9706c4f24a9a350b6b147bdb0d08b345",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/LiveChat.ts": "be9e95b7113097bfac20f3b679d3fca2fccbdfdab79f0fd640e270bc760148be",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/LiveChatAuthorBadge.ts": "b92b6966f5c0358ff583460b6f41e1402be89f1f1c2088681dec71172227d331",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/LiveChatDialog.ts": "24cf510e6187f071d597b8d0503aea4ebb18146e0f6c7653acf4f3eae1a554e4",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/LiveChatHeader.ts": "f8d28a0301f5e14ba60e33cfd57e58426236c50ba7cb44992314dc693202dddd",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/LiveChatItemList.ts": "99ea5412e20273114580528722f9f599a1ac76e6b4a1376d3c9c91a72b81cc53",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/LiveChatMessageInput.ts": "1c197ed35d386dc6341550022f22bcd268374032ff578f59cfb02d209aa53001",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/LiveChatParticipant.ts": "b199264fa36cbe09e9e914c58b3fb6ae6fb73272dfaa15284eef4cc672d07bca",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/LiveChatParticipantsList.ts": "945d064cfc1f1243a284fdbbcd425686918b1c9df6554d865a4f67bbcd344971",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/LockupMetadataView.ts": "09d67c16cfc53a73c3107a01ad591de410f6fb1a6b4c1726f5e139b0838e5bf8",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/LockupView.ts": "ebaf40d191102aaec5056735ebb783c98127b78f40dade548a0cfeddfb87d4cb",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MacroMarkersInfoItem.ts": "0989b7359972f8ea89f5c189b229465839dc226f692c4d2f0d1c47eea5f9e9b4",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MacroMarkersList.ts": "f3d69fd2eaa6daa0a4dc1179f1ed174c02f3dfdbf270758a52d23cf9a25d1f70",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MacroMarkersListEntity.ts": "29ecc650ededb5f284d6fc6b7bd4aca55f5caed4fb9756794cb09e975981c802",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MacroMarkersListItem.ts": "c7e06e91ad0daf880d269cf3118ae2513cea32d96837f5f34e9f4627f5edaf6e",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MenuTitle.ts": "5b658366326734a0add1369a7352363b402ca3d5b9e00b7c9ea26105d3feb4c6",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MerchandiseItem.ts": "24e98fa453ed1a9d2a645c70ee0483c2f049813146e91033726362de06963909",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MerchandiseShelf.ts": "bbe89ca0442e37a173f010cc519e2aae79a9430b84ea742f0ec039537d265f5e",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/Message.ts": "ae02e16b3fe5d9db83aba15ac81a5b217179f4cf6d68ef0910f811f25e2c8530",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MetadataBadge.ts": "1264beec3c7c90fc58bada6c567860465d651936df7d115127e32dc10e111832",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MetadataRow.ts": "c141bea7d47205bfad60ec28dbaf2f1865b2798661fab4365f34502fc721f864",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MetadataRowContainer.ts": "6f64eed25739ed2eaf6c76086d398d966783bdd705f55c7254a6f4be739735b6",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MetadataRowHeader.ts": "74f3d921b8bb5371be4e0a8b06060273c6b738c7cb4fdcb642f1032ba1f394d0",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MetadataScreen.ts": "e19dd2a4f189c14fe0c0cb0460f883726aba2ba074ace5ce82618570def249f1",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MicroformatData.ts": "d83da608b5080fade38279253eb663c9129adbd93bac2ca992aaf358b6f227b5",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/Mix.ts": "a4f006e68ce012c165690274743d07cf7c6886a7d225416c02689ae95c327004",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ModalWithTitleAndButton.ts": "94740289356c3897998e82001fa5c9673536cf12c34ad30e99fb53f5d154ce6f",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/Movie.ts": "ece3a9432550f36c2313491861fbad29a975ee4d43604b7726a6aaf96df82804",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MovingThumbnail.ts": "4ad2c4b947e637a73fdca56e903155c7fa191de2eb9fca30d94098d14be17c71",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MultiMarkersPlayerBar.ts": "aa70c654ef101bbbc72a0a856a0a4fba974611eef2c64b659f8e32c7b2f4717b",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicCardShelf.ts": "de4dcfa7b2ca8ef91586c0b3aaa7fdb2e87735135156c0689d4df56f995a67f4",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicCardShelfHeaderBasic.ts": "2b19859ce41700af922d36343be6a42420308a9e4d25149e23cb52e8507b5278",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicCarouselShelf.ts": "65ff30d0933095f36e515378e9ad0e8485e9a718990e2ddb0843b952b2f35c64",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicCarouselShelfBasicHeader.ts": "bf51fda17ad4fc2e8a234df181f9931318d47b4fbd01f747f4340779e812de07",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicDescriptionShelf.ts": "734f385b0a89b4e3cc65305588db2560dbbbdc67f9b6c23374dc3b8b8f16d3ec",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicDetailHeader.ts": "ba66f0f987fd5dbe159434f78a2a713b821d3c3ace1d6d9b8aefd79bf8bedb44",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicDownloadStateBadge.ts": "db5f732e96805652b8add14e228e649e74264540bb166c87ed6c97081a23ee38",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicEditablePlaylistDetailHeader.ts": "fd73a48db32308d12c1822018b6f6ceecbba47a3a82e3b36c4ef23ec095fbc7a",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicElementHeader.ts": "8cd29f81d41e1890916da8bb23db6a2f0fb83fb5174661450bf30b1653921936",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicHeader.ts": "51e8255ee23f683b24b8f33785b6f86e063e23d29debb52a9174b256454b5f22",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicImmersiveHeader.ts": "edd9961b8e0d54f54e891f1d4123411f5c6588e8e296b16da98867c4004e6de8",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicInlineBadge.ts": "9d3c9c9e279a5a40010090398d7bf0a3fb202c10ad2eac1c36030a742b37b5c5",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicItemThumbnailOverlay.ts": "a025507930943b74ac2182ba76db1452a6cf6f181994dd70c985e93a75a86cde",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicLargeCardItemCarousel.ts": "ef325d7134b17b21bb79e5bd11532fe1de902bd594012c04a7231ef7f2ed0c5b",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicMultiRowListItem.ts": "c990dd31b7f61148bedb6bc8a4704eeb3faeaba222e1b274eac185989cc7acb6",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicNavigationButton.ts": "9574afabcf79b980c606ef117bdaf673fd1dc22b2e0ab5b2459c786b96844445",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicPlayButton.ts": "0c6cbd8c78dacacee70f94622a40fcc266242858a1594cba31ec1ce21b29850f",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicPlaylistEditHeader.ts": "4d7a324b4d38ae30dca3f1d4915222271e307d191a98eb16587b2d6b9d80c75a",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicPlaylistShelf.ts": "c019df8605f630ba50382b42ac426b04eda45b15f72a8e40c9d63c600202f7dd",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicQueue.ts": "12f1178a9df779b4d754294be22ed5fa8795ed872fd254bacebcdd6acc017c62",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicResponsiveHeader.ts": "8e3da0cff688397c3613301a3ff69070f643e7565ec9eb18299c74be207c5efa",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicResponsiveListItem.ts": "8b61d03770d722f3be9676dcbc142d2cca5831690dda866c8cd6459ed869630d",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicResponsiveListItemFixedColumn.ts": "efc38b777ff0c2f4013cedf2873d20dea843ca58f5057a9146952a06cc288a8a",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicResponsiveListItemFlexColumn.ts": "b76ede69a9241020468b738e28f59dbff193aab6fd5c86486d8d1c62651d9463",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicShelf.ts": "e05e2bb56da1e9ac0b826f608fc1289dfb28e4dad5eda72b43e3297835547958",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicSideAlignedItem.ts": "9afb8fbc0879edc886398a622ee7bc39e49c646c1f9cadb95dd625eef979e86e",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicSortFilterButton.ts": "03a65c739950919813b5e59aedb5d6114287c069bad7388b749d6d54e3187e44",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicTastebuilderShelf.ts": "58a17ad8a95df12317504c55ac73781f2ccfcb0187dbc45dc927661eeb6fb5b3",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicTastebuilderShelfThumbnail.ts": "10dbaa0f9d5fe1867fde28e04d99f6191b836627ad28fc10ff2c4b2e4f47a7cd",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicThumbnail.ts": "41f8672b0537454e12f54cc13505dd1e6cf41208784fe0773f78ed7cd540b936",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicTwoRowItem.ts": "a4febe5f1d10eea35a6770b7450d53d9b93cd7dc68367d77c6c3381bb2ed28dc",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/MusicVisualHeader.ts": "a723f84db6a7c067ec743525411954711e6f10327738086ed5f7bf69ff1e8a42",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/NavigationEndpoint.ts": "6f98ea6b532091b8260581dc460674cad707335dc74342a50c3e0a00d0aa90b2",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/Notification.ts": "1d707d5e4f885710cfe42f8d1367dabc9015e8eac9789e42bb8677927510ca36",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/NotificationAction.ts": "9786dc1516ac960217de4648278e0c1546d0d575339632d2b17428c7ec26f731",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/OpenOnePickAddVideoModalCommand.ts": "ef7742a48b137f2b0bde3ccd9910fa756259c2f99076dfb38244cc0292cb2e14",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PageHeader.ts": "f74cb411d58e4150cf1b3f4f041d2373088bd09b2d3f4538e87bfe601a1d9d20",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PageHeaderView.ts": "0cf04c56c32c908e301cef5f384e232b4461f473f8805b02e5ae30781deea140",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PageIntroduction.ts": "3f14d6da7dda25c1d2d25ed183e2fdbb2bffbae150dc3e6a410a0bb6b47ac5cf",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PanelFooterView.ts": "fa6e769ccaf0b548fb1ef8b36775baa968797d385286971c151cb9b7c97fced7",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PivotButton.ts": "ce77f9a6c86c46a00aff9cedf16499a928e33d5dd6e072f289e48c25bb52e788",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PlayerAnnotationsExpanded.ts": "26726ef10a00ed8a1b1b149facd85448d971c7fc6264bfd663a024ebb6dccf1f",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PlayerCaptionsTracklist.ts": "7eb93326a2f63726301566647bb146eed72c1d1da81d5898a5de484175a3f711",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PlayerControlsOverlay.ts": "2ae7c6fe92fc803ea278aaabadd16f77875292d4e23d0f449d803cd3fe2ec30b",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PlayerErrorMessage.ts": "bb310ec338fba8d1b04ec5c71589b38858bc91696846b116008bb83426ee03cd",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PlayerLegacyDesktopYpcOffer.ts": "a65345864fc6c01b824127f652bc81da89a8e1538c92fcbc48ecdfbb895b541f",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PlayerLegacyDesktopYpcTrailer.ts": "113fef458818143c3451e464278410e979671d2e94731dc75f9c95beb739d2fb",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PlayerLiveStoryboardSpec.ts": "148eaa29dee8371121a55380aa58b17ac5cadbca31f43dabc7d67fd851919e02",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PlayerMicroformat.ts": "f01c5d396fd6c1c3dcefa9d5bdc68ad80453ac0a5d44ddec87ffa54d9484e393",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PlayerOverflow.ts": "80bf205e5c1350247b8bd373dd80b45aee66905106b3bdcdde7ae68a20eca9b1",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PlayerOverlay.ts": "fd71782dd25fcda78fac5a54cbea5191024feb98f927100a8f9afb5ac6f1d3cd",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PlayerOverlayAutoplay.ts": "b0d7a84f016fdc01d439806901e44cf5857d9a26901090fcbad31f4ec8181224",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PlayerOverlayVideoDetails.ts": "76fbc275da6ef8b33a66804e0ef9f274c818c694c12124737ef3606942e94837",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PlayerStoryboardSpec.ts": "22b756b9e0f5da75f5664d5bdb051bd8dad3480d1156f4f024bbe80fdf5d34c2",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/Playlist.ts": "102b1bcb02ddc70dbd1d248813e4a67c070df16d24a9972b92bfdc0e471cc60e",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PlaylistAddToOption.ts": "110bba848733a13ce259098e8f5e8a2c6058f5ef9df7723d5e38bf0a12f15502",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PlaylistCustomThumbnail.ts": "0a899be5e805f4284daa22e9b1a5080029ceb10935091a8bc42e32985a52450c",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PlaylistHeader.ts": "25efbb26a756c9b394f84f4fe8d8362792fbde37c386d368e80794b116c58d2b",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PlaylistInfoCardContent.ts": "8a0df917c39970e63ce8cb35c0749096be09e8a6d52028b1eb5479bfccd93425",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PlaylistMetadata.ts": "4e17156fab0ad3dc9187b7f31fe633d2adf921f63d717de8b290fa7649d0197a",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PlaylistPanel.ts": "c13dd7993ed1a85487ec4ec3fd998a6b81c7c2a83105a26e5d3d3c2ad21c37cc",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PlaylistPanelVideo.ts": "73d8901e1a9a1fe6391b3bc1c26371c3c36b31bfdf0c8d06ddcc201357e8d116",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PlaylistPanelVideoWrapper.ts": "d20370aee1c0c30b1303c8169cc452d94b50367b9822935d18bfc7d1644924cf",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PlaylistSidebar.ts": "1f101354305065f7aeeebbe0b4c31b6db16caebeb4d83bf2e4d013a6c6a9abd8",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PlaylistSidebarPrimaryInfo.ts": "6a7bff8ca434514c8c83e400930f3c86212877175a1931d1ef278aaf81bebaf4",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PlaylistSidebarSecondaryInfo.ts": "80f7b2bcce12d5ff94650a376ab94055ef45b39580a5b29bff6e283108f608b5",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PlaylistThumbnailOverlay.ts": "014b95c46c1ffbafb714413f879c3ad71c679941db7c948ddcd4cfcbc6551527",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PlaylistVideo.ts": "9a7d42c52075807a6c5a81c7d943c5742d6bf85613933ce0de162f5bcc93946a",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PlaylistVideoList.ts": "6866ed2ed241b399e5948f0109b0c068fca10b138284e075e4790146aced9971",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PlaylistVideoThumbnail.ts": "b31d50a0301cf49033b8b28858d787bbbb46cce517a0738e7772692c8e71ee1e",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/Poll.ts": "2f799883f6439367b14b5c83b7f773a6b0501a9b2e00220c58d0d145f2970fc5",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/Post.ts": "9879703fe56af733aaafb25c708f5d294c65bad75a2f7b30f34c21463868821e",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PostMultiImage.ts": "a7d537fc18346de30f0aebd9f8f49c9ebc793d4376ea11683aeef62eb7388707",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/PremiereTrailerBadge.ts": "41aee386197738594181d4866d64ef31a03ecd719b0bc4456441a8f9287da5e0",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ProductList.ts": "6224bbad1d145c4a0a20817881d0e06ebf1be0c240815971ef88ed2013eecd88",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ProductListHeader.ts": "0ccb73d9ccb7f71cfd46dbf9992ff3a8c2498b3cc16e0e452fabe65d3096bd01",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ProductListItem.ts": "2316148fcfc380b9714371bbd9e814180964fefbac282d3df9b70985b0b228e7",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ProfileColumn.ts": "1b88dd2b802298e68f115a4e3fdb7b5036be844da2116aab8d9d33152fb54a1f",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ProfileColumnStats.ts": "08984b8e624f4ba9379b25cc8ceb67b37f62a27e88c0cba7e8ff1f89f670270b",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ProfileColumnStatsEntry.ts": "eccc0b4115232b24b1e5cdb9563118acd5f0c450095f8286b29988756449c46b",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ProfileColumnUserInfo.ts": "e8366b0a51984818befd7b5827d10695aa66bcc6e5d80cec79637fa1b102cda6",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/Quiz.ts": "0e7828c8646c1112f94a0a2285c0e8662c6abb99c83b43d9c15bda51a699e7d8",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/RecognitionShelf.ts": "b9f8a431d304dddaba5a3485268bb0a283341849aa679056e1eeca6597bdfc20",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ReelItem.ts": "8ccb6a4bdefcb09a9994723d72b0c612e40e276e8f2dcd00fef426e021c4e828",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ReelPlayerHeader.ts": "ecf5d651cad256231c89032279d245ab29e8354442fa2042c9d4603ff7efc85b",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ReelPlayerOverlay.ts": "ce0540053bf25d9a511bc64b1c72675d345b13a18b6ccf9d172153d2808a4f4d",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ReelShelf.ts": "2faeb7548db719a32b3edd36601ac76dca6390ca75ef81db67f54694735da8dc",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/RelatedChipCloud.ts": "c8cafb03cc4d06fdc785160aa5427595c035efd2232290c36330736461390e99",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/RichGrid.ts": "819ac833d324eee14e36fea27adfb06a65d8b0ea2472329a84d4ef36cb6967e7",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/RichItem.ts": "602f53042e99a2ab0bcf28e329e0bf0494c01ebaafbafcf4df29c0ab1d1b04eb",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/RichListHeader.ts": "8e4e1cf7544588703c88b8a4d60142032282833b52e4fe9c4f3a4b4167bb8f03",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/RichMetadata.ts": "3bb69be43c5c598f495b1b67196eaca9e14c5d0854e910dd2c59f014322fc915",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/RichMetadataRow.ts": "542b096aee5c002af9d737044c4cfd5e707ae6672c4e9a221653e79b53517846",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/RichSection.ts": "b39ceb1692ece839697b66cc3159e55338b2b5572ef5aefa7aa4fdbdee83a177",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/RichShelf.ts": "16ad1f3074f4478e522ef4d1368ea67cf54db4de8404c1ed48ffe371c9ccd857",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SearchBox.ts": "9f802ee06bd0069c7523191463eed271a57dd5a3b8c968993642b4b1fa2aad41",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SearchFilter.ts": "addddc02fe7cfad3b44169c8ac27ae994dc279473336afe0f077a4c3b25563af",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SearchFilterGroup.ts": "54dc3fd71a207157426f664e8393ef0b26af6c2686617f499169af989fe9850d",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SearchFilterOptionsDialog.ts": "614e27d107f52ecc3a5e8254e7797e33be4561898dfd0e8a7517c16571370024",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SearchHeader.ts": "8e87372f2e4523ed42d9493979ac30df5145e4ca3e19e1501160e87f621d95db",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SearchRefinementCard.ts": "a882c437b84fb1ca9b353742e67bff73f1598b7120dad9f286573d43c7eff756",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SearchSubMenu.ts": "713e7c7b59e10f236eec28aacc64a77cf01597e601797ce55d4bb718a2ecfed5",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SearchSuggestion.ts": "b825338fdc840b17d4d7c2d9134af32226740f659f9281a2f619eef7e8ba3179",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SearchSuggestionsSection.ts": "44ef7b0526cb8d95855506d60ad2b73a8039eff520383fe0c4617dcad6cff901",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SecondarySearchContainer.ts": "06c78d2f73b8321c616a8e1e619f522bf70a830c11a5a8ca77dd9254ca763e6e",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SectionHeaderView.ts": "7d12f8253924f69849bfd8b072f5354732a275a092fd2e7d3d71756413d0aa31",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SectionList.ts": "e8b0875ddcfd5ac1899afac88b86b362c75ff551812f55dc4c7209680b64f8de",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SegmentedLikeDislikeButton.ts": "5ced84a3b23db6e723f4a782548c2515d87d850d5e74289296a09a6856571f77",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SegmentedLikeDislikeButtonView.ts": "32d1a88d4b7b0c938293d097ad337698a94fdc98a2f465a335abbc5d9c4ebc78",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SettingBoolean.ts": "0b671916bd6d7610bffc4a0ba58f1f5e0181504cca289b6b2218e174a61dbaa3",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SettingsCheckbox.ts": "03618719909cf23884311ff249dc4527a90577e73cde96a5290d2109ea4d4d05",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SettingsOptions.ts": "2f624036599c0ab498ccd09b72effe1043564c693a43c6b25b058d7cd04b1fc2",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SettingsSidebar.ts": "d86be1b7350d64d9a983c239cd62778b261dae9f0fc97caef5928065a7bba60f",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SettingsSwitch.ts": "e5fcc8c4d7432a684b10df3c7c06a5086e6a611f50178c3cfd8a3e4cb305131a",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SharePanelHeader.ts": "679ef7782a5a4d8bc98d73547de414b0d89d492fec5066c07ee3ae0450fbc58d",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SharePanelTitleV15.ts": "1e90e0497bad34b6aef419a5cc632a635f103d99fab9e3f2ca79374bcdf8da1d",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ShareTarget.ts": "1231470ec4c92ee7e3d1e2160b3af99c33a6d56982def0ad9642251fbc87a71c",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SharedPost.ts": "1224d7529b48e02a654e4bbe8f87ef1727a8e253faecc1abcfb2db4e771c44b0",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/Shelf.ts": "7acb0f92ea8ef7e08fa4d0569e3994ea452e289067c021efb9ae45fb6f928da9",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ShortsLockupView.ts": "10deeefa7b778c8c248842f1ad7150df4c45b887ac05994ecd2a808e74ff43c3",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ShowCustomThumbnail.ts": "db8dc3df7968f4972a7f36821777f24d28275ba1e0286815f77f68556e09588e",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ShowingResultsFor.ts": "35d508ab827041eec917747d4ee311b890b82c79ce938397babf9788b7b53129",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SimpleCardContent.ts": "af567487943796a1aed6ff33217412a9cac294b52c94573c224766e1f924b379",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SimpleCardTeaser.ts": "7c147b803e88db19b6c4636195b20d42547078501134ec38d2da2752b26c131f",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SimpleTextSection.ts": "1325d00021520830517d00137531e2d82a00ceda28b8bd14804b63edf982d020",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SingleActionEmergencySupport.ts": "57875038e8a642a260d767c0ffc744b4d71cf987041561a42a66417a978a1bfe",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SingleColumnBrowseResults.ts": "bade331e15a27389e1d28d5d6b45ba4ac862a1b7f7a31340e76481c1a504d8a9",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SingleColumnMusicWatchNextResults.ts": "3e685b373eb3a3815464e1dd3c37a0ca12abd27b20b79a613f489be433bfa800",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SingleHeroImage.ts": "c26cb722b9d1e38aa48801e1ecb320aea4018facb1358ae45412d10a1cad9a44",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SlimOwner.ts": "c50622add68c5ae4c8a5f068a8f95ba6fc04af385e6c797fc75bff9df4061716",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SlimVideoMetadata.ts": "7844310fa41a71293ba68d75eaf005eb8b63e555eb9c11808ede2eb1a964fe54",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SortFilterHeader.ts": "65e5b9a0a08d91b00c5a0b00eee5390fe4fef47936474a9d32daa202dc3c8113",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SortFilterSubMenu.ts": "701c6e5643968c508a41986e5361cc703375cb4c27076c4c414d8b73fdf7e7a0",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/StartAt.ts": "4332ca51758bf0d69274c8cdbee3d26c9a38f53c35ba84ba6d9c339434bc6222",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/StructuredDescriptionContent.ts": "ec691ca20cd81774e42aa34570b9420a12b65fa4451752200b369732173b6459",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/StructuredDescriptionPlaylistLockup.ts": "6448040a252952a1982f7049d362ea039401369ad3954d6970bed87943038f49",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SubFeedOption.ts": "ca0bcedcc1ade07498babe61ee95c818fb594427113535ee4ab39e6bffde8162",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SubFeedSelector.ts": "4d83771c0c25e9c9900045ad10ab73db347c08cbede0a2b9d0b90be39113fd7b",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SubscribeButton.ts": "50cbf0bed71a1b4bf866cbc468066c5e8835dad2f9403c5f813e5881c8a8ec77",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SubscribeButtonView.ts": "82d58331b66bb2ba88ca2244811cc947a44337df56f0c439fdd2a660676319fd",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/SubscriptionNotificationToggleButton.ts": "686981908107cb2f09fe1a06e2dc68b037c5424e569c9d1b2920c9e37d1fa687",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/Tab.ts": "556f323e76af4f9671c4412e793b768414df02acf11150f4b10adba2ac1897c9",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/Tabbed.ts": "6a6c9bb20e05ff2f808fdfbf2492b9daad39afeacc537654edb1f68c920bcaa7",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/TabbedSearchResults.ts": "1804aece810964a759f1b6c4451a16065017d2c68a71a71b5f6d2aef6823dc5d",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/TextCarouselItemView.ts": "c67d71008bc1978902a61da0037abed8f1b208442fa88f6e9adb660a9fcd707d",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/TextFieldView.ts": "4277a3cb272278926d44d7d4b87b02ff23d7dfdc2c0786697f7847572623a387",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/TextHeader.ts": "fe7e755d2cbb8b3d67e2979910d9d8b5135d155066d0051f59d7f2c8ac595d0f",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ThirdPartyShareTargetSection.ts": "f0d8249bcdac213b300365bd8d7bbe06684948a6ee38a3e78712a28e83e107aa",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ThumbnailBadgeView.ts": "4e3340bb09a13e684d9e9ba71d7c9a720cf77ab2e775771e9a1771c8e0c74b61",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ThumbnailBottomOverlayView.ts": "abf252051036aa2ec25a29b8aefa1334401342c64728218e7569c65f759424dc",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ThumbnailHoverOverlayToggleActionsView.ts": "f2088804de700c02b6a91908af3ae7c17e164b5afebf1cce207aad0bc7745988",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ThumbnailHoverOverlayView.ts": "fc26c6fc7b80291ca77a1e86be50f6657b02daff38c2913960a9f41bfda7a5b8",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ThumbnailLandscapePortrait.ts": "b0f052857e53d3d73c9d29627dfb5b40828db0dc0aff574e5012cb95af2ace5f",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ThumbnailOverlayBadgeView.ts": "b14dc047f695a769553ac93355257796fb575697c0a263b284cc33a23ea41e66",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ThumbnailOverlayBottomPanel.ts": "539ad58535c9d7ff641c3db289a9b6755ef6e5a1c89dd460b83ff2d75df8520a",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ThumbnailOverlayEndorsement.ts": "5151e5a6b29fc62117fc5cba9232f59796ddfcc96728b82c0765ada5ba206fda",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ThumbnailOverlayHoverText.ts": "80aa2ce46a7589766cd38d1b145368cc0867fe8527f0b299eb1d03f68e827147",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ThumbnailOverlayInlineUnplayable.ts": "d530bc3aff19d452ba5ff375c1a5bf5e6e9ce97f813ecf9194ae3807cb28e566",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ThumbnailOverlayLoadingPreview.ts": "b157a91f111365e83e0750c62015670b7016e6e8ff47393d384a56defad0829e",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ThumbnailOverlayNowPlaying.ts": "8d249d8702bead9c7ef3ff8c7f1e894c55b2af91bd7a8c650f24b021c611552a",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ThumbnailOverlayPinking.ts": "938e503857c9e1880dfce837be592cc52f7ea6c01fa84e20a105fcac1fa3d2e0",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ThumbnailOverlayPlaybackStatus.ts": "8407bfb4ac49c67c23c9bc0d5eb098a760c88d1f0db24fa1d30dd97044592ccb",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ThumbnailOverlayProgressBarView.ts": "93c5c7b1aa26b8e5f7ca097b265d9a096999a181c51bc21ba00c4a3bd9a755e0",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ThumbnailOverlayResumePlayback.ts": "a4b38f21b02f12a9a60c57e08b87fbf6edee0849ed5383a5ddcfeb7a061fd5d2",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ThumbnailOverlaySidePanel.ts": "6d9dae530d73aec0f519115533ac40a803513f461694c851950a80c5a9fd5e60",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ThumbnailOverlayTimeStatus.ts": "e25f43bcf06b78ff91e112883d5046b1ae4887c4fc5383e6674761d925e88a40",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ThumbnailOverlayToggleButton.ts": "f4564c9145832a9ceeef31935efbb78c142b601f26b04e04f6098464e1019ec0",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ThumbnailView.ts": "24f152c361f485a7bea715adcc128391c858ce4aaeaae12e1223c22d2fff13ce",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/TimedMarkerDecoration.ts": "2d05c75a747e74ee64880cc206e8320abe8949fa5b7d1f3ce00036c90ba78a8d",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/TitleAndButtonListHeader.ts": "db79cb8d2503c764bee22131ce77c6e8c4430750d87f7587d7c4dcf55fe93e08",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ToggleButton.ts": "4fe48adf0e705bd2124e06fe3649dead377e7a5f943dca9f6593c02150e5bc9e",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ToggleButtonView.ts": "94ef857c1ad26cf05f8aa74095f46d3849d3803b2ba60255207bb1b407974b3f",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ToggleFormField.ts": "ae9de70c83fd8e39388806206cb0d89c526c336cb88ca154f848e0cff2d0d6e7",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ToggleMenuServiceItem.ts": "0bf6f577e10beeb2942223f0388fd371112d4deef44f147577d06bf0e396a239",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/Tooltip.ts": "6d31852c1e4bc57eb81210db674f432877bcdd2c9c8cb3523a852c60a71c3c83",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/TopicChannelDetails.ts": "74b3326f7a728e730d1a9ce7d597493fd6a4f161609c93489e3207d794b983f8",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/Transcript.ts": "c87a21ed189a98eaeb2b19e7d2ded01d00884eb9ce6f36f890072b688ede2974",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/TranscriptFooter.ts": "0814c8bf0fe224a0a8d8305679c543de6023ecf325a022b9ee07d2f8b6e046d7",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/TranscriptSearchBox.ts": "563776e71c7e29b2f9258660c76cd079e0caea30d4bad4dd8478d7d49d9ac0bd",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/TranscriptSearchPanel.ts": "18215fca5f72b1d42a174e2af7e0009efbddf7637a8df16e7e6f620c848e504b",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/TranscriptSectionHeader.ts": "391d4f8df3f3e37a585526a6ba79808a05bf0929245e13e16119ff4ece847894",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/TranscriptSegment.ts": "af8011075f81a0215ded33504dcdee03beaa9403819995bedcb0c930fba0b3dc",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/TranscriptSegmentList.ts": "606406dea172f1971dabc9df0b2866c0890b899fef2a2917bd79477fad0142e9",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/TwoColumnBrowseResults.ts": "27e497843a39729cd850f47a838c1854a446ff166ccf517ad2a5207fd182707f",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/TwoColumnSearchResults.ts": "4dc5cc2d5281a29d3f8ca8c017577f1e60df14f434412207d45c925be022ff37",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/TwoColumnWatchNextResults.ts": "93a659711d38dc2f36b6cff98141c8bea9718384614a8dc398803fde490cc291",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/UnifiedSharePanel.ts": "64d74360ddb169c00f629ebcfd0e69bcc85884d0290f080883bd5ef9ba50a2f3",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/UniversalWatchCard.ts": "b858b19c695c79be31ab16f27f7832cde27b7c53015c775f5c6a147df25c9c6a",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/UploadTimeFactoid.ts": "083e2a6bd0257aa18cdd996041a82b90ac68fee358af395632d914293549becc",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/UpsellDialog.ts": "fa053fc521c4efdd44d9b2c44890364d0e936db2c9a72cd2edcebaf818f53362",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/VerticalList.ts": "a8278ee8516275eb050fa388406834deeca6a009048a4ac8b44988b3ed0a948a",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/VerticalWatchCardList.ts": "c7035c371f3cfb48a3fa5d6d7eea488424e04411a08fcfe288d735bb42929c11",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/Video.ts": "2862e9e8702ed773feb37cbe919bfb001c7215518ed76571a853d01a8ecf33dd",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/VideoAttributeView.ts": "2b6a3f0bb9f7a9607cb2485aef631f515d2db7d11dd6f003a50fa99cc8d4172d",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/VideoAttributesSectionView.ts": "0226ab7b38282d46087af36f9ed3e89eb10d5e465352f6b4c7b904e52b083025",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/VideoCard.ts": "d2429818d1964c56ec3b4d37661ff70e635b75a342c3e1a77e5b4c379162f153",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/VideoDescriptionCourseSection.ts": "bd6f03d1d88737efe40ea5fcde325fd13c188a440ab641643c0379580b55509a",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/VideoDescriptionHeader.ts": "a63af0f6248f1da8e888aabb169271a3ec3191de6f5f04de0f6885717861418d",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/VideoDescriptionInfocardsSection.ts": "fe9b60f3d32352eb161f14b2bec1a4030a3f0452e097865cf23f151022785a3e",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/VideoDescriptionMusicSection.ts": "2a4f1e13a2799e9db773daa0a90ea34c686ed490770ea3e81ba179c69d0c5cea",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/VideoDescriptionTranscriptSection.ts": "7eafec16826ae3eb27ee99f2af3f21288d69ca3a3a8164664e466b949a355bd0",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/VideoInfoCardContent.ts": "8187a3ace9b55a1384571f4a5a9e747f95c50b943eeb350d725827ba3ca7f821",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/VideoMetadataCarouselView.ts": "e8d41a65766755a8cb87afa33eefb452766aa10844df505b49f17c160a9fd093",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/VideoOwner.ts": "15faf8888dac8884ba39e0d8e755a9c0fc62172f0a4f04816410a35649c10ada",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/VideoPrimaryInfo.ts": "73909badd54fbb19a7f6c5d070b6350e60cab5d6a7fe016f6be7ef0b8af69b1a",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/VideoSecondaryInfo.ts": "0097a7cb7aed11f2b31f5d947e65c408b9a3207deb02108997f2d934280872fa",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/VideoViewCount.ts": "255d32597d633365006a9d99a804f784795403f2a55a93d625f7e604dd62735e",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ViewCountFactoid.ts": "4e360108fe81bbb32d133d6654f022968d6cbbc843b71677da43a5e4f59de73a",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/WatchCardCompactVideo.ts": "7c3e3f941af6819cc653365e4b557403fed5f1753839f5df19a78bd07e43ca79",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/WatchCardHeroVideo.ts": "fde492a3e7f4a6d298cae64c2a89e924bd3d8c41b75bb788ee0085ed22ced439",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/WatchCardRichHeader.ts": "39499548a3c1eddd47e8e5a507bd9639126ce7e26f803e1e2cce244188522246",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/WatchCardSectionSequence.ts": "bbfe86c3b8d62d713670bc1d7de9aee14420937038ac7f06349ee6d65539ffe5",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/WatchNextEndScreen.ts": "86e02a5465d2e857edd606c55d8053ee21fd5c7411e41413609c8da8b5e3a76e",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/WatchNextTabbedResults.ts": "54df6302318c0e0c7d284255fd17c7e076f359a4c617e91ac9a916cad8cf1472",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/YpcTrailer.ts": "90b8b98bbeb39434ecd06feb9d09283372354721cbc7e996debd2c9887341d23",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/actions/AppendContinuationItemsAction.ts": "d9405b3511b9e82817fb0540467f3c646e920877f7fc0e2631b2d809df7bc9b1",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/actions/ChangeEngagementPanelVisibilityAction.ts": "ff805436e0fdc6fa110383a5da893fa5745a0652affe87012786cd2fbd050039",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/actions/GetMultiPageMenuAction.ts": "77c2ff18dca51a2e382d06e6bf7f40376b65296718be4aac44426ca3b16891b9",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/actions/OpenPopupAction.ts": "77d326bfdc00af7fc5630f9706e86ad9c46070b6638efe45396435d8d7a5a4a0",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/actions/SendFeedbackAction.ts": "84f991c88c8060ff4841d9a3ae6f32c7b8aaa0e70323c1231c8fc842552de1c3",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/actions/SignalAction.ts": "b7ab573629c1a3d9920dc12090fda82a3f7d321d8ac5d9a4a47e53c6766a853e",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/actions/UpdateChannelSwitcherPageAction.ts": "40ff717963ac2459fc8a588c1177627a4367549bad3007e4fee1ac0f556fd42b",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/actions/UpdateEngagementPanelAction.ts": "533dc7c75f4e0be5858003292d3c5cd0c17c00dfb0c9978d33a60fb510a8e282",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/actions/UpdateSubscribeButtonAction.ts": "8c2b55d5784f221d2b58417c7f0bffc0f193303e0fde33b92b20b7dc42759504",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/commands/AddToPlaylistCommand.ts": "20f0922289d5626e436c957e9bc8e33a9fb308fead1de786f0928bb23c29a4ae",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/commands/CommandExecutorCommand.ts": "2486e9b105950eb781ceaa52d9dace5a50d301e84ffdb7abc72d76714102bbbc",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/commands/ContinuationCommand.ts": "4357ef6b73bbca64129f3ad5ecb7f4112e837d73e27f7870c11f15ff368f28dd",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/commands/GetKidsBlocklistPickerCommand.ts": "770b544b9bac44e75285936ffe00d3cab4e02874fc6c8f55aabe4b12d26b4502",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/commands/RunAttestationCommand.ts": "80e1fc4a7a370ca0c73d983f7b09e21dd970b8f1a77cfd7ff4bae396bc673104",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/commands/ShowDialogCommand.ts": "3bb9a155d01b9daa05f7e4ad2dfc8dbbaabdee93d4d0d6295d42caf71fccb542",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/commands/UpdateEngagementPanelContentCommand.ts": "6d18d475d87256aa5a588cdee990565eb918fcd4507a7c7382f473eb9ea0e525",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/comments/AuthorCommentBadge.ts": "b88cf1d08b08fdb60e74f6fa957539819a4bfed08f60d5c9c30f9e7cdf21fb50",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/comments/CommentActionButtons.ts": "a9b4a1d5888800da46ce1d48c0c28dd7f5785f0e08e8394b1e3969731e2d76ac",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/comments/CommentDialog.ts": "788ce01ca4b1d7f194983b7b467c21135d34338497506df24bbe34b3d1a4a71d",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/comments/CommentReplies.ts": "bb7c2820f95323f1e9795123e24884da7d8d697bcba6bffc4025816deb9576e9",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/comments/CommentReplyDialog.ts": "69257c8893529d741a29e53b288b6b84b9353e2aeb7c92c259709138d1b3c6ca",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/comments/CommentSimplebox.ts": "780b9a4fbc877476f6c2a9408d8ffc820abc548d2572328d992c1053133aecb0",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/comments/CommentThread.ts": "6af0425fc3ceee7b735486ef53246f025ea940147df5bf75ec939d98318dd708",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/comments/CommentView.ts": "04c42ab9eeae9b0c97db65b37810abfd361f9810392065ee1a4121fd2bb1c55e",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/comments/CommentsEntryPointHeader.ts": "2d7ac9785cb347fe61c23651084ae5e81614ce678ec8f5c4c03bce369f1cb609",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/comments/CommentsEntryPointTeaser.ts": "ea1991b78ad61527edb4d731155fe8e5031b10f1873ddccb546fca36c91c3014",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/comments/CommentsHeader.ts": "1d4dfcda82f9fa5871c4b60fb07a36604fdbb69dc6536a59c1b421bc6c4fe439",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/comments/CommentsSimplebox.ts": "375ddc189375a9c4da5008fa2161b818b6c33f13e8a723dd779c3965ef9b9736",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/comments/CreatorHeart.ts": "0f55fc680430299139f7ed360e53839adc11fccd8ac7c424785427caea9b8bef",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/comments/EmojiPicker.ts": "6917164836bea89bb8684ab22b98cc9e92546b27cb72949cb19c0f60afbe73cb",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/comments/PdgCommentChip.ts": "ff40a83544c5350d0b8066c2a31e377c9d5a4e74ec9afb3019fc4c59eb8b5c90",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/comments/SponsorCommentBadge.ts": "407dd678d0161eedc60d15f54d8704b789a61876b219c399e085d73c25ab4081",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/comments/VoiceReplyContainerView.ts": "ceea0628cfb0735f75173e2d4b37da2d1d05d355cf14513163fb288a0d06a701",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/endpoints/AddToPlaylistEndpoint.ts": "3d0b8a806846af972f0aa60c4d7fa8eaddad770c4ed70f69ac93958074fbaab5",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/endpoints/AddToPlaylistServiceEndpoint.ts": "17ee777df7def24f45e89abbf85afbdc72da887ed6f701c185ee2c4098e39ecf",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/endpoints/BrowseEndpoint.ts": "ebd5db127b359cbca1e7adedcd8109eab6a8339741b7b88dd71306fda911f461",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/endpoints/CreateCommentEndpoint.ts": "42f84fc98b019ae3ddbbaee4deeb524851bf00688e85883c5bf259e78b1ae835",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/endpoints/CreatePlaylistServiceEndpoint.ts": "e3bc39693fb255df8dc84337c6d7646991952369724a908d574d5f0c7a79525e",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/endpoints/DeletePlaylistEndpoint.ts": "3497388121f6a70386ff823489a53fa9848f7fdb5169117398fa5b35f431055d",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/endpoints/FeedbackEndpoint.ts": "31a990df64848e42f4d656b29f56a26c6b197c5972a840ea6d5e60a045c36a14",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/endpoints/GetAccountsListInnertubeEndpoint.ts": "87d2feadc29703235a08a0144ce1686195ce5ec9fd24013dae9ee1b80b3be60f",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/endpoints/HideEngagementPanelEndpoint.ts": "50ddda789eba9b5bc6b81f9aaf9ad0499e8e8eab1bc0f5c9c8945094cb2e1aeb",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/endpoints/LikeEndpoint.ts": "378e73b82a9a71ad315bbda44a9c36440aad9620719aa738ef24fee2dee6eda1",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/endpoints/LiveChatItemContextMenuEndpoint.ts": "5f839665fdaa37c490ba44899ffa127383d647214fb3ea65b31390fa782f72d0",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/endpoints/ModifyChannelNotificationPreferenceEndpoint.ts": "aef724837eddf006eda405f2de06727606a73cbc68863c4b7f141b6be4bf8274",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/endpoints/PerformCommentActionEndpoint.ts": "f21ff87222cf2afc911e8ecad88882e51c5ddae987fc7f1a159217c7bfcfbb47",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/endpoints/PlaylistEditEndpoint.ts": "6930f019e74b1ea1754267497fc65ab688cab8b43a2789249666fadf87a2c034",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/endpoints/PrefetchWatchCommand.ts": "a609ee27d718c44b48aeec38c0a56a61b5cfc1f231a42e1d6175172b8ff46979",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/endpoints/ReelWatchEndpoint.ts": "5cbbf7e0371f5cc103d9ac4fd23a52fac9ec6ea48eb42aede0da0293eef4fee1",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/endpoints/SearchEndpoint.ts": "13b7281c4698bcb71134b9a49355fc530ec39ccbb8157b46332a3277d30cbc23",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/endpoints/ShareEndpoint.ts": "bda5f3a0ff03c9833174b2c517ce41560ca2b6e28de8df7562e8d47e6f81099e",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/endpoints/ShareEntityEndpoint.ts": "bec7c5ed3d04d03808db70df1a9026580dda73a296252da60ed8698e9d0ce811",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/endpoints/ShareEntityServiceEndpoint.ts": "95fe67c01cf6971cd407cdaa7534ae44eebba676e398473939c96d4db25ab2a7",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/endpoints/ShowEngagementPanelEndpoint.ts": "01919799954a742f76a8b39d224284306dc924cb495e458ed5df78f7c58e2c0e",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/endpoints/SignalServiceEndpoint.ts": "9eb7edd67edf636bac08afc201bd6b4cc615ebe06729bf2f4baa31c44b036174",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/endpoints/SubscribeEndpoint.ts": "10b727b8f826229eefefa1a198d8e4926e00d907534f7cabd3b8c389ba235b0f",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/endpoints/UnsubscribeEndpoint.ts": "e0961c962e430d6abe1d8139ff15c0a96d4ee45f575bda773f40b8d8fde83bc3",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/endpoints/WatchEndpoint.ts": "47cbd56cd87cc3e21d73179e4f2368e23c46fae548ce400b68541b6ce3b7b5f2",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/endpoints/WatchNextEndpoint.ts": "dc44cfcc14a61c4d40fb789c806cd1fd081979cda933f4abe531dde3f6308263",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/AddBannerToLiveChatCommand.ts": "ccba1a407ef920fdc243f8d23505358fbcb6b1e2ba7ac37f41b913a950154ac3",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/AddChatItemAction.ts": "84224a2091a48b4afc904b7f82816073fabb0d489c3830ea46d0849c701d8a79",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/AddLiveChatTickerItemAction.ts": "d04afb895b7258ebbdb3889ea9c853fe3038ad6af0b2a2d1389a12dec240f09d",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/DimChatItemAction.ts": "d03feacf293be8e422ad31f7f0a8c00fff46d5e1494b0c3b4ae55ba7ac9193be",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/LiveChatActionPanel.ts": "0ee954f1a788713c6fd86f62ac9ec96567a9260354e739991ac11d082ea32666",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/MarkChatItemAsDeletedAction.ts": "427fb72dff5c9930b30d65a5b96a0056aaacb8a7a29df9dfe9d15f220eaf117c",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/MarkChatItemsByAuthorAsDeletedAction.ts": "4e4b36a2e177502ba5ae4124eecded7062a980b0e226199ebc308f662da42d02",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/RemoveBannerForLiveChatCommand.ts": "e3d50eb35164ef6946b8d96de837b86513e84f78a369cd0a718a8f482929ca84",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/RemoveChatItemAction.ts": "30b936d73c90115cb7c0261e021ffcc7446ae047e40d5d02cc4ed94a6331b307",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/RemoveChatItemByAuthorAction.ts": "6d75a1901e38dc2575a68a7b2c2f059e8e9e1132e739e6e033beed8f97ba1117",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/ReplaceChatItemAction.ts": "c5e640e28bc875c1f479f2ac0a9571854252ae24732bcd54304edbbf1134106a",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/ReplaceLiveChatAction.ts": "977f560d5fcc3adb1e1b8209dc97de6ca1a9ca9b27db9a2c8776e13a88a97c9f",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/ReplayChatItemAction.ts": "2a77f7c1c6ef12d8499c711b2a1ce3042a245b700f85dc38dd633b3a38779459",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/ShowLiveChatActionPanelAction.ts": "ef6c01ad6bc548df3bbe6bd4b8c91e535e78efec1630f910be9e61f33186ada1",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/ShowLiveChatDialogAction.ts": "bf7abc0f8f5782f286439a12bfdf4a4af15f7a865daee71e3e5dbdaffafb6eac",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/ShowLiveChatTooltipCommand.ts": "c1fd87b9926e144326768a45e2edba6d42032306bacbd3eb58a98c1dbbe88970",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/UpdateDateTextAction.ts": "1c7bcc26f7344cd98751780b258755504a5462b805b38eacfe47bfd7569332dc",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/UpdateDescriptionAction.ts": "e59e3b429c00a3db7df47d0b24d2fec4922dffe2d44ca45e23b2177d4c9b7011",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/UpdateLiveChatPollAction.ts": "963ba3a42392f8e4a7b290bf485274a01a38ae94be13861dc29c1150ca4acbcf",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/UpdateTitleAction.ts": "59c8fec38306c15db67f41759866a901e2fc7fc5bd9de370ffcbea7588cc2a0b",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/UpdateToggleButtonTextAction.ts": "a03e8f0d7b42cc0e9714644c4c9fbdda9b7e655b1921e95e7596e341564009c5",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/UpdateViewershipAction.ts": "17b0ac1c11cf1aeee6b695d31bb110a21f9dc26c83fba968c1c31686174c4f99",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/items/BumperUserEduContentView.ts": "ce26510164623042d62b04c1f62a34958257ad3ce0f851843e52be19c0aa12cd",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/items/CreatorHeartView.ts": "2e6dcb88836532f8417fda5cb841f9cf82e63ec0b03a9761fd23684e1bf0548f",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/items/LiveChatAutoModMessage.ts": "33546c3245d6c9d5f057945bfe2fa91d6a73011e29c1497b044930d11aa81a03",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/items/LiveChatBanner.ts": "702097ed5efc58b78a2640e82a8b4f6ca02080986f47764b26f492e52e2c98d1",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/items/LiveChatBannerChatSummary.ts": "b72db2136fb4ea5766e40fea705cba681eab4fab056f2bd9f5262f51edd30c2a",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/items/LiveChatBannerHeader.ts": "b786b5fd014cc934ecebac5207a3115def70170acf92ddf1b81217e054255706",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/items/LiveChatBannerPoll.ts": "58d1bc214cf490126a015f15c9f691252d741f8f991e8440ab9e0a10a699d9e3",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/items/LiveChatBannerRedirect.ts": "41e54867436b8bc7cdf2bbdc3952d5dc82897bb8b5f2b85fd00b1f06a7c6a872",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/items/LiveChatItemBumperView.ts": "f0696a74668b4b151d349f7b4cebb60f73b2be66df4adb471d3642083720a658",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/items/LiveChatMembershipItem.ts": "403f563d1a46bfb3f231244de0e62a5bb76ce6201d8dc1423f10b2c51090cade",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/items/LiveChatModeChangeMessage.ts": "dd5dcdb58b89850e24d3d921c44861670334ce87a25629f691b858248e3a3741",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/items/LiveChatPaidMessage.ts": "07ec2c28d98b621230893161832163c1d505498767cd1dda1a473816373bff1a",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/items/LiveChatPaidSticker.ts": "5e709b32e69baacb8715ea67ab039592f722ada656a9fcce1d5b3fc6e4352f57",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/items/LiveChatPlaceholderItem.ts": "30b308eccb2d6d85d14aee67d3ed0904d92acbcb1570d9f08581040eff4b3090",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/items/LiveChatProductItem.ts": "5be6a06e8513a972012a1ea23ba5d8db4f736d94146c6acda3277e59bacd2b77",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/items/LiveChatRestrictedParticipation.ts": "6ca106287e383ab1a593aa234268e677ce5515a811c6eeed8c8c970ca1ef0943",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/items/LiveChatSponsorshipsGiftPurchaseAnnouncement.ts": "83fb3ee83cb4d9e32e18e00fabe6f8355d24bbc897db383e6bce8492a919e2e7",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/items/LiveChatSponsorshipsGiftRedemptionAnnouncement.ts": "67a5f6d655fa57fdb67f7e51a6aefb7bad20a3545e5ae541b2e2f6f7388a6459",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/items/LiveChatSponsorshipsHeader.ts": "90e0bbccd4a8ca67235650a77e00da332deca32e2ca53c5fb3579a8715ad9b3a",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/items/LiveChatTextMessage.ts": "2a60bca182c9862d4c0615b3f217e8963cc884a8c8d7ff3907746d1d3132ea67",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/items/LiveChatTickerPaidMessageItem.ts": "939546cda0f634e3909ec77b3e6578acb91593e4f0608e0d375c331503420253",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/items/LiveChatTickerPaidStickerItem.ts": "e6b8600b998c5c8f942a317dacea2717b963de348df1f75ca0a4e7578e7e037c",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/items/LiveChatTickerSponsorItem.ts": "6ba17e88ffe48668bcd9602fa076f5ce92f6ef311534dd80c21c21a7212bd0de",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/items/LiveChatViewerEngagementMessage.ts": "28ecb874780ae56d25b71e22144106d4bebb2b7e85a5ae216b32e28af9b81f11",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/items/PdgReplyButtonView.ts": "65762dd2c17457ba0e6e355971bb2838249634ca3fb818f2532bdbbeddea26d6",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/livechat/items/PollHeader.ts": "1bb80b9bb1c0d963ee3ea3b7f51f8fa88aaa087093bdb720e03c6bdc00766017",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/menus/Menu.ts": "f81a97bf5d792185b9ebce818907da57ff88c790c24021b0e21e53ccfd7a1cf9",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/menus/MenuFlexibleItem.ts": "1d43401ea55a940c00a3b11e37b0f1038ba2c90c8aff3d4270c0b1f39c5eeeaa",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/menus/MenuNavigationItem.ts": "bd0c949be02af07e7743d33468a3013e50dc7737adad7eb4c44352d93e8305dd",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/menus/MenuPopup.ts": "bf0e3b6c8c544c3ee668829e821a5acaefe94a58986845b25d8af7385f0bffeb",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/menus/MenuServiceItem.ts": "cb27d2618896fbe6931ac04a0d1f8f5cf29fedb7459963c564d511bf9088be05",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/menus/MenuServiceItemDownload.ts": "e81361dd09e1a2c7001902a3700aaf5c91166b445b713c215c2a07ebdb748d3c",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/menus/MultiPageMenu.ts": "dbdec611535a9a7f5947f9258de1b99225ca4bff240049f2ff157bb72a736dc8",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/menus/MultiPageMenuNotificationSection.ts": "8b0eca2127e084af11c44596d0299918468cbf314ed290d7eb9afffbd17606fe",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/menus/MusicMenuItemDivider.ts": "2b31892d679eadb4a4d88962956ff6fc6de311f0da6f9eab02ef73f9d8b5c89d",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/menus/MusicMultiSelectMenu.ts": "e31a1c0db5478c2293086e9c20cfbe6cd5e235aae94637dc25d69c6d8dce09ad",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/menus/MusicMultiSelectMenuItem.ts": "02581cabf95e48e42d0540a144e84abebc89c6a9b5e001ec9bad0d42b85454b4",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/menus/SimpleMenuHeader.ts": "3d2144bc62ae800ca8f9c5efcf3f9ab164868ac8bd27841cb16b29115314785d",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/misc/AccessibilityContext.ts": "25fcabe96662e2d566091a4411bbf8884c17cb5c9df351dc4512663185ed7321",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/misc/AccessibilityData.ts": "eee97a01e1574a020c615b0b04c959b932d6f0c9bf0cc8eae179fd2de6654c86",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/misc/Author.ts": "b9b94d336efc644f184763567c0d7a65bfedbcac2f3be622cfb2dfaaeb747e53",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/misc/ChildElement.ts": "79f68bdc0d5ec32efc5651b5b85188b724fbf3c25f8a937d31f37ffcc334db37",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/misc/CommandContext.ts": "1b9eade7e5cefca015126c384280cecdb0e38c96ff94368dffaaea29896bfee8",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/misc/EmojiRun.ts": "60f5d3aabcaac04630494af587894f08137d2d411d72231e7cd2015d9d7bada2",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/misc/Format.ts": "e1fd5f689f12af10eb1d0cb673a0a41cf58bbffb975fe905861352db63d1cffc",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/misc/RendererContext.ts": "64099a2a4fc3b8324899de6c3fecea6b9751962a111185485e1b0e78355495c7",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/misc/SubscriptionButton.ts": "10b130c9888f033dd7adabc3463018a4b0f8410c1b878577f5ad80f25fb03049",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/misc/Text.ts": "fb21cf31e0772b3fbdeff620eb11fb6a07d41df17c3b3845e8e5c22bdce3f920",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/misc/TextRun.ts": "c2662d3bd764c4ed9ee9dac5c08c77fb342f1ea5c85019bc8f7f88276e79a5c5",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/misc/Thumbnail.ts": "6b1110ea2aaa51c3492ed1c42ffcf90dab0b9ca433bcec256bc7be413a014c9f",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/misc/VideoDetails.ts": "f5470efe6c0094b060931edfce21be0f45d0af64f0f10388d9975265b007e10e",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/mweb/MobileTopbar.ts": "44f7fbfe48514915428c98b104d9b6be755de2167ee2cf260de5712f8086ce6c",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/mweb/MultiPageMenuSection.ts": "a2410f43003ef117ba92e6a25312e4ff1027d96899d099e1cfacb5a700cff9b1",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/mweb/PivotBar.ts": "58688f48a8e28eb83d509f87567107cd8427d81bddf951e8ddc8ce06ae13437d",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/mweb/PivotBarItem.ts": "cb0a5e15747f234c76544f45ebf51598c03f5a59f196a894d1762a12e57e39d6",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/mweb/TopbarMenuButton.ts": "f6b6d1b232739d22f87432d432d5b6a4c21a050df2fba065df2a25c9cacd1988",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ytkids/AnchoredSection.ts": "fbd48d39e23788538e1dc96dbb6e06590ffd5c0dd2113535e524ce0729a629d8",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ytkids/KidsBlocklistPicker.ts": "42946dc4c361f0e94c399c4ae4ed596db9b1f0c610e082edfe62c20a90cdea2d",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ytkids/KidsBlocklistPickerItem.ts": "987024492384470cdda76695fd930290038e0547176101797ba16b9212801388",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ytkids/KidsCategoriesHeader.ts": "cc93ba2d93382b82724ba82b062012cf37cc465ac6615c896c50cfca9b42a172",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ytkids/KidsCategoryTab.ts": "a8f20f69eed8d5ba306541ed2caa52bcba71a8e91b2adcca915a6f8c1bb4479c",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/classes/ytkids/KidsHomeScreen.ts": "58dcbf376422f3ba7ecec749ff2aab6e72f3bb747e669792cfe444f9f1e8a1b2",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/continuations.ts": "8f4dc346c25ff8c20864c563e63d3b409d244ed21bf55453a344c79591c242b0",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/generator.ts": "aa6aba41a04a06470320f2f246d70fbfbb07ea85053fa0ae2610a450e4028468",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/helpers.ts": "a75b626033557cf63e0a8419a1241e657f6a994cf8c741dfb2290d003250dfa5",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/index.ts": "bb5e4232cef64df81417348f464b07193a8cf0f198f809b5744bba760337b040",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/misc.ts": "7f8d468445f5e58ff5fb61f78ba2bf38447e77400b4975b255c69b4c1cc1ad7a",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/nodes.ts": "bcf879cc6a4f526cb1c333f68551888446a8e61b85923c7e06996ed764cae9e1",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/parser.ts": "2f70a25bd6bd524da94404d482e95e10325a6c47faae3d42624fe18a7d1e2e02",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/types/CommandEndpoints.ts": "4e09e63c0f19959f642045eb178c389e9561667ed5a877040c99b74f4220ffe7",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/types/ParsedResponse.ts": "9066b5064a347759b11ee1f596b66acc2523d54b4855b5faf2ae13827021809a",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/types/RawResponse.ts": "1b014ddedb55e2229e8b41591065e96bbbd669a26aa3b037b33efbae69266715",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/types/index.ts": "007247905143a2c8fe5c1bb02470f126f9107af5ea2649c1035489587aff97d1",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/youtube/AccountInfo.ts": "55db92aea54b21ff7d21741e5b76ebbb365bd157e47913200426cf25da7dcf80",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/youtube/Channel.ts": "157735bfa0101b3c38ccd4287424151f01d95fe8fc8a53f0d4ce837584035dee",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/youtube/Comments.ts": "de5684f3c20bd3890435bd959f7d515ea989356ec6802d04af9b0de68d6a1595",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/youtube/Guide.ts": "36f38dc9574207941166b78c7ccef9d5dd59ebc72c0ee569db3452dc59d77f50",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/youtube/HashtagFeed.ts": "f3e54bae94e2fc3c2aa0c7aaf709ce1e9a708684eab8b6fce1202e2066764860",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/youtube/History.ts": "30c4bbcbffae06536f1b04fe7a478d8a3d2de12dfb201e15b17537566d43428a",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/youtube/HomeFeed.ts": "bf7606b3c4734c90af713e0d98fc4783c7deca7778a531b39f182d29b577ffa8",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/youtube/ItemMenu.ts": "ad53265ac6c382e537d81a5f96b874b16bcd195140307da4f79cc7c0451d6153",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/youtube/Library.ts": "23a00c894ab996c29cd3b33a765090d2e0cbdbf896e6c6140da7676905b0c206",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/youtube/LiveChat.ts": "f385e5b8314ead6eb71200c59b5fd2e7ab32b7168017c18bb14da1397d402ab8",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/youtube/NotificationsMenu.ts": "782f10807e96199bd3846e66ba8f2d768188e3b3287808d7bfc283db87607f38",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/youtube/Playlist.ts": "6a664495ab57f9ee2da58ee508d03438e65d505d83b58c993f0b0d02c1ee8873",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/youtube/Search.ts": "1c014ab5fd1bf2c57b373f3d3ddddcd702d3b801ab7262755bba40c0939bbfa5",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/youtube/Settings.ts": "3318060058505c878f8f4a62ee69207c75fbaecdbfe6114b80d07b78a66e78bd",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/youtube/SmoothedQueue.ts": "a6bfe6084d76489dbde3f7cff9ef50a2fdf561b33c04ac56fc923ba680b5ff67",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/youtube/TranscriptInfo.ts": "b8ecff9130ed588d867a08659a62f71a0113f425953c83288f358f82eec4a266",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/youtube/VideoInfo.ts": "e44e535131fd057c3573c80773bf66bb55d6d5602f34319e736a7e0d5356616f",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/youtube/index.ts": "43a4bf938c7e99e3ca2fefd0a83dd66c6a19ef3493d878013c612aee9f965c60",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/ytkids/Channel.ts": "48671b34567963c1802b5c411fc81f79b085d472610d30f45b454d3505210f1a",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/ytkids/HomeFeed.ts": "8ab641632309a9e06cb25863ea5612156dc00d827a62e4699ed500bc22575ab0",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/ytkids/Search.ts": "ace048184b9f86b00cbf59200c9b39b5929234651c160eb36a12c653d28ea7aa",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/ytkids/VideoInfo.ts": "ad3905e776cf6526144eb1e1e82a737c7af5ba78c8a54e11a0b1a3d0fbbbfe35",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/ytkids/index.ts": "6638e38d05663cde1515bc7e28893ac7f777cfd86783f0c05f7d0288dded7173",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/ytmusic/Album.ts": "44c7261eca6957285b45cf148c053f6ec25e7ae791173721250feac6d1831cdc",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/ytmusic/Artist.ts": "d54862c71edd59edef2f58961ce75e56e9664998de493163f436eb066cf0cd50",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/ytmusic/Explore.ts": "8309008ef8b106340df72eb1d4d3cdb2b3ee375d31471ed4bebf6f3b963b69aa",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/ytmusic/HomeFeed.ts": "3c8bc9920e0e6fef613dd14531436fd5a06fe325d075eeae2421735ceb1085fc",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/ytmusic/Library.ts": "c0609da173f7ff97ed598f2e348bb7890ceea6f37f84d2104b339033e77413c5",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/ytmusic/Playlist.ts": "2f43b31cdb9ea84f442e2948fcb35172f59aecda4575739b8e6cc78e03116ea7",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/ytmusic/Recap.ts": "3444981c0ed27e6263c0803ce27618c68f129b2cac580529844ac7e8003ad27b",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/ytmusic/Search.ts": "ff4746ec399dd528e26fbfdbdaf2811e0568ae41440adb597cf3e6246158d6c8",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/ytmusic/TrackInfo.ts": "cca58496ddd9064377e66b737a69c57b0c8fb51c66e112c8380009804d7aa7b2",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/ytmusic/index.ts": "0f4181b7311ffdf368e890c97820fdb1a33a32609be8ba002e7c131054fbb7ef",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/ytshorts/ShortFormVideoInfo.ts": "411625d6125efda6122ff6ebe4d5b220c0f8f910c67941ffad0197107b409af7",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/parser/ytshorts/index.ts": "03d0e82f61eaf3065e0229da89b5798c9bf6602f6c7ae012dc93ba9046ea74c4",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/platform/deno.ts": "44f5ce00f11469a4263f9e308eefda7551d4109159f3b9f990649580a171a1a3",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/platform/jsruntime/default.ts": "0354bd04f0c24bd659d83ecb929365f53288e45d599e9d47188dea649d9bf236",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/platform/lib.ts": "b1ef65da57cc2acfa278a6468d7a6e045f96f43f8bfebabb2dd683ee23e047dd",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/platform/polyfills/web-crypto.ts": "ae20ed00dea9eafca9ba590f4fa440299cbd57288add788c59cb19f3455ae6d1",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/types/Cache.ts": "06cd238bce7c9657055151587e36ee445e8236d54d27272124ced10ea7be0da4",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/types/DashOptions.ts": "cf694c112ab97d778b3df735ddc76fd16fd5ae0d49943e2cc580f1f986f63da6",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/types/FormatUtils.ts": "a877e6d3e84651d5de882a3e6cba7ac82ac4c64d315be64c072776cf2a9eeb72",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/types/GetVideoInfoOptions.ts": "f6bbc11dbf9ee5677e1a55b3064f50d716e9336aa8d4c4a32c164e64576c1beb",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/types/Misc.ts": "971702ea50befc95a46c818595a09af53e38c6d7dd691cec6297c6e06b0002f7",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/types/PlatformShim.ts": "24e8c48ad6754ba64bf95327317dbcc5450c6092e87e7bc06e92f757b22ed4c4",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/types/StreamingInfoOptions.ts": "e709c0f53e0361370ba455f811fc97825ba35739b2b2766b0c13bc32f8cbf264",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/types/index.ts": "16ec980101e06859f4269f8abd1bc3372dd2a9deea200dbc8396d821449738cc",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/utils/BinarySerializer.ts": "1f99ec65ec2d52a299fe70b2b8d868bd4ca1d4750d5b360220f81015936180ba",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/utils/Cache.ts": "fd90c88da32e9283adf065475a5cb4e680b5152a6bc06dd8a3dc9349358cab35",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/utils/Constants.ts": "477ec062b9c497fdeb53dc03247ca7a3ffba94df5bd57765db82304bd749e6f8",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/utils/DashManifest.js": "53435f8ee6f35ff3261a88f96824a3f040ff8ef0db6c6dda55f0494f181f2946",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/utils/DashUtils.ts": "05723c6a8dc330d4032d7d0dde416f5cee5caac98a1a5c66e4df86a1ae9fc320",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/utils/EventEmitterLike.ts": "ed053c41100dcb5d8e9ec4120fc8916a0eef1abbe7c215480f59b81d5eb75386",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/utils/FormatUtils.ts": "e4a9dbe5f013ce25ed877db655997e9375a732b1ddca3b35118b18e4d1b10450",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/utils/HTTPClient.ts": "79679cc4883b521078638c68f1388411ea762485848edac828345330c7af1535",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/utils/LZW.ts": "b31ec32b1569c57cea5342e6fbcc8c5ef99580bfc1728870ee0d34db7d632516",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/utils/Log.ts": "a2b5adadd4515d4a5879170e79201254265ba48272f8ab325e8070293aed15fc",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/utils/ProtoUtils.ts": "b81c1a202bbaeffa9a9792ec1d5a13e86ad8ca87ee81b0bdbba094bda4ae3f8f",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/utils/StreamingInfo.ts": "f9caf90f08da40734100adb022dd578ade17b1c4be2132e30b881ec8c58558d5",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/utils/Utils.ts": "d791bf3d02a362b53576a935d96847104e7a5624175faf8fd655e164ecb25d6c",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/utils/index.ts": "3725d7670be90acbc34e1829d29af0ee9362c00c5ca8f0f16b75bdbedbf71d69",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/utils/javascript/JsAnalyzer.ts": "e275870bea05feaffd1fc93780b811a624c2be42223315d88d14c5fc15dcfcc3",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/utils/javascript/JsExtractor.ts": "39a57dc56beb77972431f305c47980a232d719f2d6f3f71021530f7ee08e6f2c",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/utils/javascript/helpers.ts": "c7676a2a0b5415016114d2dd30a44379ac59d5497a6486ed5720a5ad992e1106",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/utils/javascript/index.ts": "eab40ae620d6b2e7efab257f9d50a3b80e312288a4a0a757db7541182dd2da8f",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/utils/javascript/matchers.ts": "ca3bb7e2099758d43930797fb86f39b15c611780a5d6bb8f80d0dc6db005725e",
+ "https://cdn.jsdelivr.net/gh/LuanRT/YouTube.js@v16.0.0-deno/deno/src/utils/user-agents.ts": "f17fb5becef1bc6e06c908e038f561be8b2c601ad7effd793da5fadd372abd83",
+ "https://cdn.jsdelivr.net/npm/jsdom@26.1.0/+esm": "cdc60f484ef171bd966991cfb3fbfff749edf8bda65edc7f3e251ce82ccc3e48",
+ "https://deno.land/std@0.159.0/encoding/ascii85.ts": "f2b9cb8da1a55b3f120d3de2e78ac993183a4fd00dfa9cb03b51cf3a75bc0baa",
+ "https://deno.land/std@0.224.0/assert/_constants.ts": "a271e8ef5a573f1df8e822a6eb9d09df064ad66a4390f21b3e31f820a38e0975",
+ "https://deno.land/std@0.224.0/assert/assert.ts": "09d30564c09de846855b7b071e62b5974b001bb72a4b797958fe0660e7849834",
+ "https://deno.land/std@0.224.0/assert/assert_almost_equals.ts": "9e416114322012c9a21fa68e187637ce2d7df25bcbdbfd957cd639e65d3cf293",
+ "https://deno.land/std@0.224.0/assert/assert_array_includes.ts": "14c5094471bc8e4a7895fc6aa5a184300d8a1879606574cb1cd715ef36a4a3c7",
+ "https://deno.land/std@0.224.0/assert/assert_equals.ts": "3bbca947d85b9d374a108687b1a8ba3785a7850436b5a8930d81f34a32cb8c74",
+ "https://deno.land/std@0.224.0/assert/assert_exists.ts": "43420cf7f956748ae6ed1230646567b3593cb7a36c5a5327269279c870c5ddfd",
+ "https://deno.land/std@0.224.0/assert/assert_false.ts": "3e9be8e33275db00d952e9acb0cd29481a44fa0a4af6d37239ff58d79e8edeff",
+ "https://deno.land/std@0.224.0/assert/assert_greater.ts": "5e57b201fd51b64ced36c828e3dfd773412c1a6120c1a5a99066c9b261974e46",
+ "https://deno.land/std@0.224.0/assert/assert_greater_or_equal.ts": "9870030f997a08361b6f63400273c2fb1856f5db86c0c3852aab2a002e425c5b",
+ "https://deno.land/std@0.224.0/assert/assert_instance_of.ts": "e22343c1fdcacfaea8f37784ad782683ec1cf599ae9b1b618954e9c22f376f2c",
+ "https://deno.land/std@0.224.0/assert/assert_is_error.ts": "f856b3bc978a7aa6a601f3fec6603491ab6255118afa6baa84b04426dd3cc491",
+ "https://deno.land/std@0.224.0/assert/assert_less.ts": "60b61e13a1982865a72726a5fa86c24fad7eb27c3c08b13883fb68882b307f68",
+ "https://deno.land/std@0.224.0/assert/assert_less_or_equal.ts": "d2c84e17faba4afe085e6c9123a63395accf4f9e00150db899c46e67420e0ec3",
+ "https://deno.land/std@0.224.0/assert/assert_match.ts": "ace1710dd3b2811c391946954234b5da910c5665aed817943d086d4d4871a8b7",
+ "https://deno.land/std@0.224.0/assert/assert_not_equals.ts": "78d45dd46133d76ce624b2c6c09392f6110f0df9b73f911d20208a68dee2ef29",
+ "https://deno.land/std@0.224.0/assert/assert_not_instance_of.ts": "3434a669b4d20cdcc5359779301a0588f941ffdc2ad68803c31eabdb4890cf7a",
+ "https://deno.land/std@0.224.0/assert/assert_not_match.ts": "df30417240aa2d35b1ea44df7e541991348a063d9ee823430e0b58079a72242a",
+ "https://deno.land/std@0.224.0/assert/assert_not_strict_equals.ts": "37f73880bd672709373d6dc2c5f148691119bed161f3020fff3548a0496f71b8",
+ "https://deno.land/std@0.224.0/assert/assert_object_match.ts": "411450fd194fdaabc0089ae68f916b545a49d7b7e6d0026e84a54c9e7eed2693",
+ "https://deno.land/std@0.224.0/assert/assert_rejects.ts": "4bee1d6d565a5b623146a14668da8f9eb1f026a4f338bbf92b37e43e0aa53c31",
+ "https://deno.land/std@0.224.0/assert/assert_strict_equals.ts": "b4f45f0fd2e54d9029171876bd0b42dd9ed0efd8f853ab92a3f50127acfa54f5",
+ "https://deno.land/std@0.224.0/assert/assert_string_includes.ts": "496b9ecad84deab72c8718735373feb6cdaa071eb91a98206f6f3cb4285e71b8",
+ "https://deno.land/std@0.224.0/assert/assert_throws.ts": "c6508b2879d465898dab2798009299867e67c570d7d34c90a2d235e4553906eb",
+ "https://deno.land/std@0.224.0/assert/assertion_error.ts": "ba8752bd27ebc51f723702fac2f54d3e94447598f54264a6653d6413738a8917",
+ "https://deno.land/std@0.224.0/assert/equal.ts": "bddf07bb5fc718e10bb72d5dc2c36c1ce5a8bdd3b647069b6319e07af181ac47",
+ "https://deno.land/std@0.224.0/assert/fail.ts": "0eba674ffb47dff083f02ced76d5130460bff1a9a68c6514ebe0cdea4abadb68",
+ "https://deno.land/std@0.224.0/assert/mod.ts": "48b8cb8a619ea0b7958ad7ee9376500fe902284bb36f0e32c598c3dc34cbd6f3",
+ "https://deno.land/std@0.224.0/assert/unimplemented.ts": "8c55a5793e9147b4f1ef68cd66496b7d5ba7a9e7ca30c6da070c1a58da723d73",
+ "https://deno.land/std@0.224.0/assert/unreachable.ts": "5ae3dbf63ef988615b93eb08d395dda771c96546565f9e521ed86f6510c29e19",
+ "https://deno.land/std@0.224.0/fmt/colors.ts": "508563c0659dd7198ba4bbf87e97f654af3c34eb56ba790260f252ad8012e1c5",
+ "https://deno.land/std@0.224.0/internal/diff.ts": "6234a4b493ebe65dc67a18a0eb97ef683626a1166a1906232ce186ae9f65f4e6",
+ "https://deno.land/std@0.224.0/internal/format.ts": "0a98ee226fd3d43450245b1844b47003419d34d210fa989900861c79820d21c2",
+ "https://deno.land/std@0.224.0/internal/mod.ts": "534125398c8e7426183e12dc255bb635d94e06d0f93c60a297723abe69d3b22e",
+ "https://deno.land/x/brotli@0.1.7/mod.ts": "08b913e51488b6e7fa181f2814b9ad087fdb5520041db0368f8156bfa45fd73e",
+ "https://deno.land/x/brotli@0.1.7/wasm.js": "77771b89e89ec7ff6e3e0939a7fb4f9b166abec3504cec0532ad5c127d6f35d2",
+ "https://deno.land/x/crypto@v0.11.0/aes.ts": "0f4e5af07514a07d56ec01b2186c0153f13d6e00c26fc4e70692996af6280e48",
+ "https://deno.land/x/crypto@v0.11.0/block-modes.ts": "6919d89616753727b87308c8829d885257534329e0976cc1940873bb95e25508",
+ "https://deno.land/x/crypto@v0.11.0/src/aes/consts.ts": "582aeed7afda2fe3deac4a60c4a9f29c60a7fb66f56645f95fa0ddab49bde994",
+ "https://deno.land/x/crypto@v0.11.0/src/aes/mod.ts": "883d1e48d033dc4491f3a336c07235c5c4cb0371972476b8cdeea5e94ad2efbe",
+ "https://deno.land/x/crypto@v0.11.0/src/block-modes/base.ts": "9006474c676782602ede9ea16aa49e8d084fd5670f6050641715a3d3085e1ba5",
+ "https://deno.land/x/crypto@v0.11.0/src/block-modes/cbc.ts": "c12036ea98e694a283396bab8608320ae99250940b28cd25e7a4a263b0611db0",
+ "https://deno.land/x/crypto@v0.11.0/src/block-modes/cfb.ts": "6668b84874bceea1c33ceffa9a37f378952c3766dc500054d434ce2cfba0efff",
+ "https://deno.land/x/crypto@v0.11.0/src/block-modes/ctr.ts": "06fd8e338dbda0a6a7fa49718a0fd247830759820e61646a6cf611ad69a9d464",
+ "https://deno.land/x/crypto@v0.11.0/src/block-modes/ecb.ts": "c346d692f16f8efbcc041c25dd761ca223ef92e03bb05457908953bf261ba325",
+ "https://deno.land/x/crypto@v0.11.0/src/block-modes/ige.ts": "4ce89fd3995b2b562b9036e857ee8a99d283421c45660b2c0c9f4d33b93d95ec",
+ "https://deno.land/x/crypto@v0.11.0/src/block-modes/mod.ts": "6a64cfd5dc627f161176bbf97959fe3d2dacd0a35f54184e226bf1ee4714434a",
+ "https://deno.land/x/crypto@v0.11.0/src/block-modes/ofb.ts": "0f77075505853b4ba1a55b4edecb17323f8a1489456a3d3b74717565cccbf2ef",
+ "https://deno.land/x/crypto@v0.11.0/src/utils/bytes.ts": "7ccf6cb2d747d9b9de04c9a2494999957c1e86fd4e14bc228aad25283323f5a0",
+ "https://deno.land/x/crypto@v0.11.0/src/utils/padding.ts": "544c51a471a413b15940bf08b285bc6a5db27796ff3cf240564f42701aba01dc",
+ "https://deno.land/x/lz4@v0.1.2/mod.ts": "4decfc1a3569d03fd1813bd39128b71c8f082850fe98ecfdde20025772916582",
+ "https://deno.land/x/lz4@v0.1.2/wasm.js": "b9c65605327ba273f0c76a6dc596ec534d4cda0f0225d7a94ebc606782319e46",
+ "https://deno.land/x/zod@v3.24.2/ZodError.ts": "27b41119736fcdc69cc72e63838bed1e9e1210c7ce721211f02256e06b443b55",
+ "https://deno.land/x/zod@v3.24.2/errors.ts": "5285922d2be9700cc0c70c95e4858952b07ae193aa0224be3cbd5cd5567eabef",
+ "https://deno.land/x/zod@v3.24.2/external.ts": "a6cfbd61e9e097d5f42f8a7ed6f92f93f51ff927d29c9fbaec04f03cbce130fe",
+ "https://deno.land/x/zod@v3.24.2/helpers/enumUtil.ts": "54efc393cc9860e687d8b81ff52e980def00fa67377ad0bf8b3104f8a5bf698c",
+ "https://deno.land/x/zod@v3.24.2/helpers/errorUtil.ts": "7a77328240be7b847af6de9189963bd9f79cab32bbc61502a9db4fe6683e2ea7",
+ "https://deno.land/x/zod@v3.24.2/helpers/parseUtil.ts": "c14814d167cc286972b6e094df88d7d982572a08424b7cd50f862036b6fcaa77",
+ "https://deno.land/x/zod@v3.24.2/helpers/partialUtil.ts": "998c2fe79795257d4d1cf10361e74492f3b7d852f61057c7c08ac0a46488b7e7",
+ "https://deno.land/x/zod@v3.24.2/helpers/typeAliases.ts": "0fda31a063c6736fc3cf9090dd94865c811dfff4f3cb8707b932bf937c6f2c3e",
+ "https://deno.land/x/zod@v3.24.2/helpers/util.ts": "30c273131661ca5dc973f2cfb196fa23caf3a43e224cdde7a683b72e101a31fc",
+ "https://deno.land/x/zod@v3.24.2/index.ts": "d27aabd973613985574bc31f39e45cb5d856aa122ef094a9f38a463b8ef1a268",
+ "https://deno.land/x/zod@v3.24.2/locales/en.ts": "a7a25cd23563ccb5e0eed214d9b31846305ddbcdb9c5c8f508b108943366ab4c",
+ "https://deno.land/x/zod@v3.24.2/mod.ts": "ec6e2b1255c1a350b80188f97bd0a6bac45801bb46fc48f50b9763aa66046039",
+ "https://deno.land/x/zod@v3.24.2/standard-schema.ts": "4abb2e7bd784fb95d219584673971bb317e74fb4fd0c74c196b558ba46df4456",
+ "https://deno.land/x/zod@v3.24.2/types.ts": "91f825106bcf5b2ba08daa108283aadc32c0ac332f09c9a90db3d88b142476a3",
+ "https://esm.sh/@asamuzakjp/css-color@3.2.0/denonext/css-color.mjs": "5a7c2124bfb224efba4158a1a38e8b618256d6e1fd809e93b83d96daa5a25a96",
+ "https://esm.sh/@asamuzakjp/css-color@3.2.0?target=denonext": "67bce3fe2d3c1373f13f35065aa7f528f361b398fa2455fc982e265fb4691a46",
+ "https://esm.sh/@bufbuild/protobuf@2.0.0/denonext/dist/esm/create.mjs": "d9fcaeb3dfeb517c4d59843d78d7b51b3ad91b911e35bc317dc29e97e4ba9e67",
+ "https://esm.sh/@bufbuild/protobuf@2.0.0/denonext/dist/esm/descriptors.mjs": "7e5dd304539ba1889dea236a3635983d22038ae19bd4952d1d9323f1622a9aaa",
+ "https://esm.sh/@bufbuild/protobuf@2.0.0/denonext/dist/esm/from-binary.mjs": "8619f1c113211c1bfde08795f2a8f30faeb6b48fefecf044dd12580bdd77e176",
+ "https://esm.sh/@bufbuild/protobuf@2.0.0/denonext/dist/esm/is-message.mjs": "b6435ef48053c60211391b011012c25056f8527a4b1a1201c128460adb60db29",
+ "https://esm.sh/@bufbuild/protobuf@2.0.0/denonext/dist/esm/proto-int64.mjs": "43ff71e1af9d45f2a6ad6170f4e4aa6c20f98d47a0a68be3b3f8363764ce1bd8",
+ "https://esm.sh/@bufbuild/protobuf@2.0.0/denonext/dist/esm/reflect/error.mjs": "e90d2f17fe9bf940b349915978ff389790b267313eafd016f8c80ed3633e2759",
+ "https://esm.sh/@bufbuild/protobuf@2.0.0/denonext/dist/esm/reflect/guard.mjs": "b7c13bcf7826d6dd26904e33abdc18e83896b2d9fda783d988257f62849e02cb",
+ "https://esm.sh/@bufbuild/protobuf@2.0.0/denonext/dist/esm/reflect/reflect-check.mjs": "03168260147a043ccd73d45d2e3c5e1adb37aaac3ba4834c5eca63d0f5c2fd1b",
+ "https://esm.sh/@bufbuild/protobuf@2.0.0/denonext/dist/esm/reflect/reflect.mjs": "a4daf06c0f06e91c1aac94566e7831006e35da21e2e85afe3f17c77458adc3c8",
+ "https://esm.sh/@bufbuild/protobuf@2.0.0/denonext/dist/esm/reflect/scalar.mjs": "3d4e9550eb227008f020a2b29cabe6dbf863fa17251a5a966163aec135f0314e",
+ "https://esm.sh/@bufbuild/protobuf@2.0.0/denonext/dist/esm/reflect/unsafe.mjs": "2e5be946c0a5f2fd57aa0631cd0edea8a8b836e6ca3d1f884175029de13612c1",
+ "https://esm.sh/@bufbuild/protobuf@2.0.0/denonext/dist/esm/to-binary.mjs": "28a330bdbb17e5d18957d164c9511c199615d9b13a9223d6c67260b0f1fdeab4",
+ "https://esm.sh/@bufbuild/protobuf@2.0.0/denonext/dist/esm/wire/base64-encoding.mjs": "6d5ac314fcfdc2af5529ffa8e319d4fbb5d6f18720a0eed171d012dfce3bb25f",
+ "https://esm.sh/@bufbuild/protobuf@2.0.0/denonext/dist/esm/wire/binary-encoding.mjs": "125088da118dcbcfde7fa3d11b87d8da30fc30418aa046684c348eea0f842251",
+ "https://esm.sh/@bufbuild/protobuf@2.0.0/denonext/dist/esm/wire/text-encoding.mjs": "da32b0e15885c8963c118c907927e8ee60fee57d21e3988bfd3aebf44afb1265",
+ "https://esm.sh/@bufbuild/protobuf@2.0.0/denonext/dist/esm/wire/text-format.mjs": "9c8e39da4c3cbc0618e93219f7def9356774198a005d3da92870aaa41a2eebd3",
+ "https://esm.sh/@bufbuild/protobuf@2.0.0/denonext/dist/esm/wire/varint.mjs": "148131cfbd4bebfa7aed5c4131a3c76c1a72ee95daaf3acb392ddf755e9a8bf5",
+ "https://esm.sh/@bufbuild/protobuf@2.0.0/denonext/dist/esm/wkt/wrappers.mjs": "2fdb6acdda91c53c238b51c5ea6ef8def5b4e9871f93c439ab835c0a9f1e6070",
+ "https://esm.sh/@bufbuild/protobuf@2.0.0/denonext/wire.mjs": "6a6e0ea3c95cdc3b4dd4118c4ef7fa81f677591c2cf8a8fa8e4c991a72f86a16",
+ "https://esm.sh/@bufbuild/protobuf@2.0.0/wire": "eb1be07dad5823fc419cc9e6f62077a70962ce42facb1a5240b7d5c3674e852f",
+ "https://esm.sh/@csstools/color-helpers@5.1.0/denonext/color-helpers.mjs": "e41a79a8b8770e970eaec926edbc0f801808da289e078f99a2b3eba2879d52de",
+ "https://esm.sh/@csstools/color-helpers@5.1.0?target=denonext": "75a3bf1901e7315eb57ce8d57304625cc471384d534fd9c54a61ce6426fa54c8",
+ "https://esm.sh/@csstools/css-calc@2.1.4/denonext/css-calc.mjs": "5b18ad9a5ecde39080884246c241b5c838bd49d907af09bdae683cc04d624508",
+ "https://esm.sh/@csstools/css-calc@2.1.4?target=denonext": "e251f57041dd61882defa05a49cf6dcb4917494ae2d4c9673313bfc7d416ba0b",
+ "https://esm.sh/@csstools/css-color-parser@3.1.0/denonext/css-color-parser.mjs": "80b6757e5f427f44d3a5a1b8834ccc5bd0e3e665745a2457c75d4a0c12002fa3",
+ "https://esm.sh/@csstools/css-color-parser@3.1.0?target=denonext": "3b18e660d1cf0b8a63867d30a66158839a4c284000298001acde43f099718b69",
+ "https://esm.sh/@csstools/css-parser-algorithms@3.0.5/denonext/css-parser-algorithms.mjs": "b3ebff1cb4bafa28cf52827611be3b1d40dd6427cdec731840fb4046fc66335d",
+ "https://esm.sh/@csstools/css-parser-algorithms@3.0.5?target=denonext": "b97bea9487f8a977db6c26c593180ba7b790589543166c758ea34e225108049c",
+ "https://esm.sh/@csstools/css-tokenizer@3.0.4/denonext/css-tokenizer.mjs": "0d9f924e8593df969ca0e7549e553cdbae5f8bbe5f93048ee5d9d4009e6322dd",
+ "https://esm.sh/@csstools/css-tokenizer@3.0.4?target=denonext": "76927db5a406c1a5b34e883768ea8719a3754429f95298d6ad2d22e8248336a3",
+ "https://esm.sh/@opentelemetry/api@1.9.0/denonext/api.mjs": "ecdd000d5db6c4ad6e56d28940d9344e5134ead8898d640ecf5dec3baa9b5c1c",
+ "https://esm.sh/@opentelemetry/api@1.9.0?target=denonext": "6969eed0eafa68f1c525555a674daaa3143bb7f41f531bf7c1c9967e32419038",
+ "https://esm.sh/agent-base@7.1.4/denonext/agent-base.mjs": "946861146cac434717ababac2bec4bc54265e65c6836ed13824ea7038156ad0b",
+ "https://esm.sh/agent-base@7.1.4?target=denonext": "b31574e95ddac2824849158d58de6142f36a95e344660dcf3c75e0596adc9801",
+ "https://esm.sh/asynckit@0.4.0/denonext/asynckit.mjs": "4ef3be6eb52c104699b90ca5524db55ec15bc76b361432f05c16b6106279ba72",
+ "https://esm.sh/asynckit@0.4.0?target=denonext": "c6bd8832d6d16b648e22d124a16d33c3a7f7076e92be9444f2e4f6b27545708d",
+ "https://esm.sh/bgutils-js@3.2.0": "9691c575e7f81d8c2652260f59356b5de97a242fc8b464dbba880a543f6ce075",
+ "https://esm.sh/bgutils-js@3.2.0/denonext/bgutils-js.mjs": "9acd0267c5bf7273ba122a295f97795cb81d0f6d001db708c92548ac82977f93",
+ "https://esm.sh/bintrees@1.0.2/denonext/bintrees.mjs": "a60dece304ed0119eeb04d32fcc783ef3adcdce64ef64c7f6d07af54ca078bac",
+ "https://esm.sh/combined-stream@1.0.8/denonext/combined-stream.mjs": "364b91aa4c33e5f0b4075949d93a3407b21a8695031e7c2be29999d588f9ca2c",
+ "https://esm.sh/combined-stream@1.0.8?target=denonext": "a0c89b8b29494e966774c7a708e33cc2df16a0bbe2279c841d088e169e7ab3c4",
+ "https://esm.sh/cssstyle@4.6.0/denonext/cssstyle.mjs": "1b786d2c43d33978d3a56505c299da47acfc08cd6351262110f00c94f244f82f",
+ "https://esm.sh/cssstyle@4.6.0?target=denonext": "7129256480c55ea444526675680d480e84ab3d22f99c61f98432936d1dcf1d73",
+ "https://esm.sh/data-urls@5.0.0/denonext/data-urls.mjs": "25b6809e8f13037adaa1426a551c35e41a4edaeb60bf449774f95a6548d7e7cc",
+ "https://esm.sh/data-urls@5.0.0?target=denonext": "9c793f0b7495861bb9721fe6956a441328c93c70e0159e2aba56fd54385cadf4",
+ "https://esm.sh/debug@4.4.3/denonext/debug.mjs": "76c71326f78377cc7252a22502b6c058ad13c06d77abda67e670555659f7974e",
+ "https://esm.sh/debug@4.4.3?target=denonext": "9df8b8806c13297f96185899110f8146b8b0c7944847c8392cbfb4e730a634dd",
+ "https://esm.sh/decimal.js@10.6.0/denonext/decimal.mjs": "6b826e59480ea004462b2c1bb0d13c606a666fdb2ffe58026ea5c85c390c87ee",
+ "https://esm.sh/decimal.js@10.6.0?target=denonext": "208d405529a515467f200d440b28dd77bf3f61f1a12ec0b508ec0784afc828ff",
+ "https://esm.sh/delayed-stream@1.0.0/denonext/delayed-stream.mjs": "051a3501b7b3d3c593b78a2c7305093a8e363c518cd156f1a77117185e312abe",
+ "https://esm.sh/delayed-stream@1.0.0?target=denonext": "d363b81e01f4c886114df14aa660c1a938bbb4be851ff12132260bed0db6126e",
+ "https://esm.sh/entities@6.0.1/decode?target=denonext": "3ccc9b5e285ac182223bec6c9e053ff17814f4d27a31b8abafe35d3b684faaa7",
+ "https://esm.sh/entities@6.0.1/denonext/decode.mjs": "0e11dc867c49cd73eaa3de858276b02727bf6a3e1e5a84be72722cba08697b7d",
+ "https://esm.sh/entities@6.0.1/denonext/escape.mjs": "f23f7faf0499133a54a93a5dc4276f08793b2bb36b539b1bacddcc3b1e746aca",
+ "https://esm.sh/entities@6.0.1/escape?target=denonext": "c3df42c65816226666e5a0560e68e3c058a184684488a26b3dedce05eaa329e2",
+ "https://esm.sh/form-data@4.0.5/denonext/form-data.mjs": "1898b5107b92409a9028d8d8f65213c2de681aba22eeda5b5558958ce2df08cd",
+ "https://esm.sh/form-data@4.0.5?target=denonext": "0a347e31d651ff1c7ea3e89373c5418bc475f826abfc573209d17632be61844a",
+ "https://esm.sh/html-encoding-sniffer@4.0.0/denonext/html-encoding-sniffer.mjs": "7ddb6dc2201652cb21f0ef05cf26123e73fada571849ac98bd9c7f005c3f4f31",
+ "https://esm.sh/html-encoding-sniffer@4.0.0?target=denonext": "d096f813febda0c56a0c54fa5aa1989383fe1923c94150e5b4cdcfa02187f301",
+ "https://esm.sh/http-proxy-agent@7.0.2/denonext/http-proxy-agent.mjs": "1556b1f1ed4065b42820b9557da226cef428a34529593982ab0dda56b9cee3cf",
+ "https://esm.sh/http-proxy-agent@7.0.2?target=denonext": "1c3557e656d2df5ab36e25c827cf0ec6f12502fb3273def69c0fde08c84b22d4",
+ "https://esm.sh/https-proxy-agent@7.0.6/denonext/https-proxy-agent.mjs": "37b0793e160738a4ae695ea567218fb5ad39c458c33ebd5857f06deb3605ffec",
+ "https://esm.sh/https-proxy-agent@7.0.6?target=denonext": "cd42ba7e40cb2f053a2151f66585c0c338818807c0d7208e23a8302c15659b23",
+ "https://esm.sh/iconv-lite@0.6.3/denonext/iconv-lite.mjs": "58314c9a1d16db741c1edb519849067c19f931ea0331a2e3ad5c02b1dcf2f82a",
+ "https://esm.sh/is-potential-custom-element-name@1.0.1/denonext/is-potential-custom-element-name.mjs": "ced6030c333d173179c2a00a27397f71ed68725be65e78be74f2c6c61dea18cd",
+ "https://esm.sh/is-potential-custom-element-name@1.0.1?target=denonext": "78374c1d74ce1440683f94d22341ba09b796b2a86ece0ab387708b66fbd007ce",
+ "https://esm.sh/jsdom@26.0.0/X-ZWJ1ZmZlcnV0aWwsY2FudmFzLHV0Zi04LXZhbGlkYXRl/denonext/jsdom.mjs": "dbc8edb38d4b4138c1fa683f716ba7c2ed1e18857767a34b10756a01e861a155",
+ "https://esm.sh/jsdom@26.0.0?external=canvas,bufferutil,utf-8-validate": "376894d1abef4c5673633645209082c360d7011b55991786a0429e433a6a6941",
+ "https://esm.sh/jsdom@26.1.0/X-ZWJ1ZmZlcnV0aWwsY2FudmFzLHV0Zi04LXZhbGlkYXRl/denonext/jsdom.mjs": "f0a0b0a40aa1c1e25436d413fac2ee94667b2adc199ef26d046a6d174475a527",
+ "https://esm.sh/jsdom@26.1.0?external=canvas,bufferutil,utf-8-validate": "d9cfe54cbdd50721a50d6aff792be0adbadeb92f7718ba57a9939326c3dedc22",
+ "https://esm.sh/lru-cache@10.4.3/denonext/lru-cache.mjs": "ba5ee5ff9067a3a33de530b47c0ce7e81cdff92937e5519258ed422b7e23fbdb",
+ "https://esm.sh/lru-cache@10.4.3?target=denonext": "e0a3f66d0dbb61c67fbec24fb29ba1b18e21dab43178cfba399481560e076382",
+ "https://esm.sh/mime-db@1.52.0/denonext/mime-db.mjs": "f93feb3d7150014b71bd0d06c5bd819db56a089b31b8b79a3b0466bb37ef005e",
+ "https://esm.sh/mime-types@2.1.35/denonext/mime-types.mjs": "704bdb318816fe1360c90a196f7cb3ba6e25fe207707cc2df873f890ad2e5f44",
+ "https://esm.sh/mime-types@2.1.35?target=denonext": "e4cc9a1aabecc1be22d194375ec3b99cc9d51700cc4629ab689975451c0a8ce5",
+ "https://esm.sh/ms@2.1.3/denonext/ms.mjs": "9039464da1f4ae1c2042742d335c82556c048bbe49449b5d0cd5198193afa147",
+ "https://esm.sh/ms@2.1.3?target=denonext": "36f5aa7503ff0ff44ce9e3155a60362d8d3ae5db8db048be5764a3a515b6a263",
+ "https://esm.sh/nwsapi@2.2.22/denonext/nwsapi.mjs": "a127c469c81e7bd9fa7fac9c76a6d14176d0cd9feceb9a237b0df9fe324f4396",
+ "https://esm.sh/nwsapi@2.2.22?target=denonext": "3945757b94dd2343180deed4e385a546b78dbf63f1a35442bab6037b062107ce",
+ "https://esm.sh/parse5@7.3.0/denonext/parse5.mjs": "39564d89f13b5d701ac8f869caea8660ada421fcd19ef2234adfd935cf7cf27e",
+ "https://esm.sh/parse5@7.3.0?target=denonext": "898a8cf4c02510b1cd0f90c9dffdfe9229f31f2dec425310da4404d472137f2e",
+ "https://esm.sh/prom-client@15.1.3": "8dfdb71d7bce0684fcdc43210c6ea05a645b0a6c50e142d122aed6f341e1ebf7",
+ "https://esm.sh/prom-client@15.1.3/denonext/prom-client.mjs": "a01e7faba98754dfc9f0c02cfa27babe7bc199c391c2abb3ffe012833b5884b1",
+ "https://esm.sh/prom-client@15.1.3?pin=v135": "560dcf5c7d90f8f843c82560b4640fdc66ba7ad7ccecce36503751e2825735a9",
+ "https://esm.sh/punycode@2.3.1/denonext/punycode.mjs": "5d108000c361561f4ddedc3f201df2a40a0b251c4d94df75ecc959e5ffcc99ac",
+ "https://esm.sh/punycode@2.3.1?target=denonext": "5b74b6114721e33c740cf6aeafd96838523b5cd9ff3ca71855726bfc8d48d7e3",
+ "https://esm.sh/rrweb-cssom@0.8.0/denonext/rrweb-cssom.mjs": "f610b63ab54323b433430b132329d9b921cb2c007c0529210349f40d6b07e985",
+ "https://esm.sh/rrweb-cssom@0.8.0?target=denonext": "d41fd8fa9368efbb84c06d1b955068a2a6dfae71c2c7bbf9cda0523bd64e9fe0",
+ "https://esm.sh/safer-buffer@2.1.2/denonext/safer-buffer.mjs": "63b601ca3ed03a32349ca04538ac898f2c520dbc6b554246d11c482c31e8a6b8",
+ "https://esm.sh/safer-buffer@2.1.2?target=denonext": "381ed15e73b07affd71d58c070213d027e4b8951c381f44e5f80589d3ea9957b",
+ "https://esm.sh/saxes@6.0.0/denonext/saxes.mjs": "a1160d1240a848f68ad3e280147d0c2b4ef2a9f39e4c70142ff40512c2a6d128",
+ "https://esm.sh/saxes@6.0.0?target=denonext": "49b388620601a8d5fbc00c614d8bb3bf2a59dfdf70df20e67e6dbf51067707ef",
+ "https://esm.sh/supports-color@10.2.2/denonext/supports-color.mjs": "a263c0229c209e3fed166348cd02e96f08996902c9e8e7c6de4ac50c8ee2a748",
+ "https://esm.sh/supports-color@10.2.2?target=denonext": "f938c90ae6175cd0ec937dabb90259b115033d9dfde267b69566089f06f92d06",
+ "https://esm.sh/symbol-tree@3.2.4/denonext/symbol-tree.mjs": "4b03e097480f40b035abeca19562b99968adbd551caeee93672d1a22bb5e0776",
+ "https://esm.sh/symbol-tree@3.2.4?target=denonext": "22b3ce01dc83f3e9ec2a07ea3a01f56532202e91a2f7a956dc494c7f01f51a09",
+ "https://esm.sh/tdigest@0.1.2/denonext/tdigest.mjs": "379f502069e3a3efb26dcc3e72a117e2f9c78e4ce3bf6abd6944b4bf8598cb0a",
+ "https://esm.sh/tdigest@0.1.2?target=denonext": "53806e3c28da0241f722b9d6d921cb4f4fcb628bd5af56d0315acf4865675c6c",
+ "https://esm.sh/tldts-core@6.1.86/denonext/tldts-core.mjs": "00bc0e31dea41c7122d8f807d5e22ee34069bf2766c8b9414bf7f06b4fc4fd4f",
+ "https://esm.sh/tldts-core@6.1.86?target=denonext": "3e313ea81d61bcb8e8497f1a16d4a7b50c61ac10ae9419aa3d51e36015f67aa4",
+ "https://esm.sh/tldts@6.1.86/denonext/tldts.mjs": "62a91e7036004a7b6210619578c1476ac2f752f218899d84aedff757649443c5",
+ "https://esm.sh/tldts@6.1.86?target=denonext": "e770d646c03003b4042fa1ff58f09057d1f53914e56dfe4644ce1f815ff0c137",
+ "https://esm.sh/tough-cookie@5.1.2/denonext/tough-cookie.mjs": "920596fcdf61acb18fe894d3d55b580765f7abdc6bce39a13af259653af8f734",
+ "https://esm.sh/tough-cookie@5.1.2?target=denonext": "0abb332336e4f25ce01badb0fad1522ffa0c460f2a1cbfaeb89e078b864fc2c8",
+ "https://esm.sh/tr46@5.1.1/denonext/tr46.mjs": "cdad0ad3c6087de1297ea9a9fbea3eb43fdfe19500cfcd87b4b161d9bcb27648",
+ "https://esm.sh/tr46@5.1.1?target=denonext": "d2450e422fab3f7456bd2e43be411bd44a968603f49d87c0fc343179bd0bf265",
+ "https://esm.sh/v135/@opentelemetry/api@1.9.0/denonext/api.mjs": "2c4ccab6379206aea31025d84aa2003b0d50e03257cd27d798b3470e5a06673c",
+ "https://esm.sh/v135/bintrees@1.0.2/denonext/bintrees.mjs": "3e4d167e844bae4a53837b85ad9c7a034cc1978e4f6f29e49cf3ce584fd95f01",
+ "https://esm.sh/v135/prom-client@15.1.3/denonext/prom-client.mjs": "ce8d0d95a6a654fd68398f6ef31f566e8f33dd9ee301005abd5e9479cdf35bce",
+ "https://esm.sh/v135/tdigest@0.1.2/denonext/tdigest.mjs": "af68659baf7ee778ce9fabc0fe45b3bba8ac45adae75e7676a56be43ff410bf7",
+ "https://esm.sh/w3c-xmlserializer@5.0.0/denonext/w3c-xmlserializer.mjs": "122e3e552454f1752e443fdf80f4c8ca36109208fb05943d39fc64a5e85499de",
+ "https://esm.sh/w3c-xmlserializer@5.0.0?target=denonext": "078e017f6cb607175c9246ebd38041912488fc813100625bf7502cf47dfe7c7f",
+ "https://esm.sh/webidl-conversions@7.0.0/denonext/webidl-conversions.mjs": "ba51539722c56b6c41341c14ba249f7bf8acd7f132ae1b8d9c0a81cc17360abc",
+ "https://esm.sh/webidl-conversions@7.0.0?target=denonext": "d4b1e1e5302669677ad8274d9c0f0ba59c50e9ab8c3215a3774f3aa206963bd5",
+ "https://esm.sh/whatwg-encoding@3.1.1/denonext/whatwg-encoding.mjs": "771c409a89b301144c200fe8520bce25a72b9108068a5a57e21b76670549bccb",
+ "https://esm.sh/whatwg-encoding@3.1.1?target=denonext": "0e24061fe35afd2b2c93625e4288c643837ae8d491abf572a067b55ecce45c1f",
+ "https://esm.sh/whatwg-mimetype@4.0.0/denonext/whatwg-mimetype.mjs": "077315581483d77f815ed7e227ea4bf7ffdda61b211b471053c4bca423a7fd54",
+ "https://esm.sh/whatwg-mimetype@4.0.0?target=denonext": "a0a0b4ee5d006be602d7e01eee5347c950d89662d502d72db5ba6daa60e92a3d",
+ "https://esm.sh/whatwg-url@14.2.0/denonext/webidl2js-wrapper.mjs": "ce001f19a2ac2752221bc48c77cc3be1f05d4e2044ae1c51b6d1020c9ad5bea3",
+ "https://esm.sh/whatwg-url@14.2.0/denonext/whatwg-url.mjs": "6842833804c93154dd5fe40fe8e0694b5d4bb594113f24c41a4017263bf60cbc",
+ "https://esm.sh/whatwg-url@14.2.0/webidl2js-wrapper?target=denonext": "a28f35282ba1cda458d386f8b85190f54c122183cb0b0735725d6e88b85a4864",
+ "https://esm.sh/whatwg-url@14.2.0?target=denonext": "600345dce237deb981499a6a34248f4db75de14fb73111b52924beff1bb9bd54",
+ "https://esm.sh/ws@8.18.3/X-ZWJ1ZmZlcnV0aWwsdXRmLTgtdmFsaWRhdGU/denonext/ws.mjs": "0db28a8c1ab57c2360e72e3185d11ba884fc889837f30dbd7ad0a9252f9dcc56",
+ "https://esm.sh/ws@8.18.3?external=bufferutil,utf-8-validate&target=denonext": "72053510522a7fb83a263e893e30d7e68955bd51f5cf304d546cc656a15b714a",
+ "https://esm.sh/xml-name-validator@5.0.0/denonext/xml-name-validator.mjs": "d6efa1d81da5e2e5dc725b5c6a7537e4d3729f4b0ba1364c8151be74d7dda168",
+ "https://esm.sh/xml-name-validator@5.0.0?target=denonext": "5387f3c1042cdac99adc6c11b669da12fd30838e49ec27d63110bf8e370bdfce",
+ "https://esm.sh/xmlchars@2.2.0/denonext/xml/1.0/ed5.mjs": "7e95e56afc46284fd6e3e43ec430f636d3db5a0840829b188627c160f36b1c7e",
+ "https://esm.sh/xmlchars@2.2.0/denonext/xml/1.1/ed2.mjs": "bde6c65b73865ce561376fc19316dc52bdf8b0b998fb3c3bdab481d2a4d9de0a",
+ "https://esm.sh/xmlchars@2.2.0/denonext/xmlns/1.0/ed3.mjs": "ab12a41555aa2468e6f8f42649ae209163e9ab835ca05817cb4f95bac8699ce9",
+ "https://esm.sh/xmlchars@2.2.0/xml/1.0/ed5?target=denonext": "b1ca831e2cf0954307ebded209a1657cb27223f2a1fd0b347ffcb4e566e997ad",
+ "https://esm.sh/xmlchars@2.2.0/xml/1.1/ed2?target=denonext": "005781985db506e433617587305e4176c3f3333ac4bb8b0db2a953f0e14bf71c",
+ "https://esm.sh/xmlchars@2.2.0/xmlns/1.0/ed3?target=denonext": "be6502edd92b286ab2a25e3f5db439700ff79daa964aa3d6e4a0c109af35dd36"
+ },
+ "workspace": {
+ "dependencies": [
+ "jsr:@hono/hono@4.7.4",
+ "jsr:@luanrt/googlevideo@2.0.0",
+ "jsr:@std/async@1.0.11",
+ "jsr:@std/cli@^1.0.17",
+ "jsr:@std/encoding@1.0.7",
+ "jsr:@std/fs@1.0.14",
+ "jsr:@std/path@1.0.8",
+ "jsr:@std/toml@1.0.2",
+ "npm:jsdom@26.1.0",
+ "npm:meriyah@6.1.4"
+ ]
+ }
+}
diff --git a/entrypoint.sh b/entrypoint.sh
new file mode 100644
index 0000000000000000000000000000000000000000..0f406ecd04500d160661dcd5c9d2e9e32eb91c8b
--- /dev/null
+++ b/entrypoint.sh
@@ -0,0 +1,51 @@
+#!/usr/bin/env bash
+set -ex
+
+echo "[ENTRYPOINT] Starting WireGuard HTTP Proxy"
+
+# Check if WireGuard config should be generated
+if [[ -z "${WIREGUARD_INTERFACE_PRIVATE_KEY}" ]]; then
+ echo "[ENTRYPOINT] Generating Cloudflare Warp configuration..."
+
+ # Run warp binary to generate config
+ WARP_OUTPUT=$(warp)
+
+ # Parse the warp output to extract config values
+ export WIREGUARD_INTERFACE_PRIVATE_KEY=$(echo "$WARP_OUTPUT" | grep "PrivateKey" | awk '{print $3}')
+ export WIREGUARD_INTERFACE_ADDRESS=$(echo "$WARP_OUTPUT" | grep "Address" | awk '{print $3}')
+ export WIREGUARD_PEER_PUBLIC_KEY=$(echo "$WARP_OUTPUT" | grep "PublicKey" | awk '{print $3}')
+ export WIREGUARD_PEER_ENDPOINT=$(echo "$WARP_OUTPUT" | grep "Endpoint" | awk '{print $3}')
+ export WIREGUARD_INTERFACE_DNS="${WIREGUARD_INTERFACE_DNS:-1.1.1.1}"
+
+ echo "[ENTRYPOINT] Warp config generated successfully"
+else
+ echo "[ENTRYPOINT] Using provided WireGuard configuration"
+fi
+
+# Start the proxy server in the background
+echo "[ENTRYPOINT] Starting HTTP proxy server (internal)..."
+server &
+SERVER_PID=$!
+
+# Wait for proxy to start
+echo "[ENTRYPOINT] Waiting for proxy to be ready on port 8080..."
+while ! curl -v http://127.0.0.1:8080/ 2>&1 | grep "Proxy Running"; do
+ if ! kill -0 $SERVER_PID 2>/dev/null; then
+ echo "[FATAL] Server process exited unexpectedly!"
+ wait $SERVER_PID
+ exit 1
+ fi
+ echo "[ENTRYPOINT] Proxy not ready yet... retrying in 1s"
+ sleep 1
+done
+echo "[ENTRYPOINT] Proxy is ready!"
+
+# Start Proxy Check
+echo "[ENTRYPOINT] Checking proxy connection..."
+curl -s -x http://127.0.0.1:8080 https://cloudflare.com/cdn-cgi/trace
+echo ""
+echo "[ENTRYPOINT] Proxy check complete."
+
+# Start Streamion
+echo "[ENTRYPOINT] Starting Streamion..."
+exec deno task dev
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000000000000000000000000000000000000..4cc55e0ca42ee9e5c593177ee70b1f34f3416419
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,11 @@
+module streamion
+
+go 1.20
+
+require (
+ github.com/caarlos0/env v3.5.0+incompatible
+ golang.org/x/crypto v0.47.0
+ golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb
+)
+
+exclude golang.zx2c4.com/wireguard/tun/netstack v0.0.0-20220703234212-c31a7b1ab478
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/grafana_dashboard.json b/grafana_dashboard.json
new file mode 100644
index 0000000000000000000000000000000000000000..fb9649faf15c9aaaaa441e6d0eec84059260e2fa
--- /dev/null
+++ b/grafana_dashboard.json
@@ -0,0 +1,888 @@
+{
+ "__inputs": [
+ {
+ "name": "DS_PROMETHEUS-REIMU",
+ "label": "prometheus-reimu",
+ "description": "",
+ "type": "datasource",
+ "pluginId": "prometheus",
+ "pluginName": "Prometheus"
+ }
+ ],
+ "__elements": {},
+ "__requires": [
+ {
+ "type": "grafana",
+ "id": "grafana",
+ "name": "Grafana",
+ "version": "12.3.1"
+ },
+ {
+ "type": "datasource",
+ "id": "prometheus",
+ "name": "Prometheus",
+ "version": "1.0.0"
+ },
+ {
+ "type": "panel",
+ "id": "timeseries",
+ "name": "Time series",
+ "version": ""
+ }
+ ],
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": {
+ "type": "grafana",
+ "uid": "-- Grafana --"
+ },
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations & Alerts",
+ "type": "dashboard"
+ }
+ ]
+ },
+ "editable": true,
+ "fiscalYearStartMonth": 0,
+ "graphTooltip": 0,
+ "links": [],
+ "panels": [
+ {
+ "collapsed": false,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 0
+ },
+ "id": 24,
+ "panels": [],
+ "title": "Videoplayback requests",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${DS_PROMETHEUS-REIMU}"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "showValues": false,
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": 0
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 10,
+ "w": 24,
+ "x": 0,
+ "y": 1
+ },
+ "id": 26,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "hideZeros": false,
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "12.3.1",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${DS_PROMETHEUS-REIMU}"
+ },
+ "editorMode": "builder",
+ "expr": "rate(invidious_companion_videoplayback_forbidden_total[$__rate_interval])",
+ "instant": false,
+ "legendFormat": "{{instance}}",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "Forbidden Videoplayback (403 from Youtube servers)",
+ "type": "timeseries"
+ },
+ {
+ "collapsed": false,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 11
+ },
+ "id": 18,
+ "panels": [],
+ "title": "Requests",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${DS_PROMETHEUS-REIMU}"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "showValues": false,
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": 0
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 10,
+ "w": 12,
+ "x": 0,
+ "y": 12
+ },
+ "id": 12,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "hideZeros": false,
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "12.3.1",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${DS_PROMETHEUS-REIMU}"
+ },
+ "disableTextWrap": false,
+ "editorMode": "builder",
+ "expr": "rate(invidious_companion_innertube_successful_request_total[$__rate_interval])",
+ "fullMetaSearch": false,
+ "includeNullMetadata": false,
+ "instant": false,
+ "legendFormat": "{{instance}}",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "Successful Requests Rate",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${DS_PROMETHEUS-REIMU}"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "showValues": false,
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": 0
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 10,
+ "w": 12,
+ "x": 12,
+ "y": 12
+ },
+ "id": 13,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "hideZeros": false,
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "12.3.1",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${DS_PROMETHEUS-REIMU}"
+ },
+ "editorMode": "code",
+ "expr": "rate(invidious_companion_innertube_failed_request_total[$__rate_interval])",
+ "instant": false,
+ "legendFormat": "{{instance}}",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "Failed Requests Rate",
+ "type": "timeseries"
+ },
+ {
+ "collapsed": false,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 22
+ },
+ "id": 19,
+ "panels": [],
+ "title": "Status",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${DS_PROMETHEUS-REIMU}"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineStyle": {
+ "fill": "solid"
+ },
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "showValues": false,
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": 0
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 10,
+ "w": 24,
+ "x": 0,
+ "y": 23
+ },
+ "id": 22,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "hideZeros": false,
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "12.3.1",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${DS_PROMETHEUS-REIMU}"
+ },
+ "editorMode": "code",
+ "expr": "rate(invidious_companion_innertube_error_status_loginRequired_total[$__rate_interval])",
+ "instant": false,
+ "legendFormat": "{{instance}}",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "\"LOGIN_REQUIRED\" Rate",
+ "type": "timeseries"
+ },
+ {
+ "collapsed": false,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 33
+ },
+ "id": 8,
+ "panels": [],
+ "title": "Reasons",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${DS_PROMETHEUS-REIMU}"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineStyle": {
+ "fill": "solid"
+ },
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "showValues": false,
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": 0
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 10,
+ "w": 24,
+ "x": 0,
+ "y": 34
+ },
+ "id": 4,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "hideZeros": false,
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "12.3.1",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${DS_PROMETHEUS-REIMU}"
+ },
+ "editorMode": "code",
+ "expr": "rate(invidious_companion_innertube_error_reason_SignIn_total[$__rate_interval])",
+ "instant": false,
+ "legendFormat": "{{instance}}",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "\"Sign in to confirm you’re not a bot.\" Rate",
+ "type": "timeseries"
+ },
+ {
+ "collapsed": false,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 44
+ },
+ "id": 9,
+ "panels": [],
+ "title": "Subreasons",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${DS_PROMETHEUS-REIMU}"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "showValues": false,
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": 0
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 10,
+ "w": 24,
+ "x": 0,
+ "y": 45
+ },
+ "id": 6,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "hideZeros": false,
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "12.3.1",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${DS_PROMETHEUS-REIMU}"
+ },
+ "editorMode": "code",
+ "expr": "rate(invidious_companion_innertube_error_subreason_ProtectCommunity_total[$__rate_interval])",
+ "instant": false,
+ "legendFormat": "{{instance}}",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "\"This helps protect our community.\" Rate",
+ "type": "timeseries"
+ },
+ {
+ "collapsed": false,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 55
+ },
+ "id": 20,
+ "panels": [],
+ "title": "Jobs",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${DS_PROMETHEUS-REIMU}"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineStyle": {
+ "fill": "solid"
+ },
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "showValues": false,
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": 0
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 9,
+ "w": 24,
+ "x": 0,
+ "y": 56
+ },
+ "id": 16,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "hideZeros": false,
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "12.3.1",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${DS_PROMETHEUS-REIMU}"
+ },
+ "editorMode": "code",
+ "expr": "invidious_companion_potoken_generation_failure_total",
+ "instant": false,
+ "legendFormat": "{{instance}}",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "poToken Generation Failure Rate",
+ "type": "timeseries"
+ }
+ ],
+ "preload": false,
+ "refresh": "10s",
+ "schemaVersion": 42,
+ "tags": [],
+ "templating": {
+ "list": [
+ {
+ "current": {},
+ "label": "datasource",
+ "name": "Datasource",
+ "options": [],
+ "query": "prometheus",
+ "refresh": 1,
+ "regex": "",
+ "type": "datasource"
+ },
+ {
+ "current": {},
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${Datasource}"
+ },
+ "definition": "label_values(invidious_companion_innertube_successful_request_total,job)",
+ "description": "",
+ "label": "Job",
+ "name": "job",
+ "options": [],
+ "query": {
+ "qryType": 1,
+ "query": "label_values(invidious_companion_innertube_successful_request_total,job)",
+ "refId": "PrometheusVariableQueryEditor-VariableQuery"
+ },
+ "refresh": 2,
+ "regex": "",
+ "type": "query"
+ }
+ ]
+ },
+ "time": {
+ "from": "now-1h",
+ "to": "now"
+ },
+ "timepicker": {},
+ "timezone": "browser",
+ "title": "Invidious Companion2",
+ "uid": "1-0-1",
+ "version": 9,
+ "weekStart": "",
+ "id": null
+}
\ No newline at end of file
diff --git a/invidious-companion.service b/invidious-companion.service
new file mode 100644
index 0000000000000000000000000000000000000000..4108b9d8bb1230836dd0380613558d48db8f7678
--- /dev/null
+++ b/invidious-companion.service
@@ -0,0 +1,42 @@
+[Unit]
+Description=invidious-companion (companion for Invidious which handles all the video stream retrieval from YouTube servers)
+After=syslog.target
+After=network.target
+
+[Service]
+RestartSec=2s
+Type=simple
+
+User=invidious
+Group=invidious
+
+# Security hardening - balanced approach for Deno applications
+NoNewPrivileges=true
+PrivateDevices=true
+PrivateTmp=true
+ProtectControlGroups=true
+ProtectHostname=true
+ProtectKernelLogs=true
+ProtectKernelModules=true
+ProtectKernelTunables=true
+ProtectSystem=strict
+RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
+RestrictNamespaces=true
+RestrictSUIDSGID=true
+RestrictRealtime=true
+
+# Filesystem access
+BindReadOnlyPaths=/home/invidious/invidious-companion
+BindPaths=/home/invidious/tmp
+BindPaths=/var/tmp/youtubei.js
+
+WorkingDirectory=/home/invidious/invidious-companion
+ExecStart=/home/invidious/invidious-companion/invidious_companion
+
+Environment=SERVER_SECRET_KEY=CHANGE_ME
+Environment=CACHE_DIRECTORY=/var/tmp/youtubei.js
+
+Restart=always
+
+[Install]
+WantedBy=multi-user.target
diff --git a/server.go b/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..aab7eb2bbebdad7785848c7b7c83e27f6eaafd06
--- /dev/null
+++ b/server.go
@@ -0,0 +1,311 @@
+package main
+
+import (
+ "encoding/base64"
+ "encoding/hex"
+ "fmt"
+ "io"
+ "log"
+ "net"
+ "net/http"
+ "net/netip"
+ "strings"
+ "time"
+
+ "github.com/caarlos0/env"
+ "golang.zx2c4.com/wireguard/conn"
+ "golang.zx2c4.com/wireguard/device"
+ "golang.zx2c4.com/wireguard/tun/netstack"
+)
+
+type params struct {
+ User string `env:"PROXY_USER" envDefault:""`
+ Password string `env:"PROXY_PASS" envDefault:""`
+ Port string `env:"PORT" envDefault:"8080"`
+ // WireGuard Params
+ WgPrivateKey string `env:"WIREGUARD_INTERFACE_PRIVATE_KEY"`
+ WgAddress string `env:"WIREGUARD_INTERFACE_ADDRESS"` // e.g., 10.0.0.2/32
+ WgPeerPublicKey string `env:"WIREGUARD_PEER_PUBLIC_KEY"`
+ WgPeerEndpoint string `env:"WIREGUARD_PEER_ENDPOINT"` // e.g., 1.2.3.4:51820
+ WgDNS string `env:"WIREGUARD_INTERFACE_DNS" envDefault:"1.1.1.1"`
+}
+
+var tnet *netstack.Net
+
+func handleTunneling(w http.ResponseWriter, r *http.Request) {
+ dest := r.URL.Host
+ if dest == "" {
+ dest = r.Host
+ }
+
+ // Hijack the connection first to allow custom response writing
+ hijacker, ok := w.(http.Hijacker)
+ if !ok {
+ http.Error(w, "Hijacking not supported", http.StatusInternalServerError)
+ return
+ }
+ client_conn, _, err := hijacker.Hijack()
+ if err != nil {
+ // If hijack fails, we can't do much as headers might be sent or connection broken
+ http.Error(w, err.Error(), http.StatusServiceUnavailable)
+ return
+ }
+
+ var dest_conn net.Conn
+
+ if tnet == nil {
+ dest_conn, err = net.DialTimeout("tcp", dest, 10*time.Second)
+ } else {
+ // Use tnet.Dial to connect through WireGuard
+ dest_conn, err = tnet.Dial("tcp", dest)
+ }
+
+ if err != nil {
+ log.Printf("[ERROR] TUNNEL Dial failed to %s: %v", dest, err)
+ // Send a 503 to the client through the hijacked connection and close
+ // Simple HTTP response since we hijacked
+ client_conn.Write([]byte("HTTP/1.1 503 Service Unavailable\r\n\r\n"))
+ client_conn.Close()
+ return
+ }
+
+ // Write 200 Connection Established to the client
+ // This signals the client that the tunnel is ready
+ _, err = client_conn.Write([]byte("HTTP/1.1 200 Connection Established\r\n\r\n"))
+ if err != nil {
+ log.Printf("[ERROR] TUNNEL Write 200 failed: %v", err)
+ dest_conn.Close()
+ client_conn.Close()
+ return
+ }
+
+ go transfer(dest_conn, client_conn)
+ go transfer(client_conn, dest_conn)
+}
+
+func transfer(destination io.WriteCloser, source io.ReadCloser) {
+ defer destination.Close()
+ defer source.Close()
+ io.Copy(destination, source)
+}
+
+func handleHTTP(w http.ResponseWriter, r *http.Request) {
+ transport := http.DefaultTransport.(*http.Transport).Clone()
+
+ if tnet != nil {
+ // Use tnet.DialContext for HTTP requests
+ transport.DialContext = tnet.DialContext
+ }
+
+ resp, err := transport.RoundTrip(r)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusServiceUnavailable)
+ return
+ }
+ defer resp.Body.Close()
+ copyHeader(w.Header(), resp.Header)
+ w.WriteHeader(resp.StatusCode)
+ io.Copy(w, resp.Body)
+}
+
+func handleDebug(w http.ResponseWriter, r *http.Request) {
+ if tnet == nil {
+ w.Header().Set("Content-Type", "text/plain")
+ w.WriteHeader(http.StatusOK)
+ w.Write([]byte("Error: WireGuard not initialized (Direct Mode)"))
+ return
+ }
+
+ client := &http.Client{
+ Transport: &http.Transport{
+ DialContext: tnet.DialContext,
+ },
+ Timeout: 10 * time.Second,
+ }
+
+ resp, err := client.Get("http://ifconfig.me")
+ if err != nil {
+ log.Printf("[DEBUG] VPN Test Failed: %v", err)
+ http.Error(w, fmt.Sprintf("VPN Connection Failed: %v", err), http.StatusServiceUnavailable)
+ return
+ }
+ defer resp.Body.Close()
+
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ http.Error(w, fmt.Sprintf("Failed to read response: %v", err), http.StatusInternalServerError)
+ return
+ }
+
+ log.Printf("[DEBUG] VPN Test Success. IP: %s", string(body))
+ w.Header().Set("Content-Type", "text/plain")
+ w.WriteHeader(http.StatusOK)
+ w.Write([]byte(fmt.Sprintf("VPN Connected! Public IP: %s", string(body))))
+}
+
+func copyHeader(dst, src http.Header) {
+ for k, vv := range src {
+ for _, v := range vv {
+ dst.Add(k, v)
+ }
+ }
+}
+
+func startWireGuard(cfg params) error {
+ if cfg.WgPrivateKey == "" || cfg.WgPeerEndpoint == "" {
+ log.Println("[INFO] WireGuard config missing, running in DIRECT mode (no VPN)")
+ return nil
+ }
+
+ log.Println("[INFO] Initializing Userspace WireGuard...")
+
+ localIPs := []netip.Addr{}
+ if cfg.WgAddress != "" {
+ // Handle CIDR notation if present (e.g., 10.0.0.2/32)
+ addrStr := strings.Split(cfg.WgAddress, "/")[0]
+ addr, err := netip.ParseAddr(addrStr)
+ if err == nil {
+ localIPs = append(localIPs, addr)
+ log.Printf("[INFO] Local VPN IP: %s", addr)
+ } else {
+ log.Printf("[WARN] Failed to parse local IP: %v", err)
+ }
+ }
+
+ dnsIP, err := netip.ParseAddr(cfg.WgDNS)
+ if err != nil {
+ log.Printf("[WARN] Failed to parse DNS IP, using default: %v", err)
+ dnsIP, _ = netip.ParseAddr("1.1.1.1")
+ }
+ log.Printf("[INFO] DNS Server: %s", dnsIP)
+
+ log.Println("[INFO] Creating virtual network interface...")
+ tunDev, tnetInstance, err := netstack.CreateNetTUN(
+ localIPs,
+ []netip.Addr{dnsIP},
+ 1420,
+ )
+ if err != nil {
+ return fmt.Errorf("failed to create TUN: %w", err)
+ }
+ tnet = tnetInstance
+ log.Println("[INFO] Virtual TUN device created successfully")
+
+ log.Println("[INFO] Initializing WireGuard device...")
+ dev := device.NewDevice(tunDev, conn.NewDefaultBind(), device.NewLogger(device.LogLevelSilent, ""))
+
+ log.Printf("[INFO] Configuring peer endpoint: %s", cfg.WgPeerEndpoint)
+
+ // Convert keys from Base64 to Hex
+ // wireguard-go expects hex keys in UAPI, but inputs are usually Base64
+ privateKeyHex, err := base64ToHex(cfg.WgPrivateKey)
+ if err != nil {
+ return fmt.Errorf("invalid private key (base64 decode failed): %w", err)
+ }
+
+ publicKeyHex, err := base64ToHex(cfg.WgPeerPublicKey)
+ if err != nil {
+ return fmt.Errorf("invalid peer public key (base64 decode failed): %w", err)
+ }
+
+ uapi := fmt.Sprintf(`private_key=%s
+public_key=%s
+endpoint=%s
+allowed_ip=0.0.0.0/0
+`, privateKeyHex, publicKeyHex, cfg.WgPeerEndpoint)
+
+ if err := dev.IpcSet(uapi); err != nil {
+ return fmt.Errorf("failed to configure device: %w", err)
+ }
+ log.Println("[INFO] WireGuard peer configured")
+
+ if err := dev.Up(); err != nil {
+ return fmt.Errorf("failed to bring up device: %w", err)
+ }
+
+ log.Println("[SUCCESS] WireGuard interface is UP - All traffic will route through VPN")
+ return nil
+}
+
+func main() {
+ log.SetFlags(log.LstdFlags | log.Lmsgprefix)
+ log.Println("[STARTUP] Initializing HTTP Proxy with Userspace WireGuard")
+
+ cfg := params{}
+ if err := env.Parse(&cfg); err != nil {
+ log.Printf("[WARN] Config parse warning: %+v\n", err)
+ }
+
+ log.Printf("[CONFIG] Proxy Port: %s", cfg.Port)
+ if cfg.User != "" {
+ log.Printf("[CONFIG] Authentication: Enabled (user: %s)", cfg.User)
+ } else {
+ log.Println("[CONFIG] Authentication: Disabled")
+ }
+
+ if err := startWireGuard(cfg); err != nil {
+ log.Fatalf("[FATAL] Failed to start WireGuard: %v", err)
+ }
+
+ log.Printf("[STARTUP] Starting HTTP proxy server on port %s\n", cfg.Port)
+
+ server := &http.Server{
+ Addr: ":" + cfg.Port,
+ Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if cfg.User != "" && cfg.Password != "" {
+ user, pass, ok := r.BasicAuth()
+ if !ok || user != cfg.User || pass != cfg.Password {
+ log.Printf("[AUTH] Unauthorized access attempt from %s", r.RemoteAddr)
+ w.Header().Set("Proxy-Authenticate", `Basic realm="Proxy"`)
+ http.Error(w, "Unauthorized", http.StatusProxyAuthRequired)
+ return
+ }
+ }
+
+ // Handle CONNECT (HTTPS tunnel)
+ if r.Method == http.MethodConnect {
+ log.Printf("[CONNECT] %s -> %s", r.RemoteAddr, r.Host)
+ handleTunneling(w, r)
+ return
+ }
+
+ // Direct requests to the proxy server (Health check & Debug)
+ // We check r.URL.Host == "" which means it's a direct request, not a proxy request
+ if r.URL.Host == "" {
+ if r.URL.Path == "/" {
+ log.Printf("[HEALTH] Health check from %s", r.RemoteAddr)
+ w.WriteHeader(http.StatusOK)
+ if tnet != nil {
+ w.Write([]byte("Proxy Running via Userspace WireGuard"))
+ } else {
+ w.Write([]byte("Proxy Running in Direct Mode (No VPN)"))
+ }
+ return
+ }
+
+ if r.URL.Path == "/debug" {
+ log.Printf("[DEBUG] Debug check from %s", r.RemoteAddr)
+ handleDebug(w, r)
+ return
+ }
+ }
+
+ // Proxy HTTP requests
+ log.Printf("[HTTP] %s %s -> %s", r.Method, r.RemoteAddr, r.URL.String())
+ handleHTTP(w, r)
+ }),
+ }
+
+ log.Println("[READY] Proxy server is ready to accept connections")
+ if err := server.ListenAndServe(); err != nil {
+ log.Fatalf("[FATAL] Server error: %v", err)
+ }
+}
+
+func base64ToHex(b64 string) (string, error) {
+ decoded, err := base64.StdEncoding.DecodeString(b64)
+ if err != nil {
+ return "", err
+ }
+ return hex.EncodeToString(decoded), nil
+}
diff --git a/src/.DS_Store b/src/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..0d8825abb22885494fb1006384f2270827accf73
Binary files /dev/null and b/src/.DS_Store differ
diff --git a/src/constants.ts b/src/constants.ts
new file mode 100644
index 0000000000000000000000000000000000000000..973d1a14f79f189ae4b488b98b77289c70b4f9af
--- /dev/null
+++ b/src/constants.ts
@@ -0,0 +1,11 @@
+// Set to `undefined` if it's no longer in use!
+//
+// Old Players IDs are usually available for a few more days after Youtube
+// rolls out a new Player. This is helpful when Youtube.JS is not able to
+// extract the signature decipher algorithm and we need to wait for a fix
+// in Youtube.JS.
+export const PLAYER_ID = undefined;
+
+// Error message shown when tokenMinter is not yet ready
+export const TOKEN_MINTER_NOT_READY_MESSAGE =
+ "Companion is starting. Please wait until a valid potoken is found. If this process takes too long, please consult: https://docs.invidious.io/youtube-errors-explained/#po-token-initialization-taking-too-much-time-to-complete";
diff --git a/src/lib/extra/emptyExport.ts b/src/lib/extra/emptyExport.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ff8b4c56321a3362fc00224b01800f62466f9a1f
--- /dev/null
+++ b/src/lib/extra/emptyExport.ts
@@ -0,0 +1 @@
+export default {};
diff --git a/src/lib/helpers/config.ts b/src/lib/helpers/config.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5eb2875af1fccbdf20468c83a4f87eba7d5e48a4
--- /dev/null
+++ b/src/lib/helpers/config.ts
@@ -0,0 +1,180 @@
+import { z, ZodError } from "zod";
+import { parse } from "@std/toml";
+
+export const ConfigSchema = z.object({
+ server: z.object({
+ port: z.number().default(Number(Deno.env.get("PORT")) || 8282),
+ host: z.string().default(Deno.env.get("HOST") || "127.0.0.1"),
+ use_unix_socket: z.boolean().default(
+ Deno.env.get("SERVER_USE_UNIX_SOCKET") === "true" || false,
+ ),
+ unix_socket_path: z.string().default(
+ Deno.env.get("SERVER_UNIX_SOCKET_PATH") ||
+ "/tmp/invidious-companion.sock",
+ ),
+ base_path: z.string()
+ .default(Deno.env.get("SERVER_BASE_PATH") || "/companion")
+ .refine(
+ (path) => path.startsWith("/"),
+ {
+ message:
+ "SERVER_BASE_PATH must start with a forward slash (/). Example: '/companion'",
+ },
+ )
+ .refine(
+ (path) => !path.endsWith("/") || path === "/",
+ {
+ message:
+ "SERVER_BASE_PATH must not end with a forward slash (/) unless it's the root path. Example: '/companion' not '/companion/'",
+ },
+ )
+ .refine(
+ (path) => !path.includes("//"),
+ {
+ message:
+ "SERVER_BASE_PATH must not contain double slashes (//). Example: '/companion' not '//companion' or '/comp//anion'",
+ },
+ ),
+ secret_key: z.preprocess(
+ (val) =>
+ val === undefined
+ ? Deno.env.get("SERVER_SECRET_KEY") || ""
+ : val,
+ z.string().min(1),
+ ).default(undefined),
+ verify_requests: z.boolean().default(
+ Deno.env.get("SERVER_VERIFY_REQUESTS") === "true" || false,
+ ),
+ encrypt_query_params: z.boolean().default(
+ Deno.env.get("SERVER_ENCRYPT_QUERY_PARAMS") === "true" || false,
+ ),
+ enable_metrics: z.boolean().default(
+ Deno.env.get("SERVER_ENABLE_METRICS") === "true" || false,
+ ),
+ }).strict().default({}),
+ cache: z.object({
+ enabled: z.boolean().default(
+ Deno.env.get("CACHE_ENABLED") === "false" ? false : true,
+ ),
+ directory: z.string().default(
+ Deno.env.get("CACHE_DIRECTORY") || "/var/tmp",
+ ),
+ }).strict().default({}),
+ networking: z.object({
+ proxy: z.string().nullable().default(Deno.env.get("PROXY") || null),
+ auto_proxy: z.boolean().default(
+ Deno.env.get("NETWORKING_AUTO_PROXY") === "true" || false,
+ ),
+ vpn_source: z.number().default(
+ Number(Deno.env.get("NETWORKING_VPN_SOURCE")) || 1,
+ ),
+ ipv6_block: z.string().nullable().default(
+ Deno.env.get("NETWORKING_IPV6_BLOCK") || null,
+ ),
+ fetch: z.object({
+ timeout_ms: z.number().default(
+ Number(Deno.env.get("NETWORKING_FETCH_TIMEOUT_MS")) || 30_000,
+ ),
+ retry: z.object({
+ enabled: z.boolean().default(
+ Deno.env.get("NETWORKING_FETCH_RETRY_ENABLED") === "true" ||
+ false,
+ ),
+ times: z.number().optional().default(
+ Number(Deno.env.get("NETWORKING_FETCH_RETRY_TIMES")) || 1,
+ ),
+ initial_debounce: z.number().optional().default(
+ Number(
+ Deno.env.get("NETWORKING_FETCH_RETRY_INITIAL_DEBOUNCE"),
+ ) || 0,
+ ),
+ debounce_multiplier: z.number().optional().default(
+ Number(
+ Deno.env.get(
+ "NETWORKING_FETCH_RETRY_DEBOUNCE_MULTIPLIER",
+ ),
+ ) || 0,
+ ),
+ }).strict().default({}),
+ }).strict().default({}),
+ videoplayback: z.object({
+ ump: z.boolean().default(
+ Deno.env.get("NETWORKING_VIDEOPLAYBACK_UMP") === "true" ||
+ false,
+ ),
+ video_fetch_chunk_size_mb: z.number().default(
+ Number(
+ Deno.env.get(
+ "NETWORKING_VIDEOPLAYBACK_VIDEO_FETCH_CHUNK_SIZE_MB",
+ ),
+ ) || 5,
+ ),
+ }).strict().default({}),
+ }).strict().default({}),
+ jobs: z.object({
+ youtube_session: z.object({
+ po_token_enabled: z.boolean().default(
+ Deno.env.get("JOBS_YOUTUBE_SESSION_PO_TOKEN_ENABLED") ===
+ "false"
+ ? false
+ : true,
+ ),
+ frequency: z.string().default(
+ Deno.env.get("JOBS_YOUTUBE_SESSION_FREQUENCY") || "*/5 * * * *",
+ ),
+ }).strict().default({}),
+ }).strict().default({}),
+ youtube_session: z.object({
+ oauth_enabled: z.boolean().default(
+ Deno.env.get("YOUTUBE_SESSION_OAUTH_ENABLED") === "true" || false,
+ ),
+ cookies: z.string().default(
+ Deno.env.get("YOUTUBE_SESSION_COOKIES") || "",
+ ),
+ }).strict().default({}),
+}).strict();
+
+export type Config = z.infer;
+
+export async function parseConfig() {
+ const configFileName = Deno.env.get("CONFIG_FILE") || "config/config.toml";
+ const configFileContents = await Deno.readTextFile(configFileName).catch(
+ () => null,
+ );
+ if (configFileContents) {
+ console.log("[INFO] Using custom settings local file");
+ } else {
+ console.log(
+ "[INFO] No local config file found, using default config",
+ );
+ }
+
+ try {
+ const rawConfig = configFileContents ? parse(configFileContents) : {};
+ const validatedConfig = ConfigSchema.parse(rawConfig);
+
+ console.log("Loaded Configuration", validatedConfig);
+
+ return validatedConfig;
+ } catch (err) {
+ let errorMessage =
+ "There is an error in your configuration, check your environment variables";
+ if (configFileContents) {
+ errorMessage +=
+ ` or in your configuration file located at ${configFileName}`;
+ }
+ console.log(errorMessage);
+ if (err instanceof ZodError) {
+ console.log(err.issues);
+ // Include detailed error information in the thrown error for testing
+ const detailedMessage = err.issues.map((issue) =>
+ `${issue.path.join(".")}: ${issue.message}`
+ ).join("; ");
+ throw new Error(
+ `Failed to parse configuration file: ${detailedMessage}`,
+ );
+ }
+ // rethrow error if not Zod
+ throw err;
+ }
+}
diff --git a/src/lib/helpers/encodeRFC5987ValueChars.ts b/src/lib/helpers/encodeRFC5987ValueChars.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7cfed8cd1bebb9596877b1696738dc8010501dab
--- /dev/null
+++ b/src/lib/helpers/encodeRFC5987ValueChars.ts
@@ -0,0 +1,21 @@
+// Taken from
+// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent#encoding_for_content-disposition_and_link_headers
+export function encodeRFC5987ValueChars(str: string) {
+ return (
+ encodeURIComponent(str)
+ // The following creates the sequences %27 %28 %29 %2A (Note that
+ // the valid encoding of "*" is %2A, which necessitates calling
+ // toUpperCase() to properly encode). Although RFC3986 reserves "!",
+ // RFC5987 does not, so we do not need to escape it.
+ .replace(
+ /['()*]/g,
+ (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`,
+ )
+ // The following are not required for percent-encoding per RFC5987,
+ // so we can allow for a little better readability over the wire: |`^
+ .replace(
+ /%(7C|60|5E)/g,
+ (_str, hex) => String.fromCharCode(parseInt(hex, 16)),
+ )
+ );
+}
diff --git a/src/lib/helpers/encryptQuery.ts b/src/lib/helpers/encryptQuery.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cf253adad278bc0c2a735e8287a160bbd909128b
--- /dev/null
+++ b/src/lib/helpers/encryptQuery.ts
@@ -0,0 +1,56 @@
+import { decodeBase64, encodeBase64 } from "@std/encoding/base64";
+import { Aes } from "crypto/aes.ts";
+import { Ecb, Padding } from "crypto/block-modes.ts";
+import type { Config } from "./config.ts";
+
+export const encryptQuery = (
+ queryParams: string,
+ config: Config,
+): string => {
+ try {
+ const cipher = new Ecb(
+ Aes,
+ new TextEncoder().encode(
+ config.server.secret_key,
+ ),
+ Padding.PKCS7,
+ );
+
+ const encodedData = new TextEncoder().encode(
+ queryParams,
+ );
+
+ const encryptedData = cipher.encrypt(encodedData);
+
+ return encodeBase64(encryptedData);
+ } catch (err) {
+ console.error("[ERROR] Failed to encrypt query parameters:", err);
+ return "";
+ }
+};
+
+export const decryptQuery = (
+ queryParams: string,
+ config: Config,
+): string => {
+ try {
+ const decipher = new Ecb(
+ Aes,
+ new TextEncoder().encode(config.server.secret_key),
+ Padding.PKCS7,
+ );
+
+ const decryptedData = new TextDecoder().decode(
+ decipher.decrypt(
+ decodeBase64(
+ queryParams,
+ ),
+ ),
+ );
+
+ return decryptedData;
+ } catch (err) {
+ console.error("[ERROR] Failed to decrypt query parameters:", err);
+ return "";
+ }
+};
diff --git a/src/lib/helpers/getFetchClient.ts b/src/lib/helpers/getFetchClient.ts
new file mode 100644
index 0000000000000000000000000000000000000000..acb41457c8ebfc69702b1340886d69afcceb6052
--- /dev/null
+++ b/src/lib/helpers/getFetchClient.ts
@@ -0,0 +1,107 @@
+import { retry, type RetryOptions } from "@std/async";
+import type { Config } from "./config.ts";
+import { generateRandomIPv6 } from "./ipv6Rotation.ts";
+import { getCurrentProxy } from "./proxyManager.ts";
+
+type FetchInputParameter = Parameters[0];
+type FetchInitParameterWithClient =
+ | RequestInit
+ | RequestInit & { client: Deno.HttpClient };
+type FetchReturn = ReturnType;
+
+export const getFetchClient = (config: Config): {
+ (
+ input: FetchInputParameter,
+ init?: FetchInitParameterWithClient,
+ ): FetchReturn;
+} => {
+ return async (
+ input: FetchInputParameter,
+ init?: RequestInit,
+ ) => {
+ // Use auto-fetched proxy if enabled, otherwise use configured proxy
+ const proxyAddress = config.networking.auto_proxy
+ ? getCurrentProxy()
+ : config.networking.proxy;
+ const ipv6Block = config.networking.ipv6_block;
+
+ // If proxy or IPv6 rotation is configured, create a custom HTTP client
+ if (proxyAddress || ipv6Block) {
+ const clientOptions: Deno.CreateHttpClientOptions = {};
+
+ if (proxyAddress) {
+ try {
+ const proxyUrl = new URL(proxyAddress);
+ // Extract credentials if present
+ if (proxyUrl.username && proxyUrl.password) {
+ clientOptions.proxy = {
+ url: `${proxyUrl.protocol}//${proxyUrl.host}`,
+ basicAuth: {
+ username: decodeURIComponent(proxyUrl.username),
+ password: decodeURIComponent(proxyUrl.password),
+ },
+ };
+ } else {
+ clientOptions.proxy = {
+ url: proxyAddress,
+ };
+ }
+ } catch {
+ clientOptions.proxy = {
+ url: proxyAddress,
+ };
+ }
+ }
+
+ if (ipv6Block) {
+ clientOptions.localAddress = generateRandomIPv6(ipv6Block);
+ }
+
+ const client = Deno.createHttpClient(clientOptions);
+ const fetchRes = await fetchShim(config, input, {
+ client,
+ headers: init?.headers,
+ method: init?.method,
+ body: init?.body,
+ });
+ client.close(); // Important: close client to avoid leaking resources
+ return new Response(fetchRes.body, {
+ status: fetchRes.status,
+ headers: fetchRes.headers,
+ });
+ }
+
+ return fetchShim(config, input, init);
+ };
+};
+
+function fetchShim(
+ config: Config,
+ input: FetchInputParameter,
+ init?: FetchInitParameterWithClient,
+): FetchReturn {
+ const fetchTimeout = config.networking.fetch?.timeout_ms;
+ const fetchRetry = config.networking.fetch?.retry?.enabled;
+ const fetchMaxAttempts = config.networking.fetch?.retry?.times;
+ const fetchInitialDebounce = config.networking.fetch?.retry
+ ?.initial_debounce;
+ const fetchDebounceMultiplier = config.networking.fetch?.retry
+ ?.debounce_multiplier;
+ const retryOptions: RetryOptions = {
+ maxAttempts: fetchMaxAttempts,
+ minTimeout: fetchInitialDebounce,
+ multiplier: fetchDebounceMultiplier,
+ jitter: 0,
+ };
+
+ const callFetch = () =>
+ fetch(input, {
+ // only set the AbortSignal if the timeout is supplied in the config
+ signal: fetchTimeout
+ ? AbortSignal.timeout(Number(fetchTimeout))
+ : null,
+ ...(init || {}),
+ });
+ // if retry enabled, call retry with the fetch shim, otherwise pass the fetch shim back directly
+ return fetchRetry ? retry(callFetch, retryOptions) : callFetch();
+}
diff --git a/src/lib/helpers/ipv6Rotation.ts b/src/lib/helpers/ipv6Rotation.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e8fba3bb7ede8dd2958401159f5806b88580bd9b
--- /dev/null
+++ b/src/lib/helpers/ipv6Rotation.ts
@@ -0,0 +1,109 @@
+/**
+ * IPv6 Rotation Utility
+ *
+ * This module provides IPv6 address rotation functionality to help avoid
+ * "Please login" errors from YouTube by sending each request from a unique
+ * IPv6 address within a configured IPv6 block.
+ *
+ * Setup instructions: https://gist.github.com/unixfox/2a9dbcb23d8f69c4582f7c85a849d5cc#linux-setup
+ */
+
+/**
+ * Generate a random IPv6 address within the specified IPv6 block
+ * @param ipv6Block - The IPv6 block in CIDR notation (e.g., "2001:db8::/32")
+ * @returns A random IPv6 address within the specified IPv6 block
+ */
+export function generateRandomIPv6(ipv6Block: string): string {
+ // Parse the IPv6 block
+ const [baseAddress, blockSize] = ipv6Block.split("/");
+ const blockBits = parseInt(blockSize, 10);
+
+ if (isNaN(blockBits) || blockBits < 1 || blockBits > 128) {
+ throw new Error("Invalid IPv6 block size");
+ }
+
+ // Expand IPv6 address to full form
+ const expandedBase = expandIPv6(baseAddress);
+
+ // Convert to binary representation
+ const baseBytes = ipv6ToBytes(expandedBase);
+
+ // Randomize all bits after the block prefix
+ for (let i = Math.floor(blockBits / 8); i < 16; i++) {
+ const bitOffset = Math.max(0, blockBits - i * 8);
+ if (bitOffset === 0) {
+ // Fully random byte
+ baseBytes[i] = Math.floor(Math.random() * 256);
+ } else if (bitOffset < 8) {
+ // Partially random byte
+ const mask = (1 << (8 - bitOffset)) - 1;
+ const randomPart = Math.floor(Math.random() * (mask + 1));
+ baseBytes[i] = (baseBytes[i] & ~mask) | randomPart;
+ }
+ // else: keep the original byte (bitOffset >= 8)
+ }
+
+ // Convert back to IPv6 string
+ return bytesToIPv6(baseBytes);
+}
+
+/**
+ * Expand an IPv6 address to its full form
+ */
+function expandIPv6(address: string): string {
+ // Handle :: expansion
+ if (address.includes("::")) {
+ const parts = address.split("::");
+ const leftParts = parts[0] ? parts[0].split(":") : [];
+ const rightParts = parts[1] ? parts[1].split(":") : [];
+ const missingParts = 8 - leftParts.length - rightParts.length;
+ const middle = Array(missingParts).fill("0000");
+ const allParts = [...leftParts, ...middle, ...rightParts];
+ return allParts.map((p) => p.padStart(4, "0")).join(":");
+ }
+
+ // Pad each part to 4 characters
+ return address.split(":").map((p) => p.padStart(4, "0")).join(":");
+}
+
+/**
+ * Convert IPv6 address string to byte array
+ */
+function ipv6ToBytes(address: string): number[] {
+ const parts = address.split(":");
+ const bytes: number[] = [];
+
+ for (const part of parts) {
+ const value = parseInt(part, 16);
+ bytes.push((value >> 8) & 0xFF);
+ bytes.push(value & 0xFF);
+ }
+
+ return bytes;
+}
+
+/**
+ * Convert byte array back to IPv6 address string
+ */
+function bytesToIPv6(bytes: number[]): string {
+ const parts: string[] = [];
+
+ for (let i = 0; i < 16; i += 2) {
+ const value = (bytes[i] << 8) | bytes[i + 1];
+ parts.push(value.toString(16));
+ }
+
+ // Compress consecutive zeros
+ let ipv6 = parts.join(":");
+
+ // Find the longest sequence of consecutive zeros
+ const zeroSequences = ipv6.match(/(^|:)(0:)+/g);
+ if (zeroSequences) {
+ const longestZeroSeq = zeroSequences.reduce((a, b) =>
+ a.length > b.length ? a : b
+ );
+ ipv6 = ipv6.replace(longestZeroSeq, longestZeroSeq[0] + ":");
+ }
+
+ return ipv6;
+}
diff --git a/src/lib/helpers/jsInterpreter.ts b/src/lib/helpers/jsInterpreter.ts
new file mode 100644
index 0000000000000000000000000000000000000000..524ac3c39e4182b2d56cb492ea30ee1019e3b198
--- /dev/null
+++ b/src/lib/helpers/jsInterpreter.ts
@@ -0,0 +1,22 @@
+import { Platform, Types } from "youtubei.js";
+
+// https://ytjs.dev/guide/getting-started.html#providing-a-custom-javascript-interpreter
+// deno-lint-ignore require-await
+export const jsInterpreter = Platform.shim.eval = async (
+ data: Types.BuildScriptResult,
+ env: Record,
+) => {
+ const properties = [];
+
+ if (env.n) {
+ properties.push(`n: exportedVars.nFunction("${env.n}")`);
+ }
+
+ if (env.sig) {
+ properties.push(`sig: exportedVars.sigFunction("${env.sig}")`);
+ }
+
+ const code = `${data.output}\nreturn { ${properties.join(", ")} }`;
+
+ return new Function(code)();
+};
diff --git a/src/lib/helpers/metrics.ts b/src/lib/helpers/metrics.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ca972dfdb8ac5339ff9e0442d191989bdadf915d
--- /dev/null
+++ b/src/lib/helpers/metrics.ts
@@ -0,0 +1,126 @@
+import { IRawResponse } from "youtubei.js";
+import { Counter, Registry } from "prom-client";
+
+export class Metrics {
+ private METRICS_PREFIX = "invidious_companion_";
+ public register = new Registry();
+
+ public createCounter(name: string, help?: string): Counter {
+ return new Counter({
+ name: `${this.METRICS_PREFIX}${name}`,
+ help: help || "No help has been provided for this metric",
+ registers: [this.register],
+ });
+ }
+
+ public potokenGenerationFailure = this.createCounter(
+ "potoken_generation_failure_total",
+ "Number of times that the PoToken generation job has failed for whatever reason",
+ );
+
+ private innertubeErrorStatusLoginRequired = this.createCounter(
+ "innertube_error_status_loginRequired_total",
+ 'Number of times that the status "LOGIN_REQUIRED" has been returned by Innertube API',
+ );
+
+ private innertubeErrorStatusUnknown = this.createCounter(
+ "innertube_error_status_unknown_total",
+ "Number of times that an unknown status has been returned by Innertube API",
+ );
+
+ private innertubeErrorReasonSignIn = this.createCounter(
+ "innertube_error_reason_SignIn_total",
+ 'Number of times that the message "Sign in to confirm you’re not a bot." has been returned by Innertube API',
+ );
+
+ private innertubeErrorSubreasonProtectCommunity = this.createCounter(
+ "innertube_error_subreason_ProtectCommunity_total",
+ 'Number of times that the message "This helps protect our community." has been returned by Innertube API',
+ );
+
+ private innertubeErrorReasonUnknown = this.createCounter(
+ "innertube_error_reason_unknown_total",
+ "Number of times that an unknown reason has been returned by the Innertube API",
+ );
+
+ private innertubeErrorSubreasonUnknown = this.createCounter(
+ "innertube_error_subreason_unknown_total",
+ "Number of times that an unknown subreason has been returned by the Innertube API",
+ );
+
+ public innertubeSuccessfulRequest = this.createCounter(
+ "innertube_successful_request_total",
+ "Number successful requests made to the Innertube API",
+ );
+
+ private innertubeFailedRequest = this.createCounter(
+ "innertube_failed_request_total",
+ "Number failed requests made to the Innertube API for whatever reason",
+ );
+
+ private checkStatus(videoData: IRawResponse) {
+ const status = videoData.playabilityStatus?.status;
+
+ return {
+ unplayable: status ===
+ "UNPLAYABLE",
+ contentCheckRequired: status ===
+ "CONTENT_CHECK_REQUIRED",
+ loginRequired: status === "LOGIN_REQUIRED",
+ };
+ }
+
+ private checkReason(videoData: IRawResponse) {
+ const reason = videoData.playabilityStatus?.reason;
+
+ return {
+ signInToConfirmAge: reason?.includes(
+ "Sign in to confirm your age",
+ ),
+ SignInToConfirmBot: reason?.includes(
+ "Sign in to confirm you’re not a bot",
+ ),
+ };
+ }
+
+ private checkSubreason(videoData: IRawResponse) {
+ const subReason = videoData.playabilityStatus?.errorScreen
+ ?.playerErrorMessageRenderer
+ ?.subreason?.runs?.[0]?.text;
+
+ return {
+ thisHelpsProtectCommunity: subReason?.includes(
+ "This helps protect our community",
+ ),
+ };
+ }
+
+ public checkInnertubeResponse(videoData: IRawResponse) {
+ this.innertubeFailedRequest.inc();
+ const status = this.checkStatus(videoData);
+
+ if (status.contentCheckRequired || status.unplayable) return;
+
+ if (status.loginRequired) {
+ this.innertubeErrorStatusLoginRequired.inc();
+ const reason = this.checkReason(videoData);
+
+ if (reason.signInToConfirmAge) return;
+
+ if (reason.SignInToConfirmBot) {
+ this.innertubeErrorReasonSignIn.inc();
+ const subReason = this.checkSubreason(videoData);
+
+ if (subReason.thisHelpsProtectCommunity) {
+ this.innertubeErrorSubreasonProtectCommunity.inc();
+ } else {
+ this.innertubeErrorSubreasonUnknown.inc();
+ }
+ } else {
+ this.innertubeErrorReasonUnknown.inc();
+ }
+ } else {
+ this.innertubeErrorStatusUnknown.inc();
+ }
+ }
+}
diff --git a/src/lib/helpers/proxyManager.ts b/src/lib/helpers/proxyManager.ts
new file mode 100644
index 0000000000000000000000000000000000000000..553ffdbd06a387026307290ef637457f9ecd196d
--- /dev/null
+++ b/src/lib/helpers/proxyManager.ts
@@ -0,0 +1,452 @@
+/**
+ * Automatic Proxy Manager
+ * Fetches free proxies from antpeak.com API and auto-rotates when they fail.
+ * Tests proxies against YouTube to ensure they work for the application's needs.
+ */
+
+import { fetchUrbanProxy } from "./urbanProxy.ts";
+
+// --- Configuration ---
+const API_BASE = "https://antpeak.com";
+const USER_AGENT =
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
+const APP_VERSION = "3.7.8";
+const YOUTUBE_TEST_URL = "https://www.youtube.com/watch?v=bzbsJGMVHxQ";
+const CUSTOM_PROXY_URL = "https://ytdlp-api-gbdn.onrender.com/proxies";
+
+// --- Types ---
+interface DeviceInfo {
+ udid: string;
+ appVersion: string;
+ platform: string;
+ platformVersion: string;
+ timeZone: string;
+ deviceName: string;
+}
+
+interface Location {
+ id: string;
+ region: string;
+ name: string;
+ countryCode: string;
+ type: number;
+ proxyType: number;
+}
+
+interface ProxyServer {
+ addresses: string[];
+ protocol: string;
+ port: number;
+ username?: string;
+ password?: string;
+}
+
+// --- Singleton State ---
+let currentProxyUrl: string | null = null;
+let accessToken: string | null = null;
+let freeLocations: Location[] = [];
+let isInitialized = false;
+let initializationPromise: Promise | null = null;
+let vpnSource = 1;
+
+// --- Helpers ---
+
+async function fetchJson(
+ endpoint: string,
+ method: string,
+ body?: unknown,
+ token?: string,
+): Promise {
+ const url = `${API_BASE}${endpoint}`;
+ const headers: Record = {
+ "User-Agent": USER_AGENT,
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+ };
+
+ if (token) {
+ headers["Authorization"] = `Bearer ${token}`;
+ }
+
+ const response = await fetch(url, {
+ method,
+ headers,
+ body: body ? JSON.stringify(body) : undefined,
+ });
+
+ if (!response.ok) {
+ const text = await response.text();
+ throw new Error(`API Error ${response.status}: ${text}`);
+ }
+
+ return await response.json();
+}
+
+async function testProxyAgainstYouTube(proxyUrl: string): Promise {
+ try {
+ const proxyUrlObj = new URL(proxyUrl);
+ const clientOptions: Deno.CreateHttpClientOptions = {};
+
+ if (proxyUrlObj.username && proxyUrlObj.password) {
+ clientOptions.proxy = {
+ url: `${proxyUrlObj.protocol}//${proxyUrlObj.host}`,
+ basicAuth: {
+ username: decodeURIComponent(proxyUrlObj.username),
+ password: decodeURIComponent(proxyUrlObj.password),
+ },
+ };
+ } else {
+ clientOptions.proxy = {
+ url: proxyUrl,
+ };
+ }
+
+ const client = Deno.createHttpClient(clientOptions);
+
+ const response = await fetch(YOUTUBE_TEST_URL, {
+ client,
+ signal: AbortSignal.timeout(15000), // 15 second timeout for test
+ headers: {
+ "User-Agent": USER_AGENT,
+ },
+ });
+
+ client.close();
+
+ // YouTube should return 200 or a redirect (3xx)
+ if (response.ok || (response.status >= 300 && response.status < 400)) {
+ return true;
+ }
+ return response.status;
+ } catch (err) {
+ // console.error("[ProxyManager] Proxy test failed:", err); // Verified by user request to just move to next
+ return false;
+ }
+}
+
+async function registerDevice(): Promise {
+ const deviceInfo: DeviceInfo = {
+ udid: crypto.randomUUID(),
+ appVersion: APP_VERSION,
+ platform: "chrome",
+ platformVersion: USER_AGENT,
+ timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC",
+ deviceName: "Chrome 120.0.0.0",
+ };
+
+ const launchResponse = await fetchJson(
+ "/api/launch/",
+ "POST",
+ deviceInfo,
+ ) as {
+ success: boolean;
+ data?: { accessToken: string };
+ };
+
+ if (!launchResponse.success || !launchResponse.data?.accessToken) {
+ throw new Error("Failed to register device with antpeak.com");
+ }
+
+ return launchResponse.data.accessToken;
+}
+
+async function fetchLocations(token: string): Promise {
+ const locationsResponse = await fetchJson(
+ "/api/location/list/",
+ "POST",
+ undefined,
+ token,
+ ) as {
+ success: boolean;
+ data?: { locations: Location[] };
+ };
+
+ if (!locationsResponse.success || !locationsResponse.data?.locations) {
+ throw new Error("Failed to fetch locations from antpeak.com");
+ }
+
+ // Filter for free locations (proxyType === 0)
+ return locationsResponse.data.locations.filter((l) => l.proxyType === 0);
+}
+
+async function fetchProxyServer(
+ token: string,
+ location: Location,
+): Promise {
+ const serverPayload = {
+ protocol: "https",
+ region: location.region,
+ type: location.type,
+ };
+
+ const serverResponse = await fetchJson(
+ "/api/server/list/",
+ "POST",
+ serverPayload,
+ token,
+ ) as {
+ success: boolean;
+ data?: ProxyServer[];
+ };
+
+ if (
+ !serverResponse.success ||
+ !Array.isArray(serverResponse.data) ||
+ serverResponse.data.length === 0
+ ) {
+ return null;
+ }
+
+ const server = serverResponse.data[0];
+ const ip = server.addresses[0];
+ const port = server.port;
+ const username = server.username || "";
+ const password = server.password || "";
+
+ if (!username) {
+ return `https://${ip}:${port}`;
+ } else {
+ return `https://${encodeURIComponent(username)}:${encodeURIComponent(password)}@${ip}:${port}`;
+ }
+}
+
+// --- Public API ---
+
+/**
+ * Initialize the proxy manager. Fetches initial token and locations.
+ * Safe to call multiple times - will only initialize once.
+ */
+export async function initProxyManager(source: number = 1): Promise {
+ vpnSource = source;
+ if (isInitialized) return;
+
+ if (initializationPromise) {
+ return initializationPromise;
+ }
+
+ initializationPromise = (async () => {
+ console.log("[ProxyManager] Initializing automatic proxy manager...");
+
+ try {
+ if (vpnSource === 1) {
+ accessToken = await registerDevice();
+ console.log("[ProxyManager] ✅ Registered with antpeak.com");
+
+ freeLocations = await fetchLocations(accessToken);
+ console.log(
+ `[ProxyManager] ✅ Found ${freeLocations.length} free locations`,
+ );
+
+ if (freeLocations.length === 0) {
+ throw new Error("No free proxy locations available");
+ }
+ } else if (vpnSource === 2) {
+ console.log("[ProxyManager] Using Urban VPN source");
+ } else if (vpnSource === 3) {
+ console.log("[ProxyManager] Using Custom Proxy API source");
+ }
+
+ // Fetch initial proxy
+ await rotateProxy();
+
+ isInitialized = true;
+ console.log("[ProxyManager] ✅ Initialization complete");
+ } catch (err) {
+ console.error("[ProxyManager] ❌ Initialization failed:", err);
+ throw err;
+ }
+ })();
+
+ return initializationPromise;
+}
+
+/**
+ * Get the current proxy URL. Returns null if no proxy is available.
+ */
+export function getCurrentProxy(): string | null {
+ return currentProxyUrl;
+}
+
+/**
+ * Rotate to a new proxy. Tests against YouTube before accepting.
+ * Will try multiple locations until a working proxy is found.
+ */
+export async function rotateProxy(): Promise {
+ console.log(`[ProxyManager] Rotation requested. Source: ${vpnSource}`);
+
+ if (vpnSource === 2) {
+ // Urban VPN Logic
+ try {
+ const urbanResult = await fetchUrbanProxy();
+ if (urbanResult) {
+ console.log(`[ProxyManager] Testing Urban proxy against YouTube...`);
+ const result = await testProxyAgainstYouTube(urbanResult.url);
+ if (result === true) {
+ currentProxyUrl = urbanResult.url;
+ console.log(`[ProxyManager] ✅ New Urban proxy active: ${urbanResult.host}`);
+ return currentProxyUrl;
+ } else {
+ console.log(`[ProxyManager] ❌ Urban proxy failed YouTube test`);
+ }
+ }
+ } catch (err) {
+ console.error("[ProxyManager] Failed to fetch/test Urban proxy", err);
+ }
+ console.error("[ProxyManager] ❌ Could not find a working Urban proxy");
+ currentProxyUrl = null;
+ return null;
+ }
+
+ if (vpnSource === 3) {
+ // Custom Proxy Logic
+ console.log("[ProxyManager] Fetching proxies from custom API...");
+
+ let attempts = 0;
+ const maxAttempts = 10; // Increased retry limit as requested
+
+ while (attempts < maxAttempts) {
+ try {
+ const response = await fetch(CUSTOM_PROXY_URL);
+ if (!response.ok) {
+ throw new Error(`Failed to fetch proxies: ${response.statusText}`);
+ }
+ const data = await response.json() as { proxies: string[] };
+
+ if (!data.proxies || !Array.isArray(data.proxies) || data.proxies.length === 0) {
+ console.log("[ProxyManager] No proxies returned from API, retrying...");
+ attempts++;
+ continue;
+ }
+
+ console.log(`[ProxyManager] Got ${data.proxies.length} proxies from API. Testing...`);
+
+ for (const proxy of data.proxies) {
+ console.log(`[ProxyManager] Testing ${proxy}...`);
+ const result = await testProxyAgainstYouTube(proxy);
+
+ if (result === true) {
+ currentProxyUrl = proxy;
+ console.log(`[ProxyManager] ✅ New custom proxy active: ${proxy}`);
+ return currentProxyUrl;
+ } else if (typeof result === 'number') {
+ console.log(`[ProxyManager] ❌ Proxy returned status ${result}, trying next...`);
+ } else {
+ console.log(`[ProxyManager] ❌ Proxy unreachable, trying next...`);
+ }
+ }
+
+ console.log("[ProxyManager] All proxies from this batch failed. Refetching...");
+ attempts++;
+
+ } catch (err) {
+ console.error("[ProxyManager] Error fetching custom proxies:", err);
+ attempts++;
+ // Wait a bit before retrying if it's a fetch error
+ await new Promise(resolve => setTimeout(resolve, 2000));
+ }
+ }
+
+ console.error("[ProxyManager] ❌ Failed to find a working custom proxy after multiple attempts.");
+ currentProxyUrl = null;
+ return null;
+ }
+
+ if (!accessToken || freeLocations.length === 0) {
+ console.error(
+ "[ProxyManager] Not initialized or no locations available",
+ );
+ return null;
+ }
+
+ // Default AntPeak Logic (vpnSource === 1)
+ if (!accessToken || freeLocations.length === 0) {
+ console.error(
+ "[ProxyManager] Not initialized or no locations available",
+ );
+ return null;
+ }
+
+ console.log("[ProxyManager] Rotating to new proxy (AntPeak)...");
+
+ // Shuffle locations to get variety
+ const shuffledLocations = [...freeLocations].sort(() =>
+ Math.random() - 0.5
+ );
+
+ for (const location of shuffledLocations) {
+ try {
+ console.log(
+ `[ProxyManager] Trying location: ${location.region} (${location.countryCode})`,
+ );
+
+ const proxyUrl = await fetchProxyServer(accessToken, location);
+ if (!proxyUrl) {
+ console.log(
+ `[ProxyManager] No server available for ${location.region}`,
+ );
+ continue;
+ }
+
+ // Test proxy against YouTube
+ console.log(`[ProxyManager] Testing proxy against YouTube...`);
+ const result = await testProxyAgainstYouTube(proxyUrl);
+
+ if (result === true) {
+ currentProxyUrl = proxyUrl;
+ // Log without credentials for security
+ const sanitizedUrl = proxyUrl.replace(
+ /:\/\/[^@]+@/,
+ "://***:***@",
+ );
+ console.log(
+ `[ProxyManager] ✅ New proxy active: ${sanitizedUrl}`,
+ );
+ return currentProxyUrl;
+ } else {
+ console.log(
+ `[ProxyManager] ❌ Proxy failed YouTube test, trying next...`,
+ );
+ }
+ } catch (err) {
+ console.error(
+ `[ProxyManager] Error with location ${location.region}:`,
+ err,
+ );
+ }
+ }
+
+ console.error("[ProxyManager] ❌ Could not find a working proxy");
+ currentProxyUrl = null;
+ return null;
+}
+
+/**
+ * Mark the current proxy as failed and rotate to a new one.
+ * Call this when a request fails due to proxy issues.
+ */
+export async function markProxyFailed(): Promise {
+ console.log("[ProxyManager] Current proxy marked as failed, rotating...");
+ return await rotateProxy();
+}
+
+/**
+ * Check if the proxy manager is initialized and has a working proxy.
+ */
+export function isProxyManagerReady(): boolean {
+ return isInitialized && currentProxyUrl !== null;
+}
+
+/**
+ * Re-register with the API (in case token expires).
+ */
+export async function refreshRegistration(): Promise {
+ console.log("[ProxyManager] Refreshing registration...");
+ try {
+ accessToken = await registerDevice();
+ freeLocations = await fetchLocations(accessToken);
+ console.log("[ProxyManager] ✅ Registration refreshed");
+ } catch (err) {
+ console.error("[ProxyManager] ❌ Failed to refresh registration:", err);
+ throw err;
+ }
+}
diff --git a/src/lib/helpers/urbanProxy.ts b/src/lib/helpers/urbanProxy.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fa2141efa618d27f79039895a025776a1e699b6f
--- /dev/null
+++ b/src/lib/helpers/urbanProxy.ts
@@ -0,0 +1,233 @@
+const ACCOUNT_API = "https://api-pro.urban-vpn.com/rest/v1";
+const STATS_API = "https://stats.urban-vpn.com/api/rest/v2";
+const CLIENT_APP = "URBAN_VPN_BROWSER_EXTENSION";
+const BROWSER = "CHROME";
+
+interface UrbanProxyResult {
+ url: string;
+ protocol: string;
+ host: string;
+ port: number;
+ username?: string;
+ password?: string;
+}
+
+const PREFERRED_COUNTRIES = ["US", "GB", "CA", "DE", "FR", "NL", "ES", "IT", "JP", "KR", "SG", "AU"];
+
+export async function fetchUrbanProxy(targetCountryCode = "RANDOM"): Promise {
+ console.log(`[UrbanVPN] Fetching Urban VPN Proxy (Target: ${targetCountryCode})...`);
+
+ // 1. Register Anonymous
+ // console.log("[UrbanVPN] 1. Registering Anonymous User...");
+ const regUrl = `${ACCOUNT_API}/registrations/clientApps/${CLIENT_APP}/users/anonymous`;
+
+ const regHeaders = {
+ "content-type": "application/json",
+ "accept": "application/json, text/plain, */*",
+ "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36"
+ };
+
+ const regPayload = {
+ clientApp: {
+ name: CLIENT_APP,
+ browser: BROWSER
+ }
+ };
+
+ let regResp;
+ try {
+ regResp = await fetch(regUrl, {
+ method: "POST",
+ headers: regHeaders,
+ body: JSON.stringify(regPayload)
+ });
+ } catch (err) {
+ console.error("[UrbanVPN] Network error during registration:", err);
+ return null;
+ }
+
+ if (!regResp.ok) {
+ const text = await regResp.text();
+ console.error(`[UrbanVPN] Registration failed: ${regResp.status} ${regResp.statusText}`);
+ console.error(text);
+ return null;
+ }
+
+ const regData = await regResp.json();
+ const idToken = regData.id_token || regData.idToken || regData.value;
+
+ if (!idToken) {
+ console.error("[UrbanVPN] No ID token found in registration response.");
+ return null;
+ }
+
+ // 2. Get Security Token
+ // console.log("[UrbanVPN] 2. Getting Security Token...");
+ const secUrl = `${ACCOUNT_API}/security/tokens/accs`;
+ const secHeaders = {
+ ...regHeaders,
+ "authorization": `Bearer ${idToken}`
+ };
+ const secPayload = {
+ type: "accs",
+ clientApp: {
+ name: CLIENT_APP
+ }
+ };
+
+ const secResp = await fetch(secUrl, {
+ method: "POST",
+ headers: secHeaders,
+ body: JSON.stringify(secPayload)
+ });
+
+ if (!secResp.ok) {
+ const text = await secResp.text();
+ console.error(`[UrbanVPN] Security Token request failed: ${secResp.status}`);
+ console.error(text);
+ return null;
+ }
+
+ const secData = await secResp.json();
+
+ let tokenString = "";
+ let credUsername = "";
+ const credPassword = "1";
+
+ if (secData.token && typeof secData.token === 'object' && secData.token.value) {
+ tokenString = secData.token.value;
+ credUsername = secData.token.value;
+ } else if (typeof secData.token === 'string') {
+ tokenString = secData.token;
+ credUsername = secData.token;
+
+ } else if (secData.value) {
+ tokenString = secData.value;
+ credUsername = secData.value;
+ }
+
+ if (!tokenString) {
+ console.error("[UrbanVPN] No security token found.");
+ return null;
+ }
+
+ // 3. Get Countries / Proxies
+ // console.log("[UrbanVPN] 3. Fetching Proxy List...");
+ const countriesUrl = `${STATS_API}/entrypoints/countries`;
+ const proxyHeaders = {
+ ...regHeaders,
+ "authorization": `Bearer ${tokenString}`,
+ "X-Client-App": CLIENT_APP
+ };
+
+ // @ts-ignore: delete operator on string index signature
+ delete proxyHeaders["content-type"];
+
+ const countriesResp = await fetch(countriesUrl, {
+ headers: proxyHeaders
+ });
+
+ if (!countriesResp.ok) {
+ const text = await countriesResp.text();
+ console.error(`[UrbanVPN] Failed to fetch countries: ${countriesResp.status}`);
+ console.error(text);
+ return null;
+ }
+
+ const countriesData = await countriesResp.json();
+
+ if (!countriesData.countries || !countriesData.countries.elements) {
+ console.error("[UrbanVPN] Invalid countries data format.");
+ return null;
+ }
+
+ const countries = countriesData.countries.elements;
+
+ // Pick a country
+ let selectedCountryCode = targetCountryCode;
+ if (selectedCountryCode === "RANDOM") {
+ selectedCountryCode = PREFERRED_COUNTRIES[Math.floor(Math.random() * PREFERRED_COUNTRIES.length)];
+ }
+
+ // Find target country proxy
+ // deno-lint-ignore no-explicit-any
+ let targetCountry = countries.find((c: any) => c.code.iso2 === selectedCountryCode);
+
+ // Fallback if random choice not found
+ if (!targetCountry) {
+ targetCountry = countries[0];
+ console.log(`[UrbanVPN] Requested country ${selectedCountryCode} not found, falling back to ${targetCountry.code.iso2}`);
+ }
+
+ if (targetCountry) {
+ console.log(`[UrbanVPN] Selected Country: ${targetCountry.title} (${targetCountry.code.iso2})`);
+
+ let proxyHost = null;
+ let proxyPort = null;
+ let signature = null;
+
+ if (targetCountry.address && targetCountry.address.primary) {
+ proxyHost = targetCountry.address.primary.host;
+ proxyPort = targetCountry.address.primary.port;
+ }
+ else if (targetCountry.servers && targetCountry.servers.elements && targetCountry.servers.elements.length > 0) {
+ // Pick a RANDOM server from the list
+ const serverIndex = Math.floor(Math.random() * targetCountry.servers.elements.length);
+ const srv = targetCountry.servers.elements[serverIndex];
+
+ if (srv.address && srv.address.primary) {
+ proxyHost = srv.address.primary.host;
+ proxyPort = srv.address.primary.port || srv.address.primary.port_min;
+ signature = srv.signature;
+ }
+ }
+
+ if (signature) {
+ // console.log("[UrbanVPN] Found proxy signature, fetching Auth Proxy Token...");
+ const proxyTokenUrl = `${ACCOUNT_API}/security/tokens/accs-proxy`;
+ const proxyTokenPayload = {
+ type: "accs-proxy",
+ clientApp: { name: CLIENT_APP },
+ signature: signature
+ };
+
+ const proxyTokenHeaders = {
+ ...regHeaders,
+ "authorization": `Bearer ${tokenString}`
+ };
+
+ const ptResp = await fetch(proxyTokenUrl, {
+ method: "POST",
+ headers: proxyTokenHeaders,
+ body: JSON.stringify(proxyTokenPayload)
+ });
+
+ if (ptResp.ok) {
+ const ptData = await ptResp.json();
+ if (ptData.value) {
+ credUsername = ptData.value;
+ } else if (ptData.token && ptData.token.value) {
+ credUsername = ptData.token.value;
+ }
+ } else {
+ console.error(`[UrbanVPN] Failed to get Proxy Auth Token: ${ptResp.status}`);
+ }
+ }
+
+ if (proxyHost) {
+ const proxyUrl = `http://${encodeURIComponent(credUsername)}:${encodeURIComponent(credPassword)}@${proxyHost}:${proxyPort}`;
+ console.log(`[UrbanVPN] Proxy found: ${proxyHost}:${proxyPort}`);
+ return {
+ url: proxyUrl,
+ protocol: 'http',
+ host: proxyHost,
+ port: proxyPort,
+ username: credUsername,
+ password: credPassword
+ };
+ }
+ }
+
+ console.error("[UrbanVPN] No proxy server details found.");
+ return null;
+}
diff --git a/src/lib/helpers/validateVideoId.ts b/src/lib/helpers/validateVideoId.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cfeb135917fcd56e2b15464c9c7c6d8168050600
--- /dev/null
+++ b/src/lib/helpers/validateVideoId.ts
@@ -0,0 +1,23 @@
+/**
+ * Validates a YouTube video ID format
+ * YouTube video IDs are 11 characters long and contain alphanumeric characters, hyphens, and underscores
+ * Reference: https://webapps.stackexchange.com/questions/54443/format-for-id-of-youtube-video
+ *
+ * @param videoId - The video ID to validate
+ * @returns true if the video ID is valid, false otherwise
+ */
+export const validateVideoId = (videoId: string): boolean => {
+ // Handle null, undefined, or non-string values
+ if (!videoId || typeof videoId !== "string") {
+ return false;
+ }
+
+ // YouTube video IDs are exactly 11 characters
+ if (videoId.length !== 11) {
+ return false;
+ }
+
+ // Valid characters: A-Z, a-z, 0-9, -, _
+ const validPattern = /^[A-Za-z0-9_-]{11}$/;
+ return validPattern.test(videoId);
+};
diff --git a/src/lib/helpers/verifyRequest.ts b/src/lib/helpers/verifyRequest.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0891839e9b26c73a7936544c54718cf00846093a
--- /dev/null
+++ b/src/lib/helpers/verifyRequest.ts
@@ -0,0 +1,39 @@
+import { decodeBase64 } from "@std/encoding/base64";
+import { Aes } from "crypto/aes.ts";
+import { Ecb, Padding } from "crypto/block-modes.ts";
+import type { Config } from "./config.ts";
+
+export const verifyRequest = (
+ stringToCheck: string,
+ videoId: string,
+ config: Config,
+): boolean => {
+ try {
+ const decipher = new Ecb(
+ Aes,
+ new TextEncoder().encode(config.server.secret_key),
+ Padding.PKCS7,
+ );
+
+ const encryptedData = new TextDecoder().decode(
+ decipher.decrypt(
+ decodeBase64(
+ stringToCheck.replace(/-/g, "+").replace(/_/g, "/"),
+ ),
+ ),
+ );
+ const [parsedTimestamp, parsedVideoId] = encryptedData.split("|");
+ const parsedTimestampInt = parseInt(parsedTimestamp);
+ const timestampNow = Math.round(+new Date() / 1000);
+ if (parsedVideoId !== videoId) {
+ return false;
+ }
+ // only allow ID to live for 6 hours
+ if ((timestampNow + 6 * 60 * 60) - parsedTimestampInt < 0) {
+ return false;
+ }
+ } catch (_) {
+ return false;
+ }
+ return true;
+};
diff --git a/src/lib/helpers/youtubePlayerHandling.ts b/src/lib/helpers/youtubePlayerHandling.ts
new file mode 100644
index 0000000000000000000000000000000000000000..aeb34546965958962a501ade21870c2009164d71
--- /dev/null
+++ b/src/lib/helpers/youtubePlayerHandling.ts
@@ -0,0 +1,195 @@
+import { ApiResponse, Innertube, YT } from "youtubei.js";
+import { generateRandomString } from "youtubei.js/Utils";
+import { compress, decompress } from "brotli";
+import type { TokenMinter } from "../jobs/potoken.ts";
+import { Metrics } from "../helpers/metrics.ts";
+let youtubePlayerReqLocation = "youtubePlayerReq";
+if (Deno.env.get("YT_PLAYER_REQ_LOCATION")) {
+ if (Deno.env.has("DENO_COMPILED")) {
+ youtubePlayerReqLocation = Deno.mainModule.replace("src/main.ts", "") +
+ Deno.env.get("YT_PLAYER_REQ_LOCATION");
+ } else {
+ youtubePlayerReqLocation = Deno.env.get(
+ "YT_PLAYER_REQ_LOCATION",
+ ) as string;
+ }
+}
+const { youtubePlayerReq } = await import(youtubePlayerReqLocation);
+
+import type { Config } from "./config.ts";
+
+const kv = await Deno.openKv();
+
+export const youtubePlayerParsing = async ({
+ innertubeClient,
+ videoId,
+ config,
+ tokenMinter,
+ metrics,
+ overrideCache = false,
+}: {
+ innertubeClient: Innertube;
+ videoId: string;
+ config: Config;
+ tokenMinter: TokenMinter;
+ metrics: Metrics | undefined;
+ overrideCache?: boolean;
+}): Promise