web: convert the peers tab to use an ext proxy

Instead of fetching the peer information via a rpc call simply
expose it as a fetching page via ajax so we can make use of
the Ext proxy and reader instead of manually loading it into
the store ourselves.
This commit is contained in:
Damien Churchill 2011-10-04 21:56:48 +01:00
parent 0c3d2322cc
commit 280377ad6f
4 changed files with 137 additions and 129 deletions

View File

@ -136,7 +136,7 @@ class Auth(JSONComponent):
checksum = str(make_checksum(session_id))
request.addCookie('_session_id', session_id + checksum,
path=request.base+"json", expires=expires_str)
path=request.base, expires=expires_str)
log.debug("Creating session for %s", login)
config = component.get("DelugeWeb").config
@ -233,7 +233,7 @@ class Auth(JSONComponent):
_session_id = request.getCookie("_session_id")
request.addCookie('_session_id', _session_id,
path=request.base+"json", expires=expires_str)
path=request.base, expires=expires_str)
if method:
if not hasattr(method, "_json_export"):

View File

@ -1,5 +1,5 @@
/*!
* Deluge.data.PeerRecord.js
* Deluge.data.Peer.js
*
* Copyright (c) Damien Churchill 2009-2011 <damoxc@gmail.com>
*

View File

@ -30,131 +30,93 @@
* statement from all source files in the program, then also delete it here.
*/
(function() {
function flagRenderer(value) {
if (!value.replace(' ', '').replace(' ', '')){
return '';
Ext.define('Deluge.details.PeersTab', {
extend: 'Ext.grid.Panel',
title: _('Peers'),
cls: 'x-deluge-peers',
viewConfig: {
loadMask: false,
},
invalidateScrollerOnRefresh: false,
store: {
model: 'Deluge.data.Peer',
proxy: {
type: 'ajax',
url: 'peers/',
reader: {
type: 'json',
root: 'peers'
}
}
return String.format('<img src="flag/{0}" />', value);
}
function peerAddressRenderer(value, p, record) {
var seed = (record.data['seed'] == 1024) ? 'x-deluge-seed' : 'x-deluge-peer';
if (peer_ip.length > 2) {
var port = peer_ip.pop();
var ip = peer_ip.join(":");
value = "[" + ip + "]:" + port;
},
columns: [{
text: '&nbsp;',
dataIndex: 'country',
width: 30,
sortable: true,
renderer: function(v) {
if (!v.replace(' ', '').replace(' ', '')) {
return '';
}
return Ext.String.format('<img src="flag/{0}" />', v);
}
return String.format('<div class="{0}">{1}</div>', seed, value);
}
function peerProgressRenderer(value) {
var progress = (value * 100).toFixed(0);
return Deluge.progressBar(progress, this.width - 8, progress + '%');
}
Ext.define('Deluge.details.PeersTab', {
extend: 'Ext.grid.Panel',
title: _('Peers'),
cls: 'x-deluge-peers',
store: Ext.create('Ext.data.Store', {
model: 'Deluge.data.Peer'
}),
columns: [{
header: '&nbsp;',
width: 30,
sortable: true,
renderer: flagRenderer,
dataIndex: 'country'
}, {
header: 'Address',
width: 125,
sortable: true,
renderer: peerAddressRenderer,
dataIndex: 'ip'
}, {
header: 'Client',
width: 125,
sortable: true,
renderer: function(v) { return fplain(v) },
dataIndex: 'client'
}, {
header: 'Progress',
width: 150,
sortable: true,
renderer: peerProgressRenderer,
dataIndex: 'progress'
}, {
header: 'Down Speed',
width: 100,
sortable: true,
renderer: function(v) { return fspeed(v) },
dataIndex: 'down_speed'
}, {
header: 'Up Speed',
width: 100,
sortable: true,
renderer: function(v) { return fspeed(v) },
dataIndex: 'up_speed'
}],
stripeRows: true,
deferredRender: false,
autoScroll: true,
// fast way to figure out if we have a peer already.
peers: {},
clear: function() {
this.getStore().removeAll();
this.peers = {};
},
update: function(torrentId) {
deluge.client.web.get_torrent_status(torrentId, Deluge.Keys.Peers, {
success: this.onRequestComplete,
scope: this
});
},
onRequestComplete: function(torrent, options) {
if (!torrent) return;
var store = this.getStore();
var newPeers = [];
var addresses = {};
// Go through the peers updating and creating peer records
Ext.each(torrent.peers, function(peer) {
if (this.peers[peer.ip]) {
var record = store.getById(peer.ip);
record.beginEdit();
for (var k in peer) {
if (record.get(k) != peer[k]) {
record.set(k, peer[k]);
}
}
record.endEdit();
} else {
this.peers[peer.ip] = 1;
newPeers.push(new Deluge.data.Peer(peer, peer.ip));
}
addresses[peer.ip] = 1;
}, this);
store.add(newPeers);
// Remove any peers that shouldn't be left in the store
store.each(function(record) {
if (!addresses[record.id]) {
store.remove(record);
delete this.peers[record.id];
}
}, this);
store.commitChanges();
var sortState = store.getSortState();
if (!sortState) return;
store.sort(sortState.field, sortState.direction);
}, {
text: 'Address',
dataIndex: 'ip',
width: 125,
sortable: true,
renderer: function(v, p, r) {
var cls = (r.data['seed'] == 1024) ? 'x-deluge-seed': 'x-deluge-peer';
return Ext.String.format('<div class="{0}">{1}</div>', cls, v);
}
});
})();
}, {
text: 'Client',
dataIndex: 'client',
width: 125,
sortable: true,
renderer: function(v) { return fplain(v) }
}, {
text: 'Progress',
dataIndex: 'progress',
width: 150,
sortable: true,
renderer: function(v) {
var progress = (v * 100).toFixed(0);
return Deluge.progressBar(progress, this.width - 8, progress + '%');
}
}, {
text: 'Down Speed',
dataIndex: 'down_speed',
width: 100,
sortable: true,
renderer: function(v) { return fspeed(v) }
}, {
text: 'Up Speed',
dataIndex: 'up_speed',
width: 100,
sortable: true,
renderer: function(v) { return fspeed(v) }
}],
autoScroll: true,
deferredRender: false,
stripeRows: true,
clear: function() {
this.getStore().removeAll();
},
update: function(torrentId) {
var store = this.getStore(),
view = this.getView();
if (torrentId != this.torrentId) {
store.removeAll();
store.getProxy().url = 'peers/' + torrentId;
this.torrentId = torrentId;
}
store.load();
}
});

View File

@ -34,6 +34,7 @@
#
import os
import json
import time
import shutil
import urllib
@ -52,7 +53,7 @@ from deluge import common, component, configmanager
from deluge.core.rpcserver import check_ssl_keys
from deluge.ui import common as uicommon
from deluge.ui.tracker_icons import TrackerIcons
from deluge.ui.web.auth import Auth
from deluge.ui.web.auth import Auth, AuthError, AUTH_LEVEL_DEFAULT
from deluge.ui.web.common import Template, compress
from deluge.ui.web.json_api import JSON, WebApi
from deluge.ui.web.pluginmanager import PluginManager
@ -86,6 +87,8 @@ CONFIG_DEFAULTS = {
"cert": "ssl/daemon.cert"
}
PEERS_KEYS = ["peers"]
UI_CONFIG_KEYS = (
"theme", "sidebar_show_zero", "sidebar_multiple_filters",
"show_session_speed", "base", "first_login"
@ -198,6 +201,46 @@ class Tracker(resource.Resource):
d.addCallback(self.on_got_icon, request)
return server.NOT_DONE_YET
class TorrentResource(resource.Resource):
"""
Base class for exposing parts of a torrent's information
as a REST-ish interface.
"""
def getChild(self, path, request):
request.torrent_id = path
return self
def send_response(self, response, request):
request.setHeader("content-type", "text/plain")
request.write(compress(json.dumps(response), request))
request.finish()
class Peers(TorrentResource):
"""
Returns a list of the peers that a torrent currently has in JSON format.
"""
def on_got_peers(self, torrent, request):
peers = torrent["peers"]
self.send_response({
"peers": peers,
"total": len(peers)
}, request)
def render(self, request):
try:
component.get("Auth").check_request(request,
level=AUTH_LEVEL_DEFAULT)
except AuthError:
request.setResponseCode(http.FORBIDDEN)
return '<h1>Forbidden</h1>'
component.get("SessionProxy"
).get_torrent_status(request.torrent_id, PEERS_KEYS
).addCallback(self.on_got_peers, request)
return server.NOT_DONE_YET
class Flag(resource.Resource):
def getChild(self, path, request):
request.country = path
@ -466,6 +509,9 @@ class TopLevel(resource.Resource):
self.putChild("resources", static.File(rpath("resources")))
self.putChild("tracker", Tracker())
# Torrent REST resources
self.putChild("peers", Peers())
theme = component.get("DelugeWeb").config["theme"]
if not os.path.isfile(rpath("themes", "css", "xtheme-%s.css" % theme)):
theme = CONFIG_DEFAULTS.get("theme")