From 3bd364a1a8dee1316bd5c432229d83a30822927b Mon Sep 17 00:00:00 2001 From: Ruben Rodriguez Date: Sat, 10 Oct 2015 11:26:33 -0500 Subject: HTML5 Video Everywhere updated to 0.3.3 --- .../html5-video-everywhere@lejenome.me/LICENSE | 363 ++++ .../META-INF/manifest.mf | 164 +- .../META-INF/mozilla.rsa | Bin 4192 -> 4192 bytes .../META-INF/mozilla.sf | 4 +- .../html5-video-everywhere@lejenome.me/Makefile | 18 + .../html5-video-everywhere@lejenome.me/README.md | 39 + .../html5-video-everywhere@lejenome.me/TODO.md | 25 + .../bootstrap.js | 355 +--- .../data/break.js | 47 + .../data/common.js | 109 + .../data/dailymotion.js | 47 + .../data/facebook.js | 27 + .../data/flashgot-YouTubeSwf.js | 2185 ++++++++++++++++++++ .../data/metacafe.js | 71 + .../data/video-player.js | 387 ++++ .../data/vimeo.js | 137 ++ .../data/youtube-formats.js | 33 + .../data/youtube.js | 330 +++ .../html5-video-everywhere@lejenome.me/icon.png | Bin 0 -> 6535 bytes .../html5-video-everywhere@lejenome.me/index.js | 151 ++ .../html5-video-everywhere@lejenome.me/install.rdf | 57 +- .../lib/break.js | 9 + .../lib/common.js | 20 + .../lib/dailymotion.js | 9 + .../lib/facebook.js | 7 + .../lib/flashgot-YouTube.js | 535 +++++ .../lib/metacafe.js | 11 + .../lib/vimeo.js | 11 + .../lib/youtube.js | 30 + .../package.json | 311 +++ .../spec/break.md | 5 + .../spec/dailymotion.md | 5 + .../spec/facebook.md | 11 + .../spec/metacafe.md | 10 + .../spec/vimeo.md | 15 + .../spec/youtube.md | 27 + .../test/test-main.js | 13 + .../test/test.html | 41 + 38 files changed, 5189 insertions(+), 430 deletions(-) create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/LICENSE create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/Makefile create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/README.md create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/TODO.md mode change 100755 => 100644 data/extensions/html5-video-everywhere@lejenome.me/bootstrap.js create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/data/break.js create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/data/common.js create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/data/dailymotion.js create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/data/facebook.js create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/data/flashgot-YouTubeSwf.js create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/data/metacafe.js create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/data/video-player.js create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/data/vimeo.js create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/data/youtube-formats.js create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/data/youtube.js create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/icon.png create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/index.js create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/lib/break.js create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/lib/common.js create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/lib/dailymotion.js create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/lib/facebook.js create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/lib/flashgot-YouTube.js create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/lib/metacafe.js create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/lib/vimeo.js create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/lib/youtube.js create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/package.json create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/spec/break.md create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/spec/dailymotion.md create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/spec/facebook.md create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/spec/metacafe.md create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/spec/vimeo.md create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/spec/youtube.md create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/test/test-main.js create mode 100644 data/extensions/html5-video-everywhere@lejenome.me/test/test.html diff --git a/data/extensions/html5-video-everywhere@lejenome.me/LICENSE b/data/extensions/html5-video-everywhere@lejenome.me/LICENSE new file mode 100644 index 0000000..e87a115 --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/LICENSE @@ -0,0 +1,363 @@ +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. "Contributor" + + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. "Contributor Version" + + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the terms of + a Secondary License. + +1.6. "Executable Form" + + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + + means a work that combines Covered Software with other material, in a + separate file or files, that is not Covered Software. + +1.8. "License" + + means this document. + +1.9. "Licensable" + + means having the right to grant, to the maximum extent possible, whether + at the time of the initial grant or subsequently, any and all of the + rights conveyed by this License. + +1.10. "Modifications" + + means any of the following: + + a. any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. "Patent Claims" of a Contributor + + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the License, + by the making, using, selling, offering for sale, having made, import, + or transfer of either its Contributions or its Contributor Version. + +1.12. "Secondary License" + + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. "Source Code Form" + + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, "control" means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution + become effective for each Contribution on the date the Contributor first + distributes such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under + this License. No additional rights or licenses will be implied from the + distribution or licensing of Covered Software under this License. + Notwithstanding Section 2.1(b) above, no patent license is granted by a + Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of + its Contributions. + + This License does not grant any rights in the trademarks, service marks, + or logos of any Contributor (except as may be necessary to comply with + the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this + License (see Section 10.2) or under the terms of a Secondary License (if + permitted under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its + Contributions are its original creation(s) or it has sufficient rights to + grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under + applicable copyright doctrines of fair use, fair dealing, or other + equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under + the terms of this License. You must inform recipients that the Source + Code Form of the Covered Software is governed by the terms of this + License, and how they can obtain a copy of this License. You may not + attempt to alter or restrict the recipients' rights in the Source Code + Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter the + recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for + the Covered Software. If the Larger Work is a combination of Covered + Software with a work governed by one or more Secondary Licenses, and the + Covered Software is not Incompatible With Secondary Licenses, this + License permits You to additionally distribute such Covered Software + under the terms of such Secondary License(s), so that the recipient of + the Larger Work may, at their option, further distribute the Covered + Software under the terms of either this License or such Secondary + License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices + (including copyright notices, patent notices, disclaimers of warranty, or + limitations of liability) contained within the Source Code Form of the + Covered Software, except that You may alter any license notices to the + extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on + behalf of any Contributor. You must make it absolutely clear that any + such warranty, support, indemnity, or liability obligation is offered by + You alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, + judicial order, or regulation then You must: (a) comply with the terms of + this License to the maximum extent possible; and (b) describe the + limitations and the code they affect. Such description must be placed in a + text file included with all distributions of the Covered Software under + this License. Except to the extent prohibited by statute or regulation, + such description must be sufficiently detailed for a recipient of ordinary + skill to be able to understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing + basis, if such Contributor fails to notify You of the non-compliance by + some reasonable means prior to 60 days after You have come back into + compliance. Moreover, Your grants from a particular Contributor are + reinstated on an ongoing basis if such Contributor notifies You of the + non-compliance by some reasonable means, this is the first time You have + received notice of non-compliance with this License from such + Contributor, and You become compliant prior to 30 days after Your receipt + of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, + counter-claims, and cross-claims) alleging that a Contributor Version + directly or indirectly infringes any patent, then the rights granted to + You by any and all Contributors for the Covered Software under Section + 2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an "as is" basis, + without warranty of any kind, either expressed, implied, or statutory, + including, without limitation, warranties that the Covered Software is free + of defects, merchantable, fit for a particular purpose or non-infringing. + The entire risk as to the quality and performance of the Covered Software + is with You. Should any Covered Software prove defective in any respect, + You (not any Contributor) assume the cost of any necessary servicing, + repair, or correction. This disclaimer of warranty constitutes an essential + part of this License. No use of any Covered Software is authorized under + this License except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from + such party's negligence to the extent applicable law prohibits such + limitation. Some jurisdictions do not allow the exclusion or limitation of + incidental or consequential damages, so this exclusion and limitation may + not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts + of a jurisdiction where the defendant maintains its principal place of + business and such litigation shall be governed by laws of that + jurisdiction, without reference to its conflict-of-law provisions. Nothing + in this Section shall prevent a party's ability to bring cross-claims or + counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. Any law or regulation which provides that + the language of a contract shall be construed against the drafter shall not + be used to construe this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version + of the License under which You originally received the Covered Software, + or under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a + modified version of this License if you rename the license and remove + any references to the name of the license steward (except to note that + such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary + Licenses If You choose to distribute Source Code Form that is + Incompatible With Secondary Licenses under the terms of this version of + the License, the notice described in Exhibit B of this License must be + attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, +then You may include the notice in a location (such as a LICENSE file in a +relevant directory) where a recipient would be likely to look for such a +notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice + + This Source Code Form is "Incompatible + With Secondary Licenses", as defined by + the Mozilla Public License, v. 2.0. + diff --git a/data/extensions/html5-video-everywhere@lejenome.me/META-INF/manifest.mf b/data/extensions/html5-video-everywhere@lejenome.me/META-INF/manifest.mf index 8b51fb4..72cd1a5 100644 --- a/data/extensions/html5-video-everywhere@lejenome.me/META-INF/manifest.mf +++ b/data/extensions/html5-video-everywhere@lejenome.me/META-INF/manifest.mf @@ -2,126 +2,176 @@ Manifest-Version: 1.0 Name: install.rdf Digest-Algorithms: MD5 SHA1 -MD5-Digest: yEAlVKMLjXkyf3c7msk/Xw== -SHA1-Digest: aV3Z43FdsOjtb5MVBqTufnvUCLc= +MD5-Digest: 3CZKpd7ahCT81kRmeoqgZw== +SHA1-Digest: Oc1Ig4LQZBBJ9qhZ/J7NIzXwAN8= + +Name: icon.png +Digest-Algorithms: MD5 SHA1 +MD5-Digest: dmpm2FIs94bBxuVOhXTB4w== +SHA1-Digest: zIzMCJJ0LBAMeZQ7pr9TO3Ir9uY= Name: bootstrap.js Digest-Algorithms: MD5 SHA1 -MD5-Digest: 7e6z9LJUzPhqfgyfhRMzhA== -SHA1-Digest: TWD0b5BkAoKXr56l0Tt3AkKBy8s= +MD5-Digest: gE+jXuKpPkTxUv6AL2Q+0w== +SHA1-Digest: zJpzqwwkcqSyH1WTx95W5OVwAf8= + +Name: index.js +Digest-Algorithms: MD5 SHA1 +MD5-Digest: HOjkR7QIIXjQPk1jYs58Lw== +SHA1-Digest: RxVAcn572CkaXY3rsxFvIVVl0JU= -Name: harness-options.json +Name: Makefile Digest-Algorithms: MD5 SHA1 -MD5-Digest: gvOukOdXNjdHv1ejWNp02g== -SHA1-Digest: kbPOKwGp8XnWtsH95t0fwLRplOE= +MD5-Digest: O8EMb/Pvu1YkocCav1nEyw== +SHA1-Digest: uZslA8OM6foJrLvzmAwDpj++dsw= -Name: locales.json +Name: package.json Digest-Algorithms: MD5 SHA1 -MD5-Digest: p3Qh60/6wDH/UunTuTWZjQ== -SHA1-Digest: DBci+JD/520hUnFSB3zB1xoBE9g= +MD5-Digest: xxRAx+XLx0NLnwkT8R8tPA== +SHA1-Digest: jiG7sIifE2PiFl+gbCi07N1lxI4= -Name: options.xul +Name: README.md Digest-Algorithms: MD5 SHA1 -MD5-Digest: AQXhzeAfLxO+ATnTSo9pOw== -SHA1-Digest: TjNQdfc9jutGAHq9GjWhCrjsXaI= +MD5-Digest: T5XE19z7qJAXJk6z6NZ6Ng== +SHA1-Digest: I4F+gaDIe3IIBpiojcLyl2oxCig= -Name: defaults/preferences/prefs.js +Name: TODO.md Digest-Algorithms: MD5 SHA1 -MD5-Digest: WB3cSv+b4ydlRWW5JKfkyw== -SHA1-Digest: 8NhNf95wV6r8Pj6T2VaLw4URH/k= +MD5-Digest: MA5+pBVvw/JwLi1MaG3cAQ== +SHA1-Digest: T/OmE2mQtQvuJKMh6imYM8vn13A= -Name: resources/html5-video-everywhere/data/break.js +Name: data/break.js Digest-Algorithms: MD5 SHA1 MD5-Digest: FHcsZl9Qe91hW/7B28cAkg== SHA1-Digest: P4o5B+pFXkcUyTcjR/EjVPN2NEY= -Name: resources/html5-video-everywhere/data/common.js +Name: data/common.js Digest-Algorithms: MD5 SHA1 -MD5-Digest: E1LUgC77SvIagwSzhOELtQ== -SHA1-Digest: RKAZ/SxZXihI9RR/YuPqx/qr+Zo= +MD5-Digest: T1pz4K8CA/GEgrWW/MhdMg== +SHA1-Digest: z0vDbg2HnjR62lXA2DxtQMBorkI= -Name: resources/html5-video-everywhere/data/dailymotion.js +Name: data/dailymotion.js Digest-Algorithms: MD5 SHA1 -MD5-Digest: ozG1xmyo2629YYbN1ZP18Q== -SHA1-Digest: 9opU+TINoR5HlMlK1LTq29yB3OA= +MD5-Digest: 9QsYcMzrj1tL8wW/7bG5Dg== +SHA1-Digest: LgyuBZytMRtvZ8x+smrqI9E/ufU= -Name: resources/html5-video-everywhere/data/facebook.js +Name: data/facebook.js Digest-Algorithms: MD5 SHA1 -MD5-Digest: fqr4Z17t/ZoygcVknd9/dg== -SHA1-Digest: xicvIjTGA2Usu3fRfOkDnoEZaek= +MD5-Digest: VItINacQJvbb3wtkPu8o8Q== +SHA1-Digest: zYMOQvETe6RBJybYc/ErviyavGI= -Name: resources/html5-video-everywhere/data/flashgot-YouTubeSwf.js +Name: data/flashgot-YouTubeSwf.js Digest-Algorithms: MD5 SHA1 MD5-Digest: T+XZ+Rd72kqQauBCH92lDQ== SHA1-Digest: FqZ/r+c3xYHqFzMwYAQLk2OP9vU= -Name: resources/html5-video-everywhere/data/metacafe.js +Name: data/metacafe.js Digest-Algorithms: MD5 SHA1 -MD5-Digest: NMql8+nLTPMdxjV6sP5H3g== -SHA1-Digest: q2qpkNvUhobvaJjcc7UalhgXnMI= +MD5-Digest: AAukiZ3WX6dt2AmEGM+1Ew== +SHA1-Digest: Nl+P9MCFy6YmrQUDabBfQi2tdSM= -Name: resources/html5-video-everywhere/data/video-player.js +Name: data/video-player.js Digest-Algorithms: MD5 SHA1 -MD5-Digest: GdLZBfcj0UrWd0diFLtwlQ== -SHA1-Digest: T4kSzQs0WwEMwBTvWXR0zG9Xvio= +MD5-Digest: QaVP15oXFYBib7qhCa7Uhg== +SHA1-Digest: 0o5gijudH3zYgut+Dm7h+MHJWw4= -Name: resources/html5-video-everywhere/data/vimeo.js +Name: data/vimeo.js Digest-Algorithms: MD5 SHA1 -MD5-Digest: vysLIrQbnaQs04x+JUaY1A== -SHA1-Digest: /4NusOTf5vM0OvXpI/gBGnd+CwU= +MD5-Digest: zmNcKF1Swbah6zoT76rdOw== +SHA1-Digest: 9mmwDqe6FRUdwpYD8sTwz9LDEKE= -Name: resources/html5-video-everywhere/data/youtube-formats.js +Name: data/youtube-formats.js Digest-Algorithms: MD5 SHA1 MD5-Digest: ba8pxu4YWbMTFLI7u9nSww== SHA1-Digest: 8LdbGx/9NDqLQ9CndverrJzTRHA= -Name: resources/html5-video-everywhere/data/youtube.js +Name: data/youtube.js Digest-Algorithms: MD5 SHA1 -MD5-Digest: fw7qzIDcMToQ8yY0VkEdqQ== -SHA1-Digest: ezxXLeySGAqP/Yh/E3UCTu1F/RE= +MD5-Digest: e8sjyWmwQH4By8KTou2qCA== +SHA1-Digest: IKTdbDBdFlsMpiB2Lucw+sPu7KU= -Name: resources/html5-video-everywhere/lib/break.js +Name: lib/break.js Digest-Algorithms: MD5 SHA1 MD5-Digest: Gs/Etew95x/lD3IuZifl5Q== SHA1-Digest: 56GIkHz/2jJL9bEjPkLPdO1KVNo= -Name: resources/html5-video-everywhere/lib/common.js +Name: lib/common.js Digest-Algorithms: MD5 SHA1 MD5-Digest: CYP8cXv4SBDfwNklEF7UmA== SHA1-Digest: 5B8qBt1SCvK4GERTM/ggXyZLYiM= -Name: resources/html5-video-everywhere/lib/dailymotion.js +Name: lib/dailymotion.js Digest-Algorithms: MD5 SHA1 MD5-Digest: G7beTZLJ9bV8qd4TQKvSJA== SHA1-Digest: rg2Qw46iUoN3IjqEDdcklKTq3XM= -Name: resources/html5-video-everywhere/lib/facebook.js +Name: lib/facebook.js Digest-Algorithms: MD5 SHA1 MD5-Digest: MwgClTBiBe7LFJNcvKMjlg== SHA1-Digest: H9dSJUDuos2GyVC5dqLB/wMH+9Q= -Name: resources/html5-video-everywhere/lib/flashgot-YouTube.js -Digest-Algorithms: MD5 SHA1 -MD5-Digest: VHCubRXtltPxbjJwQSP8Ug== -SHA1-Digest: ejl5VDFw9wkD6xs+QZGKxD0B6jw= - -Name: resources/html5-video-everywhere/lib/main.js +Name: lib/flashgot-YouTube.js Digest-Algorithms: MD5 SHA1 -MD5-Digest: R6NltxG2mAW+bFuh2zJOdQ== -SHA1-Digest: HjEEU999PhXM4vX4gski8wIY058= +MD5-Digest: seLzlTcid+3/YXgxPdG1Tg== +SHA1-Digest: zls6CmxZnpdcaPPP45RHJ81BDGc= -Name: resources/html5-video-everywhere/lib/metacafe.js +Name: lib/metacafe.js Digest-Algorithms: MD5 SHA1 MD5-Digest: Kapqu2JlNqdfYVl1mzWZVQ== SHA1-Digest: Wk6FJJqfUJsTY4EsqXHkk9hMkmc= -Name: resources/html5-video-everywhere/lib/vimeo.js +Name: lib/vimeo.js Digest-Algorithms: MD5 SHA1 MD5-Digest: jTJdcUeScUvccb3lLljoNg== SHA1-Digest: PFedn4/SHpnfvE32ZXSLJON1SYg= -Name: resources/html5-video-everywhere/lib/youtube.js +Name: lib/youtube.js +Digest-Algorithms: MD5 SHA1 +MD5-Digest: F0G4vKBsjJ07Z8pkZI5+mQ== +SHA1-Digest: 1w6nKvceNhIWU8LPJBLX6oqnDEY= + +Name: spec/break.md +Digest-Algorithms: MD5 SHA1 +MD5-Digest: qObenD1cChTuZxNttmn8Aw== +SHA1-Digest: u8vX080vxN6pKgXo+CRKX3t7Hfo= + +Name: spec/dailymotion.md +Digest-Algorithms: MD5 SHA1 +MD5-Digest: 6lOpkYUWAsStXLMAqYehBw== +SHA1-Digest: pGabm+cMpoBvLDdeVvgsOihEzLc= + +Name: spec/facebook.md +Digest-Algorithms: MD5 SHA1 +MD5-Digest: X5yfYREVbfMy6lPPaaNh6A== +SHA1-Digest: +O7MYdK6qk7i4psroLVB4+oBux8= + +Name: spec/metacafe.md +Digest-Algorithms: MD5 SHA1 +MD5-Digest: Ccmmf8BrP/cR16sRrKluhw== +SHA1-Digest: O+/IGkknW0DRL30cmp/Nw7yHRcA= + +Name: spec/vimeo.md +Digest-Algorithms: MD5 SHA1 +MD5-Digest: mpBaRof9+UzM7MVWZ/vhSg== +SHA1-Digest: HvRfky4y+7xvDJCO0a/q/BhCJOg= + +Name: spec/youtube.md +Digest-Algorithms: MD5 SHA1 +MD5-Digest: zL3rprD0FLrgFRnMt17ztQ== +SHA1-Digest: OfrOKYbNk4NKiQ478VugyHdGciw= + +Name: test/test-main.js +Digest-Algorithms: MD5 SHA1 +MD5-Digest: m0uIaXfcS3BiM+JWkuCe4Q== +SHA1-Digest: P3vJbOqaMLoWNFciqi8Yca0yrJI= + +Name: test/test.html +Digest-Algorithms: MD5 SHA1 +MD5-Digest: /0sS5m/n7HFEua6UBE3xEA== +SHA1-Digest: dhJ2ptKWNvsmAyD6Zyse3h5hfIU= + +Name: LICENSE Digest-Algorithms: MD5 SHA1 -MD5-Digest: 2iKx56tEhaAJbSLMWZITlA== -SHA1-Digest: nEY1iWkxNKagLglR5KVwjS+meYU= +MD5-Digest: ZdJvzC816moYGsd35C2x6g== +SHA1-Digest: +nxNdbrjpkHR+atd8CgXW/uKaco= diff --git a/data/extensions/html5-video-everywhere@lejenome.me/META-INF/mozilla.rsa b/data/extensions/html5-video-everywhere@lejenome.me/META-INF/mozilla.rsa index 71cd3a1..a3d4961 100644 Binary files a/data/extensions/html5-video-everywhere@lejenome.me/META-INF/mozilla.rsa and b/data/extensions/html5-video-everywhere@lejenome.me/META-INF/mozilla.rsa differ diff --git a/data/extensions/html5-video-everywhere@lejenome.me/META-INF/mozilla.sf b/data/extensions/html5-video-everywhere@lejenome.me/META-INF/mozilla.sf index fdecacb..7538a8b 100644 --- a/data/extensions/html5-video-everywhere@lejenome.me/META-INF/mozilla.sf +++ b/data/extensions/html5-video-everywhere@lejenome.me/META-INF/mozilla.sf @@ -1,4 +1,4 @@ Signature-Version: 1.0 -MD5-Digest-Manifest: rXdZTR03/BLV1sP4JNWVTA== -SHA1-Digest-Manifest: F+qwFdUpvRczR7PLQrIdFhIzwPE= +MD5-Digest-Manifest: Hml9wnbFfErJ0P5PjR7X/Q== +SHA1-Digest-Manifest: I1SMUhwUPiokL1DHydczQRljGvY= diff --git a/data/extensions/html5-video-everywhere@lejenome.me/Makefile b/data/extensions/html5-video-everywhere@lejenome.me/Makefile new file mode 100644 index 0000000..3f1fc1b --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/Makefile @@ -0,0 +1,18 @@ +# List of drivers to enable on build + +.NOTPARALLEL : all +all: lint beautify build +lint: + jshint --verbose *.js */*.js +beautify: + find . -maxdepth 2 -name "*.js" -a ! -name "flashgot-*.js" \ + | xargs js-beautify -r + find . -name "*.json" | xargs -n 1 jsonlint -i +build: + jpm xpi +run: + jpm run +watch: + jpm watchpost --post-url http://localhost:8888/ +test: + jpm test diff --git a/data/extensions/html5-video-everywhere@lejenome.me/README.md b/data/extensions/html5-video-everywhere@lejenome.me/README.md new file mode 100644 index 0000000..457e9fd --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/README.md @@ -0,0 +1,39 @@ +# HTML5 Video EveryWhere + +Not just an other HTML5 player extension. +This extension, unlike other extensions, will replace some video streaming sites +video player by Firefox native player. No just replacing Flash player by the +site HTML5 player version, but, booth the site HTML5 and Flash player with Firefox +native player. + +**NOTE:** +This extension will change the page content of the supported video streaming +sites, as a result, many extensions that depends on this content will no longer +work as expected. + +## Build + +`jpm` is required to build the addon. To install `jpm`, run: +```shell +npm install -g jpm +``` + +To build the addon: +```shell +jpm xpi +``` + +## Credit + +YouTube signature decoder code (data/flashgot-YouTubeSwf.js, +lib/flashgot-YouTube.js) was copied from the [GPL]( +http://www.gnu.org/copyleft/gpl.html) licenced +[flashgot](https://flashgot.net/) extension with some code removed and +other added code under the same licence. + +## Licence + +This extension is free software; it is distributed under the MPL 2.0 Licence. + +Copyright (c) 2014-2015, Moez Bouhlel (bmoez.j@gmail.com) & [The +Contributors](https://github.com/lejenome/html5-video-everywhere/graphs/contributors) diff --git a/data/extensions/html5-video-everywhere@lejenome.me/TODO.md b/data/extensions/html5-video-everywhere@lejenome.me/TODO.md new file mode 100644 index 0000000..2aaffca --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/TODO.md @@ -0,0 +1,25 @@ +A wichlist of Sites to support: + - Youtube + [[status](https://github.com/lejenome/html5-video-everywhere/milestones/YouTube support)] + - Facebook + [[status](https://github.com/lejenome/html5-video-everywhere/milestones/Facebook Support)] + - Vimeo + [[status](https://github.com/lejenome/html5-video-everywhere/milestones/Vimeo Support)] + - Dailymotion + [[status](https://github.com/lejenome/html5-video-everywhere/milestones/Dailymotion Support)] + - Break + [[status](https://github.com/lejenome/html5-video-everywhere/milestones/Break Support)] + - Metacafe + [[status](https://github.com/lejenome/html5-video-everywhere/milestones/Metacafe Support)] + - Youku + - Blip + - Liveleak + - Vine + +Live Streaming sites (require support of DASH and HLS : [#9]( +https://github.com/lejenome/html5-video-everywhere/issues/9) and [#10]( +https://github.com/lejenome/html5-video-everywhere/issues/10)) + - Yahoo screen + - ustream + +For current status of sites in progress, view [spec/](spec/) folder. diff --git a/data/extensions/html5-video-everywhere@lejenome.me/bootstrap.js b/data/extensions/html5-video-everywhere@lejenome.me/bootstrap.js old mode 100755 new mode 100644 index 840103a..4866564 --- a/data/extensions/html5-video-everywhere@lejenome.me/bootstrap.js +++ b/data/extensions/html5-video-everywhere@lejenome.me/bootstrap.js @@ -1,350 +1,11 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -// @see http://mxr.mozilla.org/mozilla-central/source/js/src/xpconnect/loader/mozJSComponentLoader.cpp - -'use strict'; - -// IMPORTANT: Avoid adding any initialization tasks here, if you need to do -// something before add-on is loaded consider addon/runner module instead! - -const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu, - results: Cr, manager: Cm } = Components; -const ioService = Cc['@mozilla.org/network/io-service;1']. - getService(Ci.nsIIOService); -const resourceHandler = ioService.getProtocolHandler('resource'). - QueryInterface(Ci.nsIResProtocolHandler); -const systemPrincipal = CC('@mozilla.org/systemprincipal;1', 'nsIPrincipal')(); -const scriptLoader = Cc['@mozilla.org/moz/jssubscript-loader;1']. - getService(Ci.mozIJSSubScriptLoader); -const prefService = Cc['@mozilla.org/preferences-service;1']. - getService(Ci.nsIPrefService). - QueryInterface(Ci.nsIPrefBranch); -const appInfo = Cc["@mozilla.org/xre/app-info;1"]. - getService(Ci.nsIXULAppInfo); -const vc = Cc["@mozilla.org/xpcom/version-comparator;1"]. - getService(Ci.nsIVersionComparator); - - -const REASON = [ 'unknown', 'startup', 'shutdown', 'enable', 'disable', - 'install', 'uninstall', 'upgrade', 'downgrade' ]; - -const bind = Function.call.bind(Function.bind); - -let loader = null; -let unload = null; -let cuddlefishSandbox = null; -let nukeTimer = null; - -let resourceDomains = []; -function setResourceSubstitution(domain, uri) { - resourceDomains.push(domain); - resourceHandler.setSubstitution(domain, uri); -} - -// Utility function that synchronously reads local resource from the given -// `uri` and returns content string. -function readURI(uri) { - let ioservice = Cc['@mozilla.org/network/io-service;1']. - getService(Ci.nsIIOService); - let channel = ioservice.newChannel(uri, 'UTF-8', null); - let stream = channel.open(); - - let cstream = Cc['@mozilla.org/intl/converter-input-stream;1']. - createInstance(Ci.nsIConverterInputStream); - cstream.init(stream, 'UTF-8', 0, 0); - - let str = {}; - let data = ''; - let read = 0; - do { - read = cstream.readString(0xffffffff, str); - data += str.value; - } while (read != 0); - - cstream.close(); - - return data; -} - -// We don't do anything on install & uninstall yet, but in a future -// we should allow add-ons to cleanup after uninstall. -function install(data, reason) {} -function uninstall(data, reason) {} - -function startup(data, reasonCode) { - try { - let reason = REASON[reasonCode]; - // URI for the root of the XPI file. - // 'jar:' URI if the addon is packed, 'file:' URI otherwise. - // (Used by l10n module in order to fetch `locale` folder) - let rootURI = data.resourceURI.spec; - - // TODO: Maybe we should perform read harness-options.json asynchronously, - // since we can't do anything until 'sessionstore-windows-restored' anyway. - let options = JSON.parse(readURI(rootURI + './harness-options.json')); - - let id = options.jetpackID; - let name = options.name; - - // Clean the metadata - options.metadata[name]['permissions'] = options.metadata[name]['permissions'] || {}; - - // freeze the permissionss - Object.freeze(options.metadata[name]['permissions']); - // freeze the metadata - Object.freeze(options.metadata[name]); - - // Register a new resource 'domain' for this addon which is mapping to - // XPI's `resources` folder. - // Generate the domain name by using jetpack ID, which is the extension ID - // by stripping common characters that doesn't work as a domain name: - let uuidRe = - /^\{([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\}$/; - - let domain = id. - toLowerCase(). - replace(/@/g, '-at-'). - replace(/\./g, '-dot-'). - replace(uuidRe, '$1'); - - let prefixURI = 'resource://' + domain + '/'; - let resourcesURI = ioService.newURI(rootURI + '/resources/', null, null); - setResourceSubstitution(domain, resourcesURI); - - // Create path to URLs mapping supported by loader. - let paths = { - // Relative modules resolve to add-on package lib - './': prefixURI + name + '/lib/', - './tests/': prefixURI + name + '/tests/', - '': 'resource://gre/modules/commonjs/' - }; - - // Maps addon lib and tests ressource folders for each package - paths = Object.keys(options.metadata).reduce(function(result, name) { - result[name + '/'] = prefixURI + name + '/lib/' - result[name + '/tests/'] = prefixURI + name + '/tests/' - return result; - }, paths); - - // We need to map tests folder when we run sdk tests whose package name - // is stripped - if (name == 'addon-sdk') - paths['tests/'] = prefixURI + name + '/tests/'; - - let useBundledSDK = options['force-use-bundled-sdk']; - if (!useBundledSDK) { - try { - useBundledSDK = prefService.getBoolPref("extensions.addon-sdk.useBundledSDK"); - } - catch (e) { - // Pref doesn't exist, allow using Firefox shipped SDK - } - } - - // Starting with Firefox 21.0a1, we start using modules shipped into firefox - // Still allow using modules from the xpi if the manifest tell us to do so. - // And only try to look for sdk modules in xpi if the xpi actually ship them - if (options['is-sdk-bundled'] && - (vc.compare(appInfo.version, '21.0a1') < 0 || useBundledSDK)) { - // Maps sdk module folders to their resource folder - paths[''] = prefixURI + 'addon-sdk/lib/'; - // test.js is usually found in root commonjs or SDK_ROOT/lib/ folder, - // so that it isn't shipped in the xpi. Keep a copy of it in sdk/ folder - // until we no longer support SDK modules in XPI: - paths['test'] = prefixURI + 'addon-sdk/lib/sdk/test.js'; - } - - // Retrieve list of module folder overloads based on preferences in order to - // eventually used a local modules instead of files shipped into Firefox. - let branch = prefService.getBranch('extensions.modules.' + id + '.path'); - paths = branch.getChildList('', {}).reduce(function (result, name) { - // Allows overloading of any sub folder by replacing . by / in pref name - let path = name.substr(1).split('.').join('/'); - // Only accept overloading folder by ensuring always ending with `/` - if (path) path += '/'; - let fileURI = branch.getCharPref(name); - - // On mobile, file URI has to end with a `/` otherwise, setSubstitution - // takes the parent folder instead. - if (fileURI[fileURI.length-1] !== '/') - fileURI += '/'; - - // Maps the given file:// URI to a resource:// in order to avoid various - // failure that happens with file:// URI and be close to production env - let resourcesURI = ioService.newURI(fileURI, null, null); - let resName = 'extensions.modules.' + domain + '.commonjs.path' + name; - setResourceSubstitution(resName, resourcesURI); - - result[path] = 'resource://' + resName + '/'; - return result; - }, paths); - - // Make version 2 of the manifest - let manifest = options.manifest; - - // Import `cuddlefish.js` module using a Sandbox and bootstrap loader. - let cuddlefishPath = 'loader/cuddlefish.js'; - let cuddlefishURI = 'resource://gre/modules/commonjs/sdk/' + cuddlefishPath; - if (paths['sdk/']) { // sdk folder has been overloaded - // (from pref, or cuddlefish is still in the xpi) - cuddlefishURI = paths['sdk/'] + cuddlefishPath; - } - else if (paths['']) { // root modules folder has been overloaded - cuddlefishURI = paths[''] + 'sdk/' + cuddlefishPath; - } - - cuddlefishSandbox = loadSandbox(cuddlefishURI); - let cuddlefish = cuddlefishSandbox.exports; - - // Normalize `options.mainPath` so that it looks like one that will come - // in a new version of linker. - let main = options.mainPath; - - unload = cuddlefish.unload; - loader = cuddlefish.Loader({ - paths: paths, - // modules manifest. - manifest: manifest, - - // Add-on ID used by different APIs as a unique identifier. - id: id, - // Add-on name. - name: name, - // Add-on version. - version: options.metadata[name].version, - // Add-on package descriptor. - metadata: options.metadata[name], - // Add-on load reason. - loadReason: reason, - - prefixURI: prefixURI, - // Add-on URI. - rootURI: rootURI, - // options used by system module. - // File to write 'OK' or 'FAIL' (exit code emulation). - resultFile: options.resultFile, - // Arguments passed as --static-args - staticArgs: options.staticArgs, - // Add-on preferences branch name - preferencesBranch: options.preferencesBranch, - - // Arguments related to test runner. - modules: { - '@test/options': { - allTestModules: options.allTestModules, - iterations: options.iterations, - filter: options.filter, - profileMemory: options.profileMemory, - stopOnError: options.stopOnError, - verbose: options.verbose, - parseable: options.parseable, - checkMemory: options.check_memory, - } - } - }); - - let module = cuddlefish.Module('sdk/loader/cuddlefish', cuddlefishURI); - let require = cuddlefish.Require(loader, module); - - require('sdk/addon/runner').startup(reason, { - loader: loader, - main: main, - prefsURI: rootURI + 'defaults/preferences/prefs.js' - }); - } catch (error) { - dump('Bootstrap error: ' + - (error.message ? error.message : String(error)) + '\n' + - (error.stack || error.fileName + ': ' + error.lineNumber) + '\n'); - throw error; - } -}; - -function loadSandbox(uri) { - let proto = { - sandboxPrototype: { - loadSandbox: loadSandbox, - ChromeWorker: ChromeWorker - } - }; - let sandbox = Cu.Sandbox(systemPrincipal, proto); - // Create a fake commonjs environnement just to enable loading loader.js - // correctly - sandbox.exports = {}; - sandbox.module = { uri: uri, exports: sandbox.exports }; - sandbox.require = function (id) { - if (id !== "chrome") - throw new Error("Bootstrap sandbox `require` method isn't implemented."); - - return Object.freeze({ Cc: Cc, Ci: Ci, Cu: Cu, Cr: Cr, Cm: Cm, - CC: bind(CC, Components), components: Components, - ChromeWorker: ChromeWorker }); - }; - scriptLoader.loadSubScript(uri, sandbox, 'UTF-8'); - return sandbox; -} - -function unloadSandbox(sandbox) { - if ("nukeSandbox" in Cu) - Cu.nukeSandbox(sandbox); -} - -function setTimeout(callback, delay) { - let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - timer.initWithCallback({ notify: callback }, delay, - Ci.nsITimer.TYPE_ONE_SHOT); - return timer; -} - -function shutdown(data, reasonCode) { - let reason = REASON[reasonCode]; - if (loader) { - unload(loader, reason); - unload = null; - - // Don't waste time cleaning up if the application is shutting down - if (reason != "shutdown") { - // Avoid leaking all modules when something goes wrong with one particular - // module. Do not clean it up immediatly in order to allow executing some - // actions on addon disabling. - // We need to keep a reference to the timer, otherwise it is collected - // and won't ever fire. - nukeTimer = setTimeout(nukeModules, 1000); - - // Bug 944951 - bootstrap.js must remove the added resource: URIs on unload - resourceDomains.forEach(domain => { - resourceHandler.setSubstitution(domain, null); - }) - } - } -}; - -function nukeModules() { - nukeTimer = null; - // module objects store `exports` which comes from sandboxes - // We should avoid keeping link to these object to avoid leaking sandboxes - for (let key in loader.modules) { - delete loader.modules[key]; - } - // Direct links to sandboxes should be removed too - for (let key in loader.sandboxes) { - let sandbox = loader.sandboxes[key]; - delete loader.sandboxes[key]; - // Bug 775067: From FF17 we can kill all CCW from a given sandbox - unloadSandbox(sandbox); - } - loader = null; - - // both `toolkit/loader` and `system/xul-app` are loaded as JSM's via - // `cuddlefish.js`, and needs to be unloaded to avoid memory leaks, when - // the addon is unload. - - unloadSandbox(cuddlefishSandbox.loaderSandbox); - unloadSandbox(cuddlefishSandbox.xulappSandbox); - - // Bug 764840: We need to unload cuddlefish otherwise it will stay alive - // and keep a reference to this compartment. - unloadSandbox(cuddlefishSandbox); - cuddlefishSandbox = null; -} +"use strict"; + +const { utils: Cu } = Components; +const rootURI = __SCRIPT_URI_SPEC__.replace("bootstrap.js", ""); +const COMMONJS_URI = "resource://gre/modules/commonjs"; +const { require } = Cu.import(COMMONJS_URI + "/toolkit/require.js", {}); +const { Bootstrap } = require(COMMONJS_URI + "/sdk/addon/bootstrap.js"); +const { startup, shutdown, install, uninstall } = new Bootstrap(rootURI); diff --git a/data/extensions/html5-video-everywhere@lejenome.me/data/break.js b/data/extensions/html5-video-everywhere@lejenome.me/data/break.js new file mode 100644 index 0000000..24fbcb8 --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/data/break.js @@ -0,0 +1,47 @@ +(function() { + "use strict"; + + onReady(() => { + var url_r = /"uri":\s*"([^"]*)"/; + var width_r = /"width":\s*([^\s,]*),/; + var height_r = /"height":\s*([^\s,]*),/; + var fmts = {}; + var data = document.head.innerHTML.match(/"media": \[\s[^\]]*\s\],/); + if (!data) + return; + data = data[0].match(/\{[^}]*\}/g); + data.forEach(it => + fmts[it.match(width_r)[1] + "x" + it.match(height_r)[1]] = it.match(url_r)[1] + ); + injectPlayer(fmts); + }); + + function injectPlayer(fmts) { + rmChildren(document.head); + var vp = new VP(document.body); + vp.srcs(fmts, { + "higher/mp4": "1280x720", + "high/mp4": "848x480", + "medium/mp4": "640x360", + "low/mp4": "301x232" // there is 300x220 too which is audio only + }); + vp.props({ + controls: true, + autoplay: autoPlay(true), + preload: preLoad(), + loop: isLoop() + }); + vp.style({ + width: "100%", + height: "100%" + }); + vp.setup(); + } + + function fallback() { + // Just fallback method if the first one didn't work + var url_r = /"videoUri":\s*"([^"]*)"/; + var url = (document.head.innerHTML.match(url_r) || ["", ""])[1]; + return url; + } +}()); \ No newline at end of file diff --git a/data/extensions/html5-video-everywhere@lejenome.me/data/common.js b/data/extensions/html5-video-everywhere@lejenome.me/data/common.js new file mode 100644 index 0000000..4686831 --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/data/common.js @@ -0,0 +1,109 @@ +/* global OPTIONS:true, onPrefChange:true, LANGS:true */ +/* global createNode:true, asyncGet:true, onReady:true, logify:true */ +/* global preLoad:true, autoPlay:true, HANDLE_VOL_PREF_CHANGE:true */ +/* global rmChildren:true, Qlt:true, Cdc:true, chgPref:true, isLoop:true */ +/* global setClipboard:true */ +// the following jshint global rule is only because jshint support for ES6 arrow +// functions is limited +/* global wrapper:true, args:true, auto:true, lp:true */ +"use strict"; + +//This Addons Preferences +var OPTIONS, init; +// push your prefernces change listner function to this table, yah the old way +const onPrefChange = []; +const Cdc = ["webm", "mp4"]; +const Qlt = ["higher", "high", "medium", "low"]; +const LANGS = ["af", "ar", "bn", "de", "en", "es", "fi", "fr", "hi", "id", "is", "it", "ja", "ko", "pt", "ru", "tu", "zh"]; +// set it to false if the module uses custom listener +var HANDLE_VOL_PREF_CHANGE = true; +self.port.on("preferences", function(prefs) { + OPTIONS = prefs; + if (init) + init(); + onPrefChange.forEach(f => f()); +}); + +self.port.on("prefChanged", function(pref) { + OPTIONS[pref.name] = pref.value; + if (pref.name === "volume" && HANDLE_VOL_PREF_CHANGE === true) + Array.forEach(document.getElementsByTagName("video"), el => { + el.volume = OPTIONS.volume / 100; + }); + onPrefChange.forEach(f => f(pref.name)); +}); +const chgPref = (name, val) => { + self.port.emit("prefChang", { + name: name, + val: val + }); +}; +const setClipboard = (text) => + self.port.emit("setClipboard", text); +const createNode = (type, prprt, style, data) => { + //logify("createNode", type, prprt); + var node = document.createElement(type); + if (prprt) + Object.keys(prprt).forEach(p => node[p] = prprt[p]); + if (style) + Object.keys(style).forEach(s => node.style[s] = style[s]); + if (data) + Object.keys(data).forEach(d => node.dataset[d] = data[d]); + return node; +}; + +const asyncGet = (url, headers, mimetype) => { + logify("asyncGet", url); + return new Promise(function(resolve, reject) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", url, true); + if (headers) + Object.keys(headers).forEach(h => xhr.setRequestHeader(h, headers[h])); + if (mimetype && xhr.overrideMimeType) + xhr.overrideMimeType(mimetype); + xhr.onload = function() { + if (this.status !== 200) { + reject(this.status); + logify("Error on asyncGet", url, headers, this.status); + return; + } + resolve(this.responseText); + }; + xhr.onerror = function() { + reject(); + }; + xhr.send(); + }); +}; + +const logify = (...args) => { + args = args.map(s => JSON.stringify(s, null, 2)); + args.unshift("[DRIVER]"); + dump(args.join(" ") + "\n"); +}; + +const onReady = f => { + if (document.readyState !== "loading") { + if (OPTIONS) + f(); + else + init = f; + } else { + document.addEventListener("DOMContentLoaded", () => { + if (OPTIONS) + f(); + else + init = f; + }); + } +}; +const autoPlay = (auto = false) => ((OPTIONS.autoplay === 1 || auto === true) && + OPTIONS.autoplay !== 0); +const preLoad = (auto = false) => ((OPTIONS.preload === 1 || auto === true) && + OPTIONS.preload !== 0) ? "auto" : "metadata"; +const isLoop = (lp = false) => ((OPTIONS.loop === 1 || lp) && OPTIONS.loop !== 0); + +const rmChildren = (prnt) => { + while (prnt && prnt.firstChild) + prnt.removeChild(prnt.firstChild); +}; \ No newline at end of file diff --git a/data/extensions/html5-video-everywhere@lejenome.me/data/dailymotion.js b/data/extensions/html5-video-everywhere@lejenome.me/data/dailymotion.js new file mode 100644 index 0000000..610ca9a --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/data/dailymotion.js @@ -0,0 +1,47 @@ +(function() { + "use strict"; + + onReady(() => { + + // VIDEO_ID = location.pathname.match(/\/embed\/video\/([^_]+)/)[1]; + // asyncGet http://www.dailymotion.com/json/video/?fields=stream_audio_url,stream_h264_hd1080_url,stream_h264_hd_url,stream_h264_hq_url,stream_h264_ld_url,stream_h264_url,stream_hls_url,stream_live_hls_url,thumbnail_120_url,thumbnail_240_url,thumbnail_url + // returns a json + var urls = {}, + poster; + if (unsafeWindow.info) { + urls = unsafeWindow.info; + poster = unsafeWindow.info.thumbnail_url || + unsafeWindow.info.thumbnail_240_url || + unsafeWindow.info.thumbnail_120_url; + } else { + var streams_r = /"stream_h264[^"]*_url":"[^"]*"/g; + var url_r = /"(stream_h264[^"]*_url)":"([^"]*)"/; + var streams = document.body.innerHTML.match(streams_r); + streams.forEach(u => { + var r = u.match(url_r); + urls[r[1]] = r[2].replace("\\/", "/", "g"); + }); + poster = (document.body.innerHTML.match(/"thumbnail_url":"([^"]*)"/) || ["", ""])[1].replace("\\/", "/", "g"); + } + var vp = new VP(document.body); + vp.srcs(urls, { + // stream_h264_hd1080_url + "higher/mp4": "stream_h264_hd_url", // H264 1280x720 + "high/mp4": "stream_h264_hq_url", // H264 848x480 + "medium/mp4": "stream_h264_url", // H264 512x384 + "low/mp4": "stream_h264_ld_url" // H264 320x240 + }); + vp.props({ + controls: true, + autoplay: autoPlay(), + preload: preLoad(), + loop: isLoop(), + poster: poster + }); + vp.style({ + width: "100%", + height: "100%" + }); + vp.setup(); + }); +}()); \ No newline at end of file diff --git a/data/extensions/html5-video-everywhere@lejenome.me/data/facebook.js b/data/extensions/html5-video-everywhere@lejenome.me/data/facebook.js new file mode 100644 index 0000000..051d32a --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/data/facebook.js @@ -0,0 +1,27 @@ +(function() { + "use strict"; + onReady(() => { + var params = document.body.innerHTML.match(/"params",("[^"]*")/)[1]; + params = JSON.parse(decodeURIComponent(JSON.parse(params))); + // var container = document.getElementsByClassName("_53j5")[0]; + var container = document.getElementsByClassName("stageContainer")[0]; + var vp = new VP(container); + logify(params.video_data[0]); + vp.srcs({ + "medium/mp4": params.video_data[0].sd_src, + "high/mp4": params.video_data[0].hd_src + }); + vp.props({ + controls: true, + autoplay: autoPlay(true), + preload: preLoad(), + loop: isLoop() + }); + vp.style({ + width: "100%", + heigth: "100%" + }); + vp.setup(); + + }); +}()); \ No newline at end of file diff --git a/data/extensions/html5-video-everywhere@lejenome.me/data/flashgot-YouTubeSwf.js b/data/extensions/html5-video-everywhere@lejenome.me/data/flashgot-YouTubeSwf.js new file mode 100644 index 0000000..8f36a47 --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/data/flashgot-YouTubeSwf.js @@ -0,0 +1,2185 @@ +/***** BEGIN LICENSE BLOCK ***** + + FlashGot - a Firefox extension for external download managers integration + Copyright (C) 2004-2013 Giorgio Maone - g.maone@informaction.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +***** END LICENSE BLOCK *****/ +/* the following jshint rules are added by Moez Bouhlel to pass + * HTML5-Video-EveryWhere build validation */ +/* jshint -W033, -W055, -W116, -W016, -W073, -W040, -W071, -W074, -W032, -W004, -W027, -W035, -W052, -W062, -W059, -W084*/ +/* jshint strict:false */ +/* global onmessage:true, postMessage */ +onmessage = function(evt) { + try { + var rc = refresh_signature_func(evt.data); + postMessage({ + type: "result", + data: rc + }); + } + // Log errors here because the stacktrace is lost + // when they're marshalled to the onerror handler. + catch (x) { + log("YoutubeSwf: exception: " + (x.message || x) + "\n" + (x.stack || new Error().stack)); + } finally { + // Notify the caller we're done. They will terminate us. + postMessage({ + type: "done" + }); + } +} + +function log(msg) { + postMessage({ + type: "log", + data: String(msg) + }); +} + + + +var zip_inflate = function() { + // http://www.onicos.com/staff/iz/amuse/javascript/expert/inflate.txt + /* Copyright (C) 1999,2012 Masanao Izumo + * Version: 1.0.1 + * LastModified: Jun 29 2012 + */ + + /* Interface: + * data = zip_inflate(src); + */ + + /* constant parameters */ + var zip_WSIZE = 32768; // Sliding Window size + var zip_STORED_BLOCK = 0; + var zip_STATIC_TREES = 1; + var zip_DYN_TREES = 2; + + /* for inflate */ + var zip_lbits = 9; // bits in base literal/length lookup table + var zip_dbits = 6; // bits in base distance lookup table + var zip_INBUFSIZ = 32768; // Input buffer size + var zip_INBUF_EXTRA = 64; // Extra buffer + + /* variables (inflate) */ + var zip_slide; + var zip_wp; // current position in slide + var zip_fixed_tl = null; // inflate static + var zip_fixed_td; // inflate static + var zip_fixed_bl, zip_fixed_bd; // inflate static + var zip_bit_buf; // bit buffer + var zip_bit_len; // bits in bit buffer + var zip_method; + var zip_eof; + var zip_copy_leng; + var zip_copy_dist; + var zip_tl, zip_td; // literal/length and distance decoder tables + var zip_bl, zip_bd; // number of bits decoded by tl and td + + var zip_inflate_data; + var zip_inflate_pos; + + + /* constant tables (inflate) */ + var zip_MASK_BITS = new Array( + 0x0000, + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff); + // Tables for deflate from PKZIP's appnote.txt. + var zip_cplens = new Array( // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0); + /* note: see note #13 above about the 258 in this list. */ + var zip_cplext = new Array( // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99); // 99==invalid + var zip_cpdist = new Array( // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577); + var zip_cpdext = new Array( // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13); + var zip_border = new Array( // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15); + /* objects (inflate) */ + + function zip_HuftList() { + this.next = null; + this.list = null; + } + + function zip_HuftNode() { + this.e = 0; // number of extra bits or operation + this.b = 0; // number of bits in this code or subcode + + // union + this.n = 0; // literal, length base, or distance base + this.t = null; // (zip_HuftNode) pointer to next level of table + } + + function zip_HuftBuild(b, // code lengths in bits (all assumed <= BMAX) + n, // number of codes (assumed <= N_MAX) + s, // number of simple-valued codes (0..s-1) + d, // list of base values for non-simple codes + e, // list of extra bits for non-simple codes + mm // maximum lookup bits + ) { + this.BMAX = 16; // maximum bit length of any code + this.N_MAX = 288; // maximum number of codes in any set + this.status = 0; // 0: success, 1: incomplete table, 2: bad input + this.root = null; // (zip_HuftList) starting table + this.m = 0; // maximum lookup bits, returns actual + + /* Given a list of code lengths and a maximum table size, make a set of + tables to decode that set of codes. Return zero on success, one if + the given code set is incomplete (the tables are still built in this + case), two if the input is invalid (all zero length codes or an + oversubscribed set of lengths), and three if not enough memory. + The code with value 256 is special, and the tables are constructed + so that no bits beyond that code are fetched when that code is + decoded. */ + { + var a; // counter for codes of length k + var c = new Array(this.BMAX + 1); // bit length count table + var el; // length of EOB code (value 256) + var f; // i repeats in table every f entries + var g; // maximum code length + var h; // table level + var i; // counter, current code + var j; // counter + var k; // number of bits in current code + var lx = new Array(this.BMAX + 1); // stack of bits per table + var p; // pointer into c[], b[], or v[] + var pidx; // index of p + var q; // (zip_HuftNode) points to current table + var r = new zip_HuftNode(); // table entry for structure assignment + var u = new Array(this.BMAX); // zip_HuftNode[BMAX][] table stack + var v = new Array(this.N_MAX); // values in order of bit length + var w; + var x = new Array(this.BMAX + 1); // bit offsets, then code stack + var xp; // pointer into x or c + var y; // number of dummy codes added + var z; // number of entries in current table + var o; + var tail; // (zip_HuftList) + + tail = this.root = null; + for (i = 0; i < c.length; i++) + c[i] = 0; + for (i = 0; i < lx.length; i++) + lx[i] = 0; + for (i = 0; i < u.length; i++) + u[i] = null; + for (i = 0; i < v.length; i++) + v[i] = 0; + for (i = 0; i < x.length; i++) + x[i] = 0; + + // Generate counts for each bit length + el = n > 256 ? b[256] : this.BMAX; // set length of EOB code, if any + p = b; + pidx = 0; + i = n; + do { + c[p[pidx]] ++; // assume all entries <= BMAX + pidx++; + } while (--i > 0); + if (c[0] == n) { // null input--all zero length codes + this.root = null; + this.m = 0; + this.status = 0; + return; + } + + // Find minimum and maximum length, bound *m by those + for (j = 1; j <= this.BMAX; j++) + if (c[j] != 0) + break; + k = j; // minimum code length + if (mm < j) + mm = j; + for (i = this.BMAX; i != 0; i--) + if (c[i] != 0) + break; + g = i; // maximum code length + if (mm > i) + mm = i; + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1) + if ((y -= c[j]) < 0) { + this.status = 2; // bad input: more codes than bits + this.m = mm; + return; + } + if ((y -= c[i]) < 0) { + this.status = 2; + this.m = mm; + return; + } + c[i] += y; + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = c; + pidx = 1; + xp = 2; + while (--i > 0) // note that i == g from above + x[xp++] = (j += p[pidx++]); + + // Make a table of values in order of bit lengths + p = b; + pidx = 0; + i = 0; + do { + if ((j = p[pidx++]) != 0) + v[x[j] ++] = i; + } while (++i < n); + n = x[g]; // set n to length of v + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = v; + pidx = 0; // grab values in bit order + h = -1; // no tables yet--level -1 + w = lx[0] = 0; // no bits decoded yet + q = null; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++) { + a = c[k]; + while (a-- > 0) { + // here i is the Huffman code of length k bits for value p[pidx] + // make tables up to required level + while (k > w + lx[1 + h]) { + w += lx[1 + h]; // add bits already decoded + h++; + + // compute minimum size table less than or equal to *m bits + z = (z = g - w) > mm ? mm : z; // upper limit + if ((f = 1 << (j = k - w)) > a + 1) { // try a k-w bit table + // too few codes for k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = k; + while (++j < z) { // try smaller tables up to z bits + if ((f <<= 1) <= c[++xp]) + break; // enough codes to use up j bits + f -= c[xp]; // else deduct codes from patterns + } + } + if (w + j > el && w < el) + j = el - w; // make EOB code end at table + z = 1 << j; // table entries for j-bit table + lx[1 + h] = j; // set table size in stack + + // allocate and link in new table + q = new Array(z); + for (o = 0; o < z; o++) { + q[o] = new zip_HuftNode(); + } + + if (tail == null) + tail = this.root = new zip_HuftList(); + else + tail = tail.next = new zip_HuftList(); + tail.next = null; + tail.list = q; + u[h] = q; // table starts after link + + /* connect to last table, if there is one */ + if (h > 0) { + x[h] = i; // save pattern for backing up + r.b = lx[h]; // bits to dump before this table + r.e = 16 + j; // bits in this table + r.t = q; // pointer to this table + j = (i & ((1 << w) - 1)) >> (w - lx[h]); + u[h - 1][j].e = r.e; + u[h - 1][j].b = r.b; + u[h - 1][j].n = r.n; + u[h - 1][j].t = r.t; + } + } + + // set up table entry in r + r.b = k - w; + if (pidx >= n) + r.e = 99; // out of values--invalid code + else if (p[pidx] < s) { + r.e = (p[pidx] < 256 ? 16 : 15); // 256 is end-of-block code + r.n = p[pidx++]; // simple code is just the value + } else { + r.e = e[p[pidx] - s]; // non-simple--look up in lists + r.n = d[p[pidx++] - s]; + } + + // fill code-like entries with r // + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) { + q[j].e = r.e; + q[j].b = r.b; + q[j].n = r.n; + q[j].t = r.t; + } + + // backwards increment the k-bit code i + for (j = 1 << (k - 1); + (i & j) != 0; j >>= 1) + i ^= j; + i ^= j; + + // backup over finished tables + while ((i & ((1 << w) - 1)) != x[h]) { + w -= lx[h]; // don't need to update q + h--; + } + } + } + + /* return actual size of base table */ + this.m = lx[1]; + + /* Return true (1) if we were given an incomplete table */ + this.status = ((y != 0 && g != 1) ? 1 : 0); + } /* end of constructor */ + } + + + /* routines (inflate) */ + + function zip_GET_BYTE() { + if (zip_inflate_data.length == zip_inflate_pos) { + throw new Error("EOF"); + return -1; + } + return zip_inflate_data.charCodeAt(zip_inflate_pos++) & 0xff; + } + + function zip_NEEDBITS(n) { + while (zip_bit_len < n) { + zip_bit_buf |= zip_GET_BYTE() << zip_bit_len; + zip_bit_len += 8; + } + } + + function zip_GETBITS(n) { + return zip_bit_buf & zip_MASK_BITS[n]; + } + + function zip_DUMPBITS(n) { + zip_bit_buf >>= n; + zip_bit_len -= n; + } + + function zip_inflate_codes(buff, off, size) { + /* inflate (decompress) the codes in a deflated (compressed) block. + Return an error code or zero if it all goes ok. */ + var e; // table entry flag/number of extra bits + var t; // (zip_HuftNode) pointer to table entry + var n; + + if (size == 0) + return 0; + + // inflate the coded data + n = 0; + for (;;) { // do until end of block + zip_NEEDBITS(zip_bl); + t = zip_tl.list[zip_GETBITS(zip_bl)]; + e = t.e; + while (e > 16) { + if (e == 99) + return -1; + zip_DUMPBITS(t.b); + e -= 16; + zip_NEEDBITS(e); + t = t.t[zip_GETBITS(e)]; + e = t.e; + } + zip_DUMPBITS(t.b); + + if (e == 16) { // then it's a literal + zip_wp &= zip_WSIZE - 1; + buff[off + n++] = zip_slide[zip_wp++] = t.n; + if (n == size) + return size; + continue; + } + + // exit if end of block + if (e == 15) + break; + + // it's an EOB or a length + + // get length of block to copy + zip_NEEDBITS(e); + zip_copy_leng = t.n + zip_GETBITS(e); + zip_DUMPBITS(e); + + // decode distance of block to copy + zip_NEEDBITS(zip_bd); + t = zip_td.list[zip_GETBITS(zip_bd)]; + e = t.e; + + while (e > 16) { + if (e == 99) + return -1; + zip_DUMPBITS(t.b); + e -= 16; + zip_NEEDBITS(e); + t = t.t[zip_GETBITS(e)]; + e = t.e; + } + zip_DUMPBITS(t.b); + zip_NEEDBITS(e); + zip_copy_dist = zip_wp - t.n - zip_GETBITS(e); + zip_DUMPBITS(e); + + // do the copy + while (zip_copy_leng > 0 && n < size) { + zip_copy_leng--; + zip_copy_dist &= zip_WSIZE - 1; + zip_wp &= zip_WSIZE - 1; + buff[off + n++] = zip_slide[zip_wp++] = zip_slide[zip_copy_dist++]; + } + + if (n == size) + return size; + } + + zip_method = -1; // done + return n; + } + + function zip_inflate_stored(buff, off, size) { + /* "decompress" an inflated type 0 (stored) block. */ + var n; + + // go to byte boundary + n = zip_bit_len & 7; + zip_DUMPBITS(n); + + // get the length and its complement + zip_NEEDBITS(16); + n = zip_GETBITS(16); + zip_DUMPBITS(16); + zip_NEEDBITS(16); + if (n != ((~zip_bit_buf) & 0xffff)) { + throw new Error("n != (~zip_bit_buf) & 0xffff"); + return -1; // error in compressed data + } + zip_DUMPBITS(16); + + // read and output the compressed data + zip_copy_leng = n; + + n = 0; + while (zip_copy_leng > 0 && n < size) { + zip_copy_leng--; + zip_wp &= zip_WSIZE - 1; + zip_NEEDBITS(8); + buff[off + n++] = zip_slide[zip_wp++] = zip_GETBITS(8); + zip_DUMPBITS(8); + } + + if (zip_copy_leng == 0) + zip_method = -1; // done + return n; + } + + function zip_inflate_fixed(buff, off, size) { + /* decompress an inflated type 1 (fixed Huffman codes) block. We should + either replace this with a custom decoder, or at least precompute the + Huffman tables. */ + + // if first time, set up tables for fixed blocks + if (zip_fixed_tl == null) { + var i; // temporary variable + var l = new Array(288); // length list for huft_build + var h; // zip_HuftBuild + + // literal table + for (i = 0; i < 144; i++) + l[i] = 8; + for (; i < 256; i++) + l[i] = 9; + for (; i < 280; i++) + l[i] = 7; + for (; i < 288; i++) // make a complete, but wrong code set + l[i] = 8; + zip_fixed_bl = 7; + + h = new zip_HuftBuild(l, 288, 257, zip_cplens, zip_cplext, + zip_fixed_bl); + if (h.status != 0) { + throw new Error("HufBuild error: " + h.status); + return -1; + } + zip_fixed_tl = h.root; + zip_fixed_bl = h.m; + + // distance table + for (i = 0; i < 30; i++) // make an incomplete code set + l[i] = 5; + zip_fixed_bd = 5; + + h = new zip_HuftBuild(l, 30, 0, zip_cpdist, zip_cpdext, zip_fixed_bd); + if (h.status > 1) { + zip_fixed_tl = null; + throw new Error("HufBuild error: " + h.status); + return -1; + } + zip_fixed_td = h.root; + zip_fixed_bd = h.m; + } + + zip_tl = zip_fixed_tl; + zip_td = zip_fixed_td; + zip_bl = zip_fixed_bl; + zip_bd = zip_fixed_bd; + return zip_inflate_codes(buff, off, size); + } + + function zip_inflate_dynamic(buff, off, size) { + // decompress an inflated type 2 (dynamic Huffman codes) block. + var i; // temporary variables + var j; + var l; // last length + var n; // number of lengths to get + var t; // (zip_HuftNode) literal/length code table + var nb; // number of bit length codes + var nl; // number of literal/length codes + var nd; // number of distance codes + var ll = new Array(286 + 30); // literal/length and distance code lengths + var h; // (zip_HuftBuild) + + for (i = 0; i < ll.length; i++) + ll[i] = 0; + + // read in table lengths + zip_NEEDBITS(5); + nl = 257 + zip_GETBITS(5); // number of literal/length codes + zip_DUMPBITS(5); + zip_NEEDBITS(5); + nd = 1 + zip_GETBITS(5); // number of distance codes + zip_DUMPBITS(5); + zip_NEEDBITS(4); + nb = 4 + zip_GETBITS(4); // number of bit length codes + zip_DUMPBITS(4); + if (nl > 286 || nd > 30) + return -1; // bad lengths + + // read in bit-length-code lengths + for (j = 0; j < nb; j++) { + zip_NEEDBITS(3); + ll[zip_border[j]] = zip_GETBITS(3); + zip_DUMPBITS(3); + } + for (; j < 19; j++) + ll[zip_border[j]] = 0; + + // build decoding table for trees--single level, 7 bit lookup + zip_bl = 7; + h = new zip_HuftBuild(ll, 19, 19, null, null, zip_bl); + if (h.status != 0) + return -1; // incomplete code set + + zip_tl = h.root; + zip_bl = h.m; + + // read in literal and distance code lengths + n = nl + nd; + i = l = 0; + while (i < n) { + zip_NEEDBITS(zip_bl); + t = zip_tl.list[zip_GETBITS(zip_bl)]; + j = t.b; + zip_DUMPBITS(j); + j = t.n; + if (j < 16) // length of code in bits (0..15) + ll[i++] = l = j; // save last length in l + else if (j == 16) { // repeat last length 3 to 6 times + zip_NEEDBITS(2); + j = 3 + zip_GETBITS(2); + zip_DUMPBITS(2); + if (i + j > n) + return -1; + while (j-- > 0) + ll[i++] = l; + } else if (j == 17) { // 3 to 10 zero length codes + zip_NEEDBITS(3); + j = 3 + zip_GETBITS(3); + zip_DUMPBITS(3); + if (i + j > n) + return -1; + while (j-- > 0) + ll[i++] = 0; + l = 0; + } else { // j == 18: 11 to 138 zero length codes + zip_NEEDBITS(7); + j = 11 + zip_GETBITS(7); + zip_DUMPBITS(7); + if (i + j > n) + return -1; + while (j-- > 0) + ll[i++] = 0; + l = 0; + } + } + + // build the decoding tables for literal/length and distance codes + zip_bl = zip_lbits; + h = new zip_HuftBuild(ll, nl, 257, zip_cplens, zip_cplext, zip_bl); + if (zip_bl == 0) // no literals or lengths + h.status = 1; + if (h.status != 0) { + if (h.status == 1) + ; // **incomplete literal tree** + return -1; // incomplete code set + } + zip_tl = h.root; + zip_bl = h.m; + + for (i = 0; i < nd; i++) + ll[i] = ll[i + nl]; + zip_bd = zip_dbits; + h = new zip_HuftBuild(ll, nd, 0, zip_cpdist, zip_cpdext, zip_bd); + zip_td = h.root; + zip_bd = h.m; + + if (zip_bd == 0 && nl > 257) { // lengths but no distances + // **incomplete distance tree** + return -1; + } + + if (h.status == 1) {; // **incomplete distance tree** + } + if (h.status != 0) + return -1; + + // decompress until an end-of-block code + return zip_inflate_codes(buff, off, size); + } + + function zip_inflate_start() { + var i; + + if (zip_slide == null) + zip_slide = new Array(2 * zip_WSIZE); + zip_wp = 0; + zip_bit_buf = 0; + zip_bit_len = 0; + zip_method = -1; + zip_eof = false; + zip_copy_leng = zip_copy_dist = 0; + zip_tl = null; + } + + function zip_inflate_internal(buff, off, size) { + // decompress an inflated entry + var n, i; + + n = 0; + while (n < size) { + if (zip_eof && zip_method == -1) { + return n; + } + + if (zip_copy_leng > 0) { + if (zip_method != zip_STORED_BLOCK) { + // STATIC_TREES or DYN_TREES + while (zip_copy_leng > 0 && n < size) { + zip_copy_leng--; + zip_copy_dist &= zip_WSIZE - 1; + zip_wp &= zip_WSIZE - 1; + buff[off + n++] = zip_slide[zip_wp++] = + zip_slide[zip_copy_dist++]; + } + } else { + while (zip_copy_leng > 0 && n < size) { + zip_copy_leng--; + zip_wp &= zip_WSIZE - 1; + zip_NEEDBITS(8); + buff[off + n++] = zip_slide[zip_wp++] = zip_GETBITS(8); + zip_DUMPBITS(8); + } + if (zip_copy_leng == 0) + zip_method = -1; // done + } + if (n == size) { + return n; + } + } + + if (zip_method == -1) { + if (zip_eof) { + break; + } + + // read in last block bit + zip_NEEDBITS(1); + if (zip_GETBITS(1) != 0) + zip_eof = true; + zip_DUMPBITS(1); + + // read in block type + zip_NEEDBITS(2); + zip_method = zip_GETBITS(2); + zip_DUMPBITS(2); + zip_tl = null; + zip_copy_leng = 0; + } + + switch (zip_method) { + case 0: // zip_STORED_BLOCK + i = zip_inflate_stored(buff, off + n, size - n); + break; + + case 1: // zip_STATIC_TREES + if (zip_tl != null) + i = zip_inflate_codes(buff, off + n, size - n); + else + i = zip_inflate_fixed(buff, off + n, size - n); + break; + + case 2: // zip_DYN_TREES + if (zip_tl != null) + i = zip_inflate_codes(buff, off + n, size - n); + else + i = zip_inflate_dynamic(buff, off + n, size - n); + break; + + default: // error + i = -1; + break; + } + + if (i == -1) { + if (zip_eof) + return 0; + return -1; + } + n += i; + } + return n; + } + + function zip_inflate(data, offset /*= 0*/ ) { + var out, buff; + var i, j; + + zip_inflate_start(); + zip_inflate_data = data; + zip_inflate_pos = offset || 0; + var last_zip_inflate_pos = -1; + + if (!0) { + zip_NEEDBITS(8); + var CMF = zip_GETBITS(8); + zip_DUMPBITS(8); + zip_NEEDBITS(8); + var FLG = zip_GETBITS(8); + zip_DUMPBITS(8); + if ((CMF & 0x0f) !== 8) { + throw new Error("Unsupported compression."); + } + if (FLG & 0x20) { + throw new Error("DICT"); + } + if ((CMF * 256 + FLG) % 31 !== 0) { + throw new Error("FCHECK"); + } + } + + buff = new Array(1024); + out = ""; + while ((i = zip_inflate_internal(buff, 0, buff.length)) > 0 && last_zip_inflate_pos != zip_inflate_pos) { + last_zip_inflate_pos = zip_inflate_pos; + for (j = 0; j < i; j++) out += String.fromCharCode(buff[j]); + } + zip_inflate_data = null; // G.C. + return out; + } + + return zip_inflate; +}(); + + + +function swf_inflate(data) { + var sig = data.substr(0, 3); + if (sig === "FWS") { + return data; + } + if (sig !== "CWS") { + throw new Error("Invalid signature: " + sig); + } + var ss = new SwfStream(data); + ss.skip_bytes(4); // Signature, version. + var inflated_size = ss.read_uint32(); + var inflated = zip_inflate(data, 8); + if (inflated.length + 8 !== inflated_size) { + throw new Error("Inflated size mismatch: expected " + inflated_size + ", got " + inflated.length); + } + return "FWS" + data.substring(3, 8) + inflated; +} + + + +function SwfStream(data) { + var m_bytes = data; + var m_byte_idx = 0; + var m_buf = 0; + var m_buf_bits = 0; + + var byte_align = this.byte_align = function() { + m_buf_bits = 0; + }; + + var read_bits = this.read_bits = function(count) { + var rc = 0; + while (count !== 0) { + if (m_buf_bits === 0) { + if (m_byte_idx >= m_bytes.length) { + throw new Error("EOF"); + } + m_buf = m_bytes.charCodeAt(m_byte_idx++) & 0xff; + m_buf_bits = 8; + } + var cpy_bits = Math.min(count, m_buf_bits); + rc = (rc << cpy_bits) | n_hbits8(m_buf, cpy_bits); + m_buf = (m_buf << cpy_bits) & 0xff; + m_buf_bits -= cpy_bits; + count -= cpy_bits; + } + return rc; + }; + + + var read_uint8 = this.read_uint8 = function() { + byte_align(); + return read_bits(8); + }; + + + var read_uint16 = this.read_uint16 = function() { + var b1 = read_uint8(); + var b2 = read_uint8() << 8; + return b1 | b2; + }; + + + var read_uint32 = this.read_uint32 = function() { + return read_sint32() >>> 0; + }; + + + var read_sint32 = this.read_sint32 = function() { + var b1 = read_uint8(); + var b2 = read_uint8() << 8; + var b3 = read_uint8() << 16; + var b4 = read_uint8() << 24; + return b1 | b2 | b3 | b4; + }; + + + var read_fixed8 = this.read_fixed8 = function() { + var a = read_uint8(); + var b = read_uint8(); + return b + (a / 0x100); + }; + + var read_fixed = this.read_fixed = function() { + var a = read_uint16(); + var b = read_uint16(); + return b + (a / 0x10000); + }; + + var read_bytes = this.read_bytes = function(count) { + byte_align(); + if (m_byte_idx + count > m_bytes.length) { + m_byte_idx = m_bytes.length; + throw new Error("EOF"); + } + return m_bytes.slice(m_byte_idx, m_byte_idx += count); + }; + + + var skip_bytes = this.skip_bytes = function(count) { + byte_align(); + m_byte_idx += count; + }; + + var n_hbits8 = this.n_hbits8 = function(n, count) { + return (n & 0xff) >> (8 - count); + }; + + + var n_hbits16 = this.n_hbits16 = function(n, count) { + return (n & 0xffff) >> (16 - count); + }; + + + var n_lbits16 = this.n_lbits16 = function(n, count) { + return n & ((1 << count) - 1); + }; +}; + + + +// namespace_info kinds. +const doabc_CONSTANT_Namespace = 0x08; +const doabc_CONSTANT_PackageNamespace = 0x16; +const doabc_CONSTANT_PackageInternalNs = 0x17; +const doabc_CONSTANT_ProtectedNamespace = 0x18; +const doabc_CONSTANT_ExplicitNamespace = 0x19; +const doabc_CONSTANT_StaticProtectedNs = 0x1A; +const doabc_CONSTANT_PrivateNs = 0x05; + +// multiname_info kinds. +const doabc_CONSTANT_QName = 0x07; +const doabc_CONSTANT_QNameA = 0x0D; +const doabc_CONSTANT_RTQName = 0x0F; +const doabc_CONSTANT_RTQNameA = 0x10; +const doabc_CONSTANT_RTQNameL = 0x11; +const doabc_CONSTANT_RTQNameLA = 0x12; +const doabc_CONSTANT_Multiname = 0x09; +const doabc_CONSTANT_MultinameA = 0x0E; +const doabc_CONSTANT_MultinameL = 0x1B; +const doabc_CONSTANT_MultinameLA = 0x1C; + +// option_detail kinds: these plus namespace_info kinds. +const doabc_CONSTANT_Int = 0x03; +const doabc_CONSTANT_UInt = 0x04; +const doabc_CONSTANT_Double = 0x06; +const doabc_CONSTANT_Utf8 = 0x01; +const doabc_CONSTANT_True = 0x0B; +const doabc_CONSTANT_False = 0x0A; +const doabc_CONSTANT_Null = 0x0C; +const doabc_CONSTANT_Undefined = 0x00; + +// method_info flags. +const doabc_MF_NEED_ARGUMENTS = 0x01; +const doabc_MF_NEED_ACTIVATION = 0x02; +const doabc_MF_NEED_REST = 0x04; +const doabc_MF_HAS_OPTIONAL = 0x08; +const doabc_MF_SET_DXNS = 0x40; +const doabc_MF_HAS_PARAM_NAMES = 0x80; + +// traits_info kinds. +const doabc_Trait_Slot = 0; +const doabc_Trait_Method = 1; +const doabc_Trait_Getter = 2; +const doabc_Trait_Setter = 3; +const doabc_Trait_Class = 4; +const doabc_Trait_Function = 5; +const doabc_Trait_Const = 6; + +// traits_info attributes. +const doabc_ATTR_Final = 0x01; +const doabc_ATTR_Override = 0x02; +const doabc_ATTR_Metadata = 0x04; + +// instance_info flags. +const doabc_CONSTANT_ClassSealed = 0x01; +const doabc_CONSTANT_ClassFinal = 0x02; +const doabc_CONSTANT_ClassInterface = 0x04; +const doabc_CONSTANT_ClassProtectedNs = 0x08; + + + +function doABCReader(bytes) { + var m_bytes = bytes; + var m_byte_idx = 0; + + var eof = this.eof = function() { + return m_byte_idx >= m_bytes.length; + }; + + + var read_bytes = this.read_bytes = function(count) { + var end = m_byte_idx + count; + if (end > m_bytes.length) { + m_byte_idx = m_bytes.length; + throw new Error("EOF"); + } + var rc = m_bytes.slice(m_byte_idx, end); + m_byte_idx = end; + return rc; + }; + + var read_u8 = this.read_u8 = function() { + if (eof()) { + throw new Error("EOF"); + } + return m_bytes.charCodeAt(m_byte_idx++) & 0xff; + }; + + + var read_u16 = this.read_u16 = function() { + var b1 = read_u8(); + var b2 = read_u8(); + return (b2 << 8) | b1; + }; + + + var read_s24 = this.read_s24 = function() { + var b1 = read_u8(); + var b2 = read_u8() << 8; + var b3 = read_u8() << 16; + var n = b1 | b2 | b3; + // 0x800000 is 2^23, 0x1000000 is 2^24. + if (n >= 0x800000) { + n -= 0x1000000; + } + return n; + }; + + + var read_u32 = this.read_u32 = function() { + var n = 0; + for (var i = 0; i !== 5; ++i) { + var b = read_u8(); + const has_next = b & 0x80; + b &= 0x7f; + // Sanity check. + if (i === 4) { + // The last byte must not have the high bit set. + if (has_next) { + throw new Error("Invalid stream: high bit is set, but no more bytes needed."); + } + // The last byte can't have non-zero bits 4-7, + // i.e. it can't be greater than 0x0f (bin: 00001111), + // otherwise it would overflow the result value: 4*7 bits + // in the previous bytes = 28, so there's only 4 bits left, + // namely, 0-3. + if (b > 0x0f) { + throw new Error("Invalid stream: too many bits set in the last byte."); + } + } // Sanity check. + n |= b << (i * 7); + if (!has_next) { + break; + } + } + return n >>> 0; + }; + + + var read_u30 = this.read_u30 = function() { + // 0x3fffffff is 2^30 - 1. + return read_u32() & 0x3fffffff; + }; + + + var read_s32 = this.read_s32 = function() { + return read_u32() | 0; + }; + + var read_d64 = this.read_d64 = function() { + // static const boolean little_endian = new Uint8Array(new Uint16Array([1]).buffer)[0] == 1; + var _static = arguments.callee; + if (_static["doABCReader::little_endian"] === undefined) { + _static["doABCReader::little_endian"] = new Uint8Array(new Uint16Array([1]).buffer)[0] === 1; + } + var b = new Array(8); + for (var i = 0; i !== 8; ++i) { + b[i] = read_u8(); + } + if (!_static["doABCReader::little_endian"]) { + b.reverse(); + } + return new Float64Array(new Uint8Array(b).buffer)[0]; + }; + + + var read_cpool_info = this.read_cpool_info = function() { + var cp = new doabc_cpool_info(); + + var cnt; + + cnt = read_u30(); + if (cnt !== 0) { + cp.integers = new Array(cnt); + cp.integers[0] = 0; + for (var i = 1; i !== cnt; ++i) { + cp.integers[i] = read_s32(); + } + } + + cnt = read_u30(); + if (cnt !== 0) { + cp.uintegers = new Array(cnt); + cp.uintegers[0] = 0; + for (var i = 1; i !== cnt; ++i) { + cp.uintegers[i] = read_u32(); + } + } + + cnt = read_u30(); + if (cnt !== 0) { + cp.doubles = new Array(cnt); + cp.doubles[0] = 0; + for (var i = 1; i !== cnt; ++i) { + cp.doubles[i] = read_d64(); + } + } + + cnt = read_u30(); + if (cnt !== 0) { + cp.strings = new Array(cnt); + cp.strings[0] = ""; + for (var i = 1; i !== cnt; ++i) { + cp.strings[i] = read_string_info(); + } + } + + cnt = read_u30(); + if (cnt !== 0) { + cp.namespaces = new Array(cnt); + cp.namespaces[0] = new doabc_namespace_info(); + for (var i = 1; i !== cnt; ++i) { + cp.namespaces[i] = read_namespace_info(); + } + } + + cnt = read_u30(); + if (cnt !== 0) { + cp.ns_sets = new Array(cnt); + cp.ns_sets[0] = new doabc_ns_set_info(); + for (var i = 1; i !== cnt; ++i) { + cp.ns_sets[i] = read_ns_set_info(); + } + } + + cnt = read_u30(); + if (cnt !== 0) { + cp.multinames = new Array(cnt); + cp.multinames[0] = new doabc_multiname_info(); + for (var i = 1; i !== cnt; ++i) { + cp.multinames[i] = read_multiname_info(); + } + } + + return cp; + }; + + + var read_string_info = this.read_string_info = function() { + var size = read_u30(); + return size !== 0 ? utf8_decode(read_bytes(size)) : ""; + }; + + + var read_namespace_info = this.read_namespace_info = function() { + var o = {}; //new doabc_namespace_info(); + o.kind = read_u8(); + o.name = read_u30(); + return o; + }; + + + var read_ns_set_info = this.read_ns_set_info = function() { + var o = {}; //new doabc_ns_set_info(); + var cnt = read_u8(); + o.ns = new Array(cnt); + for (var i = 0; i !== cnt; ++i) { + o.ns[i] = read_u30(); + } + return o; + }; + + + var read_multiname_info = this.read_multiname_info = function() { + var o; + var kind = read_u8(); + switch (kind) { + case doabc_CONSTANT_QName: + case doabc_CONSTANT_QNameA: + o = read_multiname_info_QName(); + break; + case doabc_CONSTANT_RTQName: + case doabc_CONSTANT_RTQNameA: + o = read_multiname_info_RTQName(); + break; + case doabc_CONSTANT_RTQNameL: + case doabc_CONSTANT_RTQNameLA: + o = read_multiname_info_RTQNameL(); + break; + case doabc_CONSTANT_Multiname: + case doabc_CONSTANT_MultinameA: + o = read_multiname_info_Multiname(); + break; + case doabc_CONSTANT_MultinameL: + case doabc_CONSTANT_MultinameLA: + o = read_multiname_info_MultinameL(); + break; + default: + throw new Error("Unexpected multiname kind: " + kind); + } + o.kind = kind; + return o; + }; + + + var read_method_info = this.read_method_info = function() { + var o = {}; //new doabc_method_info(); + + var cnt = read_u30(); + o.return_type = read_u30(); + + o.param_types = new Array(cnt); + for (var i = 0; i !== cnt; ++i) { + o.param_types[i] = read_u30(); + } + + o.name = read_u30(); + o.flags = read_u8(); + + if (o.flags & doabc_MF_HAS_OPTIONAL) { + var cnt = read_u30(); + o.options = new Array(cnt); + for (var i = 0; i !== cnt; ++i) { + o.options[i] = read_option_detail(); + } + } + + if (o.flags & doabc_MF_HAS_PARAM_NAMES) { + var cnt = read_u30(); + o.param_names = new Array(cnt); + for (var i = 0; i !== cnt; ++i) { + o.param_names[i] = read_u30(); + } + } + + return o; + }; + + + var read_metadata_info = this.read_metadata_info = function() { + var o = {}; //new doabc_metadata_info(); + o.name = read_u30(); + var cnt = read_u30(); + o.items = new Array(cnt); + for (var i = 0; i !== cnt; ++i) { + o.items[i] = read_item_info(); + } + return o; + }; + + + var read_instance_info = this.read_instance_info = function() { + var o = {}; //new doabc_instance_info(); + var cnt; + + o.name = read_u30(); + o.super_name = read_u30(); + o.flags = read_u8(); + + if (o.flags & doabc_CONSTANT_ClassProtectedNs) { + o.protectedNs = read_u30(); + } + + cnt = read_u30(); + o.interfaces = new Array(cnt); + for (var i = 0; i !== cnt; ++i) { + o.interfaces[i] = read_u30(); + } + + o.iinit = read_u30(); + + cnt = read_u30(); + o.traits = new Array(cnt); + for (var i = 0; i !== cnt; ++i) { + o.traits[i] = read_traits_info(); + } + + return o; + }; + + + var read_class_info = this.read_class_info = function() { + var o = {}; //new doabc_class_info(); + o.cinit = read_u30(); + var cnt = read_u30(); + o.traits = new Array(cnt); + for (var i = 0; i !== cnt; ++i) { + o.traits[i] = read_traits_info(); + } + return o; + }; + + + var read_script_info = this.read_script_info = function() { + var o = {}; //new doabc_script_info(); + o.init = read_u30(); + var cnt = read_u30(); + o.traits = new Array(cnt); + for (var i = 0; i !== cnt; ++i) { + o.traits[i] = read_traits_info(); + } + return o; + }; + + + var read_method_body_info = this.read_method_body_info = function() { + var o = {}; //new doabc_method_body_info(); + var cnt; + + o.method = read_u30(); + o.max_stack = read_u30(); + o.local_count = read_u30(); + o.init_scope_depth = read_u30(); + o.max_scope_depth = read_u30(); + + cnt = read_u30(); + o.code = read_bytes(cnt); + + cnt = read_u30(); + o.exceptions = new Array(cnt); + for (var i = 0; i !== cnt; ++i) { + o.exceptions[i] = read_exception_info(); + } + + cnt = read_u30(); + o.traits = new Array(cnt); + for (var i = 0; i !== cnt; ++i) { + o.traits[i] = read_traits_info(); + } + + return o; + }; + + + var read_option_detail = this.read_option_detail = function() { + var o = {}; //new doabc_option_detail(); + o.val = read_u30(); + o.kind = read_u8(); + return o; + }; + + + var read_item_info = this.read_item_info = function() { + var o = {}; //new doabc_item_info(); + o.key = read_u30(); + o.val = read_u30(); + return o; + }; + + + var read_exception_info = this.read_exception_info = function() { + var o = {}; //new doabc_exception_info(); + o.from = read_u30(); + o.to = read_u30(); + o.target = read_u30(); + o.exc_type = read_u30(); + o.var_name = read_u30(); + return o; + }; + + + var read_traits_info = this.read_traits_info = function() { + var o = {}; //new doabc_traits_info(); + o.name = read_u30(); + + var kind = read_u8(); + o.kind = kind & 0x0f; + o.attrs = (kind >> 4) & 0x0f; + + switch (o.kind) { + case doabc_Trait_Slot: + case doabc_Trait_Const: + o.trait = read_trait_slot(); + break; + case doabc_Trait_Class: + o.trait = read_trait_class(); + break; + case doabc_Trait_Function: + o.trait = read_trait_function(); + break; + case doabc_Trait_Method: + case doabc_Trait_Getter: + case doabc_Trait_Setter: + o.trait = read_trait_method(); + break; + default: + throw new Error("Unexpected trait kind: " + o.kind); + } + + if (o.attrs & doabc_ATTR_Metadata) { + var cnt = read_u8(); + o.metadata = new Array(cnt); + for (var i = 0; i !== cnt; ++i) { + o.metadata[i] = read_u30(); + } + } + + return o; + }; + + + var read_trait_slot = this.read_trait_slot = function() { + var o = {}; //new doabc_trait_slot(); + o.slot_id = read_u30(); + o.type_name = read_u30(); + o.vindex = read_u30(); + if (o.vindex !== 0) { + o.vkind = read_u8(); + } + return o; + }; + + + var read_trait_class = this.read_trait_class = function() { + var o = {}; //new doabc_trait_class(); + o.slot_id = read_u30(); + o.classi = read_u30(); + return o; + }; + + + var read_trait_function = this.read_trait_function = function() { + var o = {}; //new doabc_trait_function(); + o.slot_id = read_u30(); + o.func = read_u30(); + return o; + }; + + + var read_trait_method = this.read_trait_method = function() { + var o = {}; //new doabc_trait_method(); + o.disp_id = read_u30(); + o.method = read_u30(); + return o; + }; + + + var read_multiname_info_QName = this.read_multiname_info_QName = function() { + var o = {}; //new doabc_multiname_info_QName(); + o.ns = read_u30(); + o.name = read_u30(); + return o; + }; + + + var read_multiname_info_RTQName = this.read_multiname_info_RTQName = function() { + var o = {}; //new doabc_multiname_info_RTQName(); + o.name = read_u30(); + return o; + }; + + + var read_multiname_info_RTQNameL = this.read_multiname_info_RTQNameL = function() { + // This kind has no associated data. + return {}; //new doabc_multiname_info_RTQNameL(); + }; + + + var read_multiname_info_Multiname = this.read_multiname_info_Multiname = function() { + var o = {}; //new doabc_multiname_info_Multiname(); + o.name = read_u30(); + o.ns_set = read_u30(); + return o; + }; + + + var read_multiname_info_MultinameL = this.read_multiname_info_MultinameL = function() { + var o = {}; //new doabc_multiname_info_MultinameL(); + o.ns_set = read_u30(); + return o; + }; +} + + +function doabc_namespace_info() { + this.kind = 0; + this.name = 0; +} + + +function doabc_ns_set_info() { + this.ns = []; +} + + +function doabc_multiname_info() { + this.kind = 0; +} + + +function doabc_cpool_info() { + this.integers = []; + this.uintegers = []; + this.doubles = []; + this.strings = []; + this.namespaces = []; + this.ns_sets = []; + this.multinames = []; +} + +doabc_cpool_info.prototype = { + get_item: function(cont, idx) { + if (idx < 0 || idx >= cont.length) { + throw new Error("Index out of range: " + idx); + } + return cont[idx]; + }, + + resolve_integer: function(idx) { + return this.get_item(this.integers, idx); + }, + + resolve_uinteger: function(idx) { + return this.get_item(this.uintegers, idx); + }, + + resolve_string: function(idx) { + return this.get_item(this.strings, idx); + }, + + resolve_ns: function(idx) { + return this.resolve_string(this.get_item(this.namespaces, idx).name); + }, + + resolve_multiname: function(idx) { + var s = ""; + + var mi = this.get_item(this.multinames, idx); + switch (mi.kind) { + case doabc_CONSTANT_QName: + case doabc_CONSTANT_QNameA: + if (mi.ns !== 0) { + s = this.resolve_ns(mi.ns); + } + if (s.length !== 0) { + s += "."; + } + s += this.resolve_string(mi.name); + break; + case doabc_CONSTANT_RTQName: + case doabc_CONSTANT_RTQNameA: + s = this.resolve_string(mi.name); + break; + case doabc_CONSTANT_RTQNameL: + case doabc_CONSTANT_RTQNameLA: + // This kind has no associated data. + break; + case doabc_CONSTANT_Multiname: + case doabc_CONSTANT_MultinameA: + // TODO: Implement. + throw new Error("Not implemented: doabc_CONSTANT_Multiname(A)"); + break; + case doabc_CONSTANT_MultinameL: + case doabc_CONSTANT_MultinameLA: + // TODO: Implement. + throw new Error("Not implemented: doabc_CONSTANT_MultinameL(A)"); + break; + default: + throw new Error("Unexpected multiname kind: " + mi.kind); + } + return s; + }, + +}; + + + +function doABCFile() { + this.major_version = 0; + this.minor_version = 0; + this.constant_pool = null; + this.methods = []; + this.metadata = []; + this.instances = []; + this.classes = []; + this.scripts = []; + this.method_bodies = []; +} +doABCFile.prototype = { + get_method_body: function(idx) { + var mb = null; + this.method_bodies.some(function(o) { + return o.method === idx && (mb = o); + }); + return mb; + }, + + get_method_body_by_name_index: function(cls_name_idx, name_idx) { + var inst_idx = this.get_instance_index_by_name_index(cls_name_idx); + if (inst_idx === -1) { + throw new Error("Class not found: " + cls_name_idx); + } + return this.get_method_body_by_name_index0(name_idx, this.instances[inst_idx].traits, + this.classes[inst_idx].traits); + }, + get_method_body_by_name_index0: function(name_idx, traits) { + var cp = this.constant_pool; + for (var i = 1, len = arguments.length; i < len; ++i) { + var traits = arguments[i]; + for (var j = 0, jlen = traits.length; j !== jlen; ++j) { + var ti = traits[j]; + if (ti.kind !== doabc_Trait_Method || cp.multinames[ti.name].kind !== doabc_CONSTANT_QName) { + continue; + } + if (ti.name === name_idx) { + return this.get_method_body(ti.trait.method); + } + } + } + throw new Error("Method not found: " + name_idx); + }, + + get_instance_index_by_name_index: function(name_idx) { + var cp = this.constant_pool, + a = this.instances; + for (var i = 0, len = a.length; i !== len; ++i) { + var ii = a[i]; + if (cp.multinames[ii.name].kind !== doabc_CONSTANT_QName) { + continue; + } + if (ii.name === name_idx) { + return i; + } + } + return -1; + } +}; + +// ActionScript 3 opcodes. +const AS3_OP_PUSHSCOPE = 0x30; +const AS3_OP_PUSHSTRING = 0x2c; +const AS3_OP_PUSHBYTE = 0x24; +const AS3_OP_PUSHSHORT = 0x25; +const AS3_OP_PUSHINT = 0x2d; +const AS3_OP_PUSHUINT = 0x2e; +const AS3_OP_GETLOCAL_0 = 0xd0; +const AS3_OP_GETLOCAL_1 = 0xd1; +const AS3_OP_GETLOCAL_2 = 0xd2; +const AS3_OP_GETLOCAL_3 = 0xd3; +const AS3_OP_GETLOCAL = 0x62; +const AS3_OP_SETLOCAL_1 = 0xd5; +const AS3_OP_SETLOCAL_2 = 0xd6; +const AS3_OP_SETLOCAL_3 = 0xd7; +const AS3_OP_SETLOCAL = 0x63; +const AS3_OP_CALLPROPERTY = 0x46; +const AS3_OP_CONSTRUCTPROP = 0x4a; +const AS3_OP_CALLPROPVOID = 0x4f; +const AS3_OP_COERCE = 0x80; +const AS3_OP_COERCE_S = 0x85; +const AS3_OP_FINDPROPSTRICT = 0x5d; +const AS3_OP_FINDPROPERTY = 0x5e; +const AS3_OP_RETURNVOID = 0x47; +const AS3_OP_RETURNVALUE = 0x48; +const AS3_OP_GETLEX = 0x60; +const AS3_OP_IFTRUE = 0x11; +const AS3_OP_SETPROPERTY = 0x61; +const AS3_OP_GETPROPERTY = 0x66; +const AS3_OP_MODULO = 0xa4; + +function utf8_decode(str) { + var a = []; + for (var i = 0, len = str.length; i < len; ++i) { + var cc = str.charCodeAt(i); + if (cc > 255) { + throw new Error("Illegal character: must be in range [0; 255]: " + cc); + } + if (cc < 0x80) { + a.push(str.charAt(i)); + continue; + } + var n = 0; + var num_bytes = 0; + // 110xxxxx 10xxxxxx + if ((cc & 0xe0) === 0xc0) { + num_bytes = 1; + n = cc & 0x1f; + } + // 1110xxxx 10xxxxxx 10xxxxxx + else if ((cc & 0xf0) === 0xe0) { + num_bytes = 2; + n = cc & 0x0f; + } + // Other is not supported, because we have only 16 bits for code points. + // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + // 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + // 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + else { + throw new Error("Code point too big."); + } + var j = i + 1, + j_end = j + num_bytes; + if (j_end > len) { + throw new Error("Premature end of input string."); + } + for (; j !== j_end; ++j) { + cc = str.charCodeAt(j); + if (cc > 255) { + throw new Error("Illegal character: must be in range [0; 255]: " + cc); + } + if ((cc & 0xc0) !== 0x80) { + throw new Error("Illegal character: high bits must be 10: " + cc); + } + n <<= 6; + n |= cc & 0x3f; + } + a.push(String.fromCharCode(n)); + i = j_end - 1; + } + return a.join(""); +} + + + +function get_simple_date_str() { + var now = new Date(); + var y = now.getFullYear(); + var m = now.getMonth() + 1; + var d = now.getDate(); + return "" + y + "-" + (m < 10 ? "0" : "") + m + "-" + (d < 10 ? "0" : "") + d; +} + + +function Stopwatch() { + this.start_times = {}; + this.end_times = {}; +} +Stopwatch.prototype = { + start: function(name /*= null*/ ) { + this.start_times[name] = Date.now(); + this.end_times[name] = 0; + }, + + stop: function(name /*= null*/ ) { + this.end_times[name] = Date.now(); + }, + + time: function(name /*= null*/ ) { + return (this.end_times[name] || Date.now()) - this.start_times[name]; + } +}; + + +function refresh_signature_func(ctx) { + var df = parse_swf(ctx); + if (!df) { + return; + } + + var stopwatch = new Stopwatch(); + stopwatch.start(); + var rc = decode(df); + if (!rc) { + return; + } + var code = [ + "/* Not encoded. */", + "if (params.stream.sig) { return params.stream.sig; }", + "/* " + get_simple_date_str() + ": " + String(ctx.file).replace(/[\\*]/g, "_") + " */", + "var s = params.stream.s;", + "if (!s) { return ''; }", + "var swap = params.swap;", + "var a = s.split('');" + ]; + for (var i = 0, len = rc.calls.length; i !== len; ++i) { + var o = rc.calls[i]; + switch (o.name) { + case "swap": + code.push("swap(a, " + o.arg + ");"); + break; + case "clone": + code.push("a = a.slice(" + o.arg + ");"); + break; + case "reverse": + code.push("a.reverse();"); + break; + default: + return; + } + } + code.push("return a.join('');"); + stopwatch.stop(); + log("Got signature function in " + stopwatch.time() + " ms."); + return { + timestamp: rc.timestamp, + func_text: code.join(" ") + }; +} + + +function Decoder(file, reader, states, idx /*= 0*/ ) { + this.result = { + timestamp: -1, + calls: [] + }; + this.file = file; + this.reset(reader, states, idx); +} +Decoder.prototype = { + run: function() { + var done = false; + while (!done && !this.reader.eof()) { + var res; + var o = this.parse_op(); + if (!o) { + break; + } + var sv = this.states[this.idx]; + switch (typeof(sv)) { + case "number": + res = this.advance_if(o.op === sv); + break; + case "function": + res = sv.call(this, o.op, o); + break; + default: + throw new Error("Invalid state type: " + typeof(sv)); + } + if (!res) { + done = res === null; + break; + } + } + return done ? this.result : null; + }, + parse_op: function() { + var o = {}; + var dar = this.reader; + switch (o.op = dar.read_u8()) { + case AS3_OP_COERCE_S: + case AS3_OP_GETLOCAL_0: + case AS3_OP_GETLOCAL_1: + case AS3_OP_GETLOCAL_2: + case AS3_OP_GETLOCAL_3: + case AS3_OP_MODULO: + case AS3_OP_PUSHSCOPE: + case AS3_OP_SETLOCAL_1: + case AS3_OP_SETLOCAL_2: + case AS3_OP_SETLOCAL_3: + case AS3_OP_RETURNVALUE: + case AS3_OP_RETURNVOID: + break; + case AS3_OP_PUSHBYTE: + o.value = dar.read_u8(); + break; + case AS3_OP_PUSHSHORT: + o.value = dar.read_u30(); + break; + case AS3_OP_COERCE: + case AS3_OP_FINDPROPERTY: + case AS3_OP_FINDPROPSTRICT: + case AS3_OP_GETLEX: + case AS3_OP_GETLOCAL: + case AS3_OP_GETPROPERTY: + case AS3_OP_PUSHINT: + case AS3_OP_PUSHSTRING: + case AS3_OP_PUSHUINT: + case AS3_OP_SETLOCAL: + case AS3_OP_SETPROPERTY: + o.index = dar.read_u30(); + break; + case AS3_OP_IFTRUE: + o.offset = dar.read_s24(); + break; + case AS3_OP_CALLPROPERTY: + case AS3_OP_CALLPROPVOID: + case AS3_OP_CONSTRUCTPROP: + o.index = dar.read_u30(); + o.arg_count = dar.read_u30(); + break; + default: + return null; + } + return o; + }, + advance_if: function(cond) { + return cond ? ++this.idx : false; + }, + switch_if: function(cond, states, idx /*= 0*/ ) { + if (!cond) { + return false; + } + this.reset(this.reader, states, idx); + return true; + }, + done_if: function(cond) { + return cond ? null : false; + }, + reset: function(reader, states, idx /*= 0*/ ) { + this.reader = reader; + this.states = states; + this.idx = idx || 0; + }, +}; + +function decode(df) { + var cp = df.constant_pool; + var inst_idx = -1; + for (var i = 0, len = df.instances.length; i !== len; ++i) { + var ii = df.instances[i]; + if (cp.multinames[ii.name].kind !== doabc_CONSTANT_QName) { + continue; + } + if (cp.resolve_multiname(ii.name) === "com.google.youtube.util.SignatureDecipher") { + inst_idx = i; + break; + } + } + if (inst_idx === -1) { + return; + } + var mb_cinit, mb_decipher; + var cls = df.classes[inst_idx]; + mb_cinit = df.get_method_body(cls.cinit); + if (!mb_cinit) { + return; + } + for (var i = 0, len = cls.traits.length; i !== len; ++i) { + var ti = cls.traits[i]; + if (ti.kind !== doabc_Trait_Method || cp.multinames[ti.name].kind !== doabc_CONSTANT_QName) { + continue; + } + var s_name = cp.resolve_multiname(ti.name); + if (s_name === "decipher") { + if (mb_decipher) { + return; + } + mb_decipher = df.get_method_body(ti.trait.method); + if (!mb_decipher) { + return; + } + } + } + if (!mb_decipher) { + return; + } + + var ctx = { + cls_name_idx: -1, + func_name_idx: -1, + name_by_index: {}, + call_arg: null + }; + var PS_TIMESTAMP = [ + function(op, o) { + return op !== AS3_OP_FINDPROPERTY || cp.resolve_multiname(o.index) !== "TIMESTAMP" || this.advance_if(true); + }, + function(op, o) { + return this.advance_if(op === AS3_OP_PUSHSHORT && (this.result.timestamp = o.value, 1)) || this.advance_if(op === AS3_OP_PUSHINT && (this.result.timestamp = cp.resolve_integer(o.index), 1)) || this.advance_if(op === AS3_OP_PUSHUINT && (this.result.timestamp = cp.resolve_uinteger(o.index), 1)); + }, + function(op) { + if (op !== AS3_OP_SETPROPERTY) { + return false; + } + this.reset(new doABCReader(mb_decipher.code), PS_DECIPHER); + return true; + } + ]; + var PS_DECIPHER = [ + AS3_OP_GETLOCAL_0, + AS3_OP_PUSHSCOPE, + AS3_OP_GETLEX, + AS3_OP_IFTRUE, + AS3_OP_FINDPROPERTY, + function(op, o) { + return this.advance_if(op === AS3_OP_FINDPROPSTRICT && (ctx.cls_name_idx = o.index)); + }, + AS3_OP_CONSTRUCTPROP, + AS3_OP_SETPROPERTY, + AS3_OP_GETLEX, + AS3_OP_GETLOCAL_1, + function(op, o) { + return this.advance_if(op === AS3_OP_PUSHSTRING && cp.resolve_string(o.index).length === 0); + }, + function(op, o) { + return this.advance_if(op === AS3_OP_CALLPROPERTY && cp.resolve_multiname(o.index) === "http://adobe.com/AS3/2006/builtin.split"); + }, + function(op, o) { + return this.advance_if(op === AS3_OP_CALLPROPERTY && (ctx.func_name_idx = o.index)); + }, + function(op, o) { + return this.advance_if(op === AS3_OP_PUSHSTRING && cp.resolve_string(o.index).length === 0); + }, + function(op, o) { + return this.advance_if(op === AS3_OP_CALLPROPERTY && cp.resolve_multiname(o.index) === "http://adobe.com/AS3/2006/builtin.join"); + }, + function(op) { + if (op !== AS3_OP_RETURNVALUE) { + return false; + } + var mb = this.file.get_method_body_by_name_index(ctx.cls_name_idx, ctx.func_name_idx); + if (!mb) { + return false; + } + this.reset(new doABCReader(mb.code), PS_FUNC); + return true; + } + ]; + var PS_FUNC = [ + AS3_OP_GETLOCAL_0, + function(op) { + return this.switch_if(op === AS3_OP_PUSHSCOPE, PS_FUNC_BRANCH); + }, + ]; + var PS_FUNC_BRANCH = [ + function(op) { + return this.switch_if(op === AS3_OP_GETLOCAL_0, PS_FUNC_CALL) || this.advance_if(op === AS3_OP_GETLOCAL_1); + }, + function(op) { + return this.done_if(op === AS3_OP_RETURNVALUE); + } + ]; + var PS_FUNC_CALL = [ + AS3_OP_GETLOCAL_1, + function(op, o) { + return this.advance_if(op === AS3_OP_PUSHBYTE && (ctx.call_arg = o.value, 1)); + }, + function(op, o) { + if (op !== AS3_OP_CALLPROPERTY) { + return false; + } + var func_name_idx = o.index; + var func_name = ctx.name_by_index[func_name_idx]; + if (!func_name) { + var mb = this.file.get_method_body_by_name_index(ctx.cls_name_idx, func_name_idx); + if (!mb) { + return false; + } + var PS_TEST = [ + AS3_OP_GETLOCAL_0, + AS3_OP_PUSHSCOPE, + AS3_OP_GETLOCAL_1, + function(op) { + return this.done_if((op === AS3_OP_GETLOCAL_2 && (this.result = "clone")) || (op === AS3_OP_CALLPROPVOID && (this.result = "reverse")) || (op === AS3_OP_PUSHBYTE && (this.result = "swap"))); + } + ]; + var d = new Decoder(df, new doABCReader(mb.code), PS_TEST); + if (d.run()) { + func_name = d.result; + } + if (!func_name) { + return false; + } + ctx.name_by_index[func_name_idx] = func_name; + } + this.result.calls.push({ + name: func_name, + arg: ctx.call_arg + }); + return this.advance_if(true); + }, + function(op) { + return op === AS3_OP_COERCE || this.switch_if(op === AS3_OP_SETLOCAL_1, PS_FUNC_BRANCH); + } + ]; + + var d = new Decoder(df, new doABCReader(mb_cinit.code), PS_TIMESTAMP); + return d.run(); +} + + + +function parse_swf(ctx) { + var stopwatch = new Stopwatch(); + + stopwatch.start(); + var swf_inflated = swf_inflate(ctx.bytes); + stopwatch.stop(); + log("SWF inflated in " + stopwatch.time() + " ms."); + ctx.bytes = null; + + stopwatch.start(); + var bs = new SwfStream(swf_inflated); + bs.skip_bytes(8); // Signature, version, size. + var num_bits = bs.read_bits(5); + bs.read_bits(num_bits); + bs.read_bits(num_bits); + bs.read_bits(num_bits); + bs.read_bits(num_bits); + bs.skip_bytes(4); // Frame rate, frame count. + + var df = null; + for (;;) { + var t = bs.read_uint16(); + var tag_type = bs.n_hbits16(t, 10); + var tag_len = bs.n_lbits16(t, 6); + if (tag_len === 0x3f) { + tag_len = bs.read_sint32(); + } + if (0 === tag_type) { + break; + } + if (tag_type !== 82) { + bs.skip_bytes(tag_len); + continue; + } + + var bytes = bs.read_bytes(tag_len); + var dar = new doABCReader(bytes); + for (var i = 0; i !== 4; ++i) { + dar.read_u8(); + } + while (dar.read_u8() !== 0) {} + df = new doABCFile(); + df.minor_version = dar.read_u16(); + df.major_verison = dar.read_u16(); + df.constant_pool = dar.read_cpool_info(); + + var cnt; + cnt = dar.read_u30(); + df.methods = new Array(cnt); + for (var i = 0; i !== cnt; ++i) { + df.methods[i] = dar.read_method_info(); + } + + cnt = dar.read_u30(); + df.metadata = new Array(cnt); + for (var i = 0; i !== cnt; ++i) { + df.metadata[i] = dar.read_metadata_info(); + } + + cnt = dar.read_u30(); + df.instances = new Array(cnt); + for (var i = 0; i !== cnt; ++i) { + df.instances[i] = dar.read_instance_info(); + } + df.classes = new Array(cnt); + for (var i = 0; i !== cnt; ++i) { + df.classes[i] = dar.read_class_info(); + } + + cnt = dar.read_u30(); + df.scripts = new Array(cnt); + for (var i = 0; i !== cnt; ++i) { + df.scripts[i] = dar.read_script_info(); + } + + cnt = dar.read_u30(); + df.method_bodies = new Array(cnt); + for (var i = 0; i !== cnt; ++i) { + df.method_bodies[i] = dar.read_method_body_info(); + } + + break; + } + stopwatch.stop(); + log("SWF parsed in " + stopwatch.time() + " ms."); + + return df; +} diff --git a/data/extensions/html5-video-everywhere@lejenome.me/data/metacafe.js b/data/extensions/html5-video-everywhere@lejenome.me/data/metacafe.js new file mode 100644 index 0000000..e84db57 --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/data/metacafe.js @@ -0,0 +1,71 @@ +(function() { + "use strict"; + + onReady(() => { + if (/watch\/\d+\/.*/.test(location.pathname)) + watchPage(); + else if (/[^\/]+\/?$/.test(location.pathname)) + channelPage(); + }); + + function watchPage() { + var ob, url; + if ((ob = document.getElementById("flashVars"))) { + url = getURL(ob.value); + } else if ((ob = document.getElementById("FlashWrap")) && + (ob = ob.getElementsByTagName("video")).length) { + url = ob[0].src; + ob[0].pause(); + ob[0].remove(); + } + if (!url) + return; + var container = document.getElementById("ItemContainer"); + if (!container) + return; + var vp = new VP(container); + vp.addSrc(url, "medium", "mp4"); + vp.props({ + autoplay: autoPlay(true), + preload: preLoad(), + loop: isLoop(), + controls: true + }); + vp.style({ + width: "100%" + }); + vp.setup(); + } + + function channelPage() { + var embed = document.getElementsByTagName("embed"); + if (!embed) + return; + embed = embed[0]; + var page = embed.src; + page = page.replace("/fplayer/", "/watch/").replace(/.swf$/, ""); + asyncGet(page).then((data) => { + var url = getURL(data); + var container = document.getElementById("ItemContainer"); + //var container = embed.parentElement; + var vp = new VP(container); + vp.addSrc(url, "medium", "mp4"); + vp.props({ + autoplay: autoPlay(false), + preload: preLoad(), + loop: isLoop(), + controls: true + }); + vp.style({ + width: "100%" + }); + vp.setup(); + }); + } + + function getURL(e) { + var data = decodeURIComponent(e.match(/&mediaData=([^&]*)&/)[1]); + return JSON.parse(data).MP4.mediaURL; + } + +}()); \ No newline at end of file diff --git a/data/extensions/html5-video-everywhere@lejenome.me/data/video-player.js b/data/extensions/html5-video-everywhere@lejenome.me/data/video-player.js new file mode 100644 index 0000000..64d3957 --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/data/video-player.js @@ -0,0 +1,387 @@ +/* global VP:true, LBP */ +"use strict"; +const VP = function(container) { + this.attached = false; + this.player = undefined; + this.container = container; + this._srcs = []; + this._style = {}; + this._containerStyle = {}; + this._props = {}; + this._langs = []; + this._containerProps = {}; + this._CSSRules = []; + this.styleEl = undefined; +}; +VP.prototype = {}; +VP.prototype.addSrc = function(url, qlt, cdc) { + this.log("addSrc", qlt, cdc); + this._srcs[Qlt.indexOf(qlt) * 2 + Cdc.indexOf(cdc)] = url; +}; +VP.prototype.srcs = function(fmts, wrapper, get) { + var slct, i, j; + if (!wrapper) { + for (slct in fmts) { + i = Qlt.indexOf(slct.split("/")[0]); + j = Cdc.indexOf(slct.split("/")[1]); + this._srcs[i * 2 + j] = fmts[slct]; + } + return; + } + for (i = 0; i < Qlt.length; i++) { + for (j = 0; j < Cdc.length; j++) { + slct = Qlt[i] + "/" + Cdc[j]; + if (!(slct in wrapper) || !fmts[wrapper[slct]]) + continue; + this._srcs[i * 2 + j] = (get) ? + (get(fmts[wrapper[slct]]) || this._srcs[i * 2 + j]) : fmts[wrapper[slct]]; + } + } +}; +VP.prototype.mainSrcIndex = function() { + var i, j, slct; + i = OPTIONS.prefQlt; + while (i > -1) { + if (this._srcs[i * 2 + OPTIONS.prefCdc]) + return { + qlt: i, + cdc: OPTIONS.prefCdc + }; + else if (this._srcs[i * 2 + (OPTIONS.prefCdc + 1 % 2)]) + return { + qlt: i, + cdc: OPTIONS.prefCdc + 1 % 2 + }; + i = (i >= OPTIONS.prefQlt) ? i + 1 : i - 1; + if (i > 3) + i = OPTIONS.prefQlt - 1; + } +}; +VP.prototype.setup = function() { + var idx = this.mainSrcIndex(); + if (!idx) + return this.error("Failed to find video url"); + this.clean(); + // just to force contextmenu id. TODO: fix contextmenu and use createNode + this.container.innerHTML = ""; + this.player = this.container.firstChild; + // if (!this.player) { + // this.player = createNode("video", this._props, this._style); + // } + if (!this.styleEl) + this.styleEl = createNode("style"); + this.patch(this.player, this._props); + this.patch(this.player, this._style, "style"); + this.player.appendChild(createNode("source", { + src: this._srcs[idx.qlt * 2 + idx.cdc], + type: "video/" + Cdc[idx.cdc] + })); + this._srcs.forEach((url, i) => { + if (i !== idx.qlt * 2 + idx.cdc) + this.player.appendChild(createNode("source", { + src: url, + type: "video/" + Cdc[i % 2] + })); + }); + this.container.appendChild(this.player); + this.container.appendChild(this.styleEl); + this.attached = true; + this.slctLang(); + this._CSSRules.forEach(s => this.styleEl.sheet.insertRule(s, + this.styleEl.sheet.cssRules.length)); + this.patch(this.container, this._containerProps); + this.patch(this.container, this._containerStyle, "style"); + this.log("setup"); + if (OPTIONS.player === 1) + this.setupLBP(); + else + this.setupContextMenu(idx); +}; +VP.prototype.tracksList = function(langs, fnct) { + this._langs = langs.sort(); + this._slctLang = fnct; + if (this.attached) + this.slctLang(); +}; +VP.prototype.slctLang = function(lang) { + if (!(lang !== undefined || OPTIONS.lang !== 0) || this._slctLang === undefined) + return; + if (lang === undefined) + lang = LANGS[OPTIONS.lang - 1]; + if (this._lang) + this.player.textTracks.getTrackById(this._lang).mode = "disabled"; + var track; + if ((track = this.player.textTracks.getTrackById(lang))) { + track.mode = "showing"; + this._lang = lang; + } else { + new Promise((resolve, reject) => this._slctLang(lang, resolve, reject)) + .then((url) => { + track = createNode("track", { + kind: "subtitles", + id: lang, + src: url, + label: lang, + srclang: lang + }); + this.player.appendChild(track); + track.track.mode = "showing"; + this._lang = lang; + }); + } +}; +VP.prototype.on = function(evt, cb) { + this.player["on" + evt] = cb; //TODO +}; +VP.prototype.stop = function() { + this.log("stop"); + if (!this.player) + return; + this.player.pause(); + this.player.onended = undefined; + if (this.player.duration) + this.player.currentTime = this.player.duration; +}; +VP.prototype.clean = function() { + this.log("clean"); + if (this.player) { + this.player.pause(); + this.player.onended = undefined; + } + // site default video player sometime continue playing on background + var vds = this.container.getElementsByTagName("video"); + for (var i = 0; i < vds.length; i++) { + if (this.player === vds[i]) + continue; + vds[i].pause(); + vds[i].src = ""; + vds[i].addEventListener("playing", (e) => { + e.currentTarget.pause(); + e.currentTarget.src = ""; + }); + } + rmChildren(this.container); + this.attached = false; +}; +VP.prototype.end = function() { + this.log("end"); + this.stop(); + this.clean(); + this._srcs = {}; + this._style = {}; + this._containerStyle = {}; + this._props = {}; + this._containerProps = {}; + this._sheets = []; +}; +VP.prototype.addCSSRule = function(cssText) { + this.log("addCSSRule", cssText); + this._CSSRules.push(cssText); + if (this.attached) + this.styleEl.sheet.insertRule(cssText, + this.styleEl.sheet.cssRules.length); +}; +VP.prototype.style = function(style) { + this.apply(style, this.player, "_style", "style"); +}; +VP.prototype.containerStyle = function(style) { + this.apply(style, this.container, "_containerStyle", "style"); +}; +VP.prototype.props = function(props) { + this.apply(props, this.player, "_props"); +}; +VP.prototype.containerProps = function(props) { + this.apply(props, this.container, "_containerProps"); +}; +VP.prototype.error = function(msg) { + this.log("ERROR Msg:", msg); + this.clean(); + if (!this.styleEl) + this.styleEl = createNode("style"); + this.container.appendChild(createNode("p", { + textContent: "Ooops! :(" + }, { + padding: "15px", + fontSize: "20px" + })); + this.container.appendChild(createNode("p", { + textContent: msg + }, { + fontSize: "20px" + })); + this.container.appendChild(this.styleEl); + this._CSSRules.forEach(s => this.styleEl.sheet.insertRule(s, + this.styleEl.sheet.cssRules.length)); + this.patch(this.container, this._containerProps); + this.patch(this.container, this._containerStyle, "style"); +}; +VP.prototype.setupLBP = function() { + this.container.className += " leanback-player-video"; + LBP.setup(); + this.player.style = ""; + this.player.style = ""; + this.player.style.position = "relative"; + this.player.style.height = "inherit"; + this.container.style.marginLeft = "0px"; +}; +VP.prototype.setupContextMenu = function(idx) { + /* jshint maxstatements:false */ + this._contextMenu = createNode("menu", { + type: "context", //"popup", + id: "h5vew-contextmenu" + }); + var qltMenu = createNode("menu", { + id: "h5vew-menu-qlt", + label: "Video Quality" + }); + for (var i = 0; i < Qlt.length; i++) + qltMenu.appendChild(createNode("menuitem", { + type: "radio", + label: Qlt[i], + radiogroup: "menu-qlt", + checked: (idx.qlt === i), + disabled: !(this._srcs[i * 2] || this._srcs[i * 2 + 1]), + onclick: (e) => { + idx.qlt = Qlt.indexOf(e.target.label); + idx.cdc = (this._srcs[idx.qlt * 2 + idx.cdc]) ? + idx.cdc : (idx.cdc + 1 % 2); + var paused = this.player.paused; + this.player.src = this._srcs[idx.qlt * 2 + idx.cdc] + + "#t=" + this.player.currentTime; + this.player.load(); + this.player.oncanplay = () => { + if (!paused) + this.player.play(); + this.player.oncanplay = undefined; + }; + } + })); + var cdcMenu = createNode("menu", { + id: "h5vew-menu-cdc", + label: "Preferred Video Format" + }); + for (i = 0; i < Cdc.length; i++) + cdcMenu.appendChild(createNode("menuitem", { + type: "radio", + label: Cdc[i], + radiogroup: "menu-cdc", + checked: (OPTIONS.prefCdc === i), + onclick: (e) => + chgPref("prefCdc", Cdc.indexOf(e.target.label)) + })); + var langMenu = createNode("menu", { + id: "h5vew-menu-lang", + label: "Subtitles" + }); + langMenu.appendChild(createNode("menuitem", { + type: "radio", + label: "none", + radiogroup: "menu-lang", + checked: OPTIONS.lang === 0 || this._langs.findIndex((l) => l === LANGS[OPTIONS.lang - 1]) === -1, + onclick: (e) => { + if (this._lang === undefined) + return; + this.player.textTracks.getTrackById(this._lang).mode = "disabled"; + this._lang = undefined; + } + })); + for (i = 0; i < this._langs.length; i++) + langMenu.appendChild(createNode("menuitem", { + type: "radio", + label: this._langs[i], + radiogroup: "menu-lang", + checked: this._langs[i] === LANGS[OPTIONS.lang - 1], + onclick: (e) => + this.slctLang(e.target.label) + })); + var loopMenu = createNode("menu", { + id: "h5vew-menu-loop", + label: "Loop Video" + }); + ["Never", "Always", "Default"].forEach((n, i) => { + loopMenu.appendChild(createNode("menuitem", { + type: "radio", + label: n, + radiogroup: "menu-loop", + checked: (OPTIONS.loop === i), + onclick: (e) => + chgPref("loop", i) + })); + }); + var autoNextMenu = createNode("menuitem", { + id: "h5vew-menu-autonext", + type: "checkbox", + label: "Auto Play Next Video", + checked: OPTIONS.autoNext, + onclick: (e) => chgPref("autoNext", e.target.checked) + }); + var moreMenu = createNode("menu", { + id: "h5vew-menu-more", + label: "More options" + }); + var copyMenu = createNode("menuitem", { + id: "h5vew-menu-copy", + label: "Copy Page URL", + onclick: () => setClipboard(location.href) // TODO + }); + var disableMenu = createNode("menuitem", { + id: "h5vew-menu-disable", + label: "Disable " + OPTIONS.driver.charAt(0).toUpperCase() + + OPTIONS.driver.slice(1) + " Support", + onclick: () => { + self.port.emit("disable"); + this._contextMenu.removeChild(disableMenu); + } + }); + var aboutMenu = createNode("menuitem", { + id: "h5vew-menu-about", + label: "About HTML5 Video EveryWhere", + onclick: () => + window.open("http://lejenome.github.io/html5-video-everywhere#v=" + + OPTIONS.addon.version + "&id=" + OPTIONS.addon.id, + "h5vew-about", "width=550,height=280,menubar=no,toolbar=no,location=no,status=no,chrome=on,modal=on") + }); + moreMenu.appendChild(copyMenu); + moreMenu.appendChild(disableMenu); + moreMenu.appendChild(createNode("hr")); + moreMenu.appendChild(aboutMenu); + + const prefChanged = (name) => { + if (name === "autoNext") + autoNextMenu.checked = OPTIONS.autoNext; + }; + onPrefChange.push(prefChanged); + this._contextMenu.appendChild(qltMenu); + this._contextMenu.appendChild(cdcMenu); + if (this._langs.length > 0) + this._contextMenu.appendChild(langMenu); + this._contextMenu.appendChild(loopMenu); + this._contextMenu.appendChild(autoNextMenu); + this._contextMenu.appendChild(moreMenu); + this.container.appendChild(this._contextMenu); + // TODO: fix assigning contextMenu and uncommant createNode("video") ^ + this.container.contextmenu = "h5vew-contextmenu"; +}; +VP.prototype.apply = function(props, el, obj, sub) { + for (var prop in props) { + if (props.hasOwnProperty(prop)) { + this[obj][prop] = props[prop]; + if (this.attached && sub) + el[sub][prop] = props[prop]; + else if (this.attached && !sub) + el[prop] = props[prop]; + } + } +}; +VP.prototype.patch = function(el, props, sub) { + for (var prop in props) + if (props.hasOwnProperty(prop)) + if (sub) + el[sub][prop] = props[prop]; + else + el[prop] = props[prop]; +}; +VP.prototype.log = function(...args) { + args.unshift("[DRIVER::VP]"); + dump(args.join(" ") + "\n"); +}; \ No newline at end of file diff --git a/data/extensions/html5-video-everywhere@lejenome.me/data/vimeo.js b/data/extensions/html5-video-everywhere@lejenome.me/data/vimeo.js new file mode 100644 index 0000000..1e8df2b --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/data/vimeo.js @@ -0,0 +1,137 @@ +(function() { + "use strict"; + + onReady(() => + getConfig().then(getVideoInfo) + ); + + function injectPlayer(conf) { + try { + let player_container, player, stl; + if (conf.isEmbed) { + player_container = document.body; + } else if (conf.isWatch) { + player_container = document.getElementById("video") || + document.getElementById("video_wrapper"); + if ((stl = player_container.children[0]) && + (stl = stl.sheet) && + (stl.cssRules.length > 0)) { + stl = stl.cssRules[0].cssText; + } + } else { + player_container = document.getElementById("clip_" + conf.id); + } + if (!player_container) + return; + var vp = new VP(player_container); + vp.srcs(conf.fmts, { + "high/mp4": "hd", + "medium/mp4": "sd", + "low/mp4": "mobile" + }, (fmt) => fmt.url); + vp.props({ + className: conf.className, + autoplay: autoPlay(), + preload: preLoad(), + loop: isLoop(), + controls: true, + poster: conf.poster, + volume: OPTIONS.volume / 100 + }); + vp.tracksList(conf.tracks.map(l => l.lang), (lang, resolve, reject) => { + var l = conf.tracks.find(l => l.lang === lang); + if (l === undefined) + reject(); + else + resolve(l.direct_url || l.url); + }); + if (stl) + vp.addCSSRule(stl); + vp.setup(); + if (conf.isWatch) + brozarEvents(); + } catch (e) { + logify("Exception on changePlayer()", e.lineNumber, e.columnNumber, e.message, e.stack); + } + } + + function getConfig() { + return new Promise((resolve, reject) => { + var isWatch = /https?:\/\/vimeo.com\/[\d]+/.test(location.href) || + ogType().indexOf("video") > -1; + var isEmbed = /https?:\/\/player.vimeo.com\/video/.test(location.href); + var isChannel = /https?:\/\/vimeo.com\/(channels\/|)\w+/.test(location.href) || + ogType().match(/channel|profile/) !== null; + if (!isWatch && !isChannel && !isEmbed) + reject(); + var player_id, player_class; + if (isWatch) { + player_id = location.pathname.match(/\/([\d]+)/)[1]; + player_class = "player"; + } else if (isEmbed) { + player_id = location.pathname.match(/video\/([\d]+)/)[1]; + player_class = "fallback"; + } else if (isChannel) { + player_class = "player"; + } + if (!player_id && !isChannel) + reject(); + resolve({ + isWatch: isWatch, + isEmbed: isEmbed, + isChannel: isChannel, + id: player_id, + className: player_class + }); + }); + } + + function getVideoInfo(conf) { + const processData = (conf) => (data) => { + data = JSON.parse(data); + conf.fmts = data.request.files.h264; + conf.poster = data.video.thumbs.base; + conf.tracks = data.request.text_tracks || []; + return Promise.resolve(conf); + }; + const INFO_URL = "//player.vimeo.com/video/"; + if (conf.isChannel) { + return Array.map(document.getElementsByClassName("player_container"), (el) => { + var _conf = {}; + for (var va in conf) + _conf[va] = conf[va]; + _conf.id = el.id.replace("clip_", ""); + return asyncGet(INFO_URL + _conf.id + "/config").then(processData(_conf)) + .then(injectPlayer); + }); + } else { + return asyncGet(INFO_URL + conf.id + "/config").then(processData(conf)) + .then(injectPlayer); + } + } + + function brozarEvents() { + // change Vimeo default click events of items on brozar element + var clips = document.getElementById("clips"); + if (clips) + clips.onclick = function(e) { + if (e.target === e.currentTarget) + return; + var li = e.target; + while (li.tagName !== "LI") + li = li.parentElement; + window.location = "/" + li.id.replace("clip_", ""); + }; + var promos = document.getElementsByClassName("js-promo_link"); + var promoClick = function(e) { + window.location = "/" + e.currentTarget.dataset.clipId; + }; + for (var i = 0; promos && i < promos.length; i++) + promos[i].onclick = promoClick; + } + + function ogType() { + var t = document.head.querySelector("meta[property=\"og:type\"]"); + return (t) ? t.content : ""; + } +}()); \ No newline at end of file diff --git a/data/extensions/html5-video-everywhere@lejenome.me/data/youtube-formats.js b/data/extensions/html5-video-everywhere@lejenome.me/data/youtube-formats.js new file mode 100644 index 0000000..e23cf83 --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/data/youtube-formats.js @@ -0,0 +1,33 @@ +// wrap getPreferedFmt selector to YT itag eq, as we the passed fmts object +// later has itags as keys not getPreferredFmt known keys +var FMT_WRAPPER = { + "high/mp4": "22", + "medium/mp4": "18", + "medium/webm": "43" +}; +/* ++----+-----------+------------+---------+------------------------------------------- +|FMT | container | resolution | profile | type ++----+-----------+------------+---------+------------------------------------------- +| 18 | mp4 | 360p | normal | "video/mp4; codecs=\"avc1.42001E, mp4a.40.2\"" +| 22 | mp4 | 720p | normal | "video/mp4; codecs=\"avc1.64001F, mp4a.40.2\"" +| 43 | webm | 360p | normal | "video/webm; codecs=\"vp8.0, vorbis\"" ++----+-----------+------------+---------+------------------------------------------ +| 82 | mp4 | 360p | 3D | +| 83 | mp4 | 240p | 3D | +| 84 | mp4 | 720p | 3D | +| 85 | mp4 | 1080p | 3D | +|100 | webm | 360p | 3D | +|101 | webm | 360p | 3D | +|102 | webm | 700p | 3D | +|133 | mp4 | 240p | DASH V | +|134 | mp4 | 360p | DASH V | +|135 | mp4 | 480p | DASH V | +|136 | mp4 | 720p | DASH V | +|137 | mp4 | 1080p | DASH V | +|160 | mp4 | 144p | DASH V | +|264 | mp4 | 1440p | DASH V | +|. | . | . | . | ++----+-----------+------------+---------+---------------------------------------- +MORE FROM: http://en.wikipedia.org/wiki/YouTube +*/ \ No newline at end of file diff --git a/data/extensions/html5-video-everywhere@lejenome.me/data/youtube.js b/data/extensions/html5-video-everywhere@lejenome.me/data/youtube.js new file mode 100644 index 0000000..aa1c666 --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/data/youtube.js @@ -0,0 +1,330 @@ +/*globals FMT_WRAPPER*/ +(function() { + "use strict"; + var vp; + var swf_url; + + onReady(() => { + changePlayer(); + window.addEventListener("spfrequest", function() { + if (vp) + vp.stop(); + }); + window.addEventListener("spfdone", function() { + changePlayer(); + }); + }); + + function changePlayer() { + getConfig() + .then(getVideoInfo) + .then((conf) => { + try { + if (vp) + vp.end(); + var player_container = getPlayerContainer(conf); + if (!player_container) + return; + var seek = getSeek(); + vp = new VP(player_container); + vp.srcs(conf.fmts, FMT_WRAPPER, (fmt) => fmt.url + seek); + //vp.containerProps({ + // className: conf.className || "" + //}); + vp.props({ + id: "video_player", + className: conf.className || "", + autoplay: autoPlay(location.search.search("autoplay=") === -1 ? !conf.isEmbed : location.search.search("autoplay=0") === -1), + preload: preLoad(), + loop: isLoop(location.search.search("loop=1") !== -1), + controls: true, + poster: conf.poster || "", + volume: OPTIONS.volume / 100 + }); + //vp.style({ + // position: "relative" + //}); + vp.tracksList((conf.tracks || []).map(i => i.lc), (lang, resolve, reject) => { + var o = conf.tracks.find((i) => i.lc === lang); + if (o === undefined) + return reject(); + addWebVTT(lang, o.u, resolve, reject); + }); + vp.setup(); + if (conf.isWatch) + playNextOnFinish(); + } catch (e) { + logify("EXCEPTION: unexpected error on changePlayer", + e.lineNumber, e.columnNumber, e.message, e.stack); + } + }) + .catch((rej) => { + if (rej === undefined) + return; + switch (rej.error) { + case "VIDEO_URL_UNACCESSIBLE": + if (rej.data.reason) + errorMessage("Failed to load video url with the following error message: " + + rej.data.reason, rej.conf); + break; + case "NO_SUPPORTED_VIDEO_FOUND": + errorMessage("Failed to find any playable video url." + + (rej.unsig ? " All urls are not signed" : ""), rej.conf); + break; + default: + logify("EXCEPTION: unexpected error on changePlayer", rej); + break; + } + }); + } + + function errorMessage(msg, conf) { + var error_container; + if (vp) + vp.end(); + if (conf) + error_container = getPlayerContainer(conf); + if (!error_container) + error_container = document.getElementById("player-unavailable") || document.getElementById("player"); + if (!error_container) + return; + vp = new VP(error_container); + vp.srcs(conf.fmts, FMT_WRAPPER); + if (conf && conf.isWatch) + vp.containerProps({ + className: " player-height player-width player-api" + }); + if (conf && conf.isChannel) + vp.containerProps({ + className: " c4-player-container" + }); //" html5-main-video"; + if (conf && conf.isEmbed) { + vp.containerProps({ + className: " full-frame" + }); + } + vp.containerStyle({ + background: "linear-gradient(to bottom, #383838 0px, #131313 100%) repeat scroll 0% 0% #262626" + }); + vp.error(msg); + } + + function getPlayerContainer(conf) { + if (conf.isWatch) + return document.getElementById("player-mole-container"); + if (conf.isEmbed) + return document.body; + if (conf.isChannel) + return document.getElementsByClassName("c4-player-container")[0]; + } + + function getConfig() { + return new Promise((resolve, reject) => { + var conf = {}; + conf.isEmbed = location.pathname.startsWith("/embed/"); + conf.isWatch = location.pathname.startsWith("/watch"); + conf.isChannel = location.pathname.startsWith("/channel/") || location.pathname.startsWith("/user/"); + conf.withoutCookies = location.hostname.search("youtube-nocookie.com") > -1; + if (!conf.isEmbed && !conf.isWatch && !conf.isChannel) + reject(); + if (conf.isEmbed) { + conf.id = location.pathname.match(/^\/embed\/([^?#/]*)/)[1]; + conf.className = "full-frame"; + } else if (conf.isChannel) { + var upsell = document.getElementById("upsell-video"); + if (!upsell) + reject(); + conf.id = upsell.dataset["videoId"]; + conf.className = "c4-player-container"; //+ " html5-main-video" + } else { + conf.id = location.search.slice(1).match(/v=([^/?#]*)/)[1]; + conf.className = "player-width player-height player-api"; + } + if (!conf.id) + reject({ + error: "PLAYER_ID_NOT_FOUND", + conf: conf + }); + else + resolve(conf); + }); + } + + function getVideoInfo(conf) { + return new Promise((resolve, reject) => { + var INFO_URL = "https://www.youtube.com/get_video_info?html5=1&hl=en_US&el=detailpage&video_id="; + if (conf.withoutCookies) + INFO_URL = "https://www.youtube-nocookie.com/get_video_info?html5=1&hl=en_US&el=detailpage&video_id="; + if (unsafeWindow.ytplayer && unsafeWindow.ytplayer.config) { + conf.info = unsafeWindow.ytplayer.config.args.url_encoded_fmt_stream_map; + conf.poster = unsafeWindow.ytplayer.config.args.iurlsd || + unsafeWindow.ytplayer.config.args.iurl || + unsafeWindow.ytplayer.config.args.iurlhq || + unsafeWindow.ytplayer.config.args.iurlmaxres || + unsafeWindow.ytplayer.config.args.iurlmq; + if (unsafeWindow.ytplayer.config.args.caption_tracks) + conf.tracks = parse(unsafeWindow.ytplayer.config.args.caption_tracks, true); + swf_url = unsafeWindow.ytplayer.config.url; + resolve(conf); + } else { + asyncGet(INFO_URL + conf.id, {}, "text/plain").then((data) => { + if (data.endsWith("=")) + try { + data = atob(data); + } catch (_) {} + data = parse(data); + if (data.status === "fail") { + return reject({ + error: "VIDEO_URL_UNACCESSIBLE", + data: data, + conf: conf + }); + } + // get the poster url + if (data.iurlhq) + conf.poster = data.iurlhq; + // extract avalable formats to fmts object + conf.info = data.url_encoded_fmt_stream_map; + if (data.caption_tracks) + conf.tracks = parse(data.caption_tracks, true); + resolve(conf); + }); + } + }).then((conf) => { + var player = createNode("video"); + var unsignedVideos = false; + conf.fmts = {}; + parse(conf.info, true) + .filter(it5 => { + if (player.canPlayType(it5.type) !== "probably") + return false; + if (it5.url.search("signature=") === -1) { + unsignedVideos = true; + if (!OPTIONS.genYTSign) + return false; + } + return true; + }) + .forEach(fmt => { + conf.fmts[fmt.itag] = fmt; + }); + if (unsignedVideos && OPTIONS.genYTSign) { + return fixSignature(conf); + } else { + return Promise.resolve(conf); + } + }); + } + + function fixSignature(conf) { + return new Promise((resolve, reject) => { + self.port.emit("fix_signature", { + fmts: conf.fmts, + swf_url: swf_url + }); + self.port.on("fixed_signature", (fmts) => { + conf.fmts = fmts; + logify("fixed Signature"); + resolve(conf); + }); + }); + } + + function playNextOnFinish() { + //Credits to @durazell github.com/lejenome/youtube-html5-player/issues/9 + if (document.getElementsByClassName("playlist-header").length > 0) { + vp.on("ended", function(e) { + if (this.currentTime !== this.duration || OPTIONS.autoNext === false) + return; + var cur = 0, + len = 0; + var current, playlist; + if ((current = document.getElementsByClassName("currently-playing")).length > 0) { + cur = parseInt(current[0].dataset["index"]) + 1; + } else if ((current = document.getElementById("playlist-current-index"))) { + cur = parseInt(current.textContent); + } + if ((playlist = document.getElementsByClassName("playlist-videos-list")).length > 0) { + len = playlist[0].childElementCount; + } else if ((playlist = document.getElementById("playlist-length"))) { + len = parseInt(playlist.textContent); + } + + if (isNaN(cur) === true || isNaN(len) === true) { + logify("Cannot find location in playlist, autoplay failed"); + return; + } + + if (cur < len) { + window.location.href = document.getElementsByClassName("yt-uix-scroller-scroll-unit")[cur].getElementsByTagName("a")[0].href; + } + }); + } + } + + function parse(data, splitComma) { + if (splitComma) { + return data.split(",").map(i => parse(i)); + } else { + var res = {}; + var nv; + data.split("&").forEach((p) => { + try { + nv = p.split("=").map(function(v) { + return decodeURIComponent(v.replace(/\+/g, " ")); + }); + if (!(nv[0] in res)) res[nv[0]] = nv[1]; + } catch (e) {} + }); + return res; + } + } + + function addWebVTT(lang, url, resolve, reject) { + asyncGet(url).then((data) => { + var webvtt = "WEBVTT\n\n"; + var XMLParser = new DOMParser(); + var xml = XMLParser.parseFromString(data, "text/xml"); + if (xml.documentElement.nodeName !== "transcript") + reject(); + var els = xml.documentElement.childNodes; + for (var i = 0; i < els.length; i++) { + var start = els[i].attributes.getNamedItem("start"); + var dur = els[i].attributes.getNamedItem("dur"); + if (start === null || dur === null) + continue; + start = parseFloat(start.value); + dur = parseFloat(dur.value); + var s = start % 60; + var m = (start - s) / 60; + var tl1 = "" + (m < 10 ? "0" : "") + m + ":" + + (s < 10 ? "0" : "") + s.toFixed(3); + s = (start + dur) % 60; + m = (start + dur - s) / 60; + var tl2 = "" + (m < 10 ? "0" : "") + m + ":" + + (s < 10 ? "0" : "") + s.toFixed(3); + + webvtt += (i + 1) + "\n" + tl1 + " --> " + tl2 + "\n" + els[i].textContent + "\n\n"; + } + resolve("data:text/vtt;base64," + btoa(window.unescape( + encodeURIComponent(webvtt.replace("'", "'", "g"))))); + }); + } + + function getSeek() { + var seek = 0; + if (location.search.search("start=") > -1) { + seek = location.search.match(/start=(\d+)/); + seek = seek ? parseInt(seek[1]) : 0; + } else if (location.search.search(/[&?]t=\d/) > -1) { + seek = location.search.match(/[&?]t=([^&]*)/)[1]; + var h = seek.match(/(\d+)h/); + var m = seek.match(/(\d+)m/); + var s = seek.match(/(\d+)s/) || seek.match(/^(\d+)$/); + seek = (h ? parseInt(h[1]) : 0) * 3600 + + (m ? parseInt(m[1]) : 0) * 60 + + (s ? parseInt(s[1]) : 0); + } + return seek > 0 ? ("#t=" + seek) : ""; + } +}()); \ No newline at end of file diff --git a/data/extensions/html5-video-everywhere@lejenome.me/icon.png b/data/extensions/html5-video-everywhere@lejenome.me/icon.png new file mode 100644 index 0000000..4f5ebda Binary files /dev/null and b/data/extensions/html5-video-everywhere@lejenome.me/icon.png differ diff --git a/data/extensions/html5-video-everywhere@lejenome.me/index.js b/data/extensions/html5-video-everywhere@lejenome.me/index.js new file mode 100644 index 0000000..fca20cf --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/index.js @@ -0,0 +1,151 @@ +/* jshint esnext:true, node:true*/ +"use strict"; +const { + Cc, Ci, Cr +} = require("chrome"); +const { + add, remove +} = require("sdk/util/array"); +const _self = require("sdk/self"); +const pageMod = require("sdk/page-mod"); +const events = require("sdk/system/events"); +const utils = require("sdk/window/utils"); +const clipboard = require("sdk/clipboard"); +var prefs = require("sdk/simple-prefs").prefs; +// list of current workers +const workers = []; +const pageMods = {}; +const common = require("./lib/common"); +const allDrivers = { + "facebook": require("./lib/facebook"), + "vimeo": require("./lib/vimeo"), + "dailymotion": require("./lib/dailymotion"), + "break": require("./lib/break"), + "metacafe": require("./lib/metacafe"), + "youtube": require("./lib/youtube") +}; +const drivers = Object.keys(allDrivers).filter(drvName => + prefs["disable" + drvName] === false +); + +//ensure preferences match the state of disabled drivers +Object.keys(allDrivers).filter(drvName => + drivers.indexOf(drvName) === -1 +).forEach(drvName => + prefs["disable" + drvName] = true +); + + +const onWorkerAttach = (drvName, listen) => (worker) => { + logify("onAttach", worker); + //send current Addon preferences to content-script + let _prefs = {}; + for (let pref in prefs) + _prefs[pref] = prefs[pref]; + _prefs.driver = drvName; + _prefs.addon = { + id: _self.id, + version: _self.version + }; + worker.port.emit("preferences", _prefs); + add(workers, worker); + worker.port.on("prefChang", (pref) => + prefs[pref.name] = pref.val); + worker.port.on("disable", () => + prefs["disable" + drvName] = true); + worker.port.on("setClipboard", (txt) => clipboard.set(txt)); + for (let evt in listen) { + logify("Add listener:", evt); + worker.port.on(evt, (obj) => { + listen[evt](obj, worker); + }); + } + worker.on("detach", function(e) { + remove(workers, this); + + }); +}; + +drivers.forEach(setupDriver); + +function setupDriver(drvName) { + var driver = allDrivers[drvName]; + if (driver.match === void(0)) + return; + var scripts, styles; + scripts = common.inject.concat(driver.inject) + .map(i => _self.data.url(i)); + styles = common.style.concat(driver.style || []) + .map(i => _self.data.url(i)); + pageMods[drvName] = pageMod.PageMod({ + include: driver.match, + contentScriptFile: scripts, + contentStyleFile: styles, + contentScriptWhen: driver.when || "ready", + onAttach: onWorkerAttach(drvName, driver.listen) + }); +} + +function listener(event) { + var channel = event.subject.QueryInterface(Ci.nsIHttpChannel); + var url = event.subject.URI.spec; + for (let drvName of drivers) { + var driver = allDrivers[drvName]; + for (let redirect of(driver.redirect || [])) { + if (redirect.src.test(url)) { + channel.redirectTo(Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService).newURI( + String.replace(url, redirect.src, redirect.funct), + null, + null)); + logify("Redirect:", url); + return; + } + } + for (let block of(driver.block || [])) { + if (block.test(url)) { + channel.cancel(Cr.NS_BINDING_ABORTED); + logify("Block:", url); + return; + } + } + } +} + +//on Addon prefernces change, send the changes to content-script +require("sdk/simple-prefs").on("", function prefChangeHandler(pref) { + if (pref === "volume" && prefs.volume > 100) + prefs.volume = 100; + else if (pref === "volume" && prefs.volume < 0) + prefs.volume = 0; + else if (pref.startsWith("disable")) { + let drvName = /^disable(.+)/.exec(pref)[1]; + if (prefs[pref] === false) { + add(drivers, drvName); + setupDriver(drvName); + } else { + remove(drivers, drvName); + pageMods[drvName].destroy(); + } + } else + workersPrefHandler(pref); +}); + +function workersPrefHandler(pref) { + for (let worker of workers) + worker.port.emit("prefChanged", { + name: pref, + value: prefs[pref] + }); +} + +function logify(...args) { + args.unshift("[CORE]"); + dump(args.join(" ") + "\n"); +} + +exports.main = () => { + events.on("http-on-modify-request", listener); +}; +exports.onUnload = function(reason) { + events.off("http-on-modify-request", listener); +}; \ No newline at end of file diff --git a/data/extensions/html5-video-everywhere@lejenome.me/install.rdf b/data/extensions/html5-video-everywhere@lejenome.me/install.rdf index 550a5c5..7004330 100644 --- a/data/extensions/html5-video-everywhere@lejenome.me/install.rdf +++ b/data/extensions/html5-video-everywhere@lejenome.me/install.rdf @@ -1,27 +1,36 @@ - + - - html5-video-everywhere@lejenome.me - 0.2.38.1-signed - 2 - true - false + + html5-video-everywhere@lejenome.me + 2 + true + false + 0.3.3 + HTML5 Video Everywhere! + Replace video player with Firefox native video player + Moez Bouhlel <bmoez.j@gmail.com> (http://lejenome.github.io/) + https://github.com/lejenome/html5-video-everywhere + data:text/xml,<placeholder/> + 2 + true - - - - {ec8030f7-c20a-464f-9b0e-13a3a9e97384} - 26.0 - 30.0 - - + + + {ec8030f7-c20a-464f-9b0e-13a3a9e97384} + 38.0a1 + 39.0 + + - - HTML5 Video Everywhere! - Replace video player with Firefox native video player - Moez Bouhlel <bmoez.j@gmail.com> (http://lejenome.github.io/) - https://github.com/lejenome/html5-video-everywhere - 2 - - HTML5 Video Everywhere Contributors (https://github.com/lejenome/html5-video-everywhere/graphs/contributors){aa3c5121-dab2-40e2-81ca-7ea25febc110}26.030.0a1 - \ No newline at end of file + + + {aa3c5121-dab2-40e2-81ca-7ea25febc110} + 38.0a1 + 39.0 + + + + + + + diff --git a/data/extensions/html5-video-everywhere@lejenome.me/lib/break.js b/data/extensions/html5-video-everywhere@lejenome.me/lib/break.js new file mode 100644 index 0000000..0bad411 --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/lib/break.js @@ -0,0 +1,9 @@ +"use strict"; +var match = [/https?:\/\/www.break.com\/embed\/.*/]; +var inject = [ + "break.js" +]; +var when = "start"; +exports.when = when; +exports.match = match; +exports.inject = inject; \ No newline at end of file diff --git a/data/extensions/html5-video-everywhere@lejenome.me/lib/common.js b/data/extensions/html5-video-everywhere@lejenome.me/lib/common.js new file mode 100644 index 0000000..354f4e9 --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/lib/common.js @@ -0,0 +1,20 @@ +// common part between all drivers. +var prefs = require("sdk/simple-prefs").prefs; +var inject = [], + style = []; +inject = inject.concat([ + "common.js", + "video-player.js" +]); +/* +if (prefs.player === 1) { + // current player is leanBackPlayer + inject = [].concat([ + "LeanBackPlayer/js.player/leanbackPlayer.js", + "LeanBackPlayer/js.player/leanbackPlayer.en.js", + ], inject); + style.push("LeanBackPlayer/css.player/leanbackPlayer.default.css"); +} +*/ +exports.inject = inject; +exports.style = style; \ No newline at end of file diff --git a/data/extensions/html5-video-everywhere@lejenome.me/lib/dailymotion.js b/data/extensions/html5-video-everywhere@lejenome.me/lib/dailymotion.js new file mode 100644 index 0000000..4872758 --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/lib/dailymotion.js @@ -0,0 +1,9 @@ +"use strict"; +var match = [/https?:\/\/(www.)dailymotion.com\/embed\/video\/.*/]; +var inject = [ + "dailymotion.js" +]; +var when = "start"; +exports.when = when; +exports.match = match; +exports.inject = inject; \ No newline at end of file diff --git a/data/extensions/html5-video-everywhere@lejenome.me/lib/facebook.js b/data/extensions/html5-video-everywhere@lejenome.me/lib/facebook.js new file mode 100644 index 0000000..1198b28 --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/lib/facebook.js @@ -0,0 +1,7 @@ +"use strict"; +var match = [/https?:\/\/(www\.|beta\.)?facebook.com\/(.*\/videos\/.*|video.php\?.*)/]; +var inject = [ + "facebook.js" +]; +exports.match = match; +exports.inject = inject; \ No newline at end of file diff --git a/data/extensions/html5-video-everywhere@lejenome.me/lib/flashgot-YouTube.js b/data/extensions/html5-video-everywhere@lejenome.me/lib/flashgot-YouTube.js new file mode 100644 index 0000000..e8ca2d7 --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/lib/flashgot-YouTube.js @@ -0,0 +1,535 @@ +/***** BEGIN LICENSE BLOCK ***** + + FlashGot - a Firefox extension for external download managers integration + Copyright (C) 2004-2013 Giorgio Maone - g.maone@informaction.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +***** END LICENSE BLOCK *****/ +/* Modified By Moez Bouhlel to be used on HTML5-Video-EveryWhere */ +/* jshint laxbreak:true, maxstatements:false, evil:true, latedef:false */ +/* jshint maxdepth:4 */ +/* jshint maxcomplexity:false, -W116 */ +/* global args:false */ +"use strict"; +const { + Cc, Ci, Cu, components, ChromeWorker +} = require("chrome"); +const service = require("sdk/preferences/service"); +var fg = { + log: (...args) => { + args.unshift("[FLASHGOT]"); + dump(args.join(" ") + "\n"); + }, + getPref: (title, def) => service.get("flashgot." + title, def), + setPref: (title, val) => service.set("flashgot." + title, val) +}; + +/////////////////////////////////////////////////////////////////////////////// +// Public part. +var Youtube = { + + decode_signature: function (params) { + /* Not encoded. */ + return params.stream.sig || ""; + }, + decode_signature_swap: function (a, idx) { + var tmp = a[0]; + a[0] = a[idx % a.length]; + a[idx] = tmp; + return a; + }, + + create_signature_decoder: function () { + var s = fg.getPref("media.YouTube.decode_signature_func", ""); + if (!s) { + return new SignatureDecoder(Youtube.decode_signature); + } + // Fail fast: try to compile right now to check the code for + // syntax errors, so that we don't do all that heavy stuff for + // sandbox initialization only to fail later in evalInSandbox() + // and have an incorrect error message saying "error _calling_ + // the function" while actually we failed to compile it. + var func = null; + try { + func = new Function("params", s); + } catch (x) { + throw new Error("Error compiling YouTube.decode_signature_func: " + (x.message || x)); + } + if ( ! fg.getPref("media.YouTube.decode_signature_func.sandbox", true)) { + return new SignatureDecoder(func); + } + // Wrap the code into a function invocation because we promised + // to call it as a function with one parameter. + s = "(function(params){\n" + s + "\n})(params);"; + return new SandboxedSignatureDecoder(s) + // Sandboxing stuff is not supported - fall back to non-sandboxed. + || new SignatureDecoder(func); + }, + + + refresh_signature_func: function (w, callback /*= null*/, force /*= false*/) { + return SDASniffer.sniff(w, callback, force); + } + +}; // Youtube + + +/////////////////////////////////////////////////////////////////////////////// +// Private part. + +// interface ISignatureDecoder { +// string decode(Params params); +// void dispose(); +// } +// class Params { +// Map stream; +// Map video_info; +// Function swap; //Array swap(Array, int); +// } +// +// class SignatureDecoder implements ISignatureDecoder { +// SignatureDecoder(Function func); +// } +function SignatureDecoder(func) { + this.func = func; +} + +SignatureDecoder.prototype = { + decode: function(params) { return this.func(params); }, + dispose: function() { this.func = null; } +}; + + +// class SandboxedSignatureDecoder implements ISignatureDecoder { +// SandboxedSignatureDecoder(String code_str); +// } +function SandboxedSignatureDecoder(code_str) { + this.code_str = code_str; + + this.sandbox = this.create_sandbox(); + if ( ! this.sandbox) { return null; } + +} // SandboxedSignatureDecoder() + + +SandboxedSignatureDecoder.prototype = { + // https://developer.mozilla.org/en-US/docs/Security_check_basics: + // The null principal (whose contract ID is @mozilla.org/nullprincipal;1) + // fails almost all security checks. It has no privileges and can't be + // accessed by anything but itself and chrome. They aren't same-origin + // with anything but themselves. + SANDBOX_PRINCIPAL: Cc["@mozilla.org/nullprincipal;1"] + .createInstance(Ci.nsIPrincipal), + + SANDBOX_OPTIONS: {wantComponents: false, wantXHRConstructor: false}, + + create_sandbox: function() { + if (typeof Cu.Sandbox !== "function") { + return null; + } + var s_gecko_ver = Cc["@mozilla.org/xre/app-info;1"] + .getService(Ci.nsIXULAppInfo) + .platformVersion; + var gecko_ver = parseInt(s_gecko_ver); + // NaN, Infinity. + if ( ! isFinite(gecko_ver)) { + throw new Error("Failed to parse Gecko version: '" + s_gecko_ver + "'."); + } + if (gecko_ver >= 2) { + return new Cu.Sandbox(this.SANDBOX_PRINCIPAL, this.SANDBOX_OPTIONS); + } + var opts = this.SANDBOX_OPTIONS; + var proto = opts.hasOwnProperty("sandboxPrototype") ? opts.sandboxPrototype : {} /*FIXME: null?*/; + var wantXrays = opts.hasOwnProperty("wantXrays") ? opts.wantXrays : true; + return new Cu.Sandbox(this.SANDBOX_PRINCIPAL, proto, wantXrays); + }, + + decode: function (params) { + var rc = Cu.evalInSandbox( + "var params = " + params.toSource() + ";\n" + + this.code_str, + this.sandbox); + + // No fancy return values - we expect a primitive string value. + // We don't silently return something that could pass for a signature. + // Instead, we throw - to inform the user that their decode_signature_func + // function is broken (anyone can make a typo) or malicious. + // + // It's OK to pass uncaught exceptions as-is because even if they have + // getters for properties like "message", those will be executed in the + // context of the sandbox (i.e. the global |this| will point to the sandbox), + // which is useless for malicious code anyway. + // Here's what am I talking about - somewhere in the sandboxed code: + // var x = new Error(); + // x.__defineGetter__("message", function(){alert("pwned");}); + // throw x; + // or: + // var x = { message: { valueOf: function(){alert("pwned");}, toString: function(){alert("pwned");} } }; + // throw x; + // We could catch the exceptions here, manually sanitize them and rethrow + // if they're safe to use in our chrome code, but I just don't see the point + // in doing so because if there's a bug in the security manager, then our + // manual sanitization will just conceal it. + if (typeof (rc) === "string") { return rc; } + // Nulls are kinda OK. + if (rc === null) { return ""; } + // A forgotten return or outdated code that returns nonexistent stream + // properties? Worth a warning in either case. + if (rc === undefined) { + fg.log("WARNING: YouTube.decode_signature_func returned undefined."); + return ""; + } + throw new Error("Invalid return value type: expected string, got " + typeof (rc)); + }, // decode() + + dispose: function () { + if (!this.sandbox) { return; } + for (var p in this.sandbox) { + if (this.sandbox.hasOwnProperty(p)) { + delete this.sandbox[p]; + } + } + if (typeof Cu.nukeSandbox === "function") { + Cu.nukeSandbox(this.sandbox); + } + this.sandbox = null; + } +}; // SandboxedSignatureDecoder.prototype + + + +// Signature decoding algorithm (SDA) sniffer. +var SDASniffer = { + // We don't want "over 9000" workers doing the same thing when one + // is enough (can happen if we're restoring a session with several + // YouTube tabs/windows). + // static boolean working = false; + // static Array callbacks = []; + working: false, + callbacks: [], + + sniff: function (w, callback /*= null*/, force /*= false*/) { + if (typeof(callback) !== "function") { callback = null; } + + if (this.working) { + if (callback) { this.callbacks.push(callback); } + return true; + } + + // Get the SWF player URL. + w = w.wrappedJSObject; + var swf_url; + var o; + // ytplayer.config.url + if (w && (o = w.ytplayer) && (o = o.config)) { + swf_url = o.url; + } + // yt.config_["PLAYER_CONFIG"].url + else if (w && (o = w.yt) && (o = o.config_) && (o = o.PLAYER_CONFIG)) { + swf_url = o.url; + } + if (!swf_url) { return false; } + fg.log("SWF URL: " + swf_url); + + // Automatic update frequency is limited so that we waste less traffic + // and CPU cycles in case YoutubeSwf code is outdated. + if ( ! force) { + var now = Math.floor(Date.now() / 1000); + var min_int = fg.getPref("media.YouTube.decode_signature_func.auto.min_interval", 60); + var last_update = fg.getPref("media.YouTube.decode_signature_func.auto.last_update_time", 0); + if (min_int !== 0 && now - last_update < min_int) { + if ( ! fg.getPref("media.YouTube.decode_signature_func.auto.last_update_ok")) { + return false; + } + // We promised to be async, so we can't call back _before_ we return, + // hence setTimeout. + w.setTimeout(function(){ + try { + callback(); + } catch (x) { + fg.log("Callback error: " + (x.message || x) + "\n" + x.stack); + } + }, 1); + return true; + } + fg.setPref("media.YouTube.decode_signature_func.auto.last_update_time", now); + } + + var st, ft; + var stream_ctx = { + file: swf_url, //.split("/").pop().replace(/\?.*$/, "").replace(/#.*$/, ""), + bytes: "", + contentLength: -1, + bstream: null + }; + var stream_listener = { + onDataAvailable: function (req, ctx, stream, offset, count) { + stream_ctx.bstream.setInputStream(stream); + stream_ctx.bytes += stream_ctx.bstream.readBytes(count); + }, + onStartRequest: function (req /*, ctx*/) { + var channel = req.QueryInterface(Ci.nsIChannel); + if (!((channel instanceof Ci.nsIHttpChannel) + && components.isSuccessCode(channel.status) + && channel.responseStatus === 200)) + { + throw new Error("cancel"); //req.cancel(NS_BINDING_ABORTED); + } + stream_ctx.contentLength = channel.contentLength || -1; + fg.log("SWF content length: " + stream_ctx.contentLength); + stream_ctx.bstream = Cc["@mozilla.org/binaryinputstream;1"] + .createInstance(Ci.nsIBinaryInputStream); + st = Date.now(); + }, + onStopRequest: function (req, ctx, status) { + ft = Date.now(); + stream_ctx.bstream = null; + // SDASniffer::sniff0 is async, so we can't simply do if (SDASniffer.working) {clean up}. + var cleanup = true; + if (components.isSuccessCode(status)) { + fg.log("SWF downloaded in " + (ft - st) + " ms, size: " + stream_ctx.bytes.length); + if (stream_ctx.contentLength === -1 || stream_ctx.bytes.length === stream_ctx.contentLength) { + SDASniffer.sniff0(stream_ctx, callback); + cleanup = false; + } + else { + fg.log("SWF content length mismatch: expected " + stream_ctx.contentLength + ", got " + stream_ctx.bytes.length); + } + } + else { + fg.log("Failed to download the SWF: status=" + status); + } + stream_ctx = null; + if (cleanup) { + SDASniffer.working = false; + SDASniffer.callbacks = []; + } + } + }; // stream_listener + Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService) + .newChannel(swf_url, null, null) + .asyncOpen(stream_listener, null); + + this.working = true; + if (callback) { this.callbacks.push(callback); } + fg.setPref("media.YouTube.decode_signature_func.auto.last_update_ok", false); + return true; + }, + + + sniff0: function (ctx, callback) { + // Using a worker instead of a direct call resolves the problem + // with GUI freezing due to severe performance degradation: 100 ms + // vs 2400 ms for zip_inflate(), 100 ms vs 800 ms for swf_parse(). + // See bug 911570 (https://bugzilla.mozilla.org/show_bug.cgi?id=911570), + // or 776798, or 907201, or whatever is causing it. + var worker = new SDAWorker( {bytes: ctx.bytes, file: ctx.file} ); + ctx.bytes = null; + + worker.onfinish = function(rc) { + SDASniffer.working = false; + var callbacks = SDASniffer.callbacks; + SDASniffer.callbacks = []; + + if (typeof(rc) === "string") { + fg.log("Error refreshing signature function: " + rc); + return; + } + + if ( ! rc) { return; } + fg.setPref("media.YouTube.decode_signature_func.auto.last_update_ok", true); + + if (rc.timestamp !== fg.getPref("media.YouTube.decode_signature_func.timestamp")) { + fg.log("New timestamp: " + rc.timestamp); + fg.setPref("media.YouTube.decode_signature_func.timestamp", rc.timestamp); + } + + if (rc.func_text !== fg.getPref("media.YouTube.decode_signature_func")) { + fg.log("New signature function:\n" + rc.func_text); + fg.setPref("media.YouTube.decode_signature_func", rc.func_text); + callbacks.forEach(function(f){ + try { + f(); + } catch (x) { + fg.log("Callback error: " + (x.message || x) + "\n" + x.stack); + } + }); + } + }; + + try { + worker.start(); + } catch (x) { + worker.onfinish("Error starting the worker: " + (x.message || x) + "\n" + x.stack); + } + } +}; // SDASniffer + + + +// class SDAWorker; +function SDAWorker(ctx) { + this.ctx = ctx; + this.worker = null; + this.fired_onfinish = false; +} + +SDAWorker.prototype = { + // public + start: function() { + var worker = this.worker = new Worker("YoutubeSwf.js"); + worker["SDAWorker::this"] = this; + worker.onmessage = this.worker_onmessage; + worker.onerror = this.worker_onerror; + worker.postMessage(this.ctx); + this.ctx = null; + }, + + // Completion event handler, implemented by the caller. + // void onfinish(Object data); + // @param data - the result of the decoding, one of: + // 1) a primitive string value (typeof data === "string") - there was + // an uncaught exception in the worker, and |data| is the error message. + // 2) Object - struct { string func_text; int timestamp; } - the result + // of the decoding. Can be null/undefined (data == null covers both) + // if the signature function could not be decoded. + onfinish: function(){}, + + + // private + fire_onfinish: function(data) { + this.fired_onfinish = true; + try { + this.onfinish(data); + } catch (x) { + fg.log("Error in onfinish: " + (x.message || x) + "\n" + x.stack); + } + }, + + worker_onmessage: function(evt) { + var This = this["SDAWorker::this"]; + // struct msg { string type; Object data; }; + var msg = evt.data; + if (msg == null) { + fg.log("SDAWorker: Invalid message: null or undefined: " + msg); + This.finish(); + return; + } + if (typeof(msg) !== "object") { + fg.log("SDAWorker: Invalid message: expected [object], got [" + typeof(msg) + "]: " + msg); + This.finish(); + return; + } + switch (msg.type) { + case "done": + This.finish(); + return; + case "log": + fg.log(msg.data); + return; + case "result": + This.fire_onfinish(msg.data); + return; + } + fg.log("SDAWorker: Invalid message type: '" + msg.type + "'"); + This.finish(); + }, + + worker_onerror: function(evt) { + var This = this["SDAWorker::this"]; + This.fire_onfinish("Uncaught exception in worker: " + evt.message); + This.finish(); + }, + + finish: function() { + if ( ! this.fired_onfinish) { + this.fire_onfinish(null); + } + try { + this.worker.terminate(); + this.worker["SDAWorker::this"] + = this.worker.onmessage + = this.worker.onerror + = null; + this.worker = null; + } catch (x) { + fg.log("Error terminating the worker: " + (x.message || x) + "\n" + x.stack); + } + } +}; // SDAWorker.prototype + + +Youtube.fix_signature = function(data, fmts, swf_url, cb) { + try { + this.create_sig_decoder(swf_url, (signature_decoder) => { + for (let itag in fmts) { + if (fmts[itag].url.search("signature=") > 0) + continue; + fg.log("fixing stream", itag); + try { + var sig = signature_decoder.decode({ + stream: fmts[itag], + video_info: data, + swap: this.decode_signature_swap + }); + if (sig) { + fg.log("Fmt", itag, "url fixed:", sig); + fmts[itag].url += "&signature=" + encodeURIComponent(sig); + if(!sig.match(/^[0-9A-Z]{40}\.[0-9A-Z]{40}$/)) + fg.setPref("media.YouTube.decode_signature_func", ""); + } else { + fg.log("Failed to fix fmt", itag, "signature"); + delete fmts[itag]; + } + } catch (x) { + fg.log("Error calling YouTube.decode_signature_func: " + (x.message || x) + "\n" + x.stack); + } + } + try { + signature_decoder.dispose(); + } catch (e) { /* TODO: fix it */ } + cb(fmts); + }); + } catch (x) { + fg.log("Error creating signature decoder: " + (x.message || x) + "\n" + x.stack); + cb({}); + } +}; +Youtube.create_sig_decoder = function (swf_url, cb) { + // Wrapper around create_sig_decoder with callback and + // refresh_signature_decoder and emulate w object + if (fg.getPref("media.YouTube.decode_signature_func", "")) { + cb(this.create_signature_decoder()); + } else { + var w = {wrappedJSObject: {ytplayer: {config: {url: swf_url}}}}; + var success = this.refresh_signature_func(w, () => { + cb(this.create_signature_decoder()); + }, true); + if (!success) + cb(this.create_signature_decoder()); + } +}; +SDAWorker.prototype.start = function() { + var worker = this.worker = new ChromeWorker(require("sdk/self").data.url("flashgot-YouTubeSwf.js")); + worker["SDAWorker::this"] = this; + worker.onmessage = this.worker_onmessage; + worker.onerror = this.worker_onerror; + worker.postMessage(this.ctx); + this.ctx = null; +}; + +exports.flashgot = Youtube; diff --git a/data/extensions/html5-video-everywhere@lejenome.me/lib/metacafe.js b/data/extensions/html5-video-everywhere@lejenome.me/lib/metacafe.js new file mode 100644 index 0000000..4529ab7 --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/lib/metacafe.js @@ -0,0 +1,11 @@ +"use strict"; +var match = [/https?:\/\/www.metacafe.com\/watch\/.*/, + /https?:\/\/www.metacafe.com\/[^\/]+\/?/ +]; +var inject = [ + "metacafe.js" +]; +var when = "start"; +exports.when = when; +exports.match = match; +exports.inject = inject; \ No newline at end of file diff --git a/data/extensions/html5-video-everywhere@lejenome.me/lib/vimeo.js b/data/extensions/html5-video-everywhere@lejenome.me/lib/vimeo.js new file mode 100644 index 0000000..59acc9e --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/lib/vimeo.js @@ -0,0 +1,11 @@ +"use strict"; +var when = "start"; +var match = [/https?:\/\/vimeo.com\/.+/, + /https?:\/\/player.vimeo.com\/video.*/ +]; +var inject = [ + "vimeo.js" +]; +exports.when = when; +exports.match = match; +exports.inject = inject; \ No newline at end of file diff --git a/data/extensions/html5-video-everywhere@lejenome.me/lib/youtube.js b/data/extensions/html5-video-everywhere@lejenome.me/lib/youtube.js new file mode 100644 index 0000000..e655ab2 --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/lib/youtube.js @@ -0,0 +1,30 @@ +"use strict"; +const flashgot = require("./flashgot-YouTube").flashgot; +var YOUTUBE_FLASH_REGEX = /https?:\/\/(www.)?youtube.com\/v\/([^#?\/]*)/; +var YT_BIN_REGEX = /https:\/\/s.ytimg.com\/yts\/jsbin\/[^\/]*\/base.js/; +var YT_PLAYER_REGEX = /https?:\/\/s.ytimg.com\/yts\/jsbin\/[^\/]*\/html5player.js/; +var when = "start"; +var match = ["*.www.youtube.com", "*.www.youtube-nocookie.com"]; +var inject = [ + "youtube-formats.js", + "youtube.js" +]; +var redirect = [{ + src: /https?:\/\/(www.)?youtube.com\/v\/([^#?\/]*)/, + funct: (_1, _2, v) => "https://www.youtube.com/embed/" + v +}]; +var block = [/https?:\/\/s.ytimg.com\/yts\/jsbin\/[^\/]*\/html5player.js/]; +var listen = { + "fix_signature": function(obj, worker) { + flashgot.fix_signature(obj.data, obj.fmts, obj.swf_url, (fmts) => + worker.port.emit("fixed_signature", fmts) + ); + } +}; +exports.when = when; +exports.match = match; +exports.inject = inject; +exports.redirect = redirect; +exports.block = block; +exports.listen = listen; +//exports.style = []; \ No newline at end of file diff --git a/data/extensions/html5-video-everywhere@lejenome.me/package.json b/data/extensions/html5-video-everywhere@lejenome.me/package.json new file mode 100644 index 0000000..28f7b8a --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/package.json @@ -0,0 +1,311 @@ +{ + "title": "HTML5 Video Everywhere!", + "name": "html5-video-everywhere", + "id": "html5-video-everywhere@lejenome.me", + "version": "0.3.3", + "description": "Replace video player with Firefox native video player", + "main": "index.js", + "author": "Moez Bouhlel (http://lejenome.github.io/)", + "homepage": "https://github.com/lejenome/html5-video-everywhere", + "engines": { + "firefox": ">=38.0a1", + "fennec": ">=38.0a1" + }, + "license": "MPL-2.0", + "repository": { + "type": "git", + "url": "git://github.com/lejenome/html5-video-everywhere.git" + }, + "keywords": [ + "firefox", + "android", + "fennec", + "jetpack", + "jpm", + "add-on" + ], + "devDependencies": { + "jpm": "lastest" + }, + "permissions": { + "private-browsing": true, + "multiprocess": true + }, + "preferences": [ + { + "name": "autoNext", + "title": "Auto play next", + "description": "Auto play the next Video on playlist", + "type": "bool", + "value": true + }, + { + "name": "genYTSign", + "title": "Decode YouTube Signature", + "description": "Extract YouTube signature decoder from their video player, and use it to decode signatures", + "type": "bool", + "value": true + }, + { + "name": "lang", + "title": "Subtitles", + "description": "Select Subtitles language if available", + "type": "menulist", + "value": 0, + "options": [ + { + "label": "None", + "value": "0" + }, + { + "label": "Af", + "value": "1" + }, + { + "label": "Ar", + "value": "2" + }, + { + "label": "Bn", + "value": "3" + }, + { + "label": "De", + "value": "4" + }, + { + "label": "En", + "value": "5" + }, + { + "label": "Es", + "value": "6" + }, + { + "label": "Fi", + "value": "7" + }, + { + "label": "Fr", + "value": "8" + }, + { + "label": "Hi", + "value": "9" + }, + { + "label": "Id", + "value": "10" + }, + { + "label": "Is", + "value": "11" + }, + { + "label": "It", + "value": "12" + }, + { + "label": "Ja", + "value": "13" + }, + { + "label": "Ko", + "value": "14" + }, + { + "label": "Pt", + "value": "15" + }, + { + "label": "Ru", + "value": "16" + }, + { + "label": "Tu", + "value": "17" + }, + { + "label": "Zh", + "value": "18" + } + ] + }, + { + "name": "prefCdc", + "title": "Preferred Codec", + "type": "menulist", + "value": 0, + "options": [ + { + "label": "WebM", + "value": "0" + }, + { + "label": "Mp4", + "value": "1" + } + ] + }, + { + "name": "prefQlt", + "title": "Preferred Quality", + "type": "menulist", + "value": 2, + "options": [ + { + "label": "Higher", + "value": "0" + }, + { + "label": "High", + "value": "1" + }, + { + "label": "Medium", + "value": "2" + }, + { + "label": "Low", + "value": "3" + } + ] + }, + { + "name": "autoplay", + "title": "Auto Play Video", + "description": "Change default behaviour of the video player of the website", + "type": "menulist", + "value": 2, + "options": [ + { + "label": "Default", + "value": "2" + }, + { + "label": "Enabled", + "value": "1" + }, + { + "label": "Disabled", + "value": "0" + } + ] + }, + { + "name": "preload", + "title": "Preload Video", + "description": "Change default behaviour of the video player of the website in case it does not auto play the video", + "type": "menulist", + "value": 2, + "options": [ + { + "label": "Default", + "value": "2" + }, + { + "label": "Enabled", + "value": "1" + }, + { + "label": "Disabled", + "value": "0" + } + ] + }, + { + "name": "loop", + "title": "Auto Loop Video", + "type": "menulist", + "value": 2, + "options": [ + { + "label": "Default", + "value": "2" + }, + { + "label": "Always", + "value": "1" + }, + { + "label": "Never", + "value": "0" + } + ] + }, + { + "name": "volume", + "title": "Volume", + "type": "integer", + "value": 100 + }, + { + "name": "player", + "title": "Video Player", + "description": "Set video player to use", + "type": "menulist", + "value": 0, + "options": [ + { + "label": "Firefox Video Player", + "value": "0" + }, + { + "label": "Lean Back Player", + "value": "1" + } + ], + "hidden": true + }, + { + "name": "disableEmbed", + "title": "Disable for Embed Youtube", + "description": "Use default video player for embed videos", + "type": "bool", + "value": false, + "hidden": true + }, + { + "name": "disablebreak", + "title": "Disable on Break", + "description": "Use default video player on Break", + "type": "bool", + "value": false + }, + { + "name": "disabledailymotion", + "title": "Disable on Dailymotion", + "description": "Use default video player on Dailymotion", + "type": "bool", + "value": false + }, + { + "name": "disablefacebook", + "title": "Disable on Facebook", + "description": "Use default video player on Facebook", + "type": "bool", + "value": false + }, + { + "name": "disablemetacafe", + "title": "Disable on Metacafe", + "description": "Use default video player on Metacafe", + "type": "bool", + "value": false + }, + { + "name": "disablevimeo", + "title": "Disable on Vimeo", + "description": "Use default video player on Vimeo", + "type": "bool", + "value": false + }, + { + "name": "disableyoutube", + "title": "Disable on Youtube", + "description": "Use default video player on Youtube", + "type": "bool", + "value": false + } + ] +} \ No newline at end of file diff --git a/data/extensions/html5-video-everywhere@lejenome.me/spec/break.md b/data/extensions/html5-video-everywhere@lejenome.me/spec/break.md new file mode 100644 index 0000000..21d8540 --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/spec/break.md @@ -0,0 +1,5 @@ +**Milestone:** +[Break Support](https://github.com/lejenome/html5-video-everywhere/milestones/Break%20Support) + +**URL:** +- [X] `break.com/embed/` diff --git a/data/extensions/html5-video-everywhere@lejenome.me/spec/dailymotion.md b/data/extensions/html5-video-everywhere@lejenome.me/spec/dailymotion.md new file mode 100644 index 0000000..cdbddd7 --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/spec/dailymotion.md @@ -0,0 +1,5 @@ +**Milestone:** +[Dailymotion Support](https://github.com/lejenome/html5-video-everywhere/milestones/Dailymotion%20Support) + +**URL:** +- [x] `www.dailymotion.com/embed/video/` diff --git a/data/extensions/html5-video-everywhere@lejenome.me/spec/facebook.md b/data/extensions/html5-video-everywhere@lejenome.me/spec/facebook.md new file mode 100644 index 0000000..1c84156 --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/spec/facebook.md @@ -0,0 +1,11 @@ +**Milestone:** +[Facebook Support](https://github.com/lejenome/html5-video-everywhere/milestones/Facebook%20Support) + +**URL:** +- [x] `facebook.com/video?v=` +- [x] `www.facebook.com/video?v=` +- [x] `beta.facebook.com/video?v=` +- [x] `facebook.com//videos/` + +**IN PAGE EVENTS:** +- [ ] showing a video on the PhotoViewer dialog diff --git a/data/extensions/html5-video-everywhere@lejenome.me/spec/metacafe.md b/data/extensions/html5-video-everywhere@lejenome.me/spec/metacafe.md new file mode 100644 index 0000000..192ee8b --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/spec/metacafe.md @@ -0,0 +1,10 @@ +**Milestone:** +[Metacafe Support](https://github.com/lejenome/html5-video-everywhere/milestones/Metacafe%20Support) + +**URL:** +- [x] `metacafe.com/watch//` +- [x] `metacafe.com/<CHANNEL_NAME>` #6 + +**SWF:** +- [ ] `s.mcstatic.com/Flash/vp/mc/Portal_4.0.2.5.swf` +- [ ] `metacafe.com/fplayer/<VIDEO_ID>/<TITLE>.swf` diff --git a/data/extensions/html5-video-everywhere@lejenome.me/spec/vimeo.md b/data/extensions/html5-video-everywhere@lejenome.me/spec/vimeo.md new file mode 100644 index 0000000..c98f266 --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/spec/vimeo.md @@ -0,0 +1,15 @@ +**Milestone:** +[Vimeo Support](https://github.com/lejenome/html5-video-everywhere/milestones/Vimeo%20Support) + +**URL:** +- [x] `vimeo.com/channels/<CHANNEL_NAME>` +- [x] `player.vimeo.com/video/<VIDEO_ID>` +- [x] `vimeo.com/<VIDEO_ID>` +- [x] `vimeo.com/<USER>` +- [ ] `vimeo.com/couchmode/user<USER_ID>/videos/sort:<SORT_TYPE>/<VIDEO_ID>` #4 + +**SWF:** +- [ ] `vimeo.com/moogaloop.swf?clip_id=<VIDEO_ID>` (Flash player used by Facebook) #5 + +**API:** +more @ player.vimeo.com/playground diff --git a/data/extensions/html5-video-everywhere@lejenome.me/spec/youtube.md b/data/extensions/html5-video-everywhere@lejenome.me/spec/youtube.md new file mode 100644 index 0000000..51eb015 --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/spec/youtube.md @@ -0,0 +1,27 @@ +**Milestone:** +[YouTube Support](https://github.com/lejenome/html5-video-everywhere/milestones/YouTube%20Support) + +**URL:** +- [x] `www.youtube.com/v/<VIDEO_ID>` +- [x] `www.youtube.com/watch?v=<VIDEO_ID>` +- [x] `www.youtube.com/embed/<VIDEO_ID>` +- [ ] `www.youtube.com/apiplayer?video_id=<VIDEO_ID>&version=3` #2 +- [ ] `www.youtube.com/embed?listType=playlist&list=PL<PLAYLIST_ID>` #3 +- [ ] `www.youtube.com/embed?listType=user_uploads&list=<USERNAME>` #3 +- [ ] `www.youtube.com/embed?listType=search&list=<QUERY>` #3 + +**QUERY:** +- [x] autoplay 1,0 +- [ ] autohide 0,1,2 +- [ ] color +- [ ] controls 0,1,2 +- [ ] enablejsapi 0,1 +- [ ] end +- [ ] fs 0,1 +- [x] loop 0,1 +- [ ] playlist +- [x] start +- [x] t + +**API:** +more @ https://developers.google.com/youtube/player_parameters diff --git a/data/extensions/html5-video-everywhere@lejenome.me/test/test-main.js b/data/extensions/html5-video-everywhere@lejenome.me/test/test-main.js new file mode 100644 index 0000000..dd8fb7e --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/test/test-main.js @@ -0,0 +1,13 @@ +"use strict"; +var main = require("./main"); + +exports["test main"] = function(assert) { + assert.pass("Unit test running!"); +}; + +exports["test main async"] = function(assert, done) { + assert.pass("async Unit test running!"); + done(); +}; + +require("sdk/test").run(exports); \ No newline at end of file diff --git a/data/extensions/html5-video-everywhere@lejenome.me/test/test.html b/data/extensions/html5-video-everywhere@lejenome.me/test/test.html new file mode 100644 index 0000000..4480a41 --- /dev/null +++ b/data/extensions/html5-video-everywhere@lejenome.me/test/test.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<html> +<head> + <title>HTML5 Video EveryWhere Manual Test + + + + + + + + + -- cgit v1.2.3