# -*- coding: utf-8 -*- # Dbus Ipc for experimental web interface # # dbus_interface.py # # Copyright (C) Martijn Voncken 2007 # Contains copy and pasted code from other parts of deluge,see deluge AUTHORS # # 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 2, 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. # # In addition, as a special exception, the copyright holders give # permission to link the code of portions of this program with the OpenSSL # library. # You must obey the GNU General Public License in all respects for all of # the code used other than OpenSSL. If you modify file(s) with this # exception, you may extend this exception to your version of the file(s), # but you are not obligated to do so. If you do not wish to do so, delete # this exception statement from your version. If you delete this exception # statement from all source files in the program, then also delete it here. import os import gtk import dbus import deluge.common as common from lib.pythonize import pythonize import base64 import random random.seed() dbus_interface="org.deluge_torrent.dbusplugin" dbus_service="/org/deluge_torrent/DelugeDbusPlugin" dbus_manager = None def get_dbus_manager(*args): #another way to make a singleton. global dbus_manager if not dbus_manager: dbus_manager = DbusManager(*args) return dbus_manager class DbusManager(dbus.service.Object): def __init__(self, core, interface,config,config_file): self.core = core self.interface = interface self.config = config self.config_file = config_file self.bus = dbus.SessionBus() bus_name = dbus.service.BusName(dbus_interface,bus=self.bus) dbus.service.Object.__init__(self, bus_name,dbus_service) # #todo : add: get_interface_version in=i,get_config_value in=s out=s # @dbus.service.method(dbus_interface=dbus_interface, in_signature="",out_signature="as") def get_session_state(self): """Returns a list of torrent_ids in the session. same as 0.6, but returns type "as" instead of a pickle """ torrent_list = [str(key) for key in self.core.unique_IDs] return torrent_list @dbus.service.method(dbus_interface=dbus_interface, in_signature="sas",out_signature="a{sv}") def get_torrent_status(self, torrent_id, keys): """return torrent metadata of a single torrent as a dict 0.6 returns a pickle, this returns a dbus-type. +added some more values to the dict """ torrent_id = int(torrent_id) # Convert the array of strings to a python list of strings nkeys = [str(key) for key in keys] state = self.core.get_torrent_state(torrent_id) torrent = self.core.unique_IDs[torrent_id] status = { "name": state["name"].decode('utf-8','ignore'), "total_size": state["total_size"], "num_pieces": state["num_pieces"], "state": state['state'], "user_paused": self.core.is_user_paused(torrent_id), "paused":state['is_paused'], "progress": int(state["progress"] * 100), "next_announce": state["next_announce"], "total_payload_download":state["total_payload_download"], "total_payload_upload": state["total_payload_upload"], "download_payload_rate": state["download_rate"], "upload_payload_rate": state["upload_rate"], "num_peers": state["num_peers"], "num_seeds": state["num_seeds"], "total_wanted": state["total_wanted"], "eta": common.estimate_eta(state), "ratio": self.interface.manager.calc_ratio(torrent_id,state), #non 0.6 values follow here: "tracker_status": state.get("tracker_status","?").decode('utf-8','ignore'), "uploaded_memory": torrent.uploaded_memory, "tracker":state["tracker"].decode('utf-8','ignore') } #more non 0.6 values for key in ["total_seeds", "total_peers","is_seed", "total_done", "total_download", "total_upload" #, "download_rate","upload_rate" , "num_files", "piece_length", "distributed_copies" ,"next_announce","queue_pos"]: status[key] = state[key] #print 'all_keys:',sorted(status.keys()) status_subset = {} for key in keys: if key in status: status_subset[key] = status[key] else: print 'mbus error,no key named:', key return status_subset @dbus.service.method(dbus_interface=dbus_interface, in_signature="as",out_signature="") def pause_torrent(self, torrents): """same as 0.6 interface""" for torrent_id in torrents: torrent_id = int(torrent_id) self.core.set_user_pause(torrent_id, True) @dbus.service.method(dbus_interface=dbus_interface, in_signature="as", out_signature="") def resume_torrent(self, torrents): """same as 0.6 interface""" for torrent_id in torrents: torrent_id = int(torrent_id) self.core.set_user_pause(torrent_id, False) @dbus.service.method(dbus_interface=dbus_interface, in_signature="as", out_signature="") def force_reannounce(self, torrents): """same as 0.6 interface""" for torrent_id in torrents: torrent_id = int(torrent_id) self.core.update_tracker(torrent_id) @dbus.service.method(dbus_interface=dbus_interface, in_signature="asbb", out_signature="") def remove_torrent(self, torrent_ids, data_also, torrent_also): """remove a torrent,and optionally data and torrent additions compared to 0.6 interface: (data_also, torrent_also) """ for torrent_id in torrent_ids: torrent_id = int(torrent_id) self.core.remove_torrent(torrent_id, bool(data_also) ,bool( torrent_also)) #this should not be needed: gtk.gdk.threads_enter() try: self.interface.torrent_model_remove(torrent_id) except: pass @dbus.service.method(dbus_interface=dbus_interface, in_signature="s", out_signature="b") def add_torrent_url(self, url): filename = fetch_url(url) self._add_torrent(filename) return True @dbus.service.method(dbus_interface=dbus_interface, in_signature="s", out_signature="b") def queue_up(self, torrent_id): self.core.queue_up(int(torrent_id)) return True @dbus.service.method(dbus_interface=dbus_interface, in_signature="s", out_signature="b") def queue_down(self, torrent_id): self.core.queue_down(int(torrent_id)) return True @dbus.service.method(dbus_interface=dbus_interface, in_signature="ss", out_signature="b") def add_torrent_filecontent(self, name, filecontent_b64): """not available in deluge 0.6 interface""" #name = fillename without directory name = name.replace('\\','/') name = 'deluge_' + str(random.random()) + '_' + name.split('/')[-1] filename = os.path.join(self.core.config.get("default_download_path"), name) filecontent = base64.b64decode(filecontent_b64) f = open(filename,"wb") #no with statement, that's py 2.5+ f.write(filecontent) f.close() print 'write:',filename self._add_torrent(filename) return True @dbus.service.method(dbus_interface=dbus_interface, in_signature="", out_signature="a{sv}") def get_config(self): return self.core.config.mapping @dbus.service.method(dbus_interface=dbus_interface, in_signature="s", out_signature="v") def get_config_value(self,key): return self.core.config.mapping[pythonize(key)] #ugly! @dbus.service.method(dbus_interface=dbus_interface, in_signature="a{sv}", out_signature="") def set_config(self, config): """Set the config with values from dictionary""" config = deluge.common.pythonize(config) # Load all the values into the configuration for key in self.core.config.keys(): self.core.config[key] = config[key] self.core.apply_prefs() @dbus.service.method(dbus_interface=dbus_interface, in_signature="", out_signature="v") def get_download_rate(self): return self.core.get_state()['download_rate'] @dbus.service.method(dbus_interface=dbus_interface, in_signature="", out_signature="v") def get_upload_rate(self): return self.core.get_state()['upload_rate'] @dbus.service.method(dbus_interface=dbus_interface, in_signature="", out_signature="v") def get_num_connections(self): core_state = self.core.get_state() return core_state['num_connections'] #internal def _add_torrent(self, filename): filename = unicode(filename) target = self.core.config.get("default_download_path") torrent_id = self.core.add_torrent(filename, target, self.interface.config.get("use_compact_storage")) #update gtk-ui This should not be needed!! gtk.gdk.threads_enter() try: self.interface.torrent_model_append(torrent_id) except: pass #finally is 2.5 only! gtk.gdk.threads_leave() return True def fetch_url(url): import urllib try: filename, headers = urllib.urlretrieve(url) except IOError: raise Exception( "Network error while trying to fetch torrent from %s" % url) else: if (filename.endswith(".torrent") or headers["content-type"]=="application/x-bittorrent"): return filename else: raise Exception("URL doesn't appear to be a valid torrent file:%s" % url) return None