mirror of
https://github.com/codex-storage/deluge.git
synced 2025-01-28 12:14:49 +00:00
json-api+ajax demo
This commit is contained in:
parent
6a734dab69
commit
1aa4611e30
@ -67,16 +67,18 @@ web.webapi.internalerror = deluge_debugerror
|
|||||||
import pages
|
import pages
|
||||||
import config_tabs_webui #auto registers in ConfigUiManager
|
import config_tabs_webui #auto registers in ConfigUiManager
|
||||||
import config_tabs_deluge #auto registers in ConfigUiManager
|
import config_tabs_deluge #auto registers in ConfigUiManager
|
||||||
import register_menu
|
import register_menu #auto registers.
|
||||||
#
|
#manual register:
|
||||||
import torrent_add
|
import torrent_add
|
||||||
import torrent_options
|
|
||||||
import torrent_move
|
|
||||||
import config_forms
|
|
||||||
torrent_add.register()
|
torrent_add.register()
|
||||||
|
import torrent_options
|
||||||
torrent_options.register()
|
torrent_options.register()
|
||||||
|
import torrent_move
|
||||||
torrent_move.register()
|
torrent_move.register()
|
||||||
|
import config_forms
|
||||||
config_forms.register()
|
config_forms.register()
|
||||||
|
import json_api
|
||||||
|
json_api.register()
|
||||||
#/self registering pages.
|
#/self registering pages.
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#
|
#
|
||||||
# webserver_framework.py
|
# webserver_framework.py
|
||||||
#
|
#
|
||||||
# Copyright (C) Martijn Voncken 2007 <mvoncken@gmail.com>
|
# Copyright (C) Martijn Voncken 2008 <mvoncken@gmail.com>
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@ -32,180 +32,103 @@
|
|||||||
# statement from all source files in the program, then also delete it here.
|
# statement from all source files in the program, then also delete it here.
|
||||||
"""
|
"""
|
||||||
json api.
|
json api.
|
||||||
only used for XUL and/or external scripts
|
|
||||||
it would be possible not to include the python-json dependency.
|
design:
|
||||||
|
== Full client api ==
|
||||||
|
* url : /json/rpc/
|
||||||
|
* rpc-api : http://en.wikipedia.org/wiki/JSON-RPC
|
||||||
|
* methods : http://dev.deluge-torrent.org/wiki/Development/UiClient#Remoteapi
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from traceback import format_exc
|
||||||
from new import instancemethod
|
|
||||||
from inspect import getargspec
|
|
||||||
from utils import get_torrent_status,get_category_choosers, get_stats,filter_torrent_state,fsize,fspeed
|
|
||||||
from page_decorators import remote
|
|
||||||
from operator import attrgetter
|
|
||||||
import web
|
import web
|
||||||
from webserver_common import proxy, log
|
from web import webapi
|
||||||
|
from deluge.ui.client import sclient
|
||||||
|
from deluge.log import LOG as log
|
||||||
|
from deluge import component
|
||||||
|
|
||||||
def to_json(obj):
|
from page_decorators import check_session
|
||||||
from lib.pythonize import pythonize
|
|
||||||
obj = pythonize(obj)
|
|
||||||
try:
|
|
||||||
import json
|
|
||||||
return json.write(obj)
|
|
||||||
except ImportError:
|
|
||||||
raise ImportError("""Install python-json using your package-manager
|
|
||||||
http://sourceforge.net/projects/json-py/""")
|
|
||||||
|
|
||||||
class json_api:
|
|
||||||
|
class json_rpc:
|
||||||
"""
|
"""
|
||||||
eperimental json api
|
== Full client api ==
|
||||||
generic proxy for all methods onm self.
|
* url : /json/rpc
|
||||||
|
* rpc-api : http://en.wikipedia.org/wiki/JSON-RPC#Version_1.0
|
||||||
|
* methods : http://dev.deluge-torrent.org/wiki/Development/UiClient#Remoteapi
|
||||||
"""
|
"""
|
||||||
illegal_methods = ['shutdown', 'socket', 'xmlrpclib','pickle','os',
|
def GET(self):
|
||||||
'is_localhost','CoreProxy','connect_on_new_core', 'connect_on_no_core',
|
print '{"error":"only POST is supported"}'
|
||||||
'connected','deluge','GET','POST']
|
|
||||||
def __init__(self):
|
|
||||||
self._add_proxy_methods()
|
|
||||||
|
|
||||||
#extra exposed:
|
#security bug: does not check session!!
|
||||||
get_torrent_status = get_torrent_status
|
def POST(self):
|
||||||
|
web.header("Content-Type", "application/x-json")
|
||||||
|
id = 0
|
||||||
|
try:
|
||||||
|
import json #not listed in dependency's.
|
||||||
|
except:
|
||||||
|
log.error('{"error":"python-json is not installed"}')
|
||||||
|
return '{"error":"python-json is not installed"}'
|
||||||
|
try:
|
||||||
|
log.debug("json-data:")
|
||||||
|
log.debug(webapi.data())
|
||||||
|
json_data = json.read(webapi.data())
|
||||||
|
id = json_data["id"]
|
||||||
|
method = json_data["method"]
|
||||||
|
params = json_data["params"]
|
||||||
|
|
||||||
@remote
|
log.debug("PP==%s" % json_data["params"])
|
||||||
def POST(self,name):
|
|
||||||
import json
|
|
||||||
if name.startswith('_'):
|
|
||||||
raise AttributeError('_ methods are illegal.')
|
|
||||||
if name in self.illegal_methods:
|
|
||||||
raise AttributeError('Illegal method.')
|
|
||||||
if not(hasattr(self,name)):
|
|
||||||
raise AttributeError('No such method')
|
|
||||||
|
|
||||||
method = getattr(self,name)
|
if method.startswith('_'):
|
||||||
vars = web.input(kwargs= None)
|
raise Exception('_ methods are illegal.')
|
||||||
log.debug('vars=%s' % vars)
|
|
||||||
if vars.kwargs:
|
|
||||||
kwargs = json.read(vars.kwargs)
|
|
||||||
else:
|
|
||||||
kwargs = {}
|
|
||||||
|
|
||||||
result = method(**kwargs)
|
if method.startswith("system."):
|
||||||
|
result = self.exec_system_method(method, params,id)
|
||||||
|
else:
|
||||||
|
result = self.exec_client_method(method, params,id)
|
||||||
|
|
||||||
return "(" + to_json(result) + ")"
|
log.debug("JSON-result:%s(%s)[%s] = %s" % (method, params, id, result))
|
||||||
|
print json.write(result)
|
||||||
|
|
||||||
|
except Exception,e:
|
||||||
|
#verbose because you don't want exeptions in the error-handler.
|
||||||
|
message = ""
|
||||||
|
if hasattr(e,"message"):
|
||||||
|
message = e.message
|
||||||
|
log.debug(format_exc())
|
||||||
|
log.error("JSON-error:%s:%s %s" % (e, message, id))
|
||||||
|
print json.write({
|
||||||
|
"version":"1.1",
|
||||||
|
"id":id,
|
||||||
|
"error":{
|
||||||
|
"number":123,
|
||||||
|
"message":"%s:%s %s" % (e, message, id),
|
||||||
|
"error":format_exc()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
def list_methods(self):
|
def exec_client_method(self, method, params, id):
|
||||||
"""
|
if not hasattr(sclient,method):
|
||||||
list all json methods
|
raise Exception('Unknown method:%s', method)
|
||||||
returns a dict of {methodname:{args:[list of kwargs],doc:'string'},..}
|
|
||||||
"""
|
|
||||||
methods = [getattr(self,m) for m in dir(self)
|
|
||||||
if not m.startswith('_')
|
|
||||||
and (not m in self.illegal_methods)
|
|
||||||
and callable(getattr(self,m))
|
|
||||||
]
|
|
||||||
|
|
||||||
return dict([(f.__name__,
|
#Call:
|
||||||
{'args':getargspec(f)[0],'doc':(f.__doc__ or '').strip()})
|
func = getattr(sclient, method)
|
||||||
for f in methods])
|
log.debug("PARAMS=%s" % params)
|
||||||
|
result = func(*params)
|
||||||
def _add_proxy_methods(self):
|
|
||||||
methods = [getattr(proxy,m) for m in dir(proxy)
|
|
||||||
if not m.startswith('_')
|
|
||||||
and (not m in self.illegal_methods)
|
|
||||||
and callable(getattr(proxy,m))
|
|
||||||
]
|
|
||||||
for m in methods:
|
|
||||||
setattr(self,m.__name__,m)
|
|
||||||
|
|
||||||
#extra's:
|
|
||||||
def list_torrents(self):
|
|
||||||
return [get_torrent_status(torrent_id)
|
|
||||||
for torrent_id in proxy.get_session_state()]
|
|
||||||
|
|
||||||
def simplify_torrent_status(self, torrent):
|
|
||||||
"""smaller subset and preformatted data for the treelist"""
|
|
||||||
data = {
|
|
||||||
"id":torrent.id,
|
|
||||||
"message":torrent.message,
|
|
||||||
"name":torrent.name,
|
|
||||||
"total_size":fsize(torrent.total_size),
|
|
||||||
"progress":torrent.progress,
|
|
||||||
"category":torrent.category,
|
|
||||||
"seeds":"",
|
|
||||||
"peers":"",
|
|
||||||
"download_rate":"",
|
|
||||||
"upload_rate":"",
|
|
||||||
"eta":"",
|
|
||||||
"distributed_copies":"",
|
|
||||||
"ratio":"",
|
|
||||||
"calc_state_str":torrent.calc_state_str,
|
|
||||||
"queue_pos":torrent.queue_pos
|
|
||||||
}
|
|
||||||
if torrent.total_seeds > 0:
|
|
||||||
data['seeds'] = "%s (%s)" % (torrent.num_seeds, torrent.total_seeds)
|
|
||||||
if torrent.total_peers > 0:
|
|
||||||
data['peers'] = "%s (%s)" % (torrent.num_peers, torrent.total_peers)
|
|
||||||
if torrent.download_rate > 0:
|
|
||||||
data['download_rate'] = fspeed(torrent.download_rate)
|
|
||||||
if torrent.upload_rate > 0:
|
|
||||||
data['upload_rate'] = fspeed(torrent.upload_rate)
|
|
||||||
if torrent.eta > 0:
|
|
||||||
data['eta'] = ("%.3f" % torrent.eta)
|
|
||||||
if torrent.distributed_copies > 0:
|
|
||||||
data['distributed_copies'] = "%.3f" % torrent.distributed_copies
|
|
||||||
if torrent.ratio > 0:
|
|
||||||
data['ratio'] = "%.3f" % torrent.ratio
|
|
||||||
return data
|
|
||||||
|
|
||||||
def update_ui(self, filter=None, category=None ,sort='name' ,order='down'):
|
|
||||||
"""
|
|
||||||
Combines the most important ui calls into 1 composite call.
|
|
||||||
xmlhttp requests are expensive,max 2 running at the same time.
|
|
||||||
and performance over the internet is mostly related to the number
|
|
||||||
of requests (low ping)
|
|
||||||
returns :
|
|
||||||
{torrent_list:[{},..],'categories':[],'filters':'','stats':{}}
|
|
||||||
"""
|
|
||||||
torrent_list = self.list_torrents();
|
|
||||||
filter_tabs, category_tabs = get_category_choosers(torrent_list)
|
|
||||||
|
|
||||||
|
|
||||||
#filter-state
|
|
||||||
if filter:
|
|
||||||
torrent_list = filter_torrent_state(torrent_list, filter)
|
|
||||||
|
|
||||||
#filter-cat
|
|
||||||
if category:
|
|
||||||
torrent_list = [t for t in torrent_list if t.category == category]
|
|
||||||
|
|
||||||
#sorting
|
|
||||||
if sort:
|
|
||||||
torrent_list.sort(key=attrgetter(sort))
|
|
||||||
if order == 'up':
|
|
||||||
torrent_list = reversed(torrent_list)
|
|
||||||
|
|
||||||
torrent_list = [self.simplify_torrent_status(t) for t in torrent_list]
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'torrent_list':torrent_list,
|
"version":"1.1",
|
||||||
'categories':category_tabs,
|
"result":result,
|
||||||
'filters':filter_tabs,
|
"id":id
|
||||||
'stats':get_stats()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def exec_system_method(self, method, params, id):
|
||||||
|
if method == "system.listMethods":
|
||||||
|
return self.exec_client_method("list_methods", params, id)
|
||||||
|
raise Exception('Unknown method:%s', method)
|
||||||
|
|
||||||
|
def register():
|
||||||
|
component.get("PageManager").register_page("/json/rpc",json_rpc)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
from pprint import pprint
|
print "todo: tests"
|
||||||
#proxy.set_core_uri('http://localhost:58846') #How to configure this?
|
|
||||||
j = json_api()
|
|
||||||
if True:
|
|
||||||
print 'list-methods:'
|
|
||||||
methods = j.list_methods()
|
|
||||||
names = methods.keys()
|
|
||||||
names.sort()
|
|
||||||
for name in names:
|
|
||||||
m = methods[name]
|
|
||||||
print "%s(%s)\n %s\n" % (name , m['args'] , m['doc'])
|
|
||||||
|
|
||||||
#j.GET('list_torrents')
|
|
||||||
j.POST('list_torrents')
|
|
||||||
|
|
||||||
|
84
deluge/ui/webui/templates/ajax_demo/index.html
Normal file
84
deluge/ui/webui/templates/ajax_demo/index.html
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
$def with (torrent_list, organize_filters)
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
|
|
||||||
|
$:render.header(_("AJAX index"), 'home')
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Using this lib because it was linked from http://json-rpc.org/wiki/implementations
|
||||||
|
http://code.google.com/p/json-xml-rpc/wiki/DocumentationForJavaScript
|
||||||
|
-->
|
||||||
|
<script language="javascript" src="/template/static/js/rpc.js"></script>
|
||||||
|
|
||||||
|
<div class="info">Just a demo template for damoxc
|
||||||
|
(or anyone else who wants to make an ajax template)</div>
|
||||||
|
|
||||||
|
<script language="javascript">
|
||||||
|
|
||||||
|
var service = new rpc.ServiceProxy("$base/json/rpc", {/*options*/});
|
||||||
|
|
||||||
|
function get_session_state( ) {
|
||||||
|
//This code will send a data object via a GET request and alert the retrieved data.
|
||||||
|
service.get_session_state({params:[],
|
||||||
|
onSuccess:function(result){
|
||||||
|
alert("session_state: " + result);
|
||||||
|
},
|
||||||
|
onException:function(errorObj){
|
||||||
|
alert("Error: " + errorObj);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function get_torrents_status(torrent_ids) {
|
||||||
|
//alert("start status:");
|
||||||
|
service.get_torrents_status({
|
||||||
|
params:[torrent_ids,["name","progress"]],
|
||||||
|
onSuccess:function(torrent_list){
|
||||||
|
for (var torrent_id in torrent_list){
|
||||||
|
torrent = torrent_list[torrent_id]
|
||||||
|
//alert(torrent);
|
||||||
|
alert(torrent.name + " " + torrent.progress + "%");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onException:function(errorObj){
|
||||||
|
alert("Error: " + errorObj);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function list_torrents(torrent_ids) {
|
||||||
|
//This code will send a data object via a GET request and alert the retrieved data.
|
||||||
|
service.get_session_state({params:[],
|
||||||
|
onSuccess:function(torrent_ids){
|
||||||
|
get_torrents_status(torrent_ids)
|
||||||
|
},
|
||||||
|
onException:function(errorObj){
|
||||||
|
alert("Error: " + errorObj);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function update_torrent(torrent) {
|
||||||
|
alert(torrent.name);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<button onclick="get_session_state()">get_session_state()</button>
|
||||||
|
|
||||||
|
<button onclick="list_torrents()">List torrents</button>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="torrent_list">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
$:render.footer()
|
||||||
|
|
||||||
|
|
8
deluge/ui/webui/templates/ajax_demo/meta.cfg
Normal file
8
deluge/ui/webui/templates/ajax_demo/meta.cfg
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
'authors':['Martijn Voncken <mvoncken@gmail.com>'],
|
||||||
|
'inherits':['deluge','advanced','white'],
|
||||||
|
'comment':"""
|
||||||
|
Demo template for external ajax devs.
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
1030
deluge/ui/webui/templates/ajax_demo/static/js/rpc.js
Normal file
1030
deluge/ui/webui/templates/ajax_demo/static/js/rpc.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -20,6 +20,9 @@ $if get('keyword'):
|
|||||||
$for state, num in filters.state:
|
$for state, num in filters.state:
|
||||||
$if (num > 0) or (state == get('state')):
|
$if (num > 0) or (state == get('state')):
|
||||||
<li
|
<li
|
||||||
|
$if not get('state'):
|
||||||
|
$if state == "All":
|
||||||
|
class="selected"
|
||||||
$if state == get('state'):
|
$if state == get('state'):
|
||||||
class="selected"
|
class="selected"
|
||||||
>
|
>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user