initial commit
Este commit está contenido en:
13
README.md
Archivo normal
13
README.md
Archivo normal
@@ -0,0 +1,13 @@
|
|||||||
|
# p2p-media
|
||||||
|
|
||||||
|
### p2p multimedia software based on [p2p-media-loader](https://github.com/Novage/p2p-media-loader)
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
```
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
### MIT
|
||||||
23
docker-compose.yml
Archivo normal
23
docker-compose.yml
Archivo normal
@@ -0,0 +1,23 @@
|
|||||||
|
version: '2'
|
||||||
|
|
||||||
|
services:
|
||||||
|
p2p-media:
|
||||||
|
build: ./p2p-media
|
||||||
|
container_name: p2p-media
|
||||||
|
hostname: p2p-media
|
||||||
|
restart: always
|
||||||
|
entrypoint:
|
||||||
|
- /bin/bash
|
||||||
|
- /home/node/p2p-media/entrypoint.sh
|
||||||
|
volumes:
|
||||||
|
- ./p2p-media:/home/node/p2p-media
|
||||||
|
- ./p2p-media/config.json:/home/node/wt-tracker/config.json:ro
|
||||||
|
expose:
|
||||||
|
- 8080
|
||||||
|
- 9000
|
||||||
|
networks:
|
||||||
|
p2p-net:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
p2p-net:
|
||||||
|
|
||||||
8
p2p-media/Dockerfile
Archivo normal
8
p2p-media/Dockerfile
Archivo normal
@@ -0,0 +1,8 @@
|
|||||||
|
FROM node:10-slim
|
||||||
|
RUN apt update && apt -y upgrade && apt -y install git python build-essential && apt clean
|
||||||
|
USER node
|
||||||
|
WORKDIR /home/node
|
||||||
|
RUN git clone https://github.com/Novage/wt-tracker
|
||||||
|
WORKDIR /home/node/wt-tracker
|
||||||
|
RUN npm i
|
||||||
|
RUN npm run build
|
||||||
21
p2p-media/config.json
Archivo normal
21
p2p-media/config.json
Archivo normal
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"server": {
|
||||||
|
"port": 9000,
|
||||||
|
"host": "0.0.0.0"
|
||||||
|
},
|
||||||
|
"websockets": {
|
||||||
|
"path": "/*",
|
||||||
|
"maxPayloadLength": 65536,
|
||||||
|
"idleTimeout": 240,
|
||||||
|
"compression": 1,
|
||||||
|
"maxConnections": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tracker": {
|
||||||
|
"maxOffers": 20,
|
||||||
|
"announceInterval": 120
|
||||||
|
}
|
||||||
|
}
|
||||||
5
p2p-media/entrypoint.sh
Archivo normal
5
p2p-media/entrypoint.sh
Archivo normal
@@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
npm start config.json &
|
||||||
|
cd /home/node/p2p-media
|
||||||
|
npm i
|
||||||
|
node index.js
|
||||||
26
p2p-media/index.js
Archivo normal
26
p2p-media/index.js
Archivo normal
@@ -0,0 +1,26 @@
|
|||||||
|
const http = require('http'),
|
||||||
|
express = require('express'),
|
||||||
|
app = express(),
|
||||||
|
geoip = require('geoip-lite'),
|
||||||
|
morgan = require('morgan'),
|
||||||
|
compression = require('compression'),
|
||||||
|
rfs = require('rotating-file-stream'),
|
||||||
|
accessLogStream = rfs.createStream('access.log', {
|
||||||
|
interval: '1d',
|
||||||
|
path: __dirname + '/logs',
|
||||||
|
compress: 'gzip'
|
||||||
|
}),
|
||||||
|
server = http.createServer(app).listen(8080, () => {
|
||||||
|
console.log(`Listening on ${server.address().address}:${server.address().port}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.disable('x-powered-by')
|
||||||
|
.use(compression({ level: 9 }))
|
||||||
|
.use(morgan('combined', { stream: accessLogStream }))
|
||||||
|
.use(express.json())
|
||||||
|
.post('/ip', (req, res) => {
|
||||||
|
if (req.body.ip.match(/((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))/)) {
|
||||||
|
res.json(geoip.lookup(req.body.ip))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.use(express.static(__dirname + '/public'))
|
||||||
28
p2p-media/package.json
Archivo normal
28
p2p-media/package.json
Archivo normal
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"name": "p2p-media",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"install": "browserify -r p2p-graph | terser -m -c > public/js/p2p-graph.js && cp node_modules/clappr/dist/clappr.min.js node_modules/hls.js/dist/hls.min.js node_modules/level-selector/dist/level-selector.min.js node_modules/p2p-media-loader-hlsjs/build/p2p-media-loader-hlsjs.min.js node_modules/p2p-media-loader-core/build/p2p-media-loader-core.min.js public/js/ && cp node_modules/ipfs/dist/index.min.js public/js/ipfs.min.js && cp node_modules/hlsjs-ipfs-loader/dist/index.min.js public/js/hlsjs-ipfs-loader.min.js"
|
||||||
|
},
|
||||||
|
"author": "ale",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"browserify": "*",
|
||||||
|
"clappr": "*",
|
||||||
|
"compression": "*",
|
||||||
|
"express": "*",
|
||||||
|
"geoip-lite": "*",
|
||||||
|
"hls.js": "*",
|
||||||
|
"hlsjs-ipfs-loader": "*",
|
||||||
|
"ipfs": "*",
|
||||||
|
"level-selector": "github:clappr/clappr-level-selector-plugin",
|
||||||
|
"morgan": "*",
|
||||||
|
"p2p-graph": "*",
|
||||||
|
"p2p-media-loader-core": "*",
|
||||||
|
"p2p-media-loader-hlsjs": "*",
|
||||||
|
"rotating-file-stream": "*",
|
||||||
|
"terser": "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
195
p2p-media/public/css/main.css
Archivo normal
195
p2p-media/public/css/main.css
Archivo normal
@@ -0,0 +1,195 @@
|
|||||||
|
body {
|
||||||
|
font: normal 400 16px 'Roboto', sans-serif;
|
||||||
|
color: #333333;
|
||||||
|
margin-top: 1em;
|
||||||
|
margin-bottom: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-header .title {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
padding: 0 15px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 576px) {
|
||||||
|
.container {
|
||||||
|
max-width: 540px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.container {
|
||||||
|
max-width: 720px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
.container {
|
||||||
|
max-width: 960px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
.container {
|
||||||
|
max-width: 1140px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#main-view *,
|
||||||
|
::after,
|
||||||
|
::before {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main-view .wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main-view .wrapper .column-1,
|
||||||
|
#main-view .wrapper .column-2 {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
#main-view .wrapper .column-1 {
|
||||||
|
flex: 0 0 66.666667%;
|
||||||
|
max-width: 66.666667%;
|
||||||
|
padding-right: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main-view .wrapper .column-2 {
|
||||||
|
flex: 0 0 33.333333%;
|
||||||
|
max-width: 33.333333%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#video {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.embed-responsive {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main-view .form-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main-view .form-group label {
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main-view .form-control {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: calc(2.25em + 2px);
|
||||||
|
padding: .375em .75em;
|
||||||
|
font-size: 1em;
|
||||||
|
line-height: 1.5em;
|
||||||
|
color: #495057;
|
||||||
|
background-color: #fff;
|
||||||
|
background-clip: padding-box;
|
||||||
|
border: 1px solid #ced4da;
|
||||||
|
border-radius: .25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main-view .form-button {
|
||||||
|
text-align: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
padding: .375em .75em;
|
||||||
|
font-size: 1em;
|
||||||
|
line-height: 1.5em;
|
||||||
|
border-radius: .25em;
|
||||||
|
color: #fff;
|
||||||
|
background-color: #972e2d;
|
||||||
|
border-color: #972e2d;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main-view .form-button:hover {
|
||||||
|
background-color: #c60000;
|
||||||
|
border-color: #c60000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main-view .form-button:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main-view .embed-responsive::before {
|
||||||
|
display: block;
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#main-view .embed-responsive .embed-responsive-item {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main-view .embed-responsive-16by9::before {
|
||||||
|
padding-top: 56.25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main-view #level {
|
||||||
|
margin-top: 2px;
|
||||||
|
width: auto;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
#graph {
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
margin: 3em auto;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chart_container {
|
||||||
|
position: relative;
|
||||||
|
margin: 3em auto;
|
||||||
|
padding-left: 20px;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#y_axis {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 40px;
|
||||||
|
left: -20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#y_axis>svg {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
#legend {
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
left: 40px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#legend-totals {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 20px;
|
||||||
|
left: 40px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main-view .hide {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
62
p2p-media/public/index.html
Archivo normal
62
p2p-media/public/index.html
Archivo normal
@@ -0,0 +1,62 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="es">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>P2P</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link type="text/css" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/rickshaw/1.6.3/rickshaw.min.css">
|
||||||
|
<link type="text/css" rel="stylesheet" href="css/main.css">
|
||||||
|
<script src="js/p2p-media-loader-core.min.js"></script>
|
||||||
|
<script src="js/p2p-media-loader-hlsjs.min.js"></script>
|
||||||
|
<script src="js/ipfs.min.js"></script>
|
||||||
|
<script src="js/hlsjs-ipfs-loader.min.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
|
||||||
|
<script async src="https://cdnjs.cloudflare.com/ajax/libs/rickshaw/1.6.3/rickshaw.min.js"></script>
|
||||||
|
<script src="js/p2p-graph.js"></script>
|
||||||
|
<script src="js/main.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<header class="main-header">
|
||||||
|
<h2 class="title">P2P - HatThieves</h2>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div id="main-view">
|
||||||
|
<div id="error-webrtc-data-channels" class="hide">
|
||||||
|
WebRTC Data Channels API is not supported by your browser. P2P disabled.<br>
|
||||||
|
Read more at <a href="http://iswebrtcreadyyet.com/legacy.html" target="_blank">Is WebRTC ready yet?</a>.
|
||||||
|
<hr>
|
||||||
|
</div>
|
||||||
|
<div id="error-hls-js" class="hide">
|
||||||
|
Your browser doesn't support hls.js engine. P2P disabled.<br>
|
||||||
|
Read more at <a href="https://en.wikipedia.org/wiki/Media_Source_Extensions" target="_blank">Media
|
||||||
|
Source Extensions</a>.
|
||||||
|
<hr>
|
||||||
|
</div>
|
||||||
|
<div id="error-shakaplayer" class="hide">
|
||||||
|
Your browser doesn't support Shaka Player engine. P2P disabled.<br>
|
||||||
|
Read more at <a href="https://en.wikipedia.org/wiki/Media_Source_Extensions" target="_blank">Media
|
||||||
|
Source Extensions</a>.
|
||||||
|
<hr>
|
||||||
|
</div>
|
||||||
|
<div class="wrapper">
|
||||||
|
<div class="column-1">
|
||||||
|
<div id="video_container"></div>
|
||||||
|
<div id="chart_container">
|
||||||
|
<div id="legend"></div>
|
||||||
|
<div id="legend-totals"></div>
|
||||||
|
<div id="y_axis"></div>
|
||||||
|
<div id="chart"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column-2">
|
||||||
|
<div id="graph"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
428
p2p-media/public/js/main.js
Archivo normal
428
p2p-media/public/js/main.js
Archivo normal
@@ -0,0 +1,428 @@
|
|||||||
|
function waitForGlobalObject(objectName, objectNextName) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
function check() {
|
||||||
|
if ((window[objectName] !== undefined)
|
||||||
|
&& ((objectNextName === undefined) || window[objectName][objectNextName] !== undefined)) {
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
setTimeout(check, 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
check();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitForModule(moduleName) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
function check() {
|
||||||
|
try {
|
||||||
|
resolve(require(moduleName));
|
||||||
|
} catch (e) {
|
||||||
|
setTimeout(check, 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
check();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadScript(src) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.type = 'text/javascript';
|
||||||
|
script.onload = () => {
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
script.onerror = () => {
|
||||||
|
console.log("Failed to load script", src);
|
||||||
|
reject();
|
||||||
|
};
|
||||||
|
script.src = src;
|
||||||
|
document.head.appendChild(script);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class App {
|
||||||
|
async init() {
|
||||||
|
await waitForGlobalObject("p2pml", "core", "Ipfs", "HlsjsIpfsLoader", "Hls");
|
||||||
|
|
||||||
|
this.isP2PSupported = p2pml.core.HybridLoader.isSupported();
|
||||||
|
if (!this.isP2PSupported) {
|
||||||
|
document.querySelector("#error-webrtc-data-channels").classList.remove("hide");
|
||||||
|
}
|
||||||
|
|
||||||
|
var params = (new URL(document.location)).searchParams;
|
||||||
|
|
||||||
|
this.videoUrl = params.get('url') ? params.get('url') : 'https://hls.hatthieves.es/hls/streaming.m3u8';
|
||||||
|
this.videoContainer = document.getElementById("video_container");
|
||||||
|
|
||||||
|
this.loadSpeedTimespan = 10; // seconds
|
||||||
|
|
||||||
|
const P2PGraphClass = await waitForModule("p2p-graph");
|
||||||
|
this.graph = new P2PGraphClass("#graph");
|
||||||
|
this.graph.add({ id: "me", name: "Tú", me: true });
|
||||||
|
|
||||||
|
await waitForGlobalObject("Rickshaw");
|
||||||
|
|
||||||
|
this.initChart();
|
||||||
|
|
||||||
|
this.restartApp();
|
||||||
|
}
|
||||||
|
|
||||||
|
async restartApp() {
|
||||||
|
this.downloadStats = [];
|
||||||
|
this.downloadTotals = { http: 0, p2p: 0 };
|
||||||
|
this.uploadStats = [];
|
||||||
|
this.uploadTotal = 0;
|
||||||
|
|
||||||
|
while (this.videoContainer.hasChildNodes()) {
|
||||||
|
this.videoContainer.removeChild(this.videoContainer.lastChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
loader: {
|
||||||
|
trackerAnnounce: 'wss://p2p.hatthieves.es/ws'
|
||||||
|
},
|
||||||
|
rtcConfig: {
|
||||||
|
iceServers: [
|
||||||
|
{ urls: "stuns:hatthieves.es:3479" },
|
||||||
|
{ urls: "stun:hatthieves.es:3478" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await loadScript("js/hls.min.js");
|
||||||
|
if (!Hls.isSupported()) {
|
||||||
|
document.querySelector("#error-hls-js").classList.remove("hide");
|
||||||
|
}
|
||||||
|
this.engine = this.isP2PSupported ? new p2pml.hlsjs.Engine(config) : undefined;
|
||||||
|
|
||||||
|
this.initClapprPlayer();
|
||||||
|
|
||||||
|
if (this.isP2PSupported) {
|
||||||
|
this.engine.on(p2pml.core.Events.PieceBytesDownloaded, this.onBytesDownloaded.bind(this));
|
||||||
|
this.engine.on(p2pml.core.Events.PieceBytesUploaded, this.onBytesUploaded.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.refreshChart();
|
||||||
|
this.refreshGraph();
|
||||||
|
}
|
||||||
|
|
||||||
|
async initClapprPlayer() {
|
||||||
|
const scriptsPromise = (async () => {
|
||||||
|
await loadScript("js/clappr.min.js");
|
||||||
|
await loadScript("js/level-selector.min.js");
|
||||||
|
})();
|
||||||
|
|
||||||
|
var outer = document.createElement("div");
|
||||||
|
outer.className = "embed-responsive embed-responsive-16by9";
|
||||||
|
var video = document.createElement("div");
|
||||||
|
video.id = "video";
|
||||||
|
video.className = "embed-responsive-item";
|
||||||
|
outer.appendChild(video);
|
||||||
|
this.videoContainer.appendChild(outer);
|
||||||
|
|
||||||
|
const params = (new URL(document.location)).searchParams
|
||||||
|
|
||||||
|
const setup = {
|
||||||
|
parentId: "#video",
|
||||||
|
plugins: [],
|
||||||
|
source: params.get('key') && params.get('key').length > 0 ? params.get('key') : this.videoUrl,
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
muted: false,
|
||||||
|
autoPlay: true,
|
||||||
|
poster: 'https://www.hatthieves.es/wp-content/uploads/2020/04/cropped-blackground3-2.jpg',
|
||||||
|
playbackNotSupportedMessage: 'Play is not supported',
|
||||||
|
watermark: 'https://www.hatthieves.es/wp-content/uploads/2019/08/cropped-ht.png', position: 'top-left',
|
||||||
|
watermarkLink: 'https://www.hatthieves.es',
|
||||||
|
mediacontrol: { seekbar: "#ffffff", buttons: "#ffffff" },
|
||||||
|
playback: { playInline: true, preload: true, maxBufferLength: 30 }
|
||||||
|
};
|
||||||
|
|
||||||
|
if (params.get('key') && params.get('key').length > 0) {
|
||||||
|
const repoPath = 'ipfs-' + Math.random(),
|
||||||
|
node = await Ipfs.create({
|
||||||
|
repo: repoPath
|
||||||
|
}),
|
||||||
|
ipfsHash = params.get('hash') ? params.get('hash') : '',
|
||||||
|
key = params.get('key') ? params.get('key') : ''
|
||||||
|
setup.playback.hlsjsConfig = {
|
||||||
|
debug: false,
|
||||||
|
loader: HlsjsIpfsLoader,
|
||||||
|
ipfs: node,
|
||||||
|
ipfsHash: ipfsHash
|
||||||
|
};
|
||||||
|
document.getElementById('chart_container').style.display = 'none'
|
||||||
|
document.getElementById('graph').parentNode.style.display = 'none'
|
||||||
|
document.getElementById('video_container').parentNode.style.flex = '0 0 100%'
|
||||||
|
document.getElementById('video_container').parentNode.style['max-width'] = '100%'
|
||||||
|
} else {
|
||||||
|
setup.playback.hlsjsConfig = {
|
||||||
|
// liveSyncDurationCount: 7,
|
||||||
|
loader: this.isP2PSupported ? this.engine.createLoaderClass() : Hls.DefaultConfig.loader
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await scriptsPromise;
|
||||||
|
|
||||||
|
setup.plugins.push(LevelSelector);
|
||||||
|
|
||||||
|
var player = new Clappr.Player(setup);
|
||||||
|
|
||||||
|
if (this.isP2PSupported) {
|
||||||
|
p2pml.hlsjs.initClapprPlayer(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initChart() {
|
||||||
|
var chartConf = {
|
||||||
|
element: document.querySelector("#chart"),
|
||||||
|
renderer: 'multi',
|
||||||
|
interpolation: "basis",
|
||||||
|
stack: false,
|
||||||
|
min: 'auto',
|
||||||
|
strokeWidth: 1,
|
||||||
|
series: [
|
||||||
|
{ name: "Upload P2P", color: "#88eab9", data: [], renderer: 'area' },
|
||||||
|
{ name: " - P2P", color: "#88b9ea", data: [], renderer: 'area' },
|
||||||
|
{ name: " - HTTP", color: "#eae288", data: [], renderer: 'area' },
|
||||||
|
{ name: "Download", color: "#f64", data: [], renderer: 'line' }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
this.chart = new Rickshaw.Graph(chartConf);
|
||||||
|
|
||||||
|
new Rickshaw.Graph.Axis.X({
|
||||||
|
graph: this.chart,
|
||||||
|
tickFormat: () => ''
|
||||||
|
});
|
||||||
|
|
||||||
|
new Rickshaw.Graph.Axis.Y({
|
||||||
|
graph: this.chart,
|
||||||
|
orientation: 'left',
|
||||||
|
element: document.getElementById('y_axis')
|
||||||
|
});
|
||||||
|
|
||||||
|
this.legend = new Rickshaw.Graph.Legend({
|
||||||
|
graph: this.chart,
|
||||||
|
element: document.getElementById('legend')
|
||||||
|
});
|
||||||
|
|
||||||
|
this.legendTotals = new Rickshaw.Graph.Legend({
|
||||||
|
graph: this.chart,
|
||||||
|
element: document.getElementById("legend-totals")
|
||||||
|
});
|
||||||
|
|
||||||
|
this.chart.render();
|
||||||
|
setInterval(this.updateChartData.bind(this), 1000);
|
||||||
|
|
||||||
|
var chartResize = () => {
|
||||||
|
chartConf.width = this.chart.element.clientWidth;
|
||||||
|
this.chart.configure(chartConf);
|
||||||
|
this.chart.render();
|
||||||
|
};
|
||||||
|
|
||||||
|
chartResize();
|
||||||
|
window.addEventListener("resize", chartResize);
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshChart() {
|
||||||
|
if (!this.chart) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var data0 = this.chart.series[0].data;
|
||||||
|
var data1 = this.chart.series[1].data;
|
||||||
|
var data2 = this.chart.series[2].data;
|
||||||
|
var data3 = this.chart.series[3].data;
|
||||||
|
var lastX = data0.length > 0 ? data0[data0.length - 1].x : -1;
|
||||||
|
|
||||||
|
var seriesDataMapper = (currentValue, index) => ({ x: index + lastX + 1, y: 0 });
|
||||||
|
|
||||||
|
data0.length = 0;
|
||||||
|
data1.length = 0;
|
||||||
|
data2.length = 0;
|
||||||
|
data3.length = 0;
|
||||||
|
|
||||||
|
var stubData = Array.apply(null, Array(200)).map(seriesDataMapper);
|
||||||
|
data0.push.apply(data0, stubData.slice(0));
|
||||||
|
data1.push.apply(data1, stubData.slice(0));
|
||||||
|
data2.push.apply(data2, stubData.slice(0));
|
||||||
|
data3.push.apply(data3, stubData.slice(0));
|
||||||
|
|
||||||
|
this.chart.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateChartData() {
|
||||||
|
var downloadSpeed = this.getDownloadSpeed();
|
||||||
|
var http = Number((downloadSpeed.http * 8 / 1000000).toFixed(2));
|
||||||
|
var p2p = Number((downloadSpeed.p2p * 8 / 1000000).toFixed(2));
|
||||||
|
var total = Number((http + p2p).toFixed(2));
|
||||||
|
var upload = Number(this.getUploadSpeed() * 8 / 1000000).toFixed(2);
|
||||||
|
|
||||||
|
var data0 = this.chart.series[0].data;
|
||||||
|
var data1 = this.chart.series[1].data;
|
||||||
|
var data2 = this.chart.series[2].data;
|
||||||
|
var data3 = this.chart.series[3].data;
|
||||||
|
var x = data0.length > 0 ? data0[data0.length - 1].x + 1 : 0;
|
||||||
|
|
||||||
|
data0.shift();
|
||||||
|
data1.shift();
|
||||||
|
data2.shift();
|
||||||
|
data3.shift();
|
||||||
|
data0.push({ x: x, y: -upload });
|
||||||
|
data1.push({ x: x, y: total });
|
||||||
|
data2.push({ x: x, y: http });
|
||||||
|
data3.push({ x: x, y: total });
|
||||||
|
this.chart.update();
|
||||||
|
|
||||||
|
this.formatChartLegendLine(0, total);
|
||||||
|
this.formatChartLegendLine(1, http);
|
||||||
|
this.formatChartLegendLine(2, p2p);
|
||||||
|
this.formatChartLegendLine(3, upload);
|
||||||
|
|
||||||
|
this.updateLegendTotals();
|
||||||
|
}
|
||||||
|
|
||||||
|
formatChartLegendLine(index, speed) {
|
||||||
|
if (this.legend) {
|
||||||
|
var line = this.legend.lines[index];
|
||||||
|
line.element.childNodes[1].textContent = line.series.name + ' - ' + speed + ' Mbit/s';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateLegendTotals() {
|
||||||
|
if (!this.legendTotals) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var httpMb = this.downloadTotals.http / 1048576;
|
||||||
|
var p2pMb = this.downloadTotals.p2p / 1048576;
|
||||||
|
var totalMb = httpMb + p2pMb;
|
||||||
|
var uploadMb = this.uploadTotal / 1048576;
|
||||||
|
|
||||||
|
if (totalMb != 0) {
|
||||||
|
this.legendTotals.lines[0].element.childNodes[1].textContent
|
||||||
|
= "Download - "
|
||||||
|
+ Number(totalMb).toFixed(1) + " MiB";
|
||||||
|
|
||||||
|
this.legendTotals.lines[1].element.childNodes[1].textContent
|
||||||
|
= " - HTTP - "
|
||||||
|
+ Number(httpMb).toFixed(1) + " MiB - "
|
||||||
|
+ Number((httpMb * 100) / totalMb).toFixed(0) + "%";
|
||||||
|
|
||||||
|
this.legendTotals.lines[2].element.childNodes[1].textContent
|
||||||
|
= " - P2P - "
|
||||||
|
+ Number(p2pMb).toFixed(1) + " MiB - "
|
||||||
|
+ Number((p2pMb * 100) / totalMb).toFixed(0) + "%";
|
||||||
|
|
||||||
|
this.legendTotals.lines[3].element.childNodes[1].textContent
|
||||||
|
= "Upload P2P - "
|
||||||
|
+ Number(uploadMb).toFixed(1) + " MiB";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getDownloadSpeed() {
|
||||||
|
var startingPoint = performance.now() - (this.loadSpeedTimespan * 1000);
|
||||||
|
var httpSize = 0;
|
||||||
|
var p2pSize = 0;
|
||||||
|
|
||||||
|
var i = this.downloadStats.length;
|
||||||
|
while (i--) {
|
||||||
|
var stat = this.downloadStats[i];
|
||||||
|
if (stat.timestamp < startingPoint) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stat.method === "p2p") {
|
||||||
|
p2pSize += stat.size;
|
||||||
|
} else if (stat.method === "http") {
|
||||||
|
httpSize += stat.size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.downloadStats.splice(0, i + 1);
|
||||||
|
|
||||||
|
return { p2p: p2pSize / this.loadSpeedTimespan, http: httpSize / this.loadSpeedTimespan };
|
||||||
|
}
|
||||||
|
|
||||||
|
getUploadSpeed() {
|
||||||
|
var startingPoint = performance.now() - (this.loadSpeedTimespan * 1000);
|
||||||
|
var size = 0;
|
||||||
|
|
||||||
|
var i = this.uploadStats.length;
|
||||||
|
while (i--) {
|
||||||
|
var stat = this.uploadStats[i];
|
||||||
|
if (stat.timestamp < startingPoint) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
size += stat.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.uploadStats.splice(0, i + 1);
|
||||||
|
|
||||||
|
return size / this.loadSpeedTimespan;
|
||||||
|
}
|
||||||
|
|
||||||
|
onBytesDownloaded(method, size) {
|
||||||
|
this.downloadStats.push({ method: method, size: size, timestamp: performance.now() });
|
||||||
|
this.downloadTotals[method] += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
onBytesUploaded(method, size) {
|
||||||
|
this.uploadStats.push({ size: size, timestamp: performance.now() });
|
||||||
|
this.uploadTotal += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshGraph() {
|
||||||
|
if (!this.graph) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nodes = this.graph.list();
|
||||||
|
for (var i = 0; i < nodes.length; i++) {
|
||||||
|
if (nodes[i].id !== "me") {
|
||||||
|
this.graph.disconnect("me", nodes[i].id);
|
||||||
|
this.graph.remove(nodes[i].id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isP2PSupported) {
|
||||||
|
this.engine.on(p2pml.core.Events.PeerConnect, this.onPeerConnect.bind(this));
|
||||||
|
this.engine.on(p2pml.core.Events.PeerClose, this.onPeerClose.bind(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onPeerConnect(peer) {
|
||||||
|
if (!this.graph.hasPeer(peer.id)) {
|
||||||
|
var oReq = new XMLHttpRequest(),
|
||||||
|
graph = this.graph
|
||||||
|
oReq.onreadystatechange = function () {
|
||||||
|
if (oReq.readyState != 4) { return; }
|
||||||
|
|
||||||
|
var location = this.response
|
||||||
|
graph.add({ id: peer.id, name: location.city + ' - ' + location.region + ' (' + location.country + ')' || 'Unknown' });
|
||||||
|
graph.connect("me", peer.id);
|
||||||
|
}
|
||||||
|
oReq.open('POST', 'ip', true);
|
||||||
|
oReq.responseType = 'json';
|
||||||
|
oReq.setRequestHeader('Content-Type', 'application/json');
|
||||||
|
oReq.send(JSON.stringify({ ip: peer.remoteAddress }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onPeerClose(id) {
|
||||||
|
if (this.graph.hasPeer(id)) {
|
||||||
|
this.graph.disconnect("me", id);
|
||||||
|
this.graph.remove(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onload = function () {
|
||||||
|
window.app = new App();
|
||||||
|
window.app.init();
|
||||||
|
}
|
||||||
Referencia en una nueva incidencia
Block a user