├── LICENSE ├── README.md ├── client ├── index.html └── js │ ├── apiCDNP2P.js │ ├── peer.js │ ├── videojs-media-sources.js │ ├── videojs.hls.orig.js │ └── videojs │ ├── demo.captions.vtt │ ├── demo.html │ ├── font │ ├── vjs.eot │ ├── vjs.svg │ ├── vjs.ttf │ └── vjs.woff │ ├── lang │ ├── ar.js │ ├── de.js │ ├── es.js │ ├── fr.js │ ├── hu.js │ ├── it.js │ ├── ja.js │ ├── ko.js │ ├── nl.js │ ├── pt-BR.js │ ├── ru.js │ ├── uk.js │ └── zh.js │ ├── video-js.css │ ├── video-js.less │ ├── video-js.min.css │ ├── video-js.swf │ ├── video.dev.js │ └── video.js └── server └── peerjs-server ├── README.md ├── app.json ├── bin └── peerjs ├── lib ├── index.js ├── server.js └── util.js ├── package.json └── test └── server.js /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PCDN 2 | ==== 3 | 4 | 5 | PCDN is an Peer to peer/P2P CDN for video based on an hybride solution 6 | 7 | It's work with **Peerjs** , **Videojs** and **WebRTC**. 8 | 9 | It's a prof of concept so don't focus on the code quality. 10 | 11 | I hope you found it interresting and feel free to contact me 12 | 13 | An example here [http://pcdn.jairagne.ovh](http://pcdn.jairagne.ovh) 14 | 15 | Riquirement 16 | ========== 17 | 18 | - Videojs video player 19 | - Compatible Browsers 20 | - HLS video 21 | 22 | 23 | 24 | Client 25 | ===== 26 | 27 | Copy past on your video page this code 28 | 29 | 30 | 31 | 34 | 35 | Configuration: 36 | 37 | - host: host of the server 38 | - port: port of the peerjs server 39 | - key: api key of my peerjs server 40 | - debug: Level of debug 0-3 see peerjs configuration 41 | - more: [Peerjs API configuration](http://peerjs.com/) 42 | 43 | Server 44 | ===== 45 | 46 | **PCDN server** 47 | 48 | My PCDN server is free to use. feel free to make your test on it. 49 | For production mode, my advice is to do it by yourself. 50 | Or use existing solution like Streemroot or Peer5 .... 51 | [good article here](http://blog.uppersideconferences.com/the-non-telecom-side-of-webrtc-data-channel/) 52 | 53 | host: pcdn.jairagne.ovh 54 | port:9000 55 | key:peerjs 56 | 57 | **Your own PCDN server** 58 | 59 | $ cd server/peerjs-server 60 | $ npm install 61 | $ cd bin 62 | $ node peerjs --help 63 | 64 | 65 | Contact 66 | ====== 67 | Twitter : [@adelskott](https://twitter.com/adelskott) 68 | 69 | TODO 70 | ==== 71 | 72 | - Landing page for inactive users 73 | - Expose client API 74 | - Refactor code 75 | - Limit the client share to 5 76 | - Replace Peejs to use a proper server with faye 77 | - Use a redis or Elasticsearch instead of memory storage 78 | - Create a server dashboard for stats 79 | - Automatic reconnection server 80 | - Too many failure bybass P2P 81 | 82 | -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | video.js HLS Plugin Example 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | 21 | 34 | 35 | 36 | 37 |
38 | 47 |
48 |
49 |
50 | 51 | 52 | 122 | 123 | 124 | 134 | 135 | Fork me on GitHub 136 | 137 | 138 | -------------------------------------------------------------------------------- /client/js/apiCDNP2P.js: -------------------------------------------------------------------------------- 1 | 2 | var myBase = {}; 3 | var myBaseUrl = {}; 4 | var coreCache = {}; 5 | var statP2P = { 6 | p2p:0, 7 | cdn:0, 8 | leechers:0, 9 | seeders:0, 10 | requests:0 11 | }; 12 | 13 | (function(videojs){ 14 | videojs.APIP2P = {}; 15 | videojs.APIP2P.startConnect = function (options) { 16 | var self = this; 17 | var peer = new Peer(options); 18 | videojs.APIP2P.options = options; 19 | videojs.APIP2P.peer = peer; 20 | peer.on('connection', function (conn){ 21 | // manage /... 22 | myBase[conn.peer] = conn; 23 | videojs.APIP2P.initializing(conn, null); 24 | }); 25 | peer.on("error", function (error){ 26 | console.log("ERROR P2P peerjs", error.type); 27 | if (error.type == "peer-unavailable"){ 28 | var id = error.message.substring("Could not connect to peer ".length); 29 | videojs.APIP2P.cleanbase(id); 30 | var obj = { 31 | response : null 32 | }; 33 | var ca = self.callbacks[id]; 34 | if (ca){ 35 | // perdu un leecher 36 | statP2P.leechers--; 37 | ca.callback.call(obj, obj, "error no peer", ca.message.url); 38 | delete self.callbacks[id]; 39 | }else{ 40 | // perdu un seeder 41 | } 42 | } 43 | console.log("ERROR",error.message); 44 | }); 45 | }; 46 | 47 | 48 | videojs.APIP2P.callbacks = {}; 49 | 50 | 51 | videojs.APIP2P.cleanbase = function (id) { 52 | if (myBase[id]) 53 | delete myBase[id]; 54 | for (var i = 0; i < Object.keys(myBaseUrl).length; i++) { 55 | var url = Object.keys(myBaseUrl)[i]; 56 | if(myBaseUrl[url][id]) 57 | delete myBaseUrl[url][id]; 58 | } 59 | }; 60 | 61 | videojs.APIP2P.sendMyBase = function (conn) { 62 | var base = {}; 63 | for (var i = 0; i < Object.keys(myBaseUrl).length; i++) { 64 | var url = Object.keys(myBaseUrl)[i]; 65 | base[url] = Object.keys(myBaseUrl[url]); 66 | } 67 | conn.send({ 68 | type:"ABASE", 69 | data:base, 70 | id:videojs.APIP2P.peer.id 71 | }); 72 | }; 73 | videojs.APIP2P.initializing = function (conn, leacher){ 74 | if (!conn){ 75 | console.log("RRRR", "conn error",conn); 76 | return; 77 | } 78 | var self = this; 79 | // conn.on('error', function (error){ 80 | // console.log("PEER ERROR", conn.peer, error); 81 | // videojs.APIP2P.cleanbase(conn.peer); 82 | // }); 83 | var cleanfct = function(conn){ 84 | videojs.APIP2P.cleanbase(conn.peer); 85 | var obj = { 86 | response : null 87 | }; 88 | var ca = self.callbacks[conn.peer]; 89 | if (ca){ 90 | ca.callback.call(obj, "error", ca.message.url); 91 | delete self.callbacks[conn.peer]; 92 | } 93 | }; 94 | conn.on('error', function (error){ 95 | console.log("PEER ERROR P2P", conn.peer, error); 96 | cleanfct(conn); 97 | }); 98 | conn.on('disconnected', function (error){ 99 | console.log("PEER disconnected P2P", conn.peer, error); 100 | cleanfct(conn); 101 | }); 102 | conn.on('close', function (error){ 103 | console.log("PEER close P2P", conn.peer, error); 104 | cleanfct(conn); 105 | }); 106 | conn.on('open', function (){ 107 | // send my base 108 | videojs.APIP2P.sendMyBase(conn); 109 | 110 | conn.on('data', function (data){ 111 | //console.log("RCV data"); 112 | console.log("RCV data",conn.peer,data); 113 | // managing data 114 | if (data.type == "DATA"){ 115 | var obj = { 116 | response : data.data 117 | }; 118 | var ca = self.callbacks[conn.peer]; 119 | // TODO I'm Leecher for url 120 | if (data.data) 121 | videojs.APIP2P.imLeecher(data.url, videojs.APIP2P.peer.id, data.data); 122 | if (!ca) 123 | return; 124 | ca.callback.call(obj, obj, data.data == null, ca.message.url); 125 | delete self.callbacks[conn.peer]; 126 | } 127 | 128 | if (data.type == "ASK"){ 129 | // if i have the content 130 | 131 | if (coreCache[data.url]){ 132 | console.log("send data"); 133 | conn.send({ 134 | type: "DATA", 135 | data:coreCache[data.url], 136 | url: data.url 137 | }); 138 | }else{ 139 | console.log("NO data"); 140 | conn.send({ 141 | type: "DATA", 142 | url: data.url 143 | }); 144 | } 145 | } 146 | if (data.type == "UBASE"){ 147 | var all = data.data; 148 | if (!myBaseUrl[data.url]) 149 | myBaseUrl[data.url] = {}; 150 | myBaseUrl[data.url][data.id] = conn; 151 | } 152 | if (data.type == "ABASE"){ 153 | var all = data.data; 154 | for (var i = 0; i < Object.keys(all).length; i++) { 155 | var u = Object.keys(all)[i]; 156 | if (!myBaseUrl[u]){ 157 | myBaseUrl[u]= []; 158 | } 159 | for (var j = 0; j < all[u].length; j++){ 160 | var l = all[u][j]; 161 | if (l == videojs.APIP2P.peer.id) 162 | continue; 163 | if (!myBaseUrl[u][l]){ 164 | myBaseUrl[u][l] = myBase[l] || 1 ; 165 | } 166 | } 167 | } 168 | console.log("My Base",Object.keys(myBaseUrl)); 169 | } 170 | }); 171 | // if callbacks ask 172 | if (self.callbacks[conn.peer]){ 173 | conn.send(self.callbacks[conn.peer].message); 174 | } 175 | }); 176 | }; 177 | videojs.APIP2P.xhrP2P = function (leechers, url, callback){ 178 | // connect to all leechers if no connected 179 | var selectIndex = Math.floor(Math.random() * leechers.length); 180 | var error = false; 181 | for (var i = 0; i < leechers.length; i++) { 182 | var leecher = leechers[i]; 183 | if (!myBase[leecher]){ 184 | var conn = this.peer.connect(leecher); 185 | if (!conn){ 186 | if (i == selectIndex){ 187 | error = true; 188 | } 189 | continue; 190 | } 191 | myBase[leecher] = conn; 192 | myBaseUrl[url][leechers[i]] = conn; 193 | // select 1 194 | if (i == selectIndex){ 195 | // ask for content 196 | this.callbacks[leechers[i]] = { 197 | callback: callback, 198 | message: { 199 | type: 'ASK', 200 | url: url 201 | } 202 | }; 203 | // fire timer to timeout 204 | } 205 | // add inteligene for each leacher 206 | this.initializing(conn,leechers[i]); 207 | }else if (i == selectIndex){ 208 | var conn = myBase[leecher]; 209 | this.callbacks[leecher] = { 210 | callback: callback, 211 | message: { 212 | type: 'ASK', 213 | url: url 214 | } 215 | }; 216 | conn.send(this.callbacks[leecher].message); 217 | } 218 | } 219 | if (error){ 220 | callback.call({response:null}, "error", url); 221 | } 222 | }; 223 | videojs.APIP2P.updateLeechers = function (url, callback){ 224 | console.log("Update leechers",url); 225 | var options = { 226 | method: 'GET', 227 | timeout: 45 * 1000 228 | }; 229 | var checkurl = "http://"+videojs.APIP2P.options.host+":"+videojs.APIP2P.options.port+"/peerjs/url/"+window.btoa(url); 230 | var request = new window.XMLHttpRequest(); 231 | request.open(options.method, checkurl); 232 | request.onreadystatechange = function() { 233 | if (this.readyState !== 4) { 234 | return; 235 | } 236 | if (this.response && this.status == 200) { 237 | var leechers = JSON.parse(this.response); 238 | for (var i = 0; i < leechers.length; i++) { 239 | if (leechers[i] == videojs.APIP2P.peer.id){ 240 | // si c'est moi 241 | leechers.splice(i,1); 242 | i--; 243 | }else{ 244 | myBaseUrl[url][leechers[i]] = 1; 245 | } 246 | } 247 | callback(leechers); 248 | } else { 249 | callback([]); 250 | } 251 | }; 252 | request.send(null); 253 | }; 254 | 255 | videojs.APIP2P.imLeecher = function (url, id, data){ 256 | coreCache[url] = data; 257 | var options = { 258 | method: 'GET', 259 | timeout: 45 * 1000 260 | }; 261 | var checkurl = "http://"+videojs.APIP2P.options.host+":"+videojs.APIP2P.options.port+"/peerjs/url/"+window.btoa(url)+"/"+id; 262 | var request = new window.XMLHttpRequest(); 263 | request.open(options.method, checkurl); 264 | request.onreadystatechange = function() { 265 | if (this.readyState !== 4) { 266 | return; 267 | } 268 | if (this.response && this.status == 200) { 269 | console.log("I'm Leecher for :", url); 270 | } else { 271 | console.log("ERROR setting leecher for :", url); 272 | } 273 | }; 274 | request.send(null); 275 | 276 | // say to all my leecher that i'm leacher of this content 277 | console.log(myBase); 278 | for (var i = 0; i < Object.keys(myBase).length; i++) { 279 | var l = Object.keys(myBase)[i]; 280 | if (myBase[l]){ 281 | myBase[l].send({ 282 | type: "UBASE", 283 | url:url, 284 | id:videojs.APIP2P.peer.id 285 | }); 286 | } 287 | }; 288 | }; 289 | 290 | })(window.videojs); 291 | (function(videojs){ 292 | /** 293 | * Creates and sends an XMLHttpRequest. 294 | * TODO - expose video.js core's XHR and use that instead 295 | * 296 | * @param options {string | object} if this argument is a string, it 297 | * is intrepreted as a URL and a simple GET request is 298 | * inititated. If it is an object, it should contain a `url` 299 | * property that indicates the URL to request and optionally a 300 | * `method` which is the type of HTTP request to send. 301 | * @param callback (optional) {function} a function to call when the 302 | * request completes. If the request was not successful, the first 303 | * argument will be falsey. 304 | * @return {object} the XMLHttpRequest that was initiated. 305 | */ 306 | videojs.Hls.xhr = function(url_C, callback) { 307 | // return videojs.Hls.xhrCDN(url_C, callback); 308 | // console.log(url_C); 309 | try { 310 | var url = url_C; 311 | if (typeof url_C === 'object') { 312 | url = url_C.url; 313 | } 314 | 315 | console.log("Call url", url); 316 | var self = this; 317 | if (url.indexOf(".ts") < 0){ 318 | if (!myBaseUrl[url]){ 319 | myBaseUrl[url] = {};//responseType:"arraybuffer" 320 | } 321 | return videojs.Hls.xhrCDN(url, callback); 322 | }else { 323 | if (!myBaseUrl[url]){ 324 | myBaseUrl[url] = {}; 325 | } 326 | if (Object.keys(myBaseUrl[url]).length > 0){ 327 | // have leacher 328 | videojs.Hls.xhrP2P(Object.keys(myBaseUrl[url]), url, callback); 329 | console.log("I know leechers"); 330 | // update leachers 331 | videojs.APIP2P.updateLeechers(url, function (leechers){}); 332 | return { 333 | abort:function(){ 334 | console.log("========================================================================"); 335 | } 336 | }; 337 | } 338 | } 339 | if (typeof callback !== 'function') { 340 | callback = function() {}; 341 | } 342 | // videojs.Hls.xhrCDN(url, callback); 343 | 344 | console.log("Want leechers"); 345 | videojs.APIP2P.updateLeechers(url, function (leechers){ 346 | if (leechers.length){ 347 | videojs.Hls.xhrP2P(leechers, url, callback); 348 | }else{ 349 | videojs.Hls.xhrCDN(url, callback); 350 | } 351 | }); 352 | return { 353 | abort:function(){ 354 | console.log("========================================================================"); 355 | } 356 | }; 357 | }catch (e){ 358 | console.log(e); 359 | } 360 | return { 361 | abort:function(){ 362 | console.log("========================================================================"); 363 | } 364 | }; 365 | }; 366 | 367 | videojs.Hls.xhrP2P = function(leechers, url, callback) { 368 | console.log("CALL P2P", url); 369 | var sender = { 370 | requestTime: new Date().getTime() 371 | }; 372 | videojs.APIP2P.xhrP2P(leechers,url,function(obj, error, url_cb){ 373 | if (error){ 374 | // leecher fail 375 | return videojs.Hls.xhrCDN(url, callback); 376 | } 377 | sender.responseTime = new Date().getTime(); 378 | sender = videojs.util.mergeOptions(sender,{ 379 | roundTripTime : sender.responseTime - sender.requestTime, 380 | bytesReceived : obj.response.byteLength || obj.response.length, 381 | status:200 382 | }); 383 | sender.bandwidth = Math.floor((sender.bytesReceived / sender.roundTripTime) * 8 * 1000), 384 | sender.responseType = "arraybuffer"; 385 | sender = videojs.util.mergeOptions(sender,obj); 386 | console.log(sender); 387 | statP2P.p2p += sender.bytesReceived; 388 | callback.call(sender,false,url); 389 | }); 390 | }; 391 | 392 | 393 | 394 | videojs.Hls.xhrCDN = function(url, callback) { 395 | console.log("CALL CDN", url); 396 | var 397 | options = { 398 | method: 'GET', 399 | timeout: 45 * 1000 400 | }, 401 | request, 402 | abortTimeout; 403 | 404 | if (typeof callback !== 'function') { 405 | callback = function() {}; 406 | } 407 | 408 | if (typeof url === 'object') { 409 | options = videojs.util.mergeOptions(options, url); 410 | url = options.url; 411 | } 412 | if (url.indexOf(".ts") > -1){ 413 | options.responseType = "arraybuffer"; 414 | } 415 | 416 | request = new window.XMLHttpRequest(); 417 | request.open(options.method, url); 418 | request.url = url; 419 | request.requestTime = new Date().getTime(); 420 | 421 | if (options.responseType) { 422 | request.responseType = options.responseType; 423 | } 424 | if (options.withCredentials) { 425 | request.withCredentials = true; 426 | } 427 | if (options.timeout) { 428 | abortTimeout = window.setTimeout(function() { 429 | if (request.readyState !== 4) { 430 | request.timedout = true; 431 | request.abort(); 432 | } 433 | }, options.timeout); 434 | } 435 | 436 | request.onreadystatechange = function() { 437 | // wait until the request completes 438 | if (this.readyState !== 4) { 439 | return; 440 | } 441 | 442 | // clear outstanding timeouts 443 | window.clearTimeout(abortTimeout); 444 | 445 | // request timeout 446 | if (request.timedout) { 447 | return callback.call(this, 'timeout', url); 448 | } 449 | 450 | // request aborted or errored 451 | if (this.status >= 400 || this.status === 0) { 452 | return callback.call(this, true, url); 453 | } 454 | 455 | if (this.response) { 456 | this.responseTime = new Date().getTime(); 457 | this.roundTripTime = this.responseTime - this.requestTime; 458 | this.bytesReceived = this.response.byteLength || this.response.length; 459 | this.bandwidth = Math.floor((this.bytesReceived / this.roundTripTime) * 8 * 1000); 460 | statP2P.cdn += this.bytesReceived; 461 | } 462 | videojs.APIP2P.imLeecher(url, videojs.APIP2P.peer.id, this.response); 463 | //console.log(this); 464 | return callback.call(this, false, url); 465 | }; 466 | request.send(null); 467 | return request; 468 | }; 469 | 470 | })(window.videojs); 471 | 472 | 473 | window.apiCDNP2P = function(options){ 474 | // Videojs hls wrapper 475 | if (window.videojs && window.videojs.Hls){ 476 | var baseopt = {host:"127.0.0.1",port:"9000",key: 'peerjs',debug:3}; 477 | baseopt = window.videojs.util.mergeOptions(baseopt, options); 478 | window.videojs.APIP2P.startConnect(baseopt); 479 | } 480 | }; 481 | -------------------------------------------------------------------------------- /client/js/videojs-media-sources.js: -------------------------------------------------------------------------------- 1 | (function(window){ 2 | var urlCount = 0, 3 | NativeMediaSource = window.MediaSource || window.WebKitMediaSource || {}, 4 | nativeUrl = window.URL || {}, 5 | EventEmitter, 6 | flvCodec = /video\/flv; codecs=["']vp6,aac["']/, 7 | objectUrlPrefix = 'blob:vjs-media-source/'; 8 | 9 | EventEmitter = function(){}; 10 | EventEmitter.prototype.init = function(){ 11 | this.listeners = []; 12 | }; 13 | EventEmitter.prototype.addEventListener = function(type, listener){ 14 | if (!this.listeners[type]){ 15 | this.listeners[type] = []; 16 | } 17 | this.listeners[type].unshift(listener); 18 | }; 19 | EventEmitter.prototype.trigger = function(event){ 20 | var listeners = this.listeners[event.type] || [], 21 | i = listeners.length; 22 | while (i--) { 23 | listeners[i](event); 24 | } 25 | }; 26 | 27 | // extend the media source APIs 28 | 29 | // Media Source 30 | videojs.MediaSource = function(){ 31 | var self = this; 32 | videojs.MediaSource.prototype.init.call(this); 33 | 34 | this.sourceBuffers = []; 35 | this.readyState = 'closed'; 36 | this.listeners = { 37 | sourceopen: [function(event){ 38 | // find the swf where we will push media data 39 | self.swfObj = document.getElementById(event.swfId); 40 | self.readyState = 'open'; 41 | 42 | // trigger load events 43 | if (self.swfObj) { 44 | self.swfObj.vjs_load(); 45 | } 46 | }], 47 | webkitsourceopen: [function(event){ 48 | self.trigger({ 49 | type: 'sourceopen' 50 | }); 51 | }] 52 | }; 53 | }; 54 | videojs.MediaSource.prototype = new EventEmitter(); 55 | 56 | // create a new source buffer to receive a type of media data 57 | videojs.MediaSource.prototype.addSourceBuffer = function(type){ 58 | var sourceBuffer; 59 | 60 | // if this is an FLV type, we'll push data to flash 61 | if (flvCodec.test(type)) { 62 | // Flash source buffers 63 | sourceBuffer = new videojs.SourceBuffer(this); 64 | } else { 65 | // native source buffers 66 | sourceBuffer = this.nativeSource.addSourceBuffer.apply(this.nativeSource, arguments); 67 | } 68 | 69 | this.sourceBuffers.push(sourceBuffer); 70 | return sourceBuffer; 71 | }; 72 | videojs.MediaSource.prototype.endOfStream = function(){ 73 | this.swfObj.vjs_endOfStream(); 74 | this.readyState = 'ended'; 75 | }; 76 | 77 | // store references to the media sources so they can be connected 78 | // to a video element (a swf object) 79 | videojs.mediaSources = {}; 80 | // provide a method for a swf object to notify JS that a media source is now open 81 | videojs.MediaSource.open = function(msObjectURL, swfId){ 82 | var ms = videojs.mediaSources[msObjectURL]; 83 | 84 | if (ms) { 85 | ms.trigger({ 86 | type: 'sourceopen', 87 | swfId: swfId 88 | }); 89 | } else { 90 | throw new Error('Media Source not found (Video.js)'); 91 | } 92 | }; 93 | 94 | // Source Buffer 95 | videojs.SourceBuffer = function(source){ 96 | videojs.SourceBuffer.prototype.init.call(this); 97 | this.source = source; 98 | this.buffer = []; 99 | }; 100 | videojs.SourceBuffer.prototype = new EventEmitter(); 101 | 102 | // accept video data and pass to the video (swf) object 103 | videojs.SourceBuffer.prototype.appendBuffer = function(uint8Array){ 104 | var binary = '', 105 | i = 0, 106 | len = uint8Array.byteLength, 107 | b64str; 108 | 109 | this.buffer.push(uint8Array); 110 | 111 | // base64 encode the bytes 112 | for (i = 0; i < len; i++) { 113 | binary += String.fromCharCode(uint8Array[i]) 114 | } 115 | b64str = window.btoa(binary); 116 | 117 | this.trigger({type:'update'}); 118 | 119 | // bypass normal ExternalInterface calls and pass xml directly 120 | // EI can be slow by default 121 | this.source.swfObj.CallFunction('' 123 | + b64str 124 | + ''); 125 | 126 | 127 | this.trigger({type:'updateend'}); 128 | }; 129 | 130 | // URL 131 | videojs.URL = { 132 | createObjectURL: function(object){ 133 | var url = objectUrlPrefix + urlCount; 134 | 135 | urlCount++; 136 | 137 | // setup the mapping back to object 138 | videojs.mediaSources[url] = object; 139 | 140 | return url; 141 | } 142 | }; 143 | 144 | // plugin 145 | videojs.plugin('mediaSource', function(options){ 146 | var player = this; 147 | 148 | player.on('loadstart', function(){ 149 | var url = player.currentSrc(), 150 | trigger = function(event){ 151 | mediaSource.trigger(event); 152 | }, 153 | mediaSource; 154 | 155 | if (player.techName === 'Html5' && url.indexOf(objectUrlPrefix) === 0) { 156 | // use the native media source implementation 157 | mediaSource = videojs.mediaSources[url]; 158 | 159 | if (!mediaSource.nativeUrl) { 160 | // initialize the native source 161 | mediaSource.nativeSource = new NativeMediaSource(); 162 | mediaSource.nativeSource.addEventListener('sourceopen', trigger, false); 163 | mediaSource.nativeSource.addEventListener('webkitsourceopen', trigger, false); 164 | mediaSource.nativeUrl = nativeUrl.createObjectURL(mediaSource.nativeSource); 165 | } 166 | player.src(mediaSource.nativeUrl); 167 | } 168 | }); 169 | }); 170 | 171 | })(this); 172 | -------------------------------------------------------------------------------- /client/js/videojs/demo.captions.vtt: -------------------------------------------------------------------------------- 1 | WEBVTT 2 | 3 | 00:00.700 --> 00:04.110 4 | Captions describe all relevant audio for the hearing impaired. 5 | [ Heroic music playing for a seagull ] 6 | 7 | 00:04.500 --> 00:05.000 8 | [ Splash!!! ] 9 | 10 | 00:05.100 --> 00:06.000 11 | [ Sploosh!!! ] 12 | 13 | 00:08.000 --> 00:09.225 14 | [ Splash...splash...splash splash splash ] 15 | 16 | 00:10.525 --> 00:11.255 17 | [ Splash, Sploosh again ] 18 | 19 | 00:13.500 --> 00:14.984 20 | Dolphin: eeeEEEEEeeee! 21 | 22 | 00:14.984 --> 00:16.984 23 | Dolphin: Squawk! eeeEEE? 24 | 25 | 00:25.000 --> 00:28.284 26 | [ A whole ton of splashes ] 27 | 28 | 00:29.500 --> 00:31.000 29 | Mine. Mine. Mine. 30 | 31 | 00:34.300 --> 00:36.000 32 | Shark: Chomp 33 | 34 | 00:36.800 --> 00:37.900 35 | Shark: CHOMP!!! 36 | 37 | 00:37.861 --> 00:41.193 38 | EEEEEEOOOOOOOOOOWHALENOISE 39 | 40 | 00:42.593 --> 00:45.611 41 | [ BIG SPLASH ] -------------------------------------------------------------------------------- /client/js/videojs/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Video.js | HTML5 Video Player 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /client/js/videojs/font/vjs.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Iragne/PCDN/4aa1f4eb3bb28f6f03fcdcef883d5f2213931bd3/client/js/videojs/font/vjs.eot -------------------------------------------------------------------------------- /client/js/videojs/font/vjs.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /client/js/videojs/font/vjs.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Iragne/PCDN/4aa1f4eb3bb28f6f03fcdcef883d5f2213931bd3/client/js/videojs/font/vjs.ttf -------------------------------------------------------------------------------- /client/js/videojs/font/vjs.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Iragne/PCDN/4aa1f4eb3bb28f6f03fcdcef883d5f2213931bd3/client/js/videojs/font/vjs.woff -------------------------------------------------------------------------------- /client/js/videojs/lang/ar.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("ar",{ 2 | "Play": "تشغيل", 3 | "Pause": "ايقاف", 4 | "Current Time": "الوقت الحالي", 5 | "Duration Time": "Dauer", 6 | "Remaining Time": "الوقت المتبقي", 7 | "Stream Type": "نوع التيار", 8 | "LIVE": "مباشر", 9 | "Loaded": "تم التحميل", 10 | "Progress": "التقدم", 11 | "Fullscreen": "ملء الشاشة", 12 | "Non-Fullscreen": "غير ملء الشاشة", 13 | "Mute": "صامت", 14 | "Unmuted": "غير الصامت", 15 | "Playback Rate": "معدل التشغيل", 16 | "Subtitles": "الترجمة", 17 | "subtitles off": "ايقاف الترجمة", 18 | "Captions": "التعليقات", 19 | "captions off": "ايقاف التعليقات", 20 | "Chapters": "فصول", 21 | "You aborted the video playback": "لقد ألغيت تشغيل الفيديو", 22 | "A network error caused the video download to fail part-way.": "تسبب خطأ في الشبكة بفشل تحميل الفيديو بالكامل.", 23 | "The video could not be loaded, either because the server or network failed or because the format is not supported.": "لا يمكن تحميل الفيديو بسبب فشل في الخادم أو الشبكة ، أو فشل بسبب عدم امكانية قراءة تنسيق الفيديو.", 24 | "The video playback was aborted due to a corruption problem or because the video used features your browser did not support.": "تم ايقاف تشغيل الفيديو بسبب مشكلة فساد أو لأن الفيديو المستخدم يستخدم ميزات غير مدعومة من متصفحك.", 25 | "No compatible source was found for this video.": "فشل العثور على أي مصدر متوافق مع هذا الفيديو." 26 | }); -------------------------------------------------------------------------------- /client/js/videojs/lang/de.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("de",{ 2 | "Play": "Wiedergabe", 3 | "Pause": "Pause", 4 | "Current Time": "Aktueller Zeitpunkt", 5 | "Duration Time": "Dauer", 6 | "Remaining Time": "Verbleibende Zeit", 7 | "Stream Type": "Streamtyp", 8 | "LIVE": "LIVE", 9 | "Loaded": "Geladen", 10 | "Progress": "Status", 11 | "Fullscreen": "Vollbild", 12 | "Non-Fullscreen": "Kein Vollbild", 13 | "Mute": "Ton aus", 14 | "Unmuted": "Ton ein", 15 | "Playback Rate": "Wiedergabegeschwindigkeit", 16 | "Subtitles": "Untertitel", 17 | "subtitles off": "Untertitel aus", 18 | "Captions": "Untertitel", 19 | "captions off": "Untertitel aus", 20 | "Chapters": "Kapitel", 21 | "You aborted the video playback": "Sie haben die Videowiedergabe abgebrochen.", 22 | "A network error caused the video download to fail part-way.": "Der Videodownload ist aufgrund eines Netzwerkfehlers fehlgeschlagen.", 23 | "The video could not be loaded, either because the server or network failed or because the format is not supported.": "Das Video konnte nicht geladen werden, da entweder ein Server- oder Netzwerkfehler auftrat oder das Format nicht unterstützt wird.", 24 | "The video playback was aborted due to a corruption problem or because the video used features your browser did not support.": "Die Videowiedergabe wurde entweder wegen eines Problems mit einem beschädigten Video oder wegen verwendeten Funktionen, die vom Browser nicht unterstützt werden, abgebrochen.", 25 | "No compatible source was found for this video.": "Für dieses Video wurde keine kompatible Quelle gefunden." 26 | }); -------------------------------------------------------------------------------- /client/js/videojs/lang/es.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("es",{ 2 | "Play": "Reproducción", 3 | "Pause": "Pausa", 4 | "Current Time": "Tiempo reproducido", 5 | "Duration Time": "Duración total", 6 | "Remaining Time": "Tiempo restante", 7 | "Stream Type": "Tipo de secuencia", 8 | "LIVE": "DIRECTO", 9 | "Loaded": "Cargado", 10 | "Progress": "Progreso", 11 | "Fullscreen": "Pantalla completa", 12 | "Non-Fullscreen": "Pantalla no completa", 13 | "Mute": "Silenciar", 14 | "Unmuted": "No silenciado", 15 | "Playback Rate": "Velocidad de reproducción", 16 | "Subtitles": "Subtítulos", 17 | "subtitles off": "Subtítulos desactivados", 18 | "Captions": "Subtítulos especiales", 19 | "captions off": "Subtítulos especiales desactivados", 20 | "Chapters": "Capítulos", 21 | "You aborted the video playback": "Ha interrumpido la reproducción del vídeo.", 22 | "A network error caused the video download to fail part-way.": "Un error de red ha interrumpido la descarga del vídeo.", 23 | "The video could not be loaded, either because the server or network failed or because the format is not supported.": "No se ha podido cargar el vídeo debido a un fallo de red o del servidor o porque el formato es incompatible.", 24 | "The video playback was aborted due to a corruption problem or because the video used features your browser did not support.": "La reproducción de vídeo se ha interrumpido por un problema de corrupción de datos o porque el vídeo precisa funciones que su navegador no ofrece.", 25 | "No compatible source was found for this video.": "No se ha encontrado ninguna fuente compatible con este vídeo." 26 | }); -------------------------------------------------------------------------------- /client/js/videojs/lang/fr.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("fr",{ 2 | "Play": "Lecture", 3 | "Pause": "Pause", 4 | "Current Time": "Temps actuel", 5 | "Duration Time": "Durée", 6 | "Remaining Time": "Temps restant", 7 | "Stream Type": "Type de flux", 8 | "LIVE": "EN DIRECT", 9 | "Loaded": "Chargé", 10 | "Progress": "Progression", 11 | "Fullscreen": "Plein écran", 12 | "Non-Fullscreen": "Fenêtré", 13 | "Mute": "Sourdine", 14 | "Unmuted": "Son activé", 15 | "Playback Rate": "Vitesse de lecture", 16 | "Subtitles": "Sous-titres", 17 | "subtitles off": "Sous-titres désactivés", 18 | "Captions": "Sous-titres", 19 | "captions off": "Sous-titres désactivés", 20 | "Chapters": "Chapitres", 21 | "You aborted the video playback": "Vous avez interrompu la lecture de la vidéo.", 22 | "A network error caused the video download to fail part-way.": "Une erreur de réseau a interrompu le téléchargement de la vidéo.", 23 | "The video could not be loaded, either because the server or network failed or because the format is not supported.": "Cette vidéo n'a pas pu être chargée, soit parce que le serveur ou le réseau a échoué ou parce que le format n'est pas reconnu.", 24 | "The video playback was aborted due to a corruption problem or because the video used features your browser did not support.": "La lecture de la vidéo a été interrompue à cause d'un problème de corruption ou parce que la vidéo utilise des fonctionnalités non prises en charge par votre navigateur.", 25 | "No compatible source was found for this video.": "Aucune source compatible n'a été trouvée pour cette vidéo." 26 | }); -------------------------------------------------------------------------------- /client/js/videojs/lang/hu.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("hu",{ 2 | "Play": "Lejátszás", 3 | "Pause": "Szünet", 4 | "Current Time": "Aktuális időpont", 5 | "Duration Time": "Hossz", 6 | "Remaining Time": "Hátralévő idő", 7 | "Stream Type": "Adatfolyam típusa", 8 | "LIVE": "ÉLŐ", 9 | "Loaded": "Betöltve", 10 | "Progress": "Állapot", 11 | "Fullscreen": "Teljes képernyő", 12 | "Non-Fullscreen": "Normál méret", 13 | "Mute": "Némítás", 14 | "Unmuted": "Némítás kikapcsolva", 15 | "Playback Rate": "Lejátszási sebesség", 16 | "Subtitles": "Feliratok", 17 | "subtitles off": "Feliratok kikapcsolva", 18 | "Captions": "Magyarázó szöveg", 19 | "captions off": "Magyarázó szöveg kikapcsolva", 20 | "Chapters": "Fejezetek", 21 | "You aborted the video playback": "Leállította a lejátszást", 22 | "A network error caused the video download to fail part-way.": "Hálózati hiba miatt a videó részlegesen töltődött le.", 23 | "The video could not be loaded, either because the server or network failed or because the format is not supported.": "A videó nem tölthető be hálózati vagy kiszolgálói hiba miatt, vagy a formátuma nem támogatott.", 24 | "The video playback was aborted due to a corruption problem or because the video used features your browser did not support.": "A lejátszás adatsérülés miatt leállt, vagy a videó egyes tulajdonságait a böngészője nem támogatja.", 25 | "No compatible source was found for this video.": "Nincs kompatibilis forrás ehhez a videóhoz." 26 | }); -------------------------------------------------------------------------------- /client/js/videojs/lang/it.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("it",{ 2 | "Play": "Play", 3 | "Pause": "Pausa", 4 | "Current Time": "Orario attuale", 5 | "Duration Time": "Durata", 6 | "Remaining Time": "Tempo rimanente", 7 | "Stream Type": "Tipo del Streaming", 8 | "LIVE": "LIVE", 9 | "Loaded": "Caricato", 10 | "Progress": "Stato", 11 | "Fullscreen": "Schermo intero", 12 | "Non-Fullscreen": "Chiudi schermo intero", 13 | "Mute": "Muto", 14 | "Unmuted": "Audio", 15 | "Playback Rate": "Tasso di riproduzione", 16 | "Subtitles": "Sottotitoli", 17 | "subtitles off": "Senza sottotitoli", 18 | "Captions": "Sottotitoli non udenti", 19 | "captions off": "Senza sottotitoli non udenti", 20 | "Chapters": "Capitolo", 21 | "You aborted the video playback": "La riproduzione del filmato è stata interrotta.", 22 | "A network error caused the video download to fail part-way.": "Il download del filmato è stato interrotto a causa di un problema rete.", 23 | "The video could not be loaded, either because the server or network failed or because the format is not supported.": "Il filmato non può essere caricato a causa di un errore nel server o nella rete o perché il formato non viene supportato.", 24 | "The video playback was aborted due to a corruption problem or because the video used features your browser did not support.": "La riproduzione del filmato è stata interrotta a causa di un file danneggiato o per l’utilizzo di impostazioni non supportate dal browser.", 25 | "No compatible source was found for this video.": "Non ci sono fonti compatibili per questo filmato." 26 | }); -------------------------------------------------------------------------------- /client/js/videojs/lang/ja.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("ja",{ 2 | "Play": "再生", 3 | "Pause": "一時停止", 4 | "Current Time": "現在の時間", 5 | "Duration Time": "長さ", 6 | "Remaining Time": "残りの時間", 7 | "Stream Type": "ストリームの種類", 8 | "LIVE": "ライブ", 9 | "Loaded": "ロード済み", 10 | "Progress": "進行状況", 11 | "Fullscreen": "フルスクリーン", 12 | "Non-Fullscreen": "フルスクリーン以外", 13 | "Mute": "ミュート", 14 | "Unmuted": "ミュート解除", 15 | "Playback Rate": "再生レート", 16 | "Subtitles": "サブタイトル", 17 | "subtitles off": "サブタイトル オフ", 18 | "Captions": "キャプション", 19 | "captions off": "キャプション オフ", 20 | "Chapters": "チャプター", 21 | "You aborted the video playback": "動画再生を中止しました", 22 | "A network error caused the video download to fail part-way.": "ネットワーク エラーにより動画のダウンロードが途中で失敗しました", 23 | "The video could not be loaded, either because the server or network failed or because the format is not supported.": "サーバーまたはネットワークのエラー、またはフォーマットがサポートされていないため、動画をロードできませんでした", 24 | "The video playback was aborted due to a corruption problem or because the video used features your browser did not support.": "破損の問題、またはお使いのブラウザがサポートしていない機能が動画に使用されていたため、動画の再生が中止されました", 25 | "No compatible source was found for this video.": "この動画に対して互換性のあるソースが見つかりませんでした" 26 | }); -------------------------------------------------------------------------------- /client/js/videojs/lang/ko.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("ko",{ 2 | "Play": "재생", 3 | "Pause": "일시중지", 4 | "Current Time": "현재 시간", 5 | "Duration Time": "지정 기간", 6 | "Remaining Time": "남은 시간", 7 | "Stream Type": "스트리밍 유형", 8 | "LIVE": "라이브", 9 | "Loaded": "로드됨", 10 | "Progress": "진행", 11 | "Fullscreen": "전체 화면", 12 | "Non-Fullscreen": "전체 화면 해제", 13 | "Mute": "음소거", 14 | "Unmuted": "음소거 해제", 15 | "Playback Rate": "재생 비율", 16 | "Subtitles": "서브타이틀", 17 | "subtitles off": "서브타이틀 끄기", 18 | "Captions": "자막", 19 | "captions off": "자막 끄기", 20 | "Chapters": "챕터", 21 | "You aborted the video playback": "비디오 재생을 취소했습니다.", 22 | "A network error caused the video download to fail part-way.": "네트워크 오류로 인하여 비디오 일부를 다운로드하지 못 했습니다.", 23 | "The video could not be loaded, either because the server or network failed or because the format is not supported.": "비디오를 로드할 수 없습니다. 서버 혹은 네트워크 오류 때문이거나 지원되지 않는 형식 때문일 수 있습니다.", 24 | "The video playback was aborted due to a corruption problem or because the video used features your browser did not support.": "비디오 재생이 취소됐습니다. 비디오가 손상되었거나 비디오가 사용하는 기능을 브라우저에서 지원하지 않는 것 같습니다.", 25 | "No compatible source was found for this video.": "비디오에 호환되지 않는 소스가 있습니다." 26 | }); -------------------------------------------------------------------------------- /client/js/videojs/lang/nl.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("nl",{ 2 | "Play": "Afspelen", 3 | "Pause": "Pauze", 4 | "Current Time": "Huidige Tijd", 5 | "Duration Time": "Looptijd", 6 | "Remaining Time": "Resterende Tijd", 7 | "Stream Type": "Stream Type", 8 | "LIVE": "LIVE", 9 | "Loaded": "Geladen", 10 | "Progress": "Status", 11 | "Fullscreen": "Volledig scherm", 12 | "Non-Fullscreen": "Geen volledig scherm", 13 | "Mute": "Geluid Uit", 14 | "Unmuted": "Geluid Aan", 15 | "Playback Rate": "Weergave Rate", 16 | "Subtitles": "Ondertiteling", 17 | "subtitles off": "Ondertiteling uit", 18 | "Captions": "Onderschriften", 19 | "captions off": "Onderschriften uit", 20 | "Chapters": "Hoofdstukken", 21 | "You aborted the video playback": "Je hebt de video weergave afgebroken.", 22 | "A network error caused the video download to fail part-way.": "De video download is mislukt door een netwerkfout.", 23 | "The video could not be loaded, either because the server or network failed or because the format is not supported.": "De video kon niet worden geladen, veroorzaakt door een server of netwerkfout of het formaat word niet ondersteund.", 24 | "The video playback was aborted due to a corruption problem or because the video used features your browser did not support.": "De video weergave is afgebroken omdat deze beschadigd is of de video gebruikt functionaliteit die niet door je browser word ondersteund.", 25 | "No compatible source was found for this video.": "Voor deze video is geen ondersteunde bron gevonden." 26 | }); -------------------------------------------------------------------------------- /client/js/videojs/lang/pt-BR.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("pt-BR",{ 2 | "Play": "Tocar", 3 | "Pause": "Pause", 4 | "Current Time": "Tempo", 5 | "Duration Time": "Duração", 6 | "Remaining Time": "Tempo Restante", 7 | "Stream Type": "Tipo de Stream", 8 | "LIVE": "AO VIVO", 9 | "Loaded": "Carregado", 10 | "Progress": "Progressão", 11 | "Fullscreen": "Tela Cheia", 12 | "Non-Fullscreen": "Tela Normal", 13 | "Mute": "Mudo", 14 | "Unmuted": "Habilitar Som", 15 | "Playback Rate": "Velocidade", 16 | "Subtitles": "Legendas", 17 | "subtitles off": "Sem Legendas", 18 | "Captions": "Anotações", 19 | "captions off": "Sem Anotações", 20 | "Chapters": "Capítulos", 21 | "You aborted the video playback": "Você parou a execução de vídeo.", 22 | "A network error caused the video download to fail part-way.": "Um erro na rede fez o vídeo parar parcialmente.", 23 | "The video could not be loaded, either because the server or network failed or because the format is not supported.": "O vídeo não pode ser carregado, ou porque houve um problema com sua rede ou pelo formato do vídeo não ser suportado.", 24 | "The video playback was aborted due to a corruption problem or because the video used features your browser did not support.": "A Execução foi interrompida por um problema com o vídeo ou por seu navegador não dar suporte ao seu formato.", 25 | "No compatible source was found for this video.": "Não foi encontrada fonte de vídeo compatível." 26 | }); -------------------------------------------------------------------------------- /client/js/videojs/lang/ru.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("ru",{ 2 | "Play": "Воспроизвести", 3 | "Pause": "Приостановить", 4 | "Current Time": "Текущее время", 5 | "Duration Time": "Продолжительность", 6 | "Remaining Time": "Оставшееся время", 7 | "Stream Type": "Тип потока", 8 | "LIVE": "ОНЛАЙН", 9 | "Loaded": "Загрузка", 10 | "Progress": "Прогресс", 11 | "Fullscreen": "Полноэкранный режим", 12 | "Non-Fullscreen": "Неполноэкранный режим", 13 | "Mute": "Без звука", 14 | "Unmuted": "Со звуком", 15 | "Playback Rate": "Скорость воспроизведения", 16 | "Subtitles": "Субтитры", 17 | "subtitles off": "Субтитры выкл.", 18 | "Captions": "Подписи", 19 | "captions off": "Подписи выкл.", 20 | "Chapters": "Главы", 21 | "You aborted the video playback": "Вы прервали воспроизведение видео", 22 | "A network error caused the video download to fail part-way.": "Ошибка сети вызвала сбой во время загрузки видео.", 23 | "The video could not be loaded, either because the server or network failed or because the format is not supported.": "Невозможно загрузить видео из-за сетевого или серверного сбоя либо формат не поддерживается.", 24 | "The video playback was aborted due to a corruption problem or because the video used features your browser did not support.": "Воспроизведение видео было приостановлено из-за повреждения либо в связи с тем, что видео использует функции, неподдерживаемые вашим браузером.", 25 | "No compatible source was found for this video.": "Совместимые источники для этого видео отсутствуют." 26 | }); -------------------------------------------------------------------------------- /client/js/videojs/lang/uk.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("uk",{ 2 | "Play": "Відтворити", 3 | "Pause": "Призупинити", 4 | "Current Time": "Поточний час", 5 | "Duration Time": "Тривалість", 6 | "Remaining Time": "Час, що залишився", 7 | "Stream Type": "Тип потоку", 8 | "LIVE": "НАЖИВО", 9 | "Loaded": "Завантаження", 10 | "Progress": "Прогрес", 11 | "Fullscreen": "Повноекранний режим", 12 | "Non-Fullscreen": "Неповноекранний режим", 13 | "Mute": "Без звуку", 14 | "Unmuted": "Зі звуком", 15 | "Playback Rate": "Швидкість відтворення", 16 | "Subtitles": "Субтитри", 17 | "subtitles off": "Без субтитрів", 18 | "Captions": "Підписи", 19 | "captions off": "Без підписів", 20 | "Chapters": "Розділи", 21 | "You aborted the video playback": "Ви припинили відтворення відео", 22 | "A network error caused the video download to fail part-way.": "Помилка мережі викликала збій під час завантаження відео.", 23 | "The video could not be loaded, either because the server or network failed or because the format is not supported.": "Неможливо завантажити відео через мережевий чи серверний збій або формат не підтримується.", 24 | "The video playback was aborted due to a corruption problem or because the video used features your browser did not support.": "Відтворення відео було припинено через пошкодження або у зв'язку з тим, що відео використовує функції, які не підтримуються вашим браузером.", 25 | "No compatible source was found for this video.": "Сумісні джерела для цього відео відсутні." 26 | }); -------------------------------------------------------------------------------- /client/js/videojs/lang/zh.js: -------------------------------------------------------------------------------- 1 | videojs.addLanguage("zh",{ 2 | "Play": "播放", 3 | "Pause": "暂停", 4 | "Current Time": "当前时间", 5 | "Duration Time": "时长", 6 | "Remaining Time": "剩余时间", 7 | "Stream Type": "媒体流类型", 8 | "LIVE": "直播", 9 | "Loaded": "加载完毕", 10 | "Progress": "进度", 11 | "Fullscreen": "全屏", 12 | "Non-Fullscreen": "退出全屏", 13 | "Mute": "静音", 14 | "Unmuted": "取消静音", 15 | "Playback Rate": "播放码率", 16 | "Subtitles": "字幕", 17 | "subtitles off": "字幕关闭", 18 | "Captions": "内嵌字幕", 19 | "captions off": "内嵌字幕关闭", 20 | "Chapters": "节目段落", 21 | "You aborted the video playback": "视频播放被终止", 22 | "A network error caused the video download to fail part-way.": "网络错误导致视频下载中途失败。", 23 | "The video could not be loaded, either because the server or network failed or because the format is not supported.": "视频因格式不支持或者服务器或网络的问题无法加载。", 24 | "The video playback was aborted due to a corruption problem or because the video used features your browser did not support.": "由于视频文件损坏或是该视频使用了你的浏览器不支持的功能,播放终止。", 25 | "No compatible source was found for this video.": "无法找到此视频兼容的源。", 26 | "The video is encrypted and we do not have the keys to decrypt it.": "视频已加密,无法解密。" 27 | }); -------------------------------------------------------------------------------- /client/js/videojs/video-js.css: -------------------------------------------------------------------------------- 1 | /*! 2 | Video.js Default Styles (http://videojs.com) 3 | Version 4.11.2 4 | Create your own skin at http://designer.videojs.com 5 | */ 6 | /* SKIN 7 | ================================================================================ 8 | The main class name for all skin-specific styles. To make your own skin, 9 | replace all occurrences of 'vjs-default-skin' with a new name. Then add your new 10 | skin name to your video tag instead of the default skin. 11 | e.g.