# -*- 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 dbus_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"], "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","?"), "uploaded_memory": torrent.uploaded_memory, } #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","tracker","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