diff --git a/deluge/common.py b/deluge/common.py index 0775797e0..79b42d6dd 100644 --- a/deluge/common.py +++ b/deluge/common.py @@ -2,19 +2,19 @@ # common.py # # Copyright (C) 2007, 2008 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may 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 of the License, or (at your option) # any later version. -# +# # deluge 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 deluge. If not, write to: # The Free Software Foundation, Inc., @@ -89,9 +89,9 @@ def get_revision(): f.close() except IOError, e: pass - + return revision - + def get_default_config_dir(filename=None): """ Returns the config path if no filename is specified Returns the config directory + filename as a path if filename is specified @@ -131,7 +131,7 @@ def get_pixmap(fname): def get_logo(size): """Returns a deluge logo pixbuf based on the size parameter.""" import gtk - if windows_check(): + if windows_check(): return gtk.gdk.pixbuf_new_from_file_at_size(get_pixmap("deluge.png"), \ size, size) else: @@ -158,16 +158,16 @@ def open_url_in_browser(url): webbrowser.open(self.url) BrowserThread(url).start() return False - + import gobject gobject.idle_add(start_browser) -def build_menu_radio_list(value_list, callback, pref_value=None, - suffix=None, show_notset=False, notset_label=None, notset_lessthan=0, +def build_menu_radio_list(value_list, callback, pref_value=None, + suffix=None, show_notset=False, notset_label=None, notset_lessthan=0, show_other=False, show_activated=False, activated_label=None): - # Build a menu with radio menu items from a list and connect them to - # the callback. The pref_value is what you would like to test for the + # Build a menu with radio menu items from a list and connect them to + # the callback. The pref_value is what you would like to test for the # default active radio item. import gtk if notset_label is None: @@ -182,14 +182,14 @@ def build_menu_radio_list(value_list, callback, pref_value=None, if pref_value > -1 and pref_value not in value_list: value_list.pop() value_list.append(pref_value) - + for value in sorted(value_list): if suffix != None: menuitem = gtk.RadioMenuItem(group, str(value) + " " + \ suffix) else: menuitem = gtk.RadioMenuItem(group, str(value)) - + group = menuitem if value == pref_value and pref_value != None: @@ -203,7 +203,7 @@ def build_menu_radio_list(value_list, callback, pref_value=None, if show_activated is True: for value in sorted(value_list): menuitem = gtk.RadioMenuItem(group, str(activated_label)) - + group = menuitem if value == pref_value and pref_value != None: @@ -222,7 +222,7 @@ def build_menu_radio_list(value_list, callback, pref_value=None, menuitem.set_active(True) menuitem.connect("toggled", callback) menu.append(menuitem) - + # Add the Other... menuitem if show_other is True: menuitem = gtk.SeparatorMenuItem() @@ -230,7 +230,7 @@ def build_menu_radio_list(value_list, callback, pref_value=None, menuitem = gtk.MenuItem(_("Other...")) menuitem.connect("activate", callback) menu.append(menuitem) - + return menu def show_other_dialog(string, default=None): @@ -238,7 +238,7 @@ def show_other_dialog(string, default=None): import gtk import gtk.glade dialog_glade = gtk.glade.XML( - pkg_resources.resource_filename("deluge.ui.gtkui", + pkg_resources.resource_filename("deluge.ui.gtkui", "glade/dgtkpopups.glade")) speed_dialog = dialog_glade.get_widget("speed_dialog") spin_title = dialog_glade.get_widget("spin_title") @@ -253,17 +253,17 @@ def show_other_dialog(string, default=None): else: speed_dialog.destroy() return None - + speed_dialog.destroy() return value - + ## Formatting text functions def fsize(fsize_b): """Returns formatted string describing filesize fsize_b should be in bytes Returned value will be in either KB, MB, or GB - """ + """ fsize_kb = fsize_b / 1024.0 if fsize_kb < 1000: return "%.1f KiB" % fsize_kb @@ -287,7 +287,7 @@ def fpeer(num_peers, total_peers): return "%d (%d)" % (num_peers, total_peers) else: return "%d" % num_peers - + def ftime(seconds): """Returns a formatted time string""" if seconds == 0: @@ -318,7 +318,7 @@ def is_url(url): return bool(re.search('^(https?|ftp)://', url)) def fetch_url(url): - """Downloads a torrent file from a given + """Downloads a torrent file from a given URL and checks the file's validity.""" import urllib from deluge.log import LOG as log @@ -333,7 +333,7 @@ def fetch_url(url): else: log.debug("URL doesn't appear to be a valid torrent file: %s", url) return None - + def pythonize(var): """Translates DBUS types back to basic Python types.""" if isinstance(var, list): diff --git a/deluge/core/core.py b/deluge/core/core.py index 9d0eded00..87088b046 100644 --- a/deluge/core/core.py +++ b/deluge/core/core.py @@ -2,19 +2,19 @@ # core.py # # Copyright (C) 2007, 2008 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may 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 of the License, or (at your option) # any later version. -# +# # deluge 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 deluge. If not, write to: # The Free Software Foundation, Inc., @@ -56,7 +56,7 @@ from deluge.core.alertmanager import AlertManager from deluge.core.signalmanager import SignalManager from deluge.core.autoadd import AutoAdd from deluge.log import LOG as log - + DEFAULT_PREFS = { "config_location": deluge.configmanager.get_config_dir(), "send_info": False, @@ -120,27 +120,27 @@ DEFAULT_PREFS = { "random_outgoing_ports": True, "peer_tos": "0x00", } - + class Core( - ThreadingMixIn, + ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServer, component.Component): def __init__(self, port): log.debug("Core init..") component.Component.__init__(self, "Core") self.client_address = None - + # Get config self.config = deluge.configmanager.ConfigManager("core.conf", DEFAULT_PREFS) if port == None: port = self.config["daemon_port"] - + if self.config["allow_remote"]: hostname = "" else: hostname = "localhost" - + # Setup the xmlrpc server try: log.info("Starting XMLRPC server on port %s", port) @@ -151,20 +151,20 @@ class Core( sys.exit(0) self.register_multicall_functions() - + # Register all export_* functions for func in dir(self): if func.startswith("export_"): self.register_function(getattr(self, "%s" % func), func[7:]) self.register_introspection_functions() - + # Initialize gettext if deluge.common.windows_check(): locale.setlocale(locale.LC_ALL, '') else: locale.setlocale(locale.LC_MESSAGES, '') - locale.bindtextdomain("deluge", + locale.bindtextdomain("deluge", pkg_resources.resource_filename( "deluge", "i18n")) locale.textdomain("deluge") @@ -179,7 +179,7 @@ class Core( # Setup signals signal.signal(signal.SIGINT, self._shutdown) signal.signal(signal.SIGTERM, self._shutdown) - if not deluge.common.windows_check(): + if not deluge.common.windows_check(): signal.signal(signal.SIGHUP, self._shutdown) else: from win32api import SetConsoleCtrlHandler @@ -199,7 +199,7 @@ class Core( request, client_address = self.socket.accept() self.client_address = client_address[0] return (request, client_address) - + def run(self): """Starts the core""" @@ -210,20 +210,20 @@ class Core( while len(version) < 4: version.append(0) fingerprint = lt.fingerprint("DE", *version) - + # Start the libtorrent session log.debug("Starting libtorrent session..") self.session = lt.session(fingerprint) - + # Set the user agent self.settings = lt.session_settings() self.settings.user_agent = "Deluge %s" % deluge.common.get_version() - + # Set session settings self.settings.lazy_bitfields = 1 self.settings.send_redundant_have = True self.session.set_settings(self.settings) - + # Load metadata extension self.session.add_extension(lt.create_metadata_plugin) @@ -232,7 +232,7 @@ class Core( self._on_set_torrentfiles_location) self.config.register_set_function("state_location", self._on_set_state_location) - self.config.register_set_function("listen_ports", + self.config.register_set_function("listen_ports", self._on_set_listen_ports) self.config.register_set_function("random_port", self._on_set_random_port) @@ -303,23 +303,23 @@ class Core( self.config.register_change_callback(self._on_config_value_change) # Start the AlertManager self.alerts = AlertManager(self.session) - + # Start the SignalManager - self.signals = SignalManager() + self.signals = SignalManager() # Load plugins self.plugins = PluginManager(self) # Start the TorrentManager self.torrents = TorrentManager(self.session, self.alerts) - + # Create the AutoAdd component - self.autoadd = AutoAdd() + self.autoadd = AutoAdd() component.start() - + self._should_shutdown = False - + self.listen_thread = threading.Thread(target=self.handle_thread) self.listen_thread.setDaemon(False) self.listen_thread.start() @@ -336,10 +336,10 @@ class Core( while not self._should_shutdown: self.handle_request() self._should_shutdown = False - + except Exception, e: log.debug("handle_thread: %s", e) - + def _shutdown(self, *data): """This is called by a thread from shutdown()""" log.info("Shutting down core..") @@ -373,7 +373,7 @@ class Core( def export_ping(self): """A method to see if the core is running""" return True - + def export_shutdown(self): """Shutdown the core""" # Make shutdown an async call @@ -385,66 +385,66 @@ class Core( self.signals.register_client(self.client_address, port) if self.config["new_release_check"]: self.check_new_release() - + def export_deregister_client(self): """De-registers a client with the signal manager.""" self.signals.deregister_client(self.client_address) - + def export_add_torrent_file(self, filename, filedump, options): """Adds a torrent file to the libtorrent session This requires the torrents filename and a dump of it's content """ gobject.idle_add(self._add_torrent_file, filename, filedump, options) - + def _add_torrent_file(self, filename, filedump, options): # Turn the filedump into a torrent_info if not isinstance(filedump, str): filedump = filedump.data - + if len(filedump) == 0: log.warning("Torrent file is corrupt!") return - + try: torrent_info = lt.torrent_info(lt.bdecode(filedump)) except RuntimeError, e: log.warning("Unable to decode torrent file: %s", e) return None - + torrent_id = self.torrents.add(filedump=filedump, options=options, filename=filename) - + # Run the plugin hooks for 'post_torrent_add' self.plugins.run_post_torrent_add(torrent_id) def export_add_torrent_url(self, url, save_path, options): log.info("Attempting to add url %s", url) - + # Get the actual filename of the torrent from the url provided. filename = url.split("/")[-1] - + # Get the .torrent file from the url torrent_file = deluge.common.fetch_url(url) if torrent_file is None: return False - + # Dump the torrents file contents to a string try: filedump = open(torrent_file, "rb").read() except IOError: log.warning("Unable to open %s for reading.", torrent_file) return False - + # Add the torrent to session return self.export_add_torrent_file( filename, filedump, options) - + def export_remove_torrent(self, torrent_ids, remove_torrent, remove_data): log.debug("Removing torrent %s from the core.", torrent_ids) for torrent_id in torrent_ids: if self.torrents.remove(torrent_id, remove_torrent, remove_data): # Run the plugin hooks for 'post_torrent_remove' self.plugins.run_post_torrent_remove(torrent_id) - + def export_force_reannounce(self, torrent_ids): log.debug("Forcing reannouncment to: %s", torrent_ids) for torrent_id in torrent_ids: @@ -455,22 +455,22 @@ class Core( for torrent_id in torrent_ids: if not self.torrents[torrent_id].pause(): log.warning("Error pausing torrent %s", torrent_id) - + def export_move_storage(self, torrent_ids, dest): log.debug("Moving storage %s to %s", torrent_ids, dest) for torrent_id in torrent_ids: if not self.torrents[torrent_id].move_storage(dest): log.warning("Error moving torrent %s to %s", torrent_id, dest) - + def export_pause_all_torrents(self): """Pause all torrents in the session""" self.session.pause() - + def export_resume_all_torrents(self): """Resume all torrents in the session""" self.session.resume() self.torrent_all_resumed() - + def export_resume_torrent(self, torrent_ids): log.debug("Resuming: %s", torrent_ids) for torrent_id in torrent_ids: @@ -484,13 +484,13 @@ class Core( except KeyError: # The torrent_id is not found in the torrentmanager, so return None return None - + # Get the leftover fields and ask the plugin manager to fill them leftover_fields = list(set(keys) - set(status.keys())) if len(leftover_fields) > 0: status.update(self.plugins.get_status(torrent_id, leftover_fields)) return status - + def export_get_torrents_status(self, torrent_ids, keys): status_dict = {}.fromkeys(torrent_ids) @@ -505,25 +505,25 @@ class Core( if len(leftover_fields) > 0: status.update( self.plugins.get_status(torrent_id, leftover_fields)) - + status_dict[torrent_id] = status # Emit the torrent_status signal to the clients return status_dict - + def export_get_session_state(self): """Returns a list of torrent_ids in the session.""" # Get the torrent list from the TorrentManager return self.torrents.get_torrent_list() - + def export_save_state(self): """Save the current session state to file.""" # Have the TorrentManager save it's state self.torrents.save_state() - + def export_get_config(self): """Get all the preferences as a dictionary""" return self.config.get_config() - + def export_get_config_value(self, key): """Get the config value for key""" try: @@ -539,15 +539,15 @@ class Core( # Load all the values into the configuration for key in config.keys(): self.config[key] = config[key] - + def export_get_listen_port(self): """Returns the active listen port""" return self.session.listen_port() - + def export_get_num_connections(self): """Returns the current number of connections""" return self.session.num_connections() - + def export_get_dht_nodes(self): """Returns the number of dht nodes""" return self.session.status().dht_nodes @@ -559,7 +559,7 @@ class Core( def export_get_upload_rate(self): """Returns the payload upload rate""" return self.session.status().payload_upload_rate - + def export_get_available_plugins(self): """Returns a list of plugins available in the core""" return self.plugins.get_available_plugins() @@ -571,60 +571,60 @@ class Core( def export_enable_plugin(self, plugin): self.plugins.enable_plugin(plugin) return None - + def export_disable_plugin(self, plugin): self.plugins.disable_plugin(plugin) return None - + def export_force_recheck(self, torrent_ids): """Forces a data recheck on torrent_ids""" for torrent_id in torrent_ids: self.torrents[torrent_id].force_recheck() - + def export_set_torrent_trackers(self, torrent_id, trackers): """Sets a torrents tracker list. trackers will be [{"url", "tier"}]""" return self.torrents[torrent_id].set_trackers(trackers) - + def export_set_torrent_max_connections(self, torrent_id, value): """Sets a torrents max number of connections""" return self.torrents[torrent_id].set_max_connections(value) - + def export_set_torrent_max_upload_slots(self, torrent_id, value): """Sets a torrents max number of upload slots""" return self.torrents[torrent_id].set_max_upload_slots(value) - + def export_set_torrent_max_upload_speed(self, torrent_id, value): """Sets a torrents max upload speed""" return self.torrents[torrent_id].set_max_upload_speed(value) - + def export_set_torrent_max_download_speed(self, torrent_id, value): """Sets a torrents max download speed""" return self.torrents[torrent_id].set_max_download_speed(value) - + def export_set_torrent_file_priorities(self, torrent_id, priorities): """Sets a torrents file priorities""" return self.torrents[torrent_id].set_file_priorities(priorities) - + def export_set_torrent_prioritize_first_last(self, torrent_id, value): """Sets a higher priority to the first and last pieces""" return self.torrents[torrent_id].set_prioritize_first_last(value) - + def export_set_torrent_auto_managed(self, torrent_id, value): """Sets the auto managed flag for queueing purposes""" return self.torrents[torrent_id].set_auto_managed(value) - + def export_set_torrent_stop_at_ratio(self, torrent_id, value): """Sets the torrent to stop at 'stop_ratio'""" return self.torrents[torrent_id].set_stop_at_ratio(value) - + def export_set_torrent_stop_ratio(self, torrent_id, value): """Sets the ratio when to stop a torrent if 'stop_at_ratio' is set""" return self.torrents[torrent_id].set_stop_ratio(value) - + def export_set_torrent_remove_at_ratio(self, torrent_id, value): """Sets the torrent to be removed at 'stop_ratio'""" return self.torrents[torrent_id].set_remove_at_ratio(value) - + def export_set_torrent_move_on_completed(self, torrent_id, value): """Sets the torrent to be moved when completed""" return self.torrents[torrent_id].set_move_on_completed(value) @@ -632,7 +632,7 @@ class Core( def export_set_torrent_move_on_completed_path(self, torrent_id, value): """Sets the path for the torrent to be moved when completed""" return self.torrents[torrent_id].set_move_on_completed_path(value) - + def export_block_ip_range(self, range): """Block an ip range""" try: @@ -640,12 +640,12 @@ class Core( except AttributeError: self.export_reset_ip_filter() self.ip_filter.add_rule(range[0], range[1], 1) - + def export_reset_ip_filter(self): """Clears the ip filter""" self.ip_filter = lt.ip_filter() self.session.set_ip_filter(self.ip_filter) - + def export_get_health(self): """Returns True if we have established incoming connections""" return self.session.status().has_incoming_connections @@ -700,7 +700,7 @@ class Core( """Emitted when a torrent has been removed from the core""" log.debug("torrent_remove signal emitted") self.signals.emit("torrent_removed", torrent_id) - + def torrent_paused(self, torrent_id): """Emitted when a torrent is paused""" log.debug("torrent_paused signal emitted") @@ -730,31 +730,31 @@ class Core( """Emitted when a torrent queue position is changed""" log.debug("torrent_queue_changed signal emitted") self.signals.emit("torrent_queue_changed") - + # Config set functions def _on_config_value_change(self, key, value): self.config_value_changed(key, value) - + def _on_set_torrentfiles_location(self, key, value): if self.config["copy_torrent_file"]: try: os.makedirs(value) except Exception, e: log.debug("Unable to make directory: %s", e) - + def _on_set_state_location(self, key, value): if not os.access(value, os.F_OK): try: os.makedirs(value) except Exception, e: log.debug("Unable to make directory: %s", e) - + def _on_set_listen_ports(self, key, value): # Only set the listen ports if random_port is not true if self.config["random_port"] is not True: log.debug("listen port range set to %s-%s", value[0], value[1]) self.session.listen_on(value[0], value[1]) - + def _on_set_random_port(self, key, value): log.debug("random port value set to %s", value) # We need to check if the value has been changed to true and false @@ -767,17 +767,17 @@ class Core( listen_ports.append(listen_ports[0]+10) else: listen_ports = self.config["listen_ports"] - + # Set the listen ports - log.debug("listen port range set to %s-%s", listen_ports[0], + log.debug("listen port range set to %s-%s", listen_ports[0], listen_ports[1]) self.session.listen_on(listen_ports[0], listen_ports[1]) - + def _on_set_outgoing_ports(self, key, value): if not self.config["random_outgoing_ports"]: log.debug("outgoing port range set to %s-%s", value[0], value[1]) self.session.outgoing_ports(value[0], value[1]) - + def _on_set_random_outgoing_ports(self, key, value): if value: self.session.outgoing_ports(0, 0) @@ -789,9 +789,9 @@ class Core( except ValueError, e: log.debug("Invalid tos byte: %s", e) return - + self.session.set_settings(self.settings) - + def _on_set_dht(self, key, value): log.debug("dht value set to %s", value) state_file = deluge.common.get_default_config_dir('dht.state') @@ -821,14 +821,14 @@ class Core( except IOError: log.warning("failed to save dht state to file") self.session.stop_dht() - + def _on_set_upnp(self, key, value): log.debug("upnp value set to %s", value) if value: self.session.start_upnp() else: self.session.stop_upnp() - + def _on_set_natpmp(self, key, value): log.debug("natpmp value set to %s", value) if value: @@ -842,7 +842,7 @@ class Core( self.session.start_lsd() else: self.session.stop_lsd() - + def _on_set_utpex(self, key, value): log.debug("utpex value set to %s", value) if value: @@ -859,7 +859,7 @@ class Core( self.session.set_pe_settings(pe_settings) set = self.session.get_pe_settings() log.debug("encryption settings:\n\t\t\tout_policy: %s\n\t\t\ - in_policy: %s\n\t\t\tlevel: %s\n\t\t\tprefer_rc4: %s", + in_policy: %s\n\t\t\tlevel: %s\n\t\t\tprefer_rc4: %s", set.out_enc_policy, set.in_enc_policy, set.allowed_enc_level, @@ -868,7 +868,7 @@ class Core( def _on_set_max_connections_global(self, key, value): log.debug("max_connections_global set to %s..", value) self.session.set_max_connections(value) - + def _on_set_max_upload_speed(self, key, value): log.debug("max_upload_speed set to %s..", value) # We need to convert Kb/s to B/s @@ -887,22 +887,22 @@ class Core( else: v = int(value * 1024) self.session.set_download_rate_limit(v) - + def _on_set_max_upload_slots_global(self, key, value): log.debug("max_upload_slots_global set to %s..", value) self.session.set_max_uploads(value) def _on_set_max_half_open_connections(self, key, value): self.session.set_max_half_open_connections(value) - + def _on_set_max_connections_per_second(self, key, value): self.settings.connection_speed = value self.session.set_settings(self.settings) - + def _on_ignore_limits_on_local_network(self, key, value): self.settings.ignore_limits_on_local_network = value self.session.set_settings(self.settings) - + def _on_set_share_ratio_limit(self, key, value): log.debug("%s set to %s..", key, value) self.settings.share_ratio_limit = value @@ -936,7 +936,7 @@ class Core( log.debug("active_limit: %s", self.settings.active_limit) self.settings.active_limit = value self.session.set_settings(self.settings) - + def _on_set_dont_count_slow_torrents(self, key, value): log.debug("%s set to %s..", key, value) self.settings.dont_count_slow_torrents = value @@ -982,7 +982,7 @@ class Core( log.debug("Unable to get release info from website: %s", e) return self.check_new_release() - + def check_new_release(self): if self.new_release: log.debug("new_release: %s", self.new_release) @@ -990,7 +990,7 @@ class Core( self.signals.emit("new_version_available", self.new_release) return self.new_release return False - + def _on_new_release_check(self, key, value): if value: log.debug("Checking for new release..") diff --git a/deluge/core/torrent.py b/deluge/core/torrent.py index e9debeadf..4ae3a62ac 100644 --- a/deluge/core/torrent.py +++ b/deluge/core/torrent.py @@ -2,19 +2,19 @@ # torrent.py # # Copyright (C) 2007, 2008 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may 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 of the License, or (at your option) # any later version. -# +# # deluge 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 deluge. If not, write to: # The Free Software Foundation, Inc., @@ -62,7 +62,7 @@ class Torrent: # We store the filename just in case we need to make a copy of the torrentfile self.filename = filename - + # Holds status info so that we don't need to keep getting it from lt self.status = self.handle.status() self.torrent_info = self.handle.get_torrent_info() @@ -71,10 +71,10 @@ class Torrent: self.files = self.get_files() # Set the default file priorities to normal self.file_priorities = [1]* len(self.files) - + # Default total_uploaded to 0, this may be changed by the state self.total_uploaded = 0 - + # Set default auto_managed value self.auto_managed = options["auto_managed"] if not handle.is_paused(): @@ -83,7 +83,7 @@ class Torrent: # We need to keep track if the torrent is finished in the state to prevent # some weird things on state load. self.is_finished = False - + # Queueing options self.stop_at_ratio = False self.stop_ratio = 2.00 @@ -91,7 +91,7 @@ class Torrent: self.move_on_completed = False self.move_on_completed_path = deluge.common.get_default_download_dir() - + # Load values from state if we have it if state is not None: # This is for saving the total uploaded between sessions @@ -105,7 +105,7 @@ class Torrent: self.set_stop_at_ratio(state.stop_at_ratio) self.set_stop_ratio(state.stop_ratio) self.set_remove_at_ratio(state.remove_at_ratio) - else: + else: # Tracker list self.trackers = [] # Create a list of trackers @@ -121,46 +121,46 @@ class Torrent: self.set_max_upload_speed(options["max_upload_speed_per_torrent"]) self.set_max_download_speed(options["max_download_speed_per_torrent"]) self.set_prioritize_first_last(options["prioritize_first_last_pieces"]) - self.handle.resolve_countries(True) + self.handle.resolve_countries(True) if options.has_key("file_priorities"): self.set_file_priorities(options["file_priorities"]) - + # Set the allocation mode self.compact = options["compact_allocation"] # Where the torrent is being saved to self.save_path = options["download_location"] # Status message holds error info about the torrent self.statusmsg = "OK" - + # The torrents state self.update_state() - + # The tracker status self.tracker_status = "" - + log.debug("Torrent object created.") - + def set_tracker_status(self, status): """Sets the tracker status""" self.tracker_status = status - + def set_max_connections(self, max_connections): self.max_connections = int(max_connections) self.handle.set_max_connections(self.max_connections) - + def set_max_upload_slots(self, max_slots): self.max_upload_slots = int(max_slots) self.handle.set_max_uploads(self.max_upload_slots) - + def set_max_upload_speed(self, m_up_speed): self.max_upload_speed = m_up_speed if m_up_speed < 0: v = -1 else: v = int(m_up_speed * 1024) - + self.handle.set_upload_limit(v) - + def set_max_download_speed(self, m_down_speed): self.max_download_speed = m_down_speed if m_down_speed < 0: @@ -168,7 +168,7 @@ class Torrent: else: v = int(m_down_speed * 1024) self.handle.set_download_limit(v) - + def set_prioritize_first_last(self, prioritize): self.prioritize_first_last = prioritize if self.prioritize_first_last: @@ -178,30 +178,30 @@ class Torrent: priorities[0] = 7 priorities[-1] = 7 self.handle.prioritize_pieces(priorities) - + def set_save_path(self, save_path): self.save_path = save_path - + def set_auto_managed(self, auto_managed): self.auto_managed = auto_managed self.handle.auto_managed(auto_managed) self.update_state() - + def set_stop_ratio(self, stop_ratio): self.stop_ratio = stop_ratio - + def set_stop_at_ratio(self, stop_at_ratio): self.stop_at_ratio = stop_at_ratio - + def set_remove_at_ratio(self, remove_at_ratio): self.remove_at_ratio = remove_at_ratio - + def set_file_priorities(self, file_priorities): log.debug("setting %s's file priorities: %s", self.torrent_id, file_priorities) if len(file_priorities) != len(self.files): log.debug("file_priorities len != num_files") return - + self.handle.prioritize_files(file_priorities) if 0 in self.file_priorities: @@ -213,7 +213,7 @@ class Torrent: self.is_finished = False self.update_state() break - + self.file_priorities = file_priorities # Set the first/last priorities if needed @@ -223,7 +223,7 @@ class Torrent: """Sets trackers""" if trackers == None: trackers = [] - + log.debug("Setting trackers for %s: %s", self.torrent_id, trackers) tracker_list = [] @@ -231,9 +231,9 @@ class Torrent: new_entry = lt.announce_entry(tracker["url"]) new_entry.tier = tracker["tier"] tracker_list.append(new_entry) - + self.handle.replace_trackers(tracker_list) - + # Print out the trackers for t in self.handle.trackers(): log.debug("tier: %s tracker: %s", t.tier, t.url) @@ -242,19 +242,19 @@ class Torrent: if len(trackers) > 0: # Force a reannounce if there is at least 1 tracker self.force_reannounce() - + def set_move_on_completed(self, value): self.move_on_completed = value - + def set_move_on_completed_path(self, value): self.move_on_completed_path = value - + def update_state(self): """Updates the state based on what libtorrent's state for the torrent is""" # Set the initial state based on the lt state LTSTATE = deluge.common.LT_TORRENT_STATE ltstate = int(self.handle.status().state) - + log.debug("set_state_based_on_ltstate: %s", ltstate) log.debug("session.is_paused: %s", component.get("Core").session.is_paused()) if ltstate == LTSTATE["Queued"] or ltstate == LTSTATE["Checking"]: @@ -266,7 +266,7 @@ class Torrent: self.state = "Seeding" elif ltstate == LTSTATE["Allocating"]: self.state = "Allocating" - + if self.handle.is_paused() and len(self.handle.status().error) > 0: # This is an error'd torrent self.state = "Error" @@ -276,7 +276,7 @@ class Torrent: self.state = "Queued" elif component.get("Core").session.is_paused() or (self.handle.is_paused() and not self.handle.is_auto_managed()): self.state = "Paused" - + def set_state(self, state): """Accepts state strings, ie, "Paused", "Seeding", etc.""" if state not in TORRENT_STATE: @@ -295,17 +295,17 @@ class Torrent: status = self.handle.status() else: status = self.status - + left = status.total_wanted - status.total_done - + if left == 0 or status.download_payload_rate == 0: return 0 - + try: eta = left / status.download_payload_rate except ZeroDivisionError: eta = 0 - + return eta def get_ratio(self): @@ -314,14 +314,14 @@ class Torrent: status = self.handle.status() else: status = self.status - + up = self.total_uploaded + status.total_payload_upload down = status.total_done - + # Convert 'up' and 'down' to floats for proper calculation up = float(up) down = float(down) - + try: ratio = up / down except ZeroDivisionError: @@ -335,7 +335,7 @@ class Torrent: torrent_info = self.handle.get_torrent_info() else: torrent_info = self.torrent_info - + ret = [] files = torrent_info.files() for index, file in enumerate(files): @@ -346,12 +346,12 @@ class Torrent: 'offset': file.offset }) return ret - + def get_peers(self): """Returns a list of peers and various information about them""" ret = [] peers = self.handle.get_peer_info() - + for peer in peers: # We do not want to report peers that are half-connected if peer.flags & peer.connecting or peer.flags & peer.handshake: @@ -360,7 +360,7 @@ class Torrent: client = str(peer.client).decode("utf-8") except UnicodeDecodeError: client = str(peer.client).decode("latin-1") - + # Make country a proper string country = str() for c in peer.country: @@ -368,7 +368,7 @@ class Torrent: country += " " else: country += c - + ret.append({ "ip": "%s:%s" % (peer.ip[0], peer.ip[1]), "up_speed": peer.up_speed, @@ -380,11 +380,11 @@ class Torrent: }) return ret - + def get_queue_position(self): """Returns the torrents queue position""" return self.handle.queue_position() - + def get_file_progress(self): """Returns the file progress as a list of floats.. 0.0 -> 1.0""" file_progress = self.handle.file_progress() @@ -396,17 +396,17 @@ class Torrent: ret.append(0.0) return ret - + def get_tracker_host(self): """Returns just the hostname of the currently connected tracker if no tracker is connected, it uses the 1st tracker.""" if not self.status: self.status = self.handle.status() - + tracker = self.status.current_tracker if not tracker and self.trackers: tracker = self.trackers[0]["url"] - + if tracker: url = urlparse(tracker) if hasattr(url, "hostname"): @@ -415,22 +415,23 @@ class Torrent: if len(parts) > 2: host = ".".join(parts[-2:]) return host - return "" - + return "" + def get_status(self, keys): """Returns the status of the torrent based on the keys provided""" # Create the full dictionary self.status = self.handle.status() self.torrent_info = self.handle.get_torrent_info() - + # Adjust progress to be 0-100 value progress = self.status.progress * 100 - + # Adjust status.distributed_copies to return a non-negative value distributed_copies = self.status.distributed_copies if distributed_copies < 0: distributed_copies = 0.0 - + + #if you add a key here->add it to core.get_status_keys too. full_status = { "distributed_copies": distributed_copies, "total_done": self.status.total_done, @@ -472,7 +473,7 @@ class Torrent: "move_on_completed": self.move_on_completed, "move_on_completed_path": self.move_on_completed_path } - + fns = { "name": self.torrent_info.name, "private": self.torrent_info.priv, @@ -491,10 +492,10 @@ class Torrent: self.status = None self.torrent_info = None - + # Create the desired status dictionary and return it status_dict = {} - + if len(keys) == 0: status_dict = full_status for key in fns: @@ -507,7 +508,7 @@ class Torrent: status_dict[key] = fns[key]() return status_dict - + def apply_options(self): """Applies the per-torrent options that are set.""" self.handle.set_max_connections(self.max_connections) @@ -516,7 +517,7 @@ class Torrent: self.handle.set_download_limit(int(self.max_download_speed * 1024)) self.handle.prioritize_files(self.file_priorities) self.handle.resolve_countries(True) - + def pause(self): """Pause this torrent""" # Turn off auto-management so the torrent will not be unpaused by lt queueing @@ -534,19 +535,19 @@ class Torrent: except Exception, e: log.debug("Unable to pause torrent: %s", e) return False - + return True - + def resume(self): """Resumes this torrent""" - + if self.handle.is_paused() and self.handle.is_auto_managed(): log.debug("Torrent is being auto-managed, cannot resume!") return else: # Reset the status message just in case of resuming an Error'd torrent self.set_status_message("OK") - + if self.handle.is_finished(): # If the torrent has already reached it's 'stop_seed_ratio' then do not do anything if self.config["stop_seed_at_ratio"] or self.stop_at_ratio: @@ -554,7 +555,7 @@ class Torrent: ratio = self.stop_ratio else: ratio = self.config["stop_seed_ratio"] - + if self.get_ratio() >= ratio: self.signals.emit("torrent_resume_at_stop_ratio") return @@ -562,14 +563,14 @@ class Torrent: if self.auto_managed: # This torrent is to be auto-managed by lt queueing self.handle.auto_managed(True) - + try: self.handle.resume() except: pass return True - + def move_storage(self, dest): """Move a torrent's storage location""" try: @@ -591,7 +592,7 @@ class Torrent: fastresume.write(resume_data) fastresume.close() except IOError: - log.warning("Error trying to save fastresume file") + log.warning("Error trying to save fastresume file") def delete_fastresume(self): """Deletes the .fastresume file""" @@ -614,7 +615,7 @@ class Torrent: os.remove(path) except Exception, e: log.warning("Unable to delete the torrent file: %s", e) - + def force_reannounce(self): """Force a tracker reannounce""" try: @@ -622,9 +623,9 @@ class Torrent: except Exception, e: log.debug("Unable to force reannounce: %s", e) return False - + return True - + def scrape_tracker(self): """Scrape the tracker""" try: @@ -632,9 +633,9 @@ class Torrent: except Exception, e: log.debug("Unable to scrape tracker: %s", e) return False - + return True - + def force_recheck(self): """Forces a recheck of the torrents pieces""" try: