#!/usr/bin/env python # -*- coding: utf-8 -*- # # webserver_framework.py # # Copyright (C) Martijn Voncken 2008 # # 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 # the Free Software Foundation; either version 3, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, write to: # The Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor # Boston, MA 02110-1301, USA. # """ json api. 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 import web from web import webapi import page_decorators as deco from web import cookies, setcookie as w_setcookie import utils from deluge.ui.client import sclient,aclient from deluge.log import LOG as log from deluge import component from utils import dict_cb from lib import json def json_response(result, id): print json.write({ "version":"1.1", "result":result, "id":id }) def json_error(message , id=-1, msg_number=123): log.error("JSON-error:%s" % message) print json.write({ "version":"1.1", "id":id, "error":{ "number":msg_number, "message":message, "error":message } }) class json_rpc: """ == Full client api == * 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 """ #extra exposed methods json_exposed = ["update_ui","get_stats","set_torrent_options","system_listMethods"] cache = {} torrent_options = ["trackers","max_connections","max_upload_slots","max_upload_speed", "max_download_speed","file_priorities","prioritize_first_last","auto_managed","stop_at_ratio", "stop_ratio","remove_at_ratio"] def GET(self): return json_error("only POST is supported") def POST(self , name=None): web.header("Content-Type", "application/x-json") ck = cookies() id = 0 if not(ck.has_key("session_id") and ck["session_id"] in utils.SESSIONS): return json_error("not authenticated", id) try: log.debug("json-data:") log.debug(webapi.data()) json_data = json.read(webapi.data()) id = json_data["id"] method = json_data["method"].replace(".", "_") params = json_data["params"] if method.startswith('_'): raise Exception('_ methods are illegal.') elif method in self.json_exposed: func = getattr(self, method) result = func(*params) else: result = self.exec_client_method(method, params) #log.debug("JSON-result:%s(%s)[%s] = %s" % (method, params, id, result)) return json_response(result, id) 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()) return json_error("%s:%s" % (e, message), id) def exec_client_method(self, method, params): if not hasattr(sclient,method): raise Exception('Unknown method:%s', method) #Call: func = getattr(sclient, method) return func(*params) # #Extra exposed methods: # def system_listMethods(self): "system.listMethods() see json/xmlrpc docs" return sclient.list_methods() + self.json_exposed def get_stats(self): """ returns: {{{ { 'download_rate':float(), 'upload_rate':float(), 'max_download':float(), 'max_upload':float(), 'num_connections':int(), 'max_num_connections':int(), 'dht_nodes',int() } }}} """ stats = {} aclient.get_download_rate(dict_cb('download_rate',stats)) aclient.get_upload_rate(dict_cb('upload_rate',stats)) aclient.get_config_value(dict_cb('max_download',stats) ,"max_download_speed") aclient.get_config_value(dict_cb('max_upload',stats) ,"max_upload_speed") aclient.get_num_connections(dict_cb("num_connections",stats)) aclient.get_config_value(dict_cb('max_num_connections',stats) ,"max_connections_global") aclient.get_dht_nodes(dict_cb('dht_nodes',stats)) aclient.force_call(block=True) return stats def update_ui(self, keys ,filter_dict , cache_id = None ): """ Composite call. Goal : limit the number of ajax calls input: {{{ keys: see get_torrent_status filter_dict: see label_get_filtered_ids # only effective if the label plugin is enabled. cache_id: # todo }}} returns: {{{ { "torrents": see get_torrent_status "filters": see label_get_filters "stats": see get_stats "cache_id":int # todo } }}} """ if 'Label' in sclient.get_enabled_plugins(): torrent_ids = sclient.label_get_filtered_ids(filter_dict) filters = sclient.label_filter_items() else: torrent_ids = sclient.get_session_state() filters = {"state":[["All",-1]],"tracker":[],"label":[]} return { "torrents":sclient.get_torrents_status(torrent_ids, keys), "filters":filters, "stats":self.get_stats(), "cache_id":-1 } def set_torrent_options(self, torrent_id, options_dict): """ Composite call. Goal: limit the number of ajax calls input: {{{ torrent_id: the id of the torrent who's options are to be changed options_dict: a dictionary inwhich the options to be changed are contained }}} """ for option in options_dict: if option in self.torrent_options: value = options_dict[option] if type(value) == str: try: value = float(value) except: pass print func = getattr(sclient, 'set_torrent_' + option) func(torrent_id, value) def register(): component.get("PageManager").register_page("/json/rpc",json_rpc) if __name__ == '__main__': print "todo: tests"