First functional web interface version.
- Simplify by now all html and javascript. - Added a simple interface using go templates and plain javascript. - Improved REST interface for the use case. - Changed some properties into the config file to make it suitable for future use cases. Signed-off-by: Antonio Navarro Perez <antnavper@gmail.com>
This commit is contained in:
parent
80ed4e9e1e
commit
ecd524ed3c
34 changed files with 585 additions and 7383 deletions
5
torrent/.gitignore
vendored
5
torrent/.gitignore
vendored
|
@ -1,5 +1,2 @@
|
||||||
_DATA
|
_DATA
|
||||||
assets_vfsdata.go
|
assets_vfsdata.go
|
||||||
assets
|
|
||||||
front/.cache
|
|
||||||
**/node_modules/
|
|
|
@ -8,3 +8,4 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var Assets http.FileSystem = http.Dir("assets")
|
var Assets http.FileSystem = http.Dir("assets")
|
||||||
|
var Templates http.FileSystem = http.Dir("templates")
|
||||||
|
|
7
torrent/assets/css/bootstrap.min.css
vendored
Normal file
7
torrent/assets/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
torrent/assets/css/bootstrap.min.css.map
Normal file
1
torrent/assets/css/bootstrap.min.css.map
Normal file
File diff suppressed because one or more lines are too long
7
torrent/assets/js/Chart.bundle.min.js
vendored
Normal file
7
torrent/assets/js/Chart.bundle.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7
torrent/assets/js/bootstrap.bundle.min.js
vendored
Normal file
7
torrent/assets/js/bootstrap.bundle.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
torrent/assets/js/bootstrap.bundle.min.js.map
Normal file
1
torrent/assets/js/bootstrap.bundle.min.js.map
Normal file
File diff suppressed because one or more lines are too long
39
torrent/assets/js/file_chunks.js
Normal file
39
torrent/assets/js/file_chunks.js
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
function FileChunks() {
|
||||||
|
this.update = function (chunks, totalPieces, hash) {
|
||||||
|
var dom = document.getElementById("file-chunks-" + hash);
|
||||||
|
dom.innerHTML = "";
|
||||||
|
chunks.forEach(chunk => {
|
||||||
|
dom.appendChild(getPrintedChunk(chunk.status, chunk.numPieces, totalPieces));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var pieceStatus = {
|
||||||
|
"H": { class: "bg-warning", tooltip: "checking pieces" },
|
||||||
|
"P": { class: "bg-info", tooltip: "" },
|
||||||
|
"C": { class: "bg-success", tooltip: "downloaded pieces" },
|
||||||
|
"W": { class: "bg-transparent" },
|
||||||
|
"?": { class: "bg-danger", tooltip: "erroed pieces" },
|
||||||
|
};
|
||||||
|
|
||||||
|
var getPrintedChunk = function (status, pieces, totalPieces) {
|
||||||
|
var percentage = totalPieces * pieces / 100;
|
||||||
|
var pcMeta = pieceStatus[status]
|
||||||
|
var pieceStatusClass = pcMeta.class;
|
||||||
|
var pieceStatusTip = pcMeta.tooltip;
|
||||||
|
|
||||||
|
var div = document.createElement("div");
|
||||||
|
div.className = "progress-bar " + pieceStatusClass;
|
||||||
|
div.setAttribute("role", "progressbar");
|
||||||
|
|
||||||
|
if (pieceStatusTip) {
|
||||||
|
div.setAttribute("data-toggle", "tooltip");
|
||||||
|
div.setAttribute("data-placement", "top");
|
||||||
|
div.setAttribute("title", pieceStatusTip);
|
||||||
|
}
|
||||||
|
|
||||||
|
div.style.cssText = "width: " + percentage + "%";
|
||||||
|
|
||||||
|
return div;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
35
torrent/assets/js/general.js
Normal file
35
torrent/assets/js/general.js
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
GeneralChart.init();
|
||||||
|
var cacheChart = new SingleBarChart("chart-cache", "Cache disk");
|
||||||
|
var workerChart = new SingleBarChart("chart-workers", "Workers");
|
||||||
|
|
||||||
|
fetchData();
|
||||||
|
setInterval(function () {
|
||||||
|
fetchData();
|
||||||
|
}, 2000)
|
||||||
|
|
||||||
|
function fetchData() {
|
||||||
|
fetch('/api/status')
|
||||||
|
.then(function (response) {
|
||||||
|
if (response.ok) {
|
||||||
|
return response.json();
|
||||||
|
} else {
|
||||||
|
console.log('Error getting data from server. Response: ' + response.status);
|
||||||
|
}
|
||||||
|
}).then(function (stats) {
|
||||||
|
var download = stats.torrentStats.downloadedBytes / stats.torrentStats.timePassed;
|
||||||
|
var upload = stats.torrentStats.uploadedBytes / stats.torrentStats.timePassed;
|
||||||
|
|
||||||
|
GeneralChart.update(download, upload);
|
||||||
|
|
||||||
|
cacheChart.update(stats.cacheFilled, stats.cacheCapacity - stats.cacheFilled);
|
||||||
|
workerChart.update(0, stats.poolFree - stats.poolCap);
|
||||||
|
document.getElementById("down-speed-text").innerText =
|
||||||
|
Humanize.bytes(download, 1024) + "/s";
|
||||||
|
|
||||||
|
document.getElementById("up-speed-text").innerText =
|
||||||
|
Humanize.bytes(upload, 1024) + " /s";
|
||||||
|
})
|
||||||
|
.catch(function (error) {
|
||||||
|
console.log('Error getting status info: ' + error.message);
|
||||||
|
});
|
||||||
|
}
|
78
torrent/assets/js/general_chart.js
Normal file
78
torrent/assets/js/general_chart.js
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
var GeneralChart = {
|
||||||
|
_downloadData: [],
|
||||||
|
_uploadData: [],
|
||||||
|
_chart: null,
|
||||||
|
update: function (download, upload) {
|
||||||
|
if (this._downloadData.length > 20) {
|
||||||
|
this._uploadData.shift();
|
||||||
|
this._downloadData.shift();
|
||||||
|
}
|
||||||
|
var date = new Date();
|
||||||
|
this._downloadData.push({
|
||||||
|
x: date,
|
||||||
|
y: download,
|
||||||
|
});
|
||||||
|
this._uploadData.push({
|
||||||
|
x: date,
|
||||||
|
y: upload,
|
||||||
|
});
|
||||||
|
this._chart.update();
|
||||||
|
},
|
||||||
|
init: function () {
|
||||||
|
var ctx = document.getElementById('chart-general-network').getContext('2d');
|
||||||
|
this._chart = new Chart(ctx, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: 'Download Speed',
|
||||||
|
fill: false,
|
||||||
|
backgroundColor: 'rgb(255, 99, 132)',
|
||||||
|
borderColor: 'rgb(255, 99, 132)',
|
||||||
|
borderWidth: 1,
|
||||||
|
data: this._downloadData,
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Upload Speed',
|
||||||
|
fill: false,
|
||||||
|
borderWidth: 1,
|
||||||
|
data: this._uploadData,
|
||||||
|
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
title: {
|
||||||
|
text: 'Download and Upload speed'
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
xAxes: [{
|
||||||
|
scaleLabel: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
gridLines: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
type: 'time',
|
||||||
|
}],
|
||||||
|
yAxes: [{
|
||||||
|
scaleLabel: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
type: 'linear',
|
||||||
|
ticks: {
|
||||||
|
userCallback: function (tick) {
|
||||||
|
return Humanize.bytes(tick, 1024) + "/s";
|
||||||
|
},
|
||||||
|
beginAtZero: true
|
||||||
|
},
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ function logn(n, b) {
|
||||||
return Math.log(n) / Math.log(b);
|
return Math.log(n) / Math.log(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
var humanize = {
|
var Humanize = {
|
||||||
bytes: function (s, base) {
|
bytes: function (s, base) {
|
||||||
if (s < 10) {
|
if (s < 10) {
|
||||||
return s.toFixed(0) + " B";
|
return s.toFixed(0) + " B";
|
||||||
|
@ -18,9 +18,7 @@ var humanize = {
|
||||||
if (val < 10) {
|
if (val < 10) {
|
||||||
f = val.toFixed(1);
|
f = val.toFixed(1);
|
||||||
}
|
}
|
||||||
console.log("OUT", f + suffix);
|
|
||||||
return f + suffix;
|
return f + suffix;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default humanize;
|
|
32
torrent/assets/js/routes.js
Normal file
32
torrent/assets/js/routes.js
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
var fileChunks = new FileChunks();
|
||||||
|
|
||||||
|
fetchData();
|
||||||
|
setInterval(function () {
|
||||||
|
fetchData();
|
||||||
|
}, 2000)
|
||||||
|
|
||||||
|
function fetchData() {
|
||||||
|
fetch('/api/routes')
|
||||||
|
.then(function (response) {
|
||||||
|
if (response.ok) {
|
||||||
|
return response.json();
|
||||||
|
} else {
|
||||||
|
console.log('Error getting data from server. Response: ' + response.status);
|
||||||
|
}
|
||||||
|
}).then(function (routes) {
|
||||||
|
routes.forEach(route => {
|
||||||
|
route.torrentStats.forEach(torrentStat => {
|
||||||
|
fileChunks.update(torrentStat.pieceChunks, torrentStat.totalPieces, torrentStat.hash);
|
||||||
|
|
||||||
|
var download = torrentStat.downloadedBytes / torrentStat.timePassed;
|
||||||
|
var upload = torrentStat.uploadedBytes / torrentStat.timePassed;
|
||||||
|
document.getElementById("up-down-speed-text-" + torrentStat.hash).innerText =
|
||||||
|
Humanize.bytes(download, 1024) + "/s down, " + Humanize.bytes(upload, 1024) + "/s up";
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(function (error) {
|
||||||
|
console.log('Error getting status info: ' + error.message);
|
||||||
|
});
|
||||||
|
}
|
48
torrent/assets/js/single_bar_chart.js
Normal file
48
torrent/assets/js/single_bar_chart.js
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
function SingleBarChart(id, name) {
|
||||||
|
var ctx = document.getElementById(id).getContext('2d');
|
||||||
|
this._used = [];
|
||||||
|
this._free = [];
|
||||||
|
this._chart = new Chart(ctx, {
|
||||||
|
type: 'bar',
|
||||||
|
data: {
|
||||||
|
labels:[name],
|
||||||
|
datasets: [{
|
||||||
|
backgroundColor: "gray",
|
||||||
|
label: "used",
|
||||||
|
data: this._used,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backgroundColor: "green",
|
||||||
|
label: "free",
|
||||||
|
data: this._free,
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
legend: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
animation: false,
|
||||||
|
scales: {
|
||||||
|
xAxes: [{
|
||||||
|
stacked: true
|
||||||
|
}],
|
||||||
|
yAxes: [{
|
||||||
|
stacked: true,
|
||||||
|
display: true,
|
||||||
|
ticks: {
|
||||||
|
beginAtZero: true,
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
this.update = function (used, free) {
|
||||||
|
this._used.shift();
|
||||||
|
this._free.shift();
|
||||||
|
this._used.push(used);
|
||||||
|
this._free.push(free);
|
||||||
|
|
||||||
|
this._chart.update();
|
||||||
|
};
|
||||||
|
}
|
|
@ -18,4 +18,13 @@ func main() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err := vfsgen.Generate(distribyted.Assets, vfsgen.Options{
|
||||||
|
BuildTags: "release",
|
||||||
|
VariableName: "Templates",
|
||||||
|
PackageName: "distribyted",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,8 @@ type binaryFileSystem struct {
|
||||||
http.FileSystem
|
http.FileSystem
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBinaryFileSystem() *binaryFileSystem {
|
func NewBinaryFileSystem(fs http.FileSystem) *binaryFileSystem {
|
||||||
return &binaryFileSystem{Assets}
|
return &binaryFileSystem{fs}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *binaryFileSystem) Exists(prefix string, filepath string) bool {
|
func (fs *binaryFileSystem) Exists(prefix string, filepath string) bool {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
@ -19,6 +20,7 @@ import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/goccy/go-yaml"
|
"github.com/goccy/go-yaml"
|
||||||
"github.com/panjf2000/ants/v2"
|
"github.com/panjf2000/ants/v2"
|
||||||
|
"github.com/shurcooL/httpfs/html/vfstemplate"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -71,7 +73,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
ss := stats.NewTorrent()
|
ss := stats.NewTorrent()
|
||||||
mountService := mount.NewTorrent(c, pool, ss)
|
mountService := mount.NewHandler(c, pool, ss)
|
||||||
|
|
||||||
sigChan := make(chan os.Signal)
|
sigChan := make(chan os.Signal)
|
||||||
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
|
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
|
||||||
|
@ -96,15 +98,25 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
r := gin.Default()
|
r := gin.Default()
|
||||||
fs := distribyted.NewBinaryFileSystem()
|
assets := distribyted.NewBinaryFileSystem(distribyted.Assets)
|
||||||
file, err := fs.Open("index.html")
|
r.Use(static.Serve("/assets", assets))
|
||||||
|
|
||||||
|
t, err := vfstemplate.ParseGlob(distribyted.Templates, nil, "*")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("PUES SI QUE NO ESTá", err)
|
log.Fatal(err)
|
||||||
} else {
|
|
||||||
log.Println("FILE", file)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
r.Use(static.Serve("/", fs))
|
r.SetHTMLTemplate(t)
|
||||||
|
|
||||||
|
// r.LoadHTMLGlob("templates/*")
|
||||||
|
|
||||||
|
r.GET("/", func(c *gin.Context) {
|
||||||
|
c.HTML(http.StatusOK, "index.html", nil)
|
||||||
|
})
|
||||||
|
|
||||||
|
r.GET("/routes", func(c *gin.Context) {
|
||||||
|
c.HTML(http.StatusOK, "routes.html", ss.RoutesStats())
|
||||||
|
})
|
||||||
|
|
||||||
r.GET("/api/status", func(ctx *gin.Context) {
|
r.GET("/api/status", func(ctx *gin.Context) {
|
||||||
ctx.JSON(200, gin.H{
|
ctx.JSON(200, gin.H{
|
||||||
|
@ -113,17 +125,12 @@ func main() {
|
||||||
"cacheCapacity": fc.Info().Capacity / 1024 / 1024,
|
"cacheCapacity": fc.Info().Capacity / 1024 / 1024,
|
||||||
"poolCap": pool.Cap(),
|
"poolCap": pool.Cap(),
|
||||||
"poolFree": pool.Free(),
|
"poolFree": pool.Free(),
|
||||||
"torrentStats": ss.Global(),
|
"torrentStats": ss.GlobalStats(),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
r.GET("/api/status/:torrent", func(ctx *gin.Context) {
|
r.GET("/api/routes", func(ctx *gin.Context) {
|
||||||
hash := ctx.Param("torrent")
|
stats := ss.RoutesStats()
|
||||||
stats, err := ss.Torrent(hash)
|
|
||||||
if err != nil {
|
|
||||||
ctx.AbortWithError(404, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.JSON(200, stats)
|
ctx.JSON(200, stats)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -2,13 +2,17 @@
|
||||||
max-cache-size: 1024
|
max-cache-size: 1024
|
||||||
metadata-folder-name: ./_DATA/metadata
|
metadata-folder-name: ./_DATA/metadata
|
||||||
mountPoints:
|
mountPoints:
|
||||||
- path: ./_DATA/snes
|
|
||||||
magnets:
|
|
||||||
- uri: "magnet:?xt=urn:btih:3F8CA07909879B3102EFE1CD43E233C48E489519&dn=677+Super+Nintendo+%2F+SNES+ROMs&tr=udp%3A%2F%2Ftracker.ccc.se%3A80&tr=http%3A%2F%2Ftracker.ccc.de%2Fannounce&tr=udp%3A%2F%2Ftracker.publicbt.com%3A80&tr=http%3A%2F%2Ftracker.publicbt.com%2Fannounce&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=http%3A%2F%2Ftracker.openbittorrent.com%2Fannounce&tr=udp%3A%2F%2Ftracker.publicbt.com%3A80%2Fannounce&tr=http%3A%2F%2Ftracker.publicbt.com%2Fannounce&tr=http%3A%2F%2Ftracker.ccc.de%2Fannounce&tr=udp%3A%2F%2Ftracker.zer0day.to%3A1337%2Fannounce&tr=udp%3A%2F%2Ftracker.leechers-paradise.org%3A6969%2Fannounce&tr=udp%3A%2F%2Fcoppersurfer.tk%3A6969%2Fannounce"
|
|
||||||
- path: ./_DATA/nes
|
|
||||||
magnets:
|
|
||||||
- uri: "magnet:?xt=urn:btih:54F1FFB2861EE322B8641CEE232538A357341578&dn=706+Nintendo+%2F+NES+ROMs&tr=udp%3A%2F%2Ftracker.publicbt.com%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80%2Fannounce&tr=udp%3A%2F%2Ffr33domtracker.h33t.com%3A3310%2Fannounce&tr=udp%3A%2F%2Ftracker.istole.it%3A80%2Fannounce&tr=http%3A%2F%2Finferno.demonoid.me%3A3417%2Fannounce&tr=udp%3A%2F%2Ftracker.zer0day.to%3A1337%2Fannounce&tr=udp%3A%2F%2Ftracker.leechers-paradise.org%3A6969%2Fannounce&tr=udp%3A%2F%2Fcoppersurfer.tk%3A6969%2Fannounce"
|
|
||||||
- path: ./_DATA/psx
|
- path: ./_DATA/psx
|
||||||
magnets:
|
torrents:
|
||||||
# - uri: "magnet:?xt=urn:btih:3D41D4E6024AA4AB905BF0E6354D57F680C654F3&dn=Sony%20PlayStation%20%28PAL%29%20%5bRedump%5d&tr=http%3a%2f%2fbt2.t-ru.org%2fann%3fmagnet"
|
- magnetUri: "magnet:?xt=urn:btih:3D41D4E6024AA4AB905BF0E6354D57F680C654F3&dn=Sony%20PlayStation%20%28PAL%29%20%5bRedump%5d&tr=http%3a%2f%2fbt2.t-ru.org%2fann%3fmagnet"
|
||||||
- uri: "magnet:?xt=urn:btih:852299c530aaed8fa06bdf32d9bd909e0bb76fe7&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2Ftracker.internetwarriors.net%3A1337"
|
- magnetUri: "magnet:?xt=urn:btih:852299c530aaed8fa06bdf32d9bd909e0bb76fe7&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2Ftracker.internetwarriors.net%3A1337"
|
||||||
|
- path: ./_DATA/wiki
|
||||||
|
torrents:
|
||||||
|
- torrentPath: "/home/ajnavarro/Downloads/347B211B61AC09B07A7747C85D24CA7FBD5355C1.torrent"
|
||||||
|
- path: ./_DATA/snes
|
||||||
|
torrents:
|
||||||
|
- magnetUri: "magnet:?xt=urn:btih:3F8CA07909879B3102EFE1CD43E233C48E489519&dn=677+Super+Nintendo+%2F+SNES+ROMs&tr=udp%3A%2F%2Ftracker.ccc.se%3A80&tr=http%3A%2F%2Ftracker.ccc.de%2Fannounce&tr=udp%3A%2F%2Ftracker.publicbt.com%3A80&tr=http%3A%2F%2Ftracker.publicbt.com%2Fannounce&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=http%3A%2F%2Ftracker.openbittorrent.com%2Fannounce&tr=udp%3A%2F%2Ftracker.publicbt.com%3A80%2Fannounce&tr=http%3A%2F%2Ftracker.publicbt.com%2Fannounce&tr=http%3A%2F%2Ftracker.ccc.de%2Fannounce&tr=udp%3A%2F%2Ftracker.zer0day.to%3A1337%2Fannounce&tr=udp%3A%2F%2Ftracker.leechers-paradise.org%3A6969%2Fannounce&tr=udp%3A%2F%2Fcoppersurfer.tk%3A6969%2Fannounce"
|
||||||
|
- path: ./_DATA/nes
|
||||||
|
torrents:
|
||||||
|
- magnetUri: "magnet:?xt=urn:btih:54F1FFB2861EE322B8641CEE232538A357341578&dn=706+Nintendo+%2F+NES+ROMs&tr=udp%3A%2F%2Ftracker.publicbt.com%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80%2Fannounce&tr=udp%3A%2F%2Ffr33domtracker.h33t.com%3A3310%2Fannounce&tr=udp%3A%2F%2Ftracker.istole.it%3A80%2Fannounce&tr=http%3A%2F%2Finferno.demonoid.me%3A3417%2Fannounce&tr=udp%3A%2F%2Ftracker.zer0day.to%3A1337%2Fannounce&tr=udp%3A%2F%2Ftracker.leechers-paradise.org%3A6969%2Fannounce&tr=udp%3A%2F%2Fcoppersurfer.tk%3A6969%2Fannounce"
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,12 @@ type Root struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type MountPoint struct {
|
type MountPoint struct {
|
||||||
Path string `yaml:"path"`
|
Path string `yaml:"path"`
|
||||||
Magnets []struct {
|
Torrents []struct {
|
||||||
URI string `yaml:"uri"`
|
MagnetURI string `yaml:"magnetUri"`
|
||||||
FolderName string `yaml:"folderName,omitempty"`
|
TorrentPath string `yaml:"torrentPath"`
|
||||||
} `yaml:"magnets"`
|
FolderName string `yaml:"folderName,omitempty"`
|
||||||
|
} `yaml:"torrents"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddDefaults(r *Root) *Root {
|
func AddDefaults(r *Root) *Root {
|
||||||
|
|
7
torrent/front/css/bootstrap.min.css
vendored
7
torrent/front/css/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,25 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<!-- Required meta tags -->
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
|
||||||
|
|
||||||
<!-- Bootstrap CSS -->
|
|
||||||
<link rel="stylesheet" href="css/bootstrap.min.css"
|
|
||||||
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
|
|
||||||
|
|
||||||
<title>Hello, world!</title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<h1>Hello, world!</h1>
|
|
||||||
<div class="container">
|
|
||||||
<canvas id="chart-general-network"></canvas>
|
|
||||||
<canvas id="chart-file-chunks"></canvas>
|
|
||||||
</div>
|
|
||||||
<script src="js/main.js"></script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
|
@ -1,165 +0,0 @@
|
||||||
import Humanize from './humanize.js';
|
|
||||||
import Chart from 'chart.js';
|
|
||||||
import _ from 'chartjs-plugin-stacked100';
|
|
||||||
|
|
||||||
var ctx = document.getElementById('chart-general-network').getContext('2d');
|
|
||||||
|
|
||||||
var downloadData = [];
|
|
||||||
var uploadData = [];
|
|
||||||
var labels = [];
|
|
||||||
|
|
||||||
var chart = new Chart(ctx, {
|
|
||||||
type: 'line',
|
|
||||||
labels: labels,
|
|
||||||
data: {
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: 'Download Speed',
|
|
||||||
fill: false,
|
|
||||||
backgroundColor: 'rgb(255, 99, 132)',
|
|
||||||
borderColor: 'rgb(255, 99, 132)',
|
|
||||||
borderWidth: 1,
|
|
||||||
data: downloadData,
|
|
||||||
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Upload Speed',
|
|
||||||
fill: false,
|
|
||||||
borderWidth: 1,
|
|
||||||
data: uploadData,
|
|
||||||
|
|
||||||
},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
title: {
|
|
||||||
text: 'Download and Upload speed'
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
xAxes: [{
|
|
||||||
scaleLabel: {
|
|
||||||
display: true,
|
|
||||||
labelString: 'Date'
|
|
||||||
},
|
|
||||||
type: 'time',
|
|
||||||
time: {
|
|
||||||
// parser: timeFormat,
|
|
||||||
tooltipFormat: 'll HH:mm'
|
|
||||||
},
|
|
||||||
|
|
||||||
}],
|
|
||||||
yAxes: [{
|
|
||||||
scaleLabel: {
|
|
||||||
display: true,
|
|
||||||
labelString: 'value'
|
|
||||||
},
|
|
||||||
type: 'linear',
|
|
||||||
ticks: {
|
|
||||||
userCallback: function (tick) {
|
|
||||||
return Humanize.bytes(tick, 1024) + "/s";
|
|
||||||
},
|
|
||||||
beginAtZero: true
|
|
||||||
},
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var piecesData = []
|
|
||||||
var fileChart = new Chart(document.getElementById("chart-file-chunks"), {
|
|
||||||
type: "horizontalBar",
|
|
||||||
data: {
|
|
||||||
labels: ["File"],
|
|
||||||
datasets: piecesData,
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
animation: {
|
|
||||||
duration: 0
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
stacked100: { enable: true }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
setInterval(function () {
|
|
||||||
fetch('/api/status/852299c530aaed8fa06bdf32d9bd909e0bb76fe7')
|
|
||||||
.then(function (response) {
|
|
||||||
if (response.ok) {
|
|
||||||
return response.json();
|
|
||||||
} else {
|
|
||||||
console.log('Error getting data from server. Response: ' + response.status);
|
|
||||||
}
|
|
||||||
}).then(function (stats) {
|
|
||||||
piecesData.length = 0;
|
|
||||||
stats.PieceChunks.forEach(element => {
|
|
||||||
var label, color;
|
|
||||||
switch (element.Status) {
|
|
||||||
case "H":
|
|
||||||
label = "checking";
|
|
||||||
color = "#8a5999";
|
|
||||||
break;
|
|
||||||
case "P":
|
|
||||||
label = "partial";
|
|
||||||
color = "#be9600";
|
|
||||||
break;
|
|
||||||
case "C":
|
|
||||||
label = "complete";
|
|
||||||
color = "#208f09";
|
|
||||||
break;
|
|
||||||
case "W":
|
|
||||||
label = "waiting";
|
|
||||||
color = "#8a5999";
|
|
||||||
break;
|
|
||||||
case "?":
|
|
||||||
label = "error";
|
|
||||||
color = "#ff5f5c";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
label = "unknown";
|
|
||||||
color = "gray";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
piecesData.push({
|
|
||||||
label: label,
|
|
||||||
data: [element.NumPieces],
|
|
||||||
backgroundColor: color,
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
fileChart.update();
|
|
||||||
})
|
|
||||||
}, 2000)
|
|
||||||
|
|
||||||
setInterval(function () {
|
|
||||||
fetch('/api/status')
|
|
||||||
.then(function (response) {
|
|
||||||
if (response.ok) {
|
|
||||||
return response.json();
|
|
||||||
} else {
|
|
||||||
console.log('Error getting data from server. Response: ' + response.status);
|
|
||||||
}
|
|
||||||
}).then(function (stats) {
|
|
||||||
if (downloadData.length > 20) {
|
|
||||||
uploadData.shift();
|
|
||||||
downloadData.shift();
|
|
||||||
labels.shift();
|
|
||||||
}
|
|
||||||
|
|
||||||
var date = new Date();
|
|
||||||
downloadData.push({
|
|
||||||
x: date,
|
|
||||||
y: stats.torrentStats.DownloadedBytes / stats.torrentStats.TimePassed,
|
|
||||||
});
|
|
||||||
uploadData.push({
|
|
||||||
x: date,
|
|
||||||
y: stats.torrentStats.UploadedBytes / stats.torrentStats.TimePassed,
|
|
||||||
});
|
|
||||||
labels.push(date);
|
|
||||||
chart.update();
|
|
||||||
})
|
|
||||||
.catch(function (error) {
|
|
||||||
console.log('Error getting status info: ' + error.message);
|
|
||||||
});
|
|
||||||
}, 2000)
|
|
7076
torrent/front/package-lock.json
generated
7076
torrent/front/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -1,20 +0,0 @@
|
||||||
{
|
|
||||||
"name": "distribyted",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"description": "",
|
|
||||||
"dependencies": {
|
|
||||||
"bootstrap": "^4.4.1",
|
|
||||||
"bootstrap-vue": "^2.14.0",
|
|
||||||
"chart.js": "^2.9.3",
|
|
||||||
"chartjs-plugin-stacked100": "^0.7.1",
|
|
||||||
"portal-vue": "^2.1.7",
|
|
||||||
"vue": "^2.6.11"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"dev": "parcel watch index.html --out-dir ../assets",
|
|
||||||
"build": "parcel build index.html"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"parcel-bundler": "^1.12.4"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -10,7 +10,6 @@ require (
|
||||||
github.com/anacrolix/missinggo/v2 v2.4.1-0.20200227072623-f02f6484f997
|
github.com/anacrolix/missinggo/v2 v2.4.1-0.20200227072623-f02f6484f997
|
||||||
github.com/anacrolix/multiless v0.0.0-20200413040533-acfd16f65d5d // indirect
|
github.com/anacrolix/multiless v0.0.0-20200413040533-acfd16f65d5d // indirect
|
||||||
github.com/anacrolix/torrent v1.15.2
|
github.com/anacrolix/torrent v1.15.2
|
||||||
github.com/dustin/go-humanize v1.0.0
|
|
||||||
github.com/elliotchance/orderedmap v1.2.2 // indirect
|
github.com/elliotchance/orderedmap v1.2.2 // indirect
|
||||||
github.com/fatih/color v1.9.0 // indirect
|
github.com/fatih/color v1.9.0 // indirect
|
||||||
github.com/gin-contrib/static v0.0.0-20191128031702-f81c604d8ac2
|
github.com/gin-contrib/static v0.0.0-20191128031702-f81c604d8ac2
|
||||||
|
@ -25,6 +24,7 @@ require (
|
||||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||||
github.com/mschoch/smat v0.2.0 // indirect
|
github.com/mschoch/smat v0.2.0 // indirect
|
||||||
github.com/panjf2000/ants/v2 v2.3.1
|
github.com/panjf2000/ants/v2 v2.3.1
|
||||||
|
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749
|
||||||
github.com/tinylib/msgp v1.1.2 // indirect
|
github.com/tinylib/msgp v1.1.2 // indirect
|
||||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f // indirect
|
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f // indirect
|
||||||
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f // indirect
|
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f // indirect
|
||||||
|
|
|
@ -310,6 +310,8 @@ github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R
|
||||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||||
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a8tTFrMLUcfWwyC0pnifVo2ClaLq+hP8=
|
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a8tTFrMLUcfWwyC0pnifVo2ClaLq+hP8=
|
||||||
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8=
|
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8=
|
||||||
|
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06Bs80sCeARAlK8lhwqGyi6UT8ymuGk=
|
||||||
|
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package mount
|
package mount
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
@ -13,7 +14,7 @@ import (
|
||||||
"github.com/panjf2000/ants/v2"
|
"github.com/panjf2000/ants/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Torrent struct {
|
type Handler struct {
|
||||||
c *torrent.Client
|
c *torrent.Client
|
||||||
s *stats.Torrent
|
s *stats.Torrent
|
||||||
opts *fs.Options
|
opts *fs.Options
|
||||||
|
@ -22,8 +23,8 @@ type Torrent struct {
|
||||||
servers map[string]*fuse.Server
|
servers map[string]*fuse.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTorrent(c *torrent.Client, pool *ants.Pool, s *stats.Torrent) *Torrent {
|
func NewHandler(c *torrent.Client, pool *ants.Pool, s *stats.Torrent) *Handler {
|
||||||
return &Torrent{
|
return &Handler{
|
||||||
c: c,
|
c: c,
|
||||||
s: s,
|
s: s,
|
||||||
opts: &fs.Options{},
|
opts: &fs.Options{},
|
||||||
|
@ -32,17 +33,31 @@ func NewTorrent(c *torrent.Client, pool *ants.Pool, s *stats.Torrent) *Torrent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Torrent) Mount(mpc *config.MountPoint) error {
|
func (s *Handler) Mount(mpc *config.MountPoint) error {
|
||||||
var torrents []*torrent.Torrent
|
var torrents []*torrent.Torrent
|
||||||
for _, magnet := range mpc.Magnets {
|
for _, mpcTorrent := range mpc.Torrents {
|
||||||
t, err := s.c.AddMagnet(magnet.URI)
|
var t *torrent.Torrent
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case mpcTorrent.MagnetURI != "":
|
||||||
|
t, err = s.c.AddMagnet(mpcTorrent.MagnetURI)
|
||||||
|
break
|
||||||
|
case mpcTorrent.TorrentPath != "":
|
||||||
|
t, err = s.c.AddTorrentFromFile(mpcTorrent.TorrentPath)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("no magnet URI or torrent path provided")
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("getting torrent info", t.Name())
|
log.Println("getting torrent info", t.Name())
|
||||||
<-t.GotInfo()
|
<-t.GotInfo()
|
||||||
|
|
||||||
s.s.Add(t)
|
s.s.Add(mpc.Path, t)
|
||||||
|
|
||||||
log.Println("torrent info obtained", t.Name())
|
log.Println("torrent info obtained", t.Name())
|
||||||
torrents = append(torrents, t)
|
torrents = append(torrents, t)
|
||||||
|
@ -65,7 +80,7 @@ func (s *Torrent) Mount(mpc *config.MountPoint) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Torrent) Close() {
|
func (s *Handler) Close() {
|
||||||
for path, server := range s.servers {
|
for path, server := range s.servers {
|
||||||
log.Println("unmounting", path)
|
log.Println("unmounting", path)
|
||||||
err := server.Unmount()
|
err := server.Unmount()
|
57
torrent/package-lock.json
generated
Normal file
57
torrent/package-lock.json
generated
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
{
|
||||||
|
"requires": true,
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"dependencies": {
|
||||||
|
"chart.js": {
|
||||||
|
"version": "2.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.9.3.tgz",
|
||||||
|
"integrity": "sha512-+2jlOobSk52c1VU6fzkh3UwqHMdSlgH1xFv9FKMqHiNCpXsGPQa/+81AFa+i3jZ253Mq9aAycPwDjnn1XbRNNw==",
|
||||||
|
"requires": {
|
||||||
|
"chartjs-color": "^2.1.0",
|
||||||
|
"moment": "^2.10.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"chartjs-color": {
|
||||||
|
"version": "2.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.4.1.tgz",
|
||||||
|
"integrity": "sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w==",
|
||||||
|
"requires": {
|
||||||
|
"chartjs-color-string": "^0.6.0",
|
||||||
|
"color-convert": "^1.9.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"chartjs-color-string": {
|
||||||
|
"version": "0.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz",
|
||||||
|
"integrity": "sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==",
|
||||||
|
"requires": {
|
||||||
|
"color-name": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-convert": {
|
||||||
|
"version": "1.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||||
|
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||||
|
"requires": {
|
||||||
|
"color-name": "1.1.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"color-name": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||||
|
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-name": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||||
|
},
|
||||||
|
"moment": {
|
||||||
|
"version": "2.25.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.25.3.tgz",
|
||||||
|
"integrity": "sha512-PuYv0PHxZvzc15Sp8ybUCoQ+xpyPWvjOuK72a5ovzp2LI32rJXOiIfyoFoYvG3s6EwwrdkMyWuRiEHSZRLJNdg=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,12 +2,10 @@ package stats
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/anacrolix/torrent"
|
"github.com/anacrolix/torrent"
|
||||||
"github.com/dustin/go-humanize"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrTorrentNotFound = errors.New("torrent not found")
|
var ErrTorrentNotFound = errors.New("torrent not found")
|
||||||
|
@ -23,39 +21,29 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type PieceChunk struct {
|
type PieceChunk struct {
|
||||||
Status PieceStatus
|
Status PieceStatus `json:"status"`
|
||||||
NumPieces int
|
NumPieces int `json:"numPieces"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TorrentStats struct {
|
type TorrentStats struct {
|
||||||
DownloadedBytes int64
|
Name string `json:"name"`
|
||||||
UploadedBytes int64
|
Hash string `json:"hash"`
|
||||||
TimePassed float64
|
DownloadedBytes int64 `json:"downloadedBytes"`
|
||||||
PieceChunks []*PieceChunk
|
UploadedBytes int64 `json:"uploadedBytes"`
|
||||||
|
TimePassed float64 `json:"timePassed"`
|
||||||
|
PieceChunks []*PieceChunk `json:"pieceChunks"`
|
||||||
|
TotalPieces int `json:"totalPieces"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GlobalTorrentStats struct {
|
type GlobalTorrentStats struct {
|
||||||
DownloadedBytes int64
|
DownloadedBytes int64 `json:"downloadedBytes"`
|
||||||
UploadedBytes int64
|
UploadedBytes int64 `json:"uploadedBytes"`
|
||||||
TimePassed float64
|
TimePassed float64 `json:"timePassed"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GlobalTorrentStats) speed(bytes int64) float64 {
|
type RouteStats struct {
|
||||||
var bs float64
|
Name string `json:"name"`
|
||||||
t := s.TimePassed
|
TorrentStats []*TorrentStats `json:"torrentStats"`
|
||||||
if t != 0 {
|
|
||||||
bs = float64(bytes) / t
|
|
||||||
}
|
|
||||||
|
|
||||||
return bs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *GlobalTorrentStats) DownloadSpeed() string {
|
|
||||||
return fmt.Sprintf(" %s/s", humanize.IBytes(uint64(s.speed(s.DownloadedBytes))))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *GlobalTorrentStats) UploadSpeed() string {
|
|
||||||
return fmt.Sprintf(" %s/s", humanize.IBytes(uint64(s.speed(s.UploadedBytes))))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type stats struct {
|
type stats struct {
|
||||||
|
@ -67,26 +55,31 @@ type stats struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Torrent struct {
|
type Torrent struct {
|
||||||
torrents map[string]*torrent.Torrent
|
torrents map[string]*torrent.Torrent
|
||||||
previousStats map[string]*stats
|
torrentsByRoute map[string][]*torrent.Torrent
|
||||||
|
previousStats map[string]*stats
|
||||||
|
|
||||||
gTime time.Time
|
gTime time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTorrent() *Torrent {
|
func NewTorrent() *Torrent {
|
||||||
return &Torrent{
|
return &Torrent{
|
||||||
gTime: time.Now(),
|
gTime: time.Now(),
|
||||||
torrents: make(map[string]*torrent.Torrent),
|
torrents: make(map[string]*torrent.Torrent),
|
||||||
previousStats: make(map[string]*stats),
|
torrentsByRoute: make(map[string][]*torrent.Torrent),
|
||||||
|
previousStats: make(map[string]*stats),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Torrent) Add(t *torrent.Torrent) {
|
func (s *Torrent) Add(route string, t *torrent.Torrent) {
|
||||||
s.torrents[t.InfoHash().String()] = t
|
s.torrents[t.InfoHash().String()] = t
|
||||||
s.previousStats[t.InfoHash().String()] = &stats{}
|
s.previousStats[t.InfoHash().String()] = &stats{}
|
||||||
|
|
||||||
|
tbr := s.torrentsByRoute[route]
|
||||||
|
s.torrentsByRoute[route] = append(tbr, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Torrent) Torrent(hash string) (*TorrentStats, error) {
|
func (s *Torrent) Stats(hash string) (*TorrentStats, error) {
|
||||||
t, ok := s.torrents[hash]
|
t, ok := s.torrents[hash]
|
||||||
if !(ok) {
|
if !(ok) {
|
||||||
return nil, ErrTorrentNotFound
|
return nil, ErrTorrentNotFound
|
||||||
|
@ -97,16 +90,27 @@ func (s *Torrent) Torrent(hash string) (*TorrentStats, error) {
|
||||||
return s.stats(now, t, true), nil
|
return s.stats(now, t, true), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Torrent) List() []string {
|
func (s *Torrent) RoutesStats() []*RouteStats {
|
||||||
var result []string
|
now := time.Now()
|
||||||
for hash := range s.torrents {
|
|
||||||
result = append(result, hash)
|
var out []*RouteStats
|
||||||
|
for r, tl := range s.torrentsByRoute {
|
||||||
|
var tStats []*TorrentStats
|
||||||
|
for _, t := range tl {
|
||||||
|
ts := s.stats(now, t, true)
|
||||||
|
tStats = append(tStats, ts)
|
||||||
|
}
|
||||||
|
rs := &RouteStats{
|
||||||
|
Name: r,
|
||||||
|
TorrentStats: tStats,
|
||||||
|
}
|
||||||
|
out = append(out, rs)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Torrent) Global() *GlobalTorrentStats {
|
func (s *Torrent) GlobalStats() *GlobalTorrentStats {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
var totalDownload int64
|
var totalDownload int64
|
||||||
|
@ -155,7 +159,7 @@ func (s *Torrent) stats(now time.Time, t *torrent.Torrent, chunks bool) *Torrent
|
||||||
}
|
}
|
||||||
|
|
||||||
ts.TimePassed = now.Sub(prev.time).Seconds()
|
ts.TimePassed = now.Sub(prev.time).Seconds()
|
||||||
|
var totalPieces int
|
||||||
if chunks {
|
if chunks {
|
||||||
var pch []*PieceChunk
|
var pch []*PieceChunk
|
||||||
for _, psr := range t.PieceStateRuns() {
|
for _, psr := range t.PieceStateRuns() {
|
||||||
|
@ -177,10 +181,14 @@ func (s *Torrent) stats(now time.Time, t *torrent.Torrent, chunks bool) *Torrent
|
||||||
Status: s,
|
Status: s,
|
||||||
NumPieces: psr.Length,
|
NumPieces: psr.Length,
|
||||||
})
|
})
|
||||||
|
totalPieces += psr.Length
|
||||||
}
|
}
|
||||||
ts.PieceChunks = pch
|
ts.PieceChunks = pch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ts.Hash = t.InfoHash().String()
|
||||||
|
ts.Name = t.Name()
|
||||||
|
ts.TotalPieces = totalPieces
|
||||||
return ts
|
return ts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
7
torrent/templates/common_footer.html
Normal file
7
torrent/templates/common_footer.html
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"
|
||||||
|
integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"
|
||||||
|
integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
<script src="assets/js/bootstrap.bundle.min.js"></script>
|
8
torrent/templates/common_header.html
Normal file
8
torrent/templates/common_header.html
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<!-- Required meta tags -->
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
|
||||||
|
<!-- Bootstrap CSS -->
|
||||||
|
<link rel="stylesheet" href="assets/css/bootstrap.min.css">
|
||||||
|
|
||||||
|
<title>{{.Title}}</title>
|
59
torrent/templates/index.html
Normal file
59
torrent/templates/index.html
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
{{template "common_header.html"}}
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
{{template "navbar.html"}}
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<canvas id="chart-general-network"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm">
|
||||||
|
<canvas id="chart-cache" height="400"></canvas>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm">
|
||||||
|
<h3>
|
||||||
|
<svg class="bi bi-arrow-down" width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd"
|
||||||
|
d="M4.646 9.646a.5.5 0 01.708 0L8 12.293l2.646-2.647a.5.5 0 01.708.708l-3 3a.5.5 0 01-.708 0l-3-3a.5.5 0 010-.708z"
|
||||||
|
clip-rule="evenodd" />
|
||||||
|
<path fill-rule="evenodd" d="M8 2.5a.5.5 0 01.5.5v9a.5.5 0 01-1 0V3a.5.5 0 01.5-.5z"
|
||||||
|
clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
<span id="down-speed-text"></span>
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm">
|
||||||
|
<h3>
|
||||||
|
<svg class="bi bi-arrow-up" width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" d="M8 3.5a.5.5 0 01.5.5v9a.5.5 0 01-1 0V4a.5.5 0 01.5-.5z"
|
||||||
|
clip-rule="evenodd" />
|
||||||
|
<path fill-rule="evenodd"
|
||||||
|
d="M7.646 2.646a.5.5 0 01.708 0l3 3a.5.5 0 01-.708.708L8 3.707 5.354 6.354a.5.5 0 11-.708-.708l3-3z"
|
||||||
|
clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
<span id="up-speed-text"></span>
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm">
|
||||||
|
<canvas id="chart-workers" height="400"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{template "common_footer.html"}}
|
||||||
|
<script src="assets/js/Chart.bundle.min.js"></script>
|
||||||
|
<script src="assets/js/humanize.js"></script>
|
||||||
|
<script src="assets/js/general_chart.js"></script>
|
||||||
|
<script src="assets/js/single_bar_chart.js"></script>
|
||||||
|
<script src="assets/js/general.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
17
torrent/templates/navbar.html
Normal file
17
torrent/templates/navbar.html
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||||
|
<a class="navbar-brand" href="#">Distribyted</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav"
|
||||||
|
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
|
<ul class="navbar-nav">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/">General</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/routes">Routes</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</nav>
|
51
torrent/templates/routes.html
Normal file
51
torrent/templates/routes.html
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
{{template "common_header.html"}}
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
{{template "navbar.html"}}
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
{{if not .}}
|
||||||
|
<div class="alert alert-warning" role="alert">
|
||||||
|
No routes found.
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{range .}}
|
||||||
|
<div class="card shadow">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">{{.Name}}</h5>
|
||||||
|
{{range .TorrentStats}}
|
||||||
|
<div class="card shadow">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<h5>{{.Name}}</h5>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<p id="up-down-speed-text-{{.Hash}}">...</p>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<div id="file-chunks-{{.Hash}}" class="progress">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
{{template "common_footer.html"}}
|
||||||
|
<script src="assets/js/humanize.js"></script>
|
||||||
|
<script src="assets/js/file_chunks.js"></script>
|
||||||
|
<script src="assets/js/routes.js"></script>
|
||||||
|
</body>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
Loading…
Reference in a new issue