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