convert ajax calls to websocket

This commit is contained in:
iou1name 2019-09-01 16:48:45 -04:00
parent cd5689186b
commit c81ce67f92
5 changed files with 141 additions and 27 deletions

View File

@ -2,12 +2,15 @@
""" """
The primary module for serving the Aberrant application. The primary module for serving the Aberrant application.
""" """
import json
import jinja2 import jinja2
import aiohttp_jinja2 import aiohttp_jinja2
from aiohttp import web, WSMsgType from aiohttp import web, WSMsgType
from aiohttp_jinja2 import render_template from aiohttp_jinja2 import render_template
import config import config
import events
import rtorrent import rtorrent
app = web.Application() app = web.Application()
@ -18,18 +21,44 @@ rtorrent.init()
routes = web.RouteTableDef() routes = web.RouteTableDef()
@routes.get(config.prefix + "/", name='index') @routes.get(config.prefix + "/", name='index')
def index(request): async def index(request):
"""The index page.""" """The index page."""
torrents = rtorrent.get_active() torrents = rtorrent.get_active()
tracker_stats = rtorrent.get_stats() tracker_stats = rtorrent.get_stats()
return render_template("index.html", request, locals()) return render_template("index.html", request, locals())
@routes.get(config.prefix + "/get_active_torrents", name='active-torrents') @routes.get(config.prefix + "/get_active_torrents", name='active-torrents')
def get_active_torrents(request): async def get_active_torrents(request):
"""Returns all active torrents formatted as JSON.""" """Returns all active torrents formatted as JSON."""
data = [vars(t) for t in rtorrent.get_active()] data = [vars(t) for t in rtorrent.get_active()]
return web.json_response(data) return web.json_response(data)
@routes.get(config.prefix + '/ws', name='ws')
async def websocket_handler(request):
"""The websocket endpoint."""
ws = web.WebSocketResponse()
ws_ready = ws.can_prepare(request)
if not ws_ready.ok:
return web.Response(text="Cannot start websocket.")
await ws.prepare(request)
async for msg in ws:
if msg.type == WSMsgType.TEXT:
try:
data = json.loads(msg.data)
except json.JSONDecodeError:
continue
event = data.get('event')
if not event or event not in events.events.keys():
continue
await events.events[event](ws, data.get('data'))
else: # TODO: handle differnt message types properly
break
await ws.close()
return ws
app.router.add_routes(routes) app.router.add_routes(routes)
if __name__ == "__main__": if __name__ == "__main__":

18
events.py Normal file
View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
"""
WebSocket events.
"""
import types
import rtorrent
async def active_torrents(ws, data):
"""Returns active torrent information."""
data = [vars(t) for t in rtorrent.get_active()]
res = {'event': 'active_torrents', 'data': data}
await ws.send_json(res)
events = {}
for obj in dir():
if type(locals()[obj]) == types.FunctionType:
events[locals()[obj].__name__] = locals()[obj]

View File

@ -14,15 +14,21 @@ tr {
border: 1px solid #ccc; border: 1px solid #ccc;
} }
.name { #torrents td {
padding-left: 0.5em;
}
.totalSize, .state, .downrate, .downPercent, .eta, .uprate, .tracker, .rt_id {
text-align: center; text-align: center;
width: 10%; width: 10%;
} }
#torrents .name {
padding-left: 0.5em;
text-align: left;
width: 0;
}
#torrents .down_percent:after {
content: "%";
}
#tracker_stats { #tracker_stats {
border-collapse: collapse; border-collapse: collapse;
border: 1px solid #ccc; border: 1px solid #ccc;

View File

@ -1,8 +1,9 @@
function load() { function load() {
//let intervalID = window.setInterval(get_active_torrents_ajax, 20000);
let intervalID = window.setInterval(get_active_torrents, 20000); let intervalID = window.setInterval(get_active_torrents, 20000);
} }
function get_active_torrents() { function get_active_torrents_ajax() {
let httpRequest; let httpRequest;
httpRequest = new XMLHttpRequest(); httpRequest = new XMLHttpRequest();
httpRequest.onreadystatechange = function() { httpRequest.onreadystatechange = function() {
@ -13,14 +14,14 @@ function get_active_torrents() {
for (let i = 0; i < torrents.length; i++) { for (let i = 0; i < torrents.length; i++) {
html_str += '<tr>' html_str += '<tr>'
html_str += '<td class="name">' + torrents[i].name + '</td>'; html_str += '<td class="name">' + torrents[i].name + '</td>';
html_str += '<td class="totalSize">' + torrents[i].total_size_str + '</td>'; html_str += '<td class="total_size_str">' + torrents[i].total_size_str + '</td>';
html_str += '<td class="state">' + torrents[i].state + '</td>'; html_str += '<td class="state">' + torrents[i].state + '</td>';
html_str += '<td class="downrate">' + torrents[i].downrate_str + '</td>'; html_str += '<td class="downrate_str">' + torrents[i].downrate_str + '</td>';
html_str += '<td class="downPercent">' + torrents[i].down_percent + '%</td>'; html_str += '<td class="down_percent">' + torrents[i].down_percent + '%</td>';
html_str += '<td class="eta">' + torrents[i].eta_str + '</td>'; html_str += '<td class="eta_str">' + torrents[i].eta_str + '</td>';
html_str += '<td class="uprate">' + torrents[i].uprate_str + '</td>'; html_str += '<td class="uprate_str">' + torrents[i].uprate_str + '</td>';
html_str += '<td class="tracker">' + torrents[i].tracker + '</td>'; html_str += '<td class="tracker">' + torrents[i].tracker + '</td>';
html_str += '<td class="rt_id">' + torrents[i].rtorrent_id + '</td>'; html_str += '<td class="rtorrent_id">' + torrents[i].rtorrent_id + '</td>';
html_str += '</tr>'; html_str += '</tr>';
} }
document.getElementById('torrents').children[1].innerHTML = html_str; document.getElementById('torrents').children[1].innerHTML = html_str;
@ -28,3 +29,49 @@ function get_active_torrents() {
httpRequest.open('GET', get_torrents_uri, true); httpRequest.open('GET', get_torrents_uri, true);
httpRequest.send(); httpRequest.send();
} }
var socket = new WebSocket('wss://' + window.location.hostname + ws_uri);
socket.oldSend = socket.send;
socket.send = function(event_title, data) {
data = JSON.stringify({event: event_title, data: data});
socket.oldSend.apply(this, [data]);
}
socket.events = {};
socket.onmessage = function(e) {
let data = JSON.parse(e.data);
let event = data.event;
data = data.data;
if (socket.events[event] === undefined) { return; }
socket.events[event](data);
}
socket.onclose = function(e) {
console.log('WebSocket lost connection to server. Re-trying...');
// TODO: reconnect
}
/* Websocket receive */
socket.events['active_torrents'] = function(data) {
let table = document.querySelector('#torrents tbody');
while (table.firstChild) {
table.removeChild(table.firstChild);
}
data.forEach(function(torrent) {
let template = document.querySelector('#torrent_template');
let node = document.importNode(template.content, true);
for (let field of node.children[0].children) {
field.textContent = torrent[field.className];
}
table.appendChild(node);
});
}
socket.events['tracker_stats'] = function(data) {
console.log(data);
}
/* Websocket send */
function get_active_torrents() {
socket.send('active_torrents', {});
}
function get_tracker_stats() {
socket.send('tracker_stats', {});
}

View File

@ -3,10 +3,11 @@
<head> <head>
<title>Aberrant</title> <title>Aberrant</title>
<link rel="stylesheet" type="text/css" href="/static/aberrant.css"> <link rel="stylesheet" type="text/css" href="/static/aberrant.css">
<script type="text/javascript" src="/static/aberrant.js"></script>
<script> <script>
const get_torrents_uri = "{{ request.app.router['active-torrents'].url_for() }}"; const get_torrents_uri = "{{ request.app.router['active-torrents'].url_for() }}";
const ws_uri = "{{ request.app.router['ws'].url_for() }}";
</script> </script>
<script type="text/javascript" src="/static/aberrant.js"></script>
<script>window.onload = load;</script> <script>window.onload = load;</script>
</head> </head>
<body> <body>
@ -14,32 +15,45 @@
<thead> <thead>
<tr> <tr>
<th class="name">Name</th> <th class="name">Name</th>
<th class="sizeTotal">Size</th> <th class="total_size_str">Size</th>
<th class="state">State</th> <th class="state">State</th>
<th class="downrate">DL</th> <th class="downrate_str">DL</th>
<th class="downPercent">%</th> <th class="down_percent"></th>
<th class="eta">ETA</th> <th class="eta_str">ETA</th>
<th class="uprate">UL</th> <th class="uprate_str">UL</th>
<th class="tracker">Tracker</th> <th class="tracker">Tracker</th>
<th class="rt_id">RT id</th> <th class="rtorrent_id">RT id</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for torrent in torrents %} {% for torrent in torrents %}
<tr> <tr>
<td class="name">{{ torrent.name }}</td> <td class="name">{{ torrent.name }}</td>
<td class="totalSize">{{ torrent.total_size_str }}</td> <td class="total_size_str">{{ torrent.total_size_str }}</td>
<td class="state">{{ torrent.state }}</td> <td class="state">{{ torrent.state }}</td>
<td class="downrate">{{ torrent.downrate_str }}</td> <td class="downrate_str">{{ torrent.downrate_str }}</td>
<td class="downPercent">{{ torrent.down_percent }}%</td> <td class="down_percent">{{ torrent.down_percent }}</td>
<td class="eta">{{ torrent.eta_str }}</td> <td class="eta_str">{{ torrent.eta_str }}</td>
<td class="uprate">{{ torrent.uprate_str }}</td> <td class="uprate_str">{{ torrent.uprate_str }}</td>
<td class="tracker">{{ torrent.tracker }}</td> <td class="tracker">{{ torrent.tracker }}</td>
<td class="rt_id">{{ torrent.rtorrent_id }}</td> <td class="rtorrent_id">{{ torrent.rtorrent_id }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
<template id="torrent_template">
<tr>
<td class="name"></td>
<td class="total_size_str"></td>
<td class="state"></td>
<td class="downrate_str"></td>
<td class="down_percent"></td>
<td class="eta_str"></td>
<td class="uprate_str"></td>
<td class="tracker"></td>
<td class="rtorrent_id"></td>
</tr>
</template>
<br> <br>
<table id="tracker_stats"> <table id="tracker_stats">
<thead> <thead>